@agentrq/acp-gateway 0.2.3 → 0.2.4
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 +96 -1
- package/dist/__tests__/agentInstall.test.js +342 -0
- package/dist/__tests__/agentInstall.test.js.map +1 -0
- package/dist/__tests__/auth.test.js +283 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/__tests__/index.test.js +401 -8
- package/dist/__tests__/index.test.js.map +1 -1
- package/dist/__tests__/registry.test.js +175 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/agentInstall.js +241 -0
- package/dist/agentInstall.js.map +1 -0
- package/dist/auth.js +189 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.js +375 -32
- package/dist/index.js.map +1 -1
- package/dist/mcpClient.js +1 -1
- package/dist/registry.js +118 -0
- package/dist/registry.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ npm install -g @agentrq/acp-gateway
|
|
|
42
42
|
|
|
43
43
|
## Current Version
|
|
44
44
|
|
|
45
|
-
`0.2.
|
|
45
|
+
`0.2.4`
|
|
46
46
|
|
|
47
47
|
## Usage
|
|
48
48
|
|
|
@@ -66,12 +66,99 @@ acp-gateway -- your-acp-agent --flag1 --flag2
|
|
|
66
66
|
You can specify gateway options before the `--` separator:
|
|
67
67
|
|
|
68
68
|
- `--max-concurrency` / `--maxConcurrency` `<number>`: Sets the maximum number of concurrent tasks allowed to prompt the ACP agent at once. Defaults to `2`.
|
|
69
|
+
- `--agent <registry-id>`: Runs an agent from the ACP registry instead of a command you supply yourself.
|
|
70
|
+
- `--list-agents`: Prints every agent in the registry and how each one can run on this machine, then exits.
|
|
71
|
+
- `--allow-unverified-agent`: Installs a registry binary that publishes no checksum. Off by default.
|
|
72
|
+
- `--registry-url <url>`: Reads a different registry index (for pinning, or for testing).
|
|
73
|
+
- `--auth-method <id>`: The authentication method to use when the agent asks for a login. Defaults to picking one automatically.
|
|
74
|
+
- `--help` / `-h`: Explains every option, with examples. Also shown when `acp-gateway` is run with nothing to do.
|
|
75
|
+
- `--list-auth-methods`: Prints the login methods the agent advertises, then exits.
|
|
76
|
+
- `--login [method-id]`: Logs in to the agent, then exits.
|
|
77
|
+
- `--logout`: Ends the agent's authenticated state, then exits (only for agents that support logout).
|
|
69
78
|
|
|
70
79
|
Example:
|
|
71
80
|
```bash
|
|
72
81
|
acp-gateway --max-concurrency 4 -- gemini --acp
|
|
73
82
|
```
|
|
74
83
|
|
|
84
|
+
### Running an Agent from the Registry
|
|
85
|
+
|
|
86
|
+
The [ACP registry](https://github.com/agentclientprotocol/registry) lists agents that implement the
|
|
87
|
+
protocol, and `acp-gateway` can run one by name — no installing or wiring up by hand:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
acp-gateway --list-agents # see what's published
|
|
91
|
+
acp-gateway --agent gemini # run one
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Registry entries are distributed in three ways, and the gateway prefers them in this order:
|
|
95
|
+
|
|
96
|
+
| Distribution | How it runs | Downloads anything? |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `npx` | `npx -y <package> <args>` | No — npm fetches and verifies the package |
|
|
99
|
+
| `uvx` | `uvx <package> <args>` | No — uv fetches and verifies the package |
|
|
100
|
+
| `binary` | Archive is downloaded, checked, unpacked and cached | Yes |
|
|
101
|
+
|
|
102
|
+
Package distributions come first because npm and PyPI verify what they serve. A binary is only used
|
|
103
|
+
when it is the agent's only distribution.
|
|
104
|
+
|
|
105
|
+
**Binaries are verified before they run.** The registry's `sha256` for the archive is optional, and
|
|
106
|
+
roughly half of the published binary targets omit it. Where there is no checksum the gateway refuses
|
|
107
|
+
to install rather than run something it cannot vouch for:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
The ACP registry publishes no sha256 for "antigravity-acp" on this platform, so the download
|
|
111
|
+
cannot be verified. Re-run with --allow-unverified-agent to install it anyway, or install the
|
|
112
|
+
agent yourself and pass it after --.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Downloads are cached per agent, version and platform, so each build is fetched once:
|
|
116
|
+
|
|
117
|
+
| Platform | Cache location |
|
|
118
|
+
|---|---|
|
|
119
|
+
| macOS / Linux | `$XDG_CACHE_HOME/acp-gateway/agents`, else `~/.cache/acp-gateway/agents` |
|
|
120
|
+
| Windows | `%LOCALAPPDATA%\acp-gateway\agents` |
|
|
121
|
+
|
|
122
|
+
All six platforms the registry publishes for are supported — macOS, Linux and Windows on both x86_64
|
|
123
|
+
and arm64. Archives are unpacked with the system's own `tar` (and `unzip` for zips on Linux, whose
|
|
124
|
+
`tar` cannot read them), so no archive libraries are added as dependencies.
|
|
125
|
+
|
|
126
|
+
### Authentication
|
|
127
|
+
|
|
128
|
+
Agents that require a login advertise their login methods during the ACP handshake, and refuse to open a
|
|
129
|
+
session until one has been used. `acp-gateway` handles that the same way an editor such as Zed does.
|
|
130
|
+
|
|
131
|
+
List what an agent offers:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
acp-gateway --list-auth-methods -- gemini --acp
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Log in ahead of time — with no method id, you are asked to pick one:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
acp-gateway --login -- gemini --acp
|
|
141
|
+
acp-gateway --login oauth-personal -- gemini --acp # or name the method directly
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Log out again:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
acp-gateway --logout -- gemini --acp
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Nothing has to be done up front, though: if an agent refuses the first session because it needs a login,
|
|
151
|
+
`acp-gateway` logs in and retries by itself.
|
|
152
|
+
|
|
153
|
+
There are two kinds of method:
|
|
154
|
+
|
|
155
|
+
- **Agent** methods — the agent runs the login itself (a browser flow, an API key it already holds). These
|
|
156
|
+
need nobody present, so they are chosen first and work in an unattended gateway.
|
|
157
|
+
- **Terminal** methods — the agent's own binary is re-run interactively so you can log in at a TUI.
|
|
158
|
+
`acp-gateway` only offers to run these when it has a real terminal to hand over.
|
|
159
|
+
|
|
160
|
+
Credentials are never stored by the gateway; the agent keeps its own, exactly as it does under an editor.
|
|
161
|
+
|
|
75
162
|
### Configuration
|
|
76
163
|
|
|
77
164
|
`acp-gateway` searches for `.mcp.json` starting in the current working directory and up to 3 parent directories.
|
|
@@ -134,6 +221,9 @@ Example `.mcp.json`:
|
|
|
134
221
|
| `src/acpClient.ts` | Implements the ACP `Client` interface — routes permission requests, handles session updates, and provides file operations. |
|
|
135
222
|
| `src/mcpClient.ts` | `EventEmitter`-based MCP client with auto-reconnection, notification handling, and tool call dispatch. |
|
|
136
223
|
| `src/config.ts` | Parses `.mcp.json` from the current directory tree up to 3 levels deep. |
|
|
224
|
+
| `src/auth.ts` | ACP authentication — lists the agent's login methods, detects `auth_required`, and runs agent or terminal logins. |
|
|
225
|
+
| `src/registry.ts` | Reads the ACP registry index — agent lookup, host platform matching, and package launch commands. |
|
|
226
|
+
| `src/agentInstall.ts` | Downloads, verifies, unpacks and caches a registry agent's binary distribution. |
|
|
137
227
|
|
|
138
228
|
## Development
|
|
139
229
|
|
|
@@ -156,9 +246,12 @@ npm test
|
|
|
156
246
|
acp-gateway/
|
|
157
247
|
├── src/
|
|
158
248
|
│ ├── acpClient.ts # ACP Client implementation
|
|
249
|
+
│ ├── agentInstall.ts # Registry binary download / verify / cache
|
|
250
|
+
│ ├── auth.ts # ACP authentication (login / logout)
|
|
159
251
|
│ ├── config.ts # .mcp.json loader
|
|
160
252
|
│ ├── index.ts # Entry point & orchestrator
|
|
161
253
|
│ ├── mcpClient.ts # MCP Bridge with auto-reconnect
|
|
254
|
+
│ ├── registry.ts # ACP registry index client
|
|
162
255
|
│ └── __tests__/ # Unit tests
|
|
163
256
|
├── package.json
|
|
164
257
|
└── tsconfig.json
|
|
@@ -169,6 +262,8 @@ acp-gateway/
|
|
|
169
262
|
- **Auto-reconnection**: The MCP transport auto-reconnects on disconnection with exponential backoff (1s → 30s max).
|
|
170
263
|
- **Notification-driven tasks**: The MCP server pushes task content via `notifications/claude/channel`; `acp-gateway` reacts immediately.
|
|
171
264
|
- **Permission flow**: ACP agent requests permission → `acp-gateway` forwards to MCP server → waits for verdict → resolves the ACP permission.
|
|
265
|
+
- **Registry agents**: `--agent <id>` resolves through the registry index; package distributions are preferred over binaries, and a binary without a published `sha256` is refused unless explicitly allowed.
|
|
266
|
+
- **Authentication**: Login methods come from the `initialize` handshake; an `auth_required` refusal triggers a login and one retry of `newSession`.
|
|
172
267
|
- **File I/O**: `readTextFile` / `writeTextFile` are proxied directly to the filesystem; paths are resolved relative to `process.cwd()`.
|
|
173
268
|
|
|
174
269
|
## Contributing
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import { mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import { archiveKind, assertChecksum, assertVerifiable, defaultCacheDir, downloadArchive, extractArchive, installBinaryAgent, installDir, moveInto, resolveAgentLaunch, runExtractionTool, resolveExecutable, sha256, } from "../agentInstall.js";
|
|
8
|
+
const agent = {
|
|
9
|
+
id: "demo-acp",
|
|
10
|
+
name: "Demo Agent",
|
|
11
|
+
version: "1.2.3",
|
|
12
|
+
description: "A demo agent",
|
|
13
|
+
distribution: {},
|
|
14
|
+
};
|
|
15
|
+
/** Builds a real .tar.gz containing an executable, so extraction is exercised for real. */
|
|
16
|
+
async function makeTarball(cmdName, contents) {
|
|
17
|
+
const staging = await mkdtemp(path.join(tmpdir(), "acp-gateway-fixture-"));
|
|
18
|
+
try {
|
|
19
|
+
await writeFile(path.join(staging, cmdName), contents);
|
|
20
|
+
const archive = path.join(staging, "out.tar.gz");
|
|
21
|
+
execFileSync("tar", ["-czf", archive, "-C", staging, cmdName]);
|
|
22
|
+
return new Uint8Array(await readFile(archive));
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
await rm(staging, { recursive: true, force: true });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function okResponse(data) {
|
|
29
|
+
return {
|
|
30
|
+
ok: true,
|
|
31
|
+
status: 200,
|
|
32
|
+
statusText: "OK",
|
|
33
|
+
arrayBuffer: async () => data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
describe("agentInstall", () => {
|
|
37
|
+
let cacheDir;
|
|
38
|
+
let errorSpy;
|
|
39
|
+
beforeEach(async () => {
|
|
40
|
+
cacheDir = await mkdtemp(path.join(tmpdir(), "acp-gateway-cache-"));
|
|
41
|
+
errorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
42
|
+
});
|
|
43
|
+
afterEach(async () => {
|
|
44
|
+
errorSpy.mockRestore();
|
|
45
|
+
await rm(cacheDir, { recursive: true, force: true });
|
|
46
|
+
});
|
|
47
|
+
describe("defaultCacheDir", () => {
|
|
48
|
+
it("uses XDG_CACHE_HOME where it is set", () => {
|
|
49
|
+
expect(defaultCacheDir("linux", { XDG_CACHE_HOME: "/xdg" })).toBe(path.join("/xdg", "acp-gateway", "agents"));
|
|
50
|
+
});
|
|
51
|
+
it("falls back to the per-user cache on macOS and Linux", () => {
|
|
52
|
+
expect(defaultCacheDir("darwin", {})).toMatch(/\.cache[\\/]acp-gateway[\\/]agents$/);
|
|
53
|
+
});
|
|
54
|
+
it("uses LOCALAPPDATA on Windows", () => {
|
|
55
|
+
expect(defaultCacheDir("win32", { LOCALAPPDATA: "C:\\Users\\me\\AppData\\Local" })).toBe(path.join("C:\\Users\\me\\AppData\\Local", "acp-gateway", "agents"));
|
|
56
|
+
expect(defaultCacheDir("win32", {})).toMatch(/AppData[\\/]Local[\\/]acp-gateway/);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe("archiveKind", () => {
|
|
60
|
+
it("recognises every format the registry allows", () => {
|
|
61
|
+
expect(archiveKind("https://x/y.zip")).toBe("zip");
|
|
62
|
+
expect(archiveKind("https://x/y.tar.gz")).toBe("tar.gz");
|
|
63
|
+
expect(archiveKind("https://x/y.tgz")).toBe("tar.gz");
|
|
64
|
+
expect(archiveKind("https://x/y.tar.bz2")).toBe("tar.bz2");
|
|
65
|
+
expect(archiveKind("https://x/y.tbz2")).toBe("tar.bz2");
|
|
66
|
+
});
|
|
67
|
+
it("ignores query strings and fragments", () => {
|
|
68
|
+
expect(archiveKind("https://x/y.ZIP?token=abc#frag")).toBe("zip");
|
|
69
|
+
});
|
|
70
|
+
it("treats anything else as a raw executable", () => {
|
|
71
|
+
expect(archiveKind("https://x/agent-darwin-arm64")).toBe("raw");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("installDir", () => {
|
|
75
|
+
it("keeps each version and platform apart", () => {
|
|
76
|
+
expect(installDir("/cache", "demo", "darwin-aarch64", "1.2.3")).toBe(path.join("/cache", "demo@1.2.3", "darwin-aarch64"));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe("resolveExecutable", () => {
|
|
80
|
+
it("resolves the registry's cmd inside the install directory", () => {
|
|
81
|
+
expect(resolveExecutable("/cache/demo", "./bin/agent")).toBe(path.resolve("/cache/demo/bin/agent"));
|
|
82
|
+
});
|
|
83
|
+
it("refuses a cmd that climbs out of the install directory", () => {
|
|
84
|
+
expect(() => resolveExecutable("/cache/demo", "../../../usr/bin/curl")).toThrow(/points outside its install directory/);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("assertVerifiable", () => {
|
|
88
|
+
const unverifiable = { archive: "https://x/y.zip", cmd: "./demo" };
|
|
89
|
+
it("refuses an archive the registry publishes no checksum for", () => {
|
|
90
|
+
expect(() => assertVerifiable(agent, unverifiable, false)).toThrow(/publishes no sha256 for "demo-acp".*--allow-unverified-agent/s);
|
|
91
|
+
});
|
|
92
|
+
it("allows it once the user has explicitly opted in", () => {
|
|
93
|
+
expect(() => assertVerifiable(agent, unverifiable, true)).not.toThrow();
|
|
94
|
+
});
|
|
95
|
+
it("allows an archive that does publish a checksum", () => {
|
|
96
|
+
expect(() => assertVerifiable(agent, { ...unverifiable, sha256: "a".repeat(64) }, false)).not.toThrow();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe("assertChecksum", () => {
|
|
100
|
+
const data = new Uint8Array([1, 2, 3]);
|
|
101
|
+
it("accepts a download that matches, whatever the case", () => {
|
|
102
|
+
const digest = sha256(data);
|
|
103
|
+
expect(() => assertChecksum(agent, { archive: "https://x/y", cmd: "./d", sha256: digest.toUpperCase() }, data)).not.toThrow();
|
|
104
|
+
});
|
|
105
|
+
it("refuses a download that does not match", () => {
|
|
106
|
+
expect(() => assertChecksum(agent, { archive: "https://x/y", cmd: "./d", sha256: "b".repeat(64) }, data)).toThrow(/Checksum mismatch for "demo-acp"/);
|
|
107
|
+
});
|
|
108
|
+
it("has nothing to check when the registry publishes no checksum", () => {
|
|
109
|
+
expect(() => assertChecksum(agent, { archive: "https://x/y", cmd: "./d" }, data)).not.toThrow();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
describe("downloadArchive", () => {
|
|
113
|
+
it("returns the bytes", async () => {
|
|
114
|
+
const data = new Uint8Array([9, 8, 7]);
|
|
115
|
+
const fetchImpl = vi.fn().mockResolvedValue(okResponse(data));
|
|
116
|
+
expect(await downloadArchive("https://x/y.zip", fetchImpl)).toEqual(data);
|
|
117
|
+
});
|
|
118
|
+
it("reports a failed download", async () => {
|
|
119
|
+
const fetchImpl = vi
|
|
120
|
+
.fn()
|
|
121
|
+
.mockResolvedValue({ ok: false, status: 403, statusText: "Forbidden" });
|
|
122
|
+
await expect(downloadArchive("https://x/y.zip", fetchImpl)).rejects.toThrow(/Failed to download https:\/\/x\/y.zip: 403 Forbidden/);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe("extractArchive", () => {
|
|
126
|
+
it("writes a raw download straight out as the executable", async () => {
|
|
127
|
+
const dir = path.join(cacheDir, "raw");
|
|
128
|
+
await extractArchive(new Uint8Array([1, 2]), "raw", dir, "./demo-agent");
|
|
129
|
+
expect(await readFile(path.join(dir, "demo-agent"))).toEqual(Buffer.from([1, 2]));
|
|
130
|
+
});
|
|
131
|
+
it("unpacks a real tarball and removes the archive afterwards", async () => {
|
|
132
|
+
const tarball = await makeTarball("demo", "#!/bin/sh\necho hi\n");
|
|
133
|
+
const dir = path.join(cacheDir, "tar");
|
|
134
|
+
await extractArchive(tarball, "tar.gz", dir, "./demo");
|
|
135
|
+
expect(await readFile(path.join(dir, "demo"), "utf8")).toContain("echo hi");
|
|
136
|
+
expect(existsSync(path.join(dir, "archive.tar.gz"))).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
it("uses unzip for zips on Linux, where tar cannot read them", async () => {
|
|
139
|
+
const dir = path.join(cacheDir, "zip");
|
|
140
|
+
// `unzip` is not guaranteed on every machine, so assert on the failure
|
|
141
|
+
// message rather than on a successful extraction.
|
|
142
|
+
await expect(extractArchive(new Uint8Array([1]), "zip", dir, "./demo", "linux")).rejects.toThrow(/unzip/);
|
|
143
|
+
});
|
|
144
|
+
it("reports an extraction tool that is not installed", async () => {
|
|
145
|
+
await expect(runExtractionTool("acp-gateway-no-such-tool", [], cacheDir)).rejects.toThrow(/Could not run "acp-gateway-no-such-tool" to unpack the agent archive/);
|
|
146
|
+
});
|
|
147
|
+
it("reports a corrupt archive with the tool's own diagnostics", async () => {
|
|
148
|
+
const dir = path.join(cacheDir, "bad");
|
|
149
|
+
await expect(extractArchive(new Uint8Array([1, 2, 3]), "tar.gz", dir, "./demo", "darwin")).rejects.toThrow(/failed to unpack the agent archive/);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe("moveInto", () => {
|
|
153
|
+
it("copies when the staged install cannot simply be renamed into place", async () => {
|
|
154
|
+
const from = await mkdtemp(path.join(tmpdir(), "acp-gateway-move-"));
|
|
155
|
+
await writeFile(path.join(from, "demo"), "binary");
|
|
156
|
+
// A destination whose parents do not exist: rename() fails, and the copy
|
|
157
|
+
// fallback — the same path taken when the temp dir is another mount —
|
|
158
|
+
// has to create the tree.
|
|
159
|
+
const to = path.join(cacheDir, "deep", "deeper", "install");
|
|
160
|
+
await moveInto(from, to);
|
|
161
|
+
expect(await readFile(path.join(to, "demo"), "utf8")).toBe("binary");
|
|
162
|
+
await rm(from, { recursive: true, force: true });
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe("installBinaryAgent", () => {
|
|
166
|
+
async function install(overrides = {}) {
|
|
167
|
+
const tarball = await makeTarball("demo", "#!/bin/sh\nexit 0\n");
|
|
168
|
+
const target = {
|
|
169
|
+
archive: "https://example.com/demo.tar.gz",
|
|
170
|
+
sha256: sha256(tarball),
|
|
171
|
+
cmd: "./demo",
|
|
172
|
+
args: ["--acp"],
|
|
173
|
+
...overrides.target,
|
|
174
|
+
};
|
|
175
|
+
const fetchImpl = vi.fn().mockResolvedValue(okResponse(tarball));
|
|
176
|
+
const spec = await installBinaryAgent({
|
|
177
|
+
agent,
|
|
178
|
+
target,
|
|
179
|
+
platformTarget: "darwin-aarch64",
|
|
180
|
+
cacheDir,
|
|
181
|
+
fetchImpl: fetchImpl,
|
|
182
|
+
platform: "darwin",
|
|
183
|
+
...overrides.options,
|
|
184
|
+
});
|
|
185
|
+
return { spec, fetchImpl, tarball };
|
|
186
|
+
}
|
|
187
|
+
it("downloads, verifies, unpacks and makes the agent executable", async () => {
|
|
188
|
+
const { spec } = await install();
|
|
189
|
+
expect(spec).toEqual({
|
|
190
|
+
command: path.join(cacheDir, "demo-acp@1.2.3", "darwin-aarch64", "demo"),
|
|
191
|
+
args: ["--acp"],
|
|
192
|
+
env: undefined,
|
|
193
|
+
kind: "binary",
|
|
194
|
+
});
|
|
195
|
+
expect(existsSync(spec.command)).toBe(true);
|
|
196
|
+
// 0o111 — executable by someone.
|
|
197
|
+
expect((await stat(spec.command)).mode & 0o111).toBeGreaterThan(0);
|
|
198
|
+
});
|
|
199
|
+
it("skips the executable bit on Windows, which has none", async () => {
|
|
200
|
+
const { spec } = await install({ options: { platform: "win32" } });
|
|
201
|
+
expect(existsSync(spec.command)).toBe(true);
|
|
202
|
+
});
|
|
203
|
+
it("reuses a cached install instead of downloading again", async () => {
|
|
204
|
+
await install();
|
|
205
|
+
const { fetchImpl } = await install();
|
|
206
|
+
expect(fetchImpl).not.toHaveBeenCalled();
|
|
207
|
+
expect(errorSpy.mock.calls.flat().join("\n")).toContain("Using cached demo-acp 1.2.3");
|
|
208
|
+
});
|
|
209
|
+
it("refuses an archive with no published checksum", async () => {
|
|
210
|
+
await expect(install({ target: { sha256: undefined } })).rejects.toThrow(/publishes no sha256/);
|
|
211
|
+
});
|
|
212
|
+
it("installs an unverified archive when explicitly allowed, and says so", async () => {
|
|
213
|
+
const { spec } = await install({
|
|
214
|
+
target: { sha256: undefined },
|
|
215
|
+
options: { allowUnverified: true },
|
|
216
|
+
});
|
|
217
|
+
expect(existsSync(spec.command)).toBe(true);
|
|
218
|
+
expect(errorSpy.mock.calls.flat().join("\n")).toContain("publishes no checksum");
|
|
219
|
+
});
|
|
220
|
+
it("refuses a download that does not match the checksum", async () => {
|
|
221
|
+
await expect(install({ target: { sha256: "c".repeat(64) } })).rejects.toThrow(/Checksum mismatch/);
|
|
222
|
+
});
|
|
223
|
+
it("leaves nothing cached when the archive lacks the promised command", async () => {
|
|
224
|
+
await expect(install({ target: { cmd: "./not-in-archive" } })).rejects.toThrow(/does not contain "\.\/not-in-archive"/);
|
|
225
|
+
expect(existsSync(installDir(cacheDir, agent.id, "darwin-aarch64", agent.version))).toBe(false);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
describe("resolveAgentLaunch", () => {
|
|
229
|
+
const gemini = {
|
|
230
|
+
id: "gemini",
|
|
231
|
+
name: "Gemini CLI",
|
|
232
|
+
version: "0.58.0",
|
|
233
|
+
description: "Google's CLI",
|
|
234
|
+
distribution: { npx: { package: "@google/gemini-cli@0.58.0", args: ["--acp"] } },
|
|
235
|
+
};
|
|
236
|
+
const pyAgent = {
|
|
237
|
+
id: "fast-agent",
|
|
238
|
+
name: "fast-agent",
|
|
239
|
+
version: "1.0.0",
|
|
240
|
+
description: "A Python agent",
|
|
241
|
+
distribution: { uvx: { package: "fast-agent-mcp" } },
|
|
242
|
+
};
|
|
243
|
+
const binaryOnly = {
|
|
244
|
+
...agent,
|
|
245
|
+
distribution: {
|
|
246
|
+
binary: {
|
|
247
|
+
"linux-x86_64": { archive: "https://x/y.tar.gz", sha256: "a".repeat(64), cmd: "./demo" },
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
const registry = {
|
|
252
|
+
version: "1.0.0",
|
|
253
|
+
agents: [gemini, pyAgent, binaryOnly],
|
|
254
|
+
};
|
|
255
|
+
it("runs npm-distributed agents through npx without downloading anything", async () => {
|
|
256
|
+
const fetchImpl = vi.fn();
|
|
257
|
+
const spec = await resolveAgentLaunch({
|
|
258
|
+
id: "gemini",
|
|
259
|
+
registry,
|
|
260
|
+
platformTarget: "darwin-aarch64",
|
|
261
|
+
fetchImpl: fetchImpl,
|
|
262
|
+
});
|
|
263
|
+
expect(spec).toEqual({
|
|
264
|
+
command: "npx",
|
|
265
|
+
args: ["-y", "@google/gemini-cli@0.58.0", "--acp"],
|
|
266
|
+
env: undefined,
|
|
267
|
+
kind: "npx",
|
|
268
|
+
});
|
|
269
|
+
expect(fetchImpl).not.toHaveBeenCalled();
|
|
270
|
+
});
|
|
271
|
+
it("names npx explicitly on Windows, where a bare npx cannot be spawned", async () => {
|
|
272
|
+
const spec = await resolveAgentLaunch({
|
|
273
|
+
id: "gemini",
|
|
274
|
+
registry,
|
|
275
|
+
platformTarget: "windows-x86_64",
|
|
276
|
+
platform: "win32",
|
|
277
|
+
});
|
|
278
|
+
expect(spec.command).toBe("npx.cmd");
|
|
279
|
+
});
|
|
280
|
+
it("runs PyPI-distributed agents through uvx", async () => {
|
|
281
|
+
const spec = await resolveAgentLaunch({
|
|
282
|
+
id: "fast-agent",
|
|
283
|
+
registry,
|
|
284
|
+
platformTarget: "linux-x86_64",
|
|
285
|
+
});
|
|
286
|
+
expect(spec).toEqual({
|
|
287
|
+
command: "uvx",
|
|
288
|
+
args: ["fast-agent-mcp"],
|
|
289
|
+
env: undefined,
|
|
290
|
+
kind: "uvx",
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
it("installs the binary when that is the only distribution", async () => {
|
|
294
|
+
const tarball = await makeTarball("demo", "#!/bin/sh\nexit 0\n");
|
|
295
|
+
const registryWithDigest = {
|
|
296
|
+
version: "1.0.0",
|
|
297
|
+
agents: [
|
|
298
|
+
{
|
|
299
|
+
...binaryOnly,
|
|
300
|
+
distribution: {
|
|
301
|
+
binary: {
|
|
302
|
+
"linux-x86_64": {
|
|
303
|
+
archive: "https://x/y.tar.gz",
|
|
304
|
+
sha256: sha256(tarball),
|
|
305
|
+
cmd: "./demo",
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
};
|
|
312
|
+
const fetchImpl = vi.fn().mockResolvedValue(okResponse(tarball));
|
|
313
|
+
const spec = await resolveAgentLaunch({
|
|
314
|
+
id: "demo-acp",
|
|
315
|
+
registry: registryWithDigest,
|
|
316
|
+
platformTarget: "linux-x86_64",
|
|
317
|
+
cacheDir,
|
|
318
|
+
fetchImpl: fetchImpl,
|
|
319
|
+
platform: "linux",
|
|
320
|
+
});
|
|
321
|
+
expect(spec.kind).toBe("binary");
|
|
322
|
+
expect(existsSync(spec.command)).toBe(true);
|
|
323
|
+
});
|
|
324
|
+
it("reports an id the registry does not list", async () => {
|
|
325
|
+
await expect(resolveAgentLaunch({ id: "nope", registry, platformTarget: "linux-x86_64" })).rejects.toThrow(/No agent "nope" in the ACP registry/);
|
|
326
|
+
});
|
|
327
|
+
it("reports an agent that has no build for this machine", async () => {
|
|
328
|
+
await expect(resolveAgentLaunch({ id: "demo-acp", registry, platformTarget: "windows-aarch64" })).rejects.toThrow(/publishes no build of "demo-acp" for windows-aarch64 \(it publishes: binary\)/);
|
|
329
|
+
});
|
|
330
|
+
it("says so when an agent publishes no distribution at all", async () => {
|
|
331
|
+
const empty = {
|
|
332
|
+
version: "1.0.0",
|
|
333
|
+
agents: [{ ...agent, distribution: {} }],
|
|
334
|
+
};
|
|
335
|
+
await expect(resolveAgentLaunch({ id: "demo-acp", registry: empty, platformTarget: "linux-x86_64" })).rejects.toThrow(/it publishes: nothing/);
|
|
336
|
+
});
|
|
337
|
+
it("reports an unsupported platform by name", async () => {
|
|
338
|
+
await expect(resolveAgentLaunch({ id: "demo-acp", registry, platformTarget: undefined })).rejects.toThrow(/publishes no build of "demo-acp" for/);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
//# sourceMappingURL=agentInstall.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentInstall.test.js","sourceRoot":"","sources":["../../src/__tests__/agentInstall.test.ts"],"names":[],"mappings":"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"}
|