refactor(commands): share daemon runtime warning helper

This commit is contained in:
Peter Steinberger
2026-02-18 23:08:40 +00:00
parent 3ce615ff06
commit c0c10f42e2
4 changed files with 117 additions and 30 deletions

View File

@@ -3,16 +3,14 @@ import { collectConfigEnvVars } from "../config/env-vars.js";
import type { OpenClawConfig } from "../config/types.js";
import { resolveGatewayLaunchAgentLabel } from "../daemon/constants.js";
import { resolveGatewayProgramArguments } from "../daemon/program-args.js";
import {
renderSystemNodeWarning,
resolvePreferredNodePath,
resolveSystemNodeInfo,
} from "../daemon/runtime-paths.js";
import { resolvePreferredNodePath } from "../daemon/runtime-paths.js";
import { buildServiceEnvironment } from "../daemon/service-env.js";
import {
emitNodeRuntimeWarning,
type DaemonInstallWarnFn,
} from "./daemon-install-runtime-warning.js";
import type { GatewayDaemonRuntime } from "./daemon-runtime.js";
type WarnFn = (message: string, title?: string) => void;
export type GatewayInstallPlan = {
programArguments: string[];
workingDirectory?: string;
@@ -32,7 +30,7 @@ export async function buildGatewayInstallPlan(params: {
token?: string;
devMode?: boolean;
nodePath?: string;
warn?: WarnFn;
warn?: DaemonInstallWarnFn;
/** Full config to extract env vars from (env vars + inline env keys). */
config?: OpenClawConfig;
}): Promise<GatewayInstallPlan> {
@@ -49,13 +47,13 @@ export async function buildGatewayInstallPlan(params: {
runtime: params.runtime,
nodePath,
});
if (params.runtime === "node") {
const systemNode = await resolveSystemNodeInfo({ env: params.env });
const warning = renderSystemNodeWarning(systemNode, programArguments[0]);
if (warning) {
params.warn?.(warning, "Gateway runtime");
}
}
await emitNodeRuntimeWarning({
env: params.env,
runtime: params.runtime,
nodeProgram: programArguments[0],
warn: params.warn,
title: "Gateway runtime",
});
const serviceEnvironment = buildServiceEnvironment({
env: params.env,
port: params.port,

View File

@@ -0,0 +1,71 @@
import { afterEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
resolveSystemNodeInfo: vi.fn(),
renderSystemNodeWarning: vi.fn(),
}));
vi.mock("../daemon/runtime-paths.js", () => ({
resolveSystemNodeInfo: mocks.resolveSystemNodeInfo,
renderSystemNodeWarning: mocks.renderSystemNodeWarning,
}));
import { emitNodeRuntimeWarning } from "./daemon-install-runtime-warning.js";
afterEach(() => {
vi.resetAllMocks();
});
describe("emitNodeRuntimeWarning", () => {
it("skips lookup when runtime is not node", async () => {
const warn = vi.fn();
await emitNodeRuntimeWarning({
env: {},
runtime: "bun",
warn,
title: "Gateway runtime",
});
expect(mocks.resolveSystemNodeInfo).not.toHaveBeenCalled();
expect(mocks.renderSystemNodeWarning).not.toHaveBeenCalled();
expect(warn).not.toHaveBeenCalled();
});
it("emits warning when system node check returns one", async () => {
const warn = vi.fn();
mocks.resolveSystemNodeInfo.mockResolvedValue({ path: "/usr/bin/node", version: "18.0.0" });
mocks.renderSystemNodeWarning.mockReturnValue("Node too old");
await emitNodeRuntimeWarning({
env: { PATH: "/usr/bin" },
runtime: "node",
nodeProgram: "/opt/node",
warn,
title: "Node daemon runtime",
});
expect(mocks.resolveSystemNodeInfo).toHaveBeenCalledWith({
env: { PATH: "/usr/bin" },
});
expect(mocks.renderSystemNodeWarning).toHaveBeenCalledWith(
{ path: "/usr/bin/node", version: "18.0.0" },
"/opt/node",
);
expect(warn).toHaveBeenCalledWith("Node too old", "Node daemon runtime");
});
it("does not emit when warning helper returns null", async () => {
const warn = vi.fn();
mocks.resolveSystemNodeInfo.mockResolvedValue(null);
mocks.renderSystemNodeWarning.mockReturnValue(null);
await emitNodeRuntimeWarning({
env: {},
runtime: "node",
nodeProgram: "node",
warn,
title: "Gateway runtime",
});
expect(warn).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,20 @@
import { renderSystemNodeWarning, resolveSystemNodeInfo } from "../daemon/runtime-paths.js";
export type DaemonInstallWarnFn = (message: string, title?: string) => void;
export async function emitNodeRuntimeWarning(params: {
env: Record<string, string | undefined>;
runtime: string;
nodeProgram?: string;
warn?: DaemonInstallWarnFn;
title: string;
}): Promise<void> {
if (params.runtime !== "node") {
return;
}
const systemNode = await resolveSystemNodeInfo({ env: params.env });
const warning = renderSystemNodeWarning(systemNode, params.nodeProgram);
if (warning) {
params.warn?.(warning, params.title);
}
}

View File

@@ -1,16 +1,14 @@
import { formatNodeServiceDescription } from "../daemon/constants.js";
import { resolveNodeProgramArguments } from "../daemon/program-args.js";
import {
renderSystemNodeWarning,
resolvePreferredNodePath,
resolveSystemNodeInfo,
} from "../daemon/runtime-paths.js";
import { resolvePreferredNodePath } from "../daemon/runtime-paths.js";
import { buildNodeServiceEnvironment } from "../daemon/service-env.js";
import { resolveGatewayDevMode } from "./daemon-install-helpers.js";
import {
emitNodeRuntimeWarning,
type DaemonInstallWarnFn,
} from "./daemon-install-runtime-warning.js";
import type { NodeDaemonRuntime } from "./node-daemon-runtime.js";
type WarnFn = (message: string, title?: string) => void;
export type NodeInstallPlan = {
programArguments: string[];
workingDirectory?: string;
@@ -29,7 +27,7 @@ export async function buildNodeInstallPlan(params: {
runtime: NodeDaemonRuntime;
devMode?: boolean;
nodePath?: string;
warn?: WarnFn;
warn?: DaemonInstallWarnFn;
}): Promise<NodeInstallPlan> {
const devMode = params.devMode ?? resolveGatewayDevMode();
const nodePath =
@@ -50,13 +48,13 @@ export async function buildNodeInstallPlan(params: {
nodePath,
});
if (params.runtime === "node") {
const systemNode = await resolveSystemNodeInfo({ env: params.env });
const warning = renderSystemNodeWarning(systemNode, programArguments[0]);
if (warning) {
params.warn?.(warning, "Node daemon runtime");
}
}
await emitNodeRuntimeWarning({
env: params.env,
runtime: params.runtime,
nodeProgram: programArguments[0],
warn: params.warn,
title: "Node daemon runtime",
});
const environment = buildNodeServiceEnvironment({ env: params.env });
const description = formatNodeServiceDescription({