@spinabot/brigade 1.8.0 β†’ 1.9.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 (45) hide show
  1. package/README.md +19 -0
  2. package/dist/buildstamp.json +1 -1
  3. package/dist/cli/commands/expose.d.ts +40 -0
  4. package/dist/cli/commands/expose.d.ts.map +1 -0
  5. package/dist/cli/commands/expose.js +200 -0
  6. package/dist/cli/commands/expose.js.map +1 -0
  7. package/dist/cli/program/build-program.d.ts.map +1 -1
  8. package/dist/cli/program/build-program.js +61 -0
  9. package/dist/cli/program/build-program.js.map +1 -1
  10. package/dist/config/io.d.ts +41 -0
  11. package/dist/config/io.d.ts.map +1 -1
  12. package/dist/config/io.js.map +1 -1
  13. package/dist/core/tunnel/auth-proxy.d.ts +55 -0
  14. package/dist/core/tunnel/auth-proxy.d.ts.map +1 -0
  15. package/dist/core/tunnel/auth-proxy.js +179 -0
  16. package/dist/core/tunnel/auth-proxy.js.map +1 -0
  17. package/dist/core/tunnel/manager.d.ts +42 -0
  18. package/dist/core/tunnel/manager.d.ts.map +1 -0
  19. package/dist/core/tunnel/manager.js +102 -0
  20. package/dist/core/tunnel/manager.js.map +1 -0
  21. package/dist/core/tunnel/providers/bore.d.ts +18 -0
  22. package/dist/core/tunnel/providers/bore.d.ts.map +1 -0
  23. package/dist/core/tunnel/providers/bore.js +117 -0
  24. package/dist/core/tunnel/providers/bore.js.map +1 -0
  25. package/dist/core/tunnel/providers/cloudflared.d.ts +24 -0
  26. package/dist/core/tunnel/providers/cloudflared.d.ts.map +1 -0
  27. package/dist/core/tunnel/providers/cloudflared.js +179 -0
  28. package/dist/core/tunnel/providers/cloudflared.js.map +1 -0
  29. package/dist/core/tunnel/providers/custom.d.ts +21 -0
  30. package/dist/core/tunnel/providers/custom.d.ts.map +1 -0
  31. package/dist/core/tunnel/providers/custom.js +124 -0
  32. package/dist/core/tunnel/providers/custom.js.map +1 -0
  33. package/dist/core/tunnel/registry.d.ts +15 -0
  34. package/dist/core/tunnel/registry.d.ts.map +1 -0
  35. package/dist/core/tunnel/registry.js +26 -0
  36. package/dist/core/tunnel/registry.js.map +1 -0
  37. package/dist/core/tunnel/state.d.ts +39 -0
  38. package/dist/core/tunnel/state.d.ts.map +1 -0
  39. package/dist/core/tunnel/state.js +57 -0
  40. package/dist/core/tunnel/state.js.map +1 -0
  41. package/dist/core/tunnel/types.d.ts +61 -0
  42. package/dist/core/tunnel/types.d.ts.map +1 -0
  43. package/dist/core/tunnel/types.js +20 -0
  44. package/dist/core/tunnel/types.js.map +1 -0
  45. package/package.json +5 -1
package/README.md CHANGED
@@ -23,6 +23,25 @@
23
23
  <strong>Codex</strong> CLI, reuse that login (no browser, no re-auth).
24
24
  </p>
25
25
 
