@erdoai/cli 0.133.0 → 0.135.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.
Files changed (2) hide show
  1. package/dist/index.js +97 -0
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1668,6 +1668,9 @@ var ErdoClient = class {
1668
1668
  `/v1/event-pipelines/${encodeURIComponent(slug)}/executions${qs ? `?${qs}` : ""}`
1669
1669
  );
1670
1670
  }
1671
+ markEventPipelineExecutionsHandled(slug, body) {
1672
+ return this.request("POST", `/v1/event-pipelines/${encodeURIComponent(slug)}/executions/handled`, body);
1673
+ }
1671
1674
  // --- knowledge ---
1672
1675
  getKnowledge(id) {
1673
1676
  return this.request("GET", `/v1/knowledge/${encodeURIComponent(id)}`);
@@ -1819,6 +1822,16 @@ var ErdoClient = class {
1819
1822
  input
1820
1823
  );
1821
1824
  }
1825
+ listApprovalPolicies(action_key) {
1826
+ const qs = action_key ? `?action_key=${encodeURIComponent(action_key)}` : "";
1827
+ return this.request("GET", `/v1/approval-policies${qs}`);
1828
+ }
1829
+ createApprovalPolicy(input) {
1830
+ return this.request("POST", "/v1/approval-policies", input);
1831
+ }
1832
+ deleteApprovalPolicy(id) {
1833
+ return this.request("DELETE", `/v1/approval-policies/${encodeURIComponent(id)}`);
1834
+ }
1822
1835
  // --- decisions ---
1823
1836
  // The decision record: what the organization committed to, what executed, and
1824
1837
  // what the evidence said afterwards. Referenced by slug, never UUID.
@@ -2329,6 +2342,24 @@ function timedOutMessage(threadID) {
2329
2342
  function print(value) {
2330
2343
  console.log(JSON.stringify(value, null, 2));
2331
2344
  }
2345
+ function policyValue(value) {
2346
+ if (Array.isArray(value)) return value.map(policyValue);
2347
+ if (value && typeof value === "object") {
2348
+ return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, policyValue(item)]));
2349
+ }
2350
+ return value;
2351
+ }
2352
+ function effectivePolicyMismatch(requested, effective) {
2353
+ const differences = [];
2354
+ if (effective.action_key !== requested.action_key) differences.push("action_key");
2355
+ if (effective.scope_type !== requested.scope_type || (effective.scope_id ?? null) !== (requested.scope_id ?? null)) differences.push("scope");
2356
+ if (effective.decision !== requested.decision) differences.push("decision");
2357
+ if (JSON.stringify(policyValue(effective.parameter_constraints ?? null)) !== JSON.stringify(policyValue(requested.parameter_constraints ?? null))) differences.push("parameter_constraints");
2358
+ const requestedExpiry = requested.expires_at ? Date.parse(requested.expires_at) : null;
2359
+ const effectiveExpiry = effective.expires_at ? Date.parse(effective.expires_at) : null;
2360
+ if (requestedExpiry !== effectiveExpiry) differences.push("expires_at");
2361
+ return differences.length ? differences.join(", ") : void 0;
2362
+ }
2332
2363
  function formatNumber(n) {
2333
2364
  if (!Number.isFinite(n)) return String(n);
2334
2365
  if (Number.isInteger(n)) return n.toLocaleString("en-US");
@@ -4777,6 +4808,61 @@ approvalsCmd.command("list").description("List approval requests, optionally fil
4777
4808
  }
4778
4809
  }
4779
4810
  );
