refactor: consolidate daemon runtime and start hints

This commit is contained in:
Peter Steinberger
2026-03-07 21:00:55 +00:00
parent a91731a831
commit b955ba1688
5 changed files with 161 additions and 75 deletions

View File

@@ -3,8 +3,11 @@ import {
resolveGatewaySystemdServiceName,
resolveGatewayWindowsTaskName,
} from "../../daemon/constants.js";
import { resolveGatewayLogPaths } from "../../daemon/launchd.js";
import { formatRuntimeStatus } from "../../daemon/runtime-format.js";
import {
buildPlatformRuntimeLogHints,
buildPlatformServiceStartHints,
} from "../../daemon/runtime-hints.js";
import { getResolvedLoggerSettings } from "../../logging.js";
import { colorize, isRich, theme } from "../../terminal/theme.js";
import { formatCliCommand } from "../command-format.js";
@@ -144,41 +147,24 @@ export function renderRuntimeHints(
if (fileLog) {
hints.push(`File logs: ${fileLog}`);
}
if (process.platform === "darwin") {
const logs = resolveGatewayLogPaths(env);
hints.push(`Launchd stdout (if installed): ${logs.stdoutPath}`);
hints.push(`Launchd stderr (if installed): ${logs.stderrPath}`);
} else if (process.platform === "linux") {
const unit = resolveGatewaySystemdServiceName(env.OPENCLAW_PROFILE);
hints.push(`Logs: journalctl --user -u ${unit}.service -n 200 --no-pager`);
} else if (process.platform === "win32") {
const task = resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE);
hints.push(`Logs: schtasks /Query /TN "${task}" /V /FO LIST`);
}
hints.push(
...buildPlatformRuntimeLogHints({
env,
systemdServiceName: resolveGatewaySystemdServiceName(env.OPENCLAW_PROFILE),
windowsTaskName: resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE),
}),
);
}
return hints;
}
export function renderGatewayServiceStartHints(env: NodeJS.ProcessEnv = process.env): string[] {
const base = [
formatCliCommand("openclaw gateway install", env),
formatCliCommand("openclaw gateway", env),
];
const profile = env.OPENCLAW_PROFILE;
switch (process.platform) {
case "darwin": {
const label = resolveGatewayLaunchAgentLabel(profile);
return [...base, `launchctl bootstrap gui/$UID ~/Library/LaunchAgents/${label}.plist`];
}
case "linux": {
const unit = resolveGatewaySystemdServiceName(profile);
return [...base, `systemctl --user start ${unit}.service`];
}
case "win32": {
const task = resolveGatewayWindowsTaskName(profile);
return [...base, `schtasks /Run /TN "${task}"`];
}
default:
return base;
}
return buildPlatformServiceStartHints({
installCommand: formatCliCommand("openclaw gateway install", env),
startCommand: formatCliCommand("openclaw gateway", env),
launchAgentPlistPath: `~/Library/LaunchAgents/${resolveGatewayLaunchAgentLabel(profile)}.plist`,
systemdServiceName: resolveGatewaySystemdServiceName(profile),
windowsTaskName: resolveGatewayWindowsTaskName(profile),
});
}

View File

@@ -9,8 +9,11 @@ import {
resolveNodeSystemdServiceName,
resolveNodeWindowsTaskName,
} from "../../daemon/constants.js";
import { resolveGatewayLogPaths } from "../../daemon/launchd.js";
import { resolveNodeService } from "../../daemon/node-service.js";
import {
buildPlatformRuntimeLogHints,
buildPlatformServiceStartHints,
} from "../../daemon/runtime-hints.js";
import type { GatewayServiceRuntime } from "../../daemon/service-runtime.js";
import { loadNodeHostConfig } from "../../node-host/config.js";
import { defaultRuntime } from "../../runtime.js";
@@ -55,39 +58,21 @@ type NodeDaemonStatusOptions = {
};
function renderNodeServiceStartHints(): string[] {
const base = [formatCliCommand("openclaw node install"), formatCliCommand("openclaw node start")];
switch (process.platform) {
case "darwin":
return [
...base,
`launchctl bootstrap gui/$UID ~/Library/LaunchAgents/${resolveNodeLaunchAgentLabel()}.plist`,
];
case "linux":
return [...base, `systemctl --user start ${resolveNodeSystemdServiceName()}.service`];
case "win32":
return [...base, `schtasks /Run /TN "${resolveNodeWindowsTaskName()}"`];
default:
return base;
}
return buildPlatformServiceStartHints({
installCommand: formatCliCommand("openclaw node install"),
startCommand: formatCliCommand("openclaw node start"),
launchAgentPlistPath: `~/Library/LaunchAgents/${resolveNodeLaunchAgentLabel()}.plist`,
systemdServiceName: resolveNodeSystemdServiceName(),
windowsTaskName: resolveNodeWindowsTaskName(),
});
}
function buildNodeRuntimeHints(env: NodeJS.ProcessEnv = process.env): string[] {
if (process.platform === "darwin") {
const logs = resolveGatewayLogPaths(env);
return [
`Launchd stdout (if installed): ${logs.stdoutPath}`,
`Launchd stderr (if installed): ${logs.stderrPath}`,
];
}
if (process.platform === "linux") {
const unit = resolveNodeSystemdServiceName();
return [`Logs: journalctl --user -u ${unit}.service -n 200 --no-pager`];
}
if (process.platform === "win32") {
const task = resolveNodeWindowsTaskName();
return [`Logs: schtasks /Query /TN "${task}" /V /FO LIST`];
}
return [];
return buildPlatformRuntimeLogHints({
env,
systemdServiceName: resolveNodeSystemdServiceName(),
windowsTaskName: resolveNodeWindowsTaskName(),
});
}
function resolveNodeDefaults(

View File

@@ -4,8 +4,8 @@ import {
resolveGatewaySystemdServiceName,
resolveGatewayWindowsTaskName,
} from "../daemon/constants.js";
import { resolveGatewayLogPaths } from "../daemon/launchd.js";
import { formatRuntimeStatus } from "../daemon/runtime-format.js";
import { buildPlatformRuntimeLogHints } from "../daemon/runtime-hints.js";
import type { GatewayServiceRuntime } from "../daemon/service-runtime.js";
import {
isSystemdUnavailableDetail,
@@ -68,17 +68,14 @@ export function buildGatewayRuntimeHints(
if (fileLog) {
hints.push(`File logs: ${fileLog}`);
}
if (platform === "darwin") {
const logs = resolveGatewayLogPaths(env);
hints.push(`Launchd stdout (if installed): ${logs.stdoutPath}`);
hints.push(`Launchd stderr (if installed): ${logs.stderrPath}`);
} else if (platform === "linux") {
const unit = resolveGatewaySystemdServiceName(env.OPENCLAW_PROFILE);
hints.push(`Logs: journalctl --user -u ${unit}.service -n 200 --no-pager`);
} else if (platform === "win32") {
const task = resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE);
hints.push(`Logs: schtasks /Query /TN "${task}" /V /FO LIST`);
}
hints.push(
...buildPlatformRuntimeLogHints({
platform,
env,
systemdServiceName: resolveGatewaySystemdServiceName(env.OPENCLAW_PROFILE),
windowsTaskName: resolveGatewayWindowsTaskName(env.OPENCLAW_PROFILE),
}),
);
}
return hints;
}

