hyperframes 0.8.140 → 0.8.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{add-7SPPQB64.js → add-YS2RCWEK.js} +11 -11
- package/dist/{animatedGifPrep-HN3CJSFX.js → animatedGifPrep-SAMCDT7T.js} +6 -7
- package/dist/{asset-AZMFUTRL.js → asset-BLWC2NWB.js} +2 -2
- package/dist/{assetCataloger-TQUWYLHE.js → assetCataloger-FVNBBRP3.js} +2 -2
- package/dist/{auth-XGY74R4A.js → auth-DRZB7OAC.js} +5 -4
- package/dist/{autoUpdate-44NTJBQU.js → autoUpdate-I5I3BOJK.js} +2 -2
- package/dist/{backgroundChecks-ET4DJEPG.js → backgroundChecks-VR2QHGNY.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-HTWWNF3P.js → batchRender-4YUHQI6X.js} +3 -3
- package/dist/{beats-4GYTZUZG.js → beats-7OT7ALV3.js} +11 -12
- package/dist/{benchmark-CJQHE3QN.js → benchmark-Q2XQCRTR.js} +8 -8
- package/dist/{browser-NXOVPGDG.js → browser-7VAWQQZC.js} +3 -3
- package/dist/{bundleWithLocalizedFonts-6PJP54TC.js → bundleWithLocalizedFonts-DDMLLL2R.js} +2 -2
- package/dist/{capture-EHOMO4MP.js → capture-HEJS6M5A.js} +74 -33
- package/dist/{capture-5EHGRJZM.js → capture-U7ABPKS5.js} +24 -8
- package/dist/{catalog-PTX5Z52R.js → catalog-DVJ3XPDN.js} +11 -11
- package/dist/{catch-up-I45SPSCU.js → catch-up-DF6AVSN2.js} +4 -4
- package/dist/{check-LJ7LG7OI.js → check-KXORRWAE.js} +75 -72
- package/dist/{checkBrowser-6EK5E4PG.js → checkBrowser-J7HZJTRH.js} +28 -29
- package/dist/{chunk-XTFJOR3K.js → chunk-4SFGME7K.js} +1 -1
- package/dist/{chunk-EDYBFSPE.js → chunk-6RAD4BCC.js} +5 -8
- package/dist/{chunk-MTVDXB6S.js → chunk-6UM3R7PP.js} +21 -13
- package/dist/{chunk-IRBESGJ7.js → chunk-6UYZB4TY.js} +1 -1
- package/dist/{chunk-VVJFZU6B.js → chunk-75JJVANE.js} +2 -2
- package/dist/{chunk-F3SPK6SO.js → chunk-7FCDA4RH.js} +1 -1
- package/dist/{chunk-R4QIE6UY.js → chunk-7M4KAPAA.js} +92 -31
- package/dist/{chunk-HKCNZITN.js → chunk-AM5L5YIA.js} +1 -1
- package/dist/{chunk-BRITLL56.js → chunk-B4P5VDFQ.js} +1 -1
- package/dist/{chunk-2GMKWYEH.js → chunk-B6SMSALD.js} +53 -12
- package/dist/{chunk-DQNOPYAG.js → chunk-BPZVDBHY.js} +1 -1
- package/dist/{chunk-246MOR5U.js → chunk-DKSDVDXG.js} +175 -162
- package/dist/{chunk-6U3MN4HW.js → chunk-DPHJKWP5.js} +2 -2
- package/dist/{chunk-MUINFURL.js → chunk-DZEVA7DI.js} +2 -2
- package/dist/{chunk-KMKWRIMC.js → chunk-EFDXGAAD.js} +1 -1
- package/dist/{chunk-LQG6GPVK.js → chunk-FWRNQOLT.js} +1 -1
- package/dist/{chunk-IFY57LUW.js → chunk-FZK35GT4.js} +1 -1
- package/dist/{chunk-PUBKHEEA.js → chunk-GYE63V4P.js} +10 -10
- package/dist/{chunk-WQBA3MH4.js → chunk-H2X52H2N.js} +3 -3
- package/dist/{chunk-QG3PDRSO.js → chunk-HMUF7FEO.js} +1 -1
- package/dist/{chunk-TPECLKPA.js → chunk-IDX52YJN.js} +5 -5
- package/dist/{chunk-ABJNXQUA.js → chunk-IMY4QQTQ.js} +7 -7
- package/dist/{chunk-WY5OODN5.js → chunk-IW2CKH6A.js} +3645 -3
- package/dist/{chunk-X3X3VETN.js → chunk-IXCTG2WY.js} +3 -3
- package/dist/chunk-J4UX3RS3.js +3717 -0
- package/dist/{chunk-DDQ4LOML.js → chunk-JLKMWY2T.js} +1 -1
- package/dist/{chunk-ASW6NWON.js → chunk-JOTLPC6S.js} +1 -1
- package/dist/{chunk-MU6C2VP6.js → chunk-JU6Y5TTZ.js} +2 -2
- package/dist/{chunk-PPKFM5WE.js → chunk-KYN364DU.js} +2 -2
- package/dist/{chunk-C2JUHIG5.js → chunk-LBKDQKWV.js} +1 -1
- package/dist/{chunk-PB3P4MKP.js → chunk-LJAU4TPS.js} +1 -1
- package/dist/{chunk-PFJFHNU3.js → chunk-LQASVU3I.js} +3 -3
- package/dist/{chunk-KJLY2SDJ.js → chunk-LTGGTEMA.js} +2 -2
- package/dist/{chunk-L2H2L22P.js → chunk-MVTUV4TP.js} +2 -2
- package/dist/{chunk-QBK7WKHT.js → chunk-OKCR3JTO.js} +104 -4
- package/dist/{chunk-BQKEGPPL.js → chunk-OSUKETIH.js} +1 -1
- package/dist/chunk-P36MALQF.js +15 -0
- package/dist/{chunk-KECV5G4V.js → chunk-QGRFOOLE.js} +10 -10
- package/dist/{chunk-5IKJONRY.js → chunk-QXPCARFE.js} +1 -1
- package/dist/{chunk-WKTHUC2R.js → chunk-R2JEKLDZ.js} +1 -1
- package/dist/{chunk-TYT2TLJR.js → chunk-TY6XOHEM.js} +1 -1
- package/dist/{chunk-SLEPCESN.js → chunk-WGVOWWVK.js} +2 -2
- package/dist/{chunk-KFQNMY36.js → chunk-WSGY5CDQ.js} +5 -5
- package/dist/{chunk-PE2I3AFA.js → chunk-WTMUCJ6Z.js} +1 -1
- package/dist/{chunk-JSGANFFH.js → chunk-XGMNQQTJ.js} +1 -1
- package/dist/{chunk-BJ5JWOES.js → chunk-XTLBLWOV.js} +1 -1
- package/dist/{chunk-QP3BRKGF.js → chunk-YFT3BL7N.js} +19 -1
- package/dist/{chunk-MHK5DE2P.js → chunk-YHRZQJXH.js} +59 -37
- package/dist/{clean-Y3AQITOX.js → clean-KTHTV5G5.js} +12 -13
- package/dist/cli.js +52 -52
- package/dist/{cloud-TDPOMBZF.js → cloud-ARSZWALC.js} +4 -4
- package/dist/{cloudrun-FPAVAVHP.js → cloudrun-NM7RXOCL.js} +3 -3
- package/dist/commands/layout-audit.browser.js +20 -20
- package/dist/{compare-LHL225ND.js → compare-CY4UO3RW.js} +23 -24
- package/dist/{compiler-RTU3KTLF.js → compiler-VTH2D3ID.js} +3 -4
- package/dist/{component-L2YX7J56.js → component-FRUIAXIR.js} +3 -3
- package/dist/{compositions-V2UZKG7S.js → compositions-CUSY6ZYY.js} +10 -11
- package/dist/{delete-OXZEXUQY.js → delete-KT3VHP4L.js} +5 -5
- package/dist/{deterministicFonts-GTITFRSJ.js → deterministicFonts-F6WIVL2W.js} +1 -1
- package/dist/{dist-N33CM63P.js → dist-2GC6XKRQ.js} +8 -2
- package/dist/{dist-NZSQCYOF.js → dist-7SZU7MA7.js} +1 -2
- package/dist/{doctor-MWTPSIVD.js → doctor-4ROTBL5U.js} +8 -9
- package/dist/{events-4TZGQZWF.js → events-Q5PXT5UC.js} +2 -2
- package/dist/{events-7VJJGC3D.js → events-QJDVMKSR.js} +3 -3
- package/dist/{feedback-HSGAPVN7.js → feedback-PQ7TWESA.js} +10 -10
- package/dist/{figma-AMC6FAK3.js → figma-JN5DDBDS.js} +3 -3
- package/dist/fontLocalizeCli.js +9 -10
- package/dist/{get-Y7DFEFYM.js → get-KT6OTERO.js} +5 -5
- package/dist/{gpuPolicy-A45VSUKR.js → gpuPolicy-LCBWVGEH.js} +6 -7
- package/dist/{grade-compare-ERSA3OUI.js → grade-compare-VS3RP73G.js} +23 -24
- package/dist/{help-TW2AADLZ.js → help-AD6FIZIO.js} +1 -1
- package/dist/{history-DKGDSVWF.js → history-ZRWYRY7R.js} +5 -5
- package/dist/hyperframe-runtime.js +54 -54
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +54 -54
- package/dist/hyperframes-player.global.js +3 -3
- package/dist/{info-I3NRRNX4.js → info-WBBQA2AX.js} +7 -7
- package/dist/{init-5Y3ZWZNF.js → init-A2XBDFGF.js} +21 -22
- package/dist/{inspect-BJQKBB3X.js → inspect-CLTZHA5O.js} +24 -25
- package/dist/{installer-27K4JS2I.js → installer-WQDRVXH4.js} +4 -4
- package/dist/{keyframes-6V2NZO22.js → keyframes-3FYOK6H6.js} +6 -6
- package/dist/{lambda-AIMF3K4A.js → lambda-Y2PA2BGS.js} +4 -4
- package/dist/{layout-YAPEBTU4.js → layout-4KP5O7IJ.js} +24 -25
- package/dist/{lint-3LWWUIV7.js → lint-V4GRFHOJ.js} +7 -8
- package/dist/{lintProject-LM3XZRPZ.js → lintProject-RG2KGAXB.js} +1 -2
- package/dist/{list-NTSGEZJ6.js → list-OQ4VED6E.js} +5 -5
- package/dist/{login-ZTDFGCCW.js → login-W47NKN5S.js} +20 -24
- package/dist/{logout-GJXZQKOA.js → logout-B4KS4A7S.js} +6 -6
- package/dist/{media-treatment-ARHX446F.js → media-treatment-HJYL3EME.js} +43 -24
- package/dist/{models-VHNQVTHG.js → models-DYBAD2GQ.js} +7 -8
- package/dist/{motionShot-35HCSNCI.js → motionShot-7NE2YBB3.js} +12 -13
- package/dist/{normalize-audio-AUC73JH3.js → normalize-audio-INKCLMS2.js} +4 -4
- package/dist/{open-C5LVNU3U.js → open-TYC5THVY.js} +4 -4
- package/dist/{pipeline-4GDTK54T.js → pipeline-EVKEPQEW.js} +6 -7
- package/dist/{play-U7UANF7Z.js → play-QMZEM6X3.js} +17 -18
- package/dist/{present-X6V6TX3E.js → present-Y7IK3C4W.js} +13 -14
- package/dist/{preview-K43RTRU2.js → preview-5OQRYE6A.js} +16 -17
- package/dist/{producer-PVMFMW23.js → producer-Q6G6JU7B.js} +1 -1
- package/dist/{projectConfig-PI6MEN2T.js → projectConfig-RJN3DKDB.js} +6 -6
- package/dist/{publication-67C7KVTL.js → publication-QCPGGN3U.js} +2 -2
- package/dist/{publish-RBV7EAMH.js → publish-VZLFIGYY.js} +15 -15
- package/dist/{refresh-MFITBIKV.js → refresh-PVHBEABE.js} +2 -2
- package/dist/{remove-background-Z2ECZX2Q.js → remove-background-TKOEHBA5.js} +7 -8
- package/dist/{render-EFU4BZRZ.js → render-KIIIF6PS.js} +13 -13
- package/dist/{render-YBS3XGRJ.js → render-X3NFNVEQ.js} +19 -20
- package/dist/{render-2GQMQBU3.js → render-XTXE5CRU.js} +3 -3
- package/dist/{render-batch-QC64OTB5.js → render-batch-M55YZLZ5.js} +3 -3
- package/dist/{renderCancellation-NTVU7IMB.js → renderCancellation-X5BEHAZU.js} +6 -7
- package/dist/renderSetupWorker.js +1 -2
- package/dist/{resolver-GBWYG3EI.js → resolver-GAMXRA42.js} +4 -4
- package/dist/{screenshotCapture-ECBTLCU6.js → screenshotCapture-TEUIWXWN.js} +53 -33
- package/dist/{sherpa-ZW6PWJS4.js → sherpa-ZYTNFHD5.js} +7 -8
- package/dist/skills/hyperframes-cli/references/cloud.md +1 -1
- package/dist/skills/media-use/SKILL.md +6 -4
- package/dist/skills/media-use/audio/scripts/lib/heygen.mjs +47 -4
- package/dist/skills/media-use/audio/scripts/lib/heygen.test.mjs +125 -18
- package/dist/skills/media-use/references/grading.md +4 -2
- package/dist/skills/media-use/references/setup-providers.md +13 -0
- package/dist/{skills-VKZAX5AS.js → skills-CZHVAD3J.js} +5 -5
- package/dist/{skillsUpdateCheck-PAY2OPDE.js → skillsUpdateCheck-6WLLX53A.js} +3 -3
- package/dist/{snapshot-SD5R3NWX.js → snapshot-K7BTJPYQ.js} +24 -25
- package/dist/{src-7SSERHNP.js → src-3I3RH3V5.js} +5 -6
- package/dist/{src-RXTXL3VL.js → src-YUW26JU5.js} +8 -9
- package/dist/{staticProjectServer-XELJXX4J.js → staticProjectServer-IKCSGN2B.js} +13 -14
- package/dist/{status-JA6SKQVH.js → status-LXPGSSKY.js} +6 -4
- package/dist/studio/assets/hyperframes-player-sAsRuV73.js +473 -0
- package/dist/studio/assets/{index-VAHDM_zL.js → index-B-wffzRH.js} +126 -126
- package/dist/studio/assets/{index-CWfuDCoA.js → index-D3Vij9t1.js} +1 -1
- package/dist/studio/assets/{index-Dr3Bo9uH.js → index-D5q2_7Gw.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/studio/index.js +46 -15
- package/dist/studio/index.js.map +1 -1
- package/dist/{studio-api-4FNUZPJ4.js → studio-api-SQ3NXFSM.js} +7 -8
- package/dist/{studioServer-GHLL3F2M.js → studioServer-VVEO7XCH.js} +36 -37
- package/dist/{telemetry-7AO77PMH.js → telemetry-FQDKR5XA.js} +3 -3
- package/dist/{timeline-6LTHSRXM.js → timeline-N6ZASFNE.js} +13 -14
- package/dist/{tokens-K6B46KNL.js → tokens-2OIIBBCW.js} +2 -2
- package/dist/{transcribe-CLLISRLZ.js → transcribe-4F6MK73N.js} +7 -8
- package/dist/{transcribe-K2OU6FZF.js → transcribe-7YTUCXDX.js} +6 -6
- package/dist/{updateCheck-X6UQBS6I.js → updateCheck-AZ2COZFR.js} +2 -2
- package/dist/{upgrade-BZFKEADI.js → upgrade-QAJEYB4X.js} +2 -2
- package/dist/{urlDownloader-VAVUXYEQ.js → urlDownloader-LSMFEO3R.js} +5 -6
- package/dist/{validate-VO5KEXWN.js → validate-GW5MDL36.js} +15 -16
- package/dist/{video-JV6Y4UJR.js → video-PRNOKUK3.js} +6 -7
- package/package.json +1 -1
- package/dist/chunk-24OWOBQH.js +0 -3656
- package/dist/chunk-B3I3XJ47.js +0 -3711
- package/dist/studio/assets/hyperframes-player-BLuyxnK7.js +0 -473
|
@@ -4,13 +4,20 @@ import { dirname as __hf_dirname } from "node:path";
|
|
|
4
4
|
var require = __hf_createRequire(import.meta.url);
|
|
5
5
|
var __filename = __hf_fileURLToPath(import.meta.url);
|
|
6
6
|
var __dirname = __hf_dirname(__filename);
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_MAX_SCREENSHOTS
|
|
9
|
+
} from "./chunk-P36MALQF.js";
|
|
7
10
|
import {
|
|
8
11
|
isDegradableEvaluateTimeoutError
|
|
9
12
|
} from "./chunk-AWNBM3MZ.js";
|
|
10
13
|
import {
|
|
14
|
+
CaptureDirRefusedError,
|
|
11
15
|
ensureCaptureDirSync,
|
|
12
16
|
writeCaptureFileSync
|
|
13
17
|
} from "./chunk-MRF4AMKD.js";
|
|
18
|
+
import {
|
|
19
|
+
normalizeErrorMessage
|
|
20
|
+
} from "./chunk-KNU5COPB.js";
|
|
14
21
|
import "./chunk-M2WBOQ2Q.js";
|
|
15
22
|
|
|
16
23
|
// src/capture/screenshotCapture.ts
|
|
@@ -35,13 +42,15 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
|
|
|
35
42
|
const docHeight = await page.evaluate(
|
|
36
43
|
`Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)`
|
|
37
44
|
);
|
|
38
|
-
if (docHeight > MAX_PLATE_HEIGHT_PX) return
|
|
39
|
-
if ((budget.remainingMs?.() ?? 1) <= 0) return
|
|
45
|
+
if (docHeight > MAX_PLATE_HEIGHT_PX) return { kind: "omitted", reason: "height-limit" };
|
|
46
|
+
if ((budget.remainingMs?.() ?? 1) <= 0) return { kind: "omitted", reason: "budget-exhausted" };
|
|
40
47
|
const buffer = await page.screenshot({ type: "png", fullPage: true });
|
|
41
48
|
const produced = pngHeight(buffer);
|
|
42
|
-
if (produced != null && produced > MAX_PLATE_HEIGHT_PX)
|
|
49
|
+
if (produced != null && produced > MAX_PLATE_HEIGHT_PX)
|
|
50
|
+
return { kind: "omitted", reason: "height-limit" };
|
|
43
51
|
writeCaptureFileSync(join(screenshotsDir, "full-page.png"), buffer);
|
|
44
|
-
|
|
52
|
+
budget.files?.push("screenshots/full-page.png");
|
|
53
|
+
return { kind: "captured", file: "screenshots/full-page.png" };
|
|
45
54
|
} finally {
|
|
46
55
|
try {
|
|
47
56
|
await page.evaluate(
|
|
@@ -57,9 +66,21 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
|
|
|
57
66
|
async function captureScrollScreenshots(page, outputDir, budget = {}) {
|
|
58
67
|
const screenshotsDir = join(outputDir, "screenshots");
|
|
59
68
|
ensureCaptureDirSync(outputDir, screenshotsDir);
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
69
|
+
const maxScreenshots = budget.maxScreenshots ?? DEFAULT_MAX_SCREENSHOTS;
|
|
70
|
+
const viewportLimit = Math.max(1, maxScreenshots - 1);
|
|
71
|
+
const filePaths = budget.files ?? [];
|
|
72
|
+
filePaths.length = 0;
|
|
73
|
+
const filenames = /* @__PURE__ */ new Set();
|
|
74
|
+
const result = { files: filePaths, interruption: null };
|
|
75
|
+
const budgetSpent = () => {
|
|
76
|
+
if ((budget.remainingMs?.() ?? 1) > 0) return false;
|
|
77
|
+
result.interruption = {
|
|
78
|
+
reason: "budget-exhausted",
|
|
79
|
+
message: "post-navigation budget exhausted during screenshots"
|
|
80
|
+
};
|
|
81
|
+
return true;
|
|
82
|
+
};
|
|
83
|
+
if (budgetSpent()) return result;
|
|
63
84
|
try {
|
|
64
85
|
await page.evaluate(() => {
|
|
65
86
|
const selectors = [
|
|
@@ -126,50 +147,49 @@ async function captureScrollScreenshots(page, outputDir, budget = {}) {
|
|
|
126
147
|
`Math.max(document.body.scrollHeight, document.documentElement.scrollHeight)`
|
|
127
148
|
);
|
|
128
149
|
const viewportHeight = await page.evaluate(`window.innerHeight`);
|
|
129
|
-
const step = Math.floor(viewportHeight * 0.7);
|
|
130
|
-
const positions = [0];
|
|
131
|
-
for (let y = step; y < scrollHeight - viewportHeight; y += step) {
|
|
132
|
-
positions.push(y);
|
|
133
|
-
}
|
|
150
|
+
const step = Math.max(1, Math.floor(viewportHeight * 0.7));
|
|
134
151
|
const lastPos = Math.max(0, scrollHeight - viewportHeight);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
let
|
|
139
|
-
|
|
140
|
-
finalPositions
|
|
141
|
-
const stride = (positions.length - 1) / (MAX_SCREENSHOTS - 1);
|
|
142
|
-
for (let i = 1; i < MAX_SCREENSHOTS - 1; i++) {
|
|
143
|
-
finalPositions.push(positions[Math.round(i * stride)]);
|
|
144
|
-
}
|
|
145
|
-
finalPositions.push(positions[positions.length - 1]);
|
|
152
|
+
const positionCount = Math.ceil(lastPos / step) + 1;
|
|
153
|
+
const count = Math.min(viewportLimit, positionCount);
|
|
154
|
+
const finalPositions = [];
|
|
155
|
+
for (let i = 0; i < count; i++) {
|
|
156
|
+
const positionIndex = count === 1 ? 0 : Math.round(i * (positionCount - 1) / (count - 1));
|
|
157
|
+
finalPositions.push(Math.min(positionIndex * step, lastPos));
|
|
146
158
|
}
|
|
147
159
|
for (let i = 0; i < finalPositions.length; i++) {
|
|
148
|
-
if ((
|
|
160
|
+
if (budgetSpent()) break;
|
|
149
161
|
await page.evaluate(`window.scrollTo(0, ${finalPositions[i]})`);
|
|
150
162
|
await new Promise((r) => setTimeout(r, 400));
|
|
151
163
|
const pct = Math.round(
|
|
152
164
|
finalPositions[i] / Math.max(1, scrollHeight - viewportHeight) * 100
|
|
153
165
|
);
|
|
154
|
-
const
|
|
166
|
+
const stem = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}`;
|
|
167
|
+
let filename = `${stem}.png`;
|
|
168
|
+
if (filenames.has(filename)) filename = `${stem}-${i}.png`;
|
|
169
|
+
filenames.add(filename);
|
|
155
170
|
const filePath = join(screenshotsDir, filename);
|
|
156
|
-
if ((
|
|
171
|
+
if (budgetSpent()) break;
|
|
157
172
|
const buffer = await page.screenshot({ type: "png" });
|
|
158
173
|
writeCaptureFileSync(filePath, buffer);
|
|
159
174
|
filePaths.push(`screenshots/${filename}`);
|
|
160
175
|
}
|
|
161
176
|
await page.evaluate(`window.scrollTo(0, 0)`);
|
|
162
177
|
await new Promise((r) => setTimeout(r, 200));
|
|
163
|
-
if (
|
|
164
|
-
const plate = await captureFullPagePlate(page, screenshotsDir,
|
|
165
|
-
|
|
178
|
+
if (filePaths.length < maxScreenshots && !budgetSpent()) {
|
|
179
|
+
const plate = await captureFullPagePlate(page, screenshotsDir, {
|
|
180
|
+
...budget,
|
|
181
|
+
files: filePaths
|
|
182
|
+
});
|
|
183
|
+
if (plate.kind === "omitted" && plate.reason === "budget-exhausted") budgetSpent();
|
|
166
184
|
}
|
|
167
185
|
} catch (err) {
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
|
|
186
|
+
if (err instanceof CaptureDirRefusedError) throw err;
|
|
187
|
+
result.interruption = {
|
|
188
|
+
reason: isDegradableEvaluateTimeoutError(err) ? "request-timeout" : "internal-error",
|
|
189
|
+
message: normalizeErrorMessage(err)
|
|
190
|
+
};
|
|
171
191
|
}
|
|
172
|
-
return
|
|
192
|
+
return result;
|
|
173
193
|
}
|
|
174
194
|
export {
|
|
175
195
|
MAX_PLATE_HEIGHT_PX,
|
|
@@ -21,26 +21,26 @@ import "./chunk-EOVYQ3IG.js";
|
|
|
21
21
|
import {
|
|
22
22
|
getPreparedWavDurationSeconds,
|
|
23
23
|
prepareWav
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-7FCDA4RH.js";
|
|
25
25
|
import {
|
|
26
26
|
emitWords
|
|
27
27
|
} from "./chunk-6IHWCZ3W.js";
|
|
28
28
|
import {
|
|
29
29
|
stoppedByCancelSignal
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-FWRNQOLT.js";
|
|
31
31
|
import "./chunk-6BEFEWFB.js";
|
|
32
32
|
import "./chunk-PXSM4HPB.js";
|
|
33
33
|
import "./chunk-WCJGTLPF.js";
|
|
34
34
|
import "./chunk-TVCRUG2S.js";
|
|
35
35
|
import "./chunk-AMKC3AXA.js";
|
|
36
|
-
import "./chunk-
|
|
37
|
-
import "./chunk-
|
|
36
|
+
import "./chunk-IDX52YJN.js";
|
|
37
|
+
import "./chunk-DPHJKWP5.js";
|
|
38
38
|
import "./chunk-A4KQZXZI.js";
|
|
39
39
|
import "./chunk-USDEMXXL.js";
|
|
40
|
-
import "./chunk-
|
|
40
|
+
import "./chunk-YFT3BL7N.js";
|
|
41
41
|
import "./chunk-OZUQF4NB.js";
|
|
42
42
|
import "./chunk-DPVXBVLL.js";
|
|
43
|
-
import "./chunk-
|
|
43
|
+
import "./chunk-J4UX3RS3.js";
|
|
44
44
|
import "./chunk-VC76R6W2.js";
|
|
45
45
|
import "./chunk-YA62XGXS.js";
|
|
46
46
|
import "./chunk-OFQWTKPW.js";
|
|
@@ -50,8 +50,7 @@ import "./chunk-RQVDTPGD.js";
|
|
|
50
50
|
import "./chunk-LAOCA3MQ.js";
|
|
51
51
|
import "./chunk-LCSMOLGX.js";
|
|
52
52
|
import "./chunk-TWWSYAKJ.js";
|
|
53
|
-
import "./chunk-
|
|
54
|
-
import "./chunk-24OWOBQH.js";
|
|
53
|
+
import "./chunk-IW2CKH6A.js";
|
|
55
54
|
import "./chunk-JFKL3WAE.js";
|
|
56
55
|
import "./chunk-HBBJFK6I.js";
|
|
57
56
|
import "./chunk-PJ3HDUCY.js";
|
|
@@ -30,7 +30,7 @@ npx hyperframes auth refresh # force-refresh an OAuth token before a
|
|
|
30
30
|
npx hyperframes auth logout # clear the stored credential
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Credential resolution order (first match wins): `HEYGEN_API_KEY`, then `HYPERFRAMES_API_KEY`, then `~/.heygen/credentials`. Point at a different backend with `HEYGEN_API_URL` (default `https://api.heygen.com`).
|
|
33
|
+
Credential resolution order (first match wins): `HEYGEN_API_KEY`, then `HYPERFRAMES_API_KEY`, then `HEYGEN_ACCESS_TOKEN` (an OAuth token a host app injects; never refreshed or saved), then `~/.heygen/credentials`. Point at a different backend with `HEYGEN_API_URL` (default `https://api.heygen.com`).
|
|
34
34
|
|
|
35
35
|
## The render pipeline
|
|
36
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: media-use
|
|
3
|
-
description: Agent Media OS for a HyperFrames project. Resolve BGM, SFX, image, icon, brand logo, voice, color grade, or LUT into a frozen local file or paste-ready block + ledger record (one verb, `resolve`); generate via TTS / music / image models when the catalog misses; produce voiceover, transcription, captions, and background removal through one shared audio engine; operate on media (cut / reframe / transform); and reuse assets across projects. Also use for vague feedback that real footage looks dark, flat, boring, should feel retro/camcorder/print/ASCII, needs privacy, or needs a media reveal. When the host app provides its own music or sound-effect tools, use those for music and sound effects; `resolve --type bgm|sfx` needs the heygen CLI.
|
|
3
|
+
description: Agent Media OS for a HyperFrames project. Resolve BGM, SFX, image, icon, brand logo, voice, color grade, or LUT into a frozen local file or paste-ready block + ledger record (one verb, `resolve`); generate via TTS / music / image models when the catalog misses; produce voiceover, transcription, captions, and background removal through one shared audio engine; operate on media (cut / reframe / transform); and reuse assets across projects. Also use for vague feedback that real footage looks dark, flat, boring, should feel retro/camcorder/print/ASCII, needs privacy, or needs a media reveal. When the host app provides its own music or sound-effect tools, use those for music and sound effects; `resolve --type bgm|sfx` needs the heygen CLI. When `HEYGEN_API_BASE` is set, HeyGen calls go through that host with no CLI sign-in.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
**Plugin installs:** Before setup or freshness commands, follow [plugin execution rules](../hyperframes/references/plugin-installation.md) when this skill is inside a HyperFrames plugin. Standalone installs keep the update instructions below.
|
|
@@ -9,11 +9,13 @@ description: Agent Media OS for a HyperFrames project. Resolve BGM, SFX, image,
|
|
|
9
9
|
|
|
10
10
|
The media OS for HyperFrames: resolve · generate · operate · remember — every media type, one skill, zero context noise.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Only when `HEYGEN_API_BASE` is set in your environment (a host app set it and pays for HeyGen with its own key): HeyGen media is already paid for. Do not ask the person to install or sign in to the `heygen` CLI and do not offer its OAuth allowance; catalog search, TTS and avatar calls here go through the host. When that same host also gives you its own HeyGen tools, use those first. When a call through the host is refused, tell the person the host's message as written (it names the fix, such as adding or replacing the key in the app's Settings) and stop; do not switch to another provider unless they ask.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
First run otherwise (no `HEYGEN_API_BASE`), when you will use HeyGen media (catalog search, TTS, avatar video): install and sign in to the `heygen` CLI (the free-usage path), then verify with `npx hyperframes media-use resolve --doctor`. Setup and providers: `references/setup-providers.md`.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Music and sound effects inside a host app: when the app you run in gives you its own music or sound-effect tools, use those. Without `HEYGEN_API_BASE`, `resolve --type bgm` and `--type sfx` search the HeyGen catalog through the `heygen` CLI; without it they fail and say so (`sfx` still answers from its bundled library).
|
|
17
|
+
|
|
18
|
+
Without `HEYGEN_API_BASE`, before generating a voiceover or an avatar video, tell the person: signing in to the heygen CLI with OAuth (`heygen auth login --oauth`) gives a free allowance for TTS voiceover and avatar videos, while an API key bills API credits.
|
|
17
19
|
|
|
18
20
|
## Resolve — the one verb
|
|
19
21
|
|
|
@@ -1,16 +1,51 @@
|
|
|
1
1
|
import { fetchMedia } from "../../../scripts/lib/media-fetch.mjs";
|
|
2
2
|
// heygen.mjs — vendored HeyGen REST helpers (auth + transport) for the audio
|
|
3
3
|
// pipeline. The credential resolver matches the hyperframes CLI auth: first
|
|
4
|
-
// usable source wins — a host
|
|
4
|
+
// usable source wins — a host gateway ($HEYGEN_API_BASE with its own
|
|
5
|
+
// $HEYGEN_API_KEY) → a host-injected OAuth $HEYGEN_ACCESS_TOKEN (Bearer) →
|
|
5
6
|
// $HEYGEN_API_KEY / $HYPERFRAMES_API_KEY → a nearby .env → ~/.heygen/
|
|
6
7
|
// credentials (oauth → Bearer, else api_key → X-Api-Key; $HEYGEN_CONFIG_DIR
|
|
7
|
-
// overrides the dir).
|
|
8
|
+
// overrides the dir). $HEYGEN_API_BASE moves every request to that host, as it
|
|
9
|
+
// does for the heygen CLI; plain HTTP needs $HEYGEN_ALLOW_HTTP=1, as there.
|
|
10
|
+
// Vendored so the skill ships standalone. Pure node.
|
|
8
11
|
|
|
9
12
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
13
|
import { homedir } from "node:os";
|
|
11
14
|
import { dirname, join, resolve } from "node:path";
|
|
12
15
|
|
|
13
16
|
export const HEYGEN_BASE = "https://api.heygen.com/v3";
|
|
17
|
+
|
|
18
|
+
// The v3 base every request goes to: $HEYGEN_API_BASE when a host app names its own gateway (HyperFrames Desktop
|
|
19
|
+
// forwards it to HeyGen with the API key saved in its Settings), else HeyGen's public API. Plain HTTP carries the key
|
|
20
|
+
// in the clear, so it needs $HEYGEN_ALLOW_HTTP=1, the heygen CLI's own rule.
|
|
21
|
+
export function heygenBase() {
|
|
22
|
+
const host = process.env.HEYGEN_API_BASE?.trim().replace(/\/+$/, "");
|
|
23
|
+
if (!host) return HEYGEN_BASE;
|
|
24
|
+
if (host.startsWith("http://") && process.env.HEYGEN_ALLOW_HTTP !== "1")
|
|
25
|
+
throw new Error(
|
|
26
|
+
`HEYGEN_API_BASE (${host}) uses HTTP, which sends the key in plaintext. Set HEYGEN_ALLOW_HTTP=1 to allow it.`,
|
|
27
|
+
);
|
|
28
|
+
return `${host}/v3`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// No base override, or one on HeyGen's own hosts (a canary or dev API).
|
|
32
|
+
function heygenOwnBase() {
|
|
33
|
+
const host = process.env.HEYGEN_API_BASE?.trim();
|
|
34
|
+
if (!host) return true;
|
|
35
|
+
try {
|
|
36
|
+
const name = new URL(host).hostname;
|
|
37
|
+
return name === "heygen.com" || name.endsWith(".heygen.com");
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// A host gateway: the host named its own API base and the key that base accepts. It pays for every call, so it wins
|
|
44
|
+
// over any other credential the environment carries.
|
|
45
|
+
const hostGatewayKey = () =>
|
|
46
|
+
process.env.HEYGEN_API_BASE?.trim() && process.env.HEYGEN_API_KEY
|
|
47
|
+
? process.env.HEYGEN_API_KEY
|
|
48
|
+
: null;
|
|
14
49
|
export const HEYGEN_CLI_SOURCE_HEADERS = { "X-HeyGen-Source": "cli" };
|
|
15
50
|
// Tool-attribution sent on EVERY media-use HeyGen call regardless of auth type, so
|
|
16
51
|
// the backend can isolate media-use consumption from other free TTS / avatar video.
|
|
@@ -29,6 +64,10 @@ function envFileText(path) {
|
|
|
29
64
|
}
|
|
30
65
|
}
|
|
31
66
|
|
|
67
|
+
// A host app sets these in the environment it spawns, never in a project file: a project's .env naming its own base
|
|
68
|
+
// would send the person's shell HEYGEN_API_KEY to that host.
|
|
69
|
+
const HOST_ONLY = new Set(["HEYGEN_API_BASE", "HEYGEN_ALLOW_HTTP"]);
|
|
70
|
+
|
|
32
71
|
// Walk up ≤5 dirs from startDir; load the first .env (shell env always wins).
|
|
33
72
|
export function loadEnvFromDir(startDir) {
|
|
34
73
|
let dir = resolve(startDir);
|
|
@@ -48,7 +87,7 @@ export function loadEnvFromDir(startDir) {
|
|
|
48
87
|
const end = val.indexOf(q, 1);
|
|
49
88
|
val = end > 0 ? val.slice(1, end) : val.slice(1);
|
|
50
89
|
}
|
|
51
|
-
if (!(key in process.env)) process.env[key] = val;
|
|
90
|
+
if (!HOST_ONLY.has(key) && !(key in process.env)) process.env[key] = val;
|
|
52
91
|
}
|
|
53
92
|
return;
|
|
54
93
|
}
|
|
@@ -68,6 +107,10 @@ export function heygenCredential() {
|
|
|
68
107
|
// (a folder, a locked ~/.heygen), so heygenAuthHeaders can say to fix that path: logging in again would fail there too.
|
|
69
108
|
// Read without checking first, so the file cannot change between a check and the read.
|
|
70
109
|
function resolveCredential() {
|
|
110
|
+
const gatewayKey = hostGatewayKey();
|
|
111
|
+
if (gatewayKey) return { headers: { "X-Api-Key": gatewayKey } };
|
|
112
|
+
// Every other credential belongs to HeyGen: a base on any other host gets none of them.
|
|
113
|
+
if (!heygenOwnBase()) return null;
|
|
71
114
|
const accessToken = process.env.HEYGEN_ACCESS_TOKEN;
|
|
72
115
|
if (accessToken) return { headers: { Authorization: `Bearer ${accessToken}` } };
|
|
73
116
|
const envKey = process.env.HEYGEN_API_KEY || process.env.HYPERFRAMES_API_KEY;
|
|
@@ -144,7 +187,7 @@ export async function heygenJSON(path, { method = "GET", headers = {}, body } =
|
|
|
144
187
|
opts.headers["Content-Type"] = "application/json";
|
|
145
188
|
opts.body = JSON.stringify(body);
|
|
146
189
|
}
|
|
147
|
-
const res = await fetch(`${
|
|
190
|
+
const res = await fetch(`${heygenBase()}${path}`, opts);
|
|
148
191
|
if (!res.ok) {
|
|
149
192
|
const detail = await res.text().catch(() => "");
|
|
150
193
|
const message = `HeyGen ${method} ${path} → HTTP ${res.status}${detail ? `\n${detail.slice(0, 300)}` : ""}`;
|
|
@@ -1,36 +1,47 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { createServer } from "node:http";
|
|
4
5
|
import { tmpdir } from "node:os";
|
|
5
6
|
import { join } from "node:path";
|
|
6
7
|
import {
|
|
7
8
|
heygenAuthHeaders,
|
|
8
9
|
heygenAuthMethod,
|
|
10
|
+
heygenBase,
|
|
9
11
|
heygenCredential,
|
|
12
|
+
heygenJSON,
|
|
10
13
|
loadEnvFromDir,
|
|
11
14
|
} from "./heygen.mjs";
|
|
12
15
|
|
|
16
|
+
const HEYGEN_ENV = [
|
|
17
|
+
"HEYGEN_ACCESS_TOKEN",
|
|
18
|
+
"HEYGEN_API_KEY",
|
|
19
|
+
"HYPERFRAMES_API_KEY",
|
|
20
|
+
"HEYGEN_CONFIG_DIR",
|
|
21
|
+
"HEYGEN_API_BASE",
|
|
22
|
+
"HEYGEN_ALLOW_HTTP",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
// Runs fn with every HeyGen variable unset, then puts them back; an async fn restores once it settles.
|
|
13
26
|
function withCleanHeygenEnv(fn) {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
27
|
+
const previous = Object.fromEntries(HEYGEN_ENV.map((name) => [name, process.env[name]]));
|
|
28
|
+
const restore = () => {
|
|
29
|
+
for (const [name, value] of Object.entries(previous)) {
|
|
30
|
+
if (value === undefined) delete process.env[name];
|
|
31
|
+
else process.env[name] = value;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
for (const name of HEYGEN_ENV) delete process.env[name];
|
|
35
|
+
let result;
|
|
18
36
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return fn();
|
|
24
|
-
} finally {
|
|
25
|
-
if (previousAccessToken === undefined) delete process.env.HEYGEN_ACCESS_TOKEN;
|
|
26
|
-
else process.env.HEYGEN_ACCESS_TOKEN = previousAccessToken;
|
|
27
|
-
if (previousApiKey === undefined) delete process.env.HEYGEN_API_KEY;
|
|
28
|
-
else process.env.HEYGEN_API_KEY = previousApiKey;
|
|
29
|
-
if (previousHyperframesApiKey === undefined) delete process.env.HYPERFRAMES_API_KEY;
|
|
30
|
-
else process.env.HYPERFRAMES_API_KEY = previousHyperframesApiKey;
|
|
31
|
-
if (previousConfigDir === undefined) delete process.env.HEYGEN_CONFIG_DIR;
|
|
32
|
-
else process.env.HEYGEN_CONFIG_DIR = previousConfigDir;
|
|
37
|
+
result = fn();
|
|
38
|
+
} catch (error) {
|
|
39
|
+
restore();
|
|
40
|
+
throw error;
|
|
33
41
|
}
|
|
42
|
+
if (result && typeof result.then === "function") return result.finally(restore);
|
|
43
|
+
restore();
|
|
44
|
+
return result;
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
test("heygenAuthHeaders does not tag API-key requests as CLI traffic, but still carries the media-use tool tag", () => {
|
|
@@ -138,6 +149,27 @@ test("loadEnvFromDir skips a .env folder and loads the .env file above it", () =
|
|
|
138
149
|
}
|
|
139
150
|
});
|
|
140
151
|
|
|
152
|
+
test("a project's .env cannot name the HeyGen base, so a shell key never leaves for its host", () => {
|
|
153
|
+
withCleanHeygenEnv(() => {
|
|
154
|
+
const project = mkdtempSync(join(tmpdir(), "heygen-env-"));
|
|
155
|
+
writeFileSync(
|
|
156
|
+
join(project, ".env"),
|
|
157
|
+
"HEYGEN_API_BASE=https://proxy.example.com\nHEYGEN_ALLOW_HTTP=1\nMEDIA_USE_ENV_BASE_TEST=loaded\n",
|
|
158
|
+
);
|
|
159
|
+
try {
|
|
160
|
+
process.env.HEYGEN_API_KEY = "hg_shell_real";
|
|
161
|
+
loadEnvFromDir(project);
|
|
162
|
+
assert.equal(process.env.MEDIA_USE_ENV_BASE_TEST, "loaded");
|
|
163
|
+
assert.equal(process.env.HEYGEN_API_BASE, undefined);
|
|
164
|
+
assert.equal(process.env.HEYGEN_ALLOW_HTTP, undefined);
|
|
165
|
+
assert.equal(heygenBase(), "https://api.heygen.com/v3");
|
|
166
|
+
} finally {
|
|
167
|
+
delete process.env.MEDIA_USE_ENV_BASE_TEST;
|
|
168
|
+
rmSync(project, { recursive: true, force: true });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
141
173
|
test("heygenAuthMethod returns null when the credentials path is a folder", () => {
|
|
142
174
|
withCleanHeygenEnv(() => {
|
|
143
175
|
const dir = mkdtempSync(join(tmpdir(), "heygen-cred-"));
|
|
@@ -183,3 +215,78 @@ test("heygenAuthHeaders says to fix an unreadable credentials path, and to log i
|
|
|
183
215
|
}
|
|
184
216
|
});
|
|
185
217
|
});
|
|
218
|
+
|
|
219
|
+
test("heygenBase is HeyGen's public API unless a host names another", () => {
|
|
220
|
+
withCleanHeygenEnv(() => {
|
|
221
|
+
assert.equal(heygenBase(), "https://api.heygen.com/v3");
|
|
222
|
+
process.env.HEYGEN_API_BASE = "https://api-canary.heygen.com/";
|
|
223
|
+
assert.equal(heygenBase(), "https://api-canary.heygen.com/v3");
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("heygenBase refuses a plain-HTTP host unless HEYGEN_ALLOW_HTTP is set, as the heygen CLI does", () => {
|
|
228
|
+
withCleanHeygenEnv(() => {
|
|
229
|
+
process.env.HEYGEN_API_BASE = "http://127.0.0.1:4100";
|
|
230
|
+
assert.throws(() => heygenBase(), /HEYGEN_ALLOW_HTTP=1/);
|
|
231
|
+
process.env.HEYGEN_ALLOW_HTTP = "1";
|
|
232
|
+
assert.equal(heygenBase(), "http://127.0.0.1:4100/v3");
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("a host gateway (HEYGEN_API_BASE with its own HEYGEN_API_KEY) wins over a host OAuth token", () => {
|
|
237
|
+
withCleanHeygenEnv(() => {
|
|
238
|
+
process.env.HEYGEN_API_BASE = "http://127.0.0.1:4100";
|
|
239
|
+
process.env.HEYGEN_ALLOW_HTTP = "1";
|
|
240
|
+
process.env.HEYGEN_API_KEY = "gateway-token";
|
|
241
|
+
process.env.HEYGEN_ACCESS_TOKEN = "at_host";
|
|
242
|
+
assert.deepEqual(heygenAuthHeaders(), {
|
|
243
|
+
"X-Api-Key": "gateway-token",
|
|
244
|
+
"X-HeyGen-Client-Source": "media-use",
|
|
245
|
+
});
|
|
246
|
+
assert.equal(heygenAuthMethod(), "api_key");
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("heygenJSON sends its request to the host's HEYGEN_API_BASE with the host's key", async () => {
|
|
251
|
+
/** @type {{ url?: string, key?: string | string[] }} */
|
|
252
|
+
const seen = {};
|
|
253
|
+
const server = createServer((req, res) => {
|
|
254
|
+
seen.url = req.url;
|
|
255
|
+
seen.key = req.headers["x-api-key"];
|
|
256
|
+
res.writeHead(200, { "content-type": "application/json" }).end('{"data":[]}');
|
|
257
|
+
});
|
|
258
|
+
await new Promise((done) => server.listen(0, "127.0.0.1", done));
|
|
259
|
+
const { port } = /** @type {import("node:net").AddressInfo} */ (server.address());
|
|
260
|
+
try {
|
|
261
|
+
await withCleanHeygenEnv(async () => {
|
|
262
|
+
process.env.HEYGEN_API_BASE = `http://127.0.0.1:${port}`;
|
|
263
|
+
process.env.HEYGEN_ALLOW_HTTP = "1";
|
|
264
|
+
process.env.HEYGEN_API_KEY = "gateway-token";
|
|
265
|
+
const reply = await heygenJSON("/voices?limit=1", { headers: heygenAuthHeaders() });
|
|
266
|
+
assert.deepEqual(reply, { data: [] });
|
|
267
|
+
});
|
|
268
|
+
assert.equal(seen.url, "/v3/voices?limit=1");
|
|
269
|
+
assert.equal(seen.key, "gateway-token");
|
|
270
|
+
} finally {
|
|
271
|
+
server.close();
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("a base that isn't HeyGen's gets no stored or host OAuth credential, only a key named for it", () => {
|
|
276
|
+
withCleanHeygenEnv(() => {
|
|
277
|
+
const dir = mkdtempSync(join(tmpdir(), "heygen-cred-"));
|
|
278
|
+
try {
|
|
279
|
+
process.env.HEYGEN_CONFIG_DIR = dir;
|
|
280
|
+
writeFileSync(join(dir, "credentials"), JSON.stringify({ api_key: "hg_stored" }));
|
|
281
|
+
process.env.HEYGEN_ACCESS_TOKEN = "at_host";
|
|
282
|
+
process.env.HEYGEN_API_BASE = "https://proxy.example.com";
|
|
283
|
+
assert.equal(heygenCredential(), null);
|
|
284
|
+
assert.throws(() => heygenAuthHeaders(), /no HeyGen credentials/);
|
|
285
|
+
// HeyGen's own hosts keep every credential source.
|
|
286
|
+
process.env.HEYGEN_API_BASE = "https://api-canary.heygen.com";
|
|
287
|
+
assert.equal(heygenAuthMethod(), "oauth");
|
|
288
|
+
} finally {
|
|
289
|
+
rmSync(dir, { recursive: true, force: true });
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -83,8 +83,10 @@ on that same real media element with its registered paused GSAP timeline:
|
|
|
83
83
|
| `--hf-color-grading-ascii` | 0 to 1 |
|
|
84
84
|
| `--hf-color-grading-dither` | 0 to 1 |
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
Once set (inline, in a stylesheet or on a parent), a property overrides the
|
|
87
|
+
payload's value for that control, so set it only on media you animate: put the
|
|
88
|
+
tween's first value in the element's inline `style`, then use finite `tl.to()`
|
|
89
|
+
keyframes. Leave it unset for a static grade. Do not use a frame-zero `tl.set()`, CSS
|
|
88
90
|
animation clocks, timers, random values, or `onUpdate` callbacks. The static
|
|
89
91
|
`data-color-grading` payload remains the fallback and source of the other
|
|
90
92
|
controls.
|
|
@@ -15,6 +15,19 @@ This unlocks the FREE path for bgm/sfx/image/icon catalog search, TTS (voice), a
|
|
|
15
15
|
npx hyperframes media-use resolve --doctor
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
## Host HeyGen access (no CLI sign-in)
|
|
19
|
+
|
|
20
|
+
A host app can give media-use HeyGen access of its own by setting
|
|
21
|
+
`HEYGEN_API_BASE` (its gateway), `HEYGEN_API_KEY` (the token that gateway
|
|
22
|
+
accepts) and, for a loopback gateway, `HEYGEN_ALLOW_HTTP=1`. The gateway adds
|
|
23
|
+
the host's key, so it never enters the agent's environment, and every
|
|
24
|
+
call is charged to that key's API credits. The `heygen` CLI honours the same
|
|
25
|
+
variables, so `resolve` (bgm/sfx/image/icon/voice/avatar-video) and the audio
|
|
26
|
+
engine's TTS all go through the host. With host access, skip CLI install and
|
|
27
|
+
sign-in, prefer the host's own HeyGen tools where it has them, never fall back
|
|
28
|
+
to a local or third-party generator on your own, and relay a refused call's
|
|
29
|
+
message (for example "add a key in Settings > Account") as written.
|
|
30
|
+
|
|
18
31
|
## Providers
|
|
19
32
|
|
|
20
33
|
media-use holds no keys; every external tool owns its auth. Generation is
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
} from "./chunk-6BEFEWFB.js";
|
|
10
10
|
import {
|
|
11
11
|
trackSkillsInstallSkipped
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-XGMNQQTJ.js";
|
|
13
|
+
import "./chunk-LBKDQKWV.js";
|
|
14
14
|
import {
|
|
15
15
|
diag
|
|
16
16
|
} from "./chunk-VWG7OQKU.js";
|
|
@@ -39,15 +39,15 @@ import {
|
|
|
39
39
|
isCoreSkill,
|
|
40
40
|
presentSkills,
|
|
41
41
|
pruneOrphanedLockEntries
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-FZK35GT4.js";
|
|
43
43
|
import {
|
|
44
44
|
withMeta
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-JLKMWY2T.js";
|
|
46
46
|
import "./chunk-K4WIOZFP.js";
|
|
47
47
|
import "./chunk-XUXGIA7H.js";
|
|
48
48
|
import "./chunk-WERLJGIC.js";
|
|
49
49
|
import "./chunk-ISN5GRYI.js";
|
|
50
|
-
import "./chunk-
|
|
50
|
+
import "./chunk-DZEVA7DI.js";
|
|
51
51
|
import "./chunk-KNU5COPB.js";
|
|
52
52
|
import {
|
|
53
53
|
c
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
printSkillsUpdateNotice,
|
|
10
10
|
refreshSkillsCache,
|
|
11
11
|
skillsCheckDue
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-FZK35GT4.js";
|
|
13
|
+
import "./chunk-JLKMWY2T.js";
|
|
14
14
|
import "./chunk-K4WIOZFP.js";
|
|
15
15
|
import "./chunk-XUXGIA7H.js";
|
|
16
16
|
import "./chunk-WERLJGIC.js";
|
|
17
17
|
import "./chunk-ISN5GRYI.js";
|
|
18
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-DZEVA7DI.js";
|
|
19
19
|
import "./chunk-KNU5COPB.js";
|
|
20
20
|
import "./chunk-M2WBOQ2Q.js";
|
|
21
21
|
export {
|