mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
refactor(daemon): decouple service paths from display text (#162597)
This commit is contained in:
parent
9505aedf41
commit
a24f8141da
15 changed files with 229 additions and 176 deletions
|
|
@ -159,6 +159,13 @@ describe("printDaemonStatus", () => {
|
||||||
|
|
||||||
it("preserves Gateway metadata and input while redacting private definitions in JSON", () => {
|
it("preserves Gateway metadata and input while redacting private definitions in JSON", () => {
|
||||||
const server = { version: "2026.5.6", buildId: "build-2026.5.6", connId: "conn-1" };
|
const server = { version: "2026.5.6", buildId: "build-2026.5.6", connId: "conn-1" };
|
||||||
|
const extraService: ExtraGatewayService = {
|
||||||
|
platform: "linux",
|
||||||
|
label: "sibling.service",
|
||||||
|
detail: "unit: /etc/systemd/system/sibling.service",
|
||||||
|
sourcePath: "/etc/systemd/system/sibling.service",
|
||||||
|
scope: "system",
|
||||||
|
};
|
||||||
const command: GatewayServiceCommandConfig = {
|
const command: GatewayServiceCommandConfig = {
|
||||||
programArguments: ["node"],
|
programArguments: ["node"],
|
||||||
environment: {
|
environment: {
|
||||||
|
|
@ -175,12 +182,21 @@ describe("printDaemonStatus", () => {
|
||||||
};
|
};
|
||||||
const original = structuredClone(command);
|
const original = structuredClone(command);
|
||||||
printDaemonStatus(
|
printDaemonStatus(
|
||||||
{ service: { command }, rpc: { ok: true, server } },
|
{ service: { command }, rpc: { ok: true, server }, extraServices: [extraService] },
|
||||||
{ json: true, deep: true },
|
{ json: true, deep: true },
|
||||||
);
|
);
|
||||||
expect(runtime.writeJson).toHaveBeenCalledOnce();
|
expect(runtime.writeJson).toHaveBeenCalledOnce();
|
||||||
const payload = runtime.writeJson.mock.calls[0]?.[0];
|
const payload = runtime.writeJson.mock.calls[0]?.[0];
|
||||||
expect(payload).toHaveProperty("rpc.server", server);
|
expect(payload).toHaveProperty("rpc.server", server);
|
||||||
|
expect(payload).toHaveProperty("extraServices", [
|
||||||
|
{
|
||||||
|
platform: "linux",
|
||||||
|
label: "sibling.service",
|
||||||
|
detail: "unit: /etc/systemd/system/sibling.service",
|
||||||
|
scope: "system",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(extraService.sourcePath).toBe("/etc/systemd/system/sibling.service");
|
||||||
expect(payload).not.toHaveProperty("service.command.managedDefinition");
|
expect(payload).not.toHaveProperty("service.command.managedDefinition");
|
||||||
expect(payload).not.toHaveProperty("service.command.managedOverrides");
|
expect(payload).not.toHaveProperty("service.command.managedOverrides");
|
||||||
expect(payload).not.toHaveProperty("service.command.definitionPaths");
|
expect(payload).not.toHaveProperty("service.command.definitionPaths");
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean; d
|
||||||
if (opts.json) {
|
if (opts.json) {
|
||||||
defaultRuntime.writeJson({
|
defaultRuntime.writeJson({
|
||||||
...status,
|
...status,
|
||||||
|
extraServices: status.extraServices.map(({ sourcePath: _sourcePath, ...service }) => service),
|
||||||
service: projectDaemonServiceForJson(status.service, { includeDefinitionPaths: false }),
|
service: projectDaemonServiceForJson(status.service, { includeDefinitionPaths: false }),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ function setupLegacyMacService() {
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label: LEGACY_MAC_LABEL,
|
label: LEGACY_MAC_LABEL,
|
||||||
detail: `plist: ${LEGACY_MAC_PLIST}`,
|
detail: `plist: ${LEGACY_MAC_PLIST}`,
|
||||||
|
sourcePath: LEGACY_MAC_PLIST,
|
||||||
scope: "user",
|
scope: "user",
|
||||||
legacy: true,
|
legacy: true,
|
||||||
},
|
},
|
||||||
|
|
@ -108,6 +109,7 @@ describe("maybeScanExtraGatewayServices", () => {
|
||||||
platform: "linux" as const,
|
platform: "linux" as const,
|
||||||
label: "custom-gateway.service",
|
label: "custom-gateway.service",
|
||||||
detail: `unit: ${unitPath}`,
|
detail: `unit: ${unitPath}`,
|
||||||
|
sourcePath: unitPath,
|
||||||
scope,
|
scope,
|
||||||
legacy: false,
|
legacy: false,
|
||||||
};
|
};
|
||||||
|
|
@ -183,6 +185,7 @@ describe("maybeScanExtraGatewayServices", () => {
|
||||||
platform: "darwin" as const,
|
platform: "darwin" as const,
|
||||||
label: "com.example.openclaw-gateway",
|
label: "com.example.openclaw-gateway",
|
||||||
detail: "plist: /Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
detail: "plist: /Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
|
sourcePath: "/Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
scope: "user" as const,
|
scope: "user" as const,
|
||||||
legacy: false,
|
legacy: false,
|
||||||
};
|
};
|
||||||
|
|
@ -259,6 +262,7 @@ describe("maybeScanExtraGatewayServices", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "clawdbot-gateway.service",
|
label: "clawdbot-gateway.service",
|
||||||
detail: "unit: /home/test/.config/systemd/user/clawdbot-gateway.service",
|
detail: "unit: /home/test/.config/systemd/user/clawdbot-gateway.service",
|
||||||
|
sourcePath: "/home/test/.config/systemd/user/clawdbot-gateway.service",
|
||||||
scope: "user",
|
scope: "user",
|
||||||
legacy: true,
|
legacy: true,
|
||||||
},
|
},
|
||||||
|
|
@ -299,6 +303,7 @@ describe("maybeScanExtraGatewayServices", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label,
|
label,
|
||||||
detail: `unit: /home/test/.config/systemd/user/${label}`,
|
detail: `unit: /home/test/.config/systemd/user/${label}`,
|
||||||
|
sourcePath: `/home/test/.config/systemd/user/${label}`,
|
||||||
scope: "user",
|
scope: "user",
|
||||||
marker: "clawdbot",
|
marker: "clawdbot",
|
||||||
legacy: true,
|
legacy: true,
|
||||||
|
|
@ -514,6 +519,7 @@ describe("maybeScanExtraGatewayServices", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "clawdbot-gateway.service",
|
label: "clawdbot-gateway.service",
|
||||||
detail: "unit: /home/test/.config/systemd/user/clawdbot-gateway.service",
|
detail: "unit: /home/test/.config/systemd/user/clawdbot-gateway.service",
|
||||||
|
sourcePath: "/home/test/.config/systemd/user/clawdbot-gateway.service",
|
||||||
scope: "user",
|
scope: "user",
|
||||||
legacy: true,
|
legacy: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -116,14 +116,6 @@ async function confirmLegacyLaunchdServiceUnloaded(serviceTarget: string): Promi
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractDetailPath(detail: string, prefix: string): string | null {
|
|
||||||
if (!detail.startsWith(prefix)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const value = detail.slice(prefix.length).trim();
|
|
||||||
return value.length > 0 ? value : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function filterInactiveExtraGatewayServices(
|
async function filterInactiveExtraGatewayServices(
|
||||||
services: ExtraGatewayService[],
|
services: ExtraGatewayService[],
|
||||||
): Promise<ExtraGatewayService[]> {
|
): Promise<ExtraGatewayService[]> {
|
||||||
|
|
@ -205,7 +197,7 @@ async function cleanupLegacyDarwinServices(
|
||||||
const failed: string[] = [];
|
const failed: string[] = [];
|
||||||
|
|
||||||
for (const svc of services) {
|
for (const svc of services) {
|
||||||
const plistPath = extractDetailPath(svc.detail, "plist:");
|
const plistPath = svc.sourcePath;
|
||||||
if (!plistPath) {
|
if (!plistPath) {
|
||||||
failed.push(`${svc.label} (missing plist path)`);
|
failed.push(`${svc.label} (missing plist path)`);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export type ExtraGatewayService = {
|
||||||
platform: "darwin" | "linux" | "win32";
|
platform: "darwin" | "linux" | "win32";
|
||||||
label: string;
|
label: string;
|
||||||
detail: string;
|
detail: string;
|
||||||
|
sourcePath?: string;
|
||||||
scope: "user" | "system";
|
scope: "user" | "system";
|
||||||
marker?: "openclaw" | "clawdbot";
|
marker?: "openclaw" | "clawdbot";
|
||||||
legacy?: boolean;
|
legacy?: boolean;
|
||||||
|
|
@ -116,6 +117,7 @@ export async function scanSystemdDir(params: {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: entry,
|
label: entry,
|
||||||
detail: `unit: ${fullPath}`,
|
detail: `unit: ${fullPath}`,
|
||||||
|
sourcePath: fullPath,
|
||||||
scope: params.scope,
|
scope: params.scope,
|
||||||
marker,
|
marker,
|
||||||
legacy: marker !== "openclaw",
|
legacy: marker !== "openclaw",
|
||||||
|
|
|
||||||
138
src/daemon/inspect.cleanup-hints.test.ts
Normal file
138
src/daemon/inspect.cleanup-hints.test.ts
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { renderGatewayServiceCleanupHints } from "./inspect.js";
|
||||||
|
|
||||||
|
describe("renderGatewayServiceCleanupHints", () => {
|
||||||
|
it("does not suggest removing a gateway when no extra service was detected", () => {
|
||||||
|
expect(renderGatewayServiceCleanupHints([])).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
title: "targets the detected macOS LaunchAgent instead of the active gateway",
|
||||||
|
platform: "darwin",
|
||||||
|
serviceName: "com.example.openclaw-gateway",
|
||||||
|
source: "plist: /Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
|
sourcePath: "/Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
|
scope: "user",
|
||||||
|
firstHint: "launchctl bootout gui/$UID/com.example.openclaw-gateway",
|
||||||
|
secondHint: "rm /Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "uses the system domain for a detected macOS LaunchDaemon",
|
||||||
|
platform: "darwin",
|
||||||
|
serviceName: "com.example.openclaw-gateway",
|
||||||
|
source: "plist: /Library/LaunchDaemons/com.example.openclaw-gateway.plist",
|
||||||
|
sourcePath: "/Library/LaunchDaemons/com.example.openclaw-gateway.plist",
|
||||||
|
scope: "system",
|
||||||
|
firstHint: "sudo launchctl bootout system/com.example.openclaw-gateway",
|
||||||
|
secondHint: "sudo rm /Library/LaunchDaemons/com.example.openclaw-gateway.plist",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "keeps global macOS LaunchAgents in the GUI domain",
|
||||||
|
platform: "darwin",
|
||||||
|
serviceName: "com.example.openclaw-gateway",
|
||||||
|
source: "plist: /Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
|
sourcePath: "/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
|
scope: "system",
|
||||||
|
firstHint: "launchctl bootout gui/$UID/com.example.openclaw-gateway",
|
||||||
|
secondHint: "sudo rm /Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "inspects the detected user-level systemd unit without removing it",
|
||||||
|
platform: "linux",
|
||||||
|
serviceName: "custom-gateway.service",
|
||||||
|
source: "unit: /home/test/.config/systemd/user/custom-gateway.service",
|
||||||
|
sourcePath: "/home/test/.config/systemd/user/custom-gateway.service",
|
||||||
|
scope: "user",
|
||||||
|
firstHint: "systemctl --user status -- custom-gateway.service",
|
||||||
|
secondHint: "systemctl --user cat -- custom-gateway.service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "inspects the detected system-level systemd unit without removing it",
|
||||||
|
platform: "linux",
|
||||||
|
serviceName: "custom-gateway.service",
|
||||||
|
source: "unit: /etc/systemd/system/custom-gateway.service",
|
||||||
|
sourcePath: "/etc/systemd/system/custom-gateway.service",
|
||||||
|
scope: "system",
|
||||||
|
firstHint: "systemctl --system status -- custom-gateway.service",
|
||||||
|
secondHint: "systemctl --system cat -- custom-gateway.service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "terminates systemctl options before a detected unit that begins with a dash",
|
||||||
|
platform: "linux",
|
||||||
|
serviceName: "-custom-gateway.service",
|
||||||
|
source: "unit: /home/test/.config/systemd/user/-custom-gateway.service",
|
||||||
|
sourcePath: "/home/test/.config/systemd/user/-custom-gateway.service",
|
||||||
|
scope: "user",
|
||||||
|
firstHint: "systemctl --user status -- -custom-gateway.service",
|
||||||
|
secondHint: "systemctl --user cat -- -custom-gateway.service",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "shell-quotes detected POSIX service labels and paths",
|
||||||
|
platform: "darwin",
|
||||||
|
serviceName: "com.example.gateway; touch injected",
|
||||||
|
source: "plist: /Users/test/Launch Agents/example's gateway.plist",
|
||||||
|
sourcePath: "/Users/test/Launch Agents/example's gateway.plist",
|
||||||
|
scope: "user",
|
||||||
|
firstHint: "launchctl bootout gui/$UID/'com.example.gateway; touch injected'",
|
||||||
|
secondHint: "rm '/Users/test/Launch Agents/example'\\''s gateway.plist'",
|
||||||
|
},
|
||||||
|
] as const)(
|
||||||
|
"$title",
|
||||||
|
({ platform, serviceName, source, sourcePath, scope, firstHint, secondHint }) => {
|
||||||
|
expect(
|
||||||
|
renderGatewayServiceCleanupHints([
|
||||||
|
{
|
||||||
|
platform,
|
||||||
|
label: serviceName,
|
||||||
|
detail: source,
|
||||||
|
sourcePath,
|
||||||
|
scope,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
).toEqual([firstHint, secondHint]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it("inspects the detected Windows scheduled task without suggesting removal", () => {
|
||||||
|
expect(
|
||||||
|
renderGatewayServiceCleanupHints([
|
||||||
|
{
|
||||||
|
platform: "win32",
|
||||||
|
label: "\\OpenClaw Gateway Backup",
|
||||||
|
detail: "task: \\OpenClaw Gateway Backup",
|
||||||
|
scope: "system",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
).toEqual(['schtasks /Query /TN "\\OpenClaw Gateway Backup" /V /FO LIST']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(["$(Start-Process calc)", "%OPENCLAW_GATEWAY_TASK%", "unsafe&task", "task`name"])(
|
||||||
|
"does not render a Windows task name expandable by cmd.exe or PowerShell: %s",
|
||||||
|
(label) => {
|
||||||
|
expect(
|
||||||
|
renderGatewayServiceCleanupHints([
|
||||||
|
{
|
||||||
|
platform: "win32",
|
||||||
|
label,
|
||||||
|
detail: `task: ${label}`,
|
||||||
|
scope: "system",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
).toEqual([]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it("does not invent a removal path when service metadata omits it", () => {
|
||||||
|
expect(
|
||||||
|
renderGatewayServiceCleanupHints([
|
||||||
|
{
|
||||||
|
platform: "darwin",
|
||||||
|
label: "com.example.openclaw-gateway",
|
||||||
|
detail: "loaded",
|
||||||
|
scope: "user",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
).toEqual(["launchctl bootout gui/$UID/com.example.openclaw-gateway"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -131,131 +131,6 @@ describe("detectMarkerLineWithGateway", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("renderGatewayServiceCleanupHints", () => {
|
|
||||||
it("does not suggest removing a gateway when no extra service was detected", () => {
|
|
||||||
expect(renderGatewayServiceCleanupHints([])).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each([
|
|
||||||
{
|
|
||||||
title: "targets the detected macOS LaunchAgent instead of the active gateway",
|
|
||||||
platform: "darwin",
|
|
||||||
serviceName: "com.example.openclaw-gateway",
|
|
||||||
source: "plist: /Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
|
||||||
scope: "user",
|
|
||||||
firstHint: "launchctl bootout gui/$UID/com.example.openclaw-gateway",
|
|
||||||
secondHint: "rm /Users/test/Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "uses the system domain for a detected macOS LaunchDaemon",
|
|
||||||
platform: "darwin",
|
|
||||||
serviceName: "com.example.openclaw-gateway",
|
|
||||||
source: "plist: /Library/LaunchDaemons/com.example.openclaw-gateway.plist",
|
|
||||||
scope: "system",
|
|
||||||
firstHint: "sudo launchctl bootout system/com.example.openclaw-gateway",
|
|
||||||
secondHint: "sudo rm /Library/LaunchDaemons/com.example.openclaw-gateway.plist",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "keeps global macOS LaunchAgents in the GUI domain",
|
|
||||||
platform: "darwin",
|
|
||||||
serviceName: "com.example.openclaw-gateway",
|
|
||||||
source: "plist: /Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
|
||||||
scope: "system",
|
|
||||||
firstHint: "launchctl bootout gui/$UID/com.example.openclaw-gateway",
|
|
||||||
secondHint: "sudo rm /Library/LaunchAgents/com.example.openclaw-gateway.plist",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "inspects the detected user-level systemd unit without removing it",
|
|
||||||
platform: "linux",
|
|
||||||
serviceName: "custom-gateway.service",
|
|
||||||
source: "unit: /home/test/.config/systemd/user/custom-gateway.service",
|
|
||||||
scope: "user",
|
|
||||||
firstHint: "systemctl --user status -- custom-gateway.service",
|
|
||||||
secondHint: "systemctl --user cat -- custom-gateway.service",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "inspects the detected system-level systemd unit without removing it",
|
|
||||||
platform: "linux",
|
|
||||||
serviceName: "custom-gateway.service",
|
|
||||||
source: "unit: /etc/systemd/system/custom-gateway.service",
|
|
||||||
scope: "system",
|
|
||||||
firstHint: "systemctl --system status -- custom-gateway.service",
|
|
||||||
secondHint: "systemctl --system cat -- custom-gateway.service",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "terminates systemctl options before a detected unit that begins with a dash",
|
|
||||||
platform: "linux",
|
|
||||||
serviceName: "-custom-gateway.service",
|
|
||||||
source: "unit: /home/test/.config/systemd/user/-custom-gateway.service",
|
|
||||||
scope: "user",
|
|
||||||
firstHint: "systemctl --user status -- -custom-gateway.service",
|
|
||||||
secondHint: "systemctl --user cat -- -custom-gateway.service",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "shell-quotes detected POSIX service labels and paths",
|
|
||||||
platform: "darwin",
|
|
||||||
serviceName: "com.example.gateway; touch injected",
|
|
||||||
source: "plist: /Users/test/Launch Agents/example's gateway.plist",
|
|
||||||
scope: "user",
|
|
||||||
firstHint: "launchctl bootout gui/$UID/'com.example.gateway; touch injected'",
|
|
||||||
secondHint: "rm '/Users/test/Launch Agents/example'\\''s gateway.plist'",
|
|
||||||
},
|
|
||||||
] as const)("$title", ({ platform, serviceName, source, scope, firstHint, secondHint }) => {
|
|
||||||
expect(
|
|
||||||
renderGatewayServiceCleanupHints([
|
|
||||||
{
|
|
||||||
platform,
|
|
||||||
label: serviceName,
|
|
||||||
detail: source,
|
|
||||||
scope,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
).toEqual([firstHint, secondHint]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("inspects the detected Windows scheduled task without suggesting removal", () => {
|
|
||||||
expect(
|
|
||||||
renderGatewayServiceCleanupHints([
|
|
||||||
{
|
|
||||||
platform: "win32",
|
|
||||||
label: "\\OpenClaw Gateway Backup",
|
|
||||||
detail: "task: \\OpenClaw Gateway Backup",
|
|
||||||
scope: "system",
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
).toEqual(['schtasks /Query /TN "\\OpenClaw Gateway Backup" /V /FO LIST']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each(["$(Start-Process calc)", "%OPENCLAW_GATEWAY_TASK%", "unsafe&task", "task`name"])(
|
|
||||||
"does not render a Windows task name expandable by cmd.exe or PowerShell: %s",
|
|
||||||
(label) => {
|
|
||||||
expect(
|
|
||||||
renderGatewayServiceCleanupHints([
|
|
||||||
{
|
|
||||||
platform: "win32",
|
|
||||||
label,
|
|
||||||
detail: `task: ${label}`,
|
|
||||||
scope: "system",
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
).toEqual([]);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
it("does not invent a removal path when service metadata omits it", () => {
|
|
||||||
expect(
|
|
||||||
renderGatewayServiceCleanupHints([
|
|
||||||
{
|
|
||||||
platform: "darwin",
|
|
||||||
label: "com.example.openclaw-gateway",
|
|
||||||
detail: "loaded",
|
|
||||||
scope: "user",
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
).toEqual(["launchctl bootout gui/$UID/com.example.openclaw-gateway"]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("findExtraGatewayServices (linux / scanSystemdDir) — real filesystem", () => {
|
describe("findExtraGatewayServices (linux / scanSystemdDir) — real filesystem", () => {
|
||||||
// These tests write real .service files to a temp dir and call findExtraGatewayServices
|
// These tests write real .service files to a temp dir and call findExtraGatewayServices
|
||||||
// with that dir as HOME. No platform mocking or fs mocking needed.
|
// with that dir as HOME. No platform mocking or fs mocking needed.
|
||||||
|
|
@ -299,6 +174,7 @@ describe("findExtraGatewayServices (linux / scanSystemdDir) — real filesystem"
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "clawdbot-gateway.service",
|
label: "clawdbot-gateway.service",
|
||||||
detail: `unit: ${unitPath}`,
|
detail: `unit: ${unitPath}`,
|
||||||
|
sourcePath: unitPath,
|
||||||
scope: "user",
|
scope: "user",
|
||||||
marker: "clawdbot",
|
marker: "clawdbot",
|
||||||
legacy: true,
|
legacy: true,
|
||||||
|
|
@ -343,6 +219,7 @@ describe("findExtraGatewayServices (linux / scanSystemdDir) — real filesystem"
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "clawdbot-gateway.service",
|
label: "clawdbot-gateway.service",
|
||||||
detail: `unit: ${unitPath}`,
|
detail: `unit: ${unitPath}`,
|
||||||
|
sourcePath: unitPath,
|
||||||
scope: "user",
|
scope: "user",
|
||||||
marker: "clawdbot",
|
marker: "clawdbot",
|
||||||
legacy: true,
|
legacy: true,
|
||||||
|
|
@ -382,6 +259,7 @@ describe("findExtraGatewayServices (linux / scanSystemdDir) — real filesystem"
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "custom-openclaw.service",
|
label: "custom-openclaw.service",
|
||||||
detail: `unit: ${unitPath}`,
|
detail: `unit: ${unitPath}`,
|
||||||
|
sourcePath: unitPath,
|
||||||
scope: "user",
|
scope: "user",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
legacy: false,
|
legacy: false,
|
||||||
|
|
@ -507,6 +385,7 @@ describe("findExtraGatewayServices (darwin / scanLaunchdDir) — real filesystem
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label: "com.example.openclaw-gateway",
|
label: "com.example.openclaw-gateway",
|
||||||
detail: `plist: ${plistPath}`,
|
detail: `plist: ${plistPath}`,
|
||||||
|
sourcePath: plistPath,
|
||||||
scope: "user",
|
scope: "user",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
legacy: false,
|
legacy: false,
|
||||||
|
|
@ -722,6 +601,7 @@ describe("managed Gateway inventory projections", () => {
|
||||||
label: "openclaw@.service",
|
label: "openclaw@.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
detail: `unit: ${path.join("/etc/systemd/system", "openclaw@.service")}`,
|
detail: `unit: ${path.join("/etc/systemd/system", "openclaw@.service")}`,
|
||||||
|
sourcePath: path.join("/etc/systemd/system", "openclaw@.service"),
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
legacy: false,
|
legacy: false,
|
||||||
});
|
});
|
||||||
|
|
@ -761,7 +641,7 @@ describe("managed Gateway inventory projections", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("finds Gateways in XDG, runtime, and systemd control load paths before build admission", async () => {
|
it("keeps discovered native selectors independent of display details", async () => {
|
||||||
Object.defineProperty(process, "platform", { configurable: true, value: "linux" });
|
Object.defineProperty(process, "platform", { configurable: true, value: "linux" });
|
||||||
const home = tempDirs.make("managed-systemd-load-paths-", os.tmpdir());
|
const home = tempDirs.make("managed-systemd-load-paths-", os.tmpdir());
|
||||||
const write = isolateNativeRoots(home);
|
const write = isolateNativeRoots(home);
|
||||||
|
|
@ -814,6 +694,25 @@ describe("managed Gateway inventory projections", () => {
|
||||||
"system-generated-gateway.service",
|
"system-generated-gateway.service",
|
||||||
"transient-gateway.service",
|
"transient-gateway.service",
|
||||||
]);
|
]);
|
||||||
|
const inventory = await import("./inspect.js");
|
||||||
|
for (const service of result.services) {
|
||||||
|
service.detail = "Discovered Gateway";
|
||||||
|
}
|
||||||
|
vi.spyOn(inventory, "listManagedOpenClawGatewayServices").mockResolvedValue(result);
|
||||||
|
const { discoverManagedGatewayBindings } = await import("./managed-gateway-bindings.js");
|
||||||
|
const bindings = await discoverManagedGatewayBindings({ HOME: home });
|
||||||
|
expect(bindings).toHaveLength(7);
|
||||||
|
expect(bindings.map((binding) => binding.systemdReadTarget?.unitPath)).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
path.join(configHome, "systemd/user/config-gateway.service"),
|
||||||
|
path.join(dataHome, "systemd/user/data-gateway.service"),
|
||||||
|
"/etc/systemd/system.control/system-gateway.service",
|
||||||
|
path.join(runtimeDir, "systemd/generator/generated-gateway.service"),
|
||||||
|
path.join(runtimeDir, "systemd/transient/transient-gateway.service"),
|
||||||
|
"/run/systemd/user/run-gateway.service",
|
||||||
|
"/run/systemd/generator/system-generated-gateway.service",
|
||||||
|
]),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
|
@ -864,6 +763,7 @@ describe("managed Gateway inventory projections", () => {
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label,
|
label,
|
||||||
detail: `plist: ${file}`,
|
detail: `plist: ${file}`,
|
||||||
|
sourcePath: file,
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
legacy: false,
|
legacy: false,
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,7 @@ export function renderGatewayServiceCleanupHints(
|
||||||
for (const service of services) {
|
for (const service of services) {
|
||||||
switch (service.platform) {
|
switch (service.platform) {
|
||||||
case "darwin": {
|
case "darwin": {
|
||||||
const plistPath = service.detail.startsWith("plist:")
|
const plistPath = service.sourcePath;
|
||||||
? service.detail.slice("plist:".length).trim()
|
|
||||||
: undefined;
|
|
||||||
// Global LaunchAgents still run in a GUI domain; only LaunchDaemons
|
// Global LaunchAgents still run in a GUI domain; only LaunchDaemons
|
||||||
// belong to the system domain regardless of their shared file scope.
|
// belong to the system domain regardless of their shared file scope.
|
||||||
const domain =
|
const domain =
|
||||||
|
|
@ -184,6 +182,7 @@ async function scanLaunchdDir(params: {
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label,
|
label,
|
||||||
detail: `plist: ${fullPath}`,
|
detail: `plist: ${fullPath}`,
|
||||||
|
sourcePath: fullPath,
|
||||||
scope: params.scope,
|
scope: params.scope,
|
||||||
marker,
|
marker,
|
||||||
legacy: marker !== "openclaw" || isLegacyLabel(label),
|
legacy: marker !== "openclaw" || isLegacyLabel(label),
|
||||||
|
|
@ -506,6 +505,7 @@ async function scanGatewayServices(
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: unit.name,
|
label: unit.name,
|
||||||
detail: `unit: ${unit.fragmentPath}`,
|
detail: `unit: ${unit.fragmentPath}`,
|
||||||
|
sourcePath: unit.fragmentPath,
|
||||||
scope,
|
scope,
|
||||||
marker: marker ?? "openclaw",
|
marker: marker ?? "openclaw",
|
||||||
legacy: marker === "clawdbot",
|
legacy: marker === "clawdbot",
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,7 @@ it.each([
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label,
|
label,
|
||||||
detail: `plist: ${locations.local.plist}`,
|
detail: `plist: ${locations.local.plist}`,
|
||||||
|
sourcePath: locations.local.plist,
|
||||||
scope: "user",
|
scope: "user",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -190,6 +191,7 @@ it.each([
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label,
|
label,
|
||||||
detail: `plist: ${locations.global.plist}`,
|
detail: `plist: ${locations.global.plist}`,
|
||||||
|
sourcePath: locations.global.plist,
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -197,6 +199,7 @@ it.each([
|
||||||
platform: "darwin",
|
platform: "darwin",
|
||||||
label,
|
label,
|
||||||
detail: `plist: ${systemPlist}`,
|
detail: `plist: ${systemPlist}`,
|
||||||
|
sourcePath: systemPlist,
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -353,7 +356,13 @@ it.each([
|
||||||
if (scenario.loaded === "system") {
|
if (scenario.loaded === "system") {
|
||||||
expect(
|
expect(
|
||||||
inventory.renderGatewayServiceCleanupHints([
|
inventory.renderGatewayServiceCleanupHints([
|
||||||
{ platform: "darwin", label, detail: `plist: ${systemPlist}`, scope: "system" },
|
{
|
||||||
|
platform: "darwin",
|
||||||
|
label,
|
||||||
|
detail: `plist: ${systemPlist}`,
|
||||||
|
sourcePath: systemPlist,
|
||||||
|
scope: "system",
|
||||||
|
},
|
||||||
]),
|
]),
|
||||||
).toContain(`sudo launchctl bootout system/${label}`);
|
).toContain(`sudo launchctl bootout system/${label}`);
|
||||||
}
|
}
|
||||||
|
|
@ -380,6 +389,7 @@ it.each([false, true])(
|
||||||
scope: "user" as const,
|
scope: "user" as const,
|
||||||
label: "custom-sibling.service",
|
label: "custom-sibling.service",
|
||||||
detail: "unit: /synthetic/custom-sibling.service",
|
detail: "unit: /synthetic/custom-sibling.service",
|
||||||
|
sourcePath: "/synthetic/custom-sibling.service",
|
||||||
};
|
};
|
||||||
vi.spyOn(inventory, "listManagedOpenClawGatewayServices").mockResolvedValue({
|
vi.spyOn(inventory, "listManagedOpenClawGatewayServices").mockResolvedValue({
|
||||||
services: [sibling, sibling],
|
services: [sibling, sibling],
|
||||||
|
|
|
||||||
|
|
@ -98,18 +98,11 @@ function profileEnvFields(profile: string): GatewayServiceEnv {
|
||||||
return profile === "default" ? {} : { OPENCLAW_PROFILE: profile };
|
return profile === "default" ? {} : { OPENCLAW_PROFILE: profile };
|
||||||
}
|
}
|
||||||
|
|
||||||
function detailPath(prefix: string, detail: string): string | undefined {
|
|
||||||
if (!detail.startsWith(prefix)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return detail.slice(prefix.length).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindingFromSystemdService(
|
function bindingFromSystemdService(
|
||||||
svc: ExtraGatewayService,
|
svc: ExtraGatewayService,
|
||||||
env: Record<string, string | undefined>,
|
env: Record<string, string | undefined>,
|
||||||
): ManagedGatewayBinding {
|
): ManagedGatewayBinding {
|
||||||
const unitPath = detailPath("unit:", svc.detail);
|
const unitPath = svc.sourcePath;
|
||||||
const unitName = resolveSystemdTemplateInstanceName(svc.label, {
|
const unitName = resolveSystemdTemplateInstanceName(svc.label, {
|
||||||
OPENCLAW_SYSTEMD_UNIT: svc.label,
|
OPENCLAW_SYSTEMD_UNIT: svc.label,
|
||||||
});
|
});
|
||||||
|
|
@ -125,7 +118,7 @@ function bindingFromLaunchdService(
|
||||||
svc: ExtraGatewayService,
|
svc: ExtraGatewayService,
|
||||||
env: Record<string, string | undefined>,
|
env: Record<string, string | undefined>,
|
||||||
): ManagedGatewayBinding {
|
): ManagedGatewayBinding {
|
||||||
const plistPath = detailPath("plist:", svc.detail);
|
const plistPath = svc.sourcePath;
|
||||||
return {
|
return {
|
||||||
scope: svc.scope,
|
scope: svc.scope,
|
||||||
...(plistPath ? { launchAgentPlistPath: plistPath } : {}),
|
...(plistPath ? { launchAgentPlistPath: plistPath } : {}),
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ it.each<ScopeCase>([
|
||||||
marker: "openclaw" as const,
|
marker: "openclaw" as const,
|
||||||
label: otherName,
|
label: otherName,
|
||||||
detail: `unit: ${otherPath}`,
|
detail: `unit: ${otherPath}`,
|
||||||
|
sourcePath: otherPath,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|
@ -165,6 +166,7 @@ it.each<ScopeCase>([
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
label: file,
|
label: file,
|
||||||
detail: `unit: ${target.unitPath}`,
|
detail: `unit: ${target.unitPath}`,
|
||||||
|
sourcePath: target.unitPath,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
vi.spyOn(fs, "access").mockRejectedValue(
|
vi.spyOn(fs, "access").mockRejectedValue(
|
||||||
|
|
@ -511,6 +513,7 @@ it("reads the system template instance while a separate user Gateway is installe
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
label: "openclaw@.service",
|
label: "openclaw@.service",
|
||||||
detail: `unit: ${templateUnit}`,
|
detail: `unit: ${templateUnit}`,
|
||||||
|
sourcePath: templateUnit,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
mockProcessPlatform("linux");
|
mockProcessPlatform("linux");
|
||||||
|
|
@ -651,6 +654,7 @@ it("findSystemdGatewayInstallation expands a system template to this account's i
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw@.service",
|
label: "openclaw@.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw@.service",
|
detail: "unit: /etc/systemd/system/openclaw@.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw@.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { findInstalledSystemdGatewayScope, isNonFatalSystemdInstallProbeError } from "./systemd.js";
|
import { findInstalledSystemdGatewayScope, isNonFatalSystemdInstallProbeError } from "./systemd.js";
|
||||||
|
|
||||||
const findSystemGatewayServicesMock = vi.hoisted(() =>
|
const findSystemGatewayServicesMock = vi.hoisted(() =>
|
||||||
vi.fn<
|
vi.fn<typeof import("./inspect.js").findSystemGatewayServices>(async () => []),
|
||||||
() => Promise<
|
|
||||||
Array<{
|
|
||||||
platform: "linux";
|
|
||||||
label: string;
|
|
||||||
detail: string;
|
|
||||||
scope: "user" | "system";
|
|
||||||
marker?: "openclaw" | "clawdbot";
|
|
||||||
legacy?: boolean;
|
|
||||||
}>
|
|
||||||
>
|
|
||||||
>(async () => []),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
vi.mock("./inspect.js", () => ({
|
vi.mock("./inspect.js", () => ({
|
||||||
|
|
@ -81,6 +70,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw.service",
|
label: "openclaw.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw.service",
|
detail: "unit: /etc/systemd/system/openclaw.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -100,6 +90,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw-darlene.service",
|
label: "openclaw-darlene.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw-darlene.service",
|
detail: "unit: /etc/systemd/system/openclaw-darlene.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw-darlene.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -118,6 +109,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw-gateway-lisa.service",
|
label: "openclaw-gateway-lisa.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw-gateway-lisa.service",
|
detail: "unit: /etc/systemd/system/openclaw-gateway-lisa.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw-gateway-lisa.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -136,6 +128,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "my-custom-gateway.service",
|
label: "my-custom-gateway.service",
|
||||||
detail: "unit: /etc/systemd/system/my-custom-gateway.service",
|
detail: "unit: /etc/systemd/system/my-custom-gateway.service",
|
||||||
|
sourcePath: "/etc/systemd/system/my-custom-gateway.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -216,6 +209,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label,
|
label,
|
||||||
detail: `unit: /etc/systemd/system/${label}`,
|
detail: `unit: /etc/systemd/system/${label}`,
|
||||||
|
sourcePath: `/etc/systemd/system/${label}`,
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -243,6 +237,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw@.service",
|
label: "openclaw@.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw@.service",
|
detail: "unit: /etc/systemd/system/openclaw@.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw@.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -265,6 +260,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw@other.service",
|
label: "openclaw@other.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw@other.service",
|
detail: "unit: /etc/systemd/system/openclaw@other.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw@other.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -303,6 +299,7 @@ describe("systemd gateway identity (openclaw#119648)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw-darlene.service",
|
label: "openclaw-darlene.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw-darlene.service",
|
detail: "unit: /etc/systemd/system/openclaw-darlene.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw-darlene.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -460,8 +460,7 @@ async function findMarkerOwnedSystemSystemdUnit(
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const match = /^unit:\s*(.+)$/.exec(svc.detail.trim());
|
const unitPath = svc.sourcePath;
|
||||||
const unitPath = match?.[1]?.trim();
|
|
||||||
if (unitPath) {
|
if (unitPath) {
|
||||||
const target: SystemdServiceReadTarget = {
|
const target: SystemdServiceReadTarget = {
|
||||||
scope: "system",
|
scope: "system",
|
||||||
|
|
|
||||||
|
|
@ -27,18 +27,7 @@ const assertNoSystemSystemdOwnershipMock = vi.hoisted(() =>
|
||||||
vi.fn<(unitName: string, timeoutMs?: number) => Promise<void>>(async () => {}),
|
vi.fn<(unitName: string, timeoutMs?: number) => Promise<void>>(async () => {}),
|
||||||
);
|
);
|
||||||
const findSystemGatewayServicesMock = vi.hoisted(() =>
|
const findSystemGatewayServicesMock = vi.hoisted(() =>
|
||||||
vi.fn<
|
vi.fn<typeof import("./inspect.js").findSystemGatewayServices>(async () => []),
|
||||||
() => Promise<
|
|
||||||
Array<{
|
|
||||||
platform: "linux";
|
|
||||||
label: string;
|
|
||||||
detail: string;
|
|
||||||
scope: "user" | "system";
|
|
||||||
marker?: "openclaw" | "clawdbot";
|
|
||||||
legacy?: boolean;
|
|
||||||
}>
|
|
||||||
>
|
|
||||||
>(async () => []),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
vi.mock("./inspect.js", () => ({
|
vi.mock("./inspect.js", () => ({
|
||||||
|
|
@ -596,6 +585,7 @@ describe("system-scope gateway unit detection (openclaw#87577)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw-rescue.service",
|
label: "openclaw-rescue.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw-rescue.service",
|
detail: "unit: /etc/systemd/system/openclaw-rescue.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw-rescue.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -640,6 +630,7 @@ describe("system-scope gateway unit detection (openclaw#87577)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw.service",
|
label: "openclaw.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw.service",
|
detail: "unit: /etc/systemd/system/openclaw.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -660,6 +651,7 @@ describe("system-scope gateway unit detection (openclaw#87577)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw.service",
|
label: "openclaw.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw.service",
|
detail: "unit: /etc/systemd/system/openclaw.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
@ -678,6 +670,7 @@ describe("system-scope gateway unit detection (openclaw#87577)", () => {
|
||||||
platform: "linux",
|
platform: "linux",
|
||||||
label: "openclaw.service",
|
label: "openclaw.service",
|
||||||
detail: "unit: /etc/systemd/system/openclaw.service",
|
detail: "unit: /etc/systemd/system/openclaw.service",
|
||||||
|
sourcePath: "/etc/systemd/system/openclaw.service",
|
||||||
scope: "system",
|
scope: "system",
|
||||||
marker: "openclaw",
|
marker: "openclaw",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ it.each([
|
||||||
const label = "org.example.fence-sibling";
|
const label = "org.example.fence-sibling";
|
||||||
const plist = path.join(home, "Library", "LaunchAgents", `${label}.plist`);
|
const plist = path.join(home, "Library", "LaunchAgents", `${label}.plist`);
|
||||||
vi.spyOn(inventory, "listManagedOpenClawGatewayServices").mockResolvedValue({
|
vi.spyOn(inventory, "listManagedOpenClawGatewayServices").mockResolvedValue({
|
||||||
services: [{ platform: "darwin", scope: "user", label, detail: `plist: ${plist}` }],
|
services: [
|
||||||
|
{ platform: "darwin", scope: "user", label, detail: `plist: ${plist}`, sourcePath: plist },
|
||||||
|
],
|
||||||
errors: [],
|
errors: [],
|
||||||
});
|
});
|
||||||
const native = vi.spyOn(launchdExec, "execLaunchctl").mockImplementation(async (args) => {
|
const native = vi.spyOn(launchdExec, "execLaunchctl").mockImplementation(async (args) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue