@masons/agent-network 0.5.29 → 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 (49) hide show
  1. package/dist/broker/broker-daemon.d.ts +13 -3
  2. package/dist/broker/broker-daemon.d.ts.map +1 -1
  3. package/dist/broker/broker-daemon.js +294 -52
  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/control-event-dispatcher.d.ts +20 -0
  8. package/dist/broker/control-event-dispatcher.d.ts.map +1 -0
  9. package/dist/broker/control-event-dispatcher.js +45 -0
  10. package/dist/broker/control-event-types.d.ts +30 -0
  11. package/dist/broker/control-event-types.d.ts.map +1 -0
  12. package/dist/broker/control-event-types.js +1 -0
  13. package/dist/broker/endpoint-registry.d.ts +17 -1
  14. package/dist/broker/endpoint-registry.d.ts.map +1 -1
  15. package/dist/broker/endpoint-registry.js +39 -0
  16. package/dist/broker/endpoint-state-machine.d.ts +40 -0
  17. package/dist/broker/endpoint-state-machine.d.ts.map +1 -0
  18. package/dist/broker/endpoint-state-machine.js +92 -0
  19. package/dist/broker/entry.d.ts +3 -0
  20. package/dist/broker/entry.d.ts.map +1 -1
  21. package/dist/broker/entry.js +41 -3
  22. package/dist/broker/grace-timer.d.ts +9 -0
  23. package/dist/broker/grace-timer.d.ts.map +1 -0
  24. package/dist/broker/grace-timer.js +34 -0
  25. package/dist/broker/ipc-server.d.ts +8 -1
  26. package/dist/broker/ipc-server.d.ts.map +1 -1
  27. package/dist/broker/ipc-server.js +31 -0
  28. package/dist/broker/paths.d.ts +1 -0
  29. package/dist/broker/paths.d.ts.map +1 -1
  30. package/dist/broker/paths.js +2 -0
  31. package/dist/broker/plugin-liveness.d.ts +2 -0
  32. package/dist/broker/plugin-liveness.d.ts.map +1 -0
  33. package/dist/broker/plugin-liveness.js +15 -0
  34. package/dist/broker/reconnecting-buffer.d.ts +23 -0
  35. package/dist/broker/reconnecting-buffer.d.ts.map +1 -0
  36. package/dist/broker/reconnecting-buffer.js +107 -0
  37. package/dist/broker/routing-table.d.ts.map +1 -1
  38. package/dist/broker/routing-table.js +1 -1
  39. package/dist/broker/runtime-endpoint-port.d.ts +2 -1
  40. package/dist/broker/runtime-endpoint-port.d.ts.map +1 -1
  41. package/dist/broker/undispatched-inbox.d.ts +26 -0
  42. package/dist/broker/undispatched-inbox.d.ts.map +1 -0
  43. package/dist/broker/undispatched-inbox.js +53 -0
  44. package/dist/runtime-endpoint-client.d.ts +7 -0
  45. package/dist/runtime-endpoint-client.d.ts.map +1 -1
  46. package/dist/runtime-endpoint-client.js +11 -0
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/package.json +1 -1
@@ -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 {};
@@ -13,8 +13,9 @@ export interface BrokerEndpointEntry {
13
13
  agent_id: string;
14
14
  plugin_pid: number;
15
15
  ipc_ws: WebSocket;
16
- state: "active";
16
+ state: "active" | "reconnecting";
17
17
  state_since: Date;
18
+ grace_deadline?: Date;
18
19
  display_metadata: DisplayMetadata;
19
20
  correlation_ring: CorrelationRingBuffer;
20
21
  }
@@ -30,7 +31,22 @@ export declare class EndpointRegistry {
30
31
  register(args: RegisterArgs): BrokerEndpointEntry;
31
32
  get(endpoint_id: string): BrokerEndpointEntry | undefined;
32
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;
33
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
+ };
34
50
  list(): BrokerEndpointEntry[];
