mirror of https://github.com/openclaw/openclaw.git
225 lines
6.5 KiB
TypeScript
225 lines
6.5 KiB
TypeScript
import {
|
|
installLaunchAgent,
|
|
isLaunchAgentLoaded,
|
|
readLaunchAgentProgramArguments,
|
|
readLaunchAgentRuntime,
|
|
restartLaunchAgent,
|
|
stageLaunchAgent,
|
|
stopLaunchAgent,
|
|
uninstallLaunchAgent,
|
|
} from "./launchd.js";
|
|
import {
|
|
installScheduledTask,
|
|
isScheduledTaskInstalled,
|
|
readScheduledTaskCommand,
|
|
readScheduledTaskRuntime,
|
|
restartScheduledTask,
|
|
stageScheduledTask,
|
|
stopScheduledTask,
|
|
uninstallScheduledTask,
|
|
} from "./schtasks.js";
|
|
import type { GatewayServiceRuntime } from "./service-runtime.js";
|
|
import type {
|
|
GatewayServiceCommandConfig,
|
|
GatewayServiceControlArgs,
|
|
GatewayServiceEnv,
|
|
GatewayServiceEnvArgs,
|
|
GatewayServiceInstallArgs,
|
|
GatewayServiceManageArgs,
|
|
GatewayServiceRestartResult,
|
|
GatewayServiceStartResult,
|
|
GatewayServiceStageArgs,
|
|
GatewayServiceState,
|
|
} from "./service-types.js";
|
|
import {
|
|
installSystemdService,
|
|
isSystemdServiceEnabled,
|
|
readSystemdServiceExecStart,
|
|
readSystemdServiceRuntime,
|
|
restartSystemdService,
|
|
stageSystemdService,
|
|
stopSystemdService,
|
|
uninstallSystemdService,
|
|
} from "./systemd.js";
|
|
export type {
|
|
GatewayServiceCommandConfig,
|
|
GatewayServiceControlArgs,
|
|
GatewayServiceEnv,
|
|
GatewayServiceEnvArgs,
|
|
GatewayServiceInstallArgs,
|
|
GatewayServiceManageArgs,
|
|
GatewayServiceRestartResult,
|
|
GatewayServiceStartResult,
|
|
GatewayServiceStageArgs,
|
|
GatewayServiceState,
|
|
} from "./service-types.js";
|
|
|
|
function ignoreServiceWriteResult<TArgs extends GatewayServiceInstallArgs>(
|
|
write: (args: TArgs) => Promise<unknown>,
|
|
): (args: TArgs) => Promise<void> {
|
|
return async (args: TArgs) => {
|
|
await write(args);
|
|
};
|
|
}
|
|
|
|
export type GatewayService = {
|
|
label: string;
|
|
loadedText: string;
|
|
notLoadedText: string;
|
|
stage: (args: GatewayServiceStageArgs) => Promise<void>;
|
|
install: (args: GatewayServiceInstallArgs) => Promise<void>;
|
|
uninstall: (args: GatewayServiceManageArgs) => Promise<void>;
|
|
stop: (args: GatewayServiceControlArgs) => Promise<void>;
|
|
restart: (args: GatewayServiceControlArgs) => Promise<GatewayServiceRestartResult>;
|
|
isLoaded: (args: GatewayServiceEnvArgs) => Promise<boolean>;
|
|
readCommand: (env: GatewayServiceEnv) => Promise<GatewayServiceCommandConfig | null>;
|
|
readRuntime: (env: GatewayServiceEnv) => Promise<GatewayServiceRuntime>;
|
|
};
|
|
|
|
function mergeGatewayServiceEnv(
|
|
baseEnv: GatewayServiceEnv,
|
|
command: GatewayServiceCommandConfig | null,
|
|
): GatewayServiceEnv {
|
|
if (!command?.environment) {
|
|
return baseEnv;
|
|
}
|
|
return {
|
|
...baseEnv,
|
|
...command.environment,
|
|
};
|
|
}
|
|
|
|
export async function readGatewayServiceState(
|
|
service: GatewayService,
|
|
args: GatewayServiceEnvArgs = {},
|
|
): Promise<GatewayServiceState> {
|
|
const baseEnv = args.env ?? (process.env as GatewayServiceEnv);
|
|
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(() => undefined),
|
|
]);
|
|
return {
|
|
installed: command !== null,
|
|
loaded,
|
|
running: runtime?.status === "running",
|
|
env,
|
|
command,
|
|
runtime,
|
|
};
|
|
}
|
|
|
|
export async function startGatewayService(
|
|
service: GatewayService,
|
|
args: GatewayServiceControlArgs,
|
|
): Promise<GatewayServiceStartResult> {
|
|
const state = await readGatewayServiceState(service, { env: args.env });
|
|
if (!state.loaded && !state.installed) {
|
|
return {
|
|
outcome: "missing-install",
|
|
state,
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
export function describeGatewayServiceRestart(
|
|
serviceNoun: string,
|
|
result: GatewayServiceRestartResult,
|
|
): {
|
|
scheduled: boolean;
|
|
daemonActionResult: "restarted" | "scheduled";
|
|
message: string;
|
|
progressMessage: string;
|
|
} {
|
|
if (result.outcome === "scheduled") {
|
|
return {
|
|
scheduled: true,
|
|
daemonActionResult: "scheduled",
|
|
message: `restart scheduled, ${serviceNoun.toLowerCase()} will restart momentarily`,
|
|
progressMessage: `${serviceNoun} service restart scheduled.`,
|
|
};
|
|
}
|
|
return {
|
|
scheduled: false,
|
|
daemonActionResult: "restarted",
|
|
message: `${serviceNoun} service restarted.`,
|
|
progressMessage: `${serviceNoun} service restarted.`,
|
|
};
|
|
}
|
|
|
|
type SupportedGatewayServicePlatform = "darwin" | "linux" | "win32";
|
|
|
|
const GATEWAY_SERVICE_REGISTRY: Record<SupportedGatewayServicePlatform, GatewayService> = {
|
|
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",
|
|
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 isSupportedGatewayServicePlatform(
|
|
platform: NodeJS.Platform,
|
|
): platform is SupportedGatewayServicePlatform {
|
|
return Object.hasOwn(GATEWAY_SERVICE_REGISTRY, platform);
|
|
}
|
|
|
|
export function resolveGatewayService(): GatewayService {
|
|
if (isSupportedGatewayServicePlatform(process.platform)) {
|
|
return GATEWAY_SERVICE_REGISTRY[process.platform];
|
|
}
|
|
throw new Error(`Gateway service install not supported on ${process.platform}`);
|
|
}
|