@vosjs/cli 0.47.0 → 0.48.1

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 (33) hide show
  1. package/README.md +16 -5
  2. package/dist/{chunk-EFI3WUQB.js → chunk-P3X5KVA6.js} +15 -2
  3. package/dist/{chunk-EFI3WUQB.js.map → chunk-P3X5KVA6.js.map} +1 -1
  4. package/dist/{chunk-E42NCNWP.js → chunk-RHPRZQRM.js} +688 -58
  5. package/dist/chunk-RHPRZQRM.js.map +1 -0
  6. package/dist/cli.js +4 -4
  7. package/dist/index.js +2 -2
  8. package/dist/manifest-A4R367EM.js +8 -0
  9. package/dist/{run-DA5QXZ6F.js → run-DV73Z3MQ.js} +2 -2
  10. package/package.json +7 -5
  11. package/skills/VERSION +1 -0
  12. package/skills/launch-kit/SKILL.md +356 -0
  13. package/skills/launch-kit/references/channel-specs.md +56 -0
  14. package/skills/product-video/SKILL.md +263 -0
  15. package/skills/product-video/references/destinations.md +71 -0
  16. package/skills/product-video/references/sessions.md +226 -0
  17. package/skills/product-video/references/taste.md +115 -0
  18. package/skills/product-video/references/troubleshooting.md +116 -0
  19. package/skills/vos-authoring/SKILL.md +191 -0
  20. package/skills/vos-authoring/references/examples.md +239 -0
  21. package/skills/vos-authoring/references/schema-reference.md +468 -0
  22. package/skills/vos-create/SKILL.md +258 -0
  23. package/skills/vos-cut/SKILL.md +241 -0
  24. package/skills/vos-footage/SKILL.md +98 -0
  25. package/skills/vos-migrate/SKILL.md +108 -0
  26. package/skills/vos-remix/SKILL.md +136 -0
  27. package/skills/vos-remix/references/3d-recipe.md +37 -0
  28. package/skills/vos-remix/references/params-knobs.md +80 -0
  29. package/skills/vos-remix/references/remix-contract.md +81 -0
  30. package/dist/chunk-E42NCNWP.js.map +0 -1
  31. package/dist/manifest-UCS6OVWZ.js +0 -8
  32. /package/dist/{manifest-UCS6OVWZ.js.map → manifest-A4R367EM.js.map} +0 -0
  33. /package/dist/{run-DA5QXZ6F.js.map → run-DV73Z3MQ.js.map} +0 -0
