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