openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
231 lines (230 loc) • 8.43 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js";
import { c as readSystemdServiceRuntime, d as stageSystemdService, f as stopSystemdService, i as isSystemdServiceEnabled, m as uninstallSystemdService, n as installSystemdService, s as readSystemdServiceExecStart, u as restartSystemdService } from "./systemd-BXTM6gEb.js";
import { n as VERSION } from "./version-Crcn9X9T.js";
import { u as readConfigFileSnapshot } from "./io-Gi7-pyU-.js";
import "./config-C9RxTsn1.js";
import { n as formatFutureConfigActionBlock, r as resolveFutureConfigActionBlock } from "./future-version-guard-D6QWZtAq.js";
import { _ as restartLaunchAgent, a as installLaunchAgent, b as uninstallLaunchAgent, f as readLaunchAgentProgramArguments, p as readLaunchAgentRuntime, s as isLaunchAgentLoaded, v as stageLaunchAgent, y as stopLaunchAgent } from "./launchd-FSKBDZ2p.js";
import { c as stopScheduledTask, i as readScheduledTaskRuntime, l as uninstallScheduledTask, n as isScheduledTaskInstalled, o as restartScheduledTask, r as readScheduledTaskCommand, s as stageScheduledTask, t as installScheduledTask } from "./schtasks-D-SLPI7l.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) {
try {
return resolveFutureConfigActionBlock({
action,
snapshot: await readConfigFileSnapshot()
});
} 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);
};
}
function mergeGatewayServiceEnv(baseEnv, command) {
if (!command?.environment) return baseEnv;
const merged = {
...baseEnv,
...command.environment
};
for (const key of [
"OPENCLAW_LAUNCHD_LABEL",
"OPENCLAW_SYSTEMD_UNIT",
"OPENCLAW_WINDOWS_TASK_NAME"
]) {
const value = baseEnv[key]?.trim();
if (value) merged[key] = value;
}
return merged;
}
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) {
const command = state.command;
if (!state.loaded || !command) return [];
const issues = [];
const serviceVersion = command.environment?.OPENCLAW_SERVICE_VERSION?.trim();
if (serviceVersion && serviceVersion !== VERSION) issues.push({
code: "version-mismatch",
message: `service was installed by OpenClaw ${serviceVersion}, current CLI is ${VERSION}`
});
for (const candidate of command.programArguments.slice(0, 2)) {
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;
}
function formatGatewayServiceStartRepairIssues(issues) {
return issues.map((issue) => issue.message).join("; ");
}
async function readGatewayServiceState(service, args = {}) {
const baseEnv = args.env ?? process.env;
const command = await service.readCommand(baseEnv).catch(() => null);
const env = mergeGatewayServiceEnv(baseEnv, command);
const [loaded, runtime] = await Promise.all([service.isLoaded({ env }).catch(() => false), service.readRuntime(env).catch(() => void 0)]);
return {
installed: command !== null,
loaded,
running: runtime?.status === "running",
env,
command,
runtime
};
}
async function startGatewayService(service, args) {
const state = await readGatewayServiceState(service, { env: args.env });
if (!state.loaded && !state.installed) return {
outcome: "missing-install",
state
};
const repairIssues = collectGatewayServiceStartRepairIssues(state);
if (repairIssues.length > 0) return {
outcome: "repair-required",
state,
issues: repairIssues
};
try {
const restartResult = await service.restart({
...args,
env: state.env
});
const nextState = await readGatewayServiceState(service, { env: state.env });
return {
outcome: restartResult.outcome === "scheduled" ? "scheduled" : "started",
state: nextState
};
} catch (err) {
const nextState = await readGatewayServiceState(service, { env: state.env });
if (!nextState.installed) return {
outcome: "missing-install",
state: nextState
};
throw err;
}
}
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.`
};
}
const GATEWAY_SERVICE_REGISTRY = {
darwin: {
label: "LaunchAgent",
loadedText: "loaded",
notLoadedText: "not loaded",
stage: ignoreServiceWriteResult(stageLaunchAgent),
install: ignoreServiceWriteResult(installLaunchAgent),
uninstall: uninstallLaunchAgent,
stop: stopLaunchAgent,
restart: restartLaunchAgent,
isLoaded: isLaunchAgentLoaded,
readCommand: readLaunchAgentProgramArguments,
readRuntime: readLaunchAgentRuntime
},
linux: {
label: "systemd user",
loadedText: "enabled",
notLoadedText: "disabled",
stage: ignoreServiceWriteResult(stageSystemdService),
install: ignoreServiceWriteResult(installSystemdService),
uninstall: uninstallSystemdService,
stop: stopSystemdService,
restart: restartSystemdService,
isLoaded: isSystemdServiceEnabled,
readCommand: readSystemdServiceExecStart,
readRuntime: readSystemdServiceRuntime
},
win32: {
label: "Scheduled Task",
loadedText: "registered",
notLoadedText: "missing",
stage: ignoreServiceWriteResult(stageScheduledTask),
install: ignoreServiceWriteResult(installScheduledTask),
uninstall: uninstallScheduledTask,
stop: stopScheduledTask,
restart: restartScheduledTask,
isLoaded: isScheduledTaskInstalled,
readCommand: readScheduledTaskCommand,
readRuntime: readScheduledTaskRuntime
}
};
function withFutureConfigGuard(service) {
return {
...service,
stage: async (args) => {
await assertFutureConfigActionAllowed("rewrite the gateway service");
return await service.stage(args);
},
install: async (args) => {
await assertFutureConfigActionAllowed("install or rewrite the gateway service");
return await service.install(args);
},
uninstall: async (args) => {
await assertFutureConfigActionAllowed("uninstall the gateway service");
return await service.uninstall(args);
},
stop: async (args) => {
await assertFutureConfigActionAllowed("stop the gateway service");
return await service.stop(args);
},
restart: async (args) => {
await assertFutureConfigActionAllowed("restart the gateway service");
return await service.restart(args);
}
};
}
function isSupportedGatewayServicePlatform(platform) {
return Object.hasOwn(GATEWAY_SERVICE_REGISTRY, platform);
}
function resolveGatewayService() {
if (isSupportedGatewayServicePlatform(process.platform)) return withFutureConfigGuard(GATEWAY_SERVICE_REGISTRY[process.platform]);
throw new Error(`Gateway service install not supported on ${process.platform}`);
}
//#endregion
export { startGatewayService as a, resolveGatewayService as i, formatGatewayServiceStartRepairIssues as n, readGatewayServiceState as r, describeGatewayServiceRestart as t };