@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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -0
  3. package/dist/bin/wrangler-axi.d.ts +2 -0
  4. package/dist/bin/wrangler-axi.js +17 -0
  5. package/dist/src/args.d.ts +19 -0
  6. package/dist/src/args.js +105 -0
  7. package/dist/src/cli.d.ts +27 -0
  8. package/dist/src/cli.js +101 -0
  9. package/dist/src/commands/d1.d.ts +2 -0
  10. package/dist/src/commands/d1.js +149 -0
  11. package/dist/src/commands/kv.d.ts +2 -0
  12. package/dist/src/commands/kv.js +423 -0
  13. package/dist/src/commands/pages.d.ts +2 -0
  14. package/dist/src/commands/pages.js +337 -0
  15. package/dist/src/commands/r2.d.ts +2 -0
  16. package/dist/src/commands/r2.js +289 -0
  17. package/dist/src/commands/secrets.d.ts +2 -0
  18. package/dist/src/commands/secrets.js +195 -0
  19. package/dist/src/commands/whoami.d.ts +2 -0
  20. package/dist/src/commands/whoami.js +60 -0
  21. package/dist/src/commands/workers.d.ts +2 -0
  22. package/dist/src/commands/workers.js +431 -0
  23. package/dist/src/errors.d.ts +6 -0
  24. package/dist/src/errors.js +77 -0
  25. package/dist/src/fields.d.ts +15 -0
  26. package/dist/src/fields.js +36 -0
  27. package/dist/src/format.d.ts +13 -0
  28. package/dist/src/format.js +21 -0
  29. package/dist/src/list.d.ts +18 -0
  30. package/dist/src/list.js +23 -0
  31. package/dist/src/stdin.d.ts +2 -0
  32. package/dist/src/stdin.js +14 -0
  33. package/dist/src/toon.d.ts +30 -0
  34. package/dist/src/toon.js +159 -0
  35. package/dist/src/util.d.ts +8 -0
  36. package/dist/src/util.js +26 -0
  37. package/dist/src/version.d.ts +1 -0
  38. package/dist/src/version.js +23 -0
  39. package/dist/src/wrangler.d.ts +64 -0
  40. package/dist/src/wrangler.js +175 -0
  41. package/package.json +48 -0
  42. package/skills/wrangler-axi/SKILL.md +82 -0
