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.
Files changed (53) hide show
  1. package/dist/examples/client.d.ts +9 -0
  2. package/dist/examples/client.d.ts.map +1 -0
  3. package/dist/examples/client.js +51 -0
  4. package/dist/examples/client.js.map +1 -0
  5. package/dist/examples/echo-agent.d.ts +14 -0
  6. package/dist/examples/echo-agent.d.ts.map +1 -0
  7. package/dist/examples/echo-agent.js +67 -0
  8. package/dist/examples/echo-agent.js.map +1 -0
  9. package/dist/examples/server.d.ts +11 -0
  10. package/dist/examples/server.d.ts.map +1 -0
  11. package/dist/examples/server.js +36 -0
  12. package/dist/examples/server.js.map +1 -0
  13. package/dist/src/auth.d.ts +110 -0
  14. package/dist/src/auth.d.ts.map +1 -0
  15. package/dist/src/auth.js +135 -0
  16. package/dist/src/auth.js.map +1 -0
  17. package/dist/src/client.d.ts +109 -0
  18. package/dist/src/client.d.ts.map +1 -0
  19. package/dist/src/client.js +125 -0
  20. package/dist/src/client.js.map +1 -0
  21. package/dist/src/index.d.ts +58 -0
  22. package/dist/src/index.d.ts.map +1 -0
  23. package/dist/src/index.js +59 -0
  24. package/dist/src/index.js.map +1 -0
  25. package/dist/src/server.d.ts +117 -0
  26. package/dist/src/server.d.ts.map +1 -0
  27. package/dist/src/server.js +203 -0
  28. package/dist/src/server.js.map +1 -0
  29. package/dist/src/transport.d.ts +24 -0
  30. package/dist/src/transport.d.ts.map +1 -0
  31. package/dist/src/transport.js +120 -0
  32. package/dist/src/transport.js.map +1 -0
  33. package/dist/tests/auth.test.d.ts +2 -0
  34. package/dist/tests/auth.test.d.ts.map +1 -0
  35. package/dist/tests/auth.test.js +115 -0
  36. package/dist/tests/auth.test.js.map +1 -0
  37. package/dist/tests/client.test.d.ts +2 -0
  38. package/dist/tests/client.test.d.ts.map +1 -0
  39. package/dist/tests/client.test.js +121 -0
  40. package/dist/tests/client.test.js.map +1 -0
  41. package/dist/tests/e2e.test.d.ts +8 -0
  42. package/dist/tests/e2e.test.d.ts.map +1 -0
  43. package/dist/tests/e2e.test.js +130 -0
  44. package/dist/tests/e2e.test.js.map +1 -0
  45. package/dist/tests/server.test.d.ts +2 -0
  46. package/dist/tests/server.test.d.ts.map +1 -0
  47. package/dist/tests/server.test.js +173 -0
  48. package/dist/tests/server.test.js.map +1 -0
  49. package/dist/tests/transport.test.d.ts +2 -0
  50. package/dist/tests/transport.test.d.ts.map +1 -0
  51. package/dist/tests/transport.test.js +137 -0
  52. package/dist/tests/transport.test.js.map +1 -0
  53. package/package.json +41 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * ACP WebSocket client library.
