fix: restore hidden gateway-daemon alias

This commit is contained in:
Peter Steinberger
2026-01-08 19:54:20 +00:00
parent 28e3e51335
commit 04e0e10411
2 changed files with 284 additions and 249 deletions

View File

@@ -20,6 +20,7 @@
- Onboarding: QuickStart jumps straight into provider selection with Telegram preselected when unset. - Onboarding: QuickStart jumps straight into provider selection with Telegram preselected when unset.
- Onboarding: QuickStart auto-installs the Gateway daemon with Node (no runtime picker). - Onboarding: QuickStart auto-installs the Gateway daemon with Node (no runtime picker).
- Daemon runtime: remove Bun from selection options. - Daemon runtime: remove Bun from selection options.
- CLI: restore hidden `gateway-daemon` alias for legacy launchd configs.
## 2026.1.8 ## 2026.1.8

View File

@@ -37,6 +37,25 @@ type GatewayRpcOpts = {
expectFinal?: boolean; expectFinal?: boolean;
}; };
type GatewayRunOpts = {
port?: unknown;
bind?: unknown;
token?: unknown;
auth?: unknown;
password?: unknown;
tailscale?: unknown;
tailscaleResetOnExit?: boolean;
allowUnconfigured?: boolean;
force?: boolean;
verbose?: boolean;
wsLog?: unknown;
compact?: boolean;
};
type GatewayRunParams = {
legacyTokenEnv?: boolean;
};
const gatewayLog = createSubsystemLogger("gateway"); const gatewayLog = createSubsystemLogger("gateway");
type GatewayRunSignalAction = "stop" | "restart"; type GatewayRunSignalAction = "stop" | "restart";
@@ -246,67 +265,30 @@ const callGatewayCli = async (
}), }),
); );
export function registerGatewayCli(program: Command) { async function runGatewayCommand(
const gateway = program opts: GatewayRunOpts,
.command("gateway") params: GatewayRunParams = {},
.description("Run the WebSocket Gateway") ) {
.option("--port <port>", "Port for the gateway WebSocket") if (params.legacyTokenEnv) {
.option( const legacyToken = process.env.CLAWDIS_GATEWAY_TOKEN;
"--bind <mode>", if (legacyToken && !process.env.CLAWDBOT_GATEWAY_TOKEN) {
'Bind mode ("loopback"|"tailnet"|"lan"|"auto"). Defaults to config gateway.bind (or loopback).', process.env.CLAWDBOT_GATEWAY_TOKEN = legacyToken;
) }
.option( }
"--token <token>",
"Shared token required in connect.params.auth.token (default: CLAWDBOT_GATEWAY_TOKEN env if set)",
)
.option("--auth <mode>", 'Gateway auth mode ("token"|"password")')
.option("--password <password>", "Password for auth mode=password")
.option(
"--tailscale <mode>",
'Tailscale exposure mode ("off"|"serve"|"funnel")',
)
.option(
"--tailscale-reset-on-exit",
"Reset Tailscale serve/funnel configuration on shutdown",
false,
)
.option(
"--allow-unconfigured",
"Allow gateway start without gateway.mode=local in config",
false,
)
.option(
"--force",
"Kill any existing listener on the target port before starting",
false,
)
.option("--verbose", "Verbose logging to stdout/stderr", false)
.option(
"--ws-log <style>",
'WebSocket log style ("auto"|"full"|"compact")',
"auto",
)
.option("--compact", 'Alias for "--ws-log compact"', false)
.action(async (opts) => {
setVerbose(Boolean(opts.verbose)); setVerbose(Boolean(opts.verbose));
const wsLogRaw = (opts.compact ? "compact" : opts.wsLog) as const wsLogRaw = (opts.compact ? "compact" : opts.wsLog) as
| string | string
| undefined; | undefined;
const wsLogStyle: GatewayWsLogStyle = const wsLogStyle: GatewayWsLogStyle =
wsLogRaw === "compact" wsLogRaw === "compact" ? "compact" : wsLogRaw === "full" ? "full" : "auto";
? "compact"
: wsLogRaw === "full"
? "full"
: "auto";
if ( if (
wsLogRaw !== undefined && wsLogRaw !== undefined &&
wsLogRaw !== "auto" && wsLogRaw !== "auto" &&
wsLogRaw !== "compact" && wsLogRaw !== "compact" &&
wsLogRaw !== "full" wsLogRaw !== "full"
) { ) {
defaultRuntime.error( defaultRuntime.error('Invalid --ws-log (use "auto", "full", "compact")');
'Invalid --ws-log (use "auto", "full", "compact")',
);
defaultRuntime.exit(1); defaultRuntime.exit(1);
} }
setGatewayWsLogStyle(wsLogStyle); setGatewayWsLogStyle(wsLogStyle);
@@ -344,9 +326,7 @@ export function registerGatewayCli(program: Command) {
); );
} }
if (waitedMs > 0) { if (waitedMs > 0) {
gatewayLog.info( gatewayLog.info(`force: waited ${waitedMs}ms for port ${port} to free`);
`force: waited ${waitedMs}ms for port ${port} to free`,
);
} }
} }
} catch (err) { } catch (err) {
@@ -360,9 +340,7 @@ export function registerGatewayCli(program: Command) {
} }
const authModeRaw = opts.auth ? String(opts.auth) : undefined; const authModeRaw = opts.auth ? String(opts.auth) : undefined;
const authMode: GatewayAuthMode | null = const authMode: GatewayAuthMode | null =
authModeRaw === "token" || authModeRaw === "password" authModeRaw === "token" || authModeRaw === "password" ? authModeRaw : null;
? authModeRaw
: null;
if (authModeRaw && !authMode) { if (authModeRaw && !authMode) {
defaultRuntime.error('Invalid --auth (use "token" or "password")'); defaultRuntime.error('Invalid --auth (use "token" or "password")');
defaultRuntime.exit(1); defaultRuntime.exit(1);
@@ -376,9 +354,7 @@ export function registerGatewayCli(program: Command) {
? tailscaleRaw ? tailscaleRaw
: null; : null;
if (tailscaleRaw && !tailscaleMode) { if (tailscaleRaw && !tailscaleMode) {
defaultRuntime.error( defaultRuntime.error('Invalid --tailscale (use "off", "serve", or "funnel")');
'Invalid --tailscale (use "off", "serve", or "funnel")',
);
defaultRuntime.exit(1); defaultRuntime.exit(1);
return; return;
} }
@@ -491,9 +467,7 @@ export function registerGatewayCli(program: Command) {
? { ? {
mode: authMode ?? undefined, mode: authMode ?? undefined,
token: opts.token ? String(opts.token) : undefined, token: opts.token ? String(opts.token) : undefined,
password: opts.password password: opts.password ? String(opts.password) : undefined,
? String(opts.password)
: undefined,
} }
: undefined, : undefined,
tailscale: tailscale:
@@ -533,7 +507,67 @@ export function registerGatewayCli(program: Command) {
defaultRuntime.error(`Gateway failed to start: ${String(err)}`); defaultRuntime.error(`Gateway failed to start: ${String(err)}`);
defaultRuntime.exit(1); defaultRuntime.exit(1);
} }
}
function addGatewayRunCommand(
cmd: Command,
params: GatewayRunParams = {},
): Command {
return cmd
.option("--port <port>", "Port for the gateway WebSocket")
.option(
"--bind <mode>",
'Bind mode ("loopback"|"tailnet"|"lan"|"auto"). Defaults to config gateway.bind (or loopback).',
)
.option(
"--token <token>",
"Shared token required in connect.params.auth.token (default: CLAWDBOT_GATEWAY_TOKEN env if set)",
)
.option("--auth <mode>", 'Gateway auth mode ("token"|"password")')
.option("--password <password>", "Password for auth mode=password")
.option(
"--tailscale <mode>",
'Tailscale exposure mode ("off"|"serve"|"funnel")',
)
.option(
"--tailscale-reset-on-exit",
"Reset Tailscale serve/funnel configuration on shutdown",
false,
)
.option(
"--allow-unconfigured",
"Allow gateway start without gateway.mode=local in config",
false,
)
.option(
"--force",
"Kill any existing listener on the target port before starting",
false,
)
.option("--verbose", "Verbose logging to stdout/stderr", false)
.option(
"--ws-log <style>",
'WebSocket log style ("auto"|"full"|"compact")',
"auto",
)
.option("--compact", 'Alias for "--ws-log compact"', false)
.action(async (opts) => {
await runGatewayCommand(opts, params);
}); });
}
export function registerGatewayCli(program: Command) {
const gateway = addGatewayRunCommand(
program.command("gateway").description("Run the WebSocket Gateway"),
);
// Back-compat: legacy launchd plists used gateway-daemon; keep hidden alias.
addGatewayRunCommand(
program
.command("gateway-daemon", { hidden: true })
.description("Run the WebSocket Gateway as a long-lived daemon"),
{ legacyTokenEnv: true },
);
gatewayCallOpts( gatewayCallOpts(
gateway gateway