@vibeframe/mcp-server 0.74.0 → 0.75.0
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/index.js +924 -1361
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -7620,9 +7620,6 @@ tmp/
|
|
|
7620
7620
|
*.log
|
|
7621
7621
|
`;
|
|
7622
7622
|
}
|
|
7623
|
-
function isSceneScaffoldProfile(value) {
|
|
7624
|
-
return value === "minimal" || value === "agent" || value === "full";
|
|
7625
|
-
}
|
|
7626
7623
|
function describeSceneScaffold(opts) {
|
|
7627
7624
|
const dir = resolve(opts.dir);
|
|
7628
7625
|
const profile = opts.profile ?? "full";
|
|
@@ -261959,9 +261956,9 @@ ${lanes.join("\n")}
|
|
|
261959
261956
|
/*ignoreCase*/
|
|
261960
261957
|
false
|
|
261961
261958
|
)) {
|
|
261962
|
-
const
|
|
261963
|
-
if (
|
|
261964
|
-
const name = removeSuffix(removePrefix(
|
|
261959
|
+
const basename17 = getBaseFileName(a.fileName);
|
|
261960
|
+
if (basename17 === "lib.d.ts" || basename17 === "lib.es6.d.ts") return 0;
|
|
261961
|
+
const name = removeSuffix(removePrefix(basename17, "lib."), ".d.ts");
|
|
261965
261962
|
const index = libs.indexOf(name);
|
|
261966
261963
|
if (index !== -1) return index + 1;
|
|
261967
261964
|
}
|
|
@@ -325707,8 +325704,8 @@ ${options.prefix}` : "\n" : options.prefix
|
|
|
325707
325704
|
}
|
|
325708
325705
|
};
|
|
325709
325706
|
for (const file of files) {
|
|
325710
|
-
const
|
|
325711
|
-
if (
|
|
325707
|
+
const basename17 = getBaseFileName(file);
|
|
325708
|
+
if (basename17 === "package.json" || basename17 === "bower.json") {
|
|
325712
325709
|
createProjectWatcher(
|
|
325713
325710
|
file,
|
|
325714
325711
|
"FileWatcher"
|
|
@@ -329380,8 +329377,8 @@ All files are: ${JSON.stringify(names)}`,
|
|
|
329380
329377
|
var _a7;
|
|
329381
329378
|
const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
|
|
329382
329379
|
if (!fileOrDirectoryPath) return;
|
|
329383
|
-
const
|
|
329384
|
-
if (((_a7 = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a7.size) && (
|
|
329380
|
+
const basename17 = getBaseFileName(fileOrDirectoryPath);
|
|
329381
|
+
if (((_a7 = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a7.size) && (basename17 === "package.json" || basename17 === "node_modules")) {
|
|
329385
329382
|
result.affectedModuleSpecifierCacheProjects.forEach((project) => {
|
|
329386
329383
|
var _a23;
|
|
329387
329384
|
(_a23 = project.getModuleSpecifierCache()) == null ? void 0 : _a23.clear();
|
|
@@ -447058,152 +447055,266 @@ var init_scene_lint = __esm({
|
|
|
447058
447055
|
}
|
|
447059
447056
|
});
|
|
447060
447057
|
|
|
447061
|
-
// ../cli/src/
|
|
447062
|
-
|
|
447063
|
-
|
|
447064
|
-
|
|
447065
|
-
|
|
447066
|
-
|
|
447067
|
-
|
|
447068
|
-
|
|
447069
|
-
|
|
447070
|
-
|
|
447071
|
-
|
|
447072
|
-
|
|
447073
|
-
|
|
447074
|
-
|
|
447075
|
-
|
|
447076
|
-
|
|
447077
|
-
|
|
447078
|
-
|
|
447079
|
-
|
|
447080
|
-
|
|
447081
|
-
|
|
447082
|
-
|
|
447083
|
-
|
|
447084
|
-
|
|
447085
|
-
|
|
447086
|
-
];
|
|
447087
|
-
for (const c of candidates) {
|
|
447088
|
-
if (c && existsSync19(c)) return c;
|
|
447089
|
-
}
|
|
447090
|
-
return void 0;
|
|
447058
|
+
// ../cli/src/commands/output.ts
|
|
447059
|
+
var output_exports = {};
|
|
447060
|
+
__export(output_exports, {
|
|
447061
|
+
COST_ESTIMATES: () => COST_ESTIMATES,
|
|
447062
|
+
ExitCode: () => ExitCode,
|
|
447063
|
+
_resetDeprecationMemoryForTesting: () => _resetDeprecationMemoryForTesting,
|
|
447064
|
+
apiError: () => apiError,
|
|
447065
|
+
authError: () => authError,
|
|
447066
|
+
emitDeprecationWarning: () => emitDeprecationWarning,
|
|
447067
|
+
exitWithError: () => exitWithError,
|
|
447068
|
+
generalError: () => generalError,
|
|
447069
|
+
isJsonMode: () => isJsonMode,
|
|
447070
|
+
isQuietMode: () => isQuietMode,
|
|
447071
|
+
log: () => log,
|
|
447072
|
+
networkError: () => networkError,
|
|
447073
|
+
notFoundError: () => notFoundError,
|
|
447074
|
+
outputError: () => outputError,
|
|
447075
|
+
outputResult: () => outputResult,
|
|
447076
|
+
outputSuccess: () => outputSuccess,
|
|
447077
|
+
spinner: () => spinner,
|
|
447078
|
+
suggestNext: () => suggestNext,
|
|
447079
|
+
usageError: () => usageError
|
|
447080
|
+
});
|
|
447081
|
+
function usageError(msg, suggestion) {
|
|
447082
|
+
return { success: false, error: msg, code: "USAGE_ERROR", exitCode: 2 /* USAGE */, suggestion, retryable: false };
|
|
447091
447083
|
}
|
|
447092
|
-
|
|
447093
|
-
return
|
|
447084
|
+
function authError(envVar, provider) {
|
|
447085
|
+
return {
|
|
447086
|
+
success: false,
|
|
447087
|
+
error: `${provider} API key required.`,
|
|
447088
|
+
code: "API_KEY_MISSING",
|
|
447089
|
+
exitCode: 4 /* AUTH */,
|
|
447090
|
+
suggestion: `Set ${envVar} in .env, or run: vibe setup`,
|
|
447091
|
+
retryable: false
|
|
447092
|
+
};
|
|
447094
447093
|
}
|
|
447095
|
-
|
|
447096
|
-
|
|
447097
|
-
|
|
447098
|
-
|
|
447099
|
-
CHROME_NOT_FOUND_REASON = "Chrome not found. Set HYPERFRAMES_CHROME_PATH, or install Chrome (macOS: brew install --cask google-chrome \xB7 Linux: apt install chromium). Run `vibe doctor` for details.";
|
|
447100
|
-
}
|
|
447101
|
-
});
|
|
447102
|
-
|
|
447103
|
-
// ../cli/src/commands/_shared/scene-audio-scan.ts
|
|
447104
|
-
import { readFile as readFile5 } from "node:fs/promises";
|
|
447105
|
-
import { resolve as resolve14 } from "node:path";
|
|
447106
|
-
function parseRootClips(rootHtml) {
|
|
447107
|
-
const clips = [];
|
|
447108
|
-
const clipRegex = /<div\b[^>]*class="clip"[^>]*>/gi;
|
|
447109
|
-
let match2;
|
|
447110
|
-
while ((match2 = clipRegex.exec(rootHtml)) !== null) {
|
|
447111
|
-
const tag = match2[0];
|
|
447112
|
-
const compositionId = pickAttr(tag, "data-composition-id");
|
|
447113
|
-
const compositionSrc = pickAttr(tag, "data-composition-src");
|
|
447114
|
-
const start = pickNumberAttr(tag, "data-start");
|
|
447115
|
-
const duration = pickNumberAttr(tag, "data-duration");
|
|
447116
|
-
const trackIndex = pickNumberAttr(tag, "data-track-index") ?? 1;
|
|
447117
|
-
if (!compositionId || !compositionSrc || start === null || duration === null) {
|
|
447118
|
-
continue;
|
|
447094
|
+
function apiError(msg, retryable = false) {
|
|
447095
|
+
for (const hint of PROVIDER_ERROR_HINTS) {
|
|
447096
|
+
if (hint.pattern.test(msg)) {
|
|
447097
|
+
return { success: false, error: msg, code: "API_ERROR", exitCode: 5 /* API_ERROR */, suggestion: hint.suggestion, retryable: hint.retryable };
|
|
447119
447098
|
}
|
|
447120
|
-
clips.push({ compositionId, compositionSrc, start, duration, trackIndex });
|
|
447121
447099
|
}
|
|
447122
|
-
return
|
|
447100
|
+
return { success: false, error: msg, code: "API_ERROR", exitCode: 5 /* API_ERROR */, suggestion: retryable ? "Retry the command." : void 0, retryable };
|
|
447123
447101
|
}
|
|
447124
|
-
function
|
|
447125
|
-
|
|
447126
|
-
const audioRegex = /<audio\b([^>]*)>/gi;
|
|
447127
|
-
let match2;
|
|
447128
|
-
while ((match2 = audioRegex.exec(compositionHtml)) !== null) {
|
|
447129
|
-
const attrs = match2[1];
|
|
447130
|
-
const src = pickAttr(attrs, "src");
|
|
447131
|
-
if (!src) continue;
|
|
447132
|
-
const localStart = pickNumberAttr(attrs, "data-start") ?? 0;
|
|
447133
|
-
const durationRaw = pickAttr(attrs, "data-duration");
|
|
447134
|
-
const durationHint = !durationRaw || durationRaw === "auto" ? "auto" : Number(durationRaw);
|
|
447135
|
-
const volume = pickNumberAttr(attrs, "data-volume") ?? 1;
|
|
447136
|
-
const trackIndex = pickNumberAttr(attrs, "data-track-index") ?? 2;
|
|
447137
|
-
out.push({ srcRel: src, localStart, durationHint, volume, trackIndex });
|
|
447138
|
-
}
|
|
447139
|
-
return out;
|
|
447102
|
+
function notFoundError(path14) {
|
|
447103
|
+
return { success: false, error: `File not found: ${path14}`, code: "NOT_FOUND", exitCode: 3 /* NOT_FOUND */, retryable: false };
|
|
447140
447104
|
}
|
|
447141
|
-
function
|
|
447142
|
-
return
|
|
447143
|
-
|
|
447144
|
-
|
|
447145
|
-
|
|
447146
|
-
|
|
447147
|
-
|
|
447105
|
+
function networkError(msg) {
|
|
447106
|
+
return { success: false, error: msg, code: "NETWORK_ERROR", exitCode: 6 /* NETWORK */, suggestion: "Check your internet connection and retry.", retryable: true };
|
|
447107
|
+
}
|
|
447108
|
+
function generalError(msg, suggestion) {
|
|
447109
|
+
return { success: false, error: msg, code: "ERROR", exitCode: 1 /* GENERAL */, suggestion, retryable: false };
|
|
447110
|
+
}
|
|
447111
|
+
function exitWithError(err) {
|
|
447112
|
+
if (isJsonMode()) {
|
|
447113
|
+
console.error(JSON.stringify(err, null, 2));
|
|
447114
|
+
} else {
|
|
447115
|
+
console.error(source_default.red(`
|
|
447116
|
+
${err.error}`));
|
|
447117
|
+
if (err.suggestion) {
|
|
447118
|
+
console.error(source_default.dim(` ${err.suggestion}`));
|
|
447148
447119
|
}
|
|
447120
|
+
console.error();
|
|
447121
|
+
}
|
|
447122
|
+
process.exit(err.exitCode);
|
|
447123
|
+
}
|
|
447124
|
+
function isJsonMode() {
|
|
447125
|
+
return process.env.VIBE_JSON_OUTPUT === "1";
|
|
447126
|
+
}
|
|
447127
|
+
function isQuietMode() {
|
|
447128
|
+
return process.env.VIBE_QUIET_OUTPUT === "1";
|
|
447129
|
+
}
|
|
447130
|
+
function formatCost(min, max, unit) {
|
|
447131
|
+
if (min === 0 && max === 0) return "Free";
|
|
447132
|
+
if (min === max) return `~$${min.toFixed(2)} ${unit}`;
|
|
447133
|
+
return `~$${min.toFixed(2)}-$${max.toFixed(2)} ${unit}`;
|
|
447134
|
+
}
|
|
447135
|
+
function lookupCostEstimateUpperBound(command3) {
|
|
447136
|
+
return COST_ESTIMATES[command3]?.max ?? 0;
|
|
447137
|
+
}
|
|
447138
|
+
function outputSuccess(opts) {
|
|
447139
|
+
const elapsedMs = Math.max(0, Date.now() - opts.startedAt);
|
|
447140
|
+
const costUsd = opts.costUsd ?? (opts.dryRun ? lookupCostEstimateUpperBound(opts.command) : 0);
|
|
447141
|
+
const envelope = {
|
|
447142
|
+
command: opts.command,
|
|
447143
|
+
...opts.dryRun ? { dryRun: true } : {},
|
|
447144
|
+
elapsedMs,
|
|
447145
|
+
costUsd,
|
|
447146
|
+
warnings: opts.warnings ?? [],
|
|
447147
|
+
data: opts.data
|
|
447149
447148
|
};
|
|
447149
|
+
if (isJsonMode()) {
|
|
447150
|
+
const fields = process.env.VIBE_OUTPUT_FIELDS;
|
|
447151
|
+
if (fields) {
|
|
447152
|
+
const keys2 = fields.split(",").map((k) => k.trim());
|
|
447153
|
+
const data = opts.data;
|
|
447154
|
+
const filteredData = {};
|
|
447155
|
+
for (const key2 of keys2) {
|
|
447156
|
+
if (key2 in data) filteredData[key2] = data[key2];
|
|
447157
|
+
}
|
|
447158
|
+
envelope.data = filteredData;
|
|
447159
|
+
}
|
|
447160
|
+
console.log(JSON.stringify(envelope, null, 2));
|
|
447161
|
+
return;
|
|
447162
|
+
}
|
|
447163
|
+
if (isQuietMode()) {
|
|
447164
|
+
const data = opts.data;
|
|
447165
|
+
const primary = data.outputPath ?? data.output ?? data.path ?? data.url ?? data.id;
|
|
447166
|
+
if (primary !== void 0) console.log(String(primary));
|
|
447167
|
+
}
|
|
447150
447168
|
}
|
|
447151
|
-
|
|
447152
|
-
|
|
447153
|
-
|
|
447154
|
-
|
|
447155
|
-
|
|
447156
|
-
const html = await reader(clip.compositionSrc);
|
|
447157
|
-
if (!html) continue;
|
|
447158
|
-
const audios = parseSceneAudios(html);
|
|
447159
|
-
for (const audio of audios) {
|
|
447160
|
-
out.push({
|
|
447161
|
-
srcRel: audio.srcRel,
|
|
447162
|
-
srcAbs: resolve14(opts.projectDir, audio.srcRel),
|
|
447163
|
-
absoluteStart: clip.start + audio.localStart,
|
|
447164
|
-
durationHint: audio.durationHint,
|
|
447165
|
-
clipDurationCap: clip.duration - audio.localStart,
|
|
447166
|
-
volume: audio.volume,
|
|
447167
|
-
trackIndex: audio.trackIndex,
|
|
447168
|
-
compositionSrc: clip.compositionSrc
|
|
447169
|
-
});
|
|
447169
|
+
function outputResult(result) {
|
|
447170
|
+
if (result.dryRun && result.command && typeof result.command === "string") {
|
|
447171
|
+
const cost = COST_ESTIMATES[result.command];
|
|
447172
|
+
if (cost) {
|
|
447173
|
+
result.estimatedCost = formatCost(cost.min, cost.max, cost.unit);
|
|
447170
447174
|
}
|
|
447171
447175
|
}
|
|
447172
|
-
|
|
447173
|
-
|
|
447174
|
-
|
|
447175
|
-
|
|
447176
|
-
|
|
447177
|
-
|
|
447178
|
-
|
|
447179
|
-
|
|
447180
|
-
|
|
447181
|
-
|
|
447182
|
-
|
|
447183
|
-
|
|
447184
|
-
}
|
|
447176
|
+
if (isJsonMode()) {
|
|
447177
|
+
const fields = process.env.VIBE_OUTPUT_FIELDS;
|
|
447178
|
+
if (fields) {
|
|
447179
|
+
const keys2 = fields.split(",").map((k) => k.trim());
|
|
447180
|
+
const filtered = {};
|
|
447181
|
+
for (const key2 of keys2) {
|
|
447182
|
+
if (key2 in result) filtered[key2] = result[key2];
|
|
447183
|
+
}
|
|
447184
|
+
if ("success" in result) filtered.success = result.success;
|
|
447185
|
+
console.log(JSON.stringify(filtered, null, 2));
|
|
447186
|
+
} else {
|
|
447187
|
+
console.log(JSON.stringify(result, null, 2));
|
|
447188
|
+
}
|
|
447189
|
+
} else if (isQuietMode()) {
|
|
447190
|
+
const primary = result.output ?? result.path ?? result.url ?? result.id ?? result.result;
|
|
447191
|
+
if (primary !== void 0) console.log(String(primary));
|
|
447185
447192
|
}
|
|
447186
|
-
out.sort((a, b) => a.absoluteStart - b.absoluteStart);
|
|
447187
|
-
return out;
|
|
447188
447193
|
}
|
|
447189
|
-
function
|
|
447190
|
-
|
|
447191
|
-
|
|
447192
|
-
|
|
447193
|
-
return m[2] ?? m[3] ?? null;
|
|
447194
|
+
function log(...args) {
|
|
447195
|
+
if (!isJsonMode() && !isQuietMode()) {
|
|
447196
|
+
console.log(...args);
|
|
447197
|
+
}
|
|
447194
447198
|
}
|
|
447195
|
-
function
|
|
447196
|
-
|
|
447197
|
-
|
|
447198
|
-
|
|
447199
|
-
return
|
|
447199
|
+
function spinner(text) {
|
|
447200
|
+
if (isJsonMode() || isQuietMode()) {
|
|
447201
|
+
return ora({ text, isSilent: true });
|
|
447202
|
+
}
|
|
447203
|
+
return ora(text);
|
|
447200
447204
|
}
|
|
447201
|
-
function
|
|
447202
|
-
|
|
447205
|
+
function suggestNext(tip) {
|
|
447206
|
+
if (!isJsonMode() && !isQuietMode() && process.stdout.isTTY) {
|
|
447207
|
+
console.log(source_default.dim(`
|
|
447208
|
+
Tip: ${tip}`));
|
|
447209
|
+
}
|
|
447203
447210
|
}
|
|
447204
|
-
|
|
447205
|
-
|
|
447211
|
+
function emitDeprecationWarning(oldName, newName, removeIn) {
|
|
447212
|
+
if (isJsonMode() || isQuietMode()) return;
|
|
447213
|
+
if (!process.stderr.isTTY) return;
|
|
447214
|
+
const key2 = `${oldName}\u2192${newName}`;
|
|
447215
|
+
if (_seenDeprecations.has(key2)) return;
|
|
447216
|
+
_seenDeprecations.add(key2);
|
|
447217
|
+
process.stderr.write(
|
|
447218
|
+
source_default.yellow(`[deprecated] '${oldName}' is deprecated; use '${newName}' instead. Alias will be removed in ${removeIn}.`) + "\n"
|
|
447219
|
+
);
|
|
447220
|
+
}
|
|
447221
|
+
function _resetDeprecationMemoryForTesting() {
|
|
447222
|
+
_seenDeprecations.clear();
|
|
447223
|
+
}
|
|
447224
|
+
function outputError(error, details) {
|
|
447225
|
+
if (isJsonMode()) {
|
|
447226
|
+
console.error(JSON.stringify({ success: false, error, ...details }, null, 2));
|
|
447227
|
+
} else {
|
|
447228
|
+
console.error(error);
|
|
447229
|
+
}
|
|
447230
|
+
}
|
|
447231
|
+
var ExitCode, PROVIDER_ERROR_HINTS, COST_ESTIMATES, _seenDeprecations;
|
|
447232
|
+
var init_output = __esm({
|
|
447233
|
+
"../cli/src/commands/output.ts"() {
|
|
447206
447234
|
"use strict";
|
|
447235
|
+
init_source();
|
|
447236
|
+
init_ora();
|
|
447237
|
+
ExitCode = /* @__PURE__ */ ((ExitCode2) => {
|
|
447238
|
+
ExitCode2[ExitCode2["SUCCESS"] = 0] = "SUCCESS";
|
|
447239
|
+
ExitCode2[ExitCode2["GENERAL"] = 1] = "GENERAL";
|
|
447240
|
+
ExitCode2[ExitCode2["USAGE"] = 2] = "USAGE";
|
|
447241
|
+
ExitCode2[ExitCode2["NOT_FOUND"] = 3] = "NOT_FOUND";
|
|
447242
|
+
ExitCode2[ExitCode2["AUTH"] = 4] = "AUTH";
|
|
447243
|
+
ExitCode2[ExitCode2["API_ERROR"] = 5] = "API_ERROR";
|
|
447244
|
+
ExitCode2[ExitCode2["NETWORK"] = 6] = "NETWORK";
|
|
447245
|
+
return ExitCode2;
|
|
447246
|
+
})(ExitCode || {});
|
|
447247
|
+
PROVIDER_ERROR_HINTS = [
|
|
447248
|
+
// Billing (must precede the 429 rate-limit pattern)
|
|
447249
|
+
{ pattern: /402|payment.*required|billing|INSUFFICIENT_BALANCE|insufficient.*(credit|funds|balance)|balance.*(not.*enough|insufficient)|credits?.*exhausted|account.*balance/i, suggestion: "Account balance or credits exhausted. Top up at the provider dashboard, or try -p <other-provider>.", retryable: false },
|
|
447250
|
+
// Rate limits / quota
|
|
447251
|
+
{ pattern: /429|rate.?limit|too many requests/i, suggestion: "Rate limited. Wait 30-60 seconds and retry, or check your plan's rate limits.", retryable: true },
|
|
447252
|
+
{ pattern: /RESOURCE_EXHAUSTED|quota.*exceeded|requests.*per.*(minute|day)/i, suggestion: "Quota exceeded. Wait for the quota window to reset, or upgrade your plan. Consider -p <other-provider> to use a different provider.", retryable: true },
|
|
447253
|
+
// Auth
|
|
447254
|
+
{ pattern: /401|unauthorized|(invalid|incorrect).*api.?key|invalid_api_key|authentication.*(failed|error)|missing.*api.?key|did not start with 'key_'/i, suggestion: "API key is invalid or expired. Run 'vibe setup' to update, or check the key at the provider's dashboard.", retryable: false },
|
|
447255
|
+
{ pattern: /403|forbidden|permission.*denied/i, suggestion: "Access denied. Your API key may lack required permissions, or the feature requires a paid plan.", retryable: false },
|
|
447256
|
+
// Server
|
|
447257
|
+
{ pattern: /500|internal.*error|server.*error/i, suggestion: "Provider server error. Retry in a few minutes.", retryable: true },
|
|
447258
|
+
{ pattern: /503|service.*unavailable|overloaded|overloaded_error/i, suggestion: "Provider is temporarily overloaded. Retry in 1-2 minutes, or switch provider with -p.", retryable: true },
|
|
447259
|
+
{ pattern: /timeout|timed?\s*out|ETIMEDOUT|ECONNRESET|socket.*hang.?up/i, suggestion: "Request timed out. The provider may be slow. Retry, or try a different provider with -p flag.", retryable: true },
|
|
447260
|
+
// Content policy
|
|
447261
|
+
{ pattern: /content.*(policy|filter)|safety|moderation|blocked.*(by|due)|content_policy_violation|restricted.*content/i, suggestion: "Content was blocked by the provider's safety filter. Rephrase your prompt to avoid sensitive terms.", retryable: false },
|
|
447262
|
+
// Model / context
|
|
447263
|
+
{ pattern: /context_length_exceeded|maximum.*context.*length|token.*limit.*exceeded|prompt.*too.*long/i, suggestion: "Input exceeds the model's context window. Shorten the prompt, or use a model with larger context (run 'vibe schema <command>' for options).", retryable: false },
|
|
447264
|
+
{ pattern: /model.*not.*found|invalid.*model|unknown.*model|model_not_found/i, suggestion: "The specified model is unavailable. Check 'vibe schema <command>' for valid model options.", retryable: false },
|
|
447265
|
+
// Provider-specific
|
|
447266
|
+
{ pattern: /voice.*not.*found|voice_not_found|invalid.*voice.?id/i, suggestion: "Voice ID not found. Run 'vibe audio list-voices' to list available voices, then pass --voice <id>.", retryable: false },
|
|
447267
|
+
{ pattern: /character.*(count|limit).*exceeded|invalid_character_count/i, suggestion: "Text exceeds the TTS provider's character limit. Shorten the text or split into chunks.", retryable: false },
|
|
447268
|
+
{ pattern: /invalid.*aspect.*ratio|unsupported.*aspect.*ratio|unsupported.*resolution/i, suggestion: "This aspect ratio or resolution isn't supported by the chosen model. Check 'vibe schema <command>' for supported values.", retryable: false },
|
|
447269
|
+
{ pattern: /invalid.*file.*format|unsupported.*(format|codec)|unsupported.*media.?type/i, suggestion: "Input file format not supported. Convert to MP4/MP3/PNG first with 'vibe export' or 'ffmpeg'.", retryable: false },
|
|
447270
|
+
{ pattern: /region.*(restriction|not.*supported|unavailable)|geo.?blocked/i, suggestion: "Provider unavailable in your region. Try -p <other-provider>, or use a supported region.", retryable: false },
|
|
447271
|
+
{ pattern: /task.*(not.*found|expired)|job.*(not.*found|expired)/i, suggestion: "The async task expired or was never created. Re-run the command to start a new task.", retryable: true }
|
|
447272
|
+
];
|
|
447273
|
+
COST_ESTIMATES = {
|
|
447274
|
+
// Free
|
|
447275
|
+
"detect scenes": { min: 0, max: 0, unit: "free" },
|
|
447276
|
+
"detect silence": { min: 0, max: 0, unit: "free" },
|
|
447277
|
+
"detect beats": { min: 0, max: 0, unit: "free" },
|
|
447278
|
+
"edit silence-cut": { min: 0, max: 0, unit: "free" },
|
|
447279
|
+
"edit fade": { min: 0, max: 0, unit: "free" },
|
|
447280
|
+
"edit noise-reduce": { min: 0, max: 0, unit: "free" },
|
|
447281
|
+
"edit reframe": { min: 0, max: 0, unit: "free" },
|
|
447282
|
+
"edit interpolate": { min: 0, max: 0, unit: "free" },
|
|
447283
|
+
"edit upscale-video": { min: 0, max: 0, unit: "free" },
|
|
447284
|
+
// Low
|
|
447285
|
+
"analyze media": { min: 0.01, max: 0.05, unit: "per call" },
|
|
447286
|
+
"analyze video": { min: 0.01, max: 0.1, unit: "per video" },
|
|
447287
|
+
"analyze review": { min: 0.01, max: 0.1, unit: "per video" },
|
|
447288
|
+
"generate image": { min: 0.01, max: 0.07, unit: "per image" },
|
|
447289
|
+
"generate thumbnail": { min: 0.01, max: 0.05, unit: "per image" },
|
|
447290
|
+
"generate storyboard": { min: 0.01, max: 0.05, unit: "per call" },
|
|
447291
|
+
"ai transcribe": { min: 0.01, max: 0.1, unit: "per minute" },
|
|
447292
|
+
"audio transcribe": { min: 0.01, max: 0.1, unit: "per minute" },
|
|
447293
|
+
"edit caption": { min: 0.01, max: 0.1, unit: "per video" },
|
|
447294
|
+
"edit jump-cut": { min: 0.01, max: 0.1, unit: "per video" },
|
|
447295
|
+
"edit translate-srt": { min: 0.01, max: 0.05, unit: "per file" },
|
|
447296
|
+
"edit animated-caption": { min: 0.01, max: 0.1, unit: "per video" },
|
|
447297
|
+
// Medium
|
|
447298
|
+
"generate speech": { min: 0.05, max: 0.3, unit: "per request" },
|
|
447299
|
+
"generate sound-effect": { min: 0.05, max: 0.2, unit: "per request" },
|
|
447300
|
+
"generate music": { min: 0.05, max: 0.5, unit: "per request" },
|
|
447301
|
+
"generate motion": { min: 0.01, max: 0.1, unit: "per generation" },
|
|
447302
|
+
"edit grade": { min: 0.01, max: 0.05, unit: "per video" },
|
|
447303
|
+
"edit speed-ramp": { min: 0.05, max: 0.15, unit: "per video" },
|
|
447304
|
+
"edit text-overlay": { min: 0, max: 0.05, unit: "per video" },
|
|
447305
|
+
// High
|
|
447306
|
+
"generate video": { min: 0.5, max: 5, unit: "per video" },
|
|
447307
|
+
"edit image": { min: 0.05, max: 0.5, unit: "per edit" },
|
|
447308
|
+
// Very High
|
|
447309
|
+
"pipeline highlights": { min: 0.05, max: 1, unit: "per analysis" },
|
|
447310
|
+
"pipeline auto-shorts": { min: 0.1, max: 2, unit: "per batch" },
|
|
447311
|
+
"pipeline animated-caption": { min: 0.01, max: 0.1, unit: "per video" },
|
|
447312
|
+
"pipeline regenerate-scene": { min: 0.5, max: 5, unit: "per scene" },
|
|
447313
|
+
// Scene composition (v0.59+) — per-beat composer call ≈ $0.06 (PR #111
|
|
447314
|
+
// pre-flight). Range covers a 1-beat preview to a 10-beat long-form.
|
|
447315
|
+
"compose scenes with skills": { min: 0.05, max: 1.5, unit: "per pipeline" }
|
|
447316
|
+
};
|
|
447317
|
+
_seenDeprecations = /* @__PURE__ */ new Set();
|
|
447207
447318
|
}
|
|
447208
447319
|
});
|
|
447209
447320
|
|
|
@@ -447277,278 +447388,6 @@ var init_exec_safe = __esm({
|
|
|
447277
447388
|
}
|
|
447278
447389
|
});
|
|
447279
447390
|
|
|
447280
|
-
// ../cli/src/commands/_shared/scene-audio-mux.ts
|
|
447281
|
-
import { rename as rename3, unlink as unlink4 } from "node:fs/promises";
|
|
447282
|
-
import { resolve as resolve15, dirname as dirname12, extname as extname4, basename as basename4 } from "node:path";
|
|
447283
|
-
function buildAudioMuxFilter(audios) {
|
|
447284
|
-
if (audios.length === 0) return null;
|
|
447285
|
-
const labels = [];
|
|
447286
|
-
const stages = [];
|
|
447287
|
-
audios.forEach((a, i) => {
|
|
447288
|
-
const inputIdx = i + 1;
|
|
447289
|
-
const delayMs = Math.max(0, Math.round(a.absoluteStart * 1e3));
|
|
447290
|
-
const volume = Number.isFinite(a.volume) ? a.volume : 1;
|
|
447291
|
-
const durationHint = typeof a.durationHint === "number" && Number.isFinite(a.durationHint) ? Math.max(0, a.durationHint) : null;
|
|
447292
|
-
const clipCap = Math.max(0, a.clipDurationCap);
|
|
447293
|
-
const trimSec = durationHint === null ? clipCap : Math.min(durationHint, clipCap);
|
|
447294
|
-
const label = `a${i}`;
|
|
447295
|
-
const stage = [
|
|
447296
|
-
`[${inputIdx}:a]`,
|
|
447297
|
-
`atrim=duration=${trimSec.toFixed(3)},`,
|
|
447298
|
-
`asetpts=PTS-STARTPTS,`,
|
|
447299
|
-
`adelay=${delayMs}:all=1,`,
|
|
447300
|
-
`volume=${volume}`,
|
|
447301
|
-
`[${label}]`
|
|
447302
|
-
].join("");
|
|
447303
|
-
stages.push(stage);
|
|
447304
|
-
labels.push(`[${label}]`);
|
|
447305
|
-
});
|
|
447306
|
-
if (audios.length === 1) {
|
|
447307
|
-
return {
|
|
447308
|
-
filterComplex: stages.join(";"),
|
|
447309
|
-
outLabel: labels[0],
|
|
447310
|
-
inputCount: 1
|
|
447311
|
-
};
|
|
447312
|
-
}
|
|
447313
|
-
const mix = `${labels.join("")}amix=inputs=${audios.length}:dropout_transition=0:normalize=0[mixed]`;
|
|
447314
|
-
return {
|
|
447315
|
-
filterComplex: `${stages.join(";")};${mix}`,
|
|
447316
|
-
outLabel: "[mixed]",
|
|
447317
|
-
inputCount: audios.length
|
|
447318
|
-
};
|
|
447319
|
-
}
|
|
447320
|
-
function audioCodecForFormat(format4) {
|
|
447321
|
-
if (format4 === "webm") return "libopus";
|
|
447322
|
-
if (format4 === "mov") return "pcm_s16le";
|
|
447323
|
-
return "aac";
|
|
447324
|
-
}
|
|
447325
|
-
async function muxAudioIntoVideo(opts) {
|
|
447326
|
-
if (opts.audios.length === 0) {
|
|
447327
|
-
return { success: true, outputPath: opts.videoPath, audioCount: 0 };
|
|
447328
|
-
}
|
|
447329
|
-
if (!commandExists("ffmpeg")) {
|
|
447330
|
-
return {
|
|
447331
|
-
success: false,
|
|
447332
|
-
outputPath: opts.videoPath,
|
|
447333
|
-
audioCount: opts.audios.length,
|
|
447334
|
-
error: "ffmpeg not found in PATH \u2014 install via `brew install ffmpeg` (mac) or your package manager"
|
|
447335
|
-
};
|
|
447336
|
-
}
|
|
447337
|
-
const filter4 = buildAudioMuxFilter(opts.audios);
|
|
447338
|
-
if (!filter4) {
|
|
447339
|
-
return { success: true, outputPath: opts.videoPath, audioCount: 0 };
|
|
447340
|
-
}
|
|
447341
|
-
const ext = extname4(opts.videoPath) || `.${opts.format}`;
|
|
447342
|
-
const tmpPath = resolve15(
|
|
447343
|
-
dirname12(opts.videoPath),
|
|
447344
|
-
`.${basename4(opts.videoPath, ext)}.muxing${ext}`
|
|
447345
|
-
);
|
|
447346
|
-
const args = ["-y", "-loglevel", "error", "-i", opts.videoPath];
|
|
447347
|
-
for (const a of opts.audios) {
|
|
447348
|
-
args.push("-i", a.srcAbs);
|
|
447349
|
-
}
|
|
447350
|
-
args.push(
|
|
447351
|
-
"-filter_complex",
|
|
447352
|
-
filter4.filterComplex,
|
|
447353
|
-
"-map",
|
|
447354
|
-
"0:v",
|
|
447355
|
-
"-map",
|
|
447356
|
-
filter4.outLabel,
|
|
447357
|
-
"-c:v",
|
|
447358
|
-
"copy",
|
|
447359
|
-
"-c:a",
|
|
447360
|
-
audioCodecForFormat(opts.format),
|
|
447361
|
-
// Cap on the video duration so audio that overruns the producer's render
|
|
447362
|
-
// (e.g. a long Kokoro wav on a short scene) doesn't extend the output.
|
|
447363
|
-
// Video drives the timeline because the producer already counted frames.
|
|
447364
|
-
"-t",
|
|
447365
|
-
opts.totalDuration && opts.totalDuration > 0 ? opts.totalDuration.toFixed(3) : opts.videoDuration?.toFixed(3) ?? ""
|
|
447366
|
-
);
|
|
447367
|
-
if (args[args.length - 1] === "") {
|
|
447368
|
-
args.pop();
|
|
447369
|
-
args.pop();
|
|
447370
|
-
}
|
|
447371
|
-
args.push("-movflags", "+faststart", tmpPath);
|
|
447372
|
-
try {
|
|
447373
|
-
const { stderr } = await execSafe("ffmpeg", args);
|
|
447374
|
-
if (stderr && opts.onProgress) {
|
|
447375
|
-
stderr.split(/\r?\n/).forEach((line) => opts.onProgress?.(line));
|
|
447376
|
-
}
|
|
447377
|
-
} catch (err) {
|
|
447378
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
447379
|
-
try {
|
|
447380
|
-
await unlink4(tmpPath);
|
|
447381
|
-
} catch {
|
|
447382
|
-
}
|
|
447383
|
-
return {
|
|
447384
|
-
success: false,
|
|
447385
|
-
outputPath: opts.videoPath,
|
|
447386
|
-
audioCount: opts.audios.length,
|
|
447387
|
-
error: `ffmpeg mux failed: ${msg}`
|
|
447388
|
-
};
|
|
447389
|
-
}
|
|
447390
|
-
await rename3(tmpPath, opts.videoPath);
|
|
447391
|
-
return {
|
|
447392
|
-
success: true,
|
|
447393
|
-
outputPath: opts.videoPath,
|
|
447394
|
-
audioCount: opts.audios.length
|
|
447395
|
-
};
|
|
447396
|
-
}
|
|
447397
|
-
var init_scene_audio_mux = __esm({
|
|
447398
|
-
"../cli/src/commands/_shared/scene-audio-mux.ts"() {
|
|
447399
|
-
"use strict";
|
|
447400
|
-
init_exec_safe();
|
|
447401
|
-
}
|
|
447402
|
-
});
|
|
447403
|
-
|
|
447404
|
-
// ../cli/src/commands/_shared/scene-render.ts
|
|
447405
|
-
var scene_render_exports = {};
|
|
447406
|
-
__export(scene_render_exports, {
|
|
447407
|
-
buildRenderConfig: () => buildRenderConfig,
|
|
447408
|
-
defaultOutputPath: () => defaultOutputPath,
|
|
447409
|
-
executeSceneRender: () => executeSceneRender,
|
|
447410
|
-
qualityToCrf: () => qualityToCrf
|
|
447411
|
-
});
|
|
447412
|
-
import { mkdir as mkdir6, readFile as readFile6, stat as stat2 } from "node:fs/promises";
|
|
447413
|
-
import { existsSync as existsSync20 } from "node:fs";
|
|
447414
|
-
import { resolve as resolve16, relative as relative4, dirname as dirname13, basename as basename5 } from "node:path";
|
|
447415
|
-
function qualityToCrf(quality = "standard") {
|
|
447416
|
-
return quality === "draft" ? 28 : quality === "high" ? 18 : 23;
|
|
447417
|
-
}
|
|
447418
|
-
function defaultOutputPath(opts) {
|
|
447419
|
-
const fmt = opts.format ?? "mp4";
|
|
447420
|
-
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
447421
|
-
const stamp = now.toISOString().replace(/[:T]/g, "-").replace(/\..+$/, "");
|
|
447422
|
-
const name = (opts.projectName ?? basename5(resolve16(opts.projectDir))) || "scene";
|
|
447423
|
-
return resolve16(opts.projectDir, "renders", `${name}-${stamp}.${fmt}`);
|
|
447424
|
-
}
|
|
447425
|
-
async function readProjectName(projectDir) {
|
|
447426
|
-
const cfgPath = resolve16(projectDir, "vibe.project.yaml");
|
|
447427
|
-
if (!existsSync20(cfgPath)) return void 0;
|
|
447428
|
-
try {
|
|
447429
|
-
const raw2 = await (await import("node:fs/promises")).readFile(cfgPath, "utf-8");
|
|
447430
|
-
const parsed = (0, import_yaml3.parse)(raw2);
|
|
447431
|
-
return parsed?.name ?? void 0;
|
|
447432
|
-
} catch {
|
|
447433
|
-
return void 0;
|
|
447434
|
-
}
|
|
447435
|
-
}
|
|
447436
|
-
function buildRenderConfig(opts) {
|
|
447437
|
-
const quality = opts.quality ?? "standard";
|
|
447438
|
-
return {
|
|
447439
|
-
fps: opts.fps ?? 30,
|
|
447440
|
-
quality,
|
|
447441
|
-
format: opts.format ?? "mp4",
|
|
447442
|
-
entryFile: opts.entryFile ?? "index.html",
|
|
447443
|
-
crf: qualityToCrf(quality),
|
|
447444
|
-
workers: opts.workers ?? 1
|
|
447445
|
-
};
|
|
447446
|
-
}
|
|
447447
|
-
async function executeSceneRender(opts = {}) {
|
|
447448
|
-
const projectDir = resolve16(opts.projectDir ?? ".");
|
|
447449
|
-
const root2 = opts.root ?? "index.html";
|
|
447450
|
-
const projectStat = await safeStat(projectDir);
|
|
447451
|
-
if (!projectStat || !projectStat.isDirectory()) {
|
|
447452
|
-
return { success: false, error: `Project directory not found: ${projectDir}` };
|
|
447453
|
-
}
|
|
447454
|
-
if (!await rootExists(projectDir, root2)) {
|
|
447455
|
-
return {
|
|
447456
|
-
success: false,
|
|
447457
|
-
error: `Root composition not found: ${resolve16(projectDir, root2)}. Run \`vibe scene init\` first.`
|
|
447458
|
-
};
|
|
447459
|
-
}
|
|
447460
|
-
const chrome2 = await preflightChrome();
|
|
447461
|
-
if (!chrome2.ok) {
|
|
447462
|
-
return { success: false, error: chrome2.reason };
|
|
447463
|
-
}
|
|
447464
|
-
const projectName = await readProjectName(projectDir);
|
|
447465
|
-
const outputPath = opts.output ? resolve16(projectDir, opts.output) : defaultOutputPath({ projectDir, projectName, format: opts.format });
|
|
447466
|
-
await mkdir6(dirname13(outputPath), { recursive: true });
|
|
447467
|
-
const config4 = buildRenderConfig({
|
|
447468
|
-
fps: opts.fps,
|
|
447469
|
-
quality: opts.quality,
|
|
447470
|
-
format: opts.format,
|
|
447471
|
-
workers: opts.workers,
|
|
447472
|
-
entryFile: root2
|
|
447473
|
-
});
|
|
447474
|
-
const job = createRenderJob(config4);
|
|
447475
|
-
const start = Date.now();
|
|
447476
|
-
try {
|
|
447477
|
-
await executeRenderJob(
|
|
447478
|
-
job,
|
|
447479
|
-
projectDir,
|
|
447480
|
-
outputPath,
|
|
447481
|
-
(j, msg) => opts.onProgress?.(j.progress, j.currentStage ?? msg),
|
|
447482
|
-
opts.signal
|
|
447483
|
-
);
|
|
447484
|
-
} catch (err) {
|
|
447485
|
-
return {
|
|
447486
|
-
success: false,
|
|
447487
|
-
error: err instanceof Error ? err.message : String(err)
|
|
447488
|
-
};
|
|
447489
|
-
}
|
|
447490
|
-
let audioCount = 0;
|
|
447491
|
-
let audioMuxApplied = false;
|
|
447492
|
-
let audioMuxWarning;
|
|
447493
|
-
try {
|
|
447494
|
-
opts.onProgress?.(0.95, "Mixing audio");
|
|
447495
|
-
const rootHtml = await readFile6(resolve16(projectDir, root2), "utf-8");
|
|
447496
|
-
const audios = await scanSceneAudio({ projectDir, rootHtml });
|
|
447497
|
-
audioCount = audios.length;
|
|
447498
|
-
if (audios.length > 0) {
|
|
447499
|
-
const videoDuration = job.totalFrames && config4.fps ? job.totalFrames / config4.fps : void 0;
|
|
447500
|
-
const mux = await muxAudioIntoVideo({
|
|
447501
|
-
videoPath: outputPath,
|
|
447502
|
-
audios,
|
|
447503
|
-
format: config4.format ?? "mp4",
|
|
447504
|
-
videoDuration,
|
|
447505
|
-
onProgress: (line) => {
|
|
447506
|
-
if (line) opts.onProgress?.(0.97, line);
|
|
447507
|
-
}
|
|
447508
|
-
});
|
|
447509
|
-
if (mux.success) {
|
|
447510
|
-
audioMuxApplied = true;
|
|
447511
|
-
} else {
|
|
447512
|
-
audioMuxWarning = mux.error;
|
|
447513
|
-
}
|
|
447514
|
-
}
|
|
447515
|
-
} catch (err) {
|
|
447516
|
-
audioMuxWarning = err instanceof Error ? err.message : String(err);
|
|
447517
|
-
}
|
|
447518
|
-
return {
|
|
447519
|
-
success: true,
|
|
447520
|
-
outputPath: relative4(process.cwd(), outputPath) || outputPath,
|
|
447521
|
-
durationMs: Date.now() - start,
|
|
447522
|
-
framesRendered: job.framesRendered,
|
|
447523
|
-
totalFrames: job.totalFrames,
|
|
447524
|
-
fps: config4.fps,
|
|
447525
|
-
quality: config4.quality,
|
|
447526
|
-
format: config4.format,
|
|
447527
|
-
audioCount,
|
|
447528
|
-
audioMuxApplied,
|
|
447529
|
-
audioMuxWarning
|
|
447530
|
-
};
|
|
447531
|
-
}
|
|
447532
|
-
async function safeStat(p) {
|
|
447533
|
-
try {
|
|
447534
|
-
return await stat2(p);
|
|
447535
|
-
} catch {
|
|
447536
|
-
return null;
|
|
447537
|
-
}
|
|
447538
|
-
}
|
|
447539
|
-
var import_yaml3;
|
|
447540
|
-
var init_scene_render = __esm({
|
|
447541
|
-
"../cli/src/commands/_shared/scene-render.ts"() {
|
|
447542
|
-
"use strict";
|
|
447543
|
-
import_yaml3 = __toESM(require_dist(), 1);
|
|
447544
|
-
init_dist2();
|
|
447545
|
-
init_chrome3();
|
|
447546
|
-
init_scene_lint();
|
|
447547
|
-
init_scene_audio_scan();
|
|
447548
|
-
init_scene_audio_mux();
|
|
447549
|
-
}
|
|
447550
|
-
});
|
|
447551
|
-
|
|
447552
447391
|
// ../cli/src/utils/audio.ts
|
|
447553
447392
|
async function getAudioDuration(filePath) {
|
|
447554
447393
|
try {
|
|
@@ -447600,6 +447439,126 @@ var init_audio = __esm({
|
|
|
447600
447439
|
}
|
|
447601
447440
|
});
|
|
447602
447441
|
|
|
447442
|
+
// ../cli/src/utils/agent-host-detect.ts
|
|
447443
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
447444
|
+
import { homedir as homedir4 } from "node:os";
|
|
447445
|
+
import { join as join19 } from "node:path";
|
|
447446
|
+
function detectAgentHosts(env4 = process.env) {
|
|
447447
|
+
const home = homedir4();
|
|
447448
|
+
return [
|
|
447449
|
+
{
|
|
447450
|
+
id: "claude-code",
|
|
447451
|
+
label: "Claude Code",
|
|
447452
|
+
detected: false,
|
|
447453
|
+
signals: [],
|
|
447454
|
+
projectFiles: ["CLAUDE.md", ".claude/skills/"]
|
|
447455
|
+
},
|
|
447456
|
+
{
|
|
447457
|
+
id: "codex",
|
|
447458
|
+
label: "Codex (OpenAI)",
|
|
447459
|
+
detected: false,
|
|
447460
|
+
signals: [],
|
|
447461
|
+
projectFiles: ["AGENTS.md"]
|
|
447462
|
+
},
|
|
447463
|
+
{
|
|
447464
|
+
id: "cursor",
|
|
447465
|
+
label: "Cursor",
|
|
447466
|
+
detected: false,
|
|
447467
|
+
signals: [],
|
|
447468
|
+
projectFiles: ["AGENTS.md", ".cursor/rules/"]
|
|
447469
|
+
},
|
|
447470
|
+
{
|
|
447471
|
+
id: "aider",
|
|
447472
|
+
label: "Aider",
|
|
447473
|
+
detected: false,
|
|
447474
|
+
signals: [],
|
|
447475
|
+
projectFiles: ["AGENTS.md", ".aider.conf.yml"]
|
|
447476
|
+
},
|
|
447477
|
+
{
|
|
447478
|
+
id: "gemini-cli",
|
|
447479
|
+
label: "Gemini CLI",
|
|
447480
|
+
detected: false,
|
|
447481
|
+
signals: [],
|
|
447482
|
+
// Per https://geminicli.com/docs/cli/gemini-md/ Gemini CLI's primary
|
|
447483
|
+
// context file is GEMINI.md; AGENTS.md is the cross-tool fallback
|
|
447484
|
+
// VibeFrame writes by default. Both are honoured.
|
|
447485
|
+
projectFiles: ["GEMINI.md", "AGENTS.md", ".gemini/"]
|
|
447486
|
+
},
|
|
447487
|
+
{
|
|
447488
|
+
id: "opencode",
|
|
447489
|
+
label: "OpenCode",
|
|
447490
|
+
detected: false,
|
|
447491
|
+
signals: [],
|
|
447492
|
+
// sst/opencode officially supports the agents.md spec — AGENTS.md at
|
|
447493
|
+
// project root is the standard place. Local config also under
|
|
447494
|
+
// `.opencode/` per https://opencode.ai/docs/config/.
|
|
447495
|
+
projectFiles: ["AGENTS.md", ".opencode/"]
|
|
447496
|
+
}
|
|
447497
|
+
].map((host) => {
|
|
447498
|
+
const signals2 = [];
|
|
447499
|
+
const binaryName = HOST_BINARIES[host.id];
|
|
447500
|
+
if (binaryName && isOnPath(binaryName, env4)) {
|
|
447501
|
+
signals2.push({ kind: "binary", name: binaryName });
|
|
447502
|
+
}
|
|
447503
|
+
const configDirRel = HOST_CONFIG_DIRS[host.id];
|
|
447504
|
+
if (configDirRel) {
|
|
447505
|
+
const path14 = join19(home, configDirRel);
|
|
447506
|
+
if (existsSync19(path14)) {
|
|
447507
|
+
signals2.push({ kind: "configDir", path: path14 });
|
|
447508
|
+
}
|
|
447509
|
+
}
|
|
447510
|
+
return {
|
|
447511
|
+
...host,
|
|
447512
|
+
id: host.id,
|
|
447513
|
+
detected: signals2.length > 0,
|
|
447514
|
+
signals: signals2
|
|
447515
|
+
};
|
|
447516
|
+
});
|
|
447517
|
+
}
|
|
447518
|
+
function detectedAgentHosts(env4 = process.env) {
|
|
447519
|
+
return detectAgentHosts(env4).filter((h) => h.detected).sort((a, b) => {
|
|
447520
|
+
if (a.id === "claude-code") return -1;
|
|
447521
|
+
if (b.id === "claude-code") return 1;
|
|
447522
|
+
return a.id.localeCompare(b.id);
|
|
447523
|
+
});
|
|
447524
|
+
}
|
|
447525
|
+
function isOnPath(binary, env4) {
|
|
447526
|
+
const path14 = env4.PATH ?? env4.Path ?? "";
|
|
447527
|
+
const sep = process.platform === "win32" ? ";" : ":";
|
|
447528
|
+
for (const dir of path14.split(sep)) {
|
|
447529
|
+
if (!dir) continue;
|
|
447530
|
+
if (existsSync19(join19(dir, binary))) return true;
|
|
447531
|
+
if (process.platform === "win32" && existsSync19(join19(dir, `${binary}.exe`))) return true;
|
|
447532
|
+
}
|
|
447533
|
+
return false;
|
|
447534
|
+
}
|
|
447535
|
+
var HOST_BINARIES, HOST_CONFIG_DIRS;
|
|
447536
|
+
var init_agent_host_detect = __esm({
|
|
447537
|
+
"../cli/src/utils/agent-host-detect.ts"() {
|
|
447538
|
+
"use strict";
|
|
447539
|
+
HOST_BINARIES = {
|
|
447540
|
+
"claude-code": "claude",
|
|
447541
|
+
codex: "codex",
|
|
447542
|
+
cursor: "cursor",
|
|
447543
|
+
aider: "aider",
|
|
447544
|
+
"gemini-cli": "gemini",
|
|
447545
|
+
opencode: "opencode"
|
|
447546
|
+
};
|
|
447547
|
+
HOST_CONFIG_DIRS = {
|
|
447548
|
+
"claude-code": ".claude",
|
|
447549
|
+
codex: ".codex",
|
|
447550
|
+
cursor: ".cursor",
|
|
447551
|
+
// some installs; macOS app stores prefs elsewhere
|
|
447552
|
+
aider: null,
|
|
447553
|
+
"gemini-cli": ".gemini",
|
|
447554
|
+
// sst/opencode uses XDG-style `~/.config/opencode/` per
|
|
447555
|
+
// https://opencode.ai/docs/config/. The path is relative to $HOME so
|
|
447556
|
+
// the join in detectAgentHosts() resolves correctly.
|
|
447557
|
+
opencode: ".config/opencode"
|
|
447558
|
+
};
|
|
447559
|
+
}
|
|
447560
|
+
});
|
|
447561
|
+
|
|
447603
447562
|
// ../cli/src/commands/_shared/hf-skill-bundle/bundle-content.ts
|
|
447604
447563
|
var SKILL_MD, HOUSE_STYLE_MD, MOTION_PRINCIPLES_MD, TYPOGRAPHY_MD, TRANSITIONS_MD;
|
|
447605
447564
|
var init_bundle_content = __esm({
|
|
@@ -448405,13 +448364,13 @@ Avoid transitions that create visible repeating geometric patterns \u2014 grids
|
|
|
448405
448364
|
});
|
|
448406
448365
|
|
|
448407
448366
|
// ../cli/src/commands/_shared/hf-skill-bundle/bundle.ts
|
|
448408
|
-
import { readFileSync as readFileSync11, existsSync as
|
|
448367
|
+
import { readFileSync as readFileSync11, existsSync as existsSync20, statSync as statSync8 } from "node:fs";
|
|
448409
448368
|
import { join as join20 } from "node:path";
|
|
448410
448369
|
import { homedir as homedir5 } from "node:os";
|
|
448411
448370
|
import { createHash as createHash3 } from "node:crypto";
|
|
448412
448371
|
function installedSkillPath() {
|
|
448413
448372
|
const candidate = join20(homedir5(), ".claude", "skills", "hyperframes");
|
|
448414
|
-
if (!
|
|
448373
|
+
if (!existsSync20(candidate)) return null;
|
|
448415
448374
|
if (!statSync8(candidate).isDirectory()) return null;
|
|
448416
448375
|
return candidate;
|
|
448417
448376
|
}
|
|
@@ -448438,7 +448397,7 @@ function buildInstalled(skillRoot) {
|
|
|
448438
448397
|
};
|
|
448439
448398
|
const sections = [];
|
|
448440
448399
|
for (const [label, path14] of Object.entries(installedPaths)) {
|
|
448441
|
-
if (!
|
|
448400
|
+
if (!existsSync20(path14)) return null;
|
|
448442
448401
|
sections.push({ label, content: readFileSync11(path14, "utf-8") });
|
|
448443
448402
|
}
|
|
448444
448403
|
return {
|
|
@@ -448535,7 +448494,7 @@ function extractProjectFrontmatter(md) {
|
|
|
448535
448494
|
if (!match2) return { frontmatter: void 0, remaining: md };
|
|
448536
448495
|
let parsed;
|
|
448537
448496
|
try {
|
|
448538
|
-
parsed = (0,
|
|
448497
|
+
parsed = (0, import_yaml3.parse)(match2[1]);
|
|
448539
448498
|
} catch {
|
|
448540
448499
|
return { frontmatter: void 0, remaining: md };
|
|
448541
448500
|
}
|
|
@@ -448552,7 +448511,7 @@ function extractBeatCues(body) {
|
|
|
448552
448511
|
if (!match2) return { cues: void 0, body };
|
|
448553
448512
|
let parsed;
|
|
448554
448513
|
try {
|
|
448555
|
-
parsed = (0,
|
|
448514
|
+
parsed = (0, import_yaml3.parse)(match2[1]);
|
|
448556
448515
|
} catch {
|
|
448557
448516
|
return { cues: void 0, body };
|
|
448558
448517
|
}
|
|
@@ -448582,11 +448541,11 @@ function parseBeatDuration(body) {
|
|
|
448582
448541
|
const n = parseFloat(valueMatch[1]);
|
|
448583
448542
|
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
448584
448543
|
}
|
|
448585
|
-
var
|
|
448544
|
+
var import_yaml3, HEADING_RE, BEAT_PREFIX_RE, DURATION_SUBSECTION_RE, DURATION_VALUE_RE, FRONTMATTER_RE, BEAT_CUES_RE;
|
|
448586
448545
|
var init_storyboard_parse = __esm({
|
|
448587
448546
|
"../cli/src/commands/_shared/storyboard-parse.ts"() {
|
|
448588
448547
|
"use strict";
|
|
448589
|
-
|
|
448548
|
+
import_yaml3 = __toESM(require_dist(), 1);
|
|
448590
448549
|
HEADING_RE = /^##\s+(.+?)\s*$/gm;
|
|
448591
448550
|
BEAT_PREFIX_RE = /^Beat\s+(.+?)\s+(?:—|:|-)\s+(.+)$/i;
|
|
448592
448551
|
DURATION_SUBSECTION_RE = /###\s+Beat\s+duration\s*\n([\s\S]*?)(?=\n###\s|\n##\s|$)/i;
|
|
@@ -448670,9 +448629,9 @@ import OpenAI from "openai";
|
|
|
448670
448629
|
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
448671
448630
|
import { createHash as createHash4 } from "node:crypto";
|
|
448672
448631
|
import { existsSync as existsSync23, mkdirSync as mkdirSync12 } from "node:fs";
|
|
448673
|
-
import { mkdir as mkdir7, readFile as
|
|
448632
|
+
import { mkdir as mkdir7, readFile as readFile5, writeFile as writeFile8 } from "node:fs/promises";
|
|
448674
448633
|
import { homedir as homedir6 } from "node:os";
|
|
448675
|
-
import { dirname as
|
|
448634
|
+
import { dirname as dirname13, join as join23, resolve as resolve15 } from "node:path";
|
|
448676
448635
|
function buildSystemPrompt(ctx) {
|
|
448677
448636
|
return `You are a Hyperframes composition author. The skill content below
|
|
448678
448637
|
defines the framework rules, motion principles, and quality standards.
|
|
@@ -448820,7 +448779,7 @@ function computeCacheKey(parts) {
|
|
|
448820
448779
|
return createHash4("sha256").update(parts.provider).update("\u241E").update(parts.model).update("\u241E").update(parts.systemPrompt).update("\u241E").update(parts.userPrompt).digest("hex");
|
|
448821
448780
|
}
|
|
448822
448781
|
function defaultCacheDir() {
|
|
448823
|
-
return
|
|
448782
|
+
return join23(homedir6(), ".vibeframe", "cache", "compose-scenes");
|
|
448824
448783
|
}
|
|
448825
448784
|
function extractHtml(responseText) {
|
|
448826
448785
|
const fenced = responseText.match(/```html\s*\n([\s\S]*?)\n```/);
|
|
@@ -448844,9 +448803,9 @@ async function composeBeatHtml(ctx, overrides) {
|
|
|
448844
448803
|
model: settings.model
|
|
448845
448804
|
});
|
|
448846
448805
|
const cacheDir = ctx.cacheDir ?? defaultCacheDir();
|
|
448847
|
-
const cachePath =
|
|
448806
|
+
const cachePath = join23(cacheDir, `${cacheKey2}.html`);
|
|
448848
448807
|
if (existsSync23(cachePath)) {
|
|
448849
|
-
const html2 = await
|
|
448808
|
+
const html2 = await readFile5(cachePath, "utf-8");
|
|
448850
448809
|
return { html: html2, cached: true, cacheKey: cacheKey2, model: settings.model, provider: ctx.provider };
|
|
448851
448810
|
}
|
|
448852
448811
|
if (!ctx.apiKey) {
|
|
@@ -448880,7 +448839,7 @@ async function composeBeatHtml(ctx, overrides) {
|
|
|
448880
448839
|
const html = extractHtml(result.text);
|
|
448881
448840
|
try {
|
|
448882
448841
|
mkdirSync12(cacheDir, { recursive: true });
|
|
448883
|
-
await
|
|
448842
|
+
await writeFile8(cachePath, html, "utf-8");
|
|
448884
448843
|
} catch {
|
|
448885
448844
|
}
|
|
448886
448845
|
const costUsd = result.inputTokens / 1e6 * settings.costPerMTokIn + result.outputTokens / 1e6 * settings.costPerMTokOut;
|
|
@@ -448938,17 +448897,17 @@ ${formatLintFeedback(lint2.findings)}`
|
|
|
448938
448897
|
);
|
|
448939
448898
|
}
|
|
448940
448899
|
async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
448941
|
-
const projectRoot = params.project ?
|
|
448942
|
-
const designPath =
|
|
448943
|
-
const storyboardPath =
|
|
448900
|
+
const projectRoot = params.project ? resolve15(outputDir, params.project) : resolve15(outputDir);
|
|
448901
|
+
const designPath = resolve15(projectRoot, params.design ?? "DESIGN.md");
|
|
448902
|
+
const storyboardPath = resolve15(projectRoot, params.storyboard ?? "STORYBOARD.md");
|
|
448944
448903
|
if (!existsSync23(designPath)) {
|
|
448945
448904
|
return { success: false, error: `DESIGN.md not found at ${designPath}` };
|
|
448946
448905
|
}
|
|
448947
448906
|
if (!existsSync23(storyboardPath)) {
|
|
448948
448907
|
return { success: false, error: `STORYBOARD.md not found at ${storyboardPath}` };
|
|
448949
448908
|
}
|
|
448950
|
-
const designMd = await
|
|
448951
|
-
const storyboardMd = await
|
|
448909
|
+
const designMd = await readFile5(designPath, "utf-8");
|
|
448910
|
+
const storyboardMd = await readFile5(storyboardPath, "utf-8");
|
|
448952
448911
|
const { global: storyboardGlobal, beats } = parseStoryboard(storyboardMd);
|
|
448953
448912
|
if (beats.length === 0) {
|
|
448954
448913
|
return {
|
|
@@ -448968,7 +448927,7 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
448968
448927
|
provider = params.composer ?? "claude";
|
|
448969
448928
|
apiKey = "";
|
|
448970
448929
|
}
|
|
448971
|
-
const compositionsDir =
|
|
448930
|
+
const compositionsDir = join23(projectRoot, "compositions");
|
|
448972
448931
|
await mkdir7(compositionsDir, { recursive: true });
|
|
448973
448932
|
const onProgress = params.onProgress ?? (() => {
|
|
448974
448933
|
});
|
|
@@ -448989,9 +448948,9 @@ async function executeComposeScenesWithSkills(params, outputDir, overrides) {
|
|
|
448989
448948
|
},
|
|
448990
448949
|
overrides
|
|
448991
448950
|
);
|
|
448992
|
-
const compositionPath =
|
|
448993
|
-
await mkdir7(
|
|
448994
|
-
await
|
|
448951
|
+
const compositionPath = join23(compositionsDir, `scene-${beat.id}.html`);
|
|
448952
|
+
await mkdir7(dirname13(compositionPath), { recursive: true });
|
|
448953
|
+
await writeFile8(compositionPath, result.html, "utf-8");
|
|
448995
448954
|
if (result.cached) {
|
|
448996
448955
|
onProgress({
|
|
448997
448956
|
type: "beat-cached",
|
|
@@ -449170,14 +449129,14 @@ var init_compose_scenes_skills = __esm({
|
|
|
449170
449129
|
|
|
449171
449130
|
// ../cli/src/commands/_shared/compose-prompts.ts
|
|
449172
449131
|
import { existsSync as existsSync24 } from "node:fs";
|
|
449173
|
-
import { readFile as
|
|
449174
|
-
import { join as
|
|
449132
|
+
import { readFile as readFile6 } from "node:fs/promises";
|
|
449133
|
+
import { join as join24, relative as relative5, resolve as resolve16 } from "node:path";
|
|
449175
449134
|
async function getComposePrompts(opts) {
|
|
449176
|
-
const projectDir =
|
|
449177
|
-
const designPath =
|
|
449178
|
-
const storyboardPath =
|
|
449179
|
-
const skillPath =
|
|
449180
|
-
const compositionsDir =
|
|
449135
|
+
const projectDir = resolve16(opts.projectDir);
|
|
449136
|
+
const designPath = join24(projectDir, "DESIGN.md");
|
|
449137
|
+
const storyboardPath = join24(projectDir, "STORYBOARD.md");
|
|
449138
|
+
const skillPath = join24(projectDir, "SKILL.md");
|
|
449139
|
+
const compositionsDir = join24(projectDir, "compositions");
|
|
449181
449140
|
const warnings = [];
|
|
449182
449141
|
const baseError = (msg) => ({
|
|
449183
449142
|
success: false,
|
|
@@ -449203,7 +449162,7 @@ async function getComposePrompts(opts) {
|
|
|
449203
449162
|
"SKILL.md not installed \u2014 host agent won't have Hyperframes rules in context. Run `vibe scene install-skill` to install it."
|
|
449204
449163
|
);
|
|
449205
449164
|
}
|
|
449206
|
-
const storyboardMd = await
|
|
449165
|
+
const storyboardMd = await readFile6(storyboardPath, "utf-8");
|
|
449207
449166
|
const parsed = parseStoryboard(storyboardMd);
|
|
449208
449167
|
if (parsed.beats.length === 0) {
|
|
449209
449168
|
return baseError(`STORYBOARD.md has no \`## Beat \u2026\` headings.`);
|
|
@@ -449221,7 +449180,7 @@ async function getComposePrompts(opts) {
|
|
|
449221
449180
|
beats = parsed.beats;
|
|
449222
449181
|
}
|
|
449223
449182
|
const result = beats.map((beat) => {
|
|
449224
|
-
const outputPathAbs =
|
|
449183
|
+
const outputPathAbs = join24(compositionsDir, `scene-${beat.id}.html`);
|
|
449225
449184
|
const outputPathRel = relative5(projectDir, outputPathAbs);
|
|
449226
449185
|
const userPrompt = buildUserPrompt({
|
|
449227
449186
|
beat,
|
|
@@ -449285,124 +449244,425 @@ var init_compose_prompts = __esm({
|
|
|
449285
449244
|
}
|
|
449286
449245
|
});
|
|
449287
449246
|
|
|
449288
|
-
// ../cli/src/
|
|
449289
|
-
import { existsSync as
|
|
449247
|
+
// ../cli/src/pipeline/renderers/chrome.ts
|
|
449248
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
449290
449249
|
import { homedir as homedir7 } from "node:os";
|
|
449291
|
-
import { join as
|
|
449292
|
-
function
|
|
449293
|
-
const
|
|
449294
|
-
|
|
449295
|
-
|
|
449296
|
-
|
|
449297
|
-
|
|
449298
|
-
|
|
449299
|
-
|
|
449300
|
-
|
|
449301
|
-
|
|
449302
|
-
|
|
449303
|
-
|
|
449304
|
-
|
|
449305
|
-
|
|
449306
|
-
|
|
449307
|
-
|
|
449308
|
-
|
|
449309
|
-
|
|
449310
|
-
|
|
449311
|
-
|
|
449312
|
-
|
|
449313
|
-
|
|
449314
|
-
|
|
449315
|
-
|
|
449316
|
-
|
|
449317
|
-
|
|
449318
|
-
|
|
449319
|
-
|
|
449320
|
-
|
|
449321
|
-
|
|
449322
|
-
|
|
449323
|
-
|
|
449324
|
-
|
|
449325
|
-
|
|
449326
|
-
|
|
449327
|
-
|
|
449328
|
-
|
|
449329
|
-
|
|
449330
|
-
|
|
449331
|
-
|
|
449332
|
-
|
|
449333
|
-
|
|
449334
|
-
|
|
449335
|
-
|
|
449336
|
-
|
|
449337
|
-
|
|
449338
|
-
|
|
449339
|
-
|
|
449340
|
-
|
|
449341
|
-
|
|
449250
|
+
import { join as join25 } from "node:path";
|
|
449251
|
+
function findChrome() {
|
|
449252
|
+
const candidates = [
|
|
449253
|
+
process.env.HYPERFRAMES_CHROME_PATH,
|
|
449254
|
+
process.env.CHROME_PATH,
|
|
449255
|
+
// puppeteer auto-downloaded headless shell
|
|
449256
|
+
join25(
|
|
449257
|
+
homedir7(),
|
|
449258
|
+
".cache",
|
|
449259
|
+
"puppeteer",
|
|
449260
|
+
"chrome-headless-shell",
|
|
449261
|
+
"mac_arm-147.0.7727.56",
|
|
449262
|
+
"chrome-headless-shell-mac_arm",
|
|
449263
|
+
"chrome-headless-shell"
|
|
449264
|
+
),
|
|
449265
|
+
// system Chrome / Chromium
|
|
449266
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
449267
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
449268
|
+
"/usr/bin/google-chrome",
|
|
449269
|
+
"/usr/bin/chromium",
|
|
449270
|
+
"/usr/bin/chromium-browser",
|
|
449271
|
+
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
449272
|
+
];
|
|
449273
|
+
for (const c of candidates) {
|
|
449274
|
+
if (c && existsSync26(c)) return c;
|
|
449275
|
+
}
|
|
449276
|
+
return void 0;
|
|
449277
|
+
}
|
|
449278
|
+
async function preflightChrome() {
|
|
449279
|
+
return findChrome() ? { ok: true } : { ok: false, reason: CHROME_NOT_FOUND_REASON };
|
|
449280
|
+
}
|
|
449281
|
+
var CHROME_NOT_FOUND_REASON;
|
|
449282
|
+
var init_chrome3 = __esm({
|
|
449283
|
+
"../cli/src/pipeline/renderers/chrome.ts"() {
|
|
449284
|
+
"use strict";
|
|
449285
|
+
CHROME_NOT_FOUND_REASON = "Chrome not found. Set HYPERFRAMES_CHROME_PATH, or install Chrome (macOS: brew install --cask google-chrome \xB7 Linux: apt install chromium). Run `vibe doctor` for details.";
|
|
449286
|
+
}
|
|
449287
|
+
});
|
|
449288
|
+
|
|
449289
|
+
// ../cli/src/commands/_shared/scene-audio-scan.ts
|
|
449290
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
449291
|
+
import { resolve as resolve18 } from "node:path";
|
|
449292
|
+
function parseRootClips(rootHtml) {
|
|
449293
|
+
const clips = [];
|
|
449294
|
+
const clipRegex = /<div\b[^>]*class="clip"[^>]*>/gi;
|
|
449295
|
+
let match2;
|
|
449296
|
+
while ((match2 = clipRegex.exec(rootHtml)) !== null) {
|
|
449297
|
+
const tag = match2[0];
|
|
449298
|
+
const compositionId = pickAttr(tag, "data-composition-id");
|
|
449299
|
+
const compositionSrc = pickAttr(tag, "data-composition-src");
|
|
449300
|
+
const start = pickNumberAttr(tag, "data-start");
|
|
449301
|
+
const duration = pickNumberAttr(tag, "data-duration");
|
|
449302
|
+
const trackIndex = pickNumberAttr(tag, "data-track-index") ?? 1;
|
|
449303
|
+
if (!compositionId || !compositionSrc || start === null || duration === null) {
|
|
449304
|
+
continue;
|
|
449342
449305
|
}
|
|
449343
|
-
|
|
449344
|
-
|
|
449345
|
-
|
|
449346
|
-
|
|
449347
|
-
|
|
449306
|
+
clips.push({ compositionId, compositionSrc, start, duration, trackIndex });
|
|
449307
|
+
}
|
|
449308
|
+
return clips;
|
|
449309
|
+
}
|
|
449310
|
+
function parseSceneAudios(compositionHtml) {
|
|
449311
|
+
const out = [];
|
|
449312
|
+
const audioRegex = /<audio\b([^>]*)>/gi;
|
|
449313
|
+
let match2;
|
|
449314
|
+
while ((match2 = audioRegex.exec(compositionHtml)) !== null) {
|
|
449315
|
+
const attrs = match2[1];
|
|
449316
|
+
const src = pickAttr(attrs, "src");
|
|
449317
|
+
if (!src) continue;
|
|
449318
|
+
const localStart = pickNumberAttr(attrs, "data-start") ?? 0;
|
|
449319
|
+
const durationRaw = pickAttr(attrs, "data-duration");
|
|
449320
|
+
const durationHint = !durationRaw || durationRaw === "auto" ? "auto" : Number(durationRaw);
|
|
449321
|
+
const volume = pickNumberAttr(attrs, "data-volume") ?? 1;
|
|
449322
|
+
const trackIndex = pickNumberAttr(attrs, "data-track-index") ?? 2;
|
|
449323
|
+
out.push({ srcRel: src, localStart, durationHint, volume, trackIndex });
|
|
449324
|
+
}
|
|
449325
|
+
return out;
|
|
449326
|
+
}
|
|
449327
|
+
function makeFsCompositionReader(projectDir) {
|
|
449328
|
+
return async (compositionSrcRel) => {
|
|
449329
|
+
const abs = resolve18(projectDir, compositionSrcRel);
|
|
449330
|
+
try {
|
|
449331
|
+
return await readFile8(abs, "utf-8");
|
|
449332
|
+
} catch {
|
|
449333
|
+
return null;
|
|
449348
449334
|
}
|
|
449349
|
-
|
|
449350
|
-
|
|
449351
|
-
|
|
449352
|
-
|
|
449353
|
-
|
|
449354
|
-
|
|
449335
|
+
};
|
|
449336
|
+
}
|
|
449337
|
+
async function scanSceneAudio(opts) {
|
|
449338
|
+
const reader = opts.readComposition ?? makeFsCompositionReader(opts.projectDir);
|
|
449339
|
+
const clips = parseRootClips(opts.rootHtml);
|
|
449340
|
+
const out = [];
|
|
449341
|
+
for (const clip of clips) {
|
|
449342
|
+
const html = await reader(clip.compositionSrc);
|
|
449343
|
+
if (!html) continue;
|
|
449344
|
+
const audios = parseSceneAudios(html);
|
|
449345
|
+
for (const audio of audios) {
|
|
449346
|
+
out.push({
|
|
449347
|
+
srcRel: audio.srcRel,
|
|
449348
|
+
srcAbs: resolve18(opts.projectDir, audio.srcRel),
|
|
449349
|
+
absoluteStart: clip.start + audio.localStart,
|
|
449350
|
+
durationHint: audio.durationHint,
|
|
449351
|
+
clipDurationCap: clip.duration - audio.localStart,
|
|
449352
|
+
volume: audio.volume,
|
|
449353
|
+
trackIndex: audio.trackIndex,
|
|
449354
|
+
compositionSrc: clip.compositionSrc
|
|
449355
|
+
});
|
|
449355
449356
|
}
|
|
449357
|
+
}
|
|
449358
|
+
const maxClipEnd = clips.reduce((m, c) => Math.max(m, c.start + c.duration), 0);
|
|
449359
|
+
const rootAudios = parseSceneAudios(opts.rootHtml);
|
|
449360
|
+
for (const audio of rootAudios) {
|
|
449361
|
+
out.push({
|
|
449362
|
+
srcRel: audio.srcRel,
|
|
449363
|
+
srcAbs: resolve18(opts.projectDir, audio.srcRel),
|
|
449364
|
+
absoluteStart: audio.localStart,
|
|
449365
|
+
durationHint: audio.durationHint,
|
|
449366
|
+
clipDurationCap: Math.max(0, maxClipEnd - audio.localStart),
|
|
449367
|
+
volume: audio.volume,
|
|
449368
|
+
trackIndex: audio.trackIndex,
|
|
449369
|
+
compositionSrc: "(root)"
|
|
449370
|
+
});
|
|
449371
|
+
}
|
|
449372
|
+
out.sort((a, b) => a.absoluteStart - b.absoluteStart);
|
|
449373
|
+
return out;
|
|
449374
|
+
}
|
|
449375
|
+
function pickAttr(tag, name) {
|
|
449376
|
+
const re = new RegExp(`\\b${escapeRegex3(name)}\\s*=\\s*("([^"]*)"|'([^']*)')`);
|
|
449377
|
+
const m = tag.match(re);
|
|
449378
|
+
if (!m) return null;
|
|
449379
|
+
return m[2] ?? m[3] ?? null;
|
|
449380
|
+
}
|
|
449381
|
+
function pickNumberAttr(tag, name) {
|
|
449382
|
+
const raw2 = pickAttr(tag, name);
|
|
449383
|
+
if (raw2 === null) return null;
|
|
449384
|
+
const n = Number(raw2);
|
|
449385
|
+
return Number.isFinite(n) ? n : null;
|
|
449386
|
+
}
|
|
449387
|
+
function escapeRegex3(s) {
|
|
449388
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
449389
|
+
}
|
|
449390
|
+
var init_scene_audio_scan = __esm({
|
|
449391
|
+
"../cli/src/commands/_shared/scene-audio-scan.ts"() {
|
|
449392
|
+
"use strict";
|
|
449393
|
+
}
|
|
449394
|
+
});
|
|
449395
|
+
|
|
449396
|
+
// ../cli/src/commands/_shared/scene-audio-mux.ts
|
|
449397
|
+
import { rename as rename3, unlink as unlink4 } from "node:fs/promises";
|
|
449398
|
+
import { resolve as resolve19, dirname as dirname15, extname as extname4, basename as basename4 } from "node:path";
|
|
449399
|
+
function buildAudioMuxFilter(audios) {
|
|
449400
|
+
if (audios.length === 0) return null;
|
|
449401
|
+
const labels = [];
|
|
449402
|
+
const stages = [];
|
|
449403
|
+
audios.forEach((a, i) => {
|
|
449404
|
+
const inputIdx = i + 1;
|
|
449405
|
+
const delayMs = Math.max(0, Math.round(a.absoluteStart * 1e3));
|
|
449406
|
+
const volume = Number.isFinite(a.volume) ? a.volume : 1;
|
|
449407
|
+
const durationHint = typeof a.durationHint === "number" && Number.isFinite(a.durationHint) ? Math.max(0, a.durationHint) : null;
|
|
449408
|
+
const clipCap = Math.max(0, a.clipDurationCap);
|
|
449409
|
+
const trimSec = durationHint === null ? clipCap : Math.min(durationHint, clipCap);
|
|
449410
|
+
const label = `a${i}`;
|
|
449411
|
+
const stage = [
|
|
449412
|
+
`[${inputIdx}:a]`,
|
|
449413
|
+
`atrim=duration=${trimSec.toFixed(3)},`,
|
|
449414
|
+
`asetpts=PTS-STARTPTS,`,
|
|
449415
|
+
`adelay=${delayMs}:all=1,`,
|
|
449416
|
+
`volume=${volume}`,
|
|
449417
|
+
`[${label}]`
|
|
449418
|
+
].join("");
|
|
449419
|
+
stages.push(stage);
|
|
449420
|
+
labels.push(`[${label}]`);
|
|
449421
|
+
});
|
|
449422
|
+
if (audios.length === 1) {
|
|
449356
449423
|
return {
|
|
449357
|
-
|
|
449358
|
-
|
|
449359
|
-
|
|
449360
|
-
signals: signals2
|
|
449424
|
+
filterComplex: stages.join(";"),
|
|
449425
|
+
outLabel: labels[0],
|
|
449426
|
+
inputCount: 1
|
|
449361
449427
|
};
|
|
449362
|
-
}
|
|
449428
|
+
}
|
|
449429
|
+
const mix = `${labels.join("")}amix=inputs=${audios.length}:dropout_transition=0:normalize=0[mixed]`;
|
|
449430
|
+
return {
|
|
449431
|
+
filterComplex: `${stages.join(";")};${mix}`,
|
|
449432
|
+
outLabel: "[mixed]",
|
|
449433
|
+
inputCount: audios.length
|
|
449434
|
+
};
|
|
449363
449435
|
}
|
|
449364
|
-
function
|
|
449365
|
-
|
|
449366
|
-
|
|
449367
|
-
|
|
449368
|
-
return a.id.localeCompare(b.id);
|
|
449369
|
-
});
|
|
449436
|
+
function audioCodecForFormat(format4) {
|
|
449437
|
+
if (format4 === "webm") return "libopus";
|
|
449438
|
+
if (format4 === "mov") return "pcm_s16le";
|
|
449439
|
+
return "aac";
|
|
449370
449440
|
}
|
|
449371
|
-
function
|
|
449372
|
-
|
|
449373
|
-
|
|
449374
|
-
for (const dir of path14.split(sep)) {
|
|
449375
|
-
if (!dir) continue;
|
|
449376
|
-
if (existsSync25(join24(dir, binary))) return true;
|
|
449377
|
-
if (process.platform === "win32" && existsSync25(join24(dir, `${binary}.exe`))) return true;
|
|
449441
|
+
async function muxAudioIntoVideo(opts) {
|
|
449442
|
+
if (opts.audios.length === 0) {
|
|
449443
|
+
return { success: true, outputPath: opts.videoPath, audioCount: 0 };
|
|
449378
449444
|
}
|
|
449379
|
-
|
|
449445
|
+
if (!commandExists("ffmpeg")) {
|
|
449446
|
+
return {
|
|
449447
|
+
success: false,
|
|
449448
|
+
outputPath: opts.videoPath,
|
|
449449
|
+
audioCount: opts.audios.length,
|
|
449450
|
+
error: "ffmpeg not found in PATH \u2014 install via `brew install ffmpeg` (mac) or your package manager"
|
|
449451
|
+
};
|
|
449452
|
+
}
|
|
449453
|
+
const filter4 = buildAudioMuxFilter(opts.audios);
|
|
449454
|
+
if (!filter4) {
|
|
449455
|
+
return { success: true, outputPath: opts.videoPath, audioCount: 0 };
|
|
449456
|
+
}
|
|
449457
|
+
const ext = extname4(opts.videoPath) || `.${opts.format}`;
|
|
449458
|
+
const tmpPath = resolve19(
|
|
449459
|
+
dirname15(opts.videoPath),
|
|
449460
|
+
`.${basename4(opts.videoPath, ext)}.muxing${ext}`
|
|
449461
|
+
);
|
|
449462
|
+
const args = ["-y", "-loglevel", "error", "-i", opts.videoPath];
|
|
449463
|
+
for (const a of opts.audios) {
|
|
449464
|
+
args.push("-i", a.srcAbs);
|
|
449465
|
+
}
|
|
449466
|
+
args.push(
|
|
449467
|
+
"-filter_complex",
|
|
449468
|
+
filter4.filterComplex,
|
|
449469
|
+
"-map",
|
|
449470
|
+
"0:v",
|
|
449471
|
+
"-map",
|
|
449472
|
+
filter4.outLabel,
|
|
449473
|
+
"-c:v",
|
|
449474
|
+
"copy",
|
|
449475
|
+
"-c:a",
|
|
449476
|
+
audioCodecForFormat(opts.format),
|
|
449477
|
+
// Cap on the video duration so audio that overruns the producer's render
|
|
449478
|
+
// (e.g. a long Kokoro wav on a short scene) doesn't extend the output.
|
|
449479
|
+
// Video drives the timeline because the producer already counted frames.
|
|
449480
|
+
"-t",
|
|
449481
|
+
opts.totalDuration && opts.totalDuration > 0 ? opts.totalDuration.toFixed(3) : opts.videoDuration?.toFixed(3) ?? ""
|
|
449482
|
+
);
|
|
449483
|
+
if (args[args.length - 1] === "") {
|
|
449484
|
+
args.pop();
|
|
449485
|
+
args.pop();
|
|
449486
|
+
}
|
|
449487
|
+
args.push("-movflags", "+faststart", tmpPath);
|
|
449488
|
+
try {
|
|
449489
|
+
const { stderr } = await execSafe("ffmpeg", args);
|
|
449490
|
+
if (stderr && opts.onProgress) {
|
|
449491
|
+
stderr.split(/\r?\n/).forEach((line) => opts.onProgress?.(line));
|
|
449492
|
+
}
|
|
449493
|
+
} catch (err) {
|
|
449494
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
449495
|
+
try {
|
|
449496
|
+
await unlink4(tmpPath);
|
|
449497
|
+
} catch {
|
|
449498
|
+
}
|
|
449499
|
+
return {
|
|
449500
|
+
success: false,
|
|
449501
|
+
outputPath: opts.videoPath,
|
|
449502
|
+
audioCount: opts.audios.length,
|
|
449503
|
+
error: `ffmpeg mux failed: ${msg}`
|
|
449504
|
+
};
|
|
449505
|
+
}
|
|
449506
|
+
await rename3(tmpPath, opts.videoPath);
|
|
449507
|
+
return {
|
|
449508
|
+
success: true,
|
|
449509
|
+
outputPath: opts.videoPath,
|
|
449510
|
+
audioCount: opts.audios.length
|
|
449511
|
+
};
|
|
449380
449512
|
}
|
|
449381
|
-
var
|
|
449382
|
-
|
|
449383
|
-
"../cli/src/utils/agent-host-detect.ts"() {
|
|
449513
|
+
var init_scene_audio_mux = __esm({
|
|
449514
|
+
"../cli/src/commands/_shared/scene-audio-mux.ts"() {
|
|
449384
449515
|
"use strict";
|
|
449385
|
-
|
|
449386
|
-
|
|
449387
|
-
|
|
449388
|
-
|
|
449389
|
-
|
|
449390
|
-
|
|
449391
|
-
|
|
449516
|
+
init_exec_safe();
|
|
449517
|
+
}
|
|
449518
|
+
});
|
|
449519
|
+
|
|
449520
|
+
// ../cli/src/commands/_shared/scene-render.ts
|
|
449521
|
+
var scene_render_exports = {};
|
|
449522
|
+
__export(scene_render_exports, {
|
|
449523
|
+
buildRenderConfig: () => buildRenderConfig,
|
|
449524
|
+
defaultOutputPath: () => defaultOutputPath,
|
|
449525
|
+
executeSceneRender: () => executeSceneRender,
|
|
449526
|
+
qualityToCrf: () => qualityToCrf
|
|
449527
|
+
});
|
|
449528
|
+
import { mkdir as mkdir9, readFile as readFile9, stat as stat2 } from "node:fs/promises";
|
|
449529
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
449530
|
+
import { resolve as resolve20, relative as relative7, dirname as dirname16, basename as basename5 } from "node:path";
|
|
449531
|
+
function qualityToCrf(quality = "standard") {
|
|
449532
|
+
return quality === "draft" ? 28 : quality === "high" ? 18 : 23;
|
|
449533
|
+
}
|
|
449534
|
+
function defaultOutputPath(opts) {
|
|
449535
|
+
const fmt = opts.format ?? "mp4";
|
|
449536
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
449537
|
+
const stamp = now.toISOString().replace(/[:T]/g, "-").replace(/\..+$/, "");
|
|
449538
|
+
const name = (opts.projectName ?? basename5(resolve20(opts.projectDir))) || "scene";
|
|
449539
|
+
return resolve20(opts.projectDir, "renders", `${name}-${stamp}.${fmt}`);
|
|
449540
|
+
}
|
|
449541
|
+
async function readProjectName(projectDir) {
|
|
449542
|
+
const cfgPath = resolve20(projectDir, "vibe.project.yaml");
|
|
449543
|
+
if (!existsSync27(cfgPath)) return void 0;
|
|
449544
|
+
try {
|
|
449545
|
+
const raw2 = await (await import("node:fs/promises")).readFile(cfgPath, "utf-8");
|
|
449546
|
+
const parsed = (0, import_yaml5.parse)(raw2);
|
|
449547
|
+
return parsed?.name ?? void 0;
|
|
449548
|
+
} catch {
|
|
449549
|
+
return void 0;
|
|
449550
|
+
}
|
|
449551
|
+
}
|
|
449552
|
+
function buildRenderConfig(opts) {
|
|
449553
|
+
const quality = opts.quality ?? "standard";
|
|
449554
|
+
return {
|
|
449555
|
+
fps: opts.fps ?? 30,
|
|
449556
|
+
quality,
|
|
449557
|
+
format: opts.format ?? "mp4",
|
|
449558
|
+
entryFile: opts.entryFile ?? "index.html",
|
|
449559
|
+
crf: qualityToCrf(quality),
|
|
449560
|
+
workers: opts.workers ?? 1
|
|
449561
|
+
};
|
|
449562
|
+
}
|
|
449563
|
+
async function executeSceneRender(opts = {}) {
|
|
449564
|
+
const projectDir = resolve20(opts.projectDir ?? ".");
|
|
449565
|
+
const root2 = opts.root ?? "index.html";
|
|
449566
|
+
const projectStat = await safeStat(projectDir);
|
|
449567
|
+
if (!projectStat || !projectStat.isDirectory()) {
|
|
449568
|
+
return { success: false, error: `Project directory not found: ${projectDir}` };
|
|
449569
|
+
}
|
|
449570
|
+
if (!await rootExists(projectDir, root2)) {
|
|
449571
|
+
return {
|
|
449572
|
+
success: false,
|
|
449573
|
+
error: `Root composition not found: ${resolve20(projectDir, root2)}. Run \`vibe scene init\` first.`
|
|
449392
449574
|
};
|
|
449393
|
-
|
|
449394
|
-
|
|
449395
|
-
|
|
449396
|
-
|
|
449397
|
-
|
|
449398
|
-
|
|
449399
|
-
|
|
449400
|
-
|
|
449401
|
-
|
|
449402
|
-
|
|
449403
|
-
|
|
449575
|
+
}
|
|
449576
|
+
const chrome2 = await preflightChrome();
|
|
449577
|
+
if (!chrome2.ok) {
|
|
449578
|
+
return { success: false, error: chrome2.reason };
|
|
449579
|
+
}
|
|
449580
|
+
const projectName = await readProjectName(projectDir);
|
|
449581
|
+
const outputPath = opts.output ? resolve20(projectDir, opts.output) : defaultOutputPath({ projectDir, projectName, format: opts.format });
|
|
449582
|
+
await mkdir9(dirname16(outputPath), { recursive: true });
|
|
449583
|
+
const config4 = buildRenderConfig({
|
|
449584
|
+
fps: opts.fps,
|
|
449585
|
+
quality: opts.quality,
|
|
449586
|
+
format: opts.format,
|
|
449587
|
+
workers: opts.workers,
|
|
449588
|
+
entryFile: root2
|
|
449589
|
+
});
|
|
449590
|
+
const job = createRenderJob(config4);
|
|
449591
|
+
const start = Date.now();
|
|
449592
|
+
try {
|
|
449593
|
+
await executeRenderJob(
|
|
449594
|
+
job,
|
|
449595
|
+
projectDir,
|
|
449596
|
+
outputPath,
|
|
449597
|
+
(j, msg) => opts.onProgress?.(j.progress, j.currentStage ?? msg),
|
|
449598
|
+
opts.signal
|
|
449599
|
+
);
|
|
449600
|
+
} catch (err) {
|
|
449601
|
+
return {
|
|
449602
|
+
success: false,
|
|
449603
|
+
error: err instanceof Error ? err.message : String(err)
|
|
449404
449604
|
};
|
|
449405
449605
|
}
|
|
449606
|
+
let audioCount = 0;
|
|
449607
|
+
let audioMuxApplied = false;
|
|
449608
|
+
let audioMuxWarning;
|
|
449609
|
+
try {
|
|
449610
|
+
opts.onProgress?.(0.95, "Mixing audio");
|
|
449611
|
+
const rootHtml = await readFile9(resolve20(projectDir, root2), "utf-8");
|
|
449612
|
+
const audios = await scanSceneAudio({ projectDir, rootHtml });
|
|
449613
|
+
audioCount = audios.length;
|
|
449614
|
+
if (audios.length > 0) {
|
|
449615
|
+
const videoDuration = job.totalFrames && config4.fps ? job.totalFrames / config4.fps : void 0;
|
|
449616
|
+
const mux = await muxAudioIntoVideo({
|
|
449617
|
+
videoPath: outputPath,
|
|
449618
|
+
audios,
|
|
449619
|
+
format: config4.format ?? "mp4",
|
|
449620
|
+
videoDuration,
|
|
449621
|
+
onProgress: (line) => {
|
|
449622
|
+
if (line) opts.onProgress?.(0.97, line);
|
|
449623
|
+
}
|
|
449624
|
+
});
|
|
449625
|
+
if (mux.success) {
|
|
449626
|
+
audioMuxApplied = true;
|
|
449627
|
+
} else {
|
|
449628
|
+
audioMuxWarning = mux.error;
|
|
449629
|
+
}
|
|
449630
|
+
}
|
|
449631
|
+
} catch (err) {
|
|
449632
|
+
audioMuxWarning = err instanceof Error ? err.message : String(err);
|
|
449633
|
+
}
|
|
449634
|
+
return {
|
|
449635
|
+
success: true,
|
|
449636
|
+
outputPath: relative7(process.cwd(), outputPath) || outputPath,
|
|
449637
|
+
durationMs: Date.now() - start,
|
|
449638
|
+
framesRendered: job.framesRendered,
|
|
449639
|
+
totalFrames: job.totalFrames,
|
|
449640
|
+
fps: config4.fps,
|
|
449641
|
+
quality: config4.quality,
|
|
449642
|
+
format: config4.format,
|
|
449643
|
+
audioCount,
|
|
449644
|
+
audioMuxApplied,
|
|
449645
|
+
audioMuxWarning
|
|
449646
|
+
};
|
|
449647
|
+
}
|
|
449648
|
+
async function safeStat(p) {
|
|
449649
|
+
try {
|
|
449650
|
+
return await stat2(p);
|
|
449651
|
+
} catch {
|
|
449652
|
+
return null;
|
|
449653
|
+
}
|
|
449654
|
+
}
|
|
449655
|
+
var import_yaml5;
|
|
449656
|
+
var init_scene_render = __esm({
|
|
449657
|
+
"../cli/src/commands/_shared/scene-render.ts"() {
|
|
449658
|
+
"use strict";
|
|
449659
|
+
import_yaml5 = __toESM(require_dist(), 1);
|
|
449660
|
+
init_dist2();
|
|
449661
|
+
init_chrome3();
|
|
449662
|
+
init_scene_lint();
|
|
449663
|
+
init_scene_audio_scan();
|
|
449664
|
+
init_scene_audio_mux();
|
|
449665
|
+
}
|
|
449406
449666
|
});
|
|
449407
449667
|
|
|
449408
449668
|
// ../cli/src/commands/_shared/scene-build.ts
|
|
@@ -449411,23 +449671,23 @@ __export(scene_build_exports, {
|
|
|
449411
449671
|
executeSceneBuild: () => executeSceneBuild,
|
|
449412
449672
|
resolveSceneBuildMode: () => resolveSceneBuildMode
|
|
449413
449673
|
});
|
|
449414
|
-
import { existsSync as
|
|
449415
|
-
import { mkdir as
|
|
449416
|
-
import { dirname as
|
|
449674
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
449675
|
+
import { mkdir as mkdir10, readFile as readFile10, writeFile as writeFile10 } from "node:fs/promises";
|
|
449676
|
+
import { dirname as dirname17, join as join26, resolve as resolve21 } from "node:path";
|
|
449417
449677
|
async function executeSceneBuild(opts) {
|
|
449418
449678
|
const startedAt = Date.now();
|
|
449419
|
-
const projectDir =
|
|
449679
|
+
const projectDir = resolve21(opts.projectDir);
|
|
449420
449680
|
const onProgress = opts.onProgress ?? (() => {
|
|
449421
449681
|
});
|
|
449422
449682
|
const mode = resolveSceneBuildMode(opts);
|
|
449423
|
-
const storyboardPath =
|
|
449424
|
-
if (!
|
|
449683
|
+
const storyboardPath = join26(projectDir, "STORYBOARD.md");
|
|
449684
|
+
if (!existsSync28(storyboardPath)) {
|
|
449425
449685
|
return failBeforePrimitives(
|
|
449426
449686
|
`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`,
|
|
449427
449687
|
startedAt
|
|
449428
449688
|
);
|
|
449429
449689
|
}
|
|
449430
|
-
const storyboardMd = await
|
|
449690
|
+
const storyboardMd = await readFile10(storyboardPath, "utf-8");
|
|
449431
449691
|
const parsed = parseStoryboard(storyboardMd);
|
|
449432
449692
|
if (parsed.beats.length === 0) {
|
|
449433
449693
|
return failBeforePrimitives(
|
|
@@ -449455,9 +449715,9 @@ async function executeSceneBuild(opts) {
|
|
|
449455
449715
|
);
|
|
449456
449716
|
let composeData;
|
|
449457
449717
|
if (mode === "agent") {
|
|
449458
|
-
const compositionsDir =
|
|
449718
|
+
const compositionsDir = join26(projectDir, "compositions");
|
|
449459
449719
|
const missingBeats = parsed.beats.filter(
|
|
449460
|
-
(b) => !
|
|
449720
|
+
(b) => !existsSync28(join26(compositionsDir, `scene-${b.id}.html`))
|
|
449461
449721
|
);
|
|
449462
449722
|
if (missingBeats.length > 0) {
|
|
449463
449723
|
const plan = await getComposePrompts({ projectDir });
|
|
@@ -449505,7 +449765,7 @@ async function executeSceneBuild(opts) {
|
|
|
449505
449765
|
}
|
|
449506
449766
|
composeData = composeResult.data;
|
|
449507
449767
|
}
|
|
449508
|
-
if (!
|
|
449768
|
+
if (!existsSync28(join26(projectDir, "index.html"))) {
|
|
449509
449769
|
await scaffoldSceneProject({
|
|
449510
449770
|
dir: projectDir,
|
|
449511
449771
|
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
@@ -449565,7 +449825,7 @@ async function dispatchNarration(beat, ctx) {
|
|
|
449565
449825
|
if (!text) return { status: "no-cue" };
|
|
449566
449826
|
for (const ext of ["mp3", "wav"]) {
|
|
449567
449827
|
const rel2 = `assets/narration-${beat.id}.${ext}`;
|
|
449568
|
-
if (
|
|
449828
|
+
if (existsSync28(join26(ctx.projectDir, rel2)) && !ctx.force) {
|
|
449569
449829
|
ctx.onProgress({ type: "narration-cached", beatId: beat.id, path: rel2 });
|
|
449570
449830
|
return { status: "cached", path: rel2 };
|
|
449571
449831
|
}
|
|
@@ -449585,9 +449845,9 @@ async function dispatchNarration(beat, ctx) {
|
|
|
449585
449845
|
return { status: "failed", error };
|
|
449586
449846
|
}
|
|
449587
449847
|
const rel = `assets/narration-${beat.id}.${resolution.audioExtension}`;
|
|
449588
|
-
const abs =
|
|
449589
|
-
await
|
|
449590
|
-
await
|
|
449848
|
+
const abs = join26(ctx.projectDir, rel);
|
|
449849
|
+
await mkdir10(dirname17(abs), { recursive: true });
|
|
449850
|
+
await writeFile10(abs, result.audioBuffer);
|
|
449591
449851
|
ctx.onProgress({
|
|
449592
449852
|
type: "narration-generated",
|
|
449593
449853
|
beatId: beat.id,
|
|
@@ -449605,8 +449865,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
449605
449865
|
return { status: "failed", error };
|
|
449606
449866
|
}
|
|
449607
449867
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
449608
|
-
const abs =
|
|
449609
|
-
if (
|
|
449868
|
+
const abs = join26(ctx.projectDir, rel);
|
|
449869
|
+
if (existsSync28(abs) && !ctx.force) {
|
|
449610
449870
|
ctx.onProgress({ type: "backdrop-cached", beatId: beat.id, path: rel });
|
|
449611
449871
|
return { status: "cached", path: rel };
|
|
449612
449872
|
}
|
|
@@ -449629,8 +449889,8 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
449629
449889
|
ctx.onProgress({ type: "backdrop-failed", beatId: beat.id, error });
|
|
449630
449890
|
return { status: "failed", error };
|
|
449631
449891
|
}
|
|
449632
|
-
await
|
|
449633
|
-
await
|
|
449892
|
+
await mkdir10(dirname17(abs), { recursive: true });
|
|
449893
|
+
await writeFile10(abs, Buffer.from(result.images[0].base64, "base64"));
|
|
449634
449894
|
ctx.onProgress({
|
|
449635
449895
|
type: "backdrop-generated",
|
|
449636
449896
|
beatId: beat.id,
|
|
@@ -449640,15 +449900,15 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
449640
449900
|
return { status: "generated", path: rel };
|
|
449641
449901
|
}
|
|
449642
449902
|
function loadSceneBuildEnv(projectDir) {
|
|
449643
|
-
(0, import_dotenv2.config)({ path:
|
|
449644
|
-
(0, import_dotenv2.config)({ path:
|
|
449903
|
+
(0, import_dotenv2.config)({ path: join26(projectDir, ".env"), quiet: true });
|
|
449904
|
+
(0, import_dotenv2.config)({ path: resolve21(process.cwd(), ".env"), quiet: true });
|
|
449645
449905
|
let dir = process.cwd();
|
|
449646
|
-
while (dir !==
|
|
449647
|
-
if (
|
|
449648
|
-
(0, import_dotenv2.config)({ path:
|
|
449906
|
+
while (dir !== dirname17(dir)) {
|
|
449907
|
+
if (existsSync28(join26(dir, "pnpm-workspace.yaml"))) {
|
|
449908
|
+
(0, import_dotenv2.config)({ path: join26(dir, ".env"), quiet: true });
|
|
449649
449909
|
return;
|
|
449650
449910
|
}
|
|
449651
|
-
dir =
|
|
449911
|
+
dir = dirname17(dir);
|
|
449652
449912
|
}
|
|
449653
449913
|
}
|
|
449654
449914
|
async function skipped(kind, beatId, reason, ctx) {
|
|
@@ -449674,9 +449934,9 @@ function resolveSceneBuildMode(opts) {
|
|
|
449674
449934
|
return detectedAgentHosts().length > 0 ? "agent" : "batch";
|
|
449675
449935
|
}
|
|
449676
449936
|
async function syncRootClipReferences(beats, projectDir, outcomes) {
|
|
449677
|
-
const rootPath =
|
|
449678
|
-
if (!
|
|
449679
|
-
const html = await
|
|
449937
|
+
const rootPath = join26(projectDir, "index.html");
|
|
449938
|
+
if (!existsSync28(rootPath)) return;
|
|
449939
|
+
const html = await readFile10(rootPath, "utf-8");
|
|
449680
449940
|
let cursor = 0;
|
|
449681
449941
|
const clipLines = [];
|
|
449682
449942
|
const audioLines = [];
|
|
@@ -449720,14 +449980,14 @@ ${block}
|
|
|
449720
449980
|
`$1${totalDuration}$3`
|
|
449721
449981
|
);
|
|
449722
449982
|
if (next !== html) {
|
|
449723
|
-
await
|
|
449983
|
+
await writeFile10(rootPath, next, "utf-8");
|
|
449724
449984
|
}
|
|
449725
449985
|
}
|
|
449726
449986
|
async function resolveBeatDuration(opts) {
|
|
449727
449987
|
const storyboardMin = opts.beatDuration ?? 3;
|
|
449728
449988
|
if (!opts.narrationPath) return Number(storyboardMin.toFixed(2));
|
|
449729
449989
|
try {
|
|
449730
|
-
const audioDuration = await getAudioDuration(
|
|
449990
|
+
const audioDuration = await getAudioDuration(join26(opts.projectDir, opts.narrationPath));
|
|
449731
449991
|
return Number(Math.max(storyboardMin, audioDuration + 0.5).toFixed(2));
|
|
449732
449992
|
} catch {
|
|
449733
449993
|
return Number(storyboardMin.toFixed(2));
|
|
@@ -449750,269 +450010,6 @@ var init_scene_build = __esm({
|
|
|
449750
450010
|
}
|
|
449751
450011
|
});
|
|
449752
450012
|
|
|
449753
|
-
// ../cli/src/commands/output.ts
|
|
449754
|
-
var output_exports = {};
|
|
449755
|
-
__export(output_exports, {
|
|
449756
|
-
COST_ESTIMATES: () => COST_ESTIMATES,
|
|
449757
|
-
ExitCode: () => ExitCode,
|
|
449758
|
-
_resetDeprecationMemoryForTesting: () => _resetDeprecationMemoryForTesting,
|
|
449759
|
-
apiError: () => apiError,
|
|
449760
|
-
authError: () => authError,
|
|
449761
|
-
emitDeprecationWarning: () => emitDeprecationWarning,
|
|
449762
|
-
exitWithError: () => exitWithError,
|
|
449763
|
-
generalError: () => generalError,
|
|
449764
|
-
isJsonMode: () => isJsonMode,
|
|
449765
|
-
isQuietMode: () => isQuietMode,
|
|
449766
|
-
log: () => log,
|
|
449767
|
-
networkError: () => networkError,
|
|
449768
|
-
notFoundError: () => notFoundError,
|
|
449769
|
-
outputError: () => outputError,
|
|
449770
|
-
outputResult: () => outputResult,
|
|
449771
|
-
outputSuccess: () => outputSuccess,
|
|
449772
|
-
spinner: () => spinner,
|
|
449773
|
-
suggestNext: () => suggestNext,
|
|
449774
|
-
usageError: () => usageError
|
|
449775
|
-
});
|
|
449776
|
-
function usageError(msg, suggestion) {
|
|
449777
|
-
return { success: false, error: msg, code: "USAGE_ERROR", exitCode: 2 /* USAGE */, suggestion, retryable: false };
|
|
449778
|
-
}
|
|
449779
|
-
function authError(envVar, provider) {
|
|
449780
|
-
return {
|
|
449781
|
-
success: false,
|
|
449782
|
-
error: `${provider} API key required.`,
|
|
449783
|
-
code: "API_KEY_MISSING",
|
|
449784
|
-
exitCode: 4 /* AUTH */,
|
|
449785
|
-
suggestion: `Set ${envVar} in .env, or run: vibe setup`,
|
|
449786
|
-
retryable: false
|
|
449787
|
-
};
|
|
449788
|
-
}
|
|
449789
|
-
function apiError(msg, retryable = false) {
|
|
449790
|
-
for (const hint of PROVIDER_ERROR_HINTS) {
|
|
449791
|
-
if (hint.pattern.test(msg)) {
|
|
449792
|
-
return { success: false, error: msg, code: "API_ERROR", exitCode: 5 /* API_ERROR */, suggestion: hint.suggestion, retryable: hint.retryable };
|
|
449793
|
-
}
|
|
449794
|
-
}
|
|
449795
|
-
return { success: false, error: msg, code: "API_ERROR", exitCode: 5 /* API_ERROR */, suggestion: retryable ? "Retry the command." : void 0, retryable };
|
|
449796
|
-
}
|
|
449797
|
-
function notFoundError(path14) {
|
|
449798
|
-
return { success: false, error: `File not found: ${path14}`, code: "NOT_FOUND", exitCode: 3 /* NOT_FOUND */, retryable: false };
|
|
449799
|
-
}
|
|
449800
|
-
function networkError(msg) {
|
|
449801
|
-
return { success: false, error: msg, code: "NETWORK_ERROR", exitCode: 6 /* NETWORK */, suggestion: "Check your internet connection and retry.", retryable: true };
|
|
449802
|
-
}
|
|
449803
|
-
function generalError(msg, suggestion) {
|
|
449804
|
-
return { success: false, error: msg, code: "ERROR", exitCode: 1 /* GENERAL */, suggestion, retryable: false };
|
|
449805
|
-
}
|
|
449806
|
-
function exitWithError(err) {
|
|
449807
|
-
if (isJsonMode()) {
|
|
449808
|
-
console.error(JSON.stringify(err, null, 2));
|
|
449809
|
-
} else {
|
|
449810
|
-
console.error(source_default.red(`
|
|
449811
|
-
${err.error}`));
|
|
449812
|
-
if (err.suggestion) {
|
|
449813
|
-
console.error(source_default.dim(` ${err.suggestion}`));
|
|
449814
|
-
}
|
|
449815
|
-
console.error();
|
|
449816
|
-
}
|
|
449817
|
-
process.exit(err.exitCode);
|
|
449818
|
-
}
|
|
449819
|
-
function isJsonMode() {
|
|
449820
|
-
return process.env.VIBE_JSON_OUTPUT === "1";
|
|
449821
|
-
}
|
|
449822
|
-
function isQuietMode() {
|
|
449823
|
-
return process.env.VIBE_QUIET_OUTPUT === "1";
|
|
449824
|
-
}
|
|
449825
|
-
function formatCost(min, max, unit) {
|
|
449826
|
-
if (min === 0 && max === 0) return "Free";
|
|
449827
|
-
if (min === max) return `~$${min.toFixed(2)} ${unit}`;
|
|
449828
|
-
return `~$${min.toFixed(2)}-$${max.toFixed(2)} ${unit}`;
|
|
449829
|
-
}
|
|
449830
|
-
function lookupCostEstimateUpperBound(command3) {
|
|
449831
|
-
return COST_ESTIMATES[command3]?.max ?? 0;
|
|
449832
|
-
}
|
|
449833
|
-
function outputSuccess(opts) {
|
|
449834
|
-
const elapsedMs = Math.max(0, Date.now() - opts.startedAt);
|
|
449835
|
-
const costUsd = opts.costUsd ?? (opts.dryRun ? lookupCostEstimateUpperBound(opts.command) : 0);
|
|
449836
|
-
const envelope = {
|
|
449837
|
-
command: opts.command,
|
|
449838
|
-
...opts.dryRun ? { dryRun: true } : {},
|
|
449839
|
-
elapsedMs,
|
|
449840
|
-
costUsd,
|
|
449841
|
-
warnings: opts.warnings ?? [],
|
|
449842
|
-
data: opts.data
|
|
449843
|
-
};
|
|
449844
|
-
if (isJsonMode()) {
|
|
449845
|
-
const fields = process.env.VIBE_OUTPUT_FIELDS;
|
|
449846
|
-
if (fields) {
|
|
449847
|
-
const keys2 = fields.split(",").map((k) => k.trim());
|
|
449848
|
-
const data = opts.data;
|
|
449849
|
-
const filteredData = {};
|
|
449850
|
-
for (const key2 of keys2) {
|
|
449851
|
-
if (key2 in data) filteredData[key2] = data[key2];
|
|
449852
|
-
}
|
|
449853
|
-
envelope.data = filteredData;
|
|
449854
|
-
}
|
|
449855
|
-
console.log(JSON.stringify(envelope, null, 2));
|
|
449856
|
-
return;
|
|
449857
|
-
}
|
|
449858
|
-
if (isQuietMode()) {
|
|
449859
|
-
const data = opts.data;
|
|
449860
|
-
const primary = data.outputPath ?? data.output ?? data.path ?? data.url ?? data.id;
|
|
449861
|
-
if (primary !== void 0) console.log(String(primary));
|
|
449862
|
-
}
|
|
449863
|
-
}
|
|
449864
|
-
function outputResult(result) {
|
|
449865
|
-
if (result.dryRun && result.command && typeof result.command === "string") {
|
|
449866
|
-
const cost = COST_ESTIMATES[result.command];
|
|
449867
|
-
if (cost) {
|
|
449868
|
-
result.estimatedCost = formatCost(cost.min, cost.max, cost.unit);
|
|
449869
|
-
}
|
|
449870
|
-
}
|
|
449871
|
-
if (isJsonMode()) {
|
|
449872
|
-
const fields = process.env.VIBE_OUTPUT_FIELDS;
|
|
449873
|
-
if (fields) {
|
|
449874
|
-
const keys2 = fields.split(",").map((k) => k.trim());
|
|
449875
|
-
const filtered = {};
|
|
449876
|
-
for (const key2 of keys2) {
|
|
449877
|
-
if (key2 in result) filtered[key2] = result[key2];
|
|
449878
|
-
}
|
|
449879
|
-
if ("success" in result) filtered.success = result.success;
|
|
449880
|
-
console.log(JSON.stringify(filtered, null, 2));
|
|
449881
|
-
} else {
|
|
449882
|
-
console.log(JSON.stringify(result, null, 2));
|
|
449883
|
-
}
|
|
449884
|
-
} else if (isQuietMode()) {
|
|
449885
|
-
const primary = result.output ?? result.path ?? result.url ?? result.id ?? result.result;
|
|
449886
|
-
if (primary !== void 0) console.log(String(primary));
|
|
449887
|
-
}
|
|
449888
|
-
}
|
|
449889
|
-
function log(...args) {
|
|
449890
|
-
if (!isJsonMode() && !isQuietMode()) {
|
|
449891
|
-
console.log(...args);
|
|
449892
|
-
}
|
|
449893
|
-
}
|
|
449894
|
-
function spinner(text) {
|
|
449895
|
-
if (isJsonMode() || isQuietMode()) {
|
|
449896
|
-
return ora({ text, isSilent: true });
|
|
449897
|
-
}
|
|
449898
|
-
return ora(text);
|
|
449899
|
-
}
|
|
449900
|
-
function suggestNext(tip) {
|
|
449901
|
-
if (!isJsonMode() && !isQuietMode() && process.stdout.isTTY) {
|
|
449902
|
-
console.log(source_default.dim(`
|
|
449903
|
-
Tip: ${tip}`));
|
|
449904
|
-
}
|
|
449905
|
-
}
|
|
449906
|
-
function emitDeprecationWarning(oldName, newName, removeIn) {
|
|
449907
|
-
if (isJsonMode() || isQuietMode()) return;
|
|
449908
|
-
if (!process.stderr.isTTY) return;
|
|
449909
|
-
const key2 = `${oldName}\u2192${newName}`;
|
|
449910
|
-
if (_seenDeprecations.has(key2)) return;
|
|
449911
|
-
_seenDeprecations.add(key2);
|
|
449912
|
-
process.stderr.write(
|
|
449913
|
-
source_default.yellow(`[deprecated] '${oldName}' is deprecated; use '${newName}' instead. Alias will be removed in ${removeIn}.`) + "\n"
|
|
449914
|
-
);
|
|
449915
|
-
}
|
|
449916
|
-
function _resetDeprecationMemoryForTesting() {
|
|
449917
|
-
_seenDeprecations.clear();
|
|
449918
|
-
}
|
|
449919
|
-
function outputError(error, details) {
|
|
449920
|
-
if (isJsonMode()) {
|
|
449921
|
-
console.error(JSON.stringify({ success: false, error, ...details }, null, 2));
|
|
449922
|
-
} else {
|
|
449923
|
-
console.error(error);
|
|
449924
|
-
}
|
|
449925
|
-
}
|
|
449926
|
-
var ExitCode, PROVIDER_ERROR_HINTS, COST_ESTIMATES, _seenDeprecations;
|
|
449927
|
-
var init_output = __esm({
|
|
449928
|
-
"../cli/src/commands/output.ts"() {
|
|
449929
|
-
"use strict";
|
|
449930
|
-
init_source();
|
|
449931
|
-
init_ora();
|
|
449932
|
-
ExitCode = /* @__PURE__ */ ((ExitCode2) => {
|
|
449933
|
-
ExitCode2[ExitCode2["SUCCESS"] = 0] = "SUCCESS";
|
|
449934
|
-
ExitCode2[ExitCode2["GENERAL"] = 1] = "GENERAL";
|
|
449935
|
-
ExitCode2[ExitCode2["USAGE"] = 2] = "USAGE";
|
|
449936
|
-
ExitCode2[ExitCode2["NOT_FOUND"] = 3] = "NOT_FOUND";
|
|
449937
|
-
ExitCode2[ExitCode2["AUTH"] = 4] = "AUTH";
|
|
449938
|
-
ExitCode2[ExitCode2["API_ERROR"] = 5] = "API_ERROR";
|
|
449939
|
-
ExitCode2[ExitCode2["NETWORK"] = 6] = "NETWORK";
|
|
449940
|
-
return ExitCode2;
|
|
449941
|
-
})(ExitCode || {});
|
|
449942
|
-
PROVIDER_ERROR_HINTS = [
|
|
449943
|
-
// Billing (must precede the 429 rate-limit pattern)
|
|
449944
|
-
{ pattern: /402|payment.*required|billing|INSUFFICIENT_BALANCE|insufficient.*(credit|funds|balance)|balance.*(not.*enough|insufficient)|credits?.*exhausted|account.*balance/i, suggestion: "Account balance or credits exhausted. Top up at the provider dashboard, or try -p <other-provider>.", retryable: false },
|
|
449945
|
-
// Rate limits / quota
|
|
449946
|
-
{ pattern: /429|rate.?limit|too many requests/i, suggestion: "Rate limited. Wait 30-60 seconds and retry, or check your plan's rate limits.", retryable: true },
|
|
449947
|
-
{ pattern: /RESOURCE_EXHAUSTED|quota.*exceeded|requests.*per.*(minute|day)/i, suggestion: "Quota exceeded. Wait for the quota window to reset, or upgrade your plan. Consider -p <other-provider> to use a different provider.", retryable: true },
|
|
449948
|
-
// Auth
|
|
449949
|
-
{ pattern: /401|unauthorized|(invalid|incorrect).*api.?key|invalid_api_key|authentication.*(failed|error)|missing.*api.?key|did not start with 'key_'/i, suggestion: "API key is invalid or expired. Run 'vibe setup' to update, or check the key at the provider's dashboard.", retryable: false },
|
|
449950
|
-
{ pattern: /403|forbidden|permission.*denied/i, suggestion: "Access denied. Your API key may lack required permissions, or the feature requires a paid plan.", retryable: false },
|
|
449951
|
-
// Server
|
|
449952
|
-
{ pattern: /500|internal.*error|server.*error/i, suggestion: "Provider server error. Retry in a few minutes.", retryable: true },
|
|
449953
|
-
{ pattern: /503|service.*unavailable|overloaded|overloaded_error/i, suggestion: "Provider is temporarily overloaded. Retry in 1-2 minutes, or switch provider with -p.", retryable: true },
|
|
449954
|
-
{ pattern: /timeout|timed?\s*out|ETIMEDOUT|ECONNRESET|socket.*hang.?up/i, suggestion: "Request timed out. The provider may be slow. Retry, or try a different provider with -p flag.", retryable: true },
|
|
449955
|
-
// Content policy
|
|
449956
|
-
{ pattern: /content.*(policy|filter)|safety|moderation|blocked.*(by|due)|content_policy_violation|restricted.*content/i, suggestion: "Content was blocked by the provider's safety filter. Rephrase your prompt to avoid sensitive terms.", retryable: false },
|
|
449957
|
-
// Model / context
|
|
449958
|
-
{ pattern: /context_length_exceeded|maximum.*context.*length|token.*limit.*exceeded|prompt.*too.*long/i, suggestion: "Input exceeds the model's context window. Shorten the prompt, or use a model with larger context (run 'vibe schema <command>' for options).", retryable: false },
|
|
449959
|
-
{ pattern: /model.*not.*found|invalid.*model|unknown.*model|model_not_found/i, suggestion: "The specified model is unavailable. Check 'vibe schema <command>' for valid model options.", retryable: false },
|
|
449960
|
-
// Provider-specific
|
|
449961
|
-
{ pattern: /voice.*not.*found|voice_not_found|invalid.*voice.?id/i, suggestion: "Voice ID not found. Run 'vibe audio list-voices' to list available voices, then pass --voice <id>.", retryable: false },
|
|
449962
|
-
{ pattern: /character.*(count|limit).*exceeded|invalid_character_count/i, suggestion: "Text exceeds the TTS provider's character limit. Shorten the text or split into chunks.", retryable: false },
|
|
449963
|
-
{ pattern: /invalid.*aspect.*ratio|unsupported.*aspect.*ratio|unsupported.*resolution/i, suggestion: "This aspect ratio or resolution isn't supported by the chosen model. Check 'vibe schema <command>' for supported values.", retryable: false },
|
|
449964
|
-
{ pattern: /invalid.*file.*format|unsupported.*(format|codec)|unsupported.*media.?type/i, suggestion: "Input file format not supported. Convert to MP4/MP3/PNG first with 'vibe export' or 'ffmpeg'.", retryable: false },
|
|
449965
|
-
{ pattern: /region.*(restriction|not.*supported|unavailable)|geo.?blocked/i, suggestion: "Provider unavailable in your region. Try -p <other-provider>, or use a supported region.", retryable: false },
|
|
449966
|
-
{ pattern: /task.*(not.*found|expired)|job.*(not.*found|expired)/i, suggestion: "The async task expired or was never created. Re-run the command to start a new task.", retryable: true }
|
|
449967
|
-
];
|
|
449968
|
-
COST_ESTIMATES = {
|
|
449969
|
-
// Free
|
|
449970
|
-
"detect scenes": { min: 0, max: 0, unit: "free" },
|
|
449971
|
-
"detect silence": { min: 0, max: 0, unit: "free" },
|
|
449972
|
-
"detect beats": { min: 0, max: 0, unit: "free" },
|
|
449973
|
-
"edit silence-cut": { min: 0, max: 0, unit: "free" },
|
|
449974
|
-
"edit fade": { min: 0, max: 0, unit: "free" },
|
|
449975
|
-
"edit noise-reduce": { min: 0, max: 0, unit: "free" },
|
|
449976
|
-
"edit reframe": { min: 0, max: 0, unit: "free" },
|
|
449977
|
-
"edit interpolate": { min: 0, max: 0, unit: "free" },
|
|
449978
|
-
"edit upscale-video": { min: 0, max: 0, unit: "free" },
|
|
449979
|
-
// Low
|
|
449980
|
-
"analyze media": { min: 0.01, max: 0.05, unit: "per call" },
|
|
449981
|
-
"analyze video": { min: 0.01, max: 0.1, unit: "per video" },
|
|
449982
|
-
"analyze review": { min: 0.01, max: 0.1, unit: "per video" },
|
|
449983
|
-
"generate image": { min: 0.01, max: 0.07, unit: "per image" },
|
|
449984
|
-
"generate thumbnail": { min: 0.01, max: 0.05, unit: "per image" },
|
|
449985
|
-
"generate storyboard": { min: 0.01, max: 0.05, unit: "per call" },
|
|
449986
|
-
"ai transcribe": { min: 0.01, max: 0.1, unit: "per minute" },
|
|
449987
|
-
"audio transcribe": { min: 0.01, max: 0.1, unit: "per minute" },
|
|
449988
|
-
"edit caption": { min: 0.01, max: 0.1, unit: "per video" },
|
|
449989
|
-
"edit jump-cut": { min: 0.01, max: 0.1, unit: "per video" },
|
|
449990
|
-
"edit translate-srt": { min: 0.01, max: 0.05, unit: "per file" },
|
|
449991
|
-
"edit animated-caption": { min: 0.01, max: 0.1, unit: "per video" },
|
|
449992
|
-
// Medium
|
|
449993
|
-
"generate speech": { min: 0.05, max: 0.3, unit: "per request" },
|
|
449994
|
-
"generate sound-effect": { min: 0.05, max: 0.2, unit: "per request" },
|
|
449995
|
-
"generate music": { min: 0.05, max: 0.5, unit: "per request" },
|
|
449996
|
-
"generate motion": { min: 0.01, max: 0.1, unit: "per generation" },
|
|
449997
|
-
"edit grade": { min: 0.01, max: 0.05, unit: "per video" },
|
|
449998
|
-
"edit speed-ramp": { min: 0.05, max: 0.15, unit: "per video" },
|
|
449999
|
-
"edit text-overlay": { min: 0, max: 0.05, unit: "per video" },
|
|
450000
|
-
// High
|
|
450001
|
-
"generate video": { min: 0.5, max: 5, unit: "per video" },
|
|
450002
|
-
"edit image": { min: 0.05, max: 0.5, unit: "per edit" },
|
|
450003
|
-
// Very High
|
|
450004
|
-
"pipeline highlights": { min: 0.05, max: 1, unit: "per analysis" },
|
|
450005
|
-
"pipeline auto-shorts": { min: 0.1, max: 2, unit: "per batch" },
|
|
450006
|
-
"pipeline animated-caption": { min: 0.01, max: 0.1, unit: "per video" },
|
|
450007
|
-
"pipeline regenerate-scene": { min: 0.5, max: 5, unit: "per scene" },
|
|
450008
|
-
// Scene composition (v0.59+) — per-beat composer call ≈ $0.06 (PR #111
|
|
450009
|
-
// pre-flight). Range covers a 1-beat preview to a 10-beat long-form.
|
|
450010
|
-
"compose scenes with skills": { min: 0.05, max: 1.5, unit: "per pipeline" }
|
|
450011
|
-
};
|
|
450012
|
-
_seenDeprecations = /* @__PURE__ */ new Set();
|
|
450013
|
-
}
|
|
450014
|
-
});
|
|
450015
|
-
|
|
450016
450013
|
// ../cli/src/utils/subtitle.ts
|
|
450017
450014
|
function detectFormat(outputPath, explicitFormat) {
|
|
450018
450015
|
if (explicitFormat) {
|
|
@@ -450120,7 +450117,7 @@ __export(ai_audio_exports, {
|
|
|
450120
450117
|
executeTranscribe: () => executeTranscribe,
|
|
450121
450118
|
executeVoiceClone: () => executeVoiceClone
|
|
450122
450119
|
});
|
|
450123
|
-
import { resolve as resolve24, dirname as dirname18, basename as
|
|
450120
|
+
import { resolve as resolve24, dirname as dirname18, basename as basename6, extname as extname5 } from "node:path";
|
|
450124
450121
|
import { readFile as readFile11, writeFile as writeFile11 } from "node:fs/promises";
|
|
450125
450122
|
import { existsSync as existsSync29 } from "node:fs";
|
|
450126
450123
|
async function executeTranscribe(options) {
|
|
@@ -450303,7 +450300,7 @@ ${segmentTexts}`,
|
|
|
450303
450300
|
}
|
|
450304
450301
|
const combinedBuffer = Buffer.concat(dubbedBuffers);
|
|
450305
450302
|
const outputExt = isVideo ? ".mp3" : extname5(absPath);
|
|
450306
|
-
const defaultOutputPath2 = resolve24(dirname18(absPath), `${
|
|
450303
|
+
const defaultOutputPath2 = resolve24(dirname18(absPath), `${basename6(absPath, extname5(absPath))}-${language}${outputExt}`);
|
|
450307
450304
|
const finalOutputPath = resolve24(process.cwd(), output3 || defaultOutputPath2);
|
|
450308
450305
|
await writeFile11(finalOutputPath, combinedBuffer);
|
|
450309
450306
|
if (isVideo && audioPath !== absPath) {
|
|
@@ -450340,7 +450337,7 @@ async function executeDuck(options) {
|
|
|
450340
450337
|
const absVoicePath = resolve24(process.cwd(), voicePath);
|
|
450341
450338
|
if (!existsSync29(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
450342
450339
|
if (!existsSync29(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
450343
|
-
const defaultOutput = resolve24(dirname18(absMusicPath), `${
|
|
450340
|
+
const defaultOutput = resolve24(dirname18(absMusicPath), `${basename6(absMusicPath, extname5(absMusicPath))}-ducked${extname5(absMusicPath)}`);
|
|
450344
450341
|
const outputPath = resolve24(process.cwd(), output3 || defaultOutput);
|
|
450345
450342
|
const filter4 = `[1:a]asplit=2[sc][mix];[0:a][sc]sidechaincompress=threshold=${threshold}dB:ratio=${ratio}:attack=${attack}:release=${release}[ducked];[ducked][mix]amix=inputs=2:duration=longest`;
|
|
450346
450343
|
await execSafe("ffmpeg", [
|
|
@@ -451626,7 +451623,7 @@ var init_remotion = __esm({
|
|
|
451626
451623
|
// ../cli/src/commands/_shared/edit/caption.ts
|
|
451627
451624
|
import { existsSync as existsSync34 } from "node:fs";
|
|
451628
451625
|
import { readFile as readFile14, writeFile as writeFile14, mkdir as mkdir13 } from "node:fs/promises";
|
|
451629
|
-
import { dirname as dirname19, basename as
|
|
451626
|
+
import { dirname as dirname19, basename as basename7, extname as extname6, join as join29 } from "node:path";
|
|
451630
451627
|
function getCaptionForceStyle(style, fontSize, fontColor, position) {
|
|
451631
451628
|
const alignment = position === "top" ? 8 : position === "center" ? 5 : 2;
|
|
451632
451629
|
const marginV = position === "center" ? 0 : 30;
|
|
@@ -451745,7 +451742,7 @@ async function executeCaption(options) {
|
|
|
451745
451742
|
const outputDir2 = dirname19(outputPath);
|
|
451746
451743
|
const outputSrtPath2 = join29(
|
|
451747
451744
|
outputDir2,
|
|
451748
|
-
|
|
451745
|
+
basename7(outputPath, extname6(outputPath)) + ".srt"
|
|
451749
451746
|
);
|
|
451750
451747
|
await writeFile14(outputSrtPath2, srtContent);
|
|
451751
451748
|
return { success: false, error: `${remotionErr}
|
|
@@ -451784,7 +451781,7 @@ SRT saved to: ${outputSrtPath2}` };
|
|
|
451784
451781
|
const outputDir2 = dirname19(outputPath);
|
|
451785
451782
|
const outputSrtPath2 = join29(
|
|
451786
451783
|
outputDir2,
|
|
451787
|
-
|
|
451784
|
+
basename7(outputPath, extname6(outputPath)) + ".srt"
|
|
451788
451785
|
);
|
|
451789
451786
|
await writeFile14(outputSrtPath2, srtContent);
|
|
451790
451787
|
return {
|
|
@@ -451797,7 +451794,7 @@ SRT saved to: ${outputSrtPath2}`
|
|
|
451797
451794
|
const outputDir = dirname19(outputPath);
|
|
451798
451795
|
const outputSrtPath = join29(
|
|
451799
451796
|
outputDir,
|
|
451800
|
-
|
|
451797
|
+
basename7(outputPath, extname6(outputPath)) + ".srt"
|
|
451801
451798
|
);
|
|
451802
451799
|
await writeFile14(outputSrtPath, srtContent);
|
|
451803
451800
|
return {
|
|
@@ -452403,7 +452400,7 @@ var init_validate = __esm({
|
|
|
452403
452400
|
});
|
|
452404
452401
|
|
|
452405
452402
|
// ../cli/src/commands/ai-edit-cli.ts
|
|
452406
|
-
import { resolve as resolve27, extname as extname7, basename as
|
|
452403
|
+
import { resolve as resolve27, extname as extname7, basename as basename8 } from "node:path";
|
|
452407
452404
|
import { existsSync as existsSync39 } from "node:fs";
|
|
452408
452405
|
function registerEditCommands(aiCommand) {
|
|
452409
452406
|
aiCommand.command("silence-cut").alias("sc").description("Remove silent segments from video (FFmpeg default, or Gemini for smart detection)").argument("<video>", "Video file path").option("-o, --output <path>", "Output file path (default: <name>-cut.<ext>)").option("-n, --noise <dB>", "Silence threshold in dB (default: -30)", "-30").option("-d, --min-duration <seconds>", "Minimum silence duration to cut (default: 0.5)", "0.5").option("--padding <seconds>", "Padding around non-silent segments (default: 0.1)", "0.1").option("--analyze-only", "Only detect silence, don't cut").option("--use-gemini", "Use Gemini Video Understanding for context-aware silence detection").option("-m, --model <model>", "Gemini model (default: flash)").option("--low-res", "Low resolution mode for longer videos (Gemini only)").option("-k, --api-key <key>", "Google API key override (or set GOOGLE_API_KEY env)").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
@@ -452428,7 +452425,7 @@ No API key needed (FFmpeg only). Use --use-gemini for smart detection (requires
|
|
|
452428
452425
|
exitWithError(generalError("FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux). Run `vibe doctor` for details."));
|
|
452429
452426
|
}
|
|
452430
452427
|
const ext = extname7(videoPath);
|
|
452431
|
-
const name =
|
|
452428
|
+
const name = basename8(videoPath, ext);
|
|
452432
452429
|
const outputPath = options.output || `${name}-cut${ext}`;
|
|
452433
452430
|
const useGemini = options.useGemini || false;
|
|
452434
452431
|
if (options.dryRun) {
|
|
@@ -452551,7 +452548,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
452551
452548
|
exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
|
|
452552
452549
|
}
|
|
452553
452550
|
const ext = extname7(videoPath);
|
|
452554
|
-
const name =
|
|
452551
|
+
const name = basename8(videoPath, ext);
|
|
452555
452552
|
const outputPath = options.output || `${name}-captioned${ext}`;
|
|
452556
452553
|
const spinner2 = ora("Starting caption process...").start();
|
|
452557
452554
|
const result = await executeCaption({
|
|
@@ -452625,7 +452622,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
452625
452622
|
return;
|
|
452626
452623
|
}
|
|
452627
452624
|
const ext = extname7(inputPath);
|
|
452628
|
-
const name =
|
|
452625
|
+
const name = basename8(inputPath, ext);
|
|
452629
452626
|
const outputPath = options.output || `${name}-denoised${ext}`;
|
|
452630
452627
|
const spinner2 = ora("Applying noise reduction...").start();
|
|
452631
452628
|
const result = await executeNoiseReduce({
|
|
@@ -452693,7 +452690,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
452693
452690
|
return;
|
|
452694
452691
|
}
|
|
452695
452692
|
const ext = extname7(videoPath);
|
|
452696
|
-
const name =
|
|
452693
|
+
const name = basename8(videoPath, ext);
|
|
452697
452694
|
const outputPath = options.output || `${name}-faded${ext}`;
|
|
452698
452695
|
const spinner2 = ora("Applying fade effects...").start();
|
|
452699
452696
|
const result = await executeFade({
|
|
@@ -452771,7 +452768,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
452771
452768
|
exitWithError(authError(envKey, providerName));
|
|
452772
452769
|
}
|
|
452773
452770
|
const ext = extname7(srtPath);
|
|
452774
|
-
const name =
|
|
452771
|
+
const name = basename8(srtPath, ext);
|
|
452775
452772
|
const outputPath = options.output || `${name}-${options.target}${ext}`;
|
|
452776
452773
|
const spinner2 = ora(`Translating to ${options.target}...`).start();
|
|
452777
452774
|
const result = await executeTranslateSrt({
|
|
@@ -452849,7 +452846,7 @@ Requires: OPENAI_API_KEY (Whisper transcription) + FFmpeg`).action(async (videoP
|
|
|
452849
452846
|
exitWithError(authError("OPENAI_API_KEY", "OpenAI"));
|
|
452850
452847
|
}
|
|
452851
452848
|
const ext = extname7(videoPath);
|
|
452852
|
-
const name =
|
|
452849
|
+
const name = basename8(videoPath, ext);
|
|
452853
452850
|
const outputPath = options.output || `${name}-jumpcut${ext}`;
|
|
452854
452851
|
const fillers = options.fillers ? options.fillers.split(",").map((f) => f.trim()) : void 0;
|
|
452855
452852
|
const spinner2 = ora("Transcribing with word-level timestamps...").start();
|
|
@@ -456838,7 +456835,7 @@ var init_music = __esm({
|
|
|
456838
456835
|
});
|
|
456839
456836
|
|
|
456840
456837
|
// ../cli/src/commands/generate/thumbnail.ts
|
|
456841
|
-
import { resolve as resolve43, dirname as dirname26, basename as
|
|
456838
|
+
import { resolve as resolve43, dirname as dirname26, basename as basename10, extname as extname9 } from "node:path";
|
|
456842
456839
|
import { existsSync as existsSync47 } from "node:fs";
|
|
456843
456840
|
import { writeFile as writeFile27, mkdir as mkdir19 } from "node:fs/promises";
|
|
456844
456841
|
function registerThumbnailCommand(parent) {
|
|
@@ -456863,7 +456860,7 @@ function registerThumbnailCommand(parent) {
|
|
|
456863
456860
|
);
|
|
456864
456861
|
}
|
|
456865
456862
|
const apiKey2 = await requireApiKey("GOOGLE_API_KEY", "Google", options.apiKey);
|
|
456866
|
-
const name =
|
|
456863
|
+
const name = basename10(options.bestFrame, extname9(options.bestFrame));
|
|
456867
456864
|
const outputPath = options.output || `${name}-thumbnail.png`;
|
|
456868
456865
|
const spinner3 = ora("Analyzing video for best frame...").start();
|
|
456869
456866
|
const result2 = await executeThumbnailBestFrame({
|
|
@@ -458928,7 +458925,7 @@ var init_dist3 = __esm({
|
|
|
458928
458925
|
|
|
458929
458926
|
// ../cli/src/commands/_shared/video-utils.ts
|
|
458930
458927
|
import { writeFile as writeFile31, unlink as unlink5, rename as rename5 } from "node:fs/promises";
|
|
458931
|
-
import { resolve as resolve47, basename as
|
|
458928
|
+
import { resolve as resolve47, basename as basename11 } from "node:path";
|
|
458932
458929
|
function sleep(ms) {
|
|
458933
458930
|
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
458934
458931
|
}
|
|
@@ -458962,7 +458959,7 @@ async function extendVideoToTarget(videoPath, targetDuration, outputDir, sceneLa
|
|
|
458962
458959
|
const actualDuration = await getVideoDuration(videoPath);
|
|
458963
458960
|
if (actualDuration >= targetDuration - 0.1) return;
|
|
458964
458961
|
const ratio = targetDuration / actualDuration;
|
|
458965
|
-
const extendedPath = resolve47(outputDir, `${
|
|
458962
|
+
const extendedPath = resolve47(outputDir, `${basename11(videoPath, ".mp4")}-extended.mp4`);
|
|
458966
458963
|
if (ratio > 1.4 && options?.kling && options?.videoId) {
|
|
458967
458964
|
try {
|
|
458968
458965
|
options.onProgress?.(`${sceneLabel}: Extending via Kling API...`);
|
|
@@ -458980,17 +458977,17 @@ async function extendVideoToTarget(videoPath, targetDuration, outputDir, sceneLa
|
|
|
458980
458977
|
if (waitResult.status === "completed" && waitResult.videoUrl) {
|
|
458981
458978
|
const extendedVideoPath = resolve47(
|
|
458982
458979
|
outputDir,
|
|
458983
|
-
`${
|
|
458980
|
+
`${basename11(videoPath, ".mp4")}-kling-ext.mp4`
|
|
458984
458981
|
);
|
|
458985
458982
|
const buffer = await downloadVideo(waitResult.videoUrl);
|
|
458986
458983
|
await writeFile31(extendedVideoPath, buffer);
|
|
458987
458984
|
const concatPath = resolve47(
|
|
458988
458985
|
outputDir,
|
|
458989
|
-
`${
|
|
458986
|
+
`${basename11(videoPath, ".mp4")}-concat.mp4`
|
|
458990
458987
|
);
|
|
458991
458988
|
const listPath = resolve47(
|
|
458992
458989
|
outputDir,
|
|
458993
|
-
`${
|
|
458990
|
+
`${basename11(videoPath, ".mp4")}-concat.txt`
|
|
458994
458991
|
);
|
|
458995
458992
|
await writeFile31(
|
|
458996
458993
|
listPath,
|
|
@@ -460521,7 +460518,7 @@ __export(detect_exports, {
|
|
|
460521
460518
|
executeDetectSilence: () => executeDetectSilence
|
|
460522
460519
|
});
|
|
460523
460520
|
import { readFile as readFile28, writeFile as writeFile36 } from "node:fs/promises";
|
|
460524
|
-
import { resolve as resolve53, basename as
|
|
460521
|
+
import { resolve as resolve53, basename as basename13 } from "node:path";
|
|
460525
460522
|
async function executeDetectScenes(options) {
|
|
460526
460523
|
try {
|
|
460527
460524
|
if (!commandExists("ffmpeg")) {
|
|
@@ -460770,7 +460767,7 @@ var init_detect = __esm({
|
|
|
460770
460767
|
let source3 = project.getSources().find((s) => s.url === absPath);
|
|
460771
460768
|
if (!source3) {
|
|
460772
460769
|
source3 = project.addSource({
|
|
460773
|
-
name:
|
|
460770
|
+
name: basename13(absPath),
|
|
460774
460771
|
type: "video",
|
|
460775
460772
|
url: absPath,
|
|
460776
460773
|
duration: totalDuration
|
|
@@ -460974,8 +460971,8 @@ var init_detect = __esm({
|
|
|
460974
460971
|
|
|
460975
460972
|
// ../cli/src/pipeline/renderers/html-clips.ts
|
|
460976
460973
|
function relAsset(url) {
|
|
460977
|
-
const
|
|
460978
|
-
return `assets/${
|
|
460974
|
+
const basename17 = url.split("/").pop() ?? url;
|
|
460975
|
+
return `assets/${basename17}`;
|
|
460979
460976
|
}
|
|
460980
460977
|
function buildClipElements(state) {
|
|
460981
460978
|
return state.clips.map((clip) => {
|
|
@@ -461505,13 +461502,13 @@ init_scene_project();
|
|
|
461505
461502
|
init_esm();
|
|
461506
461503
|
init_source();
|
|
461507
461504
|
init_ora();
|
|
461508
|
-
var
|
|
461505
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
461509
461506
|
init_dist();
|
|
461510
461507
|
init_tts_resolve();
|
|
461511
461508
|
init_scene_project();
|
|
461512
|
-
import {
|
|
461513
|
-
import { mkdir as
|
|
461514
|
-
import { existsSync as
|
|
461509
|
+
import { resolve as resolve17, relative as relative6, dirname as dirname14 } from "node:path";
|
|
461510
|
+
import { mkdir as mkdir8, readFile as readFile7, writeFile as writeFile9, access as access4, copyFile as copyFile2 } from "node:fs/promises";
|
|
461511
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
461515
461512
|
|
|
461516
461513
|
// ../cli/src/commands/_shared/scene-html-emit.ts
|
|
461517
461514
|
var SCENE_PRESETS = [
|
|
@@ -461894,8 +461891,6 @@ function readRootDims(rootHtml) {
|
|
|
461894
461891
|
|
|
461895
461892
|
// ../cli/src/commands/scene.ts
|
|
461896
461893
|
init_scene_lint();
|
|
461897
|
-
init_scene_render();
|
|
461898
|
-
init_scene_build();
|
|
461899
461894
|
init_output();
|
|
461900
461895
|
init_api_key();
|
|
461901
461896
|
init_audio();
|
|
@@ -461904,9 +461899,9 @@ init_agent_host_detect();
|
|
|
461904
461899
|
// ../cli/src/commands/_shared/install-skill.ts
|
|
461905
461900
|
init_bundle_content();
|
|
461906
461901
|
init_bundle();
|
|
461907
|
-
import { mkdir as
|
|
461908
|
-
import { existsSync as
|
|
461909
|
-
import { dirname as
|
|
461902
|
+
import { mkdir as mkdir6, writeFile as writeFile7 } from "node:fs/promises";
|
|
461903
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
461904
|
+
import { dirname as dirname12, join as join21, relative as relative4, resolve as resolve14 } from "node:path";
|
|
461910
461905
|
function universalFiles() {
|
|
461911
461906
|
return [
|
|
461912
461907
|
{ relPath: "SKILL.md", content: SKILL_MD },
|
|
@@ -461956,21 +461951,21 @@ function selectHostFiles(hosts) {
|
|
|
461956
461951
|
return out;
|
|
461957
461952
|
}
|
|
461958
461953
|
async function installHyperframesSkill(opts) {
|
|
461959
|
-
const projectDir =
|
|
461954
|
+
const projectDir = resolve14(opts.projectDir);
|
|
461960
461955
|
const force = opts.force ?? false;
|
|
461961
461956
|
const dryRun = opts.dryRun ?? false;
|
|
461962
461957
|
const files = [...universalFiles(), ...selectHostFiles(opts.hosts)];
|
|
461963
461958
|
const actions = [];
|
|
461964
|
-
if (!dryRun && !
|
|
461965
|
-
await
|
|
461959
|
+
if (!dryRun && !existsSync21(projectDir)) {
|
|
461960
|
+
await mkdir6(projectDir, { recursive: true });
|
|
461966
461961
|
}
|
|
461967
461962
|
for (const file of files) {
|
|
461968
|
-
const absPath =
|
|
461969
|
-
const exists =
|
|
461963
|
+
const absPath = join21(projectDir, file.relPath);
|
|
461964
|
+
const exists = existsSync21(absPath);
|
|
461970
461965
|
const bytes = Buffer.byteLength(file.content, "utf-8");
|
|
461971
461966
|
if (exists && !force) {
|
|
461972
461967
|
actions.push({
|
|
461973
|
-
path:
|
|
461968
|
+
path: relative4(projectDir, absPath) || file.relPath,
|
|
461974
461969
|
status: dryRun ? "would-skip-exists" : "skipped-exists",
|
|
461975
461970
|
bytes
|
|
461976
461971
|
});
|
|
@@ -461980,8 +461975,8 @@ async function installHyperframesSkill(opts) {
|
|
|
461980
461975
|
actions.push({ path: file.relPath, status: "would-write", bytes });
|
|
461981
461976
|
continue;
|
|
461982
461977
|
}
|
|
461983
|
-
await
|
|
461984
|
-
await
|
|
461978
|
+
await mkdir6(dirname12(absPath), { recursive: true });
|
|
461979
|
+
await writeFile7(absPath, file.content, "utf-8");
|
|
461985
461980
|
actions.push({ path: file.relPath, status: "wrote", bytes });
|
|
461986
461981
|
}
|
|
461987
461982
|
return {
|
|
@@ -461999,14 +461994,6 @@ function deriveInstallHosts(detected) {
|
|
|
461999
461994
|
|
|
462000
461995
|
// ../cli/src/commands/scene.ts
|
|
462001
461996
|
init_compose_prompts();
|
|
462002
|
-
var VALID_ASPECTS2 = ["16:9", "9:16", "1:1", "4:5"];
|
|
462003
|
-
var VALID_SCENE_INIT_PROFILES = ["minimal", "agent", "full"];
|
|
462004
|
-
function validateAspect(value) {
|
|
462005
|
-
if (!VALID_ASPECTS2.includes(value)) {
|
|
462006
|
-
exitWithError(usageError(`Invalid aspect ratio: ${value}`, `Valid: ${VALID_ASPECTS2.join(", ")}`));
|
|
462007
|
-
}
|
|
462008
|
-
return value;
|
|
462009
|
-
}
|
|
462010
461997
|
function validateDuration(value) {
|
|
462011
461998
|
const n = parseFloat(value);
|
|
462012
461999
|
if (!Number.isFinite(n) || n <= 0 || n > 3600) {
|
|
@@ -462020,23 +462007,6 @@ function validatePreset(value) {
|
|
|
462020
462007
|
}
|
|
462021
462008
|
return value;
|
|
462022
462009
|
}
|
|
462023
|
-
function validateVisualStyle(value) {
|
|
462024
|
-
const found = getVisualStyle(value);
|
|
462025
|
-
if (!found) {
|
|
462026
|
-
exitWithError(
|
|
462027
|
-
usageError(
|
|
462028
|
-
`Unknown visual style: ${value}`,
|
|
462029
|
-
`Valid: ${visualStyleNames()}. Browse details with \`vibe scene styles\`.`
|
|
462030
|
-
)
|
|
462031
|
-
);
|
|
462032
|
-
}
|
|
462033
|
-
return found;
|
|
462034
|
-
}
|
|
462035
|
-
function formatSceneInitProfile(profile) {
|
|
462036
|
-
if (profile === "minimal") return "authoring files only; build will add render scaffold when needed";
|
|
462037
|
-
if (profile === "agent") return "authoring files plus local composition rules for host agents";
|
|
462038
|
-
return "complete authoring, agent, and render scaffold";
|
|
462039
|
-
}
|
|
462040
462010
|
var sceneCommand = new Command("scene").description("Lower-level scene authoring (add, lint, styles). For project flow use `vibe init` / `vibe build` / `vibe render`.").addHelpText("after", `
|
|
462041
462011
|
Examples:
|
|
462042
462012
|
$ vibe scene add intro --style announcement \\
|
|
@@ -462052,138 +462022,6 @@ For the project flow (init / build / render), use the top-level commands.
|
|
|
462052
462022
|
The \`scene init\`, \`scene build\`, and \`scene render\` legacy aliases
|
|
462053
462023
|
are still callable but hidden from this help \u2014 they will be removed in v1.0.
|
|
462054
462024
|
Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
462055
|
-
sceneCommand.command("init", { hidden: true }).description("Scaffold a new scene project (or safely augment an existing project) [legacy \u2014 prefer `vibe init`]").argument("<dir>", "Project directory (created if it doesn't exist)").option("-n, --name <name>", "Project name (defaults to directory basename)").option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, 1:1, 4:5", "16:9").option("-d, --duration <sec>", "Default root composition duration (seconds)", "10").option("--visual-style <name>", `Seed DESIGN.md from a named style (browse via \`vibe scene styles\`). E.g. "Swiss Pulse"`).option("--profile <profile>", "Scene profile: minimal (storyboard/design only), agent (recommended), full (render scaffold upfront)", "agent").option("--dry-run", "Preview parameters without writing files").action(async (dir, options) => {
|
|
462056
|
-
const startedAt = Date.now();
|
|
462057
|
-
const aspect = validateAspect(options.ratio);
|
|
462058
|
-
const duration = validateDuration(options.duration);
|
|
462059
|
-
const name = options.name ?? basename6(dir.replace(/\/+$/, ""));
|
|
462060
|
-
const visualStyle = options.visualStyle ? validateVisualStyle(options.visualStyle) : void 0;
|
|
462061
|
-
const profile = String(options.profile ?? "agent");
|
|
462062
|
-
if (!isSceneScaffoldProfile(profile)) {
|
|
462063
|
-
exitWithError(usageError(`Invalid --profile: ${profile}`, `Must be one of: ${VALID_SCENE_INIT_PROFILES.join(", ")}`));
|
|
462064
|
-
}
|
|
462065
|
-
const groups = describeSceneScaffold({ dir, profile });
|
|
462066
|
-
if (options.dryRun) {
|
|
462067
|
-
if (!isJsonMode() && !isQuietMode()) {
|
|
462068
|
-
printSceneInitDryRun({ dir, name, aspect, duration, visualStyleName: visualStyle?.name ?? null, profile, groups });
|
|
462069
|
-
return;
|
|
462070
|
-
}
|
|
462071
|
-
outputSuccess({
|
|
462072
|
-
command: "scene init",
|
|
462073
|
-
startedAt,
|
|
462074
|
-
dryRun: true,
|
|
462075
|
-
data: {
|
|
462076
|
-
params: {
|
|
462077
|
-
dir,
|
|
462078
|
-
name,
|
|
462079
|
-
aspect,
|
|
462080
|
-
duration,
|
|
462081
|
-
visualStyle: visualStyle?.name ?? null,
|
|
462082
|
-
profile
|
|
462083
|
-
},
|
|
462084
|
-
groups
|
|
462085
|
-
}
|
|
462086
|
-
});
|
|
462087
|
-
return;
|
|
462088
|
-
}
|
|
462089
|
-
const spinner2 = isJsonMode() || isQuietMode() ? null : ora(`Scaffolding scene project at ${dir}...`).start();
|
|
462090
|
-
try {
|
|
462091
|
-
const result = await scaffoldSceneProject({ dir, name, aspect, duration, visualStyle, profile });
|
|
462092
|
-
const detectedIds = detectedAgentHosts().map((h) => h.id);
|
|
462093
|
-
const skillHosts = deriveInstallHosts(detectedIds);
|
|
462094
|
-
const projectAbs = resolve21(dir);
|
|
462095
|
-
const skillResult = profile === "agent" || profile === "full" ? await installHyperframesSkill({
|
|
462096
|
-
projectDir: projectAbs,
|
|
462097
|
-
hosts: skillHosts
|
|
462098
|
-
}) : { success: true, files: [], bundleVersion: "not-installed" };
|
|
462099
|
-
if (isJsonMode() || isQuietMode()) {
|
|
462100
|
-
outputSuccess({
|
|
462101
|
-
command: "scene init",
|
|
462102
|
-
startedAt,
|
|
462103
|
-
data: {
|
|
462104
|
-
dir,
|
|
462105
|
-
name,
|
|
462106
|
-
aspect,
|
|
462107
|
-
duration,
|
|
462108
|
-
visualStyle: visualStyle?.name ?? null,
|
|
462109
|
-
profile,
|
|
462110
|
-
created: result.created,
|
|
462111
|
-
merged: result.merged,
|
|
462112
|
-
skipped: result.skipped,
|
|
462113
|
-
groups: result.groups,
|
|
462114
|
-
skillFiles: skillResult.files,
|
|
462115
|
-
skillBundleVersion: skillResult.bundleVersion
|
|
462116
|
-
}
|
|
462117
|
-
});
|
|
462118
|
-
return;
|
|
462119
|
-
}
|
|
462120
|
-
spinner2?.succeed(source_default.green(`Video project ready: ${dir}`));
|
|
462121
|
-
console.log();
|
|
462122
|
-
console.log(source_default.bold.cyan("Edit first"));
|
|
462123
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462124
|
-
console.log(` ${source_default.bold("STORYBOARD.md")} ${source_default.dim("# beats: narration, backdrop, minimum duration")}`);
|
|
462125
|
-
console.log(` ${source_default.bold("DESIGN.md")} ${source_default.dim("# palette, typography, motion rules")}`);
|
|
462126
|
-
console.log();
|
|
462127
|
-
console.log(source_default.bold.cyan("Profile"));
|
|
462128
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462129
|
-
console.log(` ${source_default.bold(profile)} ${source_default.dim(formatSceneInitProfile(profile))}`);
|
|
462130
|
-
console.log();
|
|
462131
|
-
console.log(source_default.bold.cyan("Files"));
|
|
462132
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462133
|
-
for (const f of result.created) console.log(source_default.green(" +"), f);
|
|
462134
|
-
for (const f of result.merged) console.log(source_default.yellow(" ~"), f, source_default.dim("(merged)"));
|
|
462135
|
-
for (const f of result.skipped) console.log(source_default.dim(" \xB7"), f, source_default.dim("(kept existing)"));
|
|
462136
|
-
const skillWritten = skillResult.files.filter((f) => f.status === "wrote");
|
|
462137
|
-
const skillSkipped = skillResult.files.filter((f) => f.status === "skipped-exists");
|
|
462138
|
-
if (skillWritten.length + skillSkipped.length > 0) {
|
|
462139
|
-
console.log();
|
|
462140
|
-
console.log(source_default.bold.cyan("Composition rules"));
|
|
462141
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462142
|
-
for (const f of skillWritten) console.log(source_default.green(" +"), f.path);
|
|
462143
|
-
for (const f of skillSkipped) console.log(source_default.dim(" \xB7"), f.path, source_default.dim("(kept existing)"));
|
|
462144
|
-
console.log(source_default.dim(` Bundle: ${skillResult.bundleVersion}`));
|
|
462145
|
-
}
|
|
462146
|
-
console.log();
|
|
462147
|
-
console.log(source_default.bold.cyan("Next steps"));
|
|
462148
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462149
|
-
if (visualStyle) {
|
|
462150
|
-
console.log(` ${source_default.dim("DESIGN.md seeded with")} ${source_default.bold(visualStyle.name)} ${source_default.dim("\u2014 review and customise.")}`);
|
|
462151
|
-
} else {
|
|
462152
|
-
console.log(` ${source_default.cyan("vibe scene styles")} ${source_default.dim("# pick a named style for DESIGN.md")}`);
|
|
462153
|
-
}
|
|
462154
|
-
if (profile === "agent" || profile === "full") {
|
|
462155
|
-
console.log(` ${source_default.dim("Your agent now has composition rules in")} ${source_default.cyan("SKILL.md")} ${source_default.dim("\u2014 ask it to author scene HTML directly.")}`);
|
|
462156
|
-
} else {
|
|
462157
|
-
console.log(` ${source_default.cyan("vibe scene install-skill")} ${source_default.dim("# add agent authoring rules later")}`);
|
|
462158
|
-
}
|
|
462159
|
-
console.log(` ${source_default.cyan("vibe scene add")} <name> ${source_default.dim("# fallback: 5-preset emit (no agent)")}`);
|
|
462160
|
-
console.log(` ${source_default.cyan("vibe build")} ${source_default.dim("# build STORYBOARD.md into scenes/assets")}`);
|
|
462161
|
-
console.log(` ${source_default.cyan("vibe render")} ${source_default.dim("# render to video")}`);
|
|
462162
|
-
} catch (error) {
|
|
462163
|
-
spinner2?.fail("Failed to scaffold scene project");
|
|
462164
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
462165
|
-
exitWithError(generalError(`Failed to scaffold: ${msg}`));
|
|
462166
|
-
}
|
|
462167
|
-
});
|
|
462168
|
-
function printSceneInitDryRun(opts) {
|
|
462169
|
-
console.log();
|
|
462170
|
-
console.log(source_default.bold.cyan("VibeFrame Scene Init - dry run"));
|
|
462171
|
-
console.log(source_default.dim("-".repeat(60)));
|
|
462172
|
-
console.log(` Project: ${source_default.bold(opts.dir)}`);
|
|
462173
|
-
console.log(` Name: ${source_default.bold(opts.name)}`);
|
|
462174
|
-
console.log(` Profile: ${source_default.bold(opts.profile)} ${source_default.dim(formatSceneInitProfile(opts.profile))}`);
|
|
462175
|
-
console.log(` Aspect: ${opts.aspect}`);
|
|
462176
|
-
console.log(` Duration: ${opts.duration}s`);
|
|
462177
|
-
console.log(` Visual style: ${opts.visualStyleName ?? "none"}`);
|
|
462178
|
-
console.log();
|
|
462179
|
-
console.log(source_default.bold.cyan("Files that would be prepared"));
|
|
462180
|
-
console.log(source_default.dim("-".repeat(60)));
|
|
462181
|
-
for (const file of opts.groups.authoring) console.log(` authoring ${file}`);
|
|
462182
|
-
for (const file of opts.groups.agent) console.log(` agent ${file}`);
|
|
462183
|
-
for (const file of opts.groups.render) console.log(` render ${file}`);
|
|
462184
|
-
console.log();
|
|
462185
|
-
console.log(source_default.dim("No files were written."));
|
|
462186
|
-
}
|
|
462187
462025
|
var VALID_INSTALL_SKILL_HOSTS = ["claude-code", "cursor", "auto", "all"];
|
|
462188
462026
|
sceneCommand.command("install-skill").description("Install the Hyperframes skill into a scene project so the host agent can read it (Phase H1)").argument("[project-dir]", "Project directory containing STORYBOARD.md / DESIGN.md", ".").option("--host <id>", `Host layout target: ${VALID_INSTALL_SKILL_HOSTS.join(" | ")}`, "auto").option("--force", "Overwrite existing skill files (default: skip-on-exist)").option("--dry-run", "Preview which files would be written without changing anything").action(async (projectDirArg, options) => {
|
|
462189
462027
|
const startedAt = Date.now();
|
|
@@ -462191,7 +462029,7 @@ sceneCommand.command("install-skill").description("Install the Hyperframes skill
|
|
|
462191
462029
|
if (!VALID_INSTALL_SKILL_HOSTS.includes(hostFlag)) {
|
|
462192
462030
|
exitWithError(usageError(`Invalid --host: ${hostFlag}`, `Valid: ${VALID_INSTALL_SKILL_HOSTS.join(", ")}`));
|
|
462193
462031
|
}
|
|
462194
|
-
const projectDir =
|
|
462032
|
+
const projectDir = resolve17(projectDirArg);
|
|
462195
462033
|
const hosts = (() => {
|
|
462196
462034
|
if (hostFlag === "all") return ["all"];
|
|
462197
462035
|
if (hostFlag === "auto") {
|
|
@@ -462239,7 +462077,7 @@ sceneCommand.command("install-skill").description("Install the Hyperframes skill
|
|
|
462239
462077
|
});
|
|
462240
462078
|
sceneCommand.command("compose-prompts").description("Emit the per-beat compose plan for the host agent to author HTML itself (Phase H2 \u2014 no LLM call)").argument("[project-dir]", "Project directory containing STORYBOARD.md / DESIGN.md", ".").option("--beat <id>", "Restrict the plan to a single beat by id (e.g. 'hook', '1')").action(async (projectDirArg, options) => {
|
|
462241
462079
|
const startedAt = Date.now();
|
|
462242
|
-
const projectDir =
|
|
462080
|
+
const projectDir = resolve17(projectDirArg);
|
|
462243
462081
|
const result = await getComposePrompts({
|
|
462244
462082
|
projectDir,
|
|
462245
462083
|
beatId: options.beat
|
|
@@ -462474,16 +462312,16 @@ async function pathExists2(p) {
|
|
|
462474
462312
|
}
|
|
462475
462313
|
}
|
|
462476
462314
|
async function loadVibeProjectConfig(projectDir) {
|
|
462477
|
-
const cfgPath =
|
|
462315
|
+
const cfgPath = resolve17(projectDir, "vibe.project.yaml");
|
|
462478
462316
|
if (!await pathExists2(cfgPath)) return null;
|
|
462479
|
-
const raw2 = await
|
|
462480
|
-
return (0,
|
|
462317
|
+
const raw2 = await readFile7(cfgPath, "utf-8");
|
|
462318
|
+
return (0, import_yaml4.parse)(raw2);
|
|
462481
462319
|
}
|
|
462482
462320
|
async function resolveNarrationText(value) {
|
|
462483
462321
|
if (!value) return void 0;
|
|
462484
462322
|
const looksLikePath = /\.[a-z]{2,4}$/i.test(value) || value.includes("/") || value.includes("\\");
|
|
462485
|
-
if (looksLikePath &&
|
|
462486
|
-
return (await
|
|
462323
|
+
if (looksLikePath && existsSync25(value)) {
|
|
462324
|
+
return (await readFile7(value, "utf-8")).trim();
|
|
462487
462325
|
}
|
|
462488
462326
|
return value.trim();
|
|
462489
462327
|
}
|
|
@@ -462499,12 +462337,12 @@ function aspectStringFromDims(width, height) {
|
|
|
462499
462337
|
return width > height ? "16:9" : "9:16";
|
|
462500
462338
|
}
|
|
462501
462339
|
async function executeSceneAdd(opts) {
|
|
462502
|
-
const projectDir =
|
|
462340
|
+
const projectDir = resolve17(opts.projectDir ?? ".");
|
|
462503
462341
|
const rootRel = opts.insertInto ?? "index.html";
|
|
462504
|
-
const rootPath =
|
|
462342
|
+
const rootPath = resolve17(projectDir, rootRel);
|
|
462505
462343
|
const id = slugifySceneName(opts.name);
|
|
462506
462344
|
const sceneRel = `compositions/scene-${id}.html`;
|
|
462507
|
-
const scenePath =
|
|
462345
|
+
const scenePath = resolve17(projectDir, sceneRel);
|
|
462508
462346
|
const errResult = (error) => ({
|
|
462509
462347
|
success: false,
|
|
462510
462348
|
id,
|
|
@@ -462521,7 +462359,7 @@ async function executeSceneAdd(opts) {
|
|
|
462521
462359
|
if (!opts.force && await pathExists2(scenePath)) {
|
|
462522
462360
|
return errResult(`Scene already exists: ${sceneRel}. Re-run with --force to overwrite.`);
|
|
462523
462361
|
}
|
|
462524
|
-
const rootHtmlBefore = await
|
|
462362
|
+
const rootHtmlBefore = await readFile7(rootPath, "utf-8");
|
|
462525
462363
|
let dims = readRootDims(rootHtmlBefore);
|
|
462526
462364
|
if (!dims) {
|
|
462527
462365
|
const cfg2 = await loadVibeProjectConfig(projectDir);
|
|
@@ -462535,7 +462373,7 @@ async function executeSceneAdd(opts) {
|
|
|
462535
462373
|
let audioAbsPath;
|
|
462536
462374
|
let narrationDuration;
|
|
462537
462375
|
if (opts.narrationFile && !opts.skipAudio) {
|
|
462538
|
-
const sourceAbs =
|
|
462376
|
+
const sourceAbs = resolve17(opts.narrationFile);
|
|
462539
462377
|
if (!await pathExists2(sourceAbs)) {
|
|
462540
462378
|
return errResult(`Narration file not found: ${sourceAbs}`);
|
|
462541
462379
|
}
|
|
@@ -462544,8 +462382,8 @@ async function executeSceneAdd(opts) {
|
|
|
462544
462382
|
return errResult(`Unsupported narration file extension: .${ext}. Use .wav or .mp3.`);
|
|
462545
462383
|
}
|
|
462546
462384
|
audioRelPath = `assets/narration-${id}.${ext}`;
|
|
462547
|
-
audioAbsPath =
|
|
462548
|
-
await
|
|
462385
|
+
audioAbsPath = resolve17(projectDir, audioRelPath);
|
|
462386
|
+
await mkdir8(dirname14(audioAbsPath), { recursive: true });
|
|
462549
462387
|
await copyFile2(sourceAbs, audioAbsPath);
|
|
462550
462388
|
try {
|
|
462551
462389
|
narrationDuration = await getAudioDuration(audioAbsPath);
|
|
@@ -462577,9 +462415,9 @@ async function executeSceneAdd(opts) {
|
|
|
462577
462415
|
return errResult(`${resolution.provider} TTS failed: ${tts.error ?? "unknown error"}`);
|
|
462578
462416
|
}
|
|
462579
462417
|
audioRelPath = `assets/narration-${id}.${resolution.audioExtension}`;
|
|
462580
|
-
audioAbsPath =
|
|
462581
|
-
await
|
|
462582
|
-
await
|
|
462418
|
+
audioAbsPath = resolve17(projectDir, audioRelPath);
|
|
462419
|
+
await mkdir8(dirname14(audioAbsPath), { recursive: true });
|
|
462420
|
+
await writeFile9(audioAbsPath, tts.audioBuffer);
|
|
462583
462421
|
try {
|
|
462584
462422
|
narrationDuration = await getAudioDuration(audioAbsPath);
|
|
462585
462423
|
} catch {
|
|
@@ -462600,7 +462438,7 @@ async function executeSceneAdd(opts) {
|
|
|
462600
462438
|
try {
|
|
462601
462439
|
const whisper = new WhisperProvider();
|
|
462602
462440
|
await whisper.initialize({ apiKey: whisperKey });
|
|
462603
|
-
const audioBytes = await
|
|
462441
|
+
const audioBytes = await readFile7(audioAbsPath);
|
|
462604
462442
|
const audioBlob = new Blob([new Uint8Array(audioBytes)]);
|
|
462605
462443
|
const transcript = await whisper.transcribe(audioBlob, void 0, {
|
|
462606
462444
|
granularity: "word",
|
|
@@ -462608,8 +462446,8 @@ async function executeSceneAdd(opts) {
|
|
|
462608
462446
|
});
|
|
462609
462447
|
if (transcript.status === "completed" && transcript.words?.length) {
|
|
462610
462448
|
transcriptRelPath = `assets/transcript-${id}.json`;
|
|
462611
|
-
const transcriptAbs =
|
|
462612
|
-
await
|
|
462449
|
+
const transcriptAbs = resolve17(projectDir, transcriptRelPath);
|
|
462450
|
+
await writeFile9(transcriptAbs, JSON.stringify(transcript.words, null, 2), "utf-8");
|
|
462613
462451
|
transcriptWordCount = transcript.words.length;
|
|
462614
462452
|
transcriptWords = transcript.words.map((w) => ({ text: w.text, start: w.start, end: w.end }));
|
|
462615
462453
|
} else if (transcript.status === "failed") {
|
|
@@ -462645,8 +462483,8 @@ async function executeSceneAdd(opts) {
|
|
|
462645
462483
|
}
|
|
462646
462484
|
const img = imageResult.images[0];
|
|
462647
462485
|
imageRelPath = `assets/scene-${id}.png`;
|
|
462648
|
-
imageAbsPath =
|
|
462649
|
-
await
|
|
462486
|
+
imageAbsPath = resolve17(projectDir, imageRelPath);
|
|
462487
|
+
await mkdir8(dirname14(imageAbsPath), { recursive: true });
|
|
462650
462488
|
let buffer;
|
|
462651
462489
|
if (img.base64) {
|
|
462652
462490
|
buffer = Buffer.from(img.base64, "base64");
|
|
@@ -462656,7 +462494,7 @@ async function executeSceneAdd(opts) {
|
|
|
462656
462494
|
} else {
|
|
462657
462495
|
return errResult("OpenAI returned no image data");
|
|
462658
462496
|
}
|
|
462659
|
-
await
|
|
462497
|
+
await writeFile9(imageAbsPath, buffer);
|
|
462660
462498
|
} else {
|
|
462661
462499
|
const googleKey = await getApiKey("GOOGLE_API_KEY", "Google");
|
|
462662
462500
|
if (!googleKey) {
|
|
@@ -462670,10 +462508,10 @@ async function executeSceneAdd(opts) {
|
|
|
462670
462508
|
return errResult(`Gemini image generation failed: ${imageResult.error ?? "unknown error"}`);
|
|
462671
462509
|
}
|
|
462672
462510
|
imageRelPath = `assets/scene-${id}.png`;
|
|
462673
|
-
imageAbsPath =
|
|
462674
|
-
await
|
|
462511
|
+
imageAbsPath = resolve17(projectDir, imageRelPath);
|
|
462512
|
+
await mkdir8(dirname14(imageAbsPath), { recursive: true });
|
|
462675
462513
|
const buffer = Buffer.from(imageResult.images[0].base64, "base64");
|
|
462676
|
-
await
|
|
462514
|
+
await writeFile9(imageAbsPath, buffer);
|
|
462677
462515
|
}
|
|
462678
462516
|
}
|
|
462679
462517
|
const cfg = await loadVibeProjectConfig(projectDir);
|
|
@@ -462706,35 +462544,35 @@ async function executeSceneAdd(opts) {
|
|
|
462706
462544
|
audioPath: audioRelPath,
|
|
462707
462545
|
transcript: transcriptWords
|
|
462708
462546
|
});
|
|
462709
|
-
await
|
|
462710
|
-
await
|
|
462547
|
+
await mkdir8(dirname14(scenePath), { recursive: true });
|
|
462548
|
+
await writeFile9(scenePath, sceneHtml, "utf-8");
|
|
462711
462549
|
opts.onProgress?.("Updating root composition...");
|
|
462712
462550
|
const start = nextSceneStart(rootHtmlBefore, SCENE_OVERLAP_SECONDS);
|
|
462713
462551
|
const existingClipCount = (rootHtmlBefore.match(/<div\s+class="clip"/g) || []).length;
|
|
462714
462552
|
const trackIndex = existingClipCount % 2 + 1;
|
|
462715
462553
|
const updated = insertClipIntoRoot(rootHtmlBefore, { id, start, duration, trackIndex });
|
|
462716
|
-
await
|
|
462717
|
-
const transcriptAbsPath = transcriptRelPath ?
|
|
462554
|
+
await writeFile9(rootPath, updated, "utf-8");
|
|
462555
|
+
const transcriptAbsPath = transcriptRelPath ? resolve17(projectDir, transcriptRelPath) : void 0;
|
|
462718
462556
|
return {
|
|
462719
462557
|
success: true,
|
|
462720
462558
|
id,
|
|
462721
462559
|
preset: opts.preset,
|
|
462722
462560
|
start,
|
|
462723
462561
|
duration,
|
|
462724
|
-
scenePath:
|
|
462725
|
-
rootPath:
|
|
462726
|
-
audioPath: audioAbsPath ?
|
|
462727
|
-
imagePath: imageAbsPath ?
|
|
462728
|
-
transcriptPath: transcriptAbsPath ?
|
|
462562
|
+
scenePath: relative6(process.cwd(), scenePath) || scenePath,
|
|
462563
|
+
rootPath: relative6(process.cwd(), rootPath) || rootPath,
|
|
462564
|
+
audioPath: audioAbsPath ? relative6(process.cwd(), audioAbsPath) || audioAbsPath : void 0,
|
|
462565
|
+
imagePath: imageAbsPath ? relative6(process.cwd(), imageAbsPath) || imageAbsPath : void 0,
|
|
462566
|
+
transcriptPath: transcriptAbsPath ? relative6(process.cwd(), transcriptAbsPath) || transcriptAbsPath : void 0,
|
|
462729
462567
|
transcriptWordCount
|
|
462730
462568
|
};
|
|
462731
462569
|
}
|
|
462732
462570
|
sceneCommand.command("lint").description("Validate scene HTML against composition rules (in-process, no Chrome required)").argument("[root]", "Root composition file relative to --project", "index.html").option("--project <dir>", "Project directory", ".").option("--fix", 'Apply mechanical auto-fixes (currently: missing class="clip")').action(async (root2, options) => {
|
|
462733
462571
|
const startedAt = Date.now();
|
|
462734
|
-
const projectDir =
|
|
462572
|
+
const projectDir = resolve17(options.project);
|
|
462735
462573
|
if (!await rootExists(projectDir, root2)) {
|
|
462736
462574
|
exitWithError(generalError(
|
|
462737
|
-
`Root composition not found: ${
|
|
462575
|
+
`Root composition not found: ${resolve17(projectDir, root2)}`,
|
|
462738
462576
|
"Run `vibe scene init` first, or pass --project <dir>."
|
|
462739
462577
|
));
|
|
462740
462578
|
}
|
|
@@ -462794,281 +462632,6 @@ function severityTag(severity) {
|
|
|
462794
462632
|
if (severity === "warning") return source_default.yellow("\u26A0 warn ");
|
|
462795
462633
|
return source_default.blue("\u2139 info ");
|
|
462796
462634
|
}
|
|
462797
|
-
var VALID_FPS = [24, 30, 60];
|
|
462798
|
-
var VALID_QUALITIES = ["draft", "standard", "high"];
|
|
462799
|
-
var VALID_FORMATS = ["mp4", "webm", "mov"];
|
|
462800
|
-
function validateFps(value) {
|
|
462801
|
-
const n = parseInt(value, 10);
|
|
462802
|
-
if (!VALID_FPS.includes(n)) {
|
|
462803
|
-
exitWithError(usageError(`Invalid --fps: ${value}`, `Valid: ${VALID_FPS.join(", ")}`));
|
|
462804
|
-
}
|
|
462805
|
-
return n;
|
|
462806
|
-
}
|
|
462807
|
-
function validateQuality(value) {
|
|
462808
|
-
if (!VALID_QUALITIES.includes(value)) {
|
|
462809
|
-
exitWithError(usageError(`Invalid --quality: ${value}`, `Valid: ${VALID_QUALITIES.join(", ")}`));
|
|
462810
|
-
}
|
|
462811
|
-
return value;
|
|
462812
|
-
}
|
|
462813
|
-
function validateFormat(value) {
|
|
462814
|
-
if (!VALID_FORMATS.includes(value)) {
|
|
462815
|
-
exitWithError(usageError(`Invalid --format: ${value}`, `Valid: ${VALID_FORMATS.join(", ")}`));
|
|
462816
|
-
}
|
|
462817
|
-
return value;
|
|
462818
|
-
}
|
|
462819
|
-
function validateWorkers(value) {
|
|
462820
|
-
const n = parseInt(value, 10);
|
|
462821
|
-
if (!Number.isFinite(n) || n < 1 || n > 16) {
|
|
462822
|
-
exitWithError(usageError(`Invalid --workers: ${value}`, "Must be an integer between 1 and 16"));
|
|
462823
|
-
}
|
|
462824
|
-
return n;
|
|
462825
|
-
}
|
|
462826
|
-
sceneCommand.command("render", { hidden: true }).description("Render a scene project to MP4/WebM/MOV via the Hyperframes producer (requires Chrome) [legacy \u2014 prefer `vibe render`]").argument("[root]", "Root composition file relative to --project", "index.html").option("--project <dir>", "Project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--fps <n>", `Frames per second: ${VALID_FPS.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--dry-run", "Preview parameters without rendering").action(async (root2, options) => {
|
|
462827
|
-
const startedAt = Date.now();
|
|
462828
|
-
const fps = validateFps(options.fps);
|
|
462829
|
-
const quality = validateQuality(options.quality);
|
|
462830
|
-
const format4 = validateFormat(options.format);
|
|
462831
|
-
const workers = validateWorkers(options.workers);
|
|
462832
|
-
const projectDir = resolve21(options.project);
|
|
462833
|
-
if (options.dryRun) {
|
|
462834
|
-
outputSuccess({
|
|
462835
|
-
command: "scene render",
|
|
462836
|
-
startedAt,
|
|
462837
|
-
dryRun: true,
|
|
462838
|
-
data: {
|
|
462839
|
-
params: {
|
|
462840
|
-
projectDir,
|
|
462841
|
-
root: root2,
|
|
462842
|
-
output: options.out,
|
|
462843
|
-
fps,
|
|
462844
|
-
quality,
|
|
462845
|
-
format: format4,
|
|
462846
|
-
workers
|
|
462847
|
-
}
|
|
462848
|
-
}
|
|
462849
|
-
});
|
|
462850
|
-
return;
|
|
462851
|
-
}
|
|
462852
|
-
const spinner2 = isJsonMode() ? null : ora("Rendering scene project...").start();
|
|
462853
|
-
const result = await executeSceneRender({
|
|
462854
|
-
projectDir,
|
|
462855
|
-
root: root2,
|
|
462856
|
-
output: options.out,
|
|
462857
|
-
fps,
|
|
462858
|
-
quality,
|
|
462859
|
-
format: format4,
|
|
462860
|
-
workers,
|
|
462861
|
-
onProgress: (pct, stage) => {
|
|
462862
|
-
if (spinner2) spinner2.text = `Rendering [${Math.round(pct * 100)}%] ${stage}`;
|
|
462863
|
-
}
|
|
462864
|
-
});
|
|
462865
|
-
if (!result.success) {
|
|
462866
|
-
spinner2?.fail("Render failed");
|
|
462867
|
-
if (isJsonMode()) {
|
|
462868
|
-
outputSuccess({
|
|
462869
|
-
command: "scene render",
|
|
462870
|
-
startedAt,
|
|
462871
|
-
data: { ...result }
|
|
462872
|
-
});
|
|
462873
|
-
process.exitCode = 1;
|
|
462874
|
-
return;
|
|
462875
|
-
}
|
|
462876
|
-
exitWithError(generalError(result.error ?? "Render failed"));
|
|
462877
|
-
}
|
|
462878
|
-
if (isJsonMode()) {
|
|
462879
|
-
outputSuccess({
|
|
462880
|
-
command: "scene render",
|
|
462881
|
-
startedAt,
|
|
462882
|
-
data: { ...result }
|
|
462883
|
-
});
|
|
462884
|
-
return;
|
|
462885
|
-
}
|
|
462886
|
-
spinner2?.succeed(source_default.green(`Render complete: ${result.outputPath}`));
|
|
462887
|
-
console.log();
|
|
462888
|
-
console.log(source_default.bold.cyan("Render"));
|
|
462889
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
462890
|
-
console.log(` output ${source_default.bold(result.outputPath)}`);
|
|
462891
|
-
console.log(` duration ${((result.durationMs ?? 0) / 1e3).toFixed(1)}s`);
|
|
462892
|
-
console.log(` frames ${result.framesRendered ?? "?"}${result.totalFrames ? ` / ${result.totalFrames}` : ""}`);
|
|
462893
|
-
console.log(` config ${result.fps}fps \xB7 ${result.quality} \xB7 ${result.format}`);
|
|
462894
|
-
if (result.audioCount && result.audioCount > 0) {
|
|
462895
|
-
const muxStatus = result.audioMuxApplied ? source_default.green(`\u2713 ${result.audioCount} track${result.audioCount === 1 ? "" : "s"} muxed`) : source_default.yellow(`\u26A0 ${result.audioCount} track${result.audioCount === 1 ? "" : "s"} skipped`);
|
|
462896
|
-
console.log(` audio ${muxStatus}`);
|
|
462897
|
-
if (result.audioMuxWarning) {
|
|
462898
|
-
console.log(source_default.dim(` ${result.audioMuxWarning}`));
|
|
462899
|
-
}
|
|
462900
|
-
}
|
|
462901
|
-
});
|
|
462902
|
-
sceneCommand.command("build", { hidden: true }).description("One-shot: read STORYBOARD.md cues, dispatch TTS + image-gen per beat, compose, render to MP4 [legacy \u2014 prefer `vibe build`]").argument("[project-dir]", "Project directory containing STORYBOARD.md", ".").option("--mode <mode>", "Build mode: agent (host-agent authors HTML) | batch (CLI's internal LLM authors HTML) | auto (agent if any host detected) [Plan H \u2014 Phase 3]", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "LLM that composes scene HTML in batch mode: claude|openai|gemini (default: auto-resolve from available API keys, claude > gemini > openai)").option("--skip-narration", "Don't dispatch TTS even when beats declare narration cues").option("--skip-backdrop", "Don't dispatch image-gen even when beats declare backdrop cues").option("--skip-render", "Compose only \u2014 don't render to MP4").option("--tts <provider>", "TTS provider: auto|elevenlabs|kokoro (overrides frontmatter)").option("--voice <id>", "Voice id (provider-specific \u2014 overrides frontmatter)").option("--image-provider <name>", "Image provider: openai (only one supported in v0.60)").option("--quality <q>", "Image quality: standard|hd", "hd").option("--image-size <s>", "Image size: 1024x1024|1536x1024|1024x1536", "1536x1024").option("--force", "Re-dispatch primitives even when assets already exist").option("--dry-run", "Preview parameters without dispatching").action(async (projectDirArg, options) => {
|
|
462903
|
-
const startedAt = Date.now();
|
|
462904
|
-
const projectDir = resolve21(projectDirArg);
|
|
462905
|
-
if (options.dryRun) {
|
|
462906
|
-
outputSuccess({
|
|
462907
|
-
command: "scene build",
|
|
462908
|
-
startedAt,
|
|
462909
|
-
dryRun: true,
|
|
462910
|
-
data: {
|
|
462911
|
-
params: {
|
|
462912
|
-
projectDir,
|
|
462913
|
-
mode: options.mode,
|
|
462914
|
-
effort: options.effort,
|
|
462915
|
-
composer: options.composer,
|
|
462916
|
-
skipNarration: options.skipNarration ?? false,
|
|
462917
|
-
skipBackdrop: options.skipBackdrop ?? false,
|
|
462918
|
-
skipRender: options.skipRender ?? false,
|
|
462919
|
-
ttsProvider: options.tts,
|
|
462920
|
-
voice: options.voice,
|
|
462921
|
-
imageProvider: options.imageProvider,
|
|
462922
|
-
imageQuality: options.quality,
|
|
462923
|
-
imageSize: options.imageSize,
|
|
462924
|
-
force: options.force ?? false
|
|
462925
|
-
}
|
|
462926
|
-
}
|
|
462927
|
-
});
|
|
462928
|
-
return;
|
|
462929
|
-
}
|
|
462930
|
-
const validEfforts = ["low", "medium", "high"];
|
|
462931
|
-
if (!validEfforts.includes(options.effort)) {
|
|
462932
|
-
exitWithError(usageError(`Invalid --effort: ${options.effort}`, `Must be one of: ${validEfforts.join(", ")}`));
|
|
462933
|
-
}
|
|
462934
|
-
const validComposers = ["claude", "openai", "gemini"];
|
|
462935
|
-
if (options.composer !== void 0 && !validComposers.includes(options.composer)) {
|
|
462936
|
-
exitWithError(usageError(`Invalid --composer: ${options.composer}`, `Must be one of: ${validComposers.join(", ")}`));
|
|
462937
|
-
}
|
|
462938
|
-
const validModes = ["agent", "batch", "auto"];
|
|
462939
|
-
if (options.mode !== void 0 && !validModes.includes(options.mode)) {
|
|
462940
|
-
exitWithError(usageError(`Invalid --mode: ${options.mode}`, `Must be one of: ${validModes.join(", ")}`));
|
|
462941
|
-
}
|
|
462942
|
-
const spinner2 = isJsonMode() ? null : ora("Reading STORYBOARD.md...").start();
|
|
462943
|
-
const result = await executeSceneBuild({
|
|
462944
|
-
projectDir,
|
|
462945
|
-
mode: options.mode,
|
|
462946
|
-
effort: options.effort,
|
|
462947
|
-
composer: options.composer,
|
|
462948
|
-
skipNarration: options.skipNarration,
|
|
462949
|
-
skipBackdrop: options.skipBackdrop,
|
|
462950
|
-
skipRender: options.skipRender,
|
|
462951
|
-
ttsProvider: options.tts,
|
|
462952
|
-
voice: options.voice,
|
|
462953
|
-
imageProvider: options.imageProvider,
|
|
462954
|
-
imageQuality: options.quality,
|
|
462955
|
-
imageSize: options.imageSize,
|
|
462956
|
-
force: options.force,
|
|
462957
|
-
onProgress: (e) => {
|
|
462958
|
-
if (!spinner2) return;
|
|
462959
|
-
if (e.type === "phase-start") {
|
|
462960
|
-
spinner2.text = `Phase: ${e.phase}...`;
|
|
462961
|
-
} else if (e.type === "narration-generated") {
|
|
462962
|
-
spinner2.text = `Narration ${e.beatId} \u2192 ${e.path} (${e.provider})`;
|
|
462963
|
-
} else if (e.type === "backdrop-generated") {
|
|
462964
|
-
spinner2.text = `Backdrop ${e.beatId} \u2192 ${e.path} (${e.provider})`;
|
|
462965
|
-
} else if (e.type === "beat-fresh") {
|
|
462966
|
-
spinner2.text = `Composed beat ${e.beatId} ($${(e.costUsd ?? 0).toFixed(3)} \xB7 ${e.latencyMs ?? 0}ms)`;
|
|
462967
|
-
} else if (e.type === "beat-cached") {
|
|
462968
|
-
spinner2.text = `Composed beat ${e.beatId} (cached)`;
|
|
462969
|
-
} else if (e.type === "render-start") {
|
|
462970
|
-
spinner2.text = "Rendering...";
|
|
462971
|
-
} else if (e.type === "render-done") {
|
|
462972
|
-
spinner2.text = `Rendered: ${e.outputPath}`;
|
|
462973
|
-
}
|
|
462974
|
-
}
|
|
462975
|
-
});
|
|
462976
|
-
if (!result.success) {
|
|
462977
|
-
spinner2?.fail(`Build failed: ${result.error}`);
|
|
462978
|
-
if (isJsonMode()) {
|
|
462979
|
-
outputSuccess({
|
|
462980
|
-
command: "scene build",
|
|
462981
|
-
startedAt,
|
|
462982
|
-
data: { ...result }
|
|
462983
|
-
});
|
|
462984
|
-
process.exitCode = 1;
|
|
462985
|
-
return;
|
|
462986
|
-
}
|
|
462987
|
-
exitWithError(generalError(result.error ?? "Build failed"));
|
|
462988
|
-
}
|
|
462989
|
-
if (isJsonMode()) {
|
|
462990
|
-
outputSuccess({
|
|
462991
|
-
command: "scene build",
|
|
462992
|
-
startedAt,
|
|
462993
|
-
data: { ...result }
|
|
462994
|
-
});
|
|
462995
|
-
return;
|
|
462996
|
-
}
|
|
462997
|
-
if (result.phase === "needs-author") {
|
|
462998
|
-
spinner2?.info(source_default.cyan("Agent mode \u2014 host agent must author scene HTML before rendering"));
|
|
462999
|
-
console.log();
|
|
463000
|
-
console.log(source_default.bold.cyan("Beats requiring authorship"));
|
|
463001
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
463002
|
-
const plan = result.composePrompts;
|
|
463003
|
-
if (plan) {
|
|
463004
|
-
for (const b of plan.beats) {
|
|
463005
|
-
const status = b.exists ? source_default.dim("(exists)") : source_default.green("(needs author)");
|
|
463006
|
-
const dur = b.duration !== void 0 ? source_default.dim(` ${b.duration}s`) : "";
|
|
463007
|
-
console.log(` ${source_default.bold(b.id)}${dur} \u2192 ${b.outputPath} ${status}`);
|
|
463008
|
-
}
|
|
463009
|
-
console.log();
|
|
463010
|
-
console.log(source_default.bold.cyan("Instructions"));
|
|
463011
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
463012
|
-
for (const line of plan.instructions) console.log(` ${line}`);
|
|
463013
|
-
if (plan.warnings.length > 0) {
|
|
463014
|
-
console.log();
|
|
463015
|
-
for (const w of plan.warnings) console.log(source_default.yellow(` \u26A0 ${w}`));
|
|
463016
|
-
}
|
|
463017
|
-
}
|
|
463018
|
-
console.log();
|
|
463019
|
-
console.log(source_default.dim("Once you've authored each beat's HTML, re-run `vibe build` to lint + render."));
|
|
463020
|
-
console.log(source_default.dim("Or pass `--mode batch` to use the internal LLM compose path instead."));
|
|
463021
|
-
return;
|
|
463022
|
-
}
|
|
463023
|
-
spinner2?.succeed(source_default.green(
|
|
463024
|
-
result.outputPath ? `Build complete: ${result.outputPath}` : "Build complete (compose only \u2014 render skipped)"
|
|
463025
|
-
));
|
|
463026
|
-
console.log();
|
|
463027
|
-
console.log(source_default.bold.cyan("Beats"));
|
|
463028
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
463029
|
-
for (const b of result.beats) {
|
|
463030
|
-
const narration = formatPrimitiveStatus(b.narrationStatus, b.narrationPath);
|
|
463031
|
-
const backdrop = formatPrimitiveStatus(b.backdropStatus, b.backdropPath);
|
|
463032
|
-
console.log(` ${source_default.bold(b.beatId.padEnd(12))} narration: ${narration} backdrop: ${backdrop}`);
|
|
463033
|
-
if (b.narrationError) console.log(source_default.red(` ! narration: ${b.narrationError}`));
|
|
463034
|
-
if (b.backdropError) console.log(source_default.red(` ! backdrop: ${b.backdropError}`));
|
|
463035
|
-
}
|
|
463036
|
-
if (result.composeData) {
|
|
463037
|
-
console.log();
|
|
463038
|
-
console.log(source_default.bold.cyan("Compose"));
|
|
463039
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
463040
|
-
console.log(` beats ${result.composeData.beats}`);
|
|
463041
|
-
console.log(` cache ${result.composeData.cacheHits} hit / ${result.composeData.beats - result.composeData.cacheHits} fresh`);
|
|
463042
|
-
console.log(` cost $${result.composeData.totalCostUsd.toFixed(4)}`);
|
|
463043
|
-
}
|
|
463044
|
-
if (result.outputPath) {
|
|
463045
|
-
console.log();
|
|
463046
|
-
console.log(source_default.bold.cyan("Render"));
|
|
463047
|
-
console.log(source_default.dim("\u2500".repeat(60)));
|
|
463048
|
-
console.log(` output ${source_default.bold(result.outputPath)}`);
|
|
463049
|
-
if (result.renderResult?.audioCount && result.renderResult.audioCount > 0) {
|
|
463050
|
-
console.log(` audio ${result.renderResult.audioCount} track${result.renderResult.audioCount === 1 ? "" : "s"} muxed`);
|
|
463051
|
-
}
|
|
463052
|
-
}
|
|
463053
|
-
console.log();
|
|
463054
|
-
console.log(source_default.dim(`Total: ${(result.totalLatencyMs / 1e3).toFixed(1)}s`));
|
|
463055
|
-
});
|
|
463056
|
-
function formatPrimitiveStatus(status, path14) {
|
|
463057
|
-
switch (status) {
|
|
463058
|
-
case "generated":
|
|
463059
|
-
return source_default.green(`\u2713 ${path14}`);
|
|
463060
|
-
case "cached":
|
|
463061
|
-
return source_default.dim(`\u25C7 ${path14} (cached)`);
|
|
463062
|
-
case "skipped":
|
|
463063
|
-
return source_default.dim("\xB7 skipped");
|
|
463064
|
-
case "no-cue":
|
|
463065
|
-
return source_default.dim("\xB7 no cue");
|
|
463066
|
-
case "failed":
|
|
463067
|
-
return source_default.red("\u2717 failed");
|
|
463068
|
-
default:
|
|
463069
|
-
return status;
|
|
463070
|
-
}
|
|
463071
|
-
}
|
|
463072
462635
|
|
|
463073
462636
|
// ../cli/src/tools/manifest/scene.ts
|
|
463074
462637
|
init_scene_lint();
|
|
@@ -463663,7 +463226,7 @@ init_ai_edit();
|
|
|
463663
463226
|
init_api_key();
|
|
463664
463227
|
init_exec_safe();
|
|
463665
463228
|
init_remotion();
|
|
463666
|
-
import { resolve as resolve30, dirname as dirname23, basename as
|
|
463229
|
+
import { resolve as resolve30, dirname as dirname23, basename as basename9 } from "node:path";
|
|
463667
463230
|
import { writeFile as writeFile18, mkdir as mkdir16, rm as rm4 } from "node:fs/promises";
|
|
463668
463231
|
import { existsSync as existsSync41 } from "node:fs";
|
|
463669
463232
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
@@ -463878,7 +463441,7 @@ async function executeAnimatedCaption(options) {
|
|
|
463878
463441
|
width,
|
|
463879
463442
|
height,
|
|
463880
463443
|
fps: videoFps,
|
|
463881
|
-
videoFileName:
|
|
463444
|
+
videoFileName: basename9(videoPath)
|
|
463882
463445
|
});
|
|
463883
463446
|
const durationInFrames = Math.ceil(duration * videoFps);
|
|
463884
463447
|
const renderResult = await renderWithEmbeddedVideo({
|
|
@@ -463889,7 +463452,7 @@ async function executeAnimatedCaption(options) {
|
|
|
463889
463452
|
fps: videoFps,
|
|
463890
463453
|
durationInFrames,
|
|
463891
463454
|
videoPath,
|
|
463892
|
-
videoFileName:
|
|
463455
|
+
videoFileName: basename9(videoPath),
|
|
463893
463456
|
outputPath: absOutputPath
|
|
463894
463457
|
});
|
|
463895
463458
|
if (!renderResult.success) {
|
|
@@ -464811,7 +464374,7 @@ init_ai_script_pipeline();
|
|
|
464811
464374
|
|
|
464812
464375
|
// ../cli/src/commands/ai-highlights.ts
|
|
464813
464376
|
import { readFile as readFile27, writeFile as writeFile35, mkdir as mkdir21 } from "node:fs/promises";
|
|
464814
|
-
import { resolve as resolve51, dirname as dirname28, basename as
|
|
464377
|
+
import { resolve as resolve51, dirname as dirname28, basename as basename12, extname as extname11 } from "node:path";
|
|
464815
464378
|
import { existsSync as existsSync49 } from "node:fs";
|
|
464816
464379
|
init_dist();
|
|
464817
464380
|
init_engine();
|
|
@@ -465005,7 +464568,7 @@ Analyze both what is SHOWN (visual cues, actions, expressions) and what is SAID
|
|
|
465005
464568
|
if (options.project) {
|
|
465006
464569
|
const project = new Project("Highlight Reel");
|
|
465007
464570
|
const source3 = project.addSource({
|
|
465008
|
-
name:
|
|
464571
|
+
name: basename12(absPath),
|
|
465009
464572
|
url: absPath,
|
|
465010
464573
|
type: isVideo ? "video" : "audio",
|
|
465011
464574
|
duration: sourceDuration
|
|
@@ -465183,7 +464746,7 @@ Analyze both VISUALS (expressions, actions, scene changes) and AUDIO (speech, re
|
|
|
465183
464746
|
};
|
|
465184
464747
|
for (let i = 0; i < selectedHighlights.length; i++) {
|
|
465185
464748
|
const h = selectedHighlights[i];
|
|
465186
|
-
const baseName =
|
|
464749
|
+
const baseName = basename12(absPath, extname11(absPath));
|
|
465187
464750
|
const outputPath = resolve51(outputDir, `${baseName}-short-${i + 1}.mp4`);
|
|
465188
464751
|
const { stdout: probeOut } = await execSafe("ffprobe", [
|
|
465189
464752
|
"-v",
|
|
@@ -466361,7 +465924,7 @@ init_exec_safe();
|
|
|
466361
465924
|
init_output();
|
|
466362
465925
|
init_validate();
|
|
466363
465926
|
import { readFile as readFile31, access as access5, stat as stat3 } from "node:fs/promises";
|
|
466364
|
-
import { resolve as resolve59, basename as
|
|
465927
|
+
import { resolve as resolve59, basename as basename15 } from "node:path";
|
|
466365
465928
|
import { spawn as spawn9 } from "node:child_process";
|
|
466366
465929
|
async function resolveProjectPath(inputPath) {
|
|
466367
465930
|
const filePath = resolve59(process.cwd(), inputPath);
|
|
@@ -466576,7 +466139,7 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
466576
466139
|
}
|
|
466577
466140
|
const outputPath = options.output ? resolve59(process.cwd(), options.output) : resolve59(
|
|
466578
466141
|
process.cwd(),
|
|
466579
|
-
`${
|
|
466142
|
+
`${basename15(projectPath, ".vibe.json")}.${options.format}`
|
|
466580
466143
|
);
|
|
466581
466144
|
const basePresetSettings = getPresetSettings(options.preset, summary.aspectRatio);
|
|
466582
466145
|
const presetSettings = applyCustomOverrides(basePresetSettings, customOverrides);
|
|
@@ -467165,7 +466728,7 @@ function getPresetSettings(preset, aspectRatio) {
|
|
|
467165
466728
|
async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
467166
466729
|
spinner2.text = "Loading project...";
|
|
467167
466730
|
const { readFile: readFile34 } = await import("node:fs/promises");
|
|
467168
|
-
const { resolve: resolve64, basename:
|
|
466731
|
+
const { resolve: resolve64, basename: basename17 } = await import("node:path");
|
|
467169
466732
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
467170
466733
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
467171
466734
|
const { exitWithError: exitWithError2, generalError: generalError2, outputSuccess: outputSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
@@ -467174,7 +466737,7 @@ async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
|
467174
466737
|
const content = await readFile34(filePath, "utf-8");
|
|
467175
466738
|
const project = Project2.fromJSON(JSON.parse(content));
|
|
467176
466739
|
const state = project.getState();
|
|
467177
|
-
const outputPath = options.output ? resolve64(process.cwd(), options.output) : resolve64(process.cwd(), `${
|
|
466740
|
+
const outputPath = options.output ? resolve64(process.cwd(), options.output) : resolve64(process.cwd(), `${basename17(projectPath, ".vibe.json")}.${options.format ?? "mp4"}`);
|
|
467178
466741
|
const quality = ["draft", "standard", "high"].includes(options.preset ?? "") ? options.preset : "standard";
|
|
467179
466742
|
const backend = createHyperframesBackend2();
|
|
467180
466743
|
spinner2.text = "Rendering with Hyperframes...";
|
|
@@ -467239,7 +466802,7 @@ var exportTools = [exportVideoTool];
|
|
|
467239
466802
|
|
|
467240
466803
|
// ../cli/src/tools/manifest/agent-only.ts
|
|
467241
466804
|
import { readFile as readFile32, writeFile as writeFile42, readdir as readdir5, stat as stat4, access as access6, unlink as unlink7 } from "node:fs/promises";
|
|
467242
|
-
import { resolve as resolve61, join as join33, basename as
|
|
466805
|
+
import { resolve as resolve61, join as join33, basename as basename16, extname as extname12 } from "node:path";
|
|
467243
466806
|
import { z as z11 } from "zod";
|
|
467244
466807
|
init_engine();
|
|
467245
466808
|
init_exec_safe();
|
|
@@ -467437,7 +467000,7 @@ var batchImportTool = defineTool({
|
|
|
467437
467000
|
mediaFiles.sort();
|
|
467438
467001
|
const addedSources = [];
|
|
467439
467002
|
for (const mediaFile of mediaFiles) {
|
|
467440
|
-
const mediaName =
|
|
467003
|
+
const mediaName = basename16(mediaFile);
|
|
467441
467004
|
const mediaType = detectMediaType(mediaFile);
|
|
467442
467005
|
let duration = imageDuration;
|
|
467443
467006
|
if (mediaType !== "image") {
|