@datarails-ai/dr-cli 1.6.119 → 1.6.120

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.120",
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:19:56.130Z";
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) {
@@ -38583,7 +38829,7 @@ function parseFileboxIdArg(arg) {
38583
38829
  }
38584
38830
  return n;
38585
38831
  }
38586
- function parseVersionIdArg(arg) {
38832
+ function parseVersionIdArg2(arg) {
38587
38833
  const n = Number(arg);
38588
38834
  if (!Number.isInteger(n) || String(n) !== arg) {
38589
38835
  throw new Error(`invalid version id: "${arg}"`);
@@ -38684,7 +38930,7 @@ function registerFileboxCommand(root) {
38684
38930
  addServerFlag(tagsCmd);
38685
38931
  addAccountFlag(tagsCmd);
38686
38932
  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);
38933
+ const versionId = parseVersionIdArg2(vidStr);
38688
38934
  const setup = await setupCall(opts.server, opts.account);
38689
38935
  try {
38690
38936
  await runFileboxSheets({
@@ -38702,7 +38948,7 @@ function registerFileboxCommand(root) {
38702
38948
  addAccountFlag(sheetsCmd);
38703
38949
  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
38950
  async (vidStr, opts) => {
38705
- const versionId = parseVersionIdArg(vidStr);
38951
+ const versionId = parseVersionIdArg2(vidStr);
38706
38952
  const sheetInd = parsePositiveIntFlag2(opts.sheetInd, "--sheet-ind", 0);
38707
38953
  const start = parsePositiveIntFlag2(opts.start, "--start", 1);
38708
38954
  const rows = parsePositiveIntFlag2(opts.rows, "--rows", 100);
@@ -39490,6 +39736,7 @@ function registerFileboxCommand(root) {
39490
39736
  addServerFlag(dqSyncCmd);
39491
39737
  addAccountFlag(dqSyncCmd);
39492
39738
  registerFileboxAdminCommands(fb, setupCall, addServerFlag, addAccountFlag);
39739
+ registerFileboxRescanVersionCommand(fb);
39493
39740
  }
39494
39741
 
39495
39742
  // src/commands/integrations.ts
@@ -50743,14 +50990,14 @@ function renderMecProcessDetail(out, p) {
50743
50990
  }
50744
50991
  async function runMecStepGet(cfg) {
50745
50992
  await requireMec(cfg.client, { signal: cfg.signal });
50746
- const step = await withMec(
50993
+ const step2 = await withMec(
50747
50994
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50748
50995
  );
50749
50996
  if (cfg.asJson) {
50750
- writeJSONIndented(cfg.out, step);
50997
+ writeJSONIndented(cfg.out, step2);
50751
50998
  return;
50752
50999
  }
50753
- renderMecStepDetail(cfg.out, step);
51000
+ renderMecStepDetail(cfg.out, step2);
50754
51001
  }
50755
51002
  function renderMecStepDetail(out, s) {
50756
51003
  const rows = [
@@ -50797,10 +51044,10 @@ function renderMecTaskTable(out, tasks, indent) {
50797
51044
  }
50798
51045
  async function runMecTaskList(cfg) {
50799
51046
  await requireMec(cfg.client, { signal: cfg.signal });
50800
- const step = await withMec(
51047
+ const step2 = await withMec(
50801
51048
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50802
51049
  );
50803
- const tasks = allStepTasks(step);
51050
+ const tasks = allStepTasks(step2);
50804
51051
  if (cfg.asJson) {
50805
51052
  writeJSONIndented(cfg.out, tasks);
50806
51053
  return;
@@ -50816,10 +51063,10 @@ async function runMecTaskList(cfg) {
50816
51063
  }
50817
51064
  async function runMecTaskGet(cfg) {
50818
51065
  await requireMec(cfg.client, { signal: cfg.signal });
50819
- const step = await withMec(
51066
+ const step2 = await withMec(
50820
51067
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50821
51068
  );
50822
- const tasks = allStepTasks(step);
51069
+ const tasks = allStepTasks(step2);
50823
51070
  const match = tasks.find((t) => t.id === cfg.taskId);
50824
51071
  if (match === void 0) {
50825
51072
  throw new Error(
@@ -51194,11 +51441,11 @@ async function collectMecActivationCascade(client, template, signal) {
51194
51441
  return lines;
51195
51442
  }
51196
51443
  const steps = process2.steps ?? [];
51197
- for (const step of steps) {
51198
- if (step.notify_automatically !== true) continue;
51199
- const tasks = allStepTasks(step);
51444
+ for (const step2 of steps) {
51445
+ if (step2.notify_automatically !== true) continue;
51446
+ const tasks = allStepTasks(step2);
51200
51447
  if (tasks.length === 0) continue;
51201
- lines.push(` step "${step.name}":`);
51448
+ lines.push(` step "${step2.name}":`);
51202
51449
  for (const t of tasks) {
51203
51450
  lines.push(` \u2192 ${userLabel(t.assignee)} (task: ${taskDisplayName(t)})`);
51204
51451
  }
@@ -51695,9 +51942,9 @@ async function runMecTaskDelete(cfg) {
51695
51942
  };
51696
51943
  await preflightConfirmToken(action, cfg.token);
51697
51944
  await requireMec(cfg.client, { signal: cfg.signal });
51698
- let step;
51945
+ let step2;
51699
51946
  try {
51700
- step = await withMec(
51947
+ step2 = await withMec(
51701
51948
  () => getStep(cfg.client, cfg.processId, cfg.stepId, {
51702
51949
  signal: cfg.signal
51703
51950
  })
@@ -51713,11 +51960,11 @@ async function runMecTaskDelete(cfg) {
51713
51960
  describe: async () => {
51714
51961
  const lines = [
51715
51962
  `process: ${cfg.processId}`,
51716
- `step: ${cfg.stepId}${step !== void 0 ? ` ("${step.name}")` : ""}`,
51963
+ `step: ${cfg.stepId}${step2 !== void 0 ? ` ("${step2.name}")` : ""}`,
51717
51964
  `tasks to delete: ${cfg.ids.length}`
51718
51965
  ];
51719
51966
  const tasksById = new Map(
51720
- (step === void 0 ? [] : allStepTasks(step)).map((t) => [t.id, t])
51967
+ (step2 === void 0 ? [] : allStepTasks(step2)).map((t) => [t.id, t])
51721
51968
  );
51722
51969
  for (const { id } of cfg.ids) {
51723
51970
  const found = tasksById.get(id);
@@ -52322,8 +52569,8 @@ function wireMecWorkflow(mecCmd) {
52322
52569
  });
52323
52570
  addServerFlag(procGetCmd);
52324
52571
  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(
52572
+ const step2 = workflow.command("step").description("MEC workflow steps");
52573
+ 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
52574
  async (processIdRaw, stepIdRaw, opts) => {
52328
52575
  const processId = parsePositiveIntId(processIdRaw, "process id");
52329
52576
  const stepId = parsePositiveIntId(stepIdRaw, "step id");
@@ -52705,7 +52952,7 @@ function wireMecWorkflow(mecCmd) {
52705
52952
  });
52706
52953
  addServerFlag(procDeleteCmd);
52707
52954
  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(
52955
+ 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
52956
  async (pidRaw, opts) => {
52710
52957
  const processId = parsePositiveIntId(pidRaw, "process id");
52711
52958
  const payload = await readStepCreatePayload(opts.file ?? "");
@@ -52729,7 +52976,7 @@ function wireMecWorkflow(mecCmd) {
52729
52976
  );
52730
52977
  addServerFlag(stepCreateCmd);
52731
52978
  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(
52979
+ 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
52980
  async (pidRaw, sidRaw, opts) => {
52734
52981
  const processId = parsePositiveIntId(pidRaw, "process id");
52735
52982
  const stepId = parsePositiveIntId(sidRaw, "step id");
@@ -52755,7 +53002,7 @@ function wireMecWorkflow(mecCmd) {
52755
53002
  );
52756
53003
  addServerFlag(stepUpdateCmd);
52757
53004
  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(
53005
+ 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
53006
  async (pidRaw, sidRaw, opts) => {
52760
53007
  const processId = parsePositiveIntId(pidRaw, "process id");
52761
53008
  const stepId = parsePositiveIntId(sidRaw, "step id");
@@ -52777,7 +53024,7 @@ function wireMecWorkflow(mecCmd) {
52777
53024
  );
52778
53025
  addServerFlag(stepDeleteCmd);
52779
53026
  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(
53027
+ 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
53028
  async (tidRaw, opts) => {
52782
53029
  const templateId = parsePositiveIntId(tidRaw, "template id");
52783
53030
  if ((opts.file === void 0 || opts.file === "") && (opts.order === void 0 || opts.order === "")) {
@@ -53674,16 +53921,16 @@ function renderProcessDetail(out, p) {
53674
53921
  }
53675
53922
  async function runStepGet(cfg) {
53676
53923
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53677
- const step = decodeShape(
53924
+ const step2 = decodeShape(
53678
53925
  await withWorkflowV2(
53679
53926
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53680
53927
  )
53681
53928
  );
53682
53929
  if (cfg.asJson) {
53683
- writeJSONIndented(cfg.out, step);
53930
+ writeJSONIndented(cfg.out, step2);
53684
53931
  return;
53685
53932
  }
53686
- renderStepDetail(cfg.out, step);
53933
+ renderStepDetail(cfg.out, step2);
53687
53934
  }
53688
53935
  function renderStepDetail(out, s) {
53689
53936
  const rows = [
@@ -53729,12 +53976,12 @@ function renderTaskTable(out, tasks, indent) {
53729
53976
  }
53730
53977
  async function runTaskList(cfg) {
53731
53978
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53732
- const step = decodeShape(
53979
+ const step2 = decodeShape(
53733
53980
  await withWorkflowV2(
53734
53981
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53735
53982
  )
53736
53983
  );
53737
- const tasks = allStepTasks2(step);
53984
+ const tasks = allStepTasks2(step2);
53738
53985
  if (cfg.asJson) {
53739
53986
  writeJSONIndented(cfg.out, tasks);
53740
53987
  return;
@@ -53750,12 +53997,12 @@ async function runTaskList(cfg) {
53750
53997
  }
53751
53998
  async function runTaskGet(cfg) {
53752
53999
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53753
- const step = decodeShape(
54000
+ const step2 = decodeShape(
53754
54001
  await withWorkflowV2(
53755
54002
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53756
54003
  )
53757
54004
  );
53758
- const tasks = allStepTasks2(step);
54005
+ const tasks = allStepTasks2(step2);
53759
54006
  const match = tasks.find((t) => t.id === cfg.taskId);
53760
54007
  if (match === void 0) {
53761
54008
  throw new Error(
@@ -54045,11 +54292,11 @@ async function collectActivationCascade(client, template, signal) {
54045
54292
  return lines;
54046
54293
  }
54047
54294
  const steps = process2.steps ?? [];
54048
- for (const step of steps) {
54049
- if (step.notify_automatically !== true) continue;
54050
- const tasks = allStepTasks2(step);
54295
+ for (const step2 of steps) {
54296
+ if (step2.notify_automatically !== true) continue;
54297
+ const tasks = allStepTasks2(step2);
54051
54298
  if (tasks.length === 0) continue;
54052
- lines.push(` step "${step.name}":`);
54299
+ lines.push(` step "${step2.name}":`);
54053
54300
  for (const t of tasks) {
54054
54301
  const assignee = userLabel2(t.assignee);
54055
54302
  const taskName = taskDisplayName2(t);
@@ -54543,9 +54790,9 @@ async function runTaskDelete(cfg) {
54543
54790
  };
54544
54791
  await preflightConfirmToken(action, cfg.token);
54545
54792
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
54546
- let step;
54793
+ let step2;
54547
54794
  try {
54548
- step = decodeShape(
54795
+ step2 = decodeShape(
54549
54796
  await withWorkflowV2(
54550
54797
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
54551
54798
  )
@@ -54561,11 +54808,11 @@ async function runTaskDelete(cfg) {
54561
54808
  describe: async () => {
54562
54809
  const lines = [
54563
54810
  `process: ${cfg.processId}`,
54564
- `step: ${cfg.stepId}${step !== void 0 ? ` ("${step.name}")` : ""}`,
54811
+ `step: ${cfg.stepId}${step2 !== void 0 ? ` ("${step2.name}")` : ""}`,
54565
54812
  `tasks to delete: ${cfg.ids.length}`
54566
54813
  ];
54567
54814
  const tasksById = new Map(
54568
- (step === void 0 ? [] : allStepTasks2(step)).map((t) => [t.id, t])
54815
+ (step2 === void 0 ? [] : allStepTasks2(step2)).map((t) => [t.id, t])
54569
54816
  );
54570
54817
  for (const { id } of cfg.ids) {
54571
54818
  const found = tasksById.get(id);
@@ -55191,8 +55438,8 @@ function wireWorkflow(root) {
55191
55438
  });
55192
55439
  addServerFlag(processGetCmd);
55193
55440
  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(
55441
+ const step2 = workflow.command("step").description("Workflow steps");
55442
+ 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
55443
  async (processIdRaw, stepIdRaw, opts) => {
55197
55444
  const processId = parsePositiveIntId2(processIdRaw, "process id");
55198
55445
  const stepId = parsePositiveIntId2(stepIdRaw, "step id");
@@ -55495,7 +55742,7 @@ function wireWorkflow(root) {
55495
55742
  });
55496
55743
  addServerFlag(processDeleteCmd);
55497
55744
  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(
55745
+ 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
55746
  async (pidRaw, opts) => {
55500
55747
  const processId = parsePositiveIntId2(pidRaw, "process id");
55501
55748
  const payload = await readStepCreatePayload(opts.file ?? "");
@@ -55519,7 +55766,7 @@ function wireWorkflow(root) {
55519
55766
  );
55520
55767
  addServerFlag(stepCreateCmd);
55521
55768
  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(
55769
+ 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
55770
  async (pidRaw, sidRaw, opts) => {
55524
55771
  const processId = parsePositiveIntId2(pidRaw, "process id");
55525
55772
  const stepId = parsePositiveIntId2(sidRaw, "step id");
@@ -55545,7 +55792,7 @@ function wireWorkflow(root) {
55545
55792
  );
55546
55793
  addServerFlag(stepUpdateCmd);
55547
55794
  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(
55795
+ 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
55796
  async (pidRaw, sidRaw, opts) => {
55550
55797
  const processId = parsePositiveIntId2(pidRaw, "process id");
55551
55798
  const stepId = parsePositiveIntId2(sidRaw, "step id");
@@ -55567,7 +55814,7 @@ function wireWorkflow(root) {
55567
55814
  );
55568
55815
  addServerFlag(stepDeleteCmd);
55569
55816
  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(
55817
+ 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
55818
  async (tidRaw, opts) => {
55572
55819
  const templateId = parsePositiveIntId2(tidRaw, "template id");
55573
55820
  if ((opts.file === void 0 || opts.file === "") && (opts.order === void 0 || opts.order === "")) {
@@ -66253,15 +66500,15 @@ var MAX_ATTACHMENT_FILENAME_LENGTH = 500;
66253
66500
  function writeManualSteps(out, steps) {
66254
66501
  if (steps.length === 0) return;
66255
66502
  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}
66503
+ for (const step2 of steps) {
66504
+ const mark = step2.done ? "x" : " ";
66505
+ out.write(` [${mark}] ${step2.n}. (${step2.done_by}) ${step2.title}
66259
66506
  `);
66260
- if (step.detail !== void 0 && step.detail !== null && step.detail !== "") {
66261
- out.write(` ${step.detail}
66507
+ if (step2.detail !== void 0 && step2.detail !== null && step2.detail !== "") {
66508
+ out.write(` ${step2.detail}
66262
66509
  `);
66263
66510
  }
66264
- for (const rec of step.records ?? []) {
66511
+ for (const rec of step2.records ?? []) {
66265
66512
  const type = (rec.type ?? "cname").toUpperCase();
66266
66513
  out.write(` ${type} ${rec.host ?? "?"} -> ${rec.value ?? "?"}
66267
66514
  `);
@@ -66471,9 +66718,9 @@ function renderSmtpResponse(out, resp, asJson) {
66471
66718
  writeJSONIndented(out, resp);
66472
66719
  return;
66473
66720
  }
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}
66721
+ for (const step2 of resp.steps) {
66722
+ const detail = step2.detail === void 0 || step2.detail === null || step2.detail === "" ? "" : ` - ${step2.detail}`;
66723
+ out.write(`${step2.status.padEnd(8, " ")}${step2.step}${detail}
66477
66724
  `);
66478
66725
  }
66479
66726
  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.120",
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,6 +114,42 @@ 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 …
@@ -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.120",
4
4
  "description": "Datarails CLI",
5
5
  "type": "module",
6
6
  "engines": {