refactor: centralize systemd unavailable classification

This commit is contained in:
Peter Steinberger
2026-03-24 21:56:53 -07:00
parent 30e80fb947
commit 94425764a8
13 changed files with 197 additions and 64 deletions

View File

@@ -52,7 +52,10 @@ async function maybeAugmentSystemdHints(hints: string[]): Promise<string[]> {
if (systemdAvailable) {
return hints;
}
return [...hints, ...renderSystemdUnavailableHints({ wsl: await isWSL() })];
return [
...hints,
...renderSystemdUnavailableHints({ wsl: await isWSL(), kind: "generic_unavailable" }),
];
}
function createActionIO(params: { action: DaemonAction; json: boolean }) {

View File

@@ -3,6 +3,7 @@ import { theme } from "../../terminal/theme.js";
import {
filterContainerGenericHints,
renderGatewayServiceStartHints,
resolveDaemonContainerContext,
resolveRuntimeStatusColor,
} from "./shared.js";
@@ -20,6 +21,19 @@ describe("resolveRuntimeStatusColor", () => {
});
describe("renderGatewayServiceStartHints", () => {
it("resolves daemon container context from either env key", () => {
expect(
resolveDaemonContainerContext({
OPENCLAW_CONTAINER: "openclaw-demo-container",
} as NodeJS.ProcessEnv),
).toBe("openclaw-demo-container");
expect(
resolveDaemonContainerContext({
OPENCLAW_CONTAINER_HINT: "openclaw-demo-container",
} as NodeJS.ProcessEnv),
).toBe("openclaw-demo-container");
});
it("prepends a single container restart hint when OPENCLAW_CONTAINER is set", () => {
expect(
renderGatewayServiceStartHints({

View File

@@ -4,6 +4,7 @@ import {
resolveGatewaySystemdServiceName,
resolveGatewayWindowsTaskName,
} from "../../daemon/constants.js";
import { resolveDaemonContainerContext } from "../../daemon/container-context.js";
import { formatRuntimeStatus } from "../../daemon/runtime-format.js";
import {
buildPlatformRuntimeLogHints,
@@ -17,6 +18,7 @@ import { createDaemonActionContext } from "./response.js";
export { formatRuntimeStatus };
export { parsePort };
export { resolveDaemonContainerContext };
export function createDaemonInstallActionContext(jsonFlag: unknown) {
const json = Boolean(jsonFlag);
@@ -181,7 +183,7 @@ export function renderRuntimeHints(
export function renderGatewayServiceStartHints(env: NodeJS.ProcessEnv = process.env): string[] {
const profile = env.OPENCLAW_PROFILE;
const container = env.OPENCLAW_CONTAINER_HINT?.trim() || env.OPENCLAW_CONTAINER?.trim();
const container = resolveDaemonContainerContext(env);
const hints = buildPlatformServiceStartHints({
installCommand: formatCliCommand("openclaw gateway install", env),
startCommand: formatCliCommand("openclaw gateway", env),
@@ -199,7 +201,7 @@ export function filterContainerGenericHints(
hints: string[],
env: NodeJS.ProcessEnv = process.env,
): string[] {
if (!(env.OPENCLAW_CONTAINER_HINT?.trim() || env.OPENCLAW_CONTAINER?.trim())) {
if (!resolveDaemonContainerContext(env)) {
return hints;
}
return hints.filter(

View File

@@ -59,6 +59,7 @@ vi.mock("./shared.js", () => ({
filterDaemonEnv: () => ({}),
formatRuntimeStatus: () => "running (pid 8000)",
resolveRuntimeStatusColor: () => "",
resolveDaemonContainerContext: () => null,
renderRuntimeHints: () => [],
safeDaemonEnv: () => [],
}));

View File

@@ -10,6 +10,7 @@ import {
isSystemdUnavailableDetail,
renderSystemdUnavailableHints,
} from "../../daemon/systemd-hints.js";
import { classifySystemdUnavailableDetail } from "../../daemon/systemd-unavailable.js";
import { isWSLEnv } from "../../infra/wsl.js";
import { getResolvedLoggerSettings } from "../../logging.js";
import { defaultRuntime } from "../../runtime.js";
@@ -20,6 +21,7 @@ import {
createCliStatusTextStyles,
filterDaemonEnv,
formatRuntimeStatus,
resolveDaemonContainerContext,
resolveRuntimeStatusColor,
renderRuntimeHints,
safeDaemonEnv,
@@ -219,8 +221,15 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean })
const systemdUnavailable =
process.platform === "linux" && isSystemdUnavailableDetail(service.runtime?.detail);
if (systemdUnavailable) {
const container = Boolean(
resolveDaemonContainerContext(service.command?.environment ?? process.env),
);
defaultRuntime.error(errorText("systemd user services unavailable."));
for (const hint of renderSystemdUnavailableHints({ wsl: isWSLEnv() })) {
for (const hint of renderSystemdUnavailableHints({
wsl: isWSLEnv(),
kind: classifySystemdUnavailableDetail(service.runtime?.detail),
container,
})) {
defaultRuntime.error(errorText(hint));
}
spacer();