@vibeframe/cli 0.72.0 → 0.73.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 +1363 -390
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -14343,12 +14343,12 @@ var init_api_keys = __esm({
|
|
|
14343
14343
|
label: "ImgBB",
|
|
14344
14344
|
showInSetup: false,
|
|
14345
14345
|
// not prompted in setup wizard — internal upload host
|
|
14346
|
-
envExampleComment: "ImgBB API Key (image hosting \u2014 used by Kling and
|
|
14346
|
+
envExampleComment: "ImgBB API Key (image hosting \u2014 used by Kling and Seedance for image-to-video uploads)",
|
|
14347
14347
|
envExampleUrl: "https://api.imgbb.com/",
|
|
14348
14348
|
// ImgBB has no provider class (envvar-only); doctor still shows what it
|
|
14349
14349
|
// unlocks at the apiKey level.
|
|
14350
14350
|
commandsUnlocked: [
|
|
14351
|
-
"generate video -p kling/
|
|
14351
|
+
"generate video -p kling/seedance (image-to-video upload host)"
|
|
14352
14352
|
]
|
|
14353
14353
|
});
|
|
14354
14354
|
defineProvider({
|
|
@@ -15194,7 +15194,7 @@ var init_GeminiProvider = __esm({
|
|
|
15194
15194
|
* Sleep helper
|
|
15195
15195
|
*/
|
|
15196
15196
|
sleep(ms) {
|
|
15197
|
-
return new Promise((
|
|
15197
|
+
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
15198
15198
|
}
|
|
15199
15199
|
/**
|
|
15200
15200
|
* Extend a previously generated Veo video
|
|
@@ -19366,7 +19366,7 @@ var init_RunwayProvider = __esm({
|
|
|
19366
19366
|
* Sleep helper
|
|
19367
19367
|
*/
|
|
19368
19368
|
sleep(ms) {
|
|
19369
|
-
return new Promise((
|
|
19369
|
+
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
19370
19370
|
}
|
|
19371
19371
|
};
|
|
19372
19372
|
RunwayProvider.API_VERSION = "2024-11-06";
|
|
@@ -19807,7 +19807,7 @@ var init_KlingProvider = __esm({
|
|
|
19807
19807
|
* Sleep helper
|
|
19808
19808
|
*/
|
|
19809
19809
|
sleep(ms) {
|
|
19810
|
-
return new Promise((
|
|
19810
|
+
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
19811
19811
|
}
|
|
19812
19812
|
};
|
|
19813
19813
|
klingProvider = new KlingProvider();
|
|
@@ -20125,7 +20125,7 @@ var init_GrokProvider = __esm({
|
|
|
20125
20125
|
}
|
|
20126
20126
|
}
|
|
20127
20127
|
sleep(ms) {
|
|
20128
|
-
return new Promise((
|
|
20128
|
+
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
20129
20129
|
}
|
|
20130
20130
|
};
|
|
20131
20131
|
grokProvider = new GrokProvider();
|
|
@@ -20160,11 +20160,11 @@ var require_middleware = __commonJS({
|
|
|
20160
20160
|
"use strict";
|
|
20161
20161
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20162
20162
|
function adopt(value) {
|
|
20163
|
-
return value instanceof P ? value : new P(function(
|
|
20164
|
-
|
|
20163
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
20164
|
+
resolve64(value);
|
|
20165
20165
|
});
|
|
20166
20166
|
}
|
|
20167
|
-
return new (P || (P = Promise))(function(
|
|
20167
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
20168
20168
|
function fulfilled(value) {
|
|
20169
20169
|
try {
|
|
20170
20170
|
step(generator.next(value));
|
|
@@ -20180,7 +20180,7 @@ var require_middleware = __commonJS({
|
|
|
20180
20180
|
}
|
|
20181
20181
|
}
|
|
20182
20182
|
function step(result) {
|
|
20183
|
-
result.done ?
|
|
20183
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20184
20184
|
}
|
|
20185
20185
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20186
20186
|
});
|
|
@@ -20265,11 +20265,11 @@ var require_response = __commonJS({
|
|
|
20265
20265
|
"use strict";
|
|
20266
20266
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20267
20267
|
function adopt(value) {
|
|
20268
|
-
return value instanceof P ? value : new P(function(
|
|
20269
|
-
|
|
20268
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
20269
|
+
resolve64(value);
|
|
20270
20270
|
});
|
|
20271
20271
|
}
|
|
20272
|
-
return new (P || (P = Promise))(function(
|
|
20272
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
20273
20273
|
function fulfilled(value) {
|
|
20274
20274
|
try {
|
|
20275
20275
|
step(generator.next(value));
|
|
@@ -20285,7 +20285,7 @@ var require_response = __commonJS({
|
|
|
20285
20285
|
}
|
|
20286
20286
|
}
|
|
20287
20287
|
function step(result) {
|
|
20288
|
-
result.done ?
|
|
20288
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20289
20289
|
}
|
|
20290
20290
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20291
20291
|
});
|
|
@@ -20392,11 +20392,11 @@ var require_utils = __commonJS({
|
|
|
20392
20392
|
"use strict";
|
|
20393
20393
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20394
20394
|
function adopt(value) {
|
|
20395
|
-
return value instanceof P ? value : new P(function(
|
|
20396
|
-
|
|
20395
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
20396
|
+
resolve64(value);
|
|
20397
20397
|
});
|
|
20398
20398
|
}
|
|
20399
|
-
return new (P || (P = Promise))(function(
|
|
20399
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
20400
20400
|
function fulfilled(value) {
|
|
20401
20401
|
try {
|
|
20402
20402
|
step(generator.next(value));
|
|
@@ -20412,7 +20412,7 @@ var require_utils = __commonJS({
|
|
|
20412
20412
|
}
|
|
20413
20413
|
}
|
|
20414
20414
|
function step(result) {
|
|
20415
|
-
result.done ?
|
|
20415
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20416
20416
|
}
|
|
20417
20417
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20418
20418
|
});
|
|
@@ -20505,7 +20505,7 @@ var require_utils = __commonJS({
|
|
|
20505
20505
|
}
|
|
20506
20506
|
function sleep2(ms) {
|
|
20507
20507
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20508
|
-
return new Promise((
|
|
20508
|
+
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
20509
20509
|
});
|
|
20510
20510
|
}
|
|
20511
20511
|
}
|
|
@@ -20517,11 +20517,11 @@ var require_retry = __commonJS({
|
|
|
20517
20517
|
"use strict";
|
|
20518
20518
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20519
20519
|
function adopt(value) {
|
|
20520
|
-
return value instanceof P ? value : new P(function(
|
|
20521
|
-
|
|
20520
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
20521
|
+
resolve64(value);
|
|
20522
20522
|
});
|
|
20523
20523
|
}
|
|
20524
|
-
return new (P || (P = Promise))(function(
|
|
20524
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
20525
20525
|
function fulfilled(value) {
|
|
20526
20526
|
try {
|
|
20527
20527
|
step(generator.next(value));
|
|
@@ -20537,7 +20537,7 @@ var require_retry = __commonJS({
|
|
|
20537
20537
|
}
|
|
20538
20538
|
}
|
|
20539
20539
|
function step(result) {
|
|
20540
|
-
result.done ?
|
|
20540
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20541
20541
|
}
|
|
20542
20542
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20543
20543
|
});
|
|
@@ -20744,11 +20744,11 @@ var require_request = __commonJS({
|
|
|
20744
20744
|
"use strict";
|
|
20745
20745
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20746
20746
|
function adopt(value) {
|
|
20747
|
-
return value instanceof P ? value : new P(function(
|
|
20748
|
-
|
|
20747
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
20748
|
+
resolve64(value);
|
|
20749
20749
|
});
|
|
20750
20750
|
}
|
|
20751
|
-
return new (P || (P = Promise))(function(
|
|
20751
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
20752
20752
|
function fulfilled(value) {
|
|
20753
20753
|
try {
|
|
20754
20754
|
step(generator.next(value));
|
|
@@ -20764,7 +20764,7 @@ var require_request = __commonJS({
|
|
|
20764
20764
|
}
|
|
20765
20765
|
}
|
|
20766
20766
|
function step(result) {
|
|
20767
|
-
result.done ?
|
|
20767
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20768
20768
|
}
|
|
20769
20769
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20770
20770
|
});
|
|
@@ -20851,11 +20851,11 @@ var require_storage = __commonJS({
|
|
|
20851
20851
|
"use strict";
|
|
20852
20852
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
20853
20853
|
function adopt(value) {
|
|
20854
|
-
return value instanceof P ? value : new P(function(
|
|
20855
|
-
|
|
20854
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
20855
|
+
resolve64(value);
|
|
20856
20856
|
});
|
|
20857
20857
|
}
|
|
20858
|
-
return new (P || (P = Promise))(function(
|
|
20858
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
20859
20859
|
function fulfilled(value) {
|
|
20860
20860
|
try {
|
|
20861
20861
|
step(generator.next(value));
|
|
@@ -20871,7 +20871,7 @@ var require_storage = __commonJS({
|
|
|
20871
20871
|
}
|
|
20872
20872
|
}
|
|
20873
20873
|
function step(result) {
|
|
20874
|
-
result.done ?
|
|
20874
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
20875
20875
|
}
|
|
20876
20876
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20877
20877
|
});
|
|
@@ -21199,11 +21199,11 @@ var require_auth = __commonJS({
|
|
|
21199
21199
|
"use strict";
|
|
21200
21200
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
21201
21201
|
function adopt(value) {
|
|
21202
|
-
return value instanceof P ? value : new P(function(
|
|
21203
|
-
|
|
21202
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
21203
|
+
resolve64(value);
|
|
21204
21204
|
});
|
|
21205
21205
|
}
|
|
21206
|
-
return new (P || (P = Promise))(function(
|
|
21206
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
21207
21207
|
function fulfilled(value) {
|
|
21208
21208
|
try {
|
|
21209
21209
|
step(generator.next(value));
|
|
@@ -21219,7 +21219,7 @@ var require_auth = __commonJS({
|
|
|
21219
21219
|
}
|
|
21220
21220
|
}
|
|
21221
21221
|
function step(result) {
|
|
21222
|
-
result.done ?
|
|
21222
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
21223
21223
|
}
|
|
21224
21224
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21225
21225
|
});
|
|
@@ -21258,11 +21258,11 @@ var require_streaming = __commonJS({
|
|
|
21258
21258
|
"use strict";
|
|
21259
21259
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
21260
21260
|
function adopt(value) {
|
|
21261
|
-
return value instanceof P ? value : new P(function(
|
|
21262
|
-
|
|
21261
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
21262
|
+
resolve64(value);
|
|
21263
21263
|
});
|
|
21264
21264
|
}
|
|
21265
|
-
return new (P || (P = Promise))(function(
|
|
21265
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
21266
21266
|
function fulfilled(value) {
|
|
21267
21267
|
try {
|
|
21268
21268
|
step(generator.next(value));
|
|
@@ -21278,7 +21278,7 @@ var require_streaming = __commonJS({
|
|
|
21278
21278
|
}
|
|
21279
21279
|
}
|
|
21280
21280
|
function step(result) {
|
|
21281
|
-
result.done ?
|
|
21281
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
21282
21282
|
}
|
|
21283
21283
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21284
21284
|
});
|
|
@@ -21511,7 +21511,7 @@ var require_streaming = __commonJS({
|
|
|
21511
21511
|
query: options.queryParams
|
|
21512
21512
|
});
|
|
21513
21513
|
this.options = options;
|
|
21514
|
-
this.donePromise = new Promise((
|
|
21514
|
+
this.donePromise = new Promise((resolve64, reject) => {
|
|
21515
21515
|
if (this.streamClosed) {
|
|
21516
21516
|
reject(new response_1.ApiError({
|
|
21517
21517
|
message: "Streaming connection is already closed.",
|
|
@@ -21522,11 +21522,11 @@ var require_streaming = __commonJS({
|
|
|
21522
21522
|
}
|
|
21523
21523
|
this.signal.addEventListener("abort", () => {
|
|
21524
21524
|
var _a2;
|
|
21525
|
-
|
|
21525
|
+
resolve64((_a2 = this.currentData) !== null && _a2 !== void 0 ? _a2 : {});
|
|
21526
21526
|
});
|
|
21527
21527
|
this.on("done", (data) => {
|
|
21528
21528
|
this.streamClosed = true;
|
|
21529
|
-
|
|
21529
|
+
resolve64(data);
|
|
21530
21530
|
});
|
|
21531
21531
|
this.on("error", (error) => {
|
|
21532
21532
|
this.streamClosed = true;
|
|
@@ -21551,7 +21551,7 @@ var require_streaming = __commonJS({
|
|
|
21551
21551
|
if (data) {
|
|
21552
21552
|
yield yield __await(data);
|
|
21553
21553
|
}
|
|
21554
|
-
yield __await(new Promise((
|
|
21554
|
+
yield __await(new Promise((resolve64) => setTimeout(resolve64, 16)));
|
|
21555
21555
|
}
|
|
21556
21556
|
});
|
|
21557
21557
|
}
|
|
@@ -21596,11 +21596,11 @@ var require_queue = __commonJS({
|
|
|
21596
21596
|
"use strict";
|
|
21597
21597
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
21598
21598
|
function adopt(value) {
|
|
21599
|
-
return value instanceof P ? value : new P(function(
|
|
21600
|
-
|
|
21599
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
21600
|
+
resolve64(value);
|
|
21601
21601
|
});
|
|
21602
21602
|
}
|
|
21603
|
-
return new (P || (P = Promise))(function(
|
|
21603
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
21604
21604
|
function fulfilled(value) {
|
|
21605
21605
|
try {
|
|
21606
21606
|
step(generator.next(value));
|
|
@@ -21616,7 +21616,7 @@ var require_queue = __commonJS({
|
|
|
21616
21616
|
}
|
|
21617
21617
|
}
|
|
21618
21618
|
function step(result) {
|
|
21619
|
-
result.done ?
|
|
21619
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
21620
21620
|
}
|
|
21621
21621
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
21622
21622
|
});
|
|
@@ -21751,7 +21751,7 @@ var require_queue = __commonJS({
|
|
|
21751
21751
|
}
|
|
21752
21752
|
return doneStatus;
|
|
21753
21753
|
}
|
|
21754
|
-
return new Promise((
|
|
21754
|
+
return new Promise((resolve64, reject) => {
|
|
21755
21755
|
var _a;
|
|
21756
21756
|
let pollingTimeoutId;
|
|
21757
21757
|
const pollInterval = "pollInterval" in options && typeof options.pollInterval === "number" ? (_a = options.pollInterval) !== null && _a !== void 0 ? _a : DEFAULT_POLL_INTERVAL : DEFAULT_POLL_INTERVAL;
|
|
@@ -21783,7 +21783,7 @@ var require_queue = __commonJS({
|
|
|
21783
21783
|
}
|
|
21784
21784
|
if (requestStatus.status === "COMPLETED") {
|
|
21785
21785
|
clearScheduledTasks();
|
|
21786
|
-
|
|
21786
|
+
resolve64(requestStatus);
|
|
21787
21787
|
return;
|
|
21788
21788
|
}
|
|
21789
21789
|
pollingTimeoutId = setTimeout(poll, pollInterval);
|
|
@@ -23744,11 +23744,11 @@ var require_realtime = __commonJS({
|
|
|
23744
23744
|
"use strict";
|
|
23745
23745
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
23746
23746
|
function adopt(value) {
|
|
23747
|
-
return value instanceof P ? value : new P(function(
|
|
23748
|
-
|
|
23747
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
23748
|
+
resolve64(value);
|
|
23749
23749
|
});
|
|
23750
23750
|
}
|
|
23751
|
-
return new (P || (P = Promise))(function(
|
|
23751
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
23752
23752
|
function fulfilled(value) {
|
|
23753
23753
|
try {
|
|
23754
23754
|
step(generator.next(value));
|
|
@@ -23764,7 +23764,7 @@ var require_realtime = __commonJS({
|
|
|
23764
23764
|
}
|
|
23765
23765
|
}
|
|
23766
23766
|
function step(result) {
|
|
23767
|
-
result.done ?
|
|
23767
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
23768
23768
|
}
|
|
23769
23769
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23770
23770
|
});
|
|
@@ -24089,11 +24089,11 @@ var require_client = __commonJS({
|
|
|
24089
24089
|
"use strict";
|
|
24090
24090
|
var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) {
|
|
24091
24091
|
function adopt(value) {
|
|
24092
|
-
return value instanceof P ? value : new P(function(
|
|
24093
|
-
|
|
24092
|
+
return value instanceof P ? value : new P(function(resolve64) {
|
|
24093
|
+
resolve64(value);
|
|
24094
24094
|
});
|
|
24095
24095
|
}
|
|
24096
|
-
return new (P || (P = Promise))(function(
|
|
24096
|
+
return new (P || (P = Promise))(function(resolve64, reject) {
|
|
24097
24097
|
function fulfilled(value) {
|
|
24098
24098
|
try {
|
|
24099
24099
|
step(generator.next(value));
|
|
@@ -24109,7 +24109,7 @@ var require_client = __commonJS({
|
|
|
24109
24109
|
}
|
|
24110
24110
|
}
|
|
24111
24111
|
function step(result) {
|
|
24112
|
-
result.done ?
|
|
24112
|
+
result.done ? resolve64(result.value) : adopt(result.value).then(fulfilled, rejected);
|
|
24113
24113
|
}
|
|
24114
24114
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
24115
24115
|
});
|
|
@@ -24417,13 +24417,18 @@ var init_fal = __esm({
|
|
|
24417
24417
|
defineProvider({
|
|
24418
24418
|
id: "fal",
|
|
24419
24419
|
label: "fal.ai (Seedance 2.0)",
|
|
24420
|
+
displayName: "Seedance 2.0",
|
|
24421
|
+
gateway: "fal.ai",
|
|
24422
|
+
aliases: ["seedance"],
|
|
24423
|
+
models: ["seedance-2.0", "seedance-2.0-fast"],
|
|
24424
|
+
capabilities: ["text-to-video", "image-to-video", "native-audio"],
|
|
24420
24425
|
apiKey: "fal",
|
|
24421
24426
|
kinds: ["video"],
|
|
24422
24427
|
resolverPriority: { video: 1 },
|
|
24423
24428
|
commandsUnlocked: [
|
|
24424
|
-
"generate video -p
|
|
24425
|
-
"generate video -p
|
|
24426
|
-
"generate video -p
|
|
24429
|
+
"generate video -p seedance (Seedance 2.0 via fal.ai \u2014 default since v0.57)",
|
|
24430
|
+
"generate video -p seedance --seedance-model fast (lower-latency variant)",
|
|
24431
|
+
"generate video -p seedance -i <image> (image-to-video)"
|
|
24427
24432
|
]
|
|
24428
24433
|
});
|
|
24429
24434
|
}
|
|
@@ -24687,7 +24692,7 @@ var init_ReplicateProvider = __esm({
|
|
|
24687
24692
|
* Sleep helper
|
|
24688
24693
|
*/
|
|
24689
24694
|
sleep(ms) {
|
|
24690
|
-
return new Promise((
|
|
24695
|
+
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
24691
24696
|
}
|
|
24692
24697
|
/**
|
|
24693
24698
|
* Generate music from text prompt using MusicGen
|
|
@@ -25680,7 +25685,7 @@ async function prompt(question, hidden = false) {
|
|
|
25680
25685
|
input: process.stdin,
|
|
25681
25686
|
output: process.stdout
|
|
25682
25687
|
});
|
|
25683
|
-
return new Promise((
|
|
25688
|
+
return new Promise((resolve64) => {
|
|
25684
25689
|
if (hidden && process.stdin.isTTY) {
|
|
25685
25690
|
process.stdout.write(question);
|
|
25686
25691
|
let input = "";
|
|
@@ -25694,7 +25699,7 @@ async function prompt(question, hidden = false) {
|
|
|
25694
25699
|
process.stdin.removeListener("data", onData);
|
|
25695
25700
|
process.stdout.write("\n");
|
|
25696
25701
|
rl.close();
|
|
25697
|
-
|
|
25702
|
+
resolve64(input);
|
|
25698
25703
|
} else if (char === "") {
|
|
25699
25704
|
process.exit(1);
|
|
25700
25705
|
} else if (char === "\x7F" || char === "\b") {
|
|
@@ -25709,7 +25714,7 @@ async function prompt(question, hidden = false) {
|
|
|
25709
25714
|
} else {
|
|
25710
25715
|
rl.question(question, (answer) => {
|
|
25711
25716
|
rl.close();
|
|
25712
|
-
|
|
25717
|
+
resolve64(answer);
|
|
25713
25718
|
});
|
|
25714
25719
|
}
|
|
25715
25720
|
});
|
|
@@ -26040,6 +26045,54 @@ _Browse other named styles: \`vibe scene styles\`_
|
|
|
26040
26045
|
${style ? `_This file was seeded by \`vibe scene init --visual-style "${style.name}"\`._` : `_Seed this file from a named style: \`vibe scene init <dir> --visual-style "<name>"\`._`}
|
|
26041
26046
|
`;
|
|
26042
26047
|
}
|
|
26048
|
+
function buildStoryboardMd(name, duration = 12) {
|
|
26049
|
+
return `---
|
|
26050
|
+
title: ${name}
|
|
26051
|
+
duration: ${duration}
|
|
26052
|
+
aspect: 16:9
|
|
26053
|
+
tts: auto
|
|
26054
|
+
imageProvider: openai
|
|
26055
|
+
---
|
|
26056
|
+
|
|
26057
|
+
# ${name} \u2014 Storyboard
|
|
26058
|
+
|
|
26059
|
+
Edit these beats before running \`vibe build\`. Each beat starts with
|
|
26060
|
+
YAML cues that drive narration, backdrop generation, and timing.
|
|
26061
|
+
|
|
26062
|
+
## Beat hook \u2014 Hook
|
|
26063
|
+
|
|
26064
|
+
\`\`\`yaml
|
|
26065
|
+
narration: "Introduce the promise in one crisp sentence."
|
|
26066
|
+
backdrop: "Cinematic abstract technology backdrop, precise light, premium editorial feel"
|
|
26067
|
+
duration: 4
|
|
26068
|
+
\`\`\`
|
|
26069
|
+
|
|
26070
|
+
Show the core visual identity immediately. Keep copy short enough for one
|
|
26071
|
+
screen and one spoken breath.
|
|
26072
|
+
|
|
26073
|
+
## Beat proof \u2014 Proof
|
|
26074
|
+
|
|
26075
|
+
\`\`\`yaml
|
|
26076
|
+
narration: "Show the mechanism or proof point that makes the promise believable."
|
|
26077
|
+
backdrop: "Layered interface details, subtle motion trails, high-contrast product storytelling"
|
|
26078
|
+
duration: 4
|
|
26079
|
+
\`\`\`
|
|
26080
|
+
|
|
26081
|
+
Use this beat for the concrete differentiator: command, workflow, metric, or
|
|
26082
|
+
before/after.
|
|
26083
|
+
|
|
26084
|
+
## Beat close \u2014 Close
|
|
26085
|
+
|
|
26086
|
+
\`\`\`yaml
|
|
26087
|
+
narration: "Close with the action the viewer should remember."
|
|
26088
|
+
backdrop: "Resolved hero frame, confident final composition, clean negative space"
|
|
26089
|
+
duration: 4
|
|
26090
|
+
\`\`\`
|
|
26091
|
+
|
|
26092
|
+
End on the product name, offer, or command. Avoid adding a new idea in the
|
|
26093
|
+
final beat.
|
|
26094
|
+
`;
|
|
26095
|
+
}
|
|
26043
26096
|
function buildProjectClaudeMd(name) {
|
|
26044
26097
|
return `# ${name} \u2014 Scene Authoring Project
|
|
26045
26098
|
|
|
@@ -26069,7 +26122,7 @@ semantics, VibeFrame pipeline conventions) that are NOT in generic web docs.
|
|
|
26069
26122
|
| **vibe-scene** | \`/vibe-scene\` | VibeFrame's authoring loop, AI assets, lint feedback, pipeline integration |
|
|
26070
26123
|
| **gsap** | \`/gsap\` | GSAP tweens, timelines, easing |
|
|
26071
26124
|
|
|
26072
|
-
|
|
26125
|
+
Optional: install the upstream Hyperframes skills once per machine when your agent supports skill commands:
|
|
26073
26126
|
|
|
26074
26127
|
\`\`\`bash
|
|
26075
26128
|
npx skills add heygen-com/hyperframes
|
|
@@ -26082,6 +26135,7 @@ the framework-level minimum, not the cinematic craft layer.
|
|
|
26082
26135
|
## Project structure
|
|
26083
26136
|
|
|
26084
26137
|
- \`DESIGN.md\` \u2014 visual identity contract (palette, type, motion, transitions)
|
|
26138
|
+
- \`STORYBOARD.md\` \u2014 per-beat narration/backdrop/duration cues for \`vibe build\`
|
|
26085
26139
|
- \`index.html\` \u2014 root composition (timeline)
|
|
26086
26140
|
- \`compositions/scene-*.html\` \u2014 per-scene HTML authored by you or the agent
|
|
26087
26141
|
- \`assets/\` \u2014 shared media (narration audio, images, video)
|
|
@@ -26094,6 +26148,7 @@ the framework-level minimum, not the cinematic craft layer.
|
|
|
26094
26148
|
|
|
26095
26149
|
\`\`\`bash
|
|
26096
26150
|
vibe scene add <name> --narration "..." --visuals "..." # Author a new scene via AI
|
|
26151
|
+
vibe build # STORYBOARD.md \u2192 narrated MP4
|
|
26097
26152
|
vibe scene lint # Validate scenes (in-process HF linter)
|
|
26098
26153
|
vibe scene render # Render to MP4
|
|
26099
26154
|
|
|
@@ -26138,6 +26193,41 @@ tmp/
|
|
|
26138
26193
|
*.log
|
|
26139
26194
|
`;
|
|
26140
26195
|
}
|
|
26196
|
+
function isSceneScaffoldProfile(value) {
|
|
26197
|
+
return value === "minimal" || value === "agent" || value === "full";
|
|
26198
|
+
}
|
|
26199
|
+
function describeSceneScaffold(opts) {
|
|
26200
|
+
const dir = resolve5(opts.dir);
|
|
26201
|
+
const profile = opts.profile ?? "full";
|
|
26202
|
+
const groups = {
|
|
26203
|
+
authoring: [
|
|
26204
|
+
resolve5(dir, "STORYBOARD.md"),
|
|
26205
|
+
resolve5(dir, "DESIGN.md"),
|
|
26206
|
+
resolve5(dir, "vibe.project.yaml"),
|
|
26207
|
+
resolve5(dir, ".gitignore")
|
|
26208
|
+
],
|
|
26209
|
+
render: [],
|
|
26210
|
+
agent: []
|
|
26211
|
+
};
|
|
26212
|
+
if (profile === "full") {
|
|
26213
|
+
groups.render = [
|
|
26214
|
+
resolve5(dir, "index.html"),
|
|
26215
|
+
resolve5(dir, "compositions"),
|
|
26216
|
+
resolve5(dir, "assets"),
|
|
26217
|
+
resolve5(dir, "renders"),
|
|
26218
|
+
resolve5(dir, "hyperframes.json"),
|
|
26219
|
+
resolve5(dir, "meta.json")
|
|
26220
|
+
];
|
|
26221
|
+
}
|
|
26222
|
+
if (profile === "agent" || profile === "full") {
|
|
26223
|
+
groups.agent = [
|
|
26224
|
+
resolve5(dir, "SKILL.md"),
|
|
26225
|
+
resolve5(dir, "references"),
|
|
26226
|
+
resolve5(dir, "CLAUDE.md")
|
|
26227
|
+
];
|
|
26228
|
+
}
|
|
26229
|
+
return groups;
|
|
26230
|
+
}
|
|
26141
26231
|
async function pathExists(p) {
|
|
26142
26232
|
try {
|
|
26143
26233
|
await access3(p);
|
|
@@ -26152,37 +26242,43 @@ async function scaffoldSceneProject(opts) {
|
|
|
26152
26242
|
const aspect = opts.aspect ?? "16:9";
|
|
26153
26243
|
const duration = opts.duration ?? 10;
|
|
26154
26244
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
26245
|
+
const profile = opts.profile ?? "full";
|
|
26155
26246
|
await mkdir2(dir, { recursive: true });
|
|
26156
|
-
|
|
26157
|
-
|
|
26247
|
+
if (profile === "full") {
|
|
26248
|
+
await mkdir2(resolve5(dir, "compositions"), { recursive: true });
|
|
26249
|
+
await mkdir2(resolve5(dir, "assets"), { recursive: true });
|
|
26250
|
+
await mkdir2(resolve5(dir, "renders"), { recursive: true });
|
|
26251
|
+
}
|
|
26158
26252
|
const created = [];
|
|
26159
26253
|
const skipped2 = [];
|
|
26160
26254
|
const merged = [];
|
|
26161
|
-
|
|
26162
|
-
|
|
26163
|
-
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
|
|
26176
|
-
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26181
|
-
|
|
26182
|
-
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26255
|
+
if (profile === "full") {
|
|
26256
|
+
const hfPath = resolve5(dir, "hyperframes.json");
|
|
26257
|
+
const hfDefaults = buildHyperframesConfig();
|
|
26258
|
+
if (await pathExists(hfPath)) {
|
|
26259
|
+
const existingRaw = await readFile4(hfPath, "utf-8");
|
|
26260
|
+
const existing = JSON.parse(existingRaw);
|
|
26261
|
+
const mergedConfig = mergeHyperframesConfig(existing, hfDefaults);
|
|
26262
|
+
await writeFile4(hfPath, JSON.stringify(mergedConfig, null, 2) + "\n", "utf-8");
|
|
26263
|
+
merged.push(hfPath);
|
|
26264
|
+
} else {
|
|
26265
|
+
await writeFile4(hfPath, JSON.stringify(hfDefaults, null, 2) + "\n", "utf-8");
|
|
26266
|
+
created.push(hfPath);
|
|
26267
|
+
}
|
|
26268
|
+
const metaPath = resolve5(dir, "meta.json");
|
|
26269
|
+
if (await pathExists(metaPath)) {
|
|
26270
|
+
skipped2.push(metaPath);
|
|
26271
|
+
} else {
|
|
26272
|
+
await writeFile4(metaPath, JSON.stringify(buildHyperframesMeta(name, now), null, 2) + "\n", "utf-8");
|
|
26273
|
+
created.push(metaPath);
|
|
26274
|
+
}
|
|
26275
|
+
const rootPath = resolve5(dir, "index.html");
|
|
26276
|
+
if (await pathExists(rootPath)) {
|
|
26277
|
+
skipped2.push(rootPath);
|
|
26278
|
+
} else {
|
|
26279
|
+
await writeFile4(rootPath, buildEmptyRootHtml({ aspect, duration }), "utf-8");
|
|
26280
|
+
created.push(rootPath);
|
|
26281
|
+
}
|
|
26186
26282
|
}
|
|
26187
26283
|
const vibePath = resolve5(dir, "vibe.project.yaml");
|
|
26188
26284
|
if (await pathExists(vibePath)) {
|
|
@@ -26192,12 +26288,14 @@ async function scaffoldSceneProject(opts) {
|
|
|
26192
26288
|
await writeFile4(vibePath, (0, import_yaml2.stringify)(cfg), "utf-8");
|
|
26193
26289
|
created.push(vibePath);
|
|
26194
26290
|
}
|
|
26195
|
-
|
|
26196
|
-
|
|
26197
|
-
|
|
26198
|
-
|
|
26199
|
-
|
|
26200
|
-
|
|
26291
|
+
if (profile === "agent" || profile === "full") {
|
|
26292
|
+
const claudePath = resolve5(dir, "CLAUDE.md");
|
|
26293
|
+
if (await pathExists(claudePath)) {
|
|
26294
|
+
skipped2.push(claudePath);
|
|
26295
|
+
} else {
|
|
26296
|
+
await writeFile4(claudePath, buildProjectClaudeMd(name), "utf-8");
|
|
26297
|
+
created.push(claudePath);
|
|
26298
|
+
}
|
|
26201
26299
|
}
|
|
26202
26300
|
const designPath = resolve5(dir, "DESIGN.md");
|
|
26203
26301
|
if (await pathExists(designPath)) {
|
|
@@ -26210,6 +26308,13 @@ async function scaffoldSceneProject(opts) {
|
|
|
26210
26308
|
);
|
|
26211
26309
|
created.push(designPath);
|
|
26212
26310
|
}
|
|
26311
|
+
const storyboardPath = resolve5(dir, "STORYBOARD.md");
|
|
26312
|
+
if (await pathExists(storyboardPath)) {
|
|
26313
|
+
skipped2.push(storyboardPath);
|
|
26314
|
+
} else {
|
|
26315
|
+
await writeFile4(storyboardPath, buildStoryboardMd(name, duration), "utf-8");
|
|
26316
|
+
created.push(storyboardPath);
|
|
26317
|
+
}
|
|
26213
26318
|
const gitignorePath = resolve5(dir, ".gitignore");
|
|
26214
26319
|
if (await pathExists(gitignorePath)) {
|
|
26215
26320
|
skipped2.push(gitignorePath);
|
|
@@ -26217,7 +26322,7 @@ async function scaffoldSceneProject(opts) {
|
|
|
26217
26322
|
await writeFile4(gitignorePath, buildSceneGitignore(), "utf-8");
|
|
26218
26323
|
created.push(gitignorePath);
|
|
26219
26324
|
}
|
|
26220
|
-
return { created, skipped: skipped2, merged };
|
|
26325
|
+
return { created, skipped: skipped2, merged, groups: describeSceneScaffold({ dir, profile }) };
|
|
26221
26326
|
}
|
|
26222
26327
|
var import_yaml2, ASPECT_DIMS;
|
|
26223
26328
|
var init_scene_project = __esm({
|
|
@@ -27157,7 +27262,9 @@ function buildAudioMuxFilter(audios) {
|
|
|
27157
27262
|
const inputIdx = i + 1;
|
|
27158
27263
|
const delayMs = Math.max(0, Math.round(a.absoluteStart * 1e3));
|
|
27159
27264
|
const volume = Number.isFinite(a.volume) ? a.volume : 1;
|
|
27160
|
-
const
|
|
27265
|
+
const durationHint = typeof a.durationHint === "number" && Number.isFinite(a.durationHint) ? Math.max(0, a.durationHint) : null;
|
|
27266
|
+
const clipCap = Math.max(0, a.clipDurationCap);
|
|
27267
|
+
const trimSec = durationHint === null ? clipCap : Math.min(durationHint, clipCap);
|
|
27161
27268
|
const label = `a${i}`;
|
|
27162
27269
|
const stage = [
|
|
27163
27270
|
`[${inputIdx}:a]`,
|
|
@@ -27419,6 +27526,57 @@ var init_scene_render = __esm({
|
|
|
27419
27526
|
}
|
|
27420
27527
|
});
|
|
27421
27528
|
|
|
27529
|
+
// src/utils/audio.ts
|
|
27530
|
+
async function getAudioDuration(filePath) {
|
|
27531
|
+
try {
|
|
27532
|
+
return await ffprobeDuration(filePath);
|
|
27533
|
+
} catch (error) {
|
|
27534
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27535
|
+
throw new Error(`Failed to get audio duration: ${message}`);
|
|
27536
|
+
}
|
|
27537
|
+
}
|
|
27538
|
+
async function getVideoDuration(filePath) {
|
|
27539
|
+
try {
|
|
27540
|
+
return await ffprobeDuration(filePath);
|
|
27541
|
+
} catch (error) {
|
|
27542
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27543
|
+
throw new Error(`Failed to get video duration: ${message}`);
|
|
27544
|
+
}
|
|
27545
|
+
}
|
|
27546
|
+
async function extendVideoNaturally(videoPath, targetDuration, outputPath) {
|
|
27547
|
+
const videoDuration = await getVideoDuration(videoPath);
|
|
27548
|
+
const ratio = targetDuration / videoDuration;
|
|
27549
|
+
if (ratio <= 1) {
|
|
27550
|
+
const { copyFile: copyFile4 } = await import("node:fs/promises");
|
|
27551
|
+
await copyFile4(videoPath, outputPath);
|
|
27552
|
+
return;
|
|
27553
|
+
}
|
|
27554
|
+
if (ratio <= 1.15) {
|
|
27555
|
+
const slowFactor = (1 / ratio).toFixed(4);
|
|
27556
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
27557
|
+
} else if (ratio <= 1.4) {
|
|
27558
|
+
const slowFactor = (1 / ratio).toFixed(4);
|
|
27559
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
27560
|
+
} else {
|
|
27561
|
+
const slowRatio = 0.7;
|
|
27562
|
+
const slowedDuration = videoDuration / slowRatio;
|
|
27563
|
+
const freezeDuration = targetDuration - slowedDuration;
|
|
27564
|
+
if (freezeDuration <= 0) {
|
|
27565
|
+
const slowFactor = (1 / ratio).toFixed(4);
|
|
27566
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
27567
|
+
} else {
|
|
27568
|
+
const slowFactor = (1 / slowRatio).toFixed(4);
|
|
27569
|
+
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS,tpad=stop_mode=clone:stop_duration=${freezeDuration.toFixed(2)}`, "-an", outputPath]);
|
|
27570
|
+
}
|
|
27571
|
+
}
|
|
27572
|
+
}
|
|
27573
|
+
var init_audio = __esm({
|
|
27574
|
+
"src/utils/audio.ts"() {
|
|
27575
|
+
"use strict";
|
|
27576
|
+
init_exec_safe();
|
|
27577
|
+
}
|
|
27578
|
+
});
|
|
27579
|
+
|
|
27422
27580
|
// src/commands/_shared/hf-skill-bundle/bundle-content.ts
|
|
27423
27581
|
var SKILL_MD, HOUSE_STYLE_MD, MOTION_PRINCIPLES_MD, TYPOGRAPHY_MD, TRANSITIONS_MD;
|
|
27424
27582
|
var init_bundle_content = __esm({
|
|
@@ -29020,7 +29178,7 @@ async function getComposePrompts(opts) {
|
|
|
29020
29178
|
return baseError(`DESIGN.md not found at ${designPath}. Run \`vibe scene init <dir>\` first.`);
|
|
29021
29179
|
}
|
|
29022
29180
|
if (!existsSync6(storyboardPath)) {
|
|
29023
|
-
return baseError(`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\`
|
|
29181
|
+
return baseError(`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`);
|
|
29024
29182
|
}
|
|
29025
29183
|
if (!existsSync6(skillPath)) {
|
|
29026
29184
|
warnings.push(
|
|
@@ -29256,7 +29414,10 @@ async function executeSceneBuild(opts) {
|
|
|
29256
29414
|
const mode = resolveSceneBuildMode(opts);
|
|
29257
29415
|
const storyboardPath = join7(projectDir, "STORYBOARD.md");
|
|
29258
29416
|
if (!existsSync8(storyboardPath)) {
|
|
29259
|
-
return failBeforePrimitives(
|
|
29417
|
+
return failBeforePrimitives(
|
|
29418
|
+
`STORYBOARD.md not found at ${storyboardPath}. Run \`vibe scene init <dir>\` to create a starter, or add STORYBOARD.md with per-beat cues.`,
|
|
29419
|
+
startedAt
|
|
29420
|
+
);
|
|
29260
29421
|
}
|
|
29261
29422
|
const storyboardMd = await readFile10(storyboardPath, "utf-8");
|
|
29262
29423
|
const parsed = parseStoryboard(storyboardMd);
|
|
@@ -29336,6 +29497,13 @@ async function executeSceneBuild(opts) {
|
|
|
29336
29497
|
}
|
|
29337
29498
|
composeData = composeResult.data;
|
|
29338
29499
|
}
|
|
29500
|
+
if (!existsSync8(join7(projectDir, "index.html"))) {
|
|
29501
|
+
await scaffoldSceneProject({
|
|
29502
|
+
dir: projectDir,
|
|
29503
|
+
name: projectDir.split(/[\\/]/).filter(Boolean).pop(),
|
|
29504
|
+
profile: "full"
|
|
29505
|
+
});
|
|
29506
|
+
}
|
|
29339
29507
|
await syncRootClipReferences(parsed.beats, projectDir, beatOutcomes);
|
|
29340
29508
|
let outputPath;
|
|
29341
29509
|
let renderResult;
|
|
@@ -29434,6 +29602,7 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
29434
29602
|
ctx.onProgress({ type: "backdrop-cached", beatId: beat.id, path: rel });
|
|
29435
29603
|
return { status: "cached", path: rel };
|
|
29436
29604
|
}
|
|
29605
|
+
loadSceneBuildEnv(ctx.projectDir);
|
|
29437
29606
|
const apiKey = process.env.OPENAI_API_KEY ?? "";
|
|
29438
29607
|
if (!apiKey) {
|
|
29439
29608
|
const error = "OPENAI_API_KEY not set \u2014 cannot dispatch backdrop";
|
|
@@ -29462,6 +29631,18 @@ async function dispatchBackdrop(beat, ctx) {
|
|
|
29462
29631
|
});
|
|
29463
29632
|
return { status: "generated", path: rel };
|
|
29464
29633
|
}
|
|
29634
|
+
function loadSceneBuildEnv(projectDir) {
|
|
29635
|
+
(0, import_dotenv2.config)({ path: join7(projectDir, ".env"), quiet: true });
|
|
29636
|
+
(0, import_dotenv2.config)({ path: resolve12(process.cwd(), ".env"), quiet: true });
|
|
29637
|
+
let dir = process.cwd();
|
|
29638
|
+
while (dir !== dirname4(dir)) {
|
|
29639
|
+
if (existsSync8(join7(dir, "pnpm-workspace.yaml"))) {
|
|
29640
|
+
(0, import_dotenv2.config)({ path: join7(dir, ".env"), quiet: true });
|
|
29641
|
+
return;
|
|
29642
|
+
}
|
|
29643
|
+
dir = dirname4(dir);
|
|
29644
|
+
}
|
|
29645
|
+
}
|
|
29465
29646
|
async function skipped(kind, beatId, reason, ctx) {
|
|
29466
29647
|
ctx.onProgress({ type: `${kind}-skipped`, beatId, reason });
|
|
29467
29648
|
return { status: "skipped" };
|
|
@@ -29492,20 +29673,24 @@ async function syncRootClipReferences(beats, projectDir, outcomes) {
|
|
|
29492
29673
|
const clipLines = [];
|
|
29493
29674
|
const audioLines = [];
|
|
29494
29675
|
for (const beat of beats) {
|
|
29495
|
-
const
|
|
29676
|
+
const outcome = outcomes.find((o) => o.beatId === beat.id);
|
|
29677
|
+
const duration = await resolveBeatDuration({
|
|
29678
|
+
beatDuration: beat.duration,
|
|
29679
|
+
narrationPath: outcome?.narrationPath,
|
|
29680
|
+
projectDir
|
|
29681
|
+
});
|
|
29496
29682
|
const compositionId = `scene-${beat.id}`;
|
|
29497
29683
|
clipLines.push(
|
|
29498
29684
|
` <div class="clip" data-composition-id="${compositionId}" data-composition-src="compositions/${compositionId}.html" data-start="${cursor}" data-duration="${duration}" data-track-index="0"></div>`
|
|
29499
29685
|
);
|
|
29500
|
-
const outcome = outcomes.find((o) => o.beatId === beat.id);
|
|
29501
29686
|
if (outcome?.narrationPath) {
|
|
29502
29687
|
audioLines.push(
|
|
29503
|
-
` <audio src="${outcome.narrationPath}" data-start="${cursor}" data-duration="${duration}" data-track-index="2"></audio>`
|
|
29688
|
+
` <audio id="narration-${beat.id}" src="${outcome.narrationPath}" data-start="${cursor}" data-duration="${duration}" data-track-index="2"></audio>`
|
|
29504
29689
|
);
|
|
29505
29690
|
}
|
|
29506
29691
|
cursor += duration;
|
|
29507
29692
|
}
|
|
29508
|
-
const totalDuration = cursor;
|
|
29693
|
+
const totalDuration = Number(cursor.toFixed(2));
|
|
29509
29694
|
const block = " <!-- vibe-scene-build: clip refs (auto-generated; safe to re-run) -->\n" + clipLines.join("\n") + (audioLines.length > 0 ? "\n" + audioLines.join("\n") : "") + "\n <!-- /vibe-scene-build -->";
|
|
29510
29695
|
let next;
|
|
29511
29696
|
const markerRe = /\n? *<!-- vibe-scene-build: clip refs.*?<!-- \/vibe-scene-build -->/s;
|
|
@@ -29523,77 +29708,40 @@ ${block}
|
|
|
29523
29708
|
}
|
|
29524
29709
|
}
|
|
29525
29710
|
next = next.replace(
|
|
29526
|
-
/(id="root"[\s\S]*?data-duration=")(
|
|
29711
|
+
/(id="root"[\s\S]*?data-duration=")([^"]*)(")/,
|
|
29527
29712
|
`$1${totalDuration}$3`
|
|
29528
29713
|
);
|
|
29529
29714
|
if (next !== html) {
|
|
29530
29715
|
await writeFile7(rootPath, next, "utf-8");
|
|
29531
29716
|
}
|
|
29532
29717
|
}
|
|
29718
|
+
async function resolveBeatDuration(opts) {
|
|
29719
|
+
const storyboardMin = opts.beatDuration ?? 3;
|
|
29720
|
+
if (!opts.narrationPath) return Number(storyboardMin.toFixed(2));
|
|
29721
|
+
try {
|
|
29722
|
+
const audioDuration = await getAudioDuration(join7(opts.projectDir, opts.narrationPath));
|
|
29723
|
+
return Number(Math.max(storyboardMin, audioDuration + 0.5).toFixed(2));
|
|
29724
|
+
} catch {
|
|
29725
|
+
return Number(storyboardMin.toFixed(2));
|
|
29726
|
+
}
|
|
29727
|
+
}
|
|
29728
|
+
var import_dotenv2;
|
|
29533
29729
|
var init_scene_build = __esm({
|
|
29534
29730
|
"src/commands/_shared/scene-build.ts"() {
|
|
29535
29731
|
"use strict";
|
|
29732
|
+
import_dotenv2 = __toESM(require_main(), 1);
|
|
29536
29733
|
init_dist2();
|
|
29734
|
+
init_audio();
|
|
29537
29735
|
init_compose_scenes_skills();
|
|
29538
29736
|
init_compose_prompts();
|
|
29539
29737
|
init_agent_host_detect();
|
|
29540
29738
|
init_scene_render();
|
|
29541
29739
|
init_storyboard_parse();
|
|
29740
|
+
init_scene_project();
|
|
29542
29741
|
init_tts_resolve();
|
|
29543
29742
|
}
|
|
29544
29743
|
});
|
|
29545
29744
|
|
|
29546
|
-
// src/utils/audio.ts
|
|
29547
|
-
async function getAudioDuration(filePath) {
|
|
29548
|
-
try {
|
|
29549
|
-
return await ffprobeDuration(filePath);
|
|
29550
|
-
} catch (error) {
|
|
29551
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
29552
|
-
throw new Error(`Failed to get audio duration: ${message}`);
|
|
29553
|
-
}
|
|
29554
|
-
}
|
|
29555
|
-
async function getVideoDuration(filePath) {
|
|
29556
|
-
try {
|
|
29557
|
-
return await ffprobeDuration(filePath);
|
|
29558
|
-
} catch (error) {
|
|
29559
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
29560
|
-
throw new Error(`Failed to get video duration: ${message}`);
|
|
29561
|
-
}
|
|
29562
|
-
}
|
|
29563
|
-
async function extendVideoNaturally(videoPath, targetDuration, outputPath) {
|
|
29564
|
-
const videoDuration = await getVideoDuration(videoPath);
|
|
29565
|
-
const ratio = targetDuration / videoDuration;
|
|
29566
|
-
if (ratio <= 1) {
|
|
29567
|
-
const { copyFile: copyFile4 } = await import("node:fs/promises");
|
|
29568
|
-
await copyFile4(videoPath, outputPath);
|
|
29569
|
-
return;
|
|
29570
|
-
}
|
|
29571
|
-
if (ratio <= 1.15) {
|
|
29572
|
-
const slowFactor = (1 / ratio).toFixed(4);
|
|
29573
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
29574
|
-
} else if (ratio <= 1.4) {
|
|
29575
|
-
const slowFactor = (1 / ratio).toFixed(4);
|
|
29576
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
29577
|
-
} else {
|
|
29578
|
-
const slowRatio = 0.7;
|
|
29579
|
-
const slowedDuration = videoDuration / slowRatio;
|
|
29580
|
-
const freezeDuration = targetDuration - slowedDuration;
|
|
29581
|
-
if (freezeDuration <= 0) {
|
|
29582
|
-
const slowFactor = (1 / ratio).toFixed(4);
|
|
29583
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `minterpolate=fps=60:mi_mode=mci,setpts=${slowFactor}*PTS`, "-an", outputPath]);
|
|
29584
|
-
} else {
|
|
29585
|
-
const slowFactor = (1 / slowRatio).toFixed(4);
|
|
29586
|
-
await execSafe("ffmpeg", ["-y", "-i", videoPath, "-filter:v", `setpts=${slowFactor}*PTS,tpad=stop_mode=clone:stop_duration=${freezeDuration.toFixed(2)}`, "-an", outputPath]);
|
|
29587
|
-
}
|
|
29588
|
-
}
|
|
29589
|
-
}
|
|
29590
|
-
var init_audio = __esm({
|
|
29591
|
-
"src/utils/audio.ts"() {
|
|
29592
|
-
"use strict";
|
|
29593
|
-
init_exec_safe();
|
|
29594
|
-
}
|
|
29595
|
-
});
|
|
29596
|
-
|
|
29597
29745
|
// src/commands/_shared/install-skill.ts
|
|
29598
29746
|
import { mkdir as mkdir6, writeFile as writeFile8 } from "node:fs/promises";
|
|
29599
29747
|
import { existsSync as existsSync9 } from "node:fs";
|
|
@@ -29730,6 +29878,30 @@ function validateVisualStyle(value) {
|
|
|
29730
29878
|
}
|
|
29731
29879
|
return found;
|
|
29732
29880
|
}
|
|
29881
|
+
function formatSceneInitProfile(profile) {
|
|
29882
|
+
if (profile === "minimal") return "authoring files only; build will add render scaffold when needed";
|
|
29883
|
+
if (profile === "agent") return "authoring files plus local composition rules for host agents";
|
|
29884
|
+
return "complete authoring, agent, and render scaffold";
|
|
29885
|
+
}
|
|
29886
|
+
function printSceneInitDryRun(opts) {
|
|
29887
|
+
console.log();
|
|
29888
|
+
console.log(source_default.bold.cyan("VibeFrame Scene Init - dry run"));
|
|
29889
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
29890
|
+
console.log(` Project: ${source_default.bold(opts.dir)}`);
|
|
29891
|
+
console.log(` Name: ${source_default.bold(opts.name)}`);
|
|
29892
|
+
console.log(` Profile: ${source_default.bold(opts.profile)} ${source_default.dim(formatSceneInitProfile(opts.profile))}`);
|
|
29893
|
+
console.log(` Aspect: ${opts.aspect}`);
|
|
29894
|
+
console.log(` Duration: ${opts.duration}s`);
|
|
29895
|
+
console.log(` Visual style: ${opts.visualStyleName ?? "none"}`);
|
|
29896
|
+
console.log();
|
|
29897
|
+
console.log(source_default.bold.cyan("Files that would be prepared"));
|
|
29898
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
29899
|
+
for (const file of opts.groups.authoring) console.log(` authoring ${file}`);
|
|
29900
|
+
for (const file of opts.groups.agent) console.log(` agent ${file}`);
|
|
29901
|
+
for (const file of opts.groups.render) console.log(` render ${file}`);
|
|
29902
|
+
console.log();
|
|
29903
|
+
console.log(source_default.dim("No files were written."));
|
|
29904
|
+
}
|
|
29733
29905
|
async function pathExists2(p) {
|
|
29734
29906
|
try {
|
|
29735
29907
|
await access4(p);
|
|
@@ -30041,7 +30213,7 @@ function formatPrimitiveStatus(status, path2) {
|
|
|
30041
30213
|
return status;
|
|
30042
30214
|
}
|
|
30043
30215
|
}
|
|
30044
|
-
var import_yaml5, VALID_ASPECTS2, sceneCommand, VALID_INSTALL_SKILL_HOSTS, VALID_FPS, VALID_QUALITIES, VALID_FORMATS;
|
|
30216
|
+
var import_yaml5, VALID_ASPECTS2, VALID_SCENE_INIT_PROFILES, sceneCommand, VALID_INSTALL_SKILL_HOSTS, VALID_FPS, VALID_QUALITIES, VALID_FORMATS;
|
|
30045
30217
|
var init_scene = __esm({
|
|
30046
30218
|
"src/commands/scene.ts"() {
|
|
30047
30219
|
"use strict";
|
|
@@ -30064,7 +30236,8 @@ var init_scene = __esm({
|
|
|
30064
30236
|
init_install_skill();
|
|
30065
30237
|
init_compose_prompts();
|
|
30066
30238
|
VALID_ASPECTS2 = ["16:9", "9:16", "1:1", "4:5"];
|
|
30067
|
-
|
|
30239
|
+
VALID_SCENE_INIT_PROFILES = ["minimal", "agent", "full"];
|
|
30240
|
+
sceneCommand = new Command("scene").description("Advanced scene commands for VibeFrame video projects").addHelpText("after", `
|
|
30068
30241
|
Examples:
|
|
30069
30242
|
$ vibe scene init my-video # Scaffold a new project
|
|
30070
30243
|
$ vibe scene init my-video -r 9:16 -d 30 # Vertical 30s project
|
|
@@ -30072,22 +30245,32 @@ Examples:
|
|
|
30072
30245
|
--headline "Welcome to VibeFrame" # Headline-only scene
|
|
30073
30246
|
$ vibe scene add overview --narration "VibeFrame turns scripts into video." \\
|
|
30074
30247
|
--visuals "studio desk, soft lighting" # AI narration + image
|
|
30075
|
-
$ vibe scene lint # Validate every scene against
|
|
30248
|
+
$ vibe scene lint # Validate every scene against composition rules
|
|
30076
30249
|
$ vibe scene lint --fix # Auto-fix mechanical issues (e.g. missing class="clip")
|
|
30077
30250
|
$ vibe scene lint --json # Structured output for agent loops
|
|
30078
30251
|
$ vibe scene render # Render to renders/<name>-<timestamp>.mp4
|
|
30079
30252
|
$ vibe scene render -o demo.mp4 --quality high # Custom output path + quality
|
|
30080
30253
|
$ vibe scene render --fps 60 --format webm # 60fps WebM render
|
|
30081
30254
|
|
|
30082
|
-
|
|
30255
|
+
Most users can start with \`vibe init\`, \`vibe build\`, and \`vibe render\`.
|
|
30256
|
+
This namespace exposes lower-level scene authoring and rendering controls.
|
|
30083
30257
|
Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
30084
|
-
sceneCommand.command("init").description("Scaffold a new scene project (or safely augment an existing
|
|
30258
|
+
sceneCommand.command("init").description("Scaffold a new scene project (or safely augment an existing project)").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) => {
|
|
30085
30259
|
const startedAt = Date.now();
|
|
30086
30260
|
const aspect = validateAspect(options.ratio);
|
|
30087
30261
|
const duration = validateDuration(options.duration);
|
|
30088
30262
|
const name = options.name ?? basename4(dir.replace(/\/+$/, ""));
|
|
30089
30263
|
const visualStyle = options.visualStyle ? validateVisualStyle(options.visualStyle) : void 0;
|
|
30264
|
+
const profile = String(options.profile ?? "agent");
|
|
30265
|
+
if (!isSceneScaffoldProfile(profile)) {
|
|
30266
|
+
exitWithError(usageError(`Invalid --profile: ${profile}`, `Must be one of: ${VALID_SCENE_INIT_PROFILES.join(", ")}`));
|
|
30267
|
+
}
|
|
30268
|
+
const groups = describeSceneScaffold({ dir, profile });
|
|
30090
30269
|
if (options.dryRun) {
|
|
30270
|
+
if (!isJsonMode() && !isQuietMode()) {
|
|
30271
|
+
printSceneInitDryRun({ dir, name, aspect, duration, visualStyleName: visualStyle?.name ?? null, profile, groups });
|
|
30272
|
+
return;
|
|
30273
|
+
}
|
|
30091
30274
|
outputSuccess({
|
|
30092
30275
|
command: "scene init",
|
|
30093
30276
|
startedAt,
|
|
@@ -30098,23 +30281,25 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30098
30281
|
name,
|
|
30099
30282
|
aspect,
|
|
30100
30283
|
duration,
|
|
30101
|
-
visualStyle: visualStyle?.name ?? null
|
|
30102
|
-
|
|
30284
|
+
visualStyle: visualStyle?.name ?? null,
|
|
30285
|
+
profile
|
|
30286
|
+
},
|
|
30287
|
+
groups
|
|
30103
30288
|
}
|
|
30104
30289
|
});
|
|
30105
30290
|
return;
|
|
30106
30291
|
}
|
|
30107
|
-
const spinner2 = isJsonMode() ? null : ora(`Scaffolding scene project at ${dir}...`).start();
|
|
30292
|
+
const spinner2 = isJsonMode() || isQuietMode() ? null : ora(`Scaffolding scene project at ${dir}...`).start();
|
|
30108
30293
|
try {
|
|
30109
|
-
const result = await scaffoldSceneProject({ dir, name, aspect, duration, visualStyle });
|
|
30294
|
+
const result = await scaffoldSceneProject({ dir, name, aspect, duration, visualStyle, profile });
|
|
30110
30295
|
const detectedIds = detectedAgentHosts().map((h) => h.id);
|
|
30111
30296
|
const skillHosts = deriveInstallHosts(detectedIds);
|
|
30112
30297
|
const projectAbs = resolve14(dir);
|
|
30113
|
-
const skillResult = await installHyperframesSkill({
|
|
30298
|
+
const skillResult = profile === "agent" || profile === "full" ? await installHyperframesSkill({
|
|
30114
30299
|
projectDir: projectAbs,
|
|
30115
30300
|
hosts: skillHosts
|
|
30116
|
-
});
|
|
30117
|
-
if (isJsonMode()) {
|
|
30301
|
+
}) : { success: true, files: [], bundleVersion: "not-installed" };
|
|
30302
|
+
if (isJsonMode() || isQuietMode()) {
|
|
30118
30303
|
outputSuccess({
|
|
30119
30304
|
command: "scene init",
|
|
30120
30305
|
startedAt,
|
|
@@ -30124,16 +30309,27 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30124
30309
|
aspect,
|
|
30125
30310
|
duration,
|
|
30126
30311
|
visualStyle: visualStyle?.name ?? null,
|
|
30312
|
+
profile,
|
|
30127
30313
|
created: result.created,
|
|
30128
30314
|
merged: result.merged,
|
|
30129
30315
|
skipped: result.skipped,
|
|
30316
|
+
groups: result.groups,
|
|
30130
30317
|
skillFiles: skillResult.files,
|
|
30131
30318
|
skillBundleVersion: skillResult.bundleVersion
|
|
30132
30319
|
}
|
|
30133
30320
|
});
|
|
30134
30321
|
return;
|
|
30135
30322
|
}
|
|
30136
|
-
spinner2?.succeed(source_default.green(`
|
|
30323
|
+
spinner2?.succeed(source_default.green(`Video project ready: ${dir}`));
|
|
30324
|
+
console.log();
|
|
30325
|
+
console.log(source_default.bold.cyan("Edit first"));
|
|
30326
|
+
console.log(source_default.dim("\u2500".repeat(60)));
|
|
30327
|
+
console.log(` ${source_default.bold("STORYBOARD.md")} ${source_default.dim("# beats: narration, backdrop, minimum duration")}`);
|
|
30328
|
+
console.log(` ${source_default.bold("DESIGN.md")} ${source_default.dim("# palette, typography, motion rules")}`);
|
|
30329
|
+
console.log();
|
|
30330
|
+
console.log(source_default.bold.cyan("Profile"));
|
|
30331
|
+
console.log(source_default.dim("\u2500".repeat(60)));
|
|
30332
|
+
console.log(` ${source_default.bold(profile)} ${source_default.dim(formatSceneInitProfile(profile))}`);
|
|
30137
30333
|
console.log();
|
|
30138
30334
|
console.log(source_default.bold.cyan("Files"));
|
|
30139
30335
|
console.log(source_default.dim("\u2500".repeat(60)));
|
|
@@ -30144,7 +30340,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30144
30340
|
const skillSkipped = skillResult.files.filter((f) => f.status === "skipped-exists");
|
|
30145
30341
|
if (skillWritten.length + skillSkipped.length > 0) {
|
|
30146
30342
|
console.log();
|
|
30147
|
-
console.log(source_default.bold.cyan("
|
|
30343
|
+
console.log(source_default.bold.cyan("Composition rules"));
|
|
30148
30344
|
console.log(source_default.dim("\u2500".repeat(60)));
|
|
30149
30345
|
for (const f of skillWritten) console.log(source_default.green(" +"), f.path);
|
|
30150
30346
|
for (const f of skillSkipped) console.log(source_default.dim(" \xB7"), f.path, source_default.dim("(kept existing)"));
|
|
@@ -30158,10 +30354,14 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30158
30354
|
} else {
|
|
30159
30355
|
console.log(` ${source_default.cyan("vibe scene styles")} ${source_default.dim("# pick a named style for DESIGN.md")}`);
|
|
30160
30356
|
}
|
|
30161
|
-
|
|
30357
|
+
if (profile === "agent" || profile === "full") {
|
|
30358
|
+
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.")}`);
|
|
30359
|
+
} else {
|
|
30360
|
+
console.log(` ${source_default.cyan("vibe scene install-skill")} ${source_default.dim("# add agent authoring rules later")}`);
|
|
30361
|
+
}
|
|
30162
30362
|
console.log(` ${source_default.cyan("vibe scene add")} <name> ${source_default.dim("# fallback: 5-preset emit (no agent)")}`);
|
|
30163
|
-
console.log(` ${source_default.cyan("vibe
|
|
30164
|
-
console.log(` ${source_default.cyan("vibe
|
|
30363
|
+
console.log(` ${source_default.cyan("vibe build")} ${source_default.dim("# build STORYBOARD.md into scenes/assets")}`);
|
|
30364
|
+
console.log(` ${source_default.cyan("vibe render")} ${source_default.dim("# render to video")}`);
|
|
30165
30365
|
} catch (error) {
|
|
30166
30366
|
spinner2?.fail("Failed to scaffold scene project");
|
|
30167
30367
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -30449,7 +30649,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30449
30649
|
exitWithError(generalError(msg));
|
|
30450
30650
|
}
|
|
30451
30651
|
});
|
|
30452
|
-
sceneCommand.command("lint").description("Validate scene HTML against
|
|
30652
|
+
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 (root, options) => {
|
|
30453
30653
|
const startedAt = Date.now();
|
|
30454
30654
|
const projectDir = resolve14(options.project);
|
|
30455
30655
|
if (!await rootExists(projectDir, root)) {
|
|
@@ -30705,7 +30905,7 @@ Run 'vibe schema scene.<command>' for structured parameter info.`);
|
|
|
30705
30905
|
}
|
|
30706
30906
|
}
|
|
30707
30907
|
console.log();
|
|
30708
|
-
console.log(source_default.dim("Once you've authored each beat's HTML, re-run `vibe
|
|
30908
|
+
console.log(source_default.dim("Once you've authored each beat's HTML, re-run `vibe build` to lint + render."));
|
|
30709
30909
|
console.log(source_default.dim("Or pass `--mode batch` to use the internal LLM compose path instead."));
|
|
30710
30910
|
return;
|
|
30711
30911
|
}
|
|
@@ -32463,16 +32663,16 @@ async function prompt2(question) {
|
|
|
32463
32663
|
input,
|
|
32464
32664
|
output: process.stdout
|
|
32465
32665
|
});
|
|
32466
|
-
return new Promise((
|
|
32666
|
+
return new Promise((resolve64) => {
|
|
32467
32667
|
rl.question(question, (answer) => {
|
|
32468
32668
|
rl.close();
|
|
32469
|
-
|
|
32669
|
+
resolve64(answer);
|
|
32470
32670
|
});
|
|
32471
32671
|
});
|
|
32472
32672
|
}
|
|
32473
32673
|
async function promptHidden(question) {
|
|
32474
32674
|
const input = getTTYInputStream();
|
|
32475
|
-
return new Promise((
|
|
32675
|
+
return new Promise((resolve64) => {
|
|
32476
32676
|
process.stdout.write(question);
|
|
32477
32677
|
let value = "";
|
|
32478
32678
|
if (typeof input.setRawMode === "function") {
|
|
@@ -32484,7 +32684,7 @@ async function promptHidden(question) {
|
|
|
32484
32684
|
input.setRawMode(false);
|
|
32485
32685
|
input.removeListener("data", onData);
|
|
32486
32686
|
process.stdout.write("\n");
|
|
32487
|
-
|
|
32687
|
+
resolve64(value);
|
|
32488
32688
|
} else if (char === "") {
|
|
32489
32689
|
input.setRawMode(false);
|
|
32490
32690
|
process.stdout.write("\n");
|
|
@@ -32504,7 +32704,7 @@ async function promptHidden(question) {
|
|
|
32504
32704
|
const rl = createInterface2({ input, output: process.stdout });
|
|
32505
32705
|
rl.question("", (answer) => {
|
|
32506
32706
|
rl.close();
|
|
32507
|
-
|
|
32707
|
+
resolve64(answer);
|
|
32508
32708
|
});
|
|
32509
32709
|
}
|
|
32510
32710
|
});
|
|
@@ -32512,7 +32712,7 @@ async function promptHidden(question) {
|
|
|
32512
32712
|
async function promptSelect(_question, options, defaultIndex = 0) {
|
|
32513
32713
|
const input = getTTYInputStream();
|
|
32514
32714
|
if (typeof input.setRawMode === "function") {
|
|
32515
|
-
return new Promise((
|
|
32715
|
+
return new Promise((resolve64) => {
|
|
32516
32716
|
let selected = defaultIndex;
|
|
32517
32717
|
const render = () => {
|
|
32518
32718
|
if (renderCount > 0) {
|
|
@@ -32536,7 +32736,7 @@ async function promptSelect(_question, options, defaultIndex = 0) {
|
|
|
32536
32736
|
input.setRawMode(false);
|
|
32537
32737
|
input.removeListener("data", onData);
|
|
32538
32738
|
process.stdout.write("\n");
|
|
32539
|
-
|
|
32739
|
+
resolve64(selected);
|
|
32540
32740
|
} else if (char === "") {
|
|
32541
32741
|
input.setRawMode(false);
|
|
32542
32742
|
process.stdout.write("\n");
|
|
@@ -32553,7 +32753,7 @@ async function promptSelect(_question, options, defaultIndex = 0) {
|
|
|
32553
32753
|
input.removeListener("data", onData);
|
|
32554
32754
|
render();
|
|
32555
32755
|
process.stdout.write("\n");
|
|
32556
|
-
|
|
32756
|
+
resolve64(selected);
|
|
32557
32757
|
}
|
|
32558
32758
|
};
|
|
32559
32759
|
input.on("data", onData);
|
|
@@ -35343,7 +35543,7 @@ var init_dist3 = __esm({
|
|
|
35343
35543
|
import { writeFile as writeFile23, unlink as unlink2, rename as rename2 } from "node:fs/promises";
|
|
35344
35544
|
import { resolve as resolve27, basename as basename7 } from "node:path";
|
|
35345
35545
|
function sleep(ms) {
|
|
35346
|
-
return new Promise((
|
|
35546
|
+
return new Promise((resolve64) => setTimeout(resolve64, ms));
|
|
35347
35547
|
}
|
|
35348
35548
|
async function uploadToImgbb(imageBuffer, apiKey) {
|
|
35349
35549
|
try {
|
|
@@ -36076,9 +36276,14 @@ var init_ai_script_pipeline = __esm({
|
|
|
36076
36276
|
import { resolve as resolve29 } from "node:path";
|
|
36077
36277
|
import { readFile as readFile17, writeFile as writeFile25 } from "node:fs/promises";
|
|
36078
36278
|
function registerVideoCommand(parent) {
|
|
36079
|
-
parent.command("video").alias("vid").description("Generate video using AI (Kling, Runway,
|
|
36279
|
+
parent.command("video").alias("vid").description("Generate video using AI (Seedance, Grok, Kling, Runway, or Veo)").argument("[prompt]", "Text prompt describing the video (interactive if omitted)").option("-p, --provider <provider>", "Provider: seedance (ByteDance Seedance 2.0 via fal.ai), grok, kling, runway, veo. `fal` is a backwards-compatible alias for seedance.").option("-k, --api-key <key>", "API key (or set FAL_KEY / XAI_API_KEY / RUNWAY_API_SECRET / KLING_API_KEY / GOOGLE_API_KEY env)").option("-o, --output <path>", "Output file path (downloads video)").option("-i, --image <path>", "Reference image for image-to-video").option(
|
|
36280
|
+
"-d, --duration <sec>",
|
|
36281
|
+
"Duration in seconds. Seedance accepts 4-15 (`fal` alias supported); Kling accepts 5 or 10; Veo maps to 6 or 8.",
|
|
36282
|
+
"5"
|
|
36283
|
+
).option("-r, --ratio <ratio>", "Aspect ratio: 16:9, 9:16, or 1:1 (auto-detected from image if omitted)").option("-s, --seed <number>", "Random seed for reproducibility (Runway only)").option("-m, --mode <mode>", "Generation mode: std or pro (Kling only)", "std").option("--seedance-model <model>", "Seedance variant: quality or fast (fal.ai only)", "quality").option("-n, --negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option("--ref-images <paths...>", "Reference images for character consistency (Veo 3.1 only, max 3)").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option("--runway-model <model>", "Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)", "gen4.5").option("--no-wait", "Start generation and return task ID without waiting").option("--dry-run", "Preview parameters without executing").addHelpText("after", `
|
|
36080
36284
|
Examples:
|
|
36081
|
-
$ vibe generate video "dancing cat" -o cat.mp4 #
|
|
36285
|
+
$ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
|
|
36286
|
+
$ vibe gen vid "cinematic city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
|
|
36082
36287
|
$ vibe gen vid "city timelapse" -o city.mp4 -p kling # Kling
|
|
36083
36288
|
$ vibe gen vid "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
|
|
36084
36289
|
$ vibe gen vid "ocean waves" -o waves.mp4 -p veo --resolution 1080p # Veo
|
|
@@ -36104,12 +36309,13 @@ Examples:
|
|
|
36104
36309
|
if (options.output) {
|
|
36105
36310
|
validateOutputPath(options.output);
|
|
36106
36311
|
}
|
|
36107
|
-
const validProviders = ["runway", "kling", "veo", "grok", "fal"];
|
|
36312
|
+
const validProviders = ["runway", "kling", "veo", "grok", "seedance", "fal"];
|
|
36108
36313
|
const videoEnvMap = {
|
|
36109
36314
|
grok: "XAI_API_KEY",
|
|
36110
36315
|
veo: "GOOGLE_API_KEY",
|
|
36111
36316
|
kling: "KLING_API_KEY",
|
|
36112
36317
|
runway: "RUNWAY_API_SECRET",
|
|
36318
|
+
seedance: "FAL_KEY",
|
|
36113
36319
|
fal: "FAL_KEY"
|
|
36114
36320
|
};
|
|
36115
36321
|
let provider;
|
|
@@ -36119,7 +36325,7 @@ Examples:
|
|
|
36119
36325
|
exitWithError(
|
|
36120
36326
|
usageError(
|
|
36121
36327
|
`Invalid provider: ${provider}`,
|
|
36122
|
-
|
|
36328
|
+
"Available providers: seedance, grok, kling, runway, veo. `fal` is a backwards-compatible alias for seedance."
|
|
36123
36329
|
)
|
|
36124
36330
|
);
|
|
36125
36331
|
}
|
|
@@ -36182,14 +36388,15 @@ Examples:
|
|
|
36182
36388
|
data: {
|
|
36183
36389
|
params: {
|
|
36184
36390
|
prompt: prompt3,
|
|
36185
|
-
provider,
|
|
36391
|
+
provider: provider === "fal" ? "seedance" : provider,
|
|
36186
36392
|
duration: options.duration,
|
|
36187
36393
|
ratio: options.ratio,
|
|
36188
36394
|
image: options.image,
|
|
36189
36395
|
mode: options.mode,
|
|
36190
36396
|
negative: options.negative,
|
|
36191
36397
|
resolution: options.resolution,
|
|
36192
|
-
veoModel: options.veoModel
|
|
36398
|
+
veoModel: options.veoModel,
|
|
36399
|
+
seedanceModel: options.seedanceModel
|
|
36193
36400
|
}
|
|
36194
36401
|
}
|
|
36195
36402
|
});
|
|
@@ -36200,6 +36407,7 @@ Examples:
|
|
|
36200
36407
|
kling: "KLING_API_KEY",
|
|
36201
36408
|
veo: "GOOGLE_API_KEY",
|
|
36202
36409
|
grok: "XAI_API_KEY",
|
|
36410
|
+
seedance: "FAL_KEY",
|
|
36203
36411
|
fal: "FAL_KEY"
|
|
36204
36412
|
};
|
|
36205
36413
|
const providerNameMap = {
|
|
@@ -36207,7 +36415,8 @@ Examples:
|
|
|
36207
36415
|
kling: "Kling",
|
|
36208
36416
|
veo: "Veo",
|
|
36209
36417
|
grok: "Grok",
|
|
36210
|
-
|
|
36418
|
+
seedance: "Seedance 2.0 via fal.ai",
|
|
36419
|
+
fal: "Seedance 2.0 via fal.ai"
|
|
36211
36420
|
};
|
|
36212
36421
|
const envKey = envKeyMap[provider];
|
|
36213
36422
|
const providerName = providerNameMap[provider];
|
|
@@ -36426,15 +36635,15 @@ Examples:
|
|
|
36426
36635
|
},
|
|
36427
36636
|
3e5
|
|
36428
36637
|
);
|
|
36429
|
-
} else if (provider === "fal") {
|
|
36638
|
+
} else if (provider === "fal" || provider === "seedance") {
|
|
36430
36639
|
const fal = new FalProvider();
|
|
36431
36640
|
await fal.initialize({ apiKey });
|
|
36432
36641
|
let falImage = referenceImage;
|
|
36433
36642
|
if (falImage && falImage.startsWith("data:")) {
|
|
36434
|
-
spinner2.text = "Uploading image to ImgBB for
|
|
36643
|
+
spinner2.text = "Uploading image to ImgBB for Seedance...";
|
|
36435
36644
|
const imgbbKey = await getApiKeyFromConfig("imgbb") || process.env.IMGBB_API_KEY;
|
|
36436
36645
|
if (!imgbbKey) {
|
|
36437
|
-
spinner2.fail("ImgBB API key required for
|
|
36646
|
+
spinner2.fail("ImgBB API key required for Seedance image-to-video");
|
|
36438
36647
|
exitWithError(authError("IMGBB_API_KEY", "ImgBB"));
|
|
36439
36648
|
}
|
|
36440
36649
|
const base64Data = falImage.split(",")[1];
|
|
@@ -36446,8 +36655,9 @@ Examples:
|
|
|
36446
36655
|
}
|
|
36447
36656
|
falImage = uploadResult.url;
|
|
36448
36657
|
}
|
|
36449
|
-
spinner2.text = "Generating video with Seedance 2.0 (this may take 1-3 minutes)...";
|
|
36450
|
-
const
|
|
36658
|
+
spinner2.text = "Generating video with fal.ai Seedance 2.0 (this may take 1-3 minutes)...";
|
|
36659
|
+
const seedanceModel = String(options.seedanceModel ?? "quality").toLowerCase();
|
|
36660
|
+
const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
|
|
36451
36661
|
result = await fal.generateVideo(prompt3, {
|
|
36452
36662
|
prompt: prompt3,
|
|
36453
36663
|
referenceImage: falImage,
|
|
@@ -36572,7 +36782,8 @@ var init_generate = __esm({
|
|
|
36572
36782
|
Examples:
|
|
36573
36783
|
$ vibe generate image "a sunset over the ocean" -o sunset.png
|
|
36574
36784
|
$ vibe generate image "logo design" -o logo.png -p openai
|
|
36575
|
-
$ vibe generate video "dancing cat" -o cat.mp4 #
|
|
36785
|
+
$ vibe generate video "dancing cat" -o cat.mp4 # Seedance when FAL_KEY is set
|
|
36786
|
+
$ vibe generate video "city timelapse" -o city.mp4 -p seedance # Seedance via fal.ai
|
|
36576
36787
|
$ vibe generate video "city timelapse" -o city.mp4 -p kling # Kling
|
|
36577
36788
|
$ vibe generate video "epic scene" -i frame.png -o out.mp4 -p runway # Image-to-video
|
|
36578
36789
|
$ vibe generate speech "Hello world" -o hello.mp3
|
|
@@ -36582,7 +36793,8 @@ Examples:
|
|
|
36582
36793
|
API Keys (per provider):
|
|
36583
36794
|
GOOGLE_API_KEY Image (default), Veo video
|
|
36584
36795
|
OPENAI_API_KEY Image (-p openai)
|
|
36585
|
-
|
|
36796
|
+
FAL_KEY Seedance video (-p seedance, default video)
|
|
36797
|
+
XAI_API_KEY Grok image/video
|
|
36586
36798
|
KLING_API_KEY Kling video (-p kling)
|
|
36587
36799
|
RUNWAY_API_SECRET Runway video (-p runway)
|
|
36588
36800
|
ELEVENLABS_API_KEY Speech, sound effects, music
|
|
@@ -51922,7 +52134,7 @@ function buildFFmpegArgs(clips, sources, presetSettings, outputPath, options, so
|
|
|
51922
52134
|
return args;
|
|
51923
52135
|
}
|
|
51924
52136
|
function runFFmpegProcess(ffmpegPath, args, onProgress) {
|
|
51925
|
-
return new Promise((
|
|
52137
|
+
return new Promise((resolve64, reject) => {
|
|
51926
52138
|
const ffmpeg = spawn(ffmpegPath, args, {
|
|
51927
52139
|
stdio: ["pipe", "pipe", "pipe"]
|
|
51928
52140
|
});
|
|
@@ -51946,7 +52158,7 @@ function runFFmpegProcess(ffmpegPath, args, onProgress) {
|
|
|
51946
52158
|
});
|
|
51947
52159
|
ffmpeg.on("close", (code) => {
|
|
51948
52160
|
if (code === 0) {
|
|
51949
|
-
|
|
52161
|
+
resolve64();
|
|
51950
52162
|
} else {
|
|
51951
52163
|
const errorMatch = stderr.match(/Error.*$/m);
|
|
51952
52164
|
const errorMsg = errorMatch ? errorMatch[0] : `FFmpeg exited with code ${code}`;
|
|
@@ -52083,16 +52295,16 @@ function getPresetSettings(preset, aspectRatio) {
|
|
|
52083
52295
|
async function runHyperframesExport(projectPath, options, spinner2, startedAt) {
|
|
52084
52296
|
spinner2.text = "Loading project...";
|
|
52085
52297
|
const { readFile: readFile42 } = await import("node:fs/promises");
|
|
52086
|
-
const { resolve:
|
|
52298
|
+
const { resolve: resolve64, basename: basename22 } = await import("node:path");
|
|
52087
52299
|
const { Project: Project2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
|
|
52088
52300
|
const { createHyperframesBackend: createHyperframesBackend2 } = await Promise.resolve().then(() => (init_hyperframes(), hyperframes_exports));
|
|
52089
52301
|
const { exitWithError: exitWithError2, generalError: generalError2, outputSuccess: outputSuccess2 } = await Promise.resolve().then(() => (init_output(), output_exports));
|
|
52090
52302
|
const chalk2 = (await Promise.resolve().then(() => (init_source(), source_exports))).default;
|
|
52091
|
-
const filePath =
|
|
52303
|
+
const filePath = resolve64(process.cwd(), projectPath);
|
|
52092
52304
|
const content = await readFile42(filePath, "utf-8");
|
|
52093
52305
|
const project = Project2.fromJSON(JSON.parse(content));
|
|
52094
52306
|
const state = project.getState();
|
|
52095
|
-
const outputPath = options.output ?
|
|
52307
|
+
const outputPath = options.output ? resolve64(process.cwd(), options.output) : resolve64(process.cwd(), `${basename22(projectPath, ".vibe.json")}.${options.format ?? "mp4"}`);
|
|
52096
52308
|
const quality = ["draft", "standard", "high"].includes(options.preset ?? "") ? options.preset : "standard";
|
|
52097
52309
|
const backend = createHyperframesBackend2();
|
|
52098
52310
|
spinner2.text = "Rendering with Hyperframes...";
|
|
@@ -52263,6 +52475,25 @@ Run 'vibe schema export' for structured parameter info.`).action(async (projectP
|
|
|
52263
52475
|
spinner2.text = `Encoding... ${progress}%`;
|
|
52264
52476
|
});
|
|
52265
52477
|
spinner2.succeed(source_default.green(`Exported: ${outputPath}`));
|
|
52478
|
+
if (isJsonMode()) {
|
|
52479
|
+
outputSuccess({
|
|
52480
|
+
command: "export",
|
|
52481
|
+
startedAt,
|
|
52482
|
+
data: {
|
|
52483
|
+
outputPath,
|
|
52484
|
+
backend: "ffmpeg",
|
|
52485
|
+
format: options.format,
|
|
52486
|
+
preset: options.preset,
|
|
52487
|
+
resolution: presetSettings.resolution,
|
|
52488
|
+
duration: summary.duration,
|
|
52489
|
+
clipCount: summary.clipCount,
|
|
52490
|
+
bitrate: options.bitrate ?? null,
|
|
52491
|
+
fps: options.fps ?? null,
|
|
52492
|
+
codec: options.codec ?? null
|
|
52493
|
+
}
|
|
52494
|
+
});
|
|
52495
|
+
return;
|
|
52496
|
+
}
|
|
52266
52497
|
console.log();
|
|
52267
52498
|
console.log(source_default.dim(" Duration:"), `${summary.duration.toFixed(1)}s`);
|
|
52268
52499
|
console.log(source_default.dim(" Clips:"), summary.clipCount);
|
|
@@ -52811,7 +53042,7 @@ __export(ai_video_exports, {
|
|
|
52811
53042
|
executeVideoStatus: () => executeVideoStatus
|
|
52812
53043
|
});
|
|
52813
53044
|
import { readFile as readFile36, writeFile as writeFile40 } from "node:fs/promises";
|
|
52814
|
-
import { resolve as
|
|
53045
|
+
import { resolve as resolve54 } from "node:path";
|
|
52815
53046
|
async function executeVideoGenerate(options) {
|
|
52816
53047
|
const {
|
|
52817
53048
|
prompt: prompt3,
|
|
@@ -52824,24 +53055,61 @@ async function executeVideoGenerate(options) {
|
|
|
52824
53055
|
negative,
|
|
52825
53056
|
resolution,
|
|
52826
53057
|
veoModel = "3.1-fast",
|
|
53058
|
+
seedanceModel = "quality",
|
|
52827
53059
|
output,
|
|
52828
53060
|
wait = true,
|
|
52829
53061
|
apiKey
|
|
52830
53062
|
} = options;
|
|
52831
53063
|
try {
|
|
52832
|
-
const envKeyMap = {
|
|
53064
|
+
const envKeyMap = {
|
|
53065
|
+
grok: "XAI_API_KEY",
|
|
53066
|
+
runway: "RUNWAY_API_SECRET",
|
|
53067
|
+
kling: "KLING_API_KEY",
|
|
53068
|
+
veo: "GOOGLE_API_KEY",
|
|
53069
|
+
seedance: "FAL_KEY",
|
|
53070
|
+
fal: "FAL_KEY"
|
|
53071
|
+
};
|
|
52833
53072
|
const key = apiKey || process.env[envKeyMap[provider] || ""];
|
|
52834
53073
|
if (!key) return { success: false, error: `${envKeyMap[provider]} required for ${provider}` };
|
|
52835
53074
|
let referenceImage;
|
|
52836
53075
|
if (image) {
|
|
52837
|
-
const imagePath =
|
|
53076
|
+
const imagePath = resolve54(process.cwd(), image);
|
|
52838
53077
|
const imageBuffer = await readFile36(imagePath);
|
|
52839
53078
|
const ext = image.toLowerCase().split(".").pop();
|
|
52840
53079
|
const mimeTypes2 = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp" };
|
|
52841
53080
|
const mimeType = mimeTypes2[ext || "png"] || "image/png";
|
|
52842
53081
|
referenceImage = `data:${mimeType};base64,${imageBuffer.toString("base64")}`;
|
|
52843
53082
|
}
|
|
52844
|
-
if (provider === "
|
|
53083
|
+
if (provider === "seedance" || provider === "fal") {
|
|
53084
|
+
const fal = new FalProvider();
|
|
53085
|
+
await fal.initialize({ apiKey: key });
|
|
53086
|
+
let falImage = referenceImage;
|
|
53087
|
+
if (falImage && falImage.startsWith("data:")) {
|
|
53088
|
+
const imgbbKey = process.env.IMGBB_API_KEY;
|
|
53089
|
+
if (!imgbbKey) return { success: false, error: "IMGBB_API_KEY required for Seedance image-to-video" };
|
|
53090
|
+
const base64Data = falImage.split(",")[1];
|
|
53091
|
+
const uploadResult = await uploadToImgbb(Buffer.from(base64Data, "base64"), imgbbKey);
|
|
53092
|
+
if (!uploadResult.success || !uploadResult.url) return { success: false, error: `ImgBB upload failed: ${uploadResult.error}` };
|
|
53093
|
+
falImage = uploadResult.url;
|
|
53094
|
+
}
|
|
53095
|
+
const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
|
|
53096
|
+
const result = await fal.generateVideo(prompt3, {
|
|
53097
|
+
prompt: prompt3,
|
|
53098
|
+
referenceImage: falImage,
|
|
53099
|
+
duration,
|
|
53100
|
+
aspectRatio: ratio,
|
|
53101
|
+
negativePrompt: negative,
|
|
53102
|
+
model
|
|
53103
|
+
});
|
|
53104
|
+
if (result.status === "failed") return { success: false, error: result.error || "Seedance generation failed" };
|
|
53105
|
+
let outputPath;
|
|
53106
|
+
if (output && result.videoUrl) {
|
|
53107
|
+
const buffer = await downloadVideo(result.videoUrl, key);
|
|
53108
|
+
outputPath = resolve54(process.cwd(), output);
|
|
53109
|
+
await writeFile40(outputPath, buffer);
|
|
53110
|
+
}
|
|
53111
|
+
return { success: true, taskId: result.id, status: "completed", videoUrl: result.videoUrl, outputPath, provider: "seedance" };
|
|
53112
|
+
} else if (provider === "runway") {
|
|
52845
53113
|
const runway = new RunwayProvider();
|
|
52846
53114
|
await runway.initialize({ apiKey: key });
|
|
52847
53115
|
const result = await runway.generateVideo(prompt3, {
|
|
@@ -52859,7 +53127,7 @@ async function executeVideoGenerate(options) {
|
|
|
52859
53127
|
let outputPath;
|
|
52860
53128
|
if (output && finalResult.videoUrl) {
|
|
52861
53129
|
const buffer = await downloadVideo(finalResult.videoUrl, key);
|
|
52862
|
-
outputPath =
|
|
53130
|
+
outputPath = resolve54(process.cwd(), output);
|
|
52863
53131
|
await writeFile40(outputPath, buffer);
|
|
52864
53132
|
}
|
|
52865
53133
|
return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "runway" };
|
|
@@ -52893,7 +53161,7 @@ async function executeVideoGenerate(options) {
|
|
|
52893
53161
|
let outputPath;
|
|
52894
53162
|
if (output && finalResult.videoUrl) {
|
|
52895
53163
|
const buffer = await downloadVideo(finalResult.videoUrl, key);
|
|
52896
|
-
outputPath =
|
|
53164
|
+
outputPath = resolve54(process.cwd(), output);
|
|
52897
53165
|
await writeFile40(outputPath, buffer);
|
|
52898
53166
|
}
|
|
52899
53167
|
return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "kling" };
|
|
@@ -52920,7 +53188,7 @@ async function executeVideoGenerate(options) {
|
|
|
52920
53188
|
let outputPath;
|
|
52921
53189
|
if (output && finalResult.videoUrl) {
|
|
52922
53190
|
const buffer = await downloadVideo(finalResult.videoUrl, key);
|
|
52923
|
-
outputPath =
|
|
53191
|
+
outputPath = resolve54(process.cwd(), output);
|
|
52924
53192
|
await writeFile40(outputPath, buffer);
|
|
52925
53193
|
}
|
|
52926
53194
|
return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, outputPath, provider: "veo" };
|
|
@@ -52941,7 +53209,7 @@ async function executeVideoGenerate(options) {
|
|
|
52941
53209
|
let outputPath;
|
|
52942
53210
|
if (output && finalResult.videoUrl) {
|
|
52943
53211
|
const buffer = await downloadVideo(finalResult.videoUrl, key);
|
|
52944
|
-
outputPath =
|
|
53212
|
+
outputPath = resolve54(process.cwd(), output);
|
|
52945
53213
|
await writeFile40(outputPath, buffer);
|
|
52946
53214
|
}
|
|
52947
53215
|
return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath, provider: "grok" };
|
|
@@ -52968,7 +53236,7 @@ async function executeVideoStatus(options) {
|
|
|
52968
53236
|
let outputPath;
|
|
52969
53237
|
if (output && result.videoUrl) {
|
|
52970
53238
|
const buffer = await downloadVideo(result.videoUrl, key);
|
|
52971
|
-
outputPath =
|
|
53239
|
+
outputPath = resolve54(process.cwd(), output);
|
|
52972
53240
|
await writeFile40(outputPath, buffer);
|
|
52973
53241
|
}
|
|
52974
53242
|
return { success: true, taskId, status: result.status, progress: result.progress, videoUrl: result.videoUrl, outputPath };
|
|
@@ -52983,7 +53251,7 @@ async function executeVideoStatus(options) {
|
|
|
52983
53251
|
let outputPath;
|
|
52984
53252
|
if (output && result.videoUrl) {
|
|
52985
53253
|
const buffer = await downloadVideo(result.videoUrl, key);
|
|
52986
|
-
outputPath =
|
|
53254
|
+
outputPath = resolve54(process.cwd(), output);
|
|
52987
53255
|
await writeFile40(outputPath, buffer);
|
|
52988
53256
|
}
|
|
52989
53257
|
return { success: true, taskId, status: result.status, videoUrl: result.videoUrl, duration: result.duration, outputPath };
|
|
@@ -53028,7 +53296,7 @@ async function executeVideoExtend(options) {
|
|
|
53028
53296
|
let outputPath;
|
|
53029
53297
|
if (output && finalResult.videoUrl) {
|
|
53030
53298
|
const buffer = await downloadVideo(finalResult.videoUrl, key);
|
|
53031
|
-
outputPath =
|
|
53299
|
+
outputPath = resolve54(process.cwd(), output);
|
|
53032
53300
|
await writeFile40(outputPath, buffer);
|
|
53033
53301
|
}
|
|
53034
53302
|
return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, duration: finalResult.duration, outputPath };
|
|
@@ -53051,7 +53319,7 @@ async function executeVideoExtend(options) {
|
|
|
53051
53319
|
let outputPath;
|
|
53052
53320
|
if (output && finalResult.videoUrl) {
|
|
53053
53321
|
const buffer = await downloadVideo(finalResult.videoUrl, key);
|
|
53054
|
-
outputPath =
|
|
53322
|
+
outputPath = resolve54(process.cwd(), output);
|
|
53055
53323
|
await writeFile40(outputPath, buffer);
|
|
53056
53324
|
}
|
|
53057
53325
|
return { success: true, taskId: result.id, status: "completed", videoUrl: finalResult.videoUrl, outputPath };
|
|
@@ -53079,7 +53347,7 @@ __export(ai_audio_exports, {
|
|
|
53079
53347
|
executeTranscribe: () => executeTranscribe,
|
|
53080
53348
|
executeVoiceClone: () => executeVoiceClone
|
|
53081
53349
|
});
|
|
53082
|
-
import { resolve as
|
|
53350
|
+
import { resolve as resolve55, dirname as dirname20, basename as basename20, extname as extname12 } from "node:path";
|
|
53083
53351
|
import { readFile as readFile37, writeFile as writeFile41 } from "node:fs/promises";
|
|
53084
53352
|
import { existsSync as existsSync37 } from "node:fs";
|
|
53085
53353
|
async function executeTranscribe(options) {
|
|
@@ -53087,7 +53355,7 @@ async function executeTranscribe(options) {
|
|
|
53087
53355
|
try {
|
|
53088
53356
|
const key = apiKey || process.env.OPENAI_API_KEY;
|
|
53089
53357
|
if (!key) return { success: false, error: "OPENAI_API_KEY required" };
|
|
53090
|
-
const absPath =
|
|
53358
|
+
const absPath = resolve55(process.cwd(), audioPath);
|
|
53091
53359
|
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
53092
53360
|
const whisper = new WhisperProvider();
|
|
53093
53361
|
await whisper.initialize({ apiKey: key });
|
|
@@ -53099,7 +53367,7 @@ async function executeTranscribe(options) {
|
|
|
53099
53367
|
}
|
|
53100
53368
|
let outputPath;
|
|
53101
53369
|
if (output) {
|
|
53102
|
-
outputPath =
|
|
53370
|
+
outputPath = resolve55(process.cwd(), output);
|
|
53103
53371
|
const fmt = detectFormat(output, format);
|
|
53104
53372
|
const content = formatTranscript(result, fmt);
|
|
53105
53373
|
await writeFile41(outputPath, content, "utf-8");
|
|
@@ -53120,7 +53388,7 @@ async function executeIsolate(options) {
|
|
|
53120
53388
|
try {
|
|
53121
53389
|
const key = apiKey || process.env.ELEVENLABS_API_KEY;
|
|
53122
53390
|
if (!key) return { success: false, error: "ELEVENLABS_API_KEY required" };
|
|
53123
|
-
const absPath =
|
|
53391
|
+
const absPath = resolve55(process.cwd(), audioPath);
|
|
53124
53392
|
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
53125
53393
|
const audioBuffer = await readFile37(absPath);
|
|
53126
53394
|
const elevenlabs = new ElevenLabsProvider();
|
|
@@ -53129,7 +53397,7 @@ async function executeIsolate(options) {
|
|
|
53129
53397
|
if (!result.success || !result.audioBuffer) {
|
|
53130
53398
|
return { success: false, error: result.error || "Audio isolation failed" };
|
|
53131
53399
|
}
|
|
53132
|
-
const outputPath =
|
|
53400
|
+
const outputPath = resolve55(process.cwd(), output);
|
|
53133
53401
|
await writeFile41(outputPath, result.audioBuffer);
|
|
53134
53402
|
return { success: true, outputPath };
|
|
53135
53403
|
} catch (error) {
|
|
@@ -53146,7 +53414,7 @@ async function executeVoiceClone(options) {
|
|
|
53146
53414
|
}
|
|
53147
53415
|
const audioBuffers = [];
|
|
53148
53416
|
for (const samplePath of samplePaths) {
|
|
53149
|
-
const absPath =
|
|
53417
|
+
const absPath = resolve55(process.cwd(), samplePath);
|
|
53150
53418
|
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${samplePath}` };
|
|
53151
53419
|
const buffer = await readFile37(absPath);
|
|
53152
53420
|
audioBuffers.push(buffer);
|
|
@@ -53168,7 +53436,7 @@ async function executeVoiceClone(options) {
|
|
|
53168
53436
|
async function executeDub(options) {
|
|
53169
53437
|
const { mediaPath, language, source, voice, analyzeOnly, output } = options;
|
|
53170
53438
|
try {
|
|
53171
|
-
const absPath =
|
|
53439
|
+
const absPath = resolve55(process.cwd(), mediaPath);
|
|
53172
53440
|
if (!existsSync37(absPath)) return { success: false, error: `File not found: ${absPath}` };
|
|
53173
53441
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
53174
53442
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
@@ -53180,7 +53448,7 @@ async function executeDub(options) {
|
|
|
53180
53448
|
const isVideo = [".mp4", ".mov", ".avi", ".mkv", ".webm"].includes(ext);
|
|
53181
53449
|
let audioPath = absPath;
|
|
53182
53450
|
if (isVideo) {
|
|
53183
|
-
const tempAudioPath =
|
|
53451
|
+
const tempAudioPath = resolve55(dirname20(absPath), `temp-audio-${Date.now()}.mp3`);
|
|
53184
53452
|
execSafeSync("ffmpeg", ["-i", absPath, "-vn", "-acodec", "mp3", "-y", tempAudioPath]);
|
|
53185
53453
|
audioPath = tempAudioPath;
|
|
53186
53454
|
}
|
|
@@ -53229,7 +53497,7 @@ ${segmentTexts}`,
|
|
|
53229
53497
|
}
|
|
53230
53498
|
if (analyzeOnly) {
|
|
53231
53499
|
if (output) {
|
|
53232
|
-
const timingPath =
|
|
53500
|
+
const timingPath = resolve55(process.cwd(), output);
|
|
53233
53501
|
const timingData = {
|
|
53234
53502
|
sourcePath: absPath,
|
|
53235
53503
|
sourceLanguage: transcriptResult.detectedLanguage || source || "auto",
|
|
@@ -53248,7 +53516,7 @@ ${segmentTexts}`,
|
|
|
53248
53516
|
sourceLanguage: transcriptResult.detectedLanguage || source || "auto",
|
|
53249
53517
|
targetLanguage: language,
|
|
53250
53518
|
segmentCount: segments.length,
|
|
53251
|
-
outputPath: output ?
|
|
53519
|
+
outputPath: output ? resolve55(process.cwd(), output) : void 0
|
|
53252
53520
|
};
|
|
53253
53521
|
}
|
|
53254
53522
|
const elevenlabs = new ElevenLabsProvider();
|
|
@@ -53262,8 +53530,8 @@ ${segmentTexts}`,
|
|
|
53262
53530
|
}
|
|
53263
53531
|
const combinedBuffer = Buffer.concat(dubbedBuffers);
|
|
53264
53532
|
const outputExt = isVideo ? ".mp3" : extname12(absPath);
|
|
53265
|
-
const defaultOutputPath2 =
|
|
53266
|
-
const finalOutputPath =
|
|
53533
|
+
const defaultOutputPath2 = resolve55(dirname20(absPath), `${basename20(absPath, extname12(absPath))}-${language}${outputExt}`);
|
|
53534
|
+
const finalOutputPath = resolve55(process.cwd(), output || defaultOutputPath2);
|
|
53267
53535
|
await writeFile41(finalOutputPath, combinedBuffer);
|
|
53268
53536
|
if (isVideo && audioPath !== absPath) {
|
|
53269
53537
|
try {
|
|
@@ -53295,12 +53563,12 @@ async function executeDuck(options) {
|
|
|
53295
53563
|
} = options;
|
|
53296
53564
|
try {
|
|
53297
53565
|
if (!commandExists("ffmpeg")) return { success: false, error: "FFmpeg not found" };
|
|
53298
|
-
const absMusicPath =
|
|
53299
|
-
const absVoicePath =
|
|
53566
|
+
const absMusicPath = resolve55(process.cwd(), musicPath);
|
|
53567
|
+
const absVoicePath = resolve55(process.cwd(), voicePath);
|
|
53300
53568
|
if (!existsSync37(absMusicPath)) return { success: false, error: `Music file not found: ${absMusicPath}` };
|
|
53301
53569
|
if (!existsSync37(absVoicePath)) return { success: false, error: `Voice file not found: ${absVoicePath}` };
|
|
53302
|
-
const defaultOutput =
|
|
53303
|
-
const outputPath =
|
|
53570
|
+
const defaultOutput = resolve55(dirname20(absMusicPath), `${basename20(absMusicPath, extname12(absMusicPath))}-ducked${extname12(absMusicPath)}`);
|
|
53571
|
+
const outputPath = resolve55(process.cwd(), output || defaultOutput);
|
|
53304
53572
|
const filter = `[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`;
|
|
53305
53573
|
await execSafe("ffmpeg", [
|
|
53306
53574
|
"-i",
|
|
@@ -53328,11 +53596,11 @@ var init_ai_audio = __esm({
|
|
|
53328
53596
|
});
|
|
53329
53597
|
|
|
53330
53598
|
// src/pipeline/executor.ts
|
|
53331
|
-
import { resolve as
|
|
53599
|
+
import { resolve as resolve56 } from "node:path";
|
|
53332
53600
|
import { readFile as readFile38, writeFile as writeFile42, mkdir as mkdir22 } from "node:fs/promises";
|
|
53333
53601
|
import { existsSync as existsSync38 } from "node:fs";
|
|
53334
53602
|
function maxCostFor(action) {
|
|
53335
|
-
const cmd =
|
|
53603
|
+
const cmd = ACTION_METADATA[action]?.command;
|
|
53336
53604
|
if (!cmd) return 0;
|
|
53337
53605
|
return COST_ESTIMATES[cmd]?.max ?? 0;
|
|
53338
53606
|
}
|
|
@@ -53341,9 +53609,9 @@ function registerAction(action, handler) {
|
|
|
53341
53609
|
}
|
|
53342
53610
|
function getOutput(params, outputDir, defaultName) {
|
|
53343
53611
|
if (params.output && typeof params.output === "string") {
|
|
53344
|
-
return
|
|
53612
|
+
return resolve56(outputDir, params.output);
|
|
53345
53613
|
}
|
|
53346
|
-
return
|
|
53614
|
+
return resolve56(outputDir, defaultName);
|
|
53347
53615
|
}
|
|
53348
53616
|
async function ensureActionsRegistered() {
|
|
53349
53617
|
if (Object.keys(ACTION_HANDLERS).length > 0) return;
|
|
@@ -53356,7 +53624,15 @@ async function ensureActionsRegistered() {
|
|
|
53356
53624
|
registerAction("generate-video", async (params, outputDir) => {
|
|
53357
53625
|
const { executeVideoGenerate: executeVideoGenerate2 } = await Promise.resolve().then(() => (init_ai_video(), ai_video_exports));
|
|
53358
53626
|
const output = getOutput(params, outputDir, "video.mp4");
|
|
53359
|
-
const r = await executeVideoGenerate2({
|
|
53627
|
+
const r = await executeVideoGenerate2({
|
|
53628
|
+
prompt: params.prompt,
|
|
53629
|
+
provider: params.provider,
|
|
53630
|
+
image: params.image,
|
|
53631
|
+
duration: params.duration,
|
|
53632
|
+
ratio: params.ratio,
|
|
53633
|
+
output,
|
|
53634
|
+
wait: true
|
|
53635
|
+
});
|
|
53360
53636
|
return { id: "", action: "generate-video", success: r.success, output: r.outputPath || r.videoUrl, data: { taskId: r.taskId, provider: r.provider, videoUrl: r.videoUrl }, error: r.error };
|
|
53361
53637
|
});
|
|
53362
53638
|
registerAction("generate-tts", async (params, outputDir) => {
|
|
@@ -53488,7 +53764,7 @@ async function ensureActionsRegistered() {
|
|
|
53488
53764
|
const { executeSceneBuild: executeSceneBuild2 } = await Promise.resolve().then(() => (init_scene_build(), scene_build_exports));
|
|
53489
53765
|
const projectRel = params.project ?? ".";
|
|
53490
53766
|
const r = await executeSceneBuild2({
|
|
53491
|
-
projectDir:
|
|
53767
|
+
projectDir: resolve56(outputDir, projectRel),
|
|
53492
53768
|
mode: params.mode,
|
|
53493
53769
|
effort: params.effort,
|
|
53494
53770
|
composer: params.composer,
|
|
@@ -53514,7 +53790,7 @@ async function ensureActionsRegistered() {
|
|
|
53514
53790
|
const { executeSceneRender: executeSceneRender2 } = await Promise.resolve().then(() => (init_scene_render(), scene_render_exports));
|
|
53515
53791
|
const projectRel = params.project ?? ".";
|
|
53516
53792
|
const r = await executeSceneRender2({
|
|
53517
|
-
projectDir:
|
|
53793
|
+
projectDir: resolve56(outputDir, projectRel),
|
|
53518
53794
|
root: params.root,
|
|
53519
53795
|
output: params.output,
|
|
53520
53796
|
fps: params.fps,
|
|
@@ -53538,7 +53814,7 @@ async function ensureActionsRegistered() {
|
|
|
53538
53814
|
});
|
|
53539
53815
|
}
|
|
53540
53816
|
async function loadPipeline(filePath) {
|
|
53541
|
-
const absPath =
|
|
53817
|
+
const absPath = resolve56(process.cwd(), filePath);
|
|
53542
53818
|
if (!existsSync38(absPath)) {
|
|
53543
53819
|
throw new Error(`Pipeline file not found: ${absPath}`);
|
|
53544
53820
|
}
|
|
@@ -53557,13 +53833,13 @@ async function loadPipeline(filePath) {
|
|
|
53557
53833
|
}
|
|
53558
53834
|
async function executePipeline(manifest2, options = {}) {
|
|
53559
53835
|
await ensureActionsRegistered();
|
|
53560
|
-
const outputDir =
|
|
53836
|
+
const outputDir = resolve56(process.cwd(), options.outputDir || `${manifest2.name}-output`);
|
|
53561
53837
|
await mkdir22(outputDir, { recursive: true });
|
|
53562
53838
|
const completedSteps = /* @__PURE__ */ new Map();
|
|
53563
53839
|
const results = [];
|
|
53564
53840
|
const startTime = Date.now();
|
|
53565
53841
|
if (options.resume) {
|
|
53566
|
-
const checkpointPath =
|
|
53842
|
+
const checkpointPath = resolve56(outputDir, CHECKPOINT_FILE);
|
|
53567
53843
|
if (existsSync38(checkpointPath)) {
|
|
53568
53844
|
const checkpointContent = await readFile38(checkpointPath, "utf-8");
|
|
53569
53845
|
const checkpoint = (0, import_yaml8.parse)(checkpointContent);
|
|
@@ -53684,7 +53960,7 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
53684
53960
|
}))
|
|
53685
53961
|
};
|
|
53686
53962
|
await writeFile42(
|
|
53687
|
-
|
|
53963
|
+
resolve56(outputDir, CHECKPOINT_FILE),
|
|
53688
53964
|
(0, import_yaml8.stringify)(checkpoint, { indent: 2 }),
|
|
53689
53965
|
"utf-8"
|
|
53690
53966
|
);
|
|
@@ -53731,42 +54007,48 @@ async function executePipeline(manifest2, options = {}) {
|
|
|
53731
54007
|
budget: effectiveBudget ? budgetUsage : void 0
|
|
53732
54008
|
};
|
|
53733
54009
|
}
|
|
53734
|
-
var import_yaml8,
|
|
54010
|
+
var import_yaml8, ACTION_METADATA, ACTION_HANDLERS, CHECKPOINT_FILE;
|
|
53735
54011
|
var init_executor = __esm({
|
|
53736
54012
|
"src/pipeline/executor.ts"() {
|
|
53737
54013
|
"use strict";
|
|
53738
54014
|
import_yaml8 = __toESM(require_dist(), 1);
|
|
53739
54015
|
init_resolver();
|
|
53740
54016
|
init_output();
|
|
53741
|
-
|
|
53742
|
-
"generate-image": "generate image",
|
|
53743
|
-
"generate-video": "generate video",
|
|
53744
|
-
"generate-tts": "generate speech",
|
|
53745
|
-
"generate-sfx": "generate sound-effect",
|
|
53746
|
-
"generate-music": "generate music",
|
|
53747
|
-
"generate-storyboard": "generate storyboard",
|
|
53748
|
-
"generate-motion": "generate motion",
|
|
53749
|
-
"edit-silence-cut": "edit silence-cut",
|
|
53750
|
-
"edit-jump-cut": "edit jump-cut",
|
|
53751
|
-
"edit-caption": "edit caption",
|
|
53752
|
-
"edit-noise-reduce": "edit noise-reduce",
|
|
53753
|
-
"edit-fade": "edit fade",
|
|
53754
|
-
"edit-translate-srt": "edit translate-srt",
|
|
53755
|
-
"edit-text-overlay": "edit text-overlay",
|
|
53756
|
-
"edit-grade": "edit grade",
|
|
53757
|
-
"edit-speed-ramp": "edit speed-ramp",
|
|
53758
|
-
"edit-reframe": "edit reframe",
|
|
53759
|
-
"edit-interpolate": "edit interpolate",
|
|
53760
|
-
"edit-upscale": "edit upscale-video",
|
|
53761
|
-
"edit-image": "edit image",
|
|
53762
|
-
"audio-transcribe": "audio transcribe",
|
|
53763
|
-
"
|
|
53764
|
-
"
|
|
53765
|
-
"
|
|
53766
|
-
"
|
|
53767
|
-
"
|
|
53768
|
-
"
|
|
53769
|
-
"
|
|
54017
|
+
ACTION_METADATA = {
|
|
54018
|
+
"generate-image": { id: "generate-image", title: "Generate image", category: "generate", command: "generate image", outputs: ["image"] },
|
|
54019
|
+
"generate-video": { id: "generate-video", title: "Generate video", category: "generate", command: "generate video", outputs: ["video"], requiredKeys: ["provider-dependent"] },
|
|
54020
|
+
"generate-tts": { id: "generate-tts", title: "Generate speech", category: "generate", command: "generate speech", outputs: ["audio"] },
|
|
54021
|
+
"generate-sfx": { id: "generate-sfx", title: "Generate sound effect", category: "generate", command: "generate sound-effect", outputs: ["audio"] },
|
|
54022
|
+
"generate-music": { id: "generate-music", title: "Generate music", category: "generate", command: "generate music", outputs: ["audio"] },
|
|
54023
|
+
"generate-storyboard": { id: "generate-storyboard", title: "Generate storyboard", category: "generate", command: "generate storyboard", outputs: ["storyboard"] },
|
|
54024
|
+
"generate-motion": { id: "generate-motion", title: "Generate motion", category: "generate", command: "generate motion", outputs: ["code", "video"] },
|
|
54025
|
+
"edit-silence-cut": { id: "edit-silence-cut", title: "Cut silence", category: "edit", command: "edit silence-cut", outputs: ["video"] },
|
|
54026
|
+
"edit-jump-cut": { id: "edit-jump-cut", title: "Jump cut", category: "edit", command: "edit jump-cut", outputs: ["video"] },
|
|
54027
|
+
"edit-caption": { id: "edit-caption", title: "Caption video", category: "edit", command: "edit caption", outputs: ["video", "srt"] },
|
|
54028
|
+
"edit-noise-reduce": { id: "edit-noise-reduce", title: "Reduce noise", category: "edit", command: "edit noise-reduce", outputs: ["video"] },
|
|
54029
|
+
"edit-fade": { id: "edit-fade", title: "Add fade", category: "edit", command: "edit fade", outputs: ["video"] },
|
|
54030
|
+
"edit-translate-srt": { id: "edit-translate-srt", title: "Translate subtitles", category: "edit", command: "edit translate-srt", outputs: ["srt"] },
|
|
54031
|
+
"edit-text-overlay": { id: "edit-text-overlay", title: "Add text overlay", category: "edit", command: "edit text-overlay", outputs: ["video"] },
|
|
54032
|
+
"edit-grade": { id: "edit-grade", title: "Color grade", category: "edit", command: "edit grade", outputs: ["video"] },
|
|
54033
|
+
"edit-speed-ramp": { id: "edit-speed-ramp", title: "Speed ramp", category: "edit", command: "edit speed-ramp", outputs: ["video"] },
|
|
54034
|
+
"edit-reframe": { id: "edit-reframe", title: "Reframe video", category: "edit", command: "edit reframe", outputs: ["video"] },
|
|
54035
|
+
"edit-interpolate": { id: "edit-interpolate", title: "Interpolate frames", category: "edit", command: "edit interpolate", outputs: ["video"] },
|
|
54036
|
+
"edit-upscale": { id: "edit-upscale", title: "Upscale video", category: "edit", command: "edit upscale-video", outputs: ["video"] },
|
|
54037
|
+
"edit-image": { id: "edit-image", title: "Edit image", category: "edit", command: "edit image", outputs: ["image"] },
|
|
54038
|
+
"audio-transcribe": { id: "audio-transcribe", title: "Transcribe audio", category: "audio", command: "audio transcribe", outputs: ["transcript", "srt"] },
|
|
54039
|
+
"audio-isolate": { id: "audio-isolate", title: "Isolate audio", category: "audio", outputs: ["audio"] },
|
|
54040
|
+
"audio-dub": { id: "audio-dub", title: "Dub audio", category: "audio", outputs: ["audio", "video"] },
|
|
54041
|
+
"audio-duck": { id: "audio-duck", title: "Duck audio", category: "audio", outputs: ["video"] },
|
|
54042
|
+
"detect-scenes": { id: "detect-scenes", title: "Detect scenes", category: "detect", command: "detect scenes", outputs: ["json"] },
|
|
54043
|
+
"detect-silence": { id: "detect-silence", title: "Detect silence", category: "detect", command: "detect silence", outputs: ["json"] },
|
|
54044
|
+
"detect-beats": { id: "detect-beats", title: "Detect beats", category: "detect", command: "detect beats", outputs: ["json"] },
|
|
54045
|
+
"analyze-media": { id: "analyze-media", title: "Analyze media", category: "analyze", command: "analyze media", outputs: ["json"] },
|
|
54046
|
+
"analyze-video": { id: "analyze-video", title: "Analyze video", category: "analyze", command: "analyze video", outputs: ["json"] },
|
|
54047
|
+
"review-video": { id: "review-video", title: "Review video", category: "analyze", command: "analyze review", outputs: ["json"] },
|
|
54048
|
+
"compose-scenes-with-skills": { id: "compose-scenes-with-skills", title: "Compose scenes with skills", category: "scene", command: "compose scenes with skills", outputs: ["html"] },
|
|
54049
|
+
"scene-build": { id: "scene-build", title: "Build scene project", category: "scene", outputs: ["video", "html", "assets"] },
|
|
54050
|
+
"scene-render": { id: "scene-render", title: "Render scene project", category: "scene", outputs: ["video"] },
|
|
54051
|
+
export: { id: "export", title: "Export project", category: "export", outputs: ["video"] }
|
|
53770
54052
|
};
|
|
53771
54053
|
ACTION_HANDLERS = {};
|
|
53772
54054
|
CHECKPOINT_FILE = ".pipeline-state.yaml";
|
|
@@ -58641,7 +58923,7 @@ var init_define_tool = __esm({
|
|
|
58641
58923
|
});
|
|
58642
58924
|
|
|
58643
58925
|
// src/tools/manifest/scene.ts
|
|
58644
|
-
import { resolve as
|
|
58926
|
+
import { resolve as resolve57, relative as relative7 } from "node:path";
|
|
58645
58927
|
function summariseLint(result) {
|
|
58646
58928
|
return {
|
|
58647
58929
|
ok: result.ok,
|
|
@@ -58757,10 +59039,10 @@ var init_scene2 = __esm({
|
|
|
58757
59039
|
name: "scene_init",
|
|
58758
59040
|
category: "scene",
|
|
58759
59041
|
cost: "free",
|
|
58760
|
-
description: "Scaffold a new
|
|
59042
|
+
description: "Scaffold a new VibeFrame video scene project. Supports minimal, agent, and full profiles; full includes the current HTML render backend metadata. Idempotent: re-running keeps user-authored files and merges backend config instead of overwriting. No API keys required.",
|
|
58761
59043
|
schema: sceneInitSchema,
|
|
58762
59044
|
async execute(args, ctx) {
|
|
58763
|
-
const dir =
|
|
59045
|
+
const dir = resolve57(ctx.workingDirectory, args.dir);
|
|
58764
59046
|
const result = await scaffoldSceneProject({
|
|
58765
59047
|
dir,
|
|
58766
59048
|
name: args.name,
|
|
@@ -58808,7 +59090,7 @@ var init_scene2 = __esm({
|
|
|
58808
59090
|
description: "Add a single scene to an existing scene project. Optionally generates narration audio (ElevenLabs) and/or a backdrop image (Gemini/OpenAI), then emits compositions/scene-<id>.html with a paused GSAP timeline and splices a clip reference into the root index.html. Use skipAudio:true and skipImage:true for text-only scenes that need no API calls.",
|
|
58809
59091
|
schema: sceneAddSchema,
|
|
58810
59092
|
async execute(args, ctx) {
|
|
58811
|
-
const projectDir = args.projectDir ?
|
|
59093
|
+
const projectDir = args.projectDir ? resolve57(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
|
|
58812
59094
|
const result = await executeSceneAdd({
|
|
58813
59095
|
name: args.name,
|
|
58814
59096
|
preset: args.preset ?? "simple",
|
|
@@ -58865,7 +59147,7 @@ var init_scene2 = __esm({
|
|
|
58865
59147
|
description: "Validate every scene file in a project against the public Hyperframes lint rules (in-process, no Chrome required). Returns errors, warnings, and info findings per file. Optional fix:true mechanically repairs `timed_element_missing_clip_class` only \u2014 other issues surface with fixHints.",
|
|
58866
59148
|
schema: sceneLintSchema,
|
|
58867
59149
|
async execute(args, ctx) {
|
|
58868
|
-
const projectDir = args.projectDir ?
|
|
59150
|
+
const projectDir = args.projectDir ? resolve57(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
|
|
58869
59151
|
const result = await runProjectLint({
|
|
58870
59152
|
projectDir,
|
|
58871
59153
|
rootRel: args.root,
|
|
@@ -58906,7 +59188,7 @@ var init_scene2 = __esm({
|
|
|
58906
59188
|
description: "Render a scene project to MP4/WebM/MOV via the Hyperframes producer. Requires Chrome installed locally. Output defaults to renders/<projectName>-<isoStamp>.<format>.",
|
|
58907
59189
|
schema: sceneRenderSchema,
|
|
58908
59190
|
async execute(args, ctx) {
|
|
58909
|
-
const projectDir = args.projectDir ?
|
|
59191
|
+
const projectDir = args.projectDir ? resolve57(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
|
|
58910
59192
|
const result = await executeSceneRender({
|
|
58911
59193
|
projectDir,
|
|
58912
59194
|
root: args.root,
|
|
@@ -58961,7 +59243,7 @@ var init_scene2 = __esm({
|
|
|
58961
59243
|
description: "v0.60 one-shot orchestrator: read STORYBOARD.md per-beat YAML cues (narration / backdrop / duration), dispatch TTS + image generation per beat, compose scene HTML via the compose-scenes-with-skills pipeline, then render to MP4. Use this instead of chaining scene_init + scene_add + scene_render manually. Caches by SHA256 of (DESIGN.md + cue body) so re-runs are idempotent and cheap.",
|
|
58962
59244
|
schema: sceneBuildSchema,
|
|
58963
59245
|
async execute(args, ctx) {
|
|
58964
|
-
const projectDir = args.projectDir ?
|
|
59246
|
+
const projectDir = args.projectDir ? resolve57(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
|
|
58965
59247
|
const result = await executeSceneBuild({
|
|
58966
59248
|
projectDir,
|
|
58967
59249
|
mode: args.mode,
|
|
@@ -59022,7 +59304,7 @@ var init_scene2 = __esm({
|
|
|
59022
59304
|
description: "Install the vendored Hyperframes skill bundle into a scene project so the host agent (Claude Code, Cursor, Codex, Aider) can read framework rules + house style directly. Writes a universal SKILL.md + references/ at the project root, plus per-host layouts (.claude/skills/hyperframes/ for Claude Code, .cursor/rules/hyperframes.mdc for Cursor) when those hosts are detected. Phase H1 of the agentic-native composer plan \u2014 once installed, the host agent itself can author scene HTML using the rules instead of relying on vibe's internal LLM call.",
|
|
59023
59305
|
schema: sceneInstallSkillSchema,
|
|
59024
59306
|
async execute(args, ctx) {
|
|
59025
|
-
const projectDir =
|
|
59307
|
+
const projectDir = resolve57(ctx.workingDirectory, args.projectDir);
|
|
59026
59308
|
const hostFlag = args.host ?? "auto";
|
|
59027
59309
|
const hosts = (() => {
|
|
59028
59310
|
if (hostFlag === "all") return ["all"];
|
|
@@ -59064,7 +59346,7 @@ var init_scene2 = __esm({
|
|
|
59064
59346
|
description: "Emit the per-beat compose plan for the host agent to author scene HTML itself. Reads STORYBOARD.md + DESIGN.md and returns each beat's outputPath + userPrompt + cues + body, plus references to the project's SKILL.md (Hyperframes rules) and DESIGN.md (visual identity). The host agent writes each compositions/scene-<id>.html file directly \u2014 VibeFrame makes NO LLM call here. Pairs with scene_install_skill (Phase H1). Phase H2 of the agentic-native composer plan; the internal-LLM batch path (scene_build) remains as a fallback for non-agent contexts.",
|
|
59065
59347
|
schema: sceneComposePromptsSchema,
|
|
59066
59348
|
async execute(args, ctx) {
|
|
59067
|
-
const projectDir =
|
|
59349
|
+
const projectDir = resolve57(ctx.workingDirectory, args.projectDir);
|
|
59068
59350
|
const result = await getComposePrompts({
|
|
59069
59351
|
projectDir,
|
|
59070
59352
|
beatId: args.beat
|
|
@@ -59654,12 +59936,12 @@ var init_edit = __esm({
|
|
|
59654
59936
|
|
|
59655
59937
|
// src/commands/ai-suggest-edit.ts
|
|
59656
59938
|
import { readFile as readFile39, writeFile as writeFile43 } from "node:fs/promises";
|
|
59657
|
-
import { resolve as
|
|
59939
|
+
import { resolve as resolve58 } from "node:path";
|
|
59658
59940
|
async function executeSuggestEdit(options) {
|
|
59659
59941
|
try {
|
|
59660
59942
|
const apiKey = options.apiKey ?? process.env.GOOGLE_API_KEY;
|
|
59661
59943
|
if (!apiKey) return { success: false, error: "GOOGLE_API_KEY required for suggest" };
|
|
59662
|
-
const filePath =
|
|
59944
|
+
const filePath = resolve58(process.cwd(), options.projectPath);
|
|
59663
59945
|
const content = await readFile39(filePath, "utf-8");
|
|
59664
59946
|
const data = JSON.parse(content);
|
|
59665
59947
|
const project = Project.fromJSON(data);
|
|
@@ -60438,9 +60720,9 @@ var init_detect2 = __esm({
|
|
|
60438
60720
|
|
|
60439
60721
|
// src/tools/manifest/_project-io.ts
|
|
60440
60722
|
import { readFile as readFile40, writeFile as writeFile45 } from "node:fs/promises";
|
|
60441
|
-
import { resolve as
|
|
60723
|
+
import { resolve as resolve59 } from "node:path";
|
|
60442
60724
|
async function loadProject(projectPath, cwd) {
|
|
60443
|
-
const absPath =
|
|
60725
|
+
const absPath = resolve59(cwd, projectPath);
|
|
60444
60726
|
const content = await readFile40(absPath, "utf-8");
|
|
60445
60727
|
const data = JSON.parse(content);
|
|
60446
60728
|
return { project: Project.fromJSON(data), absPath };
|
|
@@ -60456,7 +60738,7 @@ var init_project_io = __esm({
|
|
|
60456
60738
|
});
|
|
60457
60739
|
|
|
60458
60740
|
// src/tools/manifest/timeline.ts
|
|
60459
|
-
import { resolve as
|
|
60741
|
+
import { resolve as resolve60 } from "node:path";
|
|
60460
60742
|
var MEDIA_TYPES, timelineAddSourceTool, timelineAddClipTool, timelineSplitClipTool, timelineTrimClipTool, timelineMoveClipTool, timelineDeleteClipTool, timelineDuplicateClipTool, timelineAddEffectTool, timelineAddTrackTool, timelineListTool, timelineTools;
|
|
60461
60743
|
var init_timeline = __esm({
|
|
60462
60744
|
"src/tools/manifest/timeline.ts"() {
|
|
@@ -60492,7 +60774,7 @@ var init_timeline = __esm({
|
|
|
60492
60774
|
}),
|
|
60493
60775
|
async execute(args, ctx) {
|
|
60494
60776
|
const { project, absPath } = await loadProject(args.projectPath, ctx.workingDirectory);
|
|
60495
|
-
const mediaPath =
|
|
60777
|
+
const mediaPath = resolve60(ctx.workingDirectory, args.mediaPath);
|
|
60496
60778
|
const ext = mediaPath.split(".").pop()?.toLowerCase() ?? "";
|
|
60497
60779
|
const source = project.addSource({
|
|
60498
60780
|
name: args.name ?? mediaPath.split("/").pop() ?? "media",
|
|
@@ -60718,7 +61000,7 @@ var init_timeline = __esm({
|
|
|
60718
61000
|
});
|
|
60719
61001
|
|
|
60720
61002
|
// src/tools/manifest/project.ts
|
|
60721
|
-
import { resolve as
|
|
61003
|
+
import { resolve as resolve61 } from "node:path";
|
|
60722
61004
|
import { writeFile as writeFile46 } from "node:fs/promises";
|
|
60723
61005
|
var projectCreateTool, projectInfoTool, projectTools;
|
|
60724
61006
|
var init_project2 = __esm({
|
|
@@ -60741,7 +61023,7 @@ var init_project2 = __esm({
|
|
|
60741
61023
|
fps: external_exports.number().optional().describe("Frames per second (default: 30)")
|
|
60742
61024
|
}),
|
|
60743
61025
|
async execute(args, ctx) {
|
|
60744
|
-
const outputPath =
|
|
61026
|
+
const outputPath = resolve61(ctx.workingDirectory, args.outputPath ?? `${args.name}.vibe.json`);
|
|
60745
61027
|
const project = new Project(args.name);
|
|
60746
61028
|
if (args.fps) project.setFrameRate(args.fps);
|
|
60747
61029
|
await writeFile46(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
@@ -60787,7 +61069,7 @@ var init_project2 = __esm({
|
|
|
60787
61069
|
});
|
|
60788
61070
|
|
|
60789
61071
|
// src/tools/manifest/export.ts
|
|
60790
|
-
import { resolve as
|
|
61072
|
+
import { resolve as resolve62 } from "node:path";
|
|
60791
61073
|
var exportVideoTool, exportTools;
|
|
60792
61074
|
var init_export2 = __esm({
|
|
60793
61075
|
"src/tools/manifest/export.ts"() {
|
|
@@ -60808,8 +61090,8 @@ var init_export2 = __esm({
|
|
|
60808
61090
|
overwrite: external_exports.boolean().optional().describe("Overwrite existing output file (default: false)")
|
|
60809
61091
|
}),
|
|
60810
61092
|
async execute(args, ctx) {
|
|
60811
|
-
const projectPath =
|
|
60812
|
-
const outputPath =
|
|
61093
|
+
const projectPath = resolve62(ctx.workingDirectory, args.projectPath);
|
|
61094
|
+
const outputPath = resolve62(ctx.workingDirectory, args.outputPath);
|
|
60813
61095
|
const result = await runExport(projectPath, outputPath, {
|
|
60814
61096
|
preset: args.preset,
|
|
60815
61097
|
format: args.format,
|
|
@@ -60829,7 +61111,7 @@ var init_export2 = __esm({
|
|
|
60829
61111
|
|
|
60830
61112
|
// src/tools/manifest/agent-only.ts
|
|
60831
61113
|
import { readFile as readFile41, writeFile as writeFile47, readdir as readdir4, stat as stat6, access as access8, unlink as unlink4 } from "node:fs/promises";
|
|
60832
|
-
import { resolve as
|
|
61114
|
+
import { resolve as resolve63, join as join15, basename as basename21, extname as extname13 } from "node:path";
|
|
60833
61115
|
function matchPattern(filename, pattern) {
|
|
60834
61116
|
const regex2 = new RegExp(
|
|
60835
61117
|
"^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".") + "$",
|
|
@@ -60885,11 +61167,11 @@ async function getMediaDuration2(filePath) {
|
|
|
60885
61167
|
}
|
|
60886
61168
|
}
|
|
60887
61169
|
async function resolveProjectPath3(inputPath, cwd) {
|
|
60888
|
-
const filePath =
|
|
61170
|
+
const filePath = resolve63(cwd, inputPath);
|
|
60889
61171
|
try {
|
|
60890
61172
|
const stats = await stat6(filePath);
|
|
60891
61173
|
if (stats.isDirectory()) {
|
|
60892
|
-
return
|
|
61174
|
+
return resolve63(filePath, "project.vibe.json");
|
|
60893
61175
|
}
|
|
60894
61176
|
} catch {
|
|
60895
61177
|
}
|
|
@@ -60917,7 +61199,7 @@ var init_agent_only = __esm({
|
|
|
60917
61199
|
async execute(args, ctx) {
|
|
60918
61200
|
const dirPath = args.path ?? ".";
|
|
60919
61201
|
try {
|
|
60920
|
-
const absPath =
|
|
61202
|
+
const absPath = resolve63(ctx.workingDirectory, dirPath);
|
|
60921
61203
|
const entries = await readdir4(absPath, { withFileTypes: true });
|
|
60922
61204
|
const results = [];
|
|
60923
61205
|
for (const entry of entries) {
|
|
@@ -60948,7 +61230,7 @@ var init_agent_only = __esm({
|
|
|
60948
61230
|
}),
|
|
60949
61231
|
async execute(args, ctx) {
|
|
60950
61232
|
try {
|
|
60951
|
-
const absPath =
|
|
61233
|
+
const absPath = resolve63(ctx.workingDirectory, args.path);
|
|
60952
61234
|
const content = await readFile41(absPath, "utf-8");
|
|
60953
61235
|
const maxLength = 4e3;
|
|
60954
61236
|
const truncated = content.length > maxLength;
|
|
@@ -60971,7 +61253,7 @@ var init_agent_only = __esm({
|
|
|
60971
61253
|
}),
|
|
60972
61254
|
async execute(args, ctx) {
|
|
60973
61255
|
try {
|
|
60974
|
-
const absPath =
|
|
61256
|
+
const absPath = resolve63(ctx.workingDirectory, args.path);
|
|
60975
61257
|
await writeFile47(absPath, args.content, "utf-8");
|
|
60976
61258
|
return { success: true, data: { bytes: args.content.length }, humanLines: [`File written: ${args.path} (${formatSize(args.content.length)})`] };
|
|
60977
61259
|
} catch (error) {
|
|
@@ -60989,7 +61271,7 @@ var init_agent_only = __esm({
|
|
|
60989
61271
|
path: external_exports.string().describe("Path to check")
|
|
60990
61272
|
}),
|
|
60991
61273
|
async execute(args, ctx) {
|
|
60992
|
-
const absPath =
|
|
61274
|
+
const absPath = resolve63(ctx.workingDirectory, args.path);
|
|
60993
61275
|
try {
|
|
60994
61276
|
await access8(absPath);
|
|
60995
61277
|
const stats = await stat6(absPath);
|
|
@@ -61015,11 +61297,11 @@ var init_agent_only = __esm({
|
|
|
61015
61297
|
}),
|
|
61016
61298
|
async execute(args, ctx) {
|
|
61017
61299
|
try {
|
|
61018
|
-
const filePath =
|
|
61300
|
+
const filePath = resolve63(ctx.workingDirectory, args.project);
|
|
61019
61301
|
const content = await readFile41(filePath, "utf-8");
|
|
61020
61302
|
const data = JSON.parse(content);
|
|
61021
61303
|
const project = Project.fromJSON(data);
|
|
61022
|
-
const dirPath =
|
|
61304
|
+
const dirPath = resolve63(ctx.workingDirectory, args.directory);
|
|
61023
61305
|
const filterExts = args.filter ? args.filter.split(",").map((e) => e.trim().toLowerCase()) : null;
|
|
61024
61306
|
const imageDuration = args.imageDuration ?? 5;
|
|
61025
61307
|
const recursive = args.recursive ?? false;
|
|
@@ -61085,7 +61367,7 @@ var init_agent_only = __esm({
|
|
|
61085
61367
|
}),
|
|
61086
61368
|
async execute(args, ctx) {
|
|
61087
61369
|
try {
|
|
61088
|
-
const filePath =
|
|
61370
|
+
const filePath = resolve63(ctx.workingDirectory, args.project);
|
|
61089
61371
|
const content = await readFile41(filePath, "utf-8");
|
|
61090
61372
|
const data = JSON.parse(content);
|
|
61091
61373
|
const project = Project.fromJSON(data);
|
|
@@ -61154,7 +61436,7 @@ var init_agent_only = __esm({
|
|
|
61154
61436
|
}),
|
|
61155
61437
|
async execute(args, ctx) {
|
|
61156
61438
|
try {
|
|
61157
|
-
const filePath =
|
|
61439
|
+
const filePath = resolve63(ctx.workingDirectory, args.project);
|
|
61158
61440
|
const content = await readFile41(filePath, "utf-8");
|
|
61159
61441
|
const data = JSON.parse(content);
|
|
61160
61442
|
const project = Project.fromJSON(data);
|
|
@@ -61432,7 +61714,7 @@ var init_agent_only = __esm({
|
|
|
61432
61714
|
}),
|
|
61433
61715
|
async execute(args, ctx) {
|
|
61434
61716
|
try {
|
|
61435
|
-
const absPath =
|
|
61717
|
+
const absPath = resolve63(ctx.workingDirectory, args.path);
|
|
61436
61718
|
const { stdout } = await execSafe(
|
|
61437
61719
|
"ffprobe",
|
|
61438
61720
|
["-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", absPath],
|
|
@@ -61519,10 +61801,10 @@ Audio:`);
|
|
|
61519
61801
|
}),
|
|
61520
61802
|
async execute(args, ctx) {
|
|
61521
61803
|
try {
|
|
61522
|
-
const absInput =
|
|
61804
|
+
const absInput = resolve63(ctx.workingDirectory, args.input);
|
|
61523
61805
|
const ext = args.input.split(".").pop() || "mp4";
|
|
61524
61806
|
const baseName = args.input.replace(/\.[^/.]+$/, "");
|
|
61525
|
-
const outputPath = args.output ?
|
|
61807
|
+
const outputPath = args.output ? resolve63(ctx.workingDirectory, args.output) : resolve63(ctx.workingDirectory, `${baseName}-compressed.${ext}`);
|
|
61526
61808
|
const crfValues = { low: 28, medium: 23, high: 18 };
|
|
61527
61809
|
const crf = crfValues[args.quality ?? "medium"];
|
|
61528
61810
|
await execSafe(
|
|
@@ -61580,8 +61862,8 @@ Audio:`);
|
|
|
61580
61862
|
}),
|
|
61581
61863
|
async execute(args, ctx) {
|
|
61582
61864
|
try {
|
|
61583
|
-
const absInput =
|
|
61584
|
-
const absOutput =
|
|
61865
|
+
const absInput = resolve63(ctx.workingDirectory, args.input);
|
|
61866
|
+
const absOutput = resolve63(ctx.workingDirectory, args.output);
|
|
61585
61867
|
const codecMap = {
|
|
61586
61868
|
h264: "libx264",
|
|
61587
61869
|
h265: "libx265",
|
|
@@ -61632,11 +61914,11 @@ Audio:`);
|
|
|
61632
61914
|
};
|
|
61633
61915
|
}
|
|
61634
61916
|
try {
|
|
61635
|
-
const absOutput =
|
|
61917
|
+
const absOutput = resolve63(ctx.workingDirectory, args.output);
|
|
61636
61918
|
if (args.reencode) {
|
|
61637
61919
|
const ffmpegArgs = [];
|
|
61638
61920
|
for (const i of args.inputs) {
|
|
61639
|
-
ffmpegArgs.push("-i",
|
|
61921
|
+
ffmpegArgs.push("-i", resolve63(ctx.workingDirectory, i));
|
|
61640
61922
|
}
|
|
61641
61923
|
const filterComplex = args.inputs.map((_, i) => `[${i}:v][${i}:a]`).join("");
|
|
61642
61924
|
ffmpegArgs.push(
|
|
@@ -61651,8 +61933,8 @@ Audio:`);
|
|
|
61651
61933
|
);
|
|
61652
61934
|
await execSafe("ffmpeg", ffmpegArgs, { maxBuffer: 100 * 1024 * 1024 });
|
|
61653
61935
|
} else {
|
|
61654
|
-
const tempList =
|
|
61655
|
-
const listContent = args.inputs.map((i) => `file '${
|
|
61936
|
+
const tempList = resolve63(ctx.workingDirectory, `concat-list-${Date.now()}.txt`);
|
|
61937
|
+
const listContent = args.inputs.map((i) => `file '${resolve63(ctx.workingDirectory, i)}'`).join("\n");
|
|
61656
61938
|
await writeFile47(tempList, listContent, "utf-8");
|
|
61657
61939
|
try {
|
|
61658
61940
|
await execSafe(
|
|
@@ -61737,7 +62019,7 @@ A scene project is a directory that is **bilingual**: it works with both
|
|
|
61737
62019
|
and a paused GSAP timeline. Cheap to edit, cheap to lint, expensive only
|
|
61738
62020
|
at render.
|
|
61739
62021
|
|
|
61740
|
-
\`vibe
|
|
62022
|
+
\`vibe build\` (v0.60+) is the supported one-shot driver from a
|
|
61741
62023
|
written storyboard to an MP4. Plan H (v0.70) added \`--mode agent\` so the
|
|
61742
62024
|
host agent itself authors the per-beat HTML \u2014 no internal LLM call.
|
|
61743
62025
|
|
|
@@ -61745,19 +62027,19 @@ host agent itself authors the per-beat HTML \u2014 no internal LLM call.
|
|
|
61745
62027
|
|
|
61746
62028
|
| Path | Command | When to use |
|
|
61747
62029
|
|---|---|---|
|
|
61748
|
-
| **One-shot (default, v0.60+)** | \`vibe
|
|
61749
|
-
| **High-craft (manual)** | \`DESIGN.md\` +
|
|
62030
|
+
| **One-shot (default, v0.60+)** | \`vibe build [project-dir]\` | STORYBOARD.md has YAML frontmatter + per-beat cues |
|
|
62031
|
+
| **High-craft (manual)** | \`DESIGN.md\` + local composition rules in your agent | Maximum control: hand-author each scene |
|
|
61750
62032
|
| **Quick draft** | \`vibe scene add --style <preset>\` | No agent or no API keys; fast iteration |
|
|
61751
62033
|
|
|
61752
|
-
Recommend \`vibe
|
|
62034
|
+
Recommend \`vibe build\` whenever the user has a STORYBOARD with
|
|
61753
62035
|
narration / backdrop intent.
|
|
61754
62036
|
|
|
61755
62037
|
## High-craft path
|
|
61756
62038
|
|
|
61757
|
-
1. \`vibe
|
|
62039
|
+
1. \`vibe init my-promo --visual-style "Swiss Pulse"\` \u2014 seeds
|
|
61758
62040
|
\`DESIGN.md\` (palette, typography, motion, transitions) plus the
|
|
61759
62041
|
\`vibe.project.yaml\` / \`hyperframes.json\` / \`index.html\` scaffold.
|
|
61760
|
-
In Plan H this **also installs
|
|
62042
|
+
In Plan H this **also installs local composition rules** at the
|
|
61761
62043
|
right place for your host (\`.claude/skills/hyperframes/\` for Claude
|
|
61762
62044
|
Code, \`.cursor/rules/hyperframes.mdc\` for Cursor, universal
|
|
61763
62045
|
\`SKILL.md\` for everyone else).
|
|
@@ -61768,7 +62050,7 @@ narration / backdrop intent.
|
|
|
61768
62050
|
4. Author each scene HTML directly under \`compositions/scene-<id>.html\`
|
|
61769
62051
|
using the rules from steps 2 and 3. The skill enforces the visual
|
|
61770
62052
|
identity contract \u2014 scenes that contradict DESIGN.md fail lint.
|
|
61771
|
-
5. \`vibe scene lint --fix\` for mechanical issues, \`vibe
|
|
62053
|
+
5. \`vibe scene lint --fix\` for mechanical issues, \`vibe render my-promo\`
|
|
61772
62054
|
to MP4.
|
|
61773
62055
|
|
|
61774
62056
|
## Quick-draft path
|
|
@@ -61778,7 +62060,7 @@ vibe scene init my-promo -r 16:9 -d 30
|
|
|
61778
62060
|
vibe scene add intro --style announcement \\
|
|
61779
62061
|
--headline "Ship videos, not clicks"
|
|
61780
62062
|
vibe scene lint
|
|
61781
|
-
vibe
|
|
62063
|
+
vibe render my-promo
|
|
61782
62064
|
\`\`\`
|
|
61783
62065
|
|
|
61784
62066
|
\`vibe scene init\` is **idempotent** \u2014 running it on an existing
|
|
@@ -61790,12 +62072,12 @@ Safe to invoke on user-provided projects.
|
|
|
61790
62072
|
\`\`\`bash
|
|
61791
62073
|
vibe scene init <dir> [-r 16:9|9:16|1:1|4:5] [-d <sec>] [--visual-style "<name>"]
|
|
61792
62074
|
vibe scene styles [<name>] # list / show vendored visual identities
|
|
61793
|
-
vibe scene install-skill [<dir>] [--host all] # retroactive
|
|
62075
|
+
vibe scene install-skill [<dir>] [--host all] # retroactive composition-rules install
|
|
61794
62076
|
vibe scene add <name> --style <preset> [...]
|
|
61795
62077
|
vibe scene compose-prompts [<dir>] [--beat <id>] # H2: emit plan, no LLM call
|
|
61796
62078
|
vibe scene lint [<root>] [--json] [--fix]
|
|
61797
62079
|
vibe scene render [<root>] [--fps 30] [--quality standard] [--format mp4]
|
|
61798
|
-
vibe
|
|
62080
|
+
vibe build [<dir>] [--mode agent|batch|auto] # H3 dispatch
|
|
61799
62081
|
\`\`\`
|
|
61800
62082
|
|
|
61801
62083
|
## Style presets (for \`vibe scene add --style\`)
|
|
@@ -61813,18 +62095,18 @@ from the generated TTS audio.
|
|
|
61813
62095
|
## STORYBOARD-to-MP4 (one command, v0.60+)
|
|
61814
62096
|
|
|
61815
62097
|
\`\`\`bash
|
|
61816
|
-
vibe
|
|
62098
|
+
vibe init my-promo --visual-style "Swiss Pulse" -d 12
|
|
61817
62099
|
# (edit STORYBOARD.md with per-beat YAML cues \u2014 narration, backdrop, duration)
|
|
61818
|
-
vibe
|
|
62100
|
+
vibe build my-promo
|
|
61819
62101
|
\`\`\`
|
|
61820
62102
|
|
|
61821
|
-
\`vibe
|
|
62103
|
+
\`vibe build\` reads the STORYBOARD frontmatter + per-beat cues,
|
|
61822
62104
|
dispatches TTS + image-gen per beat, then either:
|
|
61823
62105
|
|
|
61824
62106
|
- **\`--mode agent\`** (default when an agent host is detected) \u2014 emits a
|
|
61825
62107
|
\`needs-author\` plan via \`vibe scene compose-prompts\`. The host agent
|
|
61826
62108
|
authors each \`compositions/scene-<id>.html\` itself, then re-invoking
|
|
61827
|
-
\`vibe
|
|
62109
|
+
\`vibe build\` proceeds to lint + render.
|
|
61828
62110
|
- **\`--mode batch\`** \u2014 VibeFrame runs an internal LLM (Claude / OpenAI /
|
|
61829
62111
|
Gemini) to compose the HTML, then renders.
|
|
61830
62112
|
|
|
@@ -61847,9 +62129,9 @@ and surface the error to the user.
|
|
|
61847
62129
|
| Task | Tool |
|
|
61848
62130
|
|------|------|
|
|
61849
62131
|
| Generate narration + image, then author scene | \`vibe scene add\` |
|
|
61850
|
-
| Generate a full scenes project from a STORYBOARD | \`vibe
|
|
62132
|
+
| Generate a full scenes project from a STORYBOARD | \`vibe build\` |
|
|
61851
62133
|
| Hand-tweak a single scene's animation | edit \`compositions/<file>.html\` directly |
|
|
61852
|
-
| Render the project | \`vibe
|
|
62134
|
+
| Render the project | \`vibe render\` *or* \`vibe scene render\` for lower-level control |
|
|
61853
62135
|
| Lint | \`vibe scene lint\` *or* \`npx hyperframes lint\` (equivalent) |
|
|
61854
62136
|
|
|
61855
62137
|
The \`vibe\` CLI adds asset generation, AI orchestration, and pipeline
|
|
@@ -61933,8 +62215,8 @@ Checkpoints land next to the YAML: \`pipeline.yaml.checkpoint.json\`.
|
|
|
61933
62215
|
|
|
61934
62216
|
## Authoring tips
|
|
61935
62217
|
|
|
61936
|
-
1. **Start from
|
|
61937
|
-
|
|
62218
|
+
1. **Start from a tiny YAML** \u2014 keep it in your project directory and run
|
|
62219
|
+
\`vibe run pipeline.yaml --dry-run\` before spending provider budget.
|
|
61938
62220
|
2. **Dry-run first** \u2014 you see estimated cost and resolved variable
|
|
61939
62221
|
graph before spending API credits.
|
|
61940
62222
|
3. **Keep step ids short and descriptive** (\`intro\`, \`scene1\`, \`voice\`,
|
|
@@ -61969,18 +62251,18 @@ overlay, etc.) \u2014 useful at the tail of a pipeline.
|
|
|
61969
62251
|
title: "Scene authoring with vibe",
|
|
61970
62252
|
summary: "Author per-scene HTML compositions and render to MP4 (BUILD flow)",
|
|
61971
62253
|
steps: [
|
|
61972
|
-
'Run `vibe
|
|
62254
|
+
'Run `vibe init <dir> --visual-style "<style name>"` to scaffold the project + install local composition rules.',
|
|
61973
62255
|
"Edit `STORYBOARD.md` with per-beat YAML cues (narration / backdrop / duration).",
|
|
61974
62256
|
"Read `SKILL.md` for the framework rules and `DESIGN.md` for the visual-identity hard-gate.",
|
|
61975
|
-
"Run `vibe
|
|
61976
|
-
"Run `vibe scene lint --fix` to validate, then `vibe
|
|
62257
|
+
"Run `vibe build <dir>`. With an agent host detected, the CLI emits a `needs-author` plan; the host agent authors each `compositions/scene-<id>.html` and re-invokes to render.",
|
|
62258
|
+
"Run `vibe scene lint --fix` to validate, then `vibe render <dir>` to produce the MP4."
|
|
61977
62259
|
],
|
|
61978
62260
|
relatedCommands: [
|
|
61979
|
-
"vibe
|
|
62261
|
+
"vibe init",
|
|
61980
62262
|
"vibe scene styles",
|
|
61981
62263
|
"vibe scene install-skill",
|
|
61982
62264
|
"vibe scene compose-prompts",
|
|
61983
|
-
"vibe
|
|
62265
|
+
"vibe build",
|
|
61984
62266
|
"vibe scene lint",
|
|
61985
62267
|
"vibe scene render",
|
|
61986
62268
|
"vibe scene add"
|
|
@@ -62306,7 +62588,7 @@ init_source();
|
|
|
62306
62588
|
// package.json
|
|
62307
62589
|
var package_default = {
|
|
62308
62590
|
name: "@vibeframe/cli",
|
|
62309
|
-
version: "0.
|
|
62591
|
+
version: "0.73.0",
|
|
62310
62592
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
62311
62593
|
private: false,
|
|
62312
62594
|
license: "MIT",
|
|
@@ -62578,6 +62860,19 @@ projectCommand.command("create").description("Create a new project").argument("<
|
|
|
62578
62860
|
const data = JSON.stringify(project.toJSON(), null, 2);
|
|
62579
62861
|
await writeFile(outputPath, data, "utf-8");
|
|
62580
62862
|
spinner2.succeed(source_default.green(`Project created: ${outputPath}`));
|
|
62863
|
+
if (isJsonMode()) {
|
|
62864
|
+
outputSuccess({
|
|
62865
|
+
command: "project create",
|
|
62866
|
+
startedAt,
|
|
62867
|
+
data: {
|
|
62868
|
+
outputPath,
|
|
62869
|
+
name: projectName,
|
|
62870
|
+
aspectRatio: options.ratio,
|
|
62871
|
+
frameRate: parseInt(options.fps, 10)
|
|
62872
|
+
}
|
|
62873
|
+
});
|
|
62874
|
+
return;
|
|
62875
|
+
}
|
|
62581
62876
|
console.log();
|
|
62582
62877
|
console.log(source_default.dim(" Name:"), projectName);
|
|
62583
62878
|
console.log(source_default.dim(" Aspect Ratio:"), options.ratio);
|
|
@@ -62589,6 +62884,7 @@ projectCommand.command("create").description("Create a new project").argument("<
|
|
|
62589
62884
|
}
|
|
62590
62885
|
});
|
|
62591
62886
|
projectCommand.command("info").description("Show project information").argument("<file>", "Project file path").action(async (file) => {
|
|
62887
|
+
const startedAt = Date.now();
|
|
62592
62888
|
const spinner2 = ora("Loading project...").start();
|
|
62593
62889
|
try {
|
|
62594
62890
|
const filePath = await resolveProjectPath(file);
|
|
@@ -62598,6 +62894,24 @@ projectCommand.command("info").description("Show project information").argument(
|
|
|
62598
62894
|
spinner2.stop();
|
|
62599
62895
|
const summary = project.getSummary();
|
|
62600
62896
|
const meta = project.getMeta();
|
|
62897
|
+
if (isJsonMode()) {
|
|
62898
|
+
outputSuccess({
|
|
62899
|
+
command: "project info",
|
|
62900
|
+
startedAt,
|
|
62901
|
+
data: {
|
|
62902
|
+
name: summary.name,
|
|
62903
|
+
duration: summary.duration,
|
|
62904
|
+
aspectRatio: summary.aspectRatio,
|
|
62905
|
+
frameRate: summary.frameRate,
|
|
62906
|
+
trackCount: summary.trackCount,
|
|
62907
|
+
clipCount: summary.clipCount,
|
|
62908
|
+
sourceCount: summary.sourceCount,
|
|
62909
|
+
createdAt: meta.createdAt.toISOString(),
|
|
62910
|
+
updatedAt: meta.updatedAt.toISOString()
|
|
62911
|
+
}
|
|
62912
|
+
});
|
|
62913
|
+
return;
|
|
62914
|
+
}
|
|
62601
62915
|
console.log();
|
|
62602
62916
|
console.log(source_default.bold.cyan("Project Info"));
|
|
62603
62917
|
console.log(source_default.dim("\u2500".repeat(40)));
|
|
@@ -62648,6 +62962,21 @@ projectCommand.command("set").description("Update project settings").argument("<
|
|
|
62648
62962
|
if (options.fps) project.setFrameRate(parseInt(options.fps, 10));
|
|
62649
62963
|
await writeFile(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
62650
62964
|
spinner2.succeed(source_default.green("Project updated"));
|
|
62965
|
+
if (isJsonMode()) {
|
|
62966
|
+
outputSuccess({
|
|
62967
|
+
command: "project set",
|
|
62968
|
+
startedAt,
|
|
62969
|
+
data: {
|
|
62970
|
+
file: filePath,
|
|
62971
|
+
updates: {
|
|
62972
|
+
name: options.name ?? null,
|
|
62973
|
+
ratio: options.ratio ?? null,
|
|
62974
|
+
fps: options.fps ? parseInt(options.fps, 10) : null
|
|
62975
|
+
}
|
|
62976
|
+
}
|
|
62977
|
+
});
|
|
62978
|
+
return;
|
|
62979
|
+
}
|
|
62651
62980
|
} catch (error) {
|
|
62652
62981
|
spinner2.fail("Failed to update project");
|
|
62653
62982
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -62721,6 +63050,20 @@ timelineCommand.command("add-source").description("Add a media source to the pro
|
|
|
62721
63050
|
});
|
|
62722
63051
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
62723
63052
|
spinner2.succeed(source_default.green(`Source added: ${source.id}`));
|
|
63053
|
+
if (isJsonMode()) {
|
|
63054
|
+
outputSuccess({
|
|
63055
|
+
command: "timeline add-source",
|
|
63056
|
+
startedAt,
|
|
63057
|
+
data: {
|
|
63058
|
+
id: source.id,
|
|
63059
|
+
name: mediaName,
|
|
63060
|
+
type: mediaType,
|
|
63061
|
+
path: absMediaPath,
|
|
63062
|
+
duration
|
|
63063
|
+
}
|
|
63064
|
+
});
|
|
63065
|
+
return;
|
|
63066
|
+
}
|
|
62724
63067
|
console.log();
|
|
62725
63068
|
console.log(source_default.dim(" Name:"), mediaName);
|
|
62726
63069
|
console.log(source_default.dim(" Type:"), mediaType);
|
|
@@ -62788,6 +63131,21 @@ timelineCommand.command("add-clip").description("Add a clip to the timeline").ar
|
|
|
62788
63131
|
});
|
|
62789
63132
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
62790
63133
|
spinner2.succeed(source_default.green(`Clip added: ${clip.id}`));
|
|
63134
|
+
if (isJsonMode()) {
|
|
63135
|
+
outputSuccess({
|
|
63136
|
+
command: "timeline add-clip",
|
|
63137
|
+
startedAt,
|
|
63138
|
+
data: {
|
|
63139
|
+
id: clip.id,
|
|
63140
|
+
sourceId,
|
|
63141
|
+
sourceName: source.name,
|
|
63142
|
+
trackId,
|
|
63143
|
+
startTime,
|
|
63144
|
+
duration
|
|
63145
|
+
}
|
|
63146
|
+
});
|
|
63147
|
+
return;
|
|
63148
|
+
}
|
|
62791
63149
|
console.log();
|
|
62792
63150
|
console.log(source_default.dim(" Source:"), source.name);
|
|
62793
63151
|
console.log(source_default.dim(" Track:"), trackId);
|
|
@@ -62835,6 +63193,18 @@ timelineCommand.command("add-track").description("Add a new track").argument("<p
|
|
|
62835
63193
|
});
|
|
62836
63194
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
62837
63195
|
spinner2.succeed(source_default.green(`Track added: ${track.id}`));
|
|
63196
|
+
if (isJsonMode()) {
|
|
63197
|
+
outputSuccess({
|
|
63198
|
+
command: "timeline add-track",
|
|
63199
|
+
startedAt,
|
|
63200
|
+
data: {
|
|
63201
|
+
id: track.id,
|
|
63202
|
+
name: track.name,
|
|
63203
|
+
type: track.type
|
|
63204
|
+
}
|
|
63205
|
+
});
|
|
63206
|
+
return;
|
|
63207
|
+
}
|
|
62838
63208
|
console.log();
|
|
62839
63209
|
console.log(source_default.dim(" Name:"), track.name);
|
|
62840
63210
|
console.log(source_default.dim(" Type:"), track.type);
|
|
@@ -62891,6 +63261,21 @@ timelineCommand.command("add-effect").description("Add an effect to a clip").arg
|
|
|
62891
63261
|
}
|
|
62892
63262
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
62893
63263
|
spinner2.succeed(source_default.green(`Effect added: ${effect.id}`));
|
|
63264
|
+
if (isJsonMode()) {
|
|
63265
|
+
outputSuccess({
|
|
63266
|
+
command: "timeline add-effect",
|
|
63267
|
+
startedAt,
|
|
63268
|
+
data: {
|
|
63269
|
+
id: effect.id,
|
|
63270
|
+
type: effectType,
|
|
63271
|
+
clipId,
|
|
63272
|
+
startTime,
|
|
63273
|
+
duration,
|
|
63274
|
+
params
|
|
63275
|
+
}
|
|
63276
|
+
});
|
|
63277
|
+
return;
|
|
63278
|
+
}
|
|
62894
63279
|
console.log();
|
|
62895
63280
|
console.log(source_default.dim(" Type:"), effectType);
|
|
62896
63281
|
console.log(source_default.dim(" Start:"), startTime, "s");
|
|
@@ -62940,6 +63325,18 @@ timelineCommand.command("trim").description("Trim a clip").argument("<project>",
|
|
|
62940
63325
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
62941
63326
|
const updatedClip = project.getClip(clipId);
|
|
62942
63327
|
spinner2.succeed(source_default.green("Clip trimmed"));
|
|
63328
|
+
if (isJsonMode()) {
|
|
63329
|
+
outputSuccess({
|
|
63330
|
+
command: "timeline trim",
|
|
63331
|
+
startedAt,
|
|
63332
|
+
data: {
|
|
63333
|
+
id: updatedClip.id,
|
|
63334
|
+
startTime: updatedClip.startTime,
|
|
63335
|
+
duration: updatedClip.duration
|
|
63336
|
+
}
|
|
63337
|
+
});
|
|
63338
|
+
return;
|
|
63339
|
+
}
|
|
62943
63340
|
console.log();
|
|
62944
63341
|
console.log(source_default.dim(" Start:"), updatedClip.startTime, "s");
|
|
62945
63342
|
console.log(source_default.dim(" Duration:"), updatedClip.duration, "s");
|
|
@@ -62950,17 +63347,61 @@ timelineCommand.command("trim").description("Trim a clip").argument("<project>",
|
|
|
62950
63347
|
}
|
|
62951
63348
|
});
|
|
62952
63349
|
timelineCommand.command("list").description("List timeline contents").argument("<project>", "Project file path").option("--sources", "List sources only").option("--tracks", "List tracks only").option("--clips", "List clips only").action(async (projectPath, options) => {
|
|
63350
|
+
const startedAt = Date.now();
|
|
62953
63351
|
try {
|
|
62954
63352
|
const filePath = resolve15(process.cwd(), projectPath);
|
|
62955
63353
|
const content = await readFile12(filePath, "utf-8");
|
|
62956
63354
|
const data = JSON.parse(content);
|
|
62957
63355
|
const project = Project.fromJSON(data);
|
|
62958
63356
|
const showAll = !options.sources && !options.tracks && !options.clips;
|
|
63357
|
+
const sources = project.getSources();
|
|
63358
|
+
const tracks = project.getTracks();
|
|
63359
|
+
const clips = project.getClips();
|
|
63360
|
+
if (isJsonMode()) {
|
|
63361
|
+
const result = {};
|
|
63362
|
+
if (showAll || options.sources) {
|
|
63363
|
+
result.sources = sources.map((s) => ({
|
|
63364
|
+
id: s.id,
|
|
63365
|
+
name: s.name,
|
|
63366
|
+
type: s.type,
|
|
63367
|
+
duration: s.duration
|
|
63368
|
+
}));
|
|
63369
|
+
}
|
|
63370
|
+
if (showAll || options.tracks) {
|
|
63371
|
+
result.tracks = tracks.map((t) => ({
|
|
63372
|
+
id: t.id,
|
|
63373
|
+
name: t.name,
|
|
63374
|
+
type: t.type,
|
|
63375
|
+
isMuted: t.isMuted,
|
|
63376
|
+
isLocked: t.isLocked,
|
|
63377
|
+
isVisible: t.isVisible
|
|
63378
|
+
}));
|
|
63379
|
+
}
|
|
63380
|
+
if (showAll || options.clips) {
|
|
63381
|
+
result.clips = clips.map((c) => {
|
|
63382
|
+
const source = project.getSource(c.sourceId);
|
|
63383
|
+
return {
|
|
63384
|
+
id: c.id,
|
|
63385
|
+
sourceId: c.sourceId,
|
|
63386
|
+
sourceName: source?.name ?? null,
|
|
63387
|
+
trackId: c.trackId,
|
|
63388
|
+
startTime: c.startTime,
|
|
63389
|
+
duration: c.duration,
|
|
63390
|
+
effects: c.effects.map((e) => ({ id: e.id, type: e.type }))
|
|
63391
|
+
};
|
|
63392
|
+
});
|
|
63393
|
+
}
|
|
63394
|
+
outputSuccess({
|
|
63395
|
+
command: "timeline list",
|
|
63396
|
+
startedAt,
|
|
63397
|
+
data: result
|
|
63398
|
+
});
|
|
63399
|
+
return;
|
|
63400
|
+
}
|
|
62959
63401
|
if (showAll || options.sources) {
|
|
62960
63402
|
console.log();
|
|
62961
63403
|
console.log(source_default.bold.cyan("Sources"));
|
|
62962
63404
|
console.log(source_default.dim("\u2500".repeat(60)));
|
|
62963
|
-
const sources = project.getSources();
|
|
62964
63405
|
if (sources.length === 0) {
|
|
62965
63406
|
console.log(source_default.dim(" (none)"));
|
|
62966
63407
|
} else {
|
|
@@ -62974,7 +63415,6 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
62974
63415
|
console.log();
|
|
62975
63416
|
console.log(source_default.bold.cyan("Tracks"));
|
|
62976
63417
|
console.log(source_default.dim("\u2500".repeat(60)));
|
|
62977
|
-
const tracks = project.getTracks();
|
|
62978
63418
|
for (const track of tracks) {
|
|
62979
63419
|
const status = [
|
|
62980
63420
|
track.isMuted ? "muted" : null,
|
|
@@ -62989,7 +63429,6 @@ timelineCommand.command("list").description("List timeline contents").argument("
|
|
|
62989
63429
|
console.log();
|
|
62990
63430
|
console.log(source_default.bold.cyan("Clips"));
|
|
62991
63431
|
console.log(source_default.dim("\u2500".repeat(60)));
|
|
62992
|
-
const clips = project.getClips();
|
|
62993
63432
|
if (clips.length === 0) {
|
|
62994
63433
|
console.log(source_default.dim(" (none)"));
|
|
62995
63434
|
} else {
|
|
@@ -63051,6 +63490,17 @@ timelineCommand.command("split").description("Split a clip at a specific time").
|
|
|
63051
63490
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
63052
63491
|
const [first, second] = result;
|
|
63053
63492
|
spinner2.succeed(source_default.green("Clip split successfully"));
|
|
63493
|
+
if (isJsonMode()) {
|
|
63494
|
+
outputSuccess({
|
|
63495
|
+
command: "timeline split",
|
|
63496
|
+
startedAt,
|
|
63497
|
+
data: {
|
|
63498
|
+
first: { id: first.id, duration: first.duration },
|
|
63499
|
+
second: { id: second.id, duration: second.duration }
|
|
63500
|
+
}
|
|
63501
|
+
});
|
|
63502
|
+
return;
|
|
63503
|
+
}
|
|
63054
63504
|
console.log();
|
|
63055
63505
|
console.log(source_default.dim(" First clip:"), first.id, `(${first.duration.toFixed(2)}s)`);
|
|
63056
63506
|
console.log(source_default.dim(" Second clip:"), second.id, `(${second.duration.toFixed(2)}s)`);
|
|
@@ -63097,6 +63547,19 @@ timelineCommand.command("duplicate").description("Duplicate a clip").argument("<
|
|
|
63097
63547
|
}
|
|
63098
63548
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
63099
63549
|
spinner2.succeed(source_default.green(`Clip duplicated: ${duplicated.id}`));
|
|
63550
|
+
if (isJsonMode()) {
|
|
63551
|
+
outputSuccess({
|
|
63552
|
+
command: "timeline duplicate",
|
|
63553
|
+
startedAt,
|
|
63554
|
+
data: {
|
|
63555
|
+
id: duplicated.id,
|
|
63556
|
+
sourceClipId: clipId,
|
|
63557
|
+
startTime: duplicated.startTime,
|
|
63558
|
+
duration: duplicated.duration
|
|
63559
|
+
}
|
|
63560
|
+
});
|
|
63561
|
+
return;
|
|
63562
|
+
}
|
|
63100
63563
|
console.log();
|
|
63101
63564
|
console.log(source_default.dim(" Start:"), duplicated.startTime, "s");
|
|
63102
63565
|
console.log(source_default.dim(" Duration:"), duplicated.duration, "s");
|
|
@@ -63141,6 +63604,17 @@ timelineCommand.command("delete").description("Delete a clip from the timeline")
|
|
|
63141
63604
|
}
|
|
63142
63605
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
63143
63606
|
spinner2.succeed(source_default.green("Clip deleted"));
|
|
63607
|
+
if (isJsonMode()) {
|
|
63608
|
+
outputSuccess({
|
|
63609
|
+
command: "timeline delete",
|
|
63610
|
+
startedAt,
|
|
63611
|
+
data: {
|
|
63612
|
+
id: clipId,
|
|
63613
|
+
deleted: true
|
|
63614
|
+
}
|
|
63615
|
+
});
|
|
63616
|
+
return;
|
|
63617
|
+
}
|
|
63144
63618
|
} catch (error) {
|
|
63145
63619
|
spinner2.fail("Failed to delete clip");
|
|
63146
63620
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -63188,6 +63662,18 @@ timelineCommand.command("move").description("Move a clip to a new position").arg
|
|
|
63188
63662
|
await writeFile10(filePath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
|
|
63189
63663
|
const updated = project.getClip(clipId);
|
|
63190
63664
|
spinner2.succeed(source_default.green("Clip moved"));
|
|
63665
|
+
if (isJsonMode()) {
|
|
63666
|
+
outputSuccess({
|
|
63667
|
+
command: "timeline move",
|
|
63668
|
+
startedAt,
|
|
63669
|
+
data: {
|
|
63670
|
+
id: updated.id,
|
|
63671
|
+
trackId: updated.trackId,
|
|
63672
|
+
startTime: updated.startTime
|
|
63673
|
+
}
|
|
63674
|
+
});
|
|
63675
|
+
return;
|
|
63676
|
+
}
|
|
63191
63677
|
console.log();
|
|
63192
63678
|
console.log(source_default.dim(" Track:"), updated.trackId);
|
|
63193
63679
|
console.log(source_default.dim(" Start:"), updated.startTime, "s");
|
|
@@ -64124,13 +64610,13 @@ init_ai_highlights();
|
|
|
64124
64610
|
init_ai_animated_caption();
|
|
64125
64611
|
init_output();
|
|
64126
64612
|
var pipelineCommand = new Command("pipeline").alias("pipe").description(
|
|
64127
|
-
"AI media transformations on existing video / audio (highlights, auto-shorts, animated captions). For BUILD-from-text flows see `vibe
|
|
64613
|
+
"AI media transformations on existing video / audio (highlights, auto-shorts, animated captions). For BUILD-from-text flows see `vibe build`."
|
|
64128
64614
|
).addHelpText(
|
|
64129
64615
|
"after",
|
|
64130
64616
|
`
|
|
64131
64617
|
Two flows \u2014 pick by intent:
|
|
64132
64618
|
BUILD \u2014 text \u2192 MP4 (intent \u2192 AI generation \u2192 new video)
|
|
64133
|
-
Use \`vibe
|
|
64619
|
+
Use \`vibe build\` with a STORYBOARD.md + DESIGN.md.
|
|
64134
64620
|
Idempotent, agent-editable, skills-driven (v0.60+).
|
|
64135
64621
|
PROCESS \u2014 existing video/audio \u2192 transformed media
|
|
64136
64622
|
Use \`vibe pipeline\` (this group) or \`vibe edit\` / \`vibe audio\`.
|
|
@@ -64142,7 +64628,7 @@ Examples (PROCESS):
|
|
|
64142
64628
|
$ vibe pipeline animated-caption video.mp4 -o captioned.mp4 -s highlight
|
|
64143
64629
|
$ vibe pipeline animated-caption video.mp4 -o out.mp4 -s karaoke-sweep --fast
|
|
64144
64630
|
|
|
64145
|
-
|
|
64631
|
+
Provider API Keys:
|
|
64146
64632
|
highlights: GOOGLE_API_KEY (Gemini analysis)
|
|
64147
64633
|
auto-shorts: GOOGLE_API_KEY + OPENAI_API_KEY (optional captions)
|
|
64148
64634
|
animated-caption: OPENAI_API_KEY (Whisper transcription)
|
|
@@ -64153,7 +64639,7 @@ Cost tiers:
|
|
|
64153
64639
|
animated-caption: $ Low (~$0.01)
|
|
64154
64640
|
regenerate-scene: $$$ High (per-scene re-run; depends on provider)
|
|
64155
64641
|
|
|
64156
|
-
Note: \`pipeline script-to-video\` was removed \u2014 use \`vibe
|
|
64642
|
+
Note: \`pipeline script-to-video\` was removed \u2014 use \`vibe build\` for
|
|
64157
64643
|
text \u2192 MP4. \`pipeline regenerate-scene\` still operates on the
|
|
64158
64644
|
storyboard.{yaml,json} layout produced by older runs.
|
|
64159
64645
|
|
|
@@ -64343,18 +64829,36 @@ function listCommands(program3) {
|
|
|
64343
64829
|
console.log(JSON.stringify(commands, null, 2));
|
|
64344
64830
|
}
|
|
64345
64831
|
function extractEnumFromDescription(description) {
|
|
64346
|
-
const
|
|
64347
|
-
|
|
64832
|
+
const cleaned = description.replace(/\s*\([^()]*\)/g, "");
|
|
64833
|
+
const finalize = (raw) => {
|
|
64834
|
+
const values = raw.map((v) => v.trim().replace(/^or\s+/i, "")).filter(Boolean);
|
|
64835
|
+
if (values.some((v) => /\s/.test(v))) return void 0;
|
|
64836
|
+
if (values.length >= 2 && values.length <= 12) return values;
|
|
64837
|
+
return void 0;
|
|
64838
|
+
};
|
|
64839
|
+
const providerMatch = cleaned.match(
|
|
64840
|
+
/(?:Provider|Providers?):\s*([a-z0-9,\s-]+?)$/i
|
|
64348
64841
|
);
|
|
64349
64842
|
if (providerMatch) {
|
|
64350
|
-
|
|
64843
|
+
const values = finalize(providerMatch[1].split(","));
|
|
64844
|
+
if (values) return values;
|
|
64351
64845
|
}
|
|
64352
|
-
const
|
|
64353
|
-
/^[A-Z][a-z]
|
|
64846
|
+
const labeledMatch = cleaned.match(
|
|
64847
|
+
/^([A-Z][a-z]+(?:\s+[a-z]+){0,2}):\s*([a-z0-9:,\s/-]+?)$/
|
|
64354
64848
|
);
|
|
64355
|
-
if (
|
|
64356
|
-
|
|
64357
|
-
|
|
64849
|
+
if (labeledMatch) {
|
|
64850
|
+
return finalize(labeledMatch[2].split(","));
|
|
64851
|
+
}
|
|
64852
|
+
const parenMatch = description.match(/\(([^()]+)\)\s*$/);
|
|
64853
|
+
if (parenMatch) {
|
|
64854
|
+
const inner = parenMatch[1].trim();
|
|
64855
|
+
if (/^default:|^e\.g\.|^or\s|via\s|run\s/i.test(inner)) return void 0;
|
|
64856
|
+
const rawValues = inner.split(",").map((s) => s.trim()).filter(Boolean);
|
|
64857
|
+
const values = rawValues.map((v) => v.replace(/^or\s+/i, "").trim());
|
|
64858
|
+
const looksLikeEnum = values.every(
|
|
64859
|
+
(v) => /^[a-z0-9][a-z0-9:/_-]*$/i.test(v) && v.length <= 24
|
|
64860
|
+
);
|
|
64861
|
+
if (looksLikeEnum && values.length >= 2 && values.length <= 12) return values;
|
|
64358
64862
|
}
|
|
64359
64863
|
return void 0;
|
|
64360
64864
|
}
|
|
@@ -68849,7 +69353,7 @@ init_detect();
|
|
|
68849
69353
|
// src/commands/setup.ts
|
|
68850
69354
|
init_esm();
|
|
68851
69355
|
init_source();
|
|
68852
|
-
var
|
|
69356
|
+
var import_dotenv3 = __toESM(require_main(), 1);
|
|
68853
69357
|
init_config();
|
|
68854
69358
|
init_output();
|
|
68855
69359
|
init_tty();
|
|
@@ -68898,7 +69402,7 @@ var AI_FEATURES = [
|
|
|
68898
69402
|
{
|
|
68899
69403
|
label: "Videos",
|
|
68900
69404
|
desc: "generate + extend",
|
|
68901
|
-
defaultProvider: "
|
|
69405
|
+
defaultProvider: "Seedance 2.0 via fal.ai (Artificial Analysis #2 t2v + i2v, since v0.57)",
|
|
68902
69406
|
alsoAvailable: "Grok Imagine, Kling, Runway Gen-4.5, Google Veo",
|
|
68903
69407
|
keys: [{ configKey: "fal", envVar: "FAL_KEY", name: "fal.ai", url: "https://fal.ai/dashboard/keys", what: "ByteDance Seedance 2.0 text-to-video and image-to-video" }],
|
|
68904
69408
|
tryCommand: 'vibe generate video "ocean waves" -o waves.mp4'
|
|
@@ -68973,7 +69477,7 @@ async function runSetupWizard(fullSetup = false) {
|
|
|
68973
69477
|
console.log();
|
|
68974
69478
|
await collectKeys(config2, pipelineKeys);
|
|
68975
69479
|
await saveConfig(config2);
|
|
68976
|
-
showComplete(config2, "vibe
|
|
69480
|
+
showComplete(config2, "vibe build my-story/ # see CONTRIBUTING.md for STORYBOARD.md format");
|
|
68977
69481
|
return;
|
|
68978
69482
|
}
|
|
68979
69483
|
console.log(source_default.dim("Step 2 of 2"));
|
|
@@ -69138,7 +69642,7 @@ function showComplete(config2, defaultTryCommand, features = []) {
|
|
|
69138
69642
|
console.log(source_default.bold(" Next steps:"));
|
|
69139
69643
|
console.log(source_default.dim(" cd <project>; vibe init Scaffold AGENTS.md / CLAUDE.md / .env.example (project scope)"));
|
|
69140
69644
|
console.log(source_default.dim(" vibe doctor Check system health + available commands"));
|
|
69141
|
-
console.log(source_default.dim(" vibe schema --list Discover all
|
|
69645
|
+
console.log(source_default.dim(" vibe schema --list Discover all 81 commands"));
|
|
69142
69646
|
console.log(source_default.dim(" vibe setup Re-run user-scope setup anytime"));
|
|
69143
69647
|
const hosts = detectedAgentHosts();
|
|
69144
69648
|
const primary = hosts[0];
|
|
@@ -69151,14 +69655,14 @@ function showComplete(config2, defaultTryCommand, features = []) {
|
|
|
69151
69655
|
);
|
|
69152
69656
|
console.log(
|
|
69153
69657
|
source_default.dim(
|
|
69154
|
-
` Scene composer will auto-dispatch to ${primary.label} (${source_default.bold("--mode agent")}). Run \`vibe
|
|
69658
|
+
` Scene composer will auto-dispatch to ${primary.label} (${source_default.bold("--mode agent")}). Run \`vibe init my-promo\` to scaffold a video project + install local composition rules.`
|
|
69155
69659
|
)
|
|
69156
69660
|
);
|
|
69157
69661
|
} else {
|
|
69158
69662
|
console.log();
|
|
69159
69663
|
console.log(
|
|
69160
69664
|
source_default.dim(
|
|
69161
|
-
` No agent host detected \u2014 \`vibe
|
|
69665
|
+
` No agent host detected \u2014 \`vibe build\` will use the internal LLM composer (${source_default.bold("--mode batch")}).`
|
|
69162
69666
|
)
|
|
69163
69667
|
);
|
|
69164
69668
|
}
|
|
@@ -69207,7 +69711,7 @@ async function showConfig() {
|
|
|
69207
69711
|
if (hasCwdEnv) {
|
|
69208
69712
|
try {
|
|
69209
69713
|
const envContent = await readFile33(cwdEnvPath, "utf-8");
|
|
69210
|
-
dotenvKeys = (0,
|
|
69714
|
+
dotenvKeys = (0, import_dotenv3.parse)(Buffer.from(envContent));
|
|
69211
69715
|
} catch {
|
|
69212
69716
|
}
|
|
69213
69717
|
}
|
|
@@ -69269,6 +69773,9 @@ async function showConfig() {
|
|
|
69269
69773
|
init_esm();
|
|
69270
69774
|
init_source();
|
|
69271
69775
|
init_agent_host_detect();
|
|
69776
|
+
init_scene_project();
|
|
69777
|
+
init_visual_styles();
|
|
69778
|
+
init_install_skill();
|
|
69272
69779
|
import { existsSync as existsSync34 } from "node:fs";
|
|
69273
69780
|
import { mkdir as mkdir20, readFile as readFile34, writeFile as writeFile39 } from "node:fs/promises";
|
|
69274
69781
|
import { basename as basename19, dirname as dirname18, resolve as resolve48 } from "node:path";
|
|
@@ -69322,9 +69829,9 @@ VibeFrame splits cleanly into two flows. Routing the user's request
|
|
|
69322
69829
|
correctly is the most important judgement call you'll make.
|
|
69323
69830
|
|
|
69324
69831
|
**BUILD \u2014 create new video from text intent.**
|
|
69325
|
-
Use \`vibe
|
|
69832
|
+
Use \`vibe build\` with a STORYBOARD.md + DESIGN.md. The
|
|
69326
69833
|
skills-driven pipeline (v0.60+) dispatches narration TTS + backdrop
|
|
69327
|
-
image-gen per beat, composes scene HTML via the
|
|
69834
|
+
image-gen per beat, composes scene HTML via the bundled composition rules
|
|
69328
69835
|
bundle, then renders to MP4. Idempotent re-runs reuse cached assets.
|
|
69329
69836
|
|
|
69330
69837
|
**PROCESS \u2014 transform existing video / audio.**
|
|
@@ -69341,8 +69848,8 @@ PROCESS.
|
|
|
69341
69848
|
|
|
69342
69849
|
| Task | Command |
|
|
69343
69850
|
|---|---|
|
|
69344
|
-
| One-shot storyboard \u2192
|
|
69345
|
-
| Render an existing scene project | \`vibe
|
|
69851
|
+
| One-shot storyboard \u2192 video | \`vibe build [project-dir]\` |
|
|
69852
|
+
| Render an existing scene project | \`vibe render [project-dir] -o out.mp4\` |
|
|
69346
69853
|
| Lint scene HTML | \`vibe scene lint --json\` |
|
|
69347
69854
|
| Generate a single image | \`vibe generate image "prompt" -o img.png --quality hd\` |
|
|
69348
69855
|
| Generate a single video | \`vibe generate video "prompt" -i image.png -o clip.mp4\` |
|
|
@@ -69375,7 +69882,7 @@ PROCESS.
|
|
|
69375
69882
|
| **Free** | \`detect *\`, \`edit silence-cut/fade/noise-reduce\`, \`schema\`, \`project\`, \`timeline\` | $0 |
|
|
69376
69883
|
| **Low** | \`analyze *\`, \`audio transcribe\`, \`generate image\` | $0.01\u2013$0.10 |
|
|
69377
69884
|
| **High** | \`generate video\`, \`edit image\` | $1\u2013$5 |
|
|
69378
|
-
| **Very High** | \`pipeline *\` (highlights, auto-shorts, regenerate-scene), \`
|
|
69885
|
+
| **Very High** | \`pipeline *\` (highlights, auto-shorts, regenerate-scene), \`build\` | $5\u2013$50+ |
|
|
69379
69886
|
|
|
69380
69887
|
## Agent invariants
|
|
69381
69888
|
|
|
@@ -69396,9 +69903,9 @@ API keys live in \`.env\` (gitignored). Copy \`.env.example\` to start. Run
|
|
|
69396
69903
|
\`vibe doctor\` to see which keys are currently detected and which providers
|
|
69397
69904
|
they unlock.
|
|
69398
69905
|
|
|
69399
|
-
###
|
|
69906
|
+
### Composition rules (scene HTML)
|
|
69400
69907
|
|
|
69401
|
-
\`vibe
|
|
69908
|
+
\`vibe init\` installs local composition rules into your project. The
|
|
69402
69909
|
universal copy lives at \`SKILL.md\` (with \`references/*.md\`), and
|
|
69403
69910
|
host-specific copies are placed where each agent expects them
|
|
69404
69911
|
(\`.claude/skills/hyperframes/\` for Claude Code, \`.cursor/rules/hyperframes.mdc\`
|
|
@@ -69413,7 +69920,7 @@ To retro-install on a project scaffolded before this command existed, run
|
|
|
69413
69920
|
|
|
69414
69921
|
### Scene composer (batch / non-agent fallback)
|
|
69415
69922
|
|
|
69416
|
-
When you don't want to author HTML yourself, \`vibe
|
|
69923
|
+
When you don't want to author HTML yourself, \`vibe build --mode batch\` runs an
|
|
69417
69924
|
LLM internally with the same skill bundle. It auto-picks a provider based
|
|
69418
69925
|
on available keys (\`claude > gemini > openai\`) \u2014 pass \`--composer <name>\`
|
|
69419
69926
|
to force one:
|
|
@@ -69453,7 +69960,7 @@ pack is available as slash commands (consolidated to 2 in v0.62 \u2014 the
|
|
|
69453
69960
|
overview content moved into AGENTS.md above):
|
|
69454
69961
|
|
|
69455
69962
|
- \`/vibe-pipeline\` \u2014 YAML pipeline authoring helper (Video as Code)
|
|
69456
|
-
- \`/vibe-scene\` \u2014 per-scene HTML authoring + \`vibe
|
|
69963
|
+
- \`/vibe-scene\` \u2014 per-scene HTML authoring + \`vibe build\`
|
|
69457
69964
|
|
|
69458
69965
|
To install / update them later:
|
|
69459
69966
|
|
|
@@ -69518,7 +70025,7 @@ renders/
|
|
|
69518
70025
|
`;
|
|
69519
70026
|
function renderProjectYaml(opts) {
|
|
69520
70027
|
return `# VibeFrame project config. Used by \`vibe scene render\` to name
|
|
69521
|
-
# outputs and by \`vibe
|
|
70028
|
+
# outputs and by \`vibe build\` for default providers / budget.
|
|
69522
70029
|
name: ${opts.name}
|
|
69523
70030
|
aspect: "16:9"
|
|
69524
70031
|
defaults:
|
|
@@ -69539,8 +70046,16 @@ defaults:
|
|
|
69539
70046
|
// src/commands/init.ts
|
|
69540
70047
|
init_output();
|
|
69541
70048
|
var VALID_AGENTS = ["claude-code", "codex", "cursor", "aider", "gemini-cli", "opencode", "all", "auto"];
|
|
69542
|
-
var initCommand = new Command("init").description("Scaffold
|
|
70049
|
+
var initCommand = new Command("init").description("Scaffold a VibeFrame project (video scene project or project-scope agent files)").argument("[project-dir]", "Project directory (defaults to cwd)", ".").option("--type <type>", "Project type: scene (video project) | agent (agent files only)", "scene").option("--profile <profile>", "Scene profile: minimal (storyboard/design only), agent (recommended), full (render scaffold upfront)", "agent").option("-r, --ratio <ratio>", "Scene aspect ratio: 16:9, 9:16, 1:1, 4:5", "16:9").option("-d, --duration <sec>", "Default scene/root duration in seconds", "10").option("--visual-style <name>", "Seed scene DESIGN.md from a named style").option("--agent <id>", `Agent target: ${VALID_AGENTS.join(" | ")}`, "auto").option("--force", "Overwrite existing files instead of skipping").option("--dry-run", "Print the file list without writing anything").action(async (projectDirArg, options) => {
|
|
69543
70050
|
const startedAt = Date.now();
|
|
70051
|
+
const initType = String(options.type ?? "scene");
|
|
70052
|
+
if (initType !== "scene" && initType !== "agent") {
|
|
70053
|
+
exitWithError(usageError(`Invalid --type: ${initType}`, "Must be one of: scene, agent"));
|
|
70054
|
+
}
|
|
70055
|
+
if (initType === "scene") {
|
|
70056
|
+
await runSceneInit(projectDirArg, options, startedAt);
|
|
70057
|
+
return;
|
|
70058
|
+
}
|
|
69544
70059
|
const agent = options.agent;
|
|
69545
70060
|
if (!VALID_AGENTS.includes(agent)) {
|
|
69546
70061
|
exitWithError(usageError(`Invalid --agent: ${agent}`, `Must be one of: ${VALID_AGENTS.join(", ")}`));
|
|
@@ -69642,6 +70157,150 @@ var initCommand = new Command("init").description("Scaffold project-scope agent
|
|
|
69642
70157
|
console.log(source_default.dim(" vibe scene init my-promo Scaffold a starter scene project"));
|
|
69643
70158
|
console.log();
|
|
69644
70159
|
});
|
|
70160
|
+
var VALID_SCENE_ASPECTS = ["16:9", "9:16", "1:1", "4:5"];
|
|
70161
|
+
async function runSceneInit(projectDirArg, options, startedAt) {
|
|
70162
|
+
const profile = String(options.profile ?? "agent");
|
|
70163
|
+
if (!isSceneScaffoldProfile(profile)) {
|
|
70164
|
+
exitWithError(usageError(`Invalid --profile: ${profile}`, "Must be one of: minimal, agent, full"));
|
|
70165
|
+
}
|
|
70166
|
+
const aspect = String(options.ratio ?? "16:9");
|
|
70167
|
+
if (!VALID_SCENE_ASPECTS.includes(aspect)) {
|
|
70168
|
+
exitWithError(usageError(`Invalid --ratio: ${aspect}`, `Must be one of: ${VALID_SCENE_ASPECTS.join(", ")}`));
|
|
70169
|
+
}
|
|
70170
|
+
const duration = Number.parseFloat(String(options.duration ?? "10"));
|
|
70171
|
+
if (!Number.isFinite(duration) || duration <= 0 || duration > 3600) {
|
|
70172
|
+
exitWithError(usageError(`Invalid --duration: ${String(options.duration)}`, "Duration must be a positive number of seconds (\u22643600)"));
|
|
70173
|
+
}
|
|
70174
|
+
const visualStyle = options.visualStyle ? getVisualStyle(String(options.visualStyle)) : void 0;
|
|
70175
|
+
if (options.visualStyle && !visualStyle) {
|
|
70176
|
+
exitWithError(usageError(`Unknown visual style: ${String(options.visualStyle)}`, `Valid: ${visualStyleNames()}. Browse with \`vibe scene styles\`.`));
|
|
70177
|
+
}
|
|
70178
|
+
const projectDir = resolve48(projectDirArg);
|
|
70179
|
+
const projectName = basename19(projectDir);
|
|
70180
|
+
const groups = describeSceneScaffold({ dir: projectDir, profile });
|
|
70181
|
+
if (options.dryRun) {
|
|
70182
|
+
if (!isJsonMode()) {
|
|
70183
|
+
printSceneInitPlan({
|
|
70184
|
+
projectDir,
|
|
70185
|
+
profile,
|
|
70186
|
+
aspect,
|
|
70187
|
+
duration,
|
|
70188
|
+
visualStyleName: visualStyle?.name ?? null,
|
|
70189
|
+
groups,
|
|
70190
|
+
dryRun: true
|
|
70191
|
+
});
|
|
70192
|
+
return;
|
|
70193
|
+
}
|
|
70194
|
+
outputSuccess({
|
|
70195
|
+
command: "init",
|
|
70196
|
+
startedAt,
|
|
70197
|
+
dryRun: true,
|
|
70198
|
+
data: {
|
|
70199
|
+
type: "scene",
|
|
70200
|
+
projectDir,
|
|
70201
|
+
name: projectName,
|
|
70202
|
+
profile,
|
|
70203
|
+
aspect,
|
|
70204
|
+
duration,
|
|
70205
|
+
visualStyle: visualStyle?.name ?? null,
|
|
70206
|
+
groups
|
|
70207
|
+
}
|
|
70208
|
+
});
|
|
70209
|
+
return;
|
|
70210
|
+
}
|
|
70211
|
+
const result = await scaffoldSceneProject({
|
|
70212
|
+
dir: projectDir,
|
|
70213
|
+
name: projectName,
|
|
70214
|
+
aspect,
|
|
70215
|
+
duration,
|
|
70216
|
+
visualStyle,
|
|
70217
|
+
profile
|
|
70218
|
+
});
|
|
70219
|
+
const detectedIds = detectedAgentHosts().map((h) => h.id);
|
|
70220
|
+
const skillResult = profile === "agent" || profile === "full" ? await installHyperframesSkill({
|
|
70221
|
+
projectDir,
|
|
70222
|
+
hosts: deriveInstallHosts(detectedIds)
|
|
70223
|
+
}) : { success: true, files: [], bundleVersion: "not-installed" };
|
|
70224
|
+
if (isJsonMode()) {
|
|
70225
|
+
outputSuccess({
|
|
70226
|
+
command: "init",
|
|
70227
|
+
startedAt,
|
|
70228
|
+
data: {
|
|
70229
|
+
type: "scene",
|
|
70230
|
+
projectDir,
|
|
70231
|
+
name: projectName,
|
|
70232
|
+
profile,
|
|
70233
|
+
aspect,
|
|
70234
|
+
duration,
|
|
70235
|
+
visualStyle: visualStyle?.name ?? null,
|
|
70236
|
+
created: result.created,
|
|
70237
|
+
merged: result.merged,
|
|
70238
|
+
skipped: result.skipped,
|
|
70239
|
+
groups: result.groups,
|
|
70240
|
+
skillFiles: skillResult.files,
|
|
70241
|
+
skillBundleVersion: skillResult.bundleVersion
|
|
70242
|
+
}
|
|
70243
|
+
});
|
|
70244
|
+
return;
|
|
70245
|
+
}
|
|
70246
|
+
console.log();
|
|
70247
|
+
console.log(source_default.bold.magenta("VibeFrame Init") + source_default.dim(" \u2014 video project ready"));
|
|
70248
|
+
console.log(source_default.dim("\u2500".repeat(60)));
|
|
70249
|
+
console.log(`${source_default.dim("Project")} ${projectDir}`);
|
|
70250
|
+
console.log(`${source_default.dim("Profile")} ${profile} ${source_default.dim(sceneProfileSummary(profile))}`);
|
|
70251
|
+
console.log();
|
|
70252
|
+
console.log(source_default.bold("Edit first"));
|
|
70253
|
+
console.log(` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`);
|
|
70254
|
+
console.log(` ${source_default.cyan("DESIGN.md")} ${source_default.dim("palette, typography, motion rules")}`);
|
|
70255
|
+
console.log();
|
|
70256
|
+
console.log(source_default.bold("Then run"));
|
|
70257
|
+
console.log(` ${source_default.cyan("vibe build")} ${source_default.dim("build storyboard assets/compositions")}`);
|
|
70258
|
+
console.log(` ${source_default.cyan("vibe render")} ${source_default.dim("render final MP4")}`);
|
|
70259
|
+
console.log();
|
|
70260
|
+
console.log(source_default.bold("Files"));
|
|
70261
|
+
for (const p of result.created) console.log(source_default.green(` + ${p.replace(projectDir + "/", "")}`));
|
|
70262
|
+
for (const p of result.merged) console.log(source_default.yellow(` ~ ${p.replace(projectDir + "/", "")} (merged)`));
|
|
70263
|
+
for (const p of result.skipped) console.log(source_default.dim(` \xB7 ${p.replace(projectDir + "/", "")} (kept existing)`));
|
|
70264
|
+
for (const f of skillResult.files.filter((f2) => f2.status === "wrote")) {
|
|
70265
|
+
console.log(source_default.green(` + ${f.path.replace(projectDir + "/", "")}`));
|
|
70266
|
+
}
|
|
70267
|
+
console.log();
|
|
70268
|
+
console.log(source_default.dim(`Tip: cd ${projectDirArg} before running the next commands.`));
|
|
70269
|
+
console.log();
|
|
70270
|
+
}
|
|
70271
|
+
function sceneProfileSummary(profile) {
|
|
70272
|
+
if (profile === "minimal") return "(storyboard/design only)";
|
|
70273
|
+
if (profile === "agent") return "(recommended: + local agent authoring rules)";
|
|
70274
|
+
return "(+ render scaffold upfront)";
|
|
70275
|
+
}
|
|
70276
|
+
function printSceneInitPlan(opts) {
|
|
70277
|
+
console.log();
|
|
70278
|
+
console.log(source_default.bold.magenta("VibeFrame Init") + source_default.dim(opts.dryRun ? " \u2014 dry run" : ""));
|
|
70279
|
+
console.log(source_default.dim("\u2500".repeat(60)));
|
|
70280
|
+
console.log(`${source_default.dim("Project")} ${opts.projectDir}`);
|
|
70281
|
+
console.log(`${source_default.dim("Profile")} ${opts.profile} ${source_default.dim(sceneProfileSummary(opts.profile))}`);
|
|
70282
|
+
console.log(`${source_default.dim("Canvas")} ${opts.aspect} \xB7 ${opts.duration}s default`);
|
|
70283
|
+
if (opts.visualStyleName) console.log(`${source_default.dim("Style")} ${opts.visualStyleName}`);
|
|
70284
|
+
console.log();
|
|
70285
|
+
console.log(source_default.bold("Edit first"));
|
|
70286
|
+
console.log(` ${source_default.cyan("STORYBOARD.md")} ${source_default.dim("beats: narration, backdrop, minimum duration")}`);
|
|
70287
|
+
console.log(` ${source_default.cyan("DESIGN.md")} ${source_default.dim("palette, typography, motion rules")}`);
|
|
70288
|
+
console.log();
|
|
70289
|
+
console.log(source_default.bold("Project contents"));
|
|
70290
|
+
printGroup("authoring", opts.groups.authoring, opts.projectDir);
|
|
70291
|
+
printGroup("agent", opts.groups.agent, opts.projectDir);
|
|
70292
|
+
printGroup("render", opts.groups.render, opts.projectDir);
|
|
70293
|
+
console.log();
|
|
70294
|
+
console.log(source_default.dim("Dry run \u2014 no files were written. Re-run without --dry-run to create the project."));
|
|
70295
|
+
console.log();
|
|
70296
|
+
}
|
|
70297
|
+
function printGroup(label, files, projectDir) {
|
|
70298
|
+
if (files.length === 0) return;
|
|
70299
|
+
console.log(` ${source_default.bold(label)}`);
|
|
70300
|
+
for (const file of files) {
|
|
70301
|
+
console.log(source_default.dim(` ${file.replace(projectDir + "/", "")}`));
|
|
70302
|
+
}
|
|
70303
|
+
}
|
|
69645
70304
|
function resolveTargets(agent) {
|
|
69646
70305
|
if (agent === "all") {
|
|
69647
70306
|
return ["claude-code", "codex", "cursor", "aider", "gemini-cli", "opencode"];
|
|
@@ -69696,6 +70355,306 @@ function formatStatusIcon(status) {
|
|
|
69696
70355
|
}
|
|
69697
70356
|
}
|
|
69698
70357
|
|
|
70358
|
+
// src/commands/build.ts
|
|
70359
|
+
init_esm();
|
|
70360
|
+
init_source();
|
|
70361
|
+
init_ora();
|
|
70362
|
+
init_scene_build();
|
|
70363
|
+
init_output();
|
|
70364
|
+
import { resolve as resolve49 } from "node:path";
|
|
70365
|
+
var VALID_MODES = ["agent", "batch", "auto"];
|
|
70366
|
+
var buildCommand = new Command("build").description("Build a VibeFrame video project from STORYBOARD.md").argument("[project-dir]", "Video project directory", ".").option("--mode <mode>", "Build mode: agent|batch|auto", "auto").option("--effort <level>", "Compose effort tier (batch mode only): low|medium|high", "medium").option("--composer <provider>", "Batch composer: claude|openai|gemini").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").option("--voice <id>", "Voice id").option("--image-provider <name>", "Image provider: openai").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").addHelpText("after", `
|
|
70367
|
+
Examples:
|
|
70368
|
+
$ vibe init my-video --profile agent
|
|
70369
|
+
$ vibe build my-video --mode agent --tts kokoro
|
|
70370
|
+
$ vibe build my-video --skip-render
|
|
70371
|
+
|
|
70372
|
+
Advanced equivalent: \`vibe scene build\`.`).action(async (projectDirArg, options) => {
|
|
70373
|
+
const startedAt = Date.now();
|
|
70374
|
+
const projectDir = resolve49(projectDirArg);
|
|
70375
|
+
const mode = String(options.mode ?? "auto");
|
|
70376
|
+
if (!VALID_MODES.includes(mode)) {
|
|
70377
|
+
exitWithError(usageError(`Invalid --mode: ${mode}`, `Must be one of: ${VALID_MODES.join(", ")}`));
|
|
70378
|
+
}
|
|
70379
|
+
const params = {
|
|
70380
|
+
projectDir,
|
|
70381
|
+
mode,
|
|
70382
|
+
effort: options.effort,
|
|
70383
|
+
composer: options.composer,
|
|
70384
|
+
skipNarration: options.skipNarration ?? false,
|
|
70385
|
+
skipBackdrop: options.skipBackdrop ?? false,
|
|
70386
|
+
skipRender: options.skipRender ?? false,
|
|
70387
|
+
ttsProvider: options.tts,
|
|
70388
|
+
voice: options.voice,
|
|
70389
|
+
imageProvider: options.imageProvider,
|
|
70390
|
+
imageQuality: options.quality,
|
|
70391
|
+
imageSize: options.imageSize,
|
|
70392
|
+
force: options.force ?? false
|
|
70393
|
+
};
|
|
70394
|
+
if (options.dryRun) {
|
|
70395
|
+
if (!isJsonMode() && !isQuietMode()) {
|
|
70396
|
+
printBuildDryRun(projectDirArg, params);
|
|
70397
|
+
return;
|
|
70398
|
+
}
|
|
70399
|
+
outputSuccess({
|
|
70400
|
+
command: "build",
|
|
70401
|
+
startedAt,
|
|
70402
|
+
dryRun: true,
|
|
70403
|
+
data: { params }
|
|
70404
|
+
});
|
|
70405
|
+
return;
|
|
70406
|
+
}
|
|
70407
|
+
const spinner2 = isJsonMode() || isQuietMode() ? null : ora("Building video project...").start();
|
|
70408
|
+
const result = await executeSceneBuild({
|
|
70409
|
+
projectDir,
|
|
70410
|
+
mode,
|
|
70411
|
+
effort: options.effort,
|
|
70412
|
+
composer: options.composer,
|
|
70413
|
+
skipNarration: options.skipNarration,
|
|
70414
|
+
skipBackdrop: options.skipBackdrop,
|
|
70415
|
+
skipRender: options.skipRender,
|
|
70416
|
+
ttsProvider: options.tts,
|
|
70417
|
+
voice: options.voice,
|
|
70418
|
+
imageProvider: options.imageProvider,
|
|
70419
|
+
imageQuality: options.quality,
|
|
70420
|
+
imageSize: options.imageSize,
|
|
70421
|
+
force: options.force,
|
|
70422
|
+
onProgress: (e) => {
|
|
70423
|
+
if (!spinner2) return;
|
|
70424
|
+
if (e.type === "phase-start") spinner2.text = `Phase: ${e.phase}...`;
|
|
70425
|
+
else if (e.type === "render-done") spinner2.text = `Rendered: ${e.outputPath}`;
|
|
70426
|
+
}
|
|
70427
|
+
});
|
|
70428
|
+
if (!result.success) {
|
|
70429
|
+
spinner2?.fail(`Build failed: ${result.error}`);
|
|
70430
|
+
if (isJsonMode()) {
|
|
70431
|
+
outputSuccess({ command: "build", startedAt, data: { ...result } });
|
|
70432
|
+
process.exitCode = 1;
|
|
70433
|
+
return;
|
|
70434
|
+
}
|
|
70435
|
+
exitWithError(generalError(result.error ?? "Build failed"));
|
|
70436
|
+
}
|
|
70437
|
+
if (isJsonMode() || isQuietMode()) {
|
|
70438
|
+
outputSuccess({ command: "build", startedAt, data: { ...result } });
|
|
70439
|
+
return;
|
|
70440
|
+
}
|
|
70441
|
+
if (result.phase === "needs-author") {
|
|
70442
|
+
printNeedsAuthor(result);
|
|
70443
|
+
return;
|
|
70444
|
+
}
|
|
70445
|
+
printBuildResult(spinner2, result, projectDirArg);
|
|
70446
|
+
});
|
|
70447
|
+
function printBuildDryRun(projectDirArg, params) {
|
|
70448
|
+
console.log();
|
|
70449
|
+
console.log(source_default.bold.cyan("VibeFrame Build - dry run"));
|
|
70450
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70451
|
+
console.log(` Project: ${source_default.bold(projectDirArg)}`);
|
|
70452
|
+
console.log(` Mode: ${source_default.bold(params.mode)}`);
|
|
70453
|
+
console.log(` Composer: ${source_default.bold(String(params.composer ?? "auto"))}`);
|
|
70454
|
+
console.log(` TTS: ${source_default.bold(String(params.ttsProvider ?? "auto"))}${params.skipNarration ? source_default.dim(" (skipped)") : ""}`);
|
|
70455
|
+
console.log(` Image: ${source_default.bold(String(params.imageProvider ?? "openai"))} ${source_default.dim(`${params.imageQuality} ${params.imageSize}`)}${params.skipBackdrop ? source_default.dim(" (skipped)") : ""}`);
|
|
70456
|
+
console.log(` Render: ${source_default.bold(params.skipRender ? "skip" : "yes")}`);
|
|
70457
|
+
console.log(` Regenerate: ${source_default.bold(params.force ? "yes" : "cache when possible")}`);
|
|
70458
|
+
console.log();
|
|
70459
|
+
console.log(source_default.dim("No assets, provider calls, or video files were created."));
|
|
70460
|
+
}
|
|
70461
|
+
function printNeedsAuthor(result) {
|
|
70462
|
+
console.log(source_default.cyan("Agent authoring required"));
|
|
70463
|
+
console.log();
|
|
70464
|
+
console.log(source_default.bold.cyan("Missing compositions"));
|
|
70465
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70466
|
+
const missing = result.composePrompts?.beats.filter((beat) => !beat.exists) ?? [];
|
|
70467
|
+
if (missing.length === 0) {
|
|
70468
|
+
console.log(source_default.dim(" No missing files were reported. Re-run `vibe build` to continue."));
|
|
70469
|
+
} else {
|
|
70470
|
+
for (const beat of missing) {
|
|
70471
|
+
const dur = beat.duration !== void 0 ? source_default.dim(` ${beat.duration}s`) : "";
|
|
70472
|
+
console.log(` ${source_default.bold(beat.id)}${dur} -> ${beat.outputPath}`);
|
|
70473
|
+
}
|
|
70474
|
+
}
|
|
70475
|
+
console.log();
|
|
70476
|
+
console.log(source_default.bold.cyan("Next"));
|
|
70477
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70478
|
+
console.log(" Ask your coding agent to author the missing HTML files.");
|
|
70479
|
+
console.log(" Then run: vibe build");
|
|
70480
|
+
console.log(source_default.dim(" Use `vibe build --mode batch` when you want the CLI to call an LLM composer instead."));
|
|
70481
|
+
}
|
|
70482
|
+
function printBuildResult(spinner2, result, projectDirArg) {
|
|
70483
|
+
spinner2?.succeed(source_default.green(result.outputPath ? `Build complete: ${result.outputPath}` : "Build complete"));
|
|
70484
|
+
console.log();
|
|
70485
|
+
console.log(source_default.bold.cyan("Beats"));
|
|
70486
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70487
|
+
for (const beat of result.beats) {
|
|
70488
|
+
const narration = formatPrimitiveStatus2(beat.narrationStatus, beat.narrationPath);
|
|
70489
|
+
const backdrop = formatPrimitiveStatus2(beat.backdropStatus, beat.backdropPath);
|
|
70490
|
+
console.log(` ${source_default.bold(beat.beatId.padEnd(12))} narration: ${narration} backdrop: ${backdrop}`);
|
|
70491
|
+
if (beat.narrationError) console.log(source_default.red(` ! narration: ${beat.narrationError}`));
|
|
70492
|
+
if (beat.backdropError) console.log(source_default.red(` ! backdrop: ${beat.backdropError}`));
|
|
70493
|
+
}
|
|
70494
|
+
if (result.composeData) {
|
|
70495
|
+
console.log();
|
|
70496
|
+
console.log(source_default.bold.cyan("Compose"));
|
|
70497
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70498
|
+
console.log(` beats ${result.composeData.beats}`);
|
|
70499
|
+
console.log(` cache ${result.composeData.cacheHits} hit / ${result.composeData.beats - result.composeData.cacheHits} fresh`);
|
|
70500
|
+
console.log(` cost $${result.composeData.totalCostUsd.toFixed(4)}`);
|
|
70501
|
+
}
|
|
70502
|
+
console.log();
|
|
70503
|
+
console.log(source_default.bold.cyan("Next"));
|
|
70504
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70505
|
+
if (result.outputPath) {
|
|
70506
|
+
console.log(` Watch: ${result.outputPath}`);
|
|
70507
|
+
} else {
|
|
70508
|
+
console.log(` Render: vibe render ${projectDirArg}`);
|
|
70509
|
+
}
|
|
70510
|
+
console.log(source_default.dim(` Total: ${(result.totalLatencyMs / 1e3).toFixed(1)}s`));
|
|
70511
|
+
}
|
|
70512
|
+
function formatPrimitiveStatus2(status, path2) {
|
|
70513
|
+
switch (status) {
|
|
70514
|
+
case "generated":
|
|
70515
|
+
return source_default.green(path2 ?? "generated");
|
|
70516
|
+
case "cached":
|
|
70517
|
+
return source_default.dim(path2 ?? "cached");
|
|
70518
|
+
case "skipped":
|
|
70519
|
+
return source_default.dim("skipped");
|
|
70520
|
+
case "missing":
|
|
70521
|
+
return source_default.yellow("missing cue");
|
|
70522
|
+
case "failed":
|
|
70523
|
+
return source_default.red("failed");
|
|
70524
|
+
default:
|
|
70525
|
+
return status;
|
|
70526
|
+
}
|
|
70527
|
+
}
|
|
70528
|
+
|
|
70529
|
+
// src/commands/render.ts
|
|
70530
|
+
init_esm();
|
|
70531
|
+
init_source();
|
|
70532
|
+
init_ora();
|
|
70533
|
+
init_scene_render();
|
|
70534
|
+
init_output();
|
|
70535
|
+
import { resolve as resolve50 } from "node:path";
|
|
70536
|
+
var VALID_FPS2 = [24, 30, 60];
|
|
70537
|
+
var VALID_QUALITIES2 = ["draft", "standard", "high"];
|
|
70538
|
+
var VALID_FORMATS2 = ["mp4", "webm", "mov"];
|
|
70539
|
+
var renderCommand = new Command("render").description("Render a VibeFrame video project to MP4/WebM/MOV").argument("[project-dir]", "Video project directory", ".").option("-o, --out <path>", "Output file (default: renders/<name>-<timestamp>.<format>)").option("--root <file>", "Root composition file", "index.html").option("--fps <n>", `Frames per second: ${VALID_FPS2.join("|")}`, "30").option("--quality <q>", `Quality preset: ${VALID_QUALITIES2.join("|")}`, "standard").option("--format <f>", `Output container: ${VALID_FORMATS2.join("|")}`, "mp4").option("--workers <n>", "Capture workers (1-16, default 1)", "1").option("--dry-run", "Preview parameters without rendering").addHelpText("after", `
|
|
70540
|
+
Examples:
|
|
70541
|
+
$ vibe render my-video
|
|
70542
|
+
$ vibe render my-video -o renders/final.mp4 --quality high
|
|
70543
|
+
|
|
70544
|
+
Alias note: this is the project-level entrypoint for \`vibe scene render\`.`).action(async (projectDirArg, options) => {
|
|
70545
|
+
const startedAt = Date.now();
|
|
70546
|
+
const projectDir = resolve50(projectDirArg);
|
|
70547
|
+
const fps = parseFps(String(options.fps));
|
|
70548
|
+
const quality = parseQuality(String(options.quality));
|
|
70549
|
+
const format = parseFormat(String(options.format));
|
|
70550
|
+
const workers = parseWorkers(String(options.workers));
|
|
70551
|
+
const params = {
|
|
70552
|
+
projectDir,
|
|
70553
|
+
root: options.root,
|
|
70554
|
+
output: options.out,
|
|
70555
|
+
fps,
|
|
70556
|
+
quality,
|
|
70557
|
+
format,
|
|
70558
|
+
workers
|
|
70559
|
+
};
|
|
70560
|
+
if (options.dryRun) {
|
|
70561
|
+
if (!isJsonMode() && !isQuietMode()) {
|
|
70562
|
+
printRenderDryRun(projectDirArg, params);
|
|
70563
|
+
return;
|
|
70564
|
+
}
|
|
70565
|
+
outputSuccess({
|
|
70566
|
+
command: "render",
|
|
70567
|
+
startedAt,
|
|
70568
|
+
dryRun: true,
|
|
70569
|
+
data: { params }
|
|
70570
|
+
});
|
|
70571
|
+
return;
|
|
70572
|
+
}
|
|
70573
|
+
const spinner2 = isJsonMode() || isQuietMode() ? null : ora("Rendering video project...").start();
|
|
70574
|
+
const result = await executeSceneRender({
|
|
70575
|
+
projectDir,
|
|
70576
|
+
root: options.root,
|
|
70577
|
+
output: options.out,
|
|
70578
|
+
fps,
|
|
70579
|
+
quality,
|
|
70580
|
+
format,
|
|
70581
|
+
workers,
|
|
70582
|
+
onProgress: (pct, stage) => {
|
|
70583
|
+
if (spinner2) spinner2.text = `Rendering [${Math.round(pct * 100)}%] ${stage}`;
|
|
70584
|
+
}
|
|
70585
|
+
});
|
|
70586
|
+
if (!result.success) {
|
|
70587
|
+
spinner2?.fail("Render failed");
|
|
70588
|
+
if (isJsonMode()) {
|
|
70589
|
+
outputSuccess({ command: "render", startedAt, data: { ...result } });
|
|
70590
|
+
process.exitCode = 1;
|
|
70591
|
+
return;
|
|
70592
|
+
}
|
|
70593
|
+
exitWithError(generalError(result.error ?? "Render failed"));
|
|
70594
|
+
}
|
|
70595
|
+
if (isJsonMode() || isQuietMode()) {
|
|
70596
|
+
outputSuccess({ command: "render", startedAt, data: { ...result } });
|
|
70597
|
+
return;
|
|
70598
|
+
}
|
|
70599
|
+
printRenderResult(spinner2, result);
|
|
70600
|
+
});
|
|
70601
|
+
function printRenderDryRun(projectDirArg, params) {
|
|
70602
|
+
console.log();
|
|
70603
|
+
console.log(source_default.bold.cyan("VibeFrame Render - dry run"));
|
|
70604
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70605
|
+
console.log(` Project: ${source_default.bold(projectDirArg)}`);
|
|
70606
|
+
console.log(` Root: ${source_default.bold(String(params.root))}`);
|
|
70607
|
+
console.log(` Output: ${source_default.bold(String(params.output ?? `renders/<name>-<timestamp>.${params.format}`))}`);
|
|
70608
|
+
console.log(` Format: ${source_default.bold(params.format)}`);
|
|
70609
|
+
console.log(` Quality/FPS: ${source_default.bold(`${params.quality} / ${params.fps}`)}`);
|
|
70610
|
+
console.log(` Workers: ${source_default.bold(String(params.workers))}`);
|
|
70611
|
+
console.log();
|
|
70612
|
+
console.log(source_default.dim("No browser capture, FFmpeg mux, or video files were created."));
|
|
70613
|
+
}
|
|
70614
|
+
function printRenderResult(spinner2, result) {
|
|
70615
|
+
spinner2?.succeed(source_default.green(`Render complete: ${result.outputPath}`));
|
|
70616
|
+
console.log();
|
|
70617
|
+
console.log(source_default.bold.cyan("Output"));
|
|
70618
|
+
console.log(source_default.dim("-".repeat(60)));
|
|
70619
|
+
console.log(` File: ${source_default.bold(result.outputPath)}`);
|
|
70620
|
+
console.log(` Format: ${result.format ?? "mp4"}`);
|
|
70621
|
+
console.log(` Quality: ${result.quality ?? "standard"}`);
|
|
70622
|
+
console.log(` FPS: ${result.fps ?? 30}`);
|
|
70623
|
+
if (result.totalFrames !== void 0) console.log(` Frames: ${result.framesRendered ?? result.totalFrames}/${result.totalFrames}`);
|
|
70624
|
+
if (result.durationMs !== void 0) console.log(` Duration: ${(result.durationMs / 1e3).toFixed(2)}s`);
|
|
70625
|
+
if (result.audioCount !== void 0) {
|
|
70626
|
+
const audio = result.audioCount > 0 ? `${result.audioCount} track${result.audioCount === 1 ? "" : "s"} muxed` : "silent";
|
|
70627
|
+
console.log(` Audio: ${audio}`);
|
|
70628
|
+
}
|
|
70629
|
+
if (result.audioMuxWarning) console.log(source_default.yellow(` Warning: ${result.audioMuxWarning}`));
|
|
70630
|
+
}
|
|
70631
|
+
function parseFps(value) {
|
|
70632
|
+
const n = Number.parseInt(value, 10);
|
|
70633
|
+
if (!VALID_FPS2.includes(n)) {
|
|
70634
|
+
exitWithError(usageError(`Invalid --fps: ${value}`, `Valid: ${VALID_FPS2.join(", ")}`));
|
|
70635
|
+
}
|
|
70636
|
+
return n;
|
|
70637
|
+
}
|
|
70638
|
+
function parseQuality(value) {
|
|
70639
|
+
if (!VALID_QUALITIES2.includes(value)) {
|
|
70640
|
+
exitWithError(usageError(`Invalid --quality: ${value}`, `Valid: ${VALID_QUALITIES2.join(", ")}`));
|
|
70641
|
+
}
|
|
70642
|
+
return value;
|
|
70643
|
+
}
|
|
70644
|
+
function parseFormat(value) {
|
|
70645
|
+
if (!VALID_FORMATS2.includes(value)) {
|
|
70646
|
+
exitWithError(usageError(`Invalid --format: ${value}`, `Valid: ${VALID_FORMATS2.join(", ")}`));
|
|
70647
|
+
}
|
|
70648
|
+
return value;
|
|
70649
|
+
}
|
|
70650
|
+
function parseWorkers(value) {
|
|
70651
|
+
const n = Number.parseInt(value, 10);
|
|
70652
|
+
if (!Number.isFinite(n) || n < 1 || n > 16) {
|
|
70653
|
+
exitWithError(usageError(`Invalid --workers: ${value}`, "Must be an integer between 1 and 16"));
|
|
70654
|
+
}
|
|
70655
|
+
return n;
|
|
70656
|
+
}
|
|
70657
|
+
|
|
69699
70658
|
// src/commands/doctor.ts
|
|
69700
70659
|
init_esm();
|
|
69701
70660
|
init_source();
|
|
@@ -69711,7 +70670,7 @@ init_scene_build();
|
|
|
69711
70670
|
init_output();
|
|
69712
70671
|
import { access as access7 } from "node:fs/promises";
|
|
69713
70672
|
import { existsSync as existsSync35 } from "node:fs";
|
|
69714
|
-
import { resolve as
|
|
70673
|
+
import { resolve as resolve51 } from "node:path";
|
|
69715
70674
|
var COMMAND_KEY_MAP = getCommandKeyMap();
|
|
69716
70675
|
var FREE_COMMANDS = [
|
|
69717
70676
|
"edit silence-cut",
|
|
@@ -69868,7 +70827,7 @@ async function runDiagnostics() {
|
|
|
69868
70827
|
const cwd = process.cwd();
|
|
69869
70828
|
const projectFiles = ["AGENTS.md", "CLAUDE.md", "vibe.project.yaml"].map((rel) => ({
|
|
69870
70829
|
path: rel,
|
|
69871
|
-
exists: existsSync35(
|
|
70830
|
+
exists: existsSync35(resolve51(cwd, rel))
|
|
69872
70831
|
}));
|
|
69873
70832
|
const projectInitialized = projectFiles.some((f) => f.exists);
|
|
69874
70833
|
const hosts = detectAgentHosts();
|
|
@@ -69883,8 +70842,8 @@ async function runDiagnostics() {
|
|
|
69883
70842
|
} catch (err) {
|
|
69884
70843
|
if (!(err instanceof ComposerResolveError)) throw err;
|
|
69885
70844
|
}
|
|
69886
|
-
const sceneProjectInCwd = existsSync35(
|
|
69887
|
-
const skillInstalled = existsSync35(
|
|
70845
|
+
const sceneProjectInCwd = existsSync35(resolve51(cwd, "STORYBOARD.md"));
|
|
70846
|
+
const skillInstalled = existsSync35(resolve51(cwd, "SKILL.md"));
|
|
69888
70847
|
return {
|
|
69889
70848
|
system: {
|
|
69890
70849
|
node: { version: nodeVersion, ok: true },
|
|
@@ -70049,10 +71008,10 @@ init_exec_safe();
|
|
|
70049
71008
|
init_detect();
|
|
70050
71009
|
init_ai_edit();
|
|
70051
71010
|
init_output();
|
|
70052
|
-
import { resolve as
|
|
71011
|
+
import { resolve as resolve52 } from "node:path";
|
|
70053
71012
|
import { existsSync as existsSync36 } from "node:fs";
|
|
70054
71013
|
import { mkdir as mkdir21, rm as rm4 } from "node:fs/promises";
|
|
70055
|
-
var DEMO_DIR =
|
|
71014
|
+
var DEMO_DIR = resolve52(process.cwd(), ".vibeframe-demo");
|
|
70056
71015
|
async function generateTestVideo(outputPath) {
|
|
70057
71016
|
const filter = [
|
|
70058
71017
|
// Video: color bars with text
|
|
@@ -70132,7 +71091,7 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
70132
71091
|
console.log();
|
|
70133
71092
|
}
|
|
70134
71093
|
await mkdir21(DEMO_DIR, { recursive: true });
|
|
70135
|
-
const testVideo =
|
|
71094
|
+
const testVideo = resolve52(DEMO_DIR, "test-input.mp4");
|
|
70136
71095
|
const genSpinner = !isJson ? ora("Generating test video...").start() : null;
|
|
70137
71096
|
const generated = await generateTestVideo(testVideo);
|
|
70138
71097
|
if (!generated) {
|
|
@@ -70161,7 +71120,7 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
70161
71120
|
name: "Silence Cut",
|
|
70162
71121
|
description: "Removing silent segments",
|
|
70163
71122
|
run: async () => {
|
|
70164
|
-
const output =
|
|
71123
|
+
const output = resolve52(DEMO_DIR, "silence-cut.mp4");
|
|
70165
71124
|
const r = await executeSilenceCut({ videoPath: testVideo, outputPath: output, padding: 0.1 });
|
|
70166
71125
|
if (r.success && r.totalDuration && r.silentDuration) {
|
|
70167
71126
|
return { success: true, detail: `Removed ${r.silentDuration.toFixed(1)}s silence (${r.totalDuration.toFixed(1)}s \u2192 ${(r.totalDuration - r.silentDuration).toFixed(1)}s)` };
|
|
@@ -70173,7 +71132,7 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
70173
71132
|
name: "Noise Reduce",
|
|
70174
71133
|
description: "Reducing background noise",
|
|
70175
71134
|
run: async () => {
|
|
70176
|
-
const output =
|
|
71135
|
+
const output = resolve52(DEMO_DIR, "noise-reduced.mp4");
|
|
70177
71136
|
const r = await executeNoiseReduce({ inputPath: testVideo, outputPath: output, strength: "medium" });
|
|
70178
71137
|
return { success: r.success, detail: r.success ? `Cleaned audio \u2192 ${output}` : r.error };
|
|
70179
71138
|
}
|
|
@@ -70182,7 +71141,7 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
70182
71141
|
name: "Fade Effects",
|
|
70183
71142
|
description: "Adding fade in/out",
|
|
70184
71143
|
run: async () => {
|
|
70185
|
-
const output =
|
|
71144
|
+
const output = resolve52(DEMO_DIR, "faded.mp4");
|
|
70186
71145
|
const r = await executeFade({ videoPath: testVideo, outputPath: output, fadeIn: 1, fadeOut: 1 });
|
|
70187
71146
|
return { success: r.success, detail: r.success ? `1s fade in + 1s fade out \u2192 ${output}` : r.error };
|
|
70188
71147
|
}
|
|
@@ -70250,13 +71209,13 @@ var demoCommand = new Command("demo").description("Run sample edits on a test vi
|
|
|
70250
71209
|
// src/commands/context.ts
|
|
70251
71210
|
init_esm();
|
|
70252
71211
|
import { readFile as readFile35 } from "node:fs/promises";
|
|
70253
|
-
import { resolve as
|
|
71212
|
+
import { resolve as resolve53, dirname as dirname19 } from "node:path";
|
|
70254
71213
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
70255
71214
|
var __filename = fileURLToPath2(import.meta.url);
|
|
70256
71215
|
var __dirname = dirname19(__filename);
|
|
70257
71216
|
var contextCommand = new Command("context").description("Print CLI context/guidelines for AI agent integration").action(async (_options, cmd) => {
|
|
70258
71217
|
const options = { json: cmd.parent?.opts()?.json || cmd.opts()?.json };
|
|
70259
|
-
const contextPath =
|
|
71218
|
+
const contextPath = resolve53(__dirname, "../../CONTEXT.md");
|
|
70260
71219
|
try {
|
|
70261
71220
|
const content = await readFile35(contextPath, "utf-8");
|
|
70262
71221
|
if (options.json) {
|
|
@@ -70997,14 +71956,14 @@ async function promptConfirm2(toolName, args) {
|
|
|
70997
71956
|
output: process.stdout,
|
|
70998
71957
|
terminal: process.stdin.isTTY ?? false
|
|
70999
71958
|
});
|
|
71000
|
-
return new Promise((
|
|
71959
|
+
return new Promise((resolve64) => {
|
|
71001
71960
|
const argsStr = JSON.stringify(args, null, 2);
|
|
71002
71961
|
console.log();
|
|
71003
71962
|
console.log(source_default.yellow(`Execute ${source_default.bold(toolName)}?`));
|
|
71004
71963
|
console.log(source_default.dim(argsStr));
|
|
71005
71964
|
rl.question(source_default.cyan("(y/n): "), (answer) => {
|
|
71006
71965
|
rl.close();
|
|
71007
|
-
|
|
71966
|
+
resolve64(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
71008
71967
|
});
|
|
71009
71968
|
});
|
|
71010
71969
|
}
|
|
@@ -71093,7 +72052,7 @@ async function startAgent(options = {}) {
|
|
|
71093
72052
|
console.log();
|
|
71094
72053
|
console.log(source_default.dim(" Commands: exit \xB7 reset \xB7 tools \xB7 context"));
|
|
71095
72054
|
console.log();
|
|
71096
|
-
return new Promise((
|
|
72055
|
+
return new Promise((resolve64) => {
|
|
71097
72056
|
if (typeof process.stdin.ref === "function") {
|
|
71098
72057
|
process.stdin.ref();
|
|
71099
72058
|
}
|
|
@@ -71195,7 +72154,7 @@ async function startAgent(options = {}) {
|
|
|
71195
72154
|
clearInterval(keepalive);
|
|
71196
72155
|
console.log();
|
|
71197
72156
|
console.log(source_default.dim("Goodbye!"));
|
|
71198
|
-
|
|
72157
|
+
resolve64();
|
|
71199
72158
|
});
|
|
71200
72159
|
rl.prompt();
|
|
71201
72160
|
});
|
|
@@ -71378,7 +72337,7 @@ async function readStdin() {
|
|
|
71378
72337
|
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
71379
72338
|
}
|
|
71380
72339
|
var program2 = new Command();
|
|
71381
|
-
program2.name("vibe").showSuggestionAfterError(true).description("VibeFrame CLI - AI
|
|
72340
|
+
program2.name("vibe").showSuggestionAfterError(true).description("VibeFrame CLI - video projects, AI media generation, and workflow automation").version(package_default.version).option("--json", "Output in JSON format").option("-q, --quiet", "Output only the primary result value (path, URL, or ID)").option("--fields <fields>", "Limit JSON output to specific fields (comma-separated)").option("--stdin", "Read options from stdin as JSON (for agent/script use)").option("--describe", "Show JSON schema for the command and exit (no execution)").exitOverride().configureOutput({
|
|
71382
72341
|
outputError: (str, write2) => {
|
|
71383
72342
|
if (process.env.VIBE_JSON_OUTPUT === "1" || process.argv.includes("--json")) {
|
|
71384
72343
|
const err = { success: false, error: str.trim(), code: "USAGE_ERROR", exitCode: 2, retryable: false };
|
|
@@ -71403,13 +72362,25 @@ Cost tiers:
|
|
|
71403
72362
|
Low analyze, audio transcribe, generate image ~$0.01-$0.10
|
|
71404
72363
|
High generate video, edit image ~$1-$5
|
|
71405
72364
|
V.High pipeline (regenerate-scene, highlights, auto-shorts) ~$5-$50+
|
|
72365
|
+
Tip Add --dry-run before paid generation to preview the work.
|
|
71406
72366
|
|
|
71407
|
-
|
|
72367
|
+
Project flow:
|
|
71408
72368
|
vibe doctor Check system health and API keys
|
|
71409
72369
|
vibe setup Configure API keys interactively
|
|
71410
|
-
vibe
|
|
72370
|
+
vibe init my-video Scaffold a video project
|
|
72371
|
+
vibe build my-video Build STORYBOARD.md into scene compositions/assets
|
|
72372
|
+
vibe render my-video Render the project to MP4
|
|
72373
|
+
|
|
72374
|
+
Asset tools:
|
|
72375
|
+
vibe generate image "..." Generate a still image
|
|
72376
|
+
vibe generate video "..." Generate a standalone AI video
|
|
72377
|
+
|
|
72378
|
+
Workflow automation:
|
|
72379
|
+
vibe run workflow.yaml Run a Video-as-YAML pipeline
|
|
72380
|
+
vibe schema --list Discover all commands
|
|
71411
72381
|
|
|
71412
72382
|
Agent integration:
|
|
72383
|
+
vibe walkthrough scene Load the scene authoring guide
|
|
71413
72384
|
echo '{"provider":"kling"}' | vibe generate video "prompt" --stdin --json
|
|
71414
72385
|
vibe schema generate.video Get parameter schema for any command
|
|
71415
72386
|
`
|
|
@@ -71478,6 +72449,8 @@ program2.addCommand(audioCommand);
|
|
|
71478
72449
|
program2.addCommand(pipelineCommand);
|
|
71479
72450
|
program2.addCommand(setupCommand);
|
|
71480
72451
|
program2.addCommand(initCommand);
|
|
72452
|
+
program2.addCommand(buildCommand);
|
|
72453
|
+
program2.addCommand(renderCommand);
|
|
71481
72454
|
program2.addCommand(doctorCommand);
|
|
71482
72455
|
program2.addCommand(demoCommand);
|
|
71483
72456
|
program2.addCommand(runCommand);
|