View File

@@ -0,0 +1,71 @@
import { describe, expect, it } from "vitest";
import { buildPlatformRuntimeLogHints, buildPlatformServiceStartHints } from "./runtime-hints.js";
describe("buildPlatformRuntimeLogHints", () => {
it("renders launchd log hints on darwin", () => {
expect(
buildPlatformRuntimeLogHints({
platform: "darwin",
env: {
OPENCLAW_STATE_DIR: "/tmp/openclaw-state",
OPENCLAW_LOG_PREFIX: "gateway",
},
systemdServiceName: "openclaw-gateway",
windowsTaskName: "OpenClaw Gateway",
}),
).toEqual([
"Launchd stdout (if installed): /tmp/openclaw-state/logs/gateway.log",
"Launchd stderr (if installed): /tmp/openclaw-state/logs/gateway.err.log",
]);
});
it("renders systemd and windows hints by platform", () => {
expect(
buildPlatformRuntimeLogHints({
platform: "linux",
systemdServiceName: "openclaw-gateway",
windowsTaskName: "OpenClaw Gateway",
}),
).toEqual(["Logs: journalctl --user -u openclaw-gateway.service -n 200 --no-pager"]);
expect(
buildPlatformRuntimeLogHints({
platform: "win32",
systemdServiceName: "openclaw-gateway",
windowsTaskName: "OpenClaw Gateway",
}),
).toEqual(['Logs: schtasks /Query /TN "OpenClaw Gateway" /V /FO LIST']);
});
});
describe("buildPlatformServiceStartHints", () => {
it("builds platform-specific service start hints", () => {
expect(
buildPlatformServiceStartHints({
platform: "darwin",
installCommand: "openclaw gateway install",
startCommand: "openclaw gateway",
launchAgentPlistPath: "~/Library/LaunchAgents/com.openclaw.gateway.plist",
systemdServiceName: "openclaw-gateway",
windowsTaskName: "OpenClaw Gateway",
}),
).toEqual([
"openclaw gateway install",
"openclaw gateway",
"launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.openclaw.gateway.plist",
]);
expect(
buildPlatformServiceStartHints({
platform: "linux",
installCommand: "openclaw gateway install",
startCommand: "openclaw gateway",
launchAgentPlistPath: "~/Library/LaunchAgents/com.openclaw.gateway.plist",
systemdServiceName: "openclaw-gateway",
windowsTaskName: "OpenClaw Gateway",
}),
).toEqual([
"openclaw gateway install",
"openclaw gateway",
"systemctl --user start openclaw-gateway.service",
]);
});
});

View File

@@ -0,0 +1,47 @@
import { resolveGatewayLogPaths } from "./launchd.js";
export function buildPlatformRuntimeLogHints(params: {
platform?: NodeJS.Platform;
env?: NodeJS.ProcessEnv;
systemdServiceName: string;
windowsTaskName: string;
}): string[] {
const platform = params.platform ?? process.platform;
const env = params.env ?? process.env;
if (platform === "darwin") {
const logs = resolveGatewayLogPaths(env);
return [
`Launchd stdout (if installed): ${logs.stdoutPath}`,
`Launchd stderr (if installed): ${logs.stderrPath}`,
];
}
if (platform === "linux") {
return [`Logs: journalctl --user -u ${params.systemdServiceName}.service -n 200 --no-pager`];
}
if (platform === "win32") {
return [`Logs: schtasks /Query /TN "${params.windowsTaskName}" /V /FO LIST`];
}
return [];
}
export function buildPlatformServiceStartHints(params: {
platform?: NodeJS.Platform;
installCommand: string;
startCommand: string;
launchAgentPlistPath: string;
systemdServiceName: string;
windowsTaskName: string;
}): string[] {
const platform = params.platform ?? process.platform;
const base = [params.installCommand, params.startCommand];
switch (platform) {
case "darwin":
return [...base, `launchctl bootstrap gui/$UID ${params.launchAgentPlistPath}`];
case "linux":
return [...base, `systemctl --user start ${params.systemdServiceName}.service`];
case "win32":
return [...base, `schtasks /Run /TN "${params.windowsTaskName}"`];
default:
return base;
}
}