@erdoai/cli 0.133.0 → 0.134.0
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/index.js +83 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1819,6 +1819,16 @@ var ErdoClient = class {
|
|
|
1819
1819
|
input
|
|
1820
1820
|
);
|
|
1821
1821
|
}
|
|
1822
|
+
listApprovalPolicies(action_key) {
|
|
1823
|
+
const qs = action_key ? `?action_key=${encodeURIComponent(action_key)}` : "";
|
|
1824
|
+
return this.request("GET", `/v1/approval-policies${qs}`);
|
|
1825
|
+
}
|
|
1826
|
+
createApprovalPolicy(input) {
|
|
1827
|
+
return this.request("POST", "/v1/approval-policies", input);
|
|
1828
|
+
}
|
|
1829
|
+
deleteApprovalPolicy(id) {
|
|
1830
|
+
return this.request("DELETE", `/v1/approval-policies/${encodeURIComponent(id)}`);
|
|
1831
|
+
}
|
|
1822
1832
|
// --- decisions ---
|
|
1823
1833
|
// The decision record: what the organization committed to, what executed, and
|
|
1824
1834
|
// what the evidence said afterwards. Referenced by slug, never UUID.
|
|
@@ -2329,6 +2339,24 @@ function timedOutMessage(threadID) {
|
|
|
2329
2339
|
function print(value) {
|
|
2330
2340
|
console.log(JSON.stringify(value, null, 2));
|
|
2331
2341
|
}
|
|
2342
|
+
function policyValue(value) {
|
|
2343
|
+
if (Array.isArray(value)) return value.map(policyValue);
|
|
2344
|
+
if (value && typeof value === "object") {
|
|
2345
|
+
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, policyValue(item)]));
|
|
2346
|
+
}
|
|
2347
|
+
return value;
|
|
2348
|
+
}
|
|
2349
|
+
function effectivePolicyMismatch(requested, effective) {
|
|
2350
|
+
const differences = [];
|
|
2351
|
+
if (effective.action_key !== requested.action_key) differences.push("action_key");
|
|
2352
|
+
if (effective.scope_type !== requested.scope_type || (effective.scope_id ?? null) !== (requested.scope_id ?? null)) differences.push("scope");
|
|
2353
|
+
if (effective.decision !== requested.decision) differences.push("decision");
|
|
2354
|
+
if (JSON.stringify(policyValue(effective.parameter_constraints ?? null)) !== JSON.stringify(policyValue(requested.parameter_constraints ?? null))) differences.push("parameter_constraints");
|
|
2355
|
+
const requestedExpiry = requested.expires_at ? Date.parse(requested.expires_at) : null;
|
|
2356
|
+
const effectiveExpiry = effective.expires_at ? Date.parse(effective.expires_at) : null;
|
|
2357
|
+
if (requestedExpiry !== effectiveExpiry) differences.push("expires_at");
|
|
2358
|
+
return differences.length ? differences.join(", ") : void 0;
|
|
2359
|
+
}
|
|
2332
2360
|
function formatNumber(n) {
|
|
2333
2361
|
if (!Number.isFinite(n)) return String(n);
|
|
2334
2362
|
if (Number.isInteger(n)) return n.toLocaleString("en-US");
|
|
@@ -4777,6 +4805,61 @@ approvalsCmd.command("list").description("List approval requests, optionally fil
|
|
|
4777
4805
|
}
|
|
4778
4806
|
}
|
|
4779
4807
|
);
|
|
4808
|
+
var policiesCmd = approvalsCmd.command("policies").description("List and manage standing approval policies");
|
|
4809
|
+
policiesCmd.command("list").description("List standing approval policies").option("--action-key <key>", "only policies for this action").option("--json", "output raw JSON").action(async (opts) => {
|
|
4810
|
+
try {
|
|
4811
|
+
const res = await new ErdoClient().listApprovalPolicies(opts.actionKey);
|
|
4812
|
+
if (opts.json) return print(res);
|
|
4813
|
+
for (const p of res.policies) {
|
|
4814
|
+
console.log(`${p.id} ${p.decision} ${p.action_key} ${p.scope_type}${p.scope_id ? `/${p.scope_id}` : ""}${p.expires_at ? ` expires ${p.expires_at}` : ""}`);
|
|
4815
|
+
}
|
|
4816
|
+
} catch (e) {
|
|
4817
|
+
fail(e);
|
|
4818
|
+
}
|
|
4819
|
+
});
|
|
4820
|
+
policiesCmd.command("create").description("Create a standing approval policy; this does not execute the action").requiredOption("--action-key <key>", "exact action key").requiredOption("--scope-type <type>", "organization | job | workstream | user").option("--scope-id <id>", "job, workstream, or user UUID").requiredOption("--decision <decision>", "always_approve | always_require").option("--constraints <json>", "parameter constraints JSON object").option("--expires-at <timestamp>", "future RFC3339 expiry").option("--json", "output raw JSON").action(async (opts) => {
|
|
4821
|
+
try {
|
|
4822
|
+
if (!["organization", "job", "workstream", "user"].includes(opts.scopeType)) throw new Error("--scope-type must be organization, job, workstream, or user");
|
|
4823
|
+
if (!["always_approve", "always_require"].includes(opts.decision)) throw new Error("--decision must be always_approve or always_require");
|
|
4824
|
+
let constraints;
|
|
4825
|
+
if (opts.constraints !== void 0) {
|
|
4826
|
+
try {
|
|
4827
|
+
constraints = JSON.parse(opts.constraints);
|
|
4828
|
+
} catch {
|
|
4829
|
+
throw new Error("--constraints must be a JSON object");
|
|
4830
|
+
}
|
|
4831
|
+
if (constraints === null || Array.isArray(constraints) || typeof constraints !== "object") throw new Error("--constraints must be a JSON object");
|
|
4832
|
+
}
|
|
4833
|
+
const requested = {
|
|
4834
|
+
action_key: opts.actionKey,
|
|
4835
|
+
scope_type: opts.scopeType,
|
|
4836
|
+
...opts.scopeId ? { scope_id: opts.scopeId } : {},
|
|
4837
|
+
decision: opts.decision,
|
|
4838
|
+
...constraints !== void 0 ? { parameter_constraints: constraints } : {},
|
|
4839
|
+
...opts.expiresAt ? { expires_at: opts.expiresAt } : {}
|
|
4840
|
+
};
|
|
4841
|
+
const res = await new ErdoClient().createApprovalPolicy(requested);
|
|
4842
|
+
const mismatch = effectivePolicyMismatch(requested, res.policy);
|
|
4843
|
+
if (opts.json) print(res);
|
|
4844
|
+
if (mismatch) {
|
|
4845
|
+
console.error(`Effective approval policy ${res.policy.id} does not grant the requested authority (${mismatch}). The existing policy was kept; inspect it with: erdo approvals policies list --action-key ${opts.actionKey}`);
|
|
4846
|
+
process.exitCode = 1;
|
|
4847
|
+
} else if (!opts.json) {
|
|
4848
|
+
console.log(`Effective ${res.policy.decision} policy ${res.policy.id}`);
|
|
4849
|
+
}
|
|
4850
|
+
} catch (e) {
|
|
4851
|
+
fail(e);
|
|
4852
|
+
}
|
|
4853
|
+
});
|
|
4854
|
+
policiesCmd.command("delete <id>").description("Delete a standing approval policy").action(async (id) => {
|
|
4855
|
+
try {
|
|
4856
|
+
const result = await new ErdoClient().deleteApprovalPolicy(id);
|
|
4857
|
+
if (!result.success) throw new Error(`Policy ${id} was not deleted`);
|
|
4858
|
+
console.log(`Deleted policy ${id}`);
|
|
4859
|
+
} catch (e) {
|
|
4860
|
+
fail(e);
|
|
4861
|
+
}
|
|
4862
|
+
});
|
|
4780
4863
|
function executionVerdict(execution) {
|
|
4781
4864
|
switch (execution.state) {
|
|
4782
4865
|
case "succeeded":
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erdoai/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Erdo CLI
|
|
3
|
+
"version": "0.134.0",
|
|
4
|
+
"description": "Erdo CLI \u2014 drive datasets, pages, and evals from the terminal or CI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"erdo": "dist/index.js"
|