26
+ <p align="center">
27
+ 🩸πŸ’₯ <strong>NEW in v1.9.0 β€” <code>brigade bloody benchmark</code></strong> πŸ’₯🩸
28
+ </p>
29
+ <p align="center">
30
+ <em>One command. Your whole crew, <strong>live to the world.</strong></em><br/>
31
+ It drags your localhost gateway out into daylight β€” HTTPS at the edge, no account, no setup. It
32
+ defaults to an anonymous <strong>Cloudflare</strong> quick-tunnel, but the providers are pluggable
33
+ and <strong>fully open-source</strong>: point it at <code>bore</code>, <code>frp</code>,
34
+ <code>sish</code>, or your own self-hosted relay β€” no proprietary cloud required. A private access
35
+ key is minted and <strong>handled for you</strong>: you never see it, and anyone who stumbles onto
36
+ the URL just bleeds out on a <code>401</code>. 🩸
37
+ <br/>
38
+ Too squeamish for the name? It also answers to the polite <code>brigade expose</code>.
39
+ </p>
40
+
41
+ <p align="center">
42
+ <code>brigade bloody&nbsp;benchmark</code> &nbsp;·&nbsp; <code>brigade expose</code> &nbsp;·&nbsp; <code>brigade expose stop</code> &nbsp;🩸
43
+ </p>
44
+
26
45
  <p align="center">
27
46
  <a href="https://github.com/spinabot/brigade/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/spinabot/brigade/ci.yml?branch=main&style=for-the-badge&logo=githubactions&logoColor=white&label=CI" alt="CI status"></a>
28
47
  <a href="https://www.npmjs.com/package/@spinabot/brigade"><img src="https://img.shields.io/npm/v/@spinabot/brigade?style=for-the-badge&logo=npm&logoColor=white&color=CB3837" alt="npm version"></a>
