fix: honor setup binary path account overrides

This commit is contained in:
Tak Hoffman
2026-04-03 11:53:53 -05:00
parent 2c0967150d
commit b2a9e0d7a7
6 changed files with 90 additions and 8 deletions

View File

@@ -28,7 +28,8 @@ export const imessageSetupWizard: ChannelSetupWizard = {
configuredScore: imessageSetupStatusBase.configuredScore,
unconfiguredScore: imessageSetupStatusBase.unconfiguredScore,
resolveConfigured: imessageSetupStatusBase.resolveConfigured,
resolveBinaryPath: ({ cfg }) => cfg.channels?.imessage?.cliPath ?? "imsg",
resolveBinaryPath: ({ cfg, accountId }) =>
resolveIMessageAccount({ cfg, accountId: accountId ?? "default" }).config.cliPath ?? "imsg",
detectBinary,
}),
credentials: [],

View File

@@ -1,6 +1,7 @@
import * as processRuntime from "openclaw/plugin-sdk/process-runtime";
import * as setupRuntime from "openclaw/plugin-sdk/setup";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createPluginSetupWizardStatus } from "../../../test/helpers/plugins/setup-wizard.js";
import * as clientModule from "./client.js";
import { imessagePlugin } from "./channel.js";
import * as channelRuntimeModule from "./channel.runtime.js";
@@ -20,6 +21,8 @@ import {
parseIMessageTarget,
} from "./targets.js";
const getIMessageSetupStatus = createPluginSetupWizardStatus(imessagePlugin);
const spawnMock = vi.hoisted(() => vi.fn());
vi.mock("node:child_process", async (importOriginal) => {
@@ -299,4 +302,24 @@ describe("probeIMessage", () => {
dbPath: "/tmp/work-db",
});
});
it("setup status lines use the selected account cliPath", async () => {
const status = await getIMessageSetupStatus({
cfg: {
channels: {
imessage: {
cliPath: "/tmp/root-imsg",
accounts: {
work: {
cliPath: "/tmp/work-imsg",
},
},
},
},
} as never,
accountOverrides: { imessage: "work" },
});
expect(status.statusLines).toContain("imsg: missing (/tmp/work-imsg)");
});
});

View File

