@datarails-ai/dr-cli 1.6.162 → 1.6.164
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/README.md +10 -10
- package/dist/_templateLibraryImageBytes-RHECBCOW.js +14 -0
- package/dist/cli.js +923 -171
- package/dist/scopes-map.json +69 -1
- package/dist/skills/dr-cli/SKILL.md +5 -0
- package/dist/skills/dr-cli/reference/template-library.md +81 -0
- package/package.json +2 -2
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.164",
|
|
12
12
|
description: "Datarails CLI",
|
|
13
13
|
type: "module",
|
|
14
14
|
engines: {
|
|
@@ -40,7 +40,7 @@ var package_default = {
|
|
|
40
40
|
postinstall: "node scripts/postinstall.mjs"
|
|
41
41
|
},
|
|
42
42
|
dependencies: {
|
|
43
|
-
"@datarails-ai/dr-appspace-cli": "0.12.
|
|
43
|
+
"@datarails-ai/dr-appspace-cli": "0.12.32",
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
45
45
|
"@napi-rs/keyring": "^1.3.0",
|
|
46
46
|
commander: "^12.1.0",
|
|
@@ -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-26T10:42:12.958Z";
|
|
73
73
|
function formatBuildTime(iso) {
|
|
74
74
|
if (iso === "") return "";
|
|
75
75
|
const d = new Date(iso);
|
|
@@ -2343,24 +2343,40 @@ var AuthorizedClient = class _AuthorizedClient {
|
|
|
2343
2343
|
* `whoami` precedent.
|
|
2344
2344
|
*/
|
|
2345
2345
|
getCurrentOrgId() {
|
|
2346
|
+
const raw2 = this.currentClaims()["organization_id"];
|
|
2347
|
+
if (typeof raw2 !== "number" || !Number.isInteger(raw2) || raw2 <= 0) {
|
|
2348
|
+
throw new Error(
|
|
2349
|
+
`JWT claim "organization_id" is missing or invalid for ${this._serverKey}; ${this.reAuthRemedy("claim")}`
|
|
2350
|
+
);
|
|
2351
|
+
}
|
|
2352
|
+
return raw2;
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* The caller's org domain from the access JWT's `organization_domain` claim — the value
|
|
2356
|
+
* the monolith stamps on rows it scopes by domain (e.g. a Template Library entry's
|
|
2357
|
+
* `domain`). Same failure modes and wording as {@link getCurrentOrgId}.
|
|
2358
|
+
*/
|
|
2359
|
+
getCurrentOrgDomain() {
|
|
2360
|
+
const raw2 = this.currentClaims()["organization_domain"];
|
|
2361
|
+
if (typeof raw2 !== "string" || raw2 === "") {
|
|
2362
|
+
throw new Error(
|
|
2363
|
+
`JWT claim "organization_domain" is missing or invalid for ${this._serverKey}; ${this.reAuthRemedy("claim")}`
|
|
2364
|
+
);
|
|
2365
|
+
}
|
|
2366
|
+
return raw2;
|
|
2367
|
+
}
|
|
2368
|
+
/** Decoded claims of the stored access JWT; throws with the re-auth remedy when there is none. */
|
|
2369
|
+
currentClaims() {
|
|
2346
2370
|
const access2 = this._session.jwtAccess ?? "";
|
|
2347
2371
|
if (access2 === "") {
|
|
2348
2372
|
throw new Error(`no JWT stored for ${this._serverKey}; ${this.reAuthRemedy("missing")}`);
|
|
2349
2373
|
}
|
|
2350
|
-
let claims;
|
|
2351
2374
|
try {
|
|
2352
|
-
|
|
2375
|
+
return decodeJwtClaims(access2);
|
|
2353
2376
|
} catch (err) {
|
|
2354
2377
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2355
2378
|
throw new Error(`decode JWT for ${this._serverKey}: ${msg}`);
|
|
2356
2379
|
}
|
|
2357
|
-
const raw2 = claims["organization_id"];
|
|
2358
|
-
if (typeof raw2 !== "number" || !Number.isInteger(raw2) || raw2 <= 0) {
|
|
2359
|
-
throw new Error(
|
|
2360
|
-
`JWT claim "organization_id" is missing or invalid for ${this._serverKey}; ${this.reAuthRemedy("claim")}`
|
|
2361
|
-
);
|
|
2362
|
-
}
|
|
2363
|
-
return raw2;
|
|
2364
2380
|
}
|
|
2365
2381
|
/**
|
|
2366
2382
|
* How this identity gets a usable JWT again. `dr login` is refused over an
|
|
@@ -2573,6 +2589,18 @@ var AuthorizedClient = class _AuthorizedClient {
|
|
|
2573
2589
|
* at `client.go:191`.
|
|
2574
2590
|
*/
|
|
2575
2591
|
async postMultipart(path13, fields, file, options) {
|
|
2592
|
+
return this.sendMultipart("POST", path13, fields, file, options);
|
|
2593
|
+
}
|
|
2594
|
+
/**
|
|
2595
|
+
* {@link AuthorizedClient.postMultipart} sent with PUT, for DRF update views that read a
|
|
2596
|
+
* multipart body (e.g. the Template Library image on `PUT /api/report_library/management/<id>`).
|
|
2597
|
+
* A separate method rather than a `method` option so the endpoint inventory
|
|
2598
|
+
* (`scripts/endpoints/extract.mts`) records the verb from the method name.
|
|
2599
|
+
*/
|
|
2600
|
+
async putMultipart(path13, fields, file, options) {
|
|
2601
|
+
return this.sendMultipart("PUT", path13, fields, file, options);
|
|
2602
|
+
}
|
|
2603
|
+
async sendMultipart(method, path13, fields, file, options) {
|
|
2576
2604
|
const fd = new FormData();
|
|
2577
2605
|
for (const [k, v] of Object.entries(fields)) {
|
|
2578
2606
|
fd.set(k, v);
|
|
@@ -2591,25 +2619,25 @@ var AuthorizedClient = class _AuthorizedClient {
|
|
|
2591
2619
|
this.applyAuthHeaders(headers, true, true);
|
|
2592
2620
|
headers.delete("Content-Type");
|
|
2593
2621
|
const res = await fetch(url, {
|
|
2594
|
-
method
|
|
2622
|
+
method,
|
|
2595
2623
|
headers,
|
|
2596
2624
|
body: fd,
|
|
2597
2625
|
signal: options?.signal
|
|
2598
2626
|
});
|
|
2599
2627
|
const text2 = await res.text();
|
|
2600
|
-
_AuthorizedClient.throwIfScopeDeniedBody(
|
|
2628
|
+
_AuthorizedClient.throwIfScopeDeniedBody(method, path13, res.status, text2);
|
|
2601
2629
|
if (res.status === 401 || res.status === 403) {
|
|
2602
|
-
throw new SessionExpiredError(
|
|
2630
|
+
throw new SessionExpiredError(`${method} ${path13} (HTTP ${res.status})`);
|
|
2603
2631
|
}
|
|
2604
2632
|
if (res.status < 200 || res.status >= 300) {
|
|
2605
|
-
throw new Error(
|
|
2633
|
+
throw new Error(`${method} ${path13}: HTTP ${res.status}: ${truncate2(text2, 200)}`);
|
|
2606
2634
|
}
|
|
2607
2635
|
if (text2 === "" || text2 === '""') return null;
|
|
2608
2636
|
try {
|
|
2609
2637
|
return JSON.parse(text2);
|
|
2610
2638
|
} catch (err) {
|
|
2611
2639
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2612
|
-
throw new Error(
|
|
2640
|
+
throw new Error(`${method} ${path13}: decode: ${msg}`);
|
|
2613
2641
|
}
|
|
2614
2642
|
}
|
|
2615
2643
|
/**
|
|
@@ -4170,7 +4198,15 @@ var PROD_GATE_TIER_B = /* @__PURE__ */ new Set([
|
|
|
4170
4198
|
// mark (the plan does not). If a seed ever lands, deletes or rewrites a version,
|
|
4171
4199
|
// these keys move back to Tier A.
|
|
4172
4200
|
"connector-poll|seed",
|
|
4173
|
-
"connector-poll|set-seed"
|
|
4201
|
+
"connector-poll|set-seed",
|
|
4202
|
+
// template library (dr template-library …): an unpublished entry's card, draft rows and
|
|
4203
|
+
// the unpublished-only archive change nothing a customer sees; publishing, and editing a
|
|
4204
|
+
// published entry's card (`update-published`), are Tier A.
|
|
4205
|
+
"template-library|add",
|
|
4206
|
+
"template-library|update",
|
|
4207
|
+
"template-library|unpublish",
|
|
4208
|
+
"template-library|archive",
|
|
4209
|
+
"template-library|restore"
|
|
4174
4210
|
]);
|
|
4175
4211
|
var PROD_GATE_TIER_A = /* @__PURE__ */ new Set([
|
|
4176
4212
|
// workflow / mec — soft-delete + cascade deletes. `archive` also covers the
|
|
@@ -4335,7 +4371,13 @@ var PROD_GATE_TIER_A = /* @__PURE__ */ new Set([
|
|
|
4335
4371
|
"managed-agent|set-global",
|
|
4336
4372
|
"managed-agent|set-visibility",
|
|
4337
4373
|
"managed-agent|archive",
|
|
4338
|
-
"agent-skill|delete"
|
|
4374
|
+
"agent-skill|delete",
|
|
4375
|
+
// template library: a published entry is offered to every customer org on the server.
|
|
4376
|
+
// Not `publish`: a Tier-A-only `publish` verb would make gen-scopes-map classify every
|
|
4377
|
+
// leaf named `publish` (app-space, agents skills) as destructive.
|
|
4378
|
+
"template-library|publish-to-customers",
|
|
4379
|
+
// ...and editing a published entry rewrites the card those orgs see.
|
|
4380
|
+
"template-library|update-published"
|
|
4339
4381
|
]);
|
|
4340
4382
|
function classifyTier(action) {
|
|
4341
4383
|
const key = `${action.resource}|${action.verb}`;
|
|
@@ -4582,8 +4624,8 @@ function parseScope(value) {
|
|
|
4582
4624
|
return m ? [m[1], m[2]] : null;
|
|
4583
4625
|
}
|
|
4584
4626
|
function parsed(scopes) {
|
|
4585
|
-
const
|
|
4586
|
-
const entries = typeof
|
|
4627
|
+
const list29 = scopes ?? [];
|
|
4628
|
+
const entries = typeof list29 === "string" ? splitScopeString(list29) : Array.isArray(list29) ? list29 : [];
|
|
4587
4629
|
return entries.map(parseScope).filter((p) => p !== null);
|
|
4588
4630
|
}
|
|
4589
4631
|
function scopeEntries(scopes) {
|
|
@@ -6840,8 +6882,8 @@ async function resolveOperator(store, serverKey, operatorFlag) {
|
|
|
6840
6882
|
}
|
|
6841
6883
|
if (operatorFlag !== void 0 && operatorFlag !== "") {
|
|
6842
6884
|
if (operators.includes(operatorFlag)) return operatorFlag;
|
|
6843
|
-
const
|
|
6844
|
-
throw new Error(`${operatorFlag} is not an operator login on ${serverKey}. Operators: ${
|
|
6885
|
+
const list29 = operators.length > 0 ? operators.join(", ") : "(none \u2014 run dr login)";
|
|
6886
|
+
throw new Error(`${operatorFlag} is not an operator login on ${serverKey}. Operators: ${list29}`);
|
|
6845
6887
|
}
|
|
6846
6888
|
const candidates = autoSelectable.length > 0 ? autoSelectable : operators;
|
|
6847
6889
|
if (candidates.length === 1) return candidates[0];
|
|
@@ -20061,8 +20103,8 @@ async function listDocumentTables(client, docId, reqOptions) {
|
|
|
20061
20103
|
`/api/documents/${docId}/bucket`,
|
|
20062
20104
|
reqOptions
|
|
20063
20105
|
);
|
|
20064
|
-
const
|
|
20065
|
-
return Array.isArray(
|
|
20106
|
+
const list29 = out?.templates;
|
|
20107
|
+
return Array.isArray(list29) ? list29 : [];
|
|
20066
20108
|
}
|
|
20067
20109
|
async function rescanBucketVersions(client, bucketId, body, reqOptions) {
|
|
20068
20110
|
await client.postJSON(`/api/buckets/${bucketId}/rescan_versions`, body, reqOptions);
|
|
@@ -20570,10 +20612,10 @@ async function maybeWarnLutKeyOverride(cfg, sentBody, created) {
|
|
|
20570
20612
|
}
|
|
20571
20613
|
let stored;
|
|
20572
20614
|
try {
|
|
20573
|
-
const
|
|
20615
|
+
const list29 = await listForDocument(cfg.client, sentDocument, {
|
|
20574
20616
|
signal: cfg.signal
|
|
20575
20617
|
});
|
|
20576
|
-
stored =
|
|
20618
|
+
stored = list29.find((row) => row.id === created.id);
|
|
20577
20619
|
} catch {
|
|
20578
20620
|
stored = created;
|
|
20579
20621
|
}
|
|
@@ -20715,8 +20757,8 @@ async function runLutExport(cfg) {
|
|
|
20715
20757
|
const buf = await getLutFile(cfg.client, cfg.documentId, {
|
|
20716
20758
|
signal: cfg.signal
|
|
20717
20759
|
});
|
|
20718
|
-
const { promises:
|
|
20719
|
-
await
|
|
20760
|
+
const { promises: fs38 } = await import("fs");
|
|
20761
|
+
await fs38.writeFile(target, buf);
|
|
20720
20762
|
cfg.out.write(
|
|
20721
20763
|
`Wrote ${buf.length} bytes to ${target} (LUT export for document ${cfg.documentId}).
|
|
20722
20764
|
`
|
|
@@ -21687,9 +21729,9 @@ function findPeriodLabelRefs(formula) {
|
|
|
21687
21729
|
function assertNoPeriodLabelRefs(formula) {
|
|
21688
21730
|
const bad = findPeriodLabelRefs(formula);
|
|
21689
21731
|
if (bad.length === 0) return;
|
|
21690
|
-
const
|
|
21732
|
+
const list29 = bad.map((r) => `[${r}]`).join(", ");
|
|
21691
21733
|
throw new Error(
|
|
21692
|
-
`formula references period labels (${
|
|
21734
|
+
`formula references period labels (${list29}) \u2014 refs must be bracket-wrapped ROW names like [1000 Cash], not month columns. The server rejects period-label refs with "Ref in formula does not exist in the report anymore."`
|
|
21693
21735
|
);
|
|
21694
21736
|
}
|
|
21695
21737
|
function extractRowValidation(resp, rowName) {
|
|
@@ -25269,9 +25311,9 @@ function scoreMetricProfile(input) {
|
|
|
25269
25311
|
const metricsByTable = /* @__PURE__ */ new Map();
|
|
25270
25312
|
for (const m of input.existingMetrics) {
|
|
25271
25313
|
if (typeof m.table_id !== "number") continue;
|
|
25272
|
-
const
|
|
25273
|
-
if (
|
|
25274
|
-
else
|
|
25314
|
+
const list29 = metricsByTable.get(m.table_id);
|
|
25315
|
+
if (list29 === void 0) metricsByTable.set(m.table_id, [m]);
|
|
25316
|
+
else list29.push(m);
|
|
25275
25317
|
}
|
|
25276
25318
|
let fieldsExamined = 0;
|
|
25277
25319
|
let profileErrors = 0;
|
|
@@ -27145,8 +27187,8 @@ function parseDimensionMappings(raws) {
|
|
|
27145
27187
|
return out;
|
|
27146
27188
|
}
|
|
27147
27189
|
function wireMetricsCreate(metrics) {
|
|
27148
|
-
const
|
|
27149
|
-
const baseCmd =
|
|
27190
|
+
const create20 = metrics.command("create").description("Create a metric (BASE, CALC or DRIVER)");
|
|
27191
|
+
const baseCmd = create20.command("base").description("Create a BASE metric (table-derived)").requiredOption("--name <name>", "Metric name (required)").requiredOption("--template <id>", "Template ID (required)").requiredOption("--value-field <id>", "Value field ID (required)").requiredOption("--date-field <id>", "Date field ID (required)").option(
|
|
27150
27192
|
"--aggregator <kind>",
|
|
27151
27193
|
"Value-field aggregation: SUM, COUNT, CUMULATIVE_SUM, CUMULATIVE_COUNT (default: SUM). The CUMULATIVE_* variants accumulate across periods (running total); they default time-aggregation to EOP and cannot use --time-aggregation SUM.",
|
|
27152
27194
|
"SUM"
|
|
@@ -27200,7 +27242,7 @@ function wireMetricsCreate(metrics) {
|
|
|
27200
27242
|
);
|
|
27201
27243
|
addServerFlag(baseCmd);
|
|
27202
27244
|
addAccountFlag(baseCmd);
|
|
27203
|
-
const calcCmd =
|
|
27245
|
+
const calcCmd = create20.command("calc").description("Create a CALC metric (formula-based)").requiredOption("--name <name>", "Metric name (required)").requiredOption(
|
|
27204
27246
|
"--formula <s>",
|
|
27205
27247
|
`Presented formula. Each referenced metric name MUST be wrapped in square brackets, e.g. "[Revenue]-[COGS]" or "[Revenue] / [Headcount]". Bare names like "revenue/headcount" are rejected (the server's flattener silently produces a broken metric). Reserved tokens are the exception \u2014 write them bare: DAYS_IN_PERIOD (days in the aggregation period, e.g. "[Revenue] / DAYS_IN_PERIOD") and PERIODS_IN_YEAR (annualization factor of the query window: 12 at month grain, 4 at quarter, 1 at year; such metrics reject day-grain queries). Time modifiers: [M].SHIFT(year|quarter|month|day|consumption_period, offset<=0) and [M].AS_OF(year_end|quarter_end|month_end); the consumption_period unit steps by the query grain instead of a fixed unit.`
|
|
27206
27248
|
).option("--description <s>", "Description").option("--category <id>", "Category ID").option(
|
|
@@ -27231,7 +27273,7 @@ function wireMetricsCreate(metrics) {
|
|
|
27231
27273
|
);
|
|
27232
27274
|
addServerFlag(calcCmd);
|
|
27233
27275
|
addAccountFlag(calcCmd);
|
|
27234
|
-
const driverCmd =
|
|
27276
|
+
const driverCmd = create20.command("driver").description(
|
|
27235
27277
|
"Create a DRIVER metric (entered values, no source table or formula). Set its definition and values per scenario with `dr metrics driver set` / `driver values set`."
|
|
27236
27278
|
).requiredOption("--name <name>", "Metric name (required)").option("--description <s>", "Description").option("--category <id>", "Category ID").option(
|
|
27237
27279
|
"--dimensions <ids>",
|
|
@@ -27962,16 +28004,16 @@ function parseDriverValuesJson(text2, source = "--file") {
|
|
|
27962
28004
|
const m = err instanceof Error ? err.message : String(err);
|
|
27963
28005
|
throw new Error(`${source}: invalid JSON: ${m}`);
|
|
27964
28006
|
}
|
|
27965
|
-
let
|
|
28007
|
+
let list29 = doc;
|
|
27966
28008
|
let where = source;
|
|
27967
28009
|
if (doc !== null && typeof doc === "object" && !Array.isArray(doc)) {
|
|
27968
|
-
|
|
28010
|
+
list29 = doc["values"];
|
|
27969
28011
|
where = `${source} values`;
|
|
27970
|
-
if (
|
|
28012
|
+
if (list29 === void 0) {
|
|
27971
28013
|
throw new Error(`${source}: expected {"values": [...]} or a bare array of cells`);
|
|
27972
28014
|
}
|
|
27973
28015
|
}
|
|
27974
|
-
const cells = parseDriverValueCells(
|
|
28016
|
+
const cells = parseDriverValueCells(list29, where);
|
|
27975
28017
|
assertDriverGrid(
|
|
27976
28018
|
cells,
|
|
27977
28019
|
cells.map((_, i) => `${where}[${i}]`),
|
|
@@ -35271,10 +35313,10 @@ async function runRecipesSave(cfg) {
|
|
|
35271
35313
|
);
|
|
35272
35314
|
}
|
|
35273
35315
|
async function readAnyJson(filePath) {
|
|
35274
|
-
const
|
|
35316
|
+
const fs38 = await import("fs");
|
|
35275
35317
|
let raw2;
|
|
35276
35318
|
try {
|
|
35277
|
-
raw2 = await
|
|
35319
|
+
raw2 = await fs38.promises.readFile(filePath, "utf8");
|
|
35278
35320
|
} catch (err) {
|
|
35279
35321
|
const m = err instanceof Error ? err.message : String(err);
|
|
35280
35322
|
throw new Error(`read --payload-file: ${m}`);
|
|
@@ -38179,7 +38221,7 @@ function mergeKindBodies(bodies) {
|
|
|
38179
38221
|
const merged = {};
|
|
38180
38222
|
for (const [key, value] of Object.entries(first)) {
|
|
38181
38223
|
const lists = [value, ...rest.map((body) => isObject(body) ? body[key] : void 0)];
|
|
38182
|
-
merged[key] = lists.every(Array.isArray) ? lists.flatMap((
|
|
38224
|
+
merged[key] = lists.every(Array.isArray) ? lists.flatMap((list29) => list29) : value;
|
|
38183
38225
|
}
|
|
38184
38226
|
return merged;
|
|
38185
38227
|
}
|
|
@@ -43895,9 +43937,9 @@ function eventsBySource(events) {
|
|
|
43895
43937
|
const m = /* @__PURE__ */ new Map();
|
|
43896
43938
|
for (const e of sorted) {
|
|
43897
43939
|
const k = sourceKey(e.server, e.data_integration_id);
|
|
43898
|
-
const
|
|
43899
|
-
if (
|
|
43900
|
-
else
|
|
43940
|
+
const list29 = m.get(k);
|
|
43941
|
+
if (list29 === void 0) m.set(k, [e]);
|
|
43942
|
+
else list29.push(e);
|
|
43901
43943
|
}
|
|
43902
43944
|
return m;
|
|
43903
43945
|
}
|
|
@@ -48043,9 +48085,9 @@ function registerConnectorsTasks(connectors) {
|
|
|
48043
48085
|
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
48044
48086
|
const orgId = opts.orgId !== void 0 ? parsePositiveIntArg2(opts.orgId, "--org-id") : void 0;
|
|
48045
48087
|
try {
|
|
48046
|
-
const
|
|
48047
|
-
if (opts.json === true) writeJSONIndented(process.stdout,
|
|
48048
|
-
else writeTaskList(process.stdout,
|
|
48088
|
+
const list29 = await listTasks(client, orgId, { signal });
|
|
48089
|
+
if (opts.json === true) writeJSONIndented(process.stdout, list29);
|
|
48090
|
+
else writeTaskList(process.stdout, list29);
|
|
48049
48091
|
} catch (err) {
|
|
48050
48092
|
throw explainTaskError(err);
|
|
48051
48093
|
}
|
|
@@ -48213,13 +48255,13 @@ function writeConnection(out, c) {
|
|
|
48213
48255
|
"\nThe customer role's permissions should be read-only on their bucket/prefix (plus kms:Decrypt for a customer-managed KMS key).\n"
|
|
48214
48256
|
);
|
|
48215
48257
|
}
|
|
48216
|
-
function writeConnectionTable(out,
|
|
48217
|
-
if (
|
|
48258
|
+
function writeConnectionTable(out, list29) {
|
|
48259
|
+
if (list29.length === 0) {
|
|
48218
48260
|
out.write("No AWS connections for this org.\n");
|
|
48219
48261
|
return;
|
|
48220
48262
|
}
|
|
48221
48263
|
const rows = [["ID", "SECRET_REF", "ROLE_ARN", "STATUS", "EXPIRES_AT", "LAST_ERROR"]];
|
|
48222
|
-
for (const c of
|
|
48264
|
+
for (const c of list29) {
|
|
48223
48265
|
rows.push([
|
|
48224
48266
|
String(c.id),
|
|
48225
48267
|
oneLine4(c.secret_ref),
|
|
@@ -53117,9 +53159,9 @@ verify the date matches the accounts' reporting period (try \`dr reconciliation
|
|
|
53117
53159
|
unchanged.push(r);
|
|
53118
53160
|
continue;
|
|
53119
53161
|
}
|
|
53120
|
-
const
|
|
53121
|
-
|
|
53122
|
-
changesByAccount.set(r.row.accountId,
|
|
53162
|
+
const list29 = changesByAccount.get(r.row.accountId) ?? [];
|
|
53163
|
+
list29.push(r);
|
|
53164
|
+
changesByAccount.set(r.row.accountId, list29);
|
|
53123
53165
|
}
|
|
53124
53166
|
const changeCount = resolved.length - unchanged.length;
|
|
53125
53167
|
const toJsonRow = (r) => ({
|
|
@@ -53246,9 +53288,9 @@ verify the date matches the accounts' reporting period (try \`dr reconciliation
|
|
|
53246
53288
|
unchanged.push(row);
|
|
53247
53289
|
continue;
|
|
53248
53290
|
}
|
|
53249
|
-
const
|
|
53250
|
-
|
|
53251
|
-
clearsByAccount.set(row.accountId,
|
|
53291
|
+
const list29 = clearsByAccount.get(row.accountId) ?? [];
|
|
53292
|
+
list29.push({ row, current });
|
|
53293
|
+
clearsByAccount.set(row.accountId, list29);
|
|
53252
53294
|
}
|
|
53253
53295
|
const clearCount = cfg.rows.length - unchanged.length;
|
|
53254
53296
|
const n = cfg.rows.length;
|
|
@@ -55413,9 +55455,9 @@ function stepGridWarnings(steps) {
|
|
|
55413
55455
|
const n = columns.length;
|
|
55414
55456
|
const hidden = steps.filter((s) => s.index < 1 || s.index > n);
|
|
55415
55457
|
if (hidden.length === 0) return [];
|
|
55416
|
-
const
|
|
55458
|
+
const list29 = hidden.map((s) => `${s.id} "${s.name}" (index ${s.index})`).join(", ");
|
|
55417
55459
|
return [
|
|
55418
|
-
`${hidden.length} of ${steps.length} steps will render stacked at the canvas origin \u2014 step index values must be the columns 1..N with no gaps (expected 1..${n}, got ${columns.join(", ")}): ${
|
|
55460
|
+
`${hidden.length} of ${steps.length} steps will render stacked at the canvas origin \u2014 step index values must be the columns 1..N with no gaps (expected 1..${n}, got ${columns.join(", ")}): ${list29}`
|
|
55419
55461
|
];
|
|
55420
55462
|
}
|
|
55421
55463
|
async function stepGridPlanLines(client, processId, change, options) {
|
|
@@ -65895,12 +65937,12 @@ function scenarioFilter(year, scenario) {
|
|
|
65895
65937
|
}
|
|
65896
65938
|
async function findScenarioById(client, scenarioId, signal) {
|
|
65897
65939
|
for (const family of ["Forecast", "Budget"]) {
|
|
65898
|
-
const
|
|
65940
|
+
const list29 = await listScenarios(
|
|
65899
65941
|
client,
|
|
65900
65942
|
{ scenario: family, ids: [scenarioId] },
|
|
65901
65943
|
{ signal }
|
|
65902
65944
|
);
|
|
65903
|
-
const hit =
|
|
65945
|
+
const hit = list29.find((s) => s.id === scenarioId);
|
|
65904
65946
|
if (hit !== void 0) return { scenario: hit, family };
|
|
65905
65947
|
}
|
|
65906
65948
|
return void 0;
|
|
@@ -66256,8 +66298,8 @@ async function runCyclesSetDefault(cfg) {
|
|
|
66256
66298
|
});
|
|
66257
66299
|
}
|
|
66258
66300
|
async function describeScenario(client, scenarioId, signal) {
|
|
66259
|
-
const
|
|
66260
|
-
const found =
|
|
66301
|
+
const list29 = await listScenarios(client, { ids: [scenarioId] }, { signal });
|
|
66302
|
+
const found = list29.find((s) => s.id === scenarioId) ?? (await listScenarios(client, {}, { signal })).find((s) => s.id === scenarioId);
|
|
66261
66303
|
if (found === void 0) {
|
|
66262
66304
|
return [`id: ${scenarioId}`, "(scenario not found \u2014 may already be deleted)"];
|
|
66263
66305
|
}
|
|
@@ -67359,8 +67401,8 @@ function parseIdArg(raw2, label) {
|
|
|
67359
67401
|
return n;
|
|
67360
67402
|
}
|
|
67361
67403
|
async function resolveNameList(singular, filePath, flagSingular, flagBulk) {
|
|
67362
|
-
const
|
|
67363
|
-
const hasSingular =
|
|
67404
|
+
const list29 = singular ?? [];
|
|
67405
|
+
const hasSingular = list29.length > 0;
|
|
67364
67406
|
const hasFile = filePath !== void 0 && filePath !== "";
|
|
67365
67407
|
if (hasSingular && hasFile) {
|
|
67366
67408
|
throw new Error(`pass either ${flagSingular} (repeatable) or ${flagBulk} @<path>, not both`);
|
|
@@ -67372,7 +67414,7 @@ async function resolveNameList(singular, filePath, flagSingular, flagBulk) {
|
|
|
67372
67414
|
if (!hasSingular) {
|
|
67373
67415
|
throw new Error(`at least one ${flagSingular} (repeatable) or ${flagBulk} @<path> is required`);
|
|
67374
67416
|
}
|
|
67375
|
-
return
|
|
67417
|
+
return list29.join("\n");
|
|
67376
67418
|
}
|
|
67377
67419
|
async function resolveTagsConfig(client, opts, signal) {
|
|
67378
67420
|
const hasSubType = opts.dateSubType !== void 0 && opts.dateSubType !== "";
|
|
@@ -69514,7 +69556,7 @@ function registerSpendControlVendorsCommands(sc) {
|
|
|
69514
69556
|
});
|
|
69515
69557
|
addServerFlag(owners);
|
|
69516
69558
|
addAccountFlag(owners);
|
|
69517
|
-
const
|
|
69559
|
+
const create20 = vendors.command("create").description(
|
|
69518
69560
|
"Get or create a vendor by name (POST /vendors/). Re-running with an existing name is a no-op"
|
|
69519
69561
|
).argument("<name>", "Vendor name (2\u2013255 chars)").option("--confirm-token <token>", "Token from a prior plan run", "").option("--json", "Output raw JSON of the vendor", false).action(async (name, opts) => {
|
|
69520
69562
|
const serverKey = resolveServer(opts.server);
|
|
@@ -69534,8 +69576,8 @@ function registerSpendControlVendorsCommands(sc) {
|
|
|
69534
69576
|
setup.cleanup();
|
|
69535
69577
|
}
|
|
69536
69578
|
});
|
|
69537
|
-
addServerFlag(
|
|
69538
|
-
addAccountFlag(
|
|
69579
|
+
addServerFlag(create20);
|
|
69580
|
+
addAccountFlag(create20);
|
|
69539
69581
|
const visibility = vendors.command("set-visibility").description("Show or hide vendors in the App Store (PATCH /vendors/visibility, admin-only)").argument("[vendor_ids...]", "Vendor UUIDs, space- or comma-separated").requiredOption("--visible <bool>", "Target visibility (true|false)").option("--from <path>", "Read ids from a file, one per line").option("--confirm-token <token>", "Token from a prior plan run", "").option("--json", "Output raw JSON of the updated vendors", false).action(
|
|
69540
69582
|
async (vendorIds, opts) => {
|
|
69541
69583
|
const serverKey = resolveServer(opts.server);
|
|
@@ -69939,7 +69981,7 @@ ${items.length} change(s).
|
|
|
69939
69981
|
}
|
|
69940
69982
|
function registerSpendControlAlertsCommands(sc) {
|
|
69941
69983
|
const alerts = sc.command("alerts").description("Spend Control alerts and the org-wide renewal-alert settings");
|
|
69942
|
-
const
|
|
69984
|
+
const list29 = alerts.command("list").description("List alerts (GET /alerts)").option(
|
|
69943
69985
|
"--status <status>",
|
|
69944
69986
|
`Filter by status (${ALERT_STATUSES.join("|")}). Server defaults to active`
|
|
69945
69987
|
).option("--page <n>", "Page number (1-based)").option("--size <n>", `Page size (max ${MAX_PAGE_SIZE3})`).option("--all", "Walk every page", false).option("--json", "Output raw JSON instead of a table", false).action(async (opts) => {
|
|
@@ -69967,8 +70009,8 @@ function registerSpendControlAlertsCommands(sc) {
|
|
|
69967
70009
|
setup.cleanup();
|
|
69968
70010
|
}
|
|
69969
70011
|
});
|
|
69970
|
-
addServerFlag(
|
|
69971
|
-
addAccountFlag(
|
|
70012
|
+
addServerFlag(list29);
|
|
70013
|
+
addAccountFlag(list29);
|
|
69972
70014
|
const dismiss = alerts.command("dismiss").description("Dismiss alerts (POST /alerts/{id}/dismiss). One-way: no un-dismiss route exists").argument("[alert_ids...]", "Alert UUIDs, space- or comma-separated").option("--from <path>", "Read ids from a file, one per line").option("--confirm-token <token>", "Token from a prior plan run", "").action(async (alertIds, opts) => {
|
|
69973
70015
|
const serverKey = resolveServer(opts.server);
|
|
69974
70016
|
const ids = await collectResourceIds(alertIds, opts.from, {
|
|
@@ -71473,7 +71515,7 @@ function registerSpendControlCommand(root) {
|
|
|
71473
71515
|
);
|
|
71474
71516
|
addServerFlag(bulkCreate);
|
|
71475
71517
|
addAccountFlag(bulkCreate);
|
|
71476
|
-
const
|
|
71518
|
+
const update13 = contracts.command("update").description("Partially update a contract by id (PATCH /contracts/{id})").argument("<contract_id>", "Contract UUID").requiredOption("--from <path>", "Path to JSON body matching ContractPartialUpdateSchema").option("--json", "Output raw JSON of the updated contract", false).action(async (contractId, opts) => {
|
|
71477
71519
|
const setup = await setupCall(opts.server, opts.account);
|
|
71478
71520
|
try {
|
|
71479
71521
|
await runContractUpdate({
|
|
@@ -71488,8 +71530,8 @@ function registerSpendControlCommand(root) {
|
|
|
71488
71530
|
setup.cleanup();
|
|
71489
71531
|
}
|
|
71490
71532
|
});
|
|
71491
|
-
addServerFlag(
|
|
71492
|
-
addAccountFlag(
|
|
71533
|
+
addServerFlag(update13);
|
|
71534
|
+
addAccountFlag(update13);
|
|
71493
71535
|
for (const spec of [
|
|
71494
71536
|
{
|
|
71495
71537
|
name: "archive",
|
|
@@ -74244,6 +74286,781 @@ function registerPalettesCommand(root) {
|
|
|
74244
74286
|
addAccountFlag(createCmd);
|
|
74245
74287
|
}
|
|
74246
74288
|
|
|
74289
|
+
// src/commands/templateLibrary.ts
|
|
74290
|
+
import { createHash as createHash17 } from "crypto";
|
|
74291
|
+
import { promises as fs36 } from "fs";
|
|
74292
|
+
import { Option as Option4 } from "commander";
|
|
74293
|
+
|
|
74294
|
+
// src/api/templateLibrary.ts
|
|
74295
|
+
var BASE5 = "/api/report_library/management";
|
|
74296
|
+
async function list28(client, options) {
|
|
74297
|
+
return await client.getJSON(BASE5, options) ?? [];
|
|
74298
|
+
}
|
|
74299
|
+
async function create19(client, body, options) {
|
|
74300
|
+
const out = await client.postJSON(BASE5, body, options);
|
|
74301
|
+
if (out === null) {
|
|
74302
|
+
throw new Error(`unexpected empty response from POST ${BASE5}`);
|
|
74303
|
+
}
|
|
74304
|
+
return out;
|
|
74305
|
+
}
|
|
74306
|
+
async function update12(client, id, body, options) {
|
|
74307
|
+
const out = await client.putJSON(`${BASE5}/${id}`, body, options);
|
|
74308
|
+
if (out === null) {
|
|
74309
|
+
throw new Error(`unexpected empty response from PUT ${BASE5}/${id}`);
|
|
74310
|
+
}
|
|
74311
|
+
return out;
|
|
74312
|
+
}
|
|
74313
|
+
async function uploadImage(client, id, png, filename, options) {
|
|
74314
|
+
const out = await client.putMultipart(
|
|
74315
|
+
`${BASE5}/${id}`,
|
|
74316
|
+
{},
|
|
74317
|
+
new Blob([png], { type: "image/png" }),
|
|
74318
|
+
{ ...options, fileFieldName: "image", filename }
|
|
74319
|
+
);
|
|
74320
|
+
if (out === null) {
|
|
74321
|
+
throw new Error(`unexpected empty response from PUT ${BASE5}/${id} (image)`);
|
|
74322
|
+
}
|
|
74323
|
+
return out;
|
|
74324
|
+
}
|
|
74325
|
+
|
|
74326
|
+
// src/commands/_agentCli.ts
|
|
74327
|
+
import { access } from "fs/promises";
|
|
74328
|
+
var JSON_HELP = "Emit raw JSON to stdout instead of a pretty render.";
|
|
74329
|
+
var CONFIRM_HELP = "Token from a prior plan run \u2014 required to actually write";
|
|
74330
|
+
function withTarget(cmd) {
|
|
74331
|
+
addServerFlag(cmd);
|
|
74332
|
+
addAccountFlag(cmd);
|
|
74333
|
+
return cmd;
|
|
74334
|
+
}
|
|
74335
|
+
function std() {
|
|
74336
|
+
return { out: process.stdout, err: process.stderr };
|
|
74337
|
+
}
|
|
74338
|
+
function requireId(value, placeholder) {
|
|
74339
|
+
const id = (value ?? "").trim();
|
|
74340
|
+
if (id === "") {
|
|
74341
|
+
throw new Error(`missing required option: --id ${placeholder}`);
|
|
74342
|
+
}
|
|
74343
|
+
return id;
|
|
74344
|
+
}
|
|
74345
|
+
function requirePositiveInt(value, flag) {
|
|
74346
|
+
const n = /^\d+$/.test(value) ? Number(value) : NaN;
|
|
74347
|
+
if (!Number.isSafeInteger(n) || n <= 0) {
|
|
74348
|
+
throw new Error(`${flag} ${JSON.stringify(value)}: expected a positive integer`);
|
|
74349
|
+
}
|
|
74350
|
+
return n;
|
|
74351
|
+
}
|
|
74352
|
+
function requireBool(value, flag) {
|
|
74353
|
+
if (value === "true") return true;
|
|
74354
|
+
if (value === "false") return false;
|
|
74355
|
+
throw new Error(`${flag} ${JSON.stringify(value ?? "")}: expected true or false`);
|
|
74356
|
+
}
|
|
74357
|
+
function splitList(value) {
|
|
74358
|
+
return [
|
|
74359
|
+
...new Set(
|
|
74360
|
+
(value ?? "").split(",").map((part) => part.trim()).filter((part) => part !== "")
|
|
74361
|
+
)
|
|
74362
|
+
];
|
|
74363
|
+
}
|
|
74364
|
+
function isNoneSentinel(parts, flag, value, what) {
|
|
74365
|
+
if (!parts.includes("none")) return false;
|
|
74366
|
+
if (parts.length === 1) return true;
|
|
74367
|
+
throw new Error(`${flag} ${JSON.stringify(value)}: "none" cannot be combined with ${what}`);
|
|
74368
|
+
}
|
|
74369
|
+
function shellArg(value) {
|
|
74370
|
+
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : shellQuote(value);
|
|
74371
|
+
}
|
|
74372
|
+
function proseStream(cfg) {
|
|
74373
|
+
return cfg.asJson ? cfg.err : cfg.out;
|
|
74374
|
+
}
|
|
74375
|
+
async function assertNoOverwrite(paths, force) {
|
|
74376
|
+
if (force) return;
|
|
74377
|
+
const existing = [];
|
|
74378
|
+
for (const path13 of paths) {
|
|
74379
|
+
try {
|
|
74380
|
+
await access(path13);
|
|
74381
|
+
existing.push(path13);
|
|
74382
|
+
} catch {
|
|
74383
|
+
}
|
|
74384
|
+
}
|
|
74385
|
+
if (existing.length > 0) {
|
|
74386
|
+
throw new Error(
|
|
74387
|
+
`refusing to overwrite existing file(s): ${existing.join(", ")}. Pull into another --dir, or pass --force to replace them.`
|
|
74388
|
+
);
|
|
74389
|
+
}
|
|
74390
|
+
}
|
|
74391
|
+
|
|
74392
|
+
// src/commands/_templateLibraryImages.ts
|
|
74393
|
+
var TEMPLATE_LIBRARY_IMAGE_COLORS = ["pink", "purple", "yellow"];
|
|
74394
|
+
var TEMPLATE_LIBRARY_IMAGE_FILENAMES = {
|
|
74395
|
+
pink: "pink_480.png",
|
|
74396
|
+
purple: "purple_480.png",
|
|
74397
|
+
yellow: "yellow_480.png"
|
|
74398
|
+
};
|
|
74399
|
+
|
|
74400
|
+
// src/commands/templateLibrary.ts
|
|
74401
|
+
var KNOWN_REPORT_TYPES = [
|
|
74402
|
+
"Financial Planning",
|
|
74403
|
+
"Financial Reporting",
|
|
74404
|
+
"Headcount Planning",
|
|
74405
|
+
"Strategic Reporting"
|
|
74406
|
+
];
|
|
74407
|
+
var LIBRARY_SERVER = "US";
|
|
74408
|
+
var LIBRARY_ORG_DOMAIN = "template.library-new";
|
|
74409
|
+
var MAX_DESCRIPTION_FILE_BYTES = 64 * 1024;
|
|
74410
|
+
var MAX_SHORT_FIELD = 100;
|
|
74411
|
+
function assertLibraryOrg(client, after = false) {
|
|
74412
|
+
const usHost = resolve(LIBRARY_SERVER).host.replace(/\/+$/, "");
|
|
74413
|
+
const domain = client.getCurrentOrgDomain();
|
|
74414
|
+
if (client.host !== usHost || domain !== LIBRARY_ORG_DOMAIN) {
|
|
74415
|
+
const lead2 = after ? `the last template library write re-authenticated as another identity (${client.serverKey}, org ${domain}) while it was sent; check the entry with \`dr template-library list\`. ` : "";
|
|
74416
|
+
throw new Error(
|
|
74417
|
+
`${lead2}the template library is managed only from org ${LIBRARY_ORG_DOMAIN} on ${LIBRARY_SERVER} (this identity: ${client.serverKey}, org ${domain}). Run as that org, e.g. \`dr login-as support@${LIBRARY_ORG_DOMAIN} --server ${LIBRARY_SERVER}\`.`
|
|
74418
|
+
);
|
|
74419
|
+
}
|
|
74420
|
+
}
|
|
74421
|
+
async function libraryWrite(client, write2) {
|
|
74422
|
+
assertLibraryOrg(client);
|
|
74423
|
+
const out = await write2();
|
|
74424
|
+
assertLibraryOrg(client, true);
|
|
74425
|
+
return out;
|
|
74426
|
+
}
|
|
74427
|
+
function boundId(cfg, target, payload) {
|
|
74428
|
+
const canonical = JSON.stringify({
|
|
74429
|
+
server: cfg.serverFlag,
|
|
74430
|
+
account: cfg.client.email,
|
|
74431
|
+
image: cfg.image ?? null,
|
|
74432
|
+
payload: Object.fromEntries(Object.entries(payload).sort(([a], [b]) => a.localeCompare(b)))
|
|
74433
|
+
});
|
|
74434
|
+
return `${target}#${createHash17("sha256").update(canonical).digest("hex").slice(0, 12)}`;
|
|
74435
|
+
}
|
|
74436
|
+
async function assertOwnFilebox(client, fileboxId, signal) {
|
|
74437
|
+
const own2 = await list16(client, {}, { signal });
|
|
74438
|
+
if (!own2.some((f) => f.id === fileboxId)) {
|
|
74439
|
+
throw new Error(`filebox ${fileboxId} is not a filebox of org ${LIBRARY_ORG_DOMAIN}`);
|
|
74440
|
+
}
|
|
74441
|
+
}
|
|
74442
|
+
async function ownFileboxName(client, fileboxId, signal) {
|
|
74443
|
+
await assertOwnFilebox(client, fileboxId, signal);
|
|
74444
|
+
let latest;
|
|
74445
|
+
try {
|
|
74446
|
+
latest = (await latestVersionId(client, fileboxId, { signal })).id;
|
|
74447
|
+
} catch (err) {
|
|
74448
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
74449
|
+
if (/HTTP 40[34]/.test(m)) {
|
|
74450
|
+
throw new Error(`filebox ${fileboxId} is not a filebox of org ${LIBRARY_ORG_DOMAIN}`);
|
|
74451
|
+
}
|
|
74452
|
+
throw err;
|
|
74453
|
+
}
|
|
74454
|
+
if (latest === null) {
|
|
74455
|
+
throw new Error(`filebox ${fileboxId} has no file yet \u2014 upload the template file first`);
|
|
74456
|
+
}
|
|
74457
|
+
return decodeShape((await getVersion(client, latest, { signal })).name);
|
|
74458
|
+
}
|
|
74459
|
+
function entryStatus(e) {
|
|
74460
|
+
if (e.is_archive) return "archived";
|
|
74461
|
+
return e.is_published ? "published" : "draft";
|
|
74462
|
+
}
|
|
74463
|
+
function imageColorOf(url) {
|
|
74464
|
+
if (url === null || url === "") return "none";
|
|
74465
|
+
const file = (url.split("?")[0]?.split("/").pop() ?? "").toLowerCase();
|
|
74466
|
+
return TEMPLATE_LIBRARY_IMAGE_COLORS.find(
|
|
74467
|
+
(c) => file.endsWith(`_${TEMPLATE_LIBRARY_IMAGE_FILENAMES[c]}`)
|
|
74468
|
+
) ?? "custom";
|
|
74469
|
+
}
|
|
74470
|
+
function parseImageColor(raw2) {
|
|
74471
|
+
const v = raw2.trim().toLowerCase();
|
|
74472
|
+
if (TEMPLATE_LIBRARY_IMAGE_COLORS.includes(v)) {
|
|
74473
|
+
return v;
|
|
74474
|
+
}
|
|
74475
|
+
throw new Error(
|
|
74476
|
+
`invalid --image "${raw2}" (expected one of: ${TEMPLATE_LIBRARY_IMAGE_COLORS.join(", ")})`
|
|
74477
|
+
);
|
|
74478
|
+
}
|
|
74479
|
+
async function fetchEntry(client, id, signal) {
|
|
74480
|
+
const rows = await list28(client, { signal });
|
|
74481
|
+
const found = rows.find((r) => r.id === id);
|
|
74482
|
+
if (found === void 0) {
|
|
74483
|
+
throw new Error(`template library entry ${id} not found`);
|
|
74484
|
+
}
|
|
74485
|
+
return decodeShape(found);
|
|
74486
|
+
}
|
|
74487
|
+
async function fetchOwnEntry(client, id, signal) {
|
|
74488
|
+
const e = await fetchEntry(client, id, signal);
|
|
74489
|
+
if (e.domain !== LIBRARY_ORG_DOMAIN) {
|
|
74490
|
+
throw new Error(
|
|
74491
|
+
`template library entry ${id} belongs to org ${e.domain ?? "(none)"}, not ${LIBRARY_ORG_DOMAIN}; refusing to change it`
|
|
74492
|
+
);
|
|
74493
|
+
}
|
|
74494
|
+
return e;
|
|
74495
|
+
}
|
|
74496
|
+
async function assertUnchangedSincePlan(client, planned, signal) {
|
|
74497
|
+
const now = await fetchOwnEntry(client, planned.id, signal);
|
|
74498
|
+
if (entryStatus(now) !== entryStatus(planned)) {
|
|
74499
|
+
throw new Error(
|
|
74500
|
+
`template library entry ${planned.id} changed from ${entryStatus(planned)} to ${entryStatus(now)} since the plan; nothing was written \u2014 plan the command again`
|
|
74501
|
+
);
|
|
74502
|
+
}
|
|
74503
|
+
return now;
|
|
74504
|
+
}
|
|
74505
|
+
function withContext(err, prefix, suffix = "") {
|
|
74506
|
+
if (err instanceof Error) {
|
|
74507
|
+
err.message = `${prefix}${err.message}${suffix}`;
|
|
74508
|
+
throw err;
|
|
74509
|
+
}
|
|
74510
|
+
throw new Error(`${prefix}${String(err)}${suffix}`);
|
|
74511
|
+
}
|
|
74512
|
+
async function uploadLibraryImage(client, id, color, signal) {
|
|
74513
|
+
const { templateLibraryImageBytes } = await import("./_templateLibraryImageBytes-RHECBCOW.js");
|
|
74514
|
+
return libraryWrite(
|
|
74515
|
+
client,
|
|
74516
|
+
() => uploadImage(
|
|
74517
|
+
client,
|
|
74518
|
+
id,
|
|
74519
|
+
templateLibraryImageBytes(color),
|
|
74520
|
+
TEMPLATE_LIBRARY_IMAGE_FILENAMES[color],
|
|
74521
|
+
{ signal }
|
|
74522
|
+
)
|
|
74523
|
+
);
|
|
74524
|
+
}
|
|
74525
|
+
async function writeMetadata(client, id, body, signal) {
|
|
74526
|
+
if (Object.keys(body).length === 0) return void 0;
|
|
74527
|
+
return libraryWrite(client, () => update12(client, id, body, { signal }));
|
|
74528
|
+
}
|
|
74529
|
+
function show(v) {
|
|
74530
|
+
if (v === null || v === void 0 || v === "") return "(empty)";
|
|
74531
|
+
if (Array.isArray(v)) return v.length === 0 ? "(empty)" : v.join(", ");
|
|
74532
|
+
return String(v);
|
|
74533
|
+
}
|
|
74534
|
+
function diffLines(current, body) {
|
|
74535
|
+
return Object.entries(body).map(([key, next]) => {
|
|
74536
|
+
const prev = key === "creator_id" ? current.creator?.id : current[key];
|
|
74537
|
+
return `${key}: ${show(prev)} -> ${show(next)}`;
|
|
74538
|
+
});
|
|
74539
|
+
}
|
|
74540
|
+
function imagePlanLine(current, image) {
|
|
74541
|
+
return image === void 0 ? [] : [`image: ${imageColorOf(current.report_image)} -> ${image}`];
|
|
74542
|
+
}
|
|
74543
|
+
function headerLines(e) {
|
|
74544
|
+
return [
|
|
74545
|
+
`entry ${e.id}: ${e.name} (filebox ${e.document.id}, domain ${e.domain ?? "?"})`,
|
|
74546
|
+
`status: ${entryStatus(e)}`
|
|
74547
|
+
];
|
|
74548
|
+
}
|
|
74549
|
+
async function runTemplateLibraryList(cfg) {
|
|
74550
|
+
const rows = decodeShape(
|
|
74551
|
+
(await list28(cfg.client, { signal: cfg.signal })).filter(
|
|
74552
|
+
(r) => cfg.status === "all" || entryStatus(r) === cfg.status
|
|
74553
|
+
)
|
|
74554
|
+
);
|
|
74555
|
+
if (cfg.asJson) {
|
|
74556
|
+
writeJSONIndented(cfg.out, rows);
|
|
74557
|
+
return;
|
|
74558
|
+
}
|
|
74559
|
+
if (rows.length === 0) {
|
|
74560
|
+
cfg.out.write("(no template library entries)\n");
|
|
74561
|
+
return;
|
|
74562
|
+
}
|
|
74563
|
+
writeTable44(cfg.out, [
|
|
74564
|
+
["ID", "FILEBOX", "STATUS", "TYPE", "INDUSTRY", "IMAGE", "DOWNLOADS", "NAME"],
|
|
74565
|
+
...rows.map((r) => [
|
|
74566
|
+
String(r.id),
|
|
74567
|
+
String(r.document.id),
|
|
74568
|
+
entryStatus(r),
|
|
74569
|
+
r.report_type ?? "",
|
|
74570
|
+
r.industry ?? "",
|
|
74571
|
+
imageColorOf(r.report_image),
|
|
74572
|
+
String(r.downloads ?? 0),
|
|
74573
|
+
r.name
|
|
74574
|
+
])
|
|
74575
|
+
]);
|
|
74576
|
+
}
|
|
74577
|
+
async function runTemplateLibraryGet(cfg) {
|
|
74578
|
+
const e = await fetchEntry(cfg.client, cfg.id, cfg.signal);
|
|
74579
|
+
if (cfg.asJson) {
|
|
74580
|
+
writeJSONIndented(cfg.out, e);
|
|
74581
|
+
return;
|
|
74582
|
+
}
|
|
74583
|
+
const lines = [
|
|
74584
|
+
...headerLines(e),
|
|
74585
|
+
`type: ${show(e.report_type)}`,
|
|
74586
|
+
`industry: ${show(e.industry)}`,
|
|
74587
|
+
`tags: ${show(e.tags)}`,
|
|
74588
|
+
`image: ${imageColorOf(e.report_image)}`,
|
|
74589
|
+
`creator: ${e.creator === null ? "(none)" : `${e.creator.email} (user ${e.creator.id})`}`,
|
|
74590
|
+
`downloads: ${e.downloads ?? 0}`,
|
|
74591
|
+
`description: ${show(e.description)}`,
|
|
74592
|
+
"tables and fields in use:"
|
|
74593
|
+
];
|
|
74594
|
+
if (e.tables_and_fields_in_use.length === 0) {
|
|
74595
|
+
lines.push(" (none mapped yet)");
|
|
74596
|
+
}
|
|
74597
|
+
for (const t of e.tables_and_fields_in_use) {
|
|
74598
|
+
lines.push(` ${t.name} {${t.alias ?? ""}} (table ${t.id})`);
|
|
74599
|
+
for (const f of t.fields) {
|
|
74600
|
+
lines.push(` ${f.name} {${f.alias ?? ""}} ${f.type ?? ""}`.trimEnd());
|
|
74601
|
+
}
|
|
74602
|
+
}
|
|
74603
|
+
cfg.out.write(lines.join("\n") + "\n");
|
|
74604
|
+
}
|
|
74605
|
+
async function runTemplateLibraryAdd(cfg) {
|
|
74606
|
+
assertLibraryOrg(cfg.client);
|
|
74607
|
+
const action = {
|
|
74608
|
+
resource: "template-library",
|
|
74609
|
+
id: boundId(cfg, cfg.fileboxId, cfg.body),
|
|
74610
|
+
verb: "add"
|
|
74611
|
+
};
|
|
74612
|
+
await preflightConfirmToken(action, cfg.token);
|
|
74613
|
+
const fileName = await ownFileboxName(cfg.client, cfg.fileboxId, cfg.signal);
|
|
74614
|
+
const rows = await list28(cfg.client, { signal: cfg.signal });
|
|
74615
|
+
const existing = rows.find((r) => r.document.id === cfg.fileboxId);
|
|
74616
|
+
if (existing !== void 0) {
|
|
74617
|
+
throw new Error(
|
|
74618
|
+
`filebox ${cfg.fileboxId} is already template library entry ${existing.id} \u2014 use \`dr template-library update ${existing.id}\``
|
|
74619
|
+
);
|
|
74620
|
+
}
|
|
74621
|
+
await twoStepConfirm({
|
|
74622
|
+
out: cfg.out,
|
|
74623
|
+
action,
|
|
74624
|
+
token: cfg.token,
|
|
74625
|
+
server: cfg.serverFlag,
|
|
74626
|
+
confirmCmd: cfg.confirmCmd,
|
|
74627
|
+
describe: async () => [
|
|
74628
|
+
`add filebox ${cfg.fileboxId} (${fileName}) to the template library as a draft (not published)`,
|
|
74629
|
+
`domain: ${LIBRARY_ORG_DOMAIN}`,
|
|
74630
|
+
...Object.entries(cfg.body).map(([k, v]) => `${k}: ${show(v)}`),
|
|
74631
|
+
...cfg.image === void 0 ? [] : [`image: ${cfg.image}`]
|
|
74632
|
+
],
|
|
74633
|
+
execute: async () => {
|
|
74634
|
+
await assertOwnFilebox(cfg.client, cfg.fileboxId, cfg.signal);
|
|
74635
|
+
assertLibraryOrg(cfg.client);
|
|
74636
|
+
const created = await create19(
|
|
74637
|
+
cfg.client,
|
|
74638
|
+
{ document_id: cfg.fileboxId },
|
|
74639
|
+
{ signal: cfg.signal }
|
|
74640
|
+
);
|
|
74641
|
+
let result = created;
|
|
74642
|
+
try {
|
|
74643
|
+
assertLibraryOrg(cfg.client, true);
|
|
74644
|
+
result = await writeMetadata(cfg.client, created.id, cfg.body, cfg.signal) ?? result;
|
|
74645
|
+
if (cfg.image !== void 0) {
|
|
74646
|
+
result = await uploadLibraryImage(cfg.client, created.id, cfg.image, cfg.signal);
|
|
74647
|
+
}
|
|
74648
|
+
} catch (err) {
|
|
74649
|
+
withContext(
|
|
74650
|
+
err,
|
|
74651
|
+
`created template library entry ${created.id} (draft), but filling in its card failed: `,
|
|
74652
|
+
` \u2014 finish it with \`dr template-library update ${created.id}\``
|
|
74653
|
+
);
|
|
74654
|
+
}
|
|
74655
|
+
if (cfg.asJson) {
|
|
74656
|
+
writeJSONIndented(cfg.out, result);
|
|
74657
|
+
return;
|
|
74658
|
+
}
|
|
74659
|
+
cfg.out.write(
|
|
74660
|
+
`Added filebox ${cfg.fileboxId} as template library entry ${created.id} (draft). The server maps its tables and fields in the background; check with \`dr template-library get ${created.id}\`.
|
|
74661
|
+
`
|
|
74662
|
+
);
|
|
74663
|
+
}
|
|
74664
|
+
});
|
|
74665
|
+
}
|
|
74666
|
+
async function runTemplateLibraryUpdate(cfg) {
|
|
74667
|
+
if (Object.keys(cfg.body).length === 0 && cfg.image === void 0) {
|
|
74668
|
+
throw new Error(
|
|
74669
|
+
"nothing to update \u2014 pass at least one of --description, --description-file, --type, --industry, --tags, --creator, --image"
|
|
74670
|
+
);
|
|
74671
|
+
}
|
|
74672
|
+
assertLibraryOrg(cfg.client);
|
|
74673
|
+
const current = await fetchOwnEntry(cfg.client, cfg.id, cfg.signal);
|
|
74674
|
+
if (typeof cfg.body.creator_id === "number") {
|
|
74675
|
+
await assertOrgContact(cfg.client, cfg.body.creator_id, cfg.signal);
|
|
74676
|
+
}
|
|
74677
|
+
const action = updateAction(boundId(cfg, cfg.id, cfg.body), current.is_published);
|
|
74678
|
+
await preflightConfirmToken(action, cfg.token);
|
|
74679
|
+
await twoStepConfirm({
|
|
74680
|
+
out: cfg.out,
|
|
74681
|
+
action,
|
|
74682
|
+
token: cfg.token,
|
|
74683
|
+
server: cfg.serverFlag,
|
|
74684
|
+
confirmCmd: cfg.confirmCmd,
|
|
74685
|
+
describe: async () => [
|
|
74686
|
+
...headerLines(current),
|
|
74687
|
+
...diffLines(current, cfg.body),
|
|
74688
|
+
...imagePlanLine(current, cfg.image)
|
|
74689
|
+
],
|
|
74690
|
+
execute: async () => {
|
|
74691
|
+
await assertUnchangedSincePlan(cfg.client, current, cfg.signal);
|
|
74692
|
+
if (typeof cfg.body.creator_id === "number") {
|
|
74693
|
+
await assertOrgContact(cfg.client, cfg.body.creator_id, cfg.signal);
|
|
74694
|
+
}
|
|
74695
|
+
let result = await writeMetadata(cfg.client, cfg.id, cfg.body, cfg.signal);
|
|
74696
|
+
if (cfg.image !== void 0) {
|
|
74697
|
+
try {
|
|
74698
|
+
result = await uploadLibraryImage(cfg.client, cfg.id, cfg.image, cfg.signal);
|
|
74699
|
+
} catch (err) {
|
|
74700
|
+
if (result === void 0) throw err;
|
|
74701
|
+
withContext(
|
|
74702
|
+
err,
|
|
74703
|
+
`updated the card of template library entry ${cfg.id}, but replacing its image failed: `,
|
|
74704
|
+
` \u2014 re-run with --image alone to finish`
|
|
74705
|
+
);
|
|
74706
|
+
}
|
|
74707
|
+
}
|
|
74708
|
+
if (cfg.asJson) {
|
|
74709
|
+
writeJSONIndented(cfg.out, result ?? current);
|
|
74710
|
+
return;
|
|
74711
|
+
}
|
|
74712
|
+
cfg.out.write(`Updated template library entry ${cfg.id} (${current.name}).
|
|
74713
|
+
`);
|
|
74714
|
+
}
|
|
74715
|
+
});
|
|
74716
|
+
}
|
|
74717
|
+
async function assertOrgContact(client, userId, signal) {
|
|
74718
|
+
const contacts = await listContacts(client, { signal });
|
|
74719
|
+
if (!contacts.some((c) => c.id === userId)) {
|
|
74720
|
+
throw new Error(`--creator ${userId} is not a user of org ${LIBRARY_ORG_DOMAIN}`);
|
|
74721
|
+
}
|
|
74722
|
+
}
|
|
74723
|
+
function updateAction(id, published) {
|
|
74724
|
+
return published ? { resource: "template-library", id, verb: "update-published" } : { resource: "template-library", id, verb: "update" };
|
|
74725
|
+
}
|
|
74726
|
+
function stateAction(id, verb) {
|
|
74727
|
+
switch (verb) {
|
|
74728
|
+
case "publish":
|
|
74729
|
+
return { resource: "template-library", id, verb: "publish-to-customers" };
|
|
74730
|
+
case "unpublish":
|
|
74731
|
+
return { resource: "template-library", id, verb: "unpublish" };
|
|
74732
|
+
case "archive":
|
|
74733
|
+
return { resource: "template-library", id, verb: "archive" };
|
|
74734
|
+
case "restore":
|
|
74735
|
+
return { resource: "template-library", id, verb: "restore" };
|
|
74736
|
+
}
|
|
74737
|
+
}
|
|
74738
|
+
var STATE_BODY = {
|
|
74739
|
+
publish: { is_published: true },
|
|
74740
|
+
unpublish: { is_published: false },
|
|
74741
|
+
archive: { is_archive: true },
|
|
74742
|
+
restore: { is_archive: false }
|
|
74743
|
+
};
|
|
74744
|
+
function stateRefusal(e, verb, image) {
|
|
74745
|
+
switch (verb) {
|
|
74746
|
+
case "publish":
|
|
74747
|
+
if (e.is_published) return `entry ${e.id} is already published`;
|
|
74748
|
+
if (e.is_archive)
|
|
74749
|
+
return `entry ${e.id} is archived \u2014 run \`dr template-library restore ${e.id}\` first`;
|
|
74750
|
+
if (imageColorOf(e.report_image) === "none" && image === void 0) {
|
|
74751
|
+
return `entry ${e.id} has no card image \u2014 pass --image ${TEMPLATE_LIBRARY_IMAGE_COLORS.join("|")}`;
|
|
74752
|
+
}
|
|
74753
|
+
return void 0;
|
|
74754
|
+
case "unpublish":
|
|
74755
|
+
return e.is_published ? void 0 : `entry ${e.id} is not published`;
|
|
74756
|
+
case "archive":
|
|
74757
|
+
if (e.is_archive) return `entry ${e.id} is already archived`;
|
|
74758
|
+
return e.is_published ? `entry ${e.id} is published \u2014 run \`dr template-library unpublish ${e.id}\` first` : void 0;
|
|
74759
|
+
case "restore":
|
|
74760
|
+
return e.is_archive ? void 0 : `entry ${e.id} is not archived`;
|
|
74761
|
+
}
|
|
74762
|
+
}
|
|
74763
|
+
function publishWarnings(e) {
|
|
74764
|
+
const empty = ["description", "report_type", "industry"].filter(
|
|
74765
|
+
(k) => e[k] === null || e[k] === ""
|
|
74766
|
+
);
|
|
74767
|
+
if (!e.tags?.length) empty.push("tags");
|
|
74768
|
+
return empty.map((k) => `warning: ${k} is empty`);
|
|
74769
|
+
}
|
|
74770
|
+
async function runTemplateLibraryState(cfg) {
|
|
74771
|
+
assertLibraryOrg(cfg.client);
|
|
74772
|
+
const action = stateAction(boundId(cfg, cfg.id, {}), cfg.verb);
|
|
74773
|
+
await preflightConfirmToken(action, cfg.token);
|
|
74774
|
+
const current = await fetchOwnEntry(cfg.client, cfg.id, cfg.signal);
|
|
74775
|
+
const refusal = stateRefusal(current, cfg.verb, cfg.image);
|
|
74776
|
+
if (refusal !== void 0) {
|
|
74777
|
+
throw new Error(refusal);
|
|
74778
|
+
}
|
|
74779
|
+
const body = STATE_BODY[cfg.verb];
|
|
74780
|
+
await twoStepConfirm({
|
|
74781
|
+
out: cfg.out,
|
|
74782
|
+
action,
|
|
74783
|
+
token: cfg.token,
|
|
74784
|
+
server: cfg.serverFlag,
|
|
74785
|
+
confirmCmd: cfg.confirmCmd,
|
|
74786
|
+
describe: async () => [
|
|
74787
|
+
...headerLines(current),
|
|
74788
|
+
`${cfg.verb}: ${entryStatus(current)} -> ${entryStatus({ ...current, ...body })}`,
|
|
74789
|
+
...cfg.verb === "publish" ? [
|
|
74790
|
+
"a published entry is offered to every customer org on this server",
|
|
74791
|
+
...publishWarnings(current)
|
|
74792
|
+
] : [],
|
|
74793
|
+
...imagePlanLine(current, cfg.image)
|
|
74794
|
+
],
|
|
74795
|
+
execute: async () => {
|
|
74796
|
+
await assertUnchangedSincePlan(cfg.client, current, cfg.signal);
|
|
74797
|
+
if (cfg.image !== void 0) {
|
|
74798
|
+
await uploadLibraryImage(cfg.client, cfg.id, cfg.image, cfg.signal);
|
|
74799
|
+
}
|
|
74800
|
+
const result = await libraryWrite(
|
|
74801
|
+
cfg.client,
|
|
74802
|
+
() => update12(cfg.client, cfg.id, body, { signal: cfg.signal })
|
|
74803
|
+
);
|
|
74804
|
+
if (cfg.asJson) {
|
|
74805
|
+
writeJSONIndented(cfg.out, result);
|
|
74806
|
+
return;
|
|
74807
|
+
}
|
|
74808
|
+
cfg.out.write(
|
|
74809
|
+
`Template library entry ${cfg.id} (${current.name}) is now ${entryStatus(result)}.
|
|
74810
|
+
`
|
|
74811
|
+
);
|
|
74812
|
+
}
|
|
74813
|
+
});
|
|
74814
|
+
}
|
|
74815
|
+
function writeTable44(out, rows) {
|
|
74816
|
+
const widths = [];
|
|
74817
|
+
for (const r of rows) {
|
|
74818
|
+
r.forEach((cell, i) => {
|
|
74819
|
+
widths[i] = Math.max(widths[i] ?? 0, cell.length);
|
|
74820
|
+
});
|
|
74821
|
+
}
|
|
74822
|
+
for (const r of rows) {
|
|
74823
|
+
out.write(
|
|
74824
|
+
r.map((cell, i) => i === r.length - 1 ? cell : cell.padEnd((widths[i] ?? 0) + 2)).join("") + "\n"
|
|
74825
|
+
);
|
|
74826
|
+
}
|
|
74827
|
+
}
|
|
74828
|
+
async function metadataBody(opts) {
|
|
74829
|
+
if (opts.description !== void 0 && opts.descriptionFile !== void 0) {
|
|
74830
|
+
throw new Error("pass --description or --description-file, not both");
|
|
74831
|
+
}
|
|
74832
|
+
const body = {};
|
|
74833
|
+
if (opts.description !== void 0) body.description = opts.description;
|
|
74834
|
+
if (opts.descriptionFile !== void 0) {
|
|
74835
|
+
let size;
|
|
74836
|
+
try {
|
|
74837
|
+
size = (await fs36.stat(opts.descriptionFile)).size;
|
|
74838
|
+
} catch (err) {
|
|
74839
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
74840
|
+
throw new Error(`--description-file ${opts.descriptionFile}: ${m}`);
|
|
74841
|
+
}
|
|
74842
|
+
if (size > MAX_DESCRIPTION_FILE_BYTES) {
|
|
74843
|
+
throw new Error(
|
|
74844
|
+
`--description-file ${opts.descriptionFile}: ${size} bytes exceeds the ${MAX_DESCRIPTION_FILE_BYTES}-byte limit`
|
|
74845
|
+
);
|
|
74846
|
+
}
|
|
74847
|
+
try {
|
|
74848
|
+
body.description = (await fs36.readFile(opts.descriptionFile, "utf8")).trim();
|
|
74849
|
+
} catch (err) {
|
|
74850
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
74851
|
+
throw new Error(`--description-file ${opts.descriptionFile}: ${m}`);
|
|
74852
|
+
}
|
|
74853
|
+
}
|
|
74854
|
+
if (opts.type !== void 0) body.report_type = shortField("--type", opts.type);
|
|
74855
|
+
if (opts.industry !== void 0) body.industry = shortField("--industry", opts.industry);
|
|
74856
|
+
if (opts.tags !== void 0) body.tags = splitList(opts.tags);
|
|
74857
|
+
return body;
|
|
74858
|
+
}
|
|
74859
|
+
function shortField(flag, raw2) {
|
|
74860
|
+
const v = raw2.trim();
|
|
74861
|
+
if (v.length > MAX_SHORT_FIELD) {
|
|
74862
|
+
throw new Error(
|
|
74863
|
+
`${flag} is ${v.length} characters; the server allows at most ${MAX_SHORT_FIELD}`
|
|
74864
|
+
);
|
|
74865
|
+
}
|
|
74866
|
+
return v;
|
|
74867
|
+
}
|
|
74868
|
+
function metadataConfirmParts(opts) {
|
|
74869
|
+
return [
|
|
74870
|
+
["--description", opts.description],
|
|
74871
|
+
["--description-file", opts.descriptionFile],
|
|
74872
|
+
["--type", opts.type],
|
|
74873
|
+
["--industry", opts.industry],
|
|
74874
|
+
["--tags", opts.tags],
|
|
74875
|
+
["--image", opts.image]
|
|
74876
|
+
];
|
|
74877
|
+
}
|
|
74878
|
+
function confirmCommand(parts) {
|
|
74879
|
+
const out = ["dr", "template-library"];
|
|
74880
|
+
for (const p of parts) {
|
|
74881
|
+
if (typeof p === "string") {
|
|
74882
|
+
out.push(p);
|
|
74883
|
+
} else if (p[1] !== void 0) {
|
|
74884
|
+
out.push(p[0], shellQuote(p[1]));
|
|
74885
|
+
}
|
|
74886
|
+
}
|
|
74887
|
+
return out.join(" ");
|
|
74888
|
+
}
|
|
74889
|
+
function addConfirmCommand(fileboxId, opts, serverFlag) {
|
|
74890
|
+
return confirmCommand([
|
|
74891
|
+
"add",
|
|
74892
|
+
String(fileboxId),
|
|
74893
|
+
...metadataConfirmParts(opts),
|
|
74894
|
+
["--server", serverFlag]
|
|
74895
|
+
]);
|
|
74896
|
+
}
|
|
74897
|
+
function updateConfirmCommand(id, opts, serverFlag) {
|
|
74898
|
+
return confirmCommand([
|
|
74899
|
+
"update",
|
|
74900
|
+
String(id),
|
|
74901
|
+
...metadataConfirmParts(opts),
|
|
74902
|
+
["--creator", opts.creator],
|
|
74903
|
+
["--server", serverFlag]
|
|
74904
|
+
]);
|
|
74905
|
+
}
|
|
74906
|
+
function stateConfirmCommand(verb, id, opts, serverFlag) {
|
|
74907
|
+
return confirmCommand([verb, String(id), ["--image", opts.image], ["--server", serverFlag]]);
|
|
74908
|
+
}
|
|
74909
|
+
function addImageOption(cmd, description) {
|
|
74910
|
+
return cmd.option(
|
|
74911
|
+
"--image <color>",
|
|
74912
|
+
`${description}: ${TEMPLATE_LIBRARY_IMAGE_COLORS.join(", ")}`,
|
|
74913
|
+
parseImageColor
|
|
74914
|
+
);
|
|
74915
|
+
}
|
|
74916
|
+
function addMetadataOptions(cmd) {
|
|
74917
|
+
cmd.option("--description <text>", "Card description shown in the library").option("--description-file <path>", "Read the description from a file instead").option(
|
|
74918
|
+
"--type <report_type>",
|
|
74919
|
+
`Report type, e.g. ${KNOWN_REPORT_TYPES.map((t) => `"${t}"`).join(", ")}`
|
|
74920
|
+
).option("--industry <text>", 'Industry the template targets ("General" for cross-industry)').option("--tags <csv>", 'Comma-separated tags; replaces the whole list ("" clears it)');
|
|
74921
|
+
return addImageOption(cmd, "Card image");
|
|
74922
|
+
}
|
|
74923
|
+
function addWriteFlags(cmd) {
|
|
74924
|
+
cmd.option("--json", "Output raw JSON instead of a summary", false).option("--confirm-token <token>", "Token from the plan-phase output; required to apply");
|
|
74925
|
+
addServerFlag(cmd);
|
|
74926
|
+
addAccountFlag(cmd);
|
|
74927
|
+
}
|
|
74928
|
+
function registerTemplateLibraryCommand(root) {
|
|
74929
|
+
const cmd = root.command("template-library").description(
|
|
74930
|
+
`Manage the Template Library catalog (api/report_library/management): list, add, fill in description/type/industry/tags/image, publish, archive. Writes run only as org ${LIBRARY_ORG_DOMAIN} on ${LIBRARY_SERVER}.`
|
|
74931
|
+
);
|
|
74932
|
+
const listCmd = cmd.command("list").description("List template library entries (published, draft and archived)").addOption(
|
|
74933
|
+
new Option4("--status <status>", "Which entries to show").choices(["all", "published", "draft", "archived"]).default("all")
|
|
74934
|
+
).option("--json", "Output raw JSON instead of a table", false).action(async (opts) => {
|
|
74935
|
+
const setup = await setupCall(opts.server, opts.account);
|
|
74936
|
+
try {
|
|
74937
|
+
await runTemplateLibraryList({
|
|
74938
|
+
client: setup.client,
|
|
74939
|
+
status: opts.status,
|
|
74940
|
+
asJson: opts.json === true,
|
|
74941
|
+
signal: setup.signal,
|
|
74942
|
+
out: process.stdout
|
|
74943
|
+
});
|
|
74944
|
+
} finally {
|
|
74945
|
+
setup.cleanup();
|
|
74946
|
+
}
|
|
74947
|
+
});
|
|
74948
|
+
addServerFlag(listCmd);
|
|
74949
|
+
addAccountFlag(listCmd);
|
|
74950
|
+
const getCmd = cmd.command("get").description("Show one entry, with the tables and fields (system names) its file reads").argument("<id>", "template library entry id").option("--json", "Output raw JSON instead of text", false).action(async (idRaw, opts) => {
|
|
74951
|
+
const id = parsePositiveIntArg(idRaw, "<id>");
|
|
74952
|
+
const setup = await setupCall(opts.server, opts.account);
|
|
74953
|
+
try {
|
|
74954
|
+
await runTemplateLibraryGet({
|
|
74955
|
+
client: setup.client,
|
|
74956
|
+
id,
|
|
74957
|
+
asJson: opts.json === true,
|
|
74958
|
+
signal: setup.signal,
|
|
74959
|
+
out: process.stdout
|
|
74960
|
+
});
|
|
74961
|
+
} finally {
|
|
74962
|
+
setup.cleanup();
|
|
74963
|
+
}
|
|
74964
|
+
});
|
|
74965
|
+
addServerFlag(getCmd);
|
|
74966
|
+
addAccountFlag(getCmd);
|
|
74967
|
+
const addCmd = addMetadataOptions(
|
|
74968
|
+
cmd.command("add").description(
|
|
74969
|
+
`Register a filebox of org ${LIBRARY_ORG_DOMAIN} on ${LIBRARY_SERVER} as a draft library entry (two-step confirm). Refused for any other server or org.`
|
|
74970
|
+
).argument("<filebox_id>", "filebox (document) id")
|
|
74971
|
+
).action(async (fileboxRaw, opts) => {
|
|
74972
|
+
const fileboxId = parsePositiveIntArg(fileboxRaw, "<filebox_id>");
|
|
74973
|
+
const body = await metadataBody(opts);
|
|
74974
|
+
const serverFlag = resolveServer(opts.server);
|
|
74975
|
+
const confirmCmd = addConfirmCommand(fileboxId, opts, serverFlag);
|
|
74976
|
+
const setup = await setupCall(opts.server, opts.account);
|
|
74977
|
+
try {
|
|
74978
|
+
await runTemplateLibraryAdd({
|
|
74979
|
+
client: setup.client,
|
|
74980
|
+
fileboxId,
|
|
74981
|
+
body,
|
|
74982
|
+
image: opts.image,
|
|
74983
|
+
token: opts.confirmToken,
|
|
74984
|
+
serverFlag,
|
|
74985
|
+
confirmCmd,
|
|
74986
|
+
asJson: opts.json === true,
|
|
74987
|
+
signal: setup.signal,
|
|
74988
|
+
out: process.stdout
|
|
74989
|
+
});
|
|
74990
|
+
} finally {
|
|
74991
|
+
setup.cleanup();
|
|
74992
|
+
}
|
|
74993
|
+
});
|
|
74994
|
+
addWriteFlags(addCmd);
|
|
74995
|
+
const updateCmd = addMetadataOptions(
|
|
74996
|
+
cmd.command("update").description(
|
|
74997
|
+
"Fill in or change an entry's card: description, type, industry, tags, image, creator (two-step confirm)"
|
|
74998
|
+
).argument("<id>", "template library entry id")
|
|
74999
|
+
).option("--creator <user_id>", "User id of the entry's creator (who gets the import emails)").action(async (idRaw, opts) => {
|
|
75000
|
+
const id = parsePositiveIntArg(idRaw, "<id>");
|
|
75001
|
+
const body = await metadataBody(opts);
|
|
75002
|
+
if (opts.creator !== void 0)
|
|
75003
|
+
body.creator_id = parsePositiveIntArg(opts.creator, "--creator");
|
|
75004
|
+
const serverFlag = resolveServer(opts.server);
|
|
75005
|
+
const confirmCmd = updateConfirmCommand(id, opts, serverFlag);
|
|
75006
|
+
const setup = await setupCall(opts.server, opts.account);
|
|
75007
|
+
try {
|
|
75008
|
+
await runTemplateLibraryUpdate({
|
|
75009
|
+
client: setup.client,
|
|
75010
|
+
id,
|
|
75011
|
+
body,
|
|
75012
|
+
image: opts.image,
|
|
75013
|
+
token: opts.confirmToken,
|
|
75014
|
+
serverFlag,
|
|
75015
|
+
confirmCmd,
|
|
75016
|
+
asJson: opts.json === true,
|
|
75017
|
+
signal: setup.signal,
|
|
75018
|
+
out: process.stdout
|
|
75019
|
+
});
|
|
75020
|
+
} finally {
|
|
75021
|
+
setup.cleanup();
|
|
75022
|
+
}
|
|
75023
|
+
});
|
|
75024
|
+
addWriteFlags(updateCmd);
|
|
75025
|
+
const stateHelp = {
|
|
75026
|
+
publish: "Publish an entry to every customer org on this server (needs a card image; two-step confirm with the production word gate)",
|
|
75027
|
+
unpublish: "Take an entry out of the customer-facing library (two-step confirm)",
|
|
75028
|
+
archive: "Archive an unpublished entry (two-step confirm)",
|
|
75029
|
+
restore: "Un-archive an entry back to draft (two-step confirm)"
|
|
75030
|
+
};
|
|
75031
|
+
for (const verb of ["publish", "unpublish", "archive", "restore"]) {
|
|
75032
|
+
const stateCmd = cmd.command(verb).description(stateHelp[verb]).argument("<id>", "template library entry id");
|
|
75033
|
+
if (verb === "publish") {
|
|
75034
|
+
addImageOption(stateCmd, "Set the card image first");
|
|
75035
|
+
}
|
|
75036
|
+
stateCmd.action(
|
|
75037
|
+
async (idRaw, opts) => {
|
|
75038
|
+
const id = parsePositiveIntArg(idRaw, "<id>");
|
|
75039
|
+
const serverFlag = resolveServer(opts.server);
|
|
75040
|
+
const confirmCmd = stateConfirmCommand(verb, id, opts, serverFlag);
|
|
75041
|
+
const setup = await setupCall(opts.server, opts.account);
|
|
75042
|
+
try {
|
|
75043
|
+
await runTemplateLibraryState({
|
|
75044
|
+
client: setup.client,
|
|
75045
|
+
id,
|
|
75046
|
+
verb,
|
|
75047
|
+
image: opts.image,
|
|
75048
|
+
token: opts.confirmToken,
|
|
75049
|
+
serverFlag,
|
|
75050
|
+
confirmCmd,
|
|
75051
|
+
asJson: opts.json === true,
|
|
75052
|
+
signal: setup.signal,
|
|
75053
|
+
out: process.stdout
|
|
75054
|
+
});
|
|
75055
|
+
} finally {
|
|
75056
|
+
setup.cleanup();
|
|
75057
|
+
}
|
|
75058
|
+
}
|
|
75059
|
+
);
|
|
75060
|
+
addWriteFlags(stateCmd);
|
|
75061
|
+
}
|
|
75062
|
+
}
|
|
75063
|
+
|
|
74247
75064
|
// src/api/businessCalendars.ts
|
|
74248
75065
|
function buildAuditQs(q) {
|
|
74249
75066
|
if (q === void 0) return "";
|
|
@@ -74316,7 +75133,7 @@ async function runCalendarsAuditList(cfg) {
|
|
|
74316
75133
|
}
|
|
74317
75134
|
renderAuditTable(cfg.out, resp);
|
|
74318
75135
|
}
|
|
74319
|
-
function
|
|
75136
|
+
function writeTable45(out, rows) {
|
|
74320
75137
|
if (rows.length === 0) return;
|
|
74321
75138
|
const widths = [];
|
|
74322
75139
|
for (const r of rows) {
|
|
@@ -74367,7 +75184,7 @@ function renderAuditTable(out, resp) {
|
|
|
74367
75184
|
]
|
|
74368
75185
|
});
|
|
74369
75186
|
}
|
|
74370
|
-
|
|
75187
|
+
writeTable45(out, rows);
|
|
74371
75188
|
if (resp.count > resp.results.length) {
|
|
74372
75189
|
out.write(`
|
|
74373
75190
|
(showing ${resp.results.length} of ${resp.count}; use --limit / --page)
|
|
@@ -74661,72 +75478,6 @@ function getSystemEntities(client, options) {
|
|
|
74661
75478
|
return read(client, "v1/ManagedAgents/getSystemEntities", {}, options);
|
|
74662
75479
|
}
|
|
74663
75480
|
|
|
74664
|
-
// src/commands/_agentCli.ts
|
|
74665
|
-
import { access } from "fs/promises";
|
|
74666
|
-
var JSON_HELP = "Emit raw JSON to stdout instead of a pretty render.";
|
|
74667
|
-
var CONFIRM_HELP = "Token from a prior plan run \u2014 required to actually write";
|
|
74668
|
-
function withTarget(cmd) {
|
|
74669
|
-
addServerFlag(cmd);
|
|
74670
|
-
addAccountFlag(cmd);
|
|
74671
|
-
return cmd;
|
|
74672
|
-
}
|
|
74673
|
-
function std() {
|
|
74674
|
-
return { out: process.stdout, err: process.stderr };
|
|
74675
|
-
}
|
|
74676
|
-
function requireId(value, placeholder) {
|
|
74677
|
-
const id = (value ?? "").trim();
|
|
74678
|
-
if (id === "") {
|
|
74679
|
-
throw new Error(`missing required option: --id ${placeholder}`);
|
|
74680
|
-
}
|
|
74681
|
-
return id;
|
|
74682
|
-
}
|
|
74683
|
-
function requirePositiveInt(value, flag) {
|
|
74684
|
-
const n = /^\d+$/.test(value) ? Number(value) : NaN;
|
|
74685
|
-
if (!Number.isSafeInteger(n) || n <= 0) {
|
|
74686
|
-
throw new Error(`${flag} ${JSON.stringify(value)}: expected a positive integer`);
|
|
74687
|
-
}
|
|
74688
|
-
return n;
|
|
74689
|
-
}
|
|
74690
|
-
function requireBool(value, flag) {
|
|
74691
|
-
if (value === "true") return true;
|
|
74692
|
-
if (value === "false") return false;
|
|
74693
|
-
throw new Error(`${flag} ${JSON.stringify(value ?? "")}: expected true or false`);
|
|
74694
|
-
}
|
|
74695
|
-
function splitList(value) {
|
|
74696
|
-
return [
|
|
74697
|
-
...new Set(
|
|
74698
|
-
(value ?? "").split(",").map((part) => part.trim()).filter((part) => part !== "")
|
|
74699
|
-
)
|
|
74700
|
-
];
|
|
74701
|
-
}
|
|
74702
|
-
function isNoneSentinel(parts, flag, value, what) {
|
|
74703
|
-
if (!parts.includes("none")) return false;
|
|
74704
|
-
if (parts.length === 1) return true;
|
|
74705
|
-
throw new Error(`${flag} ${JSON.stringify(value)}: "none" cannot be combined with ${what}`);
|
|
74706
|
-
}
|
|
74707
|
-
function shellArg(value) {
|
|
74708
|
-
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : shellQuote(value);
|
|
74709
|
-
}
|
|
74710
|
-
function proseStream(cfg) {
|
|
74711
|
-
return cfg.asJson ? cfg.err : cfg.out;
|
|
74712
|
-
}
|
|
74713
|
-
async function assertNoOverwrite(paths, force) {
|
|
74714
|
-
if (force) return;
|
|
74715
|
-
const existing = [];
|
|
74716
|
-
for (const path13 of paths) {
|
|
74717
|
-
try {
|
|
74718
|
-
await access(path13);
|
|
74719
|
-
existing.push(path13);
|
|
74720
|
-
} catch {
|
|
74721
|
-
}
|
|
74722
|
-
}
|
|
74723
|
-
if (existing.length > 0) {
|
|
74724
|
-
throw new Error(
|
|
74725
|
-
`refusing to overwrite existing file(s): ${existing.join(", ")}. Pull into another --dir, or pass --force to replace them.`
|
|
74726
|
-
);
|
|
74727
|
-
}
|
|
74728
|
-
}
|
|
74729
|
-
|
|
74730
75481
|
// src/commands/_agentCliScopes.ts
|
|
74731
75482
|
var DEFAULT_CLI_SCOPES = ["*:read"];
|
|
74732
75483
|
var CLI_SCOPE_ACTIONS = ["read", "write", "destructive", "*"];
|
|
@@ -75580,12 +76331,12 @@ async function runSkillsList(cfg) {
|
|
|
75580
76331
|
writeJSONIndented(cfg.out, res);
|
|
75581
76332
|
return;
|
|
75582
76333
|
}
|
|
75583
|
-
const
|
|
75584
|
-
if (
|
|
76334
|
+
const list29 = res.data ?? [];
|
|
76335
|
+
if (list29.length === 0) {
|
|
75585
76336
|
cfg.out.write("(no skills)\n");
|
|
75586
76337
|
return;
|
|
75587
76338
|
}
|
|
75588
|
-
for (const s of
|
|
76339
|
+
for (const s of list29) {
|
|
75589
76340
|
const title = s.display_title ?? s.name ?? "(untitled)";
|
|
75590
76341
|
const owner = s.state?.owner !== void 0 ? ` owner=${s.state.owner}` : "";
|
|
75591
76342
|
cfg.out.write(`${s.id ?? "?"} ${title}${owner}
|
|
@@ -75607,12 +76358,12 @@ async function runSkillsVersions(cfg) {
|
|
|
75607
76358
|
writeJSONIndented(cfg.out, res);
|
|
75608
76359
|
return;
|
|
75609
76360
|
}
|
|
75610
|
-
const
|
|
75611
|
-
if (
|
|
76361
|
+
const list29 = res.versions ?? [];
|
|
76362
|
+
if (list29.length === 0) {
|
|
75612
76363
|
cfg.out.write("(no versions)\n");
|
|
75613
76364
|
return;
|
|
75614
76365
|
}
|
|
75615
|
-
for (const v of
|
|
76366
|
+
for (const v of list29) {
|
|
75616
76367
|
const files = v.hasContent === true ? "[files]" : "[no files]";
|
|
75617
76368
|
cfg.out.write(`${v.version ?? "?"} ${v.createdAt ?? ""} ${files} ${v.name ?? ""}
|
|
75618
76369
|
`);
|
|
@@ -75730,7 +76481,7 @@ async function runSkillsDelete(cfg) {
|
|
|
75730
76481
|
}
|
|
75731
76482
|
function registerAgentSkillsCommand(agents) {
|
|
75732
76483
|
const skills = agents.command("skills").description("Inspect and edit Agent Studio skills (pull to disk, publish new versions).");
|
|
75733
|
-
const
|
|
76484
|
+
const list29 = skills.command("list").description("List skills, with owner where known.").option("--source <s>", "Filter by source: custom | anthropic.").option("--limit <n>", "Max skills to return (1-100).").option("--page <cursor>", "Page cursor from a previous list (its nextPage).").option("--json", JSON_HELP).action(async (opts) => {
|
|
75734
76485
|
const setup = await setupCall(opts.server, opts.account);
|
|
75735
76486
|
try {
|
|
75736
76487
|
await runSkillsList({
|
|
@@ -75746,7 +76497,7 @@ function registerAgentSkillsCommand(agents) {
|
|
|
75746
76497
|
setup.cleanup();
|
|
75747
76498
|
}
|
|
75748
76499
|
});
|
|
75749
|
-
withTarget(
|
|
76500
|
+
withTarget(list29);
|
|
75750
76501
|
const get16 = skills.command("get").description("Fetch a skill's Anthropic metadata as raw JSON (no file contents).").option("--id <skillId>", "Skill id.").action(async (opts) => {
|
|
75751
76502
|
const setup = await setupCall(opts.server, opts.account);
|
|
75752
76503
|
try {
|
|
@@ -75803,7 +76554,7 @@ function registerAgentSkillsCommand(agents) {
|
|
|
75803
76554
|
}
|
|
75804
76555
|
});
|
|
75805
76556
|
withTarget(pull);
|
|
75806
|
-
const
|
|
76557
|
+
const create20 = skills.command("create").description("Create a new skill from a directory of files (must include a SKILL.md).").option("--title <title>", "Display title (required).").option("--dir <path>", "Directory whose files form the skill (default: current directory).").option("--json", JSON_HELP).action(async (opts) => {
|
|
75807
76558
|
const setup = await setupCall(opts.server, opts.account);
|
|
75808
76559
|
try {
|
|
75809
76560
|
await runSkillsCreate({
|
|
@@ -75818,7 +76569,7 @@ function registerAgentSkillsCommand(agents) {
|
|
|
75818
76569
|
setup.cleanup();
|
|
75819
76570
|
}
|
|
75820
76571
|
});
|
|
75821
|
-
withTarget(
|
|
76572
|
+
withTarget(create20);
|
|
75822
76573
|
const publish = skills.command("publish").description("Publish a new version of an existing skill from a directory of files.").option("--id <skillId>", "Skill id.").option(
|
|
75823
76574
|
"--dir <path>",
|
|
75824
76575
|
"Directory whose files become the new version (default: current directory)."
|
|
@@ -75897,7 +76648,7 @@ async function runAgentsSystemEntities(cfg) {
|
|
|
75897
76648
|
writeJSONIndented(cfg.out, res);
|
|
75898
76649
|
return;
|
|
75899
76650
|
}
|
|
75900
|
-
const ids = (
|
|
76651
|
+
const ids = (list29) => (list29 ?? []).length > 0 ? (list29 ?? []).join(", ") : "(none)";
|
|
75901
76652
|
cfg.out.write(`agents: ${ids(res.agentIds)}
|
|
75902
76653
|
`);
|
|
75903
76654
|
const protectedIds = res.protectedAgentIds;
|
|
@@ -76003,12 +76754,12 @@ async function runAgentsList(cfg) {
|
|
|
76003
76754
|
writeJSONIndented(cfg.out, res);
|
|
76004
76755
|
return;
|
|
76005
76756
|
}
|
|
76006
|
-
const
|
|
76007
|
-
if (
|
|
76757
|
+
const list29 = res.data ?? [];
|
|
76758
|
+
if (list29.length === 0) {
|
|
76008
76759
|
cfg.out.write("(no agents)\n");
|
|
76009
76760
|
return;
|
|
76010
76761
|
}
|
|
76011
|
-
for (const a of
|
|
76762
|
+
for (const a of list29) {
|
|
76012
76763
|
const cells = [
|
|
76013
76764
|
a.id ?? "?",
|
|
76014
76765
|
versionLabel2(a.version),
|
|
@@ -76604,7 +77355,7 @@ function registerAgentsCommand(root) {
|
|
|
76604
77355
|
const agents = root.command("agents").description(
|
|
76605
77356
|
"Inspect and edit Agent Studio managed agents on dr-ai-service (writes mint a new version)."
|
|
76606
77357
|
);
|
|
76607
|
-
const
|
|
77358
|
+
const list29 = agents.command("list").description("List managed agents, newest first, with reach state and runtime exposure.").option("--limit <n>", "Max agents to return (1-100).").option("--page <cursor>", "Pagination cursor from a previous page.").option("--archived", "Include archived agents.").option("--json", JSON_HELP).action(async (opts) => {
|
|
76608
77359
|
const setup = await setupCall(opts.server, opts.account);
|
|
76609
77360
|
try {
|
|
76610
77361
|
await runAgentsList({
|
|
@@ -76620,7 +77371,7 @@ function registerAgentsCommand(root) {
|
|
|
76620
77371
|
setup.cleanup();
|
|
76621
77372
|
}
|
|
76622
77373
|
});
|
|
76623
|
-
withTarget(
|
|
77374
|
+
withTarget(list29);
|
|
76624
77375
|
const get16 = agents.command("get").description("Fetch one agent (config + studio state + derived exposure) as raw JSON.").option("--id <agentId>", "Agent id.").option("--version-number <n>", "Pin a specific version (default: latest).").action(async (opts) => {
|
|
76625
77376
|
const setup = await setupCall(opts.server, opts.account);
|
|
76626
77377
|
try {
|
|
@@ -76729,7 +77480,7 @@ function registerAgentsCommand(root) {
|
|
|
76729
77480
|
}
|
|
76730
77481
|
);
|
|
76731
77482
|
withTarget(pull);
|
|
76732
|
-
const
|
|
77483
|
+
const update13 = agents.command("update").description("Update an agent \u2192 a new version. Provide any of the fields below.").option("--id <agentId>", "Agent id.").option(
|
|
76733
77484
|
"--version-number <n>",
|
|
76734
77485
|
"Base version for optimistic concurrency (default: current latest)."
|
|
76735
77486
|
).option("--name <name>", "New name.").option("--model <model>", "New model id.").option("--system-file <path>", "File with the new system prompt.").option("--tools-file <path>", "JSON file with the new tools[] array.").option("--skills-file <path>", "JSON file with the new skills[] array.").option(
|
|
@@ -76782,8 +77533,8 @@ function registerAgentsCommand(root) {
|
|
|
76782
77533
|
}
|
|
76783
77534
|
}
|
|
76784
77535
|
);
|
|
76785
|
-
withTarget(
|
|
76786
|
-
const
|
|
77536
|
+
withTarget(update13);
|
|
77537
|
+
const create20 = agents.command("create").description("Create a new agent (its first version).").option("--name <name>", "Agent name (required).").option("--model <model>", "Model id (required).").option("--description <text>", "Description.").option(
|
|
76787
77538
|
"--origin <origin>",
|
|
76788
77539
|
"Who owns the definition: in-flow (default, the SDK upgrade pass keeps its tools current) | custom (never auto-rewritten)."
|
|
76789
77540
|
).option("--system-file <path>", "File with the system prompt.").option("--tools-file <path>", "JSON file with the tools[] array.").option("--skills-file <path>", "JSON file with the skills[] array.").option(
|
|
@@ -76824,7 +77575,7 @@ function registerAgentsCommand(root) {
|
|
|
76824
77575
|
}
|
|
76825
77576
|
}
|
|
76826
77577
|
);
|
|
76827
|
-
withTarget(
|
|
77578
|
+
withTarget(create20);
|
|
76828
77579
|
const mcpServers = agents.command("mcp-servers").description(
|
|
76829
77580
|
"List the MCP servers an agent may be given in this environment \u2014 the keys --mcp-servers accepts. Empty means none is configured here."
|
|
76830
77581
|
).option("--json", JSON_HELP).action(async (opts) => {
|
|
@@ -76957,7 +77708,7 @@ function registerAgentsCommand(root) {
|
|
|
76957
77708
|
}
|
|
76958
77709
|
|
|
76959
77710
|
// src/auth/versionCheck.ts
|
|
76960
|
-
import { promises as
|
|
77711
|
+
import { promises as fs37 } from "fs";
|
|
76961
77712
|
import * as os4 from "os";
|
|
76962
77713
|
import * as path12 from "path";
|
|
76963
77714
|
var LATEST_JSON_URL = "https://drcli.datarails.com/cli/latest.json";
|
|
@@ -76983,7 +77734,7 @@ function compareSemver(a, b) {
|
|
|
76983
77734
|
}
|
|
76984
77735
|
async function readCache() {
|
|
76985
77736
|
try {
|
|
76986
|
-
const raw2 = await
|
|
77737
|
+
const raw2 = await fs37.readFile(cachePath(), "utf8");
|
|
76987
77738
|
const parsed2 = JSON.parse(raw2);
|
|
76988
77739
|
if (typeof parsed2.checked_at !== "number" || typeof parsed2.latest_version !== "string" || parsed2.latest_version === "") {
|
|
76989
77740
|
return null;
|
|
@@ -76999,10 +77750,10 @@ async function readCache() {
|
|
|
76999
77750
|
async function writeCache(entry) {
|
|
77000
77751
|
const target = cachePath();
|
|
77001
77752
|
const dir = path12.dirname(target);
|
|
77002
|
-
await
|
|
77753
|
+
await fs37.mkdir(dir, { recursive: true });
|
|
77003
77754
|
const tmp = `${target}.tmp.${process.pid}`;
|
|
77004
|
-
await
|
|
77005
|
-
await
|
|
77755
|
+
await fs37.writeFile(tmp, JSON.stringify(entry), { mode: 384 });
|
|
77756
|
+
await fs37.rename(tmp, target);
|
|
77006
77757
|
}
|
|
77007
77758
|
async function fetchLatestVersion() {
|
|
77008
77759
|
if (LATEST_JSON_URL === "") return null;
|
|
@@ -77559,6 +78310,7 @@ function buildRootCommand() {
|
|
|
77559
78310
|
registerMultiProductCommand(root);
|
|
77560
78311
|
registerDeskCommand(root);
|
|
77561
78312
|
registerPalettesCommand(root);
|
|
78313
|
+
registerTemplateLibraryCommand(root);
|
|
77562
78314
|
registerCalendarsCommand(root);
|
|
77563
78315
|
registerAppSpaceCommand(root);
|
|
77564
78316
|
registerAgentsCommand(root);
|