@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 +366 -77
- package/dist/scopes-map.json +9 -1
- package/dist/skills/dr-cli/SKILL.md +5 -2
- package/dist/skills/dr-cli/reference/filebox-documents.md +36 -0
- package/dist/skills/dr-cli/reference/safety.md +5 -2
- package/dist/skills/dr-cli/reference/workflows.md +7 -0
- package/package.json +1 -1
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.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-
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
"
|
|
49696
|
-
"
|
|
49697
|
-
"
|
|
49698
|
-
"
|
|
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 =
|
|
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
|
|
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,
|
|
50997
|
+
writeJSONIndented(cfg.out, step2);
|
|
50709
50998
|
return;
|
|
50710
50999
|
}
|
|
50711
|
-
renderMecStepDetail(cfg.out,
|
|
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
|
|
51047
|
+
const step2 = await withMec(
|
|
50759
51048
|
() => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
|
|
50760
51049
|
);
|
|
50761
|
-
const tasks = allStepTasks(
|
|
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
|
|
51066
|
+
const step2 = await withMec(
|
|
50778
51067
|
() => getStep(cfg.client, cfg.processId, cfg.stepId, { signal: cfg.signal })
|
|
50779
51068
|
);
|
|
50780
|
-
const tasks = allStepTasks(
|
|
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
|
|
51156
|
-
if (
|
|
51157
|
-
const tasks = allStepTasks(
|
|
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 "${
|
|
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
|
|
51945
|
+
let step2;
|
|
51657
51946
|
try {
|
|
51658
|
-
|
|
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}${
|
|
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
|
-
(
|
|
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
|
|
52284
|
-
const stepGetCmd =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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,
|
|
53930
|
+
writeJSONIndented(cfg.out, step2);
|
|
53642
53931
|
return;
|
|
53643
53932
|
}
|
|
53644
|
-
renderStepDetail(cfg.out,
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
54007
|
-
if (
|
|
54008
|
-
const tasks = allStepTasks2(
|
|
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 "${
|
|
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
|
|
54793
|
+
let step2;
|
|
54505
54794
|
try {
|
|
54506
|
-
|
|
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}${
|
|
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
|
-
(
|
|
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
|
|
55153
|
-
const stepGetCmd =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
66215
|
-
const mark =
|
|
66216
|
-
out.write(` [${mark}] ${
|
|
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 (
|
|
66219
|
-
out.write(` ${
|
|
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
|
|
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
|
|
66433
|
-
const detail =
|
|
66434
|
-
out.write(`${
|
|
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}`;
|
package/dist/scopes-map.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"cliVersion": "1.6.
|
|
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`, `
|
|
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
|
|
@@ -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
|
|