mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 01:28:27 +00:00
fix: migrate legacy gateway services
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveGatewayPort, resolveIsNixMode } from "../config/paths.js";
|
||||
@@ -16,6 +20,8 @@ import { buildGatewayInstallPlan } from "./daemon-install-helpers.js";
|
||||
import { DEFAULT_GATEWAY_DAEMON_RUNTIME, type GatewayDaemonRuntime } from "./daemon-runtime.js";
|
||||
import type { DoctorOptions, DoctorPrompter } from "./doctor-prompter.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
function detectGatewayRuntime(programArguments: string[] | undefined): GatewayDaemonRuntime {
|
||||
const first = programArguments?.[0];
|
||||
if (first) {
|
||||
@@ -37,6 +43,42 @@ function normalizeExecutablePath(value: string): string {
|
||||
return path.resolve(value);
|
||||
}
|
||||
|
||||
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 cleanupLegacyLaunchdService(params: {
|
||||
label: string;
|
||||
plistPath: string;
|
||||
}): Promise<string | null> {
|
||||
const domain = typeof process.getuid === "function" ? `gui/${process.getuid()}` : "gui/501";
|
||||
await execFileAsync("launchctl", ["bootout", domain, params.plistPath]).catch(() => undefined);
|
||||
await execFileAsync("launchctl", ["unload", params.plistPath]).catch(() => undefined);
|
||||
|
||||
const trashDir = path.join(os.homedir(), ".Trash");
|
||||
try {
|
||||
await fs.mkdir(trashDir, { recursive: true });
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.access(params.plistPath);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dest = path.join(trashDir, `${params.label}-${Date.now()}.plist`);
|
||||
try {
|
||||
await fs.rename(params.plistPath, dest);
|
||||
return dest;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function maybeRepairGatewayServiceConfig(
|
||||
cfg: OpenClawConfig,
|
||||
mode: "local" | "remote",
|
||||
@@ -150,7 +192,11 @@ export async function maybeRepairGatewayServiceConfig(
|
||||
}
|
||||
}
|
||||
|
||||
export async function maybeScanExtraGatewayServices(options: DoctorOptions) {
|
||||
export async function maybeScanExtraGatewayServices(
|
||||
options: DoctorOptions,
|
||||
runtime: RuntimeEnv,
|
||||
prompter: DoctorPrompter,
|
||||
) {
|
||||
const extraServices = await findExtraGatewayServices(process.env, {
|
||||
deep: options.deep,
|
||||
});
|
||||
@@ -161,6 +207,47 @@ export async function maybeScanExtraGatewayServices(options: DoctorOptions) {
|
||||
"Other gateway-like services detected",
|
||||
);
|
||||
|
||||
const legacyServices = extraServices.filter((svc) => svc.legacy === true);
|
||||
if (legacyServices.length > 0) {
|
||||
const shouldRemove = await prompter.confirmSkipInNonInteractive({
|
||||
message: "Remove legacy gateway services (clawdbot/moltbot) now?",
|
||||
initialValue: true,
|
||||
});
|
||||
if (shouldRemove) {
|
||||
const removed: string[] = [];
|
||||
const failed: string[] = [];
|
||||
for (const svc of legacyServices) {
|
||||
if (svc.platform !== "darwin") {
|
||||
failed.push(`${svc.label} (${svc.platform})`);
|
||||
continue;
|
||||
}
|
||||
if (svc.scope !== "user") {
|
||||
failed.push(`${svc.label} (${svc.scope})`);
|
||||
continue;
|
||||
}
|
||||
const plistPath = extractDetailPath(svc.detail, "plist:");
|
||||
if (!plistPath) {
|
||||
failed.push(`${svc.label} (missing plist path)`);
|
||||
continue;
|
||||
}
|
||||
const dest = await cleanupLegacyLaunchdService({
|
||||
label: svc.label,
|
||||
plistPath,
|
||||
});
|
||||
removed.push(dest ? `${svc.label} -> ${dest}` : svc.label);
|
||||
}
|
||||
if (removed.length > 0) {
|
||||
note(removed.map((line) => `- ${line}`).join("\n"), "Legacy gateway removed");
|
||||
}
|
||||
if (failed.length > 0) {
|
||||
note(failed.map((line) => `- ${line}`).join("\n"), "Legacy gateway cleanup skipped");
|
||||
}
|
||||
if (removed.length > 0) {
|
||||
runtime.log("Legacy gateway services removed. Installing OpenClaw gateway next.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cleanupHints = renderGatewayServiceCleanupHints();
|
||||
if (cleanupHints.length > 0) {
|
||||
note(cleanupHints.map((hint) => `- ${hint}`).join("\n"), "Cleanup hints");
|
||||
|
||||
@@ -185,7 +185,7 @@ export async function doctorCommand(
|
||||
cfg = await maybeRepairSandboxImages(cfg, runtime, prompter);
|
||||
noteSandboxScopeWarnings(cfg);
|
||||
|
||||
await maybeScanExtraGatewayServices(options);
|
||||
await maybeScanExtraGatewayServices(options, runtime, prompter);
|
||||
await maybeRepairGatewayServiceConfig(cfg, resolveMode(cfg), runtime, prompter);
|
||||
await noteMacLaunchAgentOverrides();
|
||||
await noteMacLaunchctlGatewayEnvOverrides(cfg);
|
||||
|
||||
@@ -65,9 +65,11 @@ export function randomToken(): string {
|
||||
export function printWizardHeader(runtime: RuntimeEnv) {
|
||||
const header = [
|
||||
"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄",
|
||||
"█████░█████░█████░█░░░█░█████░█░░░░░░███░░█░░░█",
|
||||
"█░░░█░█░░░█░███░░░██░░█░█░░░░░█░░░░░█░░░█░█░█░█",
|
||||
"█████░████░░█████░█░░██░█████░█████░█████░██░██",
|
||||
"█████░█████░█████░█░░░█░█████░█░░░░░█████░█░░░█",
|
||||
"█░░░█░█░░░█░█░░░░░██░░█░█░░░░░█░░░░░█░░░█░█░░░█",
|
||||
"█░░░█░█████░████░░█░█░█░█░░░░░█░░░░░█████░█░█░█",
|
||||
"█░░░█░█░░░░░█░░░░░█░░██░█░░░░░█░░░░░█░░░█░██░██",
|
||||
"█████░█░░░░░█████░█░░░█░█████░█████░█░░░█░█░░░█",
|
||||
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀",
|
||||
" 🦞 FRESH DAILY 🦞 ",
|
||||
" ",
|
||||
|
||||
Reference in New Issue
Block a user