mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-16 20:21:35 +00:00
* feat(mxc): add Windows MXC sandbox backend Add the official MXC sandbox plugin package with Windows ProcessContainer execution, plugin-owned MXC SDK dependency packaging, host-backed filesystem bridge support, and configured MXC policy file loading via mxcPolicyPaths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(mxc): preserve Windows binary override paths * fix: remove stray sandbox barrel export Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9ea19539-b8ca-44fb-93bd-b8496e3deb2c * fix(mxc): address sandbox review feedback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(mxc): satisfy test type checks Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(mxc): clarify protected skill enforcement Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * test(mxc): align fail-closed expectations Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(mxc): satisfy extension lint Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(plugin-sdk): narrow fs-safe remove surface Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(mxc): repair rebased CI failures * fix(scripts): declare shrinkwrap override normalizer --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com> Co-authored-by: Dallin Romney <dallinromney@gmail.com>
125 lines
4.3 KiB
TypeScript
125 lines
4.3 KiB
TypeScript
import path from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
|
|
// Mock node:fs and node:os for controlled testing
|
|
const { existsSyncMock, homedirMock } = vi.hoisted(() => ({
|
|
existsSyncMock: vi.fn(),
|
|
homedirMock: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("node:fs", () => ({
|
|
existsSync: existsSyncMock,
|
|
}));
|
|
|
|
vi.mock("node:os", () => ({
|
|
homedir: homedirMock,
|
|
}));
|
|
|
|
import { resolveMxcBinaryPath } from "../src/binary-resolver.js";
|
|
|
|
describe("resolveMxcBinaryPath", () => {
|
|
const originalPath = process.env.PATH;
|
|
|
|
beforeEach(() => {
|
|
existsSyncMock.mockReset();
|
|
homedirMock.mockReturnValue("/home/openclaw");
|
|
process.env.PATH = "";
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env.PATH = originalPath;
|
|
});
|
|
|
|
test("config override returns the override path when file exists", () => {
|
|
existsSyncMock.mockReturnValue(true);
|
|
const result = resolveMxcBinaryPath("C:\\custom\\wxc-exec.exe");
|
|
expect(result).toBe("C:\\custom\\wxc-exec.exe");
|
|
});
|
|
|
|
test("config override returns an absolute path for relative inputs", () => {
|
|
const relativeOverride = path.join("tools", "wxc-exec.exe");
|
|
const absoluteOverride = path.resolve(relativeOverride);
|
|
existsSyncMock.mockImplementation((candidate) => candidate === absoluteOverride);
|
|
|
|
expect(resolveMxcBinaryPath(relativeOverride)).toBe(absoluteOverride);
|
|
});
|
|
|
|
test("config override throws when file does not exist", () => {
|
|
existsSyncMock.mockReturnValue(false);
|
|
expect(() => resolveMxcBinaryPath("C:\\missing\\wxc-exec.exe")).toThrow(
|
|
/not found at configured path/,
|
|
);
|
|
});
|
|
|
|
test("missing binary with no override throws descriptive error", () => {
|
|
existsSyncMock.mockReturnValue(false);
|
|
// Without override, it tries to discover; all paths will fail.
|
|
expect(() => resolveMxcBinaryPath()).toThrow(/wxc-exec\.exe.*not found/);
|
|
});
|
|
|
|
test("ignores project, PATH, and home candidates during discovery", () => {
|
|
const projectCandidate = path.join(process.cwd(), "bin", "wxc-exec.exe");
|
|
const homeCandidate = path.join("/home/openclaw", ".mxc", "wxc-exec.exe");
|
|
const trustedDir = "/trusted-path";
|
|
const pathCandidate = path.join(trustedDir, "wxc-exec.exe");
|
|
process.env.PATH = trustedDir;
|
|
existsSyncMock.mockImplementation((candidate) => {
|
|
const candidatePath = String(candidate);
|
|
return [projectCandidate, homeCandidate, pathCandidate].includes(candidatePath);
|
|
});
|
|
|
|
expect(() => resolveMxcBinaryPath()).toThrow(/wxc-exec\.exe.*not found/u);
|
|
});
|
|
|
|
test("resolves the SDK arch binary", () => {
|
|
const arch = process.arch === "arm64" ? "arm64" : "x64";
|
|
let sdkCandidate: string | undefined;
|
|
existsSyncMock.mockImplementation((candidate) => {
|
|
const candidatePath = String(candidate);
|
|
if (candidatePath.endsWith(`${path.sep}bin${path.sep}${arch}`)) {
|
|
return true;
|
|
}
|
|
if (candidatePath.endsWith(`${path.sep}bin${path.sep}${arch}${path.sep}wxc-exec.exe`)) {
|
|
sdkCandidate = candidatePath;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
expect(resolveMxcBinaryPath()).toBe(sdkCandidate);
|
|
});
|
|
|
|
test("falls back to SDK flat bin when arch bin is absent", () => {
|
|
let sdkCandidate: string | undefined;
|
|
existsSyncMock.mockImplementation((candidate) => {
|
|
const candidatePath = String(candidate);
|
|
if (candidatePath.endsWith(`${path.sep}bin`)) {
|
|
return true;
|
|
}
|
|
if (candidatePath.endsWith(`${path.sep}bin${path.sep}wxc-exec.exe`)) {
|
|
sdkCandidate = candidatePath;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
expect(resolveMxcBinaryPath()).toBe(sdkCandidate);
|
|
});
|
|
|
|
test("ignores empty and relative PATH entries during discovery", () => {
|
|
const relativeCandidate = path.join("relative-path", "wxc-exec.exe");
|
|
const currentDirectoryCandidate = path.join("", "wxc-exec.exe");
|
|
const trustedDir = "/trusted-path";
|
|
const trustedCandidate = path.join(trustedDir, "wxc-exec.exe");
|
|
process.env.PATH = `;relative-path;${trustedDir}`;
|
|
existsSyncMock.mockImplementation((candidate) => {
|
|
const candidatePath = String(candidate);
|
|
return [relativeCandidate, currentDirectoryCandidate, trustedCandidate].includes(
|
|
candidatePath,
|
|
);
|
|
});
|
|
|
|
expect(() => resolveMxcBinaryPath()).toThrow(/wxc-exec\.exe.*not found/u);
|
|
});
|
|
});
|