mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
* feat(media-use): fast heygen CLI onboarding — actionable diagnostics, --doctor, free-usage framing media-use resolves bgm/sfx/image/icon (catalog), voice (TTS), and avatar video through the heygen CLI — the free-usage path. Agents hit a dead end when it's missing/unauthed. This guides them to install it fast, at the point of need. - Centralized actionable diagnostics (lib/heygen-cli.mjs): every heygen-backed resolve, on failure, prints the exact fix on stderr — not-installed (curl install one-liner), not-authenticated (heygen auth login), outdated (heygen update). Routed through heygen-search + voice-provider. stdout stays clean JSON. - resolve --doctor preflight (human + --json): checks heygen present/version/ auth, ffmpeg, ffprobe, node, a fix per gap. Exit 0 unless ffmpeg missing. - SKILL reframe: install-first callout; heygen as the free-usage gateway for bgm/image/voice/avatar-video; removed the false "degrades gracefully" claim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): address #2065 review — classifier blocker, doctor contract, telemetry - Blocker: classifyHeygenError no longer treats a bare "not found" as CLI-missing (a stale voiceId → "voice not found" was sending users to reinstall a working CLI); keep only ENOENT + "command not found". Regression test added. - 401 now matches \b401\b, not any "401" substring (request IDs no longer misread). - --doctor: top-level ok requires ffmpeg AND ffprobe (matches SKILL.md); emits media_use_doctor_run telemetry; auth status queried with --json + JSON-only parse; auth timeout softened (network issue, not a false "unauthenticated"); node version gated on >= 18; version-without-semver labeled, not silently green. - Nits: install cmd uses && ; dropped the runResolveStatus alias. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): require OAuth-capable heygen CLI (v0.3.0), fix auth-status probe E2E against the live free-usage backend surfaced three issues: - HEYGEN_MIN_VERSION was 0.1.6, but that CLI can't use OAuth ("heygen-cli can't use OAuth yet") — free usage needs >= v0.3.0. Bumped the floor; --doctor now also nudges `heygen update` when a newer stable exists (always-latest). - Onboarding pointed at `heygen auth login --key` (API credits / billing); the free path is `--oauth` (subscription/free credits). Fixed install + auth guidance and SKILL.md accordingly. - `heygen auth status --json` is an unknown flag on v0.3.0 (JSON is the default output) — the added --json broke auth detection. Dropped it; verified --doctor reports authenticated on a real free (OAuth) account. Tests assert against the exported message constants instead of brittle literals. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): address #2065 review nits — one root cause on old CLI, floor policy - --doctor skips the auth check when the version check fails (below v0.3.0): an old CLI's auth probe fails for the same root cause, so users no longer see two errors ("outdated" + "not authenticated") — one root cause, one fix. - Comment links the auth-status probe's JSON-default assumption to HEYGEN_MIN_VERSION >= 0.3.0 so the floor isn't silently lowered later. - SKILL.md states the uniform v0.3.0 requirement (nudged even for API-key use). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv * fix(media-use): doctor prints one heygen row per fact The 'heygen on PATH' and 'heygen version' checks both rendered their detail as `heygen v0.3.0`, so --doctor printed two byte-identical green lines. Make the PATH row report presence ("heygen found on PATH") and let the version row own the version string — one row per fact, no duplicate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H5k87mPZ4d6yiFwcWSb8Vv --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
67 lines
1.9 KiB
JavaScript
67 lines
1.9 KiB
JavaScript
import { execFileSync } from "node:child_process";
|
|
import { reportHeygenFailure } from "./heygen-cli.mjs";
|
|
|
|
// Voice / TTS generation via the HeyGen CLI — the only external CLI media-use
|
|
// shells (CLI-only invariant: media-use holds no keys; the CLI owns auth).
|
|
// Flags verified against `heygen voice speech create --help` (v0.3.0).
|
|
|
|
function runJson(bin, argv, label) {
|
|
let out;
|
|
try {
|
|
out = execFileSync(bin, argv, {
|
|
encoding: "utf8",
|
|
timeout: 120000,
|
|
stdio: ["pipe", "pipe", "pipe"],
|
|
});
|
|
} catch (err) {
|
|
reportHeygenFailure(err, `${bin} ${label}`);
|
|
return null;
|
|
}
|
|
try {
|
|
return JSON.parse(out);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function result(url, duration, provider, intent) {
|
|
if (!url) return null;
|
|
return {
|
|
url,
|
|
source: "generated",
|
|
metadata: {
|
|
description: intent,
|
|
provider,
|
|
...(duration != null && { duration }),
|
|
provenance: { prompt: intent },
|
|
},
|
|
};
|
|
}
|
|
|
|
// HeyGen TTS requires a starfish-engine voice. Default to the first one the
|
|
// catalog returns (deterministic order); pass ctx.voiceId to override.
|
|
// ponytail: listed once per process; the resolved asset is frozen + cached after
|
|
// first use, so the network list only happens on a cache miss.
|
|
let cachedVoiceId;
|
|
function defaultVoiceId() {
|
|
if (cachedVoiceId !== undefined) return cachedVoiceId;
|
|
const j = runJson(
|
|
"heygen",
|
|
["voice", "list", "--engine", "starfish", "--limit", "1"],
|
|
"voice list",
|
|
);
|
|
cachedVoiceId = j?.data?.[0]?.voice_id || null;
|
|
return cachedVoiceId;
|
|
}
|
|
|
|
export async function heygenTtsGenerate(intent, ctx) {
|
|
const voiceId = ctx?.voiceId || defaultVoiceId();
|
|
if (!voiceId) return null;
|
|
const p = runJson(
|
|
"heygen",
|
|
["voice", "speech", "create", "--text", intent, "--voice-id", voiceId],
|
|
"tts",
|
|
);
|
|
return result(p?.data?.audio_url, p?.data?.duration, "heygen.tts", intent);
|
|
}
|