35
51
  size(): number;
36
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint-registry.d.ts","sourceRoot":"","sources":["../../src/broker/endpoint-registry.ts"],"names":[],"mappings":"AAaA,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,CAAC;IAChB,WAAW,EAAE,IAAI,CAAC;IAClB,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;IAchE,uBAAuB,CACrB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,mBAAmB,GAAG,SAAS;IAQlC,IAAI,IAAI,mBAAmB,EAAE;IAK7B,IAAI,IAAI,MAAM;CAGf"}
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"}
@@ -27,14 +27,53 @@ export class EndpointRegistry {
27
27
  this.byId.delete(endpoint_id);
28
28
  return entry;
29
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
+ }
30
57
  preconditionForReattach(endpoint_id, plugin_pid) {
31
58
  const entry = this.byId.get(endpoint_id);
32
59
  if (!entry)
33
60
  return undefined;
34
61
  if (entry.plugin_pid !== plugin_pid)
35
62
  return undefined;
63
+ if (entry.state !== "reconnecting")
64
+ return undefined;
36
65
  return entry;
37
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
+ }
38
77
  list() {
39
78
  return Array.from(this.byId.values());
40
79
  }
@@ -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
+ }
@@ -1,2 +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;
1
4
  export declare function main(): Promise<void>;
2
5
  //# sourceMappingURL=entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AAmGA,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAgD1C"}
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"}
@@ -1,7 +1,7 @@
1
1
  import { pathToFileURL } from "node:url";
2
2
  import { readConfig } from "../config-fs.js";
3
3
  import { DEFAULT_API_HOST } from "../platform-client.js";
4
- import { heartbeatRuntimeEndpoint, registerRuntimeEndpoint, unregisterRuntimeEndpoint, } from "../runtime-endpoint-client.js";
4
+ import { heartbeatRuntimeEndpoint, registerRuntimeEndpoint, transitionRuntimeEndpointState, unregisterRuntimeEndpoint, } from "../runtime-endpoint-client.js";
5
5
  import { startBrokerDaemon } from "./broker-daemon.js";
6
6
  import { ConnectorWS } from "./connector-ws.js";
7
7
  import { createBrokerLogger } from "./logger.js";
@@ -23,7 +23,7 @@ async function resolveCredentials(accountId, envApiHost) {
23
23
  }
24
24
  return { accountId, connectorUrl, token, apiHost };
25
25
  }
