feat(media-use): use CLI free HeyGen usage#2027
Conversation
5803392 to
374159d
Compare
vanceingalls
left a comment
There was a problem hiding this comment.
R1 — hyperframes #2027 at 374159db98d0dd27fa8b085d7fc34153167eca26
🟢 LGTM on the CLI side of the CLI-free-usage handshake. Small, single-purpose, and the header contract lines up cleanly with the EF #41448 server-side.
Cross-repo handshake — verified
- CLI sends
X-HeyGen-Source: cli(bothbuildAuthHeadersforauthorization: BearerOAuth ANDx-api-key; media-useheygen.mjsmerges the same header viaHEYGEN_CLI_SOURCE_HEADERS). - EF's
movio/api/decorators/authentication/oauth2.pyaccepts exactly"cli"as a programmatic source and stampsrequest.source = "cli"(via_resolve_programmatic_source, allowlist{"mcp", "cli"}). - Header name + value + case all match. Server's allowlist is exact-string, so
Cli/CLIwould fail — worth ensuring the CLI never mutates the constant.HEYGEN_CLI_SOURCE = "cli"is lowercase in bothclient.tsandheygen.mjs; good.
Registry reorder — the load-bearing product change
Voice provider order flipped from [kokoro.local, heygen.tts] (Kokoro-first) to [heygen.tts, kokoro.local] (HeyGen-first when credentialed). Test voice cascade: HeyGen TTS first, Kokoro remains the local fallback is updated and now asserts the new order + kept the --local-only skip semantics. This means:
- Fresh CLI OAuth user → media-use resolves voice → hits HeyGen TTS → consumes free 10min/month allowance per EF #41448's
TTS_FREE_CREDITpool. If exhausted (or ifcheck_oauth_integration_billingreturnsBLOCK_UPGRADE_REQUIREDbecause the user is already paid), HeyGen TTS returns 402/403 → the registry cascade will fall through to Kokoro. That fallback edge is worth spot-checking at runtime. --local-onlyuser → still Kokoro (network flag skips heygen.tts). ✅- User with API key only → HeyGen TTS runs on normal API billing (server's
oauth_billing_decisionis not MCP_FREE_TIER because it's not OAuth). Doc reflects this correctly.
Non-blocking nit: I can't tell from this diff alone whether media-use's cascade actually falls through to Kokoro on a 402 from heygen.tts (vs. surfacing the error). If heygenTtsGenerate throws on 402, the registry cascade's next provider gets a shot; if it just returns an unusable result, the cascade stops. Worth confirming this is exercised in the runtime e2e.
Docs
Sync'd across SKILL.md, references/requirements.md, references/tts.md, references/operations.md, and scripts/lib/registry.mjs comments. The framing — "OAuth CLI users consume the web-plan free allowance (10 min/month); API keys follow the normal API billing path" — matches the EF-side semantics exactly. skills-manifest.json hash bumped as expected.
What I can and can't verify
- ✅ Code-level e2e header contract, provider order, doc alignment.
- ❌ Live runtime hit against dev/prod (no CLI OAuth + HeyGen account in this session). Runtime leg needs someone with the env warm to confirm: (a)
X-HeyGen-Source: cliactually makes it through any intermediate proxy without being stripped, (b) the media-use TTS cascade falls through gracefully when heygen.tts returns 402, (c) fresh-user TTS_FREE_CREDIT actually grants on first request.
One flag on the pre-commit note
PR body says: "the monorepo typecheck hook failed during dependency/module resolution before change-specific type errors ... so the commit was created with --no-verify". Fine for the commit itself, but worth double-checking that typecheck in CI (which resolves against a clean install) is actually green on the current head before merge — a real type error in the touched files would land here silently and the CI signal is the only thing catching it.
R1 by Via
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Reviewed at 374159db98d0dd27fa8b085d7fc34153167eca26 as part of the BE/FE pair with experiment-framework#41448.
Static end-to-end trace across both PRs. No blockers on the FE side. Full BE analysis lives in the EF#41448 review — this side is small and clean.
Wire contract with EF#41448 — verified match:
packages/cli/src/auth/client.ts:24-25HEYGEN_CLI_SOURCE_HEADER = "X-HeyGen-Source"/HEYGEN_CLI_SOURCE = "cli"→ EF#41448movio/api/decorators/authentication/oauth2.py:83-88_PROGRAMMATIC_OAUTH_SOURCES = frozenset({"mcp", "cli"}). Header key + value both match. HTTP header lookups are case-insensitive, so the FE'sX-HeyGen-Sourceand BE's exact-case.headers.get(...)don't diverge.skills/media-use/audio/scripts/lib/heygen.mjs:12HEYGEN_CLI_SOURCE_HEADERS = { "X-HeyGen-Source": "cli" }— same wire tag, second producer. Two tests (heygen.test.mjs) cover both API-key and OAuth branches.buildAuthHeadersreturns the source header on both OAuth and API-key credentials. BE only elevates it torequest.sourcewhenis_integration_oauth_app=True(viaOAuth2Authentication.apply), so API-key traffic keeps its normal billing path. Benign, but see the small nit below.
Provider ordering flip (skills/media-use/scripts/lib/registry.mjs:56-59) — HeyGen TTS now precedes kokoro.local. The registry-test rewrite is coherent (heygen.tts first, paid: true, network; kokoro.local second, offline). Under --local-only, the network flag skips heygen.tts cleanly and Kokoro remains reachable — that's the escape hatch for offline/uncredentialed setups. Docs in SKILL.md + references/tts.md + references/requirements.md update in lockstep.
Findings
🟡 X-HeyGen-Source: cli on x-api-key requests is dead metadata. packages/cli/src/auth/client.ts:184-188 returns the header on the API-key branch too; the BE explicitly ignores it for direct API-key traffic (is_integration_oauth_app=False skips source elevation and the free-quota grant), and API-key requests keep the normal TTSAPIDeductStrategy path. Not incorrect — just extra header bytes on every API-key request, and it reads as "cli-source claim on an API-key request" which is contradictory. Consider gating the header emission to credential.type === "oauth" for symmetry with BE's actual use. Same nit applies to skills/media-use/audio/scripts/lib/heygen.mjs:74 where the spread unconditionally adds the header to both cred types. Non-blocking; the tests explicitly assert the current wide behavior, so this would be a deliberate scope-narrow.
🟡 X-HeyGen-Client-Origin not sent yet. EF#41448 wires oauth2.py to read X-HeyGen-Client-Origin (→ request.client_origin) and its tests exercise "cli:claude_code" end-to-end. Neither CLI helper here emits it — the BE tests pass because they set request.client_origin directly. Fine for this PR since BE has a bare-cli fallback, but worth exporting a HEYGEN_CLI_ORIGIN_HEADER constant now so Claude Code (and any future cli:<origin> consumer) can send it without duplicating the string. Coordination-only, no functional impact today.
🟡 paid: true on HeyGen TTS + agent-confirm X4 semantics. registry.mjs:34-37 still tags heygen.tts as P (network + paid). For OAuth CLI users with unspent monthly free quota, the first call is genuinely free — but agent-initiated calls will still hit the "confirm before paid" prompt per the X4 rule in SKILL.md. Product/UX question, not a code one: is a per-call confirmation the right UX for the first 10 free minutes/month? If not, the paid flag semantics may need a tri-state ("quota-first, paid after"). Non-blocking; flagging so the intent is on the record.
🟢 Test coverage on the auth-headers change: client.test.ts covers both branches; heygen.test.mjs (new) covers both credential shapes with a clean env-isolation helper; registry.test.mjs covers the ordering flip and the --local-only semantic. Skills manifest regenerated (skills-manifest.json hash bump for media-use).
Cross-check with recent CLI PRs — clean:
- HF#2008 citty
default: "30"unreachable-fallback trap: no citty arg definitions in this diff. - HF#2035
autoUpdate.tsisSafeVersionguard: this PR doesn't add new consumers of registry data orexec-shaped call sites; nothing to gate.
— Review by Rames D Jusso
Summary
HyperFrames CLI and media-use now mark HeyGen requests as CLI-originated so OAuth users can consume the backend-owned free usage pools instead of being routed like generic OAuth traffic.
The CLI auth helper and media-use HeyGen TTS helper both send
X-HeyGen-Source: cli. Media-use now prefers HeyGen TTS when credentials are available, keeps Kokoro as the local/offline fallback, and documents the split: OAuth browser sign-in can use the free CLI allowance, while direct API keys stay on normal API billing. The skills manifest was regenerated for the media-use docs change.Tests
node --test skills/media-use/audio/scripts/lib/heygen.test.mjs skills/media-use/scripts/lib/registry.test.mjs skills/media-use/audio/scripts/lib/tts.test.mjs— 20 passednpm exec vitest run packages/cli/src/auth/client.test.ts packages/cli/src/cloud/index.test.ts— 23 passedbunx oxfmt --check packages/cli/src/auth/client.ts packages/cli/src/auth/client.test.ts skills/media-use/audio/scripts/lib/heygen.mjs skills/media-use/audio/scripts/lib/heygen.test.mjs skills/media-use/scripts/lib/registry.mjs skills/media-use/scripts/lib/registry.test.mjs skills/media-use/audio/scripts/heygen-tts.mjs@hyperframes/parsers, Node type declarations, generated runtime-inline modules), so the commit was created with--no-verifyafter targeted tests passed.