@@ -0,0 +1,30 @@
1
+ import { encode } from "@toon-format/toon";
2
+ export { encode };
3
+ export interface FieldDef {
4
+ render: (item: any) => any;
5
+ as?: string;
6
+ }
7
+ /** Schema maps an output key to a renderer. Item is passed as `any`. */
8
+ export interface Schema {
9
+ [outputKey: string]: FieldDef;
10
+ }
11
+ /** Simple field extracted directly from the item, optionally renamed. */
12
+ export declare function field(key: string, as?: string): FieldDef;
13
+ /** Nested field: item[key][subkey]. */
14
+ export declare function pluck(key: string, subkey: string, as?: string): FieldDef;
15
+ /** Join an array field into a comma-separated string (or empty string). */
16
+ export declare function joinArray(key: string, subkey: string, as?: string, empty?: string): FieldDef;
17
+ /** ISO timestamp formatted as relative human time. */
18
+ export declare function relativeTime(key: string, as?: string): FieldDef;
19
+ export declare function boolYesNo(key: string, as?: string): FieldDef;
20
+ export declare function mapEnum(key: string, map: Record<string, string>, fallback?: string, as?: string): FieldDef;
21
+ export declare function lower(key: string, as?: string): FieldDef;
22
+ export declare function checksSummary(key: string, as?: string): FieldDef;
23
+ export declare function custom(fn: (item: any) => any, as?: string): FieldDef;
24
+ /** Map an item through a schema into a plain object of outputKey -> rendered value. */
25
+ export declare function extract(item: any, schema: Schema): Record<string, unknown>;
26
+ export declare function renderList(label: string, items: unknown[], schema: Schema): string;
27
+ export declare function renderDetail(label: string, item: unknown, schema: Schema): string;
28
+ export declare function renderHelp(lines: string[]): string;
29
+ export declare function renderError(message: string, code: string, suggestions?: string[]): string;
30
+ export declare function renderOutput(blocks: Array<string | undefined>): string;
@@ -0,0 +1,159 @@
1
+ import { encode } from "@toon-format/toon";
2
+ export { encode };
3
+ function formatRelativeTime(iso) {
4
+ if (!iso) {
5
+ return "unknown";
6
+ }
7
+ const then = new Date(iso).getTime();
8
+ if (Number.isNaN(then)) {
9
+ return "unknown";
10
+ }
11
+ const diffSec = Math.floor((Date.now() - then) / 1000);
12
+ if (diffSec < 60) {
13
+ return "just now";
14
+ }
15
+ const min = Math.floor(diffSec / 60);
16
+ if (min < 60) {
17
+ return `${min}m ago`;
18
+ }
19
+ const hour = Math.floor(min / 60);
20
+ if (hour < 24) {
21
+ return `${hour}h ago`;
22
+ }
23
+ const day = Math.floor(hour / 24);
24
+ if (day < 30) {
25
+ return `${day}d ago`;
26
+ }
27
+ const month = Math.floor(day / 30);
28
+ if (month < 12) {
29
+ return `${month}mo ago`;
30
+ }
31
+ return `${Math.floor(month / 12)}y ago`;
32
+ }
33
+ function rec(item) {
34
+ return (item ?? {});
35
+ }
36
+ /** Simple field extracted directly from the item, optionally renamed. */
37
+ export function field(key, as) {
38
+ return {
39
+ render: (item) => rec(item)[key] ?? null,
40
+ ...(as ? { as } : {}),
41
+ };
42
+ }
43
+ /** Nested field: item[key][subkey]. */
44
+ export function pluck(key, subkey, as) {
45
+ return {
46
+ render: (item) => {
47
+ const val = rec(item)[key];
48
+ if (val && typeof val === "object") {
49
+ return rec(val)[subkey] ?? null;
50
+ }
51
+ return null;
52
+ },
53
+ ...(as ? { as } : {}),
54
+ };
55
+ }
56
+ /** Join an array field into a comma-separated string (or empty string). */
57
+ export function joinArray(key, subkey, as, empty = "none") {
58
+ return {
59
+ render: (item) => {
60
+ const val = rec(item)[key];
61
+ if (!Array.isArray(val) || val.length === 0) {
62
+ return empty;
63
+ }
64
+ return val
65
+ .map((x) => (typeof x === "string" ? x : rec(x)[subkey]))
66
+ .filter(Boolean)
67
+ .join(",");
68
+ },
69
+ ...(as ? { as } : {}),
70
+ };
71
+ }
72
+ /** ISO timestamp formatted as relative human time. */
73
+ export function relativeTime(key, as) {
74
+ return {
75
+ render: (item) => {
76
+ const iso = rec(item)[key];
77
+ return typeof iso === "string" ? formatRelativeTime(iso) : "unknown";
78
+ },
79
+ ...(as ? { as } : {}),
80
+ };
81
+ }
82
+ export function boolYesNo(key, as) {
83
+ return {
84
+ render: (item) => (rec(item)[key] ? "yes" : "no"),
85
+ ...(as ? { as } : {}),
86
+ };
87
+ }
88
+ export function mapEnum(key, map, fallback, as) {
89
+ return {
90
+ render: (item) => {
91
+ const val = rec(item)[key];
92
+ const str = val == null ? undefined : String(val);
93
+ if (str !== undefined && Object.prototype.hasOwnProperty.call(map, str)) {
94
+ return map[str];
95
+ }
96
+ return fallback ?? str ?? "none";
97
+ },
98
+ ...(as ? { as } : {}),
99
+ };
100
+ }
101
+ export function lower(key, as) {
102
+ return {
103
+ render: (item) => {
104
+ const v = rec(item)[key];
105
+ return typeof v === "string" ? v.toLowerCase() : v ?? null;
106
+ },
107
+ ...(as ? { as } : {}),
108
+ };
109
+ }
110
+ export function checksSummary(key, as) {
111
+ return {
112
+ render: (item) => {
113
+ const checks = rec(item)[key];
114
+ if (!Array.isArray(checks)) {
115
+ return "none";
116
+ }
117
+ const passed = checks.filter((c) => rec(c).conclusion === "SUCCESS" ||
118
+ rec(c).conclusion === "NEUTRAL").length;
119
+ return `${passed}/${checks.length} pass`;
120
+ },
121
+ ...(as ? { as } : {}),
122
+ };
123
+ }
124
+ export function custom(fn, as) {
125
+ return {
126
+ render: fn,
127
+ ...(as ? { as } : {}),
128
+ };
129
+ }
130
+ /** Map an item through a schema into a plain object of outputKey -> rendered value. */
131
+ export function extract(item, schema) {
132
+ const out = {};
133
+ for (const [label, def] of Object.entries(schema)) {
134
+ out[label] = def.render(item);
135
+ }
136
+ return out;
137
+ }
138
+ export function renderList(label, items, schema) {
139
+ return encode({ [label]: items.map((i) => extract(i, schema)) });
140
+ }
141
+ export function renderDetail(label, item, schema) {
142
+ return encode({ [label]: extract(item, schema) });
143
+ }
144
+ export function renderHelp(lines) {
145
+ if (lines.length === 0) {
146
+ return "";
147
+ }
148
+ return `help[${lines.length}]:\n${lines.map((l) => ` ${l}`).join("\n")}`;
149
+ }
150
+ export function renderError(message, code, suggestions = []) {
151
+ const err = encode({ error: message, code });
152
+ if (suggestions.length > 0) {
153
+ return `${err}\n${renderHelp(suggestions)}`;
154
+ }
155
+ return err;
156
+ }
157
+ export function renderOutput(blocks) {
158
+ return blocks.filter(Boolean).join("\n");
159
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Validate that every flag present in args is known. Positive short flags like
3
+ * `-J` are checked too. Unknown flags throw VALIDATION_ERROR listing the valid
4
+ * flags so the agent self-corrects in one turn.
5
+ */
6
+ export declare function validateFlags(args: string[], valid: string[], commandName: string): void;
7
+ /** True if args requests help explicitly (contains --help/-h). Empty args run with defaults. */
8
+ export declare function wantsHelp(args: string[]): boolean;
@@ -0,0 +1,26 @@
1
+ import { AxiError } from "./errors.js";
2
+ import { getAllFlags } from "./args.js";
3
+ /** Global flags that are always allowed on any command. */
4
+ const ALWAYS_ALLOWED = ["--help", "-h", "--account"];
5
+ /**
6
+ * Validate that every flag present in args is known. Positive short flags like
7
+ * `-J` are checked too. Unknown flags throw VALIDATION_ERROR listing the valid
8
+ * flags so the agent self-corrects in one turn.
9
+ */
10
+ export function validateFlags(args, valid, commandName) {
11
+ const allowed = [...new Set([...ALWAYS_ALLOWED, ...valid])];
12
+ const present = getAllFlags(args);
13
+ const unknown = present.filter((f) => !allowed.includes(f));
14
+ if (unknown.length > 0) {
15
+ throw new AxiError(`Unknown flag(s): ${unknown.join(", ")} for ${commandName}`, "VALIDATION_ERROR", [
16
+ `Valid flags for ${commandName}: ${allowed
17
+ .filter((f) => f !== "--help" && f !== "-h")
18
+ .join(", ")}`,
19
+ "Run `wrangler-axi --help` or add --help for usage",
20
+ ]);
21
+ }
22
+ }
23
+ /** True if args requests help explicitly (contains --help/-h). Empty args run with defaults. */
24
+ export function wantsHelp(args) {
25
+ return args.includes("--help") || args.includes("-h");
26
+ }
@@ -0,0 +1 @@
1
+ export declare const VERSION: string;
@@ -0,0 +1,23 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, join } from "node:path";
4
+ const here = dirname(fileURLToPath(import.meta.url));
5
+ function readPackageVersion() {
6
+ const candidates = [
7
+ join(here, "..", "package.json"),
8
+ join(here, "..", "..", "package.json"),
9
+ ];
10
+ for (const path of candidates) {
11
+ try {
12
+ const pkg = JSON.parse(readFileSync(path, "utf8"));
13
+ if (pkg.version) {
14
+ return pkg.version;
15
+ }
16
+ }
17
+ catch {
18
+ // try next candidate
19
+ }
20
+ }
21
+ throw new Error("Could not resolve package version for wrangler-axi");
22
+ }
23
+ export const VERSION = readPackageVersion();
@@ -0,0 +1,64 @@
1
+ /** Result of a single wrangler invocation. */
2
+ export interface WranglerResult {
3
+ stdout: string;
4
+ stderr: string;
5
+ exitCode: number;
6
+ }
7
+ /** Options for a bounded `wrangler tail` session. */
8
+ export interface TailRunOptions {
9
+ /** Full wrangler args (the command itself is `tail --format json ...`). */
10
+ args: string[];
11
+ /** Stop after this many log lines (0 = unbounded; a timeout is then required). */
12
+ maxEntries: number;
13
+ /** Stop the stream after this many milliseconds (0 = no timeout). */
14
+ timeoutMs: number;
15
+ }
16
+ /** Result of a bounded tail session. Entries are raw JSON lines from stdout. */
17
+ export interface TailResult {
18
+ entries: string[];
19
+ stderr: string;
20
+ stoppedBy: "limit" | "timeout" | "exit";
21
+ exitCode: number;
22
+ }
23
+ /**
24
+ * A wrangler runner. Production uses the real CLI via execFile/spawn; tests
25
+ * inject a fake runner so no live API/shell calls happen.
26
+ */
27
+ export interface WranglerRunner {
28
+ run(args: string[], options?: {
29
+ input?: string;
30
+ }): Promise<WranglerResult>;
31
+ /**
32
+ * Stream `wrangler tail` output with a bound (line and/or time). Optional so
33
+ * minimal fake runners only need `run`; the tail command fails loudly (not
34
+ * silently degrading) if a runner does not implement it.
35
+ */
36
+ tail?(opts: TailRunOptions): Promise<TailResult>;
37
+ }
38
+ /**
39
+ * Execute a command against wrangler, mapping failures into structured
40
+ * AxiErrors (validated, then translated). Wranger writes human output to
41
+ * stdout; on non-zero exit the stderr is translated to a structured error.
42
+ */
43
+ export declare function callWrangler(runner: WranglerRunner, args: string[], options?: {
44
+ input?: string;
45
+ }): Promise<{
46
+ stdout: string;
47
+ exitCode: number;
48
+ }>;
49
+ /**
50
+ * Execute a command against wrangler without treating a non-zero exit as a
51
+ * failure — used where wrangler emits usable stdout yet still exits non-zero
52
+ * (e.g. `whoami --json` returns exit 1 with `{"loggedIn":false}` when logged
53
+ * out). The caller is responsible for interpreting the result.
54
+ */
55
+ export declare function callWranglerLenient(runner: WranglerRunner, args: string[], options?: {
56
+ input?: string;
57
+ }): Promise<{
58
+ stdout: string;
59
+ exitCode: number;
60
+ }>;
61
+ /** Parse a wrangler JSON output string, failing with a structured error. */
62
+ export declare function parseJson<T>(stdout: string, what: string): T;
63
+ /** Default production runner — always talks to the real wrangler. */
64
+ export declare const realRunner: WranglerRunner;
@@ -0,0 +1,175 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createInterface } from "node:readline";
3
+ import { AxiError } from "./errors.js";
4
+ import { mapWranglerError } from "./errors.js";
5
+ import { wranglerNotInstalledError } from "./errors.js";
6
+ /** Cap on captured stdout, mirroring execFile's maxBuffer contract. */
7
+ const MAX_OUTPUT = 64 * 1024 * 1024;
8
+ /**
9
+ * Run the real `wrangler` binary with the given args, capturing stdout/stderr.
10
+ * Uses spawn (not execFile) because stdin input must be written explicitly:
11
+ * async `execFile` silently ignores an `input` option and hangs the child.
12
+ * Throws a NO_SUCH_FILE error if wrangler isn't on PATH.
13
+ */
14
+ async function runWrangler(args, options) {
15
+ return new Promise((resolve, reject) => {
16
+ let child;
17
+ try {
18
+ child = spawn("wrangler", args, { windowsHide: true });
19
+ }
20
+ catch (err) {
21
+ reject(err);
22
+ return;
23
+ }
24
+ let stdout = "";
25
+ let stderr = "";
26
+ let settled = false;
27
+ let overflow = false;
28
+ const finish = (exitCode) => {
29
+ if (settled) {
30
+ return;
31
+ }
32
+ settled = true;
33
+ resolve({ stdout, stderr, exitCode });
34
+ };
35
+ child.on("error", (err) => {
36
+ if (settled) {
37
+ return;
38
+ }
39
+ settled = true;
40
+ const e = err;
41
+ if (e.code === "ENOENT") {
42
+ reject(wranglerNotInstalledError());
43
+ }
44
+ else {
45
+ reject(err);
46
+ }
47
+ });
48
+ child.on("close", (code) => finish(overflow ? 1 : (code ?? 0)));
49
+ child.stdout?.on("data", (chunk) => {
50
+ stdout += String(chunk);
51
+ if (!overflow && stdout.length > MAX_OUTPUT) {
52
+ overflow = true;
53
+ stderr += "wrangler-axi: output exceeded 64 MiB and was truncated";
54
+ child.kill("SIGKILL");
55
+ }
56
+ });
57
+ child.stderr?.on("data", (chunk) => {
58
+ stderr += String(chunk);
59
+ });
60
+ // EPIPE is expected when wrangler exits before draining stdin (e.g. an
61
+ // auth error on `secret put`); the exit code already carries the failure.
62
+ child.stdin?.on("error", () => { });
63
+ if (options?.input !== undefined) {
64
+ child.stdin?.end(options.input);
65
+ }
66
+ else {
67
+ child.stdin?.end();
68
+ }
69
+ });
70
+ }
71
+ /**
72
+ * Execute a command against wrangler, mapping failures into structured
73
+ * AxiErrors (validated, then translated). Wranger writes human output to
74
+ * stdout; on non-zero exit the stderr is translated to a structured error.
75
+ */
76
+ export async function callWrangler(runner, args, options) {
77
+ const result = await runner.run(args, options);
78
+ if (result.exitCode !== 0) {
79
+ throw mapWranglerError(result.stderr || result.stdout, result.exitCode);
80
+ }
81
+ return { stdout: result.stdout, exitCode: result.exitCode };
82
+ }
83
+ /**
84
+ * Execute a command against wrangler without treating a non-zero exit as a
85
+ * failure — used where wrangler emits usable stdout yet still exits non-zero
86
+ * (e.g. `whoami --json` returns exit 1 with `{"loggedIn":false}` when logged
87
+ * out). The caller is responsible for interpreting the result.
88
+ */
89
+ export async function callWranglerLenient(runner, args, options) {
90
+ const result = await runner.run(args, options);
91
+ return { stdout: result.stdout, exitCode: result.exitCode };
92
+ }
93
+ /** Parse a wrangler JSON output string, failing with a structured error. */
94
+ export function parseJson(stdout, what) {
95
+ try {
96
+ return JSON.parse(stdout);
97
+ }
98
+ catch {
99
+ throw new AxiError(`Could not parse ${what} from wrangler output`, "UNKNOWN", ["Run with --json to get machine-readable output"]);
100
+ }
101
+ }
102
+ /**
103
+ * Stream `wrangler tail --format json` and stop at the configured bound (max
104
+ * lines or timeout), then SIGTERM the child so the session ends like Ctrl+C.
105
+ * On entry, resolves with the captured raw lines and why the stream stopped.
106
+ */
107
+ async function runTail(opts) {
108
+ return new Promise((resolve, reject) => {
109
+ let child;
110
+ try {
111
+ child = spawn("wrangler", opts.args, { windowsHide: true });
112
+ }
113
+ catch (err) {
114
+ reject(err);
115
+ return;
116
+ }
117
+ const entries = [];
118
+ let stderrBuf = "";
119
+ let settled = false;
120
+ let timer;
121
+ const finish = (stoppedBy, exitCode = 0) => {
122
+ if (settled) {
123
+ return;
124
+ }
125
+ settled = true;
126
+ if (timer !== undefined) {
127
+ clearTimeout(timer);
128
+ }
129
+ if (!child.killed) {
130
+ try {
131
+ child.kill("SIGTERM");
132
+ }
133
+ catch {
134
+ // already gone
135
+ }
136
+ }
137
+ resolve({ entries, stderr: stderrBuf, stoppedBy, exitCode });
138
+ };
139
+ if (opts.timeoutMs > 0) {
140
+ timer = setTimeout(() => finish("timeout"), opts.timeoutMs);
141
+ }
142
+ child.on("error", (err) => {
143
+ const e = err;
144
+ if (e.code === "ENOENT") {
145
+ settled = true;
146
+ reject(wranglerNotInstalledError());
147
+ }
148
+ else {
149
+ settled = true;
150
+ reject(err);
151
+ }
152
+ });
153
+ child.on("close", (code) => {
154
+ finish("exit", code ?? 0);
155
+ });
156
+ child.stderr?.on("data", (chunk) => {
157
+ stderrBuf += String(chunk);
158
+ });
159
+ if (child.stdout) {
160
+ const rl = createInterface({ input: child.stdout });
161
+ rl.on("line", (line) => {
162
+ const trimmed = line.replace(/\s+$/, "");
163
+ if (!trimmed) {
164
+ return;
165
+ }
166
+ entries.push(trimmed);
167
+ if (opts.maxEntries > 0 && entries.length >= opts.maxEntries) {
168
+ finish("limit");
169
+ }
170
+ });
171
+ }
172
+ });
173
+ }
174
+ /** Default production runner — always talks to the real wrangler. */
175
+ export const realRunner = { run: runWrangler, tail: runTail };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@masculinecache/wrangler-axi",
3
+ "version": "0.1.0",
4
+ "description": "Agent-ergonomic TOON wrapper around Cloudflare Wrangler 4.x",
5
+ "type": "module",
6
+ "bin": {
7
+ "wrangler-axi": "dist/bin/wrangler-axi.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "!dist/test",
12
+ "skills/wrangler-axi",
13
+ "LICENSE",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "dev": "tsx bin/wrangler-axi.ts",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/masculinecache/wrangler-axi.git"
27
+ },
28
+ "homepage": "https://github.com/masculinecache/wrangler-axi#readme",
29
+ "bugs": {
30
+ "url": "https://github.com/masculinecache/wrangler-axi/issues"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "dependencies": {
39
+ "@toon-format/toon": "^2.1.0",
40
+ "axi-sdk-js": "^0.1.10"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.0.0",
44
+ "tsx": "^4.0.0",
45
+ "typescript": "^5.7.0",
46
+ "vitest": "^3.0.0"
47
+ }
48
+ }
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: wrangler-axi
3
+ description: Run Cloudflare Wrangler operations through the wrangler-axi TOON wrapper when you need worker deploys, secrets, KV/D1/R2, Pages, or account identity. Use whenever the task touches wrangler, workers, Cloudflare secrets, KV namespaces, D1 databases, R2 buckets, or Pages projects.
4
+ ---
5
+
6
+ # wrangler-axi
7
+
8
+ Agent-ergonomic TOON wrapper around Cloudflare Wrangler 4.x. Outputs compact TOON on stdout (no JSON parsing needed), with definitive empty states, aggregate counts, and structured errors with actionable suggestions.
9
+
10
+ ## Invocation
11
+
12
+ If the `wrangler-axi` binary is on PATH, run it directly:
13
+
14
+ ```sh
15
+ wrangler-axi <area> <sub> [args]
16
+ ```
17
+
18
+ Otherwise invoke without a global install:
19
+
20
+ ```sh
21
+ npx -y wrangler-axi <area> <sub> [args]
22
+ ```
23
+
24
+ Run with no args for the home view; run `wrangler-axi --help` for the full area list.
25
+
26
+ ## Global flags
27
+
28
+ - `--help` / `-h` — show help (always allowed on every command)
29
+ - `--version` / `-v` — print version
30
+ - `--account <id|name>` — select the Cloudflare account
31
+
32
+ Every list supports `--fields <a,b,c>` and `--limit <n>`.
33
+
34
+ ## Commands
35
+
36
+ ```sh
37
+ npx -y wrangler-axi whoami # identity / auth state
38
+ npx -y wrangler-axi secrets list [--name <worker>] # worker secrets
39
+ npx -y wrangler-axi secrets put <key> --name <worker> # value from stdin
40
+ npx -y wrangler-axi secrets delete <key> --name <worker>
41
+
42
+ npx -y wrangler-axi kv namespace list
43
+ npx -y wrangler-axi kv key list --namespace-id <id> [--prefix <p>]
44
+ npx -y wrangler-axi kv key get <key> --namespace-id <id> [--text]
45
+ npx -y wrangler-axi kv key put <key> <value> --namespace-id <id>
46
+
47
+ npx -y wrangler-axi d1 databases list
48
+ npx -y wrangler-axi d1 query <db> --command "SELECT 1" [--remote]
49
+
50
+ npx -y wrangler-axi r2 bucket list
51
+ npx -y wrangler-axi r2 bucket info <name>
52
+
53
+ npx -y wrangler-axi pages project list
54
+ npx -y wrangler-axi pages deployment list --project-name <name>
55
+
56
+ npx -y wrangler-axi workers deploy [path] [--name <worker>]
57
+ npx -y wrangler-axi workers versions list [--name <worker>]
58
+ npx -y wrangler-axi workers deployments list [--name <worker>]
59
+ npx -y wrangler-axi workers tail [worker] [--max-entries <n>] [--timeout <sec>] [--search <text>] [--status error]
60
+ ```
61
+
62
+ ## Exit codes
63
+
64
+ - `0` success (including no-ops / empty results)
65
+ - `1` runtime error (not authenticated, not found)
66
+ - `2` usage error (unknown flag, missing required flag)
67
+
68
+ Errors go to stdout in TOON with a suggestion; unknown flags fail loudly and list the valid flags so you can
69
+ self-correct in one step. `--help` is always allowed. No command prompts interactively.
70
+
71
+ ## Installation
72
+
73
+ ```sh
74
+ npm install -g @masculinecache/wrangler-axi
75
+ ```
76
+
77
+ Requires Node.js >= 20 and `wrangler` on PATH (`npm install -g wrangler`).
78
+
79
+ ## Integrations
80
+
81
+ This skill is one of two complementary install paths (see the README). A session hook for Claude Code / Codex /
82
+ OpenCode injects live account state at session start instead. Install whichever fits; you only need one.