@@ -1,4 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { createPluginSetupWizardStatus } from "../../../test/helpers/plugins/setup-wizard.js";
import { signalPlugin } from "./channel.js";
import * as clientModule from "./client.js";
import { classifySignalCliLogLine } from "./daemon.js";
@@ -16,6 +17,8 @@ import {
signalDmPolicy,
} from "./setup-core.js";
const getSignalSetupStatus = createPluginSetupWizardStatus(signalPlugin);
describe("looksLikeUuid", () => {
it("accepts hyphenated UUIDs", () => {
expect(looksLikeUuid("123e4567-e89b-12d3-a456-426614174000")).toBe(true);
@@ -133,6 +136,26 @@ describe("probeSignal", () => {
expect(res.status).toBe(503);
expect(res.version).toBe(null);
});
it("setup status lines use the selected account cliPath", async () => {
const status = await getSignalSetupStatus({
cfg: {
channels: {
signal: {
cliPath: "/tmp/root-signal-cli",
accounts: {
work: {
cliPath: "/tmp/work-signal-cli",
},
},
},
},
} as never,
accountOverrides: { signal: "work" },
});
expect(status.statusLines).toContain("signal-cli: missing (/tmp/work-signal-cli)");
});
});
describe("signal outbound", () => {

View File

@@ -34,7 +34,9 @@ export const signalSetupWizard: ChannelSetupWizard = {
: listSignalAccountIds(cfg).some(
(resolvedAccountId) => resolveSignalAccount({ cfg, accountId: resolvedAccountId }).configured,
),
resolveBinaryPath: ({ cfg }) => cfg.channels?.signal?.cliPath ?? "signal-cli",
resolveBinaryPath: ({ cfg, accountId }) =>
resolveSignalAccount({ cfg, accountId: accountId ?? "default" }).config.cliPath ??
"signal-cli",
detectBinary,
}),
prepare: async ({ cfg, accountId, credentialValues, runtime, prompter, options }) => {

View File

@@ -10,6 +10,7 @@ import type { ChannelSetupWizard } from "./setup-wizard.js";
describe("createDetectedBinaryStatus", () => {
it("builds status lines, hint, and score from binary detection", async () => {
const resolveConfigured = vi.fn(() => true);
const resolveBinaryPath = vi.fn(() => "/usr/local/bin/signal-cli");
const status = createDetectedBinaryStatus({
channelLabel: "Signal",
binaryLabel: "signal-cli",
@@ -20,7 +21,7 @@ describe("createDetectedBinaryStatus", () => {
configuredScore: 1,
unconfiguredScore: 0,
resolveConfigured,
resolveBinaryPath: () => "/usr/local/bin/signal-cli",
resolveBinaryPath,
detectBinary: vi.fn(async () => true),
});
@@ -30,11 +31,39 @@ describe("createDetectedBinaryStatus", () => {
"Signal: configured",
"signal-cli: found (/usr/local/bin/signal-cli)",
]);
expect(resolveBinaryPath).toHaveBeenCalledWith({ cfg: {}, accountId: undefined });
expect(await status.resolveSelectionHint?.({ cfg: {}, configured: true })).toBe(
"signal-cli found",
);
expect(await status.resolveQuickstartScore?.({ cfg: {}, configured: true })).toBe(1);
});
it("passes accountId into binary path resolution", async () => {
const resolveBinaryPath = vi.fn(({ accountId }: { accountId?: string }) =>
accountId === "work" ? "/opt/work-signal-cli" : "/usr/local/bin/signal-cli",
);
const status = createDetectedBinaryStatus({
channelLabel: "Signal",
binaryLabel: "signal-cli",
configuredLabel: "configured",
unconfiguredLabel: "needs setup",
configuredHint: "signal-cli found",
unconfiguredHint: "signal-cli missing",
configuredScore: 1,
unconfiguredScore: 0,
resolveConfigured: () => true,
resolveBinaryPath,
detectBinary: vi.fn(async () => false),
});
expect(
await status.resolveStatusLines?.({ cfg: {}, accountId: "work", configured: false }),
).toEqual([
"Signal: needs setup",
"signal-cli: missing (/opt/work-signal-cli)",
]);
expect(resolveBinaryPath).toHaveBeenCalledWith({ cfg: {}, accountId: "work" });
});
});
describe("createCliPathTextInput", () => {

View File

@@ -22,7 +22,7 @@ export function createDetectedBinaryStatus(params: {
cfg: OpenClawConfig;
accountId?: string;
}) => boolean | Promise<boolean>;
resolveBinaryPath: (params: { cfg: OpenClawConfig }) => string;
resolveBinaryPath: (params: { cfg: OpenClawConfig; accountId?: string }) => string;
detectBinary?: (path: string) => Promise<boolean>;
}): ChannelSetupWizardStatus {
const detectBinary = params.detectBinary ?? defaultDetectBinary;
@@ -35,8 +35,8 @@ export function createDetectedBinaryStatus(params: {
configuredScore: params.configuredScore,
unconfiguredScore: params.unconfiguredScore,
resolveConfigured: params.resolveConfigured,
async resolveStatusLines({ cfg, configured }: SetupStatusParams): Promise<string[]> {
const binaryPath = params.resolveBinaryPath({ cfg });
async resolveStatusLines({ cfg, accountId, configured }: SetupStatusParams): Promise<string[]> {
const binaryPath = params.resolveBinaryPath({ cfg, accountId });
const detected = await detectBinary(binaryPath);
return [
`${params.channelLabel}: ${configured ? params.configuredLabel : params.unconfiguredLabel}`,
@@ -45,21 +45,25 @@ export function createDetectedBinaryStatus(params: {
},
async resolveSelectionHint({
cfg,
accountId,
}: {
cfg: OpenClawConfig;
accountId?: string;
configured: boolean;
}): Promise<string | undefined> {
return (await detectBinary(params.resolveBinaryPath({ cfg })))
return (await detectBinary(params.resolveBinaryPath({ cfg, accountId })))
? params.configuredHint
: params.unconfiguredHint;
},
async resolveQuickstartScore({
cfg,
accountId,
}: {
cfg: OpenClawConfig;
accountId?: string;
configured: boolean;
}): Promise<number | undefined> {
return (await detectBinary(params.resolveBinaryPath({ cfg })))
return (await detectBinary(params.resolveBinaryPath({ cfg, accountId })))
? params.configuredScore
: params.unconfiguredScore;
},