@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.
Files changed (39) hide show
  1. package/README.md +15 -5
  2. package/dist/{chunk-UE66DC2K.js → chunk-ANKECV7F.js} +658 -60
  3. package/dist/chunk-ANKECV7F.js.map +1 -0
  4. package/dist/{chunk-ID4OUS5L.js → chunk-AUPGJJTL.js} +18 -1
  5. package/dist/chunk-AUPGJJTL.js.map +1 -0
  6. package/dist/{chunk-EFI3WUQB.js → chunk-P3X5KVA6.js} +15 -2
  7. package/dist/{chunk-EFI3WUQB.js.map → chunk-P3X5KVA6.js.map} +1 -1
  8. package/dist/cli.js +4 -4
  9. package/dist/index.js +3 -3
  10. package/dist/manifest-A4R367EM.js +8 -0
  11. package/dist/{run-2OZS25ER.js → run-CYHH56KA.js} +3 -3
  12. package/dist/{shotList-JC4A3FBT.js → shotList-7F2FE3RU.js} +2 -2
  13. package/package.json +9 -7
  14. package/schema/actions.schema.json +2 -2
  15. package/skills/VERSION +1 -0
  16. package/skills/launch-kit/SKILL.md +356 -0
  17. package/skills/launch-kit/references/channel-specs.md +56 -0
  18. package/skills/product-video/SKILL.md +263 -0
  19. package/skills/product-video/references/destinations.md +71 -0
  20. package/skills/product-video/references/sessions.md +226 -0
  21. package/skills/product-video/references/taste.md +115 -0
  22. package/skills/product-video/references/troubleshooting.md +116 -0
  23. package/skills/vos-authoring/SKILL.md +191 -0
  24. package/skills/vos-authoring/references/examples.md +239 -0
  25. package/skills/vos-authoring/references/schema-reference.md +468 -0
  26. package/skills/vos-create/SKILL.md +258 -0
  27. package/skills/vos-cut/SKILL.md +241 -0
  28. package/skills/vos-footage/SKILL.md +98 -0
  29. package/skills/vos-migrate/SKILL.md +108 -0
  30. package/skills/vos-remix/SKILL.md +136 -0
  31. package/skills/vos-remix/references/3d-recipe.md +37 -0
  32. package/skills/vos-remix/references/params-knobs.md +80 -0
  33. package/skills/vos-remix/references/remix-contract.md +81 -0
  34. package/dist/chunk-ID4OUS5L.js.map +0 -1
  35. package/dist/chunk-UE66DC2K.js.map +0 -1
  36. package/dist/manifest-UCS6OVWZ.js +0 -8
  37. /package/dist/{manifest-UCS6OVWZ.js.map → manifest-A4R367EM.js.map} +0 -0
  38. /package/dist/{run-2OZS25ER.js.map → run-CYHH56KA.js.map} +0 -0
  39. /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
- } from "./chunk-ID4OUS5L.js";
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 existsSync16 } from "fs";
29
- import { join as join19, resolve as resolve9 } from "path";
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 probe = await framesTake(browser, dir, {
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 probe.frames) {
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(() => sleep(settleMs(step)));
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 probe = await apiJson(origin, "/api/folders", { key });
9607
- if (probe.status === 401 || probe.status === 403) {
9608
- throw new Error(apiError("validate key", probe));
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-JC4A3FBT.js");
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/run.ts
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; --studio overrides the default http://localhost:6060.
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 (!existsSync16(path))
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 (!existsSync16(sessionDir(name)))
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
- BOOLEAN_FLAGS5,
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 (existsSync16(join19(outDir, "meta.json"))) {
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 ${join19(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
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
- BOOLEAN_FLAGS5,
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] ?? join19(outDir, `out.${format}`);
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 (existsSync16(join19(outDir, "meta.json"))) {
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
- BOOLEAN_FLAGS5,
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(join19(dir, "doc.json"), { force: true });
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") ?? join19(dir, PREV_DOC_NAME));
11455
- if (!existsSync16(from)) {
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 = existsSync16(join19(dir, "doc.json"));
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"} ${join19(dir, "doc.json")}: ${s.zoomAuto} auto + ${s.zoomManual} manual zoom spans${s.cursorKept ? "" : " (cursor track dropped)"}${layoutLines}${motionLines}${reuseLines}`
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
- BOOLEAN_FLAGS5,
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] ?? join19(dir, `out.${format}`);
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
- BOOLEAN_FLAGS5,
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
- BOOLEAN_FLAGS5,
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 = existsSync16(styleRef) ? resolve9(
12487
+ const file = existsSync18(styleRef) ? resolve9(
11898
12488
  styleRef,
11899
- existsSync16(join19(styleRef, "doc.json")) ? "doc.json" : ""
12489
+ existsSync18(join21(styleRef, "doc.json")) ? "doc.json" : ""
11900
12490
  ) : null;
11901
- if (file && existsSync16(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, BOOLEAN_FLAGS5);
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 ? join19(result.outDir, d.images.sheet) : null,
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, BOOLEAN_FLAGS5);
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 (!existsSync16(take.paths.recording)) {
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") ?? "http://localhost:6060").replace(
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, BOOLEAN_FLAGS5);
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 : join19(target, "kit.json");
12058
- if (!existsSync16(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
12059
- if (!existsSync16(against))
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, BOOLEAN_FLAGS5);
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, BOOLEAN_FLAGS5);
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 : existsSync16(join19(target, "kit.json")) && !isTakeDir(target) ? join19(target, "kit.json") : null;
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) && existsSync16(join19(target, "config.json"))) {
12803
+ if (!isTakeDir(target) && existsSync18(join21(target, "config.json"))) {
12214
12804
  const parsed = JSON.parse(
12215
- await readFile15(join19(target, "config.json"), "utf8")
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 probe = mediaProbeLints(await probeMediaUrls([...new Set(urls)]));
12232
- problems2.push(...probe.problems);
12233
- warnings2.push(...probe.warnings);
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 (!existsSync16(take.paths.recording)) {
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 probe = mediaProbeLints(
12872
+ const probe2 = mediaProbeLints(
12283
12873
  await probeMediaUrls(collectDocMediaUrls(take.doc))
12284
12874
  );
12285
- problems.push(...probe.problems);
12286
- warnings.push(...probe.warnings);
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
- BOOLEAN_FLAGS5,
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, BOOLEAN_FLAGS5, MULTI_FLAGS2);
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, BOOLEAN_FLAGS5);
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 (!existsSync16(sessionDir(arg)))
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-UE66DC2K.js.map
13277
+ //# sourceMappingURL=chunk-ANKECV7F.js.map