@vosjs/cli 0.69.0 → 0.70.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/README.md +3 -1
- package/dist/{chunk-5DUXN3RX.js → chunk-KIFXVPBR.js} +417 -261
- package/dist/chunk-KIFXVPBR.js.map +1 -0
- package/dist/{chunk-5GZ3GPAK.js → chunk-YZAM4W7B.js} +12 -7
- package/dist/{chunk-5GZ3GPAK.js.map → chunk-YZAM4W7B.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{run-NDBUFOIG.js → run-7QR3CQSF.js} +3 -3
- package/package.json +5 -5
- package/schema/actions.schema.json +7 -7
- package/dist/chunk-5DUXN3RX.js.map +0 -1
- /package/dist/{run-NDBUFOIG.js.map → run-7QR3CQSF.js.map} +0 -0
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
reencodeStill,
|
|
10
10
|
renderStill,
|
|
11
11
|
videoFormat
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YZAM4W7B.js";
|
|
13
13
|
import {
|
|
14
14
|
PREV_DOC_NAME,
|
|
15
15
|
RECORDING_NAME,
|
|
@@ -131,9 +131,9 @@ function landedLines(l) {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
// src/plugin/run.ts
|
|
134
|
-
import { mkdir as mkdir8, readFile as
|
|
135
|
-
import { existsSync as
|
|
136
|
-
import { join as
|
|
134
|
+
import { mkdir as mkdir8, readFile as readFile13, rm as rm2 } from "fs/promises";
|
|
135
|
+
import { existsSync as existsSync23 } from "fs";
|
|
136
|
+
import { join as join25, resolve as resolve13 } from "path";
|
|
137
137
|
import {
|
|
138
138
|
END_CARD_FROM as END_CARD_FROM2,
|
|
139
139
|
docOutputDuration as docOutputDuration3,
|
|
@@ -5341,8 +5341,12 @@ import {
|
|
|
5341
5341
|
} from "@vosjs/studio-core";
|
|
5342
5342
|
|
|
5343
5343
|
// src/plugin/reuse.ts
|
|
5344
|
-
import { docFreezes } from "@vosjs/studio-core";
|
|
5344
|
+
import { docFreezes, ratedSegments as ratedSegments6 } from "@vosjs/studio-core";
|
|
5345
5345
|
var MIN_SPAN = 0.15;
|
|
5346
|
+
var AFTER_STEP = 1;
|
|
5347
|
+
var LAYER_AFTER_STEP = 5;
|
|
5348
|
+
var WINDOW_SLACK = 0.05;
|
|
5349
|
+
var LAYER_DRIFT = 0.5;
|
|
5346
5350
|
function matchStep(old, newSteps) {
|
|
5347
5351
|
const usable = (s) => !!s && s.skipped !== true;
|
|
5348
5352
|
if (old.id) {
|
|
@@ -5358,6 +5362,19 @@ function matchStep(old, newSteps) {
|
|
|
5358
5362
|
);
|
|
5359
5363
|
return old.selector !== void 0 && bySignature.length === 1 ? bySignature[0] : usable(atIndex) && old.selector === void 0 && atIndex.do === old.do ? atIndex : null;
|
|
5360
5364
|
}
|
|
5365
|
+
function stepByRef(steps, ref) {
|
|
5366
|
+
return typeof ref === "string" ? steps.find((s) => s.id === ref) : steps.find((s) => s.step === ref);
|
|
5367
|
+
}
|
|
5368
|
+
var stepName = (s) => String(s.id ?? s.step);
|
|
5369
|
+
function stepUnder(t, steps, after = AFTER_STEP) {
|
|
5370
|
+
const gestures = steps.filter((s) => s.do !== "wait" && !s.skipped);
|
|
5371
|
+
const holding = gestures.find(
|
|
5372
|
+
(s) => s.tStart - WINDOW_SLACK <= t && t <= s.tEnd + WINDOW_SLACK
|
|
5373
|
+
);
|
|
5374
|
+
if (holding) return holding;
|
|
5375
|
+
const before = gestures.filter((s) => s.tEnd <= t && t - s.tEnd <= after).sort((a, b) => b.tEnd - a.tEnd);
|
|
5376
|
+
return before[0] ?? null;
|
|
5377
|
+
}
|
|
5361
5378
|
function buildStepMap(oldSteps, newSteps, oldDuration, newDuration) {
|
|
5362
5379
|
const points = [
|
|
5363
5380
|
{ from: 0, to: 0 },
|
|
@@ -5401,34 +5418,57 @@ function buildStepMap(oldSteps, newSteps, oldDuration, newDuration) {
|
|
|
5401
5418
|
return { map, unmatched, matched };
|
|
5402
5419
|
}
|
|
5403
5420
|
function resolveAnchor2(anchor, newSteps) {
|
|
5404
|
-
const step =
|
|
5421
|
+
const step = stepByRef(newSteps, anchor.step);
|
|
5405
5422
|
if (!step || step.skipped) return null;
|
|
5406
5423
|
const base = anchor.at === "end" ? step.tEnd : step.tStart;
|
|
5407
5424
|
return base + (anchor.offset ?? 0);
|
|
5408
5425
|
}
|
|
5409
|
-
function
|
|
5426
|
+
function framed(span) {
|
|
5427
|
+
const s = span;
|
|
5428
|
+
if (typeof s.level === "number" && typeof s.cx === "number") {
|
|
5429
|
+
return ` (it framed ${s.cx.toFixed(2)}, ${(s.cy ?? 0).toFixed(2)} at ${s.level.toFixed(1)}\xD7)`;
|
|
5430
|
+
}
|
|
5431
|
+
if (typeof s.rate === "number") return ` (${s.rate}\xD7 speed)`;
|
|
5432
|
+
if (typeof s.rx === "number" || typeof s.ry === "number") {
|
|
5433
|
+
return ` (lean ${s.rx ?? 0}\xB0, ${s.ry ?? 0}\xB0)`;
|
|
5434
|
+
}
|
|
5435
|
+
if (typeof s.seconds === "number") return ` (a ${s.seconds} s hold)`;
|
|
5436
|
+
return "";
|
|
5437
|
+
}
|
|
5438
|
+
function placeMoment(t, anchor, ctx) {
|
|
5439
|
+
if (anchor) {
|
|
5440
|
+
const at2 = resolveAnchor2(anchor, ctx.newSteps);
|
|
5441
|
+
if (at2 === null) return { gone: String(anchor.step) };
|
|
5442
|
+
ctx.report.anchored++;
|
|
5443
|
+
return { at: at2, via: "anchor" };
|
|
5444
|
+
}
|
|
5445
|
+
const under = stepUnder(t, ctx.oldSteps);
|
|
5446
|
+
if (under) {
|
|
5447
|
+
const hit = matchStep(under, ctx.newSteps);
|
|
5448
|
+
if (!hit) return { gone: under };
|
|
5449
|
+
ctx.report.anchored++;
|
|
5450
|
+
return { at: hit.tStart + (t - under.tStart), via: "step" };
|
|
5451
|
+
}
|
|
5452
|
+
ctx.report.mapped++;
|
|
5453
|
+
return { at: ctx.stepMap.map(t), via: "map" };
|
|
5454
|
+
}
|
|
5455
|
+
var goneWords = (gone) => typeof gone === "string" ? `its anchored step (${gone}) is missing or skipped in the new recording` : `it sat on step ${stepName(gone)} (${gone.do}${gone.selector ? ` ${gone.selector}` : ""}), which is missing or skipped in the new recording`;
|
|
5456
|
+
function retimeSpans(kind, spans, ctx, opts = { overlap: "trim" }) {
|
|
5410
5457
|
const out = [];
|
|
5411
5458
|
for (const span of spans) {
|
|
5412
5459
|
const length = span.out - span.in;
|
|
5413
|
-
|
|
5414
|
-
if (
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
);
|
|
5420
|
-
} else {
|
|
5421
|
-
report.anchored++;
|
|
5422
|
-
}
|
|
5423
|
-
}
|
|
5424
|
-
if (nextIn === null) {
|
|
5425
|
-
nextIn = stepMap.map(span.in);
|
|
5426
|
-
report.mapped++;
|
|
5460
|
+
const placed = placeMoment(span.in, span.anchor, ctx);
|
|
5461
|
+
if ("gone" in placed) {
|
|
5462
|
+
ctx.report.flagged.push(
|
|
5463
|
+
`${kind} ${span.id}: ${goneWords(placed.gone)} \u2014 dropped; re-aim it on the new footage${framed(span)}`
|
|
5464
|
+
);
|
|
5465
|
+
continue;
|
|
5427
5466
|
}
|
|
5428
|
-
const
|
|
5429
|
-
const
|
|
5467
|
+
const nextIn = placed.at;
|
|
5468
|
+
const nextOut = Math.min(nextIn + length, ctx.newDuration);
|
|
5469
|
+
const clampedIn = Math.max(0, Math.min(nextIn, ctx.newDuration));
|
|
5430
5470
|
if (nextOut - clampedIn < MIN_SPAN) {
|
|
5431
|
-
report.flagged.push(
|
|
5471
|
+
ctx.report.flagged.push(
|
|
5432
5472
|
`${kind} ${span.id}: lands outside the new recording (${clampedIn.toFixed(2)}s) \u2014 dropped`
|
|
5433
5473
|
);
|
|
5434
5474
|
continue;
|
|
@@ -5436,18 +5476,19 @@ function retimeSpans(kind, spans, stepMap, newSteps, newDuration, report) {
|
|
|
5436
5476
|
out.push({ ...span, in: +clampedIn.toFixed(3), out: +nextOut.toFixed(3) });
|
|
5437
5477
|
}
|
|
5438
5478
|
out.sort((a, b) => a.in - b.in);
|
|
5479
|
+
if (opts.overlap === "keep") return out;
|
|
5439
5480
|
const kept = [];
|
|
5440
5481
|
for (const span of out) {
|
|
5441
5482
|
const prev = kept.at(-1);
|
|
5442
5483
|
if (prev && span.in < prev.out) {
|
|
5443
5484
|
const trimmed = { ...span, in: +prev.out.toFixed(3) };
|
|
5444
5485
|
if (trimmed.out - trimmed.in < MIN_SPAN) {
|
|
5445
|
-
report.flagged.push(
|
|
5486
|
+
ctx.report.flagged.push(
|
|
5446
5487
|
`${kind} ${span.id}: fully overlapped ${prev.id} after re-timing \u2014 dropped`
|
|
5447
5488
|
);
|
|
5448
5489
|
continue;
|
|
5449
5490
|
}
|
|
5450
|
-
report.flagged.push(
|
|
5491
|
+
ctx.report.flagged.push(
|
|
5451
5492
|
`${kind} ${span.id}: overlapped ${prev.id} after re-timing \u2014 trimmed to ${trimmed.in.toFixed(2)}s`
|
|
5452
5493
|
);
|
|
5453
5494
|
kept.push(trimmed);
|
|
@@ -5466,9 +5507,10 @@ function retimeCut(prev, newSteps, newDurationMs) {
|
|
|
5466
5507
|
for (const miss of stepMap.unmatched) {
|
|
5467
5508
|
if (miss.do === "wait") continue;
|
|
5468
5509
|
report.flagged.push(
|
|
5469
|
-
`step ${miss
|
|
5510
|
+
`step ${stepName(miss)} (${miss.do}${miss.selector ? ` ${miss.selector}` : ""}) has no match in the new recording \u2014 times near it interpolate`
|
|
5470
5511
|
);
|
|
5471
5512
|
}
|
|
5513
|
+
const ctx = { oldSteps, newSteps, stepMap, newDuration, report };
|
|
5472
5514
|
const untrimmed = prev.segments.length === 0 || prev.segments.length === 1 && prev.segments[0].in <= 0.05 && prev.segments[0].out >= oldDuration - 0.05;
|
|
5473
5515
|
const segments = untrimmed ? [{ in: 0, out: +newDuration.toFixed(3) }] : prev.segments.map((s) => ({
|
|
5474
5516
|
in: +stepMap.map(s.in).toFixed(3),
|
|
@@ -5482,49 +5524,34 @@ function retimeCut(prev, newSteps, newDurationMs) {
|
|
|
5482
5524
|
const zoom = retimeSpans(
|
|
5483
5525
|
"zoom",
|
|
5484
5526
|
prev.zoom.filter((z) => z.source === "manual"),
|
|
5485
|
-
|
|
5486
|
-
newSteps,
|
|
5487
|
-
newDuration,
|
|
5488
|
-
report
|
|
5527
|
+
ctx
|
|
5489
5528
|
);
|
|
5490
5529
|
const speed = retimeSpans(
|
|
5491
5530
|
"speed",
|
|
5492
5531
|
(prev.speed ?? []).filter((s) => s.source !== "auto"),
|
|
5493
|
-
|
|
5494
|
-
newSteps,
|
|
5495
|
-
newDuration,
|
|
5496
|
-
report
|
|
5532
|
+
ctx
|
|
5497
5533
|
);
|
|
5498
5534
|
const tilt = retimeSpans(
|
|
5499
5535
|
"tilt",
|
|
5500
5536
|
(prev.tilt ?? []).filter((t) => t.source !== "auto"),
|
|
5501
|
-
|
|
5502
|
-
newSteps,
|
|
5503
|
-
newDuration,
|
|
5504
|
-
report
|
|
5537
|
+
ctx
|
|
5505
5538
|
);
|
|
5506
5539
|
const freeze = [];
|
|
5507
5540
|
for (const f of docFreezes(prev)) {
|
|
5508
|
-
|
|
5509
|
-
if (
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
);
|
|
5515
|
-
} else report.anchored++;
|
|
5516
|
-
}
|
|
5517
|
-
if (at2 === null) {
|
|
5518
|
-
at2 = stepMap.map(f.at);
|
|
5519
|
-
report.mapped++;
|
|
5541
|
+
const placed = placeMoment(f.at, f.anchor, ctx);
|
|
5542
|
+
if ("gone" in placed) {
|
|
5543
|
+
report.flagged.push(
|
|
5544
|
+
`freeze ${f.id}: ${goneWords(placed.gone)} \u2014 dropped; re-place it on the new footage${framed(f)}`
|
|
5545
|
+
);
|
|
5546
|
+
continue;
|
|
5520
5547
|
}
|
|
5521
|
-
if (
|
|
5548
|
+
if (placed.at < 0 || placed.at > newDuration) {
|
|
5522
5549
|
report.flagged.push(
|
|
5523
|
-
`freeze ${f.id}: lands outside the new recording (${
|
|
5550
|
+
`freeze ${f.id}: lands outside the new recording (${placed.at.toFixed(2)}s) \u2014 dropped`
|
|
5524
5551
|
);
|
|
5525
5552
|
continue;
|
|
5526
5553
|
}
|
|
5527
|
-
freeze.push({ ...f, at: +
|
|
5554
|
+
freeze.push({ ...f, at: +placed.at.toFixed(3) });
|
|
5528
5555
|
}
|
|
5529
5556
|
freeze.sort((a, b) => a.at - b.at);
|
|
5530
5557
|
const rejected = [];
|
|
@@ -5533,15 +5560,97 @@ function retimeCut(prev, newSteps, newDurationMs) {
|
|
|
5533
5560
|
...retimeSpans(
|
|
5534
5561
|
`rejected ${lane}`,
|
|
5535
5562
|
(prev.rejected ?? []).filter((r) => r.lane === lane),
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
newDuration,
|
|
5539
|
-
report
|
|
5563
|
+
ctx,
|
|
5564
|
+
{ overlap: "keep" }
|
|
5540
5565
|
)
|
|
5541
5566
|
);
|
|
5542
5567
|
}
|
|
5543
5568
|
return { segments, zoom, speed, freeze, tilt, rejected, report };
|
|
5544
5569
|
}
|
|
5570
|
+
function sourceToOutput(pieces, s) {
|
|
5571
|
+
let acc = 0;
|
|
5572
|
+
for (const p of pieces) {
|
|
5573
|
+
const rate = p.rate && p.rate > 0 ? p.rate : 1;
|
|
5574
|
+
const len = Math.max(0, p.out - p.in) / rate;
|
|
5575
|
+
if (s < p.in) return acc;
|
|
5576
|
+
if (s <= p.out) return acc + (s - p.in) / rate;
|
|
5577
|
+
acc += len;
|
|
5578
|
+
}
|
|
5579
|
+
return acc;
|
|
5580
|
+
}
|
|
5581
|
+
function outputToSource(pieces, t) {
|
|
5582
|
+
let acc = 0;
|
|
5583
|
+
for (const p of pieces) {
|
|
5584
|
+
const rate = p.rate && p.rate > 0 ? p.rate : 1;
|
|
5585
|
+
const len = Math.max(0, p.out - p.in) / rate;
|
|
5586
|
+
if (acc + len >= t) return p.in + Math.max(0, t - acc) * rate;
|
|
5587
|
+
acc += len;
|
|
5588
|
+
}
|
|
5589
|
+
return pieces.at(-1)?.out ?? t;
|
|
5590
|
+
}
|
|
5591
|
+
function retimeOverlays(prev, next, report) {
|
|
5592
|
+
const overlays = prev.overlays ?? [];
|
|
5593
|
+
if (!overlays.length) return [];
|
|
5594
|
+
const oldSteps = prev.source.meta.steps ?? [];
|
|
5595
|
+
const newSteps = next.source.meta.steps ?? [];
|
|
5596
|
+
const oldPieces = ratedSegments6(prev);
|
|
5597
|
+
const newPieces = ratedSegments6(next);
|
|
5598
|
+
const oldDuration = prev.source.meta.durationMs / 1e3;
|
|
5599
|
+
const newDuration = next.source.meta.durationMs / 1e3;
|
|
5600
|
+
const stepMap = buildStepMap(oldSteps, newSteps, oldDuration, newDuration);
|
|
5601
|
+
const ctx = { oldSteps, newSteps, stepMap, report };
|
|
5602
|
+
const follow = (o, oldRef, newRef) => {
|
|
5603
|
+
const offset = o.start - sourceToOutput(oldPieces, oldRef);
|
|
5604
|
+
const start = Math.max(0, sourceToOutput(newPieces, newRef) + offset);
|
|
5605
|
+
return { ...o, start: +start.toFixed(3) };
|
|
5606
|
+
};
|
|
5607
|
+
return overlays.map((o) => {
|
|
5608
|
+
const pin = o.pin;
|
|
5609
|
+
if (pin?.step !== void 0) {
|
|
5610
|
+
const was = stepByRef(oldSteps, pin.step);
|
|
5611
|
+
const now2 = was ? matchStep(was, newSteps) : stepByRef(newSteps, pin.step);
|
|
5612
|
+
if (!now2 || now2.skipped) {
|
|
5613
|
+
report.flagged.push(
|
|
5614
|
+
`overlay ${o.id}: pinned to step ${String(pin.step)}, which is missing or skipped in the new recording \u2014 kept at ${o.start.toFixed(2)}s; re-pin or re-place it`
|
|
5615
|
+
);
|
|
5616
|
+
return o;
|
|
5617
|
+
}
|
|
5618
|
+
report.anchored++;
|
|
5619
|
+
return follow(o, was?.tStart ?? now2.tStart, now2.tStart);
|
|
5620
|
+
}
|
|
5621
|
+
if (typeof pin?.press === "number") {
|
|
5622
|
+
const placed = placeMoment(pin.press, void 0, ctx);
|
|
5623
|
+
if ("gone" in placed) {
|
|
5624
|
+
report.flagged.push(
|
|
5625
|
+
`overlay ${o.id}: pinned to a press at ${pin.press.toFixed(2)}s, and ${goneWords(placed.gone)} \u2014 kept at ${o.start.toFixed(2)}s; re-pin or re-place it`
|
|
5626
|
+
);
|
|
5627
|
+
return o;
|
|
5628
|
+
}
|
|
5629
|
+
const moved = follow(o, pin.press, placed.at);
|
|
5630
|
+
return { ...moved, pin: { ...pin, press: +placed.at.toFixed(3) } };
|
|
5631
|
+
}
|
|
5632
|
+
const under = stepUnder(
|
|
5633
|
+
outputToSource(oldPieces, o.start),
|
|
5634
|
+
oldSteps,
|
|
5635
|
+
LAYER_AFTER_STEP
|
|
5636
|
+
);
|
|
5637
|
+
if (!under) return o;
|
|
5638
|
+
const now = matchStep(under, newSteps);
|
|
5639
|
+
if (!now) {
|
|
5640
|
+
report.flagged.push(
|
|
5641
|
+
`overlay ${o.id} (${o.start.toFixed(2)}s): it sat over step ${stepName(under)}, which is missing or skipped in the new recording \u2014 kept at its output time; re-place it`
|
|
5642
|
+
);
|
|
5643
|
+
return o;
|
|
5644
|
+
}
|
|
5645
|
+
const expected = follow(o, under.tStart, now.tStart).start;
|
|
5646
|
+
if (Math.abs(expected - o.start) > LAYER_DRIFT) {
|
|
5647
|
+
report.flagged.push(
|
|
5648
|
+
`overlay ${o.id} (${o.start.toFixed(2)}s): it sat over step ${stepName(under)}, which now plays at ${expected.toFixed(2)}s in the cut \u2014 kept at ${o.start.toFixed(2)}s by the output-time contract; pin it to follow (pin: { step: ${JSON.stringify(under.id ?? under.step)} }) or move it`
|
|
5649
|
+
);
|
|
5650
|
+
}
|
|
5651
|
+
return o;
|
|
5652
|
+
});
|
|
5653
|
+
}
|
|
5545
5654
|
|
|
5546
5655
|
// src/plugin/plan.ts
|
|
5547
5656
|
var overlaps = (a, b) => a.in < b.out && b.in < a.out;
|
|
@@ -5655,7 +5764,9 @@ async function planTake(dir, opts = {}) {
|
|
|
5655
5764
|
].sort((a, b) => a.in - b.in);
|
|
5656
5765
|
}
|
|
5657
5766
|
if (rt.tilt.length) doc.tilt = rt.tilt;
|
|
5658
|
-
if (prev.overlays?.length)
|
|
5767
|
+
if (prev.overlays?.length) {
|
|
5768
|
+
doc.overlays = retimeOverlays(prev, doc, rt.report);
|
|
5769
|
+
}
|
|
5659
5770
|
if (prev.objects?.length) doc.objects = prev.objects;
|
|
5660
5771
|
if (prev.audio.length) doc.audio = prev.audio;
|
|
5661
5772
|
if (prev.camMotion?.length) doc.camMotion = prev.camMotion;
|
|
@@ -5763,15 +5874,48 @@ async function planTake(dir, opts = {}) {
|
|
|
5763
5874
|
|
|
5764
5875
|
// src/plugin/sync.ts
|
|
5765
5876
|
import { createHash } from "crypto";
|
|
5766
|
-
import { existsSync as
|
|
5767
|
-
import { readFile as
|
|
5877
|
+
import { existsSync as existsSync11, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
5878
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
5768
5879
|
import { createInterface } from "readline/promises";
|
|
5769
|
-
import { basename, join as
|
|
5880
|
+
import { basename, join as join13 } from "path";
|
|
5770
5881
|
import { lowerToComposition as lowerToComposition3, migrateHostedDoc as migrateHostedDoc2 } from "@vosjs/studio-core";
|
|
5771
5882
|
|
|
5772
|
-
// src/plugin/
|
|
5773
|
-
import {
|
|
5883
|
+
// src/plugin/takeScript.ts
|
|
5884
|
+
import { existsSync as existsSync10 } from "fs";
|
|
5885
|
+
import { readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
|
|
5886
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
5774
5887
|
import { join as join11 } from "path";
|
|
5888
|
+
function scriptFromText(text) {
|
|
5889
|
+
try {
|
|
5890
|
+
const v = JSON.parse(text);
|
|
5891
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
5892
|
+
} catch {
|
|
5893
|
+
return null;
|
|
5894
|
+
}
|
|
5895
|
+
}
|
|
5896
|
+
function scriptToWrite(doc, hasLocal) {
|
|
5897
|
+
const script = doc.source.script;
|
|
5898
|
+
if (hasLocal || !script || typeof script !== "object") return null;
|
|
5899
|
+
return `${JSON.stringify(script, null, 2)}
|
|
5900
|
+
`;
|
|
5901
|
+
}
|
|
5902
|
+
function embedScript(doc, dir) {
|
|
5903
|
+
const path = join11(dir, "actions.json");
|
|
5904
|
+
if (!existsSync10(path)) return;
|
|
5905
|
+
const script = scriptFromText(readFileSync2(path, "utf8"));
|
|
5906
|
+
if (script) doc.source.script = script;
|
|
5907
|
+
}
|
|
5908
|
+
async function writeScriptBeside(dir, doc) {
|
|
5909
|
+
const path = join11(dir, "actions.json");
|
|
5910
|
+
const text = scriptToWrite(doc, existsSync10(path));
|
|
5911
|
+
if (text === null) return false;
|
|
5912
|
+
await writeFile7(path, text, "utf8");
|
|
5913
|
+
return true;
|
|
5914
|
+
}
|
|
5915
|
+
|
|
5916
|
+
// src/plugin/folder.ts
|
|
5917
|
+
import { mkdir as mkdir5, writeFile as writeFile8 } from "fs/promises";
|
|
5918
|
+
import { join as join12 } from "path";
|
|
5775
5919
|
import { recipeHints } from "@vosjs/shared/frontmatter";
|
|
5776
5920
|
import { migrateHostedDoc } from "@vosjs/studio-core";
|
|
5777
5921
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["json", "help", "media"]);
|
|
@@ -5947,25 +6091,25 @@ async function cmdPull(argv) {
|
|
|
5947
6091
|
throw new Error(apiError(`pull folder ${folder.slug}`, res));
|
|
5948
6092
|
const payload = res.body;
|
|
5949
6093
|
const out = strFlag(flags, "out") ?? folder.slug;
|
|
5950
|
-
await mkdir5(
|
|
6094
|
+
await mkdir5(join12(out, "recipes"), { recursive: true });
|
|
5951
6095
|
const lines = [];
|
|
5952
6096
|
const recipes = [];
|
|
5953
6097
|
for (const rec of payload.recipes) {
|
|
5954
|
-
const path =
|
|
5955
|
-
await
|
|
6098
|
+
const path = join12(out, "recipes", safeName(rec.filename));
|
|
6099
|
+
await writeFile8(path, rec.body ?? "");
|
|
5956
6100
|
recipes.push({ path, line: recipeLine(rec.filename, rec.body ?? "") });
|
|
5957
6101
|
}
|
|
5958
6102
|
for (const rec of payload.inheritedRecipes) {
|
|
5959
6103
|
const from = safeName(rec.folderSlug ?? "inherited");
|
|
5960
|
-
await mkdir5(
|
|
5961
|
-
const path =
|
|
6104
|
+
await mkdir5(join12(out, "recipes", "_inherited", from), { recursive: true });
|
|
6105
|
+
const path = join12(
|
|
5962
6106
|
out,
|
|
5963
6107
|
"recipes",
|
|
5964
6108
|
"_inherited",
|
|
5965
6109
|
from,
|
|
5966
6110
|
safeName(rec.filename)
|
|
5967
6111
|
);
|
|
5968
|
-
await
|
|
6112
|
+
await writeFile8(path, rec.body ?? "");
|
|
5969
6113
|
recipes.push({
|
|
5970
6114
|
path,
|
|
5971
6115
|
line: `${recipeLine(rec.filename, rec.body ?? "")} [inherited from ${rec.folderName ?? from}]`
|
|
@@ -5973,15 +6117,15 @@ async function cmdPull(argv) {
|
|
|
5973
6117
|
}
|
|
5974
6118
|
const members = [];
|
|
5975
6119
|
for (const v of payload.voses) {
|
|
5976
|
-
const dir =
|
|
6120
|
+
const dir = join12(out, "members", safeName(v.slug || v.title || v.id));
|
|
5977
6121
|
await mkdir5(dir, { recursive: true });
|
|
5978
6122
|
const cfg = await apiJson(origin, `/api/vos/${v.id}/config`, { key });
|
|
5979
6123
|
if (cfg.status !== 200) {
|
|
5980
6124
|
lines.push(`skipped ${v.title}: ${apiError("fetch config", cfg)}`);
|
|
5981
6125
|
continue;
|
|
5982
6126
|
}
|
|
5983
|
-
await
|
|
5984
|
-
|
|
6127
|
+
await writeFile8(
|
|
6128
|
+
join12(dir, "config.json"),
|
|
5985
6129
|
JSON.stringify(cfg.body.config, null, 2)
|
|
5986
6130
|
);
|
|
5987
6131
|
let take = false;
|
|
@@ -5992,8 +6136,8 @@ async function cmdPull(argv) {
|
|
|
5992
6136
|
{ key }
|
|
5993
6137
|
);
|
|
5994
6138
|
if (doc.status === 200) {
|
|
5995
|
-
await
|
|
5996
|
-
|
|
6139
|
+
await writeFile8(
|
|
6140
|
+
join12(dir, "doc.json"),
|
|
5997
6141
|
JSON.stringify(doc.body, null, 2)
|
|
5998
6142
|
);
|
|
5999
6143
|
take = true;
|
|
@@ -6119,8 +6263,8 @@ function storageStateInDir(dir) {
|
|
|
6119
6263
|
if (!f.endsWith(".json") || f === "doc.json" || f === "meta.json" || f === "actions.json" || f === "cursor.json" || f === "vos.json" || f === "doc.prev.json")
|
|
6120
6264
|
continue;
|
|
6121
6265
|
try {
|
|
6122
|
-
const v = JSON.parse(
|
|
6123
|
-
if (looksLikeStorageState(v)) return
|
|
6266
|
+
const v = JSON.parse(readFileSync3(join13(dir, f), "utf8"));
|
|
6267
|
+
if (looksLikeStorageState(v)) return join13(dir, f);
|
|
6124
6268
|
} catch {
|
|
6125
6269
|
}
|
|
6126
6270
|
}
|
|
@@ -6141,7 +6285,7 @@ async function pushTake(dir, flags, r) {
|
|
|
6141
6285
|
throw new Error(`doc.json fails lint:
|
|
6142
6286
|
${lint.problems.join("\n ")}`);
|
|
6143
6287
|
}
|
|
6144
|
-
if (!
|
|
6288
|
+
if (!existsSync11(take.paths.recording)) {
|
|
6145
6289
|
throw new Error(
|
|
6146
6290
|
"no recording in this take \u2014 record it, or `vos pull --media` to bring it home"
|
|
6147
6291
|
);
|
|
@@ -6190,7 +6334,7 @@ async function pushTake(dir, flags, r) {
|
|
|
6190
6334
|
throw new Error("push cancelled");
|
|
6191
6335
|
}
|
|
6192
6336
|
}
|
|
6193
|
-
const bytes = await
|
|
6337
|
+
const bytes = await readFile10(take.paths.recording);
|
|
6194
6338
|
const hash = createHash("sha256").update(bytes).digest("hex");
|
|
6195
6339
|
const { type: contentType } = resolveMediaType({
|
|
6196
6340
|
head: bytes.subarray(0, MEDIA_HEAD_BYTES),
|
|
@@ -6225,6 +6369,7 @@ async function pushTake(dir, flags, r) {
|
|
|
6225
6369
|
if (upload.reused) r.log(" recording already hosted \u2014 reused");
|
|
6226
6370
|
const docForPush = structuredClone(take.doc);
|
|
6227
6371
|
docForPush.source.videoKey = assetUrl;
|
|
6372
|
+
embedScript(docForPush, dir);
|
|
6228
6373
|
await uploadDocRefs(sourceSidecarRefs(docForPush), dir, ctx, (l) => r.log(l));
|
|
6229
6374
|
for (const field of ["micKey", "camKey"]) {
|
|
6230
6375
|
const key = docForPush.source[field];
|
|
@@ -6362,9 +6507,9 @@ async function pullTake(dir, flags, r) {
|
|
|
6362
6507
|
};
|
|
6363
6508
|
}
|
|
6364
6509
|
let media2;
|
|
6365
|
-
if (flags.media &&
|
|
6510
|
+
if (flags.media && existsSync11(join13(dir, "doc.json"))) {
|
|
6366
6511
|
const local = JSON.parse(
|
|
6367
|
-
await
|
|
6512
|
+
await readFile10(join13(dir, "doc.json"), "utf8")
|
|
6368
6513
|
);
|
|
6369
6514
|
media2 = await pullMedia(ctx, dir, local, r.log);
|
|
6370
6515
|
}
|
|
@@ -6425,11 +6570,14 @@ async function pullTake(dir, flags, r) {
|
|
|
6425
6570
|
const hostedDoc = migrateHostedDoc2(docRes.json);
|
|
6426
6571
|
const doc = hostedDoc;
|
|
6427
6572
|
const localRecording = takeMediaFile(dir, "recording", RECORDING_NAME);
|
|
6428
|
-
if (
|
|
6573
|
+
if (existsSync11(localRecording)) {
|
|
6429
6574
|
doc.source.videoKey = basename(localRecording);
|
|
6430
6575
|
}
|
|
6431
|
-
await writeJson(
|
|
6576
|
+
await writeJson(join13(dir, "doc.json"), doc, true);
|
|
6432
6577
|
const media = flags.media ? await pullMedia(ctx, dir, doc, r.log) : void 0;
|
|
6578
|
+
if (flags.media && await writeScriptBeside(dir, doc)) {
|
|
6579
|
+
r.log(" actions.json written from the document");
|
|
6580
|
+
}
|
|
6433
6581
|
const versions = await api(ctx, `/vos/${vosId}/versions`);
|
|
6434
6582
|
const headRow = (versions.json.versions ?? []).find((v) => v.id === head);
|
|
6435
6583
|
writeSyncState(dir, {
|
|
@@ -6463,6 +6611,7 @@ async function pushTakeClaimable(dir, flags, r) {
|
|
|
6463
6611
|
const state = readSyncState(dir);
|
|
6464
6612
|
const hosted = state?.hostedMedia ?? {};
|
|
6465
6613
|
const docForPush = structuredClone(take.doc);
|
|
6614
|
+
embedScript(docForPush, dir);
|
|
6466
6615
|
const refs = [
|
|
6467
6616
|
...isTakeRelativeKey(docForPush.source.videoKey) ? [
|
|
6468
6617
|
{
|
|
@@ -6484,8 +6633,8 @@ async function pushTakeClaimable(dir, flags, r) {
|
|
|
6484
6633
|
`${ref.where} (${file}) is not a file this take fetched from vos.so, and a claimable push never uploads footage. Push the take with an account (vos login, then vos push ${dir}), or remix a public take fetched with \`vos fetch <url> --media\``
|
|
6485
6634
|
);
|
|
6486
6635
|
}
|
|
6487
|
-
const path =
|
|
6488
|
-
const now =
|
|
6636
|
+
const path = join13(dir, file);
|
|
6637
|
+
const now = existsSync11(path) ? await fileSha256(path) : null;
|
|
6489
6638
|
if (now !== origin2.sha256) {
|
|
6490
6639
|
throw new Error(
|
|
6491
6640
|
`${ref.where} (${file}) changed since it was fetched, so it is no longer the hosted file. A claimable push names only the original; push with an account to upload the new one`
|
|
@@ -6519,10 +6668,10 @@ async function pushTakeClaimable(dir, flags, r) {
|
|
|
6519
6668
|
}
|
|
6520
6669
|
|
|
6521
6670
|
// src/plugin/program.ts
|
|
6522
|
-
import { existsSync as
|
|
6523
|
-
import { mkdir as mkdir6, readFile as
|
|
6671
|
+
import { existsSync as existsSync13 } from "fs";
|
|
6672
|
+
import { mkdir as mkdir6, readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
6524
6673
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6525
|
-
import { basename as basename3, dirname as dirname2, join as
|
|
6674
|
+
import { basename as basename3, dirname as dirname2, join as join14 } from "path";
|
|
6526
6675
|
import {
|
|
6527
6676
|
CURRENT_CONFIG_VERSION,
|
|
6528
6677
|
migrateConfig,
|
|
@@ -6656,12 +6805,12 @@ async function handoffLogin(url, r, opts = {}) {
|
|
|
6656
6805
|
|
|
6657
6806
|
// src/plugin/programFiles.ts
|
|
6658
6807
|
import { basename as basename2, extname as extname2, relative as relative2 } from "path";
|
|
6659
|
-
import { existsSync as
|
|
6808
|
+
import { existsSync as existsSync12, realpathSync } from "fs";
|
|
6660
6809
|
function isLocalName(value) {
|
|
6661
6810
|
if (/^(https?:|data:|blob:|asset:|media:)/.test(value)) return false;
|
|
6662
6811
|
if (value.startsWith("$")) return false;
|
|
6663
6812
|
if (value.startsWith("<svg") || value.startsWith("<?xml")) return false;
|
|
6664
|
-
if (value.startsWith("/")) return
|
|
6813
|
+
if (value.startsWith("/")) return existsSync12(value);
|
|
6665
6814
|
return true;
|
|
6666
6815
|
}
|
|
6667
6816
|
var FILE_EXT = /\.(png|jpe?g|webp|gif|avif|svg|mp4|webm|mov|mp3|m4a|ogg|wav|flac|aac|glb|gltf|woff2?|ttf|otf|hdr|exr)$/i;
|
|
@@ -6836,8 +6985,8 @@ function preflightConfig(parsed) {
|
|
|
6836
6985
|
}
|
|
6837
6986
|
function resolveConfigPath(target) {
|
|
6838
6987
|
if (target.endsWith(".json")) return target;
|
|
6839
|
-
const inDir =
|
|
6840
|
-
if (
|
|
6988
|
+
const inDir = join14(target, "config.json");
|
|
6989
|
+
if (existsSync13(inDir)) return inDir;
|
|
6841
6990
|
throw new UsageError(
|
|
6842
6991
|
`${target} has no config.json (and is not a take \u2014 no doc.json)`
|
|
6843
6992
|
);
|
|
@@ -6864,8 +7013,8 @@ async function cmdFetch(argv) {
|
|
|
6864
7013
|
const slug = typeof vosMeta.slug === "string" && vosMeta.slug ? vosMeta.slug : vosId;
|
|
6865
7014
|
const out = strFlag(flags, "out") ?? slug;
|
|
6866
7015
|
await mkdir6(out, { recursive: true });
|
|
6867
|
-
await
|
|
6868
|
-
|
|
7016
|
+
await writeFile9(
|
|
7017
|
+
join14(out, "config.json"),
|
|
6869
7018
|
JSON.stringify(cfg.body.config, null, 2)
|
|
6870
7019
|
);
|
|
6871
7020
|
const title = typeof vosMeta.title === "string" ? vosMeta.title : "";
|
|
@@ -6890,30 +7039,33 @@ async function cmdFetch(argv) {
|
|
|
6890
7039
|
const hosted = migrateHostedDoc3(doc.body);
|
|
6891
7040
|
const { config: own } = await writeProgramDoc(out, hosted);
|
|
6892
7041
|
if (own) {
|
|
6893
|
-
await
|
|
7042
|
+
await writeFile9(join14(out, "config.json"), JSON.stringify(own, null, 2));
|
|
6894
7043
|
}
|
|
6895
7044
|
} else if (doc.status === 200) {
|
|
6896
7045
|
take = true;
|
|
6897
7046
|
const hosted = migrateHostedDoc3(doc.body);
|
|
6898
|
-
await
|
|
7047
|
+
await writeFile9(join14(out, "doc.json"), JSON.stringify(hosted, null, 2));
|
|
6899
7048
|
if (flags.media === true) {
|
|
6900
7049
|
const media = await pullMedia({ origin, key }, out, hosted, r.log);
|
|
6901
7050
|
await recordHostedMedia(out, vosId, media);
|
|
7051
|
+
if (await writeScriptBeside(out, hosted)) {
|
|
7052
|
+
r.log(" actions.json written from the document");
|
|
7053
|
+
}
|
|
6902
7054
|
mediaLine = media.downloaded.length ? ` + ${media.downloaded.map((m) => m.file).join(", ")}` : "";
|
|
6903
7055
|
}
|
|
6904
7056
|
}
|
|
6905
7057
|
}
|
|
6906
7058
|
let assetsLine = "";
|
|
6907
7059
|
if (!take) {
|
|
6908
|
-
const configPath =
|
|
6909
|
-
const written = JSON.parse(await
|
|
7060
|
+
const configPath = join14(out, "config.json");
|
|
7061
|
+
const written = JSON.parse(await readFile11(configPath, "utf8"));
|
|
6910
7062
|
const hostedCount = manifestRefs(written).filter(
|
|
6911
7063
|
({ ref }) => hostedAssetId(ref, [origin, "https://vos.so", "https://www.vos.so"])
|
|
6912
7064
|
).length;
|
|
6913
7065
|
if (hostedCount && flags.media === true) {
|
|
6914
7066
|
const pulled = await pullManifest({ origin, key }, out, written, r.log);
|
|
6915
7067
|
if (pulled) {
|
|
6916
|
-
await
|
|
7068
|
+
await writeFile9(
|
|
6917
7069
|
configPath,
|
|
6918
7070
|
JSON.stringify({ ...written, assets: pulled.assets }, null, 2)
|
|
6919
7071
|
);
|
|
@@ -7033,7 +7185,7 @@ async function cmdPushProgram(argv) {
|
|
|
7033
7185
|
api: strFlag(flags, "api")
|
|
7034
7186
|
});
|
|
7035
7187
|
const dir = dirname2(source);
|
|
7036
|
-
const parsed = JSON.parse(await
|
|
7188
|
+
const parsed = JSON.parse(await readFile11(source, "utf8"));
|
|
7037
7189
|
const pre = preflightConfig(parsed);
|
|
7038
7190
|
if (!pre.ok || !pre.config) {
|
|
7039
7191
|
for (const issue of pre.issues) r.log(`error ${issue}`);
|
|
@@ -7381,10 +7533,10 @@ async function cmdPullProgram(argv) {
|
|
|
7381
7533
|
const cfg = await apiJson(origin, `/api/vos/${vosId}/config`, { key });
|
|
7382
7534
|
if (cfg.status !== 200)
|
|
7383
7535
|
throw new Error(apiError(`fetch head config for ${vosId}`, cfg));
|
|
7384
|
-
const configPath =
|
|
7536
|
+
const configPath = join14(dir, "config.json");
|
|
7385
7537
|
let backedUp = false;
|
|
7386
|
-
if (
|
|
7387
|
-
await
|
|
7538
|
+
if (existsSync13(configPath)) {
|
|
7539
|
+
await writeFile9(join14(dir, "config.backup.json"), await readFile11(configPath));
|
|
7388
7540
|
backedUp = true;
|
|
7389
7541
|
}
|
|
7390
7542
|
let headConfig = cfg.body.config;
|
|
@@ -7403,7 +7555,7 @@ async function cmdPullProgram(argv) {
|
|
|
7403
7555
|
if (own) headConfig = own;
|
|
7404
7556
|
}
|
|
7405
7557
|
}
|
|
7406
|
-
await
|
|
7558
|
+
await writeFile9(configPath, JSON.stringify(headConfig, null, 2));
|
|
7407
7559
|
writeSyncState(dir, {
|
|
7408
7560
|
vosId,
|
|
7409
7561
|
versionId: typeof head.id === "string" ? head.id : since
|
|
@@ -7417,7 +7569,7 @@ async function cmdPullProgram(argv) {
|
|
|
7417
7569
|
protected: protectedIds,
|
|
7418
7570
|
changes,
|
|
7419
7571
|
out: configPath,
|
|
7420
|
-
backup: backedUp ?
|
|
7572
|
+
backup: backedUp ? join14(dir, "config.backup.json") : null
|
|
7421
7573
|
},
|
|
7422
7574
|
`Pulled ${changes.length} version${changes.length === 1 ? "" : "s"} \u2192 ${configPath}` + (backedUp ? ` (previous copy: config.backup.json)` : "") + `
|
|
7423
7575
|
Re-apply your edit on the new head, then: vos push ${configPath} --vos ${vosId}`
|
|
@@ -7525,9 +7677,9 @@ function storedProgramConfig(config, programDoc) {
|
|
|
7525
7677
|
return lowerProgramDoc(doc).config;
|
|
7526
7678
|
}
|
|
7527
7679
|
async function readProgramDoc(dir, config) {
|
|
7528
|
-
const docPath =
|
|
7529
|
-
if (!
|
|
7530
|
-
const parsed = JSON.parse(await
|
|
7680
|
+
const docPath = join14(dir, "doc.json");
|
|
7681
|
+
if (!existsSync13(docPath)) return null;
|
|
7682
|
+
const parsed = JSON.parse(await readFile11(docPath, "utf8"));
|
|
7531
7683
|
if (typeof parsed !== "object" || parsed === null || "source" in parsed)
|
|
7532
7684
|
return null;
|
|
7533
7685
|
const raw = parsed;
|
|
@@ -7535,23 +7687,23 @@ async function readProgramDoc(dir, config) {
|
|
|
7535
7687
|
return { ...raw, program: { ...program, config } };
|
|
7536
7688
|
}
|
|
7537
7689
|
async function setProgramStill(dir, t) {
|
|
7538
|
-
const docPath =
|
|
7690
|
+
const docPath = join14(dir, "doc.json");
|
|
7539
7691
|
let doc = { program: {}, audio: [] };
|
|
7540
|
-
if (
|
|
7541
|
-
const parsed = JSON.parse(await
|
|
7692
|
+
if (existsSync13(docPath)) {
|
|
7693
|
+
const parsed = JSON.parse(await readFile11(docPath, "utf8"));
|
|
7542
7694
|
if (!parsed || typeof parsed !== "object" || "source" in parsed)
|
|
7543
7695
|
throw new UsageError(
|
|
7544
7696
|
`${docPath} is a take's document; a take's cover is its own "still" field`
|
|
7545
7697
|
);
|
|
7546
7698
|
doc = parsed;
|
|
7547
7699
|
}
|
|
7548
|
-
await
|
|
7700
|
+
await writeFile9(docPath, JSON.stringify({ ...doc, still: t }, null, 2) + "\n");
|
|
7549
7701
|
}
|
|
7550
7702
|
async function writeProgramDoc(dir, hosted) {
|
|
7551
7703
|
const program = hosted.program && typeof hosted.program === "object" ? hosted.program : {};
|
|
7552
7704
|
const { config, ...rest } = program;
|
|
7553
7705
|
const onDisk = { ...hosted, program: rest };
|
|
7554
|
-
await
|
|
7706
|
+
await writeFile9(join14(dir, "doc.json"), JSON.stringify(onDisk, null, 2));
|
|
7555
7707
|
return {
|
|
7556
7708
|
config: config && typeof config === "object" ? config : null
|
|
7557
7709
|
};
|
|
@@ -7559,7 +7711,7 @@ async function writeProgramDoc(dir, hosted) {
|
|
|
7559
7711
|
|
|
7560
7712
|
// src/plugin/asset.ts
|
|
7561
7713
|
import { createHash as createHash2 } from "crypto";
|
|
7562
|
-
import { readFileSync as
|
|
7714
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
7563
7715
|
import { basename as basename4 } from "path";
|
|
7564
7716
|
|
|
7565
7717
|
// src/plugin/assetWords.ts
|
|
@@ -7702,7 +7854,7 @@ async function cmdPush(argv) {
|
|
|
7702
7854
|
const uploaded = [];
|
|
7703
7855
|
for (const file of positionals) {
|
|
7704
7856
|
const name = basename4(file);
|
|
7705
|
-
const body =
|
|
7857
|
+
const body = readFileSync4(file);
|
|
7706
7858
|
const media = resolveMediaType({
|
|
7707
7859
|
head: body.subarray(0, MEDIA_HEAD_BYTES),
|
|
7708
7860
|
filename: name
|
|
@@ -7963,8 +8115,8 @@ async function cmdAsset(argv) {
|
|
|
7963
8115
|
}
|
|
7964
8116
|
|
|
7965
8117
|
// src/plugin/remove.ts
|
|
7966
|
-
import { existsSync as
|
|
7967
|
-
import { join as
|
|
8118
|
+
import { existsSync as existsSync14, rmSync as rmSync2, statSync as statSync3 } from "fs";
|
|
8119
|
+
import { join as join15 } from "path";
|
|
7968
8120
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
7969
8121
|
|
|
7970
8122
|
// src/plugin/trash.ts
|
|
@@ -8143,7 +8295,7 @@ async function cmdDelete(argv) {
|
|
|
8143
8295
|
api: strFlag(flags, "api")
|
|
8144
8296
|
}).replace(/\/+$/, "");
|
|
8145
8297
|
const key = requireCredential(strFlag(flags, "key"));
|
|
8146
|
-
const dir =
|
|
8298
|
+
const dir = existsSync14(target) && statSync3(target).isDirectory() ? target : null;
|
|
8147
8299
|
const vosId = dir ? readSyncState(dir)?.vosId : parseVosId(target);
|
|
8148
8300
|
if (!vosId)
|
|
8149
8301
|
throw new UsageError(
|
|
@@ -8186,7 +8338,7 @@ async function cmdDelete(argv) {
|
|
|
8186
8338
|
const tracking = dir ?? trackingCwd(vosId);
|
|
8187
8339
|
let unlinked = false;
|
|
8188
8340
|
if (tracking) {
|
|
8189
|
-
rmSync2(
|
|
8341
|
+
rmSync2(join15(tracking, SYNC_STATE_NAME), { force: true });
|
|
8190
8342
|
unlinked = true;
|
|
8191
8343
|
}
|
|
8192
8344
|
const trashed = res.body.trashed === true;
|
|
@@ -8211,13 +8363,13 @@ async function cmdDelete(argv) {
|
|
|
8211
8363
|
}
|
|
8212
8364
|
function trackingCwd(vosId) {
|
|
8213
8365
|
const cwd = process.cwd();
|
|
8214
|
-
if (!
|
|
8366
|
+
if (!existsSync14(join15(cwd, SYNC_STATE_NAME))) return null;
|
|
8215
8367
|
return readSyncState(cwd)?.vosId === vosId ? cwd : null;
|
|
8216
8368
|
}
|
|
8217
8369
|
|
|
8218
8370
|
// src/plugin/buildProgram.ts
|
|
8219
|
-
import { readFileSync as
|
|
8220
|
-
import { basename as basename5, dirname as dirname3, join as
|
|
8371
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
8372
|
+
import { basename as basename5, dirname as dirname3, join as join16, resolve as resolve5 } from "path";
|
|
8221
8373
|
import { pathToFileURL } from "url";
|
|
8222
8374
|
import { parse } from "acorn";
|
|
8223
8375
|
import { fullAncestor } from "acorn-walk";
|
|
@@ -8418,7 +8570,7 @@ async function cmdBuild(argv) {
|
|
|
8418
8570
|
throw new UsageError("vos build <program.mjs> [--out config.json] [--json]");
|
|
8419
8571
|
const r = createReporter(flags.json === true);
|
|
8420
8572
|
const file = resolve5(source);
|
|
8421
|
-
const text =
|
|
8573
|
+
const text = readFileSync5(file, "utf8");
|
|
8422
8574
|
const mod = await import(`${pathToFileURL(file).href}?t=${Date.now()}`);
|
|
8423
8575
|
const program = mod.default ?? mod.program;
|
|
8424
8576
|
if (!program || typeof program !== "object")
|
|
@@ -8435,7 +8587,7 @@ async function cmdBuild(argv) {
|
|
|
8435
8587
|
return EXIT_ERROR;
|
|
8436
8588
|
}
|
|
8437
8589
|
const out = resolve5(
|
|
8438
|
-
strFlag(flags, "out") ??
|
|
8590
|
+
strFlag(flags, "out") ?? join16(dirname3(file), "config.json")
|
|
8439
8591
|
);
|
|
8440
8592
|
writeFileSync2(out, JSON.stringify(built.config, null, 2) + "\n");
|
|
8441
8593
|
const sidecar = out.replace(/\.json$/, "") + ".sources.json";
|
|
@@ -8452,8 +8604,8 @@ async function cmdBuild(argv) {
|
|
|
8452
8604
|
|
|
8453
8605
|
// src/plugin/compare.ts
|
|
8454
8606
|
import { spawnSync } from "child_process";
|
|
8455
|
-
import { existsSync as
|
|
8456
|
-
import { dirname as dirname4, join as
|
|
8607
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
8608
|
+
import { dirname as dirname4, join as join17, resolve as resolve6 } from "path";
|
|
8457
8609
|
var BOOLEAN_FLAGS7 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
8458
8610
|
function sampleTimes(duration, every) {
|
|
8459
8611
|
const step = every > 0 ? every : 1;
|
|
@@ -8520,7 +8672,7 @@ async function cmdCompare(argv) {
|
|
|
8520
8672
|
const config = loaded.config;
|
|
8521
8673
|
const duration = configDuration(config) ?? 5;
|
|
8522
8674
|
const assets = await programAssets(config, {
|
|
8523
|
-
baseDir: /^https?:\/\//.test(source) ? null :
|
|
8675
|
+
baseDir: /^https?:\/\//.test(source) ? null : existsSync15(source) && statSync4(source).isDirectory() ? source : dirname4(source),
|
|
8524
8676
|
origin: platformOrigin({}),
|
|
8525
8677
|
key: resolveCredential(),
|
|
8526
8678
|
log: (line) => r.log(line)
|
|
@@ -8543,9 +8695,9 @@ async function cmdCompare(argv) {
|
|
|
8543
8695
|
try {
|
|
8544
8696
|
for (const t of times) {
|
|
8545
8697
|
const tag = t.toFixed(2);
|
|
8546
|
-
const refPng =
|
|
8547
|
-
const vosPng =
|
|
8548
|
-
const sheet =
|
|
8698
|
+
const refPng = join17(outDir, `src-${tag}.png`);
|
|
8699
|
+
const vosPng = join17(outDir, `vos-${tag}.png`);
|
|
8700
|
+
const sheet = join17(outDir, `sheet-${tag}.png`);
|
|
8549
8701
|
const grab = ffmpeg([
|
|
8550
8702
|
"-v",
|
|
8551
8703
|
"error",
|
|
@@ -8602,7 +8754,7 @@ async function cmdCompare(argv) {
|
|
|
8602
8754
|
} finally {
|
|
8603
8755
|
await browser.close();
|
|
8604
8756
|
}
|
|
8605
|
-
const overview =
|
|
8757
|
+
const overview = join17(outDir, "overview.png");
|
|
8606
8758
|
if (scores.length) {
|
|
8607
8759
|
const inputs = scores.flatMap((s) => ["-i", s.sheet]);
|
|
8608
8760
|
const scaled = scores.map((_, i) => `[${i}:v]scale=iw/3:-2[s${i}]`).join(";");
|
|
@@ -8628,7 +8780,7 @@ async function cmdCompare(argv) {
|
|
|
8628
8780
|
overview
|
|
8629
8781
|
};
|
|
8630
8782
|
writeFileSync3(
|
|
8631
|
-
|
|
8783
|
+
join17(outDir, "report.json"),
|
|
8632
8784
|
JSON.stringify(report, null, 2) + "\n"
|
|
8633
8785
|
);
|
|
8634
8786
|
r.done(
|
|
@@ -8639,28 +8791,28 @@ async function cmdCompare(argv) {
|
|
|
8639
8791
|
}
|
|
8640
8792
|
|
|
8641
8793
|
// src/plugin/port.ts
|
|
8642
|
-
import { existsSync as
|
|
8643
|
-
import { dirname as dirname7, join as
|
|
8794
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
8795
|
+
import { dirname as dirname7, join as join21, resolve as resolve9 } from "path";
|
|
8644
8796
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
8645
8797
|
|
|
8646
8798
|
// src/plugin/portInventory.ts
|
|
8647
8799
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
8648
8800
|
import {
|
|
8649
|
-
existsSync as
|
|
8801
|
+
existsSync as existsSync17,
|
|
8650
8802
|
mkdirSync as mkdirSync3,
|
|
8651
|
-
readFileSync as
|
|
8803
|
+
readFileSync as readFileSync7,
|
|
8652
8804
|
readdirSync as readdirSync4,
|
|
8653
8805
|
statSync as statSync6
|
|
8654
8806
|
} from "fs";
|
|
8655
|
-
import { dirname as dirname5, join as
|
|
8807
|
+
import { dirname as dirname5, join as join19, relative as relative3, resolve as resolve7 } from "path";
|
|
8656
8808
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
8657
8809
|
import { findFontFamily as findFontFamily3 } from "@vosjs/shared";
|
|
8658
8810
|
|
|
8659
8811
|
// src/plugin/portRemotion.ts
|
|
8660
|
-
import { existsSync as
|
|
8812
|
+
import { existsSync as existsSync16, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
|
|
8661
8813
|
import { createServer as createServer2 } from "http";
|
|
8662
8814
|
import { createRequire } from "module";
|
|
8663
|
-
import { basename as basename6, extname as extname3, join as
|
|
8815
|
+
import { basename as basename6, extname as extname3, join as join18 } from "path";
|
|
8664
8816
|
function themeColors(sources) {
|
|
8665
8817
|
const out = {};
|
|
8666
8818
|
const re = /\bconst\s+([A-Za-z_$][\w$]*)\s*(?::\s*string\s*)?=\s*['"](#[0-9a-fA-F]{3,8})['"]/g;
|
|
@@ -8726,14 +8878,14 @@ function sourceTexts(dir) {
|
|
|
8726
8878
|
if (depth > 4 || out.length > 200) return;
|
|
8727
8879
|
for (const name of readdirSync3(d)) {
|
|
8728
8880
|
if (name === "node_modules" || name.startsWith(".")) continue;
|
|
8729
|
-
const p =
|
|
8881
|
+
const p = join18(d, name);
|
|
8730
8882
|
if (statSync5(p).isDirectory()) walk(p, depth + 1);
|
|
8731
8883
|
else if (/\.(tsx?|jsx?|mjs)$/.test(name))
|
|
8732
|
-
out.push(
|
|
8884
|
+
out.push(readFileSync6(p, "utf8"));
|
|
8733
8885
|
}
|
|
8734
8886
|
};
|
|
8735
|
-
const src =
|
|
8736
|
-
walk(
|
|
8887
|
+
const src = join18(dir, "src");
|
|
8888
|
+
walk(existsSync16(src) ? src : dir, 0);
|
|
8737
8889
|
return out;
|
|
8738
8890
|
}
|
|
8739
8891
|
function entryPoint(dir) {
|
|
@@ -8745,7 +8897,7 @@ function entryPoint(dir) {
|
|
|
8745
8897
|
"remotion/index.ts",
|
|
8746
8898
|
"remotion/index.tsx"
|
|
8747
8899
|
])
|
|
8748
|
-
if (
|
|
8900
|
+
if (existsSync16(join18(dir, p))) return join18(dir, p);
|
|
8749
8901
|
return null;
|
|
8750
8902
|
}
|
|
8751
8903
|
var TYPES = {
|
|
@@ -8820,7 +8972,7 @@ async function readRemotion(dir, log) {
|
|
|
8820
8972
|
const entry = entryPoint(dir);
|
|
8821
8973
|
let bundler;
|
|
8822
8974
|
try {
|
|
8823
|
-
bundler = createRequire(
|
|
8975
|
+
bundler = createRequire(join18(dir, "package.json"))("@remotion/bundler");
|
|
8824
8976
|
} catch {
|
|
8825
8977
|
log?.(
|
|
8826
8978
|
"a Remotion project whose dependencies are not installed: run `npm install` in it so vos can read its compositions (words, scenes, faces); reading only the render"
|
|
@@ -8837,13 +8989,13 @@ async function readRemotion(dir, log) {
|
|
|
8837
8989
|
} });
|
|
8838
8990
|
const server = createServer2((rq, rs) => {
|
|
8839
8991
|
const path = decodeURIComponent(new URL(rq.url ?? "/", "http://x").pathname);
|
|
8840
|
-
let f =
|
|
8841
|
-
if (!f.startsWith(out) || !
|
|
8842
|
-
f =
|
|
8992
|
+
let f = join18(out, path);
|
|
8993
|
+
if (!f.startsWith(out) || !existsSync16(f) || statSync5(f).isDirectory())
|
|
8994
|
+
f = join18(out, "index.html");
|
|
8843
8995
|
rs.writeHead(200, {
|
|
8844
8996
|
"content-type": TYPES[extname3(f)] ?? "application/octet-stream"
|
|
8845
8997
|
});
|
|
8846
|
-
rs.end(
|
|
8998
|
+
rs.end(readFileSync6(f));
|
|
8847
8999
|
}).listen(0, "127.0.0.1");
|
|
8848
9000
|
await new Promise((r) => server.once("listening", r));
|
|
8849
9001
|
const port = server.address().port;
|
|
@@ -8938,10 +9090,10 @@ async function readRemotion(dir, log) {
|
|
|
8938
9090
|
perScene.push({ scene: s.name, texts: read.texts ?? [] });
|
|
8939
9091
|
}
|
|
8940
9092
|
const sources = sourceTexts(dir);
|
|
8941
|
-
const publicDir =
|
|
9093
|
+
const publicDir = join18(dir, "public");
|
|
8942
9094
|
const media = tree.audio.map((a) => {
|
|
8943
9095
|
const name = basename6(a.src.split("?")[0]);
|
|
8944
|
-
const local =
|
|
9096
|
+
const local = existsSync16(join18(publicDir, name)) ? `public/${name}` : a.src;
|
|
8945
9097
|
return {
|
|
8946
9098
|
kind: "audio",
|
|
8947
9099
|
src: local,
|
|
@@ -8982,11 +9134,11 @@ async function readRemotion(dir, log) {
|
|
|
8982
9134
|
|
|
8983
9135
|
// src/plugin/portInventory.ts
|
|
8984
9136
|
function detectEngine(dir) {
|
|
8985
|
-
if (
|
|
8986
|
-
const pkg =
|
|
8987
|
-
if (
|
|
9137
|
+
if (existsSync17(join19(dir, "hyperframes.json"))) return "hyperframes";
|
|
9138
|
+
const pkg = join19(dir, "package.json");
|
|
9139
|
+
if (existsSync17(pkg)) {
|
|
8988
9140
|
try {
|
|
8989
|
-
const p = JSON.parse(
|
|
9141
|
+
const p = JSON.parse(readFileSync7(pkg, "utf8"));
|
|
8990
9142
|
const deps = { ...p.dependencies ?? {}, ...p.devDependencies ?? {} };
|
|
8991
9143
|
if ("remotion" in deps) return "remotion";
|
|
8992
9144
|
if ("hyperframes" in deps) return "hyperframes";
|
|
@@ -8998,14 +9150,14 @@ function detectEngine(dir) {
|
|
|
8998
9150
|
return "page";
|
|
8999
9151
|
}
|
|
9000
9152
|
function sourcePage(source) {
|
|
9001
|
-
if (!
|
|
9153
|
+
if (!existsSync17(source)) return null;
|
|
9002
9154
|
if (statSync6(source).isFile()) return /\.html?$/i.test(source) ? source : null;
|
|
9003
|
-
const index =
|
|
9004
|
-
return
|
|
9155
|
+
const index = join19(source, "index.html");
|
|
9156
|
+
return existsSync17(index) ? index : null;
|
|
9005
9157
|
}
|
|
9006
9158
|
function findRender(dir) {
|
|
9007
|
-
const videos = (d) =>
|
|
9008
|
-
for (const d of [
|
|
9159
|
+
const videos = (d) => existsSync17(d) ? readdirSync4(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join19(d, f)) : [];
|
|
9160
|
+
for (const d of [join19(dir, "renders"), join19(dir, "out"), dir]) {
|
|
9009
9161
|
const found = videos(d);
|
|
9010
9162
|
if (found.length) return found[0];
|
|
9011
9163
|
}
|
|
@@ -9276,7 +9428,7 @@ var SEEK_AND_READ = `((args) => {
|
|
|
9276
9428
|
})`;
|
|
9277
9429
|
async function readInventory(source, opts) {
|
|
9278
9430
|
const abs = resolve7(source);
|
|
9279
|
-
const dir =
|
|
9431
|
+
const dir = existsSync17(abs) && statSync6(abs).isDirectory() ? abs : dirname5(abs);
|
|
9280
9432
|
const engine = detectEngine(dir);
|
|
9281
9433
|
const page = sourcePage(abs);
|
|
9282
9434
|
const render = opts.render === void 0 ? findRender(dir) : opts.render;
|
|
@@ -9379,7 +9531,7 @@ async function readInventory(source, opts) {
|
|
|
9379
9531
|
if (render && duration) {
|
|
9380
9532
|
const has = spawnSync2("ffmpeg", ["-version"]).status === 0;
|
|
9381
9533
|
if (has) {
|
|
9382
|
-
const refDir =
|
|
9534
|
+
const refDir = join19(opts.outDir, "ref");
|
|
9383
9535
|
mkdirSync3(refDir, { recursive: true });
|
|
9384
9536
|
const times = read.scenes.length >= 3 ? read.scenes.map(
|
|
9385
9537
|
(s) => s.start + (s.duration ?? 1) / 2
|
|
@@ -9388,7 +9540,7 @@ async function readInventory(source, opts) {
|
|
|
9388
9540
|
(_, i) => i + 0.5
|
|
9389
9541
|
);
|
|
9390
9542
|
for (const t of times.filter((t2) => t2 < duration)) {
|
|
9391
|
-
const file =
|
|
9543
|
+
const file = join19(refDir, `src-${t.toFixed(2)}.png`);
|
|
9392
9544
|
const ok = spawnSync2("ffmpeg", [
|
|
9393
9545
|
"-v",
|
|
9394
9546
|
"error",
|
|
@@ -9442,8 +9594,8 @@ async function readInventory(source, opts) {
|
|
|
9442
9594
|
|
|
9443
9595
|
// src/plugin/portScaffold.ts
|
|
9444
9596
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
9445
|
-
import { copyFileSync, existsSync as
|
|
9446
|
-
import { basename as basename7, dirname as dirname6, join as
|
|
9597
|
+
import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
9598
|
+
import { basename as basename7, dirname as dirname6, join as join20, resolve as resolve8 } from "path";
|
|
9447
9599
|
import { findFontFamily as findFontFamily4, fontFaceUrl, nearestFontWeight } from "@vosjs/shared";
|
|
9448
9600
|
|
|
9449
9601
|
// src/plugin/painterLib.ts
|
|
@@ -9854,7 +10006,7 @@ function planScaffold(inv, opts) {
|
|
|
9854
10006
|
inv.media.filter((m) => m.kind === "audio" && m.src).forEach((m, i) => {
|
|
9855
10007
|
const from = resolve8(opts.sourceDir, m.src);
|
|
9856
10008
|
const name = basename7(from);
|
|
9857
|
-
if (!
|
|
10009
|
+
if (!existsSync18(from)) {
|
|
9858
10010
|
report.push(
|
|
9859
10011
|
`- the audio ${m.src} is not in the source folder; add it to doc.json by hand`
|
|
9860
10012
|
);
|
|
@@ -10052,13 +10204,13 @@ function reportText(inv, r) {
|
|
|
10052
10204
|
}
|
|
10053
10205
|
function writeScaffold(plan, outDir) {
|
|
10054
10206
|
mkdirSync4(outDir, { recursive: true });
|
|
10055
|
-
writeFileSync4(
|
|
10207
|
+
writeFileSync4(join20(outDir, "program.mjs"), plan.program);
|
|
10056
10208
|
writeFileSync4(
|
|
10057
|
-
|
|
10209
|
+
join20(outDir, "doc.json"),
|
|
10058
10210
|
JSON.stringify(plan.doc, null, 2) + "\n"
|
|
10059
10211
|
);
|
|
10060
|
-
writeFileSync4(
|
|
10061
|
-
for (const [from, name] of plan.copies) copyFileSync(from,
|
|
10212
|
+
writeFileSync4(join20(dirname6(outDir), "REPORT.md"), plan.report);
|
|
10213
|
+
for (const [from, name] of plan.copies) copyFileSync(from, join20(outDir, name));
|
|
10062
10214
|
}
|
|
10063
10215
|
function probeAudioDuration(file) {
|
|
10064
10216
|
const res = spawnSync3(
|
|
@@ -10092,7 +10244,7 @@ async function inventory(argv) {
|
|
|
10092
10244
|
outDir,
|
|
10093
10245
|
log: (l) => r.log(`note: ${l}`)
|
|
10094
10246
|
});
|
|
10095
|
-
const file =
|
|
10247
|
+
const file = join21(outDir, "inventory.json");
|
|
10096
10248
|
writeFileSync5(file, JSON.stringify(inv, null, 2) + "\n");
|
|
10097
10249
|
const missing = inv.fonts.filter((f) => !f.catalog).map((f) => f.family);
|
|
10098
10250
|
r.log(
|
|
@@ -10124,32 +10276,32 @@ function summary(inv) {
|
|
|
10124
10276
|
async function scaffold(argv) {
|
|
10125
10277
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
10126
10278
|
const r = createReporter(flags.json === true);
|
|
10127
|
-
const from = resolve9(strFlag(flags, "from") ??
|
|
10128
|
-
if (!
|
|
10279
|
+
const from = resolve9(strFlag(flags, "from") ?? join21("port", "inventory.json"));
|
|
10280
|
+
if (!existsSync19(from))
|
|
10129
10281
|
throw new UsageError(
|
|
10130
10282
|
`no inventory at ${from}: run npx vos port inventory <source> first`
|
|
10131
10283
|
);
|
|
10132
|
-
const inv = JSON.parse(
|
|
10284
|
+
const inv = JSON.parse(readFileSync8(from, "utf8"));
|
|
10133
10285
|
const outDir = resolve9(
|
|
10134
|
-
strFlag(flags, "out") ??
|
|
10286
|
+
strFlag(flags, "out") ?? join21(dirname7(from), "program")
|
|
10135
10287
|
);
|
|
10136
10288
|
const plan = planScaffold(inv, {
|
|
10137
10289
|
sourceDir: scaffoldSourceDir(inv),
|
|
10138
10290
|
probeDuration: probeAudioDuration
|
|
10139
10291
|
});
|
|
10140
10292
|
writeScaffold(plan, outDir);
|
|
10141
|
-
const programFile =
|
|
10293
|
+
const programFile = join21(outDir, "program.mjs");
|
|
10142
10294
|
const mod = await import(`${pathToFileURL3(programFile).href}?t=${Date.now()}`);
|
|
10143
10295
|
const built = buildProgram(mod.default, "program.mjs", plan.program);
|
|
10144
10296
|
for (const p of built.problems) r.log(`error ${p.fn}: ${p.message}`);
|
|
10145
10297
|
if (built.problems.length) return EXIT_ERROR;
|
|
10146
10298
|
writeFileSync5(
|
|
10147
|
-
|
|
10299
|
+
join21(outDir, "config.json"),
|
|
10148
10300
|
JSON.stringify(built.config, null, 2) + "\n"
|
|
10149
10301
|
);
|
|
10150
10302
|
r.done(
|
|
10151
|
-
{ program: outDir, report:
|
|
10152
|
-
`Wrote ${outDir}/program.mjs, config.json and doc.json, and ${
|
|
10303
|
+
{ program: outDir, report: join21(dirname7(outDir), "REPORT.md") },
|
|
10304
|
+
`Wrote ${outDir}/program.mjs, config.json and doc.json, and ${join21(dirname7(outDir), "REPORT.md")}. Translate the motion where it says TODO, then: npx vos build program.mjs && npx vos compare . --against <their render>`
|
|
10153
10305
|
);
|
|
10154
10306
|
return EXIT_OK;
|
|
10155
10307
|
}
|
|
@@ -10160,7 +10312,7 @@ function scaffoldSourceDir(inv) {
|
|
|
10160
10312
|
|
|
10161
10313
|
// src/plugin/recipe.ts
|
|
10162
10314
|
import { createHash as createHash3 } from "crypto";
|
|
10163
|
-
import { readFileSync as
|
|
10315
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
10164
10316
|
import { basename as basename8 } from "path";
|
|
10165
10317
|
var BOOLEAN_FLAGS9 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
10166
10318
|
var USAGE = "vos recipe push <file.md> (--folder <folderId|slug> | --asset <assetId>)";
|
|
@@ -10180,7 +10332,7 @@ async function cmdPush2(argv) {
|
|
|
10180
10332
|
throw new UsageError(USAGE);
|
|
10181
10333
|
}
|
|
10182
10334
|
const filename = recipeFilename(file);
|
|
10183
|
-
const body =
|
|
10335
|
+
const body = readFileSync9(file);
|
|
10184
10336
|
const r = createReporter(flags.json === true);
|
|
10185
10337
|
const origin = platformOrigin({
|
|
10186
10338
|
origin: strFlag(flags, "origin"),
|
|
@@ -10246,7 +10398,7 @@ async function cmdRecipe(argv) {
|
|
|
10246
10398
|
}
|
|
10247
10399
|
|
|
10248
10400
|
// src/plugin/brand.ts
|
|
10249
|
-
import { writeFile as
|
|
10401
|
+
import { writeFile as writeFile10 } from "fs/promises";
|
|
10250
10402
|
import { resolve as resolve10 } from "path";
|
|
10251
10403
|
import { parseFrontmatter as parseFrontmatter2 } from "@vosjs/shared/frontmatter";
|
|
10252
10404
|
import { lookKindForGround } from "@vosjs/studio-core";
|
|
@@ -10600,7 +10752,7 @@ async function cmdBrand(argv) {
|
|
|
10600
10752
|
composition,
|
|
10601
10753
|
llms?.claim ?? design?.description ?? null
|
|
10602
10754
|
);
|
|
10603
|
-
await
|
|
10755
|
+
await writeFile10(out, md);
|
|
10604
10756
|
const k = composition.kit;
|
|
10605
10757
|
r.done(
|
|
10606
10758
|
{
|
|
@@ -10715,7 +10867,7 @@ async function recordHosted(input) {
|
|
|
10715
10867
|
}
|
|
10716
10868
|
|
|
10717
10869
|
// src/plugin/agentBrowser.ts
|
|
10718
|
-
import { writeFile as
|
|
10870
|
+
import { writeFile as writeFile11 } from "fs/promises";
|
|
10719
10871
|
import { resolve as resolve11 } from "path";
|
|
10720
10872
|
function parseAgentBrowserLog(text) {
|
|
10721
10873
|
const records = [];
|
|
@@ -11195,8 +11347,8 @@ async function cmdActions(argv) {
|
|
|
11195
11347
|
if (!m) throw new UsageError("--viewport expects WxH, e.g. 1280x720");
|
|
11196
11348
|
viewport = { width: Number(m[1]), height: Number(m[2]) };
|
|
11197
11349
|
}
|
|
11198
|
-
const { readFile:
|
|
11199
|
-
const text = await
|
|
11350
|
+
const { readFile: readFile14 } = await import("fs/promises");
|
|
11351
|
+
const text = await readFile14(resolve11(input), "utf8");
|
|
11200
11352
|
const { records, problems } = parseAgentBrowserLog(text);
|
|
11201
11353
|
for (const p of problems) r.log(` ${p}`);
|
|
11202
11354
|
const result = convertAgentBrowser(records, {
|
|
@@ -11213,7 +11365,7 @@ async function cmdActions(argv) {
|
|
|
11213
11365
|
);
|
|
11214
11366
|
return EXIT_ERROR;
|
|
11215
11367
|
}
|
|
11216
|
-
await
|
|
11368
|
+
await writeFile11(out, `${JSON.stringify(result.actions, null, 2)}
|
|
11217
11369
|
`);
|
|
11218
11370
|
r.done(
|
|
11219
11371
|
{ ok: true, out, url: result.actions.url ?? null, ...summary2(result) },
|
|
@@ -11233,9 +11385,9 @@ function summary2(result) {
|
|
|
11233
11385
|
}
|
|
11234
11386
|
async function cmdActionsScript(input, flags) {
|
|
11235
11387
|
const r = createReporter(flags.json === true);
|
|
11236
|
-
const { readFile:
|
|
11388
|
+
const { readFile: readFile14 } = await import("fs/promises");
|
|
11237
11389
|
const { shotList, formatShotList } = await import("./shotList-KH6FTX4L.js");
|
|
11238
|
-
const raw = JSON.parse(await
|
|
11390
|
+
const raw = JSON.parse(await readFile14(resolve11(input), "utf8"));
|
|
11239
11391
|
const errors = validateActions(raw);
|
|
11240
11392
|
if (errors.length) throw new UsageError(`${input}:
|
|
11241
11393
|
${errors.join("\n ")}`);
|
|
@@ -11245,9 +11397,9 @@ async function cmdActionsScript(input, flags) {
|
|
|
11245
11397
|
}
|
|
11246
11398
|
|
|
11247
11399
|
// src/plugin/ingest.ts
|
|
11248
|
-
import { existsSync as
|
|
11249
|
-
import { copyFile, mkdir as mkdir7, readFile as
|
|
11250
|
-
import { basename as basename9, join as
|
|
11400
|
+
import { existsSync as existsSync20 } from "fs";
|
|
11401
|
+
import { copyFile, mkdir as mkdir7, readFile as readFile12, stat as stat2 } from "fs/promises";
|
|
11402
|
+
import { basename as basename9, join as join22, resolve as resolve12 } from "path";
|
|
11251
11403
|
import { inflateRawSync } from "zlib";
|
|
11252
11404
|
import {
|
|
11253
11405
|
cursorFromPlaywrightTrace,
|
|
@@ -11335,7 +11487,7 @@ async function readTraceFile(path, offsetMs) {
|
|
|
11335
11487
|
);
|
|
11336
11488
|
}
|
|
11337
11489
|
if (kind === "zip") {
|
|
11338
|
-
const bytes = new Uint8Array(await
|
|
11490
|
+
const bytes = new Uint8Array(await readFile12(path));
|
|
11339
11491
|
const entry = playwrightTraceEntry(zipEntries(bytes));
|
|
11340
11492
|
if (!entry) {
|
|
11341
11493
|
throw new UsageError(
|
|
@@ -11348,7 +11500,7 @@ async function readTraceFile(path, offsetMs) {
|
|
|
11348
11500
|
);
|
|
11349
11501
|
return cursorFromPlaywrightTrace(text2, { offsetMs });
|
|
11350
11502
|
}
|
|
11351
|
-
const text = await
|
|
11503
|
+
const text = await readFile12(path, "utf8");
|
|
11352
11504
|
return cursorFromTrace(basename9(path), text, { offsetMs });
|
|
11353
11505
|
}
|
|
11354
11506
|
function parseViewport(v) {
|
|
@@ -11365,7 +11517,7 @@ async function cmdIngest(argv) {
|
|
|
11365
11517
|
"vos ingest <video.webm|mp4> [--cursor <trace.zip|steps.jsonl|cursor.csv>] [--as footage|take] [--out take] [--offset <ms>] [--viewport WxH] [--background <slug|url|none>] [--json]"
|
|
11366
11518
|
);
|
|
11367
11519
|
const videoPath = resolve12(source);
|
|
11368
|
-
if (!
|
|
11520
|
+
if (!existsSync20(videoPath)) throw new UsageError(`${source}: no such file`);
|
|
11369
11521
|
const r = createReporter(flags.json === true);
|
|
11370
11522
|
const outDir = resolve12(strFlag(flags, "out") ?? "take");
|
|
11371
11523
|
const offsetMs = numFlag(flags, "offset", 0);
|
|
@@ -11392,7 +11544,7 @@ async function cmdIngest(argv) {
|
|
|
11392
11544
|
trace = await readTraceFile(resolve12(cursorPath), offsetMs);
|
|
11393
11545
|
for (const n of trace.notes) r.log(`note: ${n}`);
|
|
11394
11546
|
}
|
|
11395
|
-
if (
|
|
11547
|
+
if (existsSync20(join22(outDir, "meta.json"))) {
|
|
11396
11548
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
11397
11549
|
r.log(
|
|
11398
11550
|
`note: re-ingesting into ${outDir}; kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? `; apply the previous cut with: vos plan ${outDir} --reuse` : "")
|
|
@@ -11400,7 +11552,7 @@ async function cmdIngest(argv) {
|
|
|
11400
11552
|
}
|
|
11401
11553
|
await mkdir7(outDir, { recursive: true });
|
|
11402
11554
|
const paths = await ensureTakeDir(outDir);
|
|
11403
|
-
const recording =
|
|
11555
|
+
const recording = join22(outDir, `recording.${probed.ext}`);
|
|
11404
11556
|
r.log("copying the recording\u2026");
|
|
11405
11557
|
const remuxed = await copyRecording(videoPath, recording, probed.ext);
|
|
11406
11558
|
const viewport = viewportFlag ?? trace?.viewport ?? {
|
|
@@ -11468,19 +11620,19 @@ async function cmdIngest(argv) {
|
|
|
11468
11620
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
11469
11621
|
import {
|
|
11470
11622
|
cpSync,
|
|
11471
|
-
existsSync as
|
|
11623
|
+
existsSync as existsSync22,
|
|
11472
11624
|
mkdirSync as mkdirSync6,
|
|
11473
|
-
readFileSync as
|
|
11625
|
+
readFileSync as readFileSync11,
|
|
11474
11626
|
rmSync as rmSync3,
|
|
11475
11627
|
writeFileSync as writeFileSync6
|
|
11476
11628
|
} from "fs";
|
|
11477
11629
|
import { homedir as homedir2 } from "os";
|
|
11478
|
-
import { join as
|
|
11630
|
+
import { join as join24 } from "path";
|
|
11479
11631
|
import { chromium as chromium2 } from "playwright";
|
|
11480
11632
|
|
|
11481
11633
|
// src/plugin/machine.ts
|
|
11482
|
-
import { existsSync as
|
|
11483
|
-
import { dirname as dirname8, join as
|
|
11634
|
+
import { existsSync as existsSync21, readFileSync as readFileSync10, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
|
|
11635
|
+
import { dirname as dirname8, join as join23 } from "path";
|
|
11484
11636
|
var AGENT_NAMES = [
|
|
11485
11637
|
"claude",
|
|
11486
11638
|
"cursor",
|
|
@@ -11500,17 +11652,17 @@ var HOME_DIRS = {
|
|
|
11500
11652
|
copilot: [".copilot"]
|
|
11501
11653
|
};
|
|
11502
11654
|
function detectAgents(root, opts = {}) {
|
|
11503
|
-
const exists = opts.exists ??
|
|
11655
|
+
const exists = opts.exists ?? existsSync21;
|
|
11504
11656
|
const out = [];
|
|
11505
11657
|
for (const agent of AGENT_NAMES) {
|
|
11506
11658
|
for (const rel of PROJECT_DIRS[agent]) {
|
|
11507
|
-
const dir =
|
|
11659
|
+
const dir = join23(root, rel);
|
|
11508
11660
|
if (exists(dir)) out.push({ agent, dir, scope: "project" });
|
|
11509
11661
|
}
|
|
11510
|
-
if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(
|
|
11662
|
+
if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(join23(root, ".github", "copilot-instructions.md"))) {
|
|
11511
11663
|
out.push({
|
|
11512
11664
|
agent,
|
|
11513
|
-
dir:
|
|
11665
|
+
dir: join23(root, ".github", "copilot"),
|
|
11514
11666
|
scope: "project"
|
|
11515
11667
|
});
|
|
11516
11668
|
}
|
|
@@ -11518,7 +11670,7 @@ function detectAgents(root, opts = {}) {
|
|
|
11518
11670
|
if (opts.global && opts.home) {
|
|
11519
11671
|
for (const agent of AGENT_NAMES) {
|
|
11520
11672
|
for (const rel of HOME_DIRS[agent]) {
|
|
11521
|
-
const dir =
|
|
11673
|
+
const dir = join23(opts.home, rel);
|
|
11522
11674
|
if (exists(dir)) out.push({ agent, dir, scope: "global" });
|
|
11523
11675
|
}
|
|
11524
11676
|
}
|
|
@@ -11536,18 +11688,18 @@ function agentsFromFlag(value, root) {
|
|
|
11536
11688
|
return {
|
|
11537
11689
|
homes: wanted.map((agent) => ({
|
|
11538
11690
|
agent,
|
|
11539
|
-
dir:
|
|
11691
|
+
dir: join23(root, PROJECT_DIRS[agent][0]),
|
|
11540
11692
|
scope: "project"
|
|
11541
11693
|
}))
|
|
11542
11694
|
};
|
|
11543
11695
|
}
|
|
11544
11696
|
function skillsDirFor(home) {
|
|
11545
|
-
return
|
|
11697
|
+
return join23(home.dir, "skills");
|
|
11546
11698
|
}
|
|
11547
11699
|
function fallbackSkillsHome(root) {
|
|
11548
|
-
return { agent: "codex", dir:
|
|
11700
|
+
return { agent: "codex", dir: join23(root, ".agents"), scope: "project" };
|
|
11549
11701
|
}
|
|
11550
|
-
function installedSkills(dir, exists =
|
|
11702
|
+
function installedSkills(dir, exists = existsSync21, list = (p) => {
|
|
11551
11703
|
try {
|
|
11552
11704
|
return readdirSync5(p);
|
|
11553
11705
|
} catch {
|
|
@@ -11555,7 +11707,7 @@ function installedSkills(dir, exists = existsSync20, list = (p) => {
|
|
|
11555
11707
|
}
|
|
11556
11708
|
}) {
|
|
11557
11709
|
if (!exists(dir)) return [];
|
|
11558
|
-
return list(dir).filter((name) => exists(
|
|
11710
|
+
return list(dir).filter((name) => exists(join23(dir, name, "SKILL.md"))).sort();
|
|
11559
11711
|
}
|
|
11560
11712
|
var RULES_BEGIN = "<!-- vos:begin (written by vos setup; the block is replaced on the next run) -->";
|
|
11561
11713
|
var RULES_END = "<!-- vos:end -->";
|
|
@@ -11588,10 +11740,10 @@ ${RULES_BLOCK}
|
|
|
11588
11740
|
}
|
|
11589
11741
|
return { text, changed: text !== current };
|
|
11590
11742
|
}
|
|
11591
|
-
function rulesFileFor(root, exists =
|
|
11592
|
-
const agents =
|
|
11743
|
+
function rulesFileFor(root, exists = existsSync21) {
|
|
11744
|
+
const agents = join23(root, "AGENTS.md");
|
|
11593
11745
|
if (exists(agents)) return agents;
|
|
11594
|
-
const claude =
|
|
11746
|
+
const claude = join23(root, "CLAUDE.md");
|
|
11595
11747
|
if (exists(claude)) return claude;
|
|
11596
11748
|
return agents;
|
|
11597
11749
|
}
|
|
@@ -11600,14 +11752,14 @@ function chromeCandidates(platform, env) {
|
|
|
11600
11752
|
return ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
|
|
11601
11753
|
if (platform === "win32")
|
|
11602
11754
|
return [
|
|
11603
|
-
|
|
11755
|
+
join23(
|
|
11604
11756
|
env["PROGRAMFILES"] ?? "C:\\Program Files",
|
|
11605
11757
|
"Google",
|
|
11606
11758
|
"Chrome",
|
|
11607
11759
|
"Application",
|
|
11608
11760
|
"chrome.exe"
|
|
11609
11761
|
),
|
|
11610
|
-
|
|
11762
|
+
join23(
|
|
11611
11763
|
env["LOCALAPPDATA"] ?? "",
|
|
11612
11764
|
"Google",
|
|
11613
11765
|
"Chrome",
|
|
@@ -11694,13 +11846,13 @@ function doctorReport(facts) {
|
|
|
11694
11846
|
function bundledSkillsDir(from) {
|
|
11695
11847
|
let dir = dirname8(from);
|
|
11696
11848
|
for (let i = 0; i < 6; i++) {
|
|
11697
|
-
const pkg =
|
|
11698
|
-
if (
|
|
11849
|
+
const pkg = join23(dir, "package.json");
|
|
11850
|
+
if (existsSync21(pkg)) {
|
|
11699
11851
|
try {
|
|
11700
|
-
const name = JSON.parse(
|
|
11852
|
+
const name = JSON.parse(readFileSync10(pkg, "utf8")).name;
|
|
11701
11853
|
if (name === "@vosjs/cli") {
|
|
11702
|
-
const skills =
|
|
11703
|
-
return
|
|
11854
|
+
const skills = join23(dir, "skills");
|
|
11855
|
+
return existsSync21(skills) && statSync7(skills).isDirectory() ? skills : null;
|
|
11704
11856
|
}
|
|
11705
11857
|
} catch {
|
|
11706
11858
|
}
|
|
@@ -11731,7 +11883,7 @@ function npx() {
|
|
|
11731
11883
|
function bundledChromium() {
|
|
11732
11884
|
try {
|
|
11733
11885
|
const p = chromium2.executablePath();
|
|
11734
|
-
return p &&
|
|
11886
|
+
return p && existsSync22(p) ? p : null;
|
|
11735
11887
|
} catch {
|
|
11736
11888
|
return null;
|
|
11737
11889
|
}
|
|
@@ -11740,7 +11892,7 @@ function probe2() {
|
|
|
11740
11892
|
return probeBrowser({
|
|
11741
11893
|
platform: process.platform,
|
|
11742
11894
|
env: process.env,
|
|
11743
|
-
exists:
|
|
11895
|
+
exists: existsSync22,
|
|
11744
11896
|
bundled: bundledChromium
|
|
11745
11897
|
});
|
|
11746
11898
|
}
|
|
@@ -11844,7 +11996,7 @@ async function cmdSetup(argv) {
|
|
|
11844
11996
|
const dest = skillsDirFor(home);
|
|
11845
11997
|
mkdirSync6(dest, { recursive: true });
|
|
11846
11998
|
for (const name of installedSkills(bundled)) {
|
|
11847
|
-
cpSync(
|
|
11999
|
+
cpSync(join24(bundled, name), join24(dest, name), { recursive: true });
|
|
11848
12000
|
}
|
|
11849
12001
|
}
|
|
11850
12002
|
skillsVia = "bundled";
|
|
@@ -11879,7 +12031,7 @@ async function cmdSetup(argv) {
|
|
|
11879
12031
|
let rules = null;
|
|
11880
12032
|
if (!noRules) {
|
|
11881
12033
|
const file = rulesFileFor(root);
|
|
11882
|
-
const existing =
|
|
12034
|
+
const existing = existsSync22(file) ? readFileSync11(file, "utf8") : null;
|
|
11883
12035
|
const { text, changed } = applyRulesBlock(existing);
|
|
11884
12036
|
if (changed) writeFileSync6(file, text);
|
|
11885
12037
|
rules = { file, changed };
|
|
@@ -11993,7 +12145,7 @@ async function cmdWhoami(argv) {
|
|
|
11993
12145
|
function cmdLogout(argv) {
|
|
11994
12146
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS11);
|
|
11995
12147
|
const r = reporterFor(flags);
|
|
11996
|
-
const had =
|
|
12148
|
+
const had = existsSync22(CREDENTIALS_PATH);
|
|
11997
12149
|
rmSync3(CREDENTIALS_PATH, { force: true });
|
|
11998
12150
|
const envKey = Boolean(process.env.VOS_API_KEY?.trim());
|
|
11999
12151
|
r.done(
|
|
@@ -12199,12 +12351,16 @@ The re-render loop (re-recording the same script after the product
|
|
|
12199
12351
|
changed): record into the SAME take dir \u2014 footage/cursor/meta are
|
|
12200
12352
|
replaced, the cut survives as doc.prev.json \u2014 then plan --reuse re-times
|
|
12201
12353
|
it onto the new recording. meta.json carries steps (when each actions
|
|
12202
|
-
step ran); a span
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12354
|
+
step ran); a human span follows the step it was made on (its window, or
|
|
12355
|
+
the gesture just before it), keeping its offset exactly, or an explicit
|
|
12356
|
+
anchor {step, at, offset}; a span between steps maps piecewise through
|
|
12357
|
+
matched step edges; a span whose step vanished is dropped and named with
|
|
12358
|
+
what it framed; autos re-plan; a layer pinned to a step follows its new
|
|
12359
|
+
output time, every other layer and audio keep OUTPUT times, and a layer
|
|
12360
|
+
over a step that moved is named with the pin to write. The done event's
|
|
12361
|
+
reuse block NAMES whatever could not follow \u2014 fix by exception, never
|
|
12362
|
+
re-cut from scratch. Give steps ids in actions.json so re-timing survives
|
|
12363
|
+
script edits.
|
|
12208
12364
|
|
|
12209
12365
|
record --strict exits 2 when any selector was skipped, the page never
|
|
12210
12366
|
reached networkidle, or the take hit --max-duration (the done event lists
|
|
@@ -12328,7 +12484,7 @@ lint-gated, so a bad override fails like a bad doc.json):
|
|
|
12328
12484
|
first ready loop (the house backdrop the studio opens on), or a flat ground offline
|
|
12329
12485
|
`;
|
|
12330
12486
|
async function loadActions(file) {
|
|
12331
|
-
const raw = JSON.parse(await
|
|
12487
|
+
const raw = JSON.parse(await readFile13(file, "utf8"));
|
|
12332
12488
|
const errors = validateActions(raw);
|
|
12333
12489
|
if (errors.length)
|
|
12334
12490
|
throw new UsageError(`invalid actions file:
|
|
@@ -12391,7 +12547,7 @@ function takeStorageState(flags) {
|
|
|
12391
12547
|
const raw = strFlag(flags, "storage-state");
|
|
12392
12548
|
if (!raw) return void 0;
|
|
12393
12549
|
const path = resolve13(raw);
|
|
12394
|
-
if (!
|
|
12550
|
+
if (!existsSync23(path))
|
|
12395
12551
|
throw new UsageError(`--storage-state: no such file: ${path}`);
|
|
12396
12552
|
return path;
|
|
12397
12553
|
}
|
|
@@ -12404,7 +12560,7 @@ function takeSessionName(flags, storageState) {
|
|
|
12404
12560
|
throw new UsageError(
|
|
12405
12561
|
"--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
|
|
12406
12562
|
);
|
|
12407
|
-
if (!
|
|
12563
|
+
if (!existsSync23(sessionDir(name)))
|
|
12408
12564
|
throw new UsageError(
|
|
12409
12565
|
`no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
|
|
12410
12566
|
);
|
|
@@ -12627,7 +12783,7 @@ ${lines.join("\n")}
|
|
|
12627
12783
|
}
|
|
12628
12784
|
const paths = takePaths(outDir);
|
|
12629
12785
|
const prepare = async () => {
|
|
12630
|
-
if (
|
|
12786
|
+
if (existsSync23(join25(outDir, "meta.json"))) {
|
|
12631
12787
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
12632
12788
|
r.log(
|
|
12633
12789
|
`note: re-recording ${outDir} \u2014 kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? "; apply the previous cut to the new footage with: vos plan " + outDir + " --reuse" : "")
|
|
@@ -12692,7 +12848,7 @@ ${lines.join("\n")}
|
|
|
12692
12848
|
},
|
|
12693
12849
|
strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}${exposureNote(rec)}` : `Take ready: ${outDir}
|
|
12694
12850
|
${(rec.meta.durationMs / 1e3).toFixed(1)}s \xB7 ${rec.frames.length} frames \xB7 ${rec.events.length} cursor events \xB7 ${clicks} clicks \xB7 ${plan.doc.zoom.length} zoom spans planned \xB7 ${rec.freezePct}% frozen \xB7 ${rec.dead.pct}% dead${deadWarns(rec.dead) ? " \u26A0 " + deadLine(rec.dead) : ""}${skippedNote}
|
|
12695
|
-
Next: edit ${
|
|
12851
|
+
Next: edit ${join25(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
|
|
12696
12852
|
);
|
|
12697
12853
|
return strictFail ? EXIT_USAGE : EXIT_OK;
|
|
12698
12854
|
} finally {
|
|
@@ -12718,7 +12874,7 @@ async function cmdCreate2(argv) {
|
|
|
12718
12874
|
throw new UsageError('no URL \u2014 set "url" in the actions file or pass --url');
|
|
12719
12875
|
const outDir = resolve13(strFlag(flags, "out") ?? "take");
|
|
12720
12876
|
const format = videoFormat(strFlag(flags, "format"), positionals[0]);
|
|
12721
|
-
const out = positionals[0] ??
|
|
12877
|
+
const out = positionals[0] ?? join25(outDir, `out.${format}`);
|
|
12722
12878
|
const parallel = numFlag(flags, "parallel", 1);
|
|
12723
12879
|
if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
|
|
12724
12880
|
throw new UsageError("--parallel expects an integer between 1 and 16");
|
|
@@ -12731,7 +12887,7 @@ async function cmdCreate2(argv) {
|
|
|
12731
12887
|
const headers = takeHeaders(multi);
|
|
12732
12888
|
const paths = takePaths(outDir);
|
|
12733
12889
|
const prepare = async () => {
|
|
12734
|
-
if (
|
|
12890
|
+
if (existsSync23(join25(outDir, "meta.json"))) {
|
|
12735
12891
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
12736
12892
|
r.log(
|
|
12737
12893
|
`note: re-recording ${outDir} \u2014 kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? "; apply the previous cut to the new footage with: vos plan " + outDir + " --reuse" : "")
|
|
@@ -12838,24 +12994,24 @@ async function cmdPlan(argv) {
|
|
|
12838
12994
|
);
|
|
12839
12995
|
const r = createReporter(flags.json === true);
|
|
12840
12996
|
if (flags.fresh === true) {
|
|
12841
|
-
await rm2(
|
|
12997
|
+
await rm2(join25(dir, "doc.json"), { force: true });
|
|
12842
12998
|
r.log("note: --fresh discarded the existing doc.json");
|
|
12843
12999
|
}
|
|
12844
13000
|
let reuse;
|
|
12845
13001
|
if (flags.reuse === true) {
|
|
12846
|
-
const from = resolve13(strFlag(flags, "from") ??
|
|
12847
|
-
if (!
|
|
13002
|
+
const from = resolve13(strFlag(flags, "from") ?? join25(dir, PREV_DOC_NAME));
|
|
13003
|
+
if (!existsSync23(from)) {
|
|
12848
13004
|
throw new UsageError(
|
|
12849
13005
|
`--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
|
|
12850
13006
|
);
|
|
12851
13007
|
}
|
|
12852
13008
|
reuse = {
|
|
12853
13009
|
from,
|
|
12854
|
-
doc: JSON.parse(await
|
|
13010
|
+
doc: JSON.parse(await readFile13(from, "utf8"))
|
|
12855
13011
|
};
|
|
12856
13012
|
}
|
|
12857
13013
|
const style = await resolveStyleRef(flags);
|
|
12858
|
-
const hasDoc =
|
|
13014
|
+
const hasDoc = existsSync23(join25(dir, "doc.json"));
|
|
12859
13015
|
const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
|
|
12860
13016
|
const release = await releaseInputs(dir, flags, r, multi.with ?? []);
|
|
12861
13017
|
const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
|
|
@@ -12913,7 +13069,7 @@ async function cmdPlan(argv) {
|
|
|
12913
13069
|
...s.motion ? { motion: s.motion } : {},
|
|
12914
13070
|
...s.reuse ? { reuse: s.reuse } : {}
|
|
12915
13071
|
},
|
|
12916
|
-
`${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${
|
|
13072
|
+
`${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${join25(dir, "doc.json")}: ${s.zoomAuto} auto + ${s.zoomManual} manual zoom spans${s.cursorKept ? "" : " (cursor track dropped)"}${layoutLines}${motionLines}${reuseLines}`
|
|
12917
13073
|
);
|
|
12918
13074
|
return EXIT_OK;
|
|
12919
13075
|
}
|
|
@@ -13036,7 +13192,7 @@ async function cmdRender(argv) {
|
|
|
13036
13192
|
);
|
|
13037
13193
|
const r = createReporter(flags.json === true);
|
|
13038
13194
|
const format = videoFormat(strFlag(flags, "format"), positionals[1]);
|
|
13039
|
-
const out = positionals[1] ??
|
|
13195
|
+
const out = positionals[1] ?? join25(dir, `out.${format}`);
|
|
13040
13196
|
const parallel = numFlag(flags, "parallel", 1);
|
|
13041
13197
|
if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
|
|
13042
13198
|
throw new UsageError("--parallel expects an integer between 1 and 16");
|
|
@@ -13267,15 +13423,15 @@ async function resolveStyleRef(flags) {
|
|
|
13267
13423
|
}
|
|
13268
13424
|
async function resolveDocRef(ref, flags, what) {
|
|
13269
13425
|
const styleRef = ref;
|
|
13270
|
-
const file =
|
|
13426
|
+
const file = existsSync23(styleRef) ? resolve13(
|
|
13271
13427
|
styleRef,
|
|
13272
|
-
|
|
13428
|
+
existsSync23(join25(styleRef, "doc.json")) ? "doc.json" : ""
|
|
13273
13429
|
) : null;
|
|
13274
|
-
if (file &&
|
|
13430
|
+
if (file && existsSync23(file)) {
|
|
13275
13431
|
return {
|
|
13276
13432
|
from: file,
|
|
13277
13433
|
doc: migrateHostedDoc4(
|
|
13278
|
-
JSON.parse(await
|
|
13434
|
+
JSON.parse(await readFile13(file, "utf8"))
|
|
13279
13435
|
)
|
|
13280
13436
|
};
|
|
13281
13437
|
}
|
|
@@ -13327,7 +13483,7 @@ async function cmdDigest(argv) {
|
|
|
13327
13483
|
const take = await loadTake(dir);
|
|
13328
13484
|
if (!take.doc) throw new UsageError(`${dir} has no doc.json \u2014 run plan first`);
|
|
13329
13485
|
const transcriptPath = strFlag(flags, "transcript");
|
|
13330
|
-
const transcript = transcriptPath ? parseTranscript(JSON.parse(await
|
|
13486
|
+
const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile13(transcriptPath, "utf8"))) : null;
|
|
13331
13487
|
const style = await resolveStyleRef(flags);
|
|
13332
13488
|
const noFrames = flags["no-frames"] === true;
|
|
13333
13489
|
let browser = null;
|
|
@@ -13358,7 +13514,7 @@ async function cmdDigest(argv) {
|
|
|
13358
13514
|
kinds,
|
|
13359
13515
|
frames: d.images.full,
|
|
13360
13516
|
crops: d.images.crop,
|
|
13361
|
-
sheet: d.images.sheet ?
|
|
13517
|
+
sheet: d.images.sheet ? join25(result.outDir, d.images.sheet) : null,
|
|
13362
13518
|
scenes: kinds.scene ?? 0,
|
|
13363
13519
|
sourceDuration: d.take.sourceDuration,
|
|
13364
13520
|
outputDuration: d.take.outputDuration,
|
|
@@ -13386,7 +13542,7 @@ async function cmdOpen(argv) {
|
|
|
13386
13542
|
if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
|
|
13387
13543
|
const r = createReporter(flags.json === true);
|
|
13388
13544
|
const take = await loadTake(dir);
|
|
13389
|
-
if (!
|
|
13545
|
+
if (!existsSync23(take.paths.recording)) {
|
|
13390
13546
|
throw new UsageError(`${dir} has no recording \u2014 re-run record`);
|
|
13391
13547
|
}
|
|
13392
13548
|
const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
|
|
@@ -13443,7 +13599,7 @@ async function cmdCallout(argv) {
|
|
|
13443
13599
|
`no register: put a BRAND.md beside the take (vos brand <url> --out BRAND.md; its bgA and accent are the ground and the accent), or pass --ground #hex --accent #hex`
|
|
13444
13600
|
);
|
|
13445
13601
|
const codeFile = strFlag(flags, "code-file");
|
|
13446
|
-
const code = codeFile ? await
|
|
13602
|
+
const code = codeFile ? await readFile13(codeFile, "utf8") : strFlag(flags, "code");
|
|
13447
13603
|
const words2 = {
|
|
13448
13604
|
kicker: strFlag(flags, "kicker"),
|
|
13449
13605
|
title: strFlag(flags, "title"),
|
|
@@ -13522,7 +13678,7 @@ async function cmdValidate(argv) {
|
|
|
13522
13678
|
const target = positionals[0];
|
|
13523
13679
|
if (!target) throw new UsageError("vos validate <actions.json|take>");
|
|
13524
13680
|
const r = createReporter(flags.json === true);
|
|
13525
|
-
const kitTarget = target.endsWith("kit.json") ? target :
|
|
13681
|
+
const kitTarget = target.endsWith("kit.json") ? target : existsSync23(join25(target, "kit.json")) && !isTakeDir(target) ? join25(target, "kit.json") : null;
|
|
13526
13682
|
if (kitTarget) {
|
|
13527
13683
|
const picture = flags.picture === true;
|
|
13528
13684
|
const verdict = await validateKit(kitTarget, { picture });
|
|
@@ -13554,9 +13710,9 @@ async function cmdValidate(argv) {
|
|
|
13554
13710
|
r.done({ valid: true, target }, `${target}: valid actions file`);
|
|
13555
13711
|
return EXIT_OK;
|
|
13556
13712
|
}
|
|
13557
|
-
if (!isTakeDir(target) &&
|
|
13713
|
+
if (!isTakeDir(target) && existsSync23(join25(target, "config.json"))) {
|
|
13558
13714
|
const parsed = JSON.parse(
|
|
13559
|
-
await
|
|
13715
|
+
await readFile13(join25(target, "config.json"), "utf8")
|
|
13560
13716
|
);
|
|
13561
13717
|
const pre = preflightConfig(parsed);
|
|
13562
13718
|
const problems2 = pre.ok ? [] : pre.issues.map((i) => `config.json: ${i}`);
|
|
@@ -13597,7 +13753,7 @@ async function cmdValidate(argv) {
|
|
|
13597
13753
|
const take = await loadTake(target);
|
|
13598
13754
|
const problems = [];
|
|
13599
13755
|
const warnings = [];
|
|
13600
|
-
if (!
|
|
13756
|
+
if (!existsSync23(take.paths.recording)) {
|
|
13601
13757
|
problems.push("missing recording (re-run record)");
|
|
13602
13758
|
} else {
|
|
13603
13759
|
const mismatch = containerMismatch(
|
|
@@ -13982,7 +14138,7 @@ async function cmdSession(argv) {
|
|
|
13982
14138
|
if (!arg) throw new UsageError(usage);
|
|
13983
14139
|
const bad = sessionNameVerdict(arg);
|
|
13984
14140
|
if (bad) throw new UsageError(bad);
|
|
13985
|
-
if (!
|
|
14141
|
+
if (!existsSync23(sessionDir(arg)))
|
|
13986
14142
|
throw new UsageError(`no session "${arg}" (vos session list)`);
|
|
13987
14143
|
const summary3 = await summarizeSession(arg);
|
|
13988
14144
|
const url = strFlag(flags, "url");
|
|
@@ -14082,4 +14238,4 @@ export {
|
|
|
14082
14238
|
verbHelp,
|
|
14083
14239
|
run
|
|
14084
14240
|
};
|
|
14085
|
-
//# sourceMappingURL=chunk-
|
|
14241
|
+
//# sourceMappingURL=chunk-KIFXVPBR.js.map
|