@datarails-ai/dr-cli 1.6.119 → 1.6.121

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/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "@datarails-ai/dr-cli",
11
- version: "1.6.119",
11
+ version: "1.6.121",
12
12
  description: "Datarails CLI",
13
13
  type: "module",
14
14
  engines: {
@@ -69,7 +69,7 @@ var package_default = {
69
69
  };
70
70
 
71
71
  // src/buildInfo.ts
72
- var BUILD_TIME = "2026-10-12T09:44:27.404Z";
72
+ var BUILD_TIME = "2026-10-12T10:41:54.439Z";
73
73
  function formatBuildTime(iso) {
74
74
  if (iso === "") return "";
75
75
  const d = new Date(iso);
@@ -3798,6 +3798,11 @@ var PROD_GATE_TIER_B = /* @__PURE__ */ new Set([
3798
3798
  // no interactive word gate. If a rescan ever gains a destructive side effect,
3799
3799
  // this key moves to Tier A.
3800
3800
  "table|rescan",
3801
+ // dr filebox rescan-version (DR-54137): the same reasoning at the scale of one
3802
+ // version — the UI's per-version Rescan. It re-extracts that version with the
3803
+ // tables' current datamappers and replaces its rows; it never deletes rows it
3804
+ // does not re-load, and it is re-runnable. Token-only.
3805
+ "filebox-version|rescan",
3801
3806
  // spend-control (dr spend-control contracts bulk-create). `archive` and its
3802
3807
  // inverse `restore` are a single PATCH of `is_archived` — each undoes the
3803
3808
  // other exactly, so token-only. The hard `delete` verb is Tier A.
@@ -19116,6 +19121,24 @@ async function updateAutoCalculateSettings(client, docId, body, reqOptions) {
19116
19121
  async function downloadVersion(client, versionId, reqOptions) {
19117
19122
  return client.do("GET", `/api/documents/versions/${versionId}/file`, void 0, reqOptions);
19118
19123
  }
19124
+ async function listBucketTemplates(client, bucketId, reqOptions) {
19125
+ const out = await client.getJSON(
19126
+ `/api/buckets/${bucketId}/templates`,
19127
+ reqOptions
19128
+ );
19129
+ return Array.isArray(out) ? out : [];
19130
+ }
19131
+ async function listDocumentTables(client, docId, reqOptions) {
19132
+ const out = await client.getJSON(
19133
+ `/api/documents/${docId}/bucket`,
19134
+ reqOptions
19135
+ );
19136
+ const list28 = out?.templates;
19137
+ return Array.isArray(list28) ? list28 : [];
19138
+ }
19139
+ async function rescanBucketVersions(client, bucketId, body, reqOptions) {
19140
+ await client.postJSON(`/api/buckets/${bucketId}/rescan_versions`, body, reqOptions);
19141
+ }
19119
19142
 
19120
19143
  // src/commands/_lutMappings.ts
19121
19144
  var ROW_FIELDS = ["create", "update", "delete"];
@@ -35341,6 +35364,232 @@ function registerFileboxAdminCommands(fb, setupCall2, addServerFlag2, addAccount
35341
35364
  addAccountFlag2(vrSetCmd);
35342
35365
  }
35343
35366
 
35367
+ // src/commands/_typedError.ts
35368
+ function isTypedError(err) {
35369
+ return !(err instanceof Error) || err.constructor !== Error || "code" in err;
35370
+ }
35371
+ function withErrorContext(context, err) {
35372
+ if (isTypedError(err)) return err;
35373
+ const e = err;
35374
+ return new Error(`${context}: ${e.message}`, { cause: e });
35375
+ }
35376
+
35377
+ // src/commands/filebox_rescanVersion.ts
35378
+ function sortedUnique(ids) {
35379
+ return [...new Set(ids)].sort((a, b) => a - b);
35380
+ }
35381
+ function rescanVersionAction(versionId, tableIds) {
35382
+ return {
35383
+ resource: "filebox-version",
35384
+ id: `${versionId}:tables=${sortedUnique(tableIds).join(",")}`,
35385
+ verb: "rescan"
35386
+ };
35387
+ }
35388
+ function buildRescanVersionConfirmCmd(versionId, tableIds, serverFlag, asJson) {
35389
+ const tableFlags = sortedUnique(tableIds).map((t) => ` --table ${t}`).join("");
35390
+ const jsonFlag = asJson ? " --json" : "";
35391
+ return `dr filebox rescan-version ${versionId}${tableFlags} --server ${serverFlag}${jsonFlag}`;
35392
+ }
35393
+ function describeVersionTags(tags) {
35394
+ if (!Array.isArray(tags) || tags.length === 0) return "(none)";
35395
+ const parts = [];
35396
+ for (const t of tags) {
35397
+ if (t === null || typeof t !== "object") continue;
35398
+ const r = t;
35399
+ const name = typeof r["name"] === "string" && r["name"] !== "" ? r["name"] : `tag ${String(r["id"])}`;
35400
+ const label = r["label"];
35401
+ const value = typeof label === "string" && label !== "" ? label : r["value"] === void 0 || r["value"] === null ? "?" : String(r["value"]);
35402
+ parts.push(`${name}: ${value}`);
35403
+ }
35404
+ return parts.length === 0 ? "(none)" : parts.join(", ");
35405
+ }
35406
+ async function step(context, p) {
35407
+ try {
35408
+ return await p;
35409
+ } catch (err) {
35410
+ throw withErrorContext(context, err);
35411
+ }
35412
+ }
35413
+ async function resolveRescan2(cfg) {
35414
+ const opts = { signal: cfg.signal };
35415
+ const version = await step(
35416
+ `load version ${cfg.versionId}`,
35417
+ getVersion(cfg.client, cfg.versionId, opts)
35418
+ );
35419
+ if (version.auto_save === true) {
35420
+ throw new Error(
35421
+ `version ${cfg.versionId} is archived; the server only rescans non-archived versions (restore it first with \`dr filebox restore-version ${cfg.versionId}\`)`
35422
+ );
35423
+ }
35424
+ if (version.include_file === false) {
35425
+ throw new Error(
35426
+ `version ${cfg.versionId} is excluded from the database; a rescan would extract nothing (include it with \`dr filebox set-version-include ${cfg.versionId} --include on\`, which itself queues the extraction)`
35427
+ );
35428
+ }
35429
+ const doc = await step(
35430
+ `load filebox ${version.document} of version ${cfg.versionId}`,
35431
+ getDetails(cfg.client, version.document, opts)
35432
+ );
35433
+ const bucketId = typeof doc.bucket === "number" && doc.bucket > 0 ? doc.bucket : void 0;
35434
+ if (bucketId === void 0) {
35435
+ throw new Error(
35436
+ `filebox ${version.document} has no bucket; the per-version rescan endpoint is bucket-scoped and cannot address this version`
35437
+ );
35438
+ }
35439
+ const [bucketTables, docTables] = await Promise.all([
35440
+ step(
35441
+ `list tables of filebox ${version.document}`,
35442
+ listBucketTemplates(cfg.client, bucketId, opts)
35443
+ ),
35444
+ step(
35445
+ `list tables filebox ${version.document} is mapped into`,
35446
+ listDocumentTables(cfg.client, version.document, opts)
35447
+ )
35448
+ ]);
35449
+ const mappedIds = new Set(docTables.map((t) => t.id));
35450
+ const eligible = bucketTables.filter((t) => mappedIds.has(t.id));
35451
+ let tables;
35452
+ if (cfg.tableIds.length === 0) {
35453
+ tables = eligible;
35454
+ } else {
35455
+ const byId = new Map(eligible.map((t) => [t.id, t]));
35456
+ const unknown = sortedUnique(cfg.tableIds).filter((id) => !byId.has(id));
35457
+ if (unknown.length > 0) {
35458
+ throw new Error(
35459
+ `--table ${unknown.join(", ")}: not an active table that filebox ${version.document} is mapped into, so the server would skip it` + (eligible.length > 0 ? ` (eligible tables: ${eligible.map((t) => t.id).join(", ")})` : "")
35460
+ );
35461
+ }
35462
+ tables = sortedUnique(cfg.tableIds).map((id) => byId.get(id));
35463
+ }
35464
+ if (tables.length === 0) {
35465
+ throw new Error(
35466
+ `filebox ${version.document} is not mapped into any active table; there is nothing to re-extract version ${cfg.versionId} into`
35467
+ );
35468
+ }
35469
+ return { version, doc, bucketId, tables };
35470
+ }
35471
+ function tableLabel(t) {
35472
+ return t.name !== "" ? `${t.name} (${t.id})` : String(t.id);
35473
+ }
35474
+ async function runFileboxRescanVersion(cfg) {
35475
+ if (cfg.versionId <= 0) {
35476
+ throw new Error("version id must be positive");
35477
+ }
35478
+ for (const id of cfg.tableIds) {
35479
+ if (!Number.isInteger(id) || id <= 0) {
35480
+ throw new Error(`invalid --table value: "${id}"`);
35481
+ }
35482
+ }
35483
+ if (cfg.tableIds.length > 0) {
35484
+ await preflightConfirmToken(rescanVersionAction(cfg.versionId, cfg.tableIds), cfg.token);
35485
+ }
35486
+ const r = await resolveRescan2(cfg);
35487
+ const tableIds = r.tables.map((t) => t.id);
35488
+ const action = rescanVersionAction(cfg.versionId, tableIds);
35489
+ await preflightConfirmToken(action, cfg.token);
35490
+ const docLabel = r.doc.name !== "" ? `${r.doc.name} (${r.doc.id})` : String(r.doc.id);
35491
+ await twoStepConfirm({
35492
+ out: cfg.out,
35493
+ action,
35494
+ token: cfg.token,
35495
+ server: cfg.serverFlag,
35496
+ confirmCmd: buildRescanVersionConfirmCmd(
35497
+ cfg.versionId,
35498
+ cfg.tableIds,
35499
+ cfg.serverFlag,
35500
+ cfg.asJson
35501
+ ),
35502
+ describe: async () => [
35503
+ `version: ${cfg.versionId} (v${r.version.version}, ${r.version.name})`,
35504
+ `filebox: ${docLabel}, bucket ${r.bucketId}`,
35505
+ `tags: ${describeVersionTags(r.version.tags)} (unchanged)`,
35506
+ `tables: ${r.tables.map(tableLabel).join(", ")}`,
35507
+ "effect: re-extracts THIS version only, in place, with the tables\u2019 current datamappers; its existing rows in each table are replaced, not duplicated",
35508
+ "scope: other versions of the filebox or collection process are not touched; the token is bound to these tables"
35509
+ ],
35510
+ execute: async () => {
35511
+ await step(
35512
+ `rescan version ${cfg.versionId}`,
35513
+ rescanBucketVersions(
35514
+ cfg.client,
35515
+ r.bucketId,
35516
+ { versions: [cfg.versionId], templates: tableIds },
35517
+ { signal: cfg.signal }
35518
+ )
35519
+ );
35520
+ if (cfg.asJson) {
35521
+ writeJSONIndented(cfg.out, {
35522
+ version_id: cfg.versionId,
35523
+ version: r.version.version,
35524
+ doc_id: r.doc.id,
35525
+ bucket_id: r.bucketId,
35526
+ tables: r.tables.map((t) => ({ id: t.id, name: t.name })),
35527
+ queued: true
35528
+ });
35529
+ return;
35530
+ }
35531
+ cfg.out.write(
35532
+ `Rescan queued for version ${cfg.versionId} (v${r.version.version}) into ${r.tables.length} table(s). Poll with: dr scans status ${tableIds[0]} --server ${cfg.serverFlag}
35533
+ `
35534
+ );
35535
+ }
35536
+ });
35537
+ }
35538
+ function parseVersionIdArg(arg) {
35539
+ const n = Number(arg);
35540
+ if (!Number.isInteger(n) || String(n) !== arg || n <= 0) {
35541
+ throw new Error(`invalid version id: "${arg}"`);
35542
+ }
35543
+ return n;
35544
+ }
35545
+ function collectTableId(value, previous) {
35546
+ const n = Number(value);
35547
+ if (!Number.isInteger(n) || String(n) !== value || n <= 0) {
35548
+ throw new Error(`invalid --table value: "${value}"`);
35549
+ }
35550
+ return previous.concat([n]);
35551
+ }
35552
+ function registerFileboxRescanVersionCommand(fb) {
35553
+ const cmd = fb.command("rescan-version").description(
35554
+ 'Re-extract ONE filebox version in place, without changing its tags \u2014 the UI version "three dots > Rescan". Only this version is re-extracted (not the rest of its filebox or collection process), with the tables\u2019 current datamappers; its rows in each table are replaced, not duplicated. Two-step confirmation: run once to see the version, filebox, tags and tables and get a token, then re-run with --confirm-token.'
35555
+ ).argument("<version_id>", "document version id (global)").option(
35556
+ "--table <table_id>",
35557
+ "Re-extract into this table only (repeatable). Default: every active table the filebox is mapped into.",
35558
+ collectTableId,
35559
+ []
35560
+ ).option(
35561
+ "--confirm-token <token>",
35562
+ "Token from a prior plan run \u2014 required to actually trigger the rescan",
35563
+ ""
35564
+ ).option("--json", "Output the queued rescan as JSON instead of prose", false).action(
35565
+ async (idStr, opts) => {
35566
+ const versionId = parseVersionIdArg(idStr);
35567
+ const tableIds = opts.table ?? [];
35568
+ if (tableIds.length > 0) {
35569
+ await preflightConfirmToken(rescanVersionAction(versionId, tableIds), opts.confirmToken);
35570
+ }
35571
+ const serverKey = resolveServer(opts.server);
35572
+ const setup = await setupCall(opts.server, opts.account);
35573
+ try {
35574
+ await runFileboxRescanVersion({
35575
+ client: setup.client,
35576
+ versionId,
35577
+ tableIds,
35578
+ token: opts.confirmToken,
35579
+ serverFlag: serverKey,
35580
+ asJson: opts.json === true,
35581
+ signal: setup.signal,
35582
+ out: process.stdout
35583
+ });
35584
+ } finally {
35585
+ setup.cleanup();
35586
+ }
35587
+ }
35588
+ );
35589
+ addServerFlag(cmd);
35590
+ addAccountFlag(cmd);
35591
+ }
35592
+
35344
35593
  // src/api/forecast.ts
35345
35594
  var SCENARIO_PARAM = "Forecast";
35346
35595
  async function listScenarios(client, filter = {}, options) {
@@ -36979,7 +37228,7 @@ function parseVersionAddressing(positional, opts, latestFlag) {
36979
37228
  );
36980
37229
  }
36981
37230
  if (hasPositional) {
36982
- return { mode: "direct", versionId: parseVersionIdArg(positional) };
37231
+ return { mode: "direct", versionId: parseVersionIdArg2(positional) };
36983
37232
  }
36984
37233
  if (hasNumber && hasLatest) {
36985
37234
  throw new Error(`pass either --version-number <n> or ${latestFlag}, not both`);
@@ -37159,9 +37408,6 @@ function parseVersionLabelFlags(label, clear) {
37159
37408
  }
37160
37409
  return validateVersionLabel(label);
37161
37410
  }
37162
- function isTypedError(err) {
37163
- return !(err instanceof Error) || err.constructor !== Error || "code" in err;
37164
- }
37165
37411
  async function writeVersionLabel(client, versionId, label, signal) {
37166
37412
  const res = await setVersionNameLabel(client, versionId, label, { signal });
37167
37413
  if (res.status < 200 || res.status >= 300) {
@@ -37344,6 +37590,34 @@ function requireTagPermission(details, perm, docId, action) {
37344
37590
  );
37345
37591
  }
37346
37592
  }
37593
+ var TagWriteRefusedError = class extends Error {
37594
+ constructor(message, reason) {
37595
+ super(message);
37596
+ this.reason = reason;
37597
+ this.name = "TagWriteRefusedError";
37598
+ }
37599
+ reason;
37600
+ };
37601
+ function fullTagSetWith(stored, write) {
37602
+ const kept = [];
37603
+ for (const raw of stored ?? []) {
37604
+ if (typeof raw !== "object" || raw === null) {
37605
+ continue;
37606
+ }
37607
+ const t = raw;
37608
+ const id = typeof t.id === "string" && t.id.trim() !== "" ? Number(t.id) : t.id;
37609
+ if (typeof id !== "number" || !Number.isInteger(id) || id === write.id || kept.some((k) => k.id === id) || t.value === void 0 || t.value === null) {
37610
+ continue;
37611
+ }
37612
+ kept.push({
37613
+ id,
37614
+ value: t.value,
37615
+ ...typeof t.name === "string" ? { name: t.name } : {},
37616
+ ...typeof t.label === "string" ? { label: t.label } : {}
37617
+ });
37618
+ }
37619
+ return [...kept, write].sort((a, b) => a.id - b.id);
37620
+ }
37347
37621
  async function runFileboxSetVersionFileStatus(cfg) {
37348
37622
  if (cfg.docId <= 0) {
37349
37623
  throw new Error("document id must be positive");
@@ -37398,6 +37672,8 @@ async function runFileboxSetVersionFileStatus(cfg) {
37398
37672
  changed: false,
37399
37673
  verified: true,
37400
37674
  reapplied: false,
37675
+ date_tag_value: target.date_tag_value ?? null,
37676
+ date_tag_value_preserved: true,
37401
37677
  demoted_version_ids: [],
37402
37678
  rescan_queued: false
37403
37679
  });
@@ -37429,19 +37705,31 @@ async function runFileboxSetVersionFileStatus(cfg) {
37429
37705
  );
37430
37706
  }
37431
37707
  }
37432
- const tagsToUpdate = JSON.stringify([
37433
- { id: tag.id, value: cfg.status, name: tag.name, label: cfg.status }
37434
- ]);
37708
+ const statusWrite = {
37709
+ id: tag.id,
37710
+ value: cfg.status,
37711
+ name: tag.name,
37712
+ label: cfg.status
37713
+ };
37714
+ let base = target.tags ?? [];
37715
+ const refreshBase = (v) => {
37716
+ if (v !== null && v.tags.length > 0) {
37717
+ base = v.tags;
37718
+ }
37719
+ };
37435
37720
  const write = async () => {
37436
37721
  const res = await setVersionTagsAndLabel(
37437
37722
  cfg.client,
37438
37723
  cfg.versionId,
37439
- { tags_to_update: tagsToUpdate },
37724
+ { tags_to_update: JSON.stringify(fullTagSetWith(base, statusWrite)) },
37440
37725
  { signal: cfg.signal }
37441
37726
  );
37442
37727
  if (res.status < 200 || res.status >= 300) {
37443
37728
  const body = await res.text();
37444
- throw new Error(`set version file status refused (HTTP ${res.status}): ${body.trim()}`);
37729
+ throw new TagWriteRefusedError(
37730
+ `set version file status refused (HTTP ${res.status}): ${body.trim()}`,
37731
+ `(HTTP ${res.status}): ${body.trim()}`
37732
+ );
37445
37733
  }
37446
37734
  };
37447
37735
  const readBack = async () => {
@@ -37453,6 +37741,7 @@ async function runFileboxSetVersionFileStatus(cfg) {
37453
37741
  let seen = await readBack();
37454
37742
  if (seen !== null && tagValueOf(seen.tags, tag.id) !== cfg.status) {
37455
37743
  reapplied = true;
37744
+ refreshBase(seen);
37456
37745
  await write();
37457
37746
  seen = await readBack();
37458
37747
  const now = seen === null ? void 0 : tagValueOf(seen.tags, tag.id);
@@ -37462,6 +37751,53 @@ async function runFileboxSetVersionFileStatus(cfg) {
37462
37751
  );
37463
37752
  }
37464
37753
  }
37754
+ const dateBefore = target.date_tag_value;
37755
+ const dateMoved = (v) => v !== null && typeof dateBefore === "number" && v.row.date_tag_value !== void 0 && v.row.date_tag_value !== dateBefore;
37756
+ let dateUnknown = false;
37757
+ if (dateMoved(seen)) {
37758
+ err.write(
37759
+ `warning: date_tag_value on version ${cfg.versionId} changed (${JSON.stringify(dateBefore)} -> ${JSON.stringify(seen.row.date_tag_value ?? null)}) when its File Status was set; re-applying the full tag set once
37760
+ `
37761
+ );
37762
+ reapplied = true;
37763
+ refreshBase(seen);
37764
+ let refused = false;
37765
+ try {
37766
+ await write();
37767
+ } catch (e) {
37768
+ if (cfg.signal.aborted) {
37769
+ throw e;
37770
+ }
37771
+ if (e instanceof TagWriteRefusedError) {
37772
+ refused = true;
37773
+ err.write(`warning: re-applying the full tag set was refused ${e.reason}
37774
+ `);
37775
+ } else {
37776
+ err.write(
37777
+ `warning: re-applying the full tag set failed (${e instanceof Error ? e.message : String(e)}); its outcome is unknown, reading the version again
37778
+ `
37779
+ );
37780
+ }
37781
+ }
37782
+ const after = refused ? seen : await readBack();
37783
+ if (after === null) {
37784
+ dateUnknown = true;
37785
+ err.write(
37786
+ `warning: could not read version ${cfg.versionId} back to confirm its date_tag_value after re-applying the full tag set \u2014 check it with \`dr filebox versions ${cfg.docId} --json\`
37787
+ `
37788
+ );
37789
+ } else {
37790
+ seen = after;
37791
+ }
37792
+ }
37793
+ const dateAfter = dateUnknown || seen === null || seen.row.date_tag_value === void 0 ? null : seen.row.date_tag_value;
37794
+ const datePreserved = dateUnknown || seen === null || dateBefore === void 0 || seen.row.date_tag_value === void 0 ? null : !dateMoved(seen);
37795
+ if (datePreserved === false) {
37796
+ err.write(
37797
+ `warning: date_tag_value on version ${cfg.versionId} is still ${JSON.stringify(dateAfter)} (was ${JSON.stringify(dateBefore)}) after one re-apply; its tags still carry the date. Restore it with \`dr filebox set-version-date-tag ${cfg.versionId} --doc ${cfg.docId} --<dimension> <date>\` (it sends the date tag alone; on a planning filebox use \`dr filebox set-version-cycle ${cfg.versionId} --doc ${cfg.docId} --date <date> --scenario <name>\` instead, which keeps the cycle), then check \`dr filebox versions ${cfg.docId} --json\`
37798
+ `
37799
+ );
37800
+ }
37465
37801
  if (seen === null) {
37466
37802
  err.write(
37467
37803
  `warning: could not read version ${cfg.versionId} back to verify its File Status \u2014 confirm with \`dr filebox versions ${cfg.docId}\`
@@ -37487,6 +37823,8 @@ async function runFileboxSetVersionFileStatus(cfg) {
37487
37823
  changed: true,
37488
37824
  verified,
37489
37825
  reapplied,
37826
+ date_tag_value: dateAfter,
37827
+ date_tag_value_preserved: datePreserved,
37490
37828
  demoted_version_ids: demotedIds,
37491
37829
  rescan_queued: true
37492
37830
  });
@@ -37497,6 +37835,12 @@ async function runFileboxSetVersionFileStatus(cfg) {
37497
37835
  `Set File Status on version ${cfg.versionId}: ${JSON.stringify(previous ?? "(unset)")} -> "${cfg.status}" (${verdict}).
37498
37836
  `
37499
37837
  );
37838
+ if (datePreserved === false) {
37839
+ cfg.out.write(
37840
+ `date_tag_value NOT preserved: it reads ${JSON.stringify(dateAfter)}, was ${JSON.stringify(dateBefore)} (see the warning above).
37841
+ `
37842
+ );
37843
+ }
37500
37844
  if (demotedIds.length > 0) {
37501
37845
  cfg.out.write(
37502
37846
  `Only one version per period can be ${cfg.status}: ${plural2(demotedIds.length, "version", "versions")} ${demotedIds.join(", ")} ${plural2(demotedIds.length, "was", "were")} moved to Draft by the server.
@@ -38583,7 +38927,7 @@ function parseFileboxIdArg(arg) {
38583
38927
  }
38584
38928
  return n;
38585
38929
  }
38586
- function parseVersionIdArg(arg) {
38930
+ function parseVersionIdArg2(arg) {
38587
38931
  const n = Number(arg);
38588
38932
  if (!Number.isInteger(n) || String(n) !== arg) {
38589
38933
  throw new Error(`invalid version id: "${arg}"`);
@@ -38684,7 +39028,7 @@ function registerFileboxCommand(root) {
38684
39028
  addServerFlag(tagsCmd);
38685
39029
  addAccountFlag(tagsCmd);
38686
39030
  const sheetsCmd = fb.command("sheets").description("List sheets in a filebox version (names, indices, row/col counts)").argument("<version_id>", "version id (positive integer)").option("--json", "Output raw JSON instead of a table", false).action(async (vidStr, opts) => {
38687
- const versionId = parseVersionIdArg(vidStr);
39031
+ const versionId = parseVersionIdArg2(vidStr);
38688
39032
  const setup = await setupCall(opts.server, opts.account);
38689
39033
  try {
38690
39034
  await runFileboxSheets({
@@ -38702,7 +39046,7 @@ function registerFileboxCommand(root) {
38702
39046
  addAccountFlag(sheetsCmd);
38703
39047
  const previewCmd = fb.command("preview").description("Preview raw cell data of a filebox version (row 1 is headers)").argument("<version_id>", "version id (positive integer)").option("--sheet <name>", "Sheet name. Defaults to first sheet from /desc.", "").option("--sheet-ind <n>", "Sheet index (0-based). Defaults to 0.", "0").option("--start <n>", "First row to read (1-based)", "1").option("--rows <n>", "Number of rows to return (start..start+rows-1)", "100").option("--data-only", "Return evaluated values only; omit raw formulas", false).option("--json", "Output raw JSON instead of a table", false).action(
38704
39048
  async (vidStr, opts) => {
38705
- const versionId = parseVersionIdArg(vidStr);
39049
+ const versionId = parseVersionIdArg2(vidStr);
38706
39050
  const sheetInd = parsePositiveIntFlag2(opts.sheetInd, "--sheet-ind", 0);
38707
39051
  const start = parsePositiveIntFlag2(opts.start, "--start", 1);
38708
39052
  const rows = parsePositiveIntFlag2(opts.rows, "--rows", 100);
@@ -39490,6 +39834,7 @@ function registerFileboxCommand(root) {
39490
39834
  addServerFlag(dqSyncCmd);
39491
39835
  addAccountFlag(dqSyncCmd);
39492
39836
  registerFileboxAdminCommands(fb, setupCall, addServerFlag, addAccountFlag);
39837
+ registerFileboxRescanVersionCommand(fb);
39493
39838
  }
39494
39839
 
39495
39840
  // src/commands/integrations.ts
@@ -50743,14 +51088,14 @@ function renderMecProcessDetail(out, p) {
50743
51088
  }
50744
51089
  async function runMecStepGet(cfg) {
50745
51090
  await requireMec(cfg.client, { signal: cfg.signal });
50746
- const step = await withMec(
51091
+ const step2 = await withMec(
50747
51092
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50748
51093
  );
50749
51094
  if (cfg.asJson) {
50750
- writeJSONIndented(cfg.out, step);
51095
+ writeJSONIndented(cfg.out, step2);
50751
51096
  return;
50752
51097
  }
50753
- renderMecStepDetail(cfg.out, step);
51098
+ renderMecStepDetail(cfg.out, step2);
50754
51099
  }
50755
51100
  function renderMecStepDetail(out, s) {
50756
51101
  const rows = [
@@ -50797,10 +51142,10 @@ function renderMecTaskTable(out, tasks, indent) {
50797
51142
  }
50798
51143
  async function runMecTaskList(cfg) {
50799
51144
  await requireMec(cfg.client, { signal: cfg.signal });
50800
- const step = await withMec(
51145
+ const step2 = await withMec(
50801
51146
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50802
51147
  );
50803
- const tasks = allStepTasks(step);
51148
+ const tasks = allStepTasks(step2);
50804
51149
  if (cfg.asJson) {
50805
51150
  writeJSONIndented(cfg.out, tasks);
50806
51151
  return;
@@ -50816,10 +51161,10 @@ async function runMecTaskList(cfg) {
50816
51161
  }
50817
51162
  async function runMecTaskGet(cfg) {
50818
51163
  await requireMec(cfg.client, { signal: cfg.signal });
50819
- const step = await withMec(
51164
+ const step2 = await withMec(
50820
51165
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50821
51166
  );
50822
- const tasks = allStepTasks(step);
51167
+ const tasks = allStepTasks(step2);
50823
51168
  const match = tasks.find((t) => t.id === cfg.taskId);
50824
51169
  if (match === void 0) {
50825
51170
  throw new Error(
@@ -51194,11 +51539,11 @@ async function collectMecActivationCascade(client, template, signal) {
51194
51539
  return lines;
51195
51540
  }
51196
51541
  const steps = process2.steps ?? [];
51197
- for (const step of steps) {
51198
- if (step.notify_automatically !== true) continue;
51199
- const tasks = allStepTasks(step);
51542
+ for (const step2 of steps) {
51543
+ if (step2.notify_automatically !== true) continue;
51544
+ const tasks = allStepTasks(step2);
51200
51545
  if (tasks.length === 0) continue;
51201
- lines.push(` step "${step.name}":`);
51546
+ lines.push(` step "${step2.name}":`);
51202
51547
  for (const t of tasks) {
51203
51548
  lines.push(` \u2192 ${userLabel(t.assignee)} (task: ${taskDisplayName(t)})`);
51204
51549
  }
@@ -51695,9 +52040,9 @@ async function runMecTaskDelete(cfg) {
51695
52040
  };
51696
52041
  await preflightConfirmToken(action, cfg.token);
51697
52042
  await requireMec(cfg.client, { signal: cfg.signal });
51698
- let step;
52043
+ let step2;
51699
52044
  try {
51700
- step = await withMec(
52045
+ step2 = await withMec(
51701
52046
  () => getStep(cfg.client, cfg.processId, cfg.stepId, {
51702
52047
  signal: cfg.signal
51703
52048
  })
@@ -51713,11 +52058,11 @@ async function runMecTaskDelete(cfg) {
51713
52058
  describe: async () => {
51714
52059
  const lines = [
51715
52060
  `process: ${cfg.processId}`,
51716
- `step: ${cfg.stepId}${step !== void 0 ? ` ("${step.name}")` : ""}`,
52061
+ `step: ${cfg.stepId}${step2 !== void 0 ? ` ("${step2.name}")` : ""}`,
51717
52062
  `tasks to delete: ${cfg.ids.length}`
51718
52063
  ];
51719
52064
  const tasksById = new Map(
51720
- (step === void 0 ? [] : allStepTasks(step)).map((t) => [t.id, t])
52065
+ (step2 === void 0 ? [] : allStepTasks(step2)).map((t) => [t.id, t])
51721
52066
  );
51722
52067
  for (const { id } of cfg.ids) {
51723
52068
  const found = tasksById.get(id);
@@ -52322,8 +52667,8 @@ function wireMecWorkflow(mecCmd) {
52322
52667
  });
52323
52668
  addServerFlag(procGetCmd);
52324
52669
  addAccountFlag(procGetCmd);
52325
- const step = workflow.command("step").description("MEC workflow steps");
52326
- const stepGetCmd = step.command("get").description("Show a step with embedded tasks").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--json", "Output raw JSON", false).action(
52670
+ const step2 = workflow.command("step").description("MEC workflow steps");
52671
+ const stepGetCmd = step2.command("get").description("Show a step with embedded tasks").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--json", "Output raw JSON", false).action(
52327
52672
  async (processIdRaw, stepIdRaw, opts) => {
52328
52673
  const processId = parsePositiveIntId(processIdRaw, "process id");
52329
52674
  const stepId = parsePositiveIntId(stepIdRaw, "step id");
@@ -52705,7 +53050,7 @@ function wireMecWorkflow(mecCmd) {
52705
53050
  });
52706
53051
  addServerFlag(procDeleteCmd);
52707
53052
  addAccountFlag(procDeleteCmd);
52708
- const stepCreateCmd = step.command("create").description("Create a new step on a MEC process instance").argument("<process-id>", "numeric process instance id").requiredOption("--file <path>", "JSON file matching the step-create shape").option("--json", "Output the created step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
53053
+ const stepCreateCmd = step2.command("create").description("Create a new step on a MEC process instance").argument("<process-id>", "numeric process instance id").requiredOption("--file <path>", "JSON file matching the step-create shape").option("--json", "Output the created step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
52709
53054
  async (pidRaw, opts) => {
52710
53055
  const processId = parsePositiveIntId(pidRaw, "process id");
52711
53056
  const payload = await readStepCreatePayload(opts.file ?? "");
@@ -52729,7 +53074,7 @@ function wireMecWorkflow(mecCmd) {
52729
53074
  );
52730
53075
  addServerFlag(stepCreateCmd);
52731
53076
  addAccountFlag(stepCreateCmd);
52732
- const stepUpdateCmd = step.command("update").description("Edit a MEC step").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").requiredOption("--file <path>", "JSON file with partial step shape").option("--json", "Output the updated step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
53077
+ const stepUpdateCmd = step2.command("update").description("Edit a MEC step").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").requiredOption("--file <path>", "JSON file with partial step shape").option("--json", "Output the updated step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
52733
53078
  async (pidRaw, sidRaw, opts) => {
52734
53079
  const processId = parsePositiveIntId(pidRaw, "process id");
52735
53080
  const stepId = parsePositiveIntId(sidRaw, "step id");
@@ -52755,7 +53100,7 @@ function wireMecWorkflow(mecCmd) {
52755
53100
  );
52756
53101
  addServerFlag(stepUpdateCmd);
52757
53102
  addAccountFlag(stepUpdateCmd);
52758
- const stepDeleteCmd = step.command("delete").description("Delete a MEC step (cascades to all its tasks)").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--confirm-token <token>", "Token from a prior plan step").action(
53103
+ const stepDeleteCmd = step2.command("delete").description("Delete a MEC step (cascades to all its tasks)").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--confirm-token <token>", "Token from a prior plan step").action(
52759
53104
  async (pidRaw, sidRaw, opts) => {
52760
53105
  const processId = parsePositiveIntId(pidRaw, "process id");
52761
53106
  const stepId = parsePositiveIntId(sidRaw, "step id");
@@ -52777,7 +53122,7 @@ function wireMecWorkflow(mecCmd) {
52777
53122
  );
52778
53123
  addServerFlag(stepDeleteCmd);
52779
53124
  addAccountFlag(stepDeleteCmd);
52780
- const stepReorderCmd = step.command("reorder").description("Reorder steps on a MEC template").argument("<template-id>", "numeric template id").option("--file <path>", "JSON array of {id, index, y_index}").option("--order <pairs>", 'Shorthand: "id:n,id2:n2,..." (mutually exclusive with --file)').option("--confirm-token <token>", "Token from a prior plan step").action(
53125
+ const stepReorderCmd = step2.command("reorder").description("Reorder steps on a MEC template").argument("<template-id>", "numeric template id").option("--file <path>", "JSON array of {id, index, y_index}").option("--order <pairs>", 'Shorthand: "id:n,id2:n2,..." (mutually exclusive with --file)').option("--confirm-token <token>", "Token from a prior plan step").action(
52781
53126
  async (tidRaw, opts) => {
52782
53127
  const templateId = parsePositiveIntId(tidRaw, "template id");
52783
53128
  if ((opts.file === void 0 || opts.file === "") && (opts.order === void 0 || opts.order === "")) {
@@ -53674,16 +54019,16 @@ function renderProcessDetail(out, p) {
53674
54019
  }
53675
54020
  async function runStepGet(cfg) {
53676
54021
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53677
- const step = decodeShape(
54022
+ const step2 = decodeShape(
53678
54023
  await withWorkflowV2(
53679
54024
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53680
54025
  )
53681
54026
  );
53682
54027
  if (cfg.asJson) {
53683
- writeJSONIndented(cfg.out, step);
54028
+ writeJSONIndented(cfg.out, step2);
53684
54029
  return;
53685
54030
  }
53686
- renderStepDetail(cfg.out, step);
54031
+ renderStepDetail(cfg.out, step2);
53687
54032
  }
53688
54033
  function renderStepDetail(out, s) {
53689
54034
  const rows = [
@@ -53729,12 +54074,12 @@ function renderTaskTable(out, tasks, indent) {
53729
54074
  }
53730
54075
  async function runTaskList(cfg) {
53731
54076
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53732
- const step = decodeShape(
54077
+ const step2 = decodeShape(
53733
54078
  await withWorkflowV2(
53734
54079
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53735
54080
  )
53736
54081
  );
53737
- const tasks = allStepTasks2(step);
54082
+ const tasks = allStepTasks2(step2);
53738
54083
  if (cfg.asJson) {
53739
54084
  writeJSONIndented(cfg.out, tasks);
53740
54085
  return;
@@ -53750,12 +54095,12 @@ async function runTaskList(cfg) {
53750
54095
  }
53751
54096
  async function runTaskGet(cfg) {
53752
54097
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53753
- const step = decodeShape(
54098
+ const step2 = decodeShape(
53754
54099
  await withWorkflowV2(
53755
54100
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53756
54101
  )
53757
54102
  );
53758
- const tasks = allStepTasks2(step);
54103
+ const tasks = allStepTasks2(step2);
53759
54104
  const match = tasks.find((t) => t.id === cfg.taskId);
53760
54105
  if (match === void 0) {
53761
54106
  throw new Error(
@@ -54045,11 +54390,11 @@ async function collectActivationCascade(client, template, signal) {
54045
54390
  return lines;
54046
54391
  }
54047
54392
  const steps = process2.steps ?? [];
54048
- for (const step of steps) {
54049
- if (step.notify_automatically !== true) continue;
54050
- const tasks = allStepTasks2(step);
54393
+ for (const step2 of steps) {
54394
+ if (step2.notify_automatically !== true) continue;
54395
+ const tasks = allStepTasks2(step2);
54051
54396
  if (tasks.length === 0) continue;
54052
- lines.push(` step "${step.name}":`);
54397
+ lines.push(` step "${step2.name}":`);
54053
54398
  for (const t of tasks) {
54054
54399
  const assignee = userLabel2(t.assignee);
54055
54400
  const taskName = taskDisplayName2(t);
@@ -54543,9 +54888,9 @@ async function runTaskDelete(cfg) {
54543
54888
  };
54544
54889
  await preflightConfirmToken(action, cfg.token);
54545
54890
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
54546
- let step;
54891
+ let step2;
54547
54892
  try {
54548
- step = decodeShape(
54893
+ step2 = decodeShape(
54549
54894
  await withWorkflowV2(
54550
54895
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
54551
54896
  )
@@ -54561,11 +54906,11 @@ async function runTaskDelete(cfg) {
54561
54906
  describe: async () => {
54562
54907
  const lines = [
54563
54908
  `process: ${cfg.processId}`,
54564
- `step: ${cfg.stepId}${step !== void 0 ? ` ("${step.name}")` : ""}`,
54909
+ `step: ${cfg.stepId}${step2 !== void 0 ? ` ("${step2.name}")` : ""}`,
54565
54910
  `tasks to delete: ${cfg.ids.length}`
54566
54911
  ];
54567
54912
  const tasksById = new Map(
54568
- (step === void 0 ? [] : allStepTasks2(step)).map((t) => [t.id, t])
54913
+ (step2 === void 0 ? [] : allStepTasks2(step2)).map((t) => [t.id, t])
54569
54914
  );
54570
54915
  for (const { id } of cfg.ids) {
54571
54916
  const found = tasksById.get(id);
@@ -55191,8 +55536,8 @@ function wireWorkflow(root) {
55191
55536
  });
55192
55537
  addServerFlag(processGetCmd);
55193
55538
  addAccountFlag(processGetCmd);
55194
- const step = workflow.command("step").description("Workflow steps");
55195
- const stepGetCmd = step.command("get").description("Show a step with embedded tasks (`GET /processes/v2/<pid>/step/<sid>`)").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--json", "Output raw JSON", false).action(
55539
+ const step2 = workflow.command("step").description("Workflow steps");
55540
+ const stepGetCmd = step2.command("get").description("Show a step with embedded tasks (`GET /processes/v2/<pid>/step/<sid>`)").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--json", "Output raw JSON", false).action(
55196
55541
  async (processIdRaw, stepIdRaw, opts) => {
55197
55542
  const processId = parsePositiveIntId2(processIdRaw, "process id");
55198
55543
  const stepId = parsePositiveIntId2(stepIdRaw, "step id");
@@ -55495,7 +55840,7 @@ function wireWorkflow(root) {
55495
55840
  });
55496
55841
  addServerFlag(processDeleteCmd);
55497
55842
  addAccountFlag(processDeleteCmd);
55498
- const stepCreateCmd = step.command("create").description("Create a new step on a process instance").argument("<process-id>", "numeric process instance id").requiredOption("--file <path>", "JSON file matching the step-create shape").option("--json", "Output the created step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
55843
+ const stepCreateCmd = step2.command("create").description("Create a new step on a process instance").argument("<process-id>", "numeric process instance id").requiredOption("--file <path>", "JSON file matching the step-create shape").option("--json", "Output the created step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
55499
55844
  async (pidRaw, opts) => {
55500
55845
  const processId = parsePositiveIntId2(pidRaw, "process id");
55501
55846
  const payload = await readStepCreatePayload(opts.file ?? "");
@@ -55519,7 +55864,7 @@ function wireWorkflow(root) {
55519
55864
  );
55520
55865
  addServerFlag(stepCreateCmd);
55521
55866
  addAccountFlag(stepCreateCmd);
55522
- const stepUpdateCmd = step.command("update").description("Edit a step").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").requiredOption("--file <path>", "JSON file with partial step shape").option("--json", "Output the updated step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
55867
+ const stepUpdateCmd = step2.command("update").description("Edit a step").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").requiredOption("--file <path>", "JSON file with partial step shape").option("--json", "Output the updated step as JSON", false).option("--confirm-token <token>", "Token from a prior plan step").action(
55523
55868
  async (pidRaw, sidRaw, opts) => {
55524
55869
  const processId = parsePositiveIntId2(pidRaw, "process id");
55525
55870
  const stepId = parsePositiveIntId2(sidRaw, "step id");
@@ -55545,7 +55890,7 @@ function wireWorkflow(root) {
55545
55890
  );
55546
55891
  addServerFlag(stepUpdateCmd);
55547
55892
  addAccountFlag(stepUpdateCmd);
55548
- const stepDeleteCmd = step.command("delete").description("Delete a step (cascades to all its tasks)").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--confirm-token <token>", "Token from a prior plan step").action(
55893
+ const stepDeleteCmd = step2.command("delete").description("Delete a step (cascades to all its tasks)").argument("<process-id>", "numeric process instance id").argument("<step-id>", "numeric step id").option("--confirm-token <token>", "Token from a prior plan step").action(
55549
55894
  async (pidRaw, sidRaw, opts) => {
55550
55895
  const processId = parsePositiveIntId2(pidRaw, "process id");
55551
55896
  const stepId = parsePositiveIntId2(sidRaw, "step id");
@@ -55567,7 +55912,7 @@ function wireWorkflow(root) {
55567
55912
  );
55568
55913
  addServerFlag(stepDeleteCmd);
55569
55914
  addAccountFlag(stepDeleteCmd);
55570
- const stepReorderCmd = step.command("reorder").description("Reorder steps on a template").argument("<template-id>", "numeric template id").option("--file <path>", "JSON array of {id, index, y_index}").option("--order <pairs>", 'Shorthand: "id:n,id2:n2,..." (mutually exclusive with --file)').option("--confirm-token <token>", "Token from a prior plan step").action(
55915
+ const stepReorderCmd = step2.command("reorder").description("Reorder steps on a template").argument("<template-id>", "numeric template id").option("--file <path>", "JSON array of {id, index, y_index}").option("--order <pairs>", 'Shorthand: "id:n,id2:n2,..." (mutually exclusive with --file)').option("--confirm-token <token>", "Token from a prior plan step").action(
55571
55916
  async (tidRaw, opts) => {
55572
55917
  const templateId = parsePositiveIntId2(tidRaw, "template id");
55573
55918
  if ((opts.file === void 0 || opts.file === "") && (opts.order === void 0 || opts.order === "")) {
@@ -66253,15 +66598,15 @@ var MAX_ATTACHMENT_FILENAME_LENGTH = 500;
66253
66598
  function writeManualSteps(out, steps) {
66254
66599
  if (steps.length === 0) return;
66255
66600
  out.write("\nManual steps:\n");
66256
- for (const step of steps) {
66257
- const mark = step.done ? "x" : " ";
66258
- out.write(` [${mark}] ${step.n}. (${step.done_by}) ${step.title}
66601
+ for (const step2 of steps) {
66602
+ const mark = step2.done ? "x" : " ";
66603
+ out.write(` [${mark}] ${step2.n}. (${step2.done_by}) ${step2.title}
66259
66604
  `);
66260
- if (step.detail !== void 0 && step.detail !== null && step.detail !== "") {
66261
- out.write(` ${step.detail}
66605
+ if (step2.detail !== void 0 && step2.detail !== null && step2.detail !== "") {
66606
+ out.write(` ${step2.detail}
66262
66607
  `);
66263
66608
  }
66264
- for (const rec of step.records ?? []) {
66609
+ for (const rec of step2.records ?? []) {
66265
66610
  const type = (rec.type ?? "cname").toUpperCase();
66266
66611
  out.write(` ${type} ${rec.host ?? "?"} -> ${rec.value ?? "?"}
66267
66612
  `);
@@ -66471,9 +66816,9 @@ function renderSmtpResponse(out, resp, asJson) {
66471
66816
  writeJSONIndented(out, resp);
66472
66817
  return;
66473
66818
  }
66474
- for (const step of resp.steps) {
66475
- const detail = step.detail === void 0 || step.detail === null || step.detail === "" ? "" : ` - ${step.detail}`;
66476
- out.write(`${step.status.padEnd(8, " ")}${step.step}${detail}
66819
+ for (const step2 of resp.steps) {
66820
+ const detail = step2.detail === void 0 || step2.detail === null || step2.detail === "" ? "" : ` - ${step2.detail}`;
66821
+ out.write(`${step2.status.padEnd(8, " ")}${step2.step}${detail}
66477
66822
  `);
66478
66823
  }
66479
66824
  const scope = resp.team_id === null ? "org-wide" : `team ${resp.team_id}`;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "cliVersion": "1.6.119",
3
+ "cliVersion": "1.6.121",
4
4
  "_contract": "Resolve a command path through `aliases` (canonical prefix + alias segment -> canonical path) before any lookup. A path listed in `pendingCommands`, or whose first segment is a key of `pending`, is UNDECIDED, not unrestricted: fail closed or skip it, and never read a path's absence from `commands` as 'no requirement'. An entry carrying `denied` is UNAVAILABLE to any AGENT-SCOPED credential — one whose `scopes` claim carries at least one `resource:action` entry; a claim that is absent, empty, capability-only or wholly unparseable is UNRESTRICTED, and this mark does not apply to it — whatever else that entry says: it is not a ceiling and it is not always-available, its `denied.reason` says why and `denied.until` names the ticket that lifts it, and the absence of `requires` on it must NEVER be read as 'no requirement'. Only an entry in `commands` with an empty `requires` means always-available.",
5
5
  "commands": {
6
6
  "login": {
@@ -2315,6 +2315,14 @@
2315
2315
  }
2316
2316
  ]
2317
2317
  },
2318
+ "filebox rescan-version": {
2319
+ "requires": [
2320
+ {
2321
+ "resource": "fileboxes",
2322
+ "action": "write"
2323
+ }
2324
+ ]
2325
+ },
2318
2326
  "widgets list": {
2319
2327
  "requires": [
2320
2328
  {
@@ -285,7 +285,8 @@ area's gotchas.
285
285
 
286
286
  **Fileboxes & documents** → [reference/filebox-documents.md](reference/filebox-documents.md)
287
287
  - `filebox list|get|versions|tags|sheets|preview|create|upload|upload-version|update|download` (alias `fileboxes`)
288
- - `filebox archive|archive-version|restore-version|set-version-include|set-version-date-tag|set-version-label|set-version-workflow|set-version-file-status|set-version-cycle`
288
+ - `filebox archive|archive-version|restore-version|set-version-include|set-version-date-tag|set-version-label|set-version-workflow|set-version-file-status|set-version-cycle|rescan-version`
289
+ - _re-extract one version in place (UI version "Rescan"): `rescan-version <vid>` (two-step confirm; only that version, current datamappers, tags unchanged, rows replaced not doubled) — the way to redo a live version that is not the newest_
289
290
  - _archived versions (opt-in; defaults unchanged): `versions --archived|--all` to discover them; `archive-version`/`restore-version` also take `--doc` + `--version-number <n>` or `--latest-archived`/`--latest-active`_
290
291
  - _include in database: `versions --included` lists the versions whose `include_file` flag is on (intent, not delivered rows); `set-version-include <vid> --include on|off` writes the flag (two-step confirm both ways — off deletes the version's table rows, on queues an ETL scan). `--doc <id> --all-except-latest` is the bulk cleanup; it never targets the latest, and warns in the plan if the latest is itself excluded_
291
292
  - `filebox share|shares|set-option|set-tag-config|set-parent|lock|unlock|lock-date-tag|unlock-date-tag|locked-date-tags`
@@ -561,7 +562,9 @@ status` again. This is a **full historical** rescan (every calc field × every
561
562
  version). To only pick up a datamapper change on the live template, prefer
562
563
  `datamappers update <id> --payload-file <p> --rescan latest` (latest version of
563
564
  the mapper's bound documents only) — far cheaper and lower-blast. It publishes a
564
- new mapper version, so the mapper gets a **new id**, which dr prints.
565
+ new mapper version, so the mapper gets a **new id**, which dr prints. To redo
566
+ **one version** (e.g. a live version that is not the newest), use `filebox
567
+ rescan-version <vid>` — the UI's per-version Rescan, no new mapper id.
565
568
 
566
569
  **Build a `create` body from plain-English intent** (calc models, datamappers,
567
570
  metrics): you resolve names → integer IDs yourself against the tenant
@@ -114,11 +114,51 @@ set-version-include` (below).
114
114
  damage, then `filebox set-version-include --doc <doc> --all-except-latest
115
115
  --include off`.
116
116
 
117
+ ### Re-extract one version (rescan-version)
118
+
119
+ - `filebox rescan-version <vid> [--table <table_id> …] [--json]` — re-extract
120
+ **one** version into its tables **in place**, without changing its tags: the
121
+ UI's version row "three dots > Rescan". It sends the same request the UI does
122
+ (`POST /api/buckets/<bucket>/rescan_versions` with that one version id and the
123
+ filebox's tables), so the server does all the work:
124
+ - **Which versions:** only the one you name. The server does not widen it to
125
+ the other versions of the filebox or of a collection process; to redo
126
+ several, run it once per version. It runs with the tables' **current**
127
+ datamappers — no datamapper is published, no mapper gets a new id, no tag
128
+ changes.
129
+ - **Rows are replaced, not doubled:** the load deletes that version's existing
130
+ rows in each table before writing the new ones.
131
+ - **Tables:** by default every active table this filebox is mapped into —
132
+ the tables the server will actually re-extract into (the UI offers every
133
+ table of the bucket, and the server silently drops the ones this filebox
134
+ does not feed). `--table` (repeatable) narrows it; an id this filebox does
135
+ not feed is refused rather than sent and skipped.
136
+ - This is the way to re-extract a live version that is **not the newest** in
137
+ its filebox — `datamappers update|bind --rescan latest` only reach the
138
+ newest, and `rescan table` redoes the whole table.
139
+ - **Two-step confirm** (Tier B, like `rescan table`: token only): the first
140
+ run prints the version, its filebox and bucket, its tags, the tables, and a
141
+ token; re-run with `--confirm-token`. The token is bound to the version and
142
+ to the exact table set the plan showed: if a table starts or stops being fed
143
+ in between, the token no longer matches — re-run the plan. Then poll `scans
144
+ status <table_id>`.
145
+ - Refused up front, with the remedy: an **archived** version
146
+ (`restore-version` first) and a version **excluded from the database**
147
+ (`set-version-include <vid> --include on`, which queues the extraction
148
+ itself). The server answers `"ok"` even when it filtered everything out, so
149
+ the success line means "queued", not "rows written" — check `scans status`.
150
+ - Needs read access to the filebox; a scoped credential needs
151
+ `fileboxes:write`.
152
+
117
153
  ### Version tagging
118
154
 
119
155
  - `filebox set-version-date-tag <vid> --doc <id> --month|--quarter|--day|--year|--week …
120
156
  [--json]` — set the date tag on one version (resolves the tag id from the doc's
121
- config; other tags untouched).
157
+ config; other tags untouched in `tags`). It sends the date tag alone, and the
158
+ server rebuilds the version's dimension value from the submitted tags, so on
159
+ a filebox with a second dimension tag (a cycle or plan LIST tag) that tag
160
+ drops out of the dimension. On a planning filebox use `set-version-cycle`,
161
+ which sends the date and the cycle together.
122
162
  - `filebox set-version-label <vid> --label "<text>" | --clear [--json]` — set or
123
163
  remove a version's free-text label; name, date and dimension tags are
124
164
  untouched, and a locked date tag does not block it. No confirm step. Address it
@@ -140,6 +180,27 @@ set-version-include` (below).
140
180
  moves that version to Draft (the output names it). The status is read back and
141
181
  re-applied once if it did not stick. The write queues a rescan of the version,
142
182
  so mapped tables show the new `File Status` only after it finishes.
183
+ Like the UI, it sends the version's **full tag set** with File Status
184
+ replaced, not File Status alone. The server merges the submitted tags into
185
+ `tags` but rebuilds the version's dimension value, and with it the
186
+ `date_tag_value` that `filebox versions --json` shows, from the submitted tags
187
+ only. A File Status-only body (CLI releases before DR-54157) therefore left
188
+ `date_tag_value` null while `tags` still held the month. That is not harmless:
189
+ the server uses the dimension value to pick the latest version per period, to
190
+ apply date locks and in rescan events. The command reads `date_tag_value` back,
191
+ re-applies the full set once if it changed, and otherwise reports it: a stderr
192
+ warning, a `date_tag_value NOT preserved` line, and
193
+ `date_tag_value_preserved: false` in `--json`. `--json` also returns
194
+ `date_tag_value`, and `date_tag_value_preserved: null` when the server does
195
+ not serve it. **To repair a version left with a null date**, run
196
+ `set-version-date-tag` with the period its `tags` hold. That writes the date
197
+ tag, and the server rebuilds the dimension from it. It sends the date tag
198
+ alone, so use it only when the date is the filebox's only dimension tag. On a
199
+ planning filebox, run `set-version-cycle` with the version's existing date
200
+ and scenario instead, which sends both. Re-running
201
+ `set-version-file-status` with the same status does not repair it, because
202
+ it writes nothing. A real status change does restore it, because it re-sends
203
+ the date tag.
143
204
  - `filebox set-version-cycle <vid> --doc <id> --date <date> --scenario <name>
144
205
  [--date-tag-name <n>] [--json]` — **forecast/budget (planning) fileboxes only.**
145
206
  Sets the cycle date tag and the scenario LIST-tag together (both flags
@@ -27,7 +27,8 @@ delete`, `devices command-delete`, `validate onboarding session delete`,
27
27
  `cash hidden-rules upsert|delete`, `forecast` lock/unlock/default/delete/clone
28
28
  verbs, `workflow`/`mec workflow` create/update/activate/delete and friends,
29
29
  `collection-processes` create/mutate verbs, and the non-`delete` mutations
30
- `templates set-field-type`, `rescan table`, `features orgs add|remove`,
30
+ `templates set-field-type`, `rescan table`, `filebox rescan-version`,
31
+ `features orgs add|remove`,
31
32
  `dashboards push`, `filebox push`, and `reconciliation accounts
32
33
  set-assignee|clear-assignee|set-due-date|sign|update-settings`. When
33
34
  in doubt, run the command without a token and read the plan output.
@@ -82,7 +83,9 @@ stop it, so check the destination afterwards.
82
83
  Everything else gated is Tier B (all `create`/`update`/`reorder`/`set-*`/`lock`/
83
84
  `unlock` verbs, `templates set-field-type`, `rescan table` (a full historical
84
85
  rescan — expensive/high-blast but reversible, so token-only; the plan step shows
85
- the document + version counts), `metrics re-aggregate`, `metrics migrate` (the
86
+ the document + version counts), `filebox rescan-version` (the same, for one
87
+ version: re-extracts it in place and replaces its rows, never deletes; the plan
88
+ shows the version, filebox, tags and tables), `metrics re-aggregate`, `metrics migrate` (the
86
89
  org-wide V1→V2 backfill — deletes nothing, but **overwrites**
87
90
  `category`/`time_aggregation` with the name-derived mapping, hand-set values
88
91
  included; per-metric before→after is in the response `details[]`. A
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarails-ai/dr-cli",
3
- "version": "1.6.119",
3
+ "version": "1.6.121",
4
4
  "description": "Datarails CLI",
5
5
  "type": "module",
6
6
  "engines": {