mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
fix(daemon): recovery hints target the wrong profile or container (#130216)
* fix(daemon): preserve profile context in systemd recovery hints * fix(cli): remove obsolete daemon container helper export
This commit is contained in:
committed by
GitHub
parent
c3ea9775ca
commit
8585e7e04e
@@ -6,7 +6,6 @@ import {
|
||||
parsePortFromArgs,
|
||||
renderRuntimeHints,
|
||||
renderGatewayServiceStartHints,
|
||||
resolveDaemonContainerContext,
|
||||
resolveRuntimeStatusColor,
|
||||
} from "./shared.js";
|
||||
|
||||
@@ -61,19 +60,6 @@ describe("renderGatewayServiceStartHints", () => {
|
||||
).toContain("logged-in macOS GUI session");
|
||||
});
|
||||
|
||||
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({
|
||||
|
||||
@@ -19,7 +19,6 @@ import { createDaemonActionContext } from "./response.js";
|
||||
|
||||
export { formatRuntimeStatus };
|
||||
export { parsePort };
|
||||
export { resolveDaemonContainerContext };
|
||||
|
||||
/** Create install action context with JSON flag normalization. */
|
||||
export function createDaemonInstallActionContext(jsonFlag: unknown) {
|
||||
|
||||
@@ -107,7 +107,6 @@ vi.mock("./shared.js", () => ({
|
||||
filterDaemonEnv: () => ({}),
|
||||
formatRuntimeStatus: () => "running (pid 8000)",
|
||||
resolveRuntimeStatusColor: () => "",
|
||||
resolveDaemonContainerContext: () => null,
|
||||
renderRuntimeHints: () => [],
|
||||
safeDaemonEnv: () => [],
|
||||
}));
|
||||
@@ -475,7 +474,7 @@ describe("printDaemonStatus", () => {
|
||||
expect(renderSystemdUnavailableHintsMock).toHaveBeenCalledWith({
|
||||
wsl: true,
|
||||
kind: "generic_unavailable",
|
||||
container: false,
|
||||
env: { WSL_DISTRO_NAME: "Ubuntu" },
|
||||
});
|
||||
expectMockLineContains(runtime.log, "Service: systemd (unknown)");
|
||||
expect(runtime.log.mock.calls.flat().join("\n")).not.toContain("Service: systemd (not loaded)");
|
||||
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
createCliStatusTextStyles,
|
||||
filterDaemonEnv,
|
||||
formatRuntimeStatus,
|
||||
resolveDaemonContainerContext,
|
||||
resolveRuntimeStatusColor,
|
||||
renderRuntimeHints,
|
||||
safeDaemonEnv,
|
||||
@@ -407,12 +406,11 @@ export function printDaemonStatus(status: DaemonStatus, opts: { json: boolean; d
|
||||
isSystemdUnavailableDetail(systemdUnavailableDetail);
|
||||
if (systemdUnavailable) {
|
||||
const serviceEnv = service.command?.environment ?? process.env;
|
||||
const container = Boolean(resolveDaemonContainerContext(serviceEnv));
|
||||
defaultRuntime.error(errorText("systemd user services unavailable."));
|
||||
for (const hint of renderSystemdUnavailableHints({
|
||||
wsl: isWSLEnv(serviceEnv),
|
||||
kind: classifySystemdUnavailableDetail(systemdUnavailableDetail),
|
||||
container,
|
||||
env: serviceEnv,
|
||||
})) {
|
||||
defaultRuntime.error(errorText(hint));
|
||||
}
|
||||
|
||||
@@ -58,6 +58,34 @@ describe("buildGatewayRuntimeHints", () => {
|
||||
expect(hints.join("\n")).not.toContain("systemd user services are unavailable");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
env: { OPENCLAW_PROFILE: "blue" },
|
||||
command: "openclaw --profile blue gateway",
|
||||
},
|
||||
{
|
||||
env: { OPENCLAW_CONTAINER_HINT: "sandbox" },
|
||||
command: "openclaw --container sandbox gateway",
|
||||
},
|
||||
{
|
||||
env: { OPENCLAW_PROFILE: "blue", OPENCLAW_CONTAINER_HINT: "sandbox" },
|
||||
command: "openclaw --container sandbox gateway",
|
||||
},
|
||||
])("preserves the active target in systemd recovery commands: $command", ({ env, command }) => {
|
||||
const hints = buildGatewayRuntimeHints(
|
||||
{
|
||||
status: "unknown",
|
||||
detail: "systemctl --user unavailable: Failed to connect to bus",
|
||||
},
|
||||
{ platform: "linux", env },
|
||||
);
|
||||
|
||||
expect(hints.some((hint) => hint.includes(command))).toBe(true);
|
||||
expect(hints.some((hint) => hint.includes("headless server"))).toBe(
|
||||
!env.OPENCLAW_CONTAINER_HINT,
|
||||
);
|
||||
});
|
||||
|
||||
it("guides recovery when systemd hit its restart start limit (crash loop)", () => {
|
||||
// Real give-up shape: process kept failing (Result=exit-code) until NRestarts
|
||||
// reached StartLimitBurst and systemd stopped restarting.
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
resolveGatewaySystemdServiceName,
|
||||
resolveGatewayWindowsTaskName,
|
||||
} from "../daemon/constants.js";
|
||||
import { resolveDaemonContainerContext } from "../daemon/container-context.js";
|
||||
import { formatRuntimeStatus } from "../daemon/runtime-format.js";
|
||||
import { buildPlatformRuntimeLogHints } from "../daemon/runtime-hints.js";
|
||||
import {
|
||||
@@ -45,7 +44,6 @@ export function buildGatewayRuntimeHints(
|
||||
}
|
||||
const platform = options.platform ?? process.platform;
|
||||
const env = options.env ?? process.env;
|
||||
const container = Boolean(resolveDaemonContainerContext(env));
|
||||
const fileLog = (() => {
|
||||
try {
|
||||
return getResolvedLoggerSettings().file;
|
||||
@@ -58,7 +56,7 @@ export function buildGatewayRuntimeHints(
|
||||
...renderSystemdUnavailableHints({
|
||||
wsl: isWSLEnv(env),
|
||||
kind: classifySystemdUnavailableDetail(runtime.detail),
|
||||
container,
|
||||
env,
|
||||
}),
|
||||
);
|
||||
if (fileLog) {
|
||||
|
||||
@@ -49,14 +49,15 @@ describe("renderSystemdUnavailableHints", () => {
|
||||
});
|
||||
|
||||
it("skips headless recovery hints when container context is known", () => {
|
||||
const env = { OPENCLAW_CONTAINER_HINT: "sandbox" };
|
||||
expect(
|
||||
renderSystemdUnavailableHints({
|
||||
kind: "user_bus_unavailable",
|
||||
container: true,
|
||||
env,
|
||||
}),
|
||||
).toEqual([
|
||||
"systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.",
|
||||
`If you're in a container, run the gateway in the foreground instead of \`${formatCliCommand("openclaw gateway")}\`.`,
|
||||
`If you're in a container, run the gateway in the foreground instead of \`${formatCliCommand("openclaw gateway", env)}\`.`,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** Renders Linux systemd availability hints for gateway service commands. */
|
||||
import { formatCliCommand } from "../cli/command-format.js";
|
||||
import { resolveDaemonContainerContext } from "./container-context.js";
|
||||
import {
|
||||
classifySystemdUnavailableDetail,
|
||||
type SystemdUnavailableKind,
|
||||
@@ -8,7 +9,7 @@ import {
|
||||
type SystemdUnavailableHintOptions = {
|
||||
wsl?: boolean;
|
||||
kind?: SystemdUnavailableKind | null;
|
||||
container?: boolean;
|
||||
env?: Record<string, string | undefined>;
|
||||
};
|
||||
|
||||
/** Detects details that should get systemd availability repair hints. */
|
||||
@@ -36,9 +37,9 @@ export function renderSystemdUnavailableHints(
|
||||
}
|
||||
return [
|
||||
"systemd user services are unavailable; install/enable systemd or run the gateway under your supervisor.",
|
||||
...(options.container || options.kind !== "user_bus_unavailable"
|
||||
...(resolveDaemonContainerContext(options.env) || options.kind !== "user_bus_unavailable"
|
||||
? []
|
||||
: renderSystemdHeadlessServerHints()),
|
||||
`If you're in a container, run the gateway in the foreground instead of \`${formatCliCommand("openclaw gateway")}\`.`,
|
||||
`If you're in a container, run the gateway in the foreground instead of \`${formatCliCommand("openclaw gateway", options.env)}\`.`,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user