3
+ *
4
+ * Provides a high-level `createClientConnection` function that opens a WebSocket
5
+ * to an {@link WebSocketACPServer} (or any ACP-over-WebSocket server) and returns
6
+ * a fully initialized `ClientSideConnection` from `@agentclientprotocol/sdk`.
7
+ *
8
+ * Authentication helpers (`withBearerToken`, `withBasicAuth`, `withApiKey`) generate
9
+ * the `wsOptions` fragment needed to pass credentials during the HTTP upgrade handshake.
10
+ *
11
+ * @example Bearer token
12
+ * ```ts
13
+ * import { createClientConnection, withBearerToken } from "acp-websocket-transport";
14
+ *
15
+ * const { connection, close } = await createClientConnection(
16
+ * "ws://localhost:3000",
17
+ * {
18
+ * ...withBearerToken("my-secret"),
19
+ * toClient: (agent) => ({
20
+ * sessionUpdate: async (params) => { console.log(params); },
21
+ * requestPermission: async () => ({ outcome: "allow_once" }),
22
+ * }),
23
+ * }
24
+ * );
25
+ *
26
+ * const init = await connection.initialize({ protocolVersion: 0, clientCapabilities: {} });
27
+ * console.log(init.agentInfo);
28
+ * close();
29
+ * ```
30
+ */
31
+ import WebSocket from "ws";
32
+ import { ClientSideConnection } from "@agentclientprotocol/sdk";
33
+ import { wsStream } from "./transport.js";
34
+ // ─── Connection factory ───────────────────────────────────────────────────────
35
+ /**
36
+ * Opens a WebSocket connection to an ACP server and returns a
37
+ * `ClientSideConnection` ready for use.
38
+ *
39
+ * @param url WebSocket URL of the ACP server (e.g. `"ws://localhost:3000"`)
40
+ * @param options Optional configuration (auth headers, client handler, timeout)
41
+ * @throws If the WebSocket fails to open within `connectTimeout` ms
42
+ */
43
+ export async function createClientConnection(url, options = {}) {
44
+ const { toClient, protocols, wsOptions, connectTimeout = 10_000 } = options;
45
+ const ws = new WebSocket(url, protocols, wsOptions);
46
+ await waitForOpen(ws, connectTimeout);
47
+ const stream = wsStream(ws);
48
+ // Default no-op client for callers that only make outgoing requests
49
+ const noopClient = () => ({
50
+ sessionUpdate: async () => { },
51
+ requestPermission: async () => ({
52
+ outcome: "reject_once",
53
+ }),
54
+ });
55
+ const connection = new ClientSideConnection(toClient ?? noopClient, stream);
56
+ const close = () => ws.close(1000, "Client closed");
57
+ return { connection, stream, close };
58
+ }
59
+ // ─── Auth helpers ─────────────────────────────────────────────────────────────
60
+ /**
61
+ * Returns the `wsOptions` fragment needed to authenticate with a Bearer token.
62
+ *
63
+ * Spread this into the options passed to {@link createClientConnection}:
64
+ * ```ts
65
+ * await createClientConnection(url, { ...withBearerToken("tok"), toClient });
66
+ * ```
67
+ */
68
+ export function withBearerToken(token) {
69
+ return {
70
+ wsOptions: { headers: { Authorization: `Bearer ${token}` } },
71
+ };
72
+ }
73
+ /**
74
+ * Returns the `wsOptions` fragment needed for HTTP Basic authentication.
75
+ *
76
+ * ⚠️ Use TLS (`wss://`) in production — credentials are only base64-encoded.
77
+ */
78
+ export function withBasicAuth(username, password) {
79
+ const encoded = Buffer.from(`${username}:${password}`).toString("base64");
80
+ return {
81
+ wsOptions: { headers: { Authorization: `Basic ${encoded}` } },
82
+ };
83
+ }
84
+ /**
85
+ * Returns the `wsOptions` fragment needed to authenticate via an arbitrary
86
+ * HTTP header (e.g. an API key).
87
+ *
88
+ * @param header Header name (e.g. `"x-api-key"`)
89
+ * @param key The key value
90
+ */
91
+ export function withApiKey(header, key) {
92
+ return {
93
+ wsOptions: { headers: { [header]: key } },
94
+ };
95
+ }
96
+ // ─── Internals ────────────────────────────────────────────────────────────────
97
+ /**
98
+ * Waits for a WebSocket to transition to the OPEN state, with a timeout.
99
+ */
100
+ function waitForOpen(ws, timeoutMs) {
101
+ return new Promise((resolve, reject) => {
102
+ if (ws.readyState === WebSocket.OPEN) {
103
+ resolve();
104
+ return;
105
+ }
106
+ const timer = setTimeout(() => {
107
+ ws.terminate();
108
+ reject(new Error(`WebSocket connection timed out after ${timeoutMs}ms`));
109
+ }, timeoutMs);
110
+ const cleanup = () => clearTimeout(timer);
111
+ ws.once("open", () => {
112
+ cleanup();
113
+ resolve();
114
+ });
115
+ ws.once("error", (err) => {
116
+ cleanup();
117
+ reject(err);
118
+ });
119
+ ws.once("close", (code, reason) => {
120
+ cleanup();
121
+ reject(new Error(`WebSocket closed before open: ${code} ${reason.toString()}`));
122
+ });
123
+ });
124
+ }
125
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * acp-websocket-transport
3
+ *
4
+ * WebSocket transport layer for ACP (Agent Client Protocol).
5
+ *
6
+ * ## Architecture
7
+ *
8
+ * ```
9
+ * [Client Library] <── WebSocket ──> [WebSocketACPServer] <── stdio ──> [ACP Agent]
10
+ * ```
11
+ *
12
+ * The server acts as a transparent proxy: it spawns one ACP agent subprocess per
13
+ * WebSocket connection and pipes raw JSON-RPC messages between them.
14
+ *
15
+ * Authentication is handled at the HTTP Upgrade handshake stage using standard
16
+ * HTTP mechanisms (Bearer token, Basic auth, API key header).
17
+ *
18
+ * ## Quick start
19
+ *
20
+ * **Server:**
21
+ * ```ts
22
+ * import { WebSocketACPServer, bearerAuth } from "acp-websocket-transport";
23
+ *
24
+ * const server = new WebSocketACPServer({
25
+ * port: 3000,
26
+ * command: "node",
27
+ * args: ["dist/my-agent.js"],
28
+ * verifyClient: bearerAuth(["my-secret-token"]),
29
+ * });
30
+ * await server.listening();
31
+ * ```
32
+ *
33
+ * **Client:**
34
+ * ```ts
35
+ * import { createClientConnection, withBearerToken } from "acp-websocket-transport";
36
+ *
37
+ * const { connection, close } = await createClientConnection(
38
+ * "ws://localhost:3000",
39
+ * withBearerToken("my-secret-token")
40
+ * );
41
+ *
42
+ * const init = await connection.initialize({ protocolVersion: 0, clientCapabilities: {} });
43
+ * console.log(init.agentInfo);
44
+ * close();
45
+ * ```
46
+ */
47
+ /**
48
+ * Creates an ACP `Stream` from an open `ws` WebSocket.
49
+ * This is the foundational building block — server and client both use it internally.
50
+ */
51
+ export { wsStream } from "./transport.js";
52
+ export { WebSocketACPServer } from "./server.js";
53
+ export type { WebSocketACPServerOptions, ErrorContext } from "./server.js";
54
+ export { createClientConnection, withBearerToken, withBasicAuth, withApiKey } from "./client.js";
55
+ export type { ClientConnectionOptions, ClientConnection } from "./client.js";
56
+ export { bearerAuth, basicAuth, apiKeyAuth, anyAuth } from "./auth.js";
57
+ export type { VerifyClientFn, VerifyClientInfo } from "./auth.js";
58
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3E,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAI7E,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACvE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * acp-websocket-transport
3
+ *
4
+ * WebSocket transport layer for ACP (Agent Client Protocol).
5
+ *
6
+ * ## Architecture
7
+ *
8
+ * ```
9
+ * [Client Library] <── WebSocket ──> [WebSocketACPServer] <── stdio ──> [ACP Agent]
10
+ * ```
11
+ *
12
+ * The server acts as a transparent proxy: it spawns one ACP agent subprocess per
13
+ * WebSocket connection and pipes raw JSON-RPC messages between them.
14
+ *
15
+ * Authentication is handled at the HTTP Upgrade handshake stage using standard
16
+ * HTTP mechanisms (Bearer token, Basic auth, API key header).
17
+ *
18
+ * ## Quick start
19
+ *
20
+ * **Server:**
21
+ * ```ts
22
+ * import { WebSocketACPServer, bearerAuth } from "acp-websocket-transport";
23
+ *
24
+ * const server = new WebSocketACPServer({
25
+ * port: 3000,
26
+ * command: "node",
27
+ * args: ["dist/my-agent.js"],
28
+ * verifyClient: bearerAuth(["my-secret-token"]),
29
+ * });
30
+ * await server.listening();
31
+ * ```
32
+ *
33
+ * **Client:**
34
+ * ```ts
35
+ * import { createClientConnection, withBearerToken } from "acp-websocket-transport";
36
+ *
37
+ * const { connection, close } = await createClientConnection(
38
+ * "ws://localhost:3000",
39
+ * withBearerToken("my-secret-token")
40
+ * );
41
+ *
42
+ * const init = await connection.initialize({ protocolVersion: 0, clientCapabilities: {} });
43
+ * console.log(init.agentInfo);
44
+ * close();
45
+ * ```
46
+ */
47
+ // ── Transport primitive ───────────────────────────────────────────────────────
48
+ /**
49
+ * Creates an ACP `Stream` from an open `ws` WebSocket.
50
+ * This is the foundational building block — server and client both use it internally.
51
+ */
52
+ export { wsStream } from "./transport.js";
53
+ // ── Server ────────────────────────────────────────────────────────────────────
54
+ export { WebSocketACPServer } from "./server.js";
55
+ // ── Client ────────────────────────────────────────────────────────────────────
56
+ export { createClientConnection, withBearerToken, withBasicAuth, withApiKey } from "./client.js";
57
+ // ── Auth helpers ──────────────────────────────────────────────────────────────
58
+ export { bearerAuth, basicAuth, apiKeyAuth, anyAuth } from "./auth.js";
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,iFAAiF;AAEjF;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,iFAAiF;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,iFAAiF;AAEjF,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGjG,iFAAiF;AAEjF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * WebSocket ACP server.
3
+ *
4
+ * Listens for incoming WebSocket connections and, for each one, spawns an
5
+ * ACP-compatible agent process via stdio. Incoming WebSocket frames are
6
+ * forwarded to the agent's stdin, and the agent's stdout is forwarded back
7
+ * as WebSocket frames.
8
+ *
9
+ * The server acts as a **transparent proxy** — it does not interpret ACP
10
+ * messages itself. All protocol handling is end-to-end between the remote
11
+ * client library and the spawned agent process.
12
+ *
13
+ * ```
14
+ * [WS Client] <── WebSocket ──> [WebSocketACPServer] <── stdio ──> [Agent process]
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { WebSocketACPServer, bearerAuth } from "acp-websocket-transport";
20
+ *
21
+ * const server = new WebSocketACPServer({
22
+ * port: 3000,
23
+ * command: "node",
24
+ * args: ["dist/my-agent.js"],
25
+ * verifyClient: bearerAuth(["my-secret-token"]),
26
+ * });
27
+ *
28
+ * await server.listening();
29
+ * console.log("Listening on", server.address);
30
+ * ```
31
+ */
32
+ import type WebSocket from "ws";
33
+ import type { VerifyClientFn } from "./auth.js";
34
+ /** Error context indicating which subsystem produced the error. */
35
+ export type ErrorContext = "websocket" | "process" | "bridge";
36
+ /**
37
+ * Configuration options for {@link WebSocketACPServer}.
38
+ */
39
+ export interface WebSocketACPServerOptions {
40
+ /**
41
+ * TCP port to listen on.
42
+ * Use `0` to let the OS pick an available port (useful in tests).
43
+ */
44
+ port: number;
45
+ /**
46
+ * The command to spawn for each incoming WebSocket connection.
47
+ * This should be the path to an ACP-compatible agent executable.
48
+ *
49
+ * @example "node"
50
+ * @example "/usr/bin/my-agent"
51
+ */
52
+ command: string;
53
+ /** Arguments to pass to the spawned command. */
54
+ args?: string[];
55
+ /**
56
+ * Additional environment variables merged into the spawned process's
57
+ * environment (on top of the current `process.env`).
58
+ */
59
+ env?: Record<string, string>;
60
+ /**
61
+ * Hostname / IP address to bind to.
62
+ * @default "localhost"
63
+ */
64
+ host?: string;
65
+ /**
66
+ * Optional authentication callback invoked on every incoming WebSocket
67
+ * upgrade request. Return `true` to allow the connection or `false` to
68
+ * reject it with HTTP 401.
69
+ *
70
+ * If omitted, all connections are accepted (no authentication).
71
+ *
72
+ * @see {@link bearerAuth}, {@link basicAuth}, {@link apiKeyAuth}, {@link anyAuth}
73
+ */
74
+ verifyClient?: VerifyClientFn;
75
+ /**
76
+ * Called when an unhandled error occurs in any subsystem.
77
+ * Useful for logging — the server attempts to recover gracefully regardless.
78
+ */
79
+ onError?: (err: Error, context: ErrorContext) => void;
80
+ }
81
+ /**
82
+ * WebSocket server that bridges ACP clients to stdio-based ACP agent processes.
83
+ *
84
+ * Each accepted WebSocket connection spawns one agent subprocess. The connection
85
+ * and subprocess lifecycles are tied together:
86
+ * - Agent exits → WebSocket is closed with code 1001
87
+ * - WebSocket closes → agent receives SIGTERM (then SIGKILL after 5 s)
88
+ */
89
+ export declare class WebSocketACPServer {
90
+ private readonly wss;
91
+ private readonly sessions;
92
+ private readonly options;
93
+ constructor(options: WebSocketACPServerOptions);
94
+ private handleConnection;
95
+ /**
96
+ * Reads all messages from `src` and writes them to `dst`.
97
+ * Errors are forwarded to `onError` and both streams are cleaned up.
98
+ */
99
+ private pumpStream;
100
+ /**
101
+ * Returns a Promise that resolves once the server is bound and listening.
102
+ * Useful for waiting before connecting clients in tests or examples.
103
+ */
104
+ listening(): Promise<void>;
105
+ /**
106
+ * Closes the server: stops accepting new connections, terminates all active
107
+ * agent sessions, and resolves once the underlying server socket is closed.
108
+ */
109
+ close(): Promise<void>;
110
+ /**
111
+ * The address the server is listening on, or `null` if not yet listening.
112
+ * Cast to `AddressInfo` after calling {@link listening()} when binding to a
113
+ * numeric port (including `0`).
114
+ */
115
+ get address(): string | WebSocket.AddressInfo | null;
116
+ }
117
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAOhC,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,WAAW,CAAC;AAIlE,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,cAAc,CAAC;IAE9B;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;CACvD;AAID;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;gBAExC,OAAO,EAAE,yBAAyB;IAsC9C,OAAO,CAAC,gBAAgB;IA6ExB;;;OAGG;YACW,UAAU;IA4BxB;;;OAGG;IACH,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAW1B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;;;OAIG;IACH,IAAI,OAAO,0CAEV;CACF"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * WebSocket ACP server.
3
+ *
4
+ * Listens for incoming WebSocket connections and, for each one, spawns an
5
+ * ACP-compatible agent process via stdio. Incoming WebSocket frames are
6
+ * forwarded to the agent's stdin, and the agent's stdout is forwarded back
7
+ * as WebSocket frames.
8
+ *
9
+ * The server acts as a **transparent proxy** — it does not interpret ACP
10
+ * messages itself. All protocol handling is end-to-end between the remote
11
+ * client library and the spawned agent process.
12
+ *
13
+ * ```
14
+ * [WS Client] <── WebSocket ──> [WebSocketACPServer] <── stdio ──> [Agent process]
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { WebSocketACPServer, bearerAuth } from "acp-websocket-transport";
20
+ *
21
+ * const server = new WebSocketACPServer({
22
+ * port: 3000,
23
+ * command: "node",
24
+ * args: ["dist/my-agent.js"],
25
+ * verifyClient: bearerAuth(["my-secret-token"]),
26
+ * });
27
+ *
28
+ * await server.listening();
29
+ * console.log("Listening on", server.address);
30
+ * ```
31
+ */
32
+ import { WebSocketServer } from "ws";
33
+ import { spawn } from "node:child_process";
34
+ import { Readable, Writable } from "node:stream";
35
+ import { ndJsonStream } from "@agentclientprotocol/sdk";
36
+ import { wsStream } from "./transport.js";
37
+ // ─── Server ───────────────────────────────────────────────────────────────────
38
+ /**
39
+ * WebSocket server that bridges ACP clients to stdio-based ACP agent processes.
40
+ *
41
+ * Each accepted WebSocket connection spawns one agent subprocess. The connection
42
+ * and subprocess lifecycles are tied together:
43
+ * - Agent exits → WebSocket is closed with code 1001
44
+ * - WebSocket closes → agent receives SIGTERM (then SIGKILL after 5 s)
45
+ */
46
+ export class WebSocketACPServer {
47
+ wss;
48
+ sessions = new Map();
49
+ options;
50
+ constructor(options) {
51
+ this.options = options;
52
+ this.wss = new WebSocketServer({
53
+ port: options.port,
54
+ host: options.host ?? "localhost",
55
+ // Wire verifyClient when provided
56
+ ...(options.verifyClient != null
57
+ ? {
58
+ verifyClient: (info, done) => {
59
+ Promise.resolve(options.verifyClient(info))
60
+ .then((ok) => done(ok, ok ? 200 : 401, ok ? "OK" : "Unauthorized"))
61
+ .catch((err) => {
62
+ options.onError?.(err, "websocket");
63
+ done(false, 500, "Internal Server Error");
64
+ });
65
+ },
66
+ }
67
+ : {}),
68
+ });
69
+ this.wss.on("connection", this.handleConnection.bind(this));
70
+ this.wss.on("error", (err) => {
71
+ options.onError?.(err, "websocket");
72
+ });
73
+ }
74
+ // ─── Connection handler ─────────────────────────────────────────────────────
75
+ handleConnection(ws) {
76
+ const { command, args = [], env = {}, onError } = this.options;
77
+ // Spawn the agent process with all stdio piped
78
+ const child = spawn(command, args, {
79
+ stdio: ["pipe", "pipe", "pipe"],
80
+ env: { ...process.env, ...env },
81
+ });
82
+ this.sessions.set(ws, child);
83
+ // Relay agent stderr → server stderr for debuggability
84
+ child.stderr?.on("data", (chunk) => {
85
+ process.stderr.write(`[echo-agent stderr] ${chunk.toString()}`);
86
+ });
87
+ // Build the stdio Stream for the agent
88
+ // ndJsonStream(output, input):
89
+ // output = WritableStream the SDK writes messages TO (→ child stdin)
90
+ // input = ReadableStream the SDK reads messages FROM (← child stdout)
91
+ const agentStream = ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
92
+ // Build the WebSocket Stream for the remote client
93
+ const clientStream = wsStream(ws);
94
+ // Bidirectional bridge (transparent proxy — no ACP interpretation)
95
+ void this.pumpStream(clientStream.readable, agentStream.writable, "client → agent", onError);
96
+ void this.pumpStream(agentStream.readable, clientStream.writable, "agent → client", onError);
97
+ // ─── Lifecycle ────────────────────────────────────────────────────────────
98
+ // Agent exits → close WebSocket
99
+ child.on("exit", (code, signal) => {
100
+ this.sessions.delete(ws);
101
+ if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {
102
+ ws.close(1001, `Agent exited (${signal ?? String(code)})`);
103
+ }
104
+ });
105
+ child.on("error", (err) => {
106
+ onError?.(err, "process");
107
+ this.sessions.delete(ws);
108
+ if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) {
109
+ ws.close(1011, "Agent process error");
110
+ }
111
+ });
112
+ // WebSocket closes → terminate agent
113
+ ws.on("close", () => {
114
+ this.sessions.delete(ws);
115
+ if (!child.killed) {
116
+ child.kill("SIGTERM");
117
+ // Grace period: escalate to SIGKILL after 5 s if the process persists
118
+ const t = setTimeout(() => {
119
+ if (!child.killed)
120
+ child.kill("SIGKILL");
121
+ }, 5_000);
122
+ // Don't prevent the Node.js event loop from exiting due to this timer
123
+ if (typeof t.unref === "function")
124
+ t.unref();
125
+ }
126
+ });
127
+ }
128
+ // ─── Stream pump ────────────────────────────────────────────────────────────
129
+ /**
130
+ * Reads all messages from `src` and writes them to `dst`.
131
+ * Errors are forwarded to `onError` and both streams are cleaned up.
132
+ */
133
+ async pumpStream(src, dst, _label, onError) {
134
+ const reader = src.getReader();
135
+ const writer = dst.getWriter();
136
+ try {
137
+ while (true) {
138
+ const { done, value } = await reader.read();
139
+ if (done)
140
+ break;
141
+ await writer.write(value);
142
+ }
143
+ await writer.close();
144
+ }
145
+ catch (err) {
146
+ onError?.(err, "bridge");
147
+ await writer.abort(err).catch(() => {
148
+ /* ignore secondary errors during abort */
149
+ });
150
+ }
151
+ finally {
152
+ reader.releaseLock();
153
+ writer.releaseLock();
154
+ }
155
+ }
156
+ // ─── Public API ─────────────────────────────────────────────────────────────
157
+ /**
158
+ * Returns a Promise that resolves once the server is bound and listening.
159
+ * Useful for waiting before connecting clients in tests or examples.
160
+ */
161
+ listening() {
162
+ return new Promise((resolve) => {
163
+ if (this.wss.address() !== null) {
164
+ // Already listening
165
+ resolve();
166
+ }
167
+ else {
168
+ this.wss.once("listening", resolve);
169
+ }
170
+ });
171
+ }
172
+ /**
173
+ * Closes the server: stops accepting new connections, terminates all active
174
+ * agent sessions, and resolves once the underlying server socket is closed.
175
+ */
176
+ async close() {
177
+ // Hard-terminate all active connections so wss.close() callback fires promptly
178
+ for (const [ws, child] of this.sessions) {
179
+ child.kill("SIGTERM");
180
+ ws.terminate(); // immediately destroy the socket (no close handshake needed)
181
+ }
182
+ this.sessions.clear();
183
+ return new Promise((resolve, reject) => {
184
+ this.wss.close((err) => {
185
+ if (err != null)
186
+ reject(err);
187
+ else
188
+ resolve();
189
+ });
190
+ // Safety net: resolve after 2 s in case the callback stalls
191
+ setTimeout(resolve, 2_000).unref?.();
192
+ });
193
+ }
194
+ /**
195
+ * The address the server is listening on, or `null` if not yet listening.
196
+ * Cast to `AddressInfo` after calling {@link listening()} when binding to a
197
+ * numeric port (including `0`).
198
+ */
199
+ get address() {
200
+ return this.wss.address();
201
+ }
202
+ }
203
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
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
+ import type WebSocket from "ws";
11
+ import type { Stream } from "@agentclientprotocol/sdk";
12
+ /**
13
+ * Creates an ACP {@link Stream} from an open (or opening) `ws` WebSocket.
14
+ *
15
+ * Stream lifetime is tied to the WebSocket:
16
+ * - WS `close` / `error` → readable stream closes / errors
17
+ * - WritableStream `close()` → `ws.close(1000, ...)`
18
+ * - WritableStream `abort()` → `ws.terminate()` (hard close)
19
+ *
20
+ * @param ws - An open or opening `ws` WebSocket instance
21
+ * @returns Bidirectional ACP message stream
22
+ */
23
+ export declare function wsStream(ws: WebSocket): Stream;
24
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAEhC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAkBvD;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,SAAS,GAAG,MAAM,CA2F9C"}