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.
- package/README.md +186 -9
- package/dist/bin/botanary.js +13 -3
- package/dist/bin/botanary.js.map +1 -1
- package/dist/package.json +2 -2
- package/dist/src/app-url.js +32 -0
- package/dist/src/app-url.js.map +1 -0
- package/dist/src/cli.js +113 -23
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands/agent.js +223 -50
- package/dist/src/commands/agent.js.map +1 -1
- package/dist/src/commands/mcp.js +136 -5
- package/dist/src/commands/mcp.js.map +1 -1
- package/dist/src/commands/system.js +109 -12
- package/dist/src/commands/system.js.map +1 -1
- package/dist/src/commands/utilities.js +44 -0
- package/dist/src/commands/utilities.js.map +1 -0
- package/dist/src/commands/wallet.js +338 -94
- package/dist/src/commands/wallet.js.map +1 -1
- package/dist/src/completion.js +69 -0
- package/dist/src/completion.js.map +1 -0
- package/dist/src/config-store.js +52 -0
- package/dist/src/config-store.js.map +1 -0
- package/dist/src/context.js +14 -4
- package/dist/src/context.js.map +1 -1
- package/dist/src/errors.js +116 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/help/examples.js +358 -0
- package/dist/src/help/examples.js.map +1 -0
- package/dist/src/help/format.js +144 -0
- package/dist/src/help/format.js.map +1 -0
- package/dist/src/help/groups.js +39 -0
- package/dist/src/help/groups.js.map +1 -0
- package/dist/src/mcp-snippets.js +6 -0
- package/dist/src/mcp-snippets.js.map +1 -1
- package/dist/src/outcome.js +63 -0
- package/dist/src/outcome.js.map +1 -0
- package/dist/src/parser.js +18 -39
- package/dist/src/parser.js.map +1 -1
- package/dist/src/progress.js +37 -0
- package/dist/src/progress.js.map +1 -0
- package/dist/src/prompt-doc.js +92 -0
- package/dist/src/prompt-doc.js.map +1 -0
- package/dist/src/render/accounts.js +21 -0
- package/dist/src/render/accounts.js.map +1 -0
- package/dist/src/render/activity.js +34 -0
- package/dist/src/render/activity.js.map +1 -0
- package/dist/src/render/agents.js +42 -0
- package/dist/src/render/agents.js.map +1 -0
- package/dist/src/render/apis.js +44 -0
- package/dist/src/render/apis.js.map +1 -0
- package/dist/src/render/balance.js +68 -0
- package/dist/src/render/balance.js.map +1 -0
- package/dist/src/render/chains.js +52 -0
- package/dist/src/render/chains.js.map +1 -0
- package/dist/src/render/gas.js +34 -0
- package/dist/src/render/gas.js.map +1 -0
- package/dist/src/render/grant.js +34 -0
- package/dist/src/render/grant.js.map +1 -0
- package/dist/src/render/kv.js +60 -0
- package/dist/src/render/kv.js.map +1 -0
- package/dist/src/render/mandates.js +111 -0
- package/dist/src/render/mandates.js.map +1 -0
- package/dist/src/render/print.js +18 -0
- package/dist/src/render/print.js.map +1 -0
- package/dist/src/repl.js +114 -10
- package/dist/src/repl.js.map +1 -1
- package/dist/src/review.js +55 -0
- package/dist/src/review.js.map +1 -0
- package/dist/src/settings.js +88 -0
- package/dist/src/settings.js.map +1 -0
- package/dist/src/update-check.js +85 -0
- package/dist/src/update-check.js.map +1 -0
- package/dist/src/validate.js +92 -0
- package/dist/src/validate.js.map +1 -0
- 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
|
-
|
|
5
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
package/dist/bin/botanary.js
CHANGED
|
@@ -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
|
-
|
|
5
|
-
//
|
|
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
|
-
|
|
18
|
+
createRuntime: (options) => createDefaultRuntime(options),
|
|
9
19
|
cliVersion: packageJson.default.version,
|
|
10
20
|
});
|
|
11
21
|
process.exit(exitCode);
|
package/dist/bin/botanary.js.map
CHANGED
|
@@ -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;
|
|
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
|
|
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.
|
|
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 {
|
|
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, '-
|
|
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
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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 &&
|
|
38
|
-
const session = await
|
|
39
|
-
const hasAgent = await
|
|
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
|
|
114
|
+
return EXIT.ok;
|
|
42
115
|
}
|
|
43
116
|
await program.parseAsync(argv, { from: 'user' });
|
|
44
|
-
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
package/dist/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|