botanary 0.1.0 → 0.2.1

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 (75) hide show
  1. package/README.md +186 -9
  2. package/dist/bin/botanary.js +13 -3
  3. package/dist/bin/botanary.js.map +1 -1
  4. package/dist/package.json +2 -2
  5. package/dist/src/app-url.js +32 -0
  6. package/dist/src/app-url.js.map +1 -0
  7. package/dist/src/cli.js +113 -23
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/commands/agent.js +223 -50
  10. package/dist/src/commands/agent.js.map +1 -1
  11. package/dist/src/commands/mcp.js +136 -5
  12. package/dist/src/commands/mcp.js.map +1 -1
  13. package/dist/src/commands/system.js +109 -12
  14. package/dist/src/commands/system.js.map +1 -1
  15. package/dist/src/commands/utilities.js +44 -0
  16. package/dist/src/commands/utilities.js.map +1 -0
  17. package/dist/src/commands/wallet.js +338 -94
  18. package/dist/src/commands/wallet.js.map +1 -1
  19. package/dist/src/completion.js +69 -0
  20. package/dist/src/completion.js.map +1 -0
  21. package/dist/src/config-store.js +52 -0
  22. package/dist/src/config-store.js.map +1 -0
  23. package/dist/src/context.js +14 -4
  24. package/dist/src/context.js.map +1 -1
  25. package/dist/src/errors.js +116 -0
  26. package/dist/src/errors.js.map +1 -0
  27. package/dist/src/help/examples.js +358 -0
  28. package/dist/src/help/examples.js.map +1 -0
  29. package/dist/src/help/format.js +144 -0
  30. package/dist/src/help/format.js.map +1 -0
  31. package/dist/src/help/groups.js +39 -0
  32. package/dist/src/help/groups.js.map +1 -0
  33. package/dist/src/mcp-snippets.js +6 -0
  34. package/dist/src/mcp-snippets.js.map +1 -1
  35. package/dist/src/outcome.js +63 -0
  36. package/dist/src/outcome.js.map +1 -0
  37. package/dist/src/parser.js +18 -39
  38. package/dist/src/parser.js.map +1 -1
  39. package/dist/src/progress.js +37 -0
  40. package/dist/src/progress.js.map +1 -0
  41. package/dist/src/prompt-doc.js +92 -0
  42. package/dist/src/prompt-doc.js.map +1 -0
  43. package/dist/src/render/accounts.js +21 -0
  44. package/dist/src/render/accounts.js.map +1 -0
  45. package/dist/src/render/activity.js +34 -0
  46. package/dist/src/render/activity.js.map +1 -0
  47. package/dist/src/render/agents.js +42 -0
  48. package/dist/src/render/agents.js.map +1 -0
  49. package/dist/src/render/apis.js +44 -0
  50. package/dist/src/render/apis.js.map +1 -0
  51. package/dist/src/render/balance.js +68 -0
  52. package/dist/src/render/balance.js.map +1 -0
  53. package/dist/src/render/chains.js +52 -0
  54. package/dist/src/render/chains.js.map +1 -0
  55. package/dist/src/render/gas.js +34 -0
  56. package/dist/src/render/gas.js.map +1 -0
  57. package/dist/src/render/grant.js +34 -0
  58. package/dist/src/render/grant.js.map +1 -0
  59. package/dist/src/render/kv.js +60 -0
  60. package/dist/src/render/kv.js.map +1 -0
  61. package/dist/src/render/mandates.js +111 -0
  62. package/dist/src/render/mandates.js.map +1 -0
  63. package/dist/src/render/print.js +18 -0
  64. package/dist/src/render/print.js.map +1 -0
  65. package/dist/src/repl.js +114 -10
  66. package/dist/src/repl.js.map +1 -1
  67. package/dist/src/review.js +55 -0
  68. package/dist/src/review.js.map +1 -0
  69. package/dist/src/settings.js +88 -0
  70. package/dist/src/settings.js.map +1 -0
  71. package/dist/src/update-check.js +85 -0
  72. package/dist/src/update-check.js.map +1 -0
  73. package/dist/src/validate.js +92 -0
  74. package/dist/src/validate.js.map +1 -0
  75. package/package.json +2 -2
