@masculinecache/wrangler-axi 0.1.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/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/bin/wrangler-axi.d.ts +2 -0
- package/dist/bin/wrangler-axi.js +17 -0
- package/dist/src/args.d.ts +19 -0
- package/dist/src/args.js +105 -0
- package/dist/src/cli.d.ts +27 -0
- package/dist/src/cli.js +101 -0
- package/dist/src/commands/d1.d.ts +2 -0
- package/dist/src/commands/d1.js +149 -0
- package/dist/src/commands/kv.d.ts +2 -0
- package/dist/src/commands/kv.js +423 -0
- package/dist/src/commands/pages.d.ts +2 -0
- package/dist/src/commands/pages.js +337 -0
- package/dist/src/commands/r2.d.ts +2 -0
- package/dist/src/commands/r2.js +289 -0
- package/dist/src/commands/secrets.d.ts +2 -0
- package/dist/src/commands/secrets.js +195 -0
- package/dist/src/commands/whoami.d.ts +2 -0
- package/dist/src/commands/whoami.js +60 -0
- package/dist/src/commands/workers.d.ts +2 -0
- package/dist/src/commands/workers.js +431 -0
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.js +77 -0
- package/dist/src/fields.d.ts +15 -0
- package/dist/src/fields.js +36 -0
- package/dist/src/format.d.ts +13 -0
- package/dist/src/format.js +21 -0
- package/dist/src/list.d.ts +18 -0
- package/dist/src/list.js +23 -0
- package/dist/src/stdin.d.ts +2 -0
- package/dist/src/stdin.js +14 -0
- package/dist/src/toon.d.ts +30 -0
- package/dist/src/toon.js +159 -0
- package/dist/src/util.d.ts +8 -0
- package/dist/src/util.js +26 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +23 -0
- package/dist/src/wrangler.d.ts +64 -0
- package/dist/src/wrangler.js +175 -0
- package/package.json +48 -0
- package/skills/wrangler-axi/SKILL.md +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 masculinecache
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# wrangler-axi
|
|
2
|
+
|
|
3
|
+
Agent-ergonomic TOON wrapper around [Cloudflare Wrangler](https://developers.cloudflare.com/workers/wrangler/) 4.x.
|
|
4
|
+
|
|
5
|
+
`wrangler-axi` is a thin, [AXI](https://toonformat.dev/)-compliant CLI that wraps the `wrangler` binary and emits
|
|
6
|
+
[TOON](https://toonformat.dev/) on stdout — compact, token-efficient output that agents can consume without a
|
|
7
|
+
JSON-parsing round trip. It stays close to Wrangler's own command surface: anything you can do with `wrangler`
|
|
8
|
+
you can do with `wrangler-axi`, plus predictable list schemas, definitive empty states, aggregate counts, and
|
|
9
|
+
structured errors with actionable suggestions.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install -g @masculinecache/wrangler-axi
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Node.js >= 20 and the `wrangler` binary available on `PATH` (install it with `npm install -g wrangler`).
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
wrangler-axi <area> <sub> [args]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Run with no arguments to see the home view.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
$ wrangler-axi --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Global flags
|
|
32
|
+
|
|
33
|
+
| Flag | Meaning |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `--help`, `-h` | Show help (always allowed on every command) |
|
|
36
|
+
| `--version`, `-v` | Print the wrangler-axi version |
|
|
37
|
+
| `--account <id\|name>` | Select the Cloudflare account (forwarded to wrangler) |
|
|
38
|
+
|
|
39
|
+
Every list command supports `--fields <a,b,c>` to request extra columns and `--limit <n>` to cap rows. `workers versions list` and `workers deployments list` default to `--limit 20` when neither `--limit` nor `--full` is given; `--full` removes the cap.
|
|
40
|
+
|
|
41
|
+
## Areas
|
|
42
|
+
|
|
43
|
+
### `whoami`
|
|
44
|
+
|
|
45
|
+
Report the authenticated identity.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
$ wrangler-axi whoami
|
|
49
|
+
identity:
|
|
50
|
+
email: a@b.c
|
|
51
|
+
accountId: acc1
|
|
52
|
+
accountName: Acme
|
|
53
|
+
loggedIn: yes
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
When logged out it reports `not authenticated` with help to run `wrangler login`.
|
|
57
|
+
|
|
58
|
+
### `secrets`
|
|
59
|
+
|
|
60
|
+
| Sub | Wrangler command | Notes |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| `list [--name <worker>]` | `secret list --format json` | |
|
|
63
|
+
| `put <key> [--name <worker>]` | `secret put <key> --quiet` | value read from stdin |
|
|
64
|
+
| `delete <key> [--name <worker>] [--force]` | `secret delete` | |
|
|
65
|
+
|
|
66
|
+
### `kv`
|
|
67
|
+
|
|
68
|
+
| Sub | Wrangler command |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `namespace list` | `kv namespace list` |
|
|
71
|
+
| `namespace create <title>` | `kv namespace create` |
|
|
72
|
+
| `namespace delete <id>` | `kv namespace delete` |
|
|
73
|
+
| `namespace rename <old>` | `kv namespace rename` |
|
|
74
|
+
| `key list --namespace-id <id>\|--binding <b> [--prefix]` | `kv key list` |
|
|
75
|
+
| `key get <key> --namespace-id <id>\|--binding <b> [--text]` | `kv key get` |
|
|
76
|
+
| `key put <key> <value\|--path <p>> --namespace-id <id>\|--binding <b>` | `kv key put` |
|
|
77
|
+
|
|
78
|
+
### `d1`
|
|
79
|
+
|
|
80
|
+
| Sub | Wrangler command | Notes |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| `databases list` | `d1 list --json` | |
|
|
83
|
+
| `query <database> --command <sql>\|--file <f> [--remote] [--yes] [--json]` | `d1 execute` | requires `--command` or `--file` |
|
|
84
|
+
|
|
85
|
+
### `r2`
|
|
86
|
+
|
|
87
|
+
| Sub | Wrangler command |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `bucket list` | `r2 bucket list` (human output parsed) |
|
|
90
|
+
| `bucket info <name>` | `r2 bucket info --json` |
|
|
91
|
+
| `bucket create <name>` | `r2 bucket create` |
|
|
92
|
+
| `bucket delete <name>` | `r2 bucket delete` |
|
|
93
|
+
| `object get/put/delete <path>` | `r2 object ...` |
|
|
94
|
+
|
|
95
|
+
### `pages`
|
|
96
|
+
|
|
97
|
+
| Sub | Wrangler command | Notes |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| `project list` | `pages project list --json` | |
|
|
100
|
+
| `project create <name>` | `pages project create` | |
|
|
101
|
+
| `project delete <name>` | `pages project delete` | |
|
|
102
|
+
| `deployment list --project-name <n> [--environment <prod\|preview>]` | `pages deployment list --json` | |
|
|
103
|
+
| `deployment create [dir] [--project-name <n>]` | `pages deployment create` | |
|
|
104
|
+
| `deployment delete <id>` | `pages deployment delete` | |
|
|
105
|
+
|
|
106
|
+
### `workers`
|
|
107
|
+
|
|
108
|
+
| Sub | Wrangler command |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `deploy [path]` | `workers deploy` |
|
|
111
|
+
| `versions list [--name <worker>]` | `versions list --json` |
|
|
112
|
+
| `deployments list [--name <worker>]` | `deployments list --json` |
|
|
113
|
+
| `tail [worker] [--max-entries <n>] [--timeout <sec>]` | `tail --format json` (bounded stream) |
|
|
114
|
+
|
|
115
|
+
`workers tail` streams live Worker logs with a hard bound and never leaves an
|
|
116
|
+
interactive session behind: it stops at `--max-entries` (default 20) or
|
|
117
|
+
`--timeout` seconds (default 15), then reports which bound fired. Pass-through
|
|
118
|
+
filters `--search`, `--status`, `--method`, `--ip`, `--sampling-rate`,
|
|
119
|
+
`--header`, and `--version-id` narrow the stream; `--json` returns the raw
|
|
120
|
+
bounded lines instead of the compact TOON rows.
|
|
121
|
+
|
|
122
|
+
`workers deploy` forwards the common single-value, repeatable, and boolean flags from `wrangler deploy`
|
|
123
|
+
(`--name`, `--tag`, `--message`, `--compatibility-date`, `--assets`, `--outdir`, `--outfile`, `--tsconfig`,
|
|
124
|
+
`--secrets-file`, `--dispatch-namespace`, `--compatibility-flags`, `--var`, `--define`, `--alias`, `--routes`,
|
|
125
|
+
`--domains`, `--triggers`, `--no-bundle`, `--latest`, `--minify`, `--dry-run`, `--keep-vars`, `--strict`,
|
|
126
|
+
`--autoconfig`).
|
|
127
|
+
|
|
128
|
+
## Output
|
|
129
|
+
|
|
130
|
+
Structured data is emitted as TOON on **stdout**.
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
$ wrangler-axi secrets list
|
|
134
|
+
count: 1
|
|
135
|
+
secrets[1]{name,type}:
|
|
136
|
+
API_KEY,text
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Errors also go to stdout, in a structured form with an actionable suggestion, and map to a stable exit code:
|
|
140
|
+
|
|
141
|
+
| Exit | Meaning |
|
|
142
|
+
| --- | --- |
|
|
143
|
+
| `0` | success (including no-ops and empty results) |
|
|
144
|
+
| `1` | runtime error (e.g. not authenticated, not found) |
|
|
145
|
+
| `2` | usage error (unknown flag, missing required flag, unknown area) |
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
$ wrangler-axi secrets list --bogus x
|
|
149
|
+
error: unknown flag --bogus for `secrets list`
|
|
150
|
+
help: valid flags for `secrets list`: --name, --limit (--help always allowed)
|
|
151
|
+
```
|
|
152
|
+
(exit 2)
|
|
153
|
+
|
|
154
|
+
Unknown flags fail loudly and list the valid flags so the agent self-corrects in one step. `--help` is always
|
|
155
|
+
allowed. No command prompts interactively — every operation is completable with flags alone.
|
|
156
|
+
|
|
157
|
+
`wrangler-axi` never runs live mutations without being asked, and it never leaks raw dependency stack traces:
|
|
158
|
+
wrangler errors are translated into the structured error format above.
|
|
159
|
+
|
|
160
|
+
## Integrations
|
|
161
|
+
|
|
162
|
+
`wrangler-axi` follows the AXI ambient-context pattern and offers two complementary ways to surface state to an
|
|
163
|
+
agent at session start. Install whichever fits; you only need one.
|
|
164
|
+
|
|
165
|
+
### Session hook (recommended for Claude Code, Codex, OpenCode)
|
|
166
|
+
|
|
167
|
+
- **OpenCode**: managed plugin under `~/.config/opencode/plugins/` injecting a compact home-view dashboard
|
|
168
|
+
(bin path + description + live `whoami`/account state) as ambient system context.
|
|
169
|
+
- **Claude Code**: `SessionStart` hook in `~/.claude/settings.json` (or project `.claude/settings.json`).
|
|
170
|
+
- **Codex**: `SessionStart` hook in `~/.codex/hooks.json` with `[features].hooks = true`.
|
|
171
|
+
|
|
172
|
+
### Installable skill
|
|
173
|
+
|
|
174
|
+
A static skill that loads on demand when an agent recognizes a wrangler task — lower overhead, works in any
|
|
175
|
+
agent that supports the Agent Skill format:
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
npx skills add masculinecache/wrangler-axi --skill wrangler-axi
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The skill is generated from the same content as the home view so it stays in sync with the CLI.
|
|
182
|
+
|
|
183
|
+
## Development
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
npm install
|
|
187
|
+
npm run build # tsc
|
|
188
|
+
npm test # vitest (all tests use an injected fake runner; no live APIs)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { run } from "../src/cli.js";
|
|
3
|
+
import { exitCodeForError } from "../src/errors.js";
|
|
4
|
+
const argv = process.argv.slice(2);
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
run(argv, undefined, cwd)
|
|
7
|
+
.then(({ stdout, exitCode }) => {
|
|
8
|
+
if (stdout) {
|
|
9
|
+
process.stdout.write(stdout + "\n");
|
|
10
|
+
}
|
|
11
|
+
process.exitCode = exitCode;
|
|
12
|
+
})
|
|
13
|
+
.catch((err) => {
|
|
14
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15
|
+
process.stderr.write(`wrangler-axi: ${message}\n`);
|
|
16
|
+
process.exitCode = exitCodeForError(err);
|
|
17
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Return the value for --flag (space or = form) without removing it. */
|
|
2
|
+
export declare function getFlag(args: string[], name: string): string | undefined;
|
|
3
|
+
/** Take and remove --flag value (space or = form); returns value or undefined. */
|
|
4
|
+
export declare function takeFlag(args: string[], flag: string): string | undefined;
|
|
5
|
+
export declare function hasFlag(args: string[], flag: string): boolean;
|
|
6
|
+
/** True iff a boolean flag is present and not negated as `--no-<flag>`. */
|
|
7
|
+
export declare function takeBoolFlag(args: string[], flag: string): boolean;
|
|
8
|
+
export declare function getAllFlags(args: string[]): string[];
|
|
9
|
+
export declare function takeAllFlags(args: string[], flags: string[]): string[];
|
|
10
|
+
export declare function pushRepeated(base: string[], flag: string, values: string[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Return the first non-flag positional token at/after startIndex.
|
|
13
|
+
* Skips `--` and token values that follow a flag.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPositional(args: string[], startIndex?: number): string | undefined;
|
|
16
|
+
export declare function requireNumber(raw: string | undefined, label: string): number;
|
|
17
|
+
export declare function takeNumber(args: string[], label: string): number | undefined;
|
|
18
|
+
/** Throw VALIDATION_ERROR if a flag needs a value but none is present. */
|
|
19
|
+
export declare function requireFlagValue(value: string | undefined, flag: string): string;
|
package/dist/src/args.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
/**
|
|
3
|
+
* Split an argv token into its flag name and any inline `=value`, if present.
|
|
4
|
+
*/
|
|
5
|
+
function flagParts(arg) {
|
|
6
|
+
if (arg.startsWith("--")) {
|
|
7
|
+
const eq = arg.indexOf("=");
|
|
8
|
+
if (eq !== -1) {
|
|
9
|
+
return { name: arg.slice(0, eq), value: arg.slice(eq + 1) };
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return { name: arg };
|
|
13
|
+
}
|
|
14
|
+
/** Return the value for --flag (space or = form) without removing it. */
|
|
15
|
+
export function getFlag(args, name) {
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
const { name: n, value } = flagParts(args[i]);
|
|
18
|
+
if (n === name) {
|
|
19
|
+
return value !== undefined ? value : args[i + 1];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
/** Take and remove --flag value (space or = form); returns value or undefined. */
|
|
25
|
+
export function takeFlag(args, flag) {
|
|
26
|
+
for (let i = 0; i < args.length; i++) {
|
|
27
|
+
const { name, value } = flagParts(args[i]);
|
|
28
|
+
if (name === flag) {
|
|
29
|
+
if (value !== undefined) {
|
|
30
|
+
args.splice(i, 1);
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
const val = args[i + 1];
|
|
34
|
+
args.splice(i, 2);
|
|
35
|
+
return val;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
export function hasFlag(args, flag) {
|
|
41
|
+
return args.some((a) => flagParts(a).name === flag);
|
|
42
|
+
}
|
|
43
|
+
/** True iff a boolean flag is present and not negated as `--no-<flag>`. */
|
|
44
|
+
export function takeBoolFlag(args, flag) {
|
|
45
|
+
const i = args.findIndex((a) => flagParts(a).name === flag);
|
|
46
|
+
if (i === -1) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
args.splice(i, 1);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
export function getAllFlags(args) {
|
|
53
|
+
return args.filter((a) => a.startsWith("-")).map((a) => flagParts(a).name);
|
|
54
|
+
}
|
|
55
|
+
export function takeAllFlags(args, flags) {
|
|
56
|
+
const found = [];
|
|
57
|
+
for (let i = args.length - 1; i >= 0; i--) {
|
|
58
|
+
const { name } = flagParts(args[i]);
|
|
59
|
+
if (flags.includes(name)) {
|
|
60
|
+
found.unshift(args.splice(i, 1)[0]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
export function pushRepeated(base, flag, values) {
|
|
66
|
+
for (const v of values) {
|
|
67
|
+
base.push(flag, v);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Return the first non-flag positional token at/after startIndex.
|
|
72
|
+
* Skips `--` and token values that follow a flag.
|
|
73
|
+
*/
|
|
74
|
+
export function getPositional(args, startIndex = 0) {
|
|
75
|
+
for (let i = startIndex; i < args.length; i++) {
|
|
76
|
+
const a = args[i];
|
|
77
|
+
if (a === "--") {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!a.startsWith("-")) {
|
|
81
|
+
return a;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
export function requireNumber(raw, label) {
|
|
87
|
+
if (raw === undefined || raw === "" || Number.isNaN(Number(raw))) {
|
|
88
|
+
throw new AxiError(`${label} must be a number`, "VALIDATION_ERROR");
|
|
89
|
+
}
|
|
90
|
+
return Number(raw);
|
|
91
|
+
}
|
|
92
|
+
export function takeNumber(args, label) {
|
|
93
|
+
const raw = takeFlag(args, label);
|
|
94
|
+
if (raw === undefined) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
return requireNumber(raw, label);
|
|
98
|
+
}
|
|
99
|
+
/** Throw VALIDATION_ERROR if a flag needs a value but none is present. */
|
|
100
|
+
export function requireFlagValue(value, flag) {
|
|
101
|
+
if (value === undefined || value === "") {
|
|
102
|
+
throw new AxiError(`${flag} requires a value`, "VALIDATION_ERROR");
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type WranglerRunner } from "./wrangler.js";
|
|
2
|
+
/** Shared context passed to every command. */
|
|
3
|
+
export interface CommandContext {
|
|
4
|
+
runner: WranglerRunner;
|
|
5
|
+
account?: string;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A top-level area command (whoami, workers, ...). */
|
|
9
|
+
export interface CommandArea {
|
|
10
|
+
name: string;
|
|
11
|
+
aliases?: string[];
|
|
12
|
+
description: string;
|
|
13
|
+
help: string[];
|
|
14
|
+
run(ctx: CommandContext, args: string[]): Promise<{
|
|
15
|
+
stdout: string;
|
|
16
|
+
exitCode: number;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export declare function topHelp(runnerNote: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Dispatch one CLI invocation. Returns the text to write to stdout and the
|
|
22
|
+
* exit code. Does not process.exit — the caller (bin entry) decides that.
|
|
23
|
+
*/
|
|
24
|
+
export declare function run(argv: string[], runner?: WranglerRunner, cwd?: string): Promise<{
|
|
25
|
+
stdout: string;
|
|
26
|
+
exitCode: number;
|
|
27
|
+
}>;
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { AxiError, exitCodeForError } from "./errors.js";
|
|
2
|
+
import { realRunner } from "./wrangler.js";
|
|
3
|
+
import { renderError, renderHelp, renderOutput } from "./toon.js";
|
|
4
|
+
import { VERSION } from "./version.js";
|
|
5
|
+
import { whoamiCommand } from "./commands/whoami.js";
|
|
6
|
+
import { workersCommand } from "./commands/workers.js";
|
|
7
|
+
import { secretsCommand } from "./commands/secrets.js";
|
|
8
|
+
import { kvCommand } from "./commands/kv.js";
|
|
9
|
+
import { d1Command } from "./commands/d1.js";
|
|
10
|
+
import { r2Command } from "./commands/r2.js";
|
|
11
|
+
import { pagesCommand } from "./commands/pages.js";
|
|
12
|
+
const areas = [
|
|
13
|
+
whoamiCommand,
|
|
14
|
+
workersCommand,
|
|
15
|
+
secretsCommand,
|
|
16
|
+
kvCommand,
|
|
17
|
+
d1Command,
|
|
18
|
+
r2Command,
|
|
19
|
+
pagesCommand,
|
|
20
|
+
];
|
|
21
|
+
function findArea(name) {
|
|
22
|
+
return areas.find((a) => a.name === name || (a.aliases?.includes(name) ?? false));
|
|
23
|
+
}
|
|
24
|
+
export function topHelp(runnerNote) {
|
|
25
|
+
const lines = areas.map((a) => `Run \`wrangler-axi ${a.name}\` — ${a.description}`);
|
|
26
|
+
lines.unshift("Run `wrangler-axi <area> <sub> [args]`");
|
|
27
|
+
lines.unshift("Global flags: --help, --version, --account <id|name>");
|
|
28
|
+
lines.push("Run `wrangler-axi <area> --help` for a specific area");
|
|
29
|
+
return renderHelp(lines);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Dispatch one CLI invocation. Returns the text to write to stdout and the
|
|
33
|
+
* exit code. Does not process.exit — the caller (bin entry) decides that.
|
|
34
|
+
*/
|
|
35
|
+
export async function run(argv, runner = realRunner, cwd) {
|
|
36
|
+
const ctx = { runner, cwd };
|
|
37
|
+
// Global --version / -v at top level.
|
|
38
|
+
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
|
|
39
|
+
return {
|
|
40
|
+
stdout: renderOutput([
|
|
41
|
+
`bin: wrangler-axi`,
|
|
42
|
+
`description: Agent-ergonomic TOON wrapper around Cloudflare Wrangler`,
|
|
43
|
+
topHelp(""),
|
|
44
|
+
]),
|
|
45
|
+
exitCode: 0,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (argv[0] === "--version" || argv[0] === "-v") {
|
|
49
|
+
return { stdout: `wrangler-axi: ${VERSION}`, exitCode: 0 };
|
|
50
|
+
}
|
|
51
|
+
let args = argv.slice();
|
|
52
|
+
const areaName = args[0];
|
|
53
|
+
if (areaName === undefined) {
|
|
54
|
+
return { stdout: topHelp(""), exitCode: 0 };
|
|
55
|
+
}
|
|
56
|
+
const area = findArea(areaName);
|
|
57
|
+
if (!area) {
|
|
58
|
+
const err = new AxiError(`Unknown area "${areaName}"`, "VALIDATION_ERROR", [
|
|
59
|
+
`Valid areas: ${areas.map((a) => a.name).join(", ")}`,
|
|
60
|
+
"Run `wrangler-axi --help` for usage",
|
|
61
|
+
]);
|
|
62
|
+
return { stdout: asError(err), exitCode: exitCodeForError(err) };
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
// Pull --account off globally (any position).
|
|
66
|
+
const accountIdx = args.findIndex((a) => a === "--account" || a.startsWith("--account="));
|
|
67
|
+
if (accountIdx !== -1) {
|
|
68
|
+
const raw = args[accountIdx];
|
|
69
|
+
if (raw.startsWith("--account=")) {
|
|
70
|
+
ctx.account = raw.slice("--account=".length);
|
|
71
|
+
args.splice(accountIdx, 1);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
if (accountIdx + 1 >= args.length) {
|
|
75
|
+
throw new AxiError("Missing value for --account flag", "VALIDATION_ERROR", ["Run `wrangler-axi --help` for usage"]);
|
|
76
|
+
}
|
|
77
|
+
ctx.account = args[accountIdx + 1];
|
|
78
|
+
args.splice(accountIdx, 2);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const rest = args.slice(1);
|
|
82
|
+
// Area-level --help (explicit only: help as the first arg after the area, e.g.
|
|
83
|
+
// `wrangler-axi workers --help`). A `--help` after a subcommand is dispatched
|
|
84
|
+
// to that subcommand so each has its own contextual help.
|
|
85
|
+
if (rest[0] === "--help" || rest[0] === "-h") {
|
|
86
|
+
return { stdout: renderHelp(area.help), exitCode: 0 };
|
|
87
|
+
}
|
|
88
|
+
const result = await area.run(ctx, rest);
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
if (err instanceof AxiError) {
|
|
93
|
+
return { stdout: asError(err), exitCode: exitCodeForError(err) };
|
|
94
|
+
}
|
|
95
|
+
const unexpected = new AxiError(err instanceof Error ? err.message : String(err), "UNKNOWN");
|
|
96
|
+
return { stdout: asError(unexpected), exitCode: 1 };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function asError(err) {
|
|
100
|
+
return renderError(err.message, err.code, err.suggestions);
|
|
101
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { AxiError } from "../errors.js";
|
|
2
|
+
import { callWrangler, parseJson } from "../wrangler.js";
|
|
3
|
+
import { takeAllFlags, takeFlag, takeNumber } from "../args.js";
|
|
4
|
+
import { validateFlags, wantsHelp } from "../util.js";
|
|
5
|
+
import { addExtraDefs, parseFields, } from "../fields.js";
|
|
6
|
+
import { custom, field, renderDetail, renderHelp } from "../toon.js";
|
|
7
|
+
import { renderListBlock } from "../list.js";
|
|
8
|
+
const dbAvailable = {
|
|
9
|
+
created: { def: field("created_at") },
|
|
10
|
+
uuid: { def: field("uuid") },
|
|
11
|
+
};
|
|
12
|
+
const DATABASE_FLAGS = ["--json"];
|
|
13
|
+
export const d1Command = {
|
|
14
|
+
name: "d1",
|
|
15
|
+
description: "manage D1 databases (list, query)",
|
|
16
|
+
help: [
|
|
17
|
+
"Usage: wrangler-axi d1 <databases|query> [args]",
|
|
18
|
+
" databases list — list D1 databases",
|
|
19
|
+
" query <name> — run a SQL command against a database",
|
|
20
|
+
"Example: wrangler-axi d1 databases list",
|
|
21
|
+
"Example: wrangler-axi d1 query my-db --command 'SELECT * FROM users'",
|
|
22
|
+
],
|
|
23
|
+
async run(ctx, args) {
|
|
24
|
+
const sub = args[0];
|
|
25
|
+
if (sub === undefined || wantsHelp(args)) {
|
|
26
|
+
return { stdout: renderHelp(d1Command.help), exitCode: 0 };
|
|
27
|
+
}
|
|
28
|
+
const rest = args.slice(1);
|
|
29
|
+
switch (sub) {
|
|
30
|
+
case "databases":
|
|
31
|
+
return databases(ctx, rest);
|
|
32
|
+
case "query":
|
|
33
|
+
return query(ctx, rest);
|
|
34
|
+
default:
|
|
35
|
+
throw new AxiError(`Unknown d1 subcommand "${sub}"`, "VALIDATION_ERROR", [
|
|
36
|
+
"Run `wrangler-axi d1 --help` for valid subcommands",
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
async function databases(ctx, args) {
|
|
42
|
+
if (wantsHelp(args)) {
|
|
43
|
+
return {
|
|
44
|
+
stdout: renderHelp([
|
|
45
|
+
"Usage: wrangler-axi d1 databases list [--fields] [--limit] [--full] [--json]",
|
|
46
|
+
]),
|
|
47
|
+
exitCode: 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const sub = args[0];
|
|
51
|
+
if (sub !== undefined && sub !== "list" && !sub.startsWith("-")) {
|
|
52
|
+
throw new AxiError(`Unknown d1 databases subcommand "${sub}"`, "VALIDATION_ERROR", [
|
|
53
|
+
"Run `wrangler-axi d1 databases --help` for valid subcommands",
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
const listArgs = sub === "list" ? args.slice(1) : args;
|
|
57
|
+
const json = takeAllFlags(listArgs, ["--json"]).length > 0;
|
|
58
|
+
const full = takeAllFlags(listArgs, ["--full"]).length > 0;
|
|
59
|
+
validateFlags(listArgs, ["--fields", "--limit", "--full", "--json"], "d1 databases list");
|
|
60
|
+
const wArgs = ["d1", "list", "--json"];
|
|
61
|
+
if (ctx.account) {
|
|
62
|
+
wArgs.push("--account", ctx.account);
|
|
63
|
+
}
|
|
64
|
+
const limit = full ? undefined : takeNumber(listArgs, "--limit");
|
|
65
|
+
const fieldsArg = takeFlag(listArgs, "--fields");
|
|
66
|
+
const { stdout } = await callWrangler(ctx.runner, wArgs);
|
|
67
|
+
if (json) {
|
|
68
|
+
return { stdout, exitCode: 0 };
|
|
69
|
+
}
|
|
70
|
+
const items = parseJson(stdout, "d1 databases");
|
|
71
|
+
const { extraDefs } = parseFields(fieldsArg, dbAvailable);
|
|
72
|
+
const schema = addExtraDefs({
|
|
73
|
+
name: field("name"),
|
|
74
|
+
id: field("id"),
|
|
75
|
+
}, extraDefs);
|
|
76
|
+
return {
|
|
77
|
+
stdout: renderListBlock({
|
|
78
|
+
noun: "databases",
|
|
79
|
+
items,
|
|
80
|
+
schema,
|
|
81
|
+
limit,
|
|
82
|
+
empty: "databases: no D1 databases found",
|
|
83
|
+
truncatedHint: "Run `wrangler-axi d1 databases list --json` for the full set",
|
|
84
|
+
}),
|
|
85
|
+
exitCode: 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function query(ctx, args) {
|
|
89
|
+
if (wantsHelp(args)) {
|
|
90
|
+
return {
|
|
91
|
+
stdout: renderHelp([
|
|
92
|
+
"Usage: wrangler-axi d1 query <database> [--command <sql> | --file <path>]",
|
|
93
|
+
"Flags: --command <sql>, --file <path>, --local/--remote, --json",
|
|
94
|
+
]),
|
|
95
|
+
exitCode: 0,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const json = takeAllFlags(args, ["--json"]).length > 0;
|
|
99
|
+
validateFlags(args, ["--command", "--file", "--local", "--remote", "--yes", "--json"], "d1 query");
|
|
100
|
+
// Take value-consuming flags before extracting the positional db name.
|
|
101
|
+
const command = takeFlag(args, "--command");
|
|
102
|
+
const file = takeFlag(args, "--file");
|
|
103
|
+
const db = args.filter((a) => !a.startsWith("-"))[0];
|
|
104
|
+
if (!db) {
|
|
105
|
+
throw new AxiError("d1 query requires a database name", "VALIDATION_ERROR", [
|
|
106
|
+
"Usage: wrangler-axi d1 query <database> --command <sql>|--file <f>",
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
if (!command && !file) {
|
|
110
|
+
throw new AxiError("d1 query requires --command or --file", "VALIDATION_ERROR", [
|
|
111
|
+
"Run `wrangler-axi d1 query --help` for usage",
|
|
112
|
+
]);
|
|
113
|
+
}
|
|
114
|
+
const wArgs = ["d1", "execute", db];
|
|
115
|
+
if (ctx.account) {
|
|
116
|
+
wArgs.push("--account", ctx.account);
|
|
117
|
+
}
|
|
118
|
+
if (command) {
|
|
119
|
+
wArgs.push("--command", command);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
wArgs.push("--file", file);
|
|
123
|
+
}
|
|
124
|
+
const remote = takeAllFlags(args, ["--remote"]).length > 0;
|
|
125
|
+
if (remote) {
|
|
126
|
+
wArgs.push("--remote");
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
wArgs.push("--local");
|
|
130
|
+
}
|
|
131
|
+
if (takeAllFlags(args, ["--yes"]).length > 0) {
|
|
132
|
+
wArgs.push("-y");
|
|
133
|
+
}
|
|
134
|
+
if (json) {
|
|
135
|
+
wArgs.push("--json");
|
|
136
|
+
}
|
|
137
|
+
const { stdout } = await callWrangler(ctx.runner, wArgs);
|
|
138
|
+
if (json) {
|
|
139
|
+
return { stdout, exitCode: 0 };
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
stdout: renderDetail("query", { database: db, status: "executed" }, {
|
|
143
|
+
database: custom((d) => d.database),
|
|
144
|
+
status: custom((d) => d.status),
|
|
145
|
+
output: custom(() => stdout.trim() || "(no rows)"),
|
|
146
|
+
}),
|
|
147
|
+
exitCode: 0,
|
|
148
|
+
};
|
|
149
|
+
}
|