@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/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.162",
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.30",
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-25T12:27:12.533Z";
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
- claims = decodeJwtClaims(access2);
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: "POST",
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("POST", path13, res.status, text2);
2628
+ _AuthorizedClient.throwIfScopeDeniedBody(method, path13, res.status, text2);
2601
2629
  if (res.status === 401 || res.status === 403) {
2602
- throw new SessionExpiredError(`POST ${path13} (HTTP ${res.status})`);
2630
+ throw new SessionExpiredError(`${method} ${path13} (HTTP ${res.status})`);
2603
2631
  }
2604
2632
  if (res.status < 200 || res.status >= 300) {
2605
- throw new Error(`POST ${path13}: HTTP ${res.status}: ${truncate2(text2, 200)}`);
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(`POST ${path13}: decode: ${msg}`);
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 list28 = scopes ?? [];
4586
- const entries = typeof list28 === "string" ? splitScopeString(list28) : Array.isArray(list28) ? list28 : [];
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 list28 = operators.length > 0 ? operators.join(", ") : "(none \u2014 run dr login)";
6844
- throw new Error(`${operatorFlag} is not an operator login on ${serverKey}. Operators: ${list28}`);
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 list28 = out?.templates;
20065
- return Array.isArray(list28) ? list28 : [];
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 list28 = await listForDocument(cfg.client, sentDocument, {
20615
+ const list29 = await listForDocument(cfg.client, sentDocument, {
20574
20616
  signal: cfg.signal
20575
20617
  });
20576
- stored = list28.find((row) => row.id === created.id);
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: fs37 } = await import("fs");
20719
- await fs37.writeFile(target, buf);
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 list28 = bad.map((r) => `[${r}]`).join(", ");
21732
+ const list29 = bad.map((r) => `[${r}]`).join(", ");
21691
21733
  throw new Error(
21692
- `formula references period labels (${list28}) \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."`
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 list28 = metricsByTable.get(m.table_id);
25273
- if (list28 === void 0) metricsByTable.set(m.table_id, [m]);
25274
- else list28.push(m);
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 create19 = metrics.command("create").description("Create a metric (BASE, CALC or DRIVER)");
27149
- const baseCmd = create19.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(
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 = create19.command("calc").description("Create a CALC metric (formula-based)").requiredOption("--name <name>", "Metric name (required)").requiredOption(
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 = create19.command("driver").description(
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 list28 = doc;
28007
+ let list29 = doc;
27966
28008
  let where = source;
27967
28009
  if (doc !== null && typeof doc === "object" && !Array.isArray(doc)) {
27968
- list28 = doc["values"];
28010
+ list29 = doc["values"];
27969
28011
  where = `${source} values`;
27970
- if (list28 === void 0) {
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(list28, where);
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 fs37 = await import("fs");
35316
+ const fs38 = await import("fs");
35275
35317
  let raw2;
35276
35318
  try {
35277
- raw2 = await fs37.promises.readFile(filePath, "utf8");
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((list28) => list28) : value;
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 list28 = m.get(k);
43899
- if (list28 === void 0) m.set(k, [e]);
43900
- else list28.push(e);
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 list28 = await listTasks(client, orgId, { signal });
48047
- if (opts.json === true) writeJSONIndented(process.stdout, list28);
48048
- else writeTaskList(process.stdout, list28);
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, list28) {
48217
- if (list28.length === 0) {
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 list28) {
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 list28 = changesByAccount.get(r.row.accountId) ?? [];
53121
- list28.push(r);
53122
- changesByAccount.set(r.row.accountId, list28);
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 list28 = clearsByAccount.get(row.accountId) ?? [];
53250
- list28.push({ row, current });
53251
- clearsByAccount.set(row.accountId, list28);
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 list28 = hidden.map((s) => `${s.id} "${s.name}" (index ${s.index})`).join(", ");
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(", ")}): ${list28}`
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 list28 = await listScenarios(
65940
+ const list29 = await listScenarios(
65899
65941
  client,
65900
65942
  { scenario: family, ids: [scenarioId] },
65901
65943
  { signal }
65902
65944
  );
65903
- const hit = list28.find((s) => s.id === scenarioId);
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 list28 = await listScenarios(client, { ids: [scenarioId] }, { signal });
66260
- const found = list28.find((s) => s.id === scenarioId) ?? (await listScenarios(client, {}, { signal })).find((s) => s.id === scenarioId);
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 list28 = singular ?? [];
67363
- const hasSingular = list28.length > 0;
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 list28.join("\n");
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 create19 = vendors.command("create").description(
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(create19);
69538
- addAccountFlag(create19);
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 list28 = alerts.command("list").description("List alerts (GET /alerts)").option(
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(list28);
69971
- addAccountFlag(list28);
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 update12 = 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) => {
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(update12);
71492
- addAccountFlag(update12);
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 writeTable44(out, rows) {
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
- writeTable44(out, rows);
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 list28 = res.data ?? [];
75584
- if (list28.length === 0) {
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 list28) {
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 list28 = res.versions ?? [];
75611
- if (list28.length === 0) {
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 list28) {
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 list28 = 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) => {
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(list28);
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 create19 = 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) => {
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(create19);
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 = (list28) => (list28 ?? []).length > 0 ? (list28 ?? []).join(", ") : "(none)";
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 list28 = res.data ?? [];
76007
- if (list28.length === 0) {
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 list28) {
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 list28 = 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) => {
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(list28);
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 update12 = agents.command("update").description("Update an agent \u2192 a new version. Provide any of the fields below.").option("--id <agentId>", "Agent id.").option(
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(update12);
76786
- const create19 = 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(
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(create19);
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 fs36 } from "fs";
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 fs36.readFile(cachePath(), "utf8");
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 fs36.mkdir(dir, { recursive: true });
77753
+ await fs37.mkdir(dir, { recursive: true });
77003
77754
  const tmp = `${target}.tmp.${process.pid}`;
77004
- await fs36.writeFile(tmp, JSON.stringify(entry), { mode: 384 });
77005
- await fs36.rename(tmp, target);
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);