@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.
- package/README.md +19 -0
- package/dist/buildstamp.json +1 -1
- package/dist/cli/commands/expose.d.ts +40 -0
- package/dist/cli/commands/expose.d.ts.map +1 -0
- package/dist/cli/commands/expose.js +200 -0
- package/dist/cli/commands/expose.js.map +1 -0
- package/dist/cli/program/build-program.d.ts.map +1 -1
- package/dist/cli/program/build-program.js +61 -0
- package/dist/cli/program/build-program.js.map +1 -1
- package/dist/config/io.d.ts +41 -0
- package/dist/config/io.d.ts.map +1 -1
- package/dist/config/io.js.map +1 -1
- package/dist/core/tunnel/auth-proxy.d.ts +55 -0
- package/dist/core/tunnel/auth-proxy.d.ts.map +1 -0
- package/dist/core/tunnel/auth-proxy.js +179 -0
- package/dist/core/tunnel/auth-proxy.js.map +1 -0
- package/dist/core/tunnel/manager.d.ts +42 -0
- package/dist/core/tunnel/manager.d.ts.map +1 -0
- package/dist/core/tunnel/manager.js +102 -0
- package/dist/core/tunnel/manager.js.map +1 -0
- package/dist/core/tunnel/providers/bore.d.ts +18 -0
- package/dist/core/tunnel/providers/bore.d.ts.map +1 -0
- package/dist/core/tunnel/providers/bore.js +117 -0
- package/dist/core/tunnel/providers/bore.js.map +1 -0
- package/dist/core/tunnel/providers/cloudflared.d.ts +24 -0
- package/dist/core/tunnel/providers/cloudflared.d.ts.map +1 -0
- package/dist/core/tunnel/providers/cloudflared.js +179 -0
- package/dist/core/tunnel/providers/cloudflared.js.map +1 -0
- package/dist/core/tunnel/providers/custom.d.ts +21 -0
- package/dist/core/tunnel/providers/custom.d.ts.map +1 -0
- package/dist/core/tunnel/providers/custom.js +124 -0
- package/dist/core/tunnel/providers/custom.js.map +1 -0
- package/dist/core/tunnel/registry.d.ts +15 -0
- package/dist/core/tunnel/registry.d.ts.map +1 -0
- package/dist/core/tunnel/registry.js +26 -0
- package/dist/core/tunnel/registry.js.map +1 -0
- package/dist/core/tunnel/state.d.ts +39 -0
- package/dist/core/tunnel/state.d.ts.map +1 -0
- package/dist/core/tunnel/state.js +57 -0
- package/dist/core/tunnel/state.js.map +1 -0
- package/dist/core/tunnel/types.d.ts +61 -0
- package/dist/core/tunnel/types.d.ts.map +1 -0
- package/dist/core/tunnel/types.js +20 -0
- package/dist/core/tunnel/types.js.map +1 -0
- 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 benchmark</code> Β· <code>brigade expose</code> Β· <code>brigade expose stop</code> π©Έ
|
|
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>
|
package/dist/buildstamp.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"builtAt":
|
|
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;
|
|
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")
|