package/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # botanary
2
2
 
3
- Botanary from your terminal - a guided CLI over the same wallet and agent lanes `botanary-mcp` exposes to
4
- a coding agent. Shares its identity/session storage (`~/.botanary-mcp`, override with
5
- `BOTANARY_MCP_HOME`): log in once, from either surface, and both see it.
3
+ Botanary from your terminal - a guided CLI over the same wallet and agent lanes `botanary-mcp` exposes to a coding agent. Shares its identity and session storage (`~/.botanary-mcp`, override with `BOTANARY_MCP_HOME`): log in once, from either surface, and both see it.
4
+
5
+ ## Renamed in 0.2.0
6
+
7
+ `agent whoami` used to print this machine's local key readout. It now asks the backend what Botanary actually says about this agent (claimed? live? which account? which grant?), which is what the name means everywhere else. The local readout moved to `agent identity`, unchanged.
6
8
 
7
9
  ## Install
8
10
 
9
11
  ```bash
10
12
  npx botanary # zero-install, always latest
11
- npm i -g botanary # resident on PATH
13
+ npm i -g botanary # resident on PATH
12
14
  ```
13
15
 
14
16
  ## Use
@@ -18,13 +20,173 @@ Run `botanary` with no arguments for a guided REPL, or run any command directly:
18
20
  ```bash
19
21
  botanary login
20
22
  botanary balance
21
- botanary send --amount 25 --token usdc --recipient 0xAb12...9F3 --chain-id 8453
23
+ botanary send --amount 25 --token USDC --recipient 0xAb12...9F3 --chain-id 8453
22
24
  botanary agent whoami
23
25
  botanary mcp config claude
24
26
  ```
25
27
 
