acp-websocket-transport 0.1.1
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/dist/examples/client.d.ts +9 -0
- package/dist/examples/client.d.ts.map +1 -0
- package/dist/examples/client.js +51 -0
- package/dist/examples/client.js.map +1 -0
- package/dist/examples/echo-agent.d.ts +14 -0
- package/dist/examples/echo-agent.d.ts.map +1 -0
- package/dist/examples/echo-agent.js +67 -0
- package/dist/examples/echo-agent.js.map +1 -0
- package/dist/examples/server.d.ts +11 -0
- package/dist/examples/server.d.ts.map +1 -0
- package/dist/examples/server.js +36 -0
- package/dist/examples/server.js.map +1 -0
- package/dist/src/auth.d.ts +110 -0
- package/dist/src/auth.d.ts.map +1 -0
- package/dist/src/auth.js +135 -0
- package/dist/src/auth.js.map +1 -0
- package/dist/src/client.d.ts +109 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +125 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/index.d.ts +58 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +59 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/server.d.ts +117 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +203 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/transport.d.ts +24 -0
- package/dist/src/transport.d.ts.map +1 -0
- package/dist/src/transport.js +120 -0
- package/dist/src/transport.js.map +1 -0
- package/dist/tests/auth.test.d.ts +2 -0
- package/dist/tests/auth.test.d.ts.map +1 -0
- package/dist/tests/auth.test.js +115 -0
- package/dist/tests/auth.test.js.map +1 -0
- package/dist/tests/client.test.d.ts +2 -0
- package/dist/tests/client.test.d.ts.map +1 -0
- package/dist/tests/client.test.js +121 -0
- package/dist/tests/client.test.js.map +1 -0
- package/dist/tests/e2e.test.d.ts +8 -0
- package/dist/tests/e2e.test.d.ts.map +1 -0
- package/dist/tests/e2e.test.js +130 -0
- package/dist/tests/e2e.test.js.map +1 -0
- package/dist/tests/server.test.d.ts +2 -0
- package/dist/tests/server.test.d.ts.map +1 -0
- package/dist/tests/server.test.js +173 -0
- package/dist/tests/server.test.js.map +1 -0
- package/dist/tests/transport.test.d.ts +2 -0
- package/dist/tests/transport.test.d.ts.map +1 -0
- package/dist/tests/transport.test.js +137 -0
- package/dist/tests/transport.test.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket transport for ACP (Agent Client Protocol).
|
|
3
|
+
*
|
|
4
|
+
* Converts a `ws` WebSocket into the SDK's {@link Stream} interface,
|
|
5
|
+
* enabling ACP connections over WebSocket instead of stdio.
|
|
6
|
+
*
|
|
7
|
+
* Each WebSocket text frame carries exactly one JSON-encoded AnyMessage.
|
|
8
|
+
* Binary frames are silently ignored (ACP only uses text frames).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Safely converts ws `RawData` (Buffer | ArrayBuffer | Buffer[]) to a UTF-8 string.
|
|
12
|
+
* Handles all three variants that `ws` may produce for text frames.
|
|
13
|
+
*/
|
|
14
|
+
function rawDataToString(data) {
|
|
15
|
+
if (Array.isArray(data)) {
|
|
16
|
+
// Buffer[] — concatenate all chunks first
|
|
17
|
+
return Buffer.concat(data).toString("utf8");
|
|
18
|
+
}
|
|
19
|
+
if (data instanceof ArrayBuffer) {
|
|
20
|
+
return Buffer.from(data).toString("utf8");
|
|
21
|
+
}
|
|
22
|
+
// Buffer (most common case for text frames)
|
|
23
|
+
return data.toString("utf8");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates an ACP {@link Stream} from an open (or opening) `ws` WebSocket.
|
|
27
|
+
*
|
|
28
|
+
* Stream lifetime is tied to the WebSocket:
|
|
29
|
+
* - WS `close` / `error` → readable stream closes / errors
|
|
30
|
+
* - WritableStream `close()` → `ws.close(1000, ...)`
|
|
31
|
+
* - WritableStream `abort()` → `ws.terminate()` (hard close)
|
|
32
|
+
*
|
|
33
|
+
* @param ws - An open or opening `ws` WebSocket instance
|
|
34
|
+
* @returns Bidirectional ACP message stream
|
|
35
|
+
*/
|
|
36
|
+
export function wsStream(ws) {
|
|
37
|
+
// ─── Readable side ───────────────────────────────────────────────────
|
|
38
|
+
// Buffers incoming frames until the consumer pulls them.
|
|
39
|
+
// ReadableStream's built-in queue provides natural backpressure.
|
|
40
|
+
let readableController;
|
|
41
|
+
const readable = new ReadableStream({
|
|
42
|
+
start(controller) {
|
|
43
|
+
readableController = controller;
|
|
44
|
+
ws.on("message", (data, isBinary) => {
|
|
45
|
+
// ACP uses only text frames; drop binary frames silently
|
|
46
|
+
if (isBinary)
|
|
47
|
+
return;
|
|
48
|
+
const text = rawDataToString(data);
|
|
49
|
+
let message;
|
|
50
|
+
try {
|
|
51
|
+
message = JSON.parse(text);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Malformed JSON frame — close the readable with an error
|
|
55
|
+
// so the connection layer can surface it to the caller.
|
|
56
|
+
try {
|
|
57
|
+
controller.error(new SyntaxError(`Invalid JSON in WebSocket frame: ${text.slice(0, 200)}`));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
/* controller already closed/errored */
|
|
61
|
+
}
|
|
62
|
+
ws.close(1003, "Invalid JSON");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
controller.enqueue(message);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* controller already closed */
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
ws.on("close", () => {
|
|
73
|
+
try {
|
|
74
|
+
controller.close();
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
/* already closed */
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
ws.on("error", (err) => {
|
|
81
|
+
try {
|
|
82
|
+
controller.error(err);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
/* already errored */
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
cancel() {
|
|
90
|
+
// Consumer cancelled the readable — close the WebSocket cleanly
|
|
91
|
+
ws.close(1000, "Stream cancelled");
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
// ─── Writable side ───────────────────────────────────────────────────
|
|
95
|
+
// Serialises each AnyMessage to a JSON text frame and calls ws.send().
|
|
96
|
+
// The Promise-based send callback provides backpressure to the
|
|
97
|
+
// WritableStream's internal queue.
|
|
98
|
+
const writable = new WritableStream({
|
|
99
|
+
write(message) {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const payload = JSON.stringify(message);
|
|
102
|
+
ws.send(payload, (err) => {
|
|
103
|
+
if (err != null)
|
|
104
|
+
reject(err);
|
|
105
|
+
else
|
|
106
|
+
resolve();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
close() {
|
|
111
|
+
ws.close(1000, "Stream closed");
|
|
112
|
+
},
|
|
113
|
+
abort(_reason) {
|
|
114
|
+
// Hard close — destroy the TCP socket immediately without a close frame
|
|
115
|
+
ws.terminate();
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
return { readable, writable };
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../../tests/auth.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { bearerAuth, basicAuth, apiKeyAuth, anyAuth, } from "../src/auth.js";
|
|
3
|
+
/** Creates a minimal mock VerifyClientInfo with the given headers. */
|
|
4
|
+
function mockInfo(headers) {
|
|
5
|
+
// IncomingMessage headers are lowercase by the Node.js HTTP parser
|
|
6
|
+
const lowerHeaders = {};
|
|
7
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
8
|
+
lowerHeaders[k.toLowerCase()] = v;
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
req: { headers: lowerHeaders },
|
|
12
|
+
origin: "",
|
|
13
|
+
secure: false,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// ─── bearerAuth ───────────────────────────────────────────────────────────────
|
|
17
|
+
describe("bearerAuth", () => {
|
|
18
|
+
const verify = bearerAuth(["token-alice", "token-bob"]);
|
|
19
|
+
it("accepts a valid bearer token", () => {
|
|
20
|
+
expect(verify(mockInfo({ authorization: "Bearer token-alice" }))).toBe(true);
|
|
21
|
+
expect(verify(mockInfo({ authorization: "Bearer token-bob" }))).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("rejects an unknown token", () => {
|
|
24
|
+
expect(verify(mockInfo({ authorization: "Bearer unknown" }))).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it("is case-sensitive for the token value", () => {
|
|
27
|
+
expect(verify(mockInfo({ authorization: "Bearer TOKEN-ALICE" }))).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
it("is case-insensitive for the 'Bearer' scheme keyword", () => {
|
|
30
|
+
expect(verify(mockInfo({ authorization: "bearer token-alice" }))).toBe(true);
|
|
31
|
+
expect(verify(mockInfo({ authorization: "BEARER token-alice" }))).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it("rejects a non-bearer scheme", () => {
|
|
34
|
+
expect(verify(mockInfo({ authorization: "Basic dG9rZW4tYWxpY2U=" }))).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it("rejects a missing Authorization header", () => {
|
|
37
|
+
expect(verify(mockInfo({}))).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
it("rejects a malformed Authorization header with no space", () => {
|
|
40
|
+
expect(verify(mockInfo({ authorization: "BearerNoSpace" }))).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
// ─── basicAuth ────────────────────────────────────────────────────────────────
|
|
44
|
+
describe("basicAuth", () => {
|
|
45
|
+
const verify = basicAuth({ alice: "pass1", bob: "pass2" });
|
|
46
|
+
it("accepts valid credentials", () => {
|
|
47
|
+
const encoded = Buffer.from("alice:pass1").toString("base64");
|
|
48
|
+
expect(verify(mockInfo({ authorization: `Basic ${encoded}` }))).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it("rejects wrong password", () => {
|
|
51
|
+
const encoded = Buffer.from("alice:wrong").toString("base64");
|
|
52
|
+
expect(verify(mockInfo({ authorization: `Basic ${encoded}` }))).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
it("rejects unknown user", () => {
|
|
55
|
+
const encoded = Buffer.from("charlie:pass1").toString("base64");
|
|
56
|
+
expect(verify(mockInfo({ authorization: `Basic ${encoded}` }))).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
it("is case-insensitive for the 'Basic' scheme keyword", () => {
|
|
59
|
+
const encoded = Buffer.from("alice:pass1").toString("base64");
|
|
60
|
+
expect(verify(mockInfo({ authorization: `basic ${encoded}` }))).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
it("handles passwords containing colons correctly", () => {
|
|
63
|
+
const verifyColonPass = basicAuth({ user: "pass:with:colons" });
|
|
64
|
+
const encoded = Buffer.from("user:pass:with:colons").toString("base64");
|
|
65
|
+
expect(verifyColonPass(mockInfo({ authorization: `Basic ${encoded}` }))).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it("rejects missing Authorization header", () => {
|
|
68
|
+
expect(verify(mockInfo({}))).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
it("rejects a non-basic scheme", () => {
|
|
71
|
+
expect(verify(mockInfo({ authorization: "Bearer alice:pass1" }))).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
// ─── apiKeyAuth ───────────────────────────────────────────────────────────────
|
|
75
|
+
describe("apiKeyAuth", () => {
|
|
76
|
+
const verify = apiKeyAuth("x-api-key", ["key1", "key2"]);
|
|
77
|
+
it("accepts a valid API key", () => {
|
|
78
|
+
expect(verify(mockInfo({ "x-api-key": "key1" }))).toBe(true);
|
|
79
|
+
expect(verify(mockInfo({ "x-api-key": "key2" }))).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
it("rejects an unknown key", () => {
|
|
82
|
+
expect(verify(mockInfo({ "x-api-key": "key3" }))).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
it("is case-insensitive for the header name", () => {
|
|
85
|
+
// Node.js lowercases header names, but the factory itself normalises too
|
|
86
|
+
const verifyUpper = apiKeyAuth("X-API-Key", ["key1"]);
|
|
87
|
+
expect(verifyUpper(mockInfo({ "x-api-key": "key1" }))).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
it("rejects a missing header", () => {
|
|
90
|
+
expect(verify(mockInfo({}))).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
it("rejects an empty key", () => {
|
|
93
|
+
expect(verify(mockInfo({ "x-api-key": "" }))).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
// ─── anyAuth ──────────────────────────────────────────────────────────────────
|
|
97
|
+
describe("anyAuth", () => {
|
|
98
|
+
const bearer = bearerAuth(["bearer-tok"]);
|
|
99
|
+
const apiKey = apiKeyAuth("x-api-key", ["api-key-tok"]);
|
|
100
|
+
const combined = anyAuth(bearer, apiKey);
|
|
101
|
+
it("accepts if the first verifier passes", async () => {
|
|
102
|
+
expect(await combined(mockInfo({ authorization: "Bearer bearer-tok" }))).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
it("accepts if the second verifier passes", async () => {
|
|
105
|
+
expect(await combined(mockInfo({ "x-api-key": "api-key-tok" }))).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
it("rejects if no verifier passes", async () => {
|
|
108
|
+
expect(await combined(mockInfo({}))).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
it("accepts with an async verifier", async () => {
|
|
111
|
+
const asyncVerifier = anyAuth(async (_info) => true);
|
|
112
|
+
expect(await asyncVerifier(mockInfo({}))).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=auth.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../../tests/auth.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../tests/client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { WebSocketServer } from "ws";
|
|
3
|
+
import { createClientConnection, withBearerToken, withBasicAuth, withApiKey, } from "../src/client.js";
|
|
4
|
+
/** Creates a minimal echo WebSocket server for testing. */
|
|
5
|
+
async function createEchoServer() {
|
|
6
|
+
const connections = [];
|
|
7
|
+
const wss = new WebSocketServer({ port: 0 });
|
|
8
|
+
wss.on("connection", (ws) => {
|
|
9
|
+
connections.push(ws);
|
|
10
|
+
// Echo all messages back (simulates an ACP agent stream)
|
|
11
|
+
ws.on("message", (data) => ws.send(data));
|
|
12
|
+
});
|
|
13
|
+
await new Promise((resolve) => wss.once("listening", resolve));
|
|
14
|
+
const port = wss.address().port;
|
|
15
|
+
return { wss, port, connections };
|
|
16
|
+
}
|
|
17
|
+
async function closeServer(wss) {
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
wss.clients.forEach((ws) => ws.terminate());
|
|
20
|
+
wss.close(() => resolve());
|
|
21
|
+
setTimeout(resolve, 500);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
describe("createClientConnection", () => {
|
|
25
|
+
let wss;
|
|
26
|
+
let port;
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
({ wss, port } = await createEchoServer());
|
|
29
|
+
});
|
|
30
|
+
afterEach(async () => {
|
|
31
|
+
await closeServer(wss);
|
|
32
|
+
});
|
|
33
|
+
it("opens a WebSocket and returns a ClientConnection", async () => {
|
|
34
|
+
const { connection, stream, close } = await createClientConnection(`ws://localhost:${port}`);
|
|
35
|
+
expect(connection).toBeDefined();
|
|
36
|
+
expect(stream).toBeDefined();
|
|
37
|
+
expect(typeof close).toBe("function");
|
|
38
|
+
close();
|
|
39
|
+
});
|
|
40
|
+
it("resolves the connection once the WebSocket is open", async () => {
|
|
41
|
+
const { close } = await createClientConnection(`ws://localhost:${port}`);
|
|
42
|
+
// If we got here without an error, the connection is open
|
|
43
|
+
expect(true).toBe(true);
|
|
44
|
+
close();
|
|
45
|
+
});
|
|
46
|
+
it("rejects if the server is not reachable", async () => {
|
|
47
|
+
await expect(createClientConnection("ws://localhost:1", { connectTimeout: 500 })).rejects.toThrow();
|
|
48
|
+
});
|
|
49
|
+
it("rejects if the connection times out", async () => {
|
|
50
|
+
// Point to a host that never responds to simulate a timeout
|
|
51
|
+
// Use an unrouteable address (TEST-NET-1) to force a timeout
|
|
52
|
+
await expect(createClientConnection("ws://192.0.2.1:9999", { connectTimeout: 200 })).rejects.toThrow(/timed out/i);
|
|
53
|
+
});
|
|
54
|
+
it("calls close() to shut down the connection", async () => {
|
|
55
|
+
const { close } = await createClientConnection(`ws://localhost:${port}`);
|
|
56
|
+
// Should not throw
|
|
57
|
+
expect(() => close()).not.toThrow();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
// ─── Auth helpers ─────────────────────────────────────────────────────────────
|
|
61
|
+
describe("withBearerToken", () => {
|
|
62
|
+
it("returns wsOptions with Authorization: Bearer header", () => {
|
|
63
|
+
const opts = withBearerToken("my-token");
|
|
64
|
+
expect(opts.wsOptions?.headers).toMatchObject({
|
|
65
|
+
Authorization: "Bearer my-token",
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("withBasicAuth", () => {
|
|
70
|
+
it("returns wsOptions with Authorization: Basic header", () => {
|
|
71
|
+
const opts = withBasicAuth("user", "pass");
|
|
72
|
+
const expected = Buffer.from("user:pass").toString("base64");
|
|
73
|
+
expect(opts.wsOptions?.headers).toMatchObject({
|
|
74
|
+
Authorization: `Basic ${expected}`,
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
it("correctly encodes credentials with special characters", () => {
|
|
78
|
+
const opts = withBasicAuth("user@example.com", "p@$$word:123");
|
|
79
|
+
const expected = Buffer.from("user@example.com:p@$$word:123").toString("base64");
|
|
80
|
+
expect(opts.wsOptions?.headers).toMatchObject({
|
|
81
|
+
Authorization: `Basic ${expected}`,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("withApiKey", () => {
|
|
86
|
+
it("returns wsOptions with the given header", () => {
|
|
87
|
+
const opts = withApiKey("x-api-key", "abc123");
|
|
88
|
+
expect(opts.wsOptions?.headers).toMatchObject({
|
|
89
|
+
"x-api-key": "abc123",
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
it("preserves the header name casing provided by the caller", () => {
|
|
93
|
+
const opts = withApiKey("X-Custom-Auth", "token");
|
|
94
|
+
expect(opts.wsOptions?.headers).toMatchObject({
|
|
95
|
+
"X-Custom-Auth": "token",
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
// ─── auth headers reach the server ───────────────────────────────────────────
|
|
100
|
+
describe("auth headers in upgrade request", () => {
|
|
101
|
+
it("sends the Authorization header during the WebSocket upgrade", async () => {
|
|
102
|
+
const receivedHeaders = [];
|
|
103
|
+
const wssAuth = new WebSocketServer({ port: 0 });
|
|
104
|
+
wssAuth.on("connection", (_ws, req) => {
|
|
105
|
+
receivedHeaders.push(req.headers);
|
|
106
|
+
});
|
|
107
|
+
await new Promise((resolve) => wssAuth.once("listening", resolve));
|
|
108
|
+
const authPort = wssAuth.address().port;
|
|
109
|
+
try {
|
|
110
|
+
const { close } = await createClientConnection(`ws://localhost:${authPort}`, withBearerToken("test-bearer-token"));
|
|
111
|
+
// Give the server a moment to record the headers
|
|
112
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
113
|
+
expect(receivedHeaders[0]?.["authorization"]).toBe("Bearer test-bearer-token");
|
|
114
|
+
close();
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
await closeServer(wssAuth);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../tests/client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e.test.d.ts","sourceRoot":"","sources":["../../tests/e2e.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end tests: Client library → WebSocketACPServer → echo-agent process.
|
|
3
|
+
*
|
|
4
|
+
* These tests exercise the full stack:
|
|
5
|
+
* createClientConnection → WebSocketACPServer → echo-agent (stdio)
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
8
|
+
import { WebSocketACPServer } from "../src/server.js";
|
|
9
|
+
import { createClientConnection, withBearerToken } from "../src/client.js";
|
|
10
|
+
import { bearerAuth } from "../src/auth.js";
|
|
11
|
+
const ECHO_AGENT = new URL("../dist/examples/echo-agent.js", import.meta.url)
|
|
12
|
+
.pathname;
|
|
13
|
+
// ─── No-auth E2E ──────────────────────────────────────────────────────────────
|
|
14
|
+
describe("E2E (no auth)", () => {
|
|
15
|
+
let server;
|
|
16
|
+
let wsUrl;
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
server = new WebSocketACPServer({
|
|
19
|
+
port: 0,
|
|
20
|
+
command: "node",
|
|
21
|
+
args: [ECHO_AGENT],
|
|
22
|
+
});
|
|
23
|
+
await server.listening();
|
|
24
|
+
const { port } = server.address;
|
|
25
|
+
wsUrl = `ws://localhost:${port}`;
|
|
26
|
+
});
|
|
27
|
+
afterAll(async () => {
|
|
28
|
+
await server.close().catch(() => { });
|
|
29
|
+
});
|
|
30
|
+
it("completes initialize → session/new → prompt flow", async () => {
|
|
31
|
+
const updates = [];
|
|
32
|
+
const { connection, close } = await createClientConnection(wsUrl, {
|
|
33
|
+
toClient: (_agent) => ({
|
|
34
|
+
sessionUpdate: async (params) => {
|
|
35
|
+
const { update } = params;
|
|
36
|
+
if ("content" in update && update.content.type === "text") {
|
|
37
|
+
updates.push(update.content.text);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
requestPermission: async () => ({ outcome: "allow_once" }),
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
// 1. Initialize
|
|
44
|
+
const init = await connection.initialize({
|
|
45
|
+
protocolVersion: 0,
|
|
46
|
+
clientInfo: { name: "e2e-test", version: "0.0.1" },
|
|
47
|
+
clientCapabilities: {},
|
|
48
|
+
});
|
|
49
|
+
expect(init.agentInfo?.name).toBe("echo-agent");
|
|
50
|
+
// 2. Create session
|
|
51
|
+
const session = await connection.newSession({
|
|
52
|
+
cwd: "/tmp",
|
|
53
|
+
mcpServers: [],
|
|
54
|
+
});
|
|
55
|
+
expect(typeof session.sessionId).toBe("string");
|
|
56
|
+
expect(session.sessionId.length).toBeGreaterThan(0);
|
|
57
|
+
// 3. Send prompt
|
|
58
|
+
const result = await connection.prompt({
|
|
59
|
+
sessionId: session.sessionId,
|
|
60
|
+
prompt: [{ type: "text", text: "Hello, world!" }],
|
|
61
|
+
});
|
|
62
|
+
expect(result.stopReason).toBe("end_turn");
|
|
63
|
+
// 4. The echo agent sends a session/update notification before responding
|
|
64
|
+
expect(updates.some((t) => t.includes("Echo: Hello, world!"))).toBe(true);
|
|
65
|
+
close();
|
|
66
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
67
|
+
});
|
|
68
|
+
it("handles multiple sessions on the same server", async () => {
|
|
69
|
+
// Open two independent client connections (each gets its own agent process)
|
|
70
|
+
const { connection: c1, close: close1 } = await createClientConnection(wsUrl);
|
|
71
|
+
const { connection: c2, close: close2 } = await createClientConnection(wsUrl);
|
|
72
|
+
const [init1, init2] = await Promise.all([
|
|
73
|
+
c1.initialize({ protocolVersion: 0, clientCapabilities: {} }),
|
|
74
|
+
c2.initialize({ protocolVersion: 0, clientCapabilities: {} }),
|
|
75
|
+
]);
|
|
76
|
+
expect(init1.agentInfo?.name).toBe("echo-agent");
|
|
77
|
+
expect(init2.agentInfo?.name).toBe("echo-agent");
|
|
78
|
+
const [sess1, sess2] = await Promise.all([
|
|
79
|
+
c1.newSession({ cwd: "/tmp", mcpServers: [] }),
|
|
80
|
+
c2.newSession({ cwd: "/tmp", mcpServers: [] }),
|
|
81
|
+
]);
|
|
82
|
+
// Each connection has its own independent session
|
|
83
|
+
expect(sess1.sessionId).not.toBe(sess2.sessionId);
|
|
84
|
+
close1();
|
|
85
|
+
close2();
|
|
86
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
// ─── Bearer-auth E2E ──────────────────────────────────────────────────────────
|
|
90
|
+
describe("E2E (bearer auth)", () => {
|
|
91
|
+
const VALID_TOKEN = "e2e-test-token";
|
|
92
|
+
let server;
|
|
93
|
+
let wsUrl;
|
|
94
|
+
beforeAll(async () => {
|
|
95
|
+
server = new WebSocketACPServer({
|
|
96
|
+
port: 0,
|
|
97
|
+
command: "node",
|
|
98
|
+
args: [ECHO_AGENT],
|
|
99
|
+
verifyClient: bearerAuth([VALID_TOKEN]),
|
|
100
|
+
});
|
|
101
|
+
await server.listening();
|
|
102
|
+
const { port } = server.address;
|
|
103
|
+
wsUrl = `ws://localhost:${port}`;
|
|
104
|
+
});
|
|
105
|
+
afterAll(async () => {
|
|
106
|
+
await server.close().catch(() => { });
|
|
107
|
+
});
|
|
108
|
+
it("connects successfully with the correct token", async () => {
|
|
109
|
+
const { connection, close } = await createClientConnection(wsUrl, {
|
|
110
|
+
...withBearerToken(VALID_TOKEN),
|
|
111
|
+
});
|
|
112
|
+
const init = await connection.initialize({
|
|
113
|
+
protocolVersion: 0,
|
|
114
|
+
clientCapabilities: {},
|
|
115
|
+
});
|
|
116
|
+
expect(init.agentInfo?.name).toBe("echo-agent");
|
|
117
|
+
close();
|
|
118
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
119
|
+
});
|
|
120
|
+
it("rejects connection with a wrong token", async () => {
|
|
121
|
+
await expect(createClientConnection(wsUrl, {
|
|
122
|
+
...withBearerToken("wrong-token"),
|
|
123
|
+
connectTimeout: 3000,
|
|
124
|
+
})).rejects.toThrow();
|
|
125
|
+
});
|
|
126
|
+
it("rejects connection with no token", async () => {
|
|
127
|
+
await expect(createClientConnection(wsUrl, { connectTimeout: 3000 })).rejects.toThrow();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e.test.js","sourceRoot":"","sources":["../../tests/e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.test.d.ts","sourceRoot":"","sources":["../../tests/server.test.ts"],"names":[],"mappings":""}
|