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,84 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { selectRelay } from "./relay-orchestrator.js";
|
|
3
|
-
describe("selectRelay", () => {
|
|
4
|
-
it("selects Cloudflare when it becomes ready", async () => {
|
|
5
|
-
const cloudflare = fakeProvider("https://ready.trycloudflare.com");
|
|
6
|
-
const result = await selectRelay(options("auto", { cloudflare }));
|
|
7
|
-
expect(result.provider).toBe("cloudflare");
|
|
8
|
-
expect(result.publicUrl).toContain("trycloudflare.com");
|
|
9
|
-
});
|
|
10
|
-
it("closes failed Cloudflare and selects Pinggy", async () => {
|
|
11
|
-
const cloudflare = fakeProvider("https://bad.trycloudflare.com");
|
|
12
|
-
const pinggy = fakeProvider("https://good.run.pinggy-free.link");
|
|
13
|
-
const readiness = vi.fn(async (url) => {
|
|
14
|
-
if (url.includes("bad"))
|
|
15
|
-
throw new Error("HTTP 530");
|
|
16
|
-
});
|
|
17
|
-
const result = await selectRelay(options("auto", { cloudflare, pinggy }, readiness));
|
|
18
|
-
expect(cloudflare.handle.close).toHaveBeenCalledOnce();
|
|
19
|
-
expect(result.provider).toBe("pinggy");
|
|
20
|
-
expect(result.failures).toEqual([{ provider: "cloudflare", message: "HTTP 530" }]);
|
|
21
|
-
});
|
|
22
|
-
it("falls back to LAN when both public providers fail", async () => {
|
|
23
|
-
const cloudflare = fakeProvider("https://bad.trycloudflare.com");
|
|
24
|
-
const pinggy = fakeProvider("https://bad.run.pinggy-free.link");
|
|
25
|
-
const result = await selectRelay(options("auto", { cloudflare, pinggy }, async () => { throw new Error("blocked"); }));
|
|
26
|
-
expect(result.provider).toBe("lan");
|
|
27
|
-
expect(result.publicUrl).toBe("http://10.0.0.2:43210");
|
|
28
|
-
expect(result.failures).toHaveLength(2);
|
|
29
|
-
});
|
|
30
|
-
it("fails instead of leaving a background service permanently on LAN", async () => {
|
|
31
|
-
const cloudflare = fakeProvider("https://bad.trycloudflare.com");
|
|
32
|
-
const pinggy = fakeProvider("https://bad.run.pinggy-free.link");
|
|
33
|
-
await expect(selectRelay({
|
|
34
|
-
...options("auto", { cloudflare, pinggy }, async () => { throw new Error("blocked"); }),
|
|
35
|
-
allowLanFallback: false,
|
|
36
|
-
})).rejects.toThrow("public relay unavailable (cloudflare: blocked; pinggy: blocked)");
|
|
37
|
-
expect(cloudflare.handle.close).toHaveBeenCalledOnce();
|
|
38
|
-
expect(pinggy.handle.close).toHaveBeenCalledOnce();
|
|
39
|
-
});
|
|
40
|
-
it("bypasses every public provider in local-only mode", async () => {
|
|
41
|
-
const cloudflare = fakeProvider("https://unused.trycloudflare.com");
|
|
42
|
-
const result = await selectRelay({ ...options("auto", { cloudflare }), cli: cli("auto", true) });
|
|
43
|
-
expect(result.provider).toBe("lan");
|
|
44
|
-
expect(cloudflare.provider.start).not.toHaveBeenCalled();
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
function options(relay, providers, readiness = async () => { }) {
|
|
48
|
-
return {
|
|
49
|
-
cli: cli(relay),
|
|
50
|
-
target: "darwin-arm64",
|
|
51
|
-
localUrl: "http://127.0.0.1:43210",
|
|
52
|
-
lanUrl: "http://10.0.0.2:43210",
|
|
53
|
-
port: 43210,
|
|
54
|
-
signal: new AbortController().signal,
|
|
55
|
-
log: vi.fn(),
|
|
56
|
-
readiness: readiness,
|
|
57
|
-
providerFactories: Object.fromEntries(Object.entries(providers).map(([name, value]) => [name, async () => value.provider])),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function cli(relay, localOnly = false) {
|
|
61
|
-
return {
|
|
62
|
-
command: "start",
|
|
63
|
-
serviceAction: null,
|
|
64
|
-
unlockServiceAction: null,
|
|
65
|
-
roots: [process.cwd()],
|
|
66
|
-
port: null,
|
|
67
|
-
relay,
|
|
68
|
-
tunnelCommand: relay === "custom" ? "relay {port}" : null,
|
|
69
|
-
localOnly,
|
|
70
|
-
qr: false,
|
|
71
|
-
dataDir: "/tmp/agentroam-test",
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function fakeProvider(publicUrl) {
|
|
75
|
-
const handle = {
|
|
76
|
-
publicUrl,
|
|
77
|
-
close: vi.fn(async () => { }),
|
|
78
|
-
exited: new Promise(() => { }),
|
|
79
|
-
};
|
|
80
|
-
const provider = {
|
|
81
|
-
start: vi.fn(async () => handle),
|
|
82
|
-
};
|
|
83
|
-
return { provider, handle };
|
|
84
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { buildElevatedPowerShell, buildUnlockServiceAdminScript, encodePowerShell, parseUnlockServiceStatus, runUnlockServiceCommand, unlockServiceExecutable, } from "./unlock-service.js";
|
|
3
|
-
function result(stdout = "", status = 0, stderr = "") {
|
|
4
|
-
return { pid: 1, output: [null, stdout, stderr], stdout, stderr, status, signal: null, error: undefined };
|
|
5
|
-
}
|
|
6
|
-
describe("Windows unlock service command", () => {
|
|
7
|
-
it("builds an elevated install script without putting credentials on disk or the command line", () => {
|
|
8
|
-
const executable = "C:\\Program Files\\AgentRoam\\agentroam-remote-unlock.exe";
|
|
9
|
-
const admin = buildUnlockServiceAdminScript("install", executable);
|
|
10
|
-
expect(admin).toContain("New-Service");
|
|
11
|
-
expect(admin).toContain("AgentRoamUnlock");
|
|
12
|
-
expect(admin).toContain(`'${executable}'`);
|
|
13
|
-
expect(admin).toContain('"C:\\Program Files\\AgentRoam\\agentroam-remote-unlock.exe" --service');
|
|
14
|
-
const elevated = buildElevatedPowerShell(admin);
|
|
15
|
-
expect(elevated).toContain("-Verb RunAs");
|
|
16
|
-
expect(elevated).toContain("-EncodedCommand");
|
|
17
|
-
expect(Buffer.from(encodePowerShell(admin), "base64").toString("utf16le")).toBe(admin);
|
|
18
|
-
});
|
|
19
|
-
it("reports localized status messages and validates post-install state", () => {
|
|
20
|
-
const runner = vi.fn()
|
|
21
|
-
.mockReturnValueOnce(result())
|
|
22
|
-
.mockReturnValueOnce(result("running\r\n"));
|
|
23
|
-
const log = vi.fn();
|
|
24
|
-
runUnlockServiceCommand("install", "C:\\runtime", { platform: "win32", runner, log });
|
|
25
|
-
expect(unlockServiceExecutable("C:\\runtime")).toMatch(/agentroam-remote-unlock\.exe$/);
|
|
26
|
-
expect(runner).toHaveBeenCalledTimes(2);
|
|
27
|
-
expect(log).toHaveBeenCalledWith("远程解锁服务已安装并启动");
|
|
28
|
-
});
|
|
29
|
-
it("fails closed on unsupported platforms, canceled elevation, and unknown status output", () => {
|
|
30
|
-
expect(() => runUnlockServiceCommand("status", "/runtime", { platform: "darwin" })).toThrow("仅支持 Windows");
|
|
31
|
-
expect(() => runUnlockServiceCommand("install", "C:\\runtime", {
|
|
32
|
-
platform: "win32",
|
|
33
|
-
runner: () => result("", 1, "canceled"),
|
|
34
|
-
})).toThrow("canceled");
|
|
35
|
-
expect(() => parseUnlockServiceStatus("paused")).toThrow("无法读取");
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { mkdtemp } from "node:fs/promises";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
import { describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { resolveUpdate } from "./update-command.js";
|
|
6
|
-
import { acquireUpdateLock, readUpdateState, updatePaths, writeUpdateState } from "./update-state.js";
|
|
7
|
-
const sha256 = "c".repeat(64);
|
|
8
|
-
const response = (body) => new Response(JSON.stringify(body), { status: 200 });
|
|
9
|
-
describe("resolveUpdate", () => {
|
|
10
|
-
it("accepts only npm latest with an audited Gitee installer", async () => {
|
|
11
|
-
const fetcher = vi.fn()
|
|
12
|
-
.mockResolvedValueOnce(response({ version: "1.2.3" }))
|
|
13
|
-
.mockResolvedValueOnce(response({ schemaVersion: 2, version: "1.2.3", channel: "latest", installers: { cli: { "windows-amd64": { fileName: "install-agentroam.ps1", sha256 } } } }));
|
|
14
|
-
await expect(resolveUpdate({ currentVersion: "1.2.2", requestedVersion: null, dataDir: "data", target: "windows-amd64", cliPath: "cli" }, fetcher))
|
|
15
|
-
.resolves.toEqual({ targetVersion: "1.2.3", fileName: "install-agentroam.ps1", sha256 });
|
|
16
|
-
});
|
|
17
|
-
it("rejects a requested version that is not npm latest", async () => {
|
|
18
|
-
const fetcher = vi.fn().mockResolvedValue(response({ version: "1.2.3" }));
|
|
19
|
-
await expect(resolveUpdate({ currentVersion: "1.0.0", requestedVersion: "1.2.2", dataDir: "data", target: "darwin-arm64", cliPath: "cli" }, fetcher)).rejects.toThrow("not the current npm candidate");
|
|
20
|
-
});
|
|
21
|
-
it("follows the preview channel and offers a higher preview", async () => {
|
|
22
|
-
const fetcher = vi.fn((url) => {
|
|
23
|
-
const u = String(url);
|
|
24
|
-
if (u.endsWith("/preview"))
|
|
25
|
-
return Promise.resolve(response({ version: "1.2.3-preview.4" }));
|
|
26
|
-
if (u.endsWith("/latest"))
|
|
27
|
-
return Promise.resolve(response({ version: "1.2.2" }));
|
|
28
|
-
return Promise.resolve(response({ schemaVersion: 2, version: "1.2.3-preview.4", channel: "preview", installers: { cli: { "darwin-arm64": { fileName: "install-agentroam.sh", sha256 } } } }));
|
|
29
|
-
});
|
|
30
|
-
await expect(resolveUpdate({ currentVersion: "1.2.3-preview.3", requestedVersion: null, dataDir: "data", target: "darwin-arm64", cliPath: "cli" }, fetcher))
|
|
31
|
-
.resolves.toEqual({ targetVersion: "1.2.3-preview.4", fileName: "install-agentroam.sh", sha256 });
|
|
32
|
-
});
|
|
33
|
-
it("offers a newer stable release to preview users", async () => {
|
|
34
|
-
const fetcher = vi.fn((url) => {
|
|
35
|
-
const u = String(url);
|
|
36
|
-
if (u.endsWith("/preview"))
|
|
37
|
-
return Promise.resolve(response({ version: "1.2.3-preview.4" }));
|
|
38
|
-
if (u.endsWith("/latest"))
|
|
39
|
-
return Promise.resolve(response({ version: "1.2.4" }));
|
|
40
|
-
return Promise.resolve(response({ schemaVersion: 2, version: "1.2.4", channel: "latest", installers: { cli: { "darwin-arm64": { fileName: "install-agentroam.sh", sha256 } } } }));
|
|
41
|
-
});
|
|
42
|
-
await expect(resolveUpdate({ currentVersion: "1.2.3-preview.4", requestedVersion: null, dataDir: "data", target: "darwin-arm64", cliPath: "cli" }, fetcher))
|
|
43
|
-
.resolves.toEqual({ targetVersion: "1.2.4", fileName: "install-agentroam.sh", sha256 });
|
|
44
|
-
});
|
|
45
|
-
it("stable channel never queries the preview tag", async () => {
|
|
46
|
-
const fetcher = vi.fn((url) => {
|
|
47
|
-
const u = String(url);
|
|
48
|
-
expect(u).not.toContain("/preview");
|
|
49
|
-
if (u.endsWith("/latest"))
|
|
50
|
-
return Promise.resolve(response({ version: "1.2.4" }));
|
|
51
|
-
return Promise.resolve(response({ schemaVersion: 2, version: "1.2.4", channel: "latest", installers: { cli: { "darwin-arm64": { fileName: "install-agentroam.sh", sha256 } } } }));
|
|
52
|
-
});
|
|
53
|
-
await resolveUpdate({ currentVersion: "1.2.3", requestedVersion: null, dataDir: "data", target: "darwin-arm64", cliPath: "cli" }, fetcher);
|
|
54
|
-
expect(fetcher.mock.calls.map((call) => String(call[0])).some((url) => url.includes("/preview"))).toBe(false);
|
|
55
|
-
});
|
|
56
|
-
it("writes durable state atomically and excludes concurrent workers", async () => {
|
|
57
|
-
const dataDir = await mkdtemp(resolve(tmpdir(), "agentroam-update-state-"));
|
|
58
|
-
const paths = updatePaths(dataDir);
|
|
59
|
-
const state = {
|
|
60
|
-
schemaVersion: 1, phase: "downloading", currentVersion: "1.0.0", targetVersion: "1.1.0",
|
|
61
|
-
dataDir, roots: ["D:\\project"], port: 3000, relay: "auto", tunnelCommand: null,
|
|
62
|
-
localOnly: false, fileName: "install-agentroam.ps1", sha256, cliPath: "C:\\agentroam.mjs", updatedAt: 1,
|
|
63
|
-
};
|
|
64
|
-
await writeUpdateState(paths.stateFile, state);
|
|
65
|
-
await expect(readUpdateState(paths.stateFile)).resolves.toEqual(state);
|
|
66
|
-
const release = await acquireUpdateLock(paths.lockFile);
|
|
67
|
-
await expect(acquireUpdateLock(paths.lockFile)).rejects.toThrow("already running");
|
|
68
|
-
await release();
|
|
69
|
-
});
|
|
70
|
-
});
|