@masons/agent-network 0.5.28 → 0.5.30

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 (72) hide show
  1. package/dist/broker/broker-daemon.d.ts +34 -0
  2. package/dist/broker/broker-daemon.d.ts.map +1 -0
  3. package/dist/broker/broker-daemon.js +471 -0
  4. package/dist/broker/closed-endpoint-lookup.d.ts +25 -0
  5. package/dist/broker/closed-endpoint-lookup.d.ts.map +1 -0
  6. package/dist/broker/closed-endpoint-lookup.js +59 -0
  7. package/dist/broker/connector-ws.d.ts +47 -0
  8. package/dist/broker/connector-ws.d.ts.map +1 -0
  9. package/dist/broker/connector-ws.js +60 -0
  10. package/dist/broker/control-event-dispatcher.d.ts +20 -0
  11. package/dist/broker/control-event-dispatcher.d.ts.map +1 -0
  12. package/dist/broker/control-event-dispatcher.js +45 -0
  13. package/dist/broker/control-event-types.d.ts +30 -0
  14. package/dist/broker/control-event-types.d.ts.map +1 -0
  15. package/dist/broker/control-event-types.js +1 -0
  16. package/dist/broker/correlation-ring.d.ts +10 -0
  17. package/dist/broker/correlation-ring.d.ts.map +1 -0
  18. package/dist/broker/correlation-ring.js +32 -0
  19. package/dist/broker/discovery-file.d.ts +12 -0
  20. package/dist/broker/discovery-file.d.ts.map +1 -0
  21. package/dist/broker/discovery-file.js +77 -0
  22. package/dist/broker/endpoint-registry.d.ts +53 -0
  23. package/dist/broker/endpoint-registry.d.ts.map +1 -0
  24. package/dist/broker/endpoint-registry.js +83 -0
  25. package/dist/broker/endpoint-state-machine.d.ts +40 -0
  26. package/dist/broker/endpoint-state-machine.d.ts.map +1 -0
  27. package/dist/broker/endpoint-state-machine.js +92 -0
  28. package/dist/broker/entry.d.ts +5 -0
  29. package/dist/broker/entry.d.ts.map +1 -0
  30. package/dist/broker/entry.js +123 -0
  31. package/dist/broker/grace-timer.d.ts +9 -0
  32. package/dist/broker/grace-timer.d.ts.map +1 -0
  33. package/dist/broker/grace-timer.js +34 -0
  34. package/dist/broker/ipc-server.d.ts +72 -0
  35. package/dist/broker/ipc-server.d.ts.map +1 -0
  36. package/dist/broker/ipc-server.js +243 -0
  37. package/dist/broker/logger.d.ts +10 -0
  38. package/dist/broker/logger.d.ts.map +1 -0
  39. package/dist/broker/logger.js +34 -0
  40. package/dist/broker/paths.d.ts +11 -0
  41. package/dist/broker/paths.d.ts.map +1 -0
  42. package/dist/broker/paths.js +30 -0
  43. package/dist/broker/plugin-liveness.d.ts +2 -0
  44. package/dist/broker/plugin-liveness.d.ts.map +1 -0
  45. package/dist/broker/plugin-liveness.js +15 -0
  46. package/dist/broker/reconnecting-buffer.d.ts +23 -0
  47. package/dist/broker/reconnecting-buffer.d.ts.map +1 -0
  48. package/dist/broker/reconnecting-buffer.js +107 -0
  49. package/dist/broker/routing-table.d.ts +22 -0
  50. package/dist/broker/routing-table.d.ts.map +1 -0
  51. package/dist/broker/routing-table.js +35 -0
  52. package/dist/broker/runtime-endpoint-port.d.ts +8 -0
  53. package/dist/broker/runtime-endpoint-port.d.ts.map +1 -0
  54. package/dist/broker/runtime-endpoint-port.js +1 -0
  55. package/dist/broker/undispatched-inbox.d.ts +26 -0
  56. package/dist/broker/undispatched-inbox.d.ts.map +1 -0
  57. package/dist/broker/undispatched-inbox.js +53 -0
  58. package/dist/broker/version-handshake.d.ts +30 -0
  59. package/dist/broker/version-handshake.d.ts.map +1 -0
  60. package/dist/broker/version-handshake.js +47 -0
  61. package/dist/broker-client/broker-client.d.ts +64 -0
  62. package/dist/broker-client/broker-client.d.ts.map +1 -0
  63. package/dist/broker-client/broker-client.js +164 -0
  64. package/dist/broker-client/lazy-spawn.d.ts +18 -0
  65. package/dist/broker-client/lazy-spawn.d.ts.map +1 -0
  66. package/dist/broker-client/lazy-spawn.js +61 -0
  67. package/dist/runtime-endpoint-client.d.ts +9 -2
  68. package/dist/runtime-endpoint-client.d.ts.map +1 -1
  69. package/dist/runtime-endpoint-client.js +15 -2
  70. package/dist/version.d.ts +1 -1
  71. package/dist/version.js +1 -1
  72. package/package.json +11 -2
