@datarails-ai/dr-cli 1.6.118 → 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.118",
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:06:34.263Z";
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
@@ -49692,11 +49939,60 @@ var WORKFLOW_TYPES_NON_MEC = [
49692
49939
  ];
49693
49940
  var RECURRENCE_TYPES = [
49694
49941
  "One Time",
49695
- "Weekly",
49696
- "Monthly",
49697
- "Quarterly",
49698
- "Yearly"
49942
+ "Week",
49943
+ "Month",
49944
+ "Quarter",
49945
+ "Year"
49699
49946
  ];
49947
+ var RECURRENCE_ADJECTIVES = {
49948
+ Weekly: "Week",
49949
+ Monthly: "Month",
49950
+ Quarterly: "Quarter",
49951
+ Yearly: "Year"
49952
+ };
49953
+ function absentStartTimeLabel(recurrence) {
49954
+ return recurrence === "One Time" ? "(none; set when the workflow is activated)" : "(from the recurrence settings)";
49955
+ }
49956
+ function asRecurrencePeriodType(value, where) {
49957
+ const v = asString(value, where);
49958
+ if (RECURRENCE_TYPES.includes(v)) return v;
49959
+ const hint = RECURRENCE_ADJECTIVES[v];
49960
+ throw new Error(
49961
+ `--file ${where}: expected one of ${RECURRENCE_TYPES.join(" | ")}, got "${v}"` + (hint !== void 0 ? ` (use "${hint}")` : "")
49962
+ );
49963
+ }
49964
+ function checkTemplatePayload(out, requireAll) {
49965
+ if (out.process_start_time !== void 0 && out.config?.recurrency_period_type !== void 0 && out.config.recurrency_period_type !== "One Time") {
49966
+ throw new Error(
49967
+ `--file template.process_start_time: only allowed with config.recurrency_period_type "One Time" (got "${out.config.recurrency_period_type}"); drop it for a recurring workflow`
49968
+ );
49969
+ }
49970
+ if (!requireAll || out.config === void 0 || out.tags === void 0) return;
49971
+ if (out.tags.length === 0) {
49972
+ throw new Error(
49973
+ "--file template.tags: must not be empty; give the date tag of config.tags_config with its Unix-seconds value"
49974
+ );
49975
+ }
49976
+ const dateTags = out.config.tags_config.filter(
49977
+ (t) => t !== null && typeof t === "object" && t.type === "DATE"
49978
+ );
49979
+ if (dateTags.length !== 1) {
49980
+ throw new Error(
49981
+ `--file template.config.tags_config: needs exactly one entry with type "DATE" (found ${dateTags.length}); copy it from \`dr workflow get <id> --json\` of a working template (\`dr mec workflow get\` for MEC)`
49982
+ );
49983
+ }
49984
+ const dateTag = dateTags[0];
49985
+ if (typeof dateTag.id !== "number" || !Number.isInteger(dateTag.id) || dateTag.id <= 0) {
49986
+ throw new Error(
49987
+ `--file template.config.tags_config: the DATE entry needs a positive integer "id" (got ${JSON.stringify(dateTag.id)})`
49988
+ );
49989
+ }
49990
+ if (!out.tags.some((t) => t.id === dateTag.id)) {
49991
+ throw new Error(
49992
+ `--file template.tags: needs the date tag of config.tags_config (id ${String(dateTag.id)}, ${String(dateTag.name)}) with its Unix-seconds value`
49993
+ );
49994
+ }
49995
+ }
49700
49996
  var STEP_TYPES = [
49701
49997
  "Input",
49702
49998
  "Output",
@@ -49805,14 +50101,13 @@ function validateTemplatePayload(raw, requireAll) {
49805
50101
  );
49806
50102
  }
49807
50103
  out.process_start_time = v;
49808
- } else if (requireAll) {
49809
- throw new Error("--file template.process_start_time: required");
49810
50104
  }
49811
50105
  if (obj["tags"] !== void 0) {
49812
50106
  out.tags = validateTagsArray(obj["tags"]);
49813
50107
  } else if (requireAll) {
49814
50108
  throw new Error("--file template.tags: required");
49815
50109
  }
