UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

510 lines (509 loc) 19.4 kB
#!/usr/bin/env node import { i as normalizeEnv, t as isTruthyEnvValue } from "./env-Di49gJwo.js"; import { c as hasFlag, d as isRootHelpInvocation, f as isRootVersionInvocation, n as getCommandPathWithRootOptions, r as getCommandPositionalsWithRootOptions } from "./argv-CnfL__6a.js"; import { t as isMainModule } from "./is-main-CH4EEB_R.js"; import { t as resolveCliArgvInvocation } from "./argv-invocation-CxGUjPqA.js"; import { a as resolveCliContainerTarget, i as parseCliContainerArgs, n as parseCliProfileArgs, t as applyCliProfileEnv } from "./profile-BEs8qKtq.js"; import { t as normalizeWindowsArgv } from "./windows-argv-QUQsoRvr.js"; import { t as attachChildProcessBridge } from "./child-process-bridge-Vp-FhPhG.js"; import { t as resolveNodeStartupTlsEnvironment } from "./node-startup-env-Chdn0e25.js"; import { n as ensureOpenClawExecMarkerOnProcess } from "./openclaw-exec-env-48iH8Lwg.js"; import { t as installProcessWarningFilter } from "./warning-filter-p3Ue6g9S.js"; import { enableCompileCache, getCompileCacheDir } from "node:module"; import process$1 from "node:process"; import { fileURLToPath } from "node:url"; import { existsSync, readFileSync, statSync } from "node:fs"; import path from "node:path"; import os from "node:os"; import { spawn } from "node:child_process"; //#region src/process/respawn-child-runner.ts const RESPAWN_SIGNAL_EXIT_GRACE_MS = 1e3; const RESPAWN_SIGNAL_FORCE_KILL_GRACE_MS = 1e3; const RESPAWN_SIGNAL_HARD_EXIT_GRACE_MS = 1e3; function runRespawnChildWithSignalBridge(params) { const { command, args, env, runtime, onError } = params; const child = runtime.spawn(command, args, { stdio: "inherit", env }); let signalExitTimer; let signalForceKillTimer; let signalHardExitTimer; const clearSignalTimers = () => { if (signalExitTimer) { clearTimeout(signalExitTimer); signalExitTimer = void 0; } if (signalForceKillTimer) { clearTimeout(signalForceKillTimer); signalForceKillTimer = void 0; } if (signalHardExitTimer) { clearTimeout(signalHardExitTimer); signalHardExitTimer = void 0; } }; const forceKillChild = () => { try { child.kill(process.platform === "win32" ? "SIGTERM" : "SIGKILL"); } catch {} }; const requestChildTermination = () => { try { child.kill("SIGTERM"); } catch {} signalForceKillTimer = setTimeout(() => { forceKillChild(); signalHardExitTimer = setTimeout(() => { runtime.exit(1); }, RESPAWN_SIGNAL_HARD_EXIT_GRACE_MS); signalHardExitTimer.unref?.(); }, RESPAWN_SIGNAL_FORCE_KILL_GRACE_MS); signalForceKillTimer.unref?.(); }; const scheduleParentExit = () => { if (signalExitTimer) return; signalExitTimer = setTimeout(() => { requestChildTermination(); }, RESPAWN_SIGNAL_EXIT_GRACE_MS); signalExitTimer.unref?.(); }; runtime.attachChildProcessBridge(child, { onSignal: scheduleParentExit }); child.once("exit", (code, signal) => { clearSignalTimers(); if (signal) { runtime.exit(1); return; } runtime.exit(code ?? 1); }); child.once("error", (error) => { clearSignalTimers(); onError(error); runtime.exit(1); }); return child; } //#endregion //#region src/entry.compile-cache.ts function resolveEntryInstallRoot(entryFile) { const entryDir = path.dirname(entryFile); const entryParent = path.basename(entryDir); return entryParent === "dist" || entryParent === "src" ? path.dirname(entryDir) : entryDir; } function isSourceCheckoutInstallRoot(installRoot) { return existsSync(path.join(installRoot, ".git")) || existsSync(path.join(installRoot, "src", "entry.ts")); } function isNodeCompileCacheDisabled(env) { return env?.NODE_DISABLE_COMPILE_CACHE !== void 0; } function isNodeCompileCacheRequested(env) { return env?.NODE_COMPILE_CACHE !== void 0 && !isNodeCompileCacheDisabled(env); } function shouldEnableOpenClawCompileCache(params) { if (isNodeCompileCacheDisabled(params.env)) return false; return !isSourceCheckoutInstallRoot(params.installRoot); } function sanitizeCompileCachePathSegment(value) { const normalized = value.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, ""); return normalized.length > 0 ? normalized : "unknown"; } function readPackageVersion(packageJsonPath) { try { const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8")); if (parsed && typeof parsed === "object" && "version" in parsed && typeof parsed.version === "string" && parsed.version.trim().length > 0) return parsed.version; } catch {} return "unknown"; } function resolveOpenClawCompileCacheDirectory(params) { const env = params.env ?? process.env; const packageJsonPath = path.join(params.installRoot, "package.json"); const version = sanitizeCompileCachePathSegment(readPackageVersion(packageJsonPath)); let installMarker = "no-package-json"; try { const stat = statSync(packageJsonPath); installMarker = `${Math.trunc(stat.mtimeMs)}-${stat.size}`; } catch {} const baseDirectory = env.NODE_COMPILE_CACHE && !isNodeCompileCacheDisabled(env) ? env.NODE_COMPILE_CACHE : path.join(os.tmpdir(), "node-compile-cache"); return path.join(baseDirectory, "openclaw", version, sanitizeCompileCachePathSegment(installMarker)); } function buildOpenClawCompileCacheRespawnPlan(params) { const env = params.env ?? process.env; if (!isSourceCheckoutInstallRoot(params.installRoot)) return; if (env.OPENCLAW_SOURCE_COMPILE_CACHE_RESPAWNED === "1") return; if (!params.compileCacheDir && !isNodeCompileCacheRequested(env)) return; const nextEnv = { ...env, NODE_DISABLE_COMPILE_CACHE: "1", OPENCLAW_SOURCE_COMPILE_CACHE_RESPAWNED: "1" }; delete nextEnv.NODE_COMPILE_CACHE; return { command: params.execPath ?? process.execPath, args: [ ...params.execArgv ?? process.execArgv, params.currentFile, ...(params.argv ?? process.argv).slice(2) ], env: nextEnv }; } function respawnWithoutOpenClawCompileCacheIfNeeded(params) { const plan = buildOpenClawCompileCacheRespawnPlan({ currentFile: params.currentFile, installRoot: params.installRoot, compileCacheDir: getCompileCacheDir?.() }); if (!plan) return false; runOpenClawCompileCacheRespawnPlan(plan); return true; } function runOpenClawCompileCacheRespawnPlan(plan, runtime = { spawn, attachChildProcessBridge, exit: process.exit.bind(process), writeError: (message) => process.stderr.write(message) }) { return runRespawnChildWithSignalBridge({ command: plan.command, args: plan.args, env: plan.env, runtime, onError: (error) => { runtime.writeError(`[openclaw] Failed to respawn CLI without compile cache: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`); } }); } function enableOpenClawCompileCache(params) { if (!shouldEnableOpenClawCompileCache(params)) return; try { enableCompileCache(resolveOpenClawCompileCacheDirectory(params)); } catch {} } //#endregion //#region src/cli/respawn-policy.ts const GATEWAY_RUN_BOOLEAN_FLAGS = [ "--allow-unconfigured", "--claude-cli-logs", "--cli-backend-logs", "--compact", "--dev", "--force", "--raw-stream", "--reset", "--tailscale-reset-on-exit", "--verbose" ]; const GATEWAY_RUN_VALUE_FLAGS = [ "--auth", "--bind", "--password", "--password-file", "--port", "--raw-stream-path", "--tailscale", "--token", "--ws-log" ]; const INTERACTIVE_TTY_COMMANDS = new Set([ "tui", "terminal", "chat" ]); function isForegroundGatewayRunArgv(argv) { const positionals = getCommandPositionalsWithRootOptions(argv, { commandPath: ["gateway"], booleanFlags: GATEWAY_RUN_BOOLEAN_FLAGS, valueFlags: GATEWAY_RUN_VALUE_FLAGS }); if (!positionals) return false; return positionals.length === 0 || positionals.length === 1 && positionals[0] === "run"; } /** Returns whether CLI startup should avoid the general respawn wrapper for this argv. */ function shouldSkipRespawnForArgv(argv) { const invocation = resolveCliArgvInvocation(argv); return invocation.hasHelpOrVersion || invocation.primary !== null && INTERACTIVE_TTY_COMMANDS.has(invocation.primary) || invocation.primary === "gateway" && isForegroundGatewayRunArgv(argv); } /** Returns whether startup-environment respawn should be skipped without suppressing TUI respawn policy. */ function shouldSkipStartupEnvironmentRespawnForArgv(argv) { const invocation = resolveCliArgvInvocation(argv); return invocation.hasHelpOrVersion || invocation.primary === "gateway" && isForegroundGatewayRunArgv(argv); } //#endregion //#region src/entry.respawn.ts const EXPERIMENTAL_WARNING_FLAG = "--disable-warning=ExperimentalWarning"; const OPENCLAW_NODE_OPTIONS_READY = "OPENCLAW_NODE_OPTIONS_READY"; const OPENCLAW_NODE_EXTRA_CA_CERTS_READY = "OPENCLAW_NODE_EXTRA_CA_CERTS_READY"; const WINDOWS_STACK_SIZE_FLAG = "--stack-size=8192"; function pathModuleForPlatform(platform) { return platform === "win32" ? path.win32 : path.posix; } function resolveCliRespawnCommand(params) { const basename = pathModuleForPlatform(params.platform ?? process.platform).basename(params.execPath).toLowerCase(); if (basename === "volta-shim" || basename === "volta-shim.exe") return "node"; return params.execPath; } function hasExperimentalWarningSuppressed(params = {}) { const env = params.env ?? process.env; const execArgv = params.execArgv ?? process.execArgv; const nodeOptions = env.NODE_OPTIONS ?? ""; if (nodeOptions.includes("--disable-warning=ExperimentalWarning") || nodeOptions.includes("--no-warnings")) return true; return execArgv.some((arg) => arg === "--disable-warning=ExperimentalWarning" || arg === "--no-warnings"); } function hasStackSizeConfigured(execArgv) { return execArgv.some((arg) => arg === "--stack-size" || arg.startsWith("--stack-size=") || arg === "--stack_size" || arg.startsWith("--stack_size=")); } function buildCliRespawnPlan(params = {}) { const argv = params.argv ?? process.argv; const env = params.env ?? process.env; const execArgv = params.execArgv ?? process.execArgv; const execPath = params.execPath ?? process.execPath; const platform = params.platform ?? process.platform; const normalizedArgv = platform === "win32" ? normalizeWindowsArgv(argv, { platform, execPath }) : argv; if (shouldSkipStartupEnvironmentRespawnForArgv(normalizedArgv) || isTruthyEnvValue(env.OPENCLAW_NO_RESPAWN)) return null; const childEnv = { ...env }; const childExecArgv = [...execArgv]; let needsRespawn = false; if (platform === "win32") { if (!hasStackSizeConfigured(childExecArgv)) { childExecArgv.unshift(WINDOWS_STACK_SIZE_FLAG); needsRespawn = true; } if (!needsRespawn) return null; return { command: resolveCliRespawnCommand({ execPath, platform }), argv: [...childExecArgv, ...normalizedArgv.slice(1)], env: childEnv }; } const autoNodeExtraCaCerts = params.autoNodeExtraCaCerts ?? resolveNodeStartupTlsEnvironment({ env, execPath, includeDarwinDefaults: false }).NODE_EXTRA_CA_CERTS; if (autoNodeExtraCaCerts && !isTruthyEnvValue(env["OPENCLAW_NODE_EXTRA_CA_CERTS_READY"]) && !env.NODE_EXTRA_CA_CERTS) { childEnv.NODE_EXTRA_CA_CERTS = autoNodeExtraCaCerts; childEnv[OPENCLAW_NODE_EXTRA_CA_CERTS_READY] = "1"; needsRespawn = true; } if (!shouldSkipRespawnForArgv(argv) && !isTruthyEnvValue(env["OPENCLAW_NODE_OPTIONS_READY"]) && !hasExperimentalWarningSuppressed({ env, execArgv })) { childEnv[OPENCLAW_NODE_OPTIONS_READY] = "1"; childExecArgv.unshift(EXPERIMENTAL_WARNING_FLAG); needsRespawn = true; } if (!needsRespawn) return null; return { command: resolveCliRespawnCommand({ execPath, platform }), argv: [...childExecArgv, ...argv.slice(1)], env: childEnv }; } function runCliRespawnPlan(plan, runtime = { spawn, attachChildProcessBridge, exit: process.exit.bind(process), writeError: (message, error) => console.error(message, error) }) { return runRespawnChildWithSignalBridge({ command: plan.command, args: plan.argv, env: plan.env, runtime, onError: (error) => { runtime.writeError("[openclaw] Failed to respawn CLI:", error instanceof Error ? error.stack ?? error.message : error); } }); } //#endregion //#region src/entry.version-fast-path.ts function tryHandleRootVersionFastPath(argv, deps = {}) { if (resolveCliContainerTarget(argv, deps.env)) return false; if (!isRootVersionInvocation(argv)) return false; const output = deps.output ?? ((message) => console.log(message)); const exit = deps.exit ?? ((code) => process.exit(code)); const onError = deps.onError ?? ((error) => { console.error("[openclaw] Failed to resolve version:", error instanceof Error ? error.stack ?? error.message : error); process.exitCode = 1; }); (deps.resolveVersion ?? (async () => { const [{ VERSION }, { resolveCommitHash }] = await Promise.all([import("./version-Dpd_K_tD.js"), import("./git-commit-B1fet-7T.js")]); return { VERSION, resolveCommitHash }; }))().then(({ VERSION, resolveCommitHash }) => { const commit = resolveCommitHash({ moduleUrl: deps.moduleUrl ?? import.meta.url }); output(commit ? `OpenClaw ${VERSION} (${commit})` : `OpenClaw ${VERSION}`); exit(0); }).catch(onError); return true; } //#endregion //#region src/entry.ts const ENTRY_WRAPPER_PAIRS = [{ wrapperBasename: "openclaw.mjs", entryBasename: "entry.js" }, { wrapperBasename: "openclaw.js", entryBasename: "entry.js" }]; const loadRootHelpLiveConfigModule = async () => await import("./root-help-live-config-DEEmccNm.js"); const loadRootHelpMetadataModule = async () => await import("./root-help-metadata-mlt8FGqn.js"); function shouldForceReadOnlyAuthStore(argv) { const tokens = argv.slice(2).filter((token) => token.length > 0 && !token.startsWith("-")); for (let index = 0; index < tokens.length - 1; index += 1) if (tokens[index] === "secrets" && tokens[index + 1] === "audit") return true; return false; } function createGatewayEntryStartupTrace(argv) { const enabled = isTruthyEnvValue(process$1.env.OPENCLAW_GATEWAY_STARTUP_TRACE) && argv.slice(2).includes("gateway"); const started = performance.now(); let last = started; const emit = (name, durationMs, totalMs) => { if (!enabled) return; process$1.stderr.write(`[gateway] startup trace: entry.${name} ${durationMs.toFixed(1)}ms total=${totalMs.toFixed(1)}ms\n`); }; return { mark(name) { const now = performance.now(); emit(name, now - last, now - started); last = now; }, async measure(name, run) { const before = performance.now(); try { return await run(); } finally { const now = performance.now(); emit(name, now - before, now - started); last = now; } } }; } const gatewayEntryStartupTrace = createGatewayEntryStartupTrace(process$1.argv); if (!isMainModule({ currentFile: fileURLToPath(import.meta.url), wrapperEntryPairs: [...ENTRY_WRAPPER_PAIRS] })) {} else { const entryFile = fileURLToPath(import.meta.url); const installRoot = resolveEntryInstallRoot(entryFile); if (!respawnWithoutOpenClawCompileCacheIfNeeded({ currentFile: entryFile, installRoot })) { process$1.title = "openclaw"; ensureOpenClawExecMarkerOnProcess(); installProcessWarningFilter(); normalizeEnv(); enableOpenClawCompileCache({ installRoot }); gatewayEntryStartupTrace.mark("bootstrap"); if (shouldForceReadOnlyAuthStore(process$1.argv)) process$1.env.OPENCLAW_AUTH_STORE_READONLY = "1"; if (process$1.argv.includes("--no-color")) { process$1.env.NO_COLOR = "1"; process$1.env.FORCE_COLOR = "0"; } function ensureCliRespawnReady() { const plan = buildCliRespawnPlan(); if (!plan) return false; runCliRespawnPlan(plan); return true; } process$1.argv = normalizeWindowsArgv(process$1.argv); if (!ensureCliRespawnReady()) { const parsedContainer = parseCliContainerArgs(process$1.argv); if (!parsedContainer.ok) { console.error(`[openclaw] ${parsedContainer.error}`); process$1.exit(2); } const parsed = parseCliProfileArgs(parsedContainer.argv); if (!parsed.ok) { console.error(`[openclaw] ${parsed.error}`); process$1.exit(2); } if (resolveCliContainerTarget(process$1.argv) && parsed.profile) { console.error("[openclaw] --container cannot be combined with --profile/--dev"); process$1.exit(2); } if (parsed.profile) { applyCliProfileEnv({ profile: parsed.profile }); process$1.argv = parsed.argv; } gatewayEntryStartupTrace.mark("argv"); if (!tryHandleRootVersionFastPath(process$1.argv)) await runMainOrRootHelp(process$1.argv); } } } async function tryHandleRootHelpFastPath(argv, deps = {}) { if (resolveCliContainerTarget(argv, deps.env)) return false; if (!isRootHelpInvocation(argv)) return false; const handleError = deps.onError ?? ((error) => { console.error("[openclaw] Failed to display help:", error instanceof Error ? error.stack ?? error.message : error); process$1.exitCode = 1; }); try { const liveRootHelpOptions = await (deps.loadRootHelpRenderOptionsForConfigSensitivePlugins ?? (await loadRootHelpLiveConfigModule()).loadRootHelpRenderOptionsForConfigSensitivePlugins)(deps.env); if (!liveRootHelpOptions) { if ((deps.outputPrecomputedRootHelpText ?? (await loadRootHelpMetadataModule()).outputPrecomputedRootHelpText)()) return true; } await (deps.outputRootHelp ?? (await import("./root-help-DLswB4Uw.js")).outputRootHelp)(liveRootHelpOptions ?? void 0); return true; } catch (error) { handleError(error); return true; } } function resolvePrecomputedCommandHelpName(argv) { if (!hasFlag(argv, "--help") && !hasFlag(argv, "-h")) return null; const commandPath = getCommandPathWithRootOptions(argv, 2); if (commandPath.length !== 1) return null; const [commandName] = commandPath; if (commandName === "browser" || commandName === "secrets" || commandName === "nodes") return commandName; return null; } async function tryHandlePrecomputedCommandHelpFastPath(argv, deps = {}) { const env = deps.env ?? process$1.env; if (env.OPENCLAW_DISABLE_CLI_STARTUP_HELP_FAST_PATH === "1") return false; if (resolveCliContainerTarget(argv, env)) return false; const commandName = resolvePrecomputedCommandHelpName(argv); if (!commandName) return false; try { if (commandName === "nodes") { if (await (deps.loadRootHelpRenderOptionsForConfigSensitivePlugins ?? (await loadRootHelpLiveConfigModule()).loadRootHelpRenderOptionsForConfigSensitivePlugins)(env)) return false; } if (commandName === "browser") return (deps.outputPrecomputedBrowserHelpText ?? (await loadRootHelpMetadataModule()).outputPrecomputedBrowserHelpText)(); if (commandName === "secrets") return (deps.outputPrecomputedSecretsHelpText ?? (await loadRootHelpMetadataModule()).outputPrecomputedSecretsHelpText)(); return (deps.outputPrecomputedNodesHelpText ?? (await loadRootHelpMetadataModule()).outputPrecomputedNodesHelpText)(); } catch { return false; } } async function runMainOrRootHelp(argv) { if (await tryHandleRootHelpFastPath(argv)) return; if (await tryHandlePrecomputedCommandHelpFastPath(argv)) return; try { const { runCli } = await gatewayEntryStartupTrace.measure("run-main-import", () => import("./cli/run-main.js")); await runCli(argv); } catch (error) { const { formatCliFailureLines } = await import("./failure-output-DAqvtNUK.js"); for (const line of formatCliFailureLines({ title: "Could not start the CLI.", error, argv })) console.error(line); process$1.exit(1); } } //#endregion export { tryHandlePrecomputedCommandHelpFastPath, tryHandleRootHelpFastPath };