@@ -27,8 +27,8 @@ import {
27
27
 
28
28
  // src/plugin/run.ts
29
29
  import { mkdir as mkdir10, readFile as readFile15, rm as rm5 } from "fs/promises";
30
- import { existsSync as existsSync16 } from "fs";
31
- 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";
32
32
  import {
33
33
  END_CARD_FROM as END_CARD_FROM2,
34
34
  docOutputDuration as docOutputDuration3,
@@ -2393,6 +2393,16 @@ function startTakeServer(rootDir, pages) {
2393
2393
  const server = createServer((req, res) => {
2394
2394
  const url = new URL(req.url ?? "/", "http://x");
2395
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
+ }
2396
2406
  if (req.method === "POST" && url.pathname === "/save") {
2397
2407
  const name = (url.searchParams.get("name") ?? "out.bin").replace(
2398
2408
  /[^\w.-]/g,
@@ -5120,7 +5130,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
5120
5130
  const vh = meta.captureHeight ?? meta.height;
5121
5131
  const probeDir = await mkdtemp(join8(tmpdir(), "vos-moments-"));
5122
5132
  try {
5123
- const probe = await framesTake(browser, dir, {
5133
+ const probe2 = await framesTake(browser, dir, {
5124
5134
  times: candidates.map((c) => c.time),
5125
5135
  width: PROBE_WIDTH,
5126
5136
  height: Math.max(2, Math.round(PROBE_WIDTH * vh / vw / 2) * 2),
@@ -5131,7 +5141,7 @@ async function pickStillTimes(browser, dir, doc, duration, opts) {
5131
5141
  }
5132
5142
  });
5133
5143
  const byTime = /* @__PURE__ */ new Map();
5134
- for (const frame of probe.frames) {
5144
+ for (const frame of probe2.frames) {
5135
5145
  const img = decodePng(new Uint8Array(await readFile6(frame.file)));
5136
5146
  if (!img) continue;
5137
5147
  const whole = { x: 0, y: 0, w: img.w, h: img.h };
@@ -9049,6 +9059,41 @@ async function browserLogin(origin, r, opts) {
9049
9059
  "no approval within 15 minutes \u2014 run `vos login` again, or mint a key at https://vos.so/app/api and pass --key"
9050
9060
  );
9051
9061
  }
9062
+ var HANDOFF_URL = /^(https?:\/\/[^/]+)\/h\/(vos_ho_[A-Za-z0-9_-]+)$/;
9063
+ function parseHandoffUrl(url) {
9064
+ const m = HANDOFF_URL.exec(url.trim());
9065
+ return m ? { origin: m[1], token: m[2] } : null;
9066
+ }
9067
+ async function handoffLogin(url, r, opts = {}) {
9068
+ const parsed = parseHandoffUrl(url);
9069
+ if (!parsed)
9070
+ throw new Error(
9071
+ "--handoff wants the link a person copied from vos.so, https://vos.so/h/vos_ho_\u2026"
9072
+ );
9073
+ r.log(`exchanging the setup link at ${parsed.origin}\u2026`);
9074
+ const res = await apiJson(parsed.origin, "/api/cli/login/exchange", {
9075
+ method: "POST",
9076
+ body: { token: parsed.token, hostname: hostname().slice(0, 64) }
9077
+ });
9078
+ if (res.status === 200 && res.body.status === "ok") {
9079
+ const path = (opts.store ?? writeCredential)(res.body.key);
9080
+ const user = typeof res.body.user === "object" && res.body.user !== null ? res.body.user.name ?? "" : "";
9081
+ return {
9082
+ path,
9083
+ user,
9084
+ keyName: String(res.body.keyName ?? ""),
9085
+ origin: parsed.origin
9086
+ };
9087
+ }
9088
+ if (res.status === 404 && !res.body.status) {
9089
+ throw new Error(
9090
+ `${parsed.origin} does not support setup links yet \u2014 run \`vos login\` instead`
9091
+ );
9092
+ }
9093
+ const said = typeof res.body.error === "string" ? res.body.error : apiError("exchange", res);
9094
+ const hint = typeof res.body.hint === "string" ? ` ${res.body.hint}` : "";
9095
+ throw new Error(`${said}${hint}`);
9096
+ }
9052
9097
 
9053
9098
  // src/plugin/program.ts
9054
9099
  var BOOLEAN_FLAGS2 = /* @__PURE__ */ new Set([
@@ -9595,6 +9640,21 @@ async function cmdLogin(argv) {
9595
9640
  origin: strFlag(flags, "origin"),
9596
9641
  api: strFlag(flags, "api")
9597
9642
  });
9643
+ const handoffUrl = strFlag(flags, "handoff");
9644
+ if (handoffUrl) {
9645
+ const done = await handoffLogin(handoffUrl, r);
9646
+ r.done(
9647
+ {
9648
+ path: done.path,
9649
+ origin: done.origin,
9650
+ user: done.user,
9651
+ keyName: done.keyName,
9652
+ next_step: "the link is spent; vos push when you want the work hosted"
9653
+ },
9654
+ `Signed in${done.user ? ` as ${done.user}` : ""} \u2014 key "${done.keyName}" stored at ${done.path} (used by every vos platform verb). The link is spent.`
9655
+ );
9656
+ return EXIT_OK;
9657
+ }
9598
9658
  const explicit = strFlag(flags, "key") ?? process.env.VOS_API_KEY?.trim();
9599
9659
  if (explicit) return storeKey(explicit, origin, r);
9600
9660
  try {
@@ -9623,9 +9683,9 @@ async function cmdLogin(argv) {
9623
9683
  return storeKey(key, origin, r);
9624
9684
  }
9625
9685
  async function storeKey(key, origin, r) {
9626
- const probe = await apiJson(origin, "/api/folders", { key });
9627
- if (probe.status === 401 || probe.status === 403) {
9628
- throw new Error(apiError("validate key", probe));
9686
+ const probe2 = await apiJson(origin, "/api/folders", { key });
9687
+ if (probe2.status === 401 || probe2.status === 403) {
9688
+ throw new Error(apiError("validate key", probe2));
9629
9689
  }
9630
9690
  const path = writeCredential(key);
9631
9691
  r.done(
@@ -10764,8 +10824,551 @@ async function cmdActionsScript(input, flags) {
10764
10824
  return EXIT_OK;
10765
10825
  }
10766
10826
 
10767
- // src/plugin/run.ts
10827
+ // src/plugin/machineCmd.ts
10828
+ import { spawnSync } from "child_process";
10829
+ import {
10830
+ cpSync,
10831
+ existsSync as existsSync17,
10832
+ mkdirSync as mkdirSync3,
10833
+ readFileSync as readFileSync8,
10834
+ rmSync as rmSync2,
10835
+ writeFileSync as writeFileSync3
10836
+ } from "fs";
10837
+ import { homedir as homedir3 } from "os";
10838
+ import { join as join20 } from "path";
10839
+ import { chromium as chromium2 } from "playwright";
10840
+
10841
+ // src/plugin/machine.ts
10842
+ import { existsSync as existsSync16, readFileSync as readFileSync7, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
10843
+ import { dirname as dirname5, join as join19 } from "path";
10844
+ var AGENT_NAMES = [
10845
+ "claude",
10846
+ "cursor",
10847
+ "codex",
10848
+ "copilot"
10849
+ ];
10850
+ var PROJECT_DIRS = {
10851
+ claude: [".claude"],
10852
+ cursor: [".cursor"],
10853
+ codex: [".codex", ".agents"],
10854
+ copilot: [".github/copilot"]
10855
+ };
10856
+ var HOME_DIRS = {
10857
+ claude: [".claude"],
10858
+ cursor: [".cursor"],
10859
+ codex: [".codex", ".agents"],
10860
+ copilot: [".copilot"]
10861
+ };
10862
+ function detectAgents(root, opts = {}) {
10863
+ const exists = opts.exists ?? existsSync16;
10864
+ const out = [];
10865
+ for (const agent of AGENT_NAMES) {
10866
+ for (const rel of PROJECT_DIRS[agent]) {
10867
+ const dir = join19(root, rel);
10868
+ if (exists(dir)) out.push({ agent, dir, scope: "project" });
10869
+ }
10870
+ if (agent === "copilot" && !out.some((h) => h.agent === "copilot") && exists(join19(root, ".github", "copilot-instructions.md"))) {
10871
+ out.push({
10872
+ agent,
10873
+ dir: join19(root, ".github", "copilot"),
10874
+ scope: "project"
10875
+ });
10876
+ }
10877
+ }
10878
+ if (opts.global && opts.home) {
10879
+ for (const agent of AGENT_NAMES) {
10880
+ for (const rel of HOME_DIRS[agent]) {
10881
+ const dir = join19(opts.home, rel);
10882
+ if (exists(dir)) out.push({ agent, dir, scope: "global" });
10883
+ }
10884
+ }
10885
+ }
10886
+ return out;
10887
+ }
10888
+ function agentsFromFlag(value, root) {
10889
+ const wanted = value.trim() === "all" ? [...AGENT_NAMES] : value.split(",").map((s) => s.trim()).filter(Boolean);
10890
+ const bad = wanted.filter((w) => !AGENT_NAMES.includes(w));
10891
+ if (bad.length)
10892
+ return {
10893
+ error: `--agent takes ${AGENT_NAMES.join(", ")} or all, not "${bad.join(", ")}"`
10894
+ };
10895
+ if (!wanted.length) return { error: "--agent expects a name" };
10896
+ return {
10897
+ homes: wanted.map((agent) => ({
10898
+ agent,
10899
+ dir: join19(root, PROJECT_DIRS[agent][0]),
10900
+ scope: "project"
10901
+ }))
10902
+ };
10903
+ }
10904
+ function skillsDirFor(home) {
10905
+ return join19(home.dir, "skills");
10906
+ }
10907
+ function fallbackSkillsHome(root) {
10908
+ return { agent: "codex", dir: join19(root, ".agents"), scope: "project" };
10909
+ }
10910
+ function installedSkills(dir, exists = existsSync16, list = (p) => {
10911
+ try {
10912
+ return readdirSync4(p);
10913
+ } catch {
10914
+ return [];
10915
+ }
10916
+ }) {
10917
+ if (!exists(dir)) return [];
10918
+ return list(dir).filter((name) => exists(join19(dir, name, "SKILL.md"))).sort();
10919
+ }
10920
+ var RULES_BEGIN = "<!-- vos:begin (written by vos setup; the block is replaced on the next run) -->";
10921
+ var RULES_END = "<!-- vos:end -->";
10922
+ var RULES_BODY = `## Product videos (vosso)
10923
+ - Skill: product-video (npx skills add vosjs/skills) records the real product from actions.json
10924
+ - Look before you cut: vos frames take --at-zooms; edit take/doc.json and re-render, never re-record
10925
+ - Hand back: vos open take serves the take into the studio at vos.so, doc.json intact
10926
+ - Local by default: vos push only when asked for a link, the shelf or a cloud render
10927
+ - Credentials: VOS_API_KEY, then ~/.config/vos/credentials, then vos login; never print one
10928
+ - Reference: https://vos.so/llms-full.txt`;
10929
+ var RULES_BLOCK = `${RULES_BEGIN}
10930
+ ${RULES_BODY}
10931
+ ${RULES_END}`;
10932
+ function applyRulesBlock(existing) {
10933
+ const current = existing ?? "";
10934
+ const begin = current.indexOf(RULES_BEGIN);
10935
+ const end = current.indexOf(RULES_END);
10936
+ let text;
10937
+ if (begin !== -1 && end !== -1 && end > begin) {
10938
+ text = current.slice(0, begin) + RULES_BLOCK + current.slice(end + RULES_END.length);
10939
+ } else if (current.trim() === "") {
10940
+ text = `${RULES_BLOCK}
10941
+ `;
10942
+ } else {
10943
+ text = `${current.replace(/\s*$/, "")}
10944
+
10945
+ ${RULES_BLOCK}
10946
+ `;
10947
+ }
10948
+ return { text, changed: text !== current };
10949
+ }
10950
+ function rulesFileFor(root, exists = existsSync16) {
10951
+ const agents = join19(root, "AGENTS.md");
10952
+ if (exists(agents)) return agents;
10953
+ const claude = join19(root, "CLAUDE.md");
10954
+ if (exists(claude)) return claude;
10955
+ return agents;
10956
+ }
10957
+ function chromeCandidates(platform, env) {
10958
+ if (platform === "darwin")
10959
+ return ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"];
10960
+ if (platform === "win32")
10961
+ return [
10962
+ join19(
10963
+ env["PROGRAMFILES"] ?? "C:\\Program Files",
10964
+ "Google",
10965
+ "Chrome",
10966
+ "Application",
10967
+ "chrome.exe"
10968
+ ),
10969
+ join19(
10970
+ env["LOCALAPPDATA"] ?? "",
10971
+ "Google",
10972
+ "Chrome",
10973
+ "Application",
10974
+ "chrome.exe"
10975
+ )
10976
+ ];
10977
+ return [
10978
+ "/usr/bin/google-chrome",
10979
+ "/usr/bin/google-chrome-stable",
10980
+ "/opt/google/chrome/chrome"
10981
+ ];
10982
+ }
10983
+ function probeBrowser(opts) {
10984
+ const explicit = opts.env.VOS_BROWSER_PATH?.trim();
10985
+ if (explicit) {
10986
+ return opts.exists(explicit) ? { kind: "env", path: explicit, line: `${explicit} (VOS_BROWSER_PATH)` } : {
10987
+ kind: "none",
10988
+ path: null,
10989
+ line: `VOS_BROWSER_PATH points at ${explicit}, which does not exist`
10990
+ };
10991
+ }
10992
+ const chrome = chromeCandidates(opts.platform, opts.env).find(opts.exists);
10993
+ if (chrome) return { kind: "chrome", path: chrome, line: "Chrome (system)" };
10994
+ const chromium3 = opts.bundled();
10995
+ if (chromium3)
10996
+ return { kind: "chromium", path: chromium3, line: "Chromium (Playwright)" };
10997
+ return {
10998
+ kind: "none",
10999
+ path: null,
11000
+ line: "none: install Google Chrome, or run npx playwright install chromium"
11001
+ };
11002
+ }
11003
+ var NODE_MIN_MAJOR = 18;
11004
+ function major(version) {
11005
+ return parseInt(version.split(".")[0] ?? "0", 10);
11006
+ }
11007
+ function doctorReport(facts) {
11008
+ const lines = [];
11009
+ const nodeOk = major(facts.node) >= NODE_MIN_MAJOR;
11010
+ lines.push(
11011
+ `node ${facts.node} ${nodeOk ? "\u2713" : `\u2717 ${NODE_MIN_MAJOR} or newer is needed`}`
11012
+ );
11013
+ const browserOk = facts.browser.kind !== "none";
11014
+ lines.push(`browser ${facts.browser.line} ${browserOk ? "\u2713" : "\u2717"}`);
11015
+ lines.push(
11016
+ `ffmpeg ${facts.ffmpeg ? `${facts.ffmpeg} \u2713` : "absent (optional)"}`
11017
+ );
11018
+ const withSkills = facts.skills.filter((s) => s.names.length);
11019
+ if (withSkills.length) {
11020
+ const names = [...new Set(withSkills.flatMap((s) => s.names))].sort();
11021
+ const agents = [
11022
+ ...new Set(
11023
+ withSkills.map(
11024
+ (s) => s.scope === "global" ? `${s.agent} (global)` : s.agent
11025
+ )
11026
+ )
11027
+ ];
11028
+ lines.push(`skills ${names.join(", ")} (${agents.join(", ")}) \u2713`);
11029
+ } else {
11030
+ lines.push("skills none \u2717 run: npx vos setup");
11031
+ }
11032
+ lines.push(
11033
+ `credential ${facts.credential ? "present \u2713" : "absent (vos login when you need hosting)"}`
11034
+ );
11035
+ if (facts.devServer) {
11036
+ const d = facts.devServer;
11037
+ lines.push(
11038
+ `dev server ${d.url} ${d.ok ? "answers \u2713" : `does not answer \u2717${d.error ? ` (${d.error})` : d.status ? ` (HTTP ${d.status})` : ""}`}`
11039
+ );
11040
+ }
11041
+ let next_step;
11042
+ if (!nodeOk)
11043
+ next_step = `Install Node ${NODE_MIN_MAJOR} or newer, then run npx vos doctor again`;
11044
+ else if (!browserOk)
11045
+ next_step = "Install Google Chrome, or run: npx playwright install chromium";
11046
+ else if (facts.devServer && !facts.devServer.ok)
11047
+ next_step = `Start the dev server so ${facts.devServer.url} answers, then record`;
11048
+ else if (!withSkills.length) next_step = "Run: npx vos setup";
11049
+ else
11050
+ next_step = "Record: npx vos record --actions actions.json --out take --strict (the product-video skill writes actions.json)";
11051
+ return { ok: nodeOk && browserOk, lines, next_step, facts };
11052
+ }
11053
+ function bundledSkillsDir(from) {
11054
+ let dir = dirname5(from);
11055
+ for (let i = 0; i < 6; i++) {
11056
+ const pkg = join19(dir, "package.json");
11057
+ if (existsSync16(pkg)) {
11058
+ try {
11059
+ const name = JSON.parse(readFileSync7(pkg, "utf8")).name;
11060
+ if (name === "@vosjs/cli") {
11061
+ const skills = join19(dir, "skills");
11062
+ return existsSync16(skills) && statSync4(skills).isDirectory() ? skills : null;
11063
+ }
11064
+ } catch {
11065
+ }
11066
+ }
11067
+ const up = dirname5(dir);
11068
+ if (up === dir) break;
11069
+ dir = up;
11070
+ }
11071
+ return null;
11072
+ }
11073
+
11074
+ // src/plugin/machineCmd.ts
10768
11075
  var BOOLEAN_FLAGS5 = /* @__PURE__ */ new Set([
11076
+ "json",
11077
+ "help",
11078
+ "global",
11079
+ "no-skills",
11080
+ "no-rules",
11081
+ "no-browser"
11082
+ ]);
11083
+ var SKILLS_SOURCE = "vosjs/skills";
11084
+ function reporterFor(flags) {
11085
+ return createReporter(flags.json === true || process.stdout.isTTY !== true);
11086
+ }
11087
+ function npx() {
11088
+ return process.platform === "win32" ? "npx.cmd" : "npx";
11089
+ }
11090
+ function bundledChromium() {
11091
+ try {
11092
+ const p = chromium2.executablePath();
11093
+ return p && existsSync17(p) ? p : null;
11094
+ } catch {
11095
+ return null;
11096
+ }
11097
+ }
11098
+ function probe() {
11099
+ return probeBrowser({
11100
+ platform: process.platform,
11101
+ env: process.env,
11102
+ exists: existsSync17,
11103
+ bundled: bundledChromium
11104
+ });
11105
+ }
11106
+ function ffmpegVersion() {
11107
+ const res = spawnSync("ffmpeg", ["-version"], { encoding: "utf8" });
11108
+ if (res.status !== 0 || !res.stdout) return null;
11109
+ const m = /ffmpeg version (\S+)/.exec(res.stdout);
11110
+ return m ? m[1] : "present";
11111
+ }
11112
+ async function devServerFact(url) {
11113
+ try {
11114
+ const res = await fetch(url, {
11115
+ signal: AbortSignal.timeout(2e3),
11116
+ redirect: "manual"
11117
+ });
11118
+ return { url, ok: res.status < 500, status: res.status };
11119
+ } catch (e) {
11120
+ return {
11121
+ url,
11122
+ ok: false,
11123
+ status: null,
11124
+ error: e instanceof Error ? e.message.split("\n")[0] : String(e)
11125
+ };
11126
+ }
11127
+ }
11128
+ function catalogNames() {
11129
+ const bundled = bundledSkillsDir(import.meta.url.replace("file://", ""));
11130
+ return bundled ? installedSkills(bundled) : null;
11131
+ }
11132
+ function skillsFacts(homes) {
11133
+ const ours = catalogNames();
11134
+ return homes.map((h) => ({
11135
+ agent: h.agent,
11136
+ scope: h.scope,
11137
+ names: installedSkills(skillsDirFor(h)).filter(
11138
+ (n) => !ours || ours.includes(n)
11139
+ )
11140
+ }));
11141
+ }
11142
+ async function doctorFacts(opts) {
11143
+ return {
11144
+ node: process.versions.node,
11145
+ browser: probe(),
11146
+ ffmpeg: ffmpegVersion(),
11147
+ skills: skillsFacts(opts.homes),
11148
+ credential: resolveCredential() !== null,
11149
+ ...opts.url ? { devServer: await devServerFact(opts.url) } : {}
11150
+ };
11151
+ }
11152
+ async function cmdSetup(argv) {
11153
+ const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
11154
+ const r = reporterFor(flags);
11155
+ const root = process.cwd();
11156
+ const global = flags.global === true;
11157
+ const url = strFlag(flags, "url");
11158
+ const noSkills = flags["no-skills"] === true;
11159
+ const noRules = flags["no-rules"] === true;
11160
+ const noBrowser = flags["no-browser"] === true;
11161
+ const wanted = strFlag(flags, "agent");
11162
+ let homes;
11163
+ if (wanted) {
11164
+ const v = agentsFromFlag(wanted, root);
11165
+ if ("error" in v) throw new UsageError(v.error);
11166
+ homes = v.homes;
11167
+ } else {
11168
+ homes = detectAgents(root, { home: homedir3(), global });
11169
+ }
11170
+ const agents = [...new Set(homes.map((h) => h.agent))];
11171
+ r.event({ event: "agents", agents, global });
11172
+ r.log(
11173
+ agents.length ? `agents: ${agents.join(", ")}` : "agents: none detected here (the skills land in .agents/skills, which every agent reads)"
11174
+ );
11175
+ let skillsVia = "skipped";
11176
+ let skillNames = [];
11177
+ if (!noSkills) {
11178
+ const args = [
11179
+ "skills",
11180
+ "add",
11181
+ SKILLS_SOURCE,
11182
+ "-y",
11183
+ ...global ? ["-g"] : []
11184
+ ];
11185
+ r.log(`installing the vos skills: npx ${args.join(" ")}`);
11186
+ const res = spawnSync(npx(), args, {
11187
+ cwd: root,
11188
+ encoding: "utf8",
11189
+ stdio: ["ignore", "pipe", "pipe"],
11190
+ shell: process.platform === "win32",
11191
+ timeout: 12e4
11192
+ });
11193
+ if (res.status === 0) {
11194
+ skillsVia = "skills.sh";
11195
+ } else {
11196
+ const bundled = bundledSkillsDir(import.meta.url.replace("file://", ""));
11197
+ if (!bundled)
11198
+ throw new Error(
11199
+ `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`
11200
+ );
11201
+ const targets = homes.length ? homes : [fallbackSkillsHome(root)];
11202
+ for (const home of targets) {
11203
+ const dest = skillsDirFor(home);
11204
+ mkdirSync3(dest, { recursive: true });
11205
+ for (const name of installedSkills(bundled)) {
11206
+ cpSync(join20(bundled, name), join20(dest, name), { recursive: true });
11207
+ }
11208
+ }
11209
+ skillsVia = "bundled";
11210
+ r.log(
11211
+ `skills.sh did not answer; copied the bundled skills into ${targets.map((t) => skillsDirFor(t)).join(", ")}`
11212
+ );
11213
+ }
11214
+ skillNames = [
11215
+ ...new Set(
11216
+ (homes.length ? homes : [fallbackSkillsHome(root)]).flatMap(
11217
+ (h) => installedSkills(skillsDirFor(h))
11218
+ )
11219
+ )
11220
+ ].sort();
11221
+ }
11222
+ r.event({ event: "skills", via: skillsVia, names: skillNames });
11223
+ let browser = probe();
11224
+ if (browser.kind === "none" && !noBrowser) {
11225
+ r.log(
11226
+ "no browser found; installing Chromium: npx playwright install chromium"
11227
+ );
11228
+ spawnSync(npx(), ["playwright", "install", "chromium"], {
11229
+ cwd: root,
11230
+ stdio: ["ignore", "ignore", "inherit"],
11231
+ shell: process.platform === "win32",
11232
+ timeout: 6e5
11233
+ });
11234
+ browser = probe();
11235
+ }
11236
+ r.event({ event: "browser", kind: browser.kind, path: browser.path });
11237
+ r.log(`browser: ${browser.line}`);
11238
+ let rules = null;
11239
+ if (!noRules) {
11240
+ const file = rulesFileFor(root);
11241
+ const existing = existsSync17(file) ? readFileSync8(file, "utf8") : null;
11242
+ const { text, changed } = applyRulesBlock(existing);
11243
+ if (changed) writeFileSync3(file, text);
11244
+ rules = { file, changed };
11245
+ r.log(
11246
+ changed ? `rules: wrote the vosso block into ${file}` : `rules: ${file} already carries the block`
11247
+ );
11248
+ }
11249
+ r.event({ event: "rules", ...rules ?? { file: null, changed: false } });
11250
+ const report = doctorReport(
11251
+ await doctorFacts({
11252
+ homes: homes.length ? homes : [fallbackSkillsHome(root)],
11253
+ url
11254
+ })
11255
+ );
11256
+ r.done(
11257
+ {
11258
+ agents,
11259
+ skills: { via: skillsVia, names: skillNames },
11260
+ browser: { kind: browser.kind, path: browser.path },
11261
+ rules,
11262
+ doctor: { ok: report.ok, ...report.facts },
11263
+ next_step: report.next_step
11264
+ },
11265
+ `${report.lines.join("\n")}
11266
+
11267
+ Next: ${report.next_step}`
11268
+ );
11269
+ return report.ok ? EXIT_OK : EXIT_NO_BROWSER;
11270
+ }
11271
+ async function cmdDoctor(argv) {
11272
+ const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
11273
+ const r = reporterFor(flags);
11274
+ const root = process.cwd();
11275
+ const homes = detectAgents(root, { home: homedir3(), global: true });
11276
+ const report = doctorReport(
11277
+ await doctorFacts({
11278
+ homes: homes.length ? homes : [fallbackSkillsHome(root)],
11279
+ url: strFlag(flags, "url")
11280
+ })
11281
+ );
11282
+ r.done(
11283
+ { ok: report.ok, ...report.facts, next_step: report.next_step },
11284
+ `${report.lines.join("\n")}
11285
+
11286
+ Next: ${report.next_step}`
11287
+ );
11288
+ return report.ok ? EXIT_OK : EXIT_NO_BROWSER;
11289
+ }
11290
+ async function cmdWhoami(argv) {
11291
+ const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
11292
+ const r = reporterFor(flags);
11293
+ const origin = platformOrigin({
11294
+ origin: strFlag(flags, "origin"),
11295
+ api: strFlag(flags, "api")
11296
+ });
11297
+ const key = resolveCredential(strFlag(flags, "key"));
11298
+ if (!key) {
11299
+ r.done(
11300
+ {
11301
+ signedIn: false,
11302
+ origin,
11303
+ next_step: "vos login (only needed for hosting, sharing or cloud rendering)"
11304
+ },
11305
+ `Not signed in to ${origin}. Recording and rendering need no account; vos login when you want a link.`
11306
+ );
11307
+ return EXIT_ERROR;
11308
+ }
11309
+ const res = await apiJson(origin, "/api/user/whoami", { key });
11310
+ if (res.status === 404) {
11311
+ r.done(
11312
+ {
11313
+ signedIn: true,
11314
+ origin,
11315
+ user: null,
11316
+ key: null,
11317
+ next_step: "vos push when you want the work hosted"
11318
+ },
11319
+ `A credential is present for ${origin} (this origin cannot say whose).`
11320
+ );
11321
+ return EXIT_OK;
11322
+ }
11323
+ if (res.status === 401 || res.status === 403) {
11324
+ r.done(
11325
+ {
11326
+ signedIn: false,
11327
+ origin,
11328
+ error: apiError("whoami", res),
11329
+ next_step: "vos login"
11330
+ },
11331
+ `The stored credential is refused by ${origin}: ${apiError("whoami", res)}. Run vos login.`
11332
+ );
11333
+ return EXIT_ERROR;
11334
+ }
11335
+ if (res.status !== 200) throw new Error(apiError("whoami", res));
11336
+ const user = res.body.user;
11337
+ const k = res.body.key;
11338
+ const who = user?.username ? `@${user.username}` : user?.name ?? "an account";
11339
+ const keyLine = k ? `key "${k.name ?? ""}" (${k.prefix ?? "\u2026"}, ${(k.scopes ?? []).join(", ") || "no scopes"})` : "a session, not a key";
11340
+ r.done(
11341
+ {
11342
+ signedIn: true,
11343
+ origin,
11344
+ user,
11345
+ key: k,
11346
+ next_step: "vos push when you want the work hosted"
11347
+ },
11348
+ `Signed in to ${origin} as ${who}, with ${keyLine}.`
11349
+ );
11350
+ return EXIT_OK;
11351
+ }
11352
+ function cmdLogout(argv) {
11353
+ const { flags } = parseArgs(argv, BOOLEAN_FLAGS5);
11354
+ const r = reporterFor(flags);
11355
+ const had = existsSync17(CREDENTIALS_PATH);
11356
+ rmSync2(CREDENTIALS_PATH, { force: true });
11357
+ const envKey = Boolean(process.env.VOS_API_KEY?.trim());
11358
+ r.done(
11359
+ {
11360
+ path: CREDENTIALS_PATH,
11361
+ removed: had,
11362
+ envCredential: envKey,
11363
+ next_step: envKey ? "unset VOS_API_KEY as well; it still resolves" : "vos login when you next need hosting"
11364
+ },
11365
+ (had ? `Removed ${CREDENTIALS_PATH}.` : `Nothing stored at ${CREDENTIALS_PATH}.`) + (envKey ? " VOS_API_KEY is still set in this shell and still resolves." : "")
11366
+ );
11367
+ return EXIT_OK;
11368
+ }
11369
+
11370
+ // src/plugin/run.ts
11371
+ var BOOLEAN_FLAGS6 = /* @__PURE__ */ new Set([
10769
11372
  "json",
10770
11373
  "help",
10771
11374
  "picture",
@@ -10811,11 +11414,29 @@ Take pipeline
10811
11414
  vos actions script <actions.json> [--json]
10812
11415
  the flow as numbered beats in words, for a person recording it by hand
10813
11416
 
11417
+ This machine (no account)
11418
+ vos setup [--agent claude,cursor,codex,copilot|all] [--global] [--no-skills] [--no-rules] [--no-browser] [--url <dev server>] [--json]
11419
+ ready this computer for the loop: the vos skills into your
11420
+ agent's directories (npx skills add vosjs/skills, else the copy
11421
+ bundled in this package), a browser found or installed, one
11422
+ idempotent block in AGENTS.md (or CLAUDE.md), then doctor.
11423
+ NDJSON whenever stdout is not a TTY; done carries next_step
11424
+ vos doctor [--url <dev server>] [--json]
11425
+ what is ready, in words: node, browser, ffmpeg, the skills per
11426
+ agent, a credential (present or absent, never printed), and the
11427
+ dev server when --url names one. Exit 3 when no browser
11428
+ vos whoami [--json]
11429
+ the key's name and the account it belongs to, never the key
11430
+ vos logout [--json]
11431
+ remove ~/.config/vos/credentials (VOS_API_KEY still resolves)
11432
+
10814
11433
  Platform (vos.so) \u2014 fetch, edit, push, pull, repeat
10815
- vos login [--key <vos_sk_\u2026>] [--label <name>] [--no-browser]
11434
+ vos login [--key <vos_sk_\u2026>] [--handoff <url>] [--label <name>] [--no-browser]
10816
11435
  sign in via the browser (approve at vos.so/cli/auth; works
10817
11436
  headless \u2014 print the URL, a human approves, the key stores
10818
- itself). --key skips the browser (mint at vos.so/app/api)
11437
+ itself). --key skips the browser (mint at vos.so/app/api).
11438
+ --handoff https://vos.so/h/\u2026 exchanges the setup link a
11439
+ signed-in person's line carries for a key, once, no click
10819
11440
  vos fetch <vosId|watch-url> [--out dir] [--media] [--json]
10820
11441
  writes config.json + vos.json (no auth needed for public programs);
10821
11442
  a take vos also gets its doc.json, and --media its footage
@@ -11014,7 +11635,8 @@ before them (-u gives links their href); what the recorder cannot follow
11014
11635
  dropped. Then: vos record --actions actions.json --strict.
11015
11636
  open serves the take and loads it into the studio (?take=<server>) \u2014
11016
11637
  doc.json edits arrive intact; a human can drag every zoom span. Keeps
11017
- serving until Ctrl-C; --studio overrides the default http://localhost:6060.
11638
+ serving until Ctrl-C. The studio is https://vos.so by default (VOS_ORIGIN
11639
+ moves it; --studio http://localhost:6060 for a dev server).
11018
11640
 
11019
11641
  push/pull \u2014 the hosted iteration loop (credentials: --key, VOS_API_KEY, or
11020
11642
  ~/.config/vos/credentials via vos login; origin via VOS_ORIGIN, default
@@ -11106,7 +11728,7 @@ function takeStorageState(flags) {
11106
11728
  const raw = strFlag(flags, "storage-state");
11107
11729
  if (!raw) return void 0;
11108
11730
  const path = resolve9(raw);
11109
- if (!existsSync16(path))
11731
+ if (!existsSync18(path))
11110
11732
  throw new UsageError(`--storage-state: no such file: ${path}`);
11111
11733
  return path;
11112
11734
  }
@@ -11119,7 +11741,7 @@ function takeSessionName(flags, storageState) {
11119
11741
  throw new UsageError(
11120
11742
  "--session and --storage-state are two doors to one take: a persistent profile cannot take a storage state. Pass one"
11121
11743
  );
11122
- if (!existsSync16(sessionDir(name)))
11744
+ if (!existsSync18(sessionDir(name)))
11123
11745
  throw new UsageError(
11124
11746
  `no session "${name}". Make one: vos session open <url> --name ${name} (a Chrome window opens; sign in and close it)`
11125
11747
  );
@@ -11175,7 +11797,7 @@ function wallGate(flags, r, opts) {
11175
11797
  async function cmdRecord(argv) {
11176
11798
  const { positionals, flags, multi } = parseArgs(
11177
11799
  argv,
11178
- BOOLEAN_FLAGS5,
11800
+ BOOLEAN_FLAGS6,
11179
11801
  MULTI_FLAGS2
11180
11802
  );
11181
11803
  const actionsPath = strFlag(flags, "actions") ?? positionals[0];
@@ -11252,7 +11874,7 @@ ${lines.join("\n")}
11252
11874
  }
11253
11875
  const paths = takePaths(outDir);
11254
11876
  const prepare = async () => {
11255
- if (existsSync16(join19(outDir, "meta.json"))) {
11877
+ if (existsSync18(join21(outDir, "meta.json"))) {
11256
11878
  const { prevDoc, kept } = await prepareReRecord(outDir);
11257
11879
  r.log(
11258
11880
  `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" : "")
@@ -11317,7 +11939,7 @@ ${lines.join("\n")}
11317
11939
  },
11318
11940
  strictFail ? `STRICT: take recorded but incomplete \u2014 ${strictReason(rec)}; fix the flow and re-record.${skippedNote}${exposureNote(rec)}` : `Take ready: ${outDir}
11319
11941
  ${(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}
11320
- Next: edit ${join19(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
11942
+ Next: edit ${join21(outDir, "doc.json")} (optional), then: vos render ${outDir}${exposureNote(rec)}`
11321
11943
  );
11322
11944
  return strictFail ? EXIT_USAGE : EXIT_OK;
11323
11945
  } finally {
@@ -11327,7 +11949,7 @@ ${lines.join("\n")}
11327
11949
  async function cmdCreate2(argv) {
11328
11950
  const { positionals, flags, multi } = parseArgs(
11329
11951
  argv,
11330
- BOOLEAN_FLAGS5,
11952
+ BOOLEAN_FLAGS6,
11331
11953
  MULTI_FLAGS2
11332
11954
  );
11333
11955
  const actionsPath = strFlag(flags, "actions");
@@ -11346,7 +11968,7 @@ async function cmdCreate2(argv) {
11346
11968
  if (fmtRaw !== "webm" && fmtRaw !== "mp4")
11347
11969
  throw new UsageError("--format must be webm or mp4");
11348
11970
  const format = fmtRaw;
11349
- const out = positionals[0] ?? join19(outDir, `out.${format}`);
11971
+ const out = positionals[0] ?? join21(outDir, `out.${format}`);
11350
11972
  const parallel = numFlag(flags, "parallel", 1);
11351
11973
  if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
11352
11974
  throw new UsageError("--parallel expects an integer between 1 and 16");
@@ -11359,7 +11981,7 @@ async function cmdCreate2(argv) {
11359
11981
  const headers = takeHeaders(multi);
11360
11982
  const paths = takePaths(outDir);
11361
11983
  const prepare = async () => {
11362
- if (existsSync16(join19(outDir, "meta.json"))) {
11984
+ if (existsSync18(join21(outDir, "meta.json"))) {
11363
11985
  const { prevDoc, kept } = await prepareReRecord(outDir);
11364
11986
  r.log(
11365
11987
  `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" : "")
@@ -11456,7 +12078,7 @@ async function cmdCreate2(argv) {
11456
12078
  async function cmdPlan(argv) {
11457
12079
  const { positionals, flags, multi } = parseArgs(
11458
12080
  argv,
11459
- BOOLEAN_FLAGS5,
12081
+ BOOLEAN_FLAGS6,
11460
12082
  /* @__PURE__ */ new Set(["with"])
11461
12083
  );
11462
12084
  const dir = positionals[0];
@@ -11466,13 +12088,13 @@ async function cmdPlan(argv) {
11466
12088
  );
11467
12089
  const r = createReporter(flags.json === true);
11468
12090
  if (flags.fresh === true) {
11469
- await rm5(join19(dir, "doc.json"), { force: true });
12091
+ await rm5(join21(dir, "doc.json"), { force: true });
11470
12092
  r.log("note: --fresh discarded the existing doc.json");
11471
12093
  }
11472
12094
  let reuse;
11473
12095
  if (flags.reuse === true) {
11474
- const from = resolve9(strFlag(flags, "from") ?? join19(dir, PREV_DOC_NAME));
11475
- if (!existsSync16(from)) {
12096
+ const from = resolve9(strFlag(flags, "from") ?? join21(dir, PREV_DOC_NAME));
12097
+ if (!existsSync18(from)) {
11476
12098
  throw new UsageError(
11477
12099
  `--reuse: ${from} does not exist \u2014 a re-record into this take writes doc.prev.json, or pass --from <doc.json>`
11478
12100
  );
@@ -11483,7 +12105,7 @@ async function cmdPlan(argv) {
11483
12105
  };
11484
12106
  }
11485
12107
  const style = await resolveStyleRef(flags);
11486
- const hasDoc = existsSync16(join19(dir, "doc.json"));
12108
+ const hasDoc = existsSync18(join21(dir, "doc.json"));
11487
12109
  const backdrop = hasDoc ? null : await takeBackdrop(flags, r);
11488
12110
  const release = await releaseInputs(dir, flags, r, multi.with ?? []);
11489
12111
  const motionWanted = !hasDoc || flags.motion === true || flags.fresh === true;
@@ -11541,7 +12163,7 @@ async function cmdPlan(argv) {
11541
12163
  ...s.motion ? { motion: s.motion } : {},
11542
12164
  ...s.reuse ? { reuse: s.reuse } : {}
11543
12165
  },
11544
- `${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}`
12166
+ `${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}`
11545
12167
  );
11546
12168
  return EXIT_OK;
11547
12169
  }
@@ -11654,7 +12276,7 @@ async function releaseInputs(dir, flags, r, withRefs = []) {
11654
12276
  async function cmdRender(argv) {
11655
12277
  const { positionals, flags, multi } = parseArgs(
11656
12278
  argv,
11657
- BOOLEAN_FLAGS5,
12279
+ BOOLEAN_FLAGS6,
11658
12280
  MULTI_FLAGS2
11659
12281
  );
11660
12282
  const dir = positionals[0];
@@ -11667,7 +12289,7 @@ async function cmdRender(argv) {
11667
12289
  if (fmtRaw !== "webm" && fmtRaw !== "mp4")
11668
12290
  throw new UsageError("--format must be webm or mp4");
11669
12291
  const format = fmtRaw;
11670
- const out = positionals[1] ?? join19(dir, `out.${format}`);
12292
+ const out = positionals[1] ?? join21(dir, `out.${format}`);
11671
12293
  const parallel = numFlag(flags, "parallel", 1);
11672
12294
  if (!Number.isInteger(parallel) || parallel < 1 || parallel > 16) {
11673
12295
  throw new UsageError("--parallel expects an integer between 1 and 16");
@@ -11725,7 +12347,7 @@ async function cmdRender(argv) {
11725
12347
  async function cmdFrames(argv) {
11726
12348
  const { positionals, flags, multi } = parseArgs(
11727
12349
  argv,
11728
- BOOLEAN_FLAGS5,
12350
+ BOOLEAN_FLAGS6,
11729
12351
  MULTI_FLAGS2
11730
12352
  );
11731
12353
  const dir = positionals[0];
@@ -11798,7 +12420,7 @@ async function cmdFrames(argv) {
11798
12420
  async function cmdDeliver(argv) {
11799
12421
  const { positionals, flags, multi } = parseArgs(
11800
12422
  argv,
11801
- BOOLEAN_FLAGS5,
12423
+ BOOLEAN_FLAGS6,
11802
12424
  MULTI_FLAGS2
11803
12425
  );
11804
12426
  const dir = positionals[0];
@@ -11914,11 +12536,11 @@ async function resolveStyleRef(flags) {
11914
12536
  }
11915
12537
  async function resolveDocRef(ref, flags, what) {
11916
12538
  const styleRef = ref;
11917
- const file = existsSync16(styleRef) ? resolve9(
12539
+ const file = existsSync18(styleRef) ? resolve9(
11918
12540
  styleRef,
11919
- existsSync16(join19(styleRef, "doc.json")) ? "doc.json" : ""
12541
+ existsSync18(join21(styleRef, "doc.json")) ? "doc.json" : ""
11920
12542
  ) : null;
11921
- if (file && existsSync16(file)) {
12543
+ if (file && existsSync18(file)) {
11922
12544
  return {
11923
12545
  from: file,
11924
12546
  doc: migrateHostedDoc5(
@@ -11964,7 +12586,7 @@ async function officialTemplate(origin, ref) {
11964
12586
  }
11965
12587
  }
11966
12588
  async function cmdDigest(argv) {
11967
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
12589
+ const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
11968
12590
  const dir = positionals[0];
11969
12591
  if (!dir)
11970
12592
  throw new UsageError(
@@ -12005,7 +12627,7 @@ async function cmdDigest(argv) {
12005
12627
  kinds,
12006
12628
  frames: d.images.full,
12007
12629
  crops: d.images.crop,
12008
- sheet: d.images.sheet ? join19(result.outDir, d.images.sheet) : null,
12630
+ sheet: d.images.sheet ? join21(result.outDir, d.images.sheet) : null,
12009
12631
  scenes: kinds.scene ?? 0,
12010
12632
  sourceDuration: d.take.sourceDuration,
12011
12633
  outputDuration: d.take.outputDuration,
@@ -12028,15 +12650,15 @@ async function cmdDigest(argv) {
12028
12650
  }
12029
12651
  }
12030
12652
  async function cmdOpen(argv) {
12031
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
12653
+ const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
12032
12654
  const dir = positionals[0];
12033
12655
  if (!dir) throw new UsageError("vos open <take> [--studio <url>]");
12034
12656
  const r = createReporter(flags.json === true);
12035
12657
  const take = await loadTake(dir);
12036
- if (!existsSync16(take.paths.recording)) {
12658
+ if (!existsSync18(take.paths.recording)) {
12037
12659
  throw new UsageError(`${dir} has no recording \u2014 re-run record`);
12038
12660
  }
12039
- const studio = (strFlag(flags, "studio") ?? "http://localhost:6060").replace(
12661
+ const studio = (strFlag(flags, "studio") ?? platformOrigin()).replace(
12040
12662
  /\/+$/,
12041
12663
  ""
12042
12664
  );
@@ -12066,7 +12688,7 @@ async function cmdOpen(argv) {
12066
12688
  return EXIT_OK;
12067
12689
  }
12068
12690
  async function cmdJudge(argv) {
12069
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
12691
+ const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
12070
12692
  const target = positionals[0];
12071
12693
  const against = strFlag(flags, "against");
12072
12694
  if (!target || !against)
@@ -12074,9 +12696,9 @@ async function cmdJudge(argv) {
12074
12696
  "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)"
12075
12697
  );
12076
12698
  const r = createReporter(flags.json === true);
12077
- const kitFile = target.endsWith("kit.json") ? target : join19(target, "kit.json");
12078
- if (!existsSync16(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
12079
- if (!existsSync16(against))
12699
+ const kitFile = target.endsWith("kit.json") ? target : join21(target, "kit.json");
12700
+ if (!existsSync18(kitFile)) throw new UsageError(`${kitFile}: no kit manifest`);
12701
+ if (!existsSync18(against))
12080
12702
  throw new UsageError(`${against}: no reference manifest`);
12081
12703
  const result = await judgeKit(kitFile, against, strFlag(flags, "out"));
12082
12704
  const verdicts = JSON.parse(await readFile15(result.verdictFile, "utf8")).verdicts;
@@ -12095,7 +12717,7 @@ Win rate so far: ${rate.wins} win(s), ${rate.ties} tie(s) of ${rate.judged} judg
12095
12717
  return EXIT_OK;
12096
12718
  }
12097
12719
  async function cmdCallout(argv) {
12098
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
12720
+ const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
12099
12721
  const dir = positionals[0];
12100
12722
  const shape = positionals[1];
12101
12723
  if (!dir || !shape || !isCalloutShape(shape))
@@ -12194,11 +12816,11 @@ async function cmdCallout(argv) {
12194
12816
  return EXIT_OK;
12195
12817
  }
12196
12818
  async function cmdValidate(argv) {
12197
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
12819
+ const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
12198
12820
  const target = positionals[0];
12199
12821
  if (!target) throw new UsageError("vos validate <actions.json|take>");
12200
12822
  const r = createReporter(flags.json === true);
12201
- const kitTarget = target.endsWith("kit.json") ? target : existsSync16(join19(target, "kit.json")) && !isTakeDir(target) ? join19(target, "kit.json") : null;
12823
+ const kitTarget = target.endsWith("kit.json") ? target : existsSync18(join21(target, "kit.json")) && !isTakeDir(target) ? join21(target, "kit.json") : null;
12202
12824
  if (kitTarget) {
12203
12825
  const picture = flags.picture === true;
12204
12826
  const verdict = await validateKit(kitTarget, { picture });
@@ -12230,9 +12852,9 @@ async function cmdValidate(argv) {
12230
12852
  r.done({ valid: true, target }, `${target}: valid actions file`);
12231
12853
  return EXIT_OK;
12232
12854
  }
12233
- if (!isTakeDir(target) && existsSync16(join19(target, "config.json"))) {
12855
+ if (!isTakeDir(target) && existsSync18(join21(target, "config.json"))) {
12234
12856
  const parsed = JSON.parse(
12235
- await readFile15(join19(target, "config.json"), "utf8")
12857
+ await readFile15(join21(target, "config.json"), "utf8")
12236
12858
  );
12237
12859
  const pre = preflightConfig(parsed);
12238
12860
  const problems2 = pre.ok ? [] : pre.issues.map((i) => `config.json: ${i}`);
@@ -12248,9 +12870,9 @@ async function cmdValidate(argv) {
12248
12870
  ...collectConfigMediaUrls(pre.config ?? parsed),
12249
12871
  ...collectDocMediaUrls(doc)
12250
12872
  ];
12251
- const probe = mediaProbeLints(await probeMediaUrls([...new Set(urls)]));
12252
- problems2.push(...probe.problems);
12253
- warnings2.push(...probe.warnings);
12873
+ const probe2 = mediaProbeLints(await probeMediaUrls([...new Set(urls)]));
12874
+ problems2.push(...probe2.problems);
12875
+ warnings2.push(...probe2.warnings);
12254
12876
  }
12255
12877
  if (problems2.length) {
12256
12878
  r.done(
@@ -12273,7 +12895,7 @@ async function cmdValidate(argv) {
12273
12895
  const take = await loadTake(target);
12274
12896
  const problems = [];
12275
12897
  const warnings = [];
12276
- if (!existsSync16(take.paths.recording)) {
12898
+ if (!existsSync18(take.paths.recording)) {
12277
12899
  problems.push("missing recording (re-run record)");
12278
12900
  } else {
12279
12901
  const mismatch = containerMismatch(
@@ -12299,11 +12921,11 @@ async function cmdValidate(argv) {
12299
12921
  const lint = lintDoc(take.doc);
12300
12922
  problems.push(...lint.problems.map((p) => `doc.json: ${p}`));
12301
12923
  warnings.push(...lint.warnings.map((w) => `doc.json: ${w}`));
12302
- const probe = mediaProbeLints(
12924
+ const probe2 = mediaProbeLints(
12303
12925
  await probeMediaUrls(collectDocMediaUrls(take.doc))
12304
12926
  );
12305
- problems.push(...probe.problems);
12306
- warnings.push(...probe.warnings);
12927
+ problems.push(...probe2.problems);
12928
+ warnings.push(...probe2.warnings);
12307
12929
  if (flags.picture === true && (take.doc.overlays ?? []).some((o) => o.kind === "html")) {
12308
12930
  const doc = take.doc;
12309
12931
  const times = [
@@ -12356,7 +12978,7 @@ async function cmdValidate(argv) {
12356
12978
  async function cmdPush3(argv) {
12357
12979
  const { positionals, flags, multi } = parseArgs(
12358
12980
  argv,
12359
- BOOLEAN_FLAGS5,
12981
+ BOOLEAN_FLAGS6,
12360
12982
  MULTI_FLAGS2
12361
12983
  );
12362
12984
  const dir = positionals[0];
@@ -12403,7 +13025,7 @@ async function cmdPush3(argv) {
12403
13025
  return EXIT_OK;
12404
13026
  }
12405
13027
  async function cmdPull2(argv) {
12406
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5, MULTI_FLAGS2);
13028
+ const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6, MULTI_FLAGS2);
12407
13029
  const dir = positionals[0] ?? ".";
12408
13030
  const r = createReporter(flags.json === true);
12409
13031
  const result = await pullTake(
@@ -12490,6 +13112,14 @@ async function run(argv) {
12490
13112
  return await cmdDuplicate(rest);
12491
13113
  case "login":
12492
13114
  return await cmdLogin(rest);
13115
+ case "setup":
13116
+ return await cmdSetup(rest);
13117
+ case "doctor":
13118
+ return await cmdDoctor(rest);
13119
+ case "whoami":
13120
+ return await cmdWhoami(rest);
13121
+ case "logout":
13122
+ return cmdLogout(rest);
12493
13123
  case "push": {
12494
13124
  const target = rest.find((a) => !a.startsWith("-"));
12495
13125
  if (target && isTakeDir(target)) {
@@ -12550,7 +13180,7 @@ async function run(argv) {
12550
13180
  }
12551
13181
  }
12552
13182
  async function cmdSession(argv) {
12553
- const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS5);
13183
+ const { positionals, flags } = parseArgs(argv, BOOLEAN_FLAGS6);
12554
13184
  const [sub, arg] = positionals;
12555
13185
  const r = createReporter(flags.json === true);
12556
13186
  const usage = "vos session open <url> --name <app> | check <name> [--url <url>] | list | rm <name>";
@@ -12586,7 +13216,7 @@ async function cmdSession(argv) {
12586
13216
  if (!arg) throw new UsageError(usage);
12587
13217
  const bad = sessionNameVerdict(arg);
12588
13218
  if (bad) throw new UsageError(bad);
12589
- if (!existsSync16(sessionDir(arg)))
13219
+ if (!existsSync18(sessionDir(arg)))
12590
13220
  throw new UsageError(`no session "${arg}" (vos session list)`);
12591
13221
  const summary2 = await summarizeSession(arg);
12592
13222
  const url = strFlag(flags, "url");
@@ -12696,4 +13326,4 @@ export {
12696
13326
  verbHelp,
12697
13327
  run
12698
13328
  };
12699
- //# sourceMappingURL=chunk-E42NCNWP.js.map
13329
+ //# sourceMappingURL=chunk-RHPRZQRM.js.map