26
- function buildApiPort(apiHost, runtimeKey) {
26
+ export function buildApiPort(apiHost, runtimeKey, logger) {
27
27
  return {
28
28
  async register(params) {
29
29
  return registerRuntimeEndpoint({ apiHost }, runtimeKey, params);
@@ -34,8 +34,46 @@ function buildApiPort(apiHost, runtimeKey) {
34
34
  async unregister(endpointId, asNodeId) {
35
35
  await unregisterRuntimeEndpoint({ apiHost }, runtimeKey, endpointId, asNodeId);
36
36
  },
37
+ async transitionState(endpointId, params) {
38
+ try {
39
+ await transitionRuntimeEndpointState({ apiHost }, runtimeKey, endpointId, params);
40
+ }
41
+ catch (err) {
42
+ const status = errorStatus(err);
43
+ if (status === 404) {
44
+ logger.info("transitionState_404_degraded", {
45
+ endpoint_id: endpointId,
46
+ state: params.state,
47
+ reason: params.reason,
48
+ note: "apps/api state endpoint not yet shipped (PR-B1 in flight); state propagation skipped",
49
+ });
50
+ return;
51
+ }
52
+ await sleep(500);
53
+ try {
54
+ await transitionRuntimeEndpointState({ apiHost }, runtimeKey, endpointId, params);
55
+ }
56
+ catch (err2) {
57
+ logger.warn("transitionState_failed", {
58
+ endpoint_id: endpointId,
59
+ state: params.state,
60
+ reason: params.reason,
61
+ err: err2 instanceof Error ? err2.message : String(err2),
62
+ });
63
+ }
64
+ }
65
+ },
37
66
  };
38
67
  }
68
+ function errorStatus(err) {
69
+ if (typeof err !== "object" || err === null)
70
+ return null;
71
+ const status = err.status;
72
+ return typeof status === "number" ? status : null;
73
+ }
74
+ function sleep(ms) {
75
+ return new Promise((resolve) => setTimeout(resolve, ms));
76
+ }
39
77
  export async function main() {
40
78
  const accountId = process.env.MASONS_BROKER_ACCOUNT_ID ?? DEFAULT_ACCOUNT_ID;
41
79
  const envApiHost = process.env.MASONS_BROKER_API_HOST;
@@ -55,7 +93,7 @@ export async function main() {
55
93
  url: creds.connectorUrl,
56
94
  token: creds.token,
57
95
  });
58
- const apiPort = buildApiPort(creds.apiHost, creds.token);
96
+ const apiPort = buildApiPort(creds.apiHost, creds.token, logger);
59
97
  const broker = await startBrokerDaemon({
60
98
  paths,
61
99
  asNodeId,
@@ -0,0 +1,9 @@
1
+ export interface GraceTimerManager {
2
+ start(endpoint_id: string, ms: number, onExpire: () => void): void;
3
+ cancel(endpoint_id: string): void;
4
+ has(endpoint_id: string): boolean;
5
+ cancelAll(): void;
6
+ active(): readonly string[];
7
+ }
8
+ export declare function createGraceTimerManager(): GraceTimerManager;
9
+ //# sourceMappingURL=grace-timer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grace-timer.d.ts","sourceRoot":"","sources":["../../src/broker/grace-timer.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,iBAAiB;IAGhC,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAEnE,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAElC,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAElC,SAAS,IAAI,IAAI,CAAC;IAElB,MAAM,IAAI,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,wBAAgB,uBAAuB,IAAI,iBAAiB,CAmC3D"}
@@ -0,0 +1,34 @@
1
+ export function createGraceTimerManager() {
2
+ const timers = new Map();
3
+ return {
4
+ start(endpoint_id, ms, onExpire) {
5
+ const existing = timers.get(endpoint_id);
6
+ if (existing)
7
+ clearTimeout(existing);
8
+ const handle = setTimeout(() => {
9
+ timers.delete(endpoint_id);
10
+ onExpire();
11
+ }, ms);
12
+ handle.unref?.();
13
+ timers.set(endpoint_id, handle);
14
+ },
15
+ cancel(endpoint_id) {
16
+ const handle = timers.get(endpoint_id);
17
+ if (handle) {
18
+ clearTimeout(handle);
19
+ timers.delete(endpoint_id);
20
+ }
21
+ },
22
+ has(endpoint_id) {
23
+ return timers.has(endpoint_id);
24
+ },
25
+ cancelAll() {
26
+ for (const [, handle] of timers)
27
+ clearTimeout(handle);
28
+ timers.clear();
29
+ },
30
+ active() {
31
+ return Array.from(timers.keys());
32
+ },
33
+ };
34
+ }
@@ -5,8 +5,15 @@ export interface IPCServerHandlers {
5
5
  heartbeatEndpoint(endpoint_id: string): Promise<void>;
6
6
  unregisterEndpoint(endpoint_id: string): Promise<void>;
7
7
  send(body: SendBody): Promise<SendResponse>;
8
- reattachEndpoint(endpoint_id: string, plugin_pid: number, ipcWs: WebSocket): Promise<boolean>;
8
+ reattachEndpoint(endpoint_id: string, plugin_pid: number, ipcWs: WebSocket): Promise<ReattachResponse>;
9
+ listUndispatched(): Promise<UndispatchedListResponse>;
10
+ dispatch(undispatched_id: string, target_endpoint_id: string): Promise<void>;
9
11
  }
12
+ export interface ReattachResponse {
13
+ restored: true;
14
+ reconnecting_buffer_count: number;
15
+ }
16
+ export type UndispatchedListResponse = import("./undispatched-inbox.js").UndispatchedMessage[];
10
17
  export interface RegisterEndpointBody {
11
18
  agent_id: string;
12
19
  plugin_pid: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ipc-server.d.ts","sourceRoot":"","sources":["../../src/broker/ipc-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ipc-server.d.ts","sourceRoot":"","sources":["../../src/broker/ipc-server.ts"],"names":[],"mappings":"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"}
@@ -94,6 +94,17 @@ async function routeHttp(req, res, bearerToken, handlers, logger) {
94
94
  sendJson(res, 200, { ok: true });
95
95
  return;
96
96
  }
97
+ const reattachMatch = /^\/v1\/endpoint\/([^/]+)\/reattach$/.exec(url);
98
+ if (method === "POST" && reattachMatch && reattachMatch[1]) {
99
+ const body = await readJson(req);
100
+ const pid = typeof body.plugin_pid === "number" ? body.plugin_pid : Number.NaN;
101
+ if (!Number.isFinite(pid) || pid <= 0) {
102
+ throw new BrokerHttpError(400, "plugin_pid_invalid", "plugin_pid is required and must be a positive number");
103
+ }
104
+ const out = await handlers.reattachEndpoint(decodeURIComponent(reattachMatch[1]), pid, null);
105
+ sendJson(res, 200, out);
106
+ return;
107
+ }
97
108
  const unregisterMatch = /^\/v1\/endpoint\/([^/]+)$/.exec(url);
98
109
  if (method === "DELETE" && unregisterMatch && unregisterMatch[1]) {
99
110
  await handlers.unregisterEndpoint(decodeURIComponent(unregisterMatch[1]));
@@ -106,6 +117,26 @@ async function routeHttp(req, res, bearerToken, handlers, logger) {
106
117
  sendJson(res, 200, out);
107
118
  return;
108
119
  }
120
+ if (method === "GET" && url === "/v1/undispatched") {
121
+ const items = await handlers.listUndispatched();
122
+ sendJson(res, 200, { items });
123
+ return;
124
+ }
125
+ if (method === "POST" && url === "/v1/dispatch") {
126
+ const body = await readJson(req);
127
+ const uid = typeof body.undispatched_id === "string"
128
+ ? body.undispatched_id
129
+ : undefined;
130
+ const tid = typeof body.target_endpoint_id === "string"
131
+ ? body.target_endpoint_id
132
+ : undefined;
133
+ if (!uid || !tid) {
134
+ throw new BrokerHttpError(400, "dispatch_body_invalid", "POST /v1/dispatch requires { undispatched_id, target_endpoint_id }");
135
+ }
136
+ await handlers.dispatch(uid, tid);
137
+ sendJson(res, 200, { ok: true });
138
+ return;
139
+ }
109
140
  sendJson(res, 404, { error: "not_found", path: url });
110
141
  }
111
142
  catch (err) {
@@ -5,6 +5,7 @@ export interface BrokerPaths {
5
5
  discoveryFile: string;
6
6
  discoveryDir: string;
7
7
  logDir: string;
8
+ buffersDir: string;
8
9
  }
9
10
  export declare function resolveBrokerPaths(principal: string, runtimeToken: string, opts?: BrokerPathOptions): BrokerPaths;
10
11
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/broker/paths.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,iBAAiB;IAEhC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAE1B,aAAa,EAAE,MAAM,CAAC;IAEtB,YAAY,EAAE,MAAM,CAAC;IAErB,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE,iBAAsB,GAC3B,WAAW,CA8Bb"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/broker/paths.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,iBAAiB;IAEhC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAE1B,aAAa,EAAE,MAAM,CAAC;IAEtB,YAAY,EAAE,MAAM,CAAC;IAErB,MAAM,EAAE,MAAM,CAAC;IAQf,UAAU,EAAE,MAAM,CAAC;CACpB;AASD,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE,iBAAsB,GAC3B,WAAW,CAgCb"}
@@ -17,10 +17,12 @@ export function resolveBrokerPaths(principal, runtimeToken, opts = {}) {
17
17
  .slice(0, AGENT_IDENTITY_HEX_LEN);
18
18
  const discoveryDir = join(userData, "runtime-broker", sanitizeSegment(principal), agentIdentity);
19
19
  const logDir = join(userData, "runtime-broker", "log");
20
+ const buffersDir = join(discoveryDir, "buffers");
20
21
  return {
21
22
  discoveryFile: join(discoveryDir, "discovery.json"),
22
23
  discoveryDir,
23
24
  logDir,
25
+ buffersDir,
24
26
  };
25
27
  }
26
28
  function sanitizeSegment(segment) {
@@ -0,0 +1,2 @@
1
+ export declare function isPluginAlive(plugin_pid: number): boolean;
2
+ //# sourceMappingURL=plugin-liveness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-liveness.d.ts","sourceRoot":"","sources":["../../src/broker/plugin-liveness.ts"],"names":[],"mappings":"AAyBA,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAiBzD"}
@@ -0,0 +1,15 @@
1
+ export function isPluginAlive(plugin_pid) {
2
+ if (!Number.isFinite(plugin_pid) || plugin_pid <= 0) {
3
+ return false;
4
+ }
5
+ try {
6
+ process.kill(plugin_pid, 0);
7
+ return true;
8
+ }
9
+ catch (err) {
10
+ const code = err.code;
11
+ if (code === "ESRCH")
12
+ return false;
13
+ return true;
14
+ }
15
+ }
@@ -0,0 +1,23 @@
1
+ export interface BufferedMessage {
2
+ id: string;
3
+ arrived_at: string;
4
+ envelope: {
5
+ from: string;
6
+ content: string;
7
+ contentType: string;
8
+ metadata?: Record<string, unknown>;
9
+ };
10
+ }
11
+ export interface ReconnectingBuffer {
12
+ append(msg: BufferedMessage): void;
13
+ read(): BufferedMessage[];
14
+ drain(): BufferedMessage[];
15
+ delete(): void;
16
+ size(): number;
17
+ }
18
+ export interface ReconnectingBufferManager {
19
+ forEndpoint(endpoint_id: string): ReconnectingBuffer;
20
+ cleanup(endpoint_id: string): void;
21
+ }
22
+ export declare function createReconnectingBufferManager(buffersDir: string): ReconnectingBufferManager;
23
+ //# sourceMappingURL=reconnecting-buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconnecting-buffer.d.ts","sourceRoot":"","sources":["../../src/broker/reconnecting-buffer.ts"],"names":[],"mappings":"AA+CA,MAAM,WAAW,eAAe;IAE9B,EAAE,EAAE,MAAM,CAAC;IAEX,UAAU,EAAE,MAAM,CAAC;IAEnB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IAEjC,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAAC;IAEnC,IAAI,IAAI,eAAe,EAAE,CAAC;IAE1B,KAAK,IAAI,eAAe,EAAE,CAAC;IAE3B,MAAM,IAAI,IAAI,CAAC;IAEf,IAAI,IAAI,MAAM,CAAC;CAChB;AA8ED,MAAM,WAAW,yBAAyB;IAExC,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAAC;IAErD,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,MAAM,GACjB,yBAAyB,CAmB3B"}
@@ -0,0 +1,107 @@
1
+ import { closeSync, fdatasyncSync, mkdirSync, openSync, readFileSync, unlinkSync, writeSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ class FileBackedBuffer {
4
+ path;
5
+ cached = null;
6
+ constructor(path) {
7
+ this.path = path;
8
+ }
9
+ append(msg) {
10
+ const line = `${JSON.stringify(msg)}\n`;
11
+ try {
12
+ mkdirSync(dirname(this.path), { recursive: true, mode: 0o700 });
13
+ }
14
+ catch {
15
+ }
16
+ const fd = openSync(this.path, "a", 0o600);
17
+ try {
18
+ writeSync(fd, line);
19
+ fdatasyncSync(fd);
20
+ }
21
+ finally {
22
+ closeSync(fd);
23
+ }
24
+ this.cached = null;
25
+ }
26
+ read() {
27
+ if (this.cached !== null)
28
+ return [...this.cached];
29
+ let raw;
30
+ try {
31
+ raw = readFileSync(this.path, "utf8");
32
+ }
33
+ catch (err) {
34
+ if (isNodeErr(err, "ENOENT")) {
35
+ this.cached = [];
36
+ return [];
37
+ }
38
+ throw err;
39
+ }
40
+ const lines = raw.split("\n").filter((l) => l.length > 0);
41
+ const parsed = [];
42
+ for (const line of lines) {
43
+ try {
44
+ parsed.push(JSON.parse(line));
45
+ }
46
+ catch {
47
+ }
48
+ }
49
+ this.cached = parsed;
50
+ return [...parsed];
51
+ }
52
+ drain() {
53
+ const out = this.read();
54
+ this.delete();
55
+ return out;
56
+ }
57
+ delete() {
58
+ try {
59
+ unlinkSync(this.path);
60
+ }
61
+ catch (err) {
62
+ if (isNodeErr(err, "ENOENT")) {
63
+ }
64
+ else {
65
+ throw err;
66
+ }
67
+ }
68
+ this.cached = [];
69
+ }
70
+ size() {
71
+ if (this.cached !== null)
72
+ return this.cached.length;
73
+ return this.read().length;
74
+ }
75
+ }
76
+ export function createReconnectingBufferManager(buffersDir) {
77
+ const cache = new Map();
78
+ return {
79
+ forEndpoint(endpoint_id) {
80
+ let buf = cache.get(endpoint_id);
81
+ if (!buf) {
82
+ buf = new FileBackedBuffer(join(buffersDir, `${sanitizeId(endpoint_id)}.ndjson`));
83
+ cache.set(endpoint_id, buf);
84
+ }
85
+ return buf;
86
+ },
87
+ cleanup(endpoint_id) {
88
+ const buf = cache.get(endpoint_id);
89
+ if (buf)
90
+ buf.delete();
91
+ cache.delete(endpoint_id);
92
+ },
93
+ };
94
+ }
95
+ function sanitizeId(id) {
96
+ return id.replace(/[^a-zA-Z0-9._-]/g, "_");
97
+ }
98
+ function dirname(p) {
99
+ const idx = p.lastIndexOf("/");
100
+ return idx >= 0 ? p.slice(0, idx) : ".";
101
+ }
102
+ function isNodeErr(err, code) {
103
+ return (typeof err === "object" &&
104
+ err !== null &&
105
+ "code" in err &&
106
+ err.code === code);
107
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"routing-table.d.ts","sourceRoot":"","sources":["../../src/broker/routing-table.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,WAAW,GACnB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,GAAG,EAAE,oBAAoB,GAAG,gBAAgB,CAAC;CAC9C,GACD;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC;AAEzB,MAAM,WAAW,mBAAmB;IAElC,WAAW,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,GAAE,mBAAwB;IASjD,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,WAAW;CA6B7C"}
1
+ {"version":3,"file":"routing-table.d.ts","sourceRoot":"","sources":["../../src/broker/routing-table.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,WAAW,GACnB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,GAAG,EAAE,oBAAoB,GAAG,gBAAgB,CAAC;CAC9C,GACD;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC;AAEzB,MAAM,WAAW,mBAAmB;IAElC,WAAW,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADJ,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,GAAE,mBAAwB;IASjD,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,WAAW;CAmC7C"}