@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.
- package/README.md +16 -5
- package/dist/{chunk-EFI3WUQB.js → chunk-P3X5KVA6.js} +15 -2
- package/dist/{chunk-EFI3WUQB.js.map → chunk-P3X5KVA6.js.map} +1 -1
- package/dist/{chunk-E42NCNWP.js → chunk-RHPRZQRM.js} +688 -58
- package/dist/chunk-RHPRZQRM.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/manifest-A4R367EM.js +8 -0
- package/dist/{run-DA5QXZ6F.js → run-DV73Z3MQ.js} +2 -2
- package/package.json +7 -5
- package/skills/VERSION +1 -0
- package/skills/launch-kit/SKILL.md +356 -0
- package/skills/launch-kit/references/channel-specs.md +56 -0
- package/skills/product-video/SKILL.md +263 -0
- package/skills/product-video/references/destinations.md +71 -0
- package/skills/product-video/references/sessions.md +226 -0
- package/skills/product-video/references/taste.md +115 -0
- package/skills/product-video/references/troubleshooting.md +116 -0
- package/skills/vos-authoring/SKILL.md +191 -0
- package/skills/vos-authoring/references/examples.md +239 -0
- package/skills/vos-authoring/references/schema-reference.md +468 -0
- package/skills/vos-create/SKILL.md +258 -0
- package/skills/vos-cut/SKILL.md +241 -0
- package/skills/vos-footage/SKILL.md +98 -0
- package/skills/vos-migrate/SKILL.md +108 -0
- package/skills/vos-remix/SKILL.md +136 -0
- package/skills/vos-remix/references/3d-recipe.md +37 -0
- package/skills/vos-remix/references/params-knobs.md +80 -0
- package/skills/vos-remix/references/remix-contract.md +81 -0
- package/dist/chunk-E42NCNWP.js.map +0 -1
- package/dist/manifest-UCS6OVWZ.js +0 -8
- /package/dist/{manifest-UCS6OVWZ.js.map → manifest-A4R367EM.js.map} +0 -0
- /package/dist/{run-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
|
|
31
|
-
import { join as
|
|
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
|
|
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
|
|
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
|
|
9627
|
-
if (
|
|
9628
|
-
throw new Error(apiError("validate key",
|
|
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/
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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 (
|
|
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 ${
|
|
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
|
-
|
|
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] ??
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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") ??
|
|
11475
|
-
if (!
|
|
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 =
|
|
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"} ${
|
|
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
|
-
|
|
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] ??
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
12539
|
+
const file = existsSync18(styleRef) ? resolve9(
|
|
11918
12540
|
styleRef,
|
|
11919
|
-
|
|
12541
|
+
existsSync18(join21(styleRef, "doc.json")) ? "doc.json" : ""
|
|
11920
12542
|
) : null;
|
|
11921
|
-
if (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,
|
|
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 ?
|
|
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,
|
|
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 (!
|
|
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") ??
|
|
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,
|
|
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 :
|
|
12078
|
-
if (!
|
|
12079
|
-
if (!
|
|
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,
|
|
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,
|
|
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 :
|
|
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) &&
|
|
12855
|
+
if (!isTakeDir(target) && existsSync18(join21(target, "config.json"))) {
|
|
12234
12856
|
const parsed = JSON.parse(
|
|
12235
|
-
await readFile15(
|
|
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
|
|
12252
|
-
problems2.push(...
|
|
12253
|
-
warnings2.push(...
|
|
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 (!
|
|
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
|
|
12924
|
+
const probe2 = mediaProbeLints(
|
|
12303
12925
|
await probeMediaUrls(collectDocMediaUrls(take.doc))
|
|
12304
12926
|
);
|
|
12305
|
-
problems.push(...
|
|
12306
|
-
warnings.push(...
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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 (!
|
|
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-
|
|
13329
|
+
//# sourceMappingURL=chunk-RHPRZQRM.js.map
|