26
- Every command works both ways: answer prompts interactively, or pass flags and add `--json` for scripts
27
- and CI. `--json` also auto-enables whenever stdout is piped or redirected.
28
+ Every command works both ways: answer prompts interactively, or pass flags and add `--json` for scripts and CI. `--json` also auto-enables whenever stdout is piped or redirected.
29
+
30
+ ## Commands
31
+
32
+ | Command | Description |
33
+ |---------|-------------|
34
+ | **Wallet** | |
35
+ | `login` | Log in as the account owner, in your browser |
36
+ | `logout` | Revoke and clear the stored owner session |
37
+ | `status` | Owner session and this machine's agent binding, side by side |
38
+ | `whoami` | Who Botanary says you are, in both lanes |
39
+ | `chains` | Every chain this backend serves, and what each can do |
40
+ | `gas [options]` | Gas methods available for this account on a chain |
41
+ | `balance` | What this account holds, across every chain |
42
+ | `send [options]` | Send tokens from an account you own |
43
+ | `activity` | Recent sends, swaps and yield transactions |
44
+ | `markets` | Public market data: prices and symbols |
45
+ | `yield` | Yield pools, a shortlist, and this account's positions |
46
+ | `mandates` | What the agent lane may spend unattended (testnet only) |
47
+ | `agents` | Connected agents and any pending approval requests |
48
+ | `open [target]` | Open Botanary in your browser |
49
+ | `accounts` | Manage which owner account subsequent commands use |
50
+ | `api [options] [path]` | Read any documented GET endpoint as the owner |
51
+ | **Agent** | |
52
+ | `agent` | This machine's own agent identity, grant, and spend under it |
53
+ | `agent identity` | This machine's local agent key: address, public key, fingerprint |
54
+ | `agent whoami` | What Botanary says about this agent: claimed, live, which account |
55
+ | `agent account [options]` | The one account this agent is bound to |
56
+ | `agent balance` | The bound account's balance, read as this agent |
57
+ | `agent request [options]` | Ask the owner to approve something this grant cannot cover |
58
+ | `agent pair [options]` | Show (or rotate) the pairing code and register it with Botanary |
59
+ | `agent grant` | This agent's current grant, rendered in plain terms |
60
+ | `agent requests` | Every approval request this agent has filed, and the owner's verdict on each |
61
+ | `agent spend [options]` | Spend under this agent's own grant, unattended |
62
+ | `agent swap [options]` | Swap under this agent's own grant, unattended |
63
+ | `agent status <opId>` | Check on a pending action this agent relayed |
64
+ | `agent apis [options]` | Paid (x402) API endpoints this agent may call, with live price and remaining budget |
65
+ | `agent pay [options] <endpointId>` | Call a paid API endpoint and pay for it inside the owner-committed budget |
66
+ | `agent budget [options]` | Remaining authorizations, per-call max, expiry and epoch for this agent on a chain |
67
+ | `agent forget` | Step 1 of 2: preview what forgetting the local identity would destroy |
68
+ | `agent confirm-forget <token>` | Step 2 of 2: actually delete the agent key from this machine |
69
+ | **Connect** | |
70
+ | `mcp` | Connect a coding agent to Botanary via botanary-mcp |
71
+ | `mcp config` | Print the install snippet for a client |
72
+ | `mcp install` | Install botanary-mcp using that client's own CLI |
73
+ | `mcp serve` | Run the botanary-mcp server on stdio |
74
+ | **System** | |
75
+ | `diagnose` | Start here when anything is unclear or an action failed |
76
+ | `doctor` | The same readiness check as diagnose |
77
+ | `config` | Where identity and session live, and this machine's CLI defaults |
78
+ | `config list` | Every setting, its value, and where that value came from |
79
+ | `config get` | Print one stored config value |
80
+ | `config set` | Store a default for one setting |
81
+ | `config unset` | Remove one stored default |
82
+ | **Utilities** | |
83
+ | `version` | Print the installed botanary CLI version |
84
+ | `docs [topic]` | Open the Botanary documentation in your browser |
85
+ | `completion <shell>` | Print a shell completion script (bash, zsh, fish, powershell) |
86
+ | `prompt` | Print the full operating manual for an automation driving this CLI |
87
+ | `update-check` | Check whether a newer botanary is published |
88
+
89
+ ## Non-interactive use
90
+
91
+ Use `--json` to get machine-readable output, `--yes` to skip the review step on money commands, and `--dry-run` to preflight without sending. The failure contract is strict: on success, stdout is the response payload (or a plain string); on failure, stdout carries an error object.
92
+
93
+ ### Global options
94
+
95
+ - `-v, --version` - Print the installed botanary CLI version
96
+ - `-h, --help` - Display help for command
97
+ - `--chain <id|key>` - Default chain: an EVM chain id (8453) or a chain key (solana)
98
+ - `--account <accountId>` - Owner account to act as
99
+ - `--api-url <url>` - Botanary API base URL
100
+ - `--home <dir>` - Directory holding identity, session and CLI config
101
+ - `--json` - Force JSON output (auto-enabled when piped)
102
+ - `--raw` - Print the full raw JSON payload instead of a table
103
+ - `-q, --quiet` - Suppress non-essential output
104
+ - `--verbose` - Show detail an ordinary run leaves out
105
+ - `--no-color` - Disable color output
106
+ - `--reveal` - Show redacted values in plain text
107
+ - `--no-interactive` - Disable prompts and the REPL
108
+ - `--timeout <ms>` - Abort any single request after this many milliseconds
109
+ - `--debug` - Log one redacted line per HTTP request to stderr
110
+ - `-y, --yes` - Skip the review step on a command that moves funds
111
+ - `--dry-run` - Preflight only: never create a sign request, never relay
112
+
113
+ ### Failure envelope
114
+
115
+ On failure with `--json`, stdout carries a single JSON line. The error object always has `code` and `message`. The `hint` field is optional and must not be assumed to exist.
116
+
117
+ Example:
118
+ ```json
119
+ {"ok":false,"error":{"code":"declined","message":"...","hint":"..."}}
120
+ ```
121
+
122
+ Branch on the exit code first and on `error.code` second. Never string-match a message.
123
+
124
+ ### Exit codes
125
+
126
+ | Code | Meaning |
127
+ |------|---------|
128
+ | 0 | Success |
129
+ | 1 | Error |
130
+ | 2 | Invalid arguments |
131
+ | 3 | Not logged in, or not paired |
132
+ | 4 | Declined (policy, bounds, or chain tier) |
133
+ | 5 | Pending: relayed, settlement unknown |
134
+ | 6 | Upstream unavailable (network, RPC, bundler) |
135
+ | 130 | Interrupted (SIGINT) |
136
+
137
+ ## Configuration
138
+
139
+ Configuration lives in `~/.botanary-mcp/cli-config.json` (override the directory with `BOTANARY_MCP_HOME`). Six config keys control defaults:
140
+
141
+ - `chainId` - Default chain for commands that ask: an EVM chain id (e.g. 8453) or a chain key (e.g. solana)
142
+ - `accountId` - Owner account to act as by default
143
+ - `apiUrl` - Botanary API base URL
144
+ - `output` - `auto` (intelligent), `json`, or `raw`
145
+ - `color` - `auto` (intelligent) or `never`
146
+ - `confirm` - `always` (require confirmation) or `never` (auto-confirm). Set to `never` to skip review prompts by default, though `--dry-run` is always available
147
+
148
+ Precedence: flag, then environment variable, then stored config, then built-in default. Run `botanary config list` to see every setting's current value and where it came from. Relevant environment variables:
149
+
150
+ - `BOTANARY_CHAIN_ID` - Default chain
151
+ - `BOTANARY_ACCOUNT_ID` - Default account
152
+ - `BOTANARY_API_URL` - API base URL
153
+ - `BOTANARY_JSON` - Force JSON output
154
+ - `BOTANARY_TIMEOUT_MS` - Request timeout in milliseconds
155
+ - `NO_COLOR` - Disable color (any value)
156
+ - `BOTANARY_MCP_HOME` - Override identity and config storage directory
157
+
158
+ ## Shell completion
159
+
160
+ Install the completion script your shell uses:
161
+
162
+ ```bash
163
+ # zsh
164
+ botanary completion zsh > ~/.zsh/completions/_botanary
165
+ # or add to ~/.zshrc:
166
+ eval "$(botanary completion zsh)"
167
+
168
+ # bash
169
+ botanary completion bash >> ~/.bashrc
170
+ # then reload:
171
+ source ~/.bashrc
172
+
173
+ # fish
174
+ botanary completion fish | source
175
+
176
+ # PowerShell
177
+ botanary completion powershell | Out-String | Invoke-Expression
178
+ ```
179
+
180
+ ## Driving it from an agent
181
+
182
+ To run `botanary` non-interactively from a script or agent:
183
+
184
+ 1. Use `--json` on every command to get machine-readable output.
185
+ 2. Use `--yes` on any command that moves funds (send, agent spend, agent swap, etc.) to skip the review step.
186
+ 3. Use `--dry-run` first to preflight a payment without sending it.
187
+ 4. Exit code 5 means the operation was relayed but settlement is unknown. For an owner-lane `send`, poll `botanary api /userops/<opId> --json` instead of retrying. For an `agent spend`, poll `botanary agent status <opId> --json`.
188
+
189
+ See `botanary prompt` for the full operating manual, including every command, every flag, and examples for every lane.
28
190
 
