@vosjs/cli 0.83.1 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ import {
10
10
  reencodeStill,
11
11
  renderStill,
12
12
  videoFormat
13
- } from "./chunk-B373NVJE.js";
13
+ } from "./chunk-Q47FUCYN.js";
14
14
  import {
15
15
  PREV_DOC_NAME,
16
16
  PasteFileRefusal,
@@ -46,7 +46,7 @@ import {
46
46
  uploadDocRefs,
47
47
  uploadManifest,
48
48
  writeJson
49
- } from "./chunk-352AC3V6.js";
49
+ } from "./chunk-M5ZBQFPG.js";
50
50
  import {
51
51
  CREDENTIALS_PATH,
52
52
  MEDIA_HEAD_BYTES,
@@ -70,6 +70,7 @@ import {
70
70
  parseArgs2 as parseArgs,
71
71
  parseVosId,
72
72
  platformOrigin,
73
+ prepareOutput,
73
74
  programAssets,
74
75
  promptHint,
75
76
  readSyncState,
@@ -82,8 +83,10 @@ import {
82
83
  strFlag,
83
84
  writeCredential,
84
85
  writeFileSafe,
86
+ writeOutput,
87
+ writeOutputSync,
85
88
  writeSyncState
86
- } from "./chunk-J3KHIZCH.js";
89
+ } from "./chunk-EPSYIF2S.js";
87
90
  import {
88
91
  deadLine,
89
92
  deadWarns
@@ -145,9 +148,9 @@ function landedLines(l) {
145
148
  }
146
149
 
147
150
  // src/plugin/run.ts
148
- import { mkdir as mkdir13, readFile as readFile18, rm as rm6 } from "fs/promises";
151
+ import { mkdir as mkdir13, readFile as readFile19, rm as rm7 } from "fs/promises";
149
152
  import { existsSync as existsSync30 } from "fs";
150
- import { dirname as dirname11, join as join33, resolve as resolve21 } from "path";
153
+ import { dirname as dirname12, join as join33, resolve as resolve21 } from "path";
151
154
  import {
152
155
  END_CARD_FROM as END_CARD_FROM2,
153
156
  docOutputDuration as docOutputDuration5,
@@ -4017,7 +4020,7 @@ async function writeIndexJson(result) {
4017
4020
  }
4018
4021
 
4019
4022
  // src/plugin/deliver.ts
4020
- import { mkdir as mkdir3, writeFile as writeFile5 } from "fs/promises";
4023
+ import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
4021
4024
  import { join as join4, relative, resolve as resolve4 } from "path";
4022
4025
  import { existsSync as existsSync4 } from "fs";
4023
4026
  import { readFile as readFile4 } from "fs/promises";
@@ -4034,7 +4037,6 @@ import {
4034
4037
  } from "@vosjs/studio-core";
4035
4038
 
4036
4039
  // src/plugin/renderTake.ts
4037
- import { writeFile as writeFile4 } from "fs/promises";
4038
4040
  import { compileVosConfig as compileVosConfig2 } from "@vosjs/core";
4039
4041
  import {
4040
4042
  audioProducerCode,
@@ -4051,6 +4053,7 @@ async function renderTake(browser, dir, outFile, opts) {
4051
4053
  const take = await loadTake(dir);
4052
4054
  if (!take.doc) throw new Error(`${dir} has no doc.json \u2014 run plan first`);
4053
4055
  const doc = take.doc;
4056
+ await prepareOutput(outFile);
4054
4057
  if (opts.overrides && hasOverrides(opts.overrides)) {
4055
4058
  await resolveBackdropSlug(opts.overrides);
4056
4059
  applyAndValidate(doc, opts.overrides);
@@ -4123,7 +4126,7 @@ async function renderTake(browser, dir, outFile, opts) {
4123
4126
  } : void 0,
4124
4127
  onProgress: opts.onProgress
4125
4128
  });
4126
- await writeFile4(outFile, result.bytes);
4129
+ await writeOutput(outFile, result.bytes);
4127
4130
  return {
4128
4131
  out: outFile,
4129
4132
  bytes: result.bytes.length,
@@ -4820,7 +4823,7 @@ async function deliverTake(browser, dir, opts) {
4820
4823
  assets
4821
4824
  };
4822
4825
  const kitFile = join4(outDir, "kit.json");
4823
- await writeFile5(kitFile, JSON.stringify(kit, null, 2));
4826
+ await writeFile4(kitFile, JSON.stringify(kit, null, 2));
4824
4827
  return { kit, kitFile, outDir };
4825
4828
  }
4826
4829
 
@@ -4968,7 +4971,7 @@ async function handoffLogin(url, r, opts = {}) {
4968
4971
  }
4969
4972
 
4970
4973
  // src/plugin/markAsset.ts
4971
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
4974
+ import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
4972
4975
  import { existsSync as existsSync5 } from "fs";
4973
4976
  import { isAbsolute, join as join5, resolve as resolve5 } from "path";
4974
4977
  function markExtension(url) {
@@ -5069,7 +5072,7 @@ async function fetchBrandMarks(takeDir, roles) {
5069
5072
  return null;
5070
5073
  }
5071
5074
  await mkdir4(join5(takeDir, "brand"), { recursive: true });
5072
- await writeFile6(join5(takeDir, "brand", `${name}.${ext2}`), bytes);
5075
+ await writeFile5(join5(takeDir, "brand", `${name}.${ext2}`), bytes);
5073
5076
  return { key: `/brand/${name}.${ext2}`, aspect };
5074
5077
  };
5075
5078
  out.light = await one("logoUrl", "mark");
@@ -7417,7 +7420,7 @@ import { lowerToComposition as lowerToComposition3, migrateHostedDoc as migrateH
7417
7420
 
7418
7421
  // src/plugin/takeScript.ts
7419
7422
  import { existsSync as existsSync12 } from "fs";
7420
- import { readFile as readFile10, writeFile as writeFile7 } from "fs/promises";
7423
+ import { readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
7421
7424
  import { readFileSync as readFileSync2 } from "fs";
7422
7425
  import { basename, join as join12 } from "path";
7423
7426
  function scriptFromText(text) {
@@ -7478,7 +7481,7 @@ async function writeScriptBeside(dir, doc) {
7478
7481
  const path = join12(dir, "actions.json");
7479
7482
  const text = scriptToWrite(doc, existsSync12(path));
7480
7483
  if (text === null) return false;
7481
- await writeFile7(path, text, "utf8");
7484
+ await writeFile6(path, text, "utf8");
7482
7485
  return true;
7483
7486
  }
7484
7487
  async function readScript(dir) {
@@ -7923,10 +7926,19 @@ function printChanges(r, changes, protectedIds) {
7923
7926
  r.event({ event: "protected", nodes: protectedIds });
7924
7927
  }
7925
7928
  }
7929
+ var DESCRIPTION_MAX = 500;
7930
+ function descriptionProblem(desc) {
7931
+ if (desc === void 0) return null;
7932
+ if (!desc.trim())
7933
+ return "--desc is empty: give the sentence that says what the video shows";
7934
+ if (desc.length > DESCRIPTION_MAX)
7935
+ return `--desc is ${desc.length} characters and a vos keeps ${DESCRIPTION_MAX}: shorten it`;
7936
+ return null;
7937
+ }
7926
7938
  function takePushRefusal(ask) {
7927
7939
  if (ask.programOnly.length) {
7928
7940
  const list = ask.programOnly.map((f) => `--${f}`).join(", ");
7929
- return `${list} ${ask.programOnly.length > 1 ? "belong" : "belongs"} to a PROGRAM push (a config.json); a take push reads --vos, --title, --label, --note, --folder, --override and --yes`;
7941
+ return `${list} ${ask.programOnly.length > 1 ? "belong" : "belongs"} to a PROGRAM push (a config.json); a take push reads --vos, --title, --desc, --prompt, --label, --note, --folder, --override and --yes`;
7930
7942
  }
7931
7943
  if (ask.vos !== void 0 && ask.trackedVosId !== null && ask.trackedVosId !== ask.vos)
7932
7944
  return `this take tracks vos ${ask.trackedVosId} (its vos.json), not ${ask.vos}. Drop --vos to push there, or remove vos.json to adopt ${ask.vos} instead`;
@@ -8101,7 +8113,8 @@ async function pushTake(dir, flags, r) {
8101
8113
  ...folderId ? { folderId } : {},
8102
8114
  ...flags.label ? { label: flags.label } : {},
8103
8115
  ...flags.note ? { note: flags.note } : {},
8104
- ...flags.prompt ? { prompt: flags.prompt } : {}
8116
+ ...flags.prompt ? { prompt: flags.prompt } : {},
8117
+ ...flags.description ? { description: flags.description } : {}
8105
8118
  }
8106
8119
  });
8107
8120
  if (created.status !== 201) {
@@ -8152,14 +8165,18 @@ async function pushTake(dir, flags, r) {
8152
8165
  );
8153
8166
  }
8154
8167
  serverWarnings(pushed.json, (l) => r.log(l));
8155
- if (flags.prompt) {
8168
+ const words2 = {
8169
+ ...flags.prompt ? { prompt: flags.prompt } : {},
8170
+ ...flags.description ? { description: flags.description } : {}
8171
+ };
8172
+ if (Object.keys(words2).length) {
8156
8173
  const set = await api(ctx, `/vos/${state.vosId}`, {
8157
8174
  method: "PATCH",
8158
- body: { prompt: flags.prompt }
8175
+ body: words2
8159
8176
  });
8160
8177
  if (set.status !== 200)
8161
8178
  r.warn(
8162
- `could not set the prompt (${set.status}): ${String(set.json.error ?? "")}${detailLine(set.json)}`
8179
+ `could not set the ${Object.keys(words2).join(" and ")} (${set.status}): ${String(set.json.error ?? "")}${detailLine(set.json)}`
8163
8180
  );
8164
8181
  }
8165
8182
  const version = pushed.json.version;
@@ -8377,6 +8394,7 @@ async function pushTakeClaimable(dir, flags, r) {
8377
8394
  const body = { title, config, doc: docForPush };
8378
8395
  if (state?.vosId) body.remixOfId = state.vosId;
8379
8396
  if (flags.prompt) body.prompt = flags.prompt;
8397
+ if (flags.description) body.description = flags.description;
8380
8398
  if (flags.share) body.share = true;
8381
8399
  const origin = platformOrigin(flags);
8382
8400
  r.log(`creating a claimable push of ${title}\u2026`);
@@ -8915,9 +8933,11 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
8915
8933
  });
8916
8934
  if (pushTarget.kind === "refuse") throw new UsageError(pushTarget.why);
8917
8935
  const vosId = pushTarget.kind === "version" ? pushTarget.vosId : null;
8918
- if (vosId && (desc || tagsFlag || folderRef)) {
8936
+ const descProblem = descriptionProblem(desc);
8937
+ if (descProblem) throw new UsageError(descProblem);
8938
+ if (vosId && (tagsFlag || folderRef)) {
8919
8939
  throw new UsageError(
8920
- "--desc/--tags/--folder apply when CREATING a vos. On an existing one: vos folder move <id> --to <folder> for filing; edit title/description/tags on vos.so"
8940
+ "--tags/--folder apply when CREATING a vos. On an existing one: vos folder move <id> --to <folder> for filing; edit tags on vos.so"
8921
8941
  );
8922
8942
  }
8923
8943
  if (vosId) {
@@ -8963,13 +8983,18 @@ with their key: vos push ${source} --vos ${String(created2.id ?? "<id>")}`
8963
8983
  if (res.status !== 201)
8964
8984
  throw new Error(apiError(`push version to ${vosId}`, res));
8965
8985
  serverWarnings(res.body, say(r));
8966
- if (prompt) {
8986
+ const words2 = {
8987
+ ...prompt ? { prompt } : {},
8988
+ ...desc ? { description: desc } : {}
8989
+ };
8990
+ if (Object.keys(words2).length) {
8967
8991
  const set = await apiJson(origin, `/api/vos/${vosId}`, {
8968
8992
  method: "PATCH",
8969
8993
  key,
8970
- body: { prompt }
8994
+ body: words2
8971
8995
  });
8972
- if (set.status !== 200) r.warn(apiError("set the prompt", set));
8996
+ if (set.status !== 200)
8997
+ r.warn(apiError(`set the ${Object.keys(words2).join(" and ")}`, set));
8973
8998
  }
8974
8999
  const version = res.body.version ?? {};
8975
9000
  if (typeof version.id === "string") {
@@ -10335,7 +10360,7 @@ async function cmdBuild(argv) {
10335
10360
  const out = resolve8(
10336
10361
  strFlag(flags, "out") ?? join17(dirname3(file), "config.json")
10337
10362
  );
10338
- writeFileSync2(out, JSON.stringify(built.config, null, 2) + "\n");
10363
+ writeOutputSync(out, JSON.stringify(built.config, null, 2) + "\n");
10339
10364
  const sidecar = out.replace(/\.json$/, "") + ".sources.json";
10340
10365
  writeFileSync2(sidecar, JSON.stringify(built.sources, null, 2) + "\n");
10341
10366
  for (const [fn, at2] of Object.entries(built.sources)) {
@@ -12153,7 +12178,6 @@ async function cmdRecipe(argv) {
12153
12178
  }
12154
12179
 
12155
12180
  // src/plugin/brand.ts
12156
- import { writeFile as writeFile8 } from "fs/promises";
12157
12181
  import { resolve as resolve13 } from "path";
12158
12182
  import { parseFrontmatter as parseFrontmatter2 } from "@vosjs/shared/frontmatter";
12159
12183
  import { lookKindForGround } from "@vosjs/studio-core";
@@ -12476,6 +12500,7 @@ async function cmdBrand(argv) {
12476
12500
  if (refused) throw new UsageError(refused);
12477
12501
  const r = createReporter(flags.json === true);
12478
12502
  const out = resolve13(strFlag(flags, "out") ?? "BRAND.md");
12503
+ await prepareOutput(out);
12479
12504
  r.log(`reading ${origin}/design.md and /llms.txt\u2026`);
12480
12505
  const [designText, llmsText] = await Promise.all([
12481
12506
  fetchText(`${origin}/design.md`),
@@ -12509,7 +12534,7 @@ async function cmdBrand(argv) {
12509
12534
  composition,
12510
12535
  llms?.claim ?? design?.description ?? null
12511
12536
  );
12512
- await writeFile8(out, md);
12537
+ await writeOutput(out, md);
12513
12538
  const k = composition.kit;
12514
12539
  r.done(
12515
12540
  {
@@ -12647,7 +12672,6 @@ async function recordHosted(input) {
12647
12672
  import { urlSchemeProblem as urlSchemeProblem2 } from "@vosjs/render-core/record";
12648
12673
 
12649
12674
  // src/plugin/agentBrowser.ts
12650
- import { writeFile as writeFile9 } from "fs/promises";
12651
12675
  import { resolve as resolve14 } from "path";
12652
12676
  function parseAgentBrowserLog(text) {
12653
12677
  const records = [];
@@ -13127,8 +13151,8 @@ async function cmdActions(argv) {
13127
13151
  if (!m) throw new UsageError("--viewport expects WxH, e.g. 1280x720");
13128
13152
  viewport = { width: Number(m[1]), height: Number(m[2]) };
13129
13153
  }
13130
- const { readFile: readFile19 } = await import("fs/promises");
13131
- const text = await readFile19(resolve14(input), "utf8");
13154
+ const { readFile: readFile20 } = await import("fs/promises");
13155
+ const text = await readFile20(resolve14(input), "utf8");
13132
13156
  const { records, problems } = parseAgentBrowserLog(text);
13133
13157
  for (const p of problems) r.log(` ${p}`);
13134
13158
  const result = convertAgentBrowser(records, {
@@ -13145,7 +13169,7 @@ async function cmdActions(argv) {
13145
13169
  );
13146
13170
  return EXIT_ERROR;
13147
13171
  }
13148
- await writeFile9(out, `${JSON.stringify(result.actions, null, 2)}
13172
+ await writeOutput(out, `${JSON.stringify(result.actions, null, 2)}
13149
13173
  `);
13150
13174
  r.done(
13151
13175
  { ok: true, out, url: result.actions.url ?? null, ...summary2(result) },
@@ -13165,9 +13189,9 @@ function summary2(result) {
13165
13189
  }
13166
13190
  async function cmdActionsScript(input, flags) {
13167
13191
  const r = createReporter(flags.json === true);
13168
- const { readFile: readFile19 } = await import("fs/promises");
13192
+ const { readFile: readFile20 } = await import("fs/promises");
13169
13193
  const { shotList, formatShotList } = await import("./shotList-PL7XP6AC.js");
13170
- const raw = JSON.parse(await readFile19(resolve14(input), "utf8"));
13194
+ const raw = JSON.parse(await readFile20(resolve14(input), "utf8"));
13171
13195
  const errors = validateActions(raw);
13172
13196
  if (errors.length) throw new UsageError(`${input}:
13173
13197
  ${errors.join("\n ")}`);
@@ -13445,7 +13469,7 @@ async function cmdIngest(argv) {
13445
13469
  // src/plugin/checkVideo.ts
13446
13470
  import { createRequire as createRequire2 } from "module";
13447
13471
  import { existsSync as existsSync23 } from "fs";
13448
- import { mkdir as mkdir9, readFile as readFile14, stat as stat3, writeFile as writeFile10 } from "fs/promises";
13472
+ import { mkdir as mkdir9, readFile as readFile14, stat as stat3, writeFile as writeFile7 } from "fs/promises";
13449
13473
  import { basename as basename11, dirname as dirname8, extname as extname4, join as join24, resolve as resolve16 } from "path";
13450
13474
  import { ALL_FORMATS as ALL_FORMATS2, FilePathSource as FilePathSource2, Input as Input2 } from "mediabunny";
13451
13475
 
@@ -13992,8 +14016,8 @@ async function checkVideo(browser, file, outDir) {
13992
14016
  await mkdir9(outDir, { recursive: true });
13993
14017
  const firstFile = join24(outDir, "first.png");
13994
14018
  const lastFile = join24(outDir, "last.png");
13995
- await writeFile10(firstFile, Buffer.from(done.firstPng, "base64"));
13996
- await writeFile10(lastFile, Buffer.from(done.lastPng, "base64"));
14019
+ await writeFile7(firstFile, Buffer.from(done.firstPng, "base64"));
14020
+ await writeFile7(lastFile, Buffer.from(done.lastPng, "base64"));
13997
14021
  const changes = sceneChanges2(
13998
14022
  done.times,
13999
14023
  done.changes,
@@ -14039,7 +14063,7 @@ async function checkVideo(browser, file, outDir) {
14039
14063
  warnings,
14040
14064
  outDir
14041
14065
  };
14042
- await writeFile10(
14066
+ await writeFile7(
14043
14067
  join24(outDir, "check.json"),
14044
14068
  `${JSON.stringify(result, null, 2)}
14045
14069
  `
@@ -14122,7 +14146,7 @@ import {
14122
14146
  mkdtemp,
14123
14147
  readFile as readFile15,
14124
14148
  rm as rm3,
14125
- writeFile as writeFile11
14149
+ writeFile as writeFile8
14126
14150
  } from "fs/promises";
14127
14151
  import { tmpdir as tmpdir2 } from "os";
14128
14152
  import { basename as basename12, isAbsolute as isAbsolute3, join as join25, relative as relative4, resolve as resolve17, sep as sep3 } from "path";
@@ -14565,7 +14589,7 @@ async function resolveTrack(dir, ref, flags, dryRun) {
14565
14589
  if (!res.ok) throw new Error(`GET ${track.url} answered ${res.status}`);
14566
14590
  const tmp = await mkdtemp(join25(tmpdir2(), "vos-score-"));
14567
14591
  const path = join25(tmp, basename12(new URL(track.url).pathname) || "track");
14568
- await writeFile11(path, new Uint8Array(await res.arrayBuffer()));
14592
+ await writeFile8(path, new Uint8Array(await res.arrayBuffer()));
14569
14593
  return {
14570
14594
  path,
14571
14595
  key: track.url,
@@ -16813,7 +16837,7 @@ async function cmdTakeInfo(argv) {
16813
16837
  }
16814
16838
 
16815
16839
  // src/plugin/audio.ts
16816
- import { mkdtemp as mkdtemp2, rm as rm5, writeFile as writeFile12 } from "fs/promises";
16840
+ import { mkdtemp as mkdtemp2, rm as rm5, writeFile as writeFile9 } from "fs/promises";
16817
16841
  import { tmpdir as tmpdir3 } from "os";
16818
16842
  import { basename as basename14, join as join29 } from "path";
16819
16843
  import { clipGain as clipGain3 } from "@vosjs/studio-core";
@@ -16829,7 +16853,7 @@ async function fileFor(dir, key, fetchImpl) {
16829
16853
  if (!res.ok) return { skipped: `GET ${key} answered ${res.status}` };
16830
16854
  const tmp = await mkdtemp2(join29(tmpdir3(), "vos-audio-"));
16831
16855
  const path = join29(tmp, basename14(new URL(key).pathname) || "audio");
16832
- await writeFile12(path, new Uint8Array(await res.arrayBuffer()));
16856
+ await writeFile9(path, new Uint8Array(await res.arrayBuffer()));
16833
16857
  return { path, cleanup: () => rm5(tmp, { recursive: true, force: true }) };
16834
16858
  }
16835
16859
  return {
@@ -16909,9 +16933,10 @@ async function cmdAudio(argv) {
16909
16933
  }
16910
16934
 
16911
16935
  // src/plugin/look.ts
16912
- import { mkdir as mkdir12 } from "fs/promises";
16936
+ import { mkdir as mkdir12, mkdtemp as mkdtemp3, readFile as readFile18, rm as rm6 } from "fs/promises";
16913
16937
  import { existsSync as existsSync27 } from "fs";
16914
- import { join as join30, resolve as resolve20 } from "path";
16938
+ import { tmpdir as tmpdir4 } from "os";
16939
+ import { dirname as dirname10, join as join30, resolve as resolve20 } from "path";
16915
16940
  import { headersFor, urlSchemeProblem } from "@vosjs/render-core/record";
16916
16941
  function quote(value) {
16917
16942
  return `"${value.replace(/\s+/g, " ").replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
@@ -17314,19 +17339,19 @@ async function lookAt(page, url, opts) {
17314
17339
  });
17315
17340
  const ran = [];
17316
17341
  if (!wall || wall.level === "soft") {
17317
- for (const step2 of opts.steps ?? []) {
17318
- const all = page.locator(step2.selector);
17342
+ for (const step of opts.steps ?? []) {
17343
+ const all = page.locator(step.selector);
17319
17344
  const target = all.first();
17320
17345
  try {
17321
17346
  await target.waitFor({ state: "visible", timeout: STEP_TIMEOUT_MS });
17322
17347
  } catch {
17323
17348
  throw new UsageError(
17324
- `--${step2.do} ${step2.selector}: no visible element matches it after ${STEP_TIMEOUT_MS / 1e3} s. Look without the step and take a selector from the list`
17349
+ `--${step.do} ${step.selector}: no visible element matches it after ${STEP_TIMEOUT_MS / 1e3} s. Look without the step and take a selector from the list`
17325
17350
  );
17326
17351
  }
17327
17352
  const count = await all.count();
17328
17353
  const note = count > 1 ? `the first of ${count} matches` : void 0;
17329
- if (step2.do === "click") {
17354
+ if (step.do === "click") {
17330
17355
  const words2 = await target.evaluate(
17331
17356
  (el) => [
17332
17357
  el.getAttribute("aria-label"),
@@ -17342,25 +17367,37 @@ async function lookAt(page, url, opts) {
17342
17367
  if (word) {
17343
17368
  const label = words2.find((w) => spendWord([w])) ?? word;
17344
17369
  throw new LookRefusal(
17345
- refusalLine(step2.selector, label.slice(0, 60), word)
17370
+ refusalLine(step.selector, label.slice(0, 60), word)
17346
17371
  );
17347
17372
  }
17348
- log(`click ${step2.selector}${note ? ` (${note})` : ""}`);
17373
+ log(`click ${step.selector}${note ? ` (${note})` : ""}`);
17349
17374
  await target.click({ timeout: STEP_TIMEOUT_MS });
17350
17375
  } else {
17351
17376
  log(
17352
- `type ${step2.text.length} chars into ${step2.selector}${note ? ` (${note})` : ""}`
17377
+ `type ${step.text.length} chars into ${step.selector}${note ? ` (${note})` : ""}`
17353
17378
  );
17354
- await target.fill(step2.text, { timeout: STEP_TIMEOUT_MS });
17379
+ await target.fill(step.text, { timeout: STEP_TIMEOUT_MS });
17355
17380
  }
17356
17381
  ran.push({
17357
- do: step2.do,
17358
- selector: step2.selector,
17382
+ do: step.do,
17383
+ selector: step.selector,
17359
17384
  ...note ? { note } : {}
17360
17385
  });
17361
17386
  settled = await settle(page);
17362
17387
  }
17363
17388
  }
17389
+ return inspectPage(page, {
17390
+ url,
17391
+ outDir: opts.outDir,
17392
+ waitMs: opts.waitMs,
17393
+ wall,
17394
+ steps: ran,
17395
+ settled
17396
+ });
17397
+ }
17398
+ async function inspectPage(page, opts) {
17399
+ const { url, wall, settled } = opts;
17400
+ const ran = opts.steps;
17364
17401
  if (opts.waitMs && opts.waitMs > 0) await page.waitForTimeout(opts.waitMs);
17365
17402
  await mkdir12(opts.outDir, { recursive: true });
17366
17403
  const screenshot = join30(opts.outDir, "look.png");
@@ -17418,7 +17455,27 @@ async function lookAt(page, url, opts) {
17418
17455
  settled
17419
17456
  };
17420
17457
  }
17421
- var USAGE5 = "vos look <url> [--session <name> | --storage-state <file>] [--viewport WxH] [--headed] [--click <selector>]... [--type <selector>=<text>]... [--wait <ms>] [--out <dir>] [--json]";
17458
+ var USAGE5 = "vos look <url> [--session <name> | --storage-state <file>] [--viewport WxH] [--headed] [--click <selector>]... [--type <selector>=<text>]... [--wait <ms>] [--out <dir>] [--json]\n vos look [url] --actions actions.json --until <step id> [--session <name> | --storage-state <file>] [--viewport WxH] [--headed] [--wait <ms>] [--out <dir>] [--json]";
17459
+ function untilProblem(given, steps) {
17460
+ if (given.until !== void 0 && given.actions === void 0)
17461
+ return "--until names a step of a script: pass the script with --actions actions.json";
17462
+ if (given.actions === void 0) return null;
17463
+ if (given.clicks)
17464
+ return "--actions walks the script; --click and --type walk by hand. Pass one";
17465
+ if (given.until === void 0)
17466
+ return "--actions needs --until <step id>: the step whose page you want to see";
17467
+ if (!steps) return null;
17468
+ if (steps.some((s) => s.id === given.until)) return null;
17469
+ const ids = steps.map((s) => s.id).filter((id) => !!id);
17470
+ return `--until ${given.until}: no step in ${given.actions} has that id. ${ids.length ? `Its ids: ${ids.join(", ")}` : 'Its steps have no ids; give the step you want an "id"'}`;
17471
+ }
17472
+ function walkedSteps(steps) {
17473
+ return steps.map((s) => ({
17474
+ do: s.id ? `${s.do} (${s.id})` : s.do,
17475
+ selector: s.selector ?? "",
17476
+ ...s.skipped ? { note: `SKIPPED: ${s.reason ?? "not found"}` } : s.guard ? { note: `guarded (${s.guard}), hovered, not pressed` } : {}
17477
+ }));
17478
+ }
17422
17479
  function lookReport(r) {
17423
17480
  const inDialog = r.controls.filter((c) => c.inDialog).length;
17424
17481
  const head = `Looked at ${r.landed} (${r.viewport.width}\xD7${r.viewport.height}): ${r.screenshot}${r.settled ? "" : "\n note: the page never held still for 0.8 s; looked at it as it was"}`;
@@ -17447,12 +17504,30 @@ async function cmdLook(argv) {
17447
17504
  );
17448
17505
  void multi.click;
17449
17506
  void multi.type;
17450
- const url = positionals[0];
17507
+ const actionsArg = strFlag(flags, "actions");
17508
+ const untilId = strFlag(flags, "until");
17509
+ const early = untilProblem({
17510
+ actions: actionsArg,
17511
+ until: untilId,
17512
+ clicks: steps.length
17513
+ });
17514
+ if (early) throw new UsageError(early);
17515
+ let actions;
17516
+ if (actionsArg) {
17517
+ actions = await loadLookActions(resolve20(actionsArg));
17518
+ const bad = untilProblem(
17519
+ { actions: actionsArg, until: untilId, clicks: 0 },
17520
+ actions.steps
17521
+ );
17522
+ if (bad) throw new UsageError(bad);
17523
+ }
17524
+ const url = positionals[0] ?? actions?.url;
17451
17525
  if (!url) throw new UsageError(USAGE5);
17452
17526
  const scheme = urlSchemeProblem(url);
17453
17527
  if (scheme) throw new UsageError(scheme);
17454
17528
  const r = createReporter(flags.json === true);
17455
- const viewport = parseViewport2(strFlag(flags, "viewport"));
17529
+ const viewportFlag = strFlag(flags, "viewport");
17530
+ const viewport = !viewportFlag && actions?.viewport ? actions.viewport : parseViewport2(viewportFlag);
17456
17531
  const outDir = resolve20(strFlag(flags, "out") ?? "look");
17457
17532
  const headed = flags.headed === true;
17458
17533
  const waitRaw = strFlag(flags, "wait");
@@ -17483,6 +17558,22 @@ async function cmdLook(argv) {
17483
17558
  throw new UsageError(e instanceof Error ? e.message : String(e));
17484
17559
  }
17485
17560
  const browserArgs = (multi["browser-arg"] ?? []).filter(Boolean);
17561
+ if (actions && untilId && actionsArg)
17562
+ return lookUntil({
17563
+ r,
17564
+ url,
17565
+ actions,
17566
+ actionsPath: resolve20(actionsArg),
17567
+ untilId,
17568
+ viewport,
17569
+ outDir,
17570
+ waitMs,
17571
+ headed,
17572
+ storageState,
17573
+ sessionName,
17574
+ headers,
17575
+ browserArgs
17576
+ });
17486
17577
  const inContext = async (ctx) => {
17487
17578
  if (Object.keys(headers).length)
17488
17579
  await ctx.route("**/*", async (route) => {
@@ -17529,6 +17620,108 @@ async function cmdLook(argv) {
17529
17620
  );
17530
17621
  return result.wall?.level === "hard" ? EXIT_WALL : EXIT_OK;
17531
17622
  }
17623
+ async function loadLookActions(file) {
17624
+ let raw;
17625
+ try {
17626
+ raw = JSON.parse(await readFile18(file, "utf8"));
17627
+ } catch (e) {
17628
+ throw new UsageError(
17629
+ `--actions ${file}: ${e instanceof Error ? e.message : String(e)}`
17630
+ );
17631
+ }
17632
+ const errors = validateActions(raw);
17633
+ if (!errors.length) errors.push(...jsSyntaxProblems(raw));
17634
+ if (errors.length)
17635
+ throw new UsageError(`invalid actions file:
17636
+ ${errors.join("\n ")}`);
17637
+ return raw;
17638
+ }
17639
+ async function lookUntil(o) {
17640
+ const { r } = o;
17641
+ const actions = { ...o.actions, viewport: o.viewport };
17642
+ let wall = null;
17643
+ let looked = null;
17644
+ const scratch = await mkdtemp3(join30(tmpdir4(), "vos-look-"));
17645
+ r.log(
17646
+ `walking ${o.actionsPath} to step ${o.untilId} (a rehearsal: guarded steps hovered, never pressed)\u2026`
17647
+ );
17648
+ const browser = await launchBrowser(o.browserArgs, { headless: !o.headed });
17649
+ let rec;
17650
+ try {
17651
+ const walk = (context2) => recordTake(browser, o.url, actions, takePaths(scratch), r.log, {
17652
+ actionsDir: dirname10(o.actionsPath),
17653
+ storageState: o.storageState,
17654
+ headers: o.headers,
17655
+ context: context2,
17656
+ dryRun: true,
17657
+ stopOnHang: true,
17658
+ onEvent: (e) => r.event(e),
17659
+ onArrival: (arrival) => {
17660
+ const verdict = wallVerdict(arrival);
17661
+ if (verdict && verdict.level === "hard") throw new WallError(verdict);
17662
+ if (verdict) r.log(` WARNING: ${verdict.message}`);
17663
+ wall = verdict;
17664
+ return verdict;
17665
+ },
17666
+ until: {
17667
+ id: o.untilId,
17668
+ at: async (raw) => {
17669
+ const page = raw;
17670
+ const settled = await settle(page);
17671
+ looked = await inspectPage(page, {
17672
+ url: o.url,
17673
+ outDir: o.outDir,
17674
+ waitMs: o.waitMs,
17675
+ wall,
17676
+ steps: [],
17677
+ settled
17678
+ });
17679
+ }
17680
+ }
17681
+ });
17682
+ rec = o.sessionName ? await withSession(o.sessionName, (ctx) => walk(ctx), {
17683
+ headless: !o.headed,
17684
+ viewport: o.viewport
17685
+ }) : await walk(void 0);
17686
+ } finally {
17687
+ await browser.close();
17688
+ await rm6(scratch, { recursive: true, force: true });
17689
+ }
17690
+ const result = looked;
17691
+ const ran = walkedSteps(rec.meta.steps ?? []);
17692
+ const skipped = rec.skipped.length;
17693
+ const teardown = rec.teardown ? `
17694
+ ${teardownLine(rec.teardown)}` : "";
17695
+ if (!result) {
17696
+ const why = rec.stopped ? rec.stopped.reason : "the walk ended before it reached that step";
17697
+ r.done(
17698
+ {
17699
+ ok: false,
17700
+ until: o.untilId,
17701
+ steps: ran,
17702
+ skipped: rec.skipped,
17703
+ ...rec.teardown ? { teardown: rec.teardown } : {}
17704
+ },
17705
+ `NOT LOOKED: step ${o.untilId} was never reached: ${why}.${ran.length ? `
17706
+ walked: ${ran.map((s) => `${s.do}${s.selector ? ` ${s.selector}` : ""}${s.note ? ` (${s.note})` : ""}`).join(", then ")}` : ""}
17707
+ Rehearse the script (vos record --actions ${o.actionsPath} --dry-run) to see where it stops.${teardown}`
17708
+ );
17709
+ return EXIT_USAGE;
17710
+ }
17711
+ const full = { ...result, steps: ran };
17712
+ const warn = skipped ? `
17713
+ WARNING: ${skipped} step${skipped === 1 ? " was" : "s were"} SKIPPED on the way, so this may not be the page the script reaches. Fix ${skipped === 1 ? "it" : "them"}, then look again.` : "";
17714
+ r.done(
17715
+ {
17716
+ ok: skipped === 0 && (!full.wall || full.wall.level === "soft"),
17717
+ until: o.untilId,
17718
+ ...full,
17719
+ ...rec.teardown ? { teardown: rec.teardown } : {}
17720
+ },
17721
+ `${lookReport(full)}${warn}${teardown}`
17722
+ );
17723
+ return skipped ? EXIT_USAGE : EXIT_OK;
17724
+ }
17532
17725
 
17533
17726
  // src/plugin/machineCmd.ts
17534
17727
  import { spawnSync as spawnSync4 } from "child_process";
@@ -17546,7 +17739,7 @@ import { chromium as chromium2 } from "playwright";
17546
17739
 
17547
17740
  // src/plugin/machine.ts
17548
17741
  import { existsSync as existsSync28, readFileSync as readFileSync10, readdirSync as readdirSync6, statSync as statSync8 } from "fs";
17549
- import { dirname as dirname10, join as join31 } from "path";
17742
+ import { dirname as dirname11, join as join31 } from "path";
17550
17743
  var AGENT_NAMES = [
17551
17744
  "claude",
17552
17745
  "cursor",
@@ -17766,7 +17959,7 @@ function bundledSkillsVersion(dir) {
17766
17959
  }
17767
17960
  }
17768
17961
  function bundledSkillsDir(from) {
17769
- let dir = dirname10(from);
17962
+ let dir = dirname11(from);
17770
17963
  for (let i = 0; i < 6; i++) {
17771
17964
  const pkg = join31(dir, "package.json");
17772
17965
  if (existsSync28(pkg)) {
@@ -17779,7 +17972,7 @@ function bundledSkillsDir(from) {
17779
17972
  } catch {
17780
17973
  }
17781
17974
  }
17782
- const up = dirname10(dir);
17975
+ const up = dirname11(dir);
17783
17976
  if (up === dir) break;
17784
17977
  dir = up;
17785
17978
  }
@@ -18501,7 +18694,7 @@ lint-gated, so a bad override fails like a bad doc.json):
18501
18694
  first ready loop (the house backdrop the studio opens on), or a flat ground offline
18502
18695
  `;
18503
18696
  async function loadActions(file) {
18504
- const raw = JSON.parse(await readFile18(file, "utf8"));
18697
+ const raw = JSON.parse(await readFile19(file, "utf8"));
18505
18698
  const errors = validateActions(raw);
18506
18699
  if (!errors.length) errors.push(...jsSyntaxProblems(raw));
18507
18700
  if (errors.length)
@@ -18723,7 +18916,7 @@ function reRecord(outDir, flags, r) {
18723
18916
  ...from ? {
18724
18917
  reuse: {
18725
18918
  from,
18726
- doc: JSON.parse(await readFile18(from, "utf8"))
18919
+ doc: JSON.parse(await readFile19(from, "utf8"))
18727
18920
  }
18728
18921
  } : {}
18729
18922
  });
@@ -18830,7 +19023,7 @@ async function cmdRecord(argv) {
18830
19023
  );
18831
19024
  const hostedActions = await scriptForHost(
18832
19025
  actions,
18833
- dirname11(resolve21(actionsPath))
19026
+ dirname12(resolve21(actionsPath))
18834
19027
  ).catch((e) => {
18835
19028
  throw new UsageError(e instanceof Error ? e.message : String(e));
18836
19029
  });
@@ -18940,7 +19133,7 @@ async function cmdRecord(argv) {
18940
19133
  sessionName,
18941
19134
  actions,
18942
19135
  (context2) => recordTake(browser2, url, actions, takePaths(outDir), r.log, {
18943
- actionsDir: dirname11(resolve21(actionsPath)),
19136
+ actionsDir: dirname12(resolve21(actionsPath)),
18944
19137
  storageState,
18945
19138
  headers,
18946
19139
  context: context2,
@@ -19048,7 +19241,7 @@ ${lines.join("\n")}
19048
19241
  sessionName,
19049
19242
  actions,
19050
19243
  (context2) => recordTake(browser, url, actions, paths, r.log, {
19051
- actionsDir: dirname11(resolve21(actionsPath)),
19244
+ actionsDir: dirname12(resolve21(actionsPath)),
19052
19245
  maxDurationSeconds,
19053
19246
  storageState,
19054
19247
  headers,
@@ -19187,7 +19380,7 @@ async function cmdCreate2(argv) {
19187
19380
  sessionName,
19188
19381
  actions,
19189
19382
  (context2) => recordTake(browser, url, actions, paths, r.log, {
19190
- actionsDir: dirname11(resolve21(actionsPath)),
19383
+ actionsDir: dirname12(resolve21(actionsPath)),
19191
19384
  maxDurationSeconds,
19192
19385
  storageState,
19193
19386
  headers,
@@ -19314,7 +19507,7 @@ async function cmdPlan(argvIn) {
19314
19507
  );
19315
19508
  const r = createReporter(flags.json === true);
19316
19509
  if (flags.fresh === true) {
19317
- await rm6(join33(dir, "doc.json"), { force: true });
19510
+ await rm7(join33(dir, "doc.json"), { force: true });
19318
19511
  r.log("note: --fresh discarded the existing doc.json");
19319
19512
  }
19320
19513
  let reuse;
@@ -19327,7 +19520,7 @@ async function cmdPlan(argvIn) {
19327
19520
  }
19328
19521
  reuse = {
19329
19522
  from,
19330
- doc: JSON.parse(await readFile18(from, "utf8"))
19523
+ doc: JSON.parse(await readFile19(from, "utf8"))
19331
19524
  };
19332
19525
  }
19333
19526
  const style = await resolveStyleRef(flags);
@@ -19799,7 +19992,7 @@ async function resolveDocRef(ref, flags, what) {
19799
19992
  return {
19800
19993
  from: file,
19801
19994
  doc: migrateHostedDoc4(
19802
- JSON.parse(await readFile18(file, "utf8"))
19995
+ JSON.parse(await readFile19(file, "utf8"))
19803
19996
  )
19804
19997
  };
19805
19998
  }
@@ -19851,7 +20044,7 @@ async function cmdDigest(argv) {
19851
20044
  const take = await loadTake(dir);
19852
20045
  if (!take.doc) throw new UsageError(`${dir} has no doc.json \u2014 run plan first`);
19853
20046
  const transcriptPath = strFlag(flags, "transcript");
19854
- const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile18(transcriptPath, "utf8"))) : null;
20047
+ const transcript = transcriptPath ? parseTranscript(JSON.parse(await readFile19(transcriptPath, "utf8"))) : null;
19855
20048
  const style = await resolveStyleRef(flags);
19856
20049
  const noFrames = flags["no-frames"] === true;
19857
20050
  let browser = null;
@@ -19964,7 +20157,7 @@ async function cmdCallout(argv) {
19964
20157
  `no register: put a BRAND.md beside the take (vos brand <url> --out BRAND.md; its bgA and accent are the ground and the accent), or pass --ground #hex --accent #hex`
19965
20158
  );
19966
20159
  const codeFile = strFlag(flags, "code-file");
19967
- const code = codeFile ? await readFile18(codeFile, "utf8") : strFlag(flags, "code");
20160
+ const code = codeFile ? await readFile19(codeFile, "utf8") : strFlag(flags, "code");
19968
20161
  const words2 = {
19969
20162
  kicker: strFlag(flags, "kicker"),
19970
20163
  title: strFlag(flags, "title"),
@@ -20077,7 +20270,7 @@ async function cmdValidate(argv) {
20077
20270
  }
20078
20271
  if (!isTakeDir(target) && existsSync30(join33(target, "config.json"))) {
20079
20272
  const parsed = JSON.parse(
20080
- await readFile18(join33(target, "config.json"), "utf8")
20273
+ await readFile19(join33(target, "config.json"), "utf8")
20081
20274
  );
20082
20275
  const pre = preflightConfig(parsed);
20083
20276
  const problems2 = pre.ok ? [] : pre.issues.map((i) => `config.json: ${i}`);
@@ -20235,16 +20428,19 @@ async function cmdPush3(argv) {
20235
20428
  const dir = positionals[0];
20236
20429
  if (!dir)
20237
20430
  throw new UsageError(
20238
- "vos push <take> [--vos|--title|--label|--note|--prompt|--folder|--override|--yes|--key|--api|--bake|--claimable [--share]]"
20431
+ "vos push <take> [--vos|--title|--desc|--label|--note|--prompt|--folder|--override|--yes|--key|--api|--bake|--claimable [--share]]"
20239
20432
  );
20240
20433
  const refusal = takePushRefusal({
20241
20434
  vos: strFlag(flags, "vos"),
20242
20435
  trackedVosId: readSyncState(resolve21(dir))?.vosId ?? null,
20243
- programOnly: ["slug", "desc", "tags", "base", "remix-of", "still"].filter(
20436
+ programOnly: ["slug", "tags", "base", "remix-of", "still"].filter(
20244
20437
  (name) => hasFlag(flags, name)
20245
20438
  )
20246
20439
  });
20247
20440
  if (refusal) throw new UsageError(refusal);
20441
+ const description = strFlag(flags, "desc");
20442
+ const descProblem = descriptionProblem(description);
20443
+ if (descProblem) throw new UsageError(descProblem);
20248
20444
  const r = createReporter(flags.json === true);
20249
20445
  if (flags.claimable === true) {
20250
20446
  const iterating = ["vos", "folder", "override", "base"].filter(
@@ -20261,6 +20457,7 @@ async function cmdPush3(argv) {
20261
20457
  origin: strFlag(flags, "origin"),
20262
20458
  title: strFlag(flags, "title"),
20263
20459
  prompt: strFlag(flags, "prompt"),
20460
+ description,
20264
20461
  share: flags.share === true,
20265
20462
  bake: flags.bake === true
20266
20463
  },
@@ -20291,6 +20488,7 @@ reference and the only credential.`
20291
20488
  folder: strFlag(flags, "folder"),
20292
20489
  overrides,
20293
20490
  prompt: strFlag(flags, "prompt"),
20491
+ description,
20294
20492
  bake: flags.bake === true
20295
20493
  },
20296
20494
  r
@@ -20682,4 +20880,4 @@ export {
20682
20880
  verbHelp,
20683
20881
  run
20684
20882
  };
20685
- //# sourceMappingURL=chunk-5VGIQKVT.js.map
20883
+ //# sourceMappingURL=chunk-DM7ADA77.js.map