@crow-agent/server 0.0.0-stage → 0.2.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.
Files changed (66) hide show
  1. package/README.md +108 -2
  2. package/dist/public-api.d.ts +5 -0
  3. package/dist/public-api.d.ts.map +1 -0
  4. package/dist/public-api.js +3 -0
  5. package/dist/public-api.js.map +1 -0
  6. package/dist/server-connection.d.ts +32 -0
  7. package/dist/server-connection.d.ts.map +1 -0
  8. package/dist/server-connection.js +5 -0
  9. package/dist/server-connection.js.map +1 -0
  10. package/dist/server-errors.d.ts +31 -0
  11. package/dist/server-errors.d.ts.map +1 -0
  12. package/dist/server-errors.js +41 -0
  13. package/dist/server-errors.js.map +1 -0
  14. package/dist/server-listener.d.ts +8 -0
  15. package/dist/server-listener.d.ts.map +1 -0
  16. package/dist/server-listener.js +2 -0
  17. package/dist/server-listener.js.map +1 -0
  18. package/dist/server-types.d.ts +52 -0
  19. package/dist/server-types.d.ts.map +1 -0
  20. package/dist/server-types.js +2 -0
  21. package/dist/server-types.js.map +1 -0
  22. package/dist/session-routing.d.ts +42 -0
  23. package/dist/session-routing.d.ts.map +1 -0
  24. package/dist/session-routing.js +300 -0
  25. package/dist/session-routing.js.map +1 -0
  26. package/dist/session-server.d.ts +14 -0
  27. package/dist/session-server.d.ts.map +1 -0
  28. package/dist/session-server.js +678 -0
  29. package/dist/session-server.js.map +1 -0
  30. package/dist/testing/public-api.d.ts +6 -0
  31. package/dist/testing/public-api.d.ts.map +1 -0
  32. package/dist/testing/public-api.js +4 -0
  33. package/dist/testing/public-api.js.map +1 -0
  34. package/dist/testing/test-client.d.ts +29 -0
  35. package/dist/testing/test-client.d.ts.map +1 -0
  36. package/dist/testing/test-client.js +196 -0
  37. package/dist/testing/test-client.js.map +1 -0
  38. package/dist/testing/test-host.d.ts +62 -0
  39. package/dist/testing/test-host.d.ts.map +1 -0
  40. package/dist/testing/test-host.js +241 -0
  41. package/dist/testing/test-host.js.map +1 -0
  42. package/dist/testing/test-server.d.ts +13 -0
  43. package/dist/testing/test-server.d.ts.map +1 -0
  44. package/dist/testing/test-server.js +17 -0
  45. package/dist/testing/test-server.js.map +1 -0
  46. package/dist/transports/unix/public-api.d.ts +5 -0
  47. package/dist/transports/unix/public-api.d.ts.map +1 -0
  48. package/dist/transports/unix/public-api.js +4 -0
  49. package/dist/transports/unix/public-api.js.map +1 -0
  50. package/dist/transports/unix/socket-address.d.ts +3 -0
  51. package/dist/transports/unix/socket-address.d.ts.map +1 -0
  52. package/dist/transports/unix/socket-address.js +11 -0
  53. package/dist/transports/unix/socket-address.js.map +1 -0
  54. package/dist/transports/unix/unix-listener.d.ts +18 -0
  55. package/dist/transports/unix/unix-listener.d.ts.map +1 -0
  56. package/dist/transports/unix/unix-listener.js +511 -0
  57. package/dist/transports/unix/unix-listener.js.map +1 -0
  58. package/dist/transports/unix/unix-preset.d.ts +7 -0
  59. package/dist/transports/unix/unix-preset.d.ts.map +1 -0
  60. package/dist/transports/unix/unix-preset.js +25 -0
  61. package/dist/transports/unix/unix-preset.js.map +1 -0
  62. package/dist/transports/unix/unix-types.d.ts +14 -0
  63. package/dist/transports/unix/unix-types.d.ts.map +1 -0
  64. package/dist/transports/unix/unix-types.js +2 -0
  65. package/dist/transports/unix/unix-types.js.map +1 -0
  66. package/package.json +53 -4
