@masculinecache/warp-axi 0.0.0-stage → 0.1.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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +45 -2
  3. package/dist/bin/warp-axi.d.ts +2 -0
  4. package/dist/bin/warp-axi.js +16 -0
  5. package/dist/src/args.d.ts +6 -0
  6. package/dist/src/args.js +56 -0
  7. package/dist/src/cli.d.ts +22 -0
  8. package/dist/src/cli.js +68 -0
  9. package/dist/src/commands/connect.d.ts +3 -0
  10. package/dist/src/commands/connect.js +50 -0
  11. package/dist/src/commands/debug.d.ts +2 -0
  12. package/dist/src/commands/debug.js +83 -0
  13. package/dist/src/commands/registration.d.ts +2 -0
  14. package/dist/src/commands/registration.js +111 -0
  15. package/dist/src/commands/settings.d.ts +2 -0
  16. package/dist/src/commands/settings.js +65 -0
  17. package/dist/src/commands/status.d.ts +2 -0
  18. package/dist/src/commands/status.js +89 -0
  19. package/dist/src/commands/target.d.ts +2 -0
  20. package/dist/src/commands/target.js +83 -0
  21. package/dist/src/commands/tunnel.d.ts +2 -0
  22. package/dist/src/commands/tunnel.js +112 -0
  23. package/dist/src/commands/vnet.d.ts +2 -0
  24. package/dist/src/commands/vnet.js +106 -0
  25. package/dist/src/errors.d.ts +2 -0
  26. package/dist/src/errors.js +2 -0
  27. package/dist/src/format.d.ts +8 -0
  28. package/dist/src/format.js +16 -0
  29. package/dist/src/list.d.ts +11 -0
  30. package/dist/src/list.js +17 -0
  31. package/dist/src/stdin.d.ts +2 -0
  32. package/dist/src/stdin.js +12 -0
  33. package/dist/src/toon.d.ts +24 -0
  34. package/dist/src/toon.js +140 -0
  35. package/dist/src/util.d.ts +2 -0
  36. package/dist/src/util.js +17 -0
  37. package/dist/src/version.d.ts +1 -0
  38. package/dist/src/version.js +22 -0
  39. package/dist/src/warp.d.ts +19 -0
  40. package/dist/src/warp.js +131 -0
  41. package/package.json +54 -4
  42. package/skills/warp-axi/SKILL.md +74 -0