@@ -0,0 +1,60 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { ConnectorClient, ConnectorError } from "../connector-client.js";
3
+ export class ConnectorWS {
4
+ client;
5
+ bus = new EventEmitter();
6
+ constructor(opts) {
7
+ this.client = new ConnectorClient(opts.url, opts.token);
8
+ this.wireForwarders();
9
+ }
10
+ connect() {
11
+ return this.client.connect();
12
+ }
13
+ disconnect() {
14
+ this.client.disconnect();
15
+ }
16
+ send(to, content, contentType = "text", metadata) {
17
+ return this.client.send(to, content, contentType, metadata);
18
+ }
19
+ ackDelivery(upTo) {
20
+ return this.client.ackDelivery(upTo);
21
+ }
22
+ on(event, listener) {
23
+ this.bus.on(event, listener);
24
+ return this;
25
+ }
26
+ off(event, listener) {
27
+ this.bus.off(event, listener);
28
+ return this;
29
+ }
30
+ _emitForTesting(e) {
31
+ this.bus.emit(e.event, e);
32
+ }
33
+ wireForwarders() {
34
+ this.client.on("message_received", (payload) => {
35
+ this.bus.emit("message_received", { event: "message_received", payload });
36
+ });
37
+ this.client.on("send_ack", (payload) => {
38
+ this.bus.emit("send_ack", { event: "send_ack", payload });
39
+ });
40
+ this.client.on("delivery_pending", (payload) => {
41
+ this.bus.emit("delivery_pending", { event: "delivery_pending", payload });
42
+ });
43
+ this.client.on("delivery_status", (payload) => {
44
+ this.bus.emit("delivery_status", { event: "delivery_status", payload });
45
+ });
46
+ this.client.on("structured_error", (payload) => {
47
+ this.bus.emit("structured_error", { event: "structured_error", payload });
48
+ });
49
+ this.client.on("connected", () => {
50
+ this.bus.emit("connected", { event: "connected" });
51
+ });
52
+ this.client.on("disconnected", () => {
53
+ this.bus.emit("disconnected", { event: "disconnected" });
54
+ });
55
+ this.client.on("error", (err) => {
56
+ this.bus.emit("error", { event: "error", payload: err });
57
+ });
58
+ }
59
+ }
60
+ export { ConnectorError };
@@ -0,0 +1,20 @@
1
+ import type { ControlEvent } from "./control-event-types.js";
2
+ export declare const DEFAULT_IDEMPOTENCY_RING_SIZE = 1024;
3
+ export type ControlEventOutcome = {
4
+ ok: true;
5
+ } | {
6
+ ok: false;
7
+ detail: string;
8
+ };
9
+ export type ControlEventHandler<E extends ControlEvent = ControlEvent> = (event: E) => Promise<ControlEventOutcome>;
10
+ export interface ControlEventDispatcher {
11
+ on<E extends ControlEvent>(type: E["type"], handler: ControlEventHandler<E>): void;
12
+ dispatch(event: ControlEvent): Promise<ControlEventOutcome>;
13
+ ringSize(): number;
14
+ hasSeen(idempotency_key: string): boolean;
15
+ }
16
+ export interface ControlEventDispatcherOptions {
17
+ ringSize?: number;
18
+ }
19
+ export declare function createControlEventDispatcher(opts?: ControlEventDispatcherOptions): ControlEventDispatcher;
20
+ //# sourceMappingURL=control-event-dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-event-dispatcher.d.ts","sourceRoot":"","sources":["../../src/broker/control-event-dispatcher.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,MAAM,MAAM,mBAAmB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,IAAI,CACvE,KAAK,EAAE,CAAC,KACL,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC,MAAM,WAAW,sBAAsB;IAErC,EAAE,CAAC,CAAC,SAAS,YAAY,EACvB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EACf,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC9B,IAAI,CAAC;IAKR,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAE5D,QAAQ,IAAI,MAAM,CAAC;IAEnB,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3C;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE,6BAAkC,GACvC,sBAAsB,CAqDxB"}
@@ -0,0 +1,45 @@
1
+ export const DEFAULT_IDEMPOTENCY_RING_SIZE = 1024;
2
+ export function createControlEventDispatcher(opts = {}) {
3
+ const cap = opts.ringSize ?? DEFAULT_IDEMPOTENCY_RING_SIZE;
4
+ if (cap <= 0)
5
+ throw new Error(`ringSize must be positive: ${cap}`);
6
+ const seen = new Set();
7
+ const order = [];
8
+ const handlers = new Map();
9
+ return {
10
+ on(type, handler) {
11
+ handlers.set(type, handler);
12
+ },
13
+ async dispatch(event) {
14
+ if (seen.has(event.idempotency_key)) {
15
+ return { ok: true };
16
+ }
17
+ seen.add(event.idempotency_key);
18
+ order.push(event.idempotency_key);
19
+ if (order.length > cap) {
20
+ const oldest = order.shift();
21
+ if (oldest !== undefined)
22
+ seen.delete(oldest);
23
+ }
24
+ const handler = handlers.get(event.type);
25
+ if (!handler) {
26
+ return { ok: false, detail: `no_handler:${event.type}` };
27
+ }
28
+ try {
29
+ return await handler(event);
30
+ }
31
+ catch (err) {
32
+ return {
33
+ ok: false,
34
+ detail: err instanceof Error ? err.message : String(err),
35
+ };
36
+ }
37
+ },
38
+ ringSize() {
39
+ return seen.size;
40
+ },
41
+ hasSeen(idempotency_key) {
42
+ return seen.has(idempotency_key);
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,30 @@
1
+ export type ControlEvent = DispatchUndispatchedEvent | SpawnRequestEvent | ForceUnregisterEvent;
2
+ export interface DispatchUndispatchedEvent {
3
+ type: "dispatch_undispatched";
4
+ version: 1;
5
+ idempotency_key: string;
6
+ undispatched_id: string;
7
+ target_endpoint_id: string;
8
+ }
9
+ export interface SpawnRequestEvent {
10
+ type: "spawn_request";
11
+ version: 1;
12
+ idempotency_key: string;
13
+ request_id: string;
14
+ prompt: string;
15
+ runtime_kind: string;
16
+ origin: "passport-spawn";
17
+ }
18
+ export interface ForceUnregisterEvent {
19
+ type: "force_unregister";
20
+ version: 1;
21
+ idempotency_key: string;
22
+ endpoint_id: string;
23
+ reason: string;
24
+ }
25
+ export interface ControlAck {
26
+ request_id: string;
27
+ status: "ok" | "error";
28
+ detail?: string;
29
+ }
30
+ //# sourceMappingURL=control-event-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-event-types.d.ts","sourceRoot":"","sources":["../../src/broker/control-event-types.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,YAAY,GACpB,yBAAyB,GACzB,iBAAiB,GACjB,oBAAoB,CAAC;AAGzB,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAGD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,CAAC,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAGD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,CAAC,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ export declare class CorrelationRingBuffer {
2
+ private readonly capacity;
3
+ private readonly set;
4
+ private readonly order;
5
+ constructor(capacity?: number);
6
+ add(correlation_id: string): void;
7
+ has(correlation_id: string): boolean;
8
+ size(): number;
9
+ }
10
+ //# sourceMappingURL=correlation-ring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation-ring.d.ts","sourceRoot":"","sources":["../../src/broker/correlation-ring.ts"],"names":[],"mappings":"AAaA,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;gBAErB,QAAQ,GAAE,MAAoB;IAU1C,GAAG,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAYjC,GAAG,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO;IAIpC,IAAI,IAAI,MAAM;CAGf"}
@@ -0,0 +1,32 @@
1
+ const DEFAULT_CAP = 1024;
2
+ export class CorrelationRingBuffer {
3
+ capacity;
4
+ set;
5
+ order;
6
+ constructor(capacity = DEFAULT_CAP) {
7
+ if (capacity <= 0) {
8
+ throw new Error(`capacity must be positive: ${capacity}`);
9
+ }
10
+ this.capacity = capacity;
11
+ this.set = new Set();
12
+ this.order = [];
13
+ }
14
+ add(correlation_id) {
15
+ if (this.set.has(correlation_id)) {
16
+ return;
17
+ }
18
+ if (this.order.length >= this.capacity) {
19
+ const oldest = this.order.shift();
20
+ if (oldest !== undefined)
21
+ this.set.delete(oldest);
22
+ }
23
+ this.set.add(correlation_id);
24
+ this.order.push(correlation_id);
25
+ }
26
+ has(correlation_id) {
27
+ return this.set.has(correlation_id);
28
+ }
29
+ size() {
30
+ return this.set.size;
31
+ }
32
+ }
@@ -0,0 +1,12 @@
1
+ export interface DiscoveryRecord {
2
+ version: 1;
3
+ pid: number;
4
+ startedAt: string;
5
+ ipcUrl: string;
6
+ bearerToken: string;
7
+ }
8
+ export declare function mintBearerToken(): string;
9
+ export declare function writeDiscoveryFile(path: string, record: DiscoveryRecord): void;
10
+ export declare function readDiscoveryFile(path: string): DiscoveryRecord | null;
11
+ export declare function deleteDiscoveryFile(path: string): void;
12
+ //# sourceMappingURL=discovery-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery-file.d.ts","sourceRoot":"","sources":["../../src/broker/discovery-file.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAGD,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAMD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,eAAe,GACtB,IAAI,CA4BN;AAMD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAetE;AAGD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAOtD"}
@@ -0,0 +1,77 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { chmodSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ export function mintBearerToken() {
5
+ return randomBytes(32).toString("base64url");
6
+ }
7
+ export function writeDiscoveryFile(path, record) {
8
+ if (record.version !== 1) {
9
+ throw new Error(`unsupported discovery record version: ${record.version}`);
10
+ }
11
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
12
+ const tmp = `${path}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
13
+ const payload = JSON.stringify(record, null, 2);
14
+ writeFileSync(tmp, payload, { mode: 0o600, encoding: "utf8" });
15
+ try {
16
+ chmodSync(tmp, 0o600);
17
+ }
18
+ catch {
19
+ }
20
+ try {
21
+ renameSync(tmp, path);
22
+ }
23
+ catch (err) {
24
+ try {
25
+ unlinkSync(tmp);
26
+ }
27
+ catch {
28
+ }
29
+ throw err;
30
+ }
31
+ }
32
+ export function readDiscoveryFile(path) {
33
+ let raw;
34
+ try {
35
+ raw = readFileSync(path, "utf8");
36
+ }
37
+ catch (err) {
38
+ if (isNodeErr(err, "ENOENT"))
39
+ return null;
40
+ throw err;
41
+ }
42
+ try {
43
+ const parsed = JSON.parse(raw);
44
+ if (!isDiscoveryRecord(parsed))
45
+ return null;
46
+ return parsed;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ export function deleteDiscoveryFile(path) {
53
+ try {
54
+ unlinkSync(path);
55
+ }
56
+ catch (err) {
57
+ if (isNodeErr(err, "ENOENT"))
58
+ return;
59
+ throw err;
60
+ }
61
+ }
62
+ function isDiscoveryRecord(value) {
63
+ if (typeof value !== "object" || value === null)
64
+ return false;
65
+ const v = value;
66
+ return (v.version === 1 &&
67
+ typeof v.pid === "number" &&
68
+ typeof v.startedAt === "string" &&
69
+ typeof v.ipcUrl === "string" &&
70
+ typeof v.bearerToken === "string");
71
+ }
72
+ function isNodeErr(err, code) {
73
+ return (typeof err === "object" &&
74
+ err !== null &&
75
+ "code" in err &&
76
+ err.code === code);
77
+ }
@@ -0,0 +1,53 @@
1
+ import type WebSocket from "ws";
2
+ import { CorrelationRingBuffer } from "./correlation-ring.js";
3
+ export interface DisplayMetadata {
4
+ session_name?: string;
5
+ kind: string;
6
+ started_at: string;
7
+ workspace?: string;
8
+ tracking_ref?: string;
9
+ task_hint?: string;
10
+ }
11
+ export interface BrokerEndpointEntry {
12
+ endpoint_id: string;
13
+ agent_id: string;
14
+ plugin_pid: number;
15
+ ipc_ws: WebSocket;
16
+ state: "active" | "reconnecting";
17
+ state_since: Date;
18
+ grace_deadline?: Date;
19
+ display_metadata: DisplayMetadata;
20
+ correlation_ring: CorrelationRingBuffer;
21
+ }
22
+ export interface RegisterArgs {
23
+ endpoint_id: string;
24
+ agent_id: string;
25
+ plugin_pid: number;
26
+ ipc_ws: WebSocket;
27
+ display_metadata: DisplayMetadata;
28
+ }
29
+ export declare class EndpointRegistry {
30
+ private readonly byId;
31
+ register(args: RegisterArgs): BrokerEndpointEntry;
32
+ get(endpoint_id: string): BrokerEndpointEntry | undefined;
33
+ unregister(endpoint_id: string): BrokerEndpointEntry | undefined;
34
+ markReconnecting(endpoint_id: string, grace_deadline: Date): BrokerEndpointEntry;
35
+ markActive(endpoint_id: string, new_ws: WebSocket): BrokerEndpointEntry;
36
+ preconditionForReattach(endpoint_id: string, plugin_pid: number): BrokerEndpointEntry | undefined;
37
+ getReattachStatus(endpoint_id: string, plugin_pid: number): {
38
+ ok: true;
39
+ entry: BrokerEndpointEntry;
40
+ } | {
41
+ ok: false;
42
+ reason: "endpoint_unknown";
43
+ } | {
44
+ ok: false;
45
+ reason: "reattach_pid_mismatch";
46
+ } | {
47
+ ok: false;
48
+ reason: "endpoint_not_reconnecting";
49
+ };
50
+ list(): BrokerEndpointEntry[];
51
+ size(): number;
52
+ }
53
+ //# sourceMappingURL=endpoint-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-registry.d.ts","sourceRoot":"","sources":["../../src/broker/endpoint-registry.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,QAAQ,GAAG,cAAc,CAAC;IACjC,WAAW,EAAE,IAAI,CAAC;IAElB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,gBAAgB,EAAE,eAAe,CAAC;IAClC,gBAAgB,EAAE,qBAAqB,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,CAAC;IAClB,gBAAgB,EAAE,eAAe,CAAC;CACnC;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0C;IAG/D,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,mBAAmB;IAkBjD,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAKzD,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAWhE,gBAAgB,CACd,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,IAAI,GACnB,mBAAmB;IAoBtB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,mBAAmB;IA8BvE,uBAAuB,CACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,mBAAmB,GAAG,SAAS;IAYlC,iBAAiB,CACf,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GAEhB;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,mBAAmB,CAAA;KAAE,GACxC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,kBAAkB,CAAA;KAAE,GACzC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,uBAAuB,CAAA;KAAE,GAC9C;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,2BAA2B,CAAA;KAAE;IAWtD,IAAI,IAAI,mBAAmB,EAAE;IAK7B,IAAI,IAAI,MAAM;CAGf"}
@@ -0,0 +1,83 @@
1
+ import { CorrelationRingBuffer } from "./correlation-ring.js";
2
+ export class EndpointRegistry {
3
+ byId = new Map();
4
+ register(args) {
5
+ if (this.byId.has(args.endpoint_id)) {
6
+ throw new Error(`endpoint_id already registered: ${args.endpoint_id}`);
7
+ }
8
+ const entry = {
9
+ endpoint_id: args.endpoint_id,
10
+ agent_id: args.agent_id,
11
+ plugin_pid: args.plugin_pid,
12
+ ipc_ws: args.ipc_ws,
13
+ state: "active",
14
+ state_since: new Date(),
15
+ display_metadata: args.display_metadata,
16
+ correlation_ring: new CorrelationRingBuffer(),
17
+ };
18
+ this.byId.set(args.endpoint_id, entry);
19
+ return entry;
20
+ }
21
+ get(endpoint_id) {
22
+ return this.byId.get(endpoint_id);
23
+ }
24
+ unregister(endpoint_id) {
25
+ const entry = this.byId.get(endpoint_id);
26
+ if (entry)
27
+ this.byId.delete(endpoint_id);
28
+ return entry;
29
+ }
30
+ markReconnecting(endpoint_id, grace_deadline) {
31
+ const entry = this.byId.get(endpoint_id);
32
+ if (!entry) {
33
+ throw new Error(`unknown endpoint_id: ${endpoint_id}`);
34
+ }
35
+ if (entry.state !== "active") {
36
+ throw new Error(`markReconnecting requires state=active, got state=${entry.state}`);
37
+ }
38
+ entry.state = "reconnecting";
39
+ entry.state_since = new Date();
40
+ entry.grace_deadline = grace_deadline;
41
+ return entry;
42
+ }
43
+ markActive(endpoint_id, new_ws) {
44
+ const entry = this.byId.get(endpoint_id);
45
+ if (!entry) {
46
+ throw new Error(`unknown endpoint_id: ${endpoint_id}`);
47
+ }
48
+ if (entry.state !== "reconnecting") {
49
+ throw new Error(`markActive requires state=reconnecting, got state=${entry.state}`);
50
+ }
51
+ entry.state = "active";
52
+ entry.state_since = new Date();
53
+ entry.grace_deadline = undefined;
54
+ entry.ipc_ws = new_ws;
55
+ return entry;
56
+ }
57
+ preconditionForReattach(endpoint_id, plugin_pid) {
58
+ const entry = this.byId.get(endpoint_id);
59
+ if (!entry)
60
+ return undefined;
61
+ if (entry.plugin_pid !== plugin_pid)
62
+ return undefined;
63
+ if (entry.state !== "reconnecting")
64
+ return undefined;
65
+ return entry;
66
+ }
67
+ getReattachStatus(endpoint_id, plugin_pid) {
68
+ const entry = this.byId.get(endpoint_id);
69
+ if (!entry)
70
+ return { ok: false, reason: "endpoint_unknown" };
71
+ if (entry.plugin_pid !== plugin_pid)
72
+ return { ok: false, reason: "reattach_pid_mismatch" };
73
+ if (entry.state !== "reconnecting")
74
+ return { ok: false, reason: "endpoint_not_reconnecting" };
75
+ return { ok: true, entry };
76
+ }
77
+ list() {
78
+ return Array.from(this.byId.values());
79
+ }
80
+ size() {
81
+ return this.byId.size;
82
+ }
83
+ }
@@ -0,0 +1,40 @@
1
+ export declare const DEFAULT_GRACE_MS: number;
2
+ export type EndpointState = "active" | "reconnecting" | "closed";
3
+ export type TransitionEvent = {
4
+ type: "register";
5
+ } | {
6
+ type: "ipc_close";
7
+ plugin_alive: boolean;
8
+ } | {
9
+ type: "reattach";
10
+ } | {
11
+ type: "grace_expired";
12
+ } | {
13
+ type: "explicit_unregister";
14
+ };
15
+ export type TransitionEffect = {
16
+ type: "start_grace_timer";
17
+ deadline_ms: number;
18
+ } | {
19
+ type: "cancel_grace_timer";
20
+ } | {
21
+ type: "emit_state";
22
+ state: EndpointState;
23
+ reason: string;
24
+ } | {
25
+ type: "drain_buffer";
26
+ } | {
27
+ type: "remove_from_registry";
28
+ } | {
29
+ type: "delete_buffer";
30
+ };
31
+ export interface TransitionResult {
32
+ next: EndpointState;
33
+ effects: readonly TransitionEffect[];
34
+ ignored?: boolean;
35
+ }
36
+ export interface TransitionOptions {
37
+ graceMs?: number;
38
+ }
39
+ export declare function transition(current: EndpointState, event: TransitionEvent, opts?: TransitionOptions): TransitionResult;
40
+ //# sourceMappingURL=endpoint-state-machine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-state-machine.d.ts","sourceRoot":"","sources":["../../src/broker/endpoint-state-machine.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,gBAAgB,QAAiB,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEjE,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAOpB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GAE5C;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAEpB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GAEzB;IAAE,IAAI,EAAE,qBAAqB,CAAA;CAAE,CAAC;AAEpC,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,oBAAoB,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,sBAAsB,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAE9B,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAMrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAEhC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,wBAAgB,UAAU,CACxB,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,eAAe,EACtB,IAAI,GAAE,iBAAsB,GAC3B,gBAAgB,CAiGlB"}
@@ -0,0 +1,92 @@
1
+ export const DEFAULT_GRACE_MS = 10 * 60 * 1000;
2
+ export function transition(current, event, opts = {}) {
3
+ const graceMs = opts.graceMs ?? DEFAULT_GRACE_MS;
4
+ switch (current) {
5
+ case "active": {
6
+ switch (event.type) {
7
+ case "ipc_close":
8
+ if (event.plugin_alive) {
9
+ return {
10
+ next: "reconnecting",
11
+ effects: [
12
+ { type: "start_grace_timer", deadline_ms: graceMs },
13
+ {
14
+ type: "emit_state",
15
+ state: "reconnecting",
16
+ reason: "ipc_close",
17
+ },
18
+ ],
19
+ };
20
+ }
21
+ return {
22
+ next: "closed",
23
+ effects: [
24
+ { type: "emit_state", state: "closed", reason: "plugin_exit" },
25
+ { type: "delete_buffer" },
26
+ { type: "remove_from_registry" },
27
+ ],
28
+ };
29
+ case "explicit_unregister":
30
+ return {
31
+ next: "closed",
32
+ effects: [
33
+ {
34
+ type: "emit_state",
35
+ state: "closed",
36
+ reason: "explicit_unregister",
37
+ },
38
+ { type: "delete_buffer" },
39
+ { type: "remove_from_registry" },
40
+ ],
41
+ };
42
+ case "register":
43
+ case "reattach":
44
+ case "grace_expired":
45
+ return { next: "active", effects: [], ignored: true };
46
+ }
47
+ return { next: current, effects: [], ignored: true };
48
+ }
49
+ case "reconnecting": {
50
+ switch (event.type) {
51
+ case "reattach":
52
+ return {
53
+ next: "active",
54
+ effects: [
55
+ { type: "cancel_grace_timer" },
56
+ { type: "drain_buffer" },
57
+ { type: "emit_state", state: "active", reason: "reattach" },
58
+ ],
59
+ };
60
+ case "grace_expired":
61
+ return {
62
+ next: "closed",
63
+ effects: [
64
+ { type: "emit_state", state: "closed", reason: "grace_timeout" },
65
+ { type: "delete_buffer" },
66
+ { type: "remove_from_registry" },
67
+ ],
68
+ };
69
+ case "explicit_unregister":
70
+ return {
71
+ next: "closed",
72
+ effects: [
73
+ { type: "cancel_grace_timer" },
74
+ {
75
+ type: "emit_state",
76
+ state: "closed",
77
+ reason: "explicit_unregister",
78
+ },
79
+ { type: "delete_buffer" },
80
+ { type: "remove_from_registry" },
81
+ ],
82
+ };
83
+ case "ipc_close":
84
+ case "register":
85
+ return { next: "reconnecting", effects: [], ignored: true };
86
+ }
87
+ return { next: current, effects: [], ignored: true };
88
+ }
89
+ case "closed":
90
+ return { next: "closed", effects: [], ignored: true };
91
+ }
92
+ }
@@ -0,0 +1,5 @@
1
+ import { type BrokerLogger } from "./logger.js";
2
+ import type { RuntimeEndpointPort } from "./runtime-endpoint-port.js";
3
+ export declare function buildApiPort(apiHost: string, runtimeKey: string, logger: BrokerLogger): RuntimeEndpointPort;
4
+ export declare function main(): Promise<void>;
5
+ //# sourceMappingURL=entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AAiCA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAsDtE,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CAkErB;AAYD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAgD1C"}