fix(doctor): show cleanup hints for detected services, not default openclaw service
renderGatewayServiceCleanupHints() always generated cleanup commands for the current openclaw service (ai.openclaw.gateway, openclaw-gateway, etc.) regardless of which extra/legacy services were actually detected. Now accepts the detected ExtraGatewayService[] array and generates platform-specific cleanup hints using each service's actual label and detail paths (plist/unit paths). Fixes #4454
This commit is contained in:
parent
9025da2296
commit
3129e59c63
@ -59,7 +59,7 @@ vi.mock("../daemon/legacy.js", () => ({
|
||||
|
||||
vi.mock("../daemon/inspect.js", () => ({
|
||||
findExtraGatewayServices: (env: unknown, opts?: unknown) => findExtraGatewayServices(env, opts),
|
||||
renderGatewayServiceCleanupHints: () => [],
|
||||
renderGatewayServiceCleanupHints: (_services: unknown[]) => [],
|
||||
}));
|
||||
|
||||
vi.mock("../infra/ports.js", () => ({
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
} from "../../config/config.js";
|
||||
import type { GatewayBindMode, GatewayControlUiConfig } from "../../config/types.js";
|
||||
import { readLastGatewayErrorLine } from "../../daemon/diagnostics.js";
|
||||
import type { FindExtraGatewayServicesOptions } from "../../daemon/inspect.js";
|
||||
import type { ExtraGatewayService, FindExtraGatewayServicesOptions } from "../../daemon/inspect.js";
|
||||
import { findExtraGatewayServices } from "../../daemon/inspect.js";
|
||||
import { resolveGatewayService } from "../../daemon/service.js";
|
||||
import type { ServiceConfigAudit } from "../../daemon/service-audit.js";
|
||||
@ -92,7 +92,7 @@ export type DaemonStatus = {
|
||||
error?: string;
|
||||
url?: string;
|
||||
};
|
||||
extraServices: Array<{ label: string; detail: string; scope: string }>;
|
||||
extraServices: ExtraGatewayService[];
|
||||
};
|
||||
|
||||
function shouldReportPortUsage(status: PortUsageStatus | undefined, rpcOk?: boolean) {
|
||||
|
||||
@ -292,7 +292,7 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
|
||||
for (const svc of extraServices) {
|
||||
defaultRuntime.error(`- ${errorText(svc.label)} (${svc.scope}, ${svc.detail})`);
|
||||
}
|
||||
for (const hint of renderGatewayServiceCleanupHints()) {
|
||||
for (const hint of renderGatewayServiceCleanupHints(extraServices)) {
|
||||
defaultRuntime.error(`${errorText("Cleanup hint:")} ${hint}`);
|
||||
}
|
||||
spacer();
|
||||
|
||||
@ -248,7 +248,7 @@ export async function maybeScanExtraGatewayServices(
|
||||
}
|
||||
}
|
||||
|
||||
const cleanupHints = renderGatewayServiceCleanupHints();
|
||||
const cleanupHints = renderGatewayServiceCleanupHints(extraServices);
|
||||
if (cleanupHints.length > 0) {
|
||||
note(cleanupHints.map((hint) => `- ${hint}`).join("\n"), "Cleanup hints");
|
||||
}
|
||||
|
||||
@ -27,29 +27,46 @@ export type FindExtraGatewayServicesOptions = {
|
||||
const EXTRA_MARKERS = ["openclaw", "clawdbot", "moltbot"] as const;
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
export function renderGatewayServiceCleanupHints(
|
||||
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
|
||||
): string[] {
|
||||
const profile = env.OPENCLAW_PROFILE;
|
||||
switch (process.platform) {
|
||||
case "darwin": {
|
||||
const label = resolveGatewayLaunchAgentLabel(profile);
|
||||
return [`launchctl bootout gui/$UID/${label}`, `rm ~/Library/LaunchAgents/${label}.plist`];
|
||||
export function renderGatewayServiceCleanupHints(services: ExtraGatewayService[]): string[] {
|
||||
const hints: string[] = [];
|
||||
for (const svc of services) {
|
||||
switch (svc.platform) {
|
||||
case "darwin": {
|
||||
hints.push(`launchctl bootout gui/$UID/${svc.label}`);
|
||||
const plistPath = extractPlistPath(svc.detail);
|
||||
if (plistPath) {
|
||||
hints.push(`rm ${plistPath}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "linux": {
|
||||
const unitName = svc.label.endsWith(".service") ? svc.label : `${svc.label}.service`;
|
||||
hints.push(`systemctl --user disable --now ${unitName}`);
|
||||
const unitPath = extractUnitPath(svc.detail);
|
||||
if (unitPath) {
|
||||
hints.push(`rm ${unitPath}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "win32": {
|
||||
hints.push(`schtasks /Delete /TN "${svc.label}" /F`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case "linux": {
|
||||
const unit = resolveGatewaySystemdServiceName(profile);
|
||||
return [
|
||||
`systemctl --user disable --now ${unit}.service`,
|
||||
`rm ~/.config/systemd/user/${unit}.service`,
|
||||
];
|
||||
}
|
||||
case "win32": {
|
||||
const task = resolveGatewayWindowsTaskName(profile);
|
||||
return [`schtasks /Delete /TN "${task}" /F`];
|
||||
}
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
return hints;
|
||||
}
|
||||
|
||||
function extractPlistPath(detail: string): string | null {
|
||||
if (!detail.startsWith("plist:")) return null;
|
||||
const value = detail.slice("plist:".length).trim();
|
||||
return value.length > 0 ? value : null;
|
||||
}
|
||||
|
||||
function extractUnitPath(detail: string): string | null {
|
||||
if (!detail.startsWith("unit:")) return null;
|
||||
const value = detail.slice("unit:".length).trim();
|
||||
return value.length > 0 ? value : null;
|
||||
}
|
||||
|
||||
function resolveHomeDir(env: Record<string, string | undefined>): string {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user