UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

324 lines (323 loc) 13 kB
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js"; import { n as parseTcpPort, r as parseTcpPortFromArgs } from "./tcp-port-C3gLZtJi.js"; import { r as assertGatewayServiceMutationAllowed } from "./gateway-supervision-D7p37rG2.js"; import { h as resolveFutureConfigActionBlock, m as formatFutureConfigActionBlock } from "./config-env-vars-DUfQlcAk.js"; import { r as readSystemdServiceExecStart } from "./systemd-service-files-BCcumv4T.js"; import { t as resolveServiceEntrypoint } from "./service-layout-bTjykwbW.js"; import { c as restartLaunchAgent, d as stageLaunchAgent, f as uninstallLaunchAgent, l as startLaunchAgent, o as stopLaunchAgent, u as installLaunchAgent } from "./launchd-BH9PtJsR.js"; import { n as mergeGatewayServiceEnv } from "./gateway-service-probe-hosts-CH6J1p0Q.js"; import { d as readLaunchAgentProgramArguments, f as readLaunchAgentRuntime, i as isLaunchAgentLoaded, r as isLaunchAgentEnabled } from "./launchd-runtime-D4UiKWFz.js"; import { _ as readScheduledTaskCommand, d as isScheduledTaskInstalled, f as readScheduledTaskRuntime, i as restartScheduledTask, n as stageScheduledTask, o as startScheduledTask, r as uninstallScheduledTask, s as stopScheduledTask, t as installScheduledTask } from "./schtasks-CgL6lOw1.js"; import { t as createServiceRuntimeInspectionFailure } from "./service-runtime-C1Las5eF.js"; import { n as findInstalledSystemdGatewayScope, o as isSystemdServiceAbsent } from "./systemd-scope-MYrAg51P.js"; import { a as restartSystemdService, f as stageSystemdService, m as readSystemdDefinitionMutationCapability, n as readSystemdServiceRuntime, o as startSystemdService, p as uninstallSystemdService, s as stopSystemdService, t as isSystemdServiceEnabled, u as installSystemdService } from "./systemd-C1W5NZTY.js"; import fs from "node:fs"; import path from "node:path"; import os from "node:os"; //#region src/daemon/future-config-guard.ts /** Prevents daemon write actions when the config belongs to a newer OpenClaw. */ async function readFutureConfigActionBlock(action) { const { readConfigFileSnapshot } = await import("./io.runtime.js"); try { const snapshot = await readConfigFileSnapshot(); return resolveFutureConfigActionBlock({ action, snapshot }); } catch { return null; } } async function assertFutureConfigActionAllowed(action) { const block = await readFutureConfigActionBlock(action); if (block) throw new Error(formatFutureConfigActionBlock(block)); } //#endregion //#region src/daemon/service.ts /** Platform service registry and shared gateway service start/repair logic. */ function ignoreServiceWriteResult(write) { return async (args) => { await write(args); }; } const TEMP_PROGRAM_ROOTS = [ os.tmpdir(), "/tmp", "/private/tmp", "/var/tmp" ].map((entry) => path.resolve(entry)); function pathIsSameOrChild(candidate, parent) { return candidate === parent || candidate.startsWith(`${parent}${path.sep}`); } function isTemporaryProgramPath(value) { if (!value || !path.isAbsolute(value)) return false; const resolved = path.resolve(value); return TEMP_PROGRAM_ROOTS.some((root) => pathIsSameOrChild(resolved, root)); } function isMissingProgramPath(value) { if (!value || !path.isAbsolute(value)) return false; return !fs.existsSync(value); } function collectGatewayServiceStartRepairIssues(state, expectedPort) { const command = state.command; if (state.loadState.status !== "loaded" || !command) return []; const issues = []; const servicePort = parseTcpPortFromArgs(command.programArguments) ?? parseTcpPort(command.environment?.OPENCLAW_GATEWAY_PORT ?? ""); if (expectedPort !== void 0 && servicePort !== null && servicePort !== expectedPort) issues.push({ code: "port-mismatch", message: `service port ${servicePort} does not match current gateway config port ${expectedPort}` }); for (const candidate of /* @__PURE__ */ new Set([command.programArguments[0], resolveServiceEntrypoint(command)])) { if (isTemporaryProgramPath(candidate)) { issues.push({ code: "temporary-program", message: `service command points at a temporary path: ${candidate}` }); continue; } if (isMissingProgramPath(candidate)) issues.push({ code: "missing-program", message: `service command points at a missing path: ${candidate}` }); } return issues; } /** Reads the installed service and reports definition drift that must be repaired before launch. */ async function inspectGatewayServiceStartRepair(service, args, expectedPort) { const state = await readGatewayServiceState(service, args); return { state, issues: collectGatewayServiceStartRepairIssues(state, expectedPort) }; } function formatGatewayServiceStartRepairIssues(issues) { return issues.map((issue) => issue.message).join("; "); } async function readGatewayServiceLoadState(service, args = {}) { try { return { status: await service.isLoaded(args) ? "loaded" : "not-loaded" }; } catch (error) { return { status: "unknown", detail: String(error) }; } } async function readGatewayServiceState(service, args = {}) { const baseEnv = args.env ?? process.env; const { timeoutMs } = args; if (await service.isAbsent?.({ env: baseEnv, timeoutMs }).catch(() => false)) { args.validateEnvBeforeStatusRead?.(baseEnv); return { installed: false, loadState: { status: "not-loaded" }, running: false, env: baseEnv, command: null, runtime: { status: "stopped", missingUnit: true } }; } const command = args.requireEffective ? await service.readCommand(baseEnv, { timeoutMs, requireEffective: true }) : await service.readCommand(baseEnv, { timeoutMs }).catch(() => null); const env = mergeGatewayServiceEnv(baseEnv, command); args.validateEnvBeforeStatusRead?.(env); const [installed, loadState, runtime, definitionMutationCapability] = await Promise.all([ command !== null ? true : service.hasInstalledDefinition?.({ env, timeoutMs }).catch(() => false) ?? false, readGatewayServiceLoadState(service, { env, timeoutMs }), service.readRuntime(env, { timeoutMs }).catch((error) => createServiceRuntimeInspectionFailure(error)), args.requireEffective ? service.readDefinitionMutationCapability?.({ env: baseEnv, environment: env, timeoutMs }).catch(() => ({ kind: "unknown", reason: "inspection-failed" })) : void 0 ]); return { installed, loadState, running: runtime?.status === "running", env, command, ...definitionMutationCapability ? { definitionMutationCapability } : {}, runtime }; } async function startGatewayService(service, args, expectedPort) { const { state, issues: repairIssues } = await inspectGatewayServiceStartRepair(service, { env: args.env }, expectedPort); if (state.loadState.status === "unknown") throw new Error(`Service status inspection failed: ${state.loadState.detail}`); if (state.loadState.status === "not-loaded" && !state.installed) return { outcome: "missing-install", state }; if (state.loadState.status === "loaded" && state.running) return { outcome: "already-running", state, issues: repairIssues }; if (repairIssues.length > 0) return { outcome: "repair-required", state, issues: repairIssues }; let nextState; try { await service.start({ ...args, env: state.env }); nextState = await readGatewayServiceState(service, { env: state.env }); } catch (err) { const recoveryState = await readGatewayServiceState(service, { env: state.env }); if (!recoveryState.installed) return { outcome: "missing-install", state: recoveryState }; throw err; } if (nextState.loadState.status === "unknown") throw new Error(`Service status inspection failed after start: ${nextState.loadState.detail}`); const runtime = nextState.runtime; const failedState = normalizeLowercaseStringOrEmpty(runtime?.state) === "failed"; const newFailedExit = runtime?.status === "stopped" && typeof runtime.lastExitStatus === "number" && runtime.lastExitStatus !== 0 && runtime.lastExitStatus !== state.runtime?.lastExitStatus; if (failedState || newFailedExit) { const failure = failedState ? "state failed" : `exit ${runtime?.lastExitStatus}`; throw new Error(`Service failed to start (${failure}). Check the service logs and retry.`); } return { outcome: "started", state: nextState }; } function describeGatewayServiceRestart(serviceNoun, result) { if (result.outcome === "scheduled") return { scheduled: true, daemonActionResult: "scheduled", message: `restart scheduled, ${normalizeLowercaseStringOrEmpty(serviceNoun)} will restart momentarily`, progressMessage: `${serviceNoun} service restart scheduled.` }; return { scheduled: false, daemonActionResult: "restarted", message: `${serviceNoun} service restarted.`, progressMessage: `${serviceNoun} service restarted.` }; } function createUnsupportedGatewayServiceError() { return /* @__PURE__ */ new Error(`Gateway service install not supported on ${process.platform}`); } async function rejectUnsupportedGatewayService() { throw createUnsupportedGatewayServiceError(); } function createUnsupportedGatewayService() { return { label: "Gateway service", loadedText: "available", notLoadedText: "not installed", stage: rejectUnsupportedGatewayService, install: rejectUnsupportedGatewayService, uninstall: rejectUnsupportedGatewayService, start: rejectUnsupportedGatewayService, stop: rejectUnsupportedGatewayService, restart: rejectUnsupportedGatewayService, isLoaded: rejectUnsupportedGatewayService, readCommand: async () => null, readRuntime: async () => ({ status: "unknown", detail: createUnsupportedGatewayServiceError().message }) }; } const GATEWAY_SERVICE_REGISTRY = { darwin: { label: "LaunchAgent", loadedText: "loaded", notLoadedText: "not loaded", stage: ignoreServiceWriteResult(stageLaunchAgent), install: ignoreServiceWriteResult(installLaunchAgent), uninstall: uninstallLaunchAgent, start: startLaunchAgent, stop: stopLaunchAgent, restart: restartLaunchAgent, isLoaded: isLaunchAgentLoaded, isEnabled: isLaunchAgentEnabled, readCommand: readLaunchAgentProgramArguments, readRuntime: readLaunchAgentRuntime }, linux: { label: "systemd user", loadedText: "enabled", notLoadedText: "disabled", stage: ignoreServiceWriteResult(stageSystemdService), install: ignoreServiceWriteResult(installSystemdService), uninstall: uninstallSystemdService, start: startSystemdService, stop: stopSystemdService, restart: restartSystemdService, isLoaded: isSystemdServiceEnabled, isAbsent: ({ env }) => isSystemdServiceAbsent(env ?? process.env), hasInstalledDefinition: async ({ env }) => await findInstalledSystemdGatewayScope(env ?? process.env) !== null, readDefinitionMutationCapability: ({ env, environment, timeoutMs }) => readSystemdDefinitionMutationCapability(env ?? process.env, { environment, timeoutMs }), readCommand: readSystemdServiceExecStart, readRuntime: readSystemdServiceRuntime }, win32: { label: "Scheduled Task", loadedText: "registered", notLoadedText: "missing", stage: ignoreServiceWriteResult(stageScheduledTask), install: ignoreServiceWriteResult(installScheduledTask), uninstall: uninstallScheduledTask, start: startScheduledTask, stop: stopScheduledTask, restart: restartScheduledTask, isLoaded: isScheduledTaskInstalled, readCommand: readScheduledTaskCommand, readRuntime: readScheduledTaskRuntime } }; function guardGatewayServiceMutation(action, mutate) { return async (args) => { assertGatewayServiceMutationAllowed(action, process.env); if (args.env && args.env !== process.env) assertGatewayServiceMutationAllowed(action, args.env); await assertFutureConfigActionAllowed(action); return await mutate(args); }; } function withGatewayServiceMutationGuards(service) { return { ...service, stage: guardGatewayServiceMutation("rewrite the gateway service", service.stage), install: guardGatewayServiceMutation("install or rewrite the gateway service", service.install), uninstall: guardGatewayServiceMutation("uninstall the gateway service", service.uninstall), start: guardGatewayServiceMutation("start the gateway service", service.start), stop: guardGatewayServiceMutation("stop the gateway service", service.stop), restart: guardGatewayServiceMutation("restart the gateway service", service.restart) }; } function isSupportedGatewayServicePlatform(platform) { return Object.hasOwn(GATEWAY_SERVICE_REGISTRY, platform); } function resolveGatewayService() { if (isSupportedGatewayServicePlatform(process.platform)) return withGatewayServiceMutationGuards(GATEWAY_SERVICE_REGISTRY[process.platform]); return createUnsupportedGatewayService(); } //#endregion export { readGatewayServiceState as a, readGatewayServiceLoadState as i, formatGatewayServiceStartRepairIssues as n, resolveGatewayService as o, inspectGatewayServiceStartRepair as r, startGatewayService as s, describeGatewayServiceRestart as t };