| |
| import { Option, type Command } from "commander"; |
| import { |
| WINDOWS_TASK_SUPERVISOR_CHILD_FLAG, |
| WINDOWS_TASK_SUPERVISOR_FLAG, |
| } from "../../daemon/windows-task-supervisor-contract.js"; |
| import type { GatewayRunOpts } from "./run-options.js"; |
| import { resolveGatewayRunOptions } from "./run-options.js"; |
| import { getGatewayRunRuntimeHooks } from "./runtime-hooks.js"; |
|
|
| const GATEWAY_AUTH_MODES = ["none", "token", "password", "trusted-proxy"] as const; |
| const GATEWAY_TAILSCALE_MODES = ["off", "serve", "funnel"] as const; |
|
|
| function formatModeChoices(modes: readonly string[]): string { |
| return modes.map((mode) => `"${mode}"`).join("|"); |
| } |
|
|
| type GatewayRunCommandHooks = { |
| beforeRun?: (opts: Pick<GatewayRunOpts, "force" | "reset">) => Promise<void> | void; |
| }; |
|
|
| export function addGatewayRunCommand(cmd: Command, hooks: GatewayRunCommandHooks = {}): Command { |
| return cmd |
| .option("--port <port>", "Port for the gateway WebSocket") |
| .option( |
| "--bind <mode>", |
| 'Bind mode ("loopback"|"lan"|"tailnet"|"auto"|"custom"). Defaults to config gateway.bind (or loopback).', |
| ) |
| .option( |
| "--token <token>", |
| "Shared token required in connect.params.auth.token (default: OPENCLAW_GATEWAY_TOKEN env if set)", |
| ) |
| .option("--auth <mode>", `Gateway auth mode (${formatModeChoices(GATEWAY_AUTH_MODES)})`) |
| .option("--password <password>", "Password for auth mode=password") |
| .option("--password-file <path>", "Read gateway password from file") |
| .option( |
| "--tailscale <mode>", |
| `Tailscale exposure mode (${formatModeChoices(GATEWAY_TAILSCALE_MODES)})`, |
| ) |
| .addOption(new Option("--tailscale-reset-on-exit").hideHelp()) |
| .option( |
| "--allow-unconfigured", |
| "Allow gateway start without enforcing gateway.mode=local in config (does not repair config)", |
| false, |
| ) |
| .option("--dev", "Create a dev config + workspace if missing (no BOOTSTRAP.md)", false) |
| .option( |
| "--ambient-channels", |
| "Allow the gateway to auto-configure channels from ambient environment variables", |
| false, |
| ) |
| .option("--dev-ambient-channels", "Deprecated alias for --ambient-channels", false) |
| .option( |
| "--reset", |
| "Reset dev config + credentials + sessions + workspace (requires --dev)", |
| false, |
| ) |
| .addOption(new Option(WINDOWS_TASK_SUPERVISOR_FLAG).hideHelp()) |
| .addOption(new Option(`${WINDOWS_TASK_SUPERVISOR_CHILD_FLAG} <restart-code>`).hideHelp()) |
| .addOption(new Option("--update-canary").hideHelp()) |
| .option("--force", "Kill any existing listener on the target port before starting", false) |
| .option("--verbose", "Verbose logging to stdout/stderr", false) |
| .option( |
| "--cli-backend-logs", |
| "Only show CLI backend logs in the console (includes stdout/stderr)", |
| false, |
| ) |
| .option("--claude-cli-logs", "Deprecated alias for --cli-backend-logs", false) |
| .option("--ws-log <style>", 'WebSocket log style ("auto"|"full"|"compact")', "auto") |
| .option("--compact", 'Alias for "--ws-log compact"', false) |
| .option("--raw-stream", "Log raw model stream events to jsonl", false) |
| .option("--raw-stream-path <path>", "Raw stream jsonl path") |
| .action(async (opts, command) => { |
| const resolved = resolveGatewayRunOptions(opts, command); |
| try { |
| await hooks.beforeRun?.(resolved); |
| const { runGatewayCommand } = await import("./run.js"); |
| await runGatewayCommand(resolved, getGatewayRunRuntimeHooks()); |
| } catch (error) { |
| const { handleGatewayStartupMaintenance } = await import("./startup-maintenance.js"); |
| if (!(await handleGatewayStartupMaintenance(error))) { |
| throw error; |
| } |
| } |
| }); |
| } |
|
|