@vosjs/cli 0.80.0 → 0.81.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 +22 -8
- package/dist/{assetManifest-ETT3C7ZA.js → assetManifest-27NKSS7N.js} +2 -2
- package/dist/{chunk-PJ6MEJZV.js → chunk-352AC3V6.js} +37 -9
- package/dist/chunk-352AC3V6.js.map +1 -0
- package/dist/{chunk-SP5OHTVB.js → chunk-S3RI2SHH.js} +563 -250
- package/dist/chunk-S3RI2SHH.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +27 -27
- package/dist/index.js +2 -2
- package/dist/{run-2ELHMPTB.js → run-MEDD2Q74.js} +7 -3
- package/dist/{shotList-MSNWLVDN.js → shotList-PL7XP6AC.js} +6 -1
- package/dist/shotList-PL7XP6AC.js.map +1 -0
- package/package.json +5 -5
- package/schema/actions.schema.json +56 -45
- package/schema/doc.schema.json +27 -1
- package/dist/chunk-PJ6MEJZV.js.map +0 -1
- package/dist/chunk-SP5OHTVB.js.map +0 -1
- package/dist/shotList-MSNWLVDN.js.map +0 -1
- /package/dist/{assetManifest-ETT3C7ZA.js.map → assetManifest-27NKSS7N.js.map} +0 -0
- /package/dist/{run-2ELHMPTB.js.map → run-MEDD2Q74.js.map} +0 -0
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
isTakeRelativeKey,
|
|
29
29
|
loadTake,
|
|
30
30
|
localManifestRefs,
|
|
31
|
+
notePlannedDoc,
|
|
31
32
|
prepareReRecord,
|
|
32
33
|
pullManifest,
|
|
33
34
|
pullMedia,
|
|
@@ -45,7 +46,7 @@ import {
|
|
|
45
46
|
uploadDocRefs,
|
|
46
47
|
uploadManifest,
|
|
47
48
|
writeJson
|
|
48
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-352AC3V6.js";
|
|
49
50
|
import {
|
|
50
51
|
CREDENTIALS_PATH,
|
|
51
52
|
MEDIA_HEAD_BYTES,
|
|
@@ -145,8 +146,8 @@ function landedLines(l) {
|
|
|
145
146
|
|
|
146
147
|
// src/plugin/run.ts
|
|
147
148
|
import { mkdir as mkdir13, readFile as readFile18, rm as rm6 } from "fs/promises";
|
|
148
|
-
import { existsSync as
|
|
149
|
-
import { dirname as dirname11, join as join33, resolve as
|
|
149
|
+
import { existsSync as existsSync30 } from "fs";
|
|
150
|
+
import { dirname as dirname11, join as join33, resolve as resolve21 } from "path";
|
|
150
151
|
import {
|
|
151
152
|
END_CARD_FROM as END_CARD_FROM2,
|
|
152
153
|
docOutputDuration as docOutputDuration5,
|
|
@@ -662,6 +663,56 @@ function lintDoc(docIn) {
|
|
|
662
663
|
);
|
|
663
664
|
}
|
|
664
665
|
}
|
|
666
|
+
const cursorStyle = isObj(doc.cursor) ? doc.cursor : {};
|
|
667
|
+
if (recording && cursorStyle.off !== void 0) {
|
|
668
|
+
const off2 = cursorStyle.off;
|
|
669
|
+
if (!Array.isArray(off2)) {
|
|
670
|
+
problems.push(
|
|
671
|
+
"cursor.off must be an array of { in, out?, from? } ranges (OUTPUT seconds)"
|
|
672
|
+
);
|
|
673
|
+
} else {
|
|
674
|
+
let end = null;
|
|
675
|
+
const filmEnd = () => {
|
|
676
|
+
if (end === null) {
|
|
677
|
+
const rated = ratedSegments(doc);
|
|
678
|
+
end = outputEnd(
|
|
679
|
+
resolveOverlayAnchors(doc, rated),
|
|
680
|
+
rated.reduce((a, s) => a + (s.out - s.in) / (s.rate ?? 1), 0)
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
return end;
|
|
684
|
+
};
|
|
685
|
+
for (const [i, raw] of off2.entries()) {
|
|
686
|
+
const name = `cursor.off[${i}]`;
|
|
687
|
+
if (!isObj(raw)) {
|
|
688
|
+
problems.push(
|
|
689
|
+
`${name} must be an object { in, out?, from? } (OUTPUT seconds)`
|
|
690
|
+
);
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
for (const k of Object.keys(raw))
|
|
694
|
+
if (k !== "in" && k !== "out" && k !== "from")
|
|
695
|
+
problems.push(`${name}.${k}: not a range field (in, out, from)`);
|
|
696
|
+
lintFrom(name, raw.from, problems);
|
|
697
|
+
if (!isNum(raw.in) || raw.in < 0) {
|
|
698
|
+
problems.push(
|
|
699
|
+
`${name}.in=${String(raw.in)} must be a number \u2265 0 (OUTPUT seconds)`
|
|
700
|
+
);
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (raw.out !== void 0 && (!isNum(raw.out) || raw.out <= raw.in)) {
|
|
704
|
+
problems.push(
|
|
705
|
+
`${name}.out=${String(raw.out)} must be a number past in (${raw.in}s); leave it out to hide the cursor to the end of the film`
|
|
706
|
+
);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
if (raw.in > filmEnd() + 1e-6)
|
|
710
|
+
warnings.push(
|
|
711
|
+
`${name} starts at ${raw.in}s, past the film's end (${filmEnd().toFixed(2)}s): it hides nothing`
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
665
716
|
for (const [i, seg] of (Array.isArray(doc.segments) ? doc.segments : []).entries()) {
|
|
666
717
|
const hold = seg.hold;
|
|
667
718
|
if (hold === void 0) continue;
|
|
@@ -4103,6 +4154,36 @@ import {
|
|
|
4103
4154
|
spanOutputExtent as spanOutputExtent3
|
|
4104
4155
|
} from "@vosjs/studio-core";
|
|
4105
4156
|
|
|
4157
|
+
// src/plugin/captionFit.ts
|
|
4158
|
+
var CAPTION_MAX = 3;
|
|
4159
|
+
var CAPTION_MIN = 1.2;
|
|
4160
|
+
var CAPTION_GAP = 0.1;
|
|
4161
|
+
function fitCaptions(items, slotOf, opts = {}) {
|
|
4162
|
+
const max = opts.max ?? CAPTION_MAX;
|
|
4163
|
+
const min = opts.min ?? CAPTION_MIN;
|
|
4164
|
+
const gap = opts.gap ?? CAPTION_GAP;
|
|
4165
|
+
const ordered = items.map((item) => ({ item, slot: slotOf(item) })).sort((a, b) => a.slot.start - b.slot.start);
|
|
4166
|
+
const kept = [];
|
|
4167
|
+
const dropped = [];
|
|
4168
|
+
let nextStart = Infinity;
|
|
4169
|
+
for (let i = ordered.length - 1; i >= 0; i--) {
|
|
4170
|
+
const { item, slot } = ordered[i];
|
|
4171
|
+
const clipRoom = slot.end - slot.start;
|
|
4172
|
+
const nextRoom = nextStart - gap - slot.start;
|
|
4173
|
+
const duration = Math.min(max, clipRoom, nextRoom);
|
|
4174
|
+
if (duration < min - 1e-9) {
|
|
4175
|
+
dropped.unshift({
|
|
4176
|
+
item,
|
|
4177
|
+
why: nextRoom < clipRoom ? `the next caption starts ${(nextStart - slot.start).toFixed(2)}s after it, too soon to read it (more time on its step, its ms, gives it room)` : `it plays in the last ${Math.max(0, clipRoom).toFixed(2)}s of its clip, too short to read it (a wait after its step gives it room)`
|
|
4178
|
+
});
|
|
4179
|
+
continue;
|
|
4180
|
+
}
|
|
4181
|
+
kept.unshift({ item, duration: +duration.toFixed(3) });
|
|
4182
|
+
nextStart = slot.start;
|
|
4183
|
+
}
|
|
4184
|
+
return { kept, dropped };
|
|
4185
|
+
}
|
|
4186
|
+
|
|
4106
4187
|
// src/plugin/moments.ts
|
|
4107
4188
|
import {
|
|
4108
4189
|
hasReplay,
|
|
@@ -4131,6 +4212,7 @@ var LOOP_DESTINATIONS = /* @__PURE__ */ new Set(["github-readme-loop"]);
|
|
|
4131
4212
|
var BED_ID = "bed";
|
|
4132
4213
|
var CLICK_ID_PREFIX = "click-";
|
|
4133
4214
|
var CAPTION_ID_PREFIX = "caption-";
|
|
4215
|
+
var CAPTION_BEAT_MAX = 3.5;
|
|
4134
4216
|
function houseCaption(id, text, start, duration) {
|
|
4135
4217
|
return {
|
|
4136
4218
|
id,
|
|
@@ -4215,6 +4297,19 @@ var outputLength = (doc) => ratedSegments4(doc).reduce((acc, s) => {
|
|
|
4215
4297
|
const rate = s.rate && s.rate > 0 ? s.rate : 1;
|
|
4216
4298
|
return acc + (s.out - s.in) / rate;
|
|
4217
4299
|
}, 0);
|
|
4300
|
+
function withEndCardCursorOff(doc, start) {
|
|
4301
|
+
const own = (doc.cursor.off ?? []).filter((r) => r.from !== END_CARD_FROM);
|
|
4302
|
+
return {
|
|
4303
|
+
...doc,
|
|
4304
|
+
cursor: {
|
|
4305
|
+
...doc.cursor,
|
|
4306
|
+
off: [
|
|
4307
|
+
...own,
|
|
4308
|
+
{ in: +Math.max(0, start).toFixed(3), from: END_CARD_FROM }
|
|
4309
|
+
]
|
|
4310
|
+
}
|
|
4311
|
+
};
|
|
4312
|
+
}
|
|
4218
4313
|
function templateWords(words2) {
|
|
4219
4314
|
const headline = (words2.headline ?? "").trim();
|
|
4220
4315
|
const kicker = (words2.kicker ?? "").trim();
|
|
@@ -4283,6 +4378,7 @@ function proposeMotion(input, opts) {
|
|
|
4283
4378
|
skipped.push(`${t.from}: ${anchorWord(t.at)} was not recorded`);
|
|
4284
4379
|
continue;
|
|
4285
4380
|
}
|
|
4381
|
+
const before = outputLength(doc);
|
|
4286
4382
|
const applied = applyTemplate(t.doc, doc, {
|
|
4287
4383
|
at: at2,
|
|
4288
4384
|
from: t.from,
|
|
@@ -4290,6 +4386,10 @@ function proposeMotion(input, opts) {
|
|
|
4290
4386
|
keys
|
|
4291
4387
|
});
|
|
4292
4388
|
doc = applied.doc;
|
|
4389
|
+
if (t.from === END_CARD_FROM) {
|
|
4390
|
+
const starts = (doc.overlays ?? []).filter((o) => o.from === END_CARD_FROM && o.start !== void 0).map((o) => o.start);
|
|
4391
|
+
doc = withEndCardCursorOff(doc, Math.min(before, ...starts));
|
|
4392
|
+
}
|
|
4293
4393
|
notes.push(`${t.from} at ${anchorWord(t.at)}`);
|
|
4294
4394
|
for (const n of applied.notes) skipped.push(`${t.from}: ${n}`);
|
|
4295
4395
|
}
|
|
@@ -4338,6 +4438,7 @@ function proposeMotion(input, opts) {
|
|
|
4338
4438
|
...doc.frame.anim ?? {},
|
|
4339
4439
|
exit: { kind: "recede", seconds: END_CARD_SECONDS }
|
|
4340
4440
|
};
|
|
4441
|
+
doc = withEndCardCursorOff(doc, endStart);
|
|
4341
4442
|
notes.push("end card");
|
|
4342
4443
|
} else {
|
|
4343
4444
|
skipped.push(
|
|
@@ -4352,22 +4453,30 @@ function proposeMotion(input, opts) {
|
|
|
4352
4453
|
if (opts.captions.length && !off(launch.captions)) {
|
|
4353
4454
|
const rated = ratedSegments4(doc);
|
|
4354
4455
|
const steps = doc.source.meta.steps ?? [];
|
|
4456
|
+
const beats = [];
|
|
4355
4457
|
for (const c of opts.captions) {
|
|
4356
4458
|
const step = steps.find(
|
|
4357
4459
|
(s) => c.id !== void 0 && s.id === c.id || s.step === c.step
|
|
4358
4460
|
);
|
|
4359
4461
|
if (!step || step.skipped) continue;
|
|
4360
4462
|
const t = stepOutputTime(rated, step, 0.2);
|
|
4361
|
-
if (t === null || t < 0 || t
|
|
4463
|
+
if (t === null || t < 0 || t >= footage) continue;
|
|
4464
|
+
beats.push({ caption: c, t: +t.toFixed(3) });
|
|
4465
|
+
}
|
|
4466
|
+
const fit = fitCaptions(beats, (b) => ({ start: b.t, end: footage }), {
|
|
4467
|
+
max: CAPTION_BEAT_MAX
|
|
4468
|
+
});
|
|
4469
|
+
for (const { item, why } of fit.dropped)
|
|
4470
|
+
skipped.push(`caption for step ${item.caption.step}: ${why}`);
|
|
4471
|
+
for (const { item, duration } of fit.kept)
|
|
4362
4472
|
captionClips.push(
|
|
4363
4473
|
houseCaption(
|
|
4364
|
-
`${CAPTION_ID_PREFIX}${
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4474
|
+
`${CAPTION_ID_PREFIX}${item.caption.step}`,
|
|
4475
|
+
item.caption.caption,
|
|
4476
|
+
item.t,
|
|
4477
|
+
duration
|
|
4368
4478
|
)
|
|
4369
4479
|
);
|
|
4370
|
-
}
|
|
4371
4480
|
if (captionClips.length) notes.push(`${captionClips.length} caption(s)`);
|
|
4372
4481
|
}
|
|
4373
4482
|
const overlays = [...kept, ...captionClips];
|
|
@@ -4452,6 +4561,10 @@ function destinationMechanics(d, doc) {
|
|
|
4452
4561
|
const keptFreezes = (doc.freeze ?? []).filter((f) => !f.from);
|
|
4453
4562
|
if (keptFreezes.length !== (doc.freeze ?? []).length)
|
|
4454
4563
|
set.push(`freeze=${JSON.stringify(keptFreezes)}`);
|
|
4564
|
+
const off2 = doc.cursor?.off ?? [];
|
|
4565
|
+
const keptOff = off2.filter((r) => !r.from);
|
|
4566
|
+
if (keptOff.length !== off2.length)
|
|
4567
|
+
set.push(`cursor.off=${JSON.stringify(keptOff)}`);
|
|
4455
4568
|
}
|
|
4456
4569
|
if (portrait) {
|
|
4457
4570
|
set.push("frame.fit=cover");
|
|
@@ -5879,14 +5992,14 @@ function chromeTree(rows, pid) {
|
|
|
5879
5992
|
}
|
|
5880
5993
|
function readChromeTree(pid) {
|
|
5881
5994
|
if (process.platform === "win32") return Promise.resolve(null);
|
|
5882
|
-
return new Promise((
|
|
5995
|
+
return new Promise((resolve22) => {
|
|
5883
5996
|
execFile2(
|
|
5884
5997
|
"ps",
|
|
5885
5998
|
["-A", "-o", "pid=", "-o", "ppid=", "-o", "command="],
|
|
5886
5999
|
{ maxBuffer: 16 * 1024 * 1024, timeout: 5e3 },
|
|
5887
6000
|
(err, stdout) => {
|
|
5888
|
-
if (err)
|
|
5889
|
-
else
|
|
6001
|
+
if (err) resolve22(null);
|
|
6002
|
+
else resolve22(chromeTree(parseProcessTable(stdout), pid));
|
|
5890
6003
|
}
|
|
5891
6004
|
);
|
|
5892
6005
|
});
|
|
@@ -5988,7 +6101,7 @@ function enterPressed() {
|
|
|
5988
6101
|
} };
|
|
5989
6102
|
const rl = createInterface({ input: process.stdin });
|
|
5990
6103
|
const pressed = new Promise(
|
|
5991
|
-
(
|
|
6104
|
+
(resolve22) => rl.once("line", () => resolve22())
|
|
5992
6105
|
);
|
|
5993
6106
|
return { pressed, close: () => rl.close() };
|
|
5994
6107
|
}
|
|
@@ -6161,14 +6274,14 @@ async function openSessionWindow(opts) {
|
|
|
6161
6274
|
const child = spawn(exe, args, { stdio: "ignore" });
|
|
6162
6275
|
let failed = null;
|
|
6163
6276
|
let code = null;
|
|
6164
|
-
const exited = new Promise((
|
|
6277
|
+
const exited = new Promise((resolve22) => {
|
|
6165
6278
|
child.once("exit", (c) => {
|
|
6166
6279
|
code = c;
|
|
6167
|
-
|
|
6280
|
+
resolve22();
|
|
6168
6281
|
});
|
|
6169
6282
|
child.once("error", (e) => {
|
|
6170
6283
|
failed = e;
|
|
6171
|
-
|
|
6284
|
+
resolve22();
|
|
6172
6285
|
});
|
|
6173
6286
|
});
|
|
6174
6287
|
try {
|
|
@@ -6507,13 +6620,58 @@ import {
|
|
|
6507
6620
|
import {
|
|
6508
6621
|
SetupEnvError,
|
|
6509
6622
|
SetupError,
|
|
6623
|
+
TEARDOWN_BOUND_MS,
|
|
6510
6624
|
parseHeaders,
|
|
6511
6625
|
resolveSetupText,
|
|
6512
6626
|
runSetup,
|
|
6627
|
+
scriptEnvNames,
|
|
6513
6628
|
setupEnvNames,
|
|
6629
|
+
teardownEnvProblem,
|
|
6630
|
+
teardownLine,
|
|
6514
6631
|
validateSetup
|
|
6515
6632
|
} from "@vosjs/render-core/record";
|
|
6516
6633
|
|
|
6634
|
+
// src/plugin/sessionExec.ts
|
|
6635
|
+
import { existsSync as existsSync10 } from "fs";
|
|
6636
|
+
import { resolve as resolve7 } from "path";
|
|
6637
|
+
import { pathToFileURL } from "url";
|
|
6638
|
+
var ExecScriptError = class extends Error {
|
|
6639
|
+
};
|
|
6640
|
+
async function loadExecScript(path) {
|
|
6641
|
+
const file = resolve7(path);
|
|
6642
|
+
if (!existsSync10(file)) throw new ExecScriptError(`no script at ${file}`);
|
|
6643
|
+
if (!/\.(m?js|cjs)$/.test(file))
|
|
6644
|
+
throw new ExecScriptError(
|
|
6645
|
+
`${path}: a script is a .mjs or .js module (compile TypeScript first)`
|
|
6646
|
+
);
|
|
6647
|
+
const mod = await import(pathToFileURL(file).href);
|
|
6648
|
+
if (typeof mod.default !== "function")
|
|
6649
|
+
throw new ExecScriptError(
|
|
6650
|
+
`${path} has no default export function: export default async ({ context, page, url }) => { \u2026 }`
|
|
6651
|
+
);
|
|
6652
|
+
return mod.default;
|
|
6653
|
+
}
|
|
6654
|
+
async function execInProfile(dir, script, opts) {
|
|
6655
|
+
return withProfile(
|
|
6656
|
+
dir,
|
|
6657
|
+
async (context2) => {
|
|
6658
|
+
const page = context2.pages()[0] ?? await context2.newPage();
|
|
6659
|
+
if (opts.url)
|
|
6660
|
+
await page.goto(opts.url, { waitUntil: "load", timeout: 3e4 });
|
|
6661
|
+
return await script({
|
|
6662
|
+
context: context2,
|
|
6663
|
+
page,
|
|
6664
|
+
name: opts.name,
|
|
6665
|
+
...opts.url ? { url: opts.url } : {}
|
|
6666
|
+
});
|
|
6667
|
+
},
|
|
6668
|
+
{
|
|
6669
|
+
headless: opts.headless ?? true,
|
|
6670
|
+
...opts.viewport ? { viewport: opts.viewport } : {}
|
|
6671
|
+
}
|
|
6672
|
+
);
|
|
6673
|
+
}
|
|
6674
|
+
|
|
6517
6675
|
// src/plugin/recordingCap.ts
|
|
6518
6676
|
import {
|
|
6519
6677
|
capReached,
|
|
@@ -6569,7 +6727,7 @@ async function encodeRecording(browser, takeDir, onProgress) {
|
|
|
6569
6727
|
}
|
|
6570
6728
|
|
|
6571
6729
|
// src/plugin/plan.ts
|
|
6572
|
-
import { existsSync as
|
|
6730
|
+
import { existsSync as existsSync11 } from "fs";
|
|
6573
6731
|
import { readFile as readFile9 } from "fs/promises";
|
|
6574
6732
|
import { join as join11 } from "path";
|
|
6575
6733
|
import {
|
|
@@ -6862,9 +7020,21 @@ function outputToSource(pieces, t) {
|
|
|
6862
7020
|
}
|
|
6863
7021
|
return pieces.at(-1)?.out ?? t;
|
|
6864
7022
|
}
|
|
7023
|
+
function footageOutputEnd(doc) {
|
|
7024
|
+
const own = {
|
|
7025
|
+
...doc,
|
|
7026
|
+
freeze: (doc.freeze ?? []).filter((f) => !f.from)
|
|
7027
|
+
};
|
|
7028
|
+
return ratedSegments6(own).reduce((acc, s) => {
|
|
7029
|
+
const rate = s.rate && s.rate > 0 ? s.rate : 1;
|
|
7030
|
+
return acc + (s.out - s.in) / rate;
|
|
7031
|
+
}, 0);
|
|
7032
|
+
}
|
|
6865
7033
|
function retimeOverlays(prev, next, report) {
|
|
6866
7034
|
const overlays = prev.overlays ?? [];
|
|
6867
7035
|
if (!overlays.length) return [];
|
|
7036
|
+
const oldEnd = footageOutputEnd(prev);
|
|
7037
|
+
const endShift = footageOutputEnd(next) - oldEnd;
|
|
6868
7038
|
const oldSteps = prev.source.meta.steps ?? [];
|
|
6869
7039
|
const newSteps = next.source.meta.steps ?? [];
|
|
6870
7040
|
const oldPieces = ratedSegments6(prev);
|
|
@@ -6916,6 +7086,10 @@ function retimeOverlays(prev, next, report) {
|
|
|
6916
7086
|
const moved = follow(o, at2, pin.press, placed.at);
|
|
6917
7087
|
return { ...moved, pin: { ...pin, press: +placed.at.toFixed(3) } };
|
|
6918
7088
|
}
|
|
7089
|
+
if (at2 >= oldEnd - 1e-6) {
|
|
7090
|
+
if (Math.abs(endShift) < 1e-6) return o;
|
|
7091
|
+
return { ...o, start: +Math.max(0, at2 + endShift).toFixed(3) };
|
|
7092
|
+
}
|
|
6919
7093
|
const under = stepUnder(
|
|
6920
7094
|
outputToSource(oldPieces, at2),
|
|
6921
7095
|
oldSteps,
|
|
@@ -6943,7 +7117,7 @@ function retimeOverlays(prev, next, report) {
|
|
|
6943
7117
|
var overlaps = (a, b) => a.in < b.out && b.in < a.out;
|
|
6944
7118
|
async function readDigestActivity(dir) {
|
|
6945
7119
|
const file = join11(dir, "digest", "digest.json");
|
|
6946
|
-
if (!
|
|
7120
|
+
if (!existsSync11(file)) return null;
|
|
6947
7121
|
try {
|
|
6948
7122
|
const d = JSON.parse(await readFile9(file, "utf8"));
|
|
6949
7123
|
return Array.isArray(d.activity) && d.activity.every((v) => typeof v === "number") ? d.activity : null;
|
|
@@ -7064,6 +7238,20 @@ async function planTake(dir, opts = {}) {
|
|
|
7064
7238
|
withPageSound(doc, meta.media);
|
|
7065
7239
|
if (prev.still !== void 0) doc.still = prev.still;
|
|
7066
7240
|
if (opts.still != null) doc.still = opts.still;
|
|
7241
|
+
if (prev.cursor?.off?.length) {
|
|
7242
|
+
const oldEnd = footageOutputEnd(prev);
|
|
7243
|
+
const shift = footageOutputEnd(doc) - oldEnd;
|
|
7244
|
+
doc.cursor = {
|
|
7245
|
+
...doc.cursor,
|
|
7246
|
+
off: prev.cursor.off.map(
|
|
7247
|
+
(r) => r.in >= oldEnd - 1e-6 && Math.abs(shift) > 1e-6 ? {
|
|
7248
|
+
...r,
|
|
7249
|
+
in: +Math.max(0, r.in + shift).toFixed(3),
|
|
7250
|
+
...r.out !== void 0 ? { out: +Math.max(0, r.out + shift).toFixed(3) } : {}
|
|
7251
|
+
} : { ...r }
|
|
7252
|
+
)
|
|
7253
|
+
};
|
|
7254
|
+
}
|
|
7067
7255
|
await writeJson(take.paths.doc, doc, true);
|
|
7068
7256
|
return {
|
|
7069
7257
|
doc,
|
|
@@ -7173,14 +7361,14 @@ async function planTake(dir, opts = {}) {
|
|
|
7173
7361
|
|
|
7174
7362
|
// src/plugin/sync.ts
|
|
7175
7363
|
import { createHash } from "crypto";
|
|
7176
|
-
import { existsSync as
|
|
7364
|
+
import { existsSync as existsSync13, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
7177
7365
|
import { readFile as readFile11 } from "fs/promises";
|
|
7178
7366
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
7179
7367
|
import { basename as basename2, join as join14 } from "path";
|
|
7180
7368
|
import { lowerToComposition as lowerToComposition3, migrateHostedDoc as migrateHostedDoc2 } from "@vosjs/studio-core";
|
|
7181
7369
|
|
|
7182
7370
|
// src/plugin/takeScript.ts
|
|
7183
|
-
import { existsSync as
|
|
7371
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7184
7372
|
import { readFile as readFile10, writeFile as writeFile7 } from "fs/promises";
|
|
7185
7373
|
import { readFileSync as readFileSync2 } from "fs";
|
|
7186
7374
|
import { basename, join as join12 } from "path";
|
|
@@ -7201,26 +7389,27 @@ function scriptToWrite(doc, hasLocal) {
|
|
|
7201
7389
|
function publicScript(script) {
|
|
7202
7390
|
const peers = publicPeers(script.peers);
|
|
7203
7391
|
if (peers) script = { ...script, peers };
|
|
7204
|
-
const setup
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7392
|
+
for (const key of ["setup", "teardown"]) {
|
|
7393
|
+
const steps = script[key];
|
|
7394
|
+
if (Array.isArray(steps))
|
|
7395
|
+
script = { ...script, [key]: steps.map(publicStep) };
|
|
7396
|
+
}
|
|
7397
|
+
return script;
|
|
7398
|
+
}
|
|
7399
|
+
function publicStep(step) {
|
|
7400
|
+
if (!step || typeof step !== "object") return step;
|
|
7401
|
+
const s = step;
|
|
7402
|
+
if (s.do === "type" && typeof s.text === "string")
|
|
7403
|
+
return { ...s, text: "[typed value not stored]" };
|
|
7404
|
+
if (s.do === "goto" && typeof s.url === "string") {
|
|
7405
|
+
try {
|
|
7406
|
+
const u = new URL(s.url);
|
|
7407
|
+
return { ...s, url: `${u.origin}${u.pathname}` };
|
|
7408
|
+
} catch {
|
|
7221
7409
|
return s;
|
|
7222
|
-
}
|
|
7223
|
-
}
|
|
7410
|
+
}
|
|
7411
|
+
}
|
|
7412
|
+
return s;
|
|
7224
7413
|
}
|
|
7225
7414
|
function publicPeers(peers) {
|
|
7226
7415
|
if (!Array.isArray(peers)) return null;
|
|
@@ -7233,20 +7422,20 @@ function publicPeers(peers) {
|
|
|
7233
7422
|
}
|
|
7234
7423
|
function embedScript(doc, dir) {
|
|
7235
7424
|
const path = join12(dir, "actions.json");
|
|
7236
|
-
if (!
|
|
7425
|
+
if (!existsSync12(path)) return;
|
|
7237
7426
|
const script = scriptFromText(readFileSync2(path, "utf8"));
|
|
7238
7427
|
if (script) doc.source.script = publicScript(script);
|
|
7239
7428
|
}
|
|
7240
7429
|
async function writeScriptBeside(dir, doc) {
|
|
7241
7430
|
const path = join12(dir, "actions.json");
|
|
7242
|
-
const text = scriptToWrite(doc,
|
|
7431
|
+
const text = scriptToWrite(doc, existsSync12(path));
|
|
7243
7432
|
if (text === null) return false;
|
|
7244
7433
|
await writeFile7(path, text, "utf8");
|
|
7245
7434
|
return true;
|
|
7246
7435
|
}
|
|
7247
7436
|
async function readScript(dir) {
|
|
7248
7437
|
const path = join12(dir, "actions.json");
|
|
7249
|
-
if (!
|
|
7438
|
+
if (!existsSync12(path)) return null;
|
|
7250
7439
|
return scriptFromText(await readFile10(path, "utf8"));
|
|
7251
7440
|
}
|
|
7252
7441
|
|
|
@@ -7737,7 +7926,7 @@ async function pushTake(dir, flags, r) {
|
|
|
7737
7926
|
throw new Error(`doc.json fails lint:
|
|
7738
7927
|
${lint.problems.join("\n ")}`);
|
|
7739
7928
|
}
|
|
7740
|
-
if (!
|
|
7929
|
+
if (!existsSync13(take.paths.recording)) {
|
|
7741
7930
|
throw new Error(
|
|
7742
7931
|
"no recording in this take \u2014 record it, or `vos pull --media` to bring it home"
|
|
7743
7932
|
);
|
|
@@ -7972,7 +8161,7 @@ async function pullTake(dir, flags, r) {
|
|
|
7972
8161
|
};
|
|
7973
8162
|
}
|
|
7974
8163
|
let media2;
|
|
7975
|
-
if (flags.media &&
|
|
8164
|
+
if (flags.media && existsSync13(join14(dir, "doc.json"))) {
|
|
7976
8165
|
const local = JSON.parse(
|
|
7977
8166
|
await readFile11(join14(dir, "doc.json"), "utf8")
|
|
7978
8167
|
);
|
|
@@ -8035,7 +8224,7 @@ async function pullTake(dir, flags, r) {
|
|
|
8035
8224
|
const hostedDoc = migrateHostedDoc2(docRes.json);
|
|
8036
8225
|
const doc = hostedDoc;
|
|
8037
8226
|
const localRecording = takeMediaFile(dir, "recording", RECORDING_NAME);
|
|
8038
|
-
if (
|
|
8227
|
+
if (existsSync13(localRecording)) {
|
|
8039
8228
|
doc.source.videoKey = basename2(localRecording);
|
|
8040
8229
|
}
|
|
8041
8230
|
await writeJson(join14(dir, "doc.json"), doc, true);
|
|
@@ -8111,7 +8300,7 @@ async function pushTakeClaimable(dir, flags, r) {
|
|
|
8111
8300
|
);
|
|
8112
8301
|
}
|
|
8113
8302
|
const path = join14(dir, file);
|
|
8114
|
-
const now =
|
|
8303
|
+
const now = existsSync13(path) ? await fileSha256(path) : null;
|
|
8115
8304
|
if (now !== origin2.sha256) {
|
|
8116
8305
|
throw new PushRefusedError(
|
|
8117
8306
|
`${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`
|
|
@@ -8160,7 +8349,7 @@ async function pushTakeClaimable(dir, flags, r) {
|
|
|
8160
8349
|
}
|
|
8161
8350
|
|
|
8162
8351
|
// src/plugin/program.ts
|
|
8163
|
-
import { existsSync as
|
|
8352
|
+
import { existsSync as existsSync15 } from "fs";
|
|
8164
8353
|
import { mkdir as mkdir7, readFile as readFile12 } from "fs/promises";
|
|
8165
8354
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
8166
8355
|
import { basename as basename4, dirname as dirname2, join as join15 } from "path";
|
|
@@ -8173,12 +8362,12 @@ import { lowerProgramDoc, migrateHostedDoc as migrateHostedDoc3 } from "@vosjs/s
|
|
|
8173
8362
|
|
|
8174
8363
|
// src/plugin/programFiles.ts
|
|
8175
8364
|
import { basename as basename3, extname as extname2, relative as relative2 } from "path";
|
|
8176
|
-
import { existsSync as
|
|
8365
|
+
import { existsSync as existsSync14, realpathSync as realpathSync2 } from "fs";
|
|
8177
8366
|
function isLocalName(value) {
|
|
8178
8367
|
if (/^(https?:|data:|blob:|asset:|media:)/.test(value)) return false;
|
|
8179
8368
|
if (value.startsWith("$")) return false;
|
|
8180
8369
|
if (value.startsWith("<svg") || value.startsWith("<?xml")) return false;
|
|
8181
|
-
if (value.startsWith("/")) return
|
|
8370
|
+
if (value.startsWith("/")) return existsSync14(value);
|
|
8182
8371
|
return true;
|
|
8183
8372
|
}
|
|
8184
8373
|
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;
|
|
@@ -8354,7 +8543,7 @@ function preflightConfig(parsed) {
|
|
|
8354
8543
|
function resolveConfigPath(target) {
|
|
8355
8544
|
if (target.endsWith(".json")) return target;
|
|
8356
8545
|
const inDir = join15(target, "config.json");
|
|
8357
|
-
if (
|
|
8546
|
+
if (existsSync15(inDir)) return inDir;
|
|
8358
8547
|
throw new UsageError(
|
|
8359
8548
|
`${target} has no config.json (and is not a take \u2014 no doc.json)`
|
|
8360
8549
|
);
|
|
@@ -8912,7 +9101,7 @@ async function cmdPullProgram(argv) {
|
|
|
8912
9101
|
throw new Error(apiError(`fetch head config for ${vosId}`, cfg));
|
|
8913
9102
|
const configPath = join15(dir, "config.json");
|
|
8914
9103
|
let backedUp = false;
|
|
8915
|
-
if (
|
|
9104
|
+
if (existsSync15(configPath)) {
|
|
8916
9105
|
await writeFileSafe(
|
|
8917
9106
|
join15(dir, "config.backup.json"),
|
|
8918
9107
|
await readFile12(configPath)
|
|
@@ -9058,7 +9247,7 @@ function storedProgramConfig(config, programDoc) {
|
|
|
9058
9247
|
}
|
|
9059
9248
|
async function readProgramDoc(dir, config) {
|
|
9060
9249
|
const docPath = join15(dir, "doc.json");
|
|
9061
|
-
if (!
|
|
9250
|
+
if (!existsSync15(docPath)) return null;
|
|
9062
9251
|
const parsed = JSON.parse(await readFile12(docPath, "utf8"));
|
|
9063
9252
|
if (typeof parsed !== "object" || parsed === null || "source" in parsed)
|
|
9064
9253
|
return null;
|
|
@@ -9069,7 +9258,7 @@ async function readProgramDoc(dir, config) {
|
|
|
9069
9258
|
async function setProgramStill(dir, t) {
|
|
9070
9259
|
const docPath = join15(dir, "doc.json");
|
|
9071
9260
|
let doc = { program: {}, audio: [] };
|
|
9072
|
-
if (
|
|
9261
|
+
if (existsSync15(docPath)) {
|
|
9073
9262
|
const parsed = JSON.parse(await readFile12(docPath, "utf8"));
|
|
9074
9263
|
if (!parsed || typeof parsed !== "object" || "source" in parsed)
|
|
9075
9264
|
throw new UsageError(
|
|
@@ -9624,7 +9813,7 @@ async function cmdAsset(argv) {
|
|
|
9624
9813
|
}
|
|
9625
9814
|
|
|
9626
9815
|
// src/plugin/remove.ts
|
|
9627
|
-
import { existsSync as
|
|
9816
|
+
import { existsSync as existsSync16, rmSync as rmSync2, statSync as statSync3 } from "fs";
|
|
9628
9817
|
import { join as join16 } from "path";
|
|
9629
9818
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
9630
9819
|
|
|
@@ -9804,7 +9993,7 @@ async function cmdDelete(argv) {
|
|
|
9804
9993
|
api: strFlag(flags, "api")
|
|
9805
9994
|
}).replace(/\/+$/, "");
|
|
9806
9995
|
const key = requireCredential(strFlag(flags, "key"));
|
|
9807
|
-
const dir =
|
|
9996
|
+
const dir = existsSync16(target) && statSync3(target).isDirectory() ? target : null;
|
|
9808
9997
|
const vosId = dir ? readSyncState(dir)?.vosId : parseVosId(target);
|
|
9809
9998
|
if (!vosId)
|
|
9810
9999
|
throw new UsageError(
|
|
@@ -9872,14 +10061,14 @@ async function cmdDelete(argv) {
|
|
|
9872
10061
|
}
|
|
9873
10062
|
function trackingCwd(vosId) {
|
|
9874
10063
|
const cwd = process.cwd();
|
|
9875
|
-
if (!
|
|
10064
|
+
if (!existsSync16(join16(cwd, SYNC_STATE_NAME))) return null;
|
|
9876
10065
|
return readSyncState(cwd)?.vosId === vosId ? cwd : null;
|
|
9877
10066
|
}
|
|
9878
10067
|
|
|
9879
10068
|
// src/plugin/buildProgram.ts
|
|
9880
10069
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
9881
|
-
import { basename as basename6, dirname as dirname3, join as join17, resolve as
|
|
9882
|
-
import { pathToFileURL } from "url";
|
|
10070
|
+
import { basename as basename6, dirname as dirname3, join as join17, resolve as resolve8 } from "path";
|
|
10071
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
9883
10072
|
import { parse } from "acorn";
|
|
9884
10073
|
import { fullAncestor } from "acorn-walk";
|
|
9885
10074
|
var FUNCTION_KEYS = [
|
|
@@ -10078,9 +10267,9 @@ async function cmdBuild(argv) {
|
|
|
10078
10267
|
if (!source)
|
|
10079
10268
|
throw new UsageError("vos build <program.mjs> [--out config.json] [--json]");
|
|
10080
10269
|
const r = createReporter(flags.json === true);
|
|
10081
|
-
const file =
|
|
10270
|
+
const file = resolve8(source);
|
|
10082
10271
|
const text = readFileSync5(file, "utf8");
|
|
10083
|
-
const mod = await import(`${
|
|
10272
|
+
const mod = await import(`${pathToFileURL2(file).href}?t=${Date.now()}`);
|
|
10084
10273
|
const program = mod.default ?? mod.program;
|
|
10085
10274
|
if (!program || typeof program !== "object")
|
|
10086
10275
|
throw new UsageError(
|
|
@@ -10095,7 +10284,7 @@ async function cmdBuild(argv) {
|
|
|
10095
10284
|
);
|
|
10096
10285
|
return EXIT_ERROR;
|
|
10097
10286
|
}
|
|
10098
|
-
const out =
|
|
10287
|
+
const out = resolve8(
|
|
10099
10288
|
strFlag(flags, "out") ?? join17(dirname3(file), "config.json")
|
|
10100
10289
|
);
|
|
10101
10290
|
writeFileSync2(out, JSON.stringify(built.config, null, 2) + "\n");
|
|
@@ -10113,8 +10302,8 @@ async function cmdBuild(argv) {
|
|
|
10113
10302
|
|
|
10114
10303
|
// src/plugin/compare.ts
|
|
10115
10304
|
import { spawnSync } from "child_process";
|
|
10116
|
-
import { existsSync as
|
|
10117
|
-
import { dirname as dirname4, join as join18, resolve as
|
|
10305
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync2, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
10306
|
+
import { dirname as dirname4, join as join18, resolve as resolve9 } from "path";
|
|
10118
10307
|
var BOOLEAN_FLAGS7 = /* @__PURE__ */ new Set(["json", "help"]);
|
|
10119
10308
|
function sampleTimes(duration, every) {
|
|
10120
10309
|
const step = every > 0 ? every : 1;
|
|
@@ -10175,13 +10364,13 @@ async function cmdCompare(argv) {
|
|
|
10175
10364
|
);
|
|
10176
10365
|
requireFfmpeg();
|
|
10177
10366
|
const r = createReporter(flags.json === true);
|
|
10178
|
-
const ref =
|
|
10367
|
+
const ref = resolve9(against);
|
|
10179
10368
|
const loaded = await loadVosConfig(source);
|
|
10180
10369
|
for (const w of loaded.warnings) r.log(`note: ${w}`);
|
|
10181
10370
|
const config = loaded.config;
|
|
10182
10371
|
const duration = configDuration(config) ?? 5;
|
|
10183
10372
|
const assets = await programAssets(config, {
|
|
10184
|
-
baseDir: /^https?:\/\//.test(source) ? null :
|
|
10373
|
+
baseDir: /^https?:\/\//.test(source) ? null : existsSync17(source) && statSync4(source).isDirectory() ? source : dirname4(source),
|
|
10185
10374
|
origin: platformOrigin({}),
|
|
10186
10375
|
key: resolveCredential(),
|
|
10187
10376
|
log: (line) => r.log(line)
|
|
@@ -10197,7 +10386,7 @@ async function cmdCompare(argv) {
|
|
|
10197
10386
|
const threshold = Number(strFlag(flags, "threshold") ?? 0.95);
|
|
10198
10387
|
const timesRaw = strFlag(flags, "times");
|
|
10199
10388
|
const times = timesRaw ? parseTimes(timesRaw, duration) : sampleTimes(duration, Number(strFlag(flags, "every") ?? 1));
|
|
10200
|
-
const outDir =
|
|
10389
|
+
const outDir = resolve9(strFlag(flags, "out") ?? "compare");
|
|
10201
10390
|
mkdirSync2(outDir, { recursive: true });
|
|
10202
10391
|
const browser = await launchBrowser();
|
|
10203
10392
|
const scores = [];
|
|
@@ -10300,25 +10489,25 @@ async function cmdCompare(argv) {
|
|
|
10300
10489
|
}
|
|
10301
10490
|
|
|
10302
10491
|
// src/plugin/port.ts
|
|
10303
|
-
import { existsSync as
|
|
10304
|
-
import { dirname as dirname7, join as join22, resolve as
|
|
10305
|
-
import { pathToFileURL as
|
|
10492
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
10493
|
+
import { dirname as dirname7, join as join22, resolve as resolve12 } from "path";
|
|
10494
|
+
import { pathToFileURL as pathToFileURL4 } from "url";
|
|
10306
10495
|
|
|
10307
10496
|
// src/plugin/portInventory.ts
|
|
10308
10497
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
10309
10498
|
import {
|
|
10310
|
-
existsSync as
|
|
10499
|
+
existsSync as existsSync19,
|
|
10311
10500
|
mkdirSync as mkdirSync3,
|
|
10312
10501
|
readFileSync as readFileSync7,
|
|
10313
10502
|
readdirSync as readdirSync4,
|
|
10314
10503
|
statSync as statSync6
|
|
10315
10504
|
} from "fs";
|
|
10316
|
-
import { dirname as dirname5, join as join20, relative as relative3, resolve as
|
|
10317
|
-
import { pathToFileURL as
|
|
10505
|
+
import { dirname as dirname5, join as join20, relative as relative3, resolve as resolve10 } from "path";
|
|
10506
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
10318
10507
|
import { findFontFamily as findFontFamily3 } from "@vosjs/shared";
|
|
10319
10508
|
|
|
10320
10509
|
// src/plugin/portRemotion.ts
|
|
10321
|
-
import { existsSync as
|
|
10510
|
+
import { existsSync as existsSync18, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
|
|
10322
10511
|
import { createServer as createServer2 } from "http";
|
|
10323
10512
|
import { createRequire } from "module";
|
|
10324
10513
|
import { basename as basename7, extname as extname3, join as join19 } from "path";
|
|
@@ -10394,7 +10583,7 @@ function sourceTexts(dir) {
|
|
|
10394
10583
|
}
|
|
10395
10584
|
};
|
|
10396
10585
|
const src = join19(dir, "src");
|
|
10397
|
-
walk(
|
|
10586
|
+
walk(existsSync18(src) ? src : dir, 0);
|
|
10398
10587
|
return out;
|
|
10399
10588
|
}
|
|
10400
10589
|
function entryPoint(dir) {
|
|
@@ -10406,7 +10595,7 @@ function entryPoint(dir) {
|
|
|
10406
10595
|
"remotion/index.ts",
|
|
10407
10596
|
"remotion/index.tsx"
|
|
10408
10597
|
])
|
|
10409
|
-
if (
|
|
10598
|
+
if (existsSync18(join19(dir, p))) return join19(dir, p);
|
|
10410
10599
|
return null;
|
|
10411
10600
|
}
|
|
10412
10601
|
var TYPES = {
|
|
@@ -10499,7 +10688,7 @@ async function readRemotion(dir, log) {
|
|
|
10499
10688
|
const server = createServer2((rq, rs) => {
|
|
10500
10689
|
const path = decodeURIComponent(new URL(rq.url ?? "/", "http://x").pathname);
|
|
10501
10690
|
let f = join19(out, path);
|
|
10502
|
-
if (!f.startsWith(out) || !
|
|
10691
|
+
if (!f.startsWith(out) || !existsSync18(f) || statSync5(f).isDirectory())
|
|
10503
10692
|
f = join19(out, "index.html");
|
|
10504
10693
|
rs.writeHead(200, {
|
|
10505
10694
|
"content-type": TYPES[extname3(f)] ?? "application/octet-stream"
|
|
@@ -10602,7 +10791,7 @@ async function readRemotion(dir, log) {
|
|
|
10602
10791
|
const publicDir = join19(dir, "public");
|
|
10603
10792
|
const media = tree.audio.map((a) => {
|
|
10604
10793
|
const name = basename7(a.src.split("?")[0]);
|
|
10605
|
-
const local =
|
|
10794
|
+
const local = existsSync18(join19(publicDir, name)) ? `public/${name}` : a.src;
|
|
10606
10795
|
return {
|
|
10607
10796
|
kind: "audio",
|
|
10608
10797
|
src: local,
|
|
@@ -10643,9 +10832,9 @@ async function readRemotion(dir, log) {
|
|
|
10643
10832
|
|
|
10644
10833
|
// src/plugin/portInventory.ts
|
|
10645
10834
|
function detectEngine(dir) {
|
|
10646
|
-
if (
|
|
10835
|
+
if (existsSync19(join20(dir, "hyperframes.json"))) return "hyperframes";
|
|
10647
10836
|
const pkg = join20(dir, "package.json");
|
|
10648
|
-
if (
|
|
10837
|
+
if (existsSync19(pkg)) {
|
|
10649
10838
|
try {
|
|
10650
10839
|
const p = JSON.parse(readFileSync7(pkg, "utf8"));
|
|
10651
10840
|
const deps = { ...p.dependencies ?? {}, ...p.devDependencies ?? {} };
|
|
@@ -10659,13 +10848,13 @@ function detectEngine(dir) {
|
|
|
10659
10848
|
return "page";
|
|
10660
10849
|
}
|
|
10661
10850
|
function sourcePage(source) {
|
|
10662
|
-
if (!
|
|
10851
|
+
if (!existsSync19(source)) return null;
|
|
10663
10852
|
if (statSync6(source).isFile()) return /\.html?$/i.test(source) ? source : null;
|
|
10664
10853
|
const index = join20(source, "index.html");
|
|
10665
|
-
return
|
|
10854
|
+
return existsSync19(index) ? index : null;
|
|
10666
10855
|
}
|
|
10667
10856
|
function findRender(dir) {
|
|
10668
|
-
const videos = (d) =>
|
|
10857
|
+
const videos = (d) => existsSync19(d) ? readdirSync4(d).filter((f) => /\.(mp4|webm|mov)$/i.test(f)).map((f) => join20(d, f)) : [];
|
|
10669
10858
|
for (const d of [join20(dir, "renders"), join20(dir, "out"), dir]) {
|
|
10670
10859
|
const found = videos(d);
|
|
10671
10860
|
if (found.length) return found[0];
|
|
@@ -10936,8 +11125,8 @@ var SEEK_AND_READ = `((args) => {
|
|
|
10936
11125
|
return out
|
|
10937
11126
|
})`;
|
|
10938
11127
|
async function readInventory(source, opts) {
|
|
10939
|
-
const abs =
|
|
10940
|
-
const dir =
|
|
11128
|
+
const abs = resolve10(source);
|
|
11129
|
+
const dir = existsSync19(abs) && statSync6(abs).isDirectory() ? abs : dirname5(abs);
|
|
10941
11130
|
const engine = detectEngine(dir);
|
|
10942
11131
|
const page = sourcePage(abs);
|
|
10943
11132
|
const render = opts.render === void 0 ? findRender(dir) : opts.render;
|
|
@@ -10968,7 +11157,7 @@ async function readInventory(source, opts) {
|
|
|
10968
11157
|
});
|
|
10969
11158
|
const p = await ctx.newPage();
|
|
10970
11159
|
await p.addInitScript("window.__timelines = window.__timelines || {}");
|
|
10971
|
-
await p.goto(
|
|
11160
|
+
await p.goto(pathToFileURL3(page).href, { waitUntil: "load" });
|
|
10972
11161
|
await p.evaluate("document.fonts && document.fonts.ready");
|
|
10973
11162
|
await p.waitForTimeout(300);
|
|
10974
11163
|
read = await p.evaluate(READ_PAGE);
|
|
@@ -11103,8 +11292,8 @@ async function readInventory(source, opts) {
|
|
|
11103
11292
|
|
|
11104
11293
|
// src/plugin/portScaffold.ts
|
|
11105
11294
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
11106
|
-
import { copyFileSync, existsSync as
|
|
11107
|
-
import { basename as basename8, dirname as dirname6, join as join21, resolve as
|
|
11295
|
+
import { copyFileSync, existsSync as existsSync20, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
11296
|
+
import { basename as basename8, dirname as dirname6, join as join21, resolve as resolve11 } from "path";
|
|
11108
11297
|
import { findFontFamily as findFontFamily4, fontFaceUrl, nearestFontWeight } from "@vosjs/shared";
|
|
11109
11298
|
|
|
11110
11299
|
// src/plugin/painterLib.ts
|
|
@@ -11513,9 +11702,9 @@ function planScaffold(inv, opts) {
|
|
|
11513
11702
|
const audio = [];
|
|
11514
11703
|
const copies = [];
|
|
11515
11704
|
inv.media.filter((m) => m.kind === "audio" && m.src).forEach((m, i) => {
|
|
11516
|
-
const from =
|
|
11705
|
+
const from = resolve11(opts.sourceDir, m.src);
|
|
11517
11706
|
const name = basename8(from);
|
|
11518
|
-
if (!
|
|
11707
|
+
if (!existsSync20(from)) {
|
|
11519
11708
|
report.push(
|
|
11520
11709
|
`- the audio ${m.src} is not in the source folder; add it to doc.json by hand`
|
|
11521
11710
|
);
|
|
@@ -11754,11 +11943,11 @@ async function inventory(argv) {
|
|
|
11754
11943
|
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
11755
11944
|
const source = positionals[0] ?? ".";
|
|
11756
11945
|
const r = createReporter(flags.json === true);
|
|
11757
|
-
const outDir =
|
|
11946
|
+
const outDir = resolve12(strFlag(flags, "out") ?? "port");
|
|
11758
11947
|
mkdirSync5(outDir, { recursive: true });
|
|
11759
11948
|
const renderFlag = strFlag(flags, "render");
|
|
11760
11949
|
const inv = await readInventory(source, {
|
|
11761
|
-
render: renderFlag === void 0 ? void 0 :
|
|
11950
|
+
render: renderFlag === void 0 ? void 0 : resolve12(renderFlag),
|
|
11762
11951
|
outDir,
|
|
11763
11952
|
log: (l) => r.log(`note: ${l}`)
|
|
11764
11953
|
});
|
|
@@ -11794,13 +11983,13 @@ function summary(inv) {
|
|
|
11794
11983
|
async function scaffold(argv) {
|
|
11795
11984
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS8);
|
|
11796
11985
|
const r = createReporter(flags.json === true);
|
|
11797
|
-
const from =
|
|
11798
|
-
if (!
|
|
11986
|
+
const from = resolve12(strFlag(flags, "from") ?? join22("port", "inventory.json"));
|
|
11987
|
+
if (!existsSync21(from))
|
|
11799
11988
|
throw new UsageError(
|
|
11800
11989
|
`no inventory at ${from}: run npx vos port inventory <source> first`
|
|
11801
11990
|
);
|
|
11802
11991
|
const inv = JSON.parse(readFileSync8(from, "utf8"));
|
|
11803
|
-
const outDir =
|
|
11992
|
+
const outDir = resolve12(
|
|
11804
11993
|
strFlag(flags, "out") ?? join22(dirname7(from), "program")
|
|
11805
11994
|
);
|
|
11806
11995
|
const plan = planScaffold(inv, {
|
|
@@ -11809,7 +11998,7 @@ async function scaffold(argv) {
|
|
|
11809
11998
|
});
|
|
11810
11999
|
writeScaffold(plan, outDir);
|
|
11811
12000
|
const programFile = join22(outDir, "program.mjs");
|
|
11812
|
-
const mod = await import(`${
|
|
12001
|
+
const mod = await import(`${pathToFileURL4(programFile).href}?t=${Date.now()}`);
|
|
11813
12002
|
const built = buildProgram(mod.default, "program.mjs", plan.program);
|
|
11814
12003
|
for (const p of built.problems) r.log(`error ${p.fn}: ${p.message}`);
|
|
11815
12004
|
if (built.problems.length) return EXIT_ERROR;
|
|
@@ -11825,7 +12014,7 @@ async function scaffold(argv) {
|
|
|
11825
12014
|
}
|
|
11826
12015
|
function scaffoldSourceDir(inv) {
|
|
11827
12016
|
if (inv.root) return inv.root;
|
|
11828
|
-
return inv.source.endsWith(".html") ? dirname7(
|
|
12017
|
+
return inv.source.endsWith(".html") ? dirname7(resolve12(inv.source)) : resolve12(inv.source);
|
|
11829
12018
|
}
|
|
11830
12019
|
|
|
11831
12020
|
// src/plugin/recipe.ts
|
|
@@ -11917,7 +12106,7 @@ async function cmdRecipe(argv) {
|
|
|
11917
12106
|
|
|
11918
12107
|
// src/plugin/brand.ts
|
|
11919
12108
|
import { writeFile as writeFile8 } from "fs/promises";
|
|
11920
|
-
import { resolve as
|
|
12109
|
+
import { resolve as resolve13 } from "path";
|
|
11921
12110
|
import { parseFrontmatter as parseFrontmatter2 } from "@vosjs/shared/frontmatter";
|
|
11922
12111
|
import { lookKindForGround } from "@vosjs/studio-core";
|
|
11923
12112
|
var HEX_RE = /#(?:[0-9a-f]{6}|[0-9a-f]{3})\b/gi;
|
|
@@ -12238,7 +12427,7 @@ async function cmdBrand(argv) {
|
|
|
12238
12427
|
const refused = fetchRefusal(url);
|
|
12239
12428
|
if (refused) throw new UsageError(refused);
|
|
12240
12429
|
const r = createReporter(flags.json === true);
|
|
12241
|
-
const out =
|
|
12430
|
+
const out = resolve13(strFlag(flags, "out") ?? "BRAND.md");
|
|
12242
12431
|
r.log(`reading ${origin}/design.md and /llms.txt\u2026`);
|
|
12243
12432
|
const [designText, llmsText] = await Promise.all([
|
|
12244
12433
|
fetchText(`${origin}/design.md`),
|
|
@@ -12290,25 +12479,31 @@ async function cmdBrand(argv) {
|
|
|
12290
12479
|
}
|
|
12291
12480
|
|
|
12292
12481
|
// src/plugin/hostedRecord.ts
|
|
12293
|
-
import { setupEnvNames as setupEnvNames2 } from "@vosjs/render-core/record";
|
|
12482
|
+
import { scriptEnvNames as scriptEnvNames2, setupEnvNames as setupEnvNames2 } from "@vosjs/render-core/record";
|
|
12294
12483
|
var POLL_MS = 3e3;
|
|
12295
12484
|
var DEFAULT_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
12296
12485
|
function hostedEnv(actions, shell) {
|
|
12297
12486
|
const env = {};
|
|
12298
12487
|
const missing = [];
|
|
12299
|
-
for (const name of
|
|
12488
|
+
for (const name of scriptEnvNames2(actions)) {
|
|
12300
12489
|
const v = shell[name];
|
|
12301
12490
|
if (v === void 0 || v === "") missing.push(name);
|
|
12302
12491
|
else env[name] = v;
|
|
12303
12492
|
}
|
|
12304
12493
|
return { env, missing };
|
|
12305
12494
|
}
|
|
12495
|
+
function envReaders(actions) {
|
|
12496
|
+
const setup = setupEnvNames2(actions.setup).length > 0;
|
|
12497
|
+
const teardown = setupEnvNames2(actions.teardown).length > 0;
|
|
12498
|
+
if (setup && teardown) return "the setup and teardown read";
|
|
12499
|
+
return teardown ? "the teardown reads" : "the setup reads";
|
|
12500
|
+
}
|
|
12306
12501
|
function envNamesNotice(actions) {
|
|
12307
|
-
const names =
|
|
12502
|
+
const names = scriptEnvNames2(actions);
|
|
12308
12503
|
if (names.length === 0) return null;
|
|
12309
12504
|
return {
|
|
12310
12505
|
names,
|
|
12311
|
-
line:
|
|
12506
|
+
line: `${envReaders(actions)} ${names.length === 1 ? "this environment variable" : "these environment variables"} from this shell: ${names.join(", ")} (names only; a value is never printed or stored)`
|
|
12312
12507
|
};
|
|
12313
12508
|
}
|
|
12314
12509
|
function announceEnvNames(actions, r) {
|
|
@@ -12405,7 +12600,7 @@ import { urlSchemeProblem as urlSchemeProblem2 } from "@vosjs/render-core/record
|
|
|
12405
12600
|
|
|
12406
12601
|
// src/plugin/agentBrowser.ts
|
|
12407
12602
|
import { writeFile as writeFile9 } from "fs/promises";
|
|
12408
|
-
import { resolve as
|
|
12603
|
+
import { resolve as resolve14 } from "path";
|
|
12409
12604
|
function parseAgentBrowserLog(text) {
|
|
12410
12605
|
const records = [];
|
|
12411
12606
|
const problems = [];
|
|
@@ -12876,7 +13071,7 @@ async function cmdActions(argv) {
|
|
|
12876
13071
|
);
|
|
12877
13072
|
}
|
|
12878
13073
|
const r = createReporter(flags.json === true);
|
|
12879
|
-
const out =
|
|
13074
|
+
const out = resolve14(strFlag(flags, "out") ?? "actions.json");
|
|
12880
13075
|
const viewportFlag = strFlag(flags, "viewport");
|
|
12881
13076
|
let viewport;
|
|
12882
13077
|
if (viewportFlag) {
|
|
@@ -12885,7 +13080,7 @@ async function cmdActions(argv) {
|
|
|
12885
13080
|
viewport = { width: Number(m[1]), height: Number(m[2]) };
|
|
12886
13081
|
}
|
|
12887
13082
|
const { readFile: readFile19 } = await import("fs/promises");
|
|
12888
|
-
const text = await readFile19(
|
|
13083
|
+
const text = await readFile19(resolve14(input), "utf8");
|
|
12889
13084
|
const { records, problems } = parseAgentBrowserLog(text);
|
|
12890
13085
|
for (const p of problems) r.log(` ${p}`);
|
|
12891
13086
|
const result = convertAgentBrowser(records, {
|
|
@@ -12923,8 +13118,8 @@ function summary2(result) {
|
|
|
12923
13118
|
async function cmdActionsScript(input, flags) {
|
|
12924
13119
|
const r = createReporter(flags.json === true);
|
|
12925
13120
|
const { readFile: readFile19 } = await import("fs/promises");
|
|
12926
|
-
const { shotList, formatShotList } = await import("./shotList-
|
|
12927
|
-
const raw = JSON.parse(await readFile19(
|
|
13121
|
+
const { shotList, formatShotList } = await import("./shotList-PL7XP6AC.js");
|
|
13122
|
+
const raw = JSON.parse(await readFile19(resolve14(input), "utf8"));
|
|
12928
13123
|
const errors = validateActions(raw);
|
|
12929
13124
|
if (errors.length) throw new UsageError(`${input}:
|
|
12930
13125
|
${errors.join("\n ")}`);
|
|
@@ -12934,9 +13129,9 @@ async function cmdActionsScript(input, flags) {
|
|
|
12934
13129
|
}
|
|
12935
13130
|
|
|
12936
13131
|
// src/plugin/ingest.ts
|
|
12937
|
-
import { existsSync as
|
|
13132
|
+
import { existsSync as existsSync22 } from "fs";
|
|
12938
13133
|
import { copyFile, mkdir as mkdir8, readFile as readFile13, stat as stat2 } from "fs/promises";
|
|
12939
|
-
import { basename as basename10, join as join23, resolve as
|
|
13134
|
+
import { basename as basename10, join as join23, resolve as resolve15 } from "path";
|
|
12940
13135
|
|
|
12941
13136
|
// src/plugin/traceInflate.ts
|
|
12942
13137
|
import { inflateRawSync } from "zlib";
|
|
@@ -13099,10 +13294,10 @@ async function cmdIngest(argv) {
|
|
|
13099
13294
|
throw new UsageError(
|
|
13100
13295
|
"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]"
|
|
13101
13296
|
);
|
|
13102
|
-
const videoPath =
|
|
13103
|
-
if (!
|
|
13297
|
+
const videoPath = resolve15(source);
|
|
13298
|
+
if (!existsSync22(videoPath)) throw new UsageError(`${source}: no such file`);
|
|
13104
13299
|
const r = createReporter(flags.json === true);
|
|
13105
|
-
const outDir =
|
|
13300
|
+
const outDir = resolve15(strFlag(flags, "out") ?? "take");
|
|
13106
13301
|
const offsetMs = numFlag(flags, "offset", 0);
|
|
13107
13302
|
const viewportFlag = parseViewport(strFlag(flags, "viewport"));
|
|
13108
13303
|
const cursorPath = strFlag(flags, "cursor");
|
|
@@ -13124,10 +13319,10 @@ async function cmdIngest(argv) {
|
|
|
13124
13319
|
}
|
|
13125
13320
|
let trace = null;
|
|
13126
13321
|
if (cursorPath) {
|
|
13127
|
-
trace = await readTraceFile(
|
|
13322
|
+
trace = await readTraceFile(resolve15(cursorPath), offsetMs);
|
|
13128
13323
|
for (const n of trace.notes) r.log(`note: ${n}`);
|
|
13129
13324
|
}
|
|
13130
|
-
if (
|
|
13325
|
+
if (existsSync22(join23(outDir, "meta.json"))) {
|
|
13131
13326
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
13132
13327
|
r.log(
|
|
13133
13328
|
`note: re-ingesting into ${outDir}; kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? `; apply the previous cut with: vos plan ${outDir} --reuse` : "")
|
|
@@ -13201,9 +13396,9 @@ async function cmdIngest(argv) {
|
|
|
13201
13396
|
|
|
13202
13397
|
// src/plugin/checkVideo.ts
|
|
13203
13398
|
import { createRequire as createRequire2 } from "module";
|
|
13204
|
-
import { existsSync as
|
|
13399
|
+
import { existsSync as existsSync23 } from "fs";
|
|
13205
13400
|
import { mkdir as mkdir9, readFile as readFile14, stat as stat3, writeFile as writeFile10 } from "fs/promises";
|
|
13206
|
-
import { basename as basename11, dirname as dirname8, extname as extname4, join as join24, resolve as
|
|
13401
|
+
import { basename as basename11, dirname as dirname8, extname as extname4, join as join24, resolve as resolve16 } from "path";
|
|
13207
13402
|
import { ALL_FORMATS as ALL_FORMATS2, FilePathSource as FilePathSource2, Input as Input2 } from "mediabunny";
|
|
13208
13403
|
|
|
13209
13404
|
// src/plugin/videoChecks.ts
|
|
@@ -13487,7 +13682,7 @@ async function probeVideoFacts(file) {
|
|
|
13487
13682
|
async function mediabunnyBundle() {
|
|
13488
13683
|
const entry = createRequire2(import.meta.url).resolve("mediabunny");
|
|
13489
13684
|
const bundle = join24(dirname8(entry), "mediabunny.min.mjs");
|
|
13490
|
-
if (
|
|
13685
|
+
if (existsSync23(bundle)) return readFile14(bundle, "utf8");
|
|
13491
13686
|
return readFile14(
|
|
13492
13687
|
join24(entry.split("/dist/")[0], "dist/bundles/mediabunny.min.mjs"),
|
|
13493
13688
|
"utf8"
|
|
@@ -13711,7 +13906,7 @@ async function checkVideo(browser, file, outDir) {
|
|
|
13711
13906
|
);
|
|
13712
13907
|
const long = Math.max(facts.width, facts.height, 1);
|
|
13713
13908
|
const side = (n) => Math.max(1, Math.round(n / long * THUMB_LONG_SIDE));
|
|
13714
|
-
const server = await startTakeServer(dirname8(
|
|
13909
|
+
const server = await startTakeServer(dirname8(resolve16(file)), {
|
|
13715
13910
|
"/check.html": '<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"></head><body></body></html>'
|
|
13716
13911
|
});
|
|
13717
13912
|
const page = await browser.newPage();
|
|
@@ -13829,10 +14024,10 @@ async function cmdCheckVideo(argv) {
|
|
|
13829
14024
|
throw new UsageError(
|
|
13830
14025
|
"vos check-video <file.mp4|webm> [--out dir] [--strict] [--json]"
|
|
13831
14026
|
);
|
|
13832
|
-
if (!
|
|
14027
|
+
if (!existsSync23(file)) throw new UsageError(`${file} does not exist`);
|
|
13833
14028
|
const r = createReporter(flags.json === true);
|
|
13834
14029
|
const strict = flags.strict === true;
|
|
13835
|
-
const outDir =
|
|
14030
|
+
const outDir = resolve16(
|
|
13836
14031
|
strFlag(flags, "out") ?? join24(dirname8(file), `${basename11(file, extname4(file))}-check`)
|
|
13837
14032
|
);
|
|
13838
14033
|
const browser = await launchBrowser();
|
|
@@ -13848,7 +14043,7 @@ async function cmdCheckVideo(argv) {
|
|
|
13848
14043
|
}
|
|
13849
14044
|
|
|
13850
14045
|
// src/plugin/join.ts
|
|
13851
|
-
import { existsSync as
|
|
14046
|
+
import { existsSync as existsSync26, readdirSync as readdirSync5, statSync as statSync7 } from "fs";
|
|
13852
14047
|
import { copyFile as copyFile3, mkdir as mkdir11, readFile as readFile17, rm as rm4 } from "fs/promises";
|
|
13853
14048
|
import {
|
|
13854
14049
|
basename as basename13,
|
|
@@ -13856,7 +14051,7 @@ import {
|
|
|
13856
14051
|
isAbsolute as isAbsolute4,
|
|
13857
14052
|
join as join27,
|
|
13858
14053
|
relative as relative5,
|
|
13859
|
-
resolve as
|
|
14054
|
+
resolve as resolve18,
|
|
13860
14055
|
sep as sep4
|
|
13861
14056
|
} from "path";
|
|
13862
14057
|
import {
|
|
@@ -13872,7 +14067,7 @@ import {
|
|
|
13872
14067
|
import { totalDuration as totalDuration3 } from "@vosjs/timeline";
|
|
13873
14068
|
|
|
13874
14069
|
// src/plugin/score.ts
|
|
13875
|
-
import { existsSync as
|
|
14070
|
+
import { existsSync as existsSync24 } from "fs";
|
|
13876
14071
|
import {
|
|
13877
14072
|
copyFile as copyFile2,
|
|
13878
14073
|
mkdir as mkdir10,
|
|
@@ -13882,7 +14077,7 @@ import {
|
|
|
13882
14077
|
writeFile as writeFile11
|
|
13883
14078
|
} from "fs/promises";
|
|
13884
14079
|
import { tmpdir as tmpdir2 } from "os";
|
|
13885
|
-
import { basename as basename12, isAbsolute as isAbsolute3, join as join25, relative as relative4, resolve as
|
|
14080
|
+
import { basename as basename12, isAbsolute as isAbsolute3, join as join25, relative as relative4, resolve as resolve17, sep as sep3 } from "path";
|
|
13886
14081
|
import {
|
|
13887
14082
|
NORMALIZE_MAX_LUFS as NORMALIZE_MAX_LUFS2,
|
|
13888
14083
|
NORMALIZE_MIN_LUFS as NORMALIZE_MIN_LUFS2,
|
|
@@ -14297,9 +14492,9 @@ function resolveAt(doc, raw) {
|
|
|
14297
14492
|
return { at: r.start, via: `step ${v} (${r.start.toFixed(2)}s)` };
|
|
14298
14493
|
}
|
|
14299
14494
|
async function resolveTrack(dir, ref, flags, dryRun) {
|
|
14300
|
-
const local =
|
|
14301
|
-
if (
|
|
14302
|
-
const rel = relative4(
|
|
14495
|
+
const local = resolve17(ref);
|
|
14496
|
+
if (existsSync24(local)) {
|
|
14497
|
+
const rel = relative4(resolve17(dir), local);
|
|
14303
14498
|
const inside = !!rel && !rel.startsWith(`..${sep3}`) && rel !== ".." && !isAbsolute3(rel);
|
|
14304
14499
|
const key = inside ? rel.split(sep3).join("/") : `music/${basename12(local)}`;
|
|
14305
14500
|
if (!inside && !dryRun) {
|
|
@@ -14438,7 +14633,7 @@ async function cmdScore(argv) {
|
|
|
14438
14633
|
const placeLine = at2 && plan.swellAt !== null ? `the track starts at ${plan.in.toFixed(2)}s, so the swell lands at ${plan.swellAt.toFixed(2)}s of film (asked: ${at2.via})` : `the track starts at ${plan.in.toFixed(2)}s${at2 ? ` (asked: ${at2.via})` : ", its first rise above its median level"}`;
|
|
14439
14634
|
r.done(
|
|
14440
14635
|
{
|
|
14441
|
-
take:
|
|
14636
|
+
take: resolve17(dir),
|
|
14442
14637
|
dryRun,
|
|
14443
14638
|
track: { key: track.key, name: track.name, duration: clip.duration },
|
|
14444
14639
|
film: r3(film),
|
|
@@ -14534,9 +14729,6 @@ function scriptDriftLine(d, file = "actions.json") {
|
|
|
14534
14729
|
// src/plugin/stepCaptions.ts
|
|
14535
14730
|
var STEP_CAPTIONS_FROM = "steps";
|
|
14536
14731
|
var STEP_CAPTION_AT = "start";
|
|
14537
|
-
var CAPTION_MAX = 3;
|
|
14538
|
-
var CAPTION_MIN = 1.2;
|
|
14539
|
-
var CAPTION_GAP = 0.1;
|
|
14540
14732
|
function captionOf(step, actions, script, recorded = []) {
|
|
14541
14733
|
const own = step.caption;
|
|
14542
14734
|
if (typeof own === "string" && own.trim()) return own.trim();
|
|
@@ -14570,6 +14762,7 @@ function landStepCaptions(doc, actions, opts = {}) {
|
|
|
14570
14762
|
return footageEnd;
|
|
14571
14763
|
};
|
|
14572
14764
|
const ends = /* @__PURE__ */ new Map();
|
|
14765
|
+
const labels = /* @__PURE__ */ new Map();
|
|
14573
14766
|
const spoken = (media, step, steps) => kept.some((o) => {
|
|
14574
14767
|
if (o.kind !== "text" || o.preset !== "caption") return false;
|
|
14575
14768
|
if (o.anchor && mediaKey(o.anchor.media) === media)
|
|
@@ -14622,6 +14815,7 @@ function landStepCaptions(doc, actions, opts = {}) {
|
|
|
14622
14815
|
from: STEP_CAPTIONS_FROM
|
|
14623
14816
|
};
|
|
14624
14817
|
ends.set(clip, end);
|
|
14818
|
+
labels.set(clip, label);
|
|
14625
14819
|
fresh.push(clip);
|
|
14626
14820
|
report.landed.push({
|
|
14627
14821
|
id,
|
|
@@ -14632,18 +14826,19 @@ function landStepCaptions(doc, actions, opts = {}) {
|
|
|
14632
14826
|
}
|
|
14633
14827
|
if (steps.length && !captioned) report.uncaptioned.push(media);
|
|
14634
14828
|
}
|
|
14635
|
-
fresh
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
|
|
14639
|
-
|
|
14640
|
-
|
|
14641
|
-
|
|
14642
|
-
|
|
14643
|
-
|
|
14644
|
-
|
|
14829
|
+
const fit = fitCaptions(fresh, (c) => ({
|
|
14830
|
+
start: c.start,
|
|
14831
|
+
end: ends.get(c) ?? footageEnd
|
|
14832
|
+
}));
|
|
14833
|
+
for (const { item, why } of fit.dropped) {
|
|
14834
|
+
report.skipped.push(`${labels.get(item) ?? item.id}: ${why}`);
|
|
14835
|
+
report.landed = report.landed.filter((l) => l.id !== item.id);
|
|
14836
|
+
}
|
|
14837
|
+
const landedClips = fit.kept.map(({ item, duration }) => {
|
|
14838
|
+
item.duration = duration;
|
|
14839
|
+
return item;
|
|
14645
14840
|
});
|
|
14646
|
-
const overlays = [...kept, ...
|
|
14841
|
+
const overlays = [...kept, ...landedClips];
|
|
14647
14842
|
if (overlays.length) doc.overlays = overlays;
|
|
14648
14843
|
else delete doc.overlays;
|
|
14649
14844
|
return report;
|
|
@@ -14951,7 +15146,7 @@ function perBeatLine(r, name = (m) => m ? `media ${m}` : "the primary") {
|
|
|
14951
15146
|
|
|
14952
15147
|
// src/plugin/rejoin.ts
|
|
14953
15148
|
import { createHash as createHash4 } from "crypto";
|
|
14954
|
-
import { createReadStream, existsSync as
|
|
15149
|
+
import { createReadStream, existsSync as existsSync25 } from "fs";
|
|
14955
15150
|
import { readFile as readFile16 } from "fs/promises";
|
|
14956
15151
|
import { join as join26 } from "path";
|
|
14957
15152
|
import {
|
|
@@ -14979,11 +15174,11 @@ async function partFootage(part) {
|
|
|
14979
15174
|
if (!isTakeRelativeKey(key))
|
|
14980
15175
|
return `key:${createHash4("sha256").update(key).digest("hex")}`;
|
|
14981
15176
|
const file = join26(part.dir, takeRelativeFile(key));
|
|
14982
|
-
return
|
|
15177
|
+
return existsSync25(file) ? fileHash(file) : `key:${createHash4("sha256").update(key).digest("hex")}`;
|
|
14983
15178
|
}
|
|
14984
15179
|
async function readJoinRecord(dir) {
|
|
14985
15180
|
const file = join26(dir, JOIN_RECORD_NAME);
|
|
14986
|
-
if (!
|
|
15181
|
+
if (!existsSync25(file)) return null;
|
|
14987
15182
|
try {
|
|
14988
15183
|
const v = JSON.parse(await readFile16(file, "utf8"));
|
|
14989
15184
|
return v && v.version === 1 && Array.isArray(v.inputs) ? v : null;
|
|
@@ -14996,7 +15191,7 @@ async function recordFromFootage(dir, doc) {
|
|
|
14996
15191
|
if (!isTakeRelativeKey(key))
|
|
14997
15192
|
return `key:${createHash4("sha256").update(key).digest("hex")}`;
|
|
14998
15193
|
const file = join26(dir, takeRelativeFile(key));
|
|
14999
|
-
return
|
|
15194
|
+
return existsSync25(file) ? fileHash(file) : `missing:${key}`;
|
|
15000
15195
|
};
|
|
15001
15196
|
const inputs = [
|
|
15002
15197
|
{
|
|
@@ -16002,10 +16197,10 @@ var within = (inner, outer) => {
|
|
|
16002
16197
|
return rel === "" || !rel.startsWith(`..${sep4}`) && rel !== ".." && !isAbsolute4(rel);
|
|
16003
16198
|
};
|
|
16004
16199
|
async function readPart(arg) {
|
|
16005
|
-
const path =
|
|
16006
|
-
if (!
|
|
16200
|
+
const path = resolve18(arg);
|
|
16201
|
+
if (!existsSync26(path)) throw new UsageError(`${arg}: no such take or file`);
|
|
16007
16202
|
if (statSync7(path).isDirectory()) {
|
|
16008
|
-
if (!
|
|
16203
|
+
if (!existsSync26(join27(path, "meta.json")))
|
|
16009
16204
|
throw new UsageError(
|
|
16010
16205
|
`${arg}: not a take directory (no meta.json); a video file is joined by its path`
|
|
16011
16206
|
);
|
|
@@ -16052,11 +16247,11 @@ async function cmdJoin(argv) {
|
|
|
16052
16247
|
throw new UsageError(
|
|
16053
16248
|
"--force replaces the joined take and --keep-edits keeps its edits; pass one"
|
|
16054
16249
|
);
|
|
16055
|
-
const out =
|
|
16056
|
-
const inputs = positionals.map((p) =>
|
|
16250
|
+
const out = resolve18(outArg);
|
|
16251
|
+
const inputs = positionals.map((p) => resolve18(p));
|
|
16057
16252
|
const muted = /* @__PURE__ */ new Set();
|
|
16058
16253
|
for (const m of multi.mute ?? []) {
|
|
16059
|
-
const i = inputs.indexOf(
|
|
16254
|
+
const i = inputs.indexOf(resolve18(m));
|
|
16060
16255
|
if (i < 0)
|
|
16061
16256
|
throw new UsageError(
|
|
16062
16257
|
`--mute ${m}: not one of the inputs; name a file as it is given to vos join`
|
|
@@ -16070,19 +16265,19 @@ async function cmdJoin(argv) {
|
|
|
16070
16265
|
);
|
|
16071
16266
|
}
|
|
16072
16267
|
let rejoin = false;
|
|
16073
|
-
if (
|
|
16268
|
+
if (existsSync26(out)) {
|
|
16074
16269
|
if (!force && !keepEdits)
|
|
16075
16270
|
throw new UsageError(
|
|
16076
16271
|
`--out ${outArg} exists; pass --keep-edits to join again and keep every edit made since (its trims, spans, layers, frame, sound), --force to replace it, or name a new directory`
|
|
16077
16272
|
);
|
|
16078
16273
|
if (keepEdits) {
|
|
16079
|
-
if (!
|
|
16274
|
+
if (!existsSync26(join27(out, "doc.json")))
|
|
16080
16275
|
throw new UsageError(
|
|
16081
16276
|
`--keep-edits: ${outArg} holds no doc.json, so there are no edits to keep; name a new directory, or --force to replace it`
|
|
16082
16277
|
);
|
|
16083
16278
|
rejoin = true;
|
|
16084
16279
|
} else {
|
|
16085
|
-
const isTake =
|
|
16280
|
+
const isTake = existsSync26(join27(out, "doc.json")) || existsSync26(join27(out, "meta.json"));
|
|
16086
16281
|
if (!isTake && readdirSync5(out).length > 0)
|
|
16087
16282
|
throw new UsageError(
|
|
16088
16283
|
`--force replaces a take directory, and ${outArg} holds other files; name a new directory`
|
|
@@ -16128,7 +16323,7 @@ async function cmdJoin(argv) {
|
|
|
16128
16323
|
);
|
|
16129
16324
|
}
|
|
16130
16325
|
rejoined = rejoinDocs(migrateMotion3(prev), record, read, {
|
|
16131
|
-
where: (p) => relative5(process.cwd(),
|
|
16326
|
+
where: (p) => relative5(process.cwd(), resolve18(out, p)) || "."
|
|
16132
16327
|
});
|
|
16133
16328
|
plan = rejoined.plan;
|
|
16134
16329
|
} else plan = joinDocs(parts);
|
|
@@ -16169,11 +16364,11 @@ async function cmdJoin(argv) {
|
|
|
16169
16364
|
for (const k of rejoined.dropped) {
|
|
16170
16365
|
if (!/^[A-Za-z0-9_-]+$/.test(k)) continue;
|
|
16171
16366
|
const dir = join27(out, k);
|
|
16172
|
-
if (
|
|
16367
|
+
if (existsSync26(dir) && statSync7(dir).isDirectory())
|
|
16173
16368
|
await rm4(dir, { recursive: true, force: true });
|
|
16174
16369
|
}
|
|
16175
16370
|
} else {
|
|
16176
|
-
if (
|
|
16371
|
+
if (existsSync26(out)) await rm4(out, { recursive: true, force: true });
|
|
16177
16372
|
await mkdir11(out, { recursive: true });
|
|
16178
16373
|
}
|
|
16179
16374
|
for (const c of plan.copies) {
|
|
@@ -16257,7 +16452,7 @@ async function cmdJoin(argv) {
|
|
|
16257
16452
|
}
|
|
16258
16453
|
|
|
16259
16454
|
// src/plugin/timeline.ts
|
|
16260
|
-
import { join as join28, resolve as
|
|
16455
|
+
import { join as join28, resolve as resolve19 } from "path";
|
|
16261
16456
|
import {
|
|
16262
16457
|
docFreezes as docFreezes2,
|
|
16263
16458
|
docMediaList as docMediaList4,
|
|
@@ -16548,7 +16743,7 @@ async function cmdTakeInfo(argv) {
|
|
|
16548
16743
|
if (drift) r.warn(scriptDriftLine(drift));
|
|
16549
16744
|
const timeline = takeTimeline(take.doc, /* @__PURE__ */ new Map([["", take.actions]]));
|
|
16550
16745
|
r.done(
|
|
16551
|
-
{ take:
|
|
16746
|
+
{ take: resolve19(dir), doc: join28(resolve19(dir), "doc.json"), ...timeline },
|
|
16552
16747
|
formatTimeline(timeline, dir)
|
|
16553
16748
|
);
|
|
16554
16749
|
return EXIT_OK;
|
|
@@ -16652,8 +16847,8 @@ async function cmdAudio(argv) {
|
|
|
16652
16847
|
|
|
16653
16848
|
// src/plugin/look.ts
|
|
16654
16849
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
16655
|
-
import { existsSync as
|
|
16656
|
-
import { join as join30, resolve as
|
|
16850
|
+
import { existsSync as existsSync27 } from "fs";
|
|
16851
|
+
import { join as join30, resolve as resolve20 } from "path";
|
|
16657
16852
|
import { headersFor, urlSchemeProblem } from "@vosjs/render-core/record";
|
|
16658
16853
|
function quote(value) {
|
|
16659
16854
|
return `"${value.replace(/\s+/g, " ").replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
@@ -17195,15 +17390,15 @@ async function cmdLook(argv) {
|
|
|
17195
17390
|
if (scheme) throw new UsageError(scheme);
|
|
17196
17391
|
const r = createReporter(flags.json === true);
|
|
17197
17392
|
const viewport = parseViewport2(strFlag(flags, "viewport"));
|
|
17198
|
-
const outDir =
|
|
17393
|
+
const outDir = resolve20(strFlag(flags, "out") ?? "look");
|
|
17199
17394
|
const headed = flags.headed === true;
|
|
17200
17395
|
const waitRaw = strFlag(flags, "wait");
|
|
17201
17396
|
const waitMs = waitRaw === void 0 ? 0 : Number(waitRaw);
|
|
17202
17397
|
if (!Number.isFinite(waitMs) || waitMs < 0 || waitMs > 6e4)
|
|
17203
17398
|
throw new UsageError("--wait expects milliseconds, 0 to 60000");
|
|
17204
17399
|
const storageRaw = strFlag(flags, "storage-state");
|
|
17205
|
-
const storageState = storageRaw ?
|
|
17206
|
-
if (storageState && !
|
|
17400
|
+
const storageState = storageRaw ? resolve20(storageRaw) : void 0;
|
|
17401
|
+
if (storageState && !existsSync27(storageState))
|
|
17207
17402
|
throw new UsageError(`--storage-state: no such file: ${storageState}`);
|
|
17208
17403
|
const sessionName = strFlag(flags, "session");
|
|
17209
17404
|
if (sessionName) {
|
|
@@ -17213,7 +17408,7 @@ async function cmdLook(argv) {
|
|
|
17213
17408
|
throw new UsageError(
|
|
17214
17409
|
"--session and --storage-state are two doors to one page: a persistent profile cannot take a storage state. Pass one"
|
|
17215
17410
|
);
|
|
17216
|
-
if (!
|
|
17411
|
+
if (!existsSync27(sessionDir(sessionName)))
|
|
17217
17412
|
throw new UsageError(
|
|
17218
17413
|
`no session "${sessionName}". Make one: vos session open <url> --name ${sessionName} (a Chrome window opens; sign in and close it)`
|
|
17219
17414
|
);
|
|
@@ -17276,7 +17471,7 @@ async function cmdLook(argv) {
|
|
|
17276
17471
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
17277
17472
|
import {
|
|
17278
17473
|
cpSync,
|
|
17279
|
-
existsSync as
|
|
17474
|
+
existsSync as existsSync29,
|
|
17280
17475
|
mkdirSync as mkdirSync6,
|
|
17281
17476
|
readFileSync as readFileSync11,
|
|
17282
17477
|
rmSync as rmSync3,
|
|
@@ -17287,7 +17482,7 @@ import { join as join32 } from "path";
|
|
|
17287
17482
|
import { chromium as chromium2 } from "playwright";
|
|
17288
17483
|
|
|
17289
17484
|
// src/plugin/machine.ts
|
|
17290
|
-
import { existsSync as
|
|
17485
|
+
import { existsSync as existsSync28, readFileSync as readFileSync10, readdirSync as readdirSync6, statSync as statSync8 } from "fs";
|
|
17291
17486
|
import { dirname as dirname10, join as join31 } from "path";
|
|
17292
17487
|
var AGENT_NAMES = [
|
|
17293
17488
|
"claude",
|
|
@@ -17308,7 +17503,7 @@ var HOME_DIRS = {
|
|
|
17308
17503
|
copilot: [".copilot"]
|
|
17309
17504
|
};
|
|
17310
17505
|
function detectAgents(root, opts = {}) {
|
|
17311
|
-
const exists = opts.exists ??
|
|
17506
|
+
const exists = opts.exists ?? existsSync28;
|
|
17312
17507
|
const out = [];
|
|
17313
17508
|
for (const agent of AGENT_NAMES) {
|
|
17314
17509
|
for (const rel of PROJECT_DIRS[agent]) {
|
|
@@ -17355,7 +17550,7 @@ function skillsDirFor(home) {
|
|
|
17355
17550
|
function fallbackSkillsHome(root) {
|
|
17356
17551
|
return { agent: "codex", dir: join31(root, ".agents"), scope: "project" };
|
|
17357
17552
|
}
|
|
17358
|
-
function installedSkills(dir, exists =
|
|
17553
|
+
function installedSkills(dir, exists = existsSync28, list = (p) => {
|
|
17359
17554
|
try {
|
|
17360
17555
|
return readdirSync6(p);
|
|
17361
17556
|
} catch {
|
|
@@ -17396,7 +17591,7 @@ ${RULES_BLOCK}
|
|
|
17396
17591
|
}
|
|
17397
17592
|
return { text, changed: text !== current };
|
|
17398
17593
|
}
|
|
17399
|
-
function rulesFileFor(root, exists =
|
|
17594
|
+
function rulesFileFor(root, exists = existsSync28) {
|
|
17400
17595
|
const agents = join31(root, "AGENTS.md");
|
|
17401
17596
|
if (exists(agents)) return agents;
|
|
17402
17597
|
const claude = join31(root, "CLAUDE.md");
|
|
@@ -17511,12 +17706,12 @@ function bundledSkillsDir(from) {
|
|
|
17511
17706
|
let dir = dirname10(from);
|
|
17512
17707
|
for (let i = 0; i < 6; i++) {
|
|
17513
17708
|
const pkg = join31(dir, "package.json");
|
|
17514
|
-
if (
|
|
17709
|
+
if (existsSync28(pkg)) {
|
|
17515
17710
|
try {
|
|
17516
17711
|
const name = JSON.parse(readFileSync10(pkg, "utf8")).name;
|
|
17517
17712
|
if (name === "@vosjs/cli") {
|
|
17518
17713
|
const skills = join31(dir, "skills");
|
|
17519
|
-
return
|
|
17714
|
+
return existsSync28(skills) && statSync8(skills).isDirectory() ? skills : null;
|
|
17520
17715
|
}
|
|
17521
17716
|
} catch {
|
|
17522
17717
|
}
|
|
@@ -17552,7 +17747,7 @@ function npx() {
|
|
|
17552
17747
|
function bundledChromium() {
|
|
17553
17748
|
try {
|
|
17554
17749
|
const p = chromium2.executablePath();
|
|
17555
|
-
return p &&
|
|
17750
|
+
return p && existsSync29(p) ? p : null;
|
|
17556
17751
|
} catch {
|
|
17557
17752
|
return null;
|
|
17558
17753
|
}
|
|
@@ -17561,7 +17756,7 @@ function probe2() {
|
|
|
17561
17756
|
return probeBrowser({
|
|
17562
17757
|
platform: process.platform,
|
|
17563
17758
|
env: process.env,
|
|
17564
|
-
exists:
|
|
17759
|
+
exists: existsSync29,
|
|
17565
17760
|
bundled: bundledChromium
|
|
17566
17761
|
});
|
|
17567
17762
|
}
|
|
@@ -17714,7 +17909,7 @@ async function cmdSetup(argv) {
|
|
|
17714
17909
|
let rules = null;
|
|
17715
17910
|
if (!noRules) {
|
|
17716
17911
|
const file = rulesFileFor(root);
|
|
17717
|
-
const existing =
|
|
17912
|
+
const existing = existsSync29(file) ? readFileSync11(file, "utf8") : null;
|
|
17718
17913
|
const { text, changed } = applyRulesBlock(existing);
|
|
17719
17914
|
if (changed) writeFileSync6(file, text);
|
|
17720
17915
|
rules = { file, changed };
|
|
@@ -17828,7 +18023,7 @@ async function cmdWhoami(argv) {
|
|
|
17828
18023
|
function cmdLogout(argv) {
|
|
17829
18024
|
const { flags } = parseArgs(argv, BOOLEAN_FLAGS14);
|
|
17830
18025
|
const r = reporterFor(flags);
|
|
17831
|
-
const had =
|
|
18026
|
+
const had = existsSync29(CREDENTIALS_PATH);
|
|
17832
18027
|
rmSync3(CREDENTIALS_PATH, { force: true });
|
|
17833
18028
|
const envKey = Boolean(process.env.VOS_API_KEY?.trim());
|
|
17834
18029
|
r.done(
|
|
@@ -17882,8 +18077,9 @@ var MULTI_FLAGS2 = /* @__PURE__ */ new Set([
|
|
|
17882
18077
|
var HELP = `vos \u2014 record a browser flow, plan effects, render a product video; sync with vos.so
|
|
17883
18078
|
|
|
17884
18079
|
Take pipeline
|
|
17885
|
-
vos create --actions actions.json [--url <url>] [--out take] [out.webm] [--strict] [--allow-wall] [--keep-frames] [--max-duration <s>] [--ready dom|networkidle] [--storage-state <file>|--session <name>] [--header name=value]... [--confirm <id>]... [--browser-arg=<switch>]... [--background <slug|url|none>] [render flags] [--json]
|
|
17886
|
-
vos record --actions actions.json [--url <url>] [--out take] [--strict] [--dry-run] [--allow-wall] [--keep-frames] [--max-duration <s>] [--ready dom|networkidle] [--storage-state <file>|--session <name>] [--header name=value]... [--confirm <id>]... [--browser-arg=<switch>]... [--background <slug|url|none>] [--verbose] [--json]
|
|
18080
|
+
vos create --actions actions.json [--url <url>] [--out take] [--reuse] [out.webm] [--strict] [--allow-wall] [--keep-frames] [--max-duration <s>] [--ready dom|networkidle] [--storage-state <file>|--session <name>] [--header name=value]... [--confirm <id>]... [--browser-arg=<switch>]... [--background <slug|url|none>] [render flags] [--json]
|
|
18081
|
+
vos record --actions actions.json [--url <url>] [--out take] [--reuse] [--strict] [--dry-run] [--allow-wall] [--keep-frames] [--max-duration <s>] [--ready dom|networkidle] [--storage-state <file>|--session <name>] [--header name=value]... [--confirm <id>]... [--browser-arg=<switch>]... [--background <slug|url|none>] [--verbose] [--json]
|
|
18082
|
+
--reuse, recording again into a take that has a cut: the cut (kept as doc.prev.json) goes onto the new footage as vos plan --reuse does; without it the take is planned fresh and its last line says where the cut is
|
|
17887
18083
|
vos record --hosted --actions actions.json [--url <url>] [--out take] [--title "\u2026"] [--folder <id>] [--header name=value]... [--json]
|
|
17888
18084
|
the take recorded on vos.so's fleet (no browser here): lands as a private vos with its doc and digest, then fetched into --out with its media
|
|
17889
18085
|
vos plan <take> [--fresh] [--reuse [--from <doc.json>]] [--style <doc.json|vosId>] [--with <doc.json|vosId>[@end|@start|@step:<id>|@<s>]]... [--background <slug|url|none>] [--motion] [--headline "\u2026"] [--kicker "\u2026"] [--launch LAUNCH.md] [--brand BRAND.md] [--music <slug|mood|none>] [--entrance tilt-in|pull-out|rise|fade|slide|none] [--transitions slide|fade|scale|none] [--end-card on|none|<doc.json|vosId>] [--captions|--captions none] [--clicks none] [--still <t>] [--release v2.1] [--zooms per-beat] [--json]
|
|
@@ -17923,6 +18119,8 @@ Take pipeline
|
|
|
17923
18119
|
see a page before scripting it, signed in the way vos record would be: <out>/look.png and every control in view with ONE proposed selector (test id, aria-label, role + name, then words in its dialog or landmark), UNIQUE or AMBIGUOUS as the recorder's own engine counts it, plus what the frame exposes. --click/--type run first, in order; a click on a spend or delete word is refused. Exit 4 at a sign-in wall
|
|
17924
18120
|
vos session open <url> --name <app> a plain Chrome window on a vos-owned profile; sign in, set the account up, close the window (or Enter)
|
|
17925
18121
|
vos session check <name> [--url <url>] [--strict] headless, against a stranger: does the page open as you? exit 0/4
|
|
18122
|
+
vos session exec <name> <script.mjs> [--url <url>] [--headed] [--viewport WxH] [--json]
|
|
18123
|
+
run your own Playwright code inside the session: the module's default export gets { context, page, url, name } and what it returns is printed (result under --json). The ONLY safe way to script a session: a profile opened by any other browser (Playwright's bundled one, a mock keychain) cannot read its cookies and is signed out
|
|
17926
18124
|
vos session list | rm <name> what exists and how old; delete one
|
|
17927
18125
|
vos actions script <actions.json> [--json]
|
|
17928
18126
|
the flow as numbered beats in words, for a person recording it by hand
|
|
@@ -18336,6 +18534,13 @@ function exposureNote(rec) {
|
|
|
18336
18534
|
${rec.exposures.map((e) => ` - ${exposureLine(e)}`).join("\n")}
|
|
18337
18535
|
${EXPOSURE_ADVICE}`;
|
|
18338
18536
|
}
|
|
18537
|
+
function teardownNote(rec) {
|
|
18538
|
+
return rec.teardown ? `
|
|
18539
|
+
${teardownLine(rec.teardown)}` : "";
|
|
18540
|
+
}
|
|
18541
|
+
function teardownField(rec) {
|
|
18542
|
+
return rec.teardown ? { teardown: rec.teardown } : {};
|
|
18543
|
+
}
|
|
18339
18544
|
function takeConfirm(multi, actions, opts) {
|
|
18340
18545
|
const given = (multi.confirm ?? []).filter(Boolean);
|
|
18341
18546
|
if (opts.dryRun) return given;
|
|
@@ -18399,8 +18604,8 @@ function takeBrowserArgs(multi) {
|
|
|
18399
18604
|
function takeStorageState(flags) {
|
|
18400
18605
|
const raw = strFlag(flags, "storage-state");
|
|
18401
18606
|
if (!raw) return void 0;
|
|
18402
|
-
const path =
|
|
18403
|
-
if (!
|
|
18607
|
+
const path = resolve21(raw);
|
|
18608
|
+
if (!existsSync30(path))
|
|
18404
18609
|
throw new UsageError(`--storage-state: no such file: ${path}`);
|
|
18405
18610
|
return path;
|
|
18406
18611
|
}
|
|
@@ -18413,7 +18618,7 @@ function takeSessionName(flags, storageState) {
|
|
|
18413
18618
|
throw new UsageError(
|
|
18414
18619
|
"--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
|
|
18415
18620
|
);
|
|
18416
|
-
if (!
|
|
18621
|
+
if (!existsSync30(sessionDir(name)))
|
|
18417
18622
|
throw new UsageError(
|
|
18418
18623
|
`no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
|
|
18419
18624
|
);
|
|
@@ -18429,6 +18634,63 @@ async function inSession(name, actions, fn) {
|
|
|
18429
18634
|
}
|
|
18430
18635
|
});
|
|
18431
18636
|
}
|
|
18637
|
+
function reRecord(outDir, flags, r) {
|
|
18638
|
+
const wantsReuse = flags.reuse === true;
|
|
18639
|
+
if (wantsReuse && !existsSync30(join33(outDir, "doc.json")))
|
|
18640
|
+
throw new UsageError(
|
|
18641
|
+
`--reuse: ${outDir} has no cut to keep (no doc.json); record without --reuse`
|
|
18642
|
+
);
|
|
18643
|
+
let previousCut = null;
|
|
18644
|
+
return {
|
|
18645
|
+
prepare: async () => {
|
|
18646
|
+
if (existsSync30(join33(outDir, "meta.json"))) {
|
|
18647
|
+
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
18648
|
+
if (prevDoc) previousCut = join33(outDir, PREV_DOC_NAME);
|
|
18649
|
+
r.log(
|
|
18650
|
+
`note: re-recording ${outDir} \u2014 kept ${kept.length ? kept.join(", ") : "nothing"}` + (prevDoc ? wantsReuse ? "; the previous cut goes onto the new footage when it is recorded (--reuse)" : `; the previous cut is ${PREV_DOC_NAME}` : "")
|
|
18651
|
+
);
|
|
18652
|
+
}
|
|
18653
|
+
await mkdir13(outDir, { recursive: true });
|
|
18654
|
+
await ensureTakeDir(outDir);
|
|
18655
|
+
},
|
|
18656
|
+
plan: async (backdrop) => {
|
|
18657
|
+
const from = wantsReuse ? previousCut : null;
|
|
18658
|
+
const plan = await planTake(outDir, {
|
|
18659
|
+
backdrop,
|
|
18660
|
+
...from ? {
|
|
18661
|
+
reuse: {
|
|
18662
|
+
from,
|
|
18663
|
+
doc: JSON.parse(await readFile18(from, "utf8"))
|
|
18664
|
+
}
|
|
18665
|
+
} : {}
|
|
18666
|
+
});
|
|
18667
|
+
if (!from) await notePlannedDoc(outDir);
|
|
18668
|
+
return plan;
|
|
18669
|
+
},
|
|
18670
|
+
previousCut: () => previousCut
|
|
18671
|
+
};
|
|
18672
|
+
}
|
|
18673
|
+
function previousCutReport(outDir, previousCut, plan) {
|
|
18674
|
+
if (plan?.reuse) {
|
|
18675
|
+
const s = plan.reuse;
|
|
18676
|
+
return {
|
|
18677
|
+
line: `
|
|
18678
|
+
reused ${s.from}: ${s.anchored} anchored + ${s.mapped} mapped span(s)` + (s.flagged.length ? `
|
|
18679
|
+
flagged:
|
|
18680
|
+
${s.flagged.join("\n ")}` : ""),
|
|
18681
|
+
next: null,
|
|
18682
|
+
fields: { previousCut: s.from, reuse: s }
|
|
18683
|
+
};
|
|
18684
|
+
}
|
|
18685
|
+
if (!previousCut) return { line: "", next: null, fields: {} };
|
|
18686
|
+
const next = `vos plan ${outDir} --reuse`;
|
|
18687
|
+
return {
|
|
18688
|
+
line: `
|
|
18689
|
+
YOUR PREVIOUS CUT is kept in ${previousCut}, not on this footage. Apply it: ${next} (or record with --reuse next time)`,
|
|
18690
|
+
next,
|
|
18691
|
+
fields: { previousCut, next_step: next }
|
|
18692
|
+
};
|
|
18693
|
+
}
|
|
18432
18694
|
async function takeBackdrop(flags, r) {
|
|
18433
18695
|
const { backdrop, note } = await openingBackdrop(
|
|
18434
18696
|
strFlag(flags, "background"),
|
|
@@ -18485,7 +18747,7 @@ async function cmdRecord(argv) {
|
|
|
18485
18747
|
const scheme = urlSchemeProblem2(url);
|
|
18486
18748
|
if (scheme) throw new UsageError(scheme);
|
|
18487
18749
|
announceEnvNames(actions, r);
|
|
18488
|
-
const outDir =
|
|
18750
|
+
const outDir = resolve21(strFlag(flags, "out") ?? "take");
|
|
18489
18751
|
const storageState = takeStorageState(flags);
|
|
18490
18752
|
const sessionName = takeSessionName(flags, storageState);
|
|
18491
18753
|
const browserArgs = takeBrowserArgs(multi);
|
|
@@ -18505,7 +18767,7 @@ async function cmdRecord(argv) {
|
|
|
18505
18767
|
);
|
|
18506
18768
|
const hostedActions = await scriptForHost(
|
|
18507
18769
|
actions,
|
|
18508
|
-
dirname11(
|
|
18770
|
+
dirname11(resolve21(actionsPath))
|
|
18509
18771
|
).catch((e) => {
|
|
18510
18772
|
throw new UsageError(e instanceof Error ? e.message : String(e));
|
|
18511
18773
|
});
|
|
@@ -18530,7 +18792,7 @@ async function cmdRecord(argv) {
|
|
|
18530
18792
|
const { env, missing } = hostedEnv(actions, process.env);
|
|
18531
18793
|
if (missing.length)
|
|
18532
18794
|
throw new UsageError(
|
|
18533
|
-
|
|
18795
|
+
`${envReaders(actions)} ${missing.map((n) => `{ "env": "${n}" }`).join(", ")} and this shell has no ${missing.length === 1 ? "value for it" : "values for them"}; export ${missing.join(", ")} and run again (the value rides the job, and is never stored)`
|
|
18534
18796
|
);
|
|
18535
18797
|
r.log("recording on vos.so\u2026");
|
|
18536
18798
|
r.event({ event: "phase", phase: "record" });
|
|
@@ -18601,9 +18863,11 @@ async function cmdRecord(argv) {
|
|
|
18601
18863
|
}
|
|
18602
18864
|
const backdrop = await takeBackdrop(flags, r);
|
|
18603
18865
|
const maxDurationSeconds = await maxDuration(flags, r);
|
|
18866
|
+
const cut = reRecord(outDir, flags, r);
|
|
18604
18867
|
if (flags["dry-run"] === true) {
|
|
18605
18868
|
void flags.strict;
|
|
18606
18869
|
void flags["keep-frames"];
|
|
18870
|
+
void flags.reuse;
|
|
18607
18871
|
const browser2 = await launchBrowser(browserArgs);
|
|
18608
18872
|
try {
|
|
18609
18873
|
r.log("rehearsing (nothing is captured or written)\u2026");
|
|
@@ -18613,7 +18877,7 @@ async function cmdRecord(argv) {
|
|
|
18613
18877
|
sessionName,
|
|
18614
18878
|
actions,
|
|
18615
18879
|
(context2) => recordTake(browser2, url, actions, takePaths(outDir), r.log, {
|
|
18616
|
-
actionsDir: dirname11(
|
|
18880
|
+
actionsDir: dirname11(resolve21(actionsPath)),
|
|
18617
18881
|
storageState,
|
|
18618
18882
|
headers,
|
|
18619
18883
|
context: context2,
|
|
@@ -18677,7 +18941,9 @@ async function cmdRecord(argv) {
|
|
|
18677
18941
|
...Object.keys(headers).map((k) => `--header ${k}=\u2026`),
|
|
18678
18942
|
...sessionName ? [`--session ${sessionName}`] : [],
|
|
18679
18943
|
...flags["allow-wall"] === true ? ["--allow-wall"] : [],
|
|
18680
|
-
...confirm.map((c) => `--confirm ${c}`)
|
|
18944
|
+
...confirm.map((c) => `--confirm ${c}`),
|
|
18945
|
+
// A take that already has a cut keeps it on the new footage.
|
|
18946
|
+
...existsSync30(join33(outDir, "doc.json")) ? ["--reuse"] : []
|
|
18681
18947
|
].join(" ");
|
|
18682
18948
|
const nextOut = strFlag(flags, "out") ?? "take";
|
|
18683
18949
|
const guardNote = !unconfirmed.ok ? unconfirmed.unconfirmed.length ? ` (it refuses until you add ${unconfirmed.unconfirmed.map((id) => `--confirm ${id}`).join(" ")}: ${unconfirmed.unconfirmed.length === 1 ? "that step spends" : "those steps spend"} something real)` : ` (${unconfirmed.unknown.map((c) => `--confirm ${c}`).join(", ")} names no guarded step)` : "";
|
|
@@ -18698,11 +18964,12 @@ async function cmdRecord(argv) {
|
|
|
18698
18964
|
exposures: rec.exposures,
|
|
18699
18965
|
notices: rec.notices,
|
|
18700
18966
|
masks: rec.masks,
|
|
18701
|
-
...rec.peers.length ? { peers: rec.peers } : {}
|
|
18967
|
+
...rec.peers.length ? { peers: rec.peers } : {},
|
|
18968
|
+
...teardownField(rec)
|
|
18702
18969
|
},
|
|
18703
18970
|
`${failed ? "REHEARSAL FAILED" : "Rehearsal passed"}: ${resolved}/${actions.steps.length} steps resolved in ${((Date.now() - started) / 1e3).toFixed(1)}s${rec.navTimeout ? " (the first load never reached networkidle)" : rec.ready.capped ? " (the first page never stopped changing)" : ""}
|
|
18704
18971
|
${lines.join("\n")}
|
|
18705
|
-
${maskMisses(rec).length ? `MASK hid nothing: ${maskMisses(rec).join(", ")} (the selector reached no element). ` : ""}${rec.stopped ? `STOPPED: ${rec.stopped.reason}. ` : ""}${failed ? "Fix the script, rehearse again, then record." : `Rects are capture px (the step rects a pin or a callout reads). Next: vos record --actions ${actionsPath} --out ${nextOut}${carried ? ` ${carried}` : ""} --strict${guardNote}`}${exposureNote(rec)}${noticeNote(rec)}`
|
|
18972
|
+
${maskMisses(rec).length ? `MASK hid nothing: ${maskMisses(rec).join(", ")} (the selector reached no element). ` : ""}${rec.stopped ? `STOPPED: ${rec.stopped.reason}. ` : ""}${failed ? "Fix the script, rehearse again, then record." : `Rects are capture px (the step rects a pin or a callout reads). Next: vos record --actions ${actionsPath} --out ${nextOut}${carried ? ` ${carried}` : ""} --strict${guardNote}`}${exposureNote(rec)}${noticeNote(rec)}${teardownNote(rec)}`
|
|
18706
18973
|
);
|
|
18707
18974
|
return rec.stopped ? EXIT_ERROR : failed ? EXIT_USAGE : EXIT_OK;
|
|
18708
18975
|
} finally {
|
|
@@ -18710,16 +18977,6 @@ ${lines.join("\n")}
|
|
|
18710
18977
|
}
|
|
18711
18978
|
}
|
|
18712
18979
|
const paths = takePaths(outDir);
|
|
18713
|
-
const prepare = async () => {
|
|
18714
|
-
if (existsSync29(join33(outDir, "meta.json"))) {
|
|
18715
|
-
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
18716
|
-
r.log(
|
|
18717
|
-
`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" : "")
|
|
18718
|
-
);
|
|
18719
|
-
}
|
|
18720
|
-
await mkdir13(outDir, { recursive: true });
|
|
18721
|
-
await ensureTakeDir(outDir);
|
|
18722
|
-
};
|
|
18723
18980
|
const browser = await launchBrowser(browserArgs);
|
|
18724
18981
|
try {
|
|
18725
18982
|
r.log("recording\u2026");
|
|
@@ -18728,7 +18985,7 @@ ${lines.join("\n")}
|
|
|
18728
18985
|
sessionName,
|
|
18729
18986
|
actions,
|
|
18730
18987
|
(context2) => recordTake(browser, url, actions, paths, r.log, {
|
|
18731
|
-
actionsDir: dirname11(
|
|
18988
|
+
actionsDir: dirname11(resolve21(actionsPath)),
|
|
18732
18989
|
maxDurationSeconds,
|
|
18733
18990
|
storageState,
|
|
18734
18991
|
headers,
|
|
@@ -18739,7 +18996,7 @@ ${lines.join("\n")}
|
|
|
18739
18996
|
onEvent: (e) => r.event(e),
|
|
18740
18997
|
onArrival: wallGate(flags, r, {
|
|
18741
18998
|
strict: flags.strict === true,
|
|
18742
|
-
prepare
|
|
18999
|
+
prepare: cut.prepare
|
|
18743
19000
|
})
|
|
18744
19001
|
})
|
|
18745
19002
|
);
|
|
@@ -18752,7 +19009,8 @@ ${lines.join("\n")}
|
|
|
18752
19009
|
);
|
|
18753
19010
|
await releaseFrames(paths, flags, r.log);
|
|
18754
19011
|
r.event({ event: "phase", phase: "plan" });
|
|
18755
|
-
const plan = await
|
|
19012
|
+
const plan = await cut.plan(backdrop);
|
|
19013
|
+
const kept = previousCutReport(outDir, cut.previousCut(), plan);
|
|
18756
19014
|
const clicks = rec.events.filter((e) => e.type === "down").length;
|
|
18757
19015
|
const strict = flags.strict === true;
|
|
18758
19016
|
const strictFail = strict && strictFails(rec);
|
|
@@ -18771,10 +19029,12 @@ ${lines.join("\n")}
|
|
|
18771
19029
|
confirmed: rec.confirmed,
|
|
18772
19030
|
skips: rec.skips,
|
|
18773
19031
|
outputs: rec.outputs,
|
|
18774
|
-
pageErrors: rec.pageErrors
|
|
19032
|
+
pageErrors: rec.pageErrors,
|
|
19033
|
+
...kept.fields,
|
|
19034
|
+
...teardownField(rec)
|
|
18775
19035
|
},
|
|
18776
19036
|
`FAILED: ${rec.stopped.reason}.
|
|
18777
|
-
The footage up to it is in ${outDir}.${skippedNote}${stepNote}${navTimeoutNote(rec)}`
|
|
19037
|
+
The footage up to it is in ${outDir}.${skippedNote}${stepNote}${navTimeoutNote(rec)}${kept.line}${teardownNote(rec)}`
|
|
18778
19038
|
);
|
|
18779
19039
|
return EXIT_ERROR;
|
|
18780
19040
|
}
|
|
@@ -18807,11 +19067,13 @@ ${lines.join("\n")}
|
|
|
18807
19067
|
overruns: rec.overruns,
|
|
18808
19068
|
pageErrors: rec.pageErrors,
|
|
18809
19069
|
...rec.peers.length ? { peers: rec.peers } : {},
|
|
18810
|
-
...
|
|
19070
|
+
...teardownField(rec),
|
|
19071
|
+
...strictFail ? { strictFailed: true } : {},
|
|
19072
|
+
...kept.fields
|
|
18811
19073
|
},
|
|
18812
|
-
strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}${stepNote}${navTimeoutNote(rec)}${exposureNote(rec)}${noticeNote(rec)}` : `Take ready: ${outDir}
|
|
18813
|
-
${(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}${stepNote}${navTimeoutNote(rec)}${pageSoundNote(rec)}
|
|
18814
|
-
Next: edit ${join33(outDir, "doc.json")} (optional), then
|
|
19074
|
+
strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}${stepNote}${navTimeoutNote(rec)}${kept.line}${exposureNote(rec)}${noticeNote(rec)}` : `Take ready: ${outDir}
|
|
19075
|
+
${(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}${stepNote}${navTimeoutNote(rec)}${pageSoundNote(rec)}${kept.line}
|
|
19076
|
+
Next: ${kept.next ? `${kept.next}, then` : `edit ${join33(outDir, "doc.json")} (optional), then:`} vos render ${outDir}${exposureNote(rec)}${noticeNote(rec)}${teardownNote(rec)}`
|
|
18815
19077
|
);
|
|
18816
19078
|
return strictFail ? EXIT_USAGE : EXIT_OK;
|
|
18817
19079
|
} finally {
|
|
@@ -18838,7 +19100,7 @@ async function cmdCreate2(argv) {
|
|
|
18838
19100
|
const scheme = urlSchemeProblem2(url);
|
|
18839
19101
|
if (scheme) throw new UsageError(scheme);
|
|
18840
19102
|
announceEnvNames(actions, r);
|
|
18841
|
-
const outDir =
|
|
19103
|
+
const outDir = resolve21(strFlag(flags, "out") ?? "take");
|
|
18842
19104
|
const format = videoFormat(strFlag(flags, "format"), positionals[0]);
|
|
18843
19105
|
const out = positionals[0] ?? join33(outDir, `out.${format}`);
|
|
18844
19106
|
const parallel = numFlag(flags, "parallel", 1);
|
|
@@ -18853,16 +19115,7 @@ async function cmdCreate2(argv) {
|
|
|
18853
19115
|
const headers = takeHeaders(multi);
|
|
18854
19116
|
const confirm = takeConfirm(multi, actions, { dryRun: false, r });
|
|
18855
19117
|
const paths = takePaths(outDir);
|
|
18856
|
-
const
|
|
18857
|
-
if (existsSync29(join33(outDir, "meta.json"))) {
|
|
18858
|
-
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
18859
|
-
r.log(
|
|
18860
|
-
`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" : "")
|
|
18861
|
-
);
|
|
18862
|
-
}
|
|
18863
|
-
await mkdir13(outDir, { recursive: true });
|
|
18864
|
-
await ensureTakeDir(outDir);
|
|
18865
|
-
};
|
|
19118
|
+
const cut = reRecord(outDir, flags, r);
|
|
18866
19119
|
const browser = await launchBrowser(browserArgs);
|
|
18867
19120
|
try {
|
|
18868
19121
|
r.log("recording\u2026");
|
|
@@ -18871,7 +19124,7 @@ async function cmdCreate2(argv) {
|
|
|
18871
19124
|
sessionName,
|
|
18872
19125
|
actions,
|
|
18873
19126
|
(context2) => recordTake(browser, url, actions, paths, r.log, {
|
|
18874
|
-
actionsDir: dirname11(
|
|
19127
|
+
actionsDir: dirname11(resolve21(actionsPath)),
|
|
18875
19128
|
maxDurationSeconds,
|
|
18876
19129
|
storageState,
|
|
18877
19130
|
headers,
|
|
@@ -18882,7 +19135,7 @@ async function cmdCreate2(argv) {
|
|
|
18882
19135
|
onEvent: (e) => r.event(e),
|
|
18883
19136
|
onArrival: wallGate(flags, r, {
|
|
18884
19137
|
strict: flags.strict === true,
|
|
18885
|
-
prepare
|
|
19138
|
+
prepare: cut.prepare
|
|
18886
19139
|
})
|
|
18887
19140
|
})
|
|
18888
19141
|
);
|
|
@@ -18895,7 +19148,8 @@ async function cmdCreate2(argv) {
|
|
|
18895
19148
|
);
|
|
18896
19149
|
await releaseFrames(paths, flags, r.log);
|
|
18897
19150
|
r.event({ event: "phase", phase: "plan" });
|
|
18898
|
-
await
|
|
19151
|
+
const plan = await cut.plan(backdrop);
|
|
19152
|
+
const kept = previousCutReport(outDir, cut.previousCut(), plan);
|
|
18899
19153
|
if (rec.stopped) {
|
|
18900
19154
|
r.done(
|
|
18901
19155
|
{
|
|
@@ -18903,9 +19157,11 @@ async function cmdCreate2(argv) {
|
|
|
18903
19157
|
ok: false,
|
|
18904
19158
|
stopped: rec.stopped,
|
|
18905
19159
|
skipped: rec.skipped,
|
|
18906
|
-
rendered: false
|
|
19160
|
+
rendered: false,
|
|
19161
|
+
...kept.fields,
|
|
19162
|
+
...teardownField(rec)
|
|
18907
19163
|
},
|
|
18908
|
-
`FAILED: ${rec.stopped.reason}. NOT rendering; the footage up to it is in ${outDir}
|
|
19164
|
+
`FAILED: ${rec.stopped.reason}. NOT rendering; the footage up to it is in ${outDir}.${kept.line}${teardownNote(rec)}`
|
|
18909
19165
|
);
|
|
18910
19166
|
return EXIT_ERROR;
|
|
18911
19167
|
}
|
|
@@ -18919,9 +19175,11 @@ async function cmdCreate2(argv) {
|
|
|
18919
19175
|
ready: rec.ready,
|
|
18920
19176
|
capped: rec.capped,
|
|
18921
19177
|
strictFailed: true,
|
|
18922
|
-
rendered: false
|
|
19178
|
+
rendered: false,
|
|
19179
|
+
...kept.fields,
|
|
19180
|
+
...teardownField(rec)
|
|
18923
19181
|
},
|
|
18924
|
-
`STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; NOT rendering. Fix the flow and re-run, or render the partial take with: vos render ${outDir}`
|
|
19182
|
+
`STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; NOT rendering. Fix the flow and re-run, or render the partial take with: vos render ${outDir}${kept.line}${teardownNote(rec)}`
|
|
18925
19183
|
);
|
|
18926
19184
|
return EXIT_USAGE;
|
|
18927
19185
|
}
|
|
@@ -18967,10 +19225,12 @@ async function cmdCreate2(argv) {
|
|
|
18967
19225
|
...rec.media.length ? { media: rec.media } : {},
|
|
18968
19226
|
overruns: rec.overruns,
|
|
18969
19227
|
pageErrors: rec.pageErrors,
|
|
18970
|
-
...
|
|
19228
|
+
...teardownField(rec),
|
|
19229
|
+
...flags.draft === true ? { draft: true } : {},
|
|
19230
|
+
...kept.fields
|
|
18971
19231
|
},
|
|
18972
19232
|
`Created ${result.out} (${(result.bytes / 1024).toFixed(0)} KB, ${result.width}x${result.height}@${result.fps}, ${result.duration.toFixed(1)}s, ${result.zoomSpans} zoom spans, ${result.clicks} click effects${result.audio ? ", audio" : ""}${flags.draft === true ? ", DRAFT" : ""})
|
|
18973
|
-
take: ${outDir} \u2014 inspect with: vos frames ${outDir} --at-zooms; edit doc.json and re-render with: vos render ${outDir}${stepReportNote(rec)}${navTimeoutNote(rec)}${exposureNote(rec)}${noticeNote(rec)}`
|
|
19233
|
+
take: ${outDir} \u2014 inspect with: vos frames ${outDir} --at-zooms; edit doc.json and re-render with: vos render ${outDir}${stepReportNote(rec)}${navTimeoutNote(rec)}${kept.line}${exposureNote(rec)}${noticeNote(rec)}${teardownNote(rec)}`
|
|
18974
19234
|
);
|
|
18975
19235
|
return EXIT_OK;
|
|
18976
19236
|
} finally {
|
|
@@ -18996,8 +19256,8 @@ async function cmdPlan(argvIn) {
|
|
|
18996
19256
|
}
|
|
18997
19257
|
let reuse;
|
|
18998
19258
|
if (flags.reuse === true) {
|
|
18999
|
-
const from =
|
|
19000
|
-
if (!
|
|
19259
|
+
const from = resolve21(strFlag(flags, "from") ?? join33(dir, PREV_DOC_NAME));
|
|
19260
|
+
if (!existsSync30(from)) {
|
|
19001
19261
|
throw new UsageError(
|
|
19002
19262
|
`--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
|
|
19003
19263
|
);
|
|
@@ -19008,7 +19268,7 @@ async function cmdPlan(argvIn) {
|
|
|
19008
19268
|
};
|
|
19009
19269
|
}
|
|
19010
19270
|
const style = await resolveStyleRef(flags);
|
|
19011
|
-
const hasDoc =
|
|
19271
|
+
const hasDoc = existsSync30(join33(dir, "doc.json"));
|
|
19012
19272
|
const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
|
|
19013
19273
|
const release = await releaseInputs(dir, flags, r, multi.with ?? []);
|
|
19014
19274
|
const stepCaptions = strFlag(flags, "captions") === "steps";
|
|
@@ -19086,7 +19346,7 @@ async function cmdPlan(argvIn) {
|
|
|
19086
19346
|
${s.reuse.flagged.join("\n ")}` : "") : "";
|
|
19087
19347
|
r.done(
|
|
19088
19348
|
{
|
|
19089
|
-
take:
|
|
19349
|
+
take: resolve21(dir),
|
|
19090
19350
|
fresh: s.fresh,
|
|
19091
19351
|
cursorKept: s.cursorKept,
|
|
19092
19352
|
zoomAuto,
|
|
@@ -19466,11 +19726,11 @@ async function resolveStyleRef(flags) {
|
|
|
19466
19726
|
}
|
|
19467
19727
|
async function resolveDocRef(ref, flags, what) {
|
|
19468
19728
|
const styleRef = ref;
|
|
19469
|
-
const file =
|
|
19729
|
+
const file = existsSync30(styleRef) ? resolve21(
|
|
19470
19730
|
styleRef,
|
|
19471
|
-
|
|
19731
|
+
existsSync30(join33(styleRef, "doc.json")) ? "doc.json" : ""
|
|
19472
19732
|
) : null;
|
|
19473
|
-
if (file &&
|
|
19733
|
+
if (file && existsSync30(file)) {
|
|
19474
19734
|
return {
|
|
19475
19735
|
from: file,
|
|
19476
19736
|
doc: migrateHostedDoc4(
|
|
@@ -19585,14 +19845,14 @@ async function cmdOpen(argv) {
|
|
|
19585
19845
|
if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
|
|
19586
19846
|
const r = createReporter(flags.json === true);
|
|
19587
19847
|
const take = await loadTake(dir);
|
|
19588
|
-
if (!
|
|
19848
|
+
if (!existsSync30(take.paths.recording)) {
|
|
19589
19849
|
throw new UsageError(`${dir} has no recording \u2014 re-run record`);
|
|
19590
19850
|
}
|
|
19591
19851
|
const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
|
|
19592
19852
|
/\/+$/,
|
|
19593
19853
|
""
|
|
19594
19854
|
);
|
|
19595
|
-
const server = await startTakeServer(
|
|
19855
|
+
const server = await startTakeServer(resolve21(dir), {});
|
|
19596
19856
|
const url = `${studio}/studio?take=${encodeURIComponent(server.base)}`;
|
|
19597
19857
|
r.event({ event: "open", server: server.base, url });
|
|
19598
19858
|
r.log(`take served at ${server.base}`);
|
|
@@ -19718,7 +19978,7 @@ async function cmdValidate(argv) {
|
|
|
19718
19978
|
const target = positionals[0];
|
|
19719
19979
|
if (!target) throw new UsageError("vos validate <actions.json|take>");
|
|
19720
19980
|
const r = createReporter(flags.json === true);
|
|
19721
|
-
const kitTarget = target.endsWith("kit.json") ? target :
|
|
19981
|
+
const kitTarget = target.endsWith("kit.json") ? target : existsSync30(join33(target, "kit.json")) && !isTakeDir(target) ? join33(target, "kit.json") : null;
|
|
19722
19982
|
if (kitTarget) {
|
|
19723
19983
|
const picture = flags.picture === true;
|
|
19724
19984
|
const verdict = await validateKit(kitTarget, { picture });
|
|
@@ -19750,7 +20010,7 @@ async function cmdValidate(argv) {
|
|
|
19750
20010
|
r.done({ valid: true, target }, `${target}: valid actions file`);
|
|
19751
20011
|
return EXIT_OK;
|
|
19752
20012
|
}
|
|
19753
|
-
if (!isTakeDir(target) &&
|
|
20013
|
+
if (!isTakeDir(target) && existsSync30(join33(target, "config.json"))) {
|
|
19754
20014
|
const parsed = JSON.parse(
|
|
19755
20015
|
await readFile18(join33(target, "config.json"), "utf8")
|
|
19756
20016
|
);
|
|
@@ -19794,7 +20054,7 @@ async function cmdValidate(argv) {
|
|
|
19794
20054
|
const problems = [];
|
|
19795
20055
|
const warnings = [];
|
|
19796
20056
|
const notes = [];
|
|
19797
|
-
if (!
|
|
20057
|
+
if (!existsSync30(take.paths.recording)) {
|
|
19798
20058
|
problems.push("missing recording (re-run record)");
|
|
19799
20059
|
} else {
|
|
19800
20060
|
const mismatch = containerMismatch(
|
|
@@ -19835,7 +20095,7 @@ async function cmdValidate(argv) {
|
|
|
19835
20095
|
const found = localFile(rel, target);
|
|
19836
20096
|
if (!("refused" in found)) continue;
|
|
19837
20097
|
problems.push(
|
|
19838
|
-
|
|
20098
|
+
existsSync30(resolve21(target, rel)) ? `doc.json: ${ref.where}: ${ref.key} ${found.refused}` : `doc.json: ${ref.where}: ${ref.key} is not a file in the take, so a render plays the film without it (put the file there, or fix the key)`
|
|
19839
20099
|
);
|
|
19840
20100
|
}
|
|
19841
20101
|
if (flags.picture === true && (take.doc.overlays ?? []).some((o) => o.kind === "html")) {
|
|
@@ -19914,7 +20174,7 @@ async function cmdPush3(argv) {
|
|
|
19914
20174
|
);
|
|
19915
20175
|
const refusal = takePushRefusal({
|
|
19916
20176
|
vos: strFlag(flags, "vos"),
|
|
19917
|
-
trackedVosId: readSyncState(
|
|
20177
|
+
trackedVosId: readSyncState(resolve21(dir))?.vosId ?? null,
|
|
19918
20178
|
programOnly: ["slug", "desc", "tags", "base", "remix-of", "still"].filter(
|
|
19919
20179
|
(name) => hasFlag(flags, name)
|
|
19920
20180
|
)
|
|
@@ -19930,7 +20190,7 @@ async function cmdPush3(argv) {
|
|
|
19930
20190
|
`--claimable creates a NEW claimable vos; ${iterating.map((f) => `--${f}`).join(", ")} ${iterating.length > 1 ? "apply" : "applies"} to a keyed push`
|
|
19931
20191
|
);
|
|
19932
20192
|
const claim = await pushTakeClaimable(
|
|
19933
|
-
|
|
20193
|
+
resolve21(dir),
|
|
19934
20194
|
{
|
|
19935
20195
|
api: strFlag(flags, "api"),
|
|
19936
20196
|
origin: strFlag(flags, "origin"),
|
|
@@ -19953,7 +20213,7 @@ reference and the only credential.`
|
|
|
19953
20213
|
}
|
|
19954
20214
|
const overrides = multi.override;
|
|
19955
20215
|
const result = await pushTake(
|
|
19956
|
-
|
|
20216
|
+
resolve21(dir),
|
|
19957
20217
|
{
|
|
19958
20218
|
key: strFlag(flags, "key"),
|
|
19959
20219
|
api: strFlag(flags, "api"),
|
|
@@ -20008,7 +20268,7 @@ async function cmdPull2(argv) {
|
|
|
20008
20268
|
const dir = positionals[0] ?? ".";
|
|
20009
20269
|
const r = createReporter(flags.json === true);
|
|
20010
20270
|
const result = await pullTake(
|
|
20011
|
-
|
|
20271
|
+
resolve21(dir),
|
|
20012
20272
|
{
|
|
20013
20273
|
key: strFlag(flags, "key"),
|
|
20014
20274
|
api: strFlag(flags, "api"),
|
|
@@ -20183,10 +20443,13 @@ function verdictExit(v, strict, none) {
|
|
|
20183
20443
|
return strict ? EXIT_WALL : EXIT_OK;
|
|
20184
20444
|
}
|
|
20185
20445
|
async function cmdSession(argv) {
|
|
20186
|
-
const { positionals, flags } = parseArgs(
|
|
20446
|
+
const { positionals, flags } = parseArgs(
|
|
20447
|
+
argv,
|
|
20448
|
+
/* @__PURE__ */ new Set([...BOOLEAN_FLAGS15, "headed"])
|
|
20449
|
+
);
|
|
20187
20450
|
const [sub, arg] = positionals;
|
|
20188
20451
|
const r = createReporter(flags.json === true);
|
|
20189
|
-
const usage = "vos session open <url> --name <app> | check <name> [--url <url>] | list | rm <name>";
|
|
20452
|
+
const usage = "vos session open <url> --name <app> | check <name> [--url <url>] | exec <name> <script.mjs> [--url <url>] [--headed] [--viewport WxH] | list | rm <name>";
|
|
20190
20453
|
switch (sub) {
|
|
20191
20454
|
case "open": {
|
|
20192
20455
|
if (!arg) throw new UsageError(usage);
|
|
@@ -20227,7 +20490,7 @@ async function cmdSession(argv) {
|
|
|
20227
20490
|
if (!arg) throw new UsageError(usage);
|
|
20228
20491
|
const bad = sessionNameVerdict(arg);
|
|
20229
20492
|
if (bad) throw new UsageError(bad);
|
|
20230
|
-
if (!
|
|
20493
|
+
if (!existsSync30(sessionDir(arg)))
|
|
20231
20494
|
throw new UsageError(`no session "${arg}" (vos session list)`);
|
|
20232
20495
|
const summary3 = await summarizeSession(arg);
|
|
20233
20496
|
const url = strFlag(flags, "url");
|
|
@@ -20252,10 +20515,58 @@ async function cmdSession(argv) {
|
|
|
20252
20515
|
origins: summary3.origins,
|
|
20253
20516
|
newestExpires: summary3.newestExpires
|
|
20254
20517
|
},
|
|
20255
|
-
`session "${arg}": ${verdictLine(verdict, url, arg)}${verdict.state === "not-signed-in" ? `. It expired,
|
|
20518
|
+
`session "${arg}": ${verdictLine(verdict, url, arg)}${verdict.state === "not-signed-in" ? `. It expired, the sign-in never landed, or the profile was opened by a browser that cannot read its cookies (a script launching it itself signs it out; run scripts with vos session exec). Sign in again: vos session open ${url} --name ${arg}` : `. ${summaryLine(summary3)}`}`
|
|
20256
20519
|
);
|
|
20257
20520
|
return verdictExit(verdict, flags.strict === true, EXIT_WALL);
|
|
20258
20521
|
}
|
|
20522
|
+
case "exec": {
|
|
20523
|
+
const scriptPath = positionals[2];
|
|
20524
|
+
if (!arg || !scriptPath) throw new UsageError(usage);
|
|
20525
|
+
const bad = sessionNameVerdict(arg);
|
|
20526
|
+
if (bad) throw new UsageError(bad);
|
|
20527
|
+
if (!existsSync30(sessionDir(arg)))
|
|
20528
|
+
throw new UsageError(`no session "${arg}" (vos session list)`);
|
|
20529
|
+
const url = strFlag(flags, "url");
|
|
20530
|
+
if (url && !/^https?:\/\//.test(url))
|
|
20531
|
+
throw new UsageError("--url wants an http(s) URL");
|
|
20532
|
+
const viewport = strFlag(flags, "viewport");
|
|
20533
|
+
const size = viewport ? /^(\d+)x(\d+)$/.exec(viewport) : null;
|
|
20534
|
+
if (viewport && !size)
|
|
20535
|
+
throw new UsageError("--viewport wants WxH, e.g. 1280x720");
|
|
20536
|
+
let script;
|
|
20537
|
+
try {
|
|
20538
|
+
script = await loadExecScript(scriptPath);
|
|
20539
|
+
} catch (e) {
|
|
20540
|
+
if (e instanceof ExecScriptError) throw new UsageError(e.message);
|
|
20541
|
+
throw new UsageError(
|
|
20542
|
+
`${scriptPath} could not be loaded: ${e instanceof Error ? e.message.split("\n")[0] : String(e)}`
|
|
20543
|
+
);
|
|
20544
|
+
}
|
|
20545
|
+
let result;
|
|
20546
|
+
try {
|
|
20547
|
+
result = await execInProfile(sessionDir(arg), script, {
|
|
20548
|
+
name: arg,
|
|
20549
|
+
...url ? { url } : {},
|
|
20550
|
+
headless: flags.headed !== true,
|
|
20551
|
+
...size ? { viewport: { width: +size[1], height: +size[2] } } : {}
|
|
20552
|
+
});
|
|
20553
|
+
} catch (e) {
|
|
20554
|
+
r.done(
|
|
20555
|
+
{
|
|
20556
|
+
name: arg,
|
|
20557
|
+
ok: false,
|
|
20558
|
+
error: e instanceof Error ? e.message.split("\n")[0] : String(e)
|
|
20559
|
+
},
|
|
20560
|
+
`the script failed in session "${arg}": ${e instanceof Error ? e.message.split("\n")[0] : String(e)} (the session was closed cleanly)`
|
|
20561
|
+
);
|
|
20562
|
+
return EXIT_ERROR;
|
|
20563
|
+
}
|
|
20564
|
+
r.done(
|
|
20565
|
+
{ name: arg, ok: true, ...result !== void 0 ? { result } : {} },
|
|
20566
|
+
result === void 0 ? `ran ${scriptPath} in session "${arg}"` : typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
20567
|
+
);
|
|
20568
|
+
return EXIT_OK;
|
|
20569
|
+
}
|
|
20259
20570
|
case "list": {
|
|
20260
20571
|
const rows = listSessions();
|
|
20261
20572
|
r.done(
|
|
@@ -20300,8 +20611,10 @@ export {
|
|
|
20300
20611
|
HELP,
|
|
20301
20612
|
strictFails,
|
|
20302
20613
|
takeBrowserArgs,
|
|
20614
|
+
reRecord,
|
|
20615
|
+
previousCutReport,
|
|
20303
20616
|
takeBackdrop,
|
|
20304
20617
|
verbHelp,
|
|
20305
20618
|
run
|
|
20306
20619
|
};
|
|
20307
|
-
//# sourceMappingURL=chunk-
|
|
20620
|
+
//# sourceMappingURL=chunk-S3RI2SHH.js.map
|