@graphorin/client 0.5.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # @graphorin/client
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Initial Phase 14b release: reference TypeScript client for the
8
+ Graphorin standalone server. Wraps the `graphorin.protocol.v1`
9
+ WebSocket subprotocol (with an optional SSE fallback for
10
+ proxy-restricted environments) behind an ergonomic
11
+ `GraphorinClient` class: `connect()`, `subscribe({ target, id })`
12
+ returning an async-iterable subscription, `cancel(runId, opts)`,
13
+ `resume(runId, directive)`, `ping()`, `disconnect()`. Handles the
14
+ browser ticket flow, exponential-backoff reconnect with replay
15
+ buffer resume, and Zod-validated frame parsing on both
16
+ directions. Browser-friendly: zero Node-only dependencies. Created
17
+ and maintained by Oleksiy Stepurenko.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oleksiy Stepurenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @graphorin/client
2
+
3
+ > Reference TypeScript client for the [Graphorin](https://github.com/o-stepper/graphorin) standalone server.
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
6
+ [![Node.js: 22+](https://img.shields.io/badge/Node.js-22%2B-43853d.svg)](https://nodejs.org)
7
+
8
+ - **Version:** v0.5.0
9
+ - **License:** [MIT](./LICENSE) (© 2026 Oleksiy Stepurenko)
10
+ - **Repository:** <https://github.com/o-stepper/graphorin/tree/main/packages/client>
11
+ - **Issues:** <https://github.com/o-stepper/graphorin/issues>
12
+
13
+ `@graphorin/client` is the reference TypeScript client for the Graphorin standalone server. It wraps the WebSocket subprotocol `graphorin.protocol.v1` (with an optional Server-Sent Events fallback for proxy-restricted environments) behind an ergonomic `GraphorinClient` class.
14
+
15
+ ## What ships in v0.1 (Phase 14b)
16
+
17
+ | Capability | Detail |
18
+ |---|---|
19
+ | **WebSocket transport** | Honours the `graphorin.protocol.v1` subprotocol; supports both bearer-token (Node SDK) and ticket-flow (browser) authentication. |
20
+ | **SSE fallback** | Read-only `EventSource` transport for environments that block WebSocket upgrades. Control-plane operations (`subscribe`, `cancel`, `resume`) fall back to REST. |
21
+ | **Async-iterable subscriptions** | `for await (const event of sub.events())` — typed `AgentEvent` / `WorkflowEvent` payload via `@graphorin/protocol`. |
22
+ | **Reconnect** | Exponential backoff with full jitter; resubscribes with the recorded `lastEventId` so the server replays buffered events. |
23
+ | **Bundle hygiene** | Browser-friendly. Zero Node-only dependencies; runtime depends only on `@graphorin/protocol` and `zod`. |
24
+
25
+ ## Direct dependencies
26
+
27
+ - [`@graphorin/protocol`](../protocol/README.md) — single source of truth for the wire format.
28
+ - [`zod`](https://zod.dev) (`^3.25.0`) — schema validation re-exported transitively through `@graphorin/protocol`.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pnpm add @graphorin/client @graphorin/protocol zod
34
+ ```
35
+
36
+ > Other npm-registry-compatible package managers (`npm`, `yarn`, `bun`) work identically.
37
+
38
+ ## Usage
39
+
40
+ ```ts
41
+ import { GraphorinClient } from '@graphorin/client';
42
+
43
+ const client = new GraphorinClient({
44
+ baseUrl: 'wss://graphorin.example.com',
45
+ auth: { kind: 'bearer', token: process.env.GRAPHORIN_TOKEN ?? '' },
46
+ transport: 'auto', // try WebSocket first, fall back to SSE
47
+ });
48
+
49
+ await client.connect();
50
+
51
+ const subscription = await client.subscribe({
52
+ target: 'agent',
53
+ id: 'echo',
54
+ runId: 'run-123',
55
+ });
56
+
57
+ for await (const event of subscription.events()) {
58
+ console.log(event.type, event.payload);
59
+ }
60
+
61
+ await client.cancel('run-123', { drain: false });
62
+ await client.disconnect();
63
+ ```
64
+
65
+ ### Browser ticket flow
66
+
67
+ ```ts
68
+ const client = new GraphorinClient({
69
+ baseUrl: 'https://graphorin.example.com',
70
+ auth: {
71
+ kind: 'ticket',
72
+ ticketProvider: async () => {
73
+ const res = await fetch('/v1/session/ws-ticket', {
74
+ method: 'POST',
75
+ headers: { Authorization: `Bearer ${browserToken}` },
76
+ });
77
+ const body = (await res.json()) as { ticket: string };
78
+ return body.ticket;
79
+ },
80
+ },
81
+ });
82
+ ```
83
+
84
+ The client attaches the ticket as a second `Sec-WebSocket-Protocol` token (`ticket.<value>`), per the wire contract documented in [`@graphorin/protocol`](../protocol/README.md).
85
+
86
+ ## License
87
+
88
+ MIT © 2026 Oleksiy Stepurenko. See [`LICENSE`](./LICENSE).
89
+
90
+ ---
91
+
92
+ **Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
@@ -0,0 +1,81 @@
1
+ //#region src/errors.d.ts
2
+ /**
3
+ * Typed error hierarchy surfaced by `@graphorin/client`. Every error
4
+ * class extends the JavaScript built-in `Error` and exposes a stable
5
+ * `kind` discriminator so consumers can pattern-match without
6
+ * relying on `instanceof` (which behaves badly across module-system
7
+ * boundaries when the package is dual-loaded).
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ /**
12
+ * IP-19: map a JSON-RPC error code from a server `RpcFailure` frame to the
13
+ * client's discriminated {@link GraphorinClientErrorKind}, so a rate-limited
14
+ * or scope-denied RPC is distinguishable from a genuine protocol violation.
15
+ */
16
+ declare function kindForRpcCode(code: number): GraphorinClientErrorKind;
17
+ /**
18
+ * Discriminator union of every error kind raised by the client.
19
+ *
20
+ * @stable
21
+ */
22
+ type GraphorinClientErrorKind = 'client-not-connected' | 'transport-failed' | 'subprotocol-mismatch' | 'auth-failed' | 'protocol-violation' | 'subscription-not-found' | 'rate-limited' | 'scope-denied' | 'run-not-found' | 'server-error' | 'aborted' | 'invalid-server-frame';
23
+ /**
24
+ * Base class for every error raised by `@graphorin/client`. Carries a
25
+ * stable {@link GraphorinClientErrorKind} discriminator and an
26
+ * optional `cause` chain.
27
+ *
28
+ * @stable
29
+ */
30
+ declare class GraphorinClientError extends Error {
31
+ readonly kind: GraphorinClientErrorKind;
32
+ constructor(kind: GraphorinClientErrorKind, message: string, options?: ErrorOptions);
33
+ }
34
+ /** @stable */
35
+ declare class ClientNotConnectedError extends GraphorinClientError {
36
+ constructor(message?: string);
37
+ }
38
+ /** @stable */
39
+ declare class TransportFailedError extends GraphorinClientError {
40
+ readonly code: number | undefined;
41
+ constructor(message: string, options?: ErrorOptions & {
42
+ readonly code?: number;
43
+ });
44
+ }
45
+ /** @stable */
46
+ declare class SubprotocolMismatchError extends GraphorinClientError {
47
+ readonly expected: string;
48
+ readonly actual: string | null;
49
+ constructor(expected: string, actual: string | null);
50
+ }
51
+ /** @stable */
52
+ declare class AuthFailedError extends GraphorinClientError {
53
+ constructor(message?: string);
54
+ }
55
+ /** @stable */
56
+ declare class ProtocolViolationError extends GraphorinClientError {
57
+ constructor(message: string, options?: ErrorOptions);
58
+ }
59
+ /** @stable */
60
+ declare class SubscriptionNotFoundError extends GraphorinClientError {
61
+ readonly subscriptionId: string;
62
+ constructor(subscriptionId: string);
63
+ }
64
+ /** @stable */
65
+ declare class ClientAbortedError extends GraphorinClientError {
66
+ constructor(message?: string);
67
+ }
68
+ /** @stable */
69
+ declare class InvalidServerFrameError extends GraphorinClientError {
70
+ readonly issues: ReadonlyArray<{
71
+ path: ReadonlyArray<string | number>;
72
+ message: string;
73
+ }>;
74
+ constructor(message: string, issues: ReadonlyArray<{
75
+ path: ReadonlyArray<string | number>;
76
+ message: string;
77
+ }>);
78
+ }
79
+ //#endregion
80
+ export { AuthFailedError, ClientAbortedError, ClientNotConnectedError, GraphorinClientError, GraphorinClientErrorKind, InvalidServerFrameError, ProtocolViolationError, SubprotocolMismatchError, SubscriptionNotFoundError, TransportFailedError, kindForRpcCode };
81
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","names":[],"sources":["../src/errors.ts"],"sourcesContent":[],"mappings":";;AAiBA;AA2BA;AAuBA;;;;;;AAWA;AAQA;AAWA;AAgBA;AAQA;AAQa,iBAhHG,cAAA,CAgHuB,IAAQ,EAAA,MAAA,CAAA,EAhHD,wBAgHqB;AAcnE;AAQA;;;;AAKY,KAhHA,wBAAA,GAgHA,sBAAA,GAAA,kBAAA,GAAA,sBAAA,GAAA,aAAA,GAAA,oBAAA,GAAA,wBAAA,GAAA,cAAA,GAAA,cAAA,GAAA,eAAA,GAAA,cAAA,GAAA,SAAA,GAAA,sBAAA;;;;;;;;cAzFC,oBAAA,SAA6B,KAAA;iBACzB;oBAEG,qDAAoD;;;cAQ3D,uBAAA,SAAgC,oBAAA;;;;cAQhC,oBAAA,SAA6B,oBAAA;;yCAGF;;;;;cAQ3B,wBAAA,SAAiC,oBAAA;;;;;;cAgBjC,eAAA,SAAwB,oBAAA;;;;cAQxB,sBAAA,SAA+B,oBAAA;yCACJ;;;cAO3B,yBAAA,SAAkC,oBAAA;;;;;cAclC,kBAAA,SAA2B,oBAAA;;;;cAQ3B,uBAAA,SAAgC,oBAAA;mBAC1B;UAAsB;;;uCAI7B;UAAsB"}
package/dist/errors.js ADDED
@@ -0,0 +1,114 @@
1
+ import { RPC_ERROR_CODES } from "@graphorin/protocol";
2
+
3
+ //#region src/errors.ts
4
+ /**
5
+ * Typed error hierarchy surfaced by `@graphorin/client`. Every error
6
+ * class extends the JavaScript built-in `Error` and exposes a stable
7
+ * `kind` discriminator so consumers can pattern-match without
8
+ * relying on `instanceof` (which behaves badly across module-system
9
+ * boundaries when the package is dual-loaded).
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ /**
14
+ * IP-19: map a JSON-RPC error code from a server `RpcFailure` frame to the
15
+ * client's discriminated {@link GraphorinClientErrorKind}, so a rate-limited
16
+ * or scope-denied RPC is distinguishable from a genuine protocol violation.
17
+ */
18
+ function kindForRpcCode(code) {
19
+ switch (code) {
20
+ case RPC_ERROR_CODES.RATE_LIMITED: return "rate-limited";
21
+ case RPC_ERROR_CODES.SCOPE_DENIED: return "scope-denied";
22
+ case RPC_ERROR_CODES.AUTH_REQUIRED:
23
+ case RPC_ERROR_CODES.AUTH_INVALID: return "auth-failed";
24
+ case RPC_ERROR_CODES.RUN_NOT_FOUND: return "run-not-found";
25
+ case RPC_ERROR_CODES.SUBSCRIPTION_NOT_FOUND: return "subscription-not-found";
26
+ case RPC_ERROR_CODES.INTERNAL_ERROR: return "server-error";
27
+ default: return "protocol-violation";
28
+ }
29
+ }
30
+ /**
31
+ * Base class for every error raised by `@graphorin/client`. Carries a
32
+ * stable {@link GraphorinClientErrorKind} discriminator and an
33
+ * optional `cause` chain.
34
+ *
35
+ * @stable
36
+ */
37
+ var GraphorinClientError = class extends Error {
38
+ kind;
39
+ constructor(kind, message, options = {}) {
40
+ super(message, options);
41
+ this.kind = kind;
42
+ this.name = "GraphorinClientError";
43
+ }
44
+ };
45
+ /** @stable */
46
+ var ClientNotConnectedError = class extends GraphorinClientError {
47
+ constructor(message = "GraphorinClient is not connected. Call connect() first.") {
48
+ super("client-not-connected", message);
49
+ this.name = "ClientNotConnectedError";
50
+ }
51
+ };
52
+ /** @stable */
53
+ var TransportFailedError = class extends GraphorinClientError {
54
+ code;
55
+ constructor(message, options = {}) {
56
+ super("transport-failed", message, options);
57
+ this.name = "TransportFailedError";
58
+ this.code = options.code;
59
+ }
60
+ };
61
+ /** @stable */
62
+ var SubprotocolMismatchError = class extends GraphorinClientError {
63
+ expected;
64
+ actual;
65
+ constructor(expected, actual) {
66
+ super("subprotocol-mismatch", `Server selected subprotocol '${actual ?? "<none>"}'; expected '${expected}'.`);
67
+ this.name = "SubprotocolMismatchError";
68
+ this.expected = expected;
69
+ this.actual = actual;
70
+ }
71
+ };
72
+ /** @stable */
73
+ var AuthFailedError = class extends GraphorinClientError {
74
+ constructor(message = "Authentication failed. Re-mint a token or request a new ticket.") {
75
+ super("auth-failed", message);
76
+ this.name = "AuthFailedError";
77
+ }
78
+ };
79
+ /** @stable */
80
+ var ProtocolViolationError = class extends GraphorinClientError {
81
+ constructor(message, options = {}) {
82
+ super("protocol-violation", message, options);
83
+ this.name = "ProtocolViolationError";
84
+ }
85
+ };
86
+ /** @stable */
87
+ var SubscriptionNotFoundError = class extends GraphorinClientError {
88
+ subscriptionId;
89
+ constructor(subscriptionId) {
90
+ super("subscription-not-found", `Subscription '${subscriptionId}' is not active on this client.`);
91
+ this.name = "SubscriptionNotFoundError";
92
+ this.subscriptionId = subscriptionId;
93
+ }
94
+ };
95
+ /** @stable */
96
+ var ClientAbortedError = class extends GraphorinClientError {
97
+ constructor(message = "Operation aborted before completion.") {
98
+ super("aborted", message);
99
+ this.name = "ClientAbortedError";
100
+ }
101
+ };
102
+ /** @stable */
103
+ var InvalidServerFrameError = class extends GraphorinClientError {
104
+ issues;
105
+ constructor(message, issues) {
106
+ super("invalid-server-frame", message);
107
+ this.name = "InvalidServerFrameError";
108
+ this.issues = issues;
109
+ }
110
+ };
111
+
112
+ //#endregion
113
+ export { AuthFailedError, ClientAbortedError, ClientNotConnectedError, GraphorinClientError, InvalidServerFrameError, ProtocolViolationError, SubprotocolMismatchError, SubscriptionNotFoundError, TransportFailedError, kindForRpcCode };
114
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Typed error hierarchy surfaced by `@graphorin/client`. Every error\n * class extends the JavaScript built-in `Error` and exposes a stable\n * `kind` discriminator so consumers can pattern-match without\n * relying on `instanceof` (which behaves badly across module-system\n * boundaries when the package is dual-loaded).\n *\n * @packageDocumentation\n */\n\nimport { RPC_ERROR_CODES } from '@graphorin/protocol';\n\n/**\n * IP-19: map a JSON-RPC error code from a server `RpcFailure` frame to the\n * client's discriminated {@link GraphorinClientErrorKind}, so a rate-limited\n * or scope-denied RPC is distinguishable from a genuine protocol violation.\n */\nexport function kindForRpcCode(code: number): GraphorinClientErrorKind {\n switch (code) {\n case RPC_ERROR_CODES.RATE_LIMITED:\n return 'rate-limited';\n case RPC_ERROR_CODES.SCOPE_DENIED:\n return 'scope-denied';\n case RPC_ERROR_CODES.AUTH_REQUIRED:\n case RPC_ERROR_CODES.AUTH_INVALID:\n return 'auth-failed';\n case RPC_ERROR_CODES.RUN_NOT_FOUND:\n return 'run-not-found';\n case RPC_ERROR_CODES.SUBSCRIPTION_NOT_FOUND:\n return 'subscription-not-found';\n case RPC_ERROR_CODES.INTERNAL_ERROR:\n return 'server-error';\n default:\n // INVALID_REQUEST / INVALID_PARAMS / METHOD_NOT_FOUND / PROTOCOL_VIOLATION\n // and any unknown code stay a protocol violation.\n return 'protocol-violation';\n }\n}\n\n/**\n * Discriminator union of every error kind raised by the client.\n *\n * @stable\n */\nexport type GraphorinClientErrorKind =\n | 'client-not-connected'\n | 'transport-failed'\n | 'subprotocol-mismatch'\n | 'auth-failed'\n | 'protocol-violation'\n | 'subscription-not-found'\n // IP-19: discriminated RPC-failure kinds so callers can branch on the\n // server's error class instead of pattern-matching the message string.\n | 'rate-limited'\n | 'scope-denied'\n | 'run-not-found'\n | 'server-error'\n | 'aborted'\n | 'invalid-server-frame';\n\n/**\n * Base class for every error raised by `@graphorin/client`. Carries a\n * stable {@link GraphorinClientErrorKind} discriminator and an\n * optional `cause` chain.\n *\n * @stable\n */\nexport class GraphorinClientError extends Error {\n readonly kind: GraphorinClientErrorKind;\n\n constructor(kind: GraphorinClientErrorKind, message: string, options: ErrorOptions = {}) {\n super(message, options);\n this.kind = kind;\n this.name = 'GraphorinClientError';\n }\n}\n\n/** @stable */\nexport class ClientNotConnectedError extends GraphorinClientError {\n constructor(message = 'GraphorinClient is not connected. Call connect() first.') {\n super('client-not-connected', message);\n this.name = 'ClientNotConnectedError';\n }\n}\n\n/** @stable */\nexport class TransportFailedError extends GraphorinClientError {\n readonly code: number | undefined;\n\n constructor(message: string, options: ErrorOptions & { readonly code?: number } = {}) {\n super('transport-failed', message, options);\n this.name = 'TransportFailedError';\n this.code = options.code;\n }\n}\n\n/** @stable */\nexport class SubprotocolMismatchError extends GraphorinClientError {\n readonly expected: string;\n readonly actual: string | null;\n\n constructor(expected: string, actual: string | null) {\n super(\n 'subprotocol-mismatch',\n `Server selected subprotocol '${actual ?? '<none>'}'; expected '${expected}'.`,\n );\n this.name = 'SubprotocolMismatchError';\n this.expected = expected;\n this.actual = actual;\n }\n}\n\n/** @stable */\nexport class AuthFailedError extends GraphorinClientError {\n constructor(message = 'Authentication failed. Re-mint a token or request a new ticket.') {\n super('auth-failed', message);\n this.name = 'AuthFailedError';\n }\n}\n\n/** @stable */\nexport class ProtocolViolationError extends GraphorinClientError {\n constructor(message: string, options: ErrorOptions = {}) {\n super('protocol-violation', message, options);\n this.name = 'ProtocolViolationError';\n }\n}\n\n/** @stable */\nexport class SubscriptionNotFoundError extends GraphorinClientError {\n readonly subscriptionId: string;\n\n constructor(subscriptionId: string) {\n super(\n 'subscription-not-found',\n `Subscription '${subscriptionId}' is not active on this client.`,\n );\n this.name = 'SubscriptionNotFoundError';\n this.subscriptionId = subscriptionId;\n }\n}\n\n/** @stable */\nexport class ClientAbortedError extends GraphorinClientError {\n constructor(message = 'Operation aborted before completion.') {\n super('aborted', message);\n this.name = 'ClientAbortedError';\n }\n}\n\n/** @stable */\nexport class InvalidServerFrameError extends GraphorinClientError {\n readonly issues: ReadonlyArray<{ path: ReadonlyArray<string | number>; message: string }>;\n\n constructor(\n message: string,\n issues: ReadonlyArray<{ path: ReadonlyArray<string | number>; message: string }>,\n ) {\n super('invalid-server-frame', message);\n this.name = 'InvalidServerFrameError';\n this.issues = issues;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,eAAe,MAAwC;AACrE,SAAQ,MAAR;EACE,KAAK,gBAAgB,aACnB,QAAO;EACT,KAAK,gBAAgB,aACnB,QAAO;EACT,KAAK,gBAAgB;EACrB,KAAK,gBAAgB,aACnB,QAAO;EACT,KAAK,gBAAgB,cACnB,QAAO;EACT,KAAK,gBAAgB,uBACnB,QAAO;EACT,KAAK,gBAAgB,eACnB,QAAO;EACT,QAGE,QAAO;;;;;;;;;;AAgCb,IAAa,uBAAb,cAA0C,MAAM;CAC9C,AAAS;CAET,YAAY,MAAgC,SAAiB,UAAwB,EAAE,EAAE;AACvF,QAAM,SAAS,QAAQ;AACvB,OAAK,OAAO;AACZ,OAAK,OAAO;;;;AAKhB,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,YAAY,UAAU,2DAA2D;AAC/E,QAAM,wBAAwB,QAAQ;AACtC,OAAK,OAAO;;;;AAKhB,IAAa,uBAAb,cAA0C,qBAAqB;CAC7D,AAAS;CAET,YAAY,SAAiB,UAAqD,EAAE,EAAE;AACpF,QAAM,oBAAoB,SAAS,QAAQ;AAC3C,OAAK,OAAO;AACZ,OAAK,OAAO,QAAQ;;;;AAKxB,IAAa,2BAAb,cAA8C,qBAAqB;CACjE,AAAS;CACT,AAAS;CAET,YAAY,UAAkB,QAAuB;AACnD,QACE,wBACA,gCAAgC,UAAU,SAAS,eAAe,SAAS,IAC5E;AACD,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,OAAK,SAAS;;;;AAKlB,IAAa,kBAAb,cAAqC,qBAAqB;CACxD,YAAY,UAAU,mEAAmE;AACvF,QAAM,eAAe,QAAQ;AAC7B,OAAK,OAAO;;;;AAKhB,IAAa,yBAAb,cAA4C,qBAAqB;CAC/D,YAAY,SAAiB,UAAwB,EAAE,EAAE;AACvD,QAAM,sBAAsB,SAAS,QAAQ;AAC7C,OAAK,OAAO;;;;AAKhB,IAAa,4BAAb,cAA+C,qBAAqB;CAClE,AAAS;CAET,YAAY,gBAAwB;AAClC,QACE,0BACA,iBAAiB,eAAe,iCACjC;AACD,OAAK,OAAO;AACZ,OAAK,iBAAiB;;;;AAK1B,IAAa,qBAAb,cAAwC,qBAAqB;CAC3D,YAAY,UAAU,wCAAwC;AAC5D,QAAM,WAAW,QAAQ;AACzB,OAAK,OAAO;;;;AAKhB,IAAa,0BAAb,cAA6C,qBAAqB;CAChE,AAAS;CAET,YACE,SACA,QACA;AACA,QAAM,wBAAwB,QAAQ;AACtC,OAAK,OAAO;AACZ,OAAK,SAAS"}
@@ -0,0 +1,180 @@
1
+ import { BackoffPolicy } from "./reconnect.js";
2
+ import { TransportAuth, TransportKind } from "./transport/types.js";
3
+ import "./transport/index.js";
4
+ import { ServerEventFrame } from "@graphorin/protocol";
5
+
6
+ //#region src/graphorin-client.d.ts
7
+
8
+ /**
9
+ * Discriminator for the subscription target. Mirrors the strict
10
+ * subject grammar enforced by the server:
11
+ * - `'session'`/`<id>` ⇒ `'session:<id>/events'`
12
+ * - `'agent'`/`<id>` + `runId` ⇒ `'agent:<id>/runs/<runId>/events'`
13
+ * - `'run'`/`<runId>` ⇒ `'session:<sessionId>/runs/<runId>/events'`
14
+ * (when `sessionId` is provided)
15
+ * - `'workflow'`/`<id>` ⇒ `'workflow:<id>/events'`
16
+ *
17
+ * @stable
18
+ */
19
+ type SubscriptionTarget = {
20
+ readonly target: 'session';
21
+ readonly id: string;
22
+ } | {
23
+ readonly target: 'agent';
24
+ readonly id: string;
25
+ readonly runId: string;
26
+ } | {
27
+ readonly target: 'run';
28
+ readonly runId: string;
29
+ readonly sessionId?: string;
30
+ } | {
31
+ readonly target: 'workflow';
32
+ readonly id: string;
33
+ };
34
+ /**
35
+ * Transport selector. `'auto'` (default) attempts a WebSocket
36
+ * handshake first and falls back to SSE on failure.
37
+ *
38
+ * @stable
39
+ */
40
+ type TransportPreference = TransportKind | 'auto';
41
+ /**
42
+ * Public configuration accepted by {@link GraphorinClient}.
43
+ *
44
+ * @stable
45
+ */
46
+ interface GraphorinClientOptions {
47
+ /**
48
+ * Session bound to the SSE fallback (IP-3): substituted into the
49
+ * `:sessionId` slot of `sseSessionPath`. Required to connect over
50
+ * SSE — the old client sent the literal template and could never
51
+ * receive an event.
52
+ */
53
+ readonly sessionId?: string;
54
+ /**
55
+ * Server base URL. Examples: `'wss://graphorin.example.com'` or
56
+ * `'http://localhost:8080'`. The path `/v1/ws` is appended for
57
+ * the WebSocket transport.
58
+ */
59
+ readonly baseUrl: string;
60
+ readonly auth: TransportAuth;
61
+ readonly transport?: TransportPreference;
62
+ /** Override the WS path (default `'/v1/ws'`). */
63
+ readonly wsPath?: string;
64
+ /**
65
+ * SSE path template. The placeholder `:sessionId` is replaced at
66
+ * subscribe time. Default: `'/v1/sessions/:sessionId/events'`.
67
+ * Required when the transport is `'sse'` or when the WS handshake
68
+ * fails on `'auto'`.
69
+ */
70
+ readonly sseSessionPath?: string;
71
+ readonly reconnect?: BackoffPolicy;
72
+ /** Inject a `WebSocket` constructor (Node SDKs / tests). */
73
+ readonly WebSocket?: typeof WebSocket;
74
+ /** Inject an `EventSource` constructor (Node SDKs / tests). */
75
+ readonly EventSource?: typeof EventSource;
76
+ /** Inject a `fetch` implementation (defaults to `globalThis.fetch`). */
77
+ readonly fetch?: typeof fetch;
78
+ /** Optional client identifier surfaced on diagnostics. */
79
+ readonly clientId?: string;
80
+ /**
81
+ * IP-19: per-RPC reply timeout in milliseconds. When set (and > 0), an RPC
82
+ * that receives no matching reply within this window rejects with a
83
+ * {@link TransportFailedError} instead of hanging forever on a
84
+ * non-responsive server. Default: unset — no timeout, so a legitimately
85
+ * slow server reply is never aborted (opt-in).
86
+ */
87
+ readonly rpcTimeoutMs?: number;
88
+ }
89
+ /**
90
+ * Snapshot returned by {@link Subscription.metadata}.
91
+ *
92
+ * @stable
93
+ */
94
+ interface SubscriptionMetadata {
95
+ readonly id: string;
96
+ readonly subject: string;
97
+ readonly target: SubscriptionTarget;
98
+ readonly snapshotEventId: string | undefined;
99
+ readonly lastEventId: string | undefined;
100
+ readonly closed: boolean;
101
+ }
102
+ /**
103
+ * Public surface returned by {@link GraphorinClient.subscribe}.
104
+ *
105
+ * @stable
106
+ */
107
+ interface Subscription {
108
+ readonly subscriptionId: string;
109
+ readonly subject: string;
110
+ events(): AsyncIterable<ServerEventFrame>;
111
+ /**
112
+ * Close the subscription on the server. Idempotent.
113
+ */
114
+ unsubscribe(): Promise<void>;
115
+ metadata(): SubscriptionMetadata;
116
+ }
117
+ /**
118
+ * @stable
119
+ */
120
+ declare class GraphorinClient {
121
+ #private;
122
+ constructor(options: GraphorinClientOptions);
123
+ /**
124
+ * Open the underlying transport. Resolves once the server has
125
+ * accepted the handshake (`'open'`); rejects with a typed
126
+ * {@link GraphorinClientError} otherwise.
127
+ *
128
+ * Calling `connect()` while already connected is a no-op; calling
129
+ * it during another `connect()` returns the same promise.
130
+ */
131
+ connect(): Promise<void>;
132
+ /** Send a `ping` RPC and resolve when the server replies with `pong`. */
133
+ ping(): Promise<void>;
134
+ /**
135
+ * Subscribe to a server-side event stream. Resolves with a
136
+ * {@link Subscription} once the server confirms with the matching
137
+ * `subscribed` frame; rejects when the server returns an
138
+ * `error` instead.
139
+ */
140
+ subscribe(target: SubscriptionTarget, opts?: {
141
+ readonly sinceEventId?: string;
142
+ }): Promise<Subscription>;
143
+ /**
144
+ * Cancel a server-side run. Sends the `run.cancel` RPC and
145
+ * resolves with the server's `result` payload (typically
146
+ * `{ cancelled: true, partialStateAvailable: true }`).
147
+ */
148
+ cancel(runId: string, opts?: {
149
+ readonly drain?: boolean;
150
+ readonly reason?: string;
151
+ readonly onPendingApprovals?: 'deny' | 'preserve';
152
+ }): Promise<unknown>;
153
+ /**
154
+ * Resume a paused (HITL) run. The WebSocket protocol intentionally
155
+ * does NOT carry a `resume` control message — resumes are durable
156
+ * + idempotent + body-carrying, which maps onto the REST endpoint
157
+ * `POST /v1/runs/:runId/resume`. NOTE (IP-14): the server endpoint
158
+ * currently answers **501** — server-side durable resume is not
159
+ * implemented yet. Library-mode callers resume directly:
160
+ * `agent.run(result.state, { directive })`.
161
+ */
162
+ resume(runId: string, directive?: unknown, opts?: {
163
+ readonly idempotencyKey?: string;
164
+ }): Promise<unknown>;
165
+ /**
166
+ * Send an MCP-compatible cancellation notification. Does not wait
167
+ * for a server reply (notifications have no `id`).
168
+ */
169
+ cancelNotify(requestId: string): void;
170
+ /**
171
+ * Disconnect the underlying transport and abort every pending RPC
172
+ * + subscription. Idempotent.
173
+ */
174
+ disconnect(): Promise<void>;
175
+ /** Return the active transport kind (or `undefined` if not connected). */
176
+ get transportKind(): TransportKind | undefined;
177
+ }
178
+ //#endregion
179
+ export { GraphorinClient, GraphorinClientOptions, Subscription, SubscriptionMetadata, SubscriptionTarget, TransportPreference };
180
+ //# sourceMappingURL=graphorin-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphorin-client.d.ts","names":[],"sources":["../src/graphorin-client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;KAsEY,kBAAA;;;;;;;;;;;;;;;;;;;;;KAoBA,mBAAA,GAAsB;;;;;;UAOjB,sBAAA;;;;;;;;;;;;;;iBAcA;uBACM;;;;;;;;;;uBAUA;;8BAEO;;gCAEE;;0BAEN;;;;;;;;;;;;;;;;;UAkBT,oBAAA;;;mBAGE;;;;;;;;;;UAWF,YAAA;;;YAGL,cAAc;;;;iBAIT;cACH;;;;;cAmBD,eAAA;;uBAYU;;;;;;;;;aAeJ;;UAkCH;;;;;;;oBAWJ;;MAEP,QAAQ;;;;;;;;;;MAoER;;;;;;;;;;;;MA+BA;;;;;;;;;;gBA4DiB;;uBAwBC"}