@erdoai/cli 0.132.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 +89 -3
- 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":
|
|
@@ -8697,7 +8780,7 @@ var autoCmd = program.command("automations").description("Scheduled automations
|
|
|
8697
8780
|
autoCmd.command("list").description("List automations").action(async () => {
|
|
8698
8781
|
try {
|
|
8699
8782
|
const { heartbeats } = await new ErdoClient().listHeartbeats();
|
|
8700
|
-
for (const h of heartbeats) console.log(`${h.id} ${h.state} ${h.name}`);
|
|
8783
|
+
for (const h of heartbeats) console.log(`${h.id} ${h.state} ${h.name} ${h.kind === "script" ? "script" : h.thread_id ? `thread:${h.thread_id}` : h.conversation ?? "new"}${h.persona_id ? ` persona:${h.persona_id}` : ""}`);
|
|
8701
8784
|
} catch (e) {
|
|
8702
8785
|
fail(e);
|
|
8703
8786
|
}
|
|
@@ -8723,7 +8806,7 @@ autoCmd.command("run <id>").description("Trigger an automation now").action(asyn
|
|
|
8723
8806
|
autoCmd.command("update <id>").description("Edit an automation \u2014 rename, reschedule, or replace its script/instructions").option("--name <name>", "New name").option("--description <text>", "New description").option("--instructions <text>", "Replacement instructions (agent automation)").addOption(new Option2("--agent-key <key>", "Executor for an agent automation; omit to keep current").choices(["erdo.heartbeat-executor", "erdo.workflow-executor"])).option("--script-js <js>", "Replacement script body (scripted automation)").option(
|
|
8724
8807
|
"--script-file <path>",
|
|
8725
8808
|
"Read the replacement script body from a file (scripted automation)"
|
|
8726
|
-
).option("--interval <minutes>", "New interval in minutes (minimum 5)", (v) => parseInt(v, 10)).option("--timezone <tz>", "New scheduling timezone, e.g. America/New_York").action(
|
|
8809
|
+
).addOption(new Option2("--conversation <target>", "Target 'new' (fresh thread) or 'direct' conversation").choices(["new", "direct"])).option("--thread-id <id>", "Post each run into an existing conversation UUID").option("--persona-id <id>", "Org-enabled persona UUID to speak as").option("--interval <minutes>", "New interval in minutes (minimum 5)", (v) => parseInt(v, 10)).option("--timezone <tz>", "New scheduling timezone, e.g. America/New_York").action(
|
|
8727
8810
|
async (id, opts) => {
|
|
8728
8811
|
try {
|
|
8729
8812
|
if (opts.scriptJs && opts.scriptFile) {
|
|
@@ -8738,7 +8821,10 @@ autoCmd.command("update <id>").description("Edit an automation \u2014 rename, re
|
|
|
8738
8821
|
agent_key: opts.agentKey,
|
|
8739
8822
|
script_js: scriptJs,
|
|
8740
8823
|
interval_minutes: opts.interval,
|
|
8741
|
-
timezone: opts.timezone
|
|
8824
|
+
timezone: opts.timezone,
|
|
8825
|
+
conversation: opts.conversation,
|
|
8826
|
+
thread_id: opts.threadId,
|
|
8827
|
+
persona_id: opts.personaId
|
|
8742
8828
|
};
|
|
8743
8829
|
if (Object.values(body).every((v) => v === void 0)) {
|
|
8744
8830
|
fail(new Error("nothing to update \u2014 pass at least one field to change"));
|
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"
|