@@ -1 +1 @@
1
- {"builtAt":1782398909347,"head":"4cc0d626b2bd8ffb4a49cd4c06347ba9d5d24f05","version":"1.8.0"}
1
+ {"builtAt":1782413017272,"head":"bbea17148ff119b4f52ebb95b1a92e16cf294c5b","version":"1.9.0"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `brigade expose` ( = `brigade bloody benchmark` ) β€” publish the gateway to
3
+ * the public internet through a secure tunnel.
4
+ *
5
+ * The gateway WebSocket is unauthenticated and loopback-only by design, so we
6
+ * never expose it directly. Instead we stand up a token-checking auth-proxy in
7
+ * front of it (`core/tunnel/auth-proxy.ts`) and tunnel THAT β€” the public URL
8
+ * carries a bearer token that the proxy enforces before any byte reaches the
9
+ * gateway. The gateway's localhost guard is untouched.
10
+ *
11
+ * brigade expose β€” start a cloudflare quick tunnel (token auto-gen)
12
+ * brigade expose --provider bore β€” self-hostable OSS tunnel
13
+ * brigade expose --insecure β€” NO token gate (loud warning; explicit opt-in)
14
+ * brigade expose status β€” show the active tunnel
15
+ * brigade expose stop β€” tear down the active tunnel
16
+ *
17
+ * This is a foreground, long-lived command: it holds the tunnel open until
18
+ * Ctrl-C (or `brigade expose stop` from another terminal).
19
+ */
20
+ export interface ExposeCommandOptions {
21
+ provider?: string;
22
+ token?: string;
23
+ insecure?: boolean;
24
+ /** `--open` β€” synonym for `--insecure` (no token gate). */
25
+ open?: boolean;
26
+ relay?: string;
27
+ command?: string;
28
+ port?: number;
29
+ verbose?: boolean;
30
+ json?: boolean;
31
+ }
32
+ export declare function runExposeCommand(opts: ExposeCommandOptions): Promise<void>;
33
+ export declare function runExposeStatusCommand(opts: {
34
+ json?: boolean;
35
+ showLink?: boolean;
36
+ }): Promise<number>;
37
+ export declare function runExposeStopCommand(opts: {
38
+ json?: boolean;
39
+ }): Promise<number>;
40
+ //# sourceMappingURL=expose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expose.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/expose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAeH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAqCD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDhF;AAyBD,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA6B1G;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBpF"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * `brigade expose` ( = `brigade bloody benchmark` ) β€” publish the gateway to
3
+ * the public internet through a secure tunnel.
4
+ *
5
+ * The gateway WebSocket is unauthenticated and loopback-only by design, so we
6
+ * never expose it directly. Instead we stand up a token-checking auth-proxy in
7
+ * front of it (`core/tunnel/auth-proxy.ts`) and tunnel THAT β€” the public URL
8
+ * carries a bearer token that the proxy enforces before any byte reaches the
9
+ * gateway. The gateway's localhost guard is untouched.
10
+ *
11
+ * brigade expose β€” start a cloudflare quick tunnel (token auto-gen)
12
+ * brigade expose --provider bore β€” self-hostable OSS tunnel
13
+ * brigade expose --insecure β€” NO token gate (loud warning; explicit opt-in)
14
+ * brigade expose status β€” show the active tunnel
15
+ * brigade expose stop β€” tear down the active tunnel
16
+ *
17
+ * This is a foreground, long-lived command: it holds the tunnel open until
18
+ * Ctrl-C (or `brigade expose stop` from another terminal).
19
+ */
20
+ import process from "node:process";
21
+ import { randomBytes } from "node:crypto";
22
+ import chalk from "chalk";
23
+ import { loadConfig } from "../../core/config.js";
24
+ import { mutateConfigAtomic } from "../../config/io.js";
25
+ import { isProcessAlive, probeGateway } from "../../core/gateway-probe.js";
26
+ import { startTunnel } from "../../core/tunnel/manager.js";
27
+ import { DEFAULT_PROVIDER, listProviderNames } from "../../core/tunnel/registry.js";
28
+ import { clearTunnelState, readTunnelState } from "../../core/tunnel/state.js";
29
+ import { DEFAULT_PORT, EXIT_FAILURE, EXIT_OK } from "../../protocol.js";
30
+ const LOOPBACK_HOST = "127.0.0.1";
31
+ /** Resolve the gateway port: flag β†’ config β†’ env β†’ default. */
32
+ function resolveGatewayPort(opts, cfg) {
33
+ if (typeof opts.port === "number" && opts.port > 0)
34
+ return opts.port;
35
+ const cfgPort = cfg.gateway?.port;
36
+ if (typeof cfgPort === "number" && cfgPort > 0)
37
+ return cfgPort;
38
+ const envPort = Number(process.env.BRIGADE_PORT);
39
+ if (Number.isInteger(envPort) && envPort > 0)
40
+ return envPort;
41
+ return DEFAULT_PORT;
42
+ }
43
+ /**
44
+ * Resolve the bearer token. `--insecure` β†’ none. `--token` β†’ that. Otherwise
45
+ * reuse `cfg.gateway.tunnel.token`, generating + persisting one on first run
46
+ * so the same URL+token survives restarts.
47
+ */
48
+ async function resolveToken(opts, cfg) {
49
+ if (opts.insecure || opts.open)
50
+ return undefined;
51
+ if (opts.token && opts.token.trim())
52
+ return opts.token.trim();
53
+ const existing = cfg.gateway?.tunnel?.token;
54
+ if (typeof existing === "string" && existing.length > 0)
55
+ return existing;
56
+ const generated = randomBytes(24).toString("base64url");
57
+ await mutateConfigAtomic((current) => {
58
+ const next = { ...current };
59
+ const gateway = { ...(next.gateway ?? {}) };
60
+ const tunnel = { ...(gateway.tunnel ?? {}) };
61
+ tunnel.token = generated;
62
+ gateway.tunnel = tunnel;
63
+ next.gateway = gateway;
64
+ return next;
65
+ });
66
+ return generated;
67
+ }
68
+ export async function runExposeCommand(opts) {
69
+ const cfg = loadConfig();
70
+ const gatewayPort = resolveGatewayPort(opts, cfg);
71
+ const provider = (opts.provider ?? cfg.gateway?.tunnel?.provider ?? DEFAULT_PROVIDER).trim();
72
+ const relay = opts.relay ?? cfg.gateway?.tunnel?.relay;
73
+ const command = opts.command ?? cfg.gateway?.tunnel?.command;
74
+ if (!listProviderNames().includes(provider)) {
75
+ console.error(chalk.red(`Unknown tunnel provider "${provider}". Known: ${listProviderNames().join(", ")}.`));
76
+ process.exit(EXIT_FAILURE);
77
+ }
78
+ // The gateway must be up β€” the tunnel forwards to it. Probe before we open
79
+ // anything to the world.
80
+ const probe = await probeGateway({ host: LOOPBACK_HOST, port: gatewayPort });
81
+ if (!probe.reachable) {
82
+ console.error(chalk.red(`No gateway reachable on ${LOOPBACK_HOST}:${gatewayPort}.`));
83
+ console.error(chalk.dim("Start it first: brigade gateway run"));
84
+ process.exit(EXIT_FAILURE);
85
+ }
86
+ // Refuse to silently re-expose if another tunnel is already live.
87
+ const existing = readTunnelState();
88
+ if (existing && isProcessAlive(existing.pid) && existing.pid !== process.pid) {
89
+ console.error(chalk.yellow(`A tunnel is already running (pid ${existing.pid}): ${existing.url}`));
90
+ console.error(chalk.dim("Stop it first: brigade expose stop"));
91
+ process.exit(EXIT_FAILURE);
92
+ }
93
+ const token = await resolveToken(opts, cfg);
94
+ const onLog = opts.verbose ? (line) => console.error(chalk.dim(` ${line}`)) : undefined;
95
+ console.error(chalk.cyan(`Opening ${provider} tunnel to the gateway on :${gatewayPort}…`));
96
+ let tunnel;
97
+ try {
98
+ tunnel = await startTunnel({ provider, gatewayHost: LOOPBACK_HOST, gatewayPort, token, relay, command, onLog });
99
+ }
100
+ catch (err) {
101
+ console.error(chalk.red(`Failed to open tunnel: ${err instanceof Error ? err.message : String(err)}`));
102
+ process.exit(EXIT_FAILURE);
103
+ }
104
+ printBanner(tunnel, provider);
105
+ // Tear down on Ctrl-C / SIGTERM so we never leave an orphaned public URL.
106
+ let shuttingDown = false;
107
+ const shutdown = async (signal) => {
108
+ if (shuttingDown)
109
+ return;
110
+ shuttingDown = true;
111
+ console.error(chalk.dim(`\nClosing tunnel (${signal})…`));
112
+ await tunnel.stop().catch(() => { });
113
+ process.exit(EXIT_OK);
114
+ };
115
+ process.on("SIGINT", () => void shutdown("SIGINT"));
116
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
117
+ // The build-program action holds the process open after we return.
118
+ }
119
+ function printBanner(tunnel, provider) {
120
+ const line = chalk.dim("─".repeat(68));
121
+ console.error("");
122
+ console.error(line);
123
+ console.error(` ${chalk.bold("🌍 Your Brigade gateway is now public")} ${chalk.dim(`(via ${provider})`)}`);
124
+ console.error("");
125
+ // Always show the CLEAN URL β€” the access key is never printed. It's
126
+ // generated + stored automatically; the operator never sees or types it.
127
+ console.error(` ${chalk.bold("Public URL β†’")} ${chalk.green.bold(tunnel.url)}`);
128
+ console.error("");
129
+ if (tunnel.secured) {
130
+ console.error(` ${chalk.green("πŸ”’ Secured automatically")} ${chalk.dim("β€” a private access key is saved to your config.")}`);
131
+ console.error(` ${chalk.dim("You never type it. Need the full link for another device? ")}${chalk.cyan("brigade expose status --show-link")}`);
132
+ }
133
+ else {
134
+ console.error(` ${chalk.red.bold("⚠ OPEN MODE: no key β€” ANYONE who finds this URL controls your crew.")}`);
135
+ console.error(` ${chalk.dim("Drop --open to secure it automatically instead.")}`);
136
+ }
137
+ console.error("");
138
+ console.error(` ${chalk.dim("Stop anytime: brigade expose stop Β· or press Ctrl-C")}`);
139
+ console.error(line);
140
+ console.error("");
141
+ }
142
+ export async function runExposeStatusCommand(opts) {
143
+ const state = readTunnelState();
144
+ if (!state || !isProcessAlive(state.pid)) {
145
+ if (state)
146
+ await clearTunnelState().catch(() => { });
147
+ if (opts.json)
148
+ console.log(JSON.stringify({ running: false }));
149
+ else
150
+ console.log("No tunnel is running.");
151
+ return EXIT_OK;
152
+ }
153
+ // `--json` is for tooling and intentionally includes the full link (key and
154
+ // all). The human-readable view hides the key unless `--show-link`.
155
+ if (opts.json) {
156
+ console.log(JSON.stringify({ running: true, ...state }));
157
+ return EXIT_OK;
158
+ }
159
+ const uptimeS = Math.round((Date.now() - state.startedAt) / 1000);
160
+ console.log(chalk.bold("Tunnel running"));
161
+ console.log(` provider ${state.provider}`);
162
+ console.log(` url ${state.url}`);
163
+ if (state.secured && opts.showLink) {
164
+ console.log(` full link ${chalk.green(state.urlWithToken)} ${chalk.dim("(includes access key β€” share carefully)")}`);
165
+ }
166
+ console.log(` gateway 127.0.0.1:${state.gatewayPort} (via auth-proxy :${state.proxyPort})`);
167
+ console.log(` secured ${state.secured ? chalk.green("yes (key handled automatically)") : chalk.red("NO (open mode)")}`);
168
+ if (state.secured && !opts.showLink) {
169
+ console.log(` ${chalk.dim("(run with --show-link to reveal the full access link for another device)")}`);
170
+ }
171
+ console.log(` pid ${state.pid}`);
172
+ console.log(` uptime ${uptimeS}s`);
173
+ return EXIT_OK;
174
+ }
175
+ export async function runExposeStopCommand(opts) {
176
+ const state = readTunnelState();
177
+ if (!state) {
178
+ if (opts.json)
179
+ console.log(JSON.stringify({ stopped: false, reason: "no-tunnel" }));
180
+ else
181
+ console.log("No tunnel is running.");
182
+ return EXIT_OK;
183
+ }
184
+ if (isProcessAlive(state.pid)) {
185
+ try {
186
+ process.kill(state.pid, "SIGTERM");
187
+ }
188
+ catch (err) {
189
+ if (!opts.json)
190
+ console.error(chalk.red(`Failed to signal tunnel pid ${state.pid}: ${err instanceof Error ? err.message : String(err)}`));
191
+ }
192
+ }
193
+ await clearTunnelState().catch(() => { });
194
+ if (opts.json)
195
+ console.log(JSON.stringify({ stopped: true, pid: state.pid }));
196
+ else
197
+ console.log(chalk.green(`Tunnel stopped (pid ${state.pid}).`));
198
+ return EXIT_OK;
199
+ }
200
+ //# sourceMappingURL=expose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expose.js","sourceRoot":"","sources":["../../../src/cli/commands/expose.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"build-program.d.ts","sourceRoot":"","sources":["../../../src/cli/program/build-program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqGpC,wBAAgB,YAAY,IAAI,OAAO,CA4jDtC"}
1
+ {"version":3,"file":"build-program.d.ts","sourceRoot":"","sources":["../../../src/cli/program/build-program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2GpC,wBAAgB,YAAY,IAAI,OAAO,CA+oDtC"}
@@ -46,6 +46,12 @@ const BOOT_OPTIONAL_PREFIXES = [
46
46
  "gateway uninstall",
47
47
  "gateway restart",
48
48
  "gateway supervise",
49
+ // `expose status/stop` just read the tunnel state file + a pid β€” they must
50
+ // work even when the storage backend is down (e.g. to tear down a tunnel).
51
+ "expose status",
52
+ "expose stop",
53
+ "bloody benchmark status",
54
+ "bloody benchmark stop",
49
55
  "store",
50
56
  "encrypt",
51
57
  "migrate",
@@ -334,6 +340,61 @@ export function buildProgram() {
334
340
  json: opts.json,
335
341
  }));
336
342
  });
