UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

346 lines (345 loc) 23.2 kB
import { r as defaultRuntime } from "./runtime-CF2WjnNZ.js"; import { l as resolveGatewayLaunchAgentLabel, p as resolveGatewaySystemdServiceName } from "./constants-ChqKLfPp.js"; import { m as shortenHomePath } from "./utils-P__uGsPB.js"; import { t as formatCliCommand } from "./command-format-C7YfyMTd.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { t as formatConfigIssueLine } from "./issue-format-CQvxWNW8.js"; import { n as isRich, r as theme, t as colorize } from "./theme-vjDs9tao.js"; import { t as formatGatewayHeapLimitReport } from "./gateway-heap-DTzoQBFN.js"; import { a as resolveGatewayRestartLogPath, o as resolveGatewaySupervisorLogPaths } from "./restart-logs-uOTpYNS7.js"; import { v as classifySystemdUnavailableDetail } from "./systemd-exec-C9fneC4I.js"; import { i as isSystemdStartLimitHit } from "./service-runtime-C1Las5eF.js"; import { a as renderSystemdUnavailableHints, i as isSystemdUnavailableDetail, r as formatRuntimeStatus } from "./runtime-hints-DTsISVnA.js"; import { r as isWSLEnv } from "./wsl-DjKrZ_YH.js"; import { c as renderRuntimeHints, d as safeDaemonEnv, i as filterDaemonEnv, l as resolveDaemonInstallBlockMessage, t as createCliStatusTextStyles, u as resolveRuntimeStatusColor } from "./shared-11cJIS_p.js"; import { i as resolvePluginVersionDriftUpdateCommand, r as resolvePluginVersionDriftTargets } from "./plugin-version-drift-Clfu4emY.js"; import { r as formatCliJsonFailure } from "./failure-output-Dc5ce9_F.js"; import { n as resolveControlUiLinks } from "./control-ui-links-BY4F5DYV.js"; import { n as formatGatewayRestartHandoffDiagnostic } from "./restart-handoff-V55GddB4.js"; import { n as renderPortDiagnosticsForCli, r as resolvePortListeningAddresses, t as gatherDaemonStatus } from "./status.gather-C8QyYha_.js"; import { i as renderGatewayServiceCleanupHints } from "./inspect-csgprKbh.js"; //#region src/cli/daemon-cli/status.print.ts function sanitizeDaemonStatusForJson(status) { const command = status.service.command; if (!command) return status; const safeEnv = filterDaemonEnv(command.environment); const nextCommand = { ...command, environment: Object.keys(safeEnv).length > 0 ? safeEnv : void 0 }; delete nextCommand.managedDefinition; delete nextCommand.managedOverrides; delete nextCommand.definitionPaths; return { ...status, service: { ...status.service, command: nextCommand } }; } function formatProbeKindLabel(kind) { return kind === "read" ? "Read probe:" : "Connectivity probe:"; } function formatCapabilityLabel(capability) { if (!capability) return null; return capability.replaceAll("_", "-"); } function formatCliVersionLine(cli) { if (!cli) return null; return cli.entrypoint ? `${cli.version} (${shortenHomePath(cli.entrypoint)})` : cli.version; } function formatConnectionLine(connection) { return `${connection.pid ? `pid=${connection.pid}` : "pid=?"}${connection.ppid ? ` ppid=${connection.ppid}` : ""}${` ${connection.direction}`}${connection.command ? ` ${connection.command}` : ""}${connection.address ? ` ${connection.address}` : ""}${connection.commandLine ? ` cmd=${shortenHomePath(connection.commandLine)}` : ""}`; } function printDaemonStatus(status, opts) { if (opts.json) { const sanitized = sanitizeDaemonStatusForJson(status); defaultRuntime.writeJson(sanitized); return; } const { rich, label, accent, infoText, okText, warnText, errorText } = createCliStatusTextStyles(); const spacer = () => defaultRuntime.log(""); const installBlock = resolveDaemonInstallBlockMessage("gateway"); const installCommand = formatCliCommand("openclaw gateway install"); const reinstallCommand = formatCliCommand("openclaw gateway install --force"); const { service, rpc, extraServices } = status; const serviceTargetsProbe = service.targetRole !== "diagnostic-only"; const serviceLoaded = service.loadState.status === "loaded"; const serviceStatus = serviceLoaded ? okText(service.loadedText) : warnText(service.loadState.status === "not-loaded" ? service.notLoadedText : "unknown"); defaultRuntime.log(`${label("Service:")} ${accent(service.label)} (${serviceStatus})`); if (status.logFile) defaultRuntime.log(`${label("File logs:")} ${infoText(shortenHomePath(status.logFile))}`); if (service.command?.programArguments?.length) defaultRuntime.log(`${label("Command:")} ${infoText(service.command.programArguments.join(" "))}`); if (service.command?.sourcePath) defaultRuntime.log(`${label("Service file:")} ${infoText(shortenHomePath(service.command.sourcePath))}`); if (service.command?.reloadPending) defaultRuntime.log(warnText("Systemd reload: pending (run systemctl --user daemon-reload)")); if (service.command?.workingDirectory) defaultRuntime.log(`${label("Working dir:")} ${infoText(shortenHomePath(service.command.workingDirectory))}`); const daemonEnvLines = safeDaemonEnv(service.command?.environment); if (daemonEnvLines.length > 0) defaultRuntime.log(`${label("Service env:")} ${daemonEnvLines.join(" ")}`); if (service.gatewayHeap) defaultRuntime.log(`${label("Gateway heap:")} ${infoText(formatGatewayHeapLimitReport(service.gatewayHeap))}`); const hostDesktop = status.hostDesktop ?? { enabled: false, state: "disabled", port: 5900 }; const hostDesktopValue = hostDesktop.state === "disabled" ? "disabled" : hostDesktop.state === "managed" ? hostDesktop.managedState === "running" ? `managed · running · display :${hostDesktop.display} · 127.0.0.1:${hostDesktop.port} · security VncAuth` : hostDesktop.managedState === "failed" ? `managed · failed: ${hostDesktop.error}` : hostDesktop.managedState === "unknown" ? "managed · runtime state unavailable" : `managed · ${hostDesktop.managedState === "not-started" ? "not started" : "starting"}` : `${hostDesktop.state} · 127.0.0.1:${hostDesktop.port}${hostDesktop.security ? ` · security ${hostDesktop.security}` : ""}`; defaultRuntime.log(`${label("Host desktop:")} ${infoText(hostDesktopValue)}`); spacer(); if (service.configAudit?.issues.length) { defaultRuntime.error(warnText("Service config looks out of date or non-standard.")); for (const issue of service.configAudit.issues) { const detail = issue.detail ? ` (${issue.detail})` : ""; defaultRuntime.error(`${warnText("Service config issue:")} ${issue.message}${detail}`); } const recommendation = installBlock ?? `Recommendation: run "${formatCliCommand("openclaw doctor")}" interactively for guided checks, or reinstall with "${reinstallCommand}".`; defaultRuntime.error(warnText(recommendation)); } if (status.config) { const cliCfg = `${shortenHomePath(status.config.cli.path)}${status.config.cli.exists ? "" : " (missing)"}${status.config.cli.valid ? "" : " (invalid)"}`; defaultRuntime.log(`${label("Config (cli):")} ${infoText(cliCfg)}`); if (!status.config.cli.valid && status.config.cli.issues?.length) for (const issue of status.config.cli.issues.slice(0, 5)) defaultRuntime.error(`${errorText("Config issue:")} ${formatConfigIssueLine(issue, "", { normalizeRoot: true })}`); if (status.config.cli.warnings?.length) { defaultRuntime.error(warnText("Config warnings:")); for (const warning of status.config.cli.warnings.slice(0, 5)) defaultRuntime.error(warnText(formatConfigIssueLine(warning, "-", { normalizeRoot: true }))); } if (status.config.daemon) { const daemonCfg = `${shortenHomePath(status.config.daemon.path)}${status.config.daemon.exists ? "" : " (missing)"}${status.config.daemon.valid ? "" : " (invalid)"}`; defaultRuntime.log(`${label("Config (service):")} ${infoText(daemonCfg)}`); if (!status.config.daemon.valid && status.config.daemon.issues?.length) for (const issue of status.config.daemon.issues.slice(0, 5)) defaultRuntime.error(`${errorText("Service config issue:")} ${formatConfigIssueLine(issue, "", { normalizeRoot: true })}`); if (status.config.daemon !== status.config.cli && status.config.daemon.warnings?.length) { const warningsLabel = status.config.daemon.path === status.config.cli.path ? "Config warnings:" : "Service config warnings:"; defaultRuntime.error(warnText(warningsLabel)); for (const warning of status.config.daemon.warnings.slice(0, 5)) defaultRuntime.error(warnText(formatConfigIssueLine(warning, "-", { normalizeRoot: true }))); } } if (status.config.mismatch) { defaultRuntime.error(errorText("Root cause: CLI and service are using different config paths (likely a profile/state-dir mismatch).")); const recovery = installBlock ?? `Fix: rerun \`${reinstallCommand}\` from the same --profile / OPENCLAW_STATE_DIR you expect.`; defaultRuntime.error(errorText(recovery)); } spacer(); } if (status.gateway) { const bindHost = status.gateway.bindHost ?? "n/a"; defaultRuntime.log(`${label("Gateway:")} bind=${infoText(status.gateway.bindMode)} (${infoText(bindHost)}), port=${infoText(String(status.gateway.port))} (${infoText(status.gateway.portSource)})`); defaultRuntime.log(`${label("Probe target:")} ${infoText(status.gateway.probeUrl)}`); if (!(status.config?.daemon?.controlUi?.enabled ?? true)) defaultRuntime.log(`${label("Dashboard:")} ${warnText("disabled")}`); else { const links = status.gateway.controlUiLinks ?? resolveControlUiLinks({ port: status.gateway.port, bind: status.gateway.bindMode, customBindHost: status.gateway.customBindHost, basePath: status.config?.daemon?.controlUi?.basePath, tlsEnabled: status.gateway.tlsEnabled === true }); defaultRuntime.log(`${label("Dashboard:")} ${infoText(links.httpUrl)}`); } if (status.gateway.probeNote) defaultRuntime.log(`${label("Probe note:")} ${infoText(status.gateway.probeNote)}`); if (status.gateway.windowsFirewall?.severity === "warning") { defaultRuntime.error(warnText(`Windows firewall: ${status.gateway.windowsFirewall.message}`)); for (const detail of status.gateway.windowsFirewall.details) defaultRuntime.error(warnText(` ${detail}`)); } spacer(); } const gatewayVersion = rpc?.server?.version?.trim() || status.gateway?.version?.trim(); const cliVersionLine = formatCliVersionLine(status.cli); if (gatewayVersion) { if (cliVersionLine) defaultRuntime.log(`${label("CLI version:")} ${infoText(cliVersionLine)}`); defaultRuntime.log(`${label("Gateway version:")} ${infoText(gatewayVersion)}`); if (status.cli?.version && status.cli.version !== gatewayVersion) { defaultRuntime.error(warnText(`Warning: this OpenClaw command is version ${status.cli.version}, but the running Gateway is version ${gatewayVersion}.`)); defaultRuntime.error(warnText("Check `openclaw --version`, `which openclaw`, and `openclaw gateway status --deep`; if this mismatch is unexpected, update PATH so `openclaw` points to the version you want, or reinstall the Gateway service from that same OpenClaw install.")); } spacer(); } const runtimeLine = formatRuntimeStatus(service.runtime); if (runtimeLine) { const runtimeColor = resolveRuntimeStatusColor(service.runtime?.status); defaultRuntime.log(`${label("Runtime:")} ${colorize(rich, runtimeColor, runtimeLine)}`); } if (service.restartHandoff) defaultRuntime.log(infoText(formatGatewayRestartHandoffDiagnostic(service.restartHandoff))); if (rpc && !rpc.ok && serviceTargetsProbe && serviceLoaded && service.runtime?.status === "running") { if (status.health?.healthy === true && status.health.staleGatewayPids.length === 0) defaultRuntime.log(warnText("Gateway process is running and owns the gateway port, so this is not a warm-up delay. Check the probe credentials/config, or restart the gateway and inspect its logs if it stays unresponsive.")); else defaultRuntime.log(warnText("Warm-up: launch agents can take a few seconds. Try again shortly.")); } if (rpc) { const probeLabel = formatProbeKindLabel(rpc.kind); if (rpc.ok) defaultRuntime.log(`${label(probeLabel)} ${okText("ok")}`); else { defaultRuntime.error(`${label(probeLabel)} ${errorText("failed")}`); if (rpc.authWarning) defaultRuntime.error(`${label("Probe auth:")} ${warnText(rpc.authWarning)}`); if (rpc.url) defaultRuntime.error(`${label("Probe target:")} ${rpc.url}`); const lines = (rpc.error ?? "unknown").split(/\r?\n/).filter(Boolean); for (const line of lines.slice(0, 12)) defaultRuntime.error(` ${errorText(line)}`); if (status.port?.status === "busy" && status.lastError) defaultRuntime.error(`${errorText("Last gateway error:")} ${status.lastError}`); } const capability = formatCapabilityLabel(rpc.capability); if (capability) defaultRuntime.log(`${label("Capability:")} ${infoText(capability)}`); spacer(); } if (status.health && status.health.staleGatewayPids.length > 0 && service.runtime?.status === "running" && typeof service.runtime.pid === "number") { defaultRuntime.error(errorText(`Gateway runtime PID does not own the listening port. Other gateway process(es) are listening: ${status.health.staleGatewayPids.join(", ")}`)); defaultRuntime.error(errorText(`Fix: run ${formatCliCommand("openclaw gateway restart")} and re-check with ${formatCliCommand("openclaw gateway status --deep")}.`)); spacer(); } if (status.connections?.established.length) { defaultRuntime.log(`${label("Established clients:")} ${infoText(String(status.connections.established.length))}`); for (const connection of status.connections.established.slice(0, 8)) defaultRuntime.log(` ${infoText(formatConnectionLine(connection))}`); if (status.connections.established.length > 8) defaultRuntime.log(` ${infoText(`... ${status.connections.established.length - 8} more connection(s)`)}`); defaultRuntime.log(warnText("If logs show protocol mismatch after rollback, stop stale OpenClaw client processes listed here and re-run gateway status.")); spacer(); } const serviceInspectionDetail = service.loadState.status === "unknown" ? service.loadState.detail : void 0; if (serviceInspectionDetail) { defaultRuntime.error(errorText(`Service inspection failed: ${serviceInspectionDetail}`)); defaultRuntime.error(errorText(`Retry: ${formatCliCommand("openclaw gateway status --deep")}`)); spacer(); } const systemdUnavailableDetail = serviceInspectionDetail ?? service.runtime?.inspectionFailure?.detail ?? service.runtime?.detail; if (process.platform === "linux" && (serviceInspectionDetail !== void 0 || rpc?.ok !== true) && isSystemdUnavailableDetail(systemdUnavailableDetail)) { const serviceEnv = service.command?.environment ?? process.env; defaultRuntime.error(errorText("systemd user services unavailable.")); for (const hint of renderSystemdUnavailableHints({ wsl: isWSLEnv(serviceEnv), kind: classifySystemdUnavailableDetail(systemdUnavailableDetail), env: serviceEnv })) defaultRuntime.error(errorText(hint)); spacer(); } if (service.runtime?.missingUnit) { defaultRuntime.error(errorText("Service unit not found.")); const recovery = installBlock ?? `Run: ${installCommand}`; defaultRuntime.error(errorText(recovery)); } else if (service.runtime?.missingGuiSession) { defaultRuntime.error(errorText("LaunchAgent plist exists, but macOS has no usable GUI session for this user.")); for (const hint of renderRuntimeHints(service.runtime, service.command?.environment ?? process.env, status.logFile)) defaultRuntime.error(errorText(hint)); } else if (service.runtime?.missingSupervision) { defaultRuntime.error(errorText("LaunchAgent plist exists but launchd has no loaded job.")); for (const hint of renderRuntimeHints(service.runtime, service.command?.environment ?? process.env, status.logFile)) defaultRuntime.error(errorText(hint)); } else if (serviceLoaded && service.runtime?.status === "stopped") { const startLimitHit = process.platform === "linux" && isSystemdStartLimitHit(service.runtime); defaultRuntime.error(errorText(startLimitHit ? `systemd stopped restarting the gateway after repeated crashes; run ${formatCliCommand("openclaw gateway restart")} or inspect logs.` : "Service is loaded but not running (likely exited immediately).")); for (const hint of renderRuntimeHints(service.runtime, service.command?.environment ?? process.env, status.logFile)) defaultRuntime.error(errorText(hint)); spacer(); } if (service.runtime?.cachedLabel) { const env = service.command?.environment ?? process.env; const labelValue = resolveGatewayLaunchAgentLabel(env.OPENCLAW_PROFILE); const recovery = installBlock ?? `Clear with: launchctl bootout gui/$UID/${labelValue}\nThen reinstall: ${installCommand}`; defaultRuntime.error(errorText(`LaunchAgent label cached but plist missing. ${recovery}`)); spacer(); } if (service.staleUpdateLaunchdJobs?.length) { defaultRuntime.error(errorText("Stale OpenClaw updater launchd job(s) detected.")); for (const job of service.staleUpdateLaunchdJobs) { const exitStatus = job.lastExitStatus !== void 0 ? `, last exit ${job.lastExitStatus}` : ""; const pid = job.pid !== void 0 ? `, pid ${job.pid}` : ""; defaultRuntime.error(errorText(`- ${job.label}${pid}${exitStatus}`)); } defaultRuntime.error(errorText(`Fix after confirming no update is running: launchctl remove <label>, then run ${formatCliCommand("openclaw gateway restart")}.`)); spacer(); } for (const line of renderPortDiagnosticsForCli(status, rpc?.ok)) defaultRuntime.error(errorText(line)); if (status.port) { const addrs = resolvePortListeningAddresses(status); if (addrs.length > 0) defaultRuntime.log(`${label("Listening:")} ${infoText(addrs.join(", "))}`); } if (status.portCli && status.portCli.port !== status.port?.port) defaultRuntime.log(`${label("Note:")} CLI config resolves gateway port=${status.portCli.port} (${status.portCli.status}).`); if (serviceTargetsProbe && serviceLoaded && service.runtime?.status === "running" && status.port && status.port.status === "free") { defaultRuntime.error(errorText(`Gateway port ${status.port.port} is not listening (service appears running).`)); const serviceEnv = { ...process.env, ...service.command?.environment }; if (status.lastError) defaultRuntime.error(`${errorText("Last gateway error:")} ${status.lastError}`); if (process.platform === "linux") { const unit = resolveGatewaySystemdServiceName(serviceEnv.OPENCLAW_PROFILE); defaultRuntime.error(errorText(`Logs: journalctl --user -u ${unit}.service -n 200 --no-pager`)); } else if (process.platform === "darwin") { const logs = resolveGatewaySupervisorLogPaths(serviceEnv, { platform: "darwin" }); defaultRuntime.error(`${errorText("Logs:")} ${shortenHomePath(logs.stdoutPath)}`); defaultRuntime.error(`${errorText("Errors:")} suppressed`); } defaultRuntime.error(`${errorText("Restart log:")} ${shortenHomePath(resolveGatewayRestartLogPath(serviceEnv))}`); spacer(); } if (extraServices.length > 0) { defaultRuntime.log(warnText("Other gateway-like services detected (best effort):")); for (const svc of extraServices) defaultRuntime.log(`- ${warnText(svc.label)} (${svc.scope}, ${svc.detail})`); for (const hint of renderGatewayServiceCleanupHints(extraServices)) defaultRuntime.log(`${infoText("Cleanup hint:")} ${hint}`); spacer(); } const drift = status.pluginVersionDrift; if (drift && drift.drifts.length > 0) { defaultRuntime.log(warnText(`Plugin version drift: ${drift.drifts.length} active official plugin${drift.drifts.length === 1 ? "" : "s"} not on gateway ${drift.gatewayVersion}`)); if (opts.deep) { for (const entry of drift.drifts) { const sourceLabel = entry.source === "clawhub" ? "clawhub" : "npm"; const resolvedTarget = entry.targetResolution?.status === "resolved" ? `; npm target ${entry.targetResolution.packageName}@${entry.targetResolution.version}` : ""; defaultRuntime.log(`- ${warnText(entry.pluginId)}: ${entry.installedVersion} (${sourceLabel}) → expected ${drift.gatewayVersion}${resolvedTarget}`); } const repairs = drift.drifts.map((entry) => ({ entry, command: resolvePluginVersionDriftUpdateCommand(entry) })); const updateCommands = repairs.map(({ command }) => command).filter((command) => Boolean(command)).map((command) => formatCliCommand(command)); const unresolvedRepairs = repairs.filter(({ command }) => !command); if (unresolvedRepairs.length > 0) { defaultRuntime.error(errorText("Plugin repair target resolution failed:")); for (const { entry } of unresolvedRepairs) { const targetResolution = entry.targetResolution; const detail = targetResolution?.status === "unresolved" ? targetResolution.error : "npm registry target was not resolved"; defaultRuntime.error(`- ${entry.pluginId}: ${detail}`); } defaultRuntime.error(errorText("No install command was generated for unresolved plugin targets. Retry gateway status --deep after checking registry availability.")); } if (updateCommands.length === 1 && unresolvedRepairs.length === 0) defaultRuntime.log(`${label("Fix:")} ${updateCommands[0]} && ${formatCliCommand("openclaw gateway restart")}.`); else if (updateCommands.length > 0) { defaultRuntime.log(`${label("Fix:")} update each drifted plugin:`); for (const command of updateCommands) defaultRuntime.log(`- ${command}`); if (unresolvedRepairs.length === 0) defaultRuntime.log(`Then run ${formatCliCommand("openclaw gateway restart")}.`); } } else defaultRuntime.log(infoText(`Run ${formatCliCommand("openclaw gateway status --deep")} for affected plugin ids and fix commands.`)); spacer(); } if (extraServices.length > 0) { defaultRuntime.log(infoText("Recommendation: run a single gateway per machine for most setups. One gateway supports multiple agents (see docs: /gateway#multiple-gateways-same-host).")); defaultRuntime.log(infoText("If you need multiple gateways (e.g., a rescue bot on the same host), isolate ports + config/state (see docs: /gateway#multiple-gateways-same-host).")); spacer(); } defaultRuntime.log(`${label("Troubles:")} run ${formatCliCommand("openclaw status")}`); defaultRuntime.log(`${label("Troubleshooting:")} https://docs.openclaw.ai/troubleshooting`); } //#endregion //#region src/cli/daemon-cli/status.ts function failDaemonStatus(opts, message) { if (opts.json) defaultRuntime.writeJson(formatCliJsonFailure(message)); else defaultRuntime.error(colorize(isRich(), theme.error, message)); defaultRuntime.exit(1); } /** Run Gateway status diagnostics and apply --require-rpc exit behavior. */ async function runDaemonStatus(opts) { if (opts.requireRpc && !opts.probe) { failDaemonStatus(opts, "Gateway status failed: --require-rpc needs probing enabled. Remove --no-probe or drop --require-rpc."); return; } let status; try { status = await gatherDaemonStatus({ rpc: opts.rpc, probe: opts.probe, requireRpc: opts.requireRpc, deep: opts.deep === true }); if (opts.deep && status.pluginVersionDrift) status.pluginVersionDrift = await resolvePluginVersionDriftTargets(status.pluginVersionDrift); printDaemonStatus(status, { json: opts.json, deep: opts.deep === true }); } catch (err) { failDaemonStatus(opts, `Gateway status failed: ${formatErrorMessage(err)}`); return; } if (opts.requireRpc && !status.rpc?.ok) defaultRuntime.exit(1); } //#endregion export { runDaemonStatus as t };