package/README.md CHANGED
@@ -1,3 +1,109 @@
1
- # Temporary Holding Version
1
+ # @crow-agent/server
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Local server that hosts durable Sessions and exposes the Agent Harness over a
4
+ byte transport.
5
+
6
+ A `Server` instance accepts connections, runs the hello handshake, and then
7
+ routes service calls at two levels: calls aimed at the server itself, and calls
8
+ aimed at one attached Session. Routing stays opaque — the server never decodes
9
+ application payloads. It only checks that the `{ serverId, sessionId,
10
+ attachmentId }` route on each request is live, then forwards the envelope.
11
+
12
+ ## What the host provides
13
+
14
+ Everything session-shaped is application code. The server asks a `ServerHost`
15
+ for three things:
16
+
17
+ - `serverServices` — the connection-scoped service endpoint. Its
18
+ `attachClient()` hands each connection a narrow attachment-management
19
+ surface (`RoutedServerServiceAttachment`).
20
+ - `resolveSession(sessionId, context)` — map an id to metadata, or throw
21
+ `SessionNotFoundError` / `SessionAmbiguousError`.
22
+ - `openSession(metadata, context)` — acquire the worker-local Session and wrap
23
+ it in a `RoutedSessionHandle`. The handle's `attachClient()` returns a
24
+ presentation-scoped capability whose `invokeService()` forwards an opaque
25
+ service/member envelope at the chosen Session endpoint.
26
+
27
+ Application-owned services (`SessionDirectory`, `SessionManagement`) project
28
+ the private session catalog into replicated, presentation-safe state and create,
29
+ remove, attach, and detach Sessions without leaking route ids into business
30
+ results. Subscription snapshots and updates stay scoped to the attachment that
31
+ requested them; attachment changes are published out of band once the router
32
+ installs or clears the live route.
33
+
34
+ ## Attachments
35
+
36
+ One Session can serve several presentation attachments at once. Repeating
37
+ `attach` from a single connection is idempotent. Each successful attachment
38
+ receives a server-generated `attachmentId` that only travels in routing
39
+ control data. Stale or mismatched routes are rejected. When a connection drops,
40
+ its pending responses fail, but its attachment is released only after admitted
41
+ service calls settle — the host decides when zero presentation demand plus
42
+ quiet worker-local Harness activity allow worker retirement. `server.close()`
43
+ shuts every routed Session handle down, relinquishing worker and Session-writer
44
+ ownership.
45
+
46
+ ## Example
47
+
48
+ ```ts
49
+ import { randomUUID } from "node:crypto";
50
+ import { MemorySessionRepo, type Context } from "@crow-agent/agent-core";
51
+ import {
52
+ SessionAmbiguousError,
53
+ SessionNotFoundError,
54
+ type RoutedServerServiceHost,
55
+ type RoutedSessionHandle,
56
+ type ServerHost,
57
+ type Session,
58
+ } from "@crow-agent/server";
59
+ import { createUnixServer, getUnixSocketPath } from "@crow-agent/server/unix";
60
+
61
+ const catalog = new MemorySessionRepo();
62
+
63
+ async function lookupSession(sessionId: string, context: Context) {
64
+ const candidates = await catalog.list(undefined, context);
65
+ const match = candidates.filter((entry) => entry.id === sessionId);
66
+ if (match.length > 1) throw new SessionAmbiguousError();
67
+ const found = match[0];
68
+ if (found === undefined) throw new SessionNotFoundError(`Unknown session: ${sessionId}`);
69
+ return found;
70
+ }
71
+
72
+ async function launch(
73
+ serverServices: RoutedServerServiceHost,
74
+ openRoutedSession: (session: Session) => Promise<RoutedSessionHandle>,
75
+ ) {
76
+ const host: ServerHost = {
77
+ serverServices,
78
+ resolveSession: lookupSession,
79
+ openSession: async (metadata, context) => {
80
+ const session = await catalog.open(metadata, context);
81
+ return openRoutedSession(session);
82
+ },
83
+ };
84
+
85
+ const serverId = randomUUID();
86
+ const socketPath = getUnixSocketPath(serverId, "/run/user/1000/crow");
87
+ const server = createUnixServer(host, { serverId, path: socketPath });
88
+ await server.start();
89
+ return server;
90
+ }
91
+ ```
92
+
93
+ The launcher picks `serverId` as a logical identity — it is not a socket
94
+ address. `getUnixSocketPath()` derives a filesystem path from a directory you
95
+ choose; prefer a short private runtime directory. A long-lived launcher may
96
+ reuse the same id and path when replacing the server process.
97
+
98
+ `Server` composes transports through the `ServerListener` interface; peer
99
+ authentication is application policy and is not provided by the Unix transport.
100
+ The Unix submodule offers `createUnixListener()` and `createUnixServer()`.
101
+ Low-level envelope validation, CBOR, and framing come from `@crow-agent/protocol`;
102
+ Chord handles service-control parsing, error codes, snapshots and updates, and
103
+ each subscription's replicated-state encoder.
104
+
105
+ Server and worker lifecycle live outside the public Crow protocol. The
106
+ replaceable application server turns connection attachments into private
107
+ demand updates; the worker merges generation-tagged demand with authoritative
108
+ Harness activity. The coordinator supplies stable routing and reports generic
109
+ server-generation connection changes.
@@ -0,0 +1,5 @@
1
+ export { INTERNAL_SERVER_ERROR_MESSAGE, ServerDrainingError, ServerError, SessionAmbiguousError, SessionNotAttachedError, SessionNotFoundError, WrongServerError, } from "./server-errors.ts";
2
+ export type { ServerListener } from "./server-listener.ts";
3
+ export { Server } from "./session-server.ts";
4
+ export type { MaybePromise, RoutedServerPresentation, RoutedServerServiceAttachment, RoutedServerServiceHost, RoutedSessionAttachment, RoutedSessionHandle, ServerHost, ServerOptions, } from "./server-types.ts";
5
+ //# sourceMappingURL=public-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EACX,YAAY,EACZ,wBAAwB,EACxB,6BAA6B,EAC7B,uBAAuB,EACvB,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,EACV,aAAa,GACb,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { INTERNAL_SERVER_ERROR_MESSAGE, ServerDrainingError, ServerError, SessionAmbiguousError, SessionNotAttachedError, SessionNotFoundError, WrongServerError, } from "./server-errors.js";
2
+ export { Server } from "./session-server.js";
3
+ //# sourceMappingURL=public-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["export {\n\tINTERNAL_SERVER_ERROR_MESSAGE,\n\tServerDrainingError,\n\tServerError,\n\tSessionAmbiguousError,\n\tSessionNotAttachedError,\n\tSessionNotFoundError,\n\tWrongServerError,\n} from \"./server-errors.ts\";\nexport type { ServerListener } from \"./server-listener.ts\";\nexport { Server } from \"./session-server.ts\";\nexport type {\n\tMaybePromise,\n\tRoutedServerPresentation,\n\tRoutedServerServiceAttachment,\n\tRoutedServerServiceHost,\n\tRoutedSessionAttachment,\n\tRoutedSessionHandle,\n\tServerHost,\n\tServerOptions,\n} from \"./server-types.ts\";\n"]}
@@ -0,0 +1,32 @@
1
+ import type { ServiceStateEncoder } from "@crow-agent/chord";
2
+ import type { ClientMessageDecoder, RpcTarget } from "@crow-agent/protocol";
3
+ import type { MaybePromise, RoutedServerServiceAttachment } from "./server-types.ts";
4
+ /** An established byte connection authorized by its transport. */
5
+ export type ByteConnection = {
6
+ readonly closed: boolean;
7
+ send(chunk: Uint8Array): Promise<void>;
8
+ close(finalChunk?: Uint8Array): MaybePromise<void>;
9
+ };
10
+ export type ByteConnectionHandler = {
11
+ onData(chunk: Uint8Array): void;
12
+ onClose(): void;
13
+ onError(error: Error): void;
14
+ };
15
+ export type ByteConnectionAcceptor = (connection: ByteConnection) => ByteConnectionHandler;
16
+ export type ConnectionStage = "awaitingHello" | "handshaking" | "ready" | "closing" | "closed";
17
+ export type ConnectionState = {
18
+ connection: ByteConnection;
19
+ decoder: ClientMessageDecoder;
20
+ serviceStateEncoders: Map<string, ServiceStateEncoder>;
21
+ stage: ConnectionStage;
22
+ disconnected: boolean;
23
+ handshake?: Promise<void>;
24
+ handshakeTimeout: NodeJS.Timeout;
25
+ serverServices?: RoutedServerServiceAttachment;
26
+ activeRequests: Map<string, {
27
+ controller: AbortController;
28
+ target: RpcTarget;
29
+ }>;
30
+ };
31
+ export declare function isTerminalConnection(state: ConnectionState): boolean;
32
+ //# sourceMappingURL=server-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-connection.d.ts","sourceRoot":"","sources":["../src/server-connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EACX,oBAAoB,EACpB,SAAS,EACT,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EACX,YAAY,EACZ,6BAA6B,EAC7B,MAAM,mBAAmB,CAAC;AAE3B,kEAAkE;AAClE,MAAM,MAAM,cAAc,GAAG;IAC5B,QAAQ,CAAC,MAAM,EACd,OAAO,CAAC;IACT,IAAI,CACH,KAAK,EAAE,UAAU,GACf,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,KAAK,CACJ,UAAU,CAAC,EAAE,UAAU,GACrB,YAAY,CAAC,IAAI,CAAC,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IACnC,MAAM,CACL,KAAK,EAAE,UAAU,GACf,IAAI,CAAC;IACR,OAAO,IAAI,IAAI,CAAC;IAChB,OAAO,CACN,KAAK,EAAE,KAAK,GACV,IAAI,CAAC;CACR,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,CACpC,UAAU,EAAE,cAAc,KACtB,qBAAqB,CAAC;AAE3B,MAAM,MAAM,eAAe,GACxB,eAAe,GACf,aAAa,GACb,OAAO,GACP,SAAS,GACT,QAAQ,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG;IAC7B,UAAU,EACT,cAAc,CAAC;IAChB,OAAO,EACN,oBAAoB,CAAC;IACtB,oBAAoB,EACnB,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAClC,KAAK,EAAE,eAAe,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EACT,OAAO,CAAC,IAAI,CAAC,CAAC;IACf,gBAAgB,EACf,MAAM,CAAC,OAAO,CAAC;IAChB,cAAc,CAAC,EACd,6BAA6B,CAAC;IAC/B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE;QAC3B,UAAU,EAAE,eAAe,CAAC;QAC5B,MAAM,EAAE,SAAS,CAAC;KAClB,CAAC,CAAC;CACH,CAAC;AAEF,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,eAAe,GACpB,OAAO,CAGT"}
@@ -0,0 +1,5 @@
1
+ export function isTerminalConnection(state) {
2
+ const { disconnected, stage } = state;
3
+ return disconnected || stage === "closing" || stage === "closed";
4
+ }
5
+ //# sourceMappingURL=server-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-connection.js","sourceRoot":"","sources":["../src/server-connection.ts"],"names":[],"mappings":"AAiEA,MAAM,UAAU,oBAAoB,CACnC,KAAsB;IAEtB,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACtC,OAAO,YAAY,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAC;AAClE,CAAC","sourcesContent":["import type { ServiceStateEncoder } from \"@crow-agent/chord\";\nimport type {\n\tClientMessageDecoder,\n\tRpcTarget,\n} from \"@crow-agent/protocol\";\n\nimport type {\n\tMaybePromise,\n\tRoutedServerServiceAttachment,\n} from \"./server-types.ts\";\n\n/** An established byte connection authorized by its transport. */\nexport type ByteConnection = {\n\treadonly closed:\n\t\tboolean;\n\tsend(\n\t\tchunk: Uint8Array,\n\t): Promise<void>;\n\tclose(\n\t\tfinalChunk?: Uint8Array,\n\t): MaybePromise<void>;\n};\n\nexport type ByteConnectionHandler = {\n\tonData(\n\t\tchunk: Uint8Array,\n\t): void;\n\tonClose(): void;\n\tonError(\n\t\terror: Error,\n\t): void;\n};\n\nexport type ByteConnectionAcceptor = (\n\tconnection: ByteConnection,\n) => ByteConnectionHandler;\n\nexport type ConnectionStage =\n\t| \"awaitingHello\"\n\t| \"handshaking\"\n\t| \"ready\"\n\t| \"closing\"\n\t| \"closed\";\n\nexport type ConnectionState = {\n\tconnection:\n\t\tByteConnection;\n\tdecoder:\n\t\tClientMessageDecoder;\n\tserviceStateEncoders:\n\t\tMap<string, ServiceStateEncoder>;\n\tstage: ConnectionStage;\n\tdisconnected: boolean;\n\thandshake?:\n\t\tPromise<void>;\n\thandshakeTimeout:\n\t\tNodeJS.Timeout;\n\tserverServices?:\n\t\tRoutedServerServiceAttachment;\n\tactiveRequests: Map<string, {\n\t\tcontroller: AbortController;\n\t\ttarget: RpcTarget;\n\t}>;\n};\n\nexport function isTerminalConnection(\n\tstate: ConnectionState,\n): boolean {\n\tconst { disconnected, stage } = state;\n\treturn disconnected || stage === \"closing\" || stage === \"closed\";\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import type { RemoteServiceErrorCode } from "@crow-agent/chord";
2
+ type ServerOperationErrorCode = RemoteServiceErrorCode | "wrong_server" | "session_not_found" | "session_ambiguous" | "session_not_attached" | "server_draining";
3
+ export declare const INTERNAL_SERVER_ERROR_MESSAGE = "Internal server error";
4
+ /** A host or lifecycle failure that may safely cross the protocol boundary. */
5
+ export declare class ServerError extends Error {
6
+ name: string;
7
+ readonly code: ServerOperationErrorCode;
8
+ constructor(code: ServerOperationErrorCode, message: string);
9
+ }
10
+ export declare class WrongServerError extends ServerError {
11
+ name: string;
12
+ constructor();
13
+ }
14
+ export declare class SessionNotFoundError extends ServerError {
15
+ name: string;
16
+ constructor(message?: string);
17
+ }
18
+ export declare class SessionAmbiguousError extends ServerError {
19
+ name: string;
20
+ constructor();
21
+ }
22
+ export declare class SessionNotAttachedError extends ServerError {
23
+ name: string;
24
+ constructor();
25
+ }
26
+ export declare class ServerDrainingError extends ServerError {
27
+ name: string;
28
+ constructor();
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=server-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-errors.d.ts","sourceRoot":"","sources":["../src/server-errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,KAAK,wBAAwB,GAAG,sBAAsB,GACnD,cAAc,GACd,mBAAmB,GACnB,mBAAmB,GACnB,sBAAsB,GACtB,iBAAiB,CAAC;AAErB,eAAO,MAAM,6BAA6B,0BAA0B,CAAC;AAErE,+EAA+E;AAC/E,qBAAa,WACZ,SAAQ,KAAK;IACJ,IAAI,SAAiB;IAC9B,QAAQ,CAAC,IAAI,EACZ,wBAAwB,CAAC;IAE1B,YACC,IAAI,EAAE,wBAAwB,EAC9B,OAAO,EAAE,MAAM,EAIf;CACD;AAED,qBAAa,gBACZ,SAAQ,WAAW;IACV,IAAI,SAAsB;IAEnC,cAKC;CACD;AAED,qBAAa,oBACZ,SAAQ,WAAW;IACV,IAAI,SAA0B;IAEvC,YACC,OAAO,SAA0B,EAMjC;CACD;AAED,qBAAa,qBACZ,SAAQ,WAAW;IACV,IAAI,SAA2B;IAExC,cAKC;CACD;AAED,qBAAa,uBACZ,SAAQ,WAAW;IACV,IAAI,SAA6B;IAE1C,cAKC;CACD;AAED,qBAAa,mBACZ,SAAQ,WAAW;IACV,IAAI,SAAyB;IAEtC,cAKC;CACD"}
@@ -0,0 +1,41 @@
1
+ export const INTERNAL_SERVER_ERROR_MESSAGE = "Internal server error";
2
+ /** A host or lifecycle failure that may safely cross the protocol boundary. */
3
+ export class ServerError extends Error {
4
+ name = "ServerError";
5
+ code;
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.code = code;
9
+ }
10
+ }
11
+ export class WrongServerError extends ServerError {
12
+ name = "WrongServerError";
13
+ constructor() {
14
+ super("wrong_server", "Request was addressed to another server");
15
+ }
16
+ }
17
+ export class SessionNotFoundError extends ServerError {
18
+ name = "SessionNotFoundError";
19
+ constructor(message = "Session was not found") {
20
+ super("session_not_found", message);
21
+ }
22
+ }
23
+ export class SessionAmbiguousError extends ServerError {
24
+ name = "SessionAmbiguousError";
25
+ constructor() {
26
+ super("session_ambiguous", "Session ID matches more than one session");
27
+ }
28
+ }
29
+ export class SessionNotAttachedError extends ServerError {
30
+ name = "SessionNotAttachedError";
31
+ constructor() {
32
+ super("session_not_attached", "Session is not attached to this client");
33
+ }
34
+ }
35
+ export class ServerDrainingError extends ServerError {
36
+ name = "ServerDrainingError";
37
+ constructor() {
38
+ super("server_draining", "Server is draining");
39
+ }
40
+ }
41
+ //# sourceMappingURL=server-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-errors.js","sourceRoot":"","sources":["../src/server-errors.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,6BAA6B,GAAG,uBAAuB,CAAC;AAErE,+EAA+E;AAC/E,MAAM,OAAO,WACZ,SAAQ,KAAK;IACJ,IAAI,GAAG,aAAa,CAAC;IACrB,IAAI,CACa;IAE1B,YACC,IAA8B,EAC9B,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAED,MAAM,OAAO,gBACZ,SAAQ,WAAW;IACV,IAAI,GAAG,kBAAkB,CAAC;IAEnC;QACC,KAAK,CACJ,cAAc,EACd,yCAAyC,CACzC,CAAC;IACH,CAAC;CACD;AAED,MAAM,OAAO,oBACZ,SAAQ,WAAW;IACV,IAAI,GAAG,sBAAsB,CAAC;IAEvC,YACC,OAAO,GAAG,uBAAuB;QAEjC,KAAK,CACJ,mBAAmB,EACnB,OAAO,CACP,CAAC;IACH,CAAC;CACD;AAED,MAAM,OAAO,qBACZ,SAAQ,WAAW;IACV,IAAI,GAAG,uBAAuB,CAAC;IAExC;QACC,KAAK,CACJ,mBAAmB,EACnB,0CAA0C,CAC1C,CAAC;IACH,CAAC;CACD;AAED,MAAM,OAAO,uBACZ,SAAQ,WAAW;IACV,IAAI,GAAG,yBAAyB,CAAC;IAE1C;QACC,KAAK,CACJ,sBAAsB,EACtB,wCAAwC,CACxC,CAAC;IACH,CAAC;CACD;AAED,MAAM,OAAO,mBACZ,SAAQ,WAAW;IACV,IAAI,GAAG,qBAAqB,CAAC;IAEtC;QACC,KAAK,CACJ,iBAAiB,EACjB,oBAAoB,CACpB,CAAC;IACH,CAAC;CACD","sourcesContent":["import type { RemoteServiceErrorCode } from \"@crow-agent/chord\";\n\ntype ServerOperationErrorCode = RemoteServiceErrorCode\n\t| \"wrong_server\"\n\t| \"session_not_found\"\n\t| \"session_ambiguous\"\n\t| \"session_not_attached\"\n\t| \"server_draining\";\n\nexport const INTERNAL_SERVER_ERROR_MESSAGE = \"Internal server error\";\n\n/** A host or lifecycle failure that may safely cross the protocol boundary. */\nexport class ServerError\n\textends Error {\n\toverride name = \"ServerError\";\n\treadonly code:\n\t\tServerOperationErrorCode;\n\n\tconstructor(\n\t\tcode: ServerOperationErrorCode,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t}\n}\n\nexport class WrongServerError\n\textends ServerError {\n\toverride name = \"WrongServerError\";\n\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"wrong_server\",\n\t\t\t\"Request was addressed to another server\",\n\t\t);\n\t}\n}\n\nexport class SessionNotFoundError\n\textends ServerError {\n\toverride name = \"SessionNotFoundError\";\n\n\tconstructor(\n\t\tmessage = \"Session was not found\",\n\t) {\n\t\tsuper(\n\t\t\t\"session_not_found\",\n\t\t\tmessage,\n\t\t);\n\t}\n}\n\nexport class SessionAmbiguousError\n\textends ServerError {\n\toverride name = \"SessionAmbiguousError\";\n\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"session_ambiguous\",\n\t\t\t\"Session ID matches more than one session\",\n\t\t);\n\t}\n}\n\nexport class SessionNotAttachedError\n\textends ServerError {\n\toverride name = \"SessionNotAttachedError\";\n\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"session_not_attached\",\n\t\t\t\"Session is not attached to this client\",\n\t\t);\n\t}\n}\n\nexport class ServerDrainingError\n\textends ServerError {\n\toverride name = \"ServerDrainingError\";\n\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"server_draining\",\n\t\t\t\"Server is draining\",\n\t\t);\n\t}\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import type { ByteConnectionAcceptor } from "./server-connection.ts";
2
+ /** Provides established byte connections once transport authentication completes. */
3
+ export type ServerListener = {
4
+ /** Begins listening and hands authorized connections to accept. */
5
+ start(accept: ByteConnectionAcceptor): Promise<void>;
6
+ close(): Promise<void>;
7
+ };
8
+ //# sourceMappingURL=server-listener.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-listener.d.ts","sourceRoot":"","sources":["../src/server-listener.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,sBAAsB,EACtB,MAAM,wBAAwB,CAAC;AAEhC,qFAAqF;AACrF,MAAM,MAAM,cAAc,GAAG;IAC5B,mEAAmE;IACnE,KAAK,CACJ,MAAM,EAAE,sBAAsB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server-listener.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-listener.js","sourceRoot":"","sources":["../src/server-listener.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n\tByteConnectionAcceptor,\n} from \"./server-connection.ts\";\n\n/** Provides established byte connections once transport authentication completes. */\nexport type ServerListener = {\n\t/** Begins listening and hands authorized connections to accept. */\n\tstart(\n\t\taccept: ByteConnectionAcceptor,\n\t): Promise<void>;\n\tclose(): Promise<void>;\n};\n"]}
@@ -0,0 +1,52 @@
1
+ import type { JsonValue, ServiceCall, ServiceProviderUpdate } from "@crow-agent/chord";
2
+ import type { Context, SessionMetadata } from "@crow-agent/agent-core";
3
+ import type { ServerListener } from "./server-listener.ts";
4
+ export type ServerOptions = {
5
+ listeners: readonly ServerListener[];
6
+ /** Long-lived logical identity for this server, set by the launcher or profile. */
7
+ serverId: string;
8
+ maxFrameLength?: number;
9
+ handshakeTimeoutMs?: number;
10
+ onConnectionCountChanged?: (count: number) => void;
11
+ onError?: (error: Error) => void;
12
+ };
13
+ export type MaybePromise<T> = T | Promise<T>;
14
+ /** Publishes one service update for a subscription owned by the calling attachment. */
15
+ type ServicePublisher = (subscriptionId: string, update: ServiceProviderUpdate, context: Context) => MaybePromise<void>;
16
+ /** The live capability one presentation connection holds for a hosted Session. */
17
+ export type RoutedSessionAttachment = {
18
+ /** Forwards one opaque service operation to the attached Session endpoint. */
19
+ invokeService: (call: ServiceCall, publish: ServicePublisher, context: Context) => Promise<JsonValue | undefined>;
20
+ release: (context: Context) => MaybePromise<void>;
21
+ };
22
+ /** Routing capabilities a presentation exposes to server service implementations. */
23
+ export type RoutedServerPresentation = {
24
+ attachSession: (sessionId: string, context: Context) => Promise<void>;
25
+ detachSession: (context: Context) => Promise<void>;
26
+ /** Drops routed attachments and handles before durable metadata is deleted. */
27
+ prepareSessionRemoval: (sessionId: string, context: Context) => Promise<void>;
28
+ };
29
+ /** The server-scoped service endpoint for one connection. */
30
+ export type RoutedServerServiceAttachment = {
31
+ invokeService: (call: ServiceCall, publish: ServicePublisher, context: Context) => Promise<JsonValue | undefined>;
32
+ release: (context: Context) => MaybePromise<void>;
33
+ };
34
+ export type RoutedServerServiceHost = {
35
+ attachClient: (presentation: RoutedServerPresentation, context: Context) => MaybePromise<RoutedServerServiceAttachment>;
36
+ };
37
+ /** Handle that acquires presentation-scoped capabilities for a Session. */
38
+ export type RoutedSessionHandle = {
39
+ attachClient: (context: Context) => MaybePromise<RoutedSessionAttachment>;
40
+ /** Settles with an error on unexpected termination, or undefined after a clean close. */
41
+ readonly terminated?: Promise<Error | undefined>;
42
+ close: (context: Context) => Promise<void>;
43
+ };
44
+ /** Application hooks for server-wide management and Session routing. */
45
+ export type ServerHost<TMetadata extends SessionMetadata = SessionMetadata> = {
46
+ readonly serverServices: RoutedServerServiceHost;
47
+ /** Resolves one durable Session ID, or throws a bounded routing error. */
48
+ resolveSession: (sessionId: string, context: Context) => Promise<TMetadata>;
49
+ openSession: (metadata: TMetadata, context: Context) => Promise<RoutedSessionHandle>;
50
+ };
51
+ export {};
52
+ //# sourceMappingURL=server-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-types.d.ts","sourceRoot":"","sources":["../src/server-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-types.js","sourceRoot":"","sources":["../src/server-types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n\tJsonValue,\n\tServiceCall,\n\tServiceProviderUpdate,\n} from \"@crow-agent/chord\";\nimport type {\n\tContext,\n\tSessionMetadata,\n} from \"@crow-agent/agent-core\";\nimport type {\n\tServerListener,\n} from \"./server-listener.ts\";\n\nexport type ServerOptions = {\n\tlisteners:\n\t\treadonly ServerListener[];\n\t/** Long-lived logical identity for this server, set by the launcher or profile. */\n\tserverId: string;\n\tmaxFrameLength?: number;\n\thandshakeTimeoutMs?:\n\t\tnumber;\n\tonConnectionCountChanged?: (\n\t\tcount: number,\n\t) => void;\n\tonError?: (\n\t\terror: Error,\n\t) => void;\n};\n\nexport type MaybePromise<T> =\n\tT | Promise<T>;\n\n/** Publishes one service update for a subscription owned by the calling attachment. */\ntype ServicePublisher = (\n\tsubscriptionId: string,\n\tupdate: ServiceProviderUpdate,\n\tcontext: Context,\n) => MaybePromise<void>;\n\n/** The live capability one presentation connection holds for a hosted Session. */\nexport type RoutedSessionAttachment = {\n\t/** Forwards one opaque service operation to the attached Session endpoint. */\n\tinvokeService: (\n\t\tcall: ServiceCall,\n\t\tpublish: ServicePublisher,\n\t\tcontext: Context,\n\t) => Promise<JsonValue | undefined>;\n\trelease: (\n\t\tcontext: Context,\n\t) => MaybePromise<void>;\n};\n\n/** Routing capabilities a presentation exposes to server service implementations. */\nexport type RoutedServerPresentation = {\n\tattachSession: (\n\t\tsessionId: string,\n\t\tcontext: Context,\n\t) => Promise<void>;\n\tdetachSession: (\n\t\tcontext: Context,\n\t) => Promise<void>;\n\t/** Drops routed attachments and handles before durable metadata is deleted. */\n\tprepareSessionRemoval: (\n\t\tsessionId: string,\n\t\tcontext: Context,\n\t) => Promise<void>;\n};\n\n/** The server-scoped service endpoint for one connection. */\nexport type RoutedServerServiceAttachment = {\n\tinvokeService: (\n\t\tcall: ServiceCall,\n\t\tpublish: ServicePublisher,\n\t\tcontext: Context,\n\t) => Promise<JsonValue | undefined>;\n\trelease: (\n\t\tcontext: Context,\n\t) => MaybePromise<void>;\n};\n\nexport type RoutedServerServiceHost = {\n\tattachClient: (\n\t\tpresentation: RoutedServerPresentation,\n\t\tcontext: Context,\n\t) => MaybePromise<RoutedServerServiceAttachment>;\n};\n\n/** Handle that acquires presentation-scoped capabilities for a Session. */\nexport type RoutedSessionHandle = {\n\tattachClient: (\n\t\tcontext: Context,\n\t) => MaybePromise<RoutedSessionAttachment>;\n\t/** Settles with an error on unexpected termination, or undefined after a clean close. */\n\treadonly terminated?:\n\t\tPromise<Error | undefined>;\n\tclose: (\n\t\tcontext: Context,\n\t) => Promise<void>;\n};\n\n/** Application hooks for server-wide management and Session routing. */\nexport type ServerHost<TMetadata extends SessionMetadata = SessionMetadata> = {\n\treadonly serverServices:\n\t\tRoutedServerServiceHost;\n\t/** Resolves one durable Session ID, or throws a bounded routing error. */\n\tresolveSession: (\n\t\tsessionId: string,\n\t\tcontext: Context,\n\t) => Promise<TMetadata>;\n\topenSession: (\n\t\tmetadata: TMetadata,\n\t\tcontext: Context,\n\t) => Promise<RoutedSessionHandle>;\n};\n"]}
@@ -0,0 +1,42 @@
1
+ import type { JsonValue, ServiceCall, ServiceProviderUpdate } from "@crow-agent/chord";
2
+ import { type Context, type SessionMetadata } from "@crow-agent/agent-core";
3
+ import type { RpcTarget, SessionTarget } from "@crow-agent/protocol";
4
+ import type { ServerHost } from "./server-types.ts";
5
+ type RouterOptions<TMetadata extends SessionMetadata> = {
6
+ host: ServerHost<TMetadata>;
7
+ isClosing: () => boolean;
8
+ reportError: (error: unknown) => void;
9
+ serverId: string;
10
+ publishAttachment(client: object, attachment: SessionTarget | undefined, context: Context): Promise<void>;
11
+ };
12
+ type UpdatePublisher = (subscriptionId: string, update: ServiceProviderUpdate, context: Context) => Promise<void>;
13
+ export declare class SessionRouter<TMetadata extends SessionMetadata = SessionMetadata> {
14
+ #private;
15
+ constructor(options: RouterOptions<TMetadata>);
16
+ executeServiceCall(call: ServiceCall, target: RpcTarget, client: object, publish: UpdatePublisher, context: Context): Promise<JsonValue | undefined>;
17
+ attachClient(client: object, sessionId: string, context: Context): Promise<void>;
18
+ detachClient(client: object, context: Context): Promise<void>;
19
+ removeSession(sessionId: string, context: Context): Promise<void>;
20
+ disconnect(client: object, context: Context): Promise<void>;
21
+ close(context: Context): Promise<void>;
22
+ private shutdown;
23
+ private draining;
24
+ private clientGone;
25
+ private enqueueForClient;
26
+ private attachNow;
27
+ private leaseLink;
28
+ private linkStale;
29
+ private beginServiceCall;
30
+ private trackCall;
31
+ private requireLink;
32
+ private unlink;
33
+ private teardownLink;
34
+ private releaseLease;
35
+ private forgetLink;
36
+ private openHosted;
37
+ private createHosted;
38
+ private closeDrainedHandle;
39
+ private dropHosted;
40
+ }
41
+ export {};
42
+ //# sourceMappingURL=session-routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-routing.d.ts","sourceRoot":"","sources":["../src/session-routing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,SAAS,EACT,WAAW,EACX,qBAAqB,EACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,eAAe,EACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAKrE,OAAO,KAAK,EAGX,UAAU,EACV,MAAM,mBAAmB,CAAC;AAoB3B,KAAK,aAAa,CAAC,SAAS,SAAS,eAAe,IAAI;IACvD,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAC5B,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAChB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,aAAa,GAAG,SAAS,EACrC,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB,CAAC;AAEF,KAAK,eAAe,GAAG,CACtB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,OAAO,KACZ,OAAO,CAAC,IAAI,CAAC,CAAC;AAOnB,qBAAa,aAAa,CAAC,SAAS,SAAS,eAAe,GAAG,eAAe;;IAS7E,YAAY,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,EAE5C;IAEK,kBAAkB,CACvB,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAKhC;IAED,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/E;IAED,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAK5D;IAEK,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAetE;IAEK,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhE;IAED,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrC;YAWa,QAAQ;IA4BtB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,gBAAgB;YAeV,SAAS;YAqBT,SAAS;IAcvB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,MAAM;YAKA,YAAY;YAeZ,YAAY;YAKZ,UAAU;YAOV,UAAU;YAcV,YAAY;YAoBZ,kBAAkB;IAYhC,OAAO,CAAC,UAAU;CAUlB"}