@haven_ai/cli 0.8.0-alpha.0 → 0.9.0-alpha.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/cli.cjs CHANGED
@@ -5,6 +5,7 @@ var promises = require('fs/promises');
5
5
  var os = require('os');
6
6
  var path = require('path');
7
7
  var child_process = require('child_process');
8
+ var crypto = require('crypto');
8
9
  var readline = require('readline');
9
10
 
10
11
  // src/args.ts
@@ -40,7 +41,8 @@ function parseArgs(argv) {
40
41
  noWait: false,
41
42
  run: false,
42
43
  wait: false,
43
- hashes: false
44
+ hashes: false,
45
+ keepBudget: false
44
46
  };
45
47
  for (let i = 0; i < argv.length; i += 1) {
46
48
  const arg = argv[i];
@@ -48,6 +50,7 @@ function parseArgs(argv) {
48
50
  else if (arg === "--help" || arg === "-h") flags.help = true;
49
51
  else if (arg === "--version" || arg === "-v") flags.version = true;
50
52
  else if (arg === "--yes" || arg === "-y") flags.yes = true;
53
+ else if (arg === "--keep-budget") flags.keepBudget = true;
51
54
  else if (arg === "--no-wait") flags.noWait = true;
52
55
  else if (arg === "--run") flags.run = true;
53
56
  else if (arg === "--wait") flags.wait = true;
@@ -149,10 +152,19 @@ function helpText() {
149
152
  " catalog list List payable services",
150
153
  " contacts list List your address book",
151
154
  "",
155
+ "Feedback:",
156
+ ' feedback submit "<text>" Send feedback or a bug report. Needs',
157
+ " `haven login`. It refuses to send text that",
158
+ " looks like a credential, but cannot catch",
159
+ " every secret shape \u2014 never include one.",
160
+ "",
152
161
  "Manage (backend-only \u2014 no on-chain signing):",
153
- " agents pause <id> Stop the agent spending; keeps its budget",
154
- " agents resume <id> Let it spend again",
155
- " agents revoke <id> --yes Permanently revoke an agent",
162
+ " agents pause <id> Block payments through Haven; the budget",
163
+ " stays live on-chain (end it: budget revoke)",
164
+ " agents resume <id> Allow payments through Haven again",
165
+ " agents revoke <id> --yes Retire the agent in Haven (terminal). Ends no",
166
+ " budget on-chain: run budget revoke first; refuses",
167
+ " while a budget is still live unless --keep-budget",
156
168
  " agents rotate-key <id> Issue a new API key (shown once)",
157
169
  " agents rename <id> <name>",
158
170
  " wallets rename <id> <name>",
@@ -369,11 +381,20 @@ var UsageError = class extends Error {
369
381
  var HavenCliError = class extends Error {
370
382
  exit;
371
383
  hint;
372
- constructor(message, exit, hint) {
384
+ /**
385
+ * #3597: additional typed fields merged into the `--json` error object
386
+ * (`error.layer`, `error.reason`, …) — for a refusal where the FREE-TEXT
387
+ * hint is not enough to branch on. `feedback submit`'s secret check is the
388
+ * first caller: an agent needs `error.layer` (which of the four checks
389
+ * refused) as a number, not a string it has to parse out of a sentence.
390
+ */
391
+ extra;
392
+ constructor(message, exit, hint, extra) {
373
393
  super(message);
374
394
  this.name = "HavenCliError";
375
395
  this.exit = exit;
376
396
  this.hint = hint;
397
+ this.extra = extra;
377
398
  }
378
399
  };
379
400
  function toFailure(err) {
@@ -382,7 +403,7 @@ function toFailure(err) {
382
403
  }
383
404
  if (err instanceof HavenCliError) {
384
405
  const code = err.exit === EXIT.notAuthenticated ? "not_authenticated" : err.exit === EXIT.refused ? "refused" : err.exit === EXIT.network ? "network" : "failed";
385
- return { code, exit: err.exit, message: err.message, hint: err.hint };
406
+ return { code, exit: err.exit, message: err.message, hint: err.hint, extra: err.extra };
386
407
  }
387
408
  if (err instanceof CliApiError) {
388
409
  if (err.status === 0) {
@@ -432,6 +453,11 @@ function createOutput(json, out, err) {
432
453
  const body = {
433
454
  ok: false,
434
455
  error: {
456
+ // `extra` spreads FIRST (#3597 round 2, N-a): it is per-command
457
+ // (e.g. the secret check's `layer`/`reason`), so it must never be
458
+ // able to clobber the contract fields every command shares —
459
+ // `code`, `message`, `hint` are spread AFTER and always win.
460
+ ...failure.extra ?? {},
435
461
  code: failure.code,
436
462
  message: failure.message,
437
463
  ...failure.hint ? { hint: failure.hint } : {}
@@ -445,7 +471,7 @@ ${failure.hint}` : failure.message);
445
471
  }
446
472
 
447
473
  // src/agent-guidance-text.ts
448
- var HAVEN_AGENT_RUNBOOK_MD = "# Haven for agents \u2014 set your user up to let you pay\n\nYou are an AI agent whose user has no Haven account yet, or has one but has not connected you.\n\n> Links below are paths on this same host: resolve them against the URL you fetched this file from.\n\n## What Haven is, in three sentences\n\nYour user gives you a **budget on their own account** \u2014 not their wallet, and not a key to their funds. The budget is a delegation they sign, enforced on-chain: a payment over it, to the wrong recipient, or past its expiry is refused at execution time, not by a dashboard promise. Haven constructs and relays the payments; you get an agent credential and a signing key made on your machine, and your user can revoke the budget without you and without Haven.\n\n## The sequence\n\nFour of the six steps are your user's \u2014 each needs a human signature or a human decision. The rest is yours. If they already have a funded account, start at step 3.\n\n1. **HUMAN \u2014 create the account.** Name, email, password, then a passkey (Face ID / Touch ID) or a wallet. Never offer to enter any of it: you must not have their password, and the passkey is bound to their device. With a terminal, `<packages.cli.one_liner> login --api <api-url>` afterwards gets you a scoped session for steps 3-4 (that flag or `HAVEN_API_URL`, on the first command \u2014 the session then remembers the backend; **the CLI's built-in default is Haven's hosted production backend**, so on any other deployment an omitted flag connects you somewhere real and wrong rather than failing) \u2014 they approve a code in the browser, you never hold their password. That command is the manifest's `packages.cli.one_liner` (`/.well-known/haven.json`) \u2014 run it as given: `packages.cli.channel` serves the full spec. Do not hold the process open while you wait: under `--json`, pass `--no-wait` to get the link object back at once, then poll it with `haven login --poll <device_code>` \u2014 one round per invocation, exit 3 while it is still pending, 0 once approved. It can set up agents and read the account; it cannot sign, approve a budget, move funds, or rotate any agent's keys.\n2. **HUMAN \u2014 fund it.** USDC only, no ETH: Haven sponsors the gas. `/.well-known/haven.json` names `chains.default` as the deployment's expected chain, but after login `haven wallets funding` prints the address, the amount and which chain in one place; confirm that chain before you message your user. Without a CLI session, the dashboard's funding card shows the address and amount and its Receive-funds screen names the chain; never assume one: a testnet deployment and production both call themselves Haven. Before you write that message, read the manifest: `environment` says whether this deployment is `production`, and each `chains.supported` entry says whether that chain is a `testnet`. Real money is at stake only on a non-testnet chain of a `production` deployment \u2014 tell your user which case theirs is.\n3. **HUMAN \u2014 create the agent, set its budget**, and paste you the **setup prompt** it hands back. With a CLI session (step 1) you can do this step yourself: `haven agents connect --name <n> --budget <amount> --token USDC --period <minutes>` prints the same connector command and approval link; add `--run` to do step 4 too.\n4. **YOU \u2014 run the connector command** in that prompt (see What you run). It makes your signing key locally, registering only the public address.\n5. **HUMAN \u2014 approve the budget** with their passkey, in the Haven tab they created the agent in: it advances to the approval step by itself once your run registers.\n6. **YOU \u2014 verify, then pay.**\n\n## Budget changes later (second token, raise, revoke)\n\nThe first budget rides the setup in The sequence. After the agent exists, a CLI session can construct the LATER changes \u2014 a second token, a bigger amount, a recipient pin, a stop \u2014 and hand your user a link; the human still signs, every time:\n\n```\nhaven budget grant <agentId> --amount <n> --token USDC --period <minutes> [--recipient <address>] [--wait]\nhaven budget revoke <agentId> <delegationHash> [--wait]\n```\n\nThe CLI never signs: it prints a dashboard link, your user opens it and signs with their passkey or wallet. `--wait` polls until the human's signature lands. The hash for `revoke` is in `haven agents show <agentId>`.\n\n## Hand-off scripts\n\nSend these as your own message, `<host>` replaced by the host you fetched this file from. Say what you cannot do, not only what they must.\n\n**Before signup** (step 1):\n\n> I can do everything except the parts that need your signature. Please open `<host>/signup?next=/agents&via=agent` \u2014 name, email, password, then a passkey (Face ID / Touch ID) or a wallet. That is your account's key: I should not have it, and I will never ask for your password. Already have one? `<host>/login?next=/agents`.\n\n**At the passkey step:**\n\n> The passkey must be made on your own device \u2014 it is what keeps the account yours, so nobody, Haven included, can move your funds without you. If this browser cannot, open `<host>/onboarding?next=/agents` on your phone and finish there.\n\n**At funding** (step 2):\n\n> Your Haven account needs USDC before I can pay for anything \u2014 USDC only, no ETH: Haven sponsors the gas. Before you send anything, let me get you the exact address **and network** \u2014 `haven wallets funding` prints both, and the funding card on `<host>/dashboard` shows the same. Please do not send to an address or a chain I have not confirmed with you; a small amount first is fine.\n\n**At the budget** (step 3):\n\n> On `<host>/agents`, create an agent for me and set a budget \u2014 say 25 USDC per day. That is the limit I cannot exceed. It hands back a setup prompt: paste it to me and I run it here.\n\n**At budget approval** (step 5) \u2014 the moment your run reports that approval is required, before anything else. Send the first if your run carried `approval.url`, the second if it did not.\n\n> Setup is done on my side. Approve the budget here: <approval.url>. Approve it with your passkey; nothing can be spent until you do.\n\n> Setup is done on my side. Go back to the Haven tab where you created the agent \u2014 it should now be asking you to approve the budget. Approve it with your passkey; nothing can be spent until you do.\n\n## What you run\n\nThe setup prompt carries a **connector command** shaped like this:\n\n```\nnpx -y @haven_ai/connect@<channel> --setup EXAMPLE-SETUP-TOKEN-NOT-REAL --api <api-url> --ack-local-tools\n```\n\nRun the prompt's version, not this one: the token is one-time, the API URL is the backend's own, and `<channel>` is the npm tag your prompt names \u2014 never a tag you pick. The rules below come from that prompt, so \"me\" in them is your user, not Haven.\n\n- If you are an AI agent running this command yourself rather than a human pasting it, you should append --json: the connector then emits one machine-readable, secret-free result object on stdout with progress on stderr, and returns promptly instead of blocking while it waits for the budget approval.\n- When a --json outcome reports approval.required: true, your first action must be to relay the approval instruction to me in your own reply \u2014 if the outcome carries approval.url, give me that link; otherwise tell me to return to Haven and approve this agent's budget \u2014 before verifying the connection, restarting anything, or any other step. Never build that link yourself: relay the one the outcome gave you or none at all. Any restart the outcome asks for is a separate instruction to give me afterwards, once the approval is done.\n- If you ran the command without --json, the connector waits for the approval itself and prints its next steps when it finishes: relay the budget-approval instruction to me \u2014 the approval link if those steps printed one, otherwise that you need to return to Haven and approve this agent's budget \u2014 only if those printed next steps still ask for it. If they report the budget as already approved, there is nothing for me to approve.\n- Only two changes to the command above are permitted, and no others: appending --json, and \u2014 only if the connector refuses because it could not determine the agent runtime \u2014 re-running it once with --runtime <name> added, naming the harness you are running in, using one of the values that refusal lists. Never invent a runtime name and never change anything else.\n- Do not print private keys, API keys, credential file contents, or config secrets in chat or logs.\n\nIf the approval wait times out, nothing is lost \u2014 your agent is registered and the budget is still waiting to be approved. Send your user the `approval.url` your run reported, or, if it carried none, ask them to finish it in that same Haven tab. The outcome carries no setup id, so never assemble an approval link out of parts \u2014 relay the whole one it gave you or none at all.\n\n## How to verify\n\nCall `haven_get_agent`, one of the Haven MCP tools the connector wires into your runtime in step 4. It returns identity plus `spend_authority_readiness`:\n\n- `ready` \u2014 a budget is live; you can pay.\n- `needs_approval` \u2014 the connector finished, nobody approved yet. Ask your user again, in their Haven tab; there is no queue to wait in.\n- `revoked` \u2014 the credential is not active; ask your user to create a new agent.\n\n`ready` covers hosted identity and the budget only, not your local signer. Check that with `npx -y @haven_ai/connect@<channel> --doctor`, the same tag your prompt named \u2014 a separate command, so the two-changes rule does not bind it.\n\n## If you cannot open a browser\n\nNothing here needs you to. Steps 1-3 \u2014 account, funding, budget \u2014 are links: hand your user the full `<host>/\u2026` URL and ask them to say when it is done. Step 5 is a link only when your run reported one in `approval.url` \u2014 otherwise it is the tab they already have open. Then poll `haven_get_agent` until it reads `ready`. Do not route around the sign-in wall \u2014 it makes the account theirs, not yours.\n\n## If something breaks\n\nIf a Haven result carries `client_update`, that client is out of date: run its `upgrade_command` as given, then any repair line it prints, then retry. `required: true` means payments are refused until you do. What changed: [/releases](/releases).\n\n## Vocabulary\n\n| Term | What it is |\n|---|---|\n| **setup prompt** | The text the dashboard hands your user to paste to you. Carries the command and its rules. |\n| **connector command** | The `npx -y @haven_ai/connect@\u2026` line you run. One-time token, one use. |\n| **agent credential** | Your API key (`sk_agent_\u2026`), written to `~/.haven`. It identifies you; alone it cannot move money. |\n| **delegate key** | Your signing key, made on this machine and never sent anywhere. |\n| **budget** | The on-chain delegation your user signed. It authorises the payment; Haven constructs and relays it. |\n\nNext: [your agent hit a 402](/402.md) \xB7 [everything agent-readable](/llms.txt)\n";
474
+ var HAVEN_AGENT_RUNBOOK_MD = "# Haven for agents \u2014 set your user up to let you pay\n\nYou are an AI agent whose user has no Haven account yet, or has one but has not connected you.\n\n> Links below are paths on this same host: resolve them against the URL you fetched this file from.\n\n## What Haven is, in three sentences\n\nYour user gives you a **budget on their own account** \u2014 not their wallet, and not a key to their funds. The budget is a delegation they sign, enforced on-chain: a payment over it, to the wrong recipient, or past its expiry is refused at execution time, not by a dashboard promise. Haven constructs and relays the payments; you get an agent credential and a signing key made on your machine, and your user can revoke the budget without you and without Haven.\n\n## The sequence\n\nFour of the six steps are your user's \u2014 each needs a human signature or a human decision. The rest is yours. If they already have a funded account, start at step 3.\n\n1. **HUMAN \u2014 create the account.** Name, email, password, then a passkey (Face ID / Touch ID) or a wallet. Never offer to enter any of it: you must not have their password, and the passkey is bound to their device. With a terminal, `<packages.cli.one_liner> login --api <api-url>` afterwards gets you a scoped session for steps 3-4 (that flag or `HAVEN_API_URL`, on the first command \u2014 the session then remembers the backend; **the CLI's built-in default is Haven's hosted production backend**, so on any other deployment an omitted flag connects you somewhere real and wrong rather than failing) \u2014 they approve a code in the browser, you never hold their password. That command is the manifest's `packages.cli.one_liner` (`/.well-known/haven.json`) \u2014 run it as given: `packages.cli.channel` serves the full spec. Do not hold the process open while you wait: under `--json`, pass `--no-wait` to get the link object back at once, then poll it with `haven login --poll <device_code>` \u2014 one round per invocation, exit 3 while it is still pending, 0 once approved. It can set up agents and read the account; it cannot sign, approve a budget, move funds, or rotate any agent's keys.\n2. **HUMAN \u2014 fund it.** USDC only, no ETH: Haven sponsors the gas. `/.well-known/haven.json` names `chains.default` as the deployment's expected chain, but after login `haven wallets funding` prints the address, the amount and which chain in one place; confirm that chain before you message your user. Without a CLI session, the dashboard's funding card shows the address and amount and its Receive-funds screen names the chain; never assume one: a testnet deployment and production both call themselves Haven. Before you write that message, read the manifest: `environment` says whether this deployment is `production`, and each `chains.supported` entry says whether that chain is a `testnet`. Real money is at stake only on a non-testnet chain of a `production` deployment \u2014 tell your user which case theirs is.\n3. **HUMAN \u2014 create the agent, set its budget**, and paste you the **setup prompt** it hands back. With a CLI session (step 1) you can do this step yourself: `haven agents connect --name <n> --budget <amount> --token USDC --period <minutes>` prints the same connector command and approval link; add `--run` to do step 4 too.\n4. **YOU \u2014 run the connector command** in that prompt (see What you run). It makes your signing key locally, registering only the public address.\n5. **HUMAN \u2014 approve the budget** with their passkey, in the Haven tab they created the agent in: it advances to the approval step by itself once your run registers.\n6. **YOU \u2014 verify, then pay.**\n\n## Budget changes later (second token, raise, revoke)\n\nThe first budget rides the setup in The sequence. After the agent exists, a CLI session can construct the LATER changes \u2014 a second token, a bigger amount, a recipient pin, a stop \u2014 and hand your user a link; the human still signs, every time:\n\n```\nhaven budget grant <agentId> --amount <n> --token USDC --period <minutes> [--recipient <address>] [--wait]\nhaven budget revoke <agentId> <delegationHash> [--wait]\n```\n\nThe CLI never signs: it prints a dashboard link, your user opens it and signs with their passkey or wallet. `--wait` polls until the human's signature lands. The hash for `revoke` is in `haven agents show <agentId>`.\n\n## Hand-off scripts\n\nSend these as your own message, `<host>` replaced by the host you fetched this file from. Say what you cannot do, not only what they must.\n\n**Before signup** (step 1):\n\n> I can do everything except the parts that need your signature. Please open `<host>/signup?next=/agents&via=agent` \u2014 name, email, password, then a passkey (Face ID / Touch ID) or a wallet. That is your account's key: I should not have it, and I will never ask for your password. Already have one? `<host>/login?next=/agents`.\n\n**At the passkey step:**\n\n> The passkey must be made on your own device \u2014 it is what keeps the account yours, so nobody, Haven included, can move your funds without you. If this browser cannot, open `<host>/onboarding?next=/agents` on your phone and finish there.\n\n**At funding** (step 2):\n\n> Your Haven account needs USDC before I can pay for anything \u2014 USDC only, no ETH: Haven sponsors the gas. Before you send anything, let me get you the exact address **and network** \u2014 `haven wallets funding` prints both, and the funding card on `<host>/dashboard` shows the same. Please do not send to an address or a chain I have not confirmed with you; a small amount first is fine.\n\n**At the budget** (step 3):\n\n> On `<host>/agents`, create an agent for me and set a budget \u2014 say 25 USDC per day. That is the limit I cannot exceed. It hands back a setup prompt: paste it to me and I run it here.\n\n**At budget approval** (step 5) \u2014 the moment your run reports that approval is required, before anything else. Send the first if your run carried `approval.url`, the second if it did not.\n\n> Setup is done on my side. Approve the budget here: <approval.url>. Approve it with your passkey; nothing can be spent until you do.\n\n> Setup is done on my side. Go back to the Haven tab where you created the agent \u2014 it should now be asking you to approve the budget. Approve it with your passkey; nothing can be spent until you do.\n\n## What you run\n\nThe setup prompt carries a **connector command** shaped like this:\n\n```\nnpx -y @haven_ai/connect@<channel> --setup EXAMPLE-SETUP-TOKEN-NOT-REAL --api <api-url> --ack-local-tools\n```\n\nRun the prompt's version, not this one: the token is one-time, the API URL is the backend's own, and `<channel>` is the npm tag your prompt names \u2014 never a tag you pick. The rules below come from that prompt, so \"me\" in them is your user, not Haven.\n\n- If you are an AI agent running this command yourself rather than a human pasting it, you should append --json: the connector then emits one machine-readable, secret-free result object on stdout with progress on stderr, and returns promptly instead of blocking while it waits for the budget approval.\n- When a --json outcome reports approval.required: true, your first action must be to relay the approval instruction to me in your own reply \u2014 if the outcome carries approval.url, give me that link; otherwise tell me to return to Haven and approve this agent's budget \u2014 before verifying the connection, restarting anything, or any other step. Never build that link yourself: relay the one the outcome gave you or none at all. Any restart the outcome asks for is a separate instruction to give me afterwards, once the approval is done.\n- If you ran the command without --json, the connector waits for the approval itself and prints its next steps when it finishes: relay the budget-approval instruction to me \u2014 the approval link if those steps printed one, otherwise that you need to return to Haven and approve this agent's budget \u2014 only if those printed next steps still ask for it. If they report the budget as already approved, there is nothing for me to approve.\n- Only three changes to the command above are permitted, and no others: appending --json; re-running it once with --runtime <name> added, only if the connector refuses because it could not determine the agent runtime, naming the harness you are running in and using one of the values that refusal lists; and re-running it once with the one flag I chose added, only after I have answered a wiring_collision relay \u2014 --name <the suggested_name or a name I gave> to add alongside, or --replace to replace \u2014 keeping any change already made. If that re-run is refused too, relay the refusal to me and stop. Never invent a runtime name and never change anything else.\n- Do not print private keys, API keys, credential file contents, or config secrets in chat or logs.\n\nIf the approval wait times out, nothing is lost \u2014 your agent is registered and the budget is still waiting to be approved. Send your user the `approval.url` your run reported, or, if it carried none, ask them to finish it in that same Haven tab. The outcome carries no setup id, so never assemble an approval link out of parts \u2014 relay the whole one it gave you or none at all.\n\n## How to verify\n\nCall `haven_get_agent`, one of the Haven MCP tools the connector wires into your runtime in step 4. It returns identity plus `spend_authority_readiness`:\n\n- `ready` \u2014 a budget is live: you have the authority to pay. It does not say the account holds funds \u2014 the `allowances[]` rows carry `funds_cover_remaining`, and `false` there is a heads-up to mention to your user, not a refusal.\n- `needs_approval` \u2014 the connector finished, nobody approved yet. Ask your user again, in their Haven tab; there is no queue to wait in.\n- `revoked` \u2014 the credential is not active; ask your user to create a new agent.\n\n`ready` covers hosted identity and the budget only, not your local signer. Check that with `npx -y @haven_ai/connect@<channel> --doctor`, the same tag your prompt named \u2014 a separate command, so the command-modification rule does not bind it.\n\n## If you cannot open a browser\n\nNothing here needs you to. Steps 1-3 \u2014 account, funding, budget \u2014 are links: hand your user the full `<host>/\u2026` URL and ask them to say when it is done. Step 5 is a link only when your run reported one in `approval.url` \u2014 otherwise it is the tab they already have open. Then poll `haven_get_agent` until it reads `ready`. Do not route around the sign-in wall \u2014 it makes the account theirs, not yours.\n\n## If something breaks\n\nIf a Haven result carries `client_update`, that client is out of date: run its `upgrade_command` as given, then any repair line it prints, then retry. `required: true` means payments are refused until you do. What changed: [/releases](/releases).\n\nSomething else wrong? `haven feedback submit \"<text>\"` sends it to Haven \u2014 it needs `haven login` first. Never put a credential in that text: no API key, no delegate key, no session token, no recovery phrase.\n\n## Vocabulary\n\n| Term | What it is |\n|---|---|\n| **setup prompt** | The text the dashboard hands your user to paste to you. Carries the command and its rules. |\n| **connector command** | The `npx -y @haven_ai/connect@\u2026` line you run. One-time token, one use. |\n| **agent credential** | Your API key (`sk_agent_\u2026`), written to `~/.haven`. It identifies you; alone it cannot move money. |\n| **delegate key** | Your signing key, made on this machine and never sent anywhere. |\n| **budget** | The on-chain delegation your user signed. It authorises the payment; Haven constructs and relays it. |\n\nNext: [your agent hit a 402](/402.md) \xB7 [everything agent-readable](/llms.txt)\n";
449
475
 
450
476
  // src/token.ts
451
477
  function sessionExpiry(token) {
@@ -618,9 +644,2515 @@ async function runConnector(connectorCommand, spawner, onStderr) {
618
644
  return { outcome, exitCode, stderr, stdoutNoise: noise };
619
645
  }
620
646
 
647
+ // ../../node_modules/@noble/hashes/esm/_u64.js
648
+ var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
649
+ var _32n = /* @__PURE__ */ BigInt(32);
650
+ function fromBig(n, le = false) {
651
+ if (le)
652
+ return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
653
+ return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
654
+ }
655
+ function split(lst, le = false) {
656
+ const len = lst.length;
657
+ let Ah = new Uint32Array(len);
658
+ let Al = new Uint32Array(len);
659
+ for (let i = 0; i < len; i++) {
660
+ const { h, l } = fromBig(lst[i], le);
661
+ [Ah[i], Al[i]] = [h, l];
662
+ }
663
+ return [Ah, Al];
664
+ }
665
+ var rotlSH = (h, l, s) => h << s | l >>> 32 - s;
666
+ var rotlSL = (h, l, s) => l << s | h >>> 32 - s;
667
+ var rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
668
+ var rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
669
+
670
+ // ../../node_modules/@noble/hashes/esm/utils.js
671
+ function isBytes(a) {
672
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
673
+ }
674
+ function anumber(n) {
675
+ if (!Number.isSafeInteger(n) || n < 0)
676
+ throw new Error("positive integer expected, got " + n);
677
+ }
678
+ function abytes(b, ...lengths) {
679
+ if (!isBytes(b))
680
+ throw new Error("Uint8Array expected");
681
+ if (lengths.length > 0 && !lengths.includes(b.length))
682
+ throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
683
+ }
684
+ function aexists(instance, checkFinished = true) {
685
+ if (instance.destroyed)
686
+ throw new Error("Hash instance has been destroyed");
687
+ if (checkFinished && instance.finished)
688
+ throw new Error("Hash#digest() has already been called");
689
+ }
690
+ function aoutput(out, instance) {
691
+ abytes(out);
692
+ const min = instance.outputLen;
693
+ if (out.length < min) {
694
+ throw new Error("digestInto() expects output buffer of length at least " + min);
695
+ }
696
+ }
697
+ function u32(arr) {
698
+ return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
699
+ }
700
+ function clean(...arrays) {
701
+ for (let i = 0; i < arrays.length; i++) {
702
+ arrays[i].fill(0);
703
+ }
704
+ }
705
+ var isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
706
+ function byteSwap(word) {
707
+ return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
708
+ }
709
+ function byteSwap32(arr) {
710
+ for (let i = 0; i < arr.length; i++) {
711
+ arr[i] = byteSwap(arr[i]);
712
+ }
713
+ return arr;
714
+ }
715
+ var swap32IfBE = isLE ? (u) => u : byteSwap32;
716
+ function utf8ToBytes(str) {
717
+ if (typeof str !== "string")
718
+ throw new Error("string expected");
719
+ return new Uint8Array(new TextEncoder().encode(str));
720
+ }
721
+ function toBytes(data) {
722
+ if (typeof data === "string")
723
+ data = utf8ToBytes(data);
724
+ abytes(data);
725
+ return data;
726
+ }
727
+ var Hash = class {
728
+ };
729
+ function createHasher(hashCons) {
730
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
731
+ const tmp = hashCons();
732
+ hashC.outputLen = tmp.outputLen;
733
+ hashC.blockLen = tmp.blockLen;
734
+ hashC.create = () => hashCons();
735
+ return hashC;
736
+ }
737
+
738
+ // ../../node_modules/@noble/hashes/esm/sha3.js
739
+ var _0n = BigInt(0);
740
+ var _1n = BigInt(1);
741
+ var _2n = BigInt(2);
742
+ var _7n = BigInt(7);
743
+ var _256n = BigInt(256);
744
+ var _0x71n = BigInt(113);
745
+ var SHA3_PI = [];
746
+ var SHA3_ROTL = [];
747
+ var _SHA3_IOTA = [];
748
+ for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
749
+ [x, y] = [y, (2 * x + 3 * y) % 5];
750
+ SHA3_PI.push(2 * (5 * y + x));
751
+ SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
752
+ let t = _0n;
753
+ for (let j = 0; j < 7; j++) {
754
+ R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
755
+ if (R & _2n)
756
+ t ^= _1n << (_1n << /* @__PURE__ */ BigInt(j)) - _1n;
757
+ }
758
+ _SHA3_IOTA.push(t);
759
+ }
760
+ var IOTAS = split(_SHA3_IOTA, true);
761
+ var SHA3_IOTA_H = IOTAS[0];
762
+ var SHA3_IOTA_L = IOTAS[1];
763
+ var rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
764
+ var rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
765
+ function keccakP(s, rounds = 24) {
766
+ const B = new Uint32Array(5 * 2);
767
+ for (let round = 24 - rounds; round < 24; round++) {
768
+ for (let x = 0; x < 10; x++)
769
+ B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
770
+ for (let x = 0; x < 10; x += 2) {
771
+ const idx1 = (x + 8) % 10;
772
+ const idx0 = (x + 2) % 10;
773
+ const B0 = B[idx0];
774
+ const B1 = B[idx0 + 1];
775
+ const Th = rotlH(B0, B1, 1) ^ B[idx1];
776
+ const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
777
+ for (let y = 0; y < 50; y += 10) {
778
+ s[x + y] ^= Th;
779
+ s[x + y + 1] ^= Tl;
780
+ }
781
+ }
782
+ let curH = s[2];
783
+ let curL = s[3];
784
+ for (let t = 0; t < 24; t++) {
785
+ const shift = SHA3_ROTL[t];
786
+ const Th = rotlH(curH, curL, shift);
787
+ const Tl = rotlL(curH, curL, shift);
788
+ const PI = SHA3_PI[t];
789
+ curH = s[PI];
790
+ curL = s[PI + 1];
791
+ s[PI] = Th;
792
+ s[PI + 1] = Tl;
793
+ }
794
+ for (let y = 0; y < 50; y += 10) {
795
+ for (let x = 0; x < 10; x++)
796
+ B[x] = s[y + x];
797
+ for (let x = 0; x < 10; x++)
798
+ s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
799
+ }
800
+ s[0] ^= SHA3_IOTA_H[round];
801
+ s[1] ^= SHA3_IOTA_L[round];
802
+ }
803
+ clean(B);
804
+ }
805
+ var Keccak = class _Keccak extends Hash {
806
+ // NOTE: we accept arguments in bytes instead of bits here.
807
+ constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
808
+ super();
809
+ this.pos = 0;
810
+ this.posOut = 0;
811
+ this.finished = false;
812
+ this.destroyed = false;
813
+ this.enableXOF = false;
814
+ this.blockLen = blockLen;
815
+ this.suffix = suffix;
816
+ this.outputLen = outputLen;
817
+ this.enableXOF = enableXOF;
818
+ this.rounds = rounds;
819
+ anumber(outputLen);
820
+ if (!(0 < blockLen && blockLen < 200))
821
+ throw new Error("only keccak-f1600 function is supported");
822
+ this.state = new Uint8Array(200);
823
+ this.state32 = u32(this.state);
824
+ }
825
+ clone() {
826
+ return this._cloneInto();
827
+ }
828
+ keccak() {
829
+ swap32IfBE(this.state32);
830
+ keccakP(this.state32, this.rounds);
831
+ swap32IfBE(this.state32);
832
+ this.posOut = 0;
833
+ this.pos = 0;
834
+ }
835
+ update(data) {
836
+ aexists(this);
837
+ data = toBytes(data);
838
+ abytes(data);
839
+ const { blockLen, state } = this;
840
+ const len = data.length;
841
+ for (let pos = 0; pos < len; ) {
842
+ const take = Math.min(blockLen - this.pos, len - pos);
843
+ for (let i = 0; i < take; i++)
844
+ state[this.pos++] ^= data[pos++];
845
+ if (this.pos === blockLen)
846
+ this.keccak();
847
+ }
848
+ return this;
849
+ }
850
+ finish() {
851
+ if (this.finished)
852
+ return;
853
+ this.finished = true;
854
+ const { state, suffix, pos, blockLen } = this;
855
+ state[pos] ^= suffix;
856
+ if ((suffix & 128) !== 0 && pos === blockLen - 1)
857
+ this.keccak();
858
+ state[blockLen - 1] ^= 128;
859
+ this.keccak();
860
+ }
861
+ writeInto(out) {
862
+ aexists(this, false);
863
+ abytes(out);
864
+ this.finish();
865
+ const bufferOut = this.state;
866
+ const { blockLen } = this;
867
+ for (let pos = 0, len = out.length; pos < len; ) {
868
+ if (this.posOut >= blockLen)
869
+ this.keccak();
870
+ const take = Math.min(blockLen - this.posOut, len - pos);
871
+ out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
872
+ this.posOut += take;
873
+ pos += take;
874
+ }
875
+ return out;
876
+ }
877
+ xofInto(out) {
878
+ if (!this.enableXOF)
879
+ throw new Error("XOF is not possible for this instance");
880
+ return this.writeInto(out);
881
+ }
882
+ xof(bytes) {
883
+ anumber(bytes);
884
+ return this.xofInto(new Uint8Array(bytes));
885
+ }
886
+ digestInto(out) {
887
+ aoutput(out, this);
888
+ if (this.finished)
889
+ throw new Error("digest() was already called");
890
+ this.writeInto(out);
891
+ this.destroy();
892
+ return out;
893
+ }
894
+ digest() {
895
+ return this.digestInto(new Uint8Array(this.outputLen));
896
+ }
897
+ destroy() {
898
+ this.destroyed = true;
899
+ clean(this.state);
900
+ }
901
+ _cloneInto(to) {
902
+ const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
903
+ to || (to = new _Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
904
+ to.state32.set(this.state32);
905
+ to.pos = this.pos;
906
+ to.posOut = this.posOut;
907
+ to.finished = this.finished;
908
+ to.rounds = rounds;
909
+ to.suffix = suffix;
910
+ to.outputLen = outputLen;
911
+ to.enableXOF = enableXOF;
912
+ to.destroyed = this.destroyed;
913
+ return to;
914
+ }
915
+ };
916
+ var gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
917
+ var keccak_256 = /* @__PURE__ */ (() => gen(1, 136, 256 / 8))();
918
+
919
+ // ../../node_modules/@scure/bip39/esm/wordlists/english.js
920
+ var wordlist = `abandon
921
+ ability
922
+ able
923
+ about
924
+ above
925
+ absent
926
+ absorb
927
+ abstract
928
+ absurd
929
+ abuse
930
+ access
931
+ accident
932
+ account
933
+ accuse
934
+ achieve
935
+ acid
936
+ acoustic
937
+ acquire
938
+ across
939
+ act
940
+ action
941
+ actor
942
+ actress
943
+ actual
944
+ adapt
945
+ add
946
+ addict
947
+ address
948
+ adjust
949
+ admit
950
+ adult
951
+ advance
952
+ advice
953
+ aerobic
954
+ affair
955
+ afford
956
+ afraid
957
+ again
958
+ age
959
+ agent
960
+ agree
961
+ ahead
962
+ aim
963
+ air
964
+ airport
965
+ aisle
966
+ alarm
967
+ album
968
+ alcohol
969
+ alert
970
+ alien
971
+ all
972
+ alley
973
+ allow
974
+ almost
975
+ alone
976
+ alpha
977
+ already
978
+ also
979
+ alter
980
+ always
981
+ amateur
982
+ amazing
983
+ among
984
+ amount
985
+ amused
986
+ analyst
987
+ anchor
988
+ ancient
989
+ anger
990
+ angle
991
+ angry
992
+ animal
993
+ ankle
994
+ announce
995
+ annual
996
+ another
997
+ answer
998
+ antenna
999
+ antique
1000
+ anxiety
1001
+ any
1002
+ apart
1003
+ apology
1004
+ appear
1005
+ apple
1006
+ approve
1007
+ april
1008
+ arch
1009
+ arctic
1010
+ area
1011
+ arena
1012
+ argue
1013
+ arm
1014
+ armed
1015
+ armor
1016
+ army
1017
+ around
1018
+ arrange
1019
+ arrest
1020
+ arrive
1021
+ arrow
1022
+ art
1023
+ artefact
1024
+ artist
1025
+ artwork
1026
+ ask
1027
+ aspect
1028
+ assault
1029
+ asset
1030
+ assist
1031
+ assume
1032
+ asthma
1033
+ athlete
1034
+ atom
1035
+ attack
1036
+ attend
1037
+ attitude
1038
+ attract
1039
+ auction
1040
+ audit
1041
+ august
1042
+ aunt
1043
+ author
1044
+ auto
1045
+ autumn
1046
+ average
1047
+ avocado
1048
+ avoid
1049
+ awake
1050
+ aware
1051
+ away
1052
+ awesome
1053
+ awful
1054
+ awkward
1055
+ axis
1056
+ baby
1057
+ bachelor
1058
+ bacon
1059
+ badge
1060
+ bag
1061
+ balance
1062
+ balcony
1063
+ ball
1064
+ bamboo
1065
+ banana
1066
+ banner
1067
+ bar
1068
+ barely
1069
+ bargain
1070
+ barrel
1071
+ base
1072
+ basic
1073
+ basket
1074
+ battle
1075
+ beach
1076
+ bean
1077
+ beauty
1078
+ because
1079
+ become
1080
+ beef
1081
+ before
1082
+ begin
1083
+ behave
1084
+ behind
1085
+ believe
1086
+ below
1087
+ belt
1088
+ bench
1089
+ benefit
1090
+ best
1091
+ betray
1092
+ better
1093
+ between
1094
+ beyond
1095
+ bicycle
1096
+ bid
1097
+ bike
1098
+ bind
1099
+ biology
1100
+ bird
1101
+ birth
1102
+ bitter
1103
+ black
1104
+ blade
1105
+ blame
1106
+ blanket
1107
+ blast
1108
+ bleak
1109
+ bless
1110
+ blind
1111
+ blood
1112
+ blossom
1113
+ blouse
1114
+ blue
1115
+ blur
1116
+ blush
1117
+ board
1118
+ boat
1119
+ body
1120
+ boil
1121
+ bomb
1122
+ bone
1123
+ bonus
1124
+ book
1125
+ boost
1126
+ border
1127
+ boring
1128
+ borrow
1129
+ boss
1130
+ bottom
1131
+ bounce
1132
+ box
1133
+ boy
1134
+ bracket
1135
+ brain
1136
+ brand
1137
+ brass
1138
+ brave
1139
+ bread
1140
+ breeze
1141
+ brick
1142
+ bridge
1143
+ brief
1144
+ bright
1145
+ bring
1146
+ brisk
1147
+ broccoli
1148
+ broken
1149
+ bronze
1150
+ broom
1151
+ brother
1152
+ brown
1153
+ brush
1154
+ bubble
1155
+ buddy
1156
+ budget
1157
+ buffalo
1158
+ build
1159
+ bulb
1160
+ bulk
1161
+ bullet
1162
+ bundle
1163
+ bunker
1164
+ burden
1165
+ burger
1166
+ burst
1167
+ bus
1168
+ business
1169
+ busy
1170
+ butter
1171
+ buyer
1172
+ buzz
1173
+ cabbage
1174
+ cabin
1175
+ cable
1176
+ cactus
1177
+ cage
1178
+ cake
1179
+ call
1180
+ calm
1181
+ camera
1182
+ camp
1183
+ can
1184
+ canal
1185
+ cancel
1186
+ candy
1187
+ cannon
1188
+ canoe
1189
+ canvas
1190
+ canyon
1191
+ capable
1192
+ capital
1193
+ captain
1194
+ car
1195
+ carbon
1196
+ card
1197
+ cargo
1198
+ carpet
1199
+ carry
1200
+ cart
1201
+ case
1202
+ cash
1203
+ casino
1204
+ castle
1205
+ casual
1206
+ cat
1207
+ catalog
1208
+ catch
1209
+ category
1210
+ cattle
1211
+ caught
1212
+ cause
1213
+ caution
1214
+ cave
1215
+ ceiling
1216
+ celery
1217
+ cement
1218
+ census
1219
+ century
1220
+ cereal
1221
+ certain
1222
+ chair
1223
+ chalk
1224
+ champion
1225
+ change
1226
+ chaos
1227
+ chapter
1228
+ charge
1229
+ chase
1230
+ chat
1231
+ cheap
1232
+ check
1233
+ cheese
1234
+ chef
1235
+ cherry
1236
+ chest
1237
+ chicken
1238
+ chief
1239
+ child
1240
+ chimney
1241
+ choice
1242
+ choose
1243
+ chronic
1244
+ chuckle
1245
+ chunk
1246
+ churn
1247
+ cigar
1248
+ cinnamon
1249
+ circle
1250
+ citizen
1251
+ city
1252
+ civil
1253
+ claim
1254
+ clap
1255
+ clarify
1256
+ claw
1257
+ clay
1258
+ clean
1259
+ clerk
1260
+ clever
1261
+ click
1262
+ client
1263
+ cliff
1264
+ climb
1265
+ clinic
1266
+ clip
1267
+ clock
1268
+ clog
1269
+ close
1270
+ cloth
1271
+ cloud
1272
+ clown
1273
+ club
1274
+ clump
1275
+ cluster
1276
+ clutch
1277
+ coach
1278
+ coast
1279
+ coconut
1280
+ code
1281
+ coffee
1282
+ coil
1283
+ coin
1284
+ collect
1285
+ color
1286
+ column
1287
+ combine
1288
+ come
1289
+ comfort
1290
+ comic
1291
+ common
1292
+ company
1293
+ concert
1294
+ conduct
1295
+ confirm
1296
+ congress
1297
+ connect
1298
+ consider
1299
+ control
1300
+ convince
1301
+ cook
1302
+ cool
1303
+ copper
1304
+ copy
1305
+ coral
1306
+ core
1307
+ corn
1308
+ correct
1309
+ cost
1310
+ cotton
1311
+ couch
1312
+ country
1313
+ couple
1314
+ course
1315
+ cousin
1316
+ cover
1317
+ coyote
1318
+ crack
1319
+ cradle
1320
+ craft
1321
+ cram
1322
+ crane
1323
+ crash
1324
+ crater
1325
+ crawl
1326
+ crazy
1327
+ cream
1328
+ credit
1329
+ creek
1330
+ crew
1331
+ cricket
1332
+ crime
1333
+ crisp
1334
+ critic
1335
+ crop
1336
+ cross
1337
+ crouch
1338
+ crowd
1339
+ crucial
1340
+ cruel
1341
+ cruise
1342
+ crumble
1343
+ crunch
1344
+ crush
1345
+ cry
1346
+ crystal
1347
+ cube
1348
+ culture
1349
+ cup
1350
+ cupboard
1351
+ curious
1352
+ current
1353
+ curtain
1354
+ curve
1355
+ cushion
1356
+ custom
1357
+ cute
1358
+ cycle
1359
+ dad
1360
+ damage
1361
+ damp
1362
+ dance
1363
+ danger
1364
+ daring
1365
+ dash
1366
+ daughter
1367
+ dawn
1368
+ day
1369
+ deal
1370
+ debate
1371
+ debris
1372
+ decade
1373
+ december
1374
+ decide
1375
+ decline
1376
+ decorate
1377
+ decrease
1378
+ deer
1379
+ defense
1380
+ define
1381
+ defy
1382
+ degree
1383
+ delay
1384
+ deliver
1385
+ demand
1386
+ demise
1387
+ denial
1388
+ dentist
1389
+ deny
1390
+ depart
1391
+ depend
1392
+ deposit
1393
+ depth
1394
+ deputy
1395
+ derive
1396
+ describe
1397
+ desert
1398
+ design
1399
+ desk
1400
+ despair
1401
+ destroy
1402
+ detail
1403
+ detect
1404
+ develop
1405
+ device
1406
+ devote
1407
+ diagram
1408
+ dial
1409
+ diamond
1410
+ diary
1411
+ dice
1412
+ diesel
1413
+ diet
1414
+ differ
1415
+ digital
1416
+ dignity
1417
+ dilemma
1418
+ dinner
1419
+ dinosaur
1420
+ direct
1421
+ dirt
1422
+ disagree
1423
+ discover
1424
+ disease
1425
+ dish
1426
+ dismiss
1427
+ disorder
1428
+ display
1429
+ distance
1430
+ divert
1431
+ divide
1432
+ divorce
1433
+ dizzy
1434
+ doctor
1435
+ document
1436
+ dog
1437
+ doll
1438
+ dolphin
1439
+ domain
1440
+ donate
1441
+ donkey
1442
+ donor
1443
+ door
1444
+ dose
1445
+ double
1446
+ dove
1447
+ draft
1448
+ dragon
1449
+ drama
1450
+ drastic
1451
+ draw
1452
+ dream
1453
+ dress
1454
+ drift
1455
+ drill
1456
+ drink
1457
+ drip
1458
+ drive
1459
+ drop
1460
+ drum
1461
+ dry
1462
+ duck
1463
+ dumb
1464
+ dune
1465
+ during
1466
+ dust
1467
+ dutch
1468
+ duty
1469
+ dwarf
1470
+ dynamic
1471
+ eager
1472
+ eagle
1473
+ early
1474
+ earn
1475
+ earth
1476
+ easily
1477
+ east
1478
+ easy
1479
+ echo
1480
+ ecology
1481
+ economy
1482
+ edge
1483
+ edit
1484
+ educate
1485
+ effort
1486
+ egg
1487
+ eight
1488
+ either
1489
+ elbow
1490
+ elder
1491
+ electric
1492
+ elegant
1493
+ element
1494
+ elephant
1495
+ elevator
1496
+ elite
1497
+ else
1498
+ embark
1499
+ embody
1500
+ embrace
1501
+ emerge
1502
+ emotion
1503
+ employ
1504
+ empower
1505
+ empty
1506
+ enable
1507
+ enact
1508
+ end
1509
+ endless
1510
+ endorse
1511
+ enemy
1512
+ energy
1513
+ enforce
1514
+ engage
1515
+ engine
1516
+ enhance
1517
+ enjoy
1518
+ enlist
1519
+ enough
1520
+ enrich
1521
+ enroll
1522
+ ensure
1523
+ enter
1524
+ entire
1525
+ entry
1526
+ envelope
1527
+ episode
1528
+ equal
1529
+ equip
1530
+ era
1531
+ erase
1532
+ erode
1533
+ erosion
1534
+ error
1535
+ erupt
1536
+ escape
1537
+ essay
1538
+ essence
1539
+ estate
1540
+ eternal
1541
+ ethics
1542
+ evidence
1543
+ evil
1544
+ evoke
1545
+ evolve
1546
+ exact
1547
+ example
1548
+ excess
1549
+ exchange
1550
+ excite
1551
+ exclude
1552
+ excuse
1553
+ execute
1554
+ exercise
1555
+ exhaust
1556
+ exhibit
1557
+ exile
1558
+ exist
1559
+ exit
1560
+ exotic
1561
+ expand
1562
+ expect
1563
+ expire
1564
+ explain
1565
+ expose
1566
+ express
1567
+ extend
1568
+ extra
1569
+ eye
1570
+ eyebrow
1571
+ fabric
1572
+ face
1573
+ faculty
1574
+ fade
1575
+ faint
1576
+ faith
1577
+ fall
1578
+ false
1579
+ fame
1580
+ family
1581
+ famous
1582
+ fan
1583
+ fancy
1584
+ fantasy
1585
+ farm
1586
+ fashion
1587
+ fat
1588
+ fatal
1589
+ father
1590
+ fatigue
1591
+ fault
1592
+ favorite
1593
+ feature
1594
+ february
1595
+ federal
1596
+ fee
1597
+ feed
1598
+ feel
1599
+ female
1600
+ fence
1601
+ festival
1602
+ fetch
1603
+ fever
1604
+ few
1605
+ fiber
1606
+ fiction
1607
+ field
1608
+ figure
1609
+ file
1610
+ film
1611
+ filter
1612
+ final
1613
+ find
1614
+ fine
1615
+ finger
1616
+ finish
1617
+ fire
1618
+ firm
1619
+ first
1620
+ fiscal
1621
+ fish
1622
+ fit
1623
+ fitness
1624
+ fix
1625
+ flag
1626
+ flame
1627
+ flash
1628
+ flat
1629
+ flavor
1630
+ flee
1631
+ flight
1632
+ flip
1633
+ float
1634
+ flock
1635
+ floor
1636
+ flower
1637
+ fluid
1638
+ flush
1639
+ fly
1640
+ foam
1641
+ focus
1642
+ fog
1643
+ foil
1644
+ fold
1645
+ follow
1646
+ food
1647
+ foot
1648
+ force
1649
+ forest
1650
+ forget
1651
+ fork
1652
+ fortune
1653
+ forum
1654
+ forward
1655
+ fossil
1656
+ foster
1657
+ found
1658
+ fox
1659
+ fragile
1660
+ frame
1661
+ frequent
1662
+ fresh
1663
+ friend
1664
+ fringe
1665
+ frog
1666
+ front
1667
+ frost
1668
+ frown
1669
+ frozen
1670
+ fruit
1671
+ fuel
1672
+ fun
1673
+ funny
1674
+ furnace
1675
+ fury
1676
+ future
1677
+ gadget
1678
+ gain
1679
+ galaxy
1680
+ gallery
1681
+ game
1682
+ gap
1683
+ garage
1684
+ garbage
1685
+ garden
1686
+ garlic
1687
+ garment
1688
+ gas
1689
+ gasp
1690
+ gate
1691
+ gather
1692
+ gauge
1693
+ gaze
1694
+ general
1695
+ genius
1696
+ genre
1697
+ gentle
1698
+ genuine
1699
+ gesture
1700
+ ghost
1701
+ giant
1702
+ gift
1703
+ giggle
1704
+ ginger
1705
+ giraffe
1706
+ girl
1707
+ give
1708
+ glad
1709
+ glance
1710
+ glare
1711
+ glass
1712
+ glide
1713
+ glimpse
1714
+ globe
1715
+ gloom
1716
+ glory
1717
+ glove
1718
+ glow
1719
+ glue
1720
+ goat
1721
+ goddess
1722
+ gold
1723
+ good
1724
+ goose
1725
+ gorilla
1726
+ gospel
1727
+ gossip
1728
+ govern
1729
+ gown
1730
+ grab
1731
+ grace
1732
+ grain
1733
+ grant
1734
+ grape
1735
+ grass
1736
+ gravity
1737
+ great
1738
+ green
1739
+ grid
1740
+ grief
1741
+ grit
1742
+ grocery
1743
+ group
1744
+ grow
1745
+ grunt
1746
+ guard
1747
+ guess
1748
+ guide
1749
+ guilt
1750
+ guitar
1751
+ gun
1752
+ gym
1753
+ habit
1754
+ hair
1755
+ half
1756
+ hammer
1757
+ hamster
1758
+ hand
1759
+ happy
1760
+ harbor
1761
+ hard
1762
+ harsh
1763
+ harvest
1764
+ hat
1765
+ have
1766
+ hawk
1767
+ hazard
1768
+ head
1769
+ health
1770
+ heart
1771
+ heavy
1772
+ hedgehog
1773
+ height
1774
+ hello
1775
+ helmet
1776
+ help
1777
+ hen
1778
+ hero
1779
+ hidden
1780
+ high
1781
+ hill
1782
+ hint
1783
+ hip
1784
+ hire
1785
+ history
1786
+ hobby
1787
+ hockey
1788
+ hold
1789
+ hole
1790
+ holiday
1791
+ hollow
1792
+ home
1793
+ honey
1794
+ hood
1795
+ hope
1796
+ horn
1797
+ horror
1798
+ horse
1799
+ hospital
1800
+ host
1801
+ hotel
1802
+ hour
1803
+ hover
1804
+ hub
1805
+ huge
1806
+ human
1807
+ humble
1808
+ humor
1809
+ hundred
1810
+ hungry
1811
+ hunt
1812
+ hurdle
1813
+ hurry
1814
+ hurt
1815
+ husband
1816
+ hybrid
1817
+ ice
1818
+ icon
1819
+ idea
1820
+ identify
1821
+ idle
1822
+ ignore
1823
+ ill
1824
+ illegal
1825
+ illness
1826
+ image
1827
+ imitate
1828
+ immense
1829
+ immune
1830
+ impact
1831
+ impose
1832
+ improve
1833
+ impulse
1834
+ inch
1835
+ include
1836
+ income
1837
+ increase
1838
+ index
1839
+ indicate
1840
+ indoor
1841
+ industry
1842
+ infant
1843
+ inflict
1844
+ inform
1845
+ inhale
1846
+ inherit
1847
+ initial
1848
+ inject
1849
+ injury
1850
+ inmate
1851
+ inner
1852
+ innocent
1853
+ input
1854
+ inquiry
1855
+ insane
1856
+ insect
1857
+ inside
1858
+ inspire
1859
+ install
1860
+ intact
1861
+ interest
1862
+ into
1863
+ invest
1864
+ invite
1865
+ involve
1866
+ iron
1867
+ island
1868
+ isolate
1869
+ issue
1870
+ item
1871
+ ivory
1872
+ jacket
1873
+ jaguar
1874
+ jar
1875
+ jazz
1876
+ jealous
1877
+ jeans
1878
+ jelly
1879
+ jewel
1880
+ job
1881
+ join
1882
+ joke
1883
+ journey
1884
+ joy
1885
+ judge
1886
+ juice
1887
+ jump
1888
+ jungle
1889
+ junior
1890
+ junk
1891
+ just
1892
+ kangaroo
1893
+ keen
1894
+ keep
1895
+ ketchup
1896
+ key
1897
+ kick
1898
+ kid
1899
+ kidney
1900
+ kind
1901
+ kingdom
1902
+ kiss
1903
+ kit
1904
+ kitchen
1905
+ kite
1906
+ kitten
1907
+ kiwi
1908
+ knee
1909
+ knife
1910
+ knock
1911
+ know
1912
+ lab
1913
+ label
1914
+ labor
1915
+ ladder
1916
+ lady
1917
+ lake
1918
+ lamp
1919
+ language
1920
+ laptop
1921
+ large
1922
+ later
1923
+ latin
1924
+ laugh
1925
+ laundry
1926
+ lava
1927
+ law
1928
+ lawn
1929
+ lawsuit
1930
+ layer
1931
+ lazy
1932
+ leader
1933
+ leaf
1934
+ learn
1935
+ leave
1936
+ lecture
1937
+ left
1938
+ leg
1939
+ legal
1940
+ legend
1941
+ leisure
1942
+ lemon
1943
+ lend
1944
+ length
1945
+ lens
1946
+ leopard
1947
+ lesson
1948
+ letter
1949
+ level
1950
+ liar
1951
+ liberty
1952
+ library
1953
+ license
1954
+ life
1955
+ lift
1956
+ light
1957
+ like
1958
+ limb
1959
+ limit
1960
+ link
1961
+ lion
1962
+ liquid
1963
+ list
1964
+ little
1965
+ live
1966
+ lizard
1967
+ load
1968
+ loan
1969
+ lobster
1970
+ local
1971
+ lock
1972
+ logic
1973
+ lonely
1974
+ long
1975
+ loop
1976
+ lottery
1977
+ loud
1978
+ lounge
1979
+ love
1980
+ loyal
1981
+ lucky
1982
+ luggage
1983
+ lumber
1984
+ lunar
1985
+ lunch
1986
+ luxury
1987
+ lyrics
1988
+ machine
1989
+ mad
1990
+ magic
1991
+ magnet
1992
+ maid
1993
+ mail
1994
+ main
1995
+ major
1996
+ make
1997
+ mammal
1998
+ man
1999
+ manage
2000
+ mandate
2001
+ mango
2002
+ mansion
2003
+ manual
2004
+ maple
2005
+ marble
2006
+ march
2007
+ margin
2008
+ marine
2009
+ market
2010
+ marriage
2011
+ mask
2012
+ mass
2013
+ master
2014
+ match
2015
+ material
2016
+ math
2017
+ matrix
2018
+ matter
2019
+ maximum
2020
+ maze
2021
+ meadow
2022
+ mean
2023
+ measure
2024
+ meat
2025
+ mechanic
2026
+ medal
2027
+ media
2028
+ melody
2029
+ melt
2030
+ member
2031
+ memory
2032
+ mention
2033
+ menu
2034
+ mercy
2035
+ merge
2036
+ merit
2037
+ merry
2038
+ mesh
2039
+ message
2040
+ metal
2041
+ method
2042
+ middle
2043
+ midnight
2044
+ milk
2045
+ million
2046
+ mimic
2047
+ mind
2048
+ minimum
2049
+ minor
2050
+ minute
2051
+ miracle
2052
+ mirror
2053
+ misery
2054
+ miss
2055
+ mistake
2056
+ mix
2057
+ mixed
2058
+ mixture
2059
+ mobile
2060
+ model
2061
+ modify
2062
+ mom
2063
+ moment
2064
+ monitor
2065
+ monkey
2066
+ monster
2067
+ month
2068
+ moon
2069
+ moral
2070
+ more
2071
+ morning
2072
+ mosquito
2073
+ mother
2074
+ motion
2075
+ motor
2076
+ mountain
2077
+ mouse
2078
+ move
2079
+ movie
2080
+ much
2081
+ muffin
2082
+ mule
2083
+ multiply
2084
+ muscle
2085
+ museum
2086
+ mushroom
2087
+ music
2088
+ must
2089
+ mutual
2090
+ myself
2091
+ mystery
2092
+ myth
2093
+ naive
2094
+ name
2095
+ napkin
2096
+ narrow
2097
+ nasty
2098
+ nation
2099
+ nature
2100
+ near
2101
+ neck
2102
+ need
2103
+ negative
2104
+ neglect
2105
+ neither
2106
+ nephew
2107
+ nerve
2108
+ nest
2109
+ net
2110
+ network
2111
+ neutral
2112
+ never
2113
+ news
2114
+ next
2115
+ nice
2116
+ night
2117
+ noble
2118
+ noise
2119
+ nominee
2120
+ noodle
2121
+ normal
2122
+ north
2123
+ nose
2124
+ notable
2125
+ note
2126
+ nothing
2127
+ notice
2128
+ novel
2129
+ now
2130
+ nuclear
2131
+ number
2132
+ nurse
2133
+ nut
2134
+ oak
2135
+ obey
2136
+ object
2137
+ oblige
2138
+ obscure
2139
+ observe
2140
+ obtain
2141
+ obvious
2142
+ occur
2143
+ ocean
2144
+ october
2145
+ odor
2146
+ off
2147
+ offer
2148
+ office
2149
+ often
2150
+ oil
2151
+ okay
2152
+ old
2153
+ olive
2154
+ olympic
2155
+ omit
2156
+ once
2157
+ one
2158
+ onion
2159
+ online
2160
+ only
2161
+ open
2162
+ opera
2163
+ opinion
2164
+ oppose
2165
+ option
2166
+ orange
2167
+ orbit
2168
+ orchard
2169
+ order
2170
+ ordinary
2171
+ organ
2172
+ orient
2173
+ original
2174
+ orphan
2175
+ ostrich
2176
+ other
2177
+ outdoor
2178
+ outer
2179
+ output
2180
+ outside
2181
+ oval
2182
+ oven
2183
+ over
2184
+ own
2185
+ owner
2186
+ oxygen
2187
+ oyster
2188
+ ozone
2189
+ pact
2190
+ paddle
2191
+ page
2192
+ pair
2193
+ palace
2194
+ palm
2195
+ panda
2196
+ panel
2197
+ panic
2198
+ panther
2199
+ paper
2200
+ parade
2201
+ parent
2202
+ park
2203
+ parrot
2204
+ party
2205
+ pass
2206
+ patch
2207
+ path
2208
+ patient
2209
+ patrol
2210
+ pattern
2211
+ pause
2212
+ pave
2213
+ payment
2214
+ peace
2215
+ peanut
2216
+ pear
2217
+ peasant
2218
+ pelican
2219
+ pen
2220
+ penalty
2221
+ pencil
2222
+ people
2223
+ pepper
2224
+ perfect
2225
+ permit
2226
+ person
2227
+ pet
2228
+ phone
2229
+ photo
2230
+ phrase
2231
+ physical
2232
+ piano
2233
+ picnic
2234
+ picture
2235
+ piece
2236
+ pig
2237
+ pigeon
2238
+ pill
2239
+ pilot
2240
+ pink
2241
+ pioneer
2242
+ pipe
2243
+ pistol
2244
+ pitch
2245
+ pizza
2246
+ place
2247
+ planet
2248
+ plastic
2249
+ plate
2250
+ play
2251
+ please
2252
+ pledge
2253
+ pluck
2254
+ plug
2255
+ plunge
2256
+ poem
2257
+ poet
2258
+ point
2259
+ polar
2260
+ pole
2261
+ police
2262
+ pond
2263
+ pony
2264
+ pool
2265
+ popular
2266
+ portion
2267
+ position
2268
+ possible
2269
+ post
2270
+ potato
2271
+ pottery
2272
+ poverty
2273
+ powder
2274
+ power
2275
+ practice
2276
+ praise
2277
+ predict
2278
+ prefer
2279
+ prepare
2280
+ present
2281
+ pretty
2282
+ prevent
2283
+ price
2284
+ pride
2285
+ primary
2286
+ print
2287
+ priority
2288
+ prison
2289
+ private
2290
+ prize
2291
+ problem
2292
+ process
2293
+ produce
2294
+ profit
2295
+ program
2296
+ project
2297
+ promote
2298
+ proof
2299
+ property
2300
+ prosper
2301
+ protect
2302
+ proud
2303
+ provide
2304
+ public
2305
+ pudding
2306
+ pull
2307
+ pulp
2308
+ pulse
2309
+ pumpkin
2310
+ punch
2311
+ pupil
2312
+ puppy
2313
+ purchase
2314
+ purity
2315
+ purpose
2316
+ purse
2317
+ push
2318
+ put
2319
+ puzzle
2320
+ pyramid
2321
+ quality
2322
+ quantum
2323
+ quarter
2324
+ question
2325
+ quick
2326
+ quit
2327
+ quiz
2328
+ quote
2329
+ rabbit
2330
+ raccoon
2331
+ race
2332
+ rack
2333
+ radar
2334
+ radio
2335
+ rail
2336
+ rain
2337
+ raise
2338
+ rally
2339
+ ramp
2340
+ ranch
2341
+ random
2342
+ range
2343
+ rapid
2344
+ rare
2345
+ rate
2346
+ rather
2347
+ raven
2348
+ raw
2349
+ razor
2350
+ ready
2351
+ real
2352
+ reason
2353
+ rebel
2354
+ rebuild
2355
+ recall
2356
+ receive
2357
+ recipe
2358
+ record
2359
+ recycle
2360
+ reduce
2361
+ reflect
2362
+ reform
2363
+ refuse
2364
+ region
2365
+ regret
2366
+ regular
2367
+ reject
2368
+ relax
2369
+ release
2370
+ relief
2371
+ rely
2372
+ remain
2373
+ remember
2374
+ remind
2375
+ remove
2376
+ render
2377
+ renew
2378
+ rent
2379
+ reopen
2380
+ repair
2381
+ repeat
2382
+ replace
2383
+ report
2384
+ require
2385
+ rescue
2386
+ resemble
2387
+ resist
2388
+ resource
2389
+ response
2390
+ result
2391
+ retire
2392
+ retreat
2393
+ return
2394
+ reunion
2395
+ reveal
2396
+ review
2397
+ reward
2398
+ rhythm
2399
+ rib
2400
+ ribbon
2401
+ rice
2402
+ rich
2403
+ ride
2404
+ ridge
2405
+ rifle
2406
+ right
2407
+ rigid
2408
+ ring
2409
+ riot
2410
+ ripple
2411
+ risk
2412
+ ritual
2413
+ rival
2414
+ river
2415
+ road
2416
+ roast
2417
+ robot
2418
+ robust
2419
+ rocket
2420
+ romance
2421
+ roof
2422
+ rookie
2423
+ room
2424
+ rose
2425
+ rotate
2426
+ rough
2427
+ round
2428
+ route
2429
+ royal
2430
+ rubber
2431
+ rude
2432
+ rug
2433
+ rule
2434
+ run
2435
+ runway
2436
+ rural
2437
+ sad
2438
+ saddle
2439
+ sadness
2440
+ safe
2441
+ sail
2442
+ salad
2443
+ salmon
2444
+ salon
2445
+ salt
2446
+ salute
2447
+ same
2448
+ sample
2449
+ sand
2450
+ satisfy
2451
+ satoshi
2452
+ sauce
2453
+ sausage
2454
+ save
2455
+ say
2456
+ scale
2457
+ scan
2458
+ scare
2459
+ scatter
2460
+ scene
2461
+ scheme
2462
+ school
2463
+ science
2464
+ scissors
2465
+ scorpion
2466
+ scout
2467
+ scrap
2468
+ screen
2469
+ script
2470
+ scrub
2471
+ sea
2472
+ search
2473
+ season
2474
+ seat
2475
+ second
2476
+ secret
2477
+ section
2478
+ security
2479
+ seed
2480
+ seek
2481
+ segment
2482
+ select
2483
+ sell
2484
+ seminar
2485
+ senior
2486
+ sense
2487
+ sentence
2488
+ series
2489
+ service
2490
+ session
2491
+ settle
2492
+ setup
2493
+ seven
2494
+ shadow
2495
+ shaft
2496
+ shallow
2497
+ share
2498
+ shed
2499
+ shell
2500
+ sheriff
2501
+ shield
2502
+ shift
2503
+ shine
2504
+ ship
2505
+ shiver
2506
+ shock
2507
+ shoe
2508
+ shoot
2509
+ shop
2510
+ short
2511
+ shoulder
2512
+ shove
2513
+ shrimp
2514
+ shrug
2515
+ shuffle
2516
+ shy
2517
+ sibling
2518
+ sick
2519
+ side
2520
+ siege
2521
+ sight
2522
+ sign
2523
+ silent
2524
+ silk
2525
+ silly
2526
+ silver
2527
+ similar
2528
+ simple
2529
+ since
2530
+ sing
2531
+ siren
2532
+ sister
2533
+ situate
2534
+ six
2535
+ size
2536
+ skate
2537
+ sketch
2538
+ ski
2539
+ skill
2540
+ skin
2541
+ skirt
2542
+ skull
2543
+ slab
2544
+ slam
2545
+ sleep
2546
+ slender
2547
+ slice
2548
+ slide
2549
+ slight
2550
+ slim
2551
+ slogan
2552
+ slot
2553
+ slow
2554
+ slush
2555
+ small
2556
+ smart
2557
+ smile
2558
+ smoke
2559
+ smooth
2560
+ snack
2561
+ snake
2562
+ snap
2563
+ sniff
2564
+ snow
2565
+ soap
2566
+ soccer
2567
+ social
2568
+ sock
2569
+ soda
2570
+ soft
2571
+ solar
2572
+ soldier
2573
+ solid
2574
+ solution
2575
+ solve
2576
+ someone
2577
+ song
2578
+ soon
2579
+ sorry
2580
+ sort
2581
+ soul
2582
+ sound
2583
+ soup
2584
+ source
2585
+ south
2586
+ space
2587
+ spare
2588
+ spatial
2589
+ spawn
2590
+ speak
2591
+ special
2592
+ speed
2593
+ spell
2594
+ spend
2595
+ sphere
2596
+ spice
2597
+ spider
2598
+ spike
2599
+ spin
2600
+ spirit
2601
+ split
2602
+ spoil
2603
+ sponsor
2604
+ spoon
2605
+ sport
2606
+ spot
2607
+ spray
2608
+ spread
2609
+ spring
2610
+ spy
2611
+ square
2612
+ squeeze
2613
+ squirrel
2614
+ stable
2615
+ stadium
2616
+ staff
2617
+ stage
2618
+ stairs
2619
+ stamp
2620
+ stand
2621
+ start
2622
+ state
2623
+ stay
2624
+ steak
2625
+ steel
2626
+ stem
2627
+ step
2628
+ stereo
2629
+ stick
2630
+ still
2631
+ sting
2632
+ stock
2633
+ stomach
2634
+ stone
2635
+ stool
2636
+ story
2637
+ stove
2638
+ strategy
2639
+ street
2640
+ strike
2641
+ strong
2642
+ struggle
2643
+ student
2644
+ stuff
2645
+ stumble
2646
+ style
2647
+ subject
2648
+ submit
2649
+ subway
2650
+ success
2651
+ such
2652
+ sudden
2653
+ suffer
2654
+ sugar
2655
+ suggest
2656
+ suit
2657
+ summer
2658
+ sun
2659
+ sunny
2660
+ sunset
2661
+ super
2662
+ supply
2663
+ supreme
2664
+ sure
2665
+ surface
2666
+ surge
2667
+ surprise
2668
+ surround
2669
+ survey
2670
+ suspect
2671
+ sustain
2672
+ swallow
2673
+ swamp
2674
+ swap
2675
+ swarm
2676
+ swear
2677
+ sweet
2678
+ swift
2679
+ swim
2680
+ swing
2681
+ switch
2682
+ sword
2683
+ symbol
2684
+ symptom
2685
+ syrup
2686
+ system
2687
+ table
2688
+ tackle
2689
+ tag
2690
+ tail
2691
+ talent
2692
+ talk
2693
+ tank
2694
+ tape
2695
+ target
2696
+ task
2697
+ taste
2698
+ tattoo
2699
+ taxi
2700
+ teach
2701
+ team
2702
+ tell
2703
+ ten
2704
+ tenant
2705
+ tennis
2706
+ tent
2707
+ term
2708
+ test
2709
+ text
2710
+ thank
2711
+ that
2712
+ theme
2713
+ then
2714
+ theory
2715
+ there
2716
+ they
2717
+ thing
2718
+ this
2719
+ thought
2720
+ three
2721
+ thrive
2722
+ throw
2723
+ thumb
2724
+ thunder
2725
+ ticket
2726
+ tide
2727
+ tiger
2728
+ tilt
2729
+ timber
2730
+ time
2731
+ tiny
2732
+ tip
2733
+ tired
2734
+ tissue
2735
+ title
2736
+ toast
2737
+ tobacco
2738
+ today
2739
+ toddler
2740
+ toe
2741
+ together
2742
+ toilet
2743
+ token
2744
+ tomato
2745
+ tomorrow
2746
+ tone
2747
+ tongue
2748
+ tonight
2749
+ tool
2750
+ tooth
2751
+ top
2752
+ topic
2753
+ topple
2754
+ torch
2755
+ tornado
2756
+ tortoise
2757
+ toss
2758
+ total
2759
+ tourist
2760
+ toward
2761
+ tower
2762
+ town
2763
+ toy
2764
+ track
2765
+ trade
2766
+ traffic
2767
+ tragic
2768
+ train
2769
+ transfer
2770
+ trap
2771
+ trash
2772
+ travel
2773
+ tray
2774
+ treat
2775
+ tree
2776
+ trend
2777
+ trial
2778
+ tribe
2779
+ trick
2780
+ trigger
2781
+ trim
2782
+ trip
2783
+ trophy
2784
+ trouble
2785
+ truck
2786
+ true
2787
+ truly
2788
+ trumpet
2789
+ trust
2790
+ truth
2791
+ try
2792
+ tube
2793
+ tuition
2794
+ tumble
2795
+ tuna
2796
+ tunnel
2797
+ turkey
2798
+ turn
2799
+ turtle
2800
+ twelve
2801
+ twenty
2802
+ twice
2803
+ twin
2804
+ twist
2805
+ two
2806
+ type
2807
+ typical
2808
+ ugly
2809
+ umbrella
2810
+ unable
2811
+ unaware
2812
+ uncle
2813
+ uncover
2814
+ under
2815
+ undo
2816
+ unfair
2817
+ unfold
2818
+ unhappy
2819
+ uniform
2820
+ unique
2821
+ unit
2822
+ universe
2823
+ unknown
2824
+ unlock
2825
+ until
2826
+ unusual
2827
+ unveil
2828
+ update
2829
+ upgrade
2830
+ uphold
2831
+ upon
2832
+ upper
2833
+ upset
2834
+ urban
2835
+ urge
2836
+ usage
2837
+ use
2838
+ used
2839
+ useful
2840
+ useless
2841
+ usual
2842
+ utility
2843
+ vacant
2844
+ vacuum
2845
+ vague
2846
+ valid
2847
+ valley
2848
+ valve
2849
+ van
2850
+ vanish
2851
+ vapor
2852
+ various
2853
+ vast
2854
+ vault
2855
+ vehicle
2856
+ velvet
2857
+ vendor
2858
+ venture
2859
+ venue
2860
+ verb
2861
+ verify
2862
+ version
2863
+ very
2864
+ vessel
2865
+ veteran
2866
+ viable
2867
+ vibrant
2868
+ vicious
2869
+ victory
2870
+ video
2871
+ view
2872
+ village
2873
+ vintage
2874
+ violin
2875
+ virtual
2876
+ virus
2877
+ visa
2878
+ visit
2879
+ visual
2880
+ vital
2881
+ vivid
2882
+ vocal
2883
+ voice
2884
+ void
2885
+ volcano
2886
+ volume
2887
+ vote
2888
+ voyage
2889
+ wage
2890
+ wagon
2891
+ wait
2892
+ walk
2893
+ wall
2894
+ walnut
2895
+ want
2896
+ warfare
2897
+ warm
2898
+ warrior
2899
+ wash
2900
+ wasp
2901
+ waste
2902
+ water
2903
+ wave
2904
+ way
2905
+ wealth
2906
+ weapon
2907
+ wear
2908
+ weasel
2909
+ weather
2910
+ web
2911
+ wedding
2912
+ weekend
2913
+ weird
2914
+ welcome
2915
+ west
2916
+ wet
2917
+ whale
2918
+ what
2919
+ wheat
2920
+ wheel
2921
+ when
2922
+ where
2923
+ whip
2924
+ whisper
2925
+ wide
2926
+ width
2927
+ wife
2928
+ wild
2929
+ will
2930
+ win
2931
+ window
2932
+ wine
2933
+ wing
2934
+ wink
2935
+ winner
2936
+ winter
2937
+ wire
2938
+ wisdom
2939
+ wise
2940
+ wish
2941
+ witness
2942
+ wolf
2943
+ woman
2944
+ wonder
2945
+ wood
2946
+ wool
2947
+ word
2948
+ work
2949
+ world
2950
+ worry
2951
+ worth
2952
+ wrap
2953
+ wreck
2954
+ wrestle
2955
+ wrist
2956
+ write
2957
+ wrong
2958
+ yard
2959
+ year
2960
+ yellow
2961
+ you
2962
+ young
2963
+ youth
2964
+ zebra
2965
+ zero
2966
+ zone
2967
+ zoo`.split("\n");
2968
+
2969
+ // src/secret-check.ts
2970
+ var MAX_FEEDBACK_TEXT_LENGTH = 4e3;
2971
+ function refusal(layer, reason, message) {
2972
+ return { layer, reason, message };
2973
+ }
2974
+ var LABELLED_PATTERNS = [
2975
+ { reason: "agent_api_key", re: /sk_agent_[A-Za-z0-9]/ },
2976
+ { reason: "setup_token", re: /hv_setup_[A-Za-z0-9]/ },
2977
+ // A session JWT: three dot-separated base64url parts, each non-empty.
2978
+ // `\beyJ` (not the looser `\bey`), which is the base64url encoding of `{"`
2979
+ // — every real JWT header starts there, and the tighter anchor stops
2980
+ // ordinary words like "eyebrow" or "eyelet" from matching their own tail.
2981
+ { reason: "session_jwt", re: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/ },
2982
+ // key= / api_key= / api-key= / apikey= / token= / secret=, `\b`-bounded —
2983
+ // the same boundary `redact-vendor-secrets.ts:19` uses.
2984
+ { reason: "labelled_parameter", re: /\b(api[_-]?key|key|token|secret)=[^&\s"'\\)]+/i },
2985
+ { reason: "url_credentials", re: /https?:\/\/[^\s/@]+:[^\s@]+@/i },
2986
+ { reason: "rpc_path_key", re: /\/(?:rpc|v2)\/[A-Za-z0-9_-]{16,}/ }
2987
+ ];
2988
+ function checkLabelledSecrets(text) {
2989
+ for (const { reason, re } of LABELLED_PATTERNS) {
2990
+ if (re.test(text)) {
2991
+ return refusal(
2992
+ 1,
2993
+ reason,
2994
+ "Refusing to send: the text looks like it contains a secret (a credential label or prefix matched). Remove it and try again."
2995
+ );
2996
+ }
2997
+ }
2998
+ return null;
2999
+ }
3000
+ function normalize(value) {
3001
+ return value.trim().toLowerCase().replace(/^0x/, "");
3002
+ }
3003
+ async function readJsonStringField(path, field) {
3004
+ try {
3005
+ const raw = await promises.readFile(path, "utf8");
3006
+ const parsed = JSON.parse(raw);
3007
+ const value = parsed[field];
3008
+ return typeof value === "string" && value.length > 0 ? value : null;
3009
+ } catch {
3010
+ return null;
3011
+ }
3012
+ }
3013
+ async function readLocalSecrets(sources = {}) {
3014
+ const secrets = /* @__PURE__ */ new Set();
3015
+ const env = sources.env ?? process.env;
3016
+ const envKey = env.HAVEN_DELEGATE_KEY;
3017
+ if (envKey) secrets.add(normalize(envKey));
3018
+ if (sources.sessionToken) secrets.add(normalize(sources.sessionToken));
3019
+ const root = sources.baseDir ?? path.join(os.homedir(), ".haven", "agents");
3020
+ let entries = [];
3021
+ try {
3022
+ entries = await promises.readdir(root);
3023
+ } catch {
3024
+ return secrets;
3025
+ }
3026
+ for (const entry of entries) {
3027
+ const dir = path.join(root, entry);
3028
+ const delegateKey = await readJsonStringField(path.join(dir, "signer.json"), "delegate_key");
3029
+ if (delegateKey) secrets.add(normalize(delegateKey));
3030
+ const apiKey = await readJsonStringField(path.join(dir, "identity.json"), "api_key");
3031
+ if (apiKey) secrets.add(normalize(apiKey));
3032
+ const pendingKey = await readJsonStringField(path.join(dir, "rekey-pending.json"), "new_delegate_key");
3033
+ if (pendingKey) secrets.add(normalize(pendingKey));
3034
+ }
3035
+ return secrets;
3036
+ }
3037
+ function checkExactSecretMatch(text, secrets) {
3038
+ if (secrets.size === 0) return null;
3039
+ const haystack = text.toLowerCase();
3040
+ for (const secret of secrets) {
3041
+ if (secret.length >= 8 && haystack.includes(secret)) {
3042
+ return refusal(
3043
+ 2,
3044
+ "local_credential",
3045
+ "Refusing to send: the text contains a credential this machine holds (an agent delegate key, an agent API key, a pending re-key, or your own session token). Remove it and try again."
3046
+ );
3047
+ }
3048
+ }
3049
+ return null;
3050
+ }
3051
+ function findHexTokenCandidates(text) {
3052
+ const matches = text.matchAll(/(?<![0-9a-fA-F])(0x)?([0-9a-fA-F]{64})(?![0-9a-fA-F])/g);
3053
+ return [...matches].map((m) => m[2]);
3054
+ }
3055
+ function deriveAddressFromHexToken(token) {
3056
+ try {
3057
+ const ecdh = crypto.createECDH("secp256k1");
3058
+ ecdh.setPrivateKey(Buffer.from(token, "hex"));
3059
+ const uncompressed = ecdh.getPublicKey();
3060
+ const hash = keccak_256(uncompressed.subarray(1));
3061
+ return `0x${Buffer.from(hash.subarray(12)).toString("hex")}`;
3062
+ } catch {
3063
+ return null;
3064
+ }
3065
+ }
3066
+ async function collectKeyBackedAddresses(api) {
3067
+ const addresses = /* @__PURE__ */ new Set();
3068
+ const { agents } = await api.get("/agents");
3069
+ for (const agent of agents) {
3070
+ if (agent.delegate_address) addresses.add(agent.delegate_address.toLowerCase());
3071
+ }
3072
+ const { accounts } = await api.get("/user/accounts");
3073
+ for (const account of accounts) {
3074
+ if (!account.account_address) continue;
3075
+ const signers = await api.get(
3076
+ `/accounts/hybrid/${account.account_address}/signers?chain_id=${account.chain_id}`
3077
+ );
3078
+ if (signers.owner_address) addresses.add(signers.owner_address.toLowerCase());
3079
+ }
3080
+ return addresses;
3081
+ }
3082
+ async function checkKeyBackedAddresses(text, api) {
3083
+ const candidates = findHexTokenCandidates(text);
3084
+ if (candidates.length === 0) return null;
3085
+ let keyBacked;
3086
+ try {
3087
+ keyBacked = await collectKeyBackedAddresses(api);
3088
+ } catch (err) {
3089
+ if (err instanceof CliApiError && (err.status === 401 || err.status === 403)) {
3090
+ return refusal(
3091
+ 3,
3092
+ "address_check_unavailable",
3093
+ "Refusing to send: could not verify whether the text contains a private key \u2014 your session cannot read the data this check needs. Run `haven login` again; retrying this submission will not help."
3094
+ );
3095
+ }
3096
+ if (err instanceof CliApiError && err.status === 409) {
3097
+ return refusal(
3098
+ 3,
3099
+ "address_check_unavailable",
3100
+ "Refusing to send: could not verify whether the text contains a private key \u2014 one of your accounts' signer configuration is unknown, so there is nothing to compare against. Retrying this submission will not help; contact support."
3101
+ );
3102
+ }
3103
+ return refusal(
3104
+ 3,
3105
+ "address_check_unavailable",
3106
+ "Refusing to send: could not verify whether the text contains a private key (the address check failed). Retry once Haven is reachable."
3107
+ );
3108
+ }
3109
+ for (const token of candidates) {
3110
+ const address = deriveAddressFromHexToken(token);
3111
+ if (address && keyBacked.has(address.toLowerCase())) {
3112
+ return refusal(
3113
+ 3,
3114
+ "private_key",
3115
+ "Refusing to send: the text contains what looks like the private key for one of your agents or accounts. Remove it and try again."
3116
+ );
3117
+ }
3118
+ }
3119
+ return null;
3120
+ }
3121
+ var BIP39_WORDS = new Set(wordlist.map((word) => word.toLowerCase()));
3122
+ var MIN_RECOVERY_PHRASE_WORDS = 12;
3123
+ function checkRecoveryPhrase(text) {
3124
+ const tokens = text.split(/\s+/).map((t) => t.replace(/^[^A-Za-z]+|[^A-Za-z]+$/g, "").toLowerCase());
3125
+ let run2 = 0;
3126
+ for (const token of tokens) {
3127
+ if (token && BIP39_WORDS.has(token)) {
3128
+ run2 += 1;
3129
+ if (run2 >= MIN_RECOVERY_PHRASE_WORDS) {
3130
+ return refusal(
3131
+ 4,
3132
+ "recovery_phrase",
3133
+ "Refusing to send: the text looks like it contains a recovery phrase (12 or more consecutive seed words). Remove it and try again."
3134
+ );
3135
+ }
3136
+ } else {
3137
+ run2 = 0;
3138
+ }
3139
+ }
3140
+ return null;
3141
+ }
3142
+ async function checkTextForSecrets(text, deps) {
3143
+ const layer1 = checkLabelledSecrets(text);
3144
+ if (layer1) return layer1;
3145
+ const localSecrets = await readLocalSecrets(deps.localSecrets);
3146
+ const layer2 = checkExactSecretMatch(text, localSecrets);
3147
+ if (layer2) return layer2;
3148
+ const layer3 = await checkKeyBackedAddresses(text, deps.api);
3149
+ if (layer3) return layer3;
3150
+ return checkRecoveryPhrase(text);
3151
+ }
3152
+
621
3153
  // src/commands.ts
622
3154
  var DEFAULT_API = "https://havenbackend-production-8a00.up.railway.app";
623
- var CLI_VERSION = "0.8.0-alpha.0";
3155
+ var CLI_VERSION = "0.9.0-alpha.0";
624
3156
  var CLI_CLIENT_IDENTITY = `@haven_ai/cli/${CLI_VERSION}`;
625
3157
  function accountAddressOf(s) {
626
3158
  return s.account_address ?? "";
@@ -641,6 +3173,7 @@ async function run(argv, deps = {}) {
641
3173
  out,
642
3174
  err,
643
3175
  env: deps.env ?? process.env,
3176
+ credentialsBaseDir: deps.credentialsBaseDir,
644
3177
  o
645
3178
  };
646
3179
  let args;
@@ -721,6 +3254,8 @@ async function dispatch(args, d) {
721
3254
  return cmdContactsAdd(args, d);
722
3255
  case "contacts remove":
723
3256
  return cmdContactsRemove(args, d);
3257
+ case "feedback submit":
3258
+ return cmdFeedbackSubmit(args, d);
724
3259
  default:
725
3260
  throw new UsageError(`Unknown command: ${key}.`, "Run `haven --help` for the command list.");
726
3261
  }
@@ -1171,12 +3706,6 @@ async function cmdBudgetRevoke(args, d) {
1171
3706
  if (row.status === "revoked") {
1172
3707
  throw new HavenCliError(`Delegation ${hash} is already revoked.`, EXIT.refused);
1173
3708
  }
1174
- if (row.status === "replaced") {
1175
- throw new HavenCliError(
1176
- `Delegation ${hash} was already replaced by a newer grant \u2014 nothing to revoke.`,
1177
- EXIT.refused
1178
- );
1179
- }
1180
3709
  const prepared = await api.post(`/agents/${id}/delegations/${hash}/revoke`, {});
1181
3710
  const revokeUrl = prepared.revocation_url;
1182
3711
  if (!revokeUrl) {
@@ -1227,25 +3756,63 @@ async function cmdAgentLifecycle(args, d, action) {
1227
3756
  const { api } = await authed(args, d);
1228
3757
  await api.post(`/agents/${id}/${action}`);
1229
3758
  const status = action === "pause" ? "paused" : "resumed";
1230
- emit(d, args.flags.json, { ok: true, agent_id: id, status }, () => `Agent ${id} ${status}.`);
3759
+ emit(
3760
+ d,
3761
+ args.flags.json,
3762
+ { ok: true, agent_id: id, status },
3763
+ () => action === "pause" ? `Agent ${id} paused: payments through Haven are blocked, but its budget stays live on-chain (end it with \`haven budget revoke\`).` : `Agent ${id} resumed: payments through Haven are allowed again.`
3764
+ );
1231
3765
  return EXIT.ok;
1232
3766
  }
1233
3767
  async function cmdAgentRevoke(args, d) {
1234
3768
  const id = args.positionals[0];
1235
- if (!id) throw new UsageError("Usage: haven agents revoke <id> --yes");
3769
+ if (!id) throw new UsageError("Usage: haven agents revoke <id> [--keep-budget] --yes");
1236
3770
  if (!args.flags.yes) {
1237
3771
  throw new UsageError(
1238
- `This permanently revokes agent ${id}.`,
1239
- "Re-run with --yes to confirm. Revoke is terminal \u2014 the agent cannot go back to active."
3772
+ `This permanently retires agent ${id} in Haven.`,
3773
+ "Re-run with --yes to confirm. Revoke is terminal \u2014 the agent cannot go back to active. It ends no budget on-chain: end each live budget with `haven budget revoke <agentId> <delegationHash>`."
1240
3774
  );
1241
3775
  }
1242
3776
  const { api } = await authed(args, d);
3777
+ let live = [];
3778
+ try {
3779
+ const agent = await api.get(`/agents/${id}`);
3780
+ if ((agent.live_delegation_count ?? 0) > 0) {
3781
+ const { delegations } = await api.get(`/agents/${id}/delegations`);
3782
+ live = delegations.filter((r) => r.status === "pending" || r.status === "active" || r.status === "replaced");
3783
+ }
3784
+ } catch (err) {
3785
+ d.o.note(`Couldn't check budgets for agent ${id}, so the revocation was not attempted.`);
3786
+ throw err;
3787
+ }
3788
+ if (live.length > 0 && !args.flags.keepBudget) {
3789
+ throw new HavenCliError(
3790
+ `Agent ${id} still has a live on-chain budget. Revoking now would leave it redeemable by the delegate key.`,
3791
+ EXIT.refused,
3792
+ [
3793
+ ...live.map((r) => `haven budget revoke ${id} ${r.delegation_hash} # ${r.status}`),
3794
+ "Remove agent\u2026 in the dashboard ends every live budget with one signature, before revoking.",
3795
+ "To revoke the agent anyway and leave the budget live on-chain, re-run with --keep-budget."
3796
+ ].join("\n"),
3797
+ // The machine branch: a script skips the prose and reads the rows.
3798
+ { live_delegations: live.map(({ delegation_hash, status }) => ({ delegation_hash, status })) }
3799
+ );
3800
+ }
1243
3801
  await api.post(`/agents/${id}/revoke`);
1244
3802
  emit(
1245
3803
  d,
1246
3804
  args.flags.json,
1247
- { ok: true, agent_id: id, status: "revoked" },
1248
- () => `Agent ${id} revoked. To also remove its on-chain allowance, use the dashboard.`
3805
+ live.length > 0 ? {
3806
+ ok: true,
3807
+ agent_id: id,
3808
+ status: "revoked",
3809
+ live_delegations: live.map(({ delegation_hash, status }) => ({ delegation_hash, status }))
3810
+ } : { ok: true, agent_id: id, status: "revoked" },
3811
+ () => live.length > 0 ? [
3812
+ `Agent ${id} revoked.`,
3813
+ `Its budget is still live on-chain: the delegate key can keep spending until each delegation is revoked.`,
3814
+ ...live.map((r) => `haven budget revoke ${id} ${r.delegation_hash} # ${r.status}`)
3815
+ ].join("\n") : `Agent ${id} revoked in Haven. It had no live budget on-chain.`
1249
3816
  );
1250
3817
  return EXIT.ok;
1251
3818
  }
@@ -1537,6 +4104,30 @@ async function cmdContactsRemove(args, d) {
1537
4104
  emit(d, args.flags.json, { ok: true, contact_id: id, removed: true }, () => `Contact ${id} removed.`);
1538
4105
  return EXIT.ok;
1539
4106
  }
4107
+ async function cmdFeedbackSubmit(args, d) {
4108
+ const text = args.positionals.join(" ").trim();
4109
+ if (!text) throw new UsageError('Usage: haven feedback submit "<text>"');
4110
+ if ([...text].length > MAX_FEEDBACK_TEXT_LENGTH) {
4111
+ throw new UsageError(`Feedback text must be ${MAX_FEEDBACK_TEXT_LENGTH} characters (code points) or fewer.`);
4112
+ }
4113
+ const { session, api } = await authed(args, d);
4114
+ const refusal2 = await checkTextForSecrets(text, {
4115
+ api,
4116
+ localSecrets: { sessionToken: session.token, env: d.env, baseDir: d.credentialsBaseDir }
4117
+ });
4118
+ if (refusal2) {
4119
+ throw new HavenCliError(refusal2.message, EXIT.refused, `layer ${refusal2.layer}: ${refusal2.reason}`, {
4120
+ layer: refusal2.layer,
4121
+ reason: refusal2.reason
4122
+ });
4123
+ }
4124
+ d.o.note(
4125
+ "Reminder: never include a password or any other credential in feedback text \u2014 this check cannot recognise one by shape."
4126
+ );
4127
+ const result = await api.post("/feedback", { text });
4128
+ emit(d, args.flags.json, { ok: true, ...result }, () => `Feedback submitted (id ${result.id}).`);
4129
+ return EXIT.ok;
4130
+ }
1540
4131
  function promptPasswordWith(streams) {
1541
4132
  return new Promise((resolve2, reject) => {
1542
4133
  const { stdin, stderr } = streams;
@@ -1592,5 +4183,10 @@ run(process.argv.slice(2), { promptPassword }).then((code) => process.exit(code)
1592
4183
  }
1593
4184
  process.exit(1);
1594
4185
  });
4186
+ /*! Bundled license information:
4187
+
4188
+ @noble/hashes/esm/utils.js:
4189
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
4190
+ */
1595
4191
  //# sourceMappingURL=cli.cjs.map
1596
4192
  //# sourceMappingURL=cli.cjs.map