343
+ // Shared option set so `expose` and `bloody benchmark` stay identical.
344
+ const addExposeOptions = (cmd) => cmd
345
+ .option("--provider <name>", "tunnel provider: cloudflare | bore | custom (default: cloudflare)")
346
+ .option("--token <token>", "bearer token required on the public URL (auto-generated + saved if omitted)")
347
+ .option("--open", "expose with NO token β€” anyone with the URL gets in (simplest, least safe)", false)
348
+ .option("--insecure", "alias for --open (NO token gate)", false)
349
+ .option("--relay <addr>", "self-hosted relay address (bore/custom providers)")
350
+ .option("--command <cmd>", "custom provider command template; {port} is replaced with the proxy port")
351
+ .option("-p, --port <port>", "gateway port to expose (default: config gateway.port or 7777)", (v) => parseInt(v, 10))
352
+ .option("-V, --verbose", "stream tunnel provider logs", false);
353
+ const runExpose = async (opts) => {
354
+ const { runExposeCommand } = await import("../commands/expose.js");
355
+ await runExposeCommand(opts);
356
+ await new Promise(() => { }); // hold the tunnel open until Ctrl-C
357
+ };
358
+ const expose = addExposeOptions(program.command("expose").description("Expose the gateway to the public internet via a secure tunnel")).action(runExpose);
359
+ expose
360
+ .command("status")
361
+ .description("Show the active tunnel (URL, provider, uptime)")
362
+ .option("--json", "emit JSON instead of human-readable text", false)
363
+ .option("--show-link", "reveal the full access link (includes the private key)", false)
364
+ .action(async (opts) => {
365
+ const { runExposeStatusCommand } = await import("../commands/expose.js");
366
+ await exitAfterFlush(await runExposeStatusCommand(opts));
367
+ });
368
+ expose
369
+ .command("stop")
370
+ .description("Tear down the active tunnel")
371
+ .option("--json", "emit JSON instead of human-readable text", false)
372
+ .action(async (opts) => {
373
+ const { runExposeStopCommand } = await import("../commands/expose.js");
374
+ await exitAfterFlush(await runExposeStopCommand(opts));
375
+ });
376
+ // Easter-egg alias: `brigade bloody benchmark [status|stop]` ≑ `brigade expose …`.
377
+ const bloody = program
378
+ .command("bloody")
379
+ .description("Alias group β€” `brigade bloody benchmark` opens a public tunnel (= `brigade expose`)");
380
+ const benchmark = addExposeOptions(bloody.command("benchmark").description("Expose the gateway to the public internet (alias for `brigade expose`)")).action(runExpose);
381
+ benchmark
382
+ .command("status")
383
+ .description("Show the active tunnel")
384
+ .option("--json", "emit JSON instead of human-readable text", false)
385
+ .option("--show-link", "reveal the full access link (includes the private key)", false)
386
+ .action(async (opts) => {
387
+ const { runExposeStatusCommand } = await import("../commands/expose.js");
388
+ await exitAfterFlush(await runExposeStatusCommand(opts));
389
+ });
390
+ benchmark
391
+ .command("stop")
392
+ .description("Tear down the active tunnel")
393
+ .option("--json", "emit JSON instead of human-readable text", false)
394
+ .action(async (opts) => {
395
+ const { runExposeStopCommand } = await import("../commands/expose.js");
396
+ await exitAfterFlush(await runExposeStopCommand(opts));
397
+ });
337
398
  /* ─────────────────────────────── connect ─────────────────────────────── */
338
399
  program
339
400
  .command("connect")