@vosjs/cli 0.46.1 → 0.48.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 +15 -5
- package/dist/{chunk-UE66DC2K.js → chunk-ANKECV7F.js} +658 -60
- package/dist/chunk-ANKECV7F.js.map +1 -0
- package/dist/{chunk-ID4OUS5L.js → chunk-AUPGJJTL.js} +18 -1
- package/dist/chunk-AUPGJJTL.js.map +1 -0
- package/dist/{chunk-EFI3WUQB.js → chunk-P3X5KVA6.js} +15 -2
- package/dist/{chunk-EFI3WUQB.js.map → chunk-P3X5KVA6.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.js +3 -3
- package/dist/manifest-A4R367EM.js +8 -0
- package/dist/{run-2OZS25ER.js → run-CYHH56KA.js} +3 -3
- package/dist/{shotList-JC4A3FBT.js → shotList-7F2FE3RU.js} +2 -2
- package/package.json +9 -7
- package/schema/actions.schema.json +2 -2
- package/skills/VERSION +1 -0
- package/skills/launch-kit/SKILL.md +356 -0
- package/skills/launch-kit/references/channel-specs.md +56 -0
- package/skills/product-video/SKILL.md +263 -0
- package/skills/product-video/references/destinations.md +71 -0
- package/skills/product-video/references/sessions.md +226 -0
- package/skills/product-video/references/taste.md +115 -0
- package/skills/product-video/references/troubleshooting.md +116 -0
- package/skills/vos-authoring/SKILL.md +191 -0
- package/skills/vos-authoring/references/examples.md +239 -0
- package/skills/vos-authoring/references/schema-reference.md +468 -0
- package/skills/vos-create/SKILL.md +258 -0
- package/skills/vos-cut/SKILL.md +241 -0
- package/skills/vos-footage/SKILL.md +98 -0
- package/skills/vos-migrate/SKILL.md +108 -0
- package/skills/vos-remix/SKILL.md +136 -0
- package/skills/vos-remix/references/3d-recipe.md +37 -0
- package/skills/vos-remix/references/params-knobs.md +80 -0
- package/skills/vos-remix/references/remix-contract.md +81 -0
- package/dist/chunk-ID4OUS5L.js.map +0 -1
- package/dist/chunk-UE66DC2K.js.map +0 -1
- package/dist/manifest-UCS6OVWZ.js +0 -8
- /package/dist/{manifest-UCS6OVWZ.js.map → manifest-A4R367EM.js.map} +0 -0
- /package/dist/{run-2OZS25ER.js.map → run-CYHH56KA.js.map} +0 -0
- /package/dist/{shotList-JC4A3FBT.js.map → shotList-7F2FE3RU.js.map} +0 -0
|
@@ -17,16 +17,18 @@ import {
|
|
|
17
17
|
deadLine,
|
|
18
18
|
deadTime,
|
|
19
19
|
deadWarns,
|
|
20
|
+
holdLeftMs,
|
|
20
21
|
paceLine,
|
|
21
22
|
paceReport,
|
|
22
23
|
pointerTravelMs,
|
|
23
|
-
settleMs
|
|
24
|
-
|
|
24
|
+
settleMs,
|
|
25
|
+
settleVerdict
|
|
26
|
+
} from "./chunk-AUPGJJTL.js";
|
|
25
27
|
|
|
26
28
|
// src/plugin/run.ts
|
|
27
29
|
import { mkdir as mkdir10, readFile as readFile15, rm as rm5 } from "fs/promises";
|
|
28
|
-
import { existsSync as
|
|
29
|
-
import { join as
|
|
30
|
+
import { existsSync as existsSync18 } from "fs";
|
|
31
|
+
import { join as join21, resolve as resolve9 } from "path";
|
|
30
32
|
import {
|
|
31
33
|
END_CARD_FROM as END_CARD_FROM2,
|
|
32
34
|
docOutputDuration as docOutputDuration3,
|
|
@@ -2391,6 +2393,16 @@ function startTakeServer(rootDir, pages) {
|
|
|
2391
2393
|
const server = createServer((req, res) => {
|
|
2392
2394
|
const url = new URL(req.url ?? "/", "http://x");
|
|
2393
2395
|
res.setHeader("access-control-allow-origin", "*");
|
|
2396
|
+
res.setHeader("access-control-allow-private-network", "true");
|
|
2397
|
+
if (req.method === "OPTIONS") {
|
|
2398
|
+
res.writeHead(204, {
|
|
2399
|
+
"access-control-allow-methods": "GET, POST, OPTIONS",
|
|
2400
|
+
"access-control-allow-headers": "*",
|
|
2401
|
+
"access-control-max-age": "600"
|
|
2402
|
+
});
|
|
2403
|
+
res.end();
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2394
2406
|
if (req.method === "POST" && url.pathname === "/save") {
|
|
2395
2407
|
const name = (url.searchParams.get("name") ?? "out.bin").replace(
|
|
2396
2408
|
/[^\w.-]/g,
|
|
@@ -5118,7 +5130,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
|
|
|
5118
5130
|
const vh = meta.captureHeight ?? meta.height;
|
|
5119
5131
|
const probeDir = await mkdtemp(join8(tmpdir(), "vos-moments-"));
|
|
5120
5132
|
try {
|
|
5121
|
-
const
|
|
5133
|
+
const probe2 = await framesTake(browser, dir, {
|
|
5122
5134
|
times: candidates.map((c) => c.time),
|
|
5123
5135
|
width: PROBE_WIDTH,
|
|
5124
5136
|
height: Math.max(2, Math.round(PROBE_WIDTH * vh / vw / 2) * 2),
|
|
@@ -5129,7 +5141,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
|
|
|
5129
5141
|
}
|
|
5130
5142
|
});
|
|
5131
5143
|
const byTime = /* @__PURE__ */ new Map();
|
|
5132
|
-
for (const frame of
|
|
5144
|
+
for (const frame of probe2.frames) {
|
|
5133
5145
|
const img = decodePng(new Uint8Array(await readFile6(frame.file)));
|
|
5134
5146
|
if (!img) continue;
|
|
5135
5147
|
const whole = { x: 0, y: 0, w: img.w, h: img.h };
|
|
@@ -7194,6 +7206,19 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
7194
7206
|
rect
|
|
7195
7207
|
});
|
|
7196
7208
|
};
|
|
7209
|
+
const waitSettled = async () => {
|
|
7210
|
+
if (dry) return null;
|
|
7211
|
+
const pressAt = now();
|
|
7212
|
+
const before = frames.length;
|
|
7213
|
+
for (; ; ) {
|
|
7214
|
+
const t = now();
|
|
7215
|
+
const last = frames.length > before ? frames[frames.length - 1] : null;
|
|
7216
|
+
const sinceChange = last ? t - last.tMs : null;
|
|
7217
|
+
if (settleVerdict({ sincePress: t - pressAt, sinceChange }) === "settled")
|
|
7218
|
+
return sinceChange;
|
|
7219
|
+
await sleep(40);
|
|
7220
|
+
}
|
|
7221
|
+
};
|
|
7197
7222
|
const skipped = [];
|
|
7198
7223
|
const stepSpans = [];
|
|
7199
7224
|
const paces = [];
|
|
@@ -7238,7 +7263,8 @@ async function recordTake(browser, url, actions, paths, log, opts = {}) {
|
|
|
7238
7263
|
if (rect) {
|
|
7239
7264
|
await gesture(() => clickAt(rect));
|
|
7240
7265
|
log(`click ${step.selector}`);
|
|
7241
|
-
await gesture(() =>
|
|
7266
|
+
const sinceChange = await gesture(() => waitSettled());
|
|
7267
|
+
await sleep(holdLeftMs(settleMs(step), sinceChange));
|
|
7242
7268
|
} else
|
|
7243
7269
|
skipped.push({ step: stepIdx, do: step.do, selector: step.selector });
|
|
7244
7270
|
break;
|
|
@@ -7911,6 +7937,7 @@ function planOtherMedia(doc, activity) {
|
|
|
7911
7937
|
...planAutoZoom(m.cursor, {
|
|
7912
7938
|
width: m.meta.width,
|
|
7913
7939
|
height: m.meta.height,
|
|
7940
|
+
duration: m.meta.durationMs / 1e3,
|
|
7914
7941
|
style: doc.zoomStyle,
|
|
7915
7942
|
params: doc.zoomParams
|
|
7916
7943
|
}).filter((z) => !manualZoom.some((x) => overlaps(z, x))).filter((z) => !isRejected("zoom", z, rejected)).map((z) => ({ ...z, id: `${z.id}-${m.id}`, media: m.id }))
|
|
@@ -7953,6 +7980,7 @@ async function planTake(dir, opts = {}) {
|
|
|
7953
7980
|
const autoZoom = planAutoZoom(doc.source.cursor, {
|
|
7954
7981
|
width: doc.source.meta.width,
|
|
7955
7982
|
height: doc.source.meta.height,
|
|
7983
|
+
duration: doc.source.meta.durationMs / 1e3,
|
|
7956
7984
|
style: doc.zoomStyle,
|
|
7957
7985
|
params: doc.zoomParams
|
|
7958
7986
|
}).filter((z) => !manualZoom.some((m) => overlaps(z, m))).filter((z) => !isRejected("zoom", z, doc.rejected));
|
|
@@ -8005,6 +8033,7 @@ async function planTake(dir, opts = {}) {
|
|
|
8005
8033
|
const auto = planAutoZoom(doc.source.cursor, {
|
|
8006
8034
|
width: doc.source.meta.width,
|
|
8007
8035
|
height: doc.source.meta.height,
|
|
8036
|
+
duration: doc.source.meta.durationMs / 1e3,
|
|
8008
8037
|
style: doc.zoomStyle,
|
|
8009
8038
|
params: doc.zoomParams
|
|
8010
8039
|
}).filter((z) => !manual.some((m) => overlaps(z, m))).filter((z) => !isRejected("zoom", z, doc.rejected));
|
|
@@ -8044,6 +8073,7 @@ async function planTake(dir, opts = {}) {
|
|
|
8044
8073
|
doc.zoom = planAutoZoom(doc.source.cursor, {
|
|
8045
8074
|
width: doc.source.meta.width,
|
|
8046
8075
|
height: doc.source.meta.height,
|
|
8076
|
+
duration: doc.source.meta.durationMs / 1e3,
|
|
8047
8077
|
style: doc.zoomStyle,
|
|
8048
8078
|
params: doc.zoomParams
|
|
8049
8079
|
});
|
|
@@ -9603,9 +9633,9 @@ async function cmdLogin(argv) {
|
|
|
9603
9633
|
return storeKey(key, origin, r);
|
|
9604
9634
|
}
|
|
9605
9635
|
async function storeKey(key, origin, r) {
|
|
9606
|
-
const
|
|
9607
|
-
if (
|
|
9608
|
-
throw new Error(apiError("validate key",
|
|
9636
|
+
const probe2 = await apiJson(origin, "/api/folders", { key });
|
|
9637
|
+
if (probe2.status === 401 || probe2.status === 403) {
|
|
9638
|
+
throw new Error(apiError("validate key", probe2));
|
|
9609
9639
|
}
|
|
9610
9640
|
const path = writeCredential(key);
|
|
9611
9641
|
r.done(
|
|
@@ -10734,7 +10764,7 @@ function summary(result) {
|
|
|
10734
10764
|
async function cmdActionsScript(input, flags) {
|
|
10735
10765
|
const r = createReporter(flags.json === true);
|
|
10736
10766
|
const { readFile: readFile16 } = await import("fs/promises");
|
|
10737
|
-
const { shotList, formatShotList } = await import("./shotList-
|
|
10767
|
+
const { shotList, formatShotList } = await import("./shotList-7F2FE3RU.js");
|
|
10738
10768
|
const raw = JSON.parse(await readFile16(resolve8(input), "utf8"));
|
|
10739
10769
|
const errors = validateActions(raw);
|
|
10740
10770
|
if (errors.length) throw new UsageError(`${input}:
|
|
@@ -10744,8 +10774,551 @@ async function cmdActionsScript(input, flags) {
|
|
|
10744
10774
|
return EXIT_OK;
|
|
10745
10775
|
}
|
|
10746
10776
|
|
|
10747
|
-
// src/plugin/
|
|
10777
|
+
// src/plugin/machineCmd.ts
|
|
10778
|
+
import { spawnSync } from "child_process";
|
|
10779
|
+
import {
|
|
10780
|
+
cpSync,
|
|
10781
|
+
existsSync as existsSync17,
|
|
10782
|
+
mkdirSync as mkdirSync3,
|
|
10783
|
+
readFileSync as readFileSync8,
|
|
10784
|
+
rmSync as rmSync2,
|
|
10785
|
+
writeFileSync as writeFileSync3
|
|
10786
|
+
} from "fs";
|
|
10787
|
+
import { homedir as homedir3 } from "os";
|
|
10788
|
+
import { join as join20 } from "path";
|
|
10789
|
+
import { chromium as chromium2 } from "playwright";
|
|
10790
|
+
|
|
10791
|
+
// src/plugin/machine.ts
|
|
10792
|
+
import { existsSync as existsSync16, readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
10793
|
+
import { dirname as dirname5, join as join19 } from "path";
|
|
10794
|
+
var AGENT_NAMES = [
|
|
10795
|
+
"claude",
|
|
10796
|
+
"cursor",
|
|
10797
|
+
"codex",
|
|
10798
|
+
"copilot"
|
|
10799
|
+
];
|
|
10800
|
+
var PROJECT_DIRS = {
|
|
10801
|
+
claude: [".claude"],
|
|
10802
|
+
cursor: [".cursor"],
|
|
10803
|
+
codex: [".codex", ".agents"],
|
|
10804
|
+
copilot: [".github/copilot"]
|
|
10805
|
+
};
|
|
10806
|
+
var HOME_DIRS = {
|
|
10807
|
+
claude: [".claude"],
|
|
10808
|
+
cursor: [".cursor"],
|
|
10809
|
+
codex: [".codex", ".agents"],
|
|
10810
|
+
copilot: [".copilot"]
|
|
10811
|
+
};
|
|
10812
|
+
function detectAgents(root, opts = {}) {
|
|
10813
|
+
const exists = opts.exists ?? existsSync16;
|
|
10814
|
+
const out = [];
|
|
10815
|
+
for (const agent of AGENT_NAMES) {
|
|
10816
|
+
for (const rel of PROJECT_DIRS[agent]) {
|
|
10817
|
+
const dir = join19(root, rel);
|
|
10818
|
+
if (exists(dir)) out.push({ agent, dir, scope: "project" });
|
|
10819
|
+
}
|
|
10820
|
+
if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(join19(root, ".github", "copilot-instructions.md"))) {
|
|
10821
|
+
out.push({
|
|
10822
|
+
agent,
|
|
10823
|
+
dir: join19(root, ".github", "copilot"),
|
|
10824
|
+
scope: "project"
|
|
10825
|
+
});
|
|
10826
|
+
}
|
|
10827
|
+
}
|
|
10828
|
+
if (opts.global && opts.home) {
|
|
10829
|
+
for (const agent of AGENT_NAMES) {
|
|
10830
|
+
for (const rel of HOME_DIRS[agent]) {
|
|
10831
|
+
const dir = join19(opts.home, rel);
|
|
10832
|
+
if (exists(dir)) out.push({ agent, dir, scope: "global" });
|
|
10833
|
+
}
|
|
10834
|
+
}
|
|
10835
|
+
}
|
|
10836
|
+
return out;
|
|
10837
|
+
}
|
|
10838
|
+
function agentsFromFlag(value, root) {
|
|
10839
|
+
const wanted = value.trim() === "all" ? [...AGENT_NAMES] : value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10840
|
+
const bad = wanted.filter((w) => !AGENT_NAMES.includes(w));
|
|
10841
|
+
if (bad.length)
|
|
10842
|
+
return {
|
|
10843
|
+
error: `--agent takes ${AGENT_NAMES.join(", ")} or all, not "${bad.join(", ")}"`
|
|
10844
|
+
};
|
|
10845
|
+
if (!wanted.length) return { error: "--agent expects a name" };
|
|
10846
|
+
return {
|
|
10847
|
+
homes: wanted.map((agent) => ({
|
|
10848
|
+
agent,
|
|
10849
|
+
dir: join19(root, PROJECT_DIRS[agent][0]),
|
|
10850
|
+
scope: "project"
|
|
10851
|
+
}))
|
|
10852
|
+
};
|
|
10853
|
+
}
|
|
10854
|
+
function skillsDirFor(home) {
|
|
10855
|
+
return join19(home.dir, "skills");
|
|
10856
|
+
}
|
|
10857
|
+
function fallbackSkillsHome(root) {
|
|
10858
|
+
return { agent: "codex", dir: join19(root, ".agents"), scope: "project" };
|
|
10859
|
+
}
|
|
10860
|
+
function installedSkills(dir, exists = existsSync16, list = (p) => {
|
|
10861
|
+
try {
|
|
10862
|
+
return readdirSync4(p);
|
|
10863
|
+
} catch {
|
|
10864
|
+
return [];
|
|
10865
|
+
}
|
|
10866
|
+
}) {
|
|
10867
|
+
if (!exists(dir)) return [];
|
|
10868
|
+
return list(dir).filter((name) => exists(join19(dir, name, "SKILL.md"))).sort();
|
|
10869
|
+
}
|
|
10870
|
+
var RULES_BEGIN = "<!-- vos:begin (written by vos setup; the block is replaced on the next run) -->";
|
|
10871
|
+
var RULES_END = "<!-- vos:end -->";
|
|
10872
|
+
var RULES_BODY = `## Product videos (vosso)
|
|
10873
|
+
- Skill: product-video (npx skills add vosjs/skills) records the real product from actions.json
|
|
10874
|
+
- Look before you cut: vos frames take --at-zooms; edit take/doc.json and re-render, never re-record
|
|
10875
|
+
- Hand back: vos open take serves the take into the studio at vos.so, doc.json intact
|
|
10876
|
+
- Local by default: vos push only when asked for a link, the shelf or a cloud render
|
|
10877
|
+
- Credentials: VOS_API_KEY, then ~/.config/vos/credentials, then vos login; never print one
|
|
10878
|
+
- Reference: https://vos.so/llms-full.txt`;
|
|
10879
|
+
var RULES_BLOCK = `${RULES_BEGIN}
|
|
10880
|
+
${RULES_BODY}
|
|
10881
|
+
${RULES_END}`;
|
|
10882
|
+
function applyRulesBlock(existing) {
|
|
10883
|
+
const current = existing ?? "";
|
|
10884
|
+
const begin = current.indexOf(RULES_BEGIN);
|
|
10885
|
+
const end = current.indexOf(RULES_END);
|
|
10886
|
+
let text;
|
|
10887
|
+
if (begin !== -1 && end !== -1 && end > begin) {
|
|
10888
|
+
text = current.slice(0, begin) + RULES_BLOCK + current.slice(end + RULES_END.length);
|
|
10889
|
+
} else if (current.trim() === "") {
|
|
10890
|
+
text = `${RULES_BLOCK}
|
|
10891
|
+
`;
|
|
10892
|
+
} else {
|
|
10893
|
+
text = `${current.replace(/\s*$/, "")}
|
|
10894
|
+
|
|
10895
|
+
${RULES_BLOCK}
|
|
10896
|
+
`;
|
|
10897
|
+
}
|
|
10898
|
+
return { text, changed: text !== current };
|
|
10899
|
+
}
|
|
10900
|
+
function rulesFileFor(root, exists = existsSync16) {
|
|
10901
|
+
const agents = join19(root, "AGENTS.md");
|
|
10902
|
+
if (exists(agents)) return agents;
|
|
10903
|
+
const claude = join19(root, "CLAUDE.md");
|
|
10904
|
+
if (exists(claude)) return claude;
|
|
10905
|
+
return agents;
|
|
10906
|
+
}
|
|
10907
|
+
function chromeCandidates(platform, env) {
|
|
10908
|
+
if (platform === "darwin")
|
|
10909
|
+
return ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
|
|
10910
|
+
if (platform === "win32")
|
|
10911
|
+
return [
|
|
10912
|
+
join19(
|
|
10913
|
+
env["PROGRAMFILES"] ?? "C:\\Program Files",
|
|
10914
|
+
"Google",
|
|
10915
|
+
"Chrome",
|
|
10916
|
+
"Application",
|
|
10917
|
+
"chrome.exe"
|
|
10918
|
+
),
|
|
10919
|
+
join19(
|
|
10920
|
+
env["LOCALAPPDATA"] ?? "",
|
|
10921
|
+
"Google",
|
|
10922
|
+
"Chrome",
|
|
10923
|
+
"Application",
|
|
10924
|
+
"chrome.exe"
|
|
10925
|
+
)
|
|
10926
|
+
];
|
|
10927
|
+
return [
|
|
10928
|
+
"/usr/bin/google-chrome",
|
|
10929
|
+
"/usr/bin/google-chrome-stable",
|
|
10930
|
+
"/opt/google/chrome/chrome"
|
|
10931
|
+
];
|
|
10932
|
+
}
|
|
10933
|
+
function probeBrowser(opts) {
|
|
10934
|
+
const explicit = opts.env.VOS_BROWSER_PATH?.trim();
|
|
10935
|
+
if (explicit) {
|
|
10936
|
+
return opts.exists(explicit) ? { kind: "env", path: explicit, line: `${explicit} (VOS_BROWSER_PATH)` } : {
|
|
10937
|
+
kind: "none",
|
|
10938
|
+
path: null,
|
|
10939
|
+
line: `VOS_BROWSER_PATH points at ${explicit}, which does not exist`
|
|
10940
|
+
};
|
|
10941
|
+
}
|
|
10942
|
+
const chrome = chromeCandidates(opts.platform, opts.env).find(opts.exists);
|
|
10943
|
+
if (chrome) return { kind: "chrome", path: chrome, line: "Chrome (system)" };
|
|
10944
|
+
const chromium3 = opts.bundled();
|
|
10945
|
+
if (chromium3)
|
|
10946
|
+
return { kind: "chromium", path: chromium3, line: "Chromium (Playwright)" };
|
|
10947
|
+
return {
|
|
10948
|
+
kind: "none",
|
|
10949
|
+
path: null,
|
|
10950
|
+
line: "none: install Google Chrome, or run npx playwright install chromium"
|
|
10951
|
+
};
|
|
10952
|
+
}
|
|
10953
|
+
var NODE_MIN_MAJOR = 18;
|
|
10954
|
+
function major(version) {
|
|
10955
|
+
return parseInt(version.split(".")[0] ?? "0", 10);
|
|
10956
|
+
}
|
|
10957
|
+
function doctorReport(facts) {
|
|
10958
|
+
const lines = [];
|
|
10959
|
+
const nodeOk = major(facts.node) >= NODE_MIN_MAJOR;
|
|
10960
|
+
lines.push(
|
|
10961
|
+
`node ${facts.node} ${nodeOk ? "\u2713" : `\u2717 ${NODE_MIN_MAJOR} or newer is needed`}`
|
|
10962
|
+
);
|
|
10963
|
+
const browserOk = facts.browser.kind !== "none";
|
|
10964
|
+
lines.push(`browser ${facts.browser.line} ${browserOk ? "\u2713" : "\u2717"}`);
|
|
10965
|
+
lines.push(
|
|
10966
|
+
`ffmpeg ${facts.ffmpeg ? `${facts.ffmpeg} \u2713` : "absent (optional)"}`
|
|
10967
|
+
);
|
|
10968
|
+
const withSkills = facts.skills.filter((s) => s.names.length);
|
|
10969
|
+
if (withSkills.length) {
|
|
10970
|
+
const names = [...new Set(withSkills.flatMap((s) => s.names))].sort();
|
|
10971
|
+
const agents = [
|
|
10972
|
+
...new Set(
|
|
10973
|
+
withSkills.map(
|
|
10974
|
+
(s) => s.scope === "global" ? `${s.agent} (global)` : s.agent
|
|
10975
|
+
)
|
|
10976
|
+
)
|
|
10977
|
+
];
|
|
10978
|
+
lines.push(`skills ${names.join(", ")} (${agents.join(", ")}) \u2713`);
|
|
10979
|
+
} else {
|
|
10980
|
+
lines.push("skills none \u2717 run: npx vos setup");
|
|
10981
|
+
}
|
|
10982
|
+
lines.push(
|
|
10983
|
+
`credential ${facts.credential ? "present \u2713" : "absent (vos login when you need hosting)"}`
|
|
10984
|
+
);
|
|
10985
|
+
if (facts.devServer) {
|
|
10986
|
+
const d = facts.devServer;
|
|
10987
|
+
lines.push(
|
|
10988
|
+
`dev server ${d.url} ${d.ok ? "answers \u2713" : `does not answer \u2717${d.error ? ` (${d.error})` : d.status ? ` (HTTP ${d.status})` : ""}`}`
|
|
10989
|
+
);
|
|
10990
|
+
}
|
|
10991
|
+
let next_step;
|
|
10992
|
+
if (!nodeOk)
|
|
10993
|
+
next_step = `Install Node ${NODE_MIN_MAJOR} or newer, then run npx vos doctor again`;
|
|
10994
|
+
else if (!browserOk)
|
|
10995
|
+
next_step = "Install Google Chrome, or run: npx playwright install chromium";
|
|
10996
|
+
else if (facts.devServer && !facts.devServer.ok)
|
|
10997
|
+
next_step = `Start the dev server so ${facts.devServer.url} answers, then record`;
|
|
10998
|
+
else if (!withSkills.length) next_step = "Run: npx vos setup";
|
|
10999
|
+
else
|
|
11000
|
+
next_step = "Record: npx vos record --actions actions.json --out take --strict (the product-video skill writes actions.json)";
|
|
11001
|
+
return { ok: nodeOk && browserOk, lines, next_step, facts };
|
|
11002
|
+
}
|
|
11003
|
+
function bundledSkillsDir(from) {
|
|
11004
|
+
let dir = dirname5(from);
|
|
11005
|
+
for (let i = 0; i < 6; i++) {
|
|
11006
|
+
const pkg = join19(dir, "package.json");
|
|
11007
|
+
if (existsSync16(pkg)) {
|
|
11008
|
+
try {
|
|
11009
|
+
const name = JSON.parse(readFileSync7(pkg, "utf8")).name;
|
|
11010
|
+
if (name === "@vosjs/cli") {
|
|
11011
|
+
const skills = join19(dir, "skills");
|
|
11012
|
+
return existsSync16(skills) && statSync4(skills).isDirectory() ? skills : null;
|
|
11013
|
+
}
|
|
11014
|
+
} catch {
|
|
11015
|
+
}
|
|
11016
|
+
}
|
|
11017
|
+
const up = dirname5(dir);
|
|
11018
|
+
if (up === dir) break;
|
|
11019
|
+
dir = up;
|
|
11020
|
+
}
|
|
11021
|
+
return null;
|
|
11022
|
+
}
|
|
11023
|
+
|
|
11024
|
+
// src/plugin/machineCmd.ts
|
|
10748
11025
|
var BOOLEAN_FLAGS5 = /* @__PURE__ */ new Set([
|
|
11026
|
+
"json",
|
|
11027
|
+
"help",
|
|
11028
|
+
"global",
|
|
11029
|
+
"no-skills",
|
|
11030
|
+
"no-rules",
|
|
11031
|
+
"no-browser"
|
|
11032
|
+
]);
|
|
11033
|
+
var SKILLS_SOURCE = "vosjs/skills";
|
|
11034
|
+
function reporterFor(flags) {
|
|
11035
|
+
return createReporter(flags.json === true || process.stdout.isTTY !== true);
|
|
11036
|
+
}
|
|
11037
|
+
function npx() {
|
|
11038
|
+
return process.platform === "win32" ? "npx.cmd" : "npx";
|
|
11039
|
+
}
|
|
11040
|
+
function bundledChromium() {
|
|
11041
|
+
try {
|
|
11042
|
+
const p = chromium2.executablePath();
|
|
11043
|
+
return p && existsSync17(p) ? p : null;
|
|
11044
|
+
} catch {
|
|
11045
|
+
return null;
|
|
11046
|
+
}
|
|
11047
|
+
}
|
|
11048
|
+
function probe() {
|
|
11049
|
+
return probeBrowser({
|
|
11050
|
+
platform: process.platform,
|
|
11051
|
+
env: process.env,
|
|
11052
|
+
exists: existsSync17,
|
|
11053
|
+
bundled: bundledChromium
|
|
11054
|
+
});
|
|
11055
|
+
}
|
|
11056
|
+
function ffmpegVersion() {
|
|
11057
|
+
const res = spawnSync("ffmpeg", ["-version"], { encoding: "utf8" });
|
|
11058
|
+
if (res.status !== 0 || !res.stdout) return null;
|
|
11059
|
+
const m = /ffmpeg version (\S+)/.exec(res.stdout);
|
|
11060
|
+
return m ? m[1] : "present";
|
|
11061
|
+
}
|
|
11062
|
+
async function devServerFact(url) {
|
|
11063
|
+
try {
|
|
11064
|
+
const res = await fetch(url, {
|
|
11065
|
+
signal: AbortSignal.timeout(2e3),
|
|
11066
|
+
redirect: "manual"
|
|
11067
|
+
});
|
|
11068
|
+
return { url, ok: res.status < 500, status: res.status };
|
|
11069
|
+
} catch (e) {
|
|
11070
|
+
return {
|
|
11071
|
+
url,
|
|
11072
|
+
ok: false,
|
|
11073
|
+
status: null,
|
|
11074
|
+
error: e instanceof Error ? e.message.split("\n")[0] : String(e)
|
|
11075
|
+
};
|
|
11076
|
+
}
|
|
11077
|
+
}
|
|
11078
|
+
function catalogNames() {
|
|
11079
|
+
const bundled = bundledSkillsDir(import.meta.url.replace("file://", ""));
|
|
11080
|
+
return bundled ? installedSkills(bundled) : null;
|
|
11081
|
+
}
|
|
11082
|
+
function skillsFacts(homes) {
|
|
11083
|
+
const ours = catalogNames();
|
|
11084
|
+
return homes.map((h) => ({
|
|
11085
|
+
agent: h.agent,
|
|
11086
|
+
scope: h.scope,
|
|
11087
|
+
names: installedSkills(skillsDirFor(h)).filter(
|
|
11088
|
+
(n) => !ours || ours.includes(n)
|
|
11089
|
+
)
|
|
11090
|
+
}));
|
|
11091
|
+
}
|
|
11092
|
+
async function doctorFacts(opts) {
|
|
11093
|
+
return {
|
|
11094
|
+
node: process.versions.node,
|
|
11095
|
+
browser: probe(),
|
|
11096
|
+
ffmpeg: ffmpegVersion(),
|
|
11097
|
+
skills: skillsFacts(opts.homes),
|
|
11098
|
+
credential: resolveCredential() !== null,
|
|
11099
|
+
...opts.url ? { devServer: await devServerFact(opts.url) } : {}
|
|
11100
|
+
};
|
|
11101
|
+
}
|
|
11102
|
+
async function cmdSetup(argv) {
|
|
11103
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
|
|
11104
|
+
const r = reporterFor(flags);
|
|
11105
|
+
const root = process.cwd();
|
|
11106
|
+
const global = flags.global === true;
|
|
11107
|
+
const url = strFlag(flags, "url");
|
|
11108
|
+
const noSkills = flags["no-skills"] === true;
|
|
11109
|
+
const noRules = flags["no-rules"] === true;
|
|
11110
|
+
const noBrowser = flags["no-browser"] === true;
|
|
11111
|
+
const wanted = strFlag(flags, "agent");
|
|
11112
|
+
let homes;
|
|
11113
|
+
if (wanted) {
|
|
11114
|
+
const v = agentsFromFlag(wanted, root);
|
|
11115
|
+
if ("error" in v) throw new UsageError(v.error);
|
|
11116
|
+
homes = v.homes;
|
|
11117
|
+
} else {
|
|
11118
|
+
homes = detectAgents(root, { home: homedir3(), global });
|
|
11119
|
+
}
|
|
11120
|
+
const agents = [...new Set(homes.map((h) => h.agent))];
|
|
11121
|
+
r.event({ event: "agents", agents, global });
|
|
11122
|
+
r.log(
|
|
11123
|
+
agents.length ? `agents: ${agents.join(", ")}` : "agents: none detected here (the skills land in .agents/skills, which every agent reads)"
|
|
11124
|
+
);
|
|
11125
|
+
let skillsVia = "skipped";
|
|
11126
|
+
let skillNames = [];
|
|
11127
|
+
if (!noSkills) {
|
|
11128
|
+
const args = [
|
|
11129
|
+
"skills",
|
|
11130
|
+
"add",
|
|
11131
|
+
SKILLS_SOURCE,
|
|
11132
|
+
"-y",
|
|
11133
|
+
...global ? ["-g"] : []
|
|
11134
|
+
];
|
|
11135
|
+
r.log(`installing the vos skills: npx ${args.join(" ")}`);
|
|
11136
|
+
const res = spawnSync(npx(), args, {
|
|
11137
|
+
cwd: root,
|
|
11138
|
+
encoding: "utf8",
|
|
11139
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11140
|
+
shell: process.platform === "win32",
|
|
11141
|
+
timeout: 12e4
|
|
11142
|
+
});
|
|
11143
|
+
if (res.status === 0) {
|
|
11144
|
+
skillsVia = "skills.sh";
|
|
11145
|
+
} else {
|
|
11146
|
+
const bundled = bundledSkillsDir(import.meta.url.replace("file://", ""));
|
|
11147
|
+
if (!bundled)
|
|
11148
|
+
throw new Error(
|
|
11149
|
+
`could not install the skills: npx skills add ${SKILLS_SOURCE} failed (${(res.stderr ?? "").trim().split("\n").at(-1) ?? res.error?.message ?? "no output"}) and this build carries no bundled copy`
|
|
11150
|
+
);
|
|
11151
|
+
const targets = homes.length ? homes : [fallbackSkillsHome(root)];
|
|
11152
|
+
for (const home of targets) {
|
|
11153
|
+
const dest = skillsDirFor(home);
|
|
11154
|
+
mkdirSync3(dest, { recursive: true });
|
|
11155
|
+
for (const name of installedSkills(bundled)) {
|
|
11156
|
+
cpSync(join20(bundled, name), join20(dest, name), { recursive: true });
|
|
11157
|
+
}
|
|
11158
|
+
}
|
|
11159
|
+
skillsVia = "bundled";
|
|
11160
|
+
r.log(
|
|
11161
|
+
`skills.sh did not answer; copied the bundled skills into ${targets.map((t) => skillsDirFor(t)).join(", ")}`
|
|
11162
|
+
);
|
|
11163
|
+
}
|
|
11164
|
+
skillNames = [
|
|
11165
|
+
...new Set(
|
|
11166
|
+
(homes.length ? homes : [fallbackSkillsHome(root)]).flatMap(
|
|
11167
|
+
(h) => installedSkills(skillsDirFor(h))
|
|
11168
|
+
)
|
|
11169
|
+
)
|
|
11170
|
+
].sort();
|
|
11171
|
+
}
|
|
11172
|
+
r.event({ event: "skills", via: skillsVia, names: skillNames });
|
|
11173
|
+
let browser = probe();
|
|
11174
|
+
if (browser.kind === "none" && !noBrowser) {
|
|
11175
|
+
r.log(
|
|
11176
|
+
"no browser found; installing Chromium: npx playwright install chromium"
|
|
11177
|
+
);
|
|
11178
|
+
spawnSync(npx(), ["playwright", "install", "chromium"], {
|
|
11179
|
+
cwd: root,
|
|
11180
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
11181
|
+
shell: process.platform === "win32",
|
|
11182
|
+
timeout: 6e5
|
|
11183
|
+
});
|
|
11184
|
+
browser = probe();
|
|
11185
|
+
}
|
|
11186
|
+
r.event({ event: "browser", kind: browser.kind, path: browser.path });
|
|
11187
|
+
r.log(`browser: ${browser.line}`);
|
|
11188
|
+
let rules = null;
|
|
11189
|
+
if (!noRules) {
|
|
11190
|
+
const file = rulesFileFor(root);
|
|
11191
|
+
const existing = existsSync17(file) ? readFileSync8(file, "utf8") : null;
|
|
11192
|
+
const { text, changed } = applyRulesBlock(existing);
|
|
11193
|
+
if (changed) writeFileSync3(file, text);
|
|
11194
|
+
rules = { file, changed };
|
|
11195
|
+
r.log(
|
|
11196
|
+
changed ? `rules: wrote the vosso block into ${file}` : `rules: ${file} already carries the block`
|
|
11197
|
+
);
|
|
11198
|
+
}
|
|
11199
|
+
r.event({ event: "rules", ...rules ?? { file: null, changed: false } });
|
|
11200
|
+
const report = doctorReport(
|
|
11201
|
+
await doctorFacts({
|
|
11202
|
+
homes: homes.length ? homes : [fallbackSkillsHome(root)],
|
|
11203
|
+
url
|
|
11204
|
+
})
|
|
11205
|
+
);
|
|
11206
|
+
r.done(
|
|
11207
|
+
{
|
|
11208
|
+
agents,
|
|
11209
|
+
skills: { via: skillsVia, names: skillNames },
|
|
11210
|
+
browser: { kind: browser.kind, path: browser.path },
|
|
11211
|
+
rules,
|
|
11212
|
+
doctor: { ok: report.ok, ...report.facts },
|
|
11213
|
+
next_step: report.next_step
|
|
11214
|
+
},
|
|
11215
|
+
`${report.lines.join("\n")}
|
|
11216
|
+
|
|
11217
|
+
Next: ${report.next_step}`
|
|
11218
|
+
);
|
|
11219
|
+
return report.ok ? EXIT_OK : EXIT_NO_BROWSER;
|
|
11220
|
+
}
|
|
11221
|
+
async function cmdDoctor(argv) {
|
|
11222
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
|
|
11223
|
+
const r = reporterFor(flags);
|
|
11224
|
+
const root = process.cwd();
|
|
11225
|
+
const homes = detectAgents(root, { home: homedir3(), global: true });
|
|
11226
|
+
const report = doctorReport(
|
|
11227
|
+
await doctorFacts({
|
|
11228
|
+
homes: homes.length ? homes : [fallbackSkillsHome(root)],
|
|
11229
|
+
url: strFlag(flags, "url")
|
|
11230
|
+
})
|
|
11231
|
+
);
|
|
11232
|
+
r.done(
|
|
11233
|
+
{ ok: report.ok, ...report.facts, next_step: report.next_step },
|
|
11234
|
+
`${report.lines.join("\n")}
|
|
11235
|
+
|
|
11236
|
+
Next: ${report.next_step}`
|
|
11237
|
+
);
|
|
11238
|
+
return report.ok ? EXIT_OK : EXIT_NO_BROWSER;
|
|
11239
|
+
}
|
|
11240
|
+
async function cmdWhoami(argv) {
|
|
11241
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
|
|
11242
|
+
const r = reporterFor(flags);
|
|
11243
|
+
const origin = platformOrigin({
|
|
11244
|
+
origin: strFlag(flags, "origin"),
|
|
11245
|
+
api: strFlag(flags, "api")
|
|
11246
|
+
});
|
|
11247
|
+
const key = resolveCredential(strFlag(flags, "key"));
|
|
11248
|
+
if (!key) {
|
|
11249
|
+
r.done(
|
|
11250
|
+
{
|
|
11251
|
+
signedIn: false,
|
|
11252
|
+
origin,
|
|
11253
|
+
next_step: "vos login (only needed for hosting, sharing or cloud rendering)"
|
|
11254
|
+
},
|
|
11255
|
+
`Not signed in to ${origin}. Recording and rendering need no account; vos login when you want a link.`
|
|
11256
|
+
);
|
|
11257
|
+
return EXIT_ERROR;
|
|
11258
|
+
}
|
|
11259
|
+
const res = await apiJson(origin, "/api/user/whoami", { key });
|
|
11260
|
+
if (res.status === 404) {
|
|
11261
|
+
r.done(
|
|
11262
|
+
{
|
|
11263
|
+
signedIn: true,
|
|
11264
|
+
origin,
|
|
11265
|
+
user: null,
|
|
11266
|
+
key: null,
|
|
11267
|
+
next_step: "vos push when you want the work hosted"
|
|
11268
|
+
},
|
|
11269
|
+
`A credential is present for ${origin} (this origin cannot say whose).`
|
|
11270
|
+
);
|
|
11271
|
+
return EXIT_OK;
|
|
11272
|
+
}
|
|
11273
|
+
if (res.status === 401 || res.status === 403) {
|
|
11274
|
+
r.done(
|
|
11275
|
+
{
|
|
11276
|
+
signedIn: false,
|
|
11277
|
+
origin,
|
|
11278
|
+
error: apiError("whoami", res),
|
|
11279
|
+
next_step: "vos login"
|
|
11280
|
+
},
|
|
11281
|
+
`The stored credential is refused by ${origin}: ${apiError("whoami", res)}. Run vos login.`
|
|
11282
|
+
);
|
|
11283
|
+
return EXIT_ERROR;
|
|
11284
|
+
}
|
|
11285
|
+
if (res.status !== 200) throw new Error(apiError("whoami", res));
|
|
11286
|
+
const user = res.body.user;
|
|
11287
|
+
const k = res.body.key;
|
|
11288
|
+
const who = user?.username ? `@${user.username}` : user?.name ?? "an account";
|
|
11289
|
+
const keyLine = k ? `key "${k.name ?? ""}" (${k.prefix ?? "\u2026"}, ${(k.scopes ?? []).join(", ") || "no scopes"})` : "a session, not a key";
|
|
11290
|
+
r.done(
|
|
11291
|
+
{
|
|
11292
|
+
signedIn: true,
|
|
11293
|
+
origin,
|
|
11294
|
+
user,
|
|
11295
|
+
key: k,
|
|
11296
|
+
next_step: "vos push when you want the work hosted"
|
|
11297
|
+
},
|
|
11298
|
+
`Signed in to ${origin} as ${who}, with ${keyLine}.`
|
|
11299
|
+
);
|
|
11300
|
+
return EXIT_OK;
|
|
11301
|
+
}
|
|
11302
|
+
function cmdLogout(argv) {
|
|
11303
|
+
const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
|
|
11304
|
+
const r = reporterFor(flags);
|
|
11305
|
+
const had = existsSync17(CREDENTIALS_PATH);
|
|
11306
|
+
rmSync2(CREDENTIALS_PATH, { force: true });
|
|
11307
|
+
const envKey = Boolean(process.env.VOS_API_KEY?.trim());
|
|
11308
|
+
r.done(
|
|
11309
|
+
{
|
|
11310
|
+
path: CREDENTIALS_PATH,
|
|
11311
|
+
removed: had,
|
|
11312
|
+
envCredential: envKey,
|
|
11313
|
+
next_step: envKey ? "unset VOS_API_KEY as well; it still resolves" : "vos login when you next need hosting"
|
|
11314
|
+
},
|
|
11315
|
+
(had ? `Removed ${CREDENTIALS_PATH}.` : `Nothing stored at ${CREDENTIALS_PATH}.`) + (envKey ? " VOS_API_KEY is still set in this shell and still resolves." : "")
|
|
11316
|
+
);
|
|
11317
|
+
return EXIT_OK;
|
|
11318
|
+
}
|
|
11319
|
+
|
|
11320
|
+
// src/plugin/run.ts
|
|
11321
|
+
var BOOLEAN_FLAGS6 = /* @__PURE__ */ new Set([
|
|
10749
11322
|
"json",
|
|
10750
11323
|
"help",
|
|
10751
11324
|
"picture",
|
|
@@ -10791,6 +11364,22 @@ Take pipeline
|
|
|
10791
11364
|
vos actions script <actions.json> [--json]
|
|
10792
11365
|
the flow as numbered beats in words, for a person recording it by hand
|
|
10793
11366
|
|
|
11367
|
+
This machine (no account)
|
|
11368
|
+
vos setup [--agent claude,cursor,codex,copilot|all] [--global] [--no-skills] [--no-rules] [--no-browser] [--url <dev server>] [--json]
|
|
11369
|
+
ready this computer for the loop: the vos skills into your
|
|
11370
|
+
agent's directories (npx skills add vosjs/skills, else the copy
|
|
11371
|
+
bundled in this package), a browser found or installed, one
|
|
11372
|
+
idempotent block in AGENTS.md (or CLAUDE.md), then doctor.
|
|
11373
|
+
NDJSON whenever stdout is not a TTY; done carries next_step
|
|
11374
|
+
vos doctor [--url <dev server>] [--json]
|
|
11375
|
+
what is ready, in words: node, browser, ffmpeg, the skills per
|
|
11376
|
+
agent, a credential (present or absent, never printed), and the
|
|
11377
|
+
dev server when --url names one. Exit 3 when no browser
|
|
11378
|
+
vos whoami [--json]
|
|
11379
|
+
the key's name and the account it belongs to, never the key
|
|
11380
|
+
vos logout [--json]
|
|
11381
|
+
remove ~/.config/vos/credentials (VOS_API_KEY still resolves)
|
|
11382
|
+
|
|
10794
11383
|
Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
|
|
10795
11384
|
vos login [--key <vos_sk_\u2026>] [--label <name>] [--no-browser]
|
|
10796
11385
|
sign in via the browser (approve at vos.so/cli/auth; works
|
|
@@ -10994,7 +11583,8 @@ before them (-u gives links their href); what the recorder cannot follow
|
|
|
10994
11583
|
dropped. Then: vos record --actions actions.json --strict.
|
|
10995
11584
|
open serves the take and loads it into the studio (?take=<server>) \u2014
|
|
10996
11585
|
doc.json edits arrive intact; a human can drag every zoom span. Keeps
|
|
10997
|
-
serving until Ctrl-C
|
|
11586
|
+
serving until Ctrl-C. The studio is https://vos.so by default (VOS_ORIGIN
|
|
11587
|
+
moves it; --studio http://localhost:6060 for a dev server).
|
|
10998
11588
|
|
|
10999
11589
|
push/pull \u2014 the hosted iteration loop (credentials: --key, VOS_API_KEY, or
|
|
11000
11590
|
~/.config/vos/credentials via vos login; origin via VOS_ORIGIN, default
|
|
@@ -11086,7 +11676,7 @@ function takeStorageState(flags) {
|
|
|
11086
11676
|
const raw = strFlag(flags, "storage-state");
|
|
11087
11677
|
if (!raw) return void 0;
|
|
11088
11678
|
const path = resolve9(raw);
|
|
11089
|
-
if (!
|
|
11679
|
+
if (!existsSync18(path))
|
|
11090
11680
|
throw new UsageError(`--storage-state: no such file: ${path}`);
|
|
11091
11681
|
return path;
|
|
11092
11682
|
}
|
|
@@ -11099,7 +11689,7 @@ function takeSessionName(flags, storageState) {
|
|
|
11099
11689
|
throw new UsageError(
|
|
11100
11690
|
"--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
|
|
11101
11691
|
);
|
|
11102
|
-
if (!
|
|
11692
|
+
if (!existsSync18(sessionDir(name)))
|
|
11103
11693
|
throw new UsageError(
|
|
11104
11694
|
`no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
|
|
11105
11695
|
);
|
|
@@ -11155,7 +11745,7 @@ function wallGate(flags, r, opts) {
|
|
|
11155
11745
|
async function cmdRecord(argv) {
|
|
11156
11746
|
const { positionals, flags, multi } = parseArgs(
|
|
11157
11747
|
argv,
|
|
11158
|
-
|
|
11748
|
+
BOOLEAN_FLAGS6,
|
|
11159
11749
|
MULTI_FLAGS2
|
|
11160
11750
|
);
|
|
11161
11751
|
const actionsPath = strFlag(flags, "actions") ?? positionals[0];
|
|
@@ -11232,7 +11822,7 @@ ${lines.join("\n")}
|
|
|
11232
11822
|
}
|
|
11233
11823
|
const paths = takePaths(outDir);
|
|
11234
11824
|
const prepare = async () => {
|
|
11235
|
-
if (
|
|
11825
|
+
if (existsSync18(join21(outDir, "meta.json"))) {
|
|
11236
11826
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
11237
11827
|
r.log(
|
|
11238
11828
|
`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" : "")
|
|
@@ -11297,7 +11887,7 @@ ${lines.join("\n")}
|
|
|
11297
11887
|
},
|
|
11298
11888
|
strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}${exposureNote(rec)}` : `Take ready: ${outDir}
|
|
11299
11889
|
${(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}
|
|
11300
|
-
Next: edit ${
|
|
11890
|
+
Next: edit ${join21(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
|
|
11301
11891
|
);
|
|
11302
11892
|
return strictFail ? EXIT_USAGE : EXIT_OK;
|
|
11303
11893
|
} finally {
|
|
@@ -11307,7 +11897,7 @@ ${lines.join("\n")}
|
|
|
11307
11897
|
async function cmdCreate2(argv) {
|
|
11308
11898
|
const { positionals, flags, multi } = parseArgs(
|
|
11309
11899
|
argv,
|
|
11310
|
-
|
|
11900
|
+
BOOLEAN_FLAGS6,
|
|
11311
11901
|
MULTI_FLAGS2
|
|
11312
11902
|
);
|
|
11313
11903
|
const actionsPath = strFlag(flags, "actions");
|
|
@@ -11326,7 +11916,7 @@ async function cmdCreate2(argv) {
|
|
|
11326
11916
|
if (fmtRaw !== "webm" && fmtRaw !== "mp4")
|
|
11327
11917
|
throw new UsageError("--format must be webm or mp4");
|
|
11328
11918
|
const format = fmtRaw;
|
|
11329
|
-
const out = positionals[0] ??
|
|
11919
|
+
const out = positionals[0] ?? join21(outDir, `out.${format}`);
|
|
11330
11920
|
const parallel = numFlag(flags, "parallel", 1);
|
|
11331
11921
|
if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
|
|
11332
11922
|
throw new UsageError("--parallel expects an integer between 1 and 16");
|
|
@@ -11339,7 +11929,7 @@ async function cmdCreate2(argv) {
|
|
|
11339
11929
|
const headers = takeHeaders(multi);
|
|
11340
11930
|
const paths = takePaths(outDir);
|
|
11341
11931
|
const prepare = async () => {
|
|
11342
|
-
if (
|
|
11932
|
+
if (existsSync18(join21(outDir, "meta.json"))) {
|
|
11343
11933
|
const { prevDoc, kept } = await prepareReRecord(outDir);
|
|
11344
11934
|
r.log(
|
|
11345
11935
|
`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" : "")
|
|
@@ -11436,7 +12026,7 @@ async function cmdCreate2(argv) {
|
|
|
11436
12026
|
async function cmdPlan(argv) {
|
|
11437
12027
|
const { positionals, flags, multi } = parseArgs(
|
|
11438
12028
|
argv,
|
|
11439
|
-
|
|
12029
|
+
BOOLEAN_FLAGS6,
|
|
11440
12030
|
/* @__PURE__ */ new Set(["with"])
|
|
11441
12031
|
);
|
|
11442
12032
|
const dir = positionals[0];
|
|
@@ -11446,13 +12036,13 @@ async function cmdPlan(argv) {
|
|
|
11446
12036
|
);
|
|
11447
12037
|
const r = createReporter(flags.json === true);
|
|
11448
12038
|
if (flags.fresh === true) {
|
|
11449
|
-
await rm5(
|
|
12039
|
+
await rm5(join21(dir, "doc.json"), { force: true });
|
|
11450
12040
|
r.log("note: --fresh discarded the existing doc.json");
|
|
11451
12041
|
}
|
|
11452
12042
|
let reuse;
|
|
11453
12043
|
if (flags.reuse === true) {
|
|
11454
|
-
const from = resolve9(strFlag(flags, "from") ??
|
|
11455
|
-
if (!
|
|
12044
|
+
const from = resolve9(strFlag(flags, "from") ?? join21(dir, PREV_DOC_NAME));
|
|
12045
|
+
if (!existsSync18(from)) {
|
|
11456
12046
|
throw new UsageError(
|
|
11457
12047
|
`--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
|
|
11458
12048
|
);
|
|
@@ -11463,7 +12053,7 @@ async function cmdPlan(argv) {
|
|
|
11463
12053
|
};
|
|
11464
12054
|
}
|
|
11465
12055
|
const style = await resolveStyleRef(flags);
|
|
11466
|
-
const hasDoc =
|
|
12056
|
+
const hasDoc = existsSync18(join21(dir, "doc.json"));
|
|
11467
12057
|
const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
|
|
11468
12058
|
const release = await releaseInputs(dir, flags, r, multi.with ?? []);
|
|
11469
12059
|
const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
|
|
@@ -11521,7 +12111,7 @@ async function cmdPlan(argv) {
|
|
|
11521
12111
|
...s.motion ? { motion: s.motion } : {},
|
|
11522
12112
|
...s.reuse ? { reuse: s.reuse } : {}
|
|
11523
12113
|
},
|
|
11524
|
-
`${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${
|
|
12114
|
+
`${s.reuse ? "Reused" : s.fresh ? "Planned" : "Refreshed"} ${join21(dir, "doc.json")}: ${s.zoomAuto} auto + ${s.zoomManual} manual zoom spans${s.cursorKept ? "" : " (cursor track dropped)"}${layoutLines}${motionLines}${reuseLines}`
|
|
11525
12115
|
);
|
|
11526
12116
|
return EXIT_OK;
|
|
11527
12117
|
}
|
|
@@ -11634,7 +12224,7 @@ async function releaseInputs(dir, flags, r, withRefs = []) {
|
|
|
11634
12224
|
async function cmdRender(argv) {
|
|
11635
12225
|
const { positionals, flags, multi } = parseArgs(
|
|
11636
12226
|
argv,
|
|
11637
|
-
|
|
12227
|
+
BOOLEAN_FLAGS6,
|
|
11638
12228
|
MULTI_FLAGS2
|
|
11639
12229
|
);
|
|
11640
12230
|
const dir = positionals[0];
|
|
@@ -11647,7 +12237,7 @@ async function cmdRender(argv) {
|
|
|
11647
12237
|
if (fmtRaw !== "webm" && fmtRaw !== "mp4")
|
|
11648
12238
|
throw new UsageError("--format must be webm or mp4");
|
|
11649
12239
|
const format = fmtRaw;
|
|
11650
|
-
const out = positionals[1] ??
|
|
12240
|
+
const out = positionals[1] ?? join21(dir, `out.${format}`);
|
|
11651
12241
|
const parallel = numFlag(flags, "parallel", 1);
|
|
11652
12242
|
if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
|
|
11653
12243
|
throw new UsageError("--parallel expects an integer between 1 and 16");
|
|
@@ -11705,7 +12295,7 @@ async function cmdRender(argv) {
|
|
|
11705
12295
|
async function cmdFrames(argv) {
|
|
11706
12296
|
const { positionals, flags, multi } = parseArgs(
|
|
11707
12297
|
argv,
|
|
11708
|
-
|
|
12298
|
+
BOOLEAN_FLAGS6,
|
|
11709
12299
|
MULTI_FLAGS2
|
|
11710
12300
|
);
|
|
11711
12301
|
const dir = positionals[0];
|
|
@@ -11778,7 +12368,7 @@ async function cmdFrames(argv) {
|
|
|
11778
12368
|
async function cmdDeliver(argv) {
|
|
11779
12369
|
const { positionals, flags, multi } = parseArgs(
|
|
11780
12370
|
argv,
|
|
11781
|
-
|
|
12371
|
+
BOOLEAN_FLAGS6,
|
|
11782
12372
|
MULTI_FLAGS2
|
|
11783
12373
|
);
|
|
11784
12374
|
const dir = positionals[0];
|
|
@@ -11894,11 +12484,11 @@ async function resolveStyleRef(flags) {
|
|
|
11894
12484
|
}
|
|
11895
12485
|
async function resolveDocRef(ref, flags, what) {
|
|
11896
12486
|
const styleRef = ref;
|
|
11897
|
-
const file =
|
|
12487
|
+
const file = existsSync18(styleRef) ? resolve9(
|
|
11898
12488
|
styleRef,
|
|
11899
|
-
|
|
12489
|
+
existsSync18(join21(styleRef, "doc.json")) ? "doc.json" : ""
|
|
11900
12490
|
) : null;
|
|
11901
|
-
if (file &&
|
|
12491
|
+
if (file && existsSync18(file)) {
|
|
11902
12492
|
return {
|
|
11903
12493
|
from: file,
|
|
11904
12494
|
doc: migrateHostedDoc5(
|
|
@@ -11944,7 +12534,7 @@ async function officialTemplate(origin, ref) {
|
|
|
11944
12534
|
}
|
|
11945
12535
|
}
|
|
11946
12536
|
async function cmdDigest(argv) {
|
|
11947
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12537
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
|
|
11948
12538
|
const dir = positionals[0];
|
|
11949
12539
|
if (!dir)
|
|
11950
12540
|
throw new UsageError(
|
|
@@ -11985,7 +12575,7 @@ async function cmdDigest(argv) {
|
|
|
11985
12575
|
kinds,
|
|
11986
12576
|
frames: d.images.full,
|
|
11987
12577
|
crops: d.images.crop,
|
|
11988
|
-
sheet: d.images.sheet ?
|
|
12578
|
+
sheet: d.images.sheet ? join21(result.outDir, d.images.sheet) : null,
|
|
11989
12579
|
scenes: kinds.scene ?? 0,
|
|
11990
12580
|
sourceDuration: d.take.sourceDuration,
|
|
11991
12581
|
outputDuration: d.take.outputDuration,
|
|
@@ -12008,15 +12598,15 @@ async function cmdDigest(argv) {
|
|
|
12008
12598
|
}
|
|
12009
12599
|
}
|
|
12010
12600
|
async function cmdOpen(argv) {
|
|
12011
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12601
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
|
|
12012
12602
|
const dir = positionals[0];
|
|
12013
12603
|
if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
|
|
12014
12604
|
const r = createReporter(flags.json === true);
|
|
12015
12605
|
const take = await loadTake(dir);
|
|
12016
|
-
if (!
|
|
12606
|
+
if (!existsSync18(take.paths.recording)) {
|
|
12017
12607
|
throw new UsageError(`${dir} has no recording \u2014 re-run record`);
|
|
12018
12608
|
}
|
|
12019
|
-
const studio = (strFlag(flags, "studio") ??
|
|
12609
|
+
const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
|
|
12020
12610
|
/\/+$/,
|
|
12021
12611
|
""
|
|
12022
12612
|
);
|
|
@@ -12046,7 +12636,7 @@ async function cmdOpen(argv) {
|
|
|
12046
12636
|
return EXIT_OK;
|
|
12047
12637
|
}
|
|
12048
12638
|
async function cmdJudge(argv) {
|
|
12049
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12639
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
|
|
12050
12640
|
const target = positionals[0];
|
|
12051
12641
|
const against = strFlag(flags, "against");
|
|
12052
12642
|
if (!target || !against)
|
|
@@ -12054,9 +12644,9 @@ async function cmdJudge(argv) {
|
|
|
12054
12644
|
"vos judge <kit.json> --against <MANIFEST.json> [--out dir] [--json]\n(the manifest names the reference set: id, file, role, layout, facts, rule per asset)"
|
|
12055
12645
|
);
|
|
12056
12646
|
const r = createReporter(flags.json === true);
|
|
12057
|
-
const kitFile = target.endsWith("kit.json") ? target :
|
|
12058
|
-
if (!
|
|
12059
|
-
if (!
|
|
12647
|
+
const kitFile = target.endsWith("kit.json") ? target : join21(target, "kit.json");
|
|
12648
|
+
if (!existsSync18(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
|
|
12649
|
+
if (!existsSync18(against))
|
|
12060
12650
|
throw new UsageError(`${against}: no reference manifest`);
|
|
12061
12651
|
const result = await judgeKit(kitFile, against, strFlag(flags, "out"));
|
|
12062
12652
|
const verdicts = JSON.parse(await readFile15(result.verdictFile, "utf8")).verdicts;
|
|
@@ -12075,7 +12665,7 @@ Win rate so far: ${rate.wins} win(s), ${rate.ties} tie(s) of ${rate.judged} judg
|
|
|
12075
12665
|
return EXIT_OK;
|
|
12076
12666
|
}
|
|
12077
12667
|
async function cmdCallout(argv) {
|
|
12078
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12668
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
|
|
12079
12669
|
const dir = positionals[0];
|
|
12080
12670
|
const shape = positionals[1];
|
|
12081
12671
|
if (!dir || !shape || !isCalloutShape(shape))
|
|
@@ -12174,11 +12764,11 @@ async function cmdCallout(argv) {
|
|
|
12174
12764
|
return EXIT_OK;
|
|
12175
12765
|
}
|
|
12176
12766
|
async function cmdValidate(argv) {
|
|
12177
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12767
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
|
|
12178
12768
|
const target = positionals[0];
|
|
12179
12769
|
if (!target) throw new UsageError("vos validate <actions.json|take>");
|
|
12180
12770
|
const r = createReporter(flags.json === true);
|
|
12181
|
-
const kitTarget = target.endsWith("kit.json") ? target :
|
|
12771
|
+
const kitTarget = target.endsWith("kit.json") ? target : existsSync18(join21(target, "kit.json")) && !isTakeDir(target) ? join21(target, "kit.json") : null;
|
|
12182
12772
|
if (kitTarget) {
|
|
12183
12773
|
const picture = flags.picture === true;
|
|
12184
12774
|
const verdict = await validateKit(kitTarget, { picture });
|
|
@@ -12210,9 +12800,9 @@ async function cmdValidate(argv) {
|
|
|
12210
12800
|
r.done({ valid: true, target }, `${target}: valid actions file`);
|
|
12211
12801
|
return EXIT_OK;
|
|
12212
12802
|
}
|
|
12213
|
-
if (!isTakeDir(target) &&
|
|
12803
|
+
if (!isTakeDir(target) && existsSync18(join21(target, "config.json"))) {
|
|
12214
12804
|
const parsed = JSON.parse(
|
|
12215
|
-
await readFile15(
|
|
12805
|
+
await readFile15(join21(target, "config.json"), "utf8")
|
|
12216
12806
|
);
|
|
12217
12807
|
const pre = preflightConfig(parsed);
|
|
12218
12808
|
const problems2 = pre.ok ? [] : pre.issues.map((i) => `config.json: ${i}`);
|
|
@@ -12228,9 +12818,9 @@ async function cmdValidate(argv) {
|
|
|
12228
12818
|
...collectConfigMediaUrls(pre.config ?? parsed),
|
|
12229
12819
|
...collectDocMediaUrls(doc)
|
|
12230
12820
|
];
|
|
12231
|
-
const
|
|
12232
|
-
problems2.push(...
|
|
12233
|
-
warnings2.push(...
|
|
12821
|
+
const probe2 = mediaProbeLints(await probeMediaUrls([...new Set(urls)]));
|
|
12822
|
+
problems2.push(...probe2.problems);
|
|
12823
|
+
warnings2.push(...probe2.warnings);
|
|
12234
12824
|
}
|
|
12235
12825
|
if (problems2.length) {
|
|
12236
12826
|
r.done(
|
|
@@ -12253,7 +12843,7 @@ async function cmdValidate(argv) {
|
|
|
12253
12843
|
const take = await loadTake(target);
|
|
12254
12844
|
const problems = [];
|
|
12255
12845
|
const warnings = [];
|
|
12256
|
-
if (!
|
|
12846
|
+
if (!existsSync18(take.paths.recording)) {
|
|
12257
12847
|
problems.push("missing recording (re-run record)");
|
|
12258
12848
|
} else {
|
|
12259
12849
|
const mismatch = containerMismatch(
|
|
@@ -12279,11 +12869,11 @@ async function cmdValidate(argv) {
|
|
|
12279
12869
|
const lint = lintDoc(take.doc);
|
|
12280
12870
|
problems.push(...lint.problems.map((p) => `doc.json: ${p}`));
|
|
12281
12871
|
warnings.push(...lint.warnings.map((w) => `doc.json: ${w}`));
|
|
12282
|
-
const
|
|
12872
|
+
const probe2 = mediaProbeLints(
|
|
12283
12873
|
await probeMediaUrls(collectDocMediaUrls(take.doc))
|
|
12284
12874
|
);
|
|
12285
|
-
problems.push(...
|
|
12286
|
-
warnings.push(...
|
|
12875
|
+
problems.push(...probe2.problems);
|
|
12876
|
+
warnings.push(...probe2.warnings);
|
|
12287
12877
|
if (flags.picture === true && (take.doc.overlays ?? []).some((o) => o.kind === "html")) {
|
|
12288
12878
|
const doc = take.doc;
|
|
12289
12879
|
const times = [
|
|
@@ -12336,7 +12926,7 @@ async function cmdValidate(argv) {
|
|
|
12336
12926
|
async function cmdPush3(argv) {
|
|
12337
12927
|
const { positionals, flags, multi } = parseArgs(
|
|
12338
12928
|
argv,
|
|
12339
|
-
|
|
12929
|
+
BOOLEAN_FLAGS6,
|
|
12340
12930
|
MULTI_FLAGS2
|
|
12341
12931
|
);
|
|
12342
12932
|
const dir = positionals[0];
|
|
@@ -12383,7 +12973,7 @@ async function cmdPush3(argv) {
|
|
|
12383
12973
|
return EXIT_OK;
|
|
12384
12974
|
}
|
|
12385
12975
|
async function cmdPull2(argv) {
|
|
12386
|
-
const { positionals, flags } = parseArgs(argv,
|
|
12976
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6, MULTI_FLAGS2);
|
|
12387
12977
|
const dir = positionals[0] ?? ".";
|
|
12388
12978
|
const r = createReporter(flags.json === true);
|
|
12389
12979
|
const result = await pullTake(
|
|
@@ -12470,6 +13060,14 @@ async function run(argv) {
|
|
|
12470
13060
|
return await cmdDuplicate(rest);
|
|
12471
13061
|
case "login":
|
|
12472
13062
|
return await cmdLogin(rest);
|
|
13063
|
+
case "setup":
|
|
13064
|
+
return await cmdSetup(rest);
|
|
13065
|
+
case "doctor":
|
|
13066
|
+
return await cmdDoctor(rest);
|
|
13067
|
+
case "whoami":
|
|
13068
|
+
return await cmdWhoami(rest);
|
|
13069
|
+
case "logout":
|
|
13070
|
+
return cmdLogout(rest);
|
|
12473
13071
|
case "push": {
|
|
12474
13072
|
const target = rest.find((a) => !a.startsWith("-"));
|
|
12475
13073
|
if (target && isTakeDir(target)) {
|
|
@@ -12530,7 +13128,7 @@ async function run(argv) {
|
|
|
12530
13128
|
}
|
|
12531
13129
|
}
|
|
12532
13130
|
async function cmdSession(argv) {
|
|
12533
|
-
const { positionals, flags } = parseArgs(argv,
|
|
13131
|
+
const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
|
|
12534
13132
|
const [sub, arg] = positionals;
|
|
12535
13133
|
const r = createReporter(flags.json === true);
|
|
12536
13134
|
const usage = "vos session open <url> --name <app> | check <name> [--url <url>] | list | rm <name>";
|
|
@@ -12566,7 +13164,7 @@ async function cmdSession(argv) {
|
|
|
12566
13164
|
if (!arg) throw new UsageError(usage);
|
|
12567
13165
|
const bad = sessionNameVerdict(arg);
|
|
12568
13166
|
if (bad) throw new UsageError(bad);
|
|
12569
|
-
if (!
|
|
13167
|
+
if (!existsSync18(sessionDir(arg)))
|
|
12570
13168
|
throw new UsageError(`no session "${arg}" (vos session list)`);
|
|
12571
13169
|
const summary2 = await summarizeSession(arg);
|
|
12572
13170
|
const url = strFlag(flags, "url");
|
|
@@ -12676,4 +13274,4 @@ export {
|
|
|
12676
13274
|
verbHelp,
|
|
12677
13275
|
run
|
|
12678
13276
|
};
|
|
12679
|
-
//# sourceMappingURL=chunk-
|
|
13277
|
+
//# sourceMappingURL=chunk-ANKECV7F.js.map
|