





















@@ -5,20 +5,36 @@ import path from "node:path";
55import JSZip from "jszip";
66import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
77import * as tar from "tar";
8-import { beforeEach, describe, expect, it, vi } from "vitest";
8+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
99import type { ReleaseAsset } from "./install-signal-cli.js";
101011-const { fetchWithSsrFGuardMock } = vi.hoisted(() => ({
12-fetchWithSsrFGuardMock: vi.fn(),
13-}));
11+const { fetchWithSsrFGuardMock, resolveBrewExecutableMock, runPluginCommandWithTimeoutMock } =
12+vi.hoisted(() => ({
13+fetchWithSsrFGuardMock: vi.fn(),
14+resolveBrewExecutableMock: vi.fn(),
15+runPluginCommandWithTimeoutMock: vi.fn(),
16+}));
14171518vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
1619fetchWithSsrFGuard: fetchWithSsrFGuardMock,
1720}));
182122+vi.mock("openclaw/plugin-sdk/setup-tools", async (importOriginal) => {
23+const actual = await importOriginal<typeof import("openclaw/plugin-sdk/setup-tools")>();
24+return {
25+ ...actual,
26+resolveBrewExecutable: resolveBrewExecutableMock,
27+};
28+});
29+30+vi.mock("openclaw/plugin-sdk/run-command", () => ({
31+runPluginCommandWithTimeout: runPluginCommandWithTimeoutMock,
32+}));
33+1934const {
2035 downloadToFile,
2136 extractSignalCliArchive,
37+ installSignalCli,
2238 installSignalCliFromRelease,
2339 looksLikeArchive,
2440 pickAsset,
@@ -74,6 +90,8 @@ async function withTempFile(run: (filePath: string) => Promise<void>) {
74907591beforeEach(() => {
7692fetchWithSsrFGuardMock.mockReset();
93+resolveBrewExecutableMock.mockReset();
94+runPluginCommandWithTimeoutMock.mockReset();
7795});
78967997function requireAsset(asset: ReleaseAsset | undefined, label: string): ReleaseAsset {
@@ -143,6 +161,25 @@ describe("pickAsset", () => {
143161const result = requireAsset(pickAsset(SAMPLE_ASSETS, "darwin", "x64"), "darwin x64");
144162expect(result.name).toContain("macOS-native");
145163});
164+165+it("does not fall back to Linux client archives when macOS assets are absent", () => {
166+const currentUpstreamAssets: ReleaseAsset[] = [
167+{
168+name: "signal-cli-0.14.5-Linux-client.tar.gz",
169+browser_download_url: "https://example.com/linux-client.tar.gz",
170+},
171+{
172+name: "signal-cli-0.14.5-Linux-native.tar.gz",
173+browser_download_url: "https://example.com/linux-native.tar.gz",
174+},
175+{
176+name: "signal-cli-0.14.5.tar.gz",
177+browser_download_url: "https://example.com/jvm.tar.gz",
178+},
179+];
180+181+expect(pickAsset(currentUpstreamAssets, "darwin", "arm64")).toBeUndefined();
182+});
146183});
147184148185describe("win32", () => {
@@ -305,6 +342,46 @@ describe("installSignalCliFromRelease", () => {
305342});
306343});
307344345+describe("installSignalCli", () => {
346+const originalPlatform = process.platform;
347+const originalArch = process.arch;
348+349+function setProcessPlatform(platform: NodeJS.Platform, arch: string) {
350+Object.defineProperty(process, "platform", { configurable: true, value: platform });
351+Object.defineProperty(process, "arch", { configurable: true, value: arch });
352+}
353+354+afterEach(() => {
355+Object.defineProperty(process, "platform", { configurable: true, value: originalPlatform });
356+Object.defineProperty(process, "arch", { configurable: true, value: originalArch });
357+});
358+359+it("uses Homebrew on macOS instead of downloading the first GitHub release archive", async () => {
360+setProcessPlatform("darwin", "arm64");
361+const brewPrefix = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-signal-brew-"));
362+await fs.mkdir(path.join(brewPrefix, "bin"), { recursive: true });
363+await fs.writeFile(path.join(brewPrefix, "bin", "signal-cli"), "");
364+resolveBrewExecutableMock.mockReturnValue("/opt/homebrew/bin/brew");
365+runPluginCommandWithTimeoutMock
366+.mockResolvedValueOnce({ code: 0, stdout: "", stderr: "" })
367+.mockResolvedValueOnce({ code: 0, stdout: `${brewPrefix}\n`, stderr: "" })
368+.mockResolvedValueOnce({ code: 0, stdout: "signal-cli 0.14.5\n", stderr: "" });
369+370+try {
371+const result = await installSignalCli({ log: vi.fn() } as unknown as RuntimeEnv);
372+373+expect(result).toEqual({
374+ok: true,
375+cliPath: path.join(brewPrefix, "bin", "signal-cli"),
376+version: "0.14.5",
377+});
378+expect(fetchWithSsrFGuardMock).not.toHaveBeenCalled();
379+} finally {
380+await fs.rm(brewPrefix, { recursive: true, force: true });
381+}
382+});
383+});
384+308385describe("extractSignalCliArchive", () => {
309386async function withArchiveWorkspace(run: (workDir: string) => Promise<void>) {
310387const workDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-signal-install-"));
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。