agentroam 0.2.0-preview.26 → 0.2.0-preview.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -12
- package/bin/desktop-download.mjs +4 -1
- package/bin/node-preflight.mjs +5 -4
- package/dist/device-pairing.js +46 -5
- package/dist/platform-packages.js +9 -2
- package/dist/tunnel/public-readiness.js +2 -1
- package/dist/update/update-command.js +6 -12
- package/dist/update/update-install.js +34 -0
- package/dist/update/update-worker.js +12 -3
- package/install/README.md +5 -5
- package/install/install-agentroam.ps1 +1 -1
- package/install/install-agentroam.sh +2 -2
- package/install/install-manifest.json +15 -0
- package/package.json +8 -8
- package/dist/args.test.js +0 -84
- package/dist/cloudflared/bundled-asset.test.js +0 -91
- package/dist/cloudflared/installer.test.js +0 -95
- package/dist/codex-runtime-manager.test.js +0 -275
- package/dist/desktop-download.test.js +0 -83
- package/dist/device-pairing.test.js +0 -33
- package/dist/install-script-contract.test.js +0 -176
- package/dist/instance-takeover.test.js +0 -121
- package/dist/native-runtime.test.js +0 -40
- package/dist/network.test.js +0 -20
- package/dist/node-preflight.test.js +0 -136
- package/dist/node-runtime-manager.test.js +0 -184
- package/dist/opencode-runtime-manager.test.js +0 -111
- package/dist/pairing.js +0 -2
- package/dist/platform-packages.test.js +0 -86
- package/dist/platform.test.js +0 -18
- package/dist/port.test.js +0 -3
- package/dist/power/sleep-inhibitor.test.js +0 -104
- package/dist/process-supervisor.test.js +0 -42
- package/dist/runtime-manager.test.js +0 -38
- package/dist/service/launcher-cleanup.test.js +0 -81
- package/dist/service/macos-launch-agent.test.js +0 -327
- package/dist/service/runtime-state.test.js +0 -46
- package/dist/service/service-command.test.js +0 -189
- package/dist/service/service-files.test.js +0 -48
- package/dist/service/windows-service-host.test.js +0 -52
- package/dist/service/windows-task-service.test.js +0 -230
- package/dist/tunnel/cloudflare-provider.test.js +0 -45
- package/dist/tunnel/pinggy-provider.test.js +0 -40
- package/dist/tunnel/proxy-settings.test.js +0 -111
- package/dist/tunnel/public-readiness.test.js +0 -104
- package/dist/tunnel/relay-orchestrator.test.js +0 -84
- package/dist/unlock-service.test.js +0 -37
- package/dist/update/update-command.test.js +0 -70
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { mkdir, mkdtemp, readFile, readdir, rm, utimes, writeFile } from "node:fs/promises";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { dirname, resolve } from "node:path";
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { MANAGED_NODE_VERSION, NODE_RUNTIME_ASSETS, detectNodeRuntimeTarget, ensureManagedNode, managedNodeExecutable, managedNodeNpmCli, } from "./node-runtime-manager.js";
|
|
6
|
-
const temporaryDirectories = [];
|
|
7
|
-
afterEach(async () => {
|
|
8
|
-
await Promise.all(temporaryDirectories.splice(0).map((path) => rm(path, { recursive: true, force: true })));
|
|
9
|
-
});
|
|
10
|
-
describe("managed Node.js runtime", () => {
|
|
11
|
-
it("uses the exact official manifest", () => {
|
|
12
|
-
expect(MANAGED_NODE_VERSION).toBe("22.22.0");
|
|
13
|
-
expect(NODE_RUNTIME_ASSETS["darwin-arm64"]).toEqual({
|
|
14
|
-
archive: "node-v22.22.0-darwin-arm64.tar.xz",
|
|
15
|
-
sha256: "2bd596bbfc4a275ceb8721a5954ee97daea5ebe673e96a185ebd732f6fb023ac",
|
|
16
|
-
archiveRoot: "node-v22.22.0-darwin-arm64",
|
|
17
|
-
});
|
|
18
|
-
expect(NODE_RUNTIME_ASSETS["windows-amd64"].archive).toBe("node-v22.22.0-win-x64.zip");
|
|
19
|
-
});
|
|
20
|
-
it("selects only supported managed targets", () => {
|
|
21
|
-
expect(detectNodeRuntimeTarget("darwin", "arm64")).toBe("darwin-arm64");
|
|
22
|
-
expect(detectNodeRuntimeTarget("win32", "x64")).toBe("windows-amd64");
|
|
23
|
-
expect(() => detectNodeRuntimeTarget("darwin", "x64")).toThrow("unsupported");
|
|
24
|
-
expect(() => detectNodeRuntimeTarget("linux", "x64")).toThrow("unsupported");
|
|
25
|
-
});
|
|
26
|
-
it("reuses a validated cached runtime", async () => {
|
|
27
|
-
const dataDir = await temporaryRoot();
|
|
28
|
-
await fakeRuntime(dataDir, "darwin-arm64");
|
|
29
|
-
const download = vi.fn();
|
|
30
|
-
const resolution = await ensureManagedNode({
|
|
31
|
-
dataDir,
|
|
32
|
-
target: "darwin-arm64",
|
|
33
|
-
dependencies: { download, run: versionRunner() },
|
|
34
|
-
});
|
|
35
|
-
expect(resolution.executable).toBe(managedNodeExecutable(dataDir, "darwin-arm64"));
|
|
36
|
-
expect(resolution.npmCli).toBe(managedNodeNpmCli(dataDir, "darwin-arm64"));
|
|
37
|
-
expect(download).not.toHaveBeenCalled();
|
|
38
|
-
});
|
|
39
|
-
it("downloads, extracts, validates, and atomically activates a runtime", async () => {
|
|
40
|
-
const dataDir = await temporaryRoot();
|
|
41
|
-
const download = vi.fn(async (_url, destination) => writeFile(destination, "archive"));
|
|
42
|
-
const run = extractionRunner("darwin-arm64");
|
|
43
|
-
const resolution = await ensureManagedNode({
|
|
44
|
-
dataDir,
|
|
45
|
-
target: "darwin-arm64",
|
|
46
|
-
dependencies: {
|
|
47
|
-
download,
|
|
48
|
-
sha256: async () => NODE_RUNTIME_ASSETS["darwin-arm64"].sha256,
|
|
49
|
-
run,
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
expect(download).toHaveBeenCalledWith("https://nodejs.org/dist/v22.22.0/node-v22.22.0-darwin-arm64.tar.xz", expect.any(String), 10 * 60_000);
|
|
53
|
-
expect(resolution.version).toBe(MANAGED_NODE_VERSION);
|
|
54
|
-
expect(await readFile(resolution.npmCli, "utf8")).toBe("npm");
|
|
55
|
-
});
|
|
56
|
-
it("rejects a checksum mismatch and cleans temporary state", async () => {
|
|
57
|
-
const dataDir = await temporaryRoot();
|
|
58
|
-
await expect(ensureManagedNode({
|
|
59
|
-
dataDir,
|
|
60
|
-
target: "darwin-arm64",
|
|
61
|
-
dependencies: {
|
|
62
|
-
download: async (_url, destination) => writeFile(destination, "bad"),
|
|
63
|
-
sha256: async () => "bad-checksum",
|
|
64
|
-
},
|
|
65
|
-
})).rejects.toThrow("checksum mismatch");
|
|
66
|
-
expect(await readdir(resolve(dataDir, "runtimes", "node"))).toEqual([]);
|
|
67
|
-
});
|
|
68
|
-
it("rejects an unexpected archive root", async () => {
|
|
69
|
-
const dataDir = await temporaryRoot();
|
|
70
|
-
await expect(ensureManagedNode({
|
|
71
|
-
dataDir,
|
|
72
|
-
target: "darwin-arm64",
|
|
73
|
-
dependencies: {
|
|
74
|
-
download: async (_url, destination) => writeFile(destination, "archive"),
|
|
75
|
-
sha256: async () => NODE_RUNTIME_ASSETS["darwin-arm64"].sha256,
|
|
76
|
-
run: async (command, args) => {
|
|
77
|
-
if (command === "/usr/bin/tar")
|
|
78
|
-
await mkdir(resolve(args[args.indexOf("-C") + 1], "wrong-root"));
|
|
79
|
-
return { stdout: "", stderr: "" };
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
})).rejects.toThrow("unexpected Node.js archive root");
|
|
83
|
-
});
|
|
84
|
-
it("rejects a runtime that reports the wrong version", async () => {
|
|
85
|
-
const dataDir = await temporaryRoot();
|
|
86
|
-
const run = extractionRunner("darwin-arm64", "v22.21.0");
|
|
87
|
-
await expect(ensureManagedNode({
|
|
88
|
-
dataDir,
|
|
89
|
-
target: "darwin-arm64",
|
|
90
|
-
dependencies: {
|
|
91
|
-
download: async (_url, destination) => writeFile(destination, "archive"),
|
|
92
|
-
sha256: async () => NODE_RUNTIME_ASSETS["darwin-arm64"].sha256,
|
|
93
|
-
run,
|
|
94
|
-
},
|
|
95
|
-
})).rejects.toThrow("validation failed");
|
|
96
|
-
});
|
|
97
|
-
it("serializes concurrent installs", async () => {
|
|
98
|
-
const dataDir = await temporaryRoot();
|
|
99
|
-
let downloads = 0;
|
|
100
|
-
const dependencies = {
|
|
101
|
-
download: async (_url, destination) => {
|
|
102
|
-
downloads += 1;
|
|
103
|
-
await new Promise((resolveDelay) => setTimeout(resolveDelay, 30));
|
|
104
|
-
await writeFile(destination, "archive");
|
|
105
|
-
},
|
|
106
|
-
sha256: async () => NODE_RUNTIME_ASSETS["darwin-arm64"].sha256,
|
|
107
|
-
run: extractionRunner("darwin-arm64"),
|
|
108
|
-
};
|
|
109
|
-
const [first, second] = await Promise.all([
|
|
110
|
-
ensureManagedNode({ dataDir, target: "darwin-arm64", dependencies }),
|
|
111
|
-
ensureManagedNode({ dataDir, target: "darwin-arm64", dependencies }),
|
|
112
|
-
]);
|
|
113
|
-
expect(downloads).toBe(1);
|
|
114
|
-
expect(second.executable).toBe(first.executable);
|
|
115
|
-
});
|
|
116
|
-
it("recovers a stale lock", async () => {
|
|
117
|
-
const dataDir = await temporaryRoot();
|
|
118
|
-
const lockPath = resolve(dataDir, "runtimes", "node", `${MANAGED_NODE_VERSION}.lock`);
|
|
119
|
-
await mkdir(dirname(lockPath), { recursive: true });
|
|
120
|
-
await writeFile(lockPath, "stale");
|
|
121
|
-
await utimes(lockPath, new Date(0), new Date(0));
|
|
122
|
-
const resolution = await ensureManagedNode({
|
|
123
|
-
dataDir,
|
|
124
|
-
target: "darwin-arm64",
|
|
125
|
-
staleLockMs: 1,
|
|
126
|
-
dependencies: {
|
|
127
|
-
download: async (_url, destination) => writeFile(destination, "archive"),
|
|
128
|
-
sha256: async () => NODE_RUNTIME_ASSETS["darwin-arm64"].sha256,
|
|
129
|
-
run: extractionRunner("darwin-arm64"),
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
expect(resolution.version).toBe(MANAGED_NODE_VERSION);
|
|
133
|
-
});
|
|
134
|
-
it("uses the Windows executable and npm layout", async () => {
|
|
135
|
-
const dataDir = await temporaryRoot();
|
|
136
|
-
await fakeRuntime(dataDir, "windows-amd64");
|
|
137
|
-
await expect(ensureManagedNode({
|
|
138
|
-
dataDir,
|
|
139
|
-
target: "windows-amd64",
|
|
140
|
-
dependencies: { run: versionRunner() },
|
|
141
|
-
})).resolves.toMatchObject({
|
|
142
|
-
executable: managedNodeExecutable(dataDir, "windows-amd64"),
|
|
143
|
-
npmCli: managedNodeNpmCli(dataDir, "windows-amd64"),
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
async function temporaryRoot() {
|
|
148
|
-
const root = await mkdtemp(resolve(tmpdir(), "agentroam-node-runtime-"));
|
|
149
|
-
temporaryDirectories.push(root);
|
|
150
|
-
return root;
|
|
151
|
-
}
|
|
152
|
-
async function fakeRuntime(dataDir, target) {
|
|
153
|
-
const executable = managedNodeExecutable(dataDir, target);
|
|
154
|
-
const npmCli = managedNodeNpmCli(dataDir, target);
|
|
155
|
-
await mkdir(dirname(executable), { recursive: true });
|
|
156
|
-
await mkdir(dirname(npmCli), { recursive: true });
|
|
157
|
-
await writeFile(executable, "node");
|
|
158
|
-
await writeFile(npmCli, "npm");
|
|
159
|
-
}
|
|
160
|
-
function versionRunner(version = `v${MANAGED_NODE_VERSION}`) {
|
|
161
|
-
return async () => ({ stdout: `${version}\n`, stderr: "" });
|
|
162
|
-
}
|
|
163
|
-
function extractionRunner(target, version = `v${MANAGED_NODE_VERSION}`) {
|
|
164
|
-
return vi.fn(async (command, args) => {
|
|
165
|
-
if (command === "/usr/bin/tar" || command === "powershell.exe") {
|
|
166
|
-
const extractRoot = command === "/usr/bin/tar"
|
|
167
|
-
? args[args.indexOf("-C") + 1]
|
|
168
|
-
: args.at(-1);
|
|
169
|
-
const archiveRoot = resolve(extractRoot, NODE_RUNTIME_ASSETS[target].archiveRoot);
|
|
170
|
-
const executable = target === "windows-amd64"
|
|
171
|
-
? resolve(archiveRoot, "node.exe")
|
|
172
|
-
: resolve(archiveRoot, "bin", "node");
|
|
173
|
-
const npmCli = target === "windows-amd64"
|
|
174
|
-
? resolve(archiveRoot, "node_modules", "npm", "bin", "npm-cli.js")
|
|
175
|
-
: resolve(archiveRoot, "lib", "node_modules", "npm", "bin", "npm-cli.js");
|
|
176
|
-
await mkdir(dirname(executable), { recursive: true });
|
|
177
|
-
await mkdir(dirname(npmCli), { recursive: true });
|
|
178
|
-
await writeFile(executable, "node");
|
|
179
|
-
await writeFile(npmCli, "npm");
|
|
180
|
-
return { stdout: "", stderr: "" };
|
|
181
|
-
}
|
|
182
|
-
return { stdout: `${version}\n`, stderr: "" };
|
|
183
|
-
});
|
|
184
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { chmod, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { OPENCODE_RUNTIME_VERSION, OPENCODE_MINIMUM_VERSION, managedOpenCodeBinaryCandidates, parseOpenCodeVersion, resolveOpenCodeRuntime, } from "./opencode-runtime-manager.js";
|
|
6
|
-
const roots = [];
|
|
7
|
-
afterEach(async () => {
|
|
8
|
-
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
|
9
|
-
});
|
|
10
|
-
async function executable(name = "opencode") {
|
|
11
|
-
const root = await mkdtemp(join(tmpdir(), "agentroam-opencode-"));
|
|
12
|
-
roots.push(root);
|
|
13
|
-
const path = join(root, name);
|
|
14
|
-
await writeFile(path, "#!/bin/sh\nexit 0\n");
|
|
15
|
-
await chmod(path, 0o755);
|
|
16
|
-
return { root, path };
|
|
17
|
-
}
|
|
18
|
-
describe("resolveOpenCodeRuntime", () => {
|
|
19
|
-
it("prefers a compatible absolute override", async () => {
|
|
20
|
-
const binary = await executable();
|
|
21
|
-
const run = vi.fn(async (_command, args) => ({
|
|
22
|
-
stdout: args[0] === "--version" ? `opencode ${OPENCODE_RUNTIME_VERSION}` : "help",
|
|
23
|
-
stderr: "",
|
|
24
|
-
}));
|
|
25
|
-
await expect(resolveOpenCodeRuntime({
|
|
26
|
-
dataDir: binary.root,
|
|
27
|
-
target: "darwin-arm64",
|
|
28
|
-
environment: { AGENT_OPENCODE_BIN: binary.path, PATH: "" },
|
|
29
|
-
platform: "darwin",
|
|
30
|
-
dependencies: { run },
|
|
31
|
-
})).resolves.toEqual({ executable: binary.path, version: OPENCODE_RUNTIME_VERSION, source: "explicit" });
|
|
32
|
-
expect(run).toHaveBeenCalledWith(binary.path, ["serve", "--help"], 5_000);
|
|
33
|
-
});
|
|
34
|
-
it.each([OPENCODE_MINIMUM_VERSION, "1.18.30", "1.18.100", "1.19.0", "2.0.0"])("reuses PATH version %s without installation", async (version) => {
|
|
35
|
-
const binary = await executable();
|
|
36
|
-
const run = vi.fn(async (_command, args) => ({
|
|
37
|
-
stdout: args[0] === "--version" ? version : "serve help", stderr: "",
|
|
38
|
-
}));
|
|
39
|
-
await expect(resolveOpenCodeRuntime({
|
|
40
|
-
dataDir: binary.root, target: "darwin-arm64", platform: "darwin",
|
|
41
|
-
environment: { PATH: binary.root }, dependencies: { run },
|
|
42
|
-
})).resolves.toEqual({ executable: binary.path, version, source: "global" });
|
|
43
|
-
expect(run.mock.calls.map(([, args]) => args)).toEqual([["--version"], ["serve", "--help"]]);
|
|
44
|
-
});
|
|
45
|
-
it.each(["1.18.26", "1.18.9", "1.17.99", "0.99.99", "dev", "1.19.0-beta.1", "01.19.0"])("rejects unsupported explicit version %s", async (version) => {
|
|
46
|
-
const binary = await executable();
|
|
47
|
-
const run = vi.fn(async () => ({ stdout: version, stderr: "" }));
|
|
48
|
-
await expect(resolveOpenCodeRuntime({
|
|
49
|
-
dataDir: binary.root, target: "darwin-arm64", platform: "darwin",
|
|
50
|
-
environment: { AGENT_OPENCODE_BIN: binary.path, PATH: "" }, dependencies: { run },
|
|
51
|
-
})).rejects.toThrow(/requires >=|Unable to parse OpenCode version/);
|
|
52
|
-
expect(run).toHaveBeenCalledTimes(1);
|
|
53
|
-
});
|
|
54
|
-
it("rejects a newer executable without serve support", async () => {
|
|
55
|
-
const binary = await executable();
|
|
56
|
-
await expect(resolveOpenCodeRuntime({
|
|
57
|
-
dataDir: binary.root, target: "darwin-arm64", platform: "darwin",
|
|
58
|
-
environment: { AGENT_OPENCODE_BIN: binary.path, PATH: "" },
|
|
59
|
-
dependencies: { run: async (_command, args) => {
|
|
60
|
-
if (args[0] === "serve")
|
|
61
|
-
throw new Error("serve unavailable");
|
|
62
|
-
return { stdout: "1.18.30", stderr: "" };
|
|
63
|
-
} },
|
|
64
|
-
})).rejects.toThrow("serve unavailable");
|
|
65
|
-
});
|
|
66
|
-
it("rejects relative explicit overrides", async () => {
|
|
67
|
-
await expect(resolveOpenCodeRuntime({
|
|
68
|
-
dataDir: "/data",
|
|
69
|
-
target: "darwin-arm64",
|
|
70
|
-
environment: { AGENT_OPENCODE_BIN: "opencode", PATH: "" },
|
|
71
|
-
platform: "darwin",
|
|
72
|
-
})).rejects.toThrow("absolute path");
|
|
73
|
-
});
|
|
74
|
-
it("uses a compatible PATH executable", async () => {
|
|
75
|
-
const binary = await executable();
|
|
76
|
-
await expect(resolveOpenCodeRuntime({
|
|
77
|
-
dataDir: join(binary.root, "data"),
|
|
78
|
-
target: "darwin-arm64",
|
|
79
|
-
environment: { PATH: binary.root },
|
|
80
|
-
platform: "darwin",
|
|
81
|
-
dependencies: { run: async () => ({ stdout: OPENCODE_RUNTIME_VERSION, stderr: "" }) },
|
|
82
|
-
})).resolves.toMatchObject({ executable: binary.path, source: "global" });
|
|
83
|
-
});
|
|
84
|
-
it("reuses a validated managed installation", async () => {
|
|
85
|
-
const root = await mkdtemp(join(tmpdir(), "agentroam-opencode-managed-"));
|
|
86
|
-
roots.push(root);
|
|
87
|
-
const managedRoot = join(root, "runtimes", "opencode", OPENCODE_RUNTIME_VERSION);
|
|
88
|
-
const candidate = managedOpenCodeBinaryCandidates(managedRoot, "darwin")[0];
|
|
89
|
-
await mkdir(join(candidate, ".."), { recursive: true });
|
|
90
|
-
await writeFile(candidate, "#!/bin/sh\nexit 0\n");
|
|
91
|
-
await chmod(candidate, 0o755);
|
|
92
|
-
await expect(resolveOpenCodeRuntime({
|
|
93
|
-
dataDir: root,
|
|
94
|
-
target: "darwin-arm64",
|
|
95
|
-
environment: { PATH: "" },
|
|
96
|
-
platform: "darwin",
|
|
97
|
-
dependencies: { run: async () => ({ stdout: OPENCODE_RUNTIME_VERSION, stderr: "" }) },
|
|
98
|
-
})).resolves.toEqual({ executable: candidate, version: OPENCODE_RUNTIME_VERSION, source: "managed" });
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
describe("OpenCode runtime helpers", () => {
|
|
102
|
-
it("parses only semantic versions", () => {
|
|
103
|
-
expect(parseOpenCodeVersion("opencode 1.18.27")).toBe("1.18.27");
|
|
104
|
-
expect(parseOpenCodeVersion("dev")).toBeNull();
|
|
105
|
-
});
|
|
106
|
-
it("keeps managed binary candidates under the runtime root", () => {
|
|
107
|
-
expect(managedOpenCodeBinaryCandidates("/data/runtime", "darwin")[0])
|
|
108
|
-
.toBe("/data/runtime/node_modules/opencode-ai/bin/opencode.exe");
|
|
109
|
-
expect(managedOpenCodeBinaryCandidates("C:\\runtime", "win32").some((path) => path.endsWith("opencode.cmd"))).toBe(true);
|
|
110
|
-
});
|
|
111
|
-
});
|
package/dist/pairing.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
-
export function createPairingSecret(now = Date.now()) { const token = randomBytes(32).toString("base64url"); return { token, hashHex: createHash("sha256").update(token).digest("hex"), expiresAt: new Date(now + 5 * 60 * 1000).toISOString() }; }
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { dirname, resolve } from "node:path";
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
import { PLATFORM_DEPENDENCY_VERSIONS, requirePlatformPackages, resolvePlatformRuntime, resolvePlatformTui, } from "./platform-packages.js";
|
|
6
|
-
describe("platform packages", () => {
|
|
7
|
-
it("maps macOS arm64 and Windows x64 package names", () => {
|
|
8
|
-
expect(requirePlatformPackages("darwin-arm64")).toEqual({
|
|
9
|
-
runtime: "agentroam-runtime-darwin-arm64",
|
|
10
|
-
cloudflared: "agentroam-cloudflared-darwin-arm64",
|
|
11
|
-
tui: "agentroam-tui-darwin-arm64",
|
|
12
|
-
});
|
|
13
|
-
expect(requirePlatformPackages("windows-amd64")).toEqual({
|
|
14
|
-
runtime: "agentroam-runtime-win32-x64",
|
|
15
|
-
cloudflared: "agentroam-cloudflared-win32-x64",
|
|
16
|
-
tui: "@caoqu/agentroam-tui-win32-x64",
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
it("rejects targets without a published package set", () => {
|
|
20
|
-
expect(() => requirePlatformPackages("darwin-amd64")).toThrow("unavailable for darwin-amd64");
|
|
21
|
-
});
|
|
22
|
-
it("resolves a matching runtime manifest", () => {
|
|
23
|
-
const root = mkdtempSync(resolve(tmpdir(), "agentroam-runtime-resolver-"));
|
|
24
|
-
const manifestPath = resolve(root, "manifest.json");
|
|
25
|
-
const runtimePackageJson = resolve(root, "runtime/package.json");
|
|
26
|
-
mkdirSync(dirname(runtimePackageJson), { recursive: true });
|
|
27
|
-
writeFileSync(runtimePackageJson, "{}");
|
|
28
|
-
writeFileSync(manifestPath, JSON.stringify({
|
|
29
|
-
packageVersion: PLATFORM_DEPENDENCY_VERSIONS["agentroam-runtime-win32-x64"],
|
|
30
|
-
target: "windows-amd64",
|
|
31
|
-
schemaVersion: 2,
|
|
32
|
-
minimumNodeVersion: "22.22.0",
|
|
33
|
-
nativeFiles: { "node_modules/example.node": "a".repeat(64) },
|
|
34
|
-
}));
|
|
35
|
-
const resolved = resolvePlatformRuntime("windows-amd64", fakeRequire({
|
|
36
|
-
"/manifest.json": manifestPath,
|
|
37
|
-
"/runtime": runtimePackageJson,
|
|
38
|
-
}));
|
|
39
|
-
expect(resolved.runtimeRoot).toBe(dirname(runtimePackageJson));
|
|
40
|
-
expect(resolved.manifest.target).toBe("windows-amd64");
|
|
41
|
-
});
|
|
42
|
-
it("rejects mismatched runtime versions and targets", () => {
|
|
43
|
-
const root = mkdtempSync(resolve(tmpdir(), "agentroam-runtime-invalid-"));
|
|
44
|
-
const manifestPath = resolve(root, "manifest.json");
|
|
45
|
-
const runtimePackageJson = resolve(root, "runtime/package.json");
|
|
46
|
-
mkdirSync(dirname(runtimePackageJson), { recursive: true });
|
|
47
|
-
writeFileSync(runtimePackageJson, "{}");
|
|
48
|
-
writeFileSync(manifestPath, JSON.stringify({
|
|
49
|
-
packageVersion: "0.0.0",
|
|
50
|
-
target: "darwin-arm64",
|
|
51
|
-
schemaVersion: 2,
|
|
52
|
-
minimumNodeVersion: "22.22.0",
|
|
53
|
-
nativeFiles: {},
|
|
54
|
-
}));
|
|
55
|
-
expect(() => resolvePlatformRuntime("windows-amd64", fakeRequire({
|
|
56
|
-
"/manifest.json": manifestPath,
|
|
57
|
-
"/runtime": runtimePackageJson,
|
|
58
|
-
}))).toThrow("invalid AgentRoam runtime manifest");
|
|
59
|
-
});
|
|
60
|
-
it("resolves the target TUI entry", () => {
|
|
61
|
-
const entry = resolvePlatformTui("windows-amd64", fakeRequire({ "/entry": "C:\\agentroam\\agent-tui.js" }));
|
|
62
|
-
expect(entry).toBe("C:\\agentroam\\agent-tui.js");
|
|
63
|
-
});
|
|
64
|
-
it("rejects legacy ABI manifests even when their release version matches", () => {
|
|
65
|
-
const root = mkdtempSync(resolve(tmpdir(), "agentroam-runtime-legacy-"));
|
|
66
|
-
const manifestPath = resolve(root, "manifest.json");
|
|
67
|
-
writeFileSync(manifestPath, JSON.stringify({
|
|
68
|
-
packageVersion: PLATFORM_DEPENDENCY_VERSIONS["agentroam-runtime-darwin-arm64"], target: "darwin-arm64",
|
|
69
|
-
nodeMajor: 22, nodeModuleAbi: 127, nativeFiles: {},
|
|
70
|
-
}));
|
|
71
|
-
expect(() => resolvePlatformRuntime("darwin-arm64", fakeRequire({
|
|
72
|
-
"/manifest.json": manifestPath, "/runtime": resolve(root, "runtime/package.json"),
|
|
73
|
-
}))).toThrow("invalid AgentRoam runtime manifest");
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
function fakeRequire(suffixes) {
|
|
77
|
-
return {
|
|
78
|
-
resolve(id) {
|
|
79
|
-
for (const [suffix, value] of Object.entries(suffixes)) {
|
|
80
|
-
if (id.endsWith(suffix))
|
|
81
|
-
return value;
|
|
82
|
-
}
|
|
83
|
-
throw Object.assign(new Error(`missing ${id}`), { code: "MODULE_NOT_FOUND" });
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
}
|
package/dist/platform.test.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { detectPlatform, detectPlatformTarget } from "./platform.js";
|
|
3
|
-
describe("detectPlatform", () => {
|
|
4
|
-
it("maps supported targets", () => {
|
|
5
|
-
expect(detectPlatform("darwin", "arm64", "22.22.0")).toBe("darwin-arm64");
|
|
6
|
-
expect(detectPlatform("win32", "x64", "25.8.0")).toBe("windows-amd64");
|
|
7
|
-
});
|
|
8
|
-
it("detects a target before Node version validation", () => {
|
|
9
|
-
expect(detectPlatformTarget("darwin", "arm64")).toBe("darwin-arm64");
|
|
10
|
-
expect(detectPlatformTarget("win32", "x64")).toBe("windows-amd64");
|
|
11
|
-
});
|
|
12
|
-
it("rejects unsupported versions", () => {
|
|
13
|
-
expect(() => detectPlatform("darwin", "arm64", "22.21.9")).toThrow("Node.js >=22.22.0");
|
|
14
|
-
});
|
|
15
|
-
it("rejects unsupported platforms", () => {
|
|
16
|
-
expect(() => detectPlatformTarget("linux", "x64")).toThrow("unsupported platform");
|
|
17
|
-
});
|
|
18
|
-
});
|
package/dist/port.test.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
import { PassThrough } from "node:stream";
|
|
3
|
-
import { describe, expect, it, vi } from "vitest";
|
|
4
|
-
import { acquireSleepInhibitor, buildWindowsSleepInhibitorScript } from "./sleep-inhibitor.js";
|
|
5
|
-
function fakeChild(withPipes = false) {
|
|
6
|
-
const child = new EventEmitter();
|
|
7
|
-
Object.assign(child, {
|
|
8
|
-
pid: 9001,
|
|
9
|
-
exitCode: null,
|
|
10
|
-
signalCode: null,
|
|
11
|
-
stdout: withPipes ? new PassThrough() : null,
|
|
12
|
-
stderr: withPipes ? new PassThrough() : null,
|
|
13
|
-
kill: vi.fn((signal) => {
|
|
14
|
-
Object.assign(child, { signalCode: signal });
|
|
15
|
-
queueMicrotask(() => child.emit("exit", null, signal));
|
|
16
|
-
return true;
|
|
17
|
-
}),
|
|
18
|
-
});
|
|
19
|
-
return child;
|
|
20
|
-
}
|
|
21
|
-
describe("sleep inhibitor", () => {
|
|
22
|
-
it("uses caffeinate to prevent idle sleep without preventing display sleep", async () => {
|
|
23
|
-
const child = fakeChild();
|
|
24
|
-
const spawnProcess = vi.fn(() => {
|
|
25
|
-
queueMicrotask(() => child.emit("spawn"));
|
|
26
|
-
return child;
|
|
27
|
-
});
|
|
28
|
-
const inhibitor = await acquireSleepInhibitor({
|
|
29
|
-
platform: "darwin",
|
|
30
|
-
pid: 4321,
|
|
31
|
-
spawnProcess: spawnProcess,
|
|
32
|
-
readyTimeoutMs: 50,
|
|
33
|
-
stopTimeoutMs: 50,
|
|
34
|
-
});
|
|
35
|
-
expect(spawnProcess).toHaveBeenCalledWith("/usr/bin/caffeinate", ["-i", "-w", "4321"], {
|
|
36
|
-
stdio: "ignore",
|
|
37
|
-
windowsHide: true,
|
|
38
|
-
});
|
|
39
|
-
const calls = spawnProcess.mock.calls;
|
|
40
|
-
expect(calls[0][1]).not.toContain("-d");
|
|
41
|
-
await inhibitor.release();
|
|
42
|
-
await inhibitor.release();
|
|
43
|
-
expect(child.kill).toHaveBeenCalledTimes(1);
|
|
44
|
-
});
|
|
45
|
-
it("uses only the Windows system-required execution state", async () => {
|
|
46
|
-
const child = fakeChild(true);
|
|
47
|
-
const spawnProcess = vi.fn(() => {
|
|
48
|
-
queueMicrotask(() => {
|
|
49
|
-
child.emit("spawn");
|
|
50
|
-
child.stdout.write("AGENTROAM_SLEEP_INHIBITOR_READY\n");
|
|
51
|
-
});
|
|
52
|
-
return child;
|
|
53
|
-
});
|
|
54
|
-
const inhibitor = await acquireSleepInhibitor({
|
|
55
|
-
platform: "win32",
|
|
56
|
-
pid: 7654,
|
|
57
|
-
spawnProcess: spawnProcess,
|
|
58
|
-
readyTimeoutMs: 50,
|
|
59
|
-
stopTimeoutMs: 50,
|
|
60
|
-
});
|
|
61
|
-
const calls = spawnProcess.mock.calls;
|
|
62
|
-
const args = calls[0][1];
|
|
63
|
-
const script = Buffer.from(args[args.indexOf("-EncodedCommand") + 1], "base64").toString("utf16le");
|
|
64
|
-
expect(script).toContain("SetThreadExecutionState");
|
|
65
|
-
expect(script).toContain("$systemRequired = [uint32]1");
|
|
66
|
-
expect(script).toContain("Wait-Process -Id 7654");
|
|
67
|
-
expect(script).not.toContain("ES_DISPLAY_REQUIRED");
|
|
68
|
-
await inhibitor.release();
|
|
69
|
-
});
|
|
70
|
-
it("reports a helper that stops after acquisition", async () => {
|
|
71
|
-
const child = fakeChild();
|
|
72
|
-
const spawnProcess = vi.fn(() => {
|
|
73
|
-
queueMicrotask(() => child.emit("spawn"));
|
|
74
|
-
return child;
|
|
75
|
-
});
|
|
76
|
-
const inhibitor = await acquireSleepInhibitor({
|
|
77
|
-
platform: "darwin",
|
|
78
|
-
spawnProcess: spawnProcess,
|
|
79
|
-
readyTimeoutMs: 50,
|
|
80
|
-
});
|
|
81
|
-
Object.assign(child, { exitCode: 3 });
|
|
82
|
-
child.emit("exit", 3, null);
|
|
83
|
-
await expect(inhibitor.lost).resolves.toMatchObject({ message: expect.stringContaining("stopped unexpectedly") });
|
|
84
|
-
});
|
|
85
|
-
it("rejects unsupported platforms and early helper failures", async () => {
|
|
86
|
-
await expect(acquireSleepInhibitor({ platform: "linux" })).rejects.toThrow("unsupported on linux");
|
|
87
|
-
const child = fakeChild();
|
|
88
|
-
const spawnProcess = vi.fn(() => {
|
|
89
|
-
queueMicrotask(() => child.emit("error", new Error("missing caffeinate")));
|
|
90
|
-
return child;
|
|
91
|
-
});
|
|
92
|
-
await expect(acquireSleepInhibitor({
|
|
93
|
-
platform: "darwin",
|
|
94
|
-
spawnProcess: spawnProcess,
|
|
95
|
-
readyTimeoutMs: 50,
|
|
96
|
-
})).rejects.toThrow("missing caffeinate");
|
|
97
|
-
});
|
|
98
|
-
it("builds a Windows helper that clears its assertion in finally", () => {
|
|
99
|
-
const script = buildWindowsSleepInhibitorScript(123);
|
|
100
|
-
expect(script).toContain("$continuous -bor $systemRequired");
|
|
101
|
-
expect(script).toContain("finally");
|
|
102
|
-
expect(script).toContain("SetThreadExecutionState($continuous)");
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { ProcessSupervisor } from "./process-supervisor.js";
|
|
4
|
-
function fakeChild(pid = 42) {
|
|
5
|
-
const child = new EventEmitter();
|
|
6
|
-
Object.assign(child, { pid, exitCode: null, kill: vi.fn(() => true) });
|
|
7
|
-
return child;
|
|
8
|
-
}
|
|
9
|
-
describe("ProcessSupervisor", () => {
|
|
10
|
-
it("falls back to child.kill when taskkill cannot start", async () => {
|
|
11
|
-
const child = fakeChild();
|
|
12
|
-
const spawnProcess = vi.fn(() => {
|
|
13
|
-
const killer = new EventEmitter();
|
|
14
|
-
queueMicrotask(() => killer.emit("error", new Error("ENOENT")));
|
|
15
|
-
return killer;
|
|
16
|
-
});
|
|
17
|
-
const supervisor = new ProcessSupervisor("win32", spawnProcess);
|
|
18
|
-
const stopping = supervisor.stop(child, 20);
|
|
19
|
-
queueMicrotask(() => {
|
|
20
|
-
Object.assign(child, { exitCode: 0 });
|
|
21
|
-
child.emit("exit", 0, null);
|
|
22
|
-
});
|
|
23
|
-
await stopping;
|
|
24
|
-
expect(spawnProcess).toHaveBeenCalledWith("taskkill", ["/PID", "42", "/T", "/F"], { stdio: "ignore" });
|
|
25
|
-
expect(child.kill).toHaveBeenCalledWith("SIGTERM");
|
|
26
|
-
});
|
|
27
|
-
it("uses taskkill for a running Windows process tree", async () => {
|
|
28
|
-
const child = fakeChild();
|
|
29
|
-
const spawnProcess = vi.fn(() => {
|
|
30
|
-
const killer = new EventEmitter();
|
|
31
|
-
queueMicrotask(() => {
|
|
32
|
-
killer.emit("exit", 0, null);
|
|
33
|
-
Object.assign(child, { exitCode: 0 });
|
|
34
|
-
child.emit("exit", 0, null);
|
|
35
|
-
});
|
|
36
|
-
return killer;
|
|
37
|
-
});
|
|
38
|
-
const supervisor = new ProcessSupervisor("win32", spawnProcess);
|
|
39
|
-
await supervisor.stop(child, 20);
|
|
40
|
-
expect(child.kill).not.toHaveBeenCalled();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { prepareCodexRuntimeEnvironment, prepareOpenCodeRuntimeEnvironment } from "./runtime-manager.js";
|
|
3
|
-
describe("prepareCodexRuntimeEnvironment", () => {
|
|
4
|
-
it("injects the resolved absolute Codex executable", async () => {
|
|
5
|
-
const report = vi.fn();
|
|
6
|
-
const environment = await prepareCodexRuntimeEnvironment({ PATH: "/bin" }, { dataDir: "/data", target: "windows-amd64" }, async () => ({
|
|
7
|
-
executable: "D:\\AgentRoam\\codex.exe",
|
|
8
|
-
version: "0.153.0",
|
|
9
|
-
source: "managed",
|
|
10
|
-
}), report);
|
|
11
|
-
expect(environment.AGENT_CODEX_BIN).toBe("D:\\AgentRoam\\codex.exe");
|
|
12
|
-
expect(environment.AGENT_CODEX_RUNTIME_ERROR).toBeUndefined();
|
|
13
|
-
expect(report).toHaveBeenCalledWith(expect.stringContaining("managed"));
|
|
14
|
-
});
|
|
15
|
-
it("removes an unusable inherited override and keeps startup available", async () => {
|
|
16
|
-
const report = vi.fn();
|
|
17
|
-
const environment = await prepareCodexRuntimeEnvironment({ PATH: "/bin", AGENT_CODEX_BIN: "/missing/codex" }, { dataDir: "/data", target: "darwin-arm64" }, async () => { throw new Error("download failed"); }, report);
|
|
18
|
-
expect(environment.AGENT_CODEX_BIN).toBeUndefined();
|
|
19
|
-
expect(environment.AGENT_CODEX_RUNTIME_ERROR).toBe("download failed");
|
|
20
|
-
expect(environment.PATH).toBe("/bin");
|
|
21
|
-
expect(report).toHaveBeenLastCalledWith(expect.stringContaining("continue without Codex sessions"));
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
describe("prepareOpenCodeRuntimeEnvironment", () => {
|
|
25
|
-
it("injects OpenCode independently from Codex", async () => {
|
|
26
|
-
const environment = await prepareOpenCodeRuntimeEnvironment({ PATH: "/bin", AGENT_CODEX_BIN: "/managed/codex" }, { dataDir: "/data", target: "darwin-arm64" }, async () => ({ executable: "/managed/opencode", version: "1.18.27", source: "managed" }), vi.fn());
|
|
27
|
-
expect(environment).toMatchObject({
|
|
28
|
-
AGENT_CODEX_BIN: "/managed/codex",
|
|
29
|
-
AGENT_OPENCODE_BIN: "/managed/opencode",
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
it("records OpenCode failure without removing Codex", async () => {
|
|
33
|
-
const environment = await prepareOpenCodeRuntimeEnvironment({ AGENT_CODEX_BIN: "/managed/codex", AGENT_OPENCODE_BIN: "/bad/opencode" }, { dataDir: "/data", target: "darwin-arm64" }, async () => { throw new Error("OpenCode download failed"); }, vi.fn());
|
|
34
|
-
expect(environment.AGENT_CODEX_BIN).toBe("/managed/codex");
|
|
35
|
-
expect(environment.AGENT_OPENCODE_BIN).toBeUndefined();
|
|
36
|
-
expect(environment.AGENT_OPENCODE_RUNTIME_ERROR).toBe("OpenCode download failed");
|
|
37
|
-
});
|
|
38
|
-
});
|