29
191
  ## Development
30
192
 
@@ -35,5 +197,20 @@ pnpm test
35
197
  pnpm dev # run bin/botanary.ts directly, watching for changes
36
198
  ```
37
199
 
38
- `BOTANARY_MCP_HOME` and `BOTANARY_API_URL` behave exactly as documented in `botanary-mcp`'s own README -
39
- this package depends on that one as its engine and adds no storage or network logic of its own.
200
+ `BOTANARY_MCP_HOME` and `BOTANARY_API_URL` behave exactly as documented in `botanary-mcp`'s own README - this package depends on that one as its engine and adds no storage or network logic of its own.
201
+
202
+ ## Release note
203
+
204
+ `botanary` depends on `botanary-mcp` as its engine, so the two publish in a fixed order:
205
+
206
+ 1. `botanary-mcp@0.6.0` is published to npm. **Done** - it carries the optional login and send progress
207
+ callbacks this CLI needs to print a login code and an approval URL before its poll loop, rather than
208
+ after it.
209
+ 2. `tools/botanary-cli/package.json` depends on `^0.6.0` from the registry. **Done** - the suite and the
210
+ `mcp serve` smoke check both pass against the published package, not a local link.
211
+ 3. `botanary@0.2.0` publishes.
212
+
213
+ Repeat that order for any future release where the CLI needs a change in the engine: a `botanary` published
214
+ against an unpublished `botanary-mcp` installs broken.
215
+
216
+ See `docs/superpowers/specs/2026-08-19-botanary-cli-completion-design.md` section 5 for the rationale.
@@ -1,11 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import { createDefaultRuntime } from 'botanary-mcp';
3
3
  import { runCli } from '../src/cli.js';
4
- // Deliberately the only file in this package that is not unit tested directly - mirrors
5
- // botanary-mcp's own bin/botanary-mcp.ts, which wires the real runtime into runCli and nothing else.
4
+ import { EXIT } from '../src/errors.js';
5
+ // Deliberately the only file in this package that is not unit tested directly - it wires the real runtime
6
+ // factory into runCli and traps SIGINT, and nothing else.
6
7
  const packageJson = await import('../package.json', { with: { type: 'json' } });
8
+ // @clack/prompts leaves the cursor hidden and the terminal in raw mode if a prompt is interrupted, so a
9
+ // bare Ctrl-C would hand back an unusable shell. Restore both, then exit with the conventional 130.
10
+ process.on('SIGINT', () => {
11
+ process.stdout.write('\x1b[?25h');
12
+ if (process.stdin.isTTY)
13
+ process.stdin.setRawMode(false);
14
+ process.stdout.write('\n');
15
+ process.exit(EXIT.interrupted);
16
+ });
7
17
  const exitCode = await runCli(process.argv.slice(2), {
8
- runtime: createDefaultRuntime(),
18
+ createRuntime: (options) => createDefaultRuntime(options),
9
19
  cliVersion: packageJson.default.version,
10
20
  });
11
21
  process.exit(exitCode);
@@ -1 +1 @@
1
- {"version":3,"file":"botanary.js","sourceRoot":"","sources":["../../bin/botanary.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,wFAAwF;AACxF,qGAAqG;AACrG,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;AAChF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACnD,OAAO,EAAE,oBAAoB,EAAE;IAC/B,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,OAAiB;CAClD,CAAC,CAAC;AACH,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC"}
1
+ {"version":3,"file":"botanary.js","sourceRoot":"","sources":["../../bin/botanary.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC,0GAA0G;AAC1G,0DAA0D;AAC1D,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;AAEhF,wGAAwG;AACxG,oGAAoG;AACpG,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;IACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAClC,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACnD,aAAa,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,oBAAoB,CAAC,OAAO,CAAC;IACzD,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,OAAiB;CAClD,CAAC,CAAC;AACH,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC"}
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "botanary",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.2.1",
5
5
  "description": "Botanary from your terminal - a guided CLI over the same wallet and agent lanes botanary-mcp exposes to a coding agent.",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://docs.botanary.xyz",
@@ -23,7 +23,7 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "botanary-mcp": "^0.5.0",
26
+ "botanary-mcp": "^0.6.1",
27
27
  "commander": "^13.1.0",
28
28
  "@clack/prompts": "^0.9.1",
29
29
  "picocolors": "^1.1.1",
@@ -0,0 +1,32 @@
1
+ import { invalidArgs } from './errors.js';
2
+ /** Surfaces `botanary open <target>` knows about. Each is a real route in the app. */
3
+ export const APP_TARGETS = ['mandates', 'agents', 'history', 'yield', 'markets', 'settings'];
4
+ /**
5
+ * The app origin for an API base URL, by dropping a leading `api.` label: `api.app.botanary.xyz` is served
6
+ * by Caddy in front of the same deployment as `app.botanary.xyz`.
7
+ *
8
+ * A host WITHOUT that label is returned unchanged rather than rewritten - guessing where someone's local
9
+ * or staging front end lives would open the wrong page silently, and a local stack can always pass
10
+ * `--api-url`.
11
+ */
12
+ export function appOrigin(apiBaseUrl) {
13
+ try {
14
+ const url = new URL(apiBaseUrl);
15
+ if (url.hostname.startsWith('api.'))
16
+ url.hostname = url.hostname.slice('api.'.length);
17
+ return url.origin;
18
+ }
19
+ catch {
20
+ return apiBaseUrl;
21
+ }
22
+ }
23
+ export function appUrl(apiBaseUrl, target) {
24
+ const origin = appOrigin(apiBaseUrl);
25
+ if (!target)
26
+ return origin;
27
+ if (!APP_TARGETS.includes(target)) {
28
+ throw invalidArgs(`Unknown target "${target}". Try one of: ${APP_TARGETS.join(', ')}.`, 'Run `botanary open` with no argument to open the app root.');
29
+ }
30
+ return `${origin}/${target}`;
31
+ }
32
+ //# sourceMappingURL=app-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-url.js","sourceRoot":"","sources":["../../src/app-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,sFAAsF;AACtF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC;AAEtG;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC1C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;QAChC,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtF,OAAO,GAAG,CAAC,MAAM,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,UAAkB,EAAE,MAAe;IACxD,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IAC3B,IAAI,CAAE,WAAiC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACzD,MAAM,WAAW,CACf,mBAAmB,MAAM,kBAAkB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EACpE,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;AAC/B,CAAC"}
package/dist/src/cli.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createDefaultRuntime } from 'botanary-mcp';
1
2
  import { CommanderError } from 'commander';
2
3
  import { createContext } from './context.js';
3
4
  import { createProgram } from './parser.js';
@@ -5,53 +6,142 @@ import { registerWalletCommands } from './commands/wallet.js';
5
6
  import { registerAgentCommands } from './commands/agent.js';
6
7
  import { registerMcpCommands } from './commands/mcp.js';
7
8
  import { registerSystemCommands } from './commands/system.js';
9
+ import { registerUtilityCommands } from './commands/utilities.js';
8
10
  import { runRepl } from './repl.js';
9
- import { setColorEnabled } from './render/colors.js';
10
- import { isJsonMode } from './render/json-mode.js';
11
+ import { colors, setColorEnabled } from './render/colors.js';
12
+ import { readConfig } from './config-store.js';
13
+ import { resolveSettings } from './settings.js';
14
+ import { EXIT, CliError, errorEnvelope, errorLines, toCliError } from './errors.js';
15
+ import { checkForUpdate, nagSuppressed, nagLine } from './update-check.js';
16
+ /**
17
+ * Wrap fetch when, and only when, a global flag asks for it. `--timeout` needs an AbortController per
18
+ * request; `--debug` needs a line per request. Returns undefined otherwise so the ordinary path keeps
19
+ * using the platform fetch untouched.
20
+ *
21
+ * The debug line carries method, path and status only. No Botanary URL carries a credential in its query
22
+ * (botanary-mcp's test/no-leak.spec.ts pins that), and the bearer lives in a header this never reads.
23
+ */
24
+ function wrapFetch(settings, stderr) {
25
+ if (settings.timeoutMs === null && !settings.debug)
26
+ return undefined;
27
+ return (async (input, init) => {
28
+ const started = Date.now();
29
+ const controller = new AbortController();
30
+ const timer = settings.timeoutMs === null ? null : setTimeout(() => controller.abort(), settings.timeoutMs);
31
+ try {
32
+ const response = await fetch(input, { ...init, signal: controller.signal });
33
+ if (settings.debug) {
34
+ const url = new URL(String(input instanceof Request ? input.url : input));
35
+ stderr(`[debug] ${init?.method ?? 'GET'} ${url.pathname}${url.search} ${response.status} ${Date.now() - started}ms`);
36
+ }
37
+ return response;
38
+ }
39
+ finally {
40
+ if (timer)
41
+ clearTimeout(timer);
42
+ }
43
+ });
44
+ }
45
+ /**
46
+ * A courtesy line, on stderr so it can never corrupt piped output, and only where a human can act on it.
47
+ * The refresh is capped at 1.5s and runs at most once a day; every failure is silent. This must never be
48
+ * able to slow down or break a command.
49
+ */
50
+ async function maybeNag(ctx, cliVersion, stderr) {
51
+ if (ctx.json || ctx.quiet || !ctx.interactive || nagSuppressed(process.env))
52
+ return;
53
+ try {
54
+ const result = await checkForUpdate(cliVersion, { fetchImpl: fetch, now: () => Date.now() });
55
+ if (result.newer && result.latest)
56
+ stderr(colors.dim(nagLine(result.latest, cliVersion)));
57
+ }
58
+ catch {
59
+ // Never let a courtesy check affect the command's outcome.
60
+ }
61
+ }
11
62
  export async function runCli(argv, deps) {
12
63
  const stderr = deps.stderr ?? ((line) => process.stderr.write(`${line}\n`));
64
+ // Detect JSON mode early, before option parsing can throw, so an error envelope is always available.
65
+ // Check for explicit --json flag or non-TTY stdout (piped mode auto-enables JSON).
66
+ const jsonModeFromArgv = argv.includes('--json');
67
+ const jsonModeFromPipe = !process.stdout.isTTY;
68
+ const jsonModeEarly = jsonModeFromArgv || jsonModeFromPipe;
13
69
  const program = createProgram();
14
- program.version(deps.cliVersion, '-V, --version', 'Print the installed botanary-cli version');
70
+ program.version(deps.cliVersion, '-v, --version', 'Print the installed botanary CLI version');
15
71
  registerWalletCommands(program);
16
72
  registerAgentCommands(program);
17
73
  registerMcpCommands(program);
18
74
  registerSystemCommands(program, deps.cliVersion);
75
+ registerUtilityCommands(program, deps.cliVersion);
19
76
  program.exitOverride();
77
+ let ctx = null;
20
78
  try {
21
79
  program.parseOptions(argv);
22
80
  const opts = program.opts();
23
- // colors.ts's own default (pc.isColorSupported) already correctly auto-detects whether stdout is
24
- // a real color-capable terminal - only override it to force OFF when the user explicitly opted
25
- // out (--no-color, which commander surfaces as opts.color === false - not merely "falsy", since
26
- // commander defaults opts.color to true when the flag isn't passed) or set NO_COLOR. Never force
27
- // it ON: that was the bug - it made every ordinary invocation ignore the real TTY/pipe state.
28
- if (opts.color === false || process.env.NO_COLOR) {
29
- setColorEnabled(false);
81
+ // `-V` is the hidden alias for `-v` (commander allows one short flag per option), answered before
82
+ // any dispatch.
83
+ if (opts.V) {
84
+ process.stdout.write(`${deps.cliVersion}\n`);
85
+ return EXIT.ok;
86
+ }
87
+ // --home must land in the environment BEFORE anything resolves a path: the config file, the identity
88
+ // keystore and the session store all read botanaryMcpHome() at call time.
89
+ if (typeof opts.home === 'string' && opts.home) {
90
+ process.env.BOTANARY_MCP_HOME = opts.home;
30
91
  }
31
- const ctx = createContext(deps.runtime, {
32
- ...opts,
33
- json: isJsonMode({ json: opts.json }, process.stdout),
34
- interactive: opts.interactive === false ? false : !!process.stdin.isTTY && !!process.stdout.isTTY,
92
+ const settings = resolveSettings({
93
+ opts,
94
+ env: process.env,
95
+ file: await readConfig(),
96
+ stdout: process.stdout,
97
+ stdin: process.stdin,
35
98
  });
99
+ setColorEnabled(settings.color);
100
+ const runtime = deps.runtime ??
101
+ (deps.createRuntime ?? ((o) => createDefaultRuntime(o)))({
102
+ ...(settings.apiUrl ? { baseUrl: settings.apiUrl } : {}),
103
+ ...(() => {
104
+ const wrapped = wrapFetch(settings, stderr);
105
+ return wrapped ? { fetch: wrapped } : {};
106
+ })(),
107
+ });
108
+ ctx = createContext(runtime, settings);
36
109
  program.opts = () => ({ ...opts, __ctx: ctx });
37
- if (argv.length === 0 && process.stdout.isTTY && !ctx.noInteractive) {
38
- const session = await deps.runtime.walletSession();
39
- const hasAgent = await deps.runtime.store.hasIdentity();
110
+ if (argv.length === 0 && process.stdout.isTTY && ctx.interactive) {
111
+ const session = await runtime.walletSession();
112
+ const hasAgent = await runtime.store.hasIdentity();
40
113
  await runRepl(program, { loggedIn: !!session, hasAgent });
41
- return 0;
114
+ return EXIT.ok;
42
115
  }
43
116
  await program.parseAsync(argv, { from: 'user' });
44
- return 0;
117
+ await maybeNag(ctx, deps.cliVersion, stderr);
118
+ return ctx.outcome.exit;
45
119
  }
46
120
  catch (e) {
47
121
  if (e instanceof CommanderError) {
48
- if (e.exitCode !== 0) {
49
- stderr(e.message);
122
+ // Map usage errors (unknown command, unknown option, missing argument) to exit 2.
123
+ // Keep exit 0 for --help and --version (commander reports those as errors too).
124
+ const isUsageError = e.code === 'commander.unknownCommand' || e.code === 'commander.unknownOption' || e.code === 'commander.missingMandatoryOptionValue';
125
+ if (isUsageError) {
126
+ if (jsonModeEarly) {
127
+ console.log(JSON.stringify(errorEnvelope(new CliError(e.message, 'invalid_args', EXIT.invalidArgs))));
128
+ }
129
+ return EXIT.invalidArgs;
50
130
  }
131
+ // Help and version are exit 0
132
+ if (e.code === 'commander.help' || e.code === 'commander.version') {
133
+ return EXIT.ok;
134
+ }
135
+ // Fallback for any other CommanderError
51
136
  return e.exitCode;
52
137
  }
53
- stderr(e instanceof Error ? e.message : String(e));
54
- return 1;
138
+ const err = toCliError(e);
139
+ if (ctx?.json || jsonModeEarly) {
140
+ console.log(JSON.stringify(errorEnvelope(err)));
141
+ }
142
+ for (const line of errorLines(err))
143
+ stderr(line);
144
+ return err.exit;
55
145
  }
56
146
  }
57
147
  //# sourceMappingURL=cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}