@haven_ai/cli 0.0.0-dev.202610061536.ad3de49 → 0.0.0-dev.202610071154.39eddce

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.d.cts CHANGED
@@ -117,6 +117,14 @@ interface ParsedArgs {
117
117
  hashes: boolean;
118
118
  version: boolean;
119
119
  yes: boolean;
120
+ /**
121
+ * #3729: override for `agents revoke` — revoke the agent even though a
122
+ * budget delegation is still live on-chain (pending, active or replaced),
123
+ * saying so out loud instead of silently leaving the budget redeemable.
124
+ * Without it the command refuses and lists the exact `haven budget
125
+ * revoke` command for each live row.
126
+ */
127
+ keepBudget: boolean;
120
128
  api?: string;
121
129
  email?: string;
122
130
  safe?: string;
package/dist/index.d.ts CHANGED
@@ -117,6 +117,14 @@ interface ParsedArgs {
117
117
  hashes: boolean;
118
118
  version: boolean;
119
119
  yes: boolean;
120
+ /**
121
+ * #3729: override for `agents revoke` — revoke the agent even though a
122
+ * budget delegation is still live on-chain (pending, active or replaced),
123
+ * saying so out loud instead of silently leaving the budget redeemable.
124
+ * Without it the command refuses and lists the exact `haven budget
125
+ * revoke` command for each live row.
126
+ */
127
+ keepBudget: boolean;
120
128
  api?: string;
121
129
  email?: string;
122
130
  safe?: string;
package/dist/index.js CHANGED
@@ -37,7 +37,8 @@ function parseArgs(argv) {
37
37
  noWait: false,
38
38
  run: false,
39
39
  wait: false,
40
- hashes: false
40
+ hashes: false,
41
+ keepBudget: false
41
42
  };
42
43
  for (let i = 0; i < argv.length; i += 1) {
43
44
  const arg = argv[i];
@@ -45,6 +46,7 @@ function parseArgs(argv) {
45
46
  else if (arg === "--help" || arg === "-h") flags.help = true;
46
47
  else if (arg === "--version" || arg === "-v") flags.version = true;
47
48
  else if (arg === "--yes" || arg === "-y") flags.yes = true;
49
+ else if (arg === "--keep-budget") flags.keepBudget = true;
48
50
  else if (arg === "--no-wait") flags.noWait = true;
49
51
  else if (arg === "--run") flags.run = true;
50
52
  else if (arg === "--wait") flags.wait = true;
@@ -155,7 +157,8 @@ function helpText() {
155
157
  "Manage (backend-only \u2014 no on-chain signing):",
156
158
  " agents pause <id> Stop the agent spending; keeps its budget",
157
159
  " agents resume <id> Let it spend again",
158
- " agents revoke <id> --yes Permanently revoke an agent",
160
+ " agents revoke <id> --yes Permanently revoke an agent. Refuses while a",
161
+ " budget is still live on-chain unless --keep-budget",
159
162
  " agents rotate-key <id> Issue a new API key (shown once)",
160
163
  " agents rename <id> <name>",
161
164
  " wallets rename <id> <name>",
@@ -3143,7 +3146,7 @@ async function checkTextForSecrets(text, deps) {
3143
3146
 
3144
3147
  // src/commands.ts
3145
3148
  var DEFAULT_API = "https://havenbackend-production-8a00.up.railway.app";
3146
- var CLI_VERSION = "0.0.0-dev.202610061536.ad3de49";
3149
+ var CLI_VERSION = "0.0.0-dev.202610071154.39eddce";
3147
3150
  var CLI_CLIENT_IDENTITY = `@haven_ai/cli/${CLI_VERSION}`;
3148
3151
  function accountAddressOf(s) {
3149
3152
  return s.account_address ?? "";
@@ -3697,12 +3700,6 @@ async function cmdBudgetRevoke(args, d) {
3697
3700
  if (row.status === "revoked") {
3698
3701
  throw new HavenCliError(`Delegation ${hash} is already revoked.`, EXIT.refused);
3699
3702
  }
3700
- if (row.status === "replaced") {
3701
- throw new HavenCliError(
3702
- `Delegation ${hash} was already replaced by a newer grant \u2014 nothing to revoke.`,
3703
- EXIT.refused
3704
- );
3705
- }
3706
3703
  const prepared = await api.post(`/agents/${id}/delegations/${hash}/revoke`, {});
3707
3704
  const revokeUrl = prepared.revocation_url;
3708
3705
  if (!revokeUrl) {
@@ -3758,7 +3755,7 @@ async function cmdAgentLifecycle(args, d, action) {
3758
3755
  }
3759
3756
  async function cmdAgentRevoke(args, d) {
3760
3757
  const id = args.positionals[0];
3761
- if (!id) throw new UsageError("Usage: haven agents revoke <id> --yes");
3758
+ if (!id) throw new UsageError("Usage: haven agents revoke <id> [--keep-budget] --yes");
3762
3759
  if (!args.flags.yes) {
3763
3760
  throw new UsageError(
3764
3761
  `This permanently revokes agent ${id}.`,
@@ -3766,12 +3763,45 @@ async function cmdAgentRevoke(args, d) {
3766
3763
  );
3767
3764
  }
3768
3765
  const { api } = await authed(args, d);
3766
+ let live = [];
3767
+ try {
3768
+ const agent = await api.get(`/agents/${id}`);
3769
+ if ((agent.live_delegation_count ?? 0) > 0) {
3770
+ const { delegations } = await api.get(`/agents/${id}/delegations`);
3771
+ live = delegations.filter((r) => r.status === "pending" || r.status === "active" || r.status === "replaced");
3772
+ }
3773
+ } catch (err) {
3774
+ d.o.note(`Couldn't check budgets for agent ${id}, so the revocation was not attempted.`);
3775
+ throw err;
3776
+ }
3777
+ if (live.length > 0 && !args.flags.keepBudget) {
3778
+ throw new HavenCliError(
3779
+ `Agent ${id} still has a live on-chain budget. Revoking now would leave it redeemable by the delegate key.`,
3780
+ EXIT.refused,
3781
+ [
3782
+ ...live.map((r) => `haven budget revoke ${id} ${r.delegation_hash} # ${r.status}`),
3783
+ "Remove agent\u2026 in the dashboard ends every live budget with one signature, before revoking.",
3784
+ "To revoke the agent anyway and leave the budget live on-chain, re-run with --keep-budget."
3785
+ ].join("\n"),
3786
+ // The machine branch: a script skips the prose and reads the rows.
3787
+ { live_delegations: live.map(({ delegation_hash, status }) => ({ delegation_hash, status })) }
3788
+ );
3789
+ }
3769
3790
  await api.post(`/agents/${id}/revoke`);
3770
3791
  emit(
3771
3792
  d,
3772
3793
  args.flags.json,
3773
- { ok: true, agent_id: id, status: "revoked" },
3774
- () => `Agent ${id} revoked. To also remove its on-chain allowance, use the dashboard.`
3794
+ live.length > 0 ? {
3795
+ ok: true,
3796
+ agent_id: id,
3797
+ status: "revoked",
3798
+ live_delegations: live.map(({ delegation_hash, status }) => ({ delegation_hash, status }))
3799
+ } : { ok: true, agent_id: id, status: "revoked" },
3800
+ () => live.length > 0 ? [
3801
+ `Agent ${id} revoked.`,
3802
+ `Its budget is still live on-chain: the delegate key can keep spending until each delegation is revoked.`,
3803
+ ...live.map((r) => `haven budget revoke ${id} ${r.delegation_hash} # ${r.status}`)
3804
+ ].join("\n") : `Agent ${id} revoked. To also remove its on-chain allowance, use the dashboard.`
3775
3805
  );
3776
3806
  return EXIT.ok;
3777
3807
  }