File size: 3,797 Bytes
f778c12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// Gateway run command option registration and lazy handoff to runtime startup.
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;
        }
      }
    });
}