feat(gateway): allow webchat port override

This commit is contained in:
Peter Steinberger
2025-12-10 16:55:17 +00:00
parent e9fd73141d
commit 93a5784c58
4 changed files with 28 additions and 6 deletions

View File

@@ -214,6 +214,10 @@ Examples:
.command("gateway") .command("gateway")
.description("Run the WebSocket Gateway") .description("Run the WebSocket Gateway")
.option("--port <port>", "Port for the gateway WebSocket", "18789") .option("--port <port>", "Port for the gateway WebSocket", "18789")
.option(
"--webchat-port <port>",
"Port for the loopback WebChat HTTP server (default 18788)",
)
.option( .option(
"--token <token>", "--token <token>",
"Shared token required in hello.auth.token (default: CLAWDIS_GATEWAY_TOKEN env if set)", "Shared token required in hello.auth.token (default: CLAWDIS_GATEWAY_TOKEN env if set)",
@@ -231,6 +235,13 @@ Examples:
defaultRuntime.error("Invalid port"); defaultRuntime.error("Invalid port");
defaultRuntime.exit(1); defaultRuntime.exit(1);
} }
const webchatPort = opts.webchatPort
? Number.parseInt(String(opts.webchatPort), 10)
: undefined;
if (webchatPort !== undefined && (Number.isNaN(webchatPort) || webchatPort <= 0)) {
defaultRuntime.error("Invalid webchat port");
defaultRuntime.exit(1);
}
if (opts.force) { if (opts.force) {
try { try {
const killed = forceFreePort(port); const killed = forceFreePort(port);
@@ -256,7 +267,7 @@ Examples:
process.env.CLAWDIS_GATEWAY_TOKEN = String(opts.token); process.env.CLAWDIS_GATEWAY_TOKEN = String(opts.token);
} }
try { try {
await startGatewayServer(port); await startGatewayServer(port, { webchatPort });
} catch (err) { } catch (err) {
if (err instanceof GatewayLockError) { if (err instanceof GatewayLockError) {
defaultRuntime.error(`Gateway failed to start: ${err.message}`); defaultRuntime.error(`Gateway failed to start: ${err.message}`);

View File

@@ -22,6 +22,12 @@ export type HealthSummary = {
web: { web: {
linked: boolean; linked: boolean;
authAgeMs: number | null; authAgeMs: number | null;
connect?: {
ok: boolean;
status?: number | null;
error?: string | null;
elapsedMs?: number | null;
};
}; };
telegram: { telegram: {
configured: boolean; configured: boolean;

View File

@@ -236,7 +236,7 @@ function formatError(err: unknown): string {
async function refreshHealthSnapshot(opts?: { probe?: boolean }) { async function refreshHealthSnapshot(opts?: { probe?: boolean }) {
if (!healthRefresh) { if (!healthRefresh) {
healthRefresh = (async () => { healthRefresh = (async () => {
const snap = await getHealthSnapshot(undefined, opts); const snap = await getHealthSnapshot(undefined);
healthCache = snap; healthCache = snap;
healthVersion += 1; healthVersion += 1;
if (broadcastHealthUpdate) { if (broadcastHealthUpdate) {
@@ -251,7 +251,10 @@ async function refreshHealthSnapshot(opts?: { probe?: boolean }) {
return healthRefresh; return healthRefresh;
} }
export async function startGatewayServer(port = 18789): Promise<GatewayServer> { export async function startGatewayServer(
port = 18789,
opts?: { webchatPort?: number },
): Promise<GatewayServer> {
const releaseLock = await acquireGatewayLock().catch((err) => { const releaseLock = await acquireGatewayLock().catch((err) => {
// Bubble known lock errors so callers can present a nice message. // Bubble known lock errors so callers can present a nice message.
if (err instanceof GatewayLockError) throw err; if (err instanceof GatewayLockError) throw err;
@@ -1146,7 +1149,7 @@ export async function startGatewayServer(port = 18789): Promise<GatewayServer> {
defaultRuntime.log(`gateway log file: ${getResolvedLoggerSettings().file}`); defaultRuntime.log(`gateway log file: ${getResolvedLoggerSettings().file}`);
// Start loopback WebChat server (unless disabled via config). // Start loopback WebChat server (unless disabled via config).
void ensureWebChatServerFromConfig() void ensureWebChatServerFromConfig(opts?.webchatPort)
.then((webchat) => { .then((webchat) => {
if (webchat) { if (webchat) {
defaultRuntime.log( defaultRuntime.log(

View File

@@ -160,10 +160,12 @@ export async function __broadcastGatewayEventForTests() {
// no-op // no-op
} }
export async function ensureWebChatServerFromConfig() { export async function ensureWebChatServerFromConfig(
overridePort?: number,
) {
const cfg = loadConfig(); const cfg = loadConfig();
if (cfg.webchat?.enabled === false) return null; if (cfg.webchat?.enabled === false) return null;
const port = cfg.webchat?.port ?? WEBCHAT_DEFAULT_PORT; const port = overridePort ?? cfg.webchat?.port ?? WEBCHAT_DEFAULT_PORT;
try { try {
return await startWebChatServer(port); return await startWebChatServer(port);
} catch (err) { } catch (err) {