/** Cross-platform daemon service names, labels, and profile-aware descriptions. */ import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; // Default service labels (canonical + legacy compatibility) export const GATEWAY_LAUNCH_AGENT_LABEL = "ai.openclaw.gateway"; const GATEWAY_SYSTEMD_SERVICE_NAME = "openclaw-gateway"; const GATEWAY_WINDOWS_TASK_NAME = "OpenClaw Gateway"; export const GATEWAY_SERVICE_MARKER = "openclaw"; export const GATEWAY_SERVICE_KIND = "gateway"; export const GATEWAY_SERVICE_RUNTIME_PID_ENV = "OPENCLAW_GATEWAY_SERVICE_PID"; export const GATEWAY_SERVICE_SELECTOR_ENV_KEYS = [ "OPENCLAW_STATE_DIR", "OPENCLAW_CONFIG_PATH", "OPENCLAW_PROFILE", "OPENCLAW_GATEWAY_PORT", "OPENCLAW_LAUNCHD_LABEL", "OPENCLAW_SYSTEMD_UNIT", "OPENCLAW_WINDOWS_TASK_NAME", ] as const; export function isGatewayServiceEnv(env: Record): boolean { if (env.OPENCLAW_SERVICE_MARKER?.trim() !== GATEWAY_SERVICE_MARKER) { return false; } const serviceKind = env.OPENCLAW_SERVICE_KIND?.trim(); return !serviceKind || serviceKind === GATEWAY_SERVICE_KIND; } const NODE_LAUNCH_AGENT_LABEL = "ai.openclaw.node"; const NODE_SYSTEMD_SERVICE_NAME = "openclaw-node"; const NODE_WINDOWS_TASK_NAME = "OpenClaw Node"; const NODE_SERVICE_MARKER = "openclaw"; export const NODE_SERVICE_KIND = "node"; const NODE_WINDOWS_TASK_SCRIPT_NAME = "node.cmd"; export const LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES: string[] = ["clawdbot-gateway"]; function normalizeGatewayProfile(profile?: string): string | null { const trimmed = profile?.trim(); if (!trimmed || normalizeLowercaseStringOrEmpty(trimmed) === "default") { // The default profile keeps the historical unqualified service names. return null; } return trimmed; } export function resolveGatewayProfileSuffix(profile?: string): string { const normalized = normalizeGatewayProfile(profile); return normalized ? `-${normalized}` : ""; } export function resolveGatewayLaunchAgentLabel(profile?: string): string { const normalized = normalizeGatewayProfile(profile); if (!normalized) { return GATEWAY_LAUNCH_AGENT_LABEL; } return `ai.openclaw.${normalized}`; } export function resolveGatewaySystemdServiceName(profile?: string): string { const suffix = resolveGatewayProfileSuffix(profile); if (!suffix) { return GATEWAY_SYSTEMD_SERVICE_NAME; } return `openclaw-gateway${suffix}`; } export function resolveGatewayWindowsTaskName(profile?: string): string { const normalized = normalizeGatewayProfile(profile); if (!normalized) { return GATEWAY_WINDOWS_TASK_NAME; } return `OpenClaw Gateway (${normalized})`; } type GatewayNativeServiceIdentityConflict = { envKey: "OPENCLAW_LAUNCHD_LABEL" | "OPENCLAW_SYSTEMD_UNIT" | "OPENCLAW_WINDOWS_TASK_NAME"; expected: string; }; export function resolveGatewayNativeServiceIdentityConflict( env: Record, platform: NodeJS.Platform = process.platform, ): GatewayNativeServiceIdentityConflict | null { const profile = normalizeGatewayProfile(env.OPENCLAW_PROFILE); if (!profile) { return null; } if (platform === "darwin") { const envKey = "OPENCLAW_LAUNCHD_LABEL"; const actual = env[envKey]?.trim(); const expected = resolveGatewayLaunchAgentLabel(profile); return actual && actual !== expected ? { envKey, expected } : null; } if (platform === "linux") { const envKey = "OPENCLAW_SYSTEMD_UNIT"; const actual = env[envKey]?.trim(); const normalizedActual = actual?.endsWith(".service") ? actual : actual && `${actual}.service`; const expected = `${resolveGatewaySystemdServiceName(profile)}.service`; return normalizedActual && normalizedActual !== expected ? { envKey, expected } : null; } if (platform === "win32") { const envKey = "OPENCLAW_WINDOWS_TASK_NAME"; const actual = env[envKey]?.trim(); const expected = resolveGatewayWindowsTaskName(profile); return actual && actual !== expected ? { envKey, expected } : null; } return null; } function formatGatewayServiceDescription(profile?: string): string { const normalized = normalizeGatewayProfile(profile); if (!normalized) { return "OpenClaw Gateway"; } return `OpenClaw Gateway (profile: ${normalized})`; } export function resolveGatewayServiceDescription(params: { env: Record; description?: string; }): string { return params.description ?? formatGatewayServiceDescription(params.env.OPENCLAW_PROFILE); } export function resolveNodeLaunchAgentLabel(): string { return NODE_LAUNCH_AGENT_LABEL; } export function resolveNodeSystemdServiceName(): string { return NODE_SYSTEMD_SERVICE_NAME; } export function resolveNodeWindowsTaskName(): string { return NODE_WINDOWS_TASK_NAME; } export function resolveNodeServiceIdentityEnvironment(): Record { return { OPENCLAW_LAUNCHD_LABEL: resolveNodeLaunchAgentLabel(), OPENCLAW_SYSTEMD_UNIT: resolveNodeSystemdServiceName(), OPENCLAW_WINDOWS_TASK_NAME: resolveNodeWindowsTaskName(), OPENCLAW_WINDOWS_TASK_HIDDEN_LAUNCHER: "1", OPENCLAW_TASK_SCRIPT_NAME: NODE_WINDOWS_TASK_SCRIPT_NAME, OPENCLAW_LOG_PREFIX: "node", OPENCLAW_SERVICE_MARKER: NODE_SERVICE_MARKER, OPENCLAW_SERVICE_KIND: NODE_SERVICE_KIND, }; }