@datarails-ai/dr-cli 1.6.164 → 1.6.166
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
CHANGED
|
@@ -457,6 +457,7 @@ dr skill print <name> # print an embedded skill's SKILL.md
|
|
|
457
457
|
| Desk | `desk teams list`, `desk support-email set <team> <email>`, `desk smtp set/rotate/get/test <team>`, `desk sendgrid register <domain>/status/verify`, `desk ticket create <team> --title --requester-email [--attach <path> ...] [--no-thread]` (`<team>` is an id, support email, or team name. `ticket create` **emails the requester**, so it is confirm-gated and keeps the word gate on prod — see `docs/how-to-create-a-desk-ticket.md`) |
|
|
458
458
|
| Connectors | `connectors publish <dataqueryFile>`, `connectors query repoint <queryId>`, `connectors binding get <fileboxUuid>` (drives the connectors control-plane over VPN) |
|
|
459
459
|
| Agent Studio | `agents list/get/versions/test-link/refresh/pull/update/create/mcp-servers/cli-scopes/scope-registry`, `agents set-listed/set-global/set-visibility/archive` (two-step confirm), `agents models/catalog/system-entities`, `agents skills list/get/versions/bundle/pull/create/publish/delete` (`delete` two-step confirm). A CLI-exposed agent's `dr` sessions are capped by its CLI scopes (`resource:action`, default `*:read`): set with `update --cli-scopes <list>` / `--reset-cli-scopes [--cli-scopes-reason <text>]` or `create --expose-cli --cli-scopes <list>`, read with `cli-scopes`; the scope history and restore stay in the Backoffice (service-token only) |
|
|
460
|
+
| Agent release pins | `agents served`, `agents pin/unpin` (two-step confirm): the version each deployment serves; `--server`, `--servers <keys>` or `--production` |
|
|
460
461
|
| Skill | `skill` — prints embedded `SKILL.md` |
|
|
461
462
|
|
|
462
463
|
### Connectors: TLS over VPN
|
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.166",
|
|
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.33",
|
|
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-26T13:44:23.917Z";
|
|
73
73
|
function formatBuildTime(iso) {
|
|
74
74
|
if (iso === "") return "";
|
|
75
75
|
const d = new Date(iso);
|
|
@@ -707,6 +707,9 @@ function keys() {
|
|
|
707
707
|
return ordered.map((s) => s.key);
|
|
708
708
|
}
|
|
709
709
|
var PROD_KEYS = /* @__PURE__ */ new Set(["US", "US2", "UK", "CA", "EU", "STUDIO", "DEMO"]);
|
|
710
|
+
function prodKeys() {
|
|
711
|
+
return ordered.filter((s) => PROD_KEYS.has(s.key)).map((s) => s.key);
|
|
712
|
+
}
|
|
710
713
|
function isProdServer(input) {
|
|
711
714
|
let server;
|
|
712
715
|
try {
|
|
@@ -4371,6 +4374,11 @@ var PROD_GATE_TIER_A = /* @__PURE__ */ new Set([
|
|
|
4371
4374
|
"managed-agent|set-global",
|
|
4372
4375
|
"managed-agent|set-visibility",
|
|
4373
4376
|
"managed-agent|archive",
|
|
4377
|
+
// Release pins (dr agents pin / unpin). A pin decides which agent version customers get on
|
|
4378
|
+
// a deployment, and unpinning can jump it several versions at once; one run can cover every
|
|
4379
|
+
// production server. The plan prints each server's current pin, which is the undo.
|
|
4380
|
+
"managed-agent|pin",
|
|
4381
|
+
"managed-agent|unpin",
|
|
4374
4382
|
"agent-skill|delete",
|
|
4375
4383
|
// template library: a published entry is offered to every customer org on the server.
|
|
4376
4384
|
// Not `publish`: a Tier-A-only `publish` verb would make gen-scopes-map classify every
|
|
@@ -10050,14 +10058,14 @@ function parseRow3(cells, lineNo, seenSources, seenTargets) {
|
|
|
10050
10058
|
);
|
|
10051
10059
|
}
|
|
10052
10060
|
seenSources.set(sourceKey2, lineNo);
|
|
10053
|
-
const
|
|
10054
|
-
const priorTarget = seenTargets.get(
|
|
10061
|
+
const targetKey2 = target.toLowerCase();
|
|
10062
|
+
const priorTarget = seenTargets.get(targetKey2);
|
|
10055
10063
|
if (priorTarget !== void 0) {
|
|
10056
10064
|
throw new Error(
|
|
10057
10065
|
`--file: line ${lineNo}: duplicate target "${target}" (already on line ${priorTarget})`
|
|
10058
10066
|
);
|
|
10059
10067
|
}
|
|
10060
|
-
seenTargets.set(
|
|
10068
|
+
seenTargets.set(targetKey2, lineNo);
|
|
10061
10069
|
return { lineNo, source, target };
|
|
10062
10070
|
}
|
|
10063
10071
|
function requireEmail(value, lineNo, column) {
|
|
@@ -21600,7 +21608,7 @@ function registerRescanCommand(root) {
|
|
|
21600
21608
|
}
|
|
21601
21609
|
|
|
21602
21610
|
// src/commands/reports.ts
|
|
21603
|
-
import { isDeepStrictEqual } from "util";
|
|
21611
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
21604
21612
|
|
|
21605
21613
|
// src/api/reports.ts
|
|
21606
21614
|
async function list17(client, options) {
|
|
@@ -21692,6 +21700,18 @@ function coerceOptionValue(value) {
|
|
|
21692
21700
|
return value;
|
|
21693
21701
|
}
|
|
21694
21702
|
function buildCalculatedRow(params) {
|
|
21703
|
+
return buildCalculatedValue(
|
|
21704
|
+
params,
|
|
21705
|
+
"--position is required (name of the existing row to anchor to; run `budget-builder build` to list the available anchors)"
|
|
21706
|
+
);
|
|
21707
|
+
}
|
|
21708
|
+
function buildCalculatedColumn(params) {
|
|
21709
|
+
return buildCalculatedValue(
|
|
21710
|
+
params,
|
|
21711
|
+
'--position is required (name of the existing column to anchor to, e.g. "01/31/2026/Actuals")'
|
|
21712
|
+
);
|
|
21713
|
+
}
|
|
21714
|
+
function buildCalculatedValue(params, missingPositionMessage) {
|
|
21695
21715
|
const name = params.name.trim();
|
|
21696
21716
|
if (name === "") {
|
|
21697
21717
|
throw new Error("--name is required");
|
|
@@ -21702,9 +21722,7 @@ function buildCalculatedRow(params) {
|
|
|
21702
21722
|
}
|
|
21703
21723
|
const positionValue = (params.positionValue ?? "").trim();
|
|
21704
21724
|
if (positionValue === "") {
|
|
21705
|
-
throw new Error(
|
|
21706
|
-
"--position is required (name of the existing row to anchor to; run `budget-builder build` to list the available anchors)"
|
|
21707
|
-
);
|
|
21725
|
+
throw new Error(missingPositionMessage);
|
|
21708
21726
|
}
|
|
21709
21727
|
return {
|
|
21710
21728
|
name,
|
|
@@ -21735,19 +21753,27 @@ function assertNoPeriodLabelRefs(formula) {
|
|
|
21735
21753
|
);
|
|
21736
21754
|
}
|
|
21737
21755
|
function extractRowValidation(resp, rowName) {
|
|
21756
|
+
return extractCustomValueValidation(resp, rowName, "rows");
|
|
21757
|
+
}
|
|
21758
|
+
function extractCustomValueValidation(resp, name, axis) {
|
|
21759
|
+
const label = axis === "rows" ? "row" : "column";
|
|
21760
|
+
const noVerdictMap = {
|
|
21761
|
+
valid: false,
|
|
21762
|
+
message: "server returned no custom_value_validation.cols verdict map"
|
|
21763
|
+
};
|
|
21738
21764
|
if (resp === null || typeof resp !== "object") {
|
|
21739
|
-
return { valid: true, message: "" };
|
|
21765
|
+
return axis === "cols" ? noVerdictMap : { valid: true, message: "" };
|
|
21740
21766
|
}
|
|
21741
21767
|
const obj = resp;
|
|
21742
21768
|
const validation = obj["custom_value_validation"];
|
|
21743
21769
|
if (validation !== null && typeof validation === "object") {
|
|
21744
|
-
const
|
|
21745
|
-
if (
|
|
21746
|
-
const entry =
|
|
21770
|
+
const axisMap = validation[axis];
|
|
21771
|
+
if (axisMap !== null && typeof axisMap === "object") {
|
|
21772
|
+
const entry = axisMap[name];
|
|
21747
21773
|
if (entry === void 0 || entry === null || typeof entry !== "object") {
|
|
21748
21774
|
return {
|
|
21749
21775
|
valid: false,
|
|
21750
|
-
message: `server returned no verdict for
|
|
21776
|
+
message: `server returned no verdict for ${label} ${JSON.stringify(name)}`
|
|
21751
21777
|
};
|
|
21752
21778
|
}
|
|
21753
21779
|
const verdict = entry;
|
|
@@ -21771,12 +21797,15 @@ function extractRowValidation(resp, rowName) {
|
|
|
21771
21797
|
};
|
|
21772
21798
|
}
|
|
21773
21799
|
}
|
|
21800
|
+
if (axis === "cols") {
|
|
21801
|
+
return noVerdictMap;
|
|
21802
|
+
}
|
|
21774
21803
|
const rows = obj["calculatedRows"];
|
|
21775
21804
|
if (Array.isArray(rows)) {
|
|
21776
21805
|
for (const r of rows) {
|
|
21777
21806
|
if (r === null || typeof r !== "object") continue;
|
|
21778
21807
|
const row = r;
|
|
21779
|
-
if (row["name"] !==
|
|
21808
|
+
if (row["name"] !== name) continue;
|
|
21780
21809
|
if (row["invalid"] === true) {
|
|
21781
21810
|
return { valid: false, message: tooltipOf(row) };
|
|
21782
21811
|
}
|
|
@@ -21793,6 +21822,62 @@ function tooltipOf(obj) {
|
|
|
21793
21822
|
return "server marked the formula invalid (no errorTooltip provided)";
|
|
21794
21823
|
}
|
|
21795
21824
|
|
|
21825
|
+
// src/commands/_reportsShared.ts
|
|
21826
|
+
function parseReportIdArg(idStr) {
|
|
21827
|
+
const n = Number(idStr);
|
|
21828
|
+
if (!Number.isInteger(n) || String(n) !== idStr) {
|
|
21829
|
+
throw new Error(`invalid report id: "${idStr}"`);
|
|
21830
|
+
}
|
|
21831
|
+
return n;
|
|
21832
|
+
}
|
|
21833
|
+
function regenerationError(resp) {
|
|
21834
|
+
if (resp === null || typeof resp !== "object" || Array.isArray(resp)) return void 0;
|
|
21835
|
+
const details = resp["generated_report_details"];
|
|
21836
|
+
if (details === null || typeof details !== "object" || Array.isArray(details)) return void 0;
|
|
21837
|
+
const err = details["error"];
|
|
21838
|
+
return typeof err === "string" && err !== "" ? err : void 0;
|
|
21839
|
+
}
|
|
21840
|
+
|
|
21841
|
+
// src/commands/reports_customColumns.ts
|
|
21842
|
+
import { isDeepStrictEqual } from "util";
|
|
21843
|
+
|
|
21844
|
+
// src/api/budgetBuilder.ts
|
|
21845
|
+
import { randomUUID } from "crypto";
|
|
21846
|
+
var TREE_TO_REPORT_PATH = "/api/exreports/reports/tree_to_report";
|
|
21847
|
+
var VALIDATE_CUSTOM_VALUE_PATH = "/api/exreports/reports/validate_custom_value";
|
|
21848
|
+
var SAVE_REPORT_PATH = "/api/exreports/reports";
|
|
21849
|
+
function withTraceId(options) {
|
|
21850
|
+
const { traceId, headers, ...rest } = options ?? {};
|
|
21851
|
+
return {
|
|
21852
|
+
...rest,
|
|
21853
|
+
headers: { "X-Trace-id": traceId ?? randomUUID(), ...headers }
|
|
21854
|
+
};
|
|
21855
|
+
}
|
|
21856
|
+
async function treeToReport(client, definition, options) {
|
|
21857
|
+
const out = await client.putJSON(TREE_TO_REPORT_PATH, definition, withTraceId(options));
|
|
21858
|
+
if (out === null) {
|
|
21859
|
+
throw new Error(`PUT ${TREE_TO_REPORT_PATH}: empty response from tree_to_report`);
|
|
21860
|
+
}
|
|
21861
|
+
return out;
|
|
21862
|
+
}
|
|
21863
|
+
async function validateCustomValue(client, definition, options) {
|
|
21864
|
+
return client.putJSON(VALIDATE_CUSTOM_VALUE_PATH, definition, withTraceId(options));
|
|
21865
|
+
}
|
|
21866
|
+
async function save(client, definition, options) {
|
|
21867
|
+
const { timeoutMs, pollIntervalMs, sleep: sleep3, now, ...requestOptions } = options ?? {};
|
|
21868
|
+
const out = await writeWithCacheKeyPolling(client, "POST", SAVE_REPORT_PATH, definition, {
|
|
21869
|
+
...withTraceId(requestOptions),
|
|
21870
|
+
timeoutMs,
|
|
21871
|
+
pollIntervalMs,
|
|
21872
|
+
sleep: sleep3,
|
|
21873
|
+
now
|
|
21874
|
+
});
|
|
21875
|
+
if (out === null) {
|
|
21876
|
+
throw new Error(`POST ${SAVE_REPORT_PATH}: empty response from budget-builder save`);
|
|
21877
|
+
}
|
|
21878
|
+
return out;
|
|
21879
|
+
}
|
|
21880
|
+
|
|
21796
21881
|
// src/commands/_reportWriteTimeout.ts
|
|
21797
21882
|
var REPORT_WRITE_TIMEOUT_FLAG_HELP = 'Polling budget in milliseconds while the server answers "still running" (orgs with large-reports support process the write on a worker)';
|
|
21798
21883
|
var REPORT_WRITE_TIMEOUT_DEFAULT = String(WRITE_POLL_TIMEOUT_MS);
|
|
@@ -21811,6 +21896,238 @@ function reportWriteCallTimeoutMs(timeoutMs) {
|
|
|
21811
21896
|
return timeoutMs + REPORT_WRITE_SETUP_OVERHEAD_MS;
|
|
21812
21897
|
}
|
|
21813
21898
|
|
|
21899
|
+
// src/commands/reports_customColumns.ts
|
|
21900
|
+
var MAX_ANCHORS_SHOWN = 20;
|
|
21901
|
+
function isPlainObject2(v) {
|
|
21902
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
21903
|
+
}
|
|
21904
|
+
function extractColumnAnchors(resp) {
|
|
21905
|
+
if (!isPlainObject2(resp)) return [];
|
|
21906
|
+
const raw2 = resp["cols_position_options"];
|
|
21907
|
+
if (!Array.isArray(raw2)) return [];
|
|
21908
|
+
return raw2.map((o) => isPlainObject2(o) ? o["value"] : void 0).filter((v) => typeof v === "string" && v !== "").map((v) => decodeShape(v));
|
|
21909
|
+
}
|
|
21910
|
+
async function anchorsHint(client, definition, signal) {
|
|
21911
|
+
let anchors;
|
|
21912
|
+
try {
|
|
21913
|
+
anchors = extractColumnAnchors(
|
|
21914
|
+
await treeToReport(client, definition, { signal })
|
|
21915
|
+
);
|
|
21916
|
+
} catch {
|
|
21917
|
+
return "";
|
|
21918
|
+
}
|
|
21919
|
+
if (anchors.length === 0) return "";
|
|
21920
|
+
const shown = anchors.slice(0, MAX_ANCHORS_SHOWN);
|
|
21921
|
+
const more = anchors.length - shown.length;
|
|
21922
|
+
return ` Valid column names for --position and formula refs (${anchors.length}): ` + shown.map((a) => JSON.stringify(a)).join(", ") + (more > 0 ? `, \u2026 +${more} more` : "");
|
|
21923
|
+
}
|
|
21924
|
+
async function validateColumnAgainstServer(client, id, flags, signal) {
|
|
21925
|
+
if (id <= 0) {
|
|
21926
|
+
throw new Error("report id must be positive");
|
|
21927
|
+
}
|
|
21928
|
+
const column = buildCalculatedColumn({
|
|
21929
|
+
name: flags.name,
|
|
21930
|
+
formula: flags.formula,
|
|
21931
|
+
...flags.format !== void 0 ? { format: flags.format } : {},
|
|
21932
|
+
...flags.positionValue !== void 0 ? { positionValue: flags.positionValue } : {},
|
|
21933
|
+
...flags.before ? { afterPositionValue: false } : {}
|
|
21934
|
+
});
|
|
21935
|
+
const report = await get10(client, id, { signal });
|
|
21936
|
+
const type = typeof report.type === "string" ? report.type : "";
|
|
21937
|
+
if (type.toLowerCase() !== "financials") {
|
|
21938
|
+
throw new Error(
|
|
21939
|
+
`report ${id} is a ${JSON.stringify(type)} report \u2014 custom columns exist on Financials (Report Builder) reports only; budget columns are generated from the budget year and cycle`
|
|
21940
|
+
);
|
|
21941
|
+
}
|
|
21942
|
+
const stored = isPlainObject2(report.payload) ? decodeShape(report.payload) : {};
|
|
21943
|
+
const existing = Array.isArray(stored["calculatedCols"]) ? stored["calculatedCols"] : [];
|
|
21944
|
+
if (existing.some((c) => isPlainObject2(c) && c["name"] === column.name)) {
|
|
21945
|
+
throw new Error(
|
|
21946
|
+
`report ${id} already has a custom column named ${JSON.stringify(column.name)} \u2014 pick a different --name`
|
|
21947
|
+
);
|
|
21948
|
+
}
|
|
21949
|
+
const withColumn = {
|
|
21950
|
+
...stored,
|
|
21951
|
+
calculatedCols: [...existing, column]
|
|
21952
|
+
};
|
|
21953
|
+
const resp = await validateCustomValue(client, withColumn, { signal });
|
|
21954
|
+
const verdict = extractCustomValueValidation(resp, column.name, "cols");
|
|
21955
|
+
if (!verdict.valid) {
|
|
21956
|
+
throw new Error(
|
|
21957
|
+
`custom column ${JSON.stringify(column.name)} rejected: ${verdict.message.replace(/\.+$/, "")}.` + await anchorsHint(client, stored, signal)
|
|
21958
|
+
);
|
|
21959
|
+
}
|
|
21960
|
+
return { report, stored, existing, column, resp, warning: verdict.warning };
|
|
21961
|
+
}
|
|
21962
|
+
async function runReportsValidateColumn(cfg) {
|
|
21963
|
+
const { column, resp, warning } = await validateColumnAgainstServer(
|
|
21964
|
+
cfg.client,
|
|
21965
|
+
cfg.id,
|
|
21966
|
+
cfg.column,
|
|
21967
|
+
cfg.signal
|
|
21968
|
+
);
|
|
21969
|
+
if (cfg.asJson) {
|
|
21970
|
+
writeJSONIndented(cfg.out, resp);
|
|
21971
|
+
return;
|
|
21972
|
+
}
|
|
21973
|
+
if (warning !== void 0) {
|
|
21974
|
+
cfg.out.write(`warning: ${warning}
|
|
21975
|
+
`);
|
|
21976
|
+
}
|
|
21977
|
+
cfg.out.write(`Custom column ${JSON.stringify(column.name)} is valid for report ${cfg.id}.
|
|
21978
|
+
`);
|
|
21979
|
+
}
|
|
21980
|
+
async function runReportsAddColumn(cfg) {
|
|
21981
|
+
const { report, stored, existing, column, warning } = await validateColumnAgainstServer(
|
|
21982
|
+
cfg.client,
|
|
21983
|
+
cfg.id,
|
|
21984
|
+
cfg.column,
|
|
21985
|
+
cfg.signal
|
|
21986
|
+
);
|
|
21987
|
+
const savedColumn = { ...column, invalid: false, errorTooltip: "" };
|
|
21988
|
+
const body = { calculatedCols: [...existing, savedColumn] };
|
|
21989
|
+
const total = body.calculatedCols.length;
|
|
21990
|
+
if (warning !== void 0 && !cfg.asJson) {
|
|
21991
|
+
cfg.out.write(`warning: ${warning}
|
|
21992
|
+
`);
|
|
21993
|
+
}
|
|
21994
|
+
if (!cfg.confirm) {
|
|
21995
|
+
if (cfg.asJson) {
|
|
21996
|
+
writeJSONIndented(cfg.out, {
|
|
21997
|
+
action: "add-column",
|
|
21998
|
+
report_id: cfg.id,
|
|
21999
|
+
dry_run: true,
|
|
22000
|
+
...warning !== void 0 ? { warning } : {},
|
|
22001
|
+
body
|
|
22002
|
+
});
|
|
22003
|
+
return;
|
|
22004
|
+
}
|
|
22005
|
+
cfg.out.write(`Target: account ${cfg.client.serverKey}, operator ${cfg.client.email}
|
|
22006
|
+
`);
|
|
22007
|
+
cfg.out.write(
|
|
22008
|
+
`dry-run: would add custom column ${JSON.stringify(column.name)} to report ${cfg.id} ${JSON.stringify(report.name)} (${total} custom column${plural(total)} after). Re-run with --confirm to apply.
|
|
22009
|
+
`
|
|
22010
|
+
);
|
|
22011
|
+
cfg.out.write(` formula: ${column.formula}
|
|
22012
|
+
`);
|
|
22013
|
+
cfg.out.write(
|
|
22014
|
+
` position: ${column.afterPositionValue ? "right of" : "left of"} ${JSON.stringify(column.positionValue)}
|
|
22015
|
+
`
|
|
22016
|
+
);
|
|
22017
|
+
cfg.out.write(` format: ${column.format}
|
|
22018
|
+
`);
|
|
22019
|
+
return;
|
|
22020
|
+
}
|
|
22021
|
+
const current = await get10(cfg.client, cfg.id, { signal: cfg.signal });
|
|
22022
|
+
const currentPayload = isPlainObject2(current.payload) ? decodeShape(current.payload) : {};
|
|
22023
|
+
if (!isDeepStrictEqual(currentPayload, stored)) {
|
|
22024
|
+
throw new Error(
|
|
22025
|
+
`report ${cfg.id} changed while the column was being validated \u2014 nothing was written; re-run to validate against the current report`
|
|
22026
|
+
);
|
|
22027
|
+
}
|
|
22028
|
+
let resp;
|
|
22029
|
+
try {
|
|
22030
|
+
resp = await update5(cfg.client, cfg.id, body, {
|
|
22031
|
+
signal: cfg.signal,
|
|
22032
|
+
timeoutMs: cfg.timeoutMs,
|
|
22033
|
+
pollIntervalMs: cfg.pollIntervalMs
|
|
22034
|
+
});
|
|
22035
|
+
} catch (err) {
|
|
22036
|
+
if (err instanceof CacheKeyWriteTimeoutError) {
|
|
22037
|
+
throw new Error(
|
|
22038
|
+
`${err.message}. The column may still be added \u2014 check with \`dr reports get ${cfg.id} --json\` before retrying, or raise --timeout.`,
|
|
22039
|
+
{ cause: err }
|
|
22040
|
+
);
|
|
22041
|
+
}
|
|
22042
|
+
throw err;
|
|
22043
|
+
}
|
|
22044
|
+
if (cfg.asJson) {
|
|
22045
|
+
cfg.out.write(JSON.stringify(resp) + "\n");
|
|
22046
|
+
} else {
|
|
22047
|
+
cfg.out.write(
|
|
22048
|
+
`Added custom column ${JSON.stringify(column.name)} to report ${cfg.id} ${JSON.stringify(report.name)} (${total} custom column${plural(total)}).
|
|
22049
|
+
`
|
|
22050
|
+
);
|
|
22051
|
+
}
|
|
22052
|
+
const regenError = regenerationError(resp);
|
|
22053
|
+
if (regenError !== void 0) {
|
|
22054
|
+
throw new Error(
|
|
22055
|
+
`report ${cfg.id} was updated, but the platform could not regenerate its Excel output: ` + regenError
|
|
22056
|
+
);
|
|
22057
|
+
}
|
|
22058
|
+
}
|
|
22059
|
+
function columnFlagsFromOpts(opts) {
|
|
22060
|
+
return {
|
|
22061
|
+
name: opts.name ?? "",
|
|
22062
|
+
formula: opts.formula ?? "",
|
|
22063
|
+
format: opts.format,
|
|
22064
|
+
positionValue: opts.position,
|
|
22065
|
+
before: opts.before === true
|
|
22066
|
+
};
|
|
22067
|
+
}
|
|
22068
|
+
function addColumnFlags(cmd) {
|
|
22069
|
+
return cmd.argument("<id>", "report id (positive integer)").requiredOption("--name <name>", "Custom column name (unique within the report)").requiredOption(
|
|
22070
|
+
"--formula <formula>",
|
|
22071
|
+
'Column formula using bracket-wrapped COLUMN names, e.g. "[01/31/2026/Actuals]-[01/31/2026/Budget]" (nested column levels are joined with /)'
|
|
22072
|
+
).option("--format <format>", "Display format for the column (default: '#,###')").requiredOption(
|
|
22073
|
+
"--position <column-name>",
|
|
22074
|
+
"Name of the EXISTING column to place this one next to \u2014 a rejected column lists the valid names"
|
|
22075
|
+
).option("--before", "Place the column LEFT of the --position anchor (default: right)", false);
|
|
22076
|
+
}
|
|
22077
|
+
function registerReportsColumnCommands(reports) {
|
|
22078
|
+
const validateCmd = addColumnFlags(
|
|
22079
|
+
reports.command("validate-column").description(
|
|
22080
|
+
"Check a custom column against a Financials report (PUT validate_custom_value) without changing anything"
|
|
22081
|
+
)
|
|
22082
|
+
).option("--json", "Output the raw validation response JSON", false).action(async (idStr, opts) => {
|
|
22083
|
+
const id = parseReportIdArg(idStr);
|
|
22084
|
+
const column = columnFlagsFromOpts(opts);
|
|
22085
|
+
const setup = await setupCall(opts.server, opts.account);
|
|
22086
|
+
try {
|
|
22087
|
+
await runReportsValidateColumn({
|
|
22088
|
+
client: setup.client,
|
|
22089
|
+
id,
|
|
22090
|
+
column,
|
|
22091
|
+
asJson: opts.json === true,
|
|
22092
|
+
signal: setup.signal,
|
|
22093
|
+
out: process.stdout
|
|
22094
|
+
});
|
|
22095
|
+
} finally {
|
|
22096
|
+
setup.cleanup();
|
|
22097
|
+
}
|
|
22098
|
+
});
|
|
22099
|
+
addServerFlag(validateCmd);
|
|
22100
|
+
addAccountFlag(validateCmd);
|
|
22101
|
+
const addCmd = addColumnFlags(
|
|
22102
|
+
reports.command("add-column").description(
|
|
22103
|
+
"Validate a custom column and add it to a Financials report (validate_custom_value + PUT report; dry-run unless --confirm)"
|
|
22104
|
+
)
|
|
22105
|
+
).option("--confirm", "Actually add the column (default is dry-run)", false).option("--timeout <ms>", REPORT_WRITE_TIMEOUT_FLAG_HELP, REPORT_WRITE_TIMEOUT_DEFAULT).option("--json", "Output JSON: the dry-run plan, or the raw server response", false).action(async (idStr, opts) => {
|
|
22106
|
+
const id = parseReportIdArg(idStr);
|
|
22107
|
+
const column = columnFlagsFromOpts(opts);
|
|
22108
|
+
const timeoutMs = parseReportWriteTimeout(opts.timeout);
|
|
22109
|
+
const setup = await setupCall(opts.server, opts.account, {
|
|
22110
|
+
timeoutMs: reportWriteCallTimeoutMs(timeoutMs)
|
|
22111
|
+
});
|
|
22112
|
+
try {
|
|
22113
|
+
await runReportsAddColumn({
|
|
22114
|
+
client: setup.client,
|
|
22115
|
+
id,
|
|
22116
|
+
column,
|
|
22117
|
+
confirm: opts.confirm === true,
|
|
22118
|
+
asJson: opts.json === true,
|
|
22119
|
+
timeoutMs,
|
|
22120
|
+
signal: setup.signal,
|
|
22121
|
+
out: process.stdout
|
|
22122
|
+
});
|
|
22123
|
+
} finally {
|
|
22124
|
+
setup.cleanup();
|
|
22125
|
+
}
|
|
22126
|
+
});
|
|
22127
|
+
addServerFlag(addCmd);
|
|
22128
|
+
addAccountFlag(addCmd);
|
|
22129
|
+
}
|
|
22130
|
+
|
|
21814
22131
|
// src/commands/reports.ts
|
|
21815
22132
|
async function runReportsList(cfg) {
|
|
21816
22133
|
const all = await list17(cfg.client, { signal: cfg.signal });
|
|
@@ -21865,7 +22182,7 @@ ${JSON.stringify(resp)}
|
|
|
21865
22182
|
var DATE_PICKER_PRESETS = ["today", "yesterday", "this_month", "prev_month"];
|
|
21866
22183
|
var FILE_LINK_KEYS = ["document_data", "report_space_data", "workflow_data"];
|
|
21867
22184
|
var NOTHING_TO_UPDATE = "nothing to update \u2014 pass --payload-file, --name, --option, or --date-picker-preset";
|
|
21868
|
-
function
|
|
22185
|
+
function isPlainObject3(v) {
|
|
21869
22186
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
21870
22187
|
}
|
|
21871
22188
|
function assertDatePickerPreset(value) {
|
|
@@ -21884,7 +22201,7 @@ async function readRequestedChanges(flags) {
|
|
|
21884
22201
|
if (flags.payloadFile !== void 0) {
|
|
21885
22202
|
const raw2 = await readJsonPayload(flags.payloadFile);
|
|
21886
22203
|
const inner = raw2["payload"];
|
|
21887
|
-
if (
|
|
22204
|
+
if (isPlainObject3(inner)) {
|
|
21888
22205
|
Object.assign(changes, inner);
|
|
21889
22206
|
if (typeof raw2["id"] === "number") sourceReportId = raw2["id"];
|
|
21890
22207
|
} else {
|
|
@@ -21912,7 +22229,7 @@ async function readRequestedChanges(flags) {
|
|
|
21912
22229
|
function previewValue(v) {
|
|
21913
22230
|
if (v === void 0) return "(unset)";
|
|
21914
22231
|
if (Array.isArray(v)) return `[${v.length} item${plural(v.length)}]`;
|
|
21915
|
-
if (
|
|
22232
|
+
if (isPlainObject3(v)) {
|
|
21916
22233
|
const n = Object.keys(v).length;
|
|
21917
22234
|
return `{${n} key${plural(n)}}`;
|
|
21918
22235
|
}
|
|
@@ -21922,10 +22239,10 @@ function previewLines(stored, body) {
|
|
|
21922
22239
|
const lines = [];
|
|
21923
22240
|
for (const [key, next] of Object.entries(body)) {
|
|
21924
22241
|
const prev = stored[key];
|
|
21925
|
-
if (
|
|
22242
|
+
if (isPlainObject3(prev) && isPlainObject3(next)) {
|
|
21926
22243
|
const subKeys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
|
|
21927
22244
|
for (const sub2 of subKeys) {
|
|
21928
|
-
if (
|
|
22245
|
+
if (isDeepStrictEqual2(prev[sub2], next[sub2])) continue;
|
|
21929
22246
|
lines.push(`${key}.${sub2}: ${previewValue(prev[sub2])} \u2192 ${previewValue(next[sub2])}`);
|
|
21930
22247
|
}
|
|
21931
22248
|
continue;
|
|
@@ -21934,13 +22251,6 @@ function previewLines(stored, body) {
|
|
|
21934
22251
|
}
|
|
21935
22252
|
return lines;
|
|
21936
22253
|
}
|
|
21937
|
-
function regenerationError(resp) {
|
|
21938
|
-
if (!isPlainObject2(resp)) return void 0;
|
|
21939
|
-
const details = resp["generated_report_details"];
|
|
21940
|
-
if (!isPlainObject2(details)) return void 0;
|
|
21941
|
-
const err = details["error"];
|
|
21942
|
-
return typeof err === "string" && err !== "" ? err : void 0;
|
|
21943
|
-
}
|
|
21944
22254
|
async function runReportsUpdate(cfg) {
|
|
21945
22255
|
if (cfg.id <= 0) {
|
|
21946
22256
|
throw new Error("report id must be positive");
|
|
@@ -21957,15 +22267,15 @@ async function runReportsUpdate(cfg) {
|
|
|
21957
22267
|
`report ${cfg.id} is a ${JSON.stringify(type)} report \u2014 \`reports update\` edits Financials (Report Builder) reports only; budget reports cannot be edited from the CLI (edit them in Budget management)`
|
|
21958
22268
|
);
|
|
21959
22269
|
}
|
|
21960
|
-
const stored =
|
|
22270
|
+
const stored = isPlainObject3(report.payload) ? report.payload : {};
|
|
21961
22271
|
if (Object.keys(assignments).length > 0) {
|
|
21962
22272
|
const fileOptions = changes["options"];
|
|
21963
|
-
const base =
|
|
22273
|
+
const base = isPlainObject3(fileOptions) ? fileOptions : isPlainObject3(stored["options"]) ? stored["options"] : {};
|
|
21964
22274
|
changes["options"] = { ...base, ...assignments };
|
|
21965
22275
|
}
|
|
21966
22276
|
const body = {};
|
|
21967
22277
|
for (const [key, value] of Object.entries(changes)) {
|
|
21968
|
-
if (!
|
|
22278
|
+
if (!isDeepStrictEqual2(value, stored[key])) body[key] = value;
|
|
21969
22279
|
}
|
|
21970
22280
|
const changedKeys = Object.keys(body);
|
|
21971
22281
|
if (sourceReportId !== void 0 && sourceReportId !== cfg.id) {
|
|
@@ -22030,7 +22340,7 @@ async function runReportsUpdate(cfg) {
|
|
|
22030
22340
|
if (cfg.asJson) {
|
|
22031
22341
|
cfg.out.write(JSON.stringify(resp) + "\n");
|
|
22032
22342
|
} else {
|
|
22033
|
-
const respObj =
|
|
22343
|
+
const respObj = isPlainObject3(resp) ? resp : {};
|
|
22034
22344
|
const name = typeof respObj["name"] === "string" ? respObj["name"] : typeof body["name"] === "string" ? body["name"] : report.name;
|
|
22035
22345
|
const updatedAt = strOr(
|
|
22036
22346
|
typeof respObj["updated_at"] === "string" ? respObj["updated_at"] : "",
|
|
@@ -22141,13 +22451,6 @@ function renderReportsTable(out, reports) {
|
|
|
22141
22451
|
}
|
|
22142
22452
|
writeTable17(out, rows);
|
|
22143
22453
|
}
|
|
22144
|
-
function parseReportIdArg(idStr) {
|
|
22145
|
-
const n = Number(idStr);
|
|
22146
|
-
if (!Number.isInteger(n) || String(n) !== idStr) {
|
|
22147
|
-
throw new Error(`invalid report id: "${idStr}"`);
|
|
22148
|
-
}
|
|
22149
|
-
return n;
|
|
22150
|
-
}
|
|
22151
22454
|
function parseTableIdFlag4(raw2) {
|
|
22152
22455
|
if (raw2 === void 0 || raw2 === "") return void 0;
|
|
22153
22456
|
const n = Number(raw2);
|
|
@@ -22293,49 +22596,11 @@ function registerReportsCommand(root) {
|
|
|
22293
22596
|
});
|
|
22294
22597
|
addServerFlag(deleteCmd);
|
|
22295
22598
|
addAccountFlag(deleteCmd);
|
|
22599
|
+
registerReportsColumnCommands(reports);
|
|
22296
22600
|
}
|
|
22297
22601
|
|
|
22298
22602
|
// src/commands/budgetBuilder.ts
|
|
22299
22603
|
import { promises as fs12 } from "fs";
|
|
22300
|
-
|
|
22301
|
-
// src/api/budgetBuilder.ts
|
|
22302
|
-
import { randomUUID } from "crypto";
|
|
22303
|
-
var TREE_TO_REPORT_PATH = "/api/exreports/reports/tree_to_report";
|
|
22304
|
-
var VALIDATE_CUSTOM_VALUE_PATH = "/api/exreports/reports/validate_custom_value";
|
|
22305
|
-
var SAVE_REPORT_PATH = "/api/exreports/reports";
|
|
22306
|
-
function withTraceId(options) {
|
|
22307
|
-
const { traceId, headers, ...rest } = options ?? {};
|
|
22308
|
-
return {
|
|
22309
|
-
...rest,
|
|
22310
|
-
headers: { "X-Trace-id": traceId ?? randomUUID(), ...headers }
|
|
22311
|
-
};
|
|
22312
|
-
}
|
|
22313
|
-
async function treeToReport(client, definition, options) {
|
|
22314
|
-
const out = await client.putJSON(TREE_TO_REPORT_PATH, definition, withTraceId(options));
|
|
22315
|
-
if (out === null) {
|
|
22316
|
-
throw new Error(`PUT ${TREE_TO_REPORT_PATH}: empty response from tree_to_report`);
|
|
22317
|
-
}
|
|
22318
|
-
return out;
|
|
22319
|
-
}
|
|
22320
|
-
async function validateCustomValue(client, definition, options) {
|
|
22321
|
-
return client.putJSON(VALIDATE_CUSTOM_VALUE_PATH, definition, withTraceId(options));
|
|
22322
|
-
}
|
|
22323
|
-
async function save(client, definition, options) {
|
|
22324
|
-
const { timeoutMs, pollIntervalMs, sleep: sleep3, now, ...requestOptions } = options ?? {};
|
|
22325
|
-
const out = await writeWithCacheKeyPolling(client, "POST", SAVE_REPORT_PATH, definition, {
|
|
22326
|
-
...withTraceId(requestOptions),
|
|
22327
|
-
timeoutMs,
|
|
22328
|
-
pollIntervalMs,
|
|
22329
|
-
sleep: sleep3,
|
|
22330
|
-
now
|
|
22331
|
-
});
|
|
22332
|
-
if (out === null) {
|
|
22333
|
-
throw new Error(`POST ${SAVE_REPORT_PATH}: empty response from budget-builder save`);
|
|
22334
|
-
}
|
|
22335
|
-
return out;
|
|
22336
|
-
}
|
|
22337
|
-
|
|
22338
|
-
// src/commands/budgetBuilder.ts
|
|
22339
22604
|
function assertValidDesign(value, flag) {
|
|
22340
22605
|
if (typeof value !== "string" || !BUDGET_DESIGN_STYLES.includes(value)) {
|
|
22341
22606
|
throw new Error(
|
|
@@ -22379,7 +22644,7 @@ function designSummary(def) {
|
|
|
22379
22644
|
const newDesign = def.options?.standardDesign === true ? "off" : "on";
|
|
22380
22645
|
return `design=${design}, new design=${newDesign}`;
|
|
22381
22646
|
}
|
|
22382
|
-
var
|
|
22647
|
+
var MAX_ANCHORS_SHOWN2 = 20;
|
|
22383
22648
|
function extractRowAnchors(resp) {
|
|
22384
22649
|
if (resp === null || typeof resp !== "object") return [];
|
|
22385
22650
|
const raw2 = resp["rows_position_options"];
|
|
@@ -22405,7 +22670,7 @@ async function runBudgetBuilderBuild(cfg) {
|
|
|
22405
22670
|
`);
|
|
22406
22671
|
const anchors = extractRowAnchors(resp);
|
|
22407
22672
|
if (anchors.length > 0) {
|
|
22408
|
-
const shown = anchors.slice(0,
|
|
22673
|
+
const shown = anchors.slice(0, MAX_ANCHORS_SHOWN2);
|
|
22409
22674
|
const more = anchors.length - shown.length;
|
|
22410
22675
|
cfg.out.write(
|
|
22411
22676
|
`Row anchors for --position (${anchors.length}): ` + shown.map((a) => JSON.stringify(a)).join(", ") + (more > 0 ? `, \u2026 +${more} more` : "") + "\n"
|
|
@@ -33639,7 +33904,7 @@ function describeValue(v) {
|
|
|
33639
33904
|
return typeof v;
|
|
33640
33905
|
}
|
|
33641
33906
|
}
|
|
33642
|
-
function
|
|
33907
|
+
function isPlainObject4(v) {
|
|
33643
33908
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
33644
33909
|
}
|
|
33645
33910
|
function tableIdProblems(value) {
|
|
@@ -33655,7 +33920,7 @@ function tableIdProblems(value) {
|
|
|
33655
33920
|
);
|
|
33656
33921
|
}
|
|
33657
33922
|
value.forEach((entry, i) => {
|
|
33658
|
-
if (!
|
|
33923
|
+
if (!isPlainObject4(entry)) {
|
|
33659
33924
|
out.push(`config.table_id[${i}] must be an object, got ${describeValue(entry)}.`);
|
|
33660
33925
|
} else if (typeof entry["match_type"] !== "string") {
|
|
33661
33926
|
out.push(
|
|
@@ -33667,7 +33932,7 @@ function tableIdProblems(value) {
|
|
|
33667
33932
|
}
|
|
33668
33933
|
function validateMapperConfigShape(config) {
|
|
33669
33934
|
if (config === void 0) return;
|
|
33670
|
-
if (!
|
|
33935
|
+
if (!isPlainObject4(config)) {
|
|
33671
33936
|
throw new Error(`config must be a JSON object, got ${describeValue(config)}. ${GET_HINT}`);
|
|
33672
33937
|
}
|
|
33673
33938
|
const problems = [];
|
|
@@ -33683,7 +33948,7 @@ function validateMapperConfigShape(config) {
|
|
|
33683
33948
|
continue;
|
|
33684
33949
|
}
|
|
33685
33950
|
value.forEach((entry, i) => {
|
|
33686
|
-
if (!
|
|
33951
|
+
if (!isPlainObject4(entry)) {
|
|
33687
33952
|
problems.push(`config.${key}[${i}] must be an object, got ${describeValue(entry)}.`);
|
|
33688
33953
|
}
|
|
33689
33954
|
});
|
|
@@ -62481,7 +62746,7 @@ async function readHiddenRulesPayload(path13) {
|
|
|
62481
62746
|
raw2.forEach((entry, idx) => {
|
|
62482
62747
|
const ctx = `rules[${idx}]`;
|
|
62483
62748
|
const base = validateCreate(entry, ctx);
|
|
62484
|
-
if (!
|
|
62749
|
+
if (!isPlainObject5(entry)) {
|
|
62485
62750
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
62486
62751
|
}
|
|
62487
62752
|
const ctxnId = entry["customer_transaction_id"];
|
|
@@ -62540,11 +62805,11 @@ function parseReorderShorthand(raw2) {
|
|
|
62540
62805
|
}
|
|
62541
62806
|
return { rules: entries };
|
|
62542
62807
|
}
|
|
62543
|
-
function
|
|
62808
|
+
function isPlainObject5(v) {
|
|
62544
62809
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
62545
62810
|
}
|
|
62546
62811
|
function validateCreate(raw2, ctx) {
|
|
62547
|
-
if (!
|
|
62812
|
+
if (!isPlainObject5(raw2)) {
|
|
62548
62813
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
62549
62814
|
}
|
|
62550
62815
|
if (!Array.isArray(raw2["conditions"]) || raw2["conditions"].length === 0) {
|
|
@@ -62600,7 +62865,7 @@ function validateCreate(raw2, ctx) {
|
|
|
62600
62865
|
return out;
|
|
62601
62866
|
}
|
|
62602
62867
|
function validateCondition(raw2, ctx) {
|
|
62603
|
-
if (!
|
|
62868
|
+
if (!isPlainObject5(raw2)) {
|
|
62604
62869
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
62605
62870
|
}
|
|
62606
62871
|
const inputField = raw2["input_field"];
|
|
@@ -62665,7 +62930,7 @@ function validateArgsForOperation(op, args, ctx) {
|
|
|
62665
62930
|
}
|
|
62666
62931
|
}
|
|
62667
62932
|
function validateReorder(raw2, ctx) {
|
|
62668
|
-
if (!
|
|
62933
|
+
if (!isPlainObject5(raw2)) {
|
|
62669
62934
|
throw new Error(`--file: ${ctx} must be a JSON object`);
|
|
62670
62935
|
}
|
|
62671
62936
|
if (!Array.isArray(raw2["rules"]) || raw2["rules"].length === 0) {
|
|
@@ -62674,7 +62939,7 @@ function validateReorder(raw2, ctx) {
|
|
|
62674
62939
|
const seenIds = /* @__PURE__ */ new Set();
|
|
62675
62940
|
const entries = [];
|
|
62676
62941
|
raw2["rules"].forEach((entry, idx) => {
|
|
62677
|
-
if (!
|
|
62942
|
+
if (!isPlainObject5(entry)) {
|
|
62678
62943
|
throw new Error(`--file: ${ctx}.rules[${idx}] must be a JSON object`);
|
|
62679
62944
|
}
|
|
62680
62945
|
const id = entry["id"];
|
|
@@ -75447,6 +75712,15 @@ function setAgentGlobal(client, body, options) {
|
|
|
75447
75712
|
function setAgentVisibility(client, body, options) {
|
|
75448
75713
|
return write(client, "v1/ManagedAgents/setAgentVisibility", body, options);
|
|
75449
75714
|
}
|
|
75715
|
+
function getServedAgentVersion(client, body, options) {
|
|
75716
|
+
return read(client, "v1/ManagedAgents/getServedAgentVersion", body, options);
|
|
75717
|
+
}
|
|
75718
|
+
function pinAgentVersion(client, body, options) {
|
|
75719
|
+
return write(client, "v1/ManagedAgents/pinAgentVersion", body, options);
|
|
75720
|
+
}
|
|
75721
|
+
function unpinAgent(client, body, options) {
|
|
75722
|
+
return write(client, "v1/ManagedAgents/unpinAgent", body, options);
|
|
75723
|
+
}
|
|
75450
75724
|
function listSkills(client, body, options) {
|
|
75451
75725
|
return read(client, "v1/ManagedAgents/listSkills", body, options);
|
|
75452
75726
|
}
|
|
@@ -76138,6 +76412,356 @@ function renderToolsProposalText(proposal, resolved) {
|
|
|
76138
76412
|
return lines.join("\n");
|
|
76139
76413
|
}
|
|
76140
76414
|
|
|
76415
|
+
// src/commands/agents_pin.ts
|
|
76416
|
+
var AGENT_ID = "<agentId>";
|
|
76417
|
+
var REQUEST_BUDGET_MS = 3e4;
|
|
76418
|
+
function resolvePinTargets(opts) {
|
|
76419
|
+
const named = opts.servers !== void 0;
|
|
76420
|
+
const production = opts.production === true;
|
|
76421
|
+
const chosen = [opts.server !== void 0, named, production].filter(Boolean).length;
|
|
76422
|
+
if (chosen > 1) {
|
|
76423
|
+
throw new Error("pass one of --server, --servers or --production");
|
|
76424
|
+
}
|
|
76425
|
+
if (production) return [...prodKeys()];
|
|
76426
|
+
if (!named) return void 0;
|
|
76427
|
+
const keys2 = splitList(opts.servers).map((k) => lookup(k).key);
|
|
76428
|
+
if (keys2.length === 0) throw new Error("--servers: expected comma-separated server keys");
|
|
76429
|
+
return [...new Set(keys2)];
|
|
76430
|
+
}
|
|
76431
|
+
function targetKey(t) {
|
|
76432
|
+
try {
|
|
76433
|
+
return lookup(t.client.serverKey).key;
|
|
76434
|
+
} catch {
|
|
76435
|
+
return t.client.serverKey;
|
|
76436
|
+
}
|
|
76437
|
+
}
|
|
76438
|
+
function targetFlag(targets) {
|
|
76439
|
+
const keys2 = targets.map((t) => targetKey(t));
|
|
76440
|
+
const prod = prodKeys();
|
|
76441
|
+
if (keys2.length === prod.length && keys2.every((k, i) => k === prod[i])) return "--production";
|
|
76442
|
+
if (keys2.length === 1) return `--server ${shellArg(keys2[0] ?? "")}`;
|
|
76443
|
+
return `--servers ${shellArg(keys2.join(","))}`;
|
|
76444
|
+
}
|
|
76445
|
+
function gateServer(targets) {
|
|
76446
|
+
const keys2 = targets.map((t) => targetKey(t));
|
|
76447
|
+
return keys2.find((k) => isProdServer(k)) ?? keys2[0] ?? "";
|
|
76448
|
+
}
|
|
76449
|
+
function versionText(v) {
|
|
76450
|
+
return v !== void 0 ? `v${v}` : "nothing";
|
|
76451
|
+
}
|
|
76452
|
+
function describeServed(state) {
|
|
76453
|
+
const serves = `serves ${versionText(state.servedVersion)}`;
|
|
76454
|
+
const pin = state.pinnedVersion !== void 0 ? `pinned v${state.pinnedVersion}` + (state.pinnedBy !== void 0 ? ` by ${state.pinnedBy}` : "") + (state.pinnedAt !== void 0 ? ` on ${state.pinnedAt.slice(0, 10)}` : "") : "not pinned";
|
|
76455
|
+
const reason = state.reason !== void 0 ? ` (${state.reason})` : "";
|
|
76456
|
+
return `${serves} \xB7 ${pin}${reason}`;
|
|
76457
|
+
}
|
|
76458
|
+
function padKey(key, width) {
|
|
76459
|
+
return key.padEnd(width, " ");
|
|
76460
|
+
}
|
|
76461
|
+
function keyWidth(targets) {
|
|
76462
|
+
return Math.max(...targets.map((t) => targetKey(t).length));
|
|
76463
|
+
}
|
|
76464
|
+
function errorText(err) {
|
|
76465
|
+
return err instanceof Error ? err.message : String(err);
|
|
76466
|
+
}
|
|
76467
|
+
function summaryError(summary, causes) {
|
|
76468
|
+
const refusal = causes.find((e) => e instanceof ScopeDeniedError || e instanceof TenantPinError);
|
|
76469
|
+
const first = causes[0];
|
|
76470
|
+
const oneClass = first instanceof Error && first.constructor !== Error && causes.every((e) => e instanceof Error && e.constructor === first.constructor);
|
|
76471
|
+
const keep = refusal ?? (oneClass ? first : void 0);
|
|
76472
|
+
if (keep instanceof Error) {
|
|
76473
|
+
keep.message = summary;
|
|
76474
|
+
return keep;
|
|
76475
|
+
}
|
|
76476
|
+
return new Error(summary, {
|
|
76477
|
+
cause: causes.length === 1 ? first : new AggregateError(causes, summary)
|
|
76478
|
+
});
|
|
76479
|
+
}
|
|
76480
|
+
function forJson(outcomes) {
|
|
76481
|
+
return outcomes.map((o) => ({
|
|
76482
|
+
server: o.server,
|
|
76483
|
+
...o.result !== void 0 && { result: o.result },
|
|
76484
|
+
...o.error !== void 0 && { error: o.error }
|
|
76485
|
+
}));
|
|
76486
|
+
}
|
|
76487
|
+
function causesOf(outcomes) {
|
|
76488
|
+
return outcomes.filter((o) => o.error !== void 0).map((o) => o.cause);
|
|
76489
|
+
}
|
|
76490
|
+
async function readServed(targets, agentId) {
|
|
76491
|
+
const outcomes = [];
|
|
76492
|
+
for (const t of targets) {
|
|
76493
|
+
t.extendTimeout?.(REQUEST_BUDGET_MS);
|
|
76494
|
+
try {
|
|
76495
|
+
const result = await getServedAgentVersion(t.client, { agentId }, { signal: t.signal });
|
|
76496
|
+
outcomes.push({ server: targetKey(t), result });
|
|
76497
|
+
} catch (err) {
|
|
76498
|
+
outcomes.push({ server: targetKey(t), error: errorText(err), cause: err });
|
|
76499
|
+
} finally {
|
|
76500
|
+
t.pauseTimeout?.();
|
|
76501
|
+
}
|
|
76502
|
+
}
|
|
76503
|
+
return outcomes;
|
|
76504
|
+
}
|
|
76505
|
+
function failedServers(outcomes) {
|
|
76506
|
+
return outcomes.filter((o) => o.error !== void 0).map((o) => o.server);
|
|
76507
|
+
}
|
|
76508
|
+
async function runAgentsServed(cfg) {
|
|
76509
|
+
const agentId = requireId(cfg.id, AGENT_ID);
|
|
76510
|
+
const outcomes = await readServed(cfg.targets, agentId);
|
|
76511
|
+
if (cfg.asJson) {
|
|
76512
|
+
writeJSONIndented(cfg.out, forJson(outcomes));
|
|
76513
|
+
} else {
|
|
76514
|
+
const width = keyWidth(cfg.targets);
|
|
76515
|
+
for (const o of outcomes) {
|
|
76516
|
+
const line = o.error !== void 0 ? `could not read: ${o.error}` : describeServed(o.result ?? {});
|
|
76517
|
+
cfg.out.write(`${padKey(o.server, width)} ${line}
|
|
76518
|
+
`);
|
|
76519
|
+
}
|
|
76520
|
+
}
|
|
76521
|
+
const failed = failedServers(outcomes);
|
|
76522
|
+
if (failed.length > 0) {
|
|
76523
|
+
throw summaryError(
|
|
76524
|
+
`could not read ${failed.length} of ${outcomes.length} server(s): ${failed.join(", ")}`,
|
|
76525
|
+
causesOf(outcomes)
|
|
76526
|
+
);
|
|
76527
|
+
}
|
|
76528
|
+
}
|
|
76529
|
+
async function writeEach(cfg, verb, write2) {
|
|
76530
|
+
const prose = proseStream(cfg);
|
|
76531
|
+
const width = keyWidth(cfg.targets);
|
|
76532
|
+
const outcomes = [];
|
|
76533
|
+
for (const t of cfg.targets) {
|
|
76534
|
+
t.extendTimeout?.(REQUEST_BUDGET_MS);
|
|
76535
|
+
try {
|
|
76536
|
+
const result = await write2(t);
|
|
76537
|
+
outcomes.push({ server: targetKey(t), result });
|
|
76538
|
+
const after = result !== null ? describeServed(result) : "done";
|
|
76539
|
+
prose.write(`${padKey(targetKey(t), width)} ${verb} \u2014 ${after}
|
|
76540
|
+
`);
|
|
76541
|
+
} catch (err) {
|
|
76542
|
+
outcomes.push({ server: targetKey(t), error: errorText(err), cause: err });
|
|
76543
|
+
prose.write(`${padKey(targetKey(t), width)} FAILED \u2014 ${errorText(err)}
|
|
76544
|
+
`);
|
|
76545
|
+
}
|
|
76546
|
+
}
|
|
76547
|
+
if (cfg.asJson) writeJSONIndented(cfg.out, forJson(outcomes));
|
|
76548
|
+
const failed = failedServers(outcomes);
|
|
76549
|
+
if (failed.length > 0) {
|
|
76550
|
+
const done = outcomes.filter((o) => o.error === void 0).map((o) => o.server);
|
|
76551
|
+
throw summaryError(
|
|
76552
|
+
`${verb === "pinned" ? "pin" : "unpin"} failed on ${failed.join(", ")}` + (done.length > 0 ? ` but succeeded on ${done.join(", ")} \u2014 those now serve differently` : "") + ". Re-run for the failed server(s) only with --servers.",
|
|
76553
|
+
causesOf(outcomes)
|
|
76554
|
+
);
|
|
76555
|
+
}
|
|
76556
|
+
}
|
|
76557
|
+
async function planLines(targets, agentId, change) {
|
|
76558
|
+
const outcomes = await readServed(targets, agentId);
|
|
76559
|
+
const failed = failedServers(outcomes);
|
|
76560
|
+
if (failed.length > 0) {
|
|
76561
|
+
const detail = outcomes.filter((o) => o.error !== void 0).map((o) => `${o.server}: ${o.error}`).join("; ");
|
|
76562
|
+
throw summaryError(
|
|
76563
|
+
`could not read the current pin on ${failed.join(", ")} \u2014 ${detail}`,
|
|
76564
|
+
causesOf(outcomes)
|
|
76565
|
+
);
|
|
76566
|
+
}
|
|
76567
|
+
const width = keyWidth(targets);
|
|
76568
|
+
const lines = outcomes.map(
|
|
76569
|
+
(o) => `${padKey(o.server, width)} ${describeServed(o.result ?? {})} \u2192 ${change(o.result ?? {})}`
|
|
76570
|
+
);
|
|
76571
|
+
return lines;
|
|
76572
|
+
}
|
|
76573
|
+
async function assertVersionExists(target, agentId, version) {
|
|
76574
|
+
target.extendTimeout?.(REQUEST_BUDGET_MS);
|
|
76575
|
+
let res;
|
|
76576
|
+
try {
|
|
76577
|
+
res = await listAgentVersions(target.client, { agentId }, { signal: target.signal });
|
|
76578
|
+
} finally {
|
|
76579
|
+
target.pauseTimeout?.();
|
|
76580
|
+
}
|
|
76581
|
+
const data4 = res?.data;
|
|
76582
|
+
if (!Array.isArray(data4)) return;
|
|
76583
|
+
if (!data4.some((v) => v.version === version)) {
|
|
76584
|
+
const known = data4.map((v) => v.version).filter((v) => typeof v === "number").sort((a, b) => b - a);
|
|
76585
|
+
throw new Error(
|
|
76586
|
+
`${agentId} has no version ${version}` + (known.length > 0 ? ` (latest is v${known[0]})` : "")
|
|
76587
|
+
);
|
|
76588
|
+
}
|
|
76589
|
+
}
|
|
76590
|
+
function requireVersion(version) {
|
|
76591
|
+
if (version === void 0) throw new Error("missing required option: --version-number <n>");
|
|
76592
|
+
return requirePositiveInt(version, "--version-number");
|
|
76593
|
+
}
|
|
76594
|
+
function pinAction(keys2, agentId, version) {
|
|
76595
|
+
return { resource: "managed-agent", id: `${keys2.join(",")}:${agentId}:v${version}`, verb: "pin" };
|
|
76596
|
+
}
|
|
76597
|
+
function unpinAction(keys2, agentId) {
|
|
76598
|
+
return { resource: "managed-agent", id: `${keys2.join(",")}:${agentId}`, verb: "unpin" };
|
|
76599
|
+
}
|
|
76600
|
+
async function runAgentsPin(cfg) {
|
|
76601
|
+
const agentId = requireId(cfg.id, AGENT_ID);
|
|
76602
|
+
const version = requireVersion(cfg.version);
|
|
76603
|
+
const flag = targetFlag(cfg.targets);
|
|
76604
|
+
const keys2 = cfg.targets.map((t) => targetKey(t));
|
|
76605
|
+
const action = pinAction(keys2, agentId, version);
|
|
76606
|
+
await preflightConfirmToken(action, cfg.token);
|
|
76607
|
+
const first = cfg.targets[0];
|
|
76608
|
+
if (first === void 0) throw new Error("no server to pin on");
|
|
76609
|
+
await assertVersionExists(first, agentId, version);
|
|
76610
|
+
await twoStepConfirm({
|
|
76611
|
+
out: cfg.out,
|
|
76612
|
+
action,
|
|
76613
|
+
token: cfg.token,
|
|
76614
|
+
server: gateServer(cfg.targets),
|
|
76615
|
+
confirmCmd: `dr agents pin --id ${shellArg(agentId)} --version-number ${version} ${flag}`,
|
|
76616
|
+
describe: async () => [
|
|
76617
|
+
`agent: ${agentId}`,
|
|
76618
|
+
`pin: v${version} on ${keys2.join(", ")}`,
|
|
76619
|
+
...await planLines(cfg.targets, agentId, () => `pinned v${version}`),
|
|
76620
|
+
"Each server keeps serving the pinned version until someone pins another or unpins it."
|
|
76621
|
+
],
|
|
76622
|
+
execute: () => writeEach(
|
|
76623
|
+
cfg,
|
|
76624
|
+
"pinned",
|
|
76625
|
+
(t) => pinAgentVersion(t.client, { agentId, version }, { signal: t.signal })
|
|
76626
|
+
)
|
|
76627
|
+
});
|
|
76628
|
+
}
|
|
76629
|
+
async function runAgentsUnpin(cfg) {
|
|
76630
|
+
const agentId = requireId(cfg.id, AGENT_ID);
|
|
76631
|
+
const flag = targetFlag(cfg.targets);
|
|
76632
|
+
const keys2 = cfg.targets.map((t) => targetKey(t));
|
|
76633
|
+
const action = unpinAction(keys2, agentId);
|
|
76634
|
+
await preflightConfirmToken(action, cfg.token);
|
|
76635
|
+
await twoStepConfirm({
|
|
76636
|
+
out: cfg.out,
|
|
76637
|
+
action,
|
|
76638
|
+
token: cfg.token,
|
|
76639
|
+
server: gateServer(cfg.targets),
|
|
76640
|
+
confirmCmd: `dr agents unpin --id ${shellArg(agentId)} ${flag}`,
|
|
76641
|
+
describe: async () => [
|
|
76642
|
+
`agent: ${agentId}`,
|
|
76643
|
+
`unpin on ${keys2.join(", ")}`,
|
|
76644
|
+
...await planLines(cfg.targets, agentId, () => "not pinned"),
|
|
76645
|
+
"An unpinned server serves the newest version on its SDK track, which can be several versions ahead of the pin."
|
|
76646
|
+
],
|
|
76647
|
+
execute: () => writeEach(cfg, "unpinned", (t) => unpinAgent(t.client, { agentId }, { signal: t.signal }))
|
|
76648
|
+
});
|
|
76649
|
+
}
|
|
76650
|
+
async function openEach(keys2, open3) {
|
|
76651
|
+
const setups = [];
|
|
76652
|
+
const failures = [];
|
|
76653
|
+
const causes = [];
|
|
76654
|
+
for (const key of keys2) {
|
|
76655
|
+
try {
|
|
76656
|
+
setups.push(await open3(key));
|
|
76657
|
+
} catch (err) {
|
|
76658
|
+
failures.push(`${key}: ${errorText(err)}`);
|
|
76659
|
+
causes.push(err);
|
|
76660
|
+
}
|
|
76661
|
+
}
|
|
76662
|
+
if (failures.length > 0) {
|
|
76663
|
+
for (const s of setups) s.cleanup();
|
|
76664
|
+
throw summaryError(
|
|
76665
|
+
`cannot open ${failures.length} of ${keys2.length} server(s) \u2014 nothing was read or written:
|
|
76666
|
+
` + failures.join("\n "),
|
|
76667
|
+
causes
|
|
76668
|
+
);
|
|
76669
|
+
}
|
|
76670
|
+
return setups;
|
|
76671
|
+
}
|
|
76672
|
+
function knownKeys(opts) {
|
|
76673
|
+
const keys2 = resolvePinTargets(opts);
|
|
76674
|
+
if (keys2 !== void 0 || opts.server === void 0) return keys2;
|
|
76675
|
+
try {
|
|
76676
|
+
return [lookup(opts.server.trim()).key];
|
|
76677
|
+
} catch {
|
|
76678
|
+
return void 0;
|
|
76679
|
+
}
|
|
76680
|
+
}
|
|
76681
|
+
async function preflightTargets(opts, token, action) {
|
|
76682
|
+
if (token === void 0 || token.trim() === "") return;
|
|
76683
|
+
const keys2 = knownKeys(opts);
|
|
76684
|
+
if (keys2 !== void 0) await preflightConfirmToken(action(keys2), token);
|
|
76685
|
+
}
|
|
76686
|
+
async function openTargets(opts) {
|
|
76687
|
+
const keys2 = resolvePinTargets(opts);
|
|
76688
|
+
const open3 = async (key) => {
|
|
76689
|
+
const setup = await setupCall(key, opts.account);
|
|
76690
|
+
setup.pauseTimeout();
|
|
76691
|
+
return setup;
|
|
76692
|
+
};
|
|
76693
|
+
const setups = keys2 === void 0 ? [await open3(opts.server)] : await openEach(keys2, open3);
|
|
76694
|
+
const targets = setups.map((s) => ({
|
|
76695
|
+
client: s.client,
|
|
76696
|
+
signal: s.signal,
|
|
76697
|
+
pauseTimeout: s.pauseTimeout,
|
|
76698
|
+
extendTimeout: s.extendTimeout
|
|
76699
|
+
}));
|
|
76700
|
+
return { targets, setups };
|
|
76701
|
+
}
|
|
76702
|
+
function withPinTargets(cmd) {
|
|
76703
|
+
withTarget(cmd);
|
|
76704
|
+
cmd.option(
|
|
76705
|
+
"--servers <keys>",
|
|
76706
|
+
"Comma-separated server keys, e.g. US,UK. Each needs its own dr login."
|
|
76707
|
+
);
|
|
76708
|
+
cmd.option("--production", "Every production server: US, US2, UK, CA, EU, STUDIO, DEMO.");
|
|
76709
|
+
return cmd;
|
|
76710
|
+
}
|
|
76711
|
+
function registerAgentPinCommands(agents) {
|
|
76712
|
+
const served = agents.command("served").description(
|
|
76713
|
+
"Show which version of an agent each server serves, and the release pin it holds, if any."
|
|
76714
|
+
).option("--id <agentId>", "Agent id.").option("--json", JSON_HELP).action(async (opts) => {
|
|
76715
|
+
const { targets, setups } = await openTargets(opts);
|
|
76716
|
+
try {
|
|
76717
|
+
await runAgentsServed({ targets, ...std(), id: opts.id, asJson: opts.json === true });
|
|
76718
|
+
} finally {
|
|
76719
|
+
for (const s of setups) s.cleanup();
|
|
76720
|
+
}
|
|
76721
|
+
});
|
|
76722
|
+
withPinTargets(served);
|
|
76723
|
+
const pin = agents.command("pin").description(
|
|
76724
|
+
"Hold a server on one agent version: it serves that version whatever is saved later. (two-step confirm)"
|
|
76725
|
+
).option("--id <agentId>", "Agent id.").option("--version-number <n>", "The version to serve.").option("--confirm-token <token>", CONFIRM_HELP).option("--json", JSON_HELP).action(async (opts) => {
|
|
76726
|
+
const agentId = requireId(opts.id, AGENT_ID);
|
|
76727
|
+
const version = requireVersion(opts.versionNumber);
|
|
76728
|
+
await preflightTargets(opts, opts.confirmToken, (keys2) => pinAction(keys2, agentId, version));
|
|
76729
|
+
const { targets, setups } = await openTargets(opts);
|
|
76730
|
+
try {
|
|
76731
|
+
await runAgentsPin({
|
|
76732
|
+
targets,
|
|
76733
|
+
...std(),
|
|
76734
|
+
id: opts.id,
|
|
76735
|
+
version: opts.versionNumber,
|
|
76736
|
+
token: opts.confirmToken,
|
|
76737
|
+
asJson: opts.json === true
|
|
76738
|
+
});
|
|
76739
|
+
} finally {
|
|
76740
|
+
for (const s of setups) s.cleanup();
|
|
76741
|
+
}
|
|
76742
|
+
});
|
|
76743
|
+
withPinTargets(pin);
|
|
76744
|
+
const unpin = agents.command("unpin").description(
|
|
76745
|
+
"Drop a server's pin: it goes back to serving the newest version on its SDK track. (two-step confirm)"
|
|
76746
|
+
).option("--id <agentId>", "Agent id.").option("--confirm-token <token>", CONFIRM_HELP).option("--json", JSON_HELP).action(async (opts) => {
|
|
76747
|
+
const agentId = requireId(opts.id, AGENT_ID);
|
|
76748
|
+
await preflightTargets(opts, opts.confirmToken, (keys2) => unpinAction(keys2, agentId));
|
|
76749
|
+
const { targets, setups } = await openTargets(opts);
|
|
76750
|
+
try {
|
|
76751
|
+
await runAgentsUnpin({
|
|
76752
|
+
targets,
|
|
76753
|
+
...std(),
|
|
76754
|
+
id: opts.id,
|
|
76755
|
+
token: opts.confirmToken,
|
|
76756
|
+
asJson: opts.json === true
|
|
76757
|
+
});
|
|
76758
|
+
} finally {
|
|
76759
|
+
for (const s of setups) s.cleanup();
|
|
76760
|
+
}
|
|
76761
|
+
});
|
|
76762
|
+
withPinTargets(unpin);
|
|
76763
|
+
}
|
|
76764
|
+
|
|
76141
76765
|
// src/commands/agents_skills.ts
|
|
76142
76766
|
import { mkdir as mkdir4, writeFile as writeFile7 } from "fs/promises";
|
|
76143
76767
|
import { dirname as dirname9, join as join17 } from "path";
|
|
@@ -76707,7 +77331,7 @@ function registerAgentStudioCommands(agents) {
|
|
|
76707
77331
|
}
|
|
76708
77332
|
|
|
76709
77333
|
// src/commands/agents.ts
|
|
76710
|
-
var
|
|
77334
|
+
var AGENT_ID2 = "<agentId>";
|
|
76711
77335
|
function agentRef(agentId, version) {
|
|
76712
77336
|
return version !== void 0 ? { agentId, version: requirePositiveInt(version, "--version-number") } : { agentId };
|
|
76713
77337
|
}
|
|
@@ -76773,7 +77397,7 @@ async function runAgentsList(cfg) {
|
|
|
76773
77397
|
}
|
|
76774
77398
|
}
|
|
76775
77399
|
async function runAgentsGet(cfg) {
|
|
76776
|
-
const agentId = requireId(cfg.id,
|
|
77400
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
76777
77401
|
const ref2 = agentRef(agentId, cfg.version);
|
|
76778
77402
|
const [res, cliScopes] = await Promise.all([
|
|
76779
77403
|
getAgent(cfg.client, ref2, { signal: cfg.signal }),
|
|
@@ -76794,7 +77418,7 @@ async function tryFetchCliScopes(client, agentId, signal) {
|
|
|
76794
77418
|
}
|
|
76795
77419
|
}
|
|
76796
77420
|
async function runAgentsVersions(cfg) {
|
|
76797
|
-
const agentId = requireId(cfg.id,
|
|
77421
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
76798
77422
|
writeJSONIndented(
|
|
76799
77423
|
cfg.out,
|
|
76800
77424
|
await listAgentVersions(cfg.client, { agentId }, { signal: cfg.signal })
|
|
@@ -76815,7 +77439,7 @@ async function runAgentsMcpServers(cfg) {
|
|
|
76815
77439
|
`);
|
|
76816
77440
|
}
|
|
76817
77441
|
async function runAgentsCliScopes(cfg) {
|
|
76818
|
-
const agentId = requireId(cfg.id,
|
|
77442
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
76819
77443
|
const res = await getAgentCliScopes(cfg.client, { agentId }, { signal: cfg.signal });
|
|
76820
77444
|
if (cfg.asJson) {
|
|
76821
77445
|
writeJSONIndented(cfg.out, res);
|
|
@@ -76838,7 +77462,7 @@ async function runAgentsScopeRegistry(cfg) {
|
|
|
76838
77462
|
`);
|
|
76839
77463
|
}
|
|
76840
77464
|
async function runAgentsTestLink(cfg) {
|
|
76841
|
-
const agentId = requireId(cfg.id,
|
|
77465
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
76842
77466
|
const url = resolveTestChatLink({
|
|
76843
77467
|
client: cfg.client,
|
|
76844
77468
|
agentId,
|
|
@@ -76867,7 +77491,7 @@ async function runAgentsTestLink(cfg) {
|
|
|
76867
77491
|
}
|
|
76868
77492
|
}
|
|
76869
77493
|
async function runAgentsRefresh(cfg) {
|
|
76870
|
-
const agentId = requireId(cfg.id,
|
|
77494
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
76871
77495
|
const hasPlan = (cfg.plan ?? "").trim() !== "";
|
|
76872
77496
|
if (cfg.apply === true && !hasPlan) {
|
|
76873
77497
|
throw new Error(
|
|
@@ -76987,7 +77611,7 @@ Applied. ${agentId} (base v${version}) \u2192 v${res?.version ?? "?"}.
|
|
|
76987
77611
|
}
|
|
76988
77612
|
}
|
|
76989
77613
|
async function runAgentsPull(cfg) {
|
|
76990
|
-
const agentId = requireId(cfg.id,
|
|
77614
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
76991
77615
|
const agent = await getAgent(cfg.client, agentRef(agentId, cfg.version), {
|
|
76992
77616
|
signal: cfg.signal
|
|
76993
77617
|
});
|
|
@@ -77025,7 +77649,7 @@ async function runAgentsPull(cfg) {
|
|
|
77025
77649
|
);
|
|
77026
77650
|
}
|
|
77027
77651
|
async function runAgentsUpdate(cfg) {
|
|
77028
|
-
const agentId = requireId(cfg.id,
|
|
77652
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
77029
77653
|
const toggleCli = cfg.exposeCli !== void 0 ? parseExposureToggle("cli", cfg.exposeCli) : void 0;
|
|
77030
77654
|
const removeSdk = cfg.exposeSdk !== void 0 ? requireExposureRemoval("sdk", cfg.exposeSdk) : void 0;
|
|
77031
77655
|
const mcpServers = cfg.mcpServers !== void 0 ? parseMcpServers(cfg.mcpServers) : void 0;
|
|
@@ -77220,7 +77844,7 @@ function reachMessage(kind, agentId, value) {
|
|
|
77220
77844
|
`;
|
|
77221
77845
|
}
|
|
77222
77846
|
async function runReachFlip(cfg, kind, write2) {
|
|
77223
|
-
const agentId = requireId(cfg.id,
|
|
77847
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
77224
77848
|
const value = requireBool(cfg.value, "--value");
|
|
77225
77849
|
const serverKey = cfg.client.serverKey;
|
|
77226
77850
|
const action = {
|
|
@@ -77267,7 +77891,7 @@ async function runAgentsSetGlobal(cfg) {
|
|
|
77267
77891
|
);
|
|
77268
77892
|
}
|
|
77269
77893
|
async function runAgentsSetVisibility(cfg) {
|
|
77270
|
-
const agentId = requireId(cfg.id,
|
|
77894
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
77271
77895
|
const everyone = cfg.everyone === true;
|
|
77272
77896
|
const grants = cfg.grant ?? [];
|
|
77273
77897
|
if (everyone && grants.length > 0) {
|
|
@@ -77327,7 +77951,7 @@ function describeVisibility(visibility) {
|
|
|
77327
77951
|
return `restricted to ${formatVisibilityGrants(visibility.grants)}`;
|
|
77328
77952
|
}
|
|
77329
77953
|
async function runAgentsArchive(cfg) {
|
|
77330
|
-
const agentId = requireId(cfg.id,
|
|
77954
|
+
const agentId = requireId(cfg.id, AGENT_ID2);
|
|
77331
77955
|
const serverKey = cfg.client.serverKey;
|
|
77332
77956
|
const action = {
|
|
77333
77957
|
resource: "managed-agent",
|
|
@@ -77703,6 +78327,7 @@ function registerAgentsCommand(root) {
|
|
|
77703
78327
|
}
|
|
77704
78328
|
});
|
|
77705
78329
|
withTarget(archive);
|
|
78330
|
+
registerAgentPinCommands(agents);
|
|
77706
78331
|
registerAgentStudioCommands(agents);
|
|
77707
78332
|
registerAgentSkillsCommand(agents);
|
|
77708
78333
|
}
|
package/dist/scopes-map.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"cliVersion": "1.6.
|
|
3
|
+
"cliVersion": "1.6.166",
|
|
4
4
|
"_contract": "Resolve a command path through `aliases` (canonical prefix + alias segment -> canonical path) before any lookup. A path listed in `pendingCommands`, or whose first segment is a key of `pending`, is UNDECIDED, not unrestricted: fail closed or skip it, and never read a path's absence from `commands` as 'no requirement'. An entry carrying `denied` is UNAVAILABLE to any AGENT-SCOPED credential — one whose `scopes` claim carries at least one `resource:action` entry; a claim that is absent, empty, capability-only or wholly unparseable is UNRESTRICTED, and this mark does not apply to it — whatever else that entry says: it is not a ceiling and it is not always-available, its `denied.reason` says why and `denied.until` names the ticket that lifts it, and the absence of `requires` on it must NEVER be read as 'no requirement'. Only an entry in `commands` with an empty `requires` means always-available.",
|
|
5
5
|
"commands": {
|
|
6
6
|
"login": {
|
|
@@ -1047,6 +1047,22 @@
|
|
|
1047
1047
|
}
|
|
1048
1048
|
]
|
|
1049
1049
|
},
|
|
1050
|
+
"reports validate-column": {
|
|
1051
|
+
"requires": [
|
|
1052
|
+
{
|
|
1053
|
+
"resource": "tables",
|
|
1054
|
+
"action": "read"
|
|
1055
|
+
}
|
|
1056
|
+
]
|
|
1057
|
+
},
|
|
1058
|
+
"reports add-column": {
|
|
1059
|
+
"requires": [
|
|
1060
|
+
{
|
|
1061
|
+
"resource": "tables",
|
|
1062
|
+
"action": "write"
|
|
1063
|
+
}
|
|
1064
|
+
]
|
|
1065
|
+
},
|
|
1050
1066
|
"budget-builder build": {
|
|
1051
1067
|
"requires": [
|
|
1052
1068
|
{
|
|
@@ -4969,6 +4985,30 @@
|
|
|
4969
4985
|
}
|
|
4970
4986
|
]
|
|
4971
4987
|
},
|
|
4988
|
+
"agents served": {
|
|
4989
|
+
"requires": [
|
|
4990
|
+
{
|
|
4991
|
+
"resource": "managed-agents",
|
|
4992
|
+
"action": "read"
|
|
4993
|
+
}
|
|
4994
|
+
]
|
|
4995
|
+
},
|
|
4996
|
+
"agents pin": {
|
|
4997
|
+
"requires": [
|
|
4998
|
+
{
|
|
4999
|
+
"resource": "managed-agents",
|
|
5000
|
+
"action": "destructive"
|
|
5001
|
+
}
|
|
5002
|
+
]
|
|
5003
|
+
},
|
|
5004
|
+
"agents unpin": {
|
|
5005
|
+
"requires": [
|
|
5006
|
+
{
|
|
5007
|
+
"resource": "managed-agents",
|
|
5008
|
+
"action": "destructive"
|
|
5009
|
+
}
|
|
5010
|
+
]
|
|
5011
|
+
},
|
|
4972
5012
|
"agents catalog": {
|
|
4973
5013
|
"requires": [
|
|
4974
5014
|
{
|
|
@@ -273,6 +273,7 @@ area's gotchas.
|
|
|
273
273
|
from the widget's own settings only.
|
|
274
274
|
- `storyboards list|get|slides|prepare|delete`
|
|
275
275
|
- `reports list|get|clone|update|delete` — `update <id>` edits a Financials (Report Builder) report: `--payload-file` (payload keys, or `reports get --json` output), `--name`, `--option k=v`, `--date-picker-preset`; dry-run unless `--confirm`
|
|
276
|
+
- `reports validate-column|add-column <id> --name <s> --formula <s> --position <col> [--before] [--format <f>]` — custom (calculated) column on a Financials report, stored in `payload.calculatedCols`. Formula refs and `--position` are COLUMN names with nested levels joined by `/` (`[01/31/2026/Actuals]-[01/31/2026/Budget]`); a rejected column lists the valid names. `--before` = left of the anchor (default right). `add-column` is dry-run unless `--confirm`. Budget reports have no custom columns (their columns come from the budget year/cycle)
|
|
276
277
|
- `extables list|get|clone|delete`
|
|
277
278
|
|
|
278
279
|
**Budget Builder (Budget management templates)** → [reference/budget-builder.md](reference/budget-builder.md)
|
|
@@ -111,7 +111,10 @@ dr reports get <new-id> --server <key> --json # round-trip check; echo settings
|
|
|
111
111
|
- `budget_info.scenario` wire values are lowercase: `budget` | `forecast` |
|
|
112
112
|
`manual` (the UI's "leave empty / manual" option = `manual`).
|
|
113
113
|
- Budget COLUMNS are auto-generated server-side from `budget_info` +
|
|
114
|
-
`date_picker` — leave `cols: []`; only rows are authored.
|
|
114
|
+
`date_picker` — leave `cols: []`; only rows are authored. There are no
|
|
115
|
+
custom columns on a budget template: the server ignores `calculatedCols`
|
|
116
|
+
for `type: "budget"` and the web Budget Builder has no control for them.
|
|
117
|
+
Custom columns exist on Financials reports only (`dr reports add-column`).
|
|
115
118
|
- Row HIERARCHY is encoded by nesting, NOT by flat array order: the child
|
|
116
119
|
field node goes in the parent node's `children: [...]` array (recursive).
|
|
117
120
|
Flat siblings in `rows[]` render as independent top-level row fields.
|
|
@@ -64,6 +64,9 @@ delete-all`; `filebox archive` and `filebox data-query clear`;
|
|
|
64
64
|
`agents archive` and the three agent-reach flips `agents set-listed` /
|
|
65
65
|
`set-global` / `set-visibility` (each changes who can reach the agent for every
|
|
66
66
|
organization at once);
|
|
67
|
+
`agents pin` / `unpin` (decide which agent version customers get on a server; one
|
|
68
|
+
run with `--production` covers every production server, so relay the plan's
|
|
69
|
+
per-server lines — they are also the undo);
|
|
67
70
|
`error-enrichments import` (replaces every rule on the server, zeroes all
|
|
68
71
|
hits/feedback counters, and reassigns every rule id); and
|
|
69
72
|
`dashboards push --override` / `filebox push --override`. `import` is **not** an
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datarails-ai/dr-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.166",
|
|
4
4
|
"description": "Datarails CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@datarails-ai/dr-appspace-cli": "0.12.
|
|
20
|
+
"@datarails-ai/dr-appspace-cli": "0.12.33",
|
|
21
21
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
22
22
|
"@napi-rs/keyring": "^1.3.0",
|
|
23
23
|
"commander": "^12.1.0",
|