@@ -0,0 +1,83 @@
1
+ import { callWarpLenient } from "../warp.js";
2
+ import { hasFlag } from "../args.js";
3
+ import { renderError, renderOutput, field } from "../toon.js";
4
+ import { renderListBlock } from "../list.js";
5
+ const targetSubcommands = ["list"];
6
+ const targetSchema = {
7
+ name: field("name"),
8
+ type: field("type"),
9
+ id: field("target_id"),
10
+ };
11
+ function renderTargetHelp() {
12
+ return [
13
+ `usage: warp-axi target <subcommand> [flags]`,
14
+ ``,
15
+ `Subcommands:`,
16
+ ` list List infrastructure targets`,
17
+ ``,
18
+ `Flags:`,
19
+ ` --json Raw JSON output`,
20
+ ` --help Show this help`,
21
+ ];
22
+ }
23
+ export const targetArea = {
24
+ name: "target",
25
+ description: "Infrastructure target management",
26
+ help: renderTargetHelp(),
27
+ async run(ctx, args) {
28
+ if (args.length === 0) {
29
+ return { stdout: renderTargetHelp().join("\n"), exitCode: 0 };
30
+ }
31
+ const [sub, ...rest] = args;
32
+ if (!targetSubcommands.includes(sub)) {
33
+ return {
34
+ stdout: renderError(`unknown target subcommand '${sub}'`, "VALIDATION_ERROR", [
35
+ `Available: ${targetSubcommands.join(", ")}`,
36
+ ]),
37
+ exitCode: 1,
38
+ };
39
+ }
40
+ const useJson = hasFlag(rest, "--json");
41
+ if (sub === "list") {
42
+ const warpArgs = ["target", "list"];
43
+ if (useJson)
44
+ warpArgs.push("--json");
45
+ try {
46
+ const result = await callWarpLenient(ctx.runner, warpArgs);
47
+ if (useJson)
48
+ return { stdout: result.stdout.trim(), exitCode: 0 };
49
+ const lines = result.stdout.trim().split("\n").filter((l) => l.trim());
50
+ if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) {
51
+ return {
52
+ stdout: renderOutput([`targets: no infrastructure targets configured`]),
53
+ exitCode: 0,
54
+ };
55
+ }
56
+ const items = lines.map((line) => {
57
+ const parts = line.split(/\s{2,}/);
58
+ return {
59
+ name: parts[0] ?? "",
60
+ type: parts[1] ?? "",
61
+ target_id: parts[2] ?? "",
62
+ };
63
+ });
64
+ return {
65
+ stdout: renderOutput([
66
+ renderListBlock({
67
+ noun: "targets",
68
+ items,
69
+ schema: targetSchema,
70
+ empty: "targets: no infrastructure targets configured",
71
+ }),
72
+ ]),
73
+ exitCode: 0,
74
+ };
75
+ }
76
+ catch (err) {
77
+ const message = err instanceof Error ? err.message : String(err);
78
+ return { stdout: renderError(message, "UNKNOWN"), exitCode: 1 };
79
+ }
80
+ }
81
+ return { stdout: renderTargetHelp().join("\n"), exitCode: 0 };
82
+ },
83
+ };
@@ -0,0 +1,2 @@
1
+ import { type CommandArea } from "../cli.js";
2
+ export declare const tunnelArea: CommandArea;
@@ -0,0 +1,112 @@
1
+ import { callWarp, callWarpLenient } from "../warp.js";
2
+ import { takeBoolFlag } from "../args.js";
3
+ import { wantsHelp, validateFlags } from "../util.js";
4
+ import { renderDetail, renderHelp, field, custom } from "../toon.js";
5
+ import { renderListBlock } from "../list.js";
6
+ const TUNNEL_SCHEMA = {
7
+ cidr: custom((item) => {
8
+ const cidr = item.cidr ?? item.address;
9
+ return cidr ? String(cidr) : String(item.route ?? "unknown");
10
+ }),
11
+ description: field("description"),
12
+ type: field("type"),
13
+ via: field("via"),
14
+ };
15
+ const STATS_SCHEMA = {
16
+ rx: field("rx"),
17
+ tx: field("tx"),
18
+ };
19
+ const RAW_SCHEMA = {
20
+ raw: field("raw"),
21
+ };
22
+ function parseTunnelDump(text) {
23
+ try {
24
+ const data = JSON.parse(text);
25
+ if (Array.isArray(data))
26
+ return data;
27
+ if (data.routes)
28
+ return data.routes;
29
+ if (data.tunnels)
30
+ return data.tunnels;
31
+ return [data];
32
+ }
33
+ catch {
34
+ const items = [];
35
+ let current = {};
36
+ for (const line of text.split("\n")) {
37
+ const m = line.match(/^\s*(.+?):\s+(.+)$/);
38
+ if (m) {
39
+ current[m[1].trim().toLowerCase().replace(/\s+/g, "_")] = m[2].trim();
40
+ }
41
+ else if (line.trim() === "" && Object.keys(current).length > 0) {
42
+ items.push(current);
43
+ current = {};
44
+ }
45
+ }
46
+ if (Object.keys(current).length > 0)
47
+ items.push(current);
48
+ return items;
49
+ }
50
+ }
51
+ export const tunnelArea = {
52
+ name: "tunnel",
53
+ aliases: ["split-tunnel", "routes"],
54
+ description: "Show split tunnel configuration and routes",
55
+ help: [
56
+ "usage: warp-axi tunnel <subcommand> [options]",
57
+ "",
58
+ "Subcommands:",
59
+ " dump List all split tunnel routes",
60
+ " stats Show tunnel traffic statistics",
61
+ "",
62
+ "Flags:",
63
+ " --json Use warp-cli --json for structured output",
64
+ ],
65
+ async run(ctx, args) {
66
+ if (wantsHelp(args))
67
+ return { stdout: renderHelp(this.help), exitCode: 0 };
68
+ const sub = args.shift();
69
+ switch (sub) {
70
+ case "dump":
71
+ return tunnelDump(ctx, args);
72
+ case "stats":
73
+ return tunnelStats(ctx, args);
74
+ default:
75
+ return { stdout: renderHelp(this.help), exitCode: 0 };
76
+ }
77
+ },
78
+ };
79
+ async function tunnelDump(ctx, args) {
80
+ const useJson = takeBoolFlag(args, "--json");
81
+ validateFlags(args, ["--json"], "tunnel dump");
82
+ const warpArgs = ["tunnel", "dump"];
83
+ if (useJson)
84
+ warpArgs.push("--json");
85
+ const result = await callWarpLenient(ctx.runner, warpArgs);
86
+ const items = parseTunnelDump(result.stdout);
87
+ return {
88
+ stdout: renderListBlock({
89
+ noun: "routes",
90
+ items,
91
+ schema: TUNNEL_SCHEMA,
92
+ empty: "routes: no split tunnel routes configured",
93
+ truncatedHint: "Run `warp-axi tunnel dump --json` for the full set",
94
+ }),
95
+ exitCode: 0,
96
+ };
97
+ }
98
+ async function tunnelStats(ctx, args) {
99
+ const useJson = takeBoolFlag(args, "--json");
100
+ validateFlags(args, ["--json"], "tunnel stats");
101
+ const warpArgs = ["tunnel", "stats"];
102
+ if (useJson)
103
+ warpArgs.push("--json");
104
+ const result = await callWarp(ctx.runner, warpArgs);
105
+ try {
106
+ const data = JSON.parse(result.stdout);
107
+ return { stdout: renderDetail("stats", data, STATS_SCHEMA), exitCode: 0 };
108
+ }
109
+ catch {
110
+ return { stdout: renderDetail("stats", { raw: result.stdout }, RAW_SCHEMA), exitCode: 0 };
111
+ }
112
+ }
@@ -0,0 +1,2 @@
1
+ import { CommandArea } from "../cli.js";
2
+ export declare const vnetArea: CommandArea;
@@ -0,0 +1,106 @@
1
+ import { callWarp, callWarpLenient } from "../warp.js";
2
+ import { hasFlag } from "../args.js";
3
+ import { renderError, renderOutput, field, boolYesNo } from "../toon.js";
4
+ import { renderListBlock } from "../list.js";
5
+ const vnetSubcommands = ["add", "remove", "list"];
6
+ const vnetSchema = {
7
+ name: field("name"),
8
+ id: field("vnet_id"),
9
+ is_default: boolYesNo("is_default"),
10
+ };
11
+ function renderVnetHelp() {
12
+ return [
13
+ `usage: warp-axi vnet <subcommand> [flags]`,
14
+ ``,
15
+ `Subcommands:`,
16
+ ` add <name> Create a virtual network`,
17
+ ` remove <name> Remove a virtual network`,
18
+ ` list List virtual networks`,
19
+ ``,
20
+ `Flags:`,
21
+ ` --json Raw JSON output`,
22
+ ` --help Show this help`,
23
+ ];
24
+ }
25
+ export const vnetArea = {
26
+ name: "vnet",
27
+ description: "Virtual network management",
28
+ help: renderVnetHelp(),
29
+ async run(ctx, args) {
30
+ if (args.length === 0) {
31
+ return { stdout: renderVnetHelp().join("\n"), exitCode: 0 };
32
+ }
33
+ const [sub, ...rest] = args;
34
+ if (!vnetSubcommands.includes(sub)) {
35
+ return {
36
+ stdout: renderError(`unknown vnet subcommand '${sub}'`, "VALIDATION_ERROR", [
37
+ `Available: ${vnetSubcommands.join(", ")}`,
38
+ ]),
39
+ exitCode: 1,
40
+ };
41
+ }
42
+ const useJson = hasFlag(rest, "--json");
43
+ if (sub === "list") {
44
+ const warpArgs = ["virtual-network", "list"];
45
+ if (useJson)
46
+ warpArgs.push("--json");
47
+ try {
48
+ const result = await callWarpLenient(ctx.runner, warpArgs);
49
+ if (useJson)
50
+ return { stdout: result.stdout.trim(), exitCode: 0 };
51
+ const lines = result.stdout.trim().split("\n").filter((l) => l.trim());
52
+ if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) {
53
+ return {
54
+ stdout: renderOutput([`vnets: no virtual networks configured`]),
55
+ exitCode: 0,
56
+ };
57
+ }
58
+ const items = lines.map((line) => {
59
+ const parts = line.split(/\s{2,}/);
60
+ return {
61
+ name: parts[0] ?? "",
62
+ vnet_id: parts[1] ?? "",
63
+ is_default: parts[2]?.toLowerCase() === "true" || parts[2]?.toLowerCase() === "yes",
64
+ };
65
+ });
66
+ return {
67
+ stdout: renderOutput([
68
+ renderListBlock({
69
+ noun: "vnets",
70
+ items,
71
+ schema: vnetSchema,
72
+ empty: "vnets: no virtual networks configured",
73
+ }),
74
+ ]),
75
+ exitCode: 0,
76
+ };
77
+ }
78
+ catch (err) {
79
+ const message = err instanceof Error ? err.message : String(err);
80
+ return { stdout: renderError(message, "UNKNOWN"), exitCode: 1 };
81
+ }
82
+ }
83
+ if (sub === "add" || sub === "remove") {
84
+ const positionalArgs = rest.filter((a) => !a.startsWith("--"));
85
+ const name = positionalArgs[0];
86
+ if (!name) {
87
+ return {
88
+ stdout: renderError(`vnet ${sub} requires a name`, "VALIDATION_ERROR", [
89
+ `Usage: warp-axi vnet ${sub} <name>`,
90
+ ]),
91
+ exitCode: 1,
92
+ };
93
+ }
94
+ const warpArgs = ["virtual-network", sub, name];
95
+ try {
96
+ const result = await callWarp(ctx.runner, warpArgs);
97
+ return { stdout: result.stdout.trim(), exitCode: 0 };
98
+ }
99
+ catch (err) {
100
+ const message = err instanceof Error ? err.message : String(err);
101
+ return { stdout: renderError(message, "UNKNOWN"), exitCode: 1 };
102
+ }
103
+ }
104
+ return { stdout: renderVnetHelp().join("\n"), exitCode: 0 };
105
+ },
106
+ };
@@ -0,0 +1,2 @@
1
+ export { AxiError, exitCodeForError } from "axi-sdk-js";
2
+ export { mapWarpError } from "./warp.js";
@@ -0,0 +1,2 @@
1
+ export { AxiError, exitCodeForError } from "axi-sdk-js";
2
+ export { mapWarpError } from "./warp.js";
@@ -0,0 +1,8 @@
1
+ export interface CountLineOptions {
2
+ count: number;
3
+ limit?: number;
4
+ totalCount?: number;
5
+ apiLimitHit?: boolean;
6
+ displayLimit?: number;
7
+ }
8
+ export declare function formatCountLine(opts: CountLineOptions): string;
@@ -0,0 +1,16 @@
1
+ export function formatCountLine(opts) {
2
+ const { count, limit, totalCount, apiLimitHit, displayLimit } = opts;
3
+ if (apiLimitHit) {
4
+ return `count: ${count}+ (api limit reached)`;
5
+ }
6
+ if (totalCount !== undefined && totalCount >= count) {
7
+ return `count: ${count} of ${totalCount} total`;
8
+ }
9
+ if (displayLimit !== undefined && count > displayLimit) {
10
+ return `count: ${count} (showing first ${displayLimit})`;
11
+ }
12
+ if (limit !== undefined && count === limit && count > 0) {
13
+ return `count: ${count} (showing first ${count})`;
14
+ }
15
+ return `count: ${count}`;
16
+ }
@@ -0,0 +1,11 @@
1
+ import { type Schema } from "./toon.js";
2
+ export interface ListBlockOptions {
3
+ noun: string;
4
+ items: unknown[];
5
+ schema: Schema;
6
+ limit?: number;
7
+ totalCount?: number;
8
+ empty: string;
9
+ truncatedHint?: string;
10
+ }
11
+ export declare function renderListBlock(opts: ListBlockOptions): string;
@@ -0,0 +1,17 @@
1
+ import { formatCountLine } from "./format.js";
2
+ import { renderHelp, renderList } from "./toon.js";
3
+ export function renderListBlock(opts) {
4
+ const { noun, items, schema, limit, totalCount, empty, truncatedHint } = opts;
5
+ const display = limit !== undefined ? items.slice(0, limit) : items;
6
+ const total = totalCount ?? items.length;
7
+ const countLine = formatCountLine({ count: total, limit, displayLimit: display.length });
8
+ if (items.length === 0) {
9
+ return [countLine, empty].filter(Boolean).join("\n");
10
+ }
11
+ const blocks = [countLine];
12
+ blocks.push(renderList(`${noun}[${display.length}]`, display, schema));
13
+ if (truncatedHint && limit !== undefined && items.length > limit) {
14
+ blocks.push(renderHelp([truncatedHint]));
15
+ }
16
+ return blocks.filter(Boolean).join("\n");
17
+ }
@@ -0,0 +1,2 @@
1
+ export declare function readStdin(): Promise<string>;
2
+ export declare function isStdinTTY(): boolean;
@@ -0,0 +1,12 @@
1
+ export async function readStdin() {
2
+ if (isStdinTTY())
3
+ return "";
4
+ const chunks = [];
5
+ for await (const chunk of process.stdin) {
6
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
7
+ }
8
+ return Buffer.concat(chunks).toString().trim();
9
+ }
10
+ export function isStdinTTY() {
11
+ return process.stdin.isTTY ?? false;
12
+ }
@@ -0,0 +1,24 @@
1
+ import { encode } from "@toon-format/toon";
2
+ export { encode };
3
+ export interface FieldDef {
4
+ render: (item: any) => any;
5
+ as?: string;
6
+ }
7
+ export interface Schema {
8
+ [outputKey: string]: FieldDef;
9
+ }
10
+ export declare function field(key: string, as?: string): FieldDef;
11
+ export declare function pluck(key: string, subkey: string, as?: string): FieldDef;
12
+ export declare function joinArray(key: string, subkey: string, as?: string, empty?: string): FieldDef;
13
+ export declare function relativeTime(key: string, as?: string): FieldDef;
14
+ export declare function boolYesNo(key: string, as?: string): FieldDef;
15
+ export declare function mapEnum(key: string, map: Record<string, string>, fallback?: string, as?: string): FieldDef;
16
+ export declare function lower(key: string, as?: string): FieldDef;
17
+ export declare function checksSummary(key: string, as?: string): FieldDef;
18
+ export declare function custom(fn: (item: any) => any, as?: string): FieldDef;
19
+ export declare function extract(item: any, schema: Schema): Record<string, unknown>;
20
+ export declare function renderList(label: string, items: unknown[], schema: Schema): string;
21
+ export declare function renderDetail(label: string, item: unknown, schema: Schema): string;
22
+ export declare function renderHelp(lines: string[]): string;
23
+ export declare function renderError(message: string, code: string, suggestions?: string[]): string;
24
+ export declare function renderOutput(blocks: Array<string | undefined>): string;
@@ -0,0 +1,140 @@
1
+ import { encode } from "@toon-format/toon";
2
+ export { encode };
3
+ function formatRelativeTime(iso) {
4
+ if (!iso)
5
+ return "unknown";
6
+ const then = new Date(iso).getTime();
7
+ if (Number.isNaN(then))
8
+ return "unknown";
9
+ const diffSec = Math.floor((Date.now() - then) / 1000);
10
+ if (diffSec < 60)
11
+ return "just now";
12
+ const min = Math.floor(diffSec / 60);
13
+ if (min < 60)
14
+ return `${min}m ago`;
15
+ const hour = Math.floor(min / 60);
16
+ if (hour < 24)
17
+ return `${hour}h ago`;
18
+ const day = Math.floor(hour / 24);
19
+ if (day < 30)
20
+ return `${day}d ago`;
21
+ const month = Math.floor(day / 30);
22
+ if (month < 12)
23
+ return `${month}mo ago`;
24
+ return `${Math.floor(month / 12)}y ago`;
25
+ }
26
+ function rec(item) {
27
+ return (item ?? {});
28
+ }
29
+ export function field(key, as) {
30
+ return {
31
+ render: (item) => rec(item)[key] ?? null,
32
+ ...(as ? { as } : {}),
33
+ };
34
+ }
35
+ export function pluck(key, subkey, as) {
36
+ return {
37
+ render: (item) => {
38
+ const val = rec(item)[key];
39
+ if (val && typeof val === "object")
40
+ return rec(val)[subkey] ?? null;
41
+ return null;
42
+ },
43
+ ...(as ? { as } : {}),
44
+ };
45
+ }
46
+ export function joinArray(key, subkey, as, empty = "none") {
47
+ return {
48
+ render: (item) => {
49
+ const val = rec(item)[key];
50
+ if (!Array.isArray(val) || val.length === 0)
51
+ return empty;
52
+ return val
53
+ .map((x) => (typeof x === "string" ? x : rec(x)[subkey]))
54
+ .filter(Boolean)
55
+ .join(",");
56
+ },
57
+ ...(as ? { as } : {}),
58
+ };
59
+ }
60
+ export function relativeTime(key, as) {
61
+ return {
62
+ render: (item) => {
63
+ const iso = rec(item)[key];
64
+ return typeof iso === "string" ? formatRelativeTime(iso) : "unknown";
65
+ },
66
+ ...(as ? { as } : {}),
67
+ };
68
+ }
69
+ export function boolYesNo(key, as) {
70
+ return {
71
+ render: (item) => (rec(item)[key] ? "yes" : "no"),
72
+ ...(as ? { as } : {}),
73
+ };
74
+ }
75
+ export function mapEnum(key, map, fallback, as) {
76
+ return {
77
+ render: (item) => {
78
+ const val = rec(item)[key];
79
+ const str = val == null ? undefined : String(val);
80
+ if (str !== undefined && Object.prototype.hasOwnProperty.call(map, str))
81
+ return map[str];
82
+ return fallback ?? str ?? "none";
83
+ },
84
+ ...(as ? { as } : {}),
85
+ };
86
+ }
87
+ export function lower(key, as) {
88
+ return {
89
+ render: (item) => {
90
+ const v = rec(item)[key];
91
+ return typeof v === "string" ? v.toLowerCase() : v ?? null;
92
+ },
93
+ ...(as ? { as } : {}),
94
+ };
95
+ }
96
+ export function checksSummary(key, as) {
97
+ return {
98
+ render: (item) => {
99
+ const checks = rec(item)[key];
100
+ if (!Array.isArray(checks))
101
+ return "none";
102
+ const passed = checks.filter((c) => rec(c).conclusion === "SUCCESS" || rec(c).conclusion === "NEUTRAL").length;
103
+ return `${passed}/${checks.length} pass`;
104
+ },
105
+ ...(as ? { as } : {}),
106
+ };
107
+ }
108
+ export function custom(fn, as) {
109
+ return {
110
+ render: fn,
111
+ ...(as ? { as } : {}),
112
+ };
113
+ }
114
+ export function extract(item, schema) {
115
+ const out = {};
116
+ for (const [label, def] of Object.entries(schema)) {
117
+ out[label] = def.render(item);
118
+ }
119
+ return out;
120
+ }
121
+ export function renderList(label, items, schema) {
122
+ return encode({ [label]: items.map((i) => extract(i, schema)) });
123
+ }
124
+ export function renderDetail(label, item, schema) {
125
+ return encode({ [label]: extract(item, schema) });
126
+ }
127
+ export function renderHelp(lines) {
128
+ if (lines.length === 0)
129
+ return "";
130
+ return `help[${lines.length}]:\n${lines.map((l) => ` ${l}`).join("\n")}`;
131
+ }
132
+ export function renderError(message, code, suggestions = []) {
133
+ const err = encode({ error: message, code });
134
+ if (suggestions.length > 0)
135
+ return `${err}\n${renderHelp(suggestions)}`;
136
+ return err;
137
+ }
138
+ export function renderOutput(blocks) {
139
+ return blocks.filter(Boolean).join("\n");
140
+ }
@@ -0,0 +1,2 @@
1
+ export declare function validateFlags(args: string[], valid: string[], commandName: string): void;
2
+ export declare function wantsHelp(args: string[]): boolean;
@@ -0,0 +1,17 @@
1
+ import { AxiError } from "axi-sdk-js";
2
+ const ALWAYS_ALLOWED = new Set(["--help", "-h", "--json"]);
3
+ export function validateFlags(args, valid, commandName) {
4
+ const validSet = new Set([...valid, ...ALWAYS_ALLOWED]);
5
+ const unknown = [];
6
+ for (const arg of args) {
7
+ if (arg.startsWith("--") && !validSet.has(arg)) {
8
+ unknown.push(arg);
9
+ }
10
+ }
11
+ if (unknown.length > 0) {
12
+ throw new AxiError("VALIDATION_ERROR", `Unknown flag(s) for ${commandName}: ${unknown.join(", ")}`, [`Valid flags: ${valid.sort().join(", ")}`, "Run with --help for usage"]);
13
+ }
14
+ }
15
+ export function wantsHelp(args) {
16
+ return args.includes("--help") || args.includes("-h");
17
+ }
@@ -0,0 +1 @@
1
+ export declare function readPackageVersion(): string;
@@ -0,0 +1,22 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ let cached;
5
+ export function readPackageVersion() {
6
+ if (cached)
7
+ return cached;
8
+ const dir = dirname(fileURLToPath(import.meta.url));
9
+ for (let d = dir; d !== "/"; d = dirname(d)) {
10
+ try {
11
+ const pkg = JSON.parse(readFileSync(join(d, "package.json"), "utf8"));
12
+ if (pkg.version) {
13
+ cached = String(pkg.version);
14
+ return cached;
15
+ }
16
+ }
17
+ catch {
18
+ continue;
19
+ }
20
+ }
21
+ return "0.0.0";
22
+ }
@@ -0,0 +1,19 @@
1
+ export interface WarpResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ }
6
+ export interface WarpRunner {
7
+ run(args: string[], options?: {
8
+ input?: string;
9
+ }): Promise<WarpResult>;
10
+ }
11
+ import { AxiError } from "axi-sdk-js";
12
+ export declare function realRunner(cwd?: string): WarpRunner;
13
+ export declare function callWarp(runner: WarpRunner, args: string[], options?: {
14
+ input?: string;
15
+ }): Promise<WarpResult>;
16
+ export declare function callWarpLenient(runner: WarpRunner, args: string[], options?: {
17
+ input?: string;
18
+ }): Promise<WarpResult>;
19
+ export declare function mapWarpError(stderr: string, _exitCode: number): AxiError;