diff --git a/src/daemon/managed-gateway-bindings.ts b/src/daemon/managed-gateway-bindings.ts index 9126b2dd99cf..ef2a997058d6 100644 --- a/src/daemon/managed-gateway-bindings.ts +++ b/src/daemon/managed-gateway-bindings.ts @@ -1,8 +1,8 @@ /** Bind discovered native service selectors without granting lifecycle authority. */ import path from "node:path"; -import { normalizeLowercaseStringOrEmpty } from "@openclaw/normalization-core/string-coerce"; import { hasCommandProcessCleanupError } from "../process/exec-result.js"; -import { listManagedOpenClawGatewayServices, type ExtraGatewayService } from "./inspect.js"; +import { resolveGatewayProfileSuffix } from "./constants.js"; +import { listManagedOpenClawGatewayServices } from "./inspect.js"; import type { LoadedLaunchAgentState } from "./launchd-runtime.js"; import { resolveTaskName } from "./schtasks-layout.js"; import type { GatewayServiceEnv, SystemdServiceReadTarget } from "./service-types.js"; @@ -51,7 +51,7 @@ export async function readManagedGatewayBindingState( function bindingSelectorKey(binding: ManagedGatewayBinding): string { return [ - normalizeDiscoveredProfile(binding.env.OPENCLAW_PROFILE), + resolveGatewayProfileSuffix(binding.env.OPENCLAW_PROFILE), binding.scope ?? binding.systemdReadTarget?.scope ?? "", binding.systemdReadTarget?.unitPath ?? "", binding.launchAgentPlistPath ?? "", @@ -64,14 +64,6 @@ function bindingSelectorKey(binding: ManagedGatewayBinding): string { ].join("\0"); } -function normalizeDiscoveredProfile(value: string | undefined): string { - const trimmed = value?.trim(); - if (!trimmed || normalizeLowercaseStringOrEmpty(trimmed) === "default") { - return "default"; - } - return trimmed; -} - function hostBindingEnv( env: Record, extras: GatewayServiceEnv, @@ -98,48 +90,6 @@ function profileEnvFields(profile: string): GatewayServiceEnv { return profile === "default" ? {} : { OPENCLAW_PROFILE: profile }; } -function bindingFromSystemdService( - svc: ExtraGatewayService, - env: Record, -): ManagedGatewayBinding { - const unitPath = svc.sourcePath; - const unitName = resolveSystemdTemplateInstanceName(svc.label, { - OPENCLAW_SYSTEMD_UNIT: svc.label, - }); - const systemdReadTarget = unitPath ? { scope: svc.scope, unitName, unitPath } : undefined; - return { - scope: svc.scope, - ...(systemdReadTarget ? { systemdReadTarget } : {}), - env: hostBindingEnv(env, { OPENCLAW_SYSTEMD_UNIT: unitName }), - }; -} - -function bindingFromLaunchdService( - svc: ExtraGatewayService, - env: Record, -): ManagedGatewayBinding { - const plistPath = svc.sourcePath; - return { - scope: svc.scope, - ...(plistPath ? { launchAgentPlistPath: plistPath } : {}), - env: hostBindingEnv(env, { OPENCLAW_LAUNCHD_LABEL: svc.label }), - }; -} - -function bindingFromWindowsTask( - name: string, - profile: string, - env: Record, -): ManagedGatewayBinding { - return { - scope: "system", - env: hostBindingEnv(env, { - ...profileEnvFields(profile), - OPENCLAW_WINDOWS_TASK_NAME: name.replace(/^\\+/, "").trim() || name, - }), - }; -} - /** * Enumerate installed managed Gateway selectors for runtime mutation checks. */ @@ -171,11 +121,24 @@ export async function discoverManagedGatewayBindings( // Best-effort callers retain known bindings; automatic writers require complete discovery. for (const svc of services) { if (svc.platform === "linux") { - push(bindingFromSystemdService(svc, env)); + const unitName = resolveSystemdTemplateInstanceName(svc.label, { + OPENCLAW_SYSTEMD_UNIT: svc.label, + }); + push({ + scope: svc.scope, + ...(svc.sourcePath + ? { systemdReadTarget: { scope: svc.scope, unitName, unitPath: svc.sourcePath } } + : {}), + env: hostBindingEnv(env, { OPENCLAW_SYSTEMD_UNIT: unitName }), + }); continue; } if (svc.platform === "darwin") { - push(bindingFromLaunchdService(svc, env)); + push({ + scope: svc.scope, + ...(svc.sourcePath ? { launchAgentPlistPath: svc.sourcePath } : {}), + env: hostBindingEnv(env, { OPENCLAW_LAUNCHD_LABEL: svc.label }), + }); continue; } if (svc.windowsProfile === undefined) { @@ -189,7 +152,13 @@ export async function discoverManagedGatewayBindings( }); continue; } - push(bindingFromWindowsTask(svc.label, svc.windowsProfile, env)); + push({ + scope: "system", + env: hostBindingEnv(env, { + ...profileEnvFields(svc.windowsProfile), + OPENCLAW_WINDOWS_TASK_NAME: svc.label.replace(/^\\+/, "").trim() || svc.label, + }), + }); } } catch (error) { if (options.requireComplete || hasCommandProcessCleanupError(error)) { diff --git a/test/scripts/live-gateway-dist-fence.test.ts b/test/scripts/live-gateway-dist-fence.test.ts index 46c895570629..1f6be4a2d669 100644 --- a/test/scripts/live-gateway-dist-fence.test.ts +++ b/test/scripts/live-gateway-dist-fence.test.ts @@ -232,7 +232,6 @@ describe("live-gateway-dist-fence", () => { runtime: { status: "stopped", state: "inactive", pid: process.pid }, }), }); - expect(kill).toHaveBeenCalledTimes(1); expect(kill).toHaveBeenCalledWith(process.pid, 0); expect(result.refuse).toBe(refuse); });