@masons/runtime-broker 0.1.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 (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -0
  3. package/dist/broker/broker-daemon.d.ts +71 -0
  4. package/dist/broker/broker-daemon.d.ts.map +1 -0
  5. package/dist/broker/broker-daemon.js +837 -0
  6. package/dist/broker/claude-code-spawn-driver.d.ts +14 -0
  7. package/dist/broker/claude-code-spawn-driver.d.ts.map +1 -0
  8. package/dist/broker/claude-code-spawn-driver.js +39 -0
  9. package/dist/broker/closed-endpoint-lookup.d.ts +25 -0
  10. package/dist/broker/closed-endpoint-lookup.d.ts.map +1 -0
  11. package/dist/broker/closed-endpoint-lookup.js +59 -0
  12. package/dist/broker/codex-spawn-driver-stub.d.ts +7 -0
  13. package/dist/broker/codex-spawn-driver-stub.d.ts.map +1 -0
  14. package/dist/broker/codex-spawn-driver-stub.js +13 -0
  15. package/dist/broker/connector-ws.d.ts +47 -0
  16. package/dist/broker/connector-ws.d.ts.map +1 -0
  17. package/dist/broker/connector-ws.js +60 -0
  18. package/dist/broker/control-event-dispatcher.d.ts +21 -0
  19. package/dist/broker/control-event-dispatcher.d.ts.map +1 -0
  20. package/dist/broker/control-event-dispatcher.js +45 -0
  21. package/dist/broker/control-event-types.d.ts +28 -0
  22. package/dist/broker/control-event-types.d.ts.map +1 -0
  23. package/dist/broker/control-event-types.js +1 -0
  24. package/dist/broker/correlation-ring.d.ts +10 -0
  25. package/dist/broker/correlation-ring.d.ts.map +1 -0
  26. package/dist/broker/correlation-ring.js +32 -0
  27. package/dist/broker/discovery-file.d.ts +12 -0
  28. package/dist/broker/discovery-file.d.ts.map +1 -0
  29. package/dist/broker/discovery-file.js +77 -0
  30. package/dist/broker/endpoint-registry.d.ts +53 -0
  31. package/dist/broker/endpoint-registry.d.ts.map +1 -0
  32. package/dist/broker/endpoint-registry.js +83 -0
  33. package/dist/broker/endpoint-state-machine.d.ts +40 -0
  34. package/dist/broker/endpoint-state-machine.d.ts.map +1 -0
  35. package/dist/broker/endpoint-state-machine.js +92 -0
  36. package/dist/broker/entry.d.ts +13 -0
  37. package/dist/broker/entry.d.ts.map +1 -0
  38. package/dist/broker/entry.js +235 -0
  39. package/dist/broker/grace-timer.d.ts +9 -0
  40. package/dist/broker/grace-timer.d.ts.map +1 -0
  41. package/dist/broker/grace-timer.js +34 -0
  42. package/dist/broker/ipc-server.d.ts +79 -0
  43. package/dist/broker/ipc-server.d.ts.map +1 -0
  44. package/dist/broker/ipc-server.js +263 -0
  45. package/dist/broker/logger.d.ts +10 -0
  46. package/dist/broker/logger.d.ts.map +1 -0
  47. package/dist/broker/logger.js +34 -0
  48. package/dist/broker/network-presence-changed-event-types.d.ts +8 -0
  49. package/dist/broker/network-presence-changed-event-types.d.ts.map +1 -0
  50. package/dist/broker/network-presence-changed-event-types.js +1 -0
  51. package/dist/broker/network-presence-emitter.d.ts +22 -0
  52. package/dist/broker/network-presence-emitter.d.ts.map +1 -0
  53. package/dist/broker/network-presence-emitter.js +150 -0
  54. package/dist/broker/network-presence.d.ts +31 -0
  55. package/dist/broker/network-presence.d.ts.map +1 -0
  56. package/dist/broker/network-presence.js +109 -0
  57. package/dist/broker/paths.d.ts +11 -0
  58. package/dist/broker/paths.d.ts.map +1 -0
  59. package/dist/broker/paths.js +30 -0
  60. package/dist/broker/plugin-liveness.d.ts +2 -0
  61. package/dist/broker/plugin-liveness.d.ts.map +1 -0
  62. package/dist/broker/plugin-liveness.js +15 -0
  63. package/dist/broker/received-message-correlation-cache.d.ts +23 -0
  64. package/dist/broker/received-message-correlation-cache.d.ts.map +1 -0
  65. package/dist/broker/received-message-correlation-cache.js +114 -0
  66. package/dist/broker/reconnecting-buffer.d.ts +23 -0
  67. package/dist/broker/reconnecting-buffer.d.ts.map +1 -0
  68. package/dist/broker/reconnecting-buffer.js +107 -0
  69. package/dist/broker/routing-table.d.ts +22 -0
  70. package/dist/broker/routing-table.d.ts.map +1 -0
  71. package/dist/broker/routing-table.js +35 -0
  72. package/dist/broker/runtime-endpoint-port.d.ts +20 -0
  73. package/dist/broker/runtime-endpoint-port.d.ts.map +1 -0
  74. package/dist/broker/runtime-endpoint-port.js +1 -0
  75. package/dist/broker/services-event-client.d.ts +21 -0
  76. package/dist/broker/services-event-client.d.ts.map +1 -0
  77. package/dist/broker/services-event-client.js +221 -0
  78. package/dist/broker/spawn-correlation.d.ts +28 -0
  79. package/dist/broker/spawn-correlation.d.ts.map +1 -0
  80. package/dist/broker/spawn-correlation.js +77 -0
  81. package/dist/broker/spawn-driver.d.ts +27 -0
  82. package/dist/broker/spawn-driver.d.ts.map +1 -0
  83. package/dist/broker/spawn-driver.js +15 -0
  84. package/dist/broker/task-hint-handler.d.ts +21 -0
  85. package/dist/broker/task-hint-handler.d.ts.map +1 -0
  86. package/dist/broker/task-hint-handler.js +33 -0
  87. package/dist/broker/transition-state-retry-queue.d.ts +20 -0
  88. package/dist/broker/transition-state-retry-queue.d.ts.map +1 -0
  89. package/dist/broker/transition-state-retry-queue.js +48 -0
  90. package/dist/broker/undispatched-changed-event-types.d.ts +29 -0
  91. package/dist/broker/undispatched-changed-event-types.d.ts.map +1 -0
  92. package/dist/broker/undispatched-changed-event-types.js +14 -0
  93. package/dist/broker/undispatched-emitter.d.ts +22 -0
  94. package/dist/broker/undispatched-emitter.d.ts.map +1 -0
  95. package/dist/broker/undispatched-emitter.js +149 -0
  96. package/dist/broker/undispatched-inbox.d.ts +26 -0
  97. package/dist/broker/undispatched-inbox.d.ts.map +1 -0
  98. package/dist/broker/undispatched-inbox.js +53 -0
  99. package/dist/broker/version-handshake.d.ts +30 -0
  100. package/dist/broker/version-handshake.d.ts.map +1 -0
  101. package/dist/broker/version-handshake.js +47 -0
  102. package/dist/broker-client/broker-client.d.ts +65 -0
  103. package/dist/broker-client/broker-client.d.ts.map +1 -0
  104. package/dist/broker-client/broker-client.js +165 -0
  105. package/dist/broker-client/lazy-spawn.d.ts +18 -0
  106. package/dist/broker-client/lazy-spawn.d.ts.map +1 -0
  107. package/dist/broker-client/lazy-spawn.js +61 -0
  108. package/dist/config-fs.d.ts +4 -0
  109. package/dist/config-fs.d.ts.map +1 -0
  110. package/dist/config-fs.js +23 -0
  111. package/dist/connector-client.d.ts +65 -0
  112. package/dist/connector-client.d.ts.map +1 -0
  113. package/dist/connector-client.js +364 -0
  114. package/dist/environment-context.d.ts +21 -0
  115. package/dist/environment-context.d.ts.map +1 -0
  116. package/dist/environment-context.js +39 -0
  117. package/dist/platform-client.d.ts +84 -0
  118. package/dist/platform-client.d.ts.map +1 -0
  119. package/dist/platform-client.js +94 -0
  120. package/dist/runtime-endpoint-client.d.ts +74 -0
  121. package/dist/runtime-endpoint-client.d.ts.map +1 -0
  122. package/dist/runtime-endpoint-client.js +163 -0
  123. package/dist/types.d.ts +90 -0
  124. package/dist/types.d.ts.map +1 -0
  125. package/dist/types.js +38 -0
  126. package/dist/version.d.ts +2 -0
  127. package/dist/version.d.ts.map +1 -0
  128. package/dist/version.js +1 -0
  129. package/package.json +60 -0
@@ -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,13 @@
1
+ import { type BrokerLogger } from "./logger.js";
2
+ import type { RuntimeEndpointPort } from "./runtime-endpoint-port.js";
3
+ interface ResolvedCredentials {
4
+ accountId: string;
5
+ connectorUrl: string;
6
+ token: string;
7
+ apiHost: string;
8
+ }
9
+ export declare function resolveCredentialsFromFile(filePath: string, accountId: string, envApiHost: string | undefined): Promise<ResolvedCredentials>;
10
+ export declare function buildApiPort(apiHost: string, runtimeKey: string, logger: BrokerLogger): RuntimeEndpointPort;
11
+ export declare function main(): Promise<void>;
12
+ export {};
13
+ //# sourceMappingURL=entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AA+EA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMtE,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA8F9B;AAwDD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CAwGrB;AAYD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA8F1C"}
@@ -0,0 +1,235 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { pathToFileURL } from "node:url";
3
+ import { readConfig } from "../config-fs.js";
4
+ import { DEFAULT_API_HOST } from "../platform-client.js";
5
+ import { emitRuntimeNetworkPresenceChanged, emitRuntimeUndispatchedChanged, heartbeatRuntimeEndpoint, registerRuntimeEndpoint, transitionRuntimeEndpointState, unregisterRuntimeEndpoint, } from "../runtime-endpoint-client.js";
6
+ import { startBrokerDaemon } from "./broker-daemon.js";
7
+ import { ClaudeCodeSpawnDriver } from "./claude-code-spawn-driver.js";
8
+ import { CodexSpawnDriverStub } from "./codex-spawn-driver-stub.js";
9
+ import { ConnectorWS } from "./connector-ws.js";
10
+ import { createBrokerLogger } from "./logger.js";
11
+ import { resolveBrokerPaths } from "./paths.js";
12
+ import { createServicesEventClient } from "./services-event-client.js";
13
+ import { SpawnDriverRegistry } from "./spawn-driver.js";
14
+ const DEFAULT_ACCOUNT_ID = "default";
15
+ export async function resolveCredentialsFromFile(filePath, accountId, envApiHost) {
16
+ let raw;
17
+ try {
18
+ raw = await readFile(filePath, "utf8");
19
+ }
20
+ catch (err) {
21
+ const code = err.code;
22
+ if (code === "ENOENT") {
23
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE points to non-existent path: ${filePath}`);
24
+ }
25
+ throw new Error(`failed to read MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: ` +
26
+ (err instanceof Error ? err.message : String(err)));
27
+ }
28
+ let parsed;
29
+ try {
30
+ parsed = JSON.parse(raw);
31
+ }
32
+ catch (err) {
33
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath} is not valid JSON: ` +
34
+ (err instanceof Error ? err.message : String(err)));
35
+ }
36
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
37
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: top-level must be an object`);
38
+ }
39
+ const accounts = parsed.accounts;
40
+ if (typeof accounts !== "object" ||
41
+ accounts === null ||
42
+ Array.isArray(accounts)) {
43
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: missing or invalid "accounts" object`);
44
+ }
45
+ const accountRaw = accounts[accountId];
46
+ if (typeof accountRaw !== "object" ||
47
+ accountRaw === null ||
48
+ Array.isArray(accountRaw)) {
49
+ const available = Object.keys(accounts).join(", ") || "<none>";
50
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: account "${accountId}" not found (available: ${available})`);
51
+ }
52
+ const acct = accountRaw;
53
+ const connectorUrl = typeof acct.connectorUrl === "string" ? acct.connectorUrl : null;
54
+ const token = typeof acct.token === "string" ? acct.token : null;
55
+ if (!connectorUrl) {
56
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: account "${accountId}" missing required field "connectorUrl"`);
57
+ }
58
+ if (!token) {
59
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: account "${accountId}" missing required field "token"`);
60
+ }
61
+ if (acct.apiHost !== undefined && typeof acct.apiHost !== "string") {
62
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: account "${accountId}" optional field "apiHost" must be a string when present`);
63
+ }
64
+ if (acct.handle !== undefined && typeof acct.handle !== "string") {
65
+ throw new Error(`MASONS_BROKER_CREDENTIALS_FILE at ${filePath}: account "${accountId}" optional field "handle" must be a string when present`);
66
+ }
67
+ const apiHost = envApiHost ??
68
+ (typeof acct.apiHost === "string" ? acct.apiHost : DEFAULT_API_HOST);
69
+ return { accountId, connectorUrl, token, apiHost };
70
+ }
71
+ async function resolveCredentialsFromOpenClawConfig(accountId, envApiHost) {
72
+ const config = await readConfig();
73
+ const channels = config.channels;
74
+ const network = channels?.["agent-network"];
75
+ const apiHost = envApiHost ??
76
+ (typeof network?.apiHost === "string" ? network.apiHost : DEFAULT_API_HOST);
77
+ const accounts = network?.accounts;
78
+ const account = accounts?.[accountId];
79
+ const connectorUrl = typeof account?.connectorUrl === "string" ? account.connectorUrl : null;
80
+ const token = typeof account?.token === "string" ? account.token : null;
81
+ if (!connectorUrl || !token) {
82
+ throw new Error(`agent-network credentials missing for account "${accountId}"; ` +
83
+ "run setup before lazy-spawning the broker " +
84
+ "(or set MASONS_BROKER_CREDENTIALS_FILE for Plugin hosts)");
85
+ }
86
+ return { accountId, connectorUrl, token, apiHost };
87
+ }
88
+ async function resolveCredentials(accountId, envApiHost) {
89
+ const credentialsFile = process.env.MASONS_BROKER_CREDENTIALS_FILE;
90
+ if (credentialsFile) {
91
+ return resolveCredentialsFromFile(credentialsFile, accountId, envApiHost);
92
+ }
93
+ return resolveCredentialsFromOpenClawConfig(accountId, envApiHost);
94
+ }
95
+ export function buildApiPort(apiHost, runtimeKey, logger) {
96
+ return {
97
+ async register(params) {
98
+ return registerRuntimeEndpoint({ apiHost }, runtimeKey, params);
99
+ },
100
+ async heartbeat(endpointId) {
101
+ await heartbeatRuntimeEndpoint({ apiHost }, runtimeKey, endpointId);
102
+ },
103
+ async unregister(endpointId, asNodeId) {
104
+ await unregisterRuntimeEndpoint({ apiHost }, runtimeKey, endpointId, asNodeId);
105
+ },
106
+ async transitionState(endpointId, params) {
107
+ try {
108
+ await transitionRuntimeEndpointState({ apiHost }, runtimeKey, endpointId, params);
109
+ }
110
+ catch (err) {
111
+ const status = errorStatus(err);
112
+ if (status === 404) {
113
+ logger.info("transitionState_404_degraded", {
114
+ endpoint_id: endpointId,
115
+ state: params.state,
116
+ reason: params.reason,
117
+ note: "apps/api state endpoint not yet shipped (PR-B1 in flight); state propagation skipped",
118
+ });
119
+ return;
120
+ }
121
+ await sleep(500);
122
+ try {
123
+ await transitionRuntimeEndpointState({ apiHost }, runtimeKey, endpointId, params);
124
+ }
125
+ catch (err2) {
126
+ logger.warn("transitionState_failed", {
127
+ endpoint_id: endpointId,
128
+ state: params.state,
129
+ reason: params.reason,
130
+ err: err2 instanceof Error ? err2.message : String(err2),
131
+ });
132
+ }
133
+ }
134
+ },
135
+ async emitUndispatchedChanged(event) {
136
+ return emitRuntimeUndispatchedChanged({ apiHost }, runtimeKey, event);
137
+ },
138
+ async emitNetworkPresenceChanged(event) {
139
+ return emitRuntimeNetworkPresenceChanged({ apiHost }, runtimeKey, event);
140
+ },
141
+ };
142
+ }
143
+ function errorStatus(err) {
144
+ if (typeof err !== "object" || err === null)
145
+ return null;
146
+ const status = err.status;
147
+ return typeof status === "number" ? status : null;
148
+ }
149
+ function sleep(ms) {
150
+ return new Promise((resolve) => setTimeout(resolve, ms));
151
+ }
152
+ export async function main() {
153
+ const accountId = process.env.MASONS_BROKER_ACCOUNT_ID ?? DEFAULT_ACCOUNT_ID;
154
+ const envApiHost = process.env.MASONS_BROKER_API_HOST;
155
+ const userDataOverride = process.env.MASONS_BROKER_USER_DATA;
156
+ const creds = await resolveCredentials(accountId, envApiHost);
157
+ const paths = resolveBrokerPaths(creds.accountId, creds.token, {
158
+ userDataDir: userDataOverride,
159
+ });
160
+ const logger = createBrokerLogger(paths.logDir);
161
+ logger.info("broker_entry_starting", {
162
+ accountId: creds.accountId,
163
+ apiHost: creds.apiHost,
164
+ pid: process.pid,
165
+ });
166
+ const asNodeId = creds.accountId;
167
+ const connector = new ConnectorWS({
168
+ url: creds.connectorUrl,
169
+ token: creds.token,
170
+ });
171
+ const apiPort = buildApiPort(creds.apiHost, creds.token, logger);
172
+ const spawnDriverRegistry = new SpawnDriverRegistry();
173
+ spawnDriverRegistry.register(new ClaudeCodeSpawnDriver());
174
+ spawnDriverRegistry.register(new CodexSpawnDriverStub());
175
+ const postControlAck = async (ack) => {
176
+ const url = `https://${creds.apiHost.replace(/^https?:\/\//, "").replace(/\/+$/, "")}/v1/runtime/control-ack`;
177
+ try {
178
+ const res = await fetch(url, {
179
+ method: "POST",
180
+ headers: {
181
+ Authorization: `Bearer ${creds.token}`,
182
+ "Content-Type": "application/json",
183
+ },
184
+ body: JSON.stringify(ack),
185
+ });
186
+ if (!res.ok) {
187
+ logger.warn("post_control_ack_non_ok", {
188
+ status: res.status,
189
+ idempotency_key: ack.idempotency_key,
190
+ });
191
+ }
192
+ }
193
+ catch (err) {
194
+ logger.warn("post_control_ack_error", {
195
+ err: err instanceof Error ? err.message : String(err),
196
+ idempotency_key: ack.idempotency_key,
197
+ });
198
+ }
199
+ };
200
+ const broker = await startBrokerDaemon({
201
+ paths,
202
+ asNodeId,
203
+ connector,
204
+ apiPort,
205
+ logger,
206
+ spawnDriverRegistry,
207
+ postControlAck,
208
+ servicesEventClientFactory: ({ dispatcher, logger: clientLogger }) => createServicesEventClient({
209
+ apiHost: creds.apiHost,
210
+ runtimeKey: creds.token,
211
+ agentId: creds.accountId,
212
+ dispatcher,
213
+ logger: clientLogger,
214
+ }),
215
+ });
216
+ let shuttingDown = false;
217
+ const handleSignal = (sig) => async () => {
218
+ if (shuttingDown)
219
+ return;
220
+ shuttingDown = true;
221
+ logger.info("broker_signal", { sig });
222
+ await broker.shutdown(`signal:${sig}`);
223
+ process.exit(0);
224
+ };
225
+ process.on("SIGTERM", handleSignal("SIGTERM"));
226
+ process.on("SIGINT", handleSignal("SIGINT"));
227
+ logger.info("broker_ready", { ipcUrl: broker.ipcUrl });
228
+ }
229
+ const argvUrl = process.argv[1] ? pathToFileURL(process.argv[1]).href : "";
230
+ if (import.meta.url === argvUrl) {
231
+ main().catch((err) => {
232
+ console.error("broker_entry_fatal", err);
233
+ process.exit(1);
234
+ });
235
+ }
@@ -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
+ }
@@ -0,0 +1,79 @@
1
+ import { type RawData, WebSocket } from "ws";
2
+ import type { BrokerLogger } from "./logger.js";
3
+ export interface IPCServerHandlers {
4
+ registerEndpoint(body: RegisterEndpointBody, ipcWs: WebSocket): Promise<RegisterEndpointResponse>;
5
+ heartbeatEndpoint(endpoint_id: string): Promise<void>;
6
+ unregisterEndpoint(endpoint_id: string): Promise<void>;
7
+ send(body: SendBody): Promise<SendResponse>;
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>;
11
+ setTaskHint(body: TaskHintBody): Promise<void>;
12
+ }
13
+ export interface ReattachResponse {
14
+ restored: true;
15
+ reconnecting_buffer_count: number;
16
+ }
17
+ export type UndispatchedListResponse = import("./undispatched-inbox.js").UndispatchedMessage[];
18
+ export interface RegisterEndpointBody {
19
+ agent_id: string;
20
+ plugin_pid: number;
21
+ kind: string;
22
+ started_at?: string;
23
+ workspace?: string;
24
+ tracking_ref?: string;
25
+ session_name?: string;
26
+ task_hint?: string;
27
+ spawn_token?: string;
28
+ }
29
+ export interface TaskHintBody {
30
+ endpoint_id: string;
31
+ plugin_pid: number;
32
+ task_hint: string;
33
+ }
34
+ export interface RegisterEndpointResponse {
35
+ endpoint_id: string;
36
+ }
37
+ export interface SendBody {
38
+ endpoint_id: string;
39
+ to: string;
40
+ content: string;
41
+ contentType?: string;
42
+ metadata?: Record<string, unknown>;
43
+ require_live?: boolean;
44
+ }
45
+ export interface SendResponse {
46
+ messageId: string;
47
+ status: string;
48
+ }
49
+ export declare class BrokerHttpError extends Error {
50
+ readonly status: number;
51
+ readonly code: string;
52
+ constructor(status: number, code: string, message: string);
53
+ }
54
+ export interface IPCServerOptions {
55
+ bearerToken: string;
56
+ handlers: IPCServerHandlers;
57
+ logger: BrokerLogger;
58
+ onChannelOpened?: (plugin_pid: number, ws: WebSocket) => void;
59
+ onChannelClosed?: (plugin_pid: number, ws: WebSocket) => void;
60
+ }
61
+ export interface RunningIPCServer {
62
+ ipcUrl: string;
63
+ port: number;
64
+ close(): Promise<void>;
65
+ }
66
+ export declare function startIPCServer(opts: IPCServerOptions): Promise<RunningIPCServer>;
67
+ export declare function pushToPlugin(ws: WebSocket, event: PushEvent): void;
68
+ export type PushEvent = {
69
+ event: "message_received";
70
+ from: string;
71
+ content: string;
72
+ contentType: string;
73
+ metadata?: Record<string, unknown>;
74
+ } | {
75
+ event: "ping";
76
+ ts: string;
77
+ };
78
+ export declare function parsePluginFrame(data: RawData): unknown;
79
+ //# sourceMappingURL=ipc-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipc-server.d.ts","sourceRoot":"","sources":["../../src/broker/ipc-server.ts"],"names":[],"mappings":"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"}