50110
+ checkTemplatePayload(out, requireAll);
49816
50111
  return out;
49817
50112
  }
49818
50113
  function validateConfig(raw) {
@@ -49834,15 +50129,10 @@ function validateConfig(raw) {
49834
50129
  `--file template.config.type: expected one of ${WORKFLOW_TYPES_NON_MEC.join(" | ")}, got "${type}"`
49835
50130
  );
49836
50131
  }
49837
- const recurrency_period_type = asString(
50132
+ const recurrency_period_type = asRecurrencePeriodType(
49838
50133
  obj["recurrency_period_type"],
49839
50134
  "template.config.recurrency_period_type"
49840
50135
  );
49841
- if (!RECURRENCE_TYPES.includes(recurrency_period_type)) {
49842
- throw new Error(
49843
- `--file template.config.recurrency_period_type: expected one of ${RECURRENCE_TYPES.join(" | ")}, got "${recurrency_period_type}"`
49844
- );
49845
- }
49846
50136
  const tagsConfig = asArray2(obj["tags_config"], "template.config.tags_config");
49847
50137
  return {
49848
50138
  ...obj,
@@ -50345,14 +50635,13 @@ function validateMecTemplatePayload(raw, requireAll) {
50345
50635
  );
50346
50636
  }
50347
50637
  out.process_start_time = v;
50348
- } else if (requireAll) {
50349
- throw new Error("--file template.process_start_time: required");
50350
50638
  }
50351
50639
  if (obj["tags"] !== void 0) {
50352
50640
  out.tags = validateTagsArray2(obj["tags"]);
50353
50641
  } else if (requireAll) {
50354
50642
  throw new Error("--file template.tags: required");
50355
50643
  }
50644
+ checkTemplatePayload(out, requireAll);
50356
50645
  return out;
50357
50646
  }