4811
+ var policiesCmd = approvalsCmd.command("policies").description("List and manage standing approval policies");
4812
+ 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) => {
4813
+ try {
4814
+ const res = await new ErdoClient().listApprovalPolicies(opts.actionKey);
4815
+ if (opts.json) return print(res);
4816
+ for (const p of res.policies) {
4817
+ 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}` : ""}`);
4818
+ }
4819
+ } catch (e) {
4820
+ fail(e);
4821
+ }
4822
+ });
4823
+ 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) => {
4824
+ try {
4825
+ if (!["organization", "job", "workstream", "user"].includes(opts.scopeType)) throw new Error("--scope-type must be organization, job, workstream, or user");
4826
+ if (!["always_approve", "always_require"].includes(opts.decision)) throw new Error("--decision must be always_approve or always_require");
4827
+ let constraints;
4828
+ if (opts.constraints !== void 0) {
4829
+ try {
4830
+ constraints = JSON.parse(opts.constraints);
4831
+ } catch {
4832
+ throw new Error("--constraints must be a JSON object");
4833
+ }
4834
+ if (constraints === null || Array.isArray(constraints) || typeof constraints !== "object") throw new Error("--constraints must be a JSON object");
4835
+ }
4836
+ const requested = {
4837
+ action_key: opts.actionKey,
4838
+ scope_type: opts.scopeType,
4839
+ ...opts.scopeId ? { scope_id: opts.scopeId } : {},
4840
+ decision: opts.decision,
4841
+ ...constraints !== void 0 ? { parameter_constraints: constraints } : {},
4842
+ ...opts.expiresAt ? { expires_at: opts.expiresAt } : {}
4843
+ };
4844
+ const res = await new ErdoClient().createApprovalPolicy(requested);
4845
+ const mismatch = effectivePolicyMismatch(requested, res.policy);
4846
+ if (opts.json) print(res);
4847
+ if (mismatch) {
4848
+ 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}`);
4849
+ process.exitCode = 1;
4850
+ } else if (!opts.json) {
4851
+ console.log(`Effective ${res.policy.decision} policy ${res.policy.id}`);
4852
+ }
4853
+ } catch (e) {
4854
+ fail(e);
4855
+ }
4856
+ });
4857
+ policiesCmd.command("delete <id>").description("Delete a standing approval policy").action(async (id) => {
4858
+ try {
4859
+ const result = await new ErdoClient().deleteApprovalPolicy(id);
4860
+ if (!result.success) throw new Error(`Policy ${id} was not deleted`);
4861
+ console.log(`Deleted policy ${id}`);
4862
+ } catch (e) {
4863
+ fail(e);
4864
+ }
4865
+ });
4780
4866
  function executionVerdict(execution) {
4781
4867
  switch (execution.state) {
4782
4868
  case "succeeded":
@@ -8583,6 +8669,17 @@ pipelinesCmd.command("executions <slug>").description("Show an event pipeline's
8583
8669
  fail(e);
8584
8670
  }
8585
8671
  });
8672
+ pipelinesCmd.command("mark-handled <slug>").description("Record that failed executions have been dealt with").requiredOption("--note <text>", "explanation, up to 500 characters").option("--through <iso>", "inclusive RFC3339 receipt-time cutoff (default: now)").option("--execution <id>", "execution UUID; repeat to select specific executions instead of a cutoff", (id, ids) => [...ids, id], []).action(async (slug, opts) => {
8673
+ try {
8674
+ print(await new ErdoClient().markEventPipelineExecutionsHandled(slug, {
8675
+ note: opts.note,
8676
+ through: opts.through,
8677
+ execution_ids: opts.execution.length ? opts.execution : void 0
8678
+ }));
8679
+ } catch (e) {
8680
+ fail(e);
8681
+ }
8682
+ });
8586
8683
  var knowledgeCmd = program.command("knowledge").description("Knowledge objects");
8587
8684
  knowledgeCmd.command("create").description("Create a Knowledge object").requiredOption("--title <title>", "title").requiredOption("--body <body>", "body text, or @file").option("--description <description>", "short description").option("--type <type>", "snippet or skill", "snippet").option("--category <category>", "category").option("--tags <tags>", "comma-separated tags").option("--agent <name>", "name of the agent making the write").action(async (opts) => {
8588
8685
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erdoai/cli",
3
- "version": "0.133.0",
4
- "description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
3
+ "version": "0.135.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"