50358
50647
  function validateMecConfig(raw, requireAll) {
@@ -50392,7 +50681,7 @@ function validateMecConfig(raw, requireAll) {
50392
50681
  name,
50393
50682
  type: MEC_WORKFLOW_TYPE,
50394
50683
  process_timezone: tz,
50395
- recurrency_period_type: recur,
50684
+ recurrency_period_type: recur === void 0 ? void 0 : asRecurrencePeriodType(recur, "template.config.recurrency_period_type"),
50396
50685
  recurrency_start_day: obj["recurrency_start_day"] === null ? null : obj["recurrency_start_day"] === void 0 && !requireAll ? void 0 : asNonNegativeInt2(obj["recurrency_start_day"], "template.config.recurrency_start_day"),
50397
50686
  recurrency_start_hour: obj["recurrency_start_hour"] === void 0 && !requireAll ? void 0 : asNonNegativeInt2(obj["recurrency_start_hour"], "template.config.recurrency_start_hour"),
50398
50687
  recurrency_start_minute: obj["recurrency_start_minute"] === void 0 && !requireAll ? void 0 : asNonNegativeInt2(
@@ -50701,14 +50990,14 @@ function renderMecProcessDetail(out, p) {
50701
50990
  }
50702
50991
  async function runMecStepGet(cfg) {
50703
50992
  await requireMec(cfg.client, { signal: cfg.signal });
50704
- const step = await withMec(
50993
+ const step2 = await withMec(
50705
50994
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50706
50995
  );
50707
50996
  if (cfg.asJson) {
50708
- writeJSONIndented(cfg.out, step);
50997
+ writeJSONIndented(cfg.out, step2);
50709
50998
  return;
50710
50999
  }
50711
- renderMecStepDetail(cfg.out, step);
51000
+ renderMecStepDetail(cfg.out, step2);
50712
51001
  }
50713
51002
  function renderMecStepDetail(out, s) {
50714
51003
  const rows = [
@@ -50755,10 +51044,10 @@ function renderMecTaskTable(out, tasks, indent) {
50755
51044
  }
50756
51045
  async function runMecTaskList(cfg) {
50757
51046
  await requireMec(cfg.client, { signal: cfg.signal });
50758
- const step = await withMec(
51047
+ const step2 = await withMec(
50759
51048
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50760
51049
  );
50761
- const tasks = allStepTasks(step);
51050
+ const tasks = allStepTasks(step2);
50762
51051
  if (cfg.asJson) {
50763
51052
  writeJSONIndented(cfg.out, tasks);
50764
51053
  return;
@@ -50774,10 +51063,10 @@ async function runMecTaskList(cfg) {
50774
51063
  }
50775
51064
  async function runMecTaskGet(cfg) {
50776
51065
  await requireMec(cfg.client, { signal: cfg.signal });
50777
- const step = await withMec(
51066
+ const step2 = await withMec(
50778
51067
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
50779
51068
  );
50780
- const tasks = allStepTasks(step);
51069
+ const tasks = allStepTasks(step2);
50781
51070
  const match = tasks.find((t) => t.id === cfg.taskId);
50782
51071
  if (match === void 0) {
50783
51072
  throw new Error(
@@ -51017,7 +51306,7 @@ async function runMecWorkflowCreate(cfg) {
51017
51306
  `type: ${cfg.payload.config.type}`,
51018
51307
  `recurrence: ${cfg.payload.config.recurrency_period_type}`,
51019
51308
  `timezone: ${timezoneLabel(cfg.payload.config.process_timezone)}`,
51020
- `process_start_time: ${cfg.payload.process_start_time}`,
51309
+ `process_start_time: ${cfg.payload.process_start_time ?? absentStartTimeLabel(cfg.payload.config.recurrency_period_type)}`,
51021
51310
  `tags: ${cfg.payload.tags.length}`,
51022
51311
  "NOTE: created MEC template starts INACTIVE \u2014 activate separately."
51023
51312
  ],
@@ -51152,11 +51441,11 @@ async function collectMecActivationCascade(client, template, signal) {
51152
51441
  return lines;
51153
51442
  }
51154
51443
  const steps = process2.steps ?? [];
51155
- for (const step of steps) {
51156
- if (step.notify_automatically !== true) continue;
51157
- const tasks = allStepTasks(step);
51444
+ for (const step2 of steps) {
51445
+ if (step2.notify_automatically !== true) continue;
51446
+ const tasks = allStepTasks(step2);
51158
51447
  if (tasks.length === 0) continue;
51159
- lines.push(` step "${step.name}":`);
51448
+ lines.push(` step "${step2.name}":`);
51160
51449
  for (const t of tasks) {
51161
51450
  lines.push(` \u2192 ${userLabel(t.assignee)} (task: ${taskDisplayName(t)})`);
51162
51451
  }
@@ -51653,9 +51942,9 @@ async function runMecTaskDelete(cfg) {
51653
51942
  };
51654
51943
  await preflightConfirmToken(action, cfg.token);
51655
51944
  await requireMec(cfg.client, { signal: cfg.signal });
51656
- let step;
51945
+ let step2;
51657
51946
  try {
51658
- step = await withMec(
51947
+ step2 = await withMec(
51659
51948
  () => getStep(cfg.client, cfg.processId, cfg.stepId, {
51660
51949
  signal: cfg.signal
51661
51950
  })
@@ -51671,11 +51960,11 @@ async function runMecTaskDelete(cfg) {
51671
51960
  describe: async () => {
51672
51961
  const lines = [
51673
51962
  `process: ${cfg.processId}`,
51674
- `step: ${cfg.stepId}${step !== void 0 ? ` ("${step.name}")` : ""}`,
51963
+ `step: ${cfg.stepId}${step2 !== void 0 ? ` ("${step2.name}")` : ""}`,
51675
51964
  `tasks to delete: ${cfg.ids.length}`
51676
51965
  ];
51677
51966
  const tasksById = new Map(
51678
- (step === void 0 ? [] : allStepTasks(step)).map((t) => [t.id, t])
51967
+ (step2 === void 0 ? [] : allStepTasks(step2)).map((t) => [t.id, t])
51679
51968
  );
51680
51969
  for (const { id } of cfg.ids) {
51681
51970
  const found = tasksById.get(id);
@@ -52280,8 +52569,8 @@ function wireMecWorkflow(mecCmd) {
52280
52569
  });
52281
52570
  addServerFlag(procGetCmd);
52282
52571
  addAccountFlag(procGetCmd);
52283
- const step = workflow.command("step").description("MEC workflow steps");
52284
- 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(
52285
52574
  async (processIdRaw, stepIdRaw, opts) => {
52286
52575
  const processId = parsePositiveIntId(processIdRaw, "process id");
52287
52576
  const stepId = parsePositiveIntId(stepIdRaw, "step id");
@@ -52663,7 +52952,7 @@ function wireMecWorkflow(mecCmd) {
52663
52952
  });
52664
52953
  addServerFlag(procDeleteCmd);
52665
52954
  addAccountFlag(procDeleteCmd);
52666
- 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(
52667
52956
  async (pidRaw, opts) => {
52668
52957
  const processId = parsePositiveIntId(pidRaw, "process id");
52669
52958
  const payload = await readStepCreatePayload(opts.file ?? "");
@@ -52687,7 +52976,7 @@ function wireMecWorkflow(mecCmd) {
52687
52976
  );
52688
52977
  addServerFlag(stepCreateCmd);
52689
52978
  addAccountFlag(stepCreateCmd);
52690
- 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(
52691
52980
  async (pidRaw, sidRaw, opts) => {
52692
52981
  const processId = parsePositiveIntId(pidRaw, "process id");
52693
52982
  const stepId = parsePositiveIntId(sidRaw, "step id");
@@ -52713,7 +53002,7 @@ function wireMecWorkflow(mecCmd) {
52713
53002
  );
52714
53003
  addServerFlag(stepUpdateCmd);
52715
53004
  addAccountFlag(stepUpdateCmd);
52716
- 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(
52717
53006
  async (pidRaw, sidRaw, opts) => {
52718
53007
  const processId = parsePositiveIntId(pidRaw, "process id");
52719
53008
  const stepId = parsePositiveIntId(sidRaw, "step id");
@@ -52735,7 +53024,7 @@ function wireMecWorkflow(mecCmd) {
52735
53024
  );
52736
53025
  addServerFlag(stepDeleteCmd);
52737
53026
  addAccountFlag(stepDeleteCmd);
52738
- 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(
52739
53028
  async (tidRaw, opts) => {
52740
53029
  const templateId = parsePositiveIntId(tidRaw, "template id");
52741
53030
  if ((opts.file === void 0 || opts.file === "") && (opts.order === void 0 || opts.order === "")) {
@@ -53632,16 +53921,16 @@ function renderProcessDetail(out, p) {
53632
53921
  }
53633
53922
  async function runStepGet(cfg) {
53634
53923
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53635
- const step = decodeShape(
53924
+ const step2 = decodeShape(
53636
53925
  await withWorkflowV2(
53637
53926
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53638
53927
  )
53639
53928
  );
53640
53929
  if (cfg.asJson) {
53641
- writeJSONIndented(cfg.out, step);
53930
+ writeJSONIndented(cfg.out, step2);
53642
53931
  return;
53643
53932
  }
53644
- renderStepDetail(cfg.out, step);
53933
+ renderStepDetail(cfg.out, step2);
53645
53934
  }
53646
53935
  function renderStepDetail(out, s) {
53647
53936
  const rows = [
@@ -53687,12 +53976,12 @@ function renderTaskTable(out, tasks, indent) {
53687
53976
  }
53688
53977
  async function runTaskList(cfg) {
53689
53978
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53690
- const step = decodeShape(
53979
+ const step2 = decodeShape(
53691
53980
  await withWorkflowV2(
53692
53981
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53693
53982
  )
53694
53983
  );
53695
- const tasks = allStepTasks2(step);
53984
+ const tasks = allStepTasks2(step2);
53696
53985
  if (cfg.asJson) {
53697
53986
  writeJSONIndented(cfg.out, tasks);
53698
53987
  return;
@@ -53708,12 +53997,12 @@ async function runTaskList(cfg) {
53708
53997
  }
53709
53998
  async function runTaskGet(cfg) {
53710
53999
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
53711
- const step = decodeShape(
54000
+ const step2 = decodeShape(
53712
54001
  await withWorkflowV2(
53713
54002
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
53714
54003
  )
53715
54004
  );
53716
- const tasks = allStepTasks2(step);
54005
+ const tasks = allStepTasks2(step2);
53717
54006
  const match = tasks.find((t) => t.id === cfg.taskId);
53718
54007
  if (match === void 0) {
53719
54008
  throw new Error(
@@ -53870,7 +54159,7 @@ async function runWorkflowCreate(cfg) {
53870
54159
  `type: ${cfg.payload.config.type}`,
53871
54160
  `recurrence: ${cfg.payload.config.recurrency_period_type}`,
53872
54161
  `timezone: ${timezoneLabel2(cfg.payload.config.process_timezone)}`,
53873
- `process_start_time: ${cfg.payload.process_start_time}`,
54162
+ `process_start_time: ${cfg.payload.process_start_time ?? absentStartTimeLabel(cfg.payload.config.recurrency_period_type)}`,
53874
54163
  `tags: ${cfg.payload.tags.length}`,
53875
54164
  "NOTE: created template starts INACTIVE \u2014 activate separately."
53876
54165
  ],
@@ -54003,11 +54292,11 @@ async function collectActivationCascade(client, template, signal) {
54003
54292
  return lines;
54004
54293
  }
54005
54294
  const steps = process2.steps ?? [];
54006
- for (const step of steps) {
54007
- if (step.notify_automatically !== true) continue;
54008
- const tasks = allStepTasks2(step);
54295
+ for (const step2 of steps) {
54296
+ if (step2.notify_automatically !== true) continue;
54297
+ const tasks = allStepTasks2(step2);
54009
54298
  if (tasks.length === 0) continue;
54010
- lines.push(` step "${step.name}":`);
54299
+ lines.push(` step "${step2.name}":`);
54011
54300
  for (const t of tasks) {
54012
54301
  const assignee = userLabel2(t.assignee);
54013
54302
  const taskName = taskDisplayName2(t);
@@ -54501,9 +54790,9 @@ async function runTaskDelete(cfg) {
54501
54790
  };
54502
54791
  await preflightConfirmToken(action, cfg.token);
54503
54792
  await requireWorkflowV2(cfg.client, { signal: cfg.signal });
54504
- let step;
54793
+ let step2;
54505
54794
  try {
54506
- step = decodeShape(
54795
+ step2 = decodeShape(
54507
54796
  await withWorkflowV2(
54508
54797
  () => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
54509
54798
  )
@@ -54519,11 +54808,11 @@ async function runTaskDelete(cfg) {
54519
54808
  describe: async () => {
54520
54809
  const lines = [
54521
54810
  `process: ${cfg.processId}`,
54522
- `step: ${cfg.stepId}${step !== void 0 ? ` ("${step.name}")` : ""}`,
54811
+ `step: ${cfg.stepId}${step2 !== void 0 ? ` ("${step2.name}")` : ""}`,
54523
54812
  `tasks to delete: ${cfg.ids.length}`
54524
54813
  ];
54525
54814
  const tasksById = new Map(
54526
- (step === void 0 ? [] : allStepTasks2(step)).map((t) => [t.id, t])
54815
+ (step2 === void 0 ? [] : allStepTasks2(step2)).map((t) => [t.id, t])
54527
54816
  );
54528
54817
  for (const { id } of cfg.ids) {
54529
54818
  const found = tasksById.get(id);
@@ -55149,8 +55438,8 @@ function wireWorkflow(root) {
55149
55438
  });
55150
55439
  addServerFlag(processGetCmd);
55151
55440
  addAccountFlag(processGetCmd);
55152
- const step = workflow.command("step").description("Workflow steps");
55153
- 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(
55154
55443
  async (processIdRaw, stepIdRaw, opts) => {
55155
55444
  const processId = parsePositiveIntId2(processIdRaw, "process id");
55156
55445
  const stepId = parsePositiveIntId2(stepIdRaw, "step id");
@@ -55453,7 +55742,7 @@ function wireWorkflow(root) {
55453
55742
  });
55454
55743
  addServerFlag(processDeleteCmd);
55455
55744
  addAccountFlag(processDeleteCmd);
55456
- 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(
55457
55746
  async (pidRaw, opts) => {
55458
55747
  const processId = parsePositiveIntId2(pidRaw, "process id");
55459
55748
  const payload = await readStepCreatePayload(opts.file ?? "");
@@ -55477,7 +55766,7 @@ function wireWorkflow(root) {
55477
55766
  );
55478
55767
  addServerFlag(stepCreateCmd);
55479
55768
  addAccountFlag(stepCreateCmd);
55480
- 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(
55481
55770
  async (pidRaw, sidRaw, opts) => {
55482
55771
  const processId = parsePositiveIntId2(pidRaw, "process id");
55483
55772
  const stepId = parsePositiveIntId2(sidRaw, "step id");
@@ -55503,7 +55792,7 @@ function wireWorkflow(root) {
55503
55792
  );
55504
55793
  addServerFlag(stepUpdateCmd);
55505
55794
  addAccountFlag(stepUpdateCmd);
55506
- 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(
55507
55796
  async (pidRaw, sidRaw, opts) => {
55508
55797
  const processId = parsePositiveIntId2(pidRaw, "process id");
55509
55798
  const stepId = parsePositiveIntId2(sidRaw, "step id");
@@ -55525,7 +55814,7 @@ function wireWorkflow(root) {
55525
55814
  );
55526
55815
  addServerFlag(stepDeleteCmd);
55527
55816
  addAccountFlag(stepDeleteCmd);
55528
- 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(
55529
55818
  async (tidRaw, opts) => {
55530
55819
  const templateId = parsePositiveIntId2(tidRaw, "template id");
55531
55820
  if ((opts.file === void 0 || opts.file === "") && (opts.order === void 0 || opts.order === "")) {
@@ -66211,15 +66500,15 @@ var MAX_ATTACHMENT_FILENAME_LENGTH = 500;
66211
66500
  function writeManualSteps(out, steps) {
66212
66501
  if (steps.length === 0) return;
66213
66502
  out.write("\nManual steps:\n");
66214
- for (const step of steps) {
66215
- const mark = step.done ? "x" : " ";
66216
- 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}
66217
66506
  `);
66218
- if (step.detail !== void 0 && step.detail !== null && step.detail !== "") {
66219
- out.write(` ${step.detail}
66507
+ if (step2.detail !== void 0 && step2.detail !== null && step2.detail !== "") {
66508
+ out.write(` ${step2.detail}
66220
66509
  `);
66221
66510
  }
66222
- for (const rec of step.records ?? []) {
66511
+ for (const rec of step2.records ?? []) {
66223
66512
  const type = (rec.type ?? "cname").toUpperCase();
66224
66513
  out.write(` ${type} ${rec.host ?? "?"} -> ${rec.value ?? "?"}
66225
66514
  `);
@@ -66429,9 +66718,9 @@ function renderSmtpResponse(out, resp, asJson) {
66429
66718
  writeJSONIndented(out, resp);
66430
66719
  return;
66431
66720
  }
66432
- for (const step of resp.steps) {
66433
- const detail = step.detail === void 0 || step.detail === null || step.detail === "" ? "" : ` - ${step.detail}`;
66434
- 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}
66435
66724
  `);
66436
66725
  }
66437
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.118",
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
@@ -102,6 +102,13 @@ everything else is identical. Confirm tokens are scoped per family
102
102
  `recurrence_*` with a hint.
103
103
  - `process_start_time` and `tag.value` are 10-digit **Unix-seconds** epochs;
104
104
  small ints are rejected with a "did you pass day-of-month?" hint.
105
+ - `recurrency_period_type` is one of `One Time | Week | Month | Quarter | Year`.
106
+ `process_start_time` is only allowed with `One Time`; leave it out for a
107
+ recurring workflow.
108
+ - On `create`, `tags` dates the first process. `config.tags_config` needs
109
+ exactly one `type: "DATE"` entry, and `tags` needs a row with that entry's
110
+ `id` and a Unix-seconds `value`. Copy both from `workflow get <id> --json` of a
111
+ working template. The CLI rejects a payload that breaks this.
105
112
 
106
113
  ## Collection processes
107
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datarails-ai/dr-cli",
3
- "version": "1.6.118",
3
+ "version": "1.6.120",
4
4
  "description": "Datarails CLI",
5
5
  "type": "module",
6
6
  "engines": {