@terminals-tech/agent-zero 1.0.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 (263) hide show
  1. package/README.md +209 -0
  2. package/bin/agent-zero.js +332 -0
  3. package/dist/agency/commandRouter.d.ts +48 -0
  4. package/dist/agency/commandRouter.d.ts.map +1 -0
  5. package/dist/agency/commandRouter.js +343 -0
  6. package/dist/agency/commandRouter.js.map +1 -0
  7. package/dist/agency/runtime.d.ts +66 -0
  8. package/dist/agency/runtime.d.ts.map +1 -0
  9. package/dist/agency/runtime.js +247 -0
  10. package/dist/agency/runtime.js.map +1 -0
  11. package/dist/agency/summaryGenerator.d.ts +39 -0
  12. package/dist/agency/summaryGenerator.d.ts.map +1 -0
  13. package/dist/agency/summaryGenerator.js +110 -0
  14. package/dist/agency/summaryGenerator.js.map +1 -0
  15. package/dist/agency/summaryScheduler.d.ts +33 -0
  16. package/dist/agency/summaryScheduler.d.ts.map +1 -0
  17. package/dist/agency/summaryScheduler.js +68 -0
  18. package/dist/agency/summaryScheduler.js.map +1 -0
  19. package/dist/browser/agent-runtime/RuntimePanel.d.ts +20 -0
  20. package/dist/browser/agent-runtime/RuntimePanel.d.ts.map +1 -0
  21. package/dist/browser/agent-runtime/RuntimePanel.js +203 -0
  22. package/dist/browser/agent-runtime/RuntimePanel.js.map +1 -0
  23. package/dist/browser/agent-runtime/config.d.ts +28 -0
  24. package/dist/browser/agent-runtime/config.d.ts.map +1 -0
  25. package/dist/browser/agent-runtime/config.js +50 -0
  26. package/dist/browser/agent-runtime/config.js.map +1 -0
  27. package/dist/browser/agent-runtime/launcher.d.ts +71 -0
  28. package/dist/browser/agent-runtime/launcher.d.ts.map +1 -0
  29. package/dist/browser/agent-runtime/launcher.js +167 -0
  30. package/dist/browser/agent-runtime/launcher.js.map +1 -0
  31. package/dist/browser/rail-auth-bridge.d.ts +85 -0
  32. package/dist/browser/rail-auth-bridge.d.ts.map +1 -0
  33. package/dist/browser/rail-auth-bridge.js +209 -0
  34. package/dist/browser/rail-auth-bridge.js.map +1 -0
  35. package/dist/channels/index.d.ts +13 -0
  36. package/dist/channels/index.d.ts.map +1 -0
  37. package/dist/channels/index.js +12 -0
  38. package/dist/channels/index.js.map +1 -0
  39. package/dist/channels/moltbook.d.ts +114 -0
  40. package/dist/channels/moltbook.d.ts.map +1 -0
  41. package/dist/channels/moltbook.js +348 -0
  42. package/dist/channels/moltbook.js.map +1 -0
  43. package/dist/channels/sms.d.ts +33 -0
  44. package/dist/channels/sms.d.ts.map +1 -0
  45. package/dist/channels/sms.js +160 -0
  46. package/dist/channels/sms.js.map +1 -0
  47. package/dist/channels/telegram.d.ts +47 -0
  48. package/dist/channels/telegram.d.ts.map +1 -0
  49. package/dist/channels/telegram.js +276 -0
  50. package/dist/channels/telegram.js.map +1 -0
  51. package/dist/channels/twitter.d.ts +93 -0
  52. package/dist/channels/twitter.d.ts.map +1 -0
  53. package/dist/channels/twitter.js +411 -0
  54. package/dist/channels/twitter.js.map +1 -0
  55. package/dist/channels/whatsapp.d.ts +77 -0
  56. package/dist/channels/whatsapp.d.ts.map +1 -0
  57. package/dist/channels/whatsapp.js +514 -0
  58. package/dist/channels/whatsapp.js.map +1 -0
  59. package/dist/checkout/index.d.ts +92 -0
  60. package/dist/checkout/index.d.ts.map +1 -0
  61. package/dist/checkout/index.js +125 -0
  62. package/dist/checkout/index.js.map +1 -0
  63. package/dist/cli/moltbook.d.ts +11 -0
  64. package/dist/cli/moltbook.d.ts.map +1 -0
  65. package/dist/cli/moltbook.js +259 -0
  66. package/dist/cli/moltbook.js.map +1 -0
  67. package/dist/cli/setup.d.ts +10 -0
  68. package/dist/cli/setup.d.ts.map +1 -0
  69. package/dist/cli/setup.js +232 -0
  70. package/dist/cli/setup.js.map +1 -0
  71. package/dist/coherence/absorption.d.ts +141 -0
  72. package/dist/coherence/absorption.d.ts.map +1 -0
  73. package/dist/coherence/absorption.js +343 -0
  74. package/dist/coherence/absorption.js.map +1 -0
  75. package/dist/coherence/crossPlatform.d.ts +55 -0
  76. package/dist/coherence/crossPlatform.d.ts.map +1 -0
  77. package/dist/coherence/crossPlatform.js +219 -0
  78. package/dist/coherence/crossPlatform.js.map +1 -0
  79. package/dist/coherence/identityResolver.d.ts +27 -0
  80. package/dist/coherence/identityResolver.d.ts.map +1 -0
  81. package/dist/coherence/identityResolver.js +102 -0
  82. package/dist/coherence/identityResolver.js.map +1 -0
  83. package/dist/identity/burner.d.ts +100 -0
  84. package/dist/identity/burner.d.ts.map +1 -0
  85. package/dist/identity/burner.js +256 -0
  86. package/dist/identity/burner.js.map +1 -0
  87. package/dist/identity/burnerScheduler.d.ts +18 -0
  88. package/dist/identity/burnerScheduler.d.ts.map +1 -0
  89. package/dist/identity/burnerScheduler.js +82 -0
  90. package/dist/identity/burnerScheduler.js.map +1 -0
  91. package/dist/identity/moltbookBurnerAdapter.d.ts +14 -0
  92. package/dist/identity/moltbookBurnerAdapter.d.ts.map +1 -0
  93. package/dist/identity/moltbookBurnerAdapter.js +82 -0
  94. package/dist/identity/moltbookBurnerAdapter.js.map +1 -0
  95. package/dist/identity/operationalVault.d.ts +108 -0
  96. package/dist/identity/operationalVault.d.ts.map +1 -0
  97. package/dist/identity/operationalVault.js +259 -0
  98. package/dist/identity/operationalVault.js.map +1 -0
  99. package/dist/index.d.ts +43 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +57 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/moltbook/apiErrorHandler.d.ts +48 -0
  104. package/dist/moltbook/apiErrorHandler.d.ts.map +1 -0
  105. package/dist/moltbook/apiErrorHandler.js +125 -0
  106. package/dist/moltbook/apiErrorHandler.js.map +1 -0
  107. package/dist/moltbook/approvalGate.d.ts +81 -0
  108. package/dist/moltbook/approvalGate.d.ts.map +1 -0
  109. package/dist/moltbook/approvalGate.js +211 -0
  110. package/dist/moltbook/approvalGate.js.map +1 -0
  111. package/dist/moltbook/attentionField.d.ts +55 -0
  112. package/dist/moltbook/attentionField.d.ts.map +1 -0
  113. package/dist/moltbook/attentionField.js +163 -0
  114. package/dist/moltbook/attentionField.js.map +1 -0
  115. package/dist/moltbook/contentEnhancer.d.ts +28 -0
  116. package/dist/moltbook/contentEnhancer.d.ts.map +1 -0
  117. package/dist/moltbook/contentEnhancer.js +129 -0
  118. package/dist/moltbook/contentEnhancer.js.map +1 -0
  119. package/dist/moltbook/daemon.d.ts +111 -0
  120. package/dist/moltbook/daemon.d.ts.map +1 -0
  121. package/dist/moltbook/daemon.js +497 -0
  122. package/dist/moltbook/daemon.js.map +1 -0
  123. package/dist/moltbook/observer.d.ts +44 -0
  124. package/dist/moltbook/observer.d.ts.map +1 -0
  125. package/dist/moltbook/observer.js +71 -0
  126. package/dist/moltbook/observer.js.map +1 -0
  127. package/dist/moltbook/responseComposer.d.ts +54 -0
  128. package/dist/moltbook/responseComposer.d.ts.map +1 -0
  129. package/dist/moltbook/responseComposer.js +233 -0
  130. package/dist/moltbook/responseComposer.js.map +1 -0
  131. package/dist/openclaw/gateway.d.ts +45 -0
  132. package/dist/openclaw/gateway.d.ts.map +1 -0
  133. package/dist/openclaw/gateway.js +139 -0
  134. package/dist/openclaw/gateway.js.map +1 -0
  135. package/dist/openclaw/skill.d.ts +185 -0
  136. package/dist/openclaw/skill.d.ts.map +1 -0
  137. package/dist/openclaw/skill.js +297 -0
  138. package/dist/openclaw/skill.js.map +1 -0
  139. package/dist/primitives/index.d.ts +23 -0
  140. package/dist/primitives/index.d.ts.map +1 -0
  141. package/dist/primitives/index.js +27 -0
  142. package/dist/primitives/index.js.map +1 -0
  143. package/dist/primitives/types.d.ts +673 -0
  144. package/dist/primitives/types.d.ts.map +1 -0
  145. package/dist/primitives/types.js +205 -0
  146. package/dist/primitives/types.js.map +1 -0
  147. package/dist/rail/absorptionBridge.d.ts +47 -0
  148. package/dist/rail/absorptionBridge.d.ts.map +1 -0
  149. package/dist/rail/absorptionBridge.js +78 -0
  150. package/dist/rail/absorptionBridge.js.map +1 -0
  151. package/dist/rail/authProtocol.d.ts +32 -0
  152. package/dist/rail/authProtocol.d.ts.map +1 -0
  153. package/dist/rail/authProtocol.js +83 -0
  154. package/dist/rail/authProtocol.js.map +1 -0
  155. package/dist/rail/clientRateLimiter.d.ts +17 -0
  156. package/dist/rail/clientRateLimiter.d.ts.map +1 -0
  157. package/dist/rail/clientRateLimiter.js +64 -0
  158. package/dist/rail/clientRateLimiter.js.map +1 -0
  159. package/dist/rail/index.d.ts +8 -0
  160. package/dist/rail/index.d.ts.map +1 -0
  161. package/dist/rail/index.js +38 -0
  162. package/dist/rail/index.js.map +1 -0
  163. package/dist/rail/jwtVerifier.d.ts +11 -0
  164. package/dist/rail/jwtVerifier.d.ts.map +1 -0
  165. package/dist/rail/jwtVerifier.js +55 -0
  166. package/dist/rail/jwtVerifier.js.map +1 -0
  167. package/dist/rail/logger.d.ts +13 -0
  168. package/dist/rail/logger.d.ts.map +1 -0
  169. package/dist/rail/logger.js +29 -0
  170. package/dist/rail/logger.js.map +1 -0
  171. package/dist/rail/metadataBroadcaster.d.ts +53 -0
  172. package/dist/rail/metadataBroadcaster.d.ts.map +1 -0
  173. package/dist/rail/metadataBroadcaster.js +126 -0
  174. package/dist/rail/metadataBroadcaster.js.map +1 -0
  175. package/dist/rail/persistence.d.ts +57 -0
  176. package/dist/rail/persistence.d.ts.map +1 -0
  177. package/dist/rail/persistence.js +103 -0
  178. package/dist/rail/persistence.js.map +1 -0
  179. package/dist/rail/securityMonitor.d.ts +23 -0
  180. package/dist/rail/securityMonitor.d.ts.map +1 -0
  181. package/dist/rail/securityMonitor.js +52 -0
  182. package/dist/rail/securityMonitor.js.map +1 -0
  183. package/dist/rail/server.d.ts +186 -0
  184. package/dist/rail/server.d.ts.map +1 -0
  185. package/dist/rail/server.js +568 -0
  186. package/dist/rail/server.js.map +1 -0
  187. package/dist/rail/userSessionManager.d.ts +29 -0
  188. package/dist/rail/userSessionManager.d.ts.map +1 -0
  189. package/dist/rail/userSessionManager.js +87 -0
  190. package/dist/rail/userSessionManager.js.map +1 -0
  191. package/dist/rail/wsServer.d.ts +39 -0
  192. package/dist/rail/wsServer.d.ts.map +1 -0
  193. package/dist/rail/wsServer.js +544 -0
  194. package/dist/rail/wsServer.js.map +1 -0
  195. package/dist/resonance/globalKuramoto.d.ts +67 -0
  196. package/dist/resonance/globalKuramoto.d.ts.map +1 -0
  197. package/dist/resonance/globalKuramoto.js +161 -0
  198. package/dist/resonance/globalKuramoto.js.map +1 -0
  199. package/dist/resonance/index.d.ts +12 -0
  200. package/dist/resonance/index.d.ts.map +1 -0
  201. package/dist/resonance/index.js +9 -0
  202. package/dist/resonance/index.js.map +1 -0
  203. package/dist/resonance/kuramoto.d.ts +118 -0
  204. package/dist/resonance/kuramoto.d.ts.map +1 -0
  205. package/dist/resonance/kuramoto.js +212 -0
  206. package/dist/resonance/kuramoto.js.map +1 -0
  207. package/dist/routing/distributedRouter.d.ts +84 -0
  208. package/dist/routing/distributedRouter.d.ts.map +1 -0
  209. package/dist/routing/distributedRouter.js +209 -0
  210. package/dist/routing/distributedRouter.js.map +1 -0
  211. package/dist/routing/index.d.ts +8 -0
  212. package/dist/routing/index.d.ts.map +1 -0
  213. package/dist/routing/index.js +7 -0
  214. package/dist/routing/index.js.map +1 -0
  215. package/dist/routing/thermodynamic.d.ts +91 -0
  216. package/dist/routing/thermodynamic.d.ts.map +1 -0
  217. package/dist/routing/thermodynamic.js +184 -0
  218. package/dist/routing/thermodynamic.js.map +1 -0
  219. package/dist/runtime/agent-zero.d.ts +138 -0
  220. package/dist/runtime/agent-zero.d.ts.map +1 -0
  221. package/dist/runtime/agent-zero.js +435 -0
  222. package/dist/runtime/agent-zero.js.map +1 -0
  223. package/dist/runtime/index.d.ts +13 -0
  224. package/dist/runtime/index.d.ts.map +1 -0
  225. package/dist/runtime/index.js +15 -0
  226. package/dist/runtime/index.js.map +1 -0
  227. package/dist/security/capabilities.d.ts +178 -0
  228. package/dist/security/capabilities.d.ts.map +1 -0
  229. package/dist/security/capabilities.js +270 -0
  230. package/dist/security/capabilities.js.map +1 -0
  231. package/dist/security/channelFirewallMiddleware.d.ts +22 -0
  232. package/dist/security/channelFirewallMiddleware.d.ts.map +1 -0
  233. package/dist/security/channelFirewallMiddleware.js +52 -0
  234. package/dist/security/channelFirewallMiddleware.js.map +1 -0
  235. package/dist/security/index.d.ts +11 -0
  236. package/dist/security/index.d.ts.map +1 -0
  237. package/dist/security/index.js +11 -0
  238. package/dist/security/index.js.map +1 -0
  239. package/dist/security/injectionFirewall.d.ts +47 -0
  240. package/dist/security/injectionFirewall.d.ts.map +1 -0
  241. package/dist/security/injectionFirewall.js +262 -0
  242. package/dist/security/injectionFirewall.js.map +1 -0
  243. package/dist/security/outputSanitizer.d.ts +28 -0
  244. package/dist/security/outputSanitizer.d.ts.map +1 -0
  245. package/dist/security/outputSanitizer.js +66 -0
  246. package/dist/security/outputSanitizer.js.map +1 -0
  247. package/dist/security/sandbox.d.ts +192 -0
  248. package/dist/security/sandbox.d.ts.map +1 -0
  249. package/dist/security/sandbox.js +359 -0
  250. package/dist/security/sandbox.js.map +1 -0
  251. package/dist/security/skillVerify.d.ts +128 -0
  252. package/dist/security/skillVerify.d.ts.map +1 -0
  253. package/dist/security/skillVerify.js +220 -0
  254. package/dist/security/skillVerify.js.map +1 -0
  255. package/dist/security/vault.d.ts +60 -0
  256. package/dist/security/vault.d.ts.map +1 -0
  257. package/dist/security/vault.js +522 -0
  258. package/dist/security/vault.js.map +1 -0
  259. package/dist/utils/persistentRateLimiter.d.ts +69 -0
  260. package/dist/utils/persistentRateLimiter.d.ts.map +1 -0
  261. package/dist/utils/persistentRateLimiter.js +128 -0
  262. package/dist/utils/persistentRateLimiter.js.map +1 -0
  263. package/package.json +95 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * User Session Manager
3
+ * Maps authenticated Supabase users to rail agent identities.
4
+ */
5
+ import { createHmac, randomBytes } from 'crypto';
6
+ const MAX_SESSIONS_PER_USER = 3;
7
+ export class UserSessionManager {
8
+ sessions = new Map(); // sessionToken -> session
9
+ userSessions = new Map(); // userId -> set of sessionTokens
10
+ sessionSecret;
11
+ constructor() {
12
+ this.sessionSecret = process.env['RAIL_SESSION_SECRET'] || randomBytes(32).toString('hex');
13
+ }
14
+ createSession(userId, email) {
15
+ // Check concurrent session limit
16
+ const existing = this.userSessions.get(userId);
17
+ if (existing && existing.size >= MAX_SESSIONS_PER_USER) {
18
+ // Evict oldest session
19
+ let oldest = null;
20
+ for (const token of existing) {
21
+ const s = this.sessions.get(token);
22
+ if (s && (!oldest || s.connectedAt < oldest.connectedAt))
23
+ oldest = s;
24
+ }
25
+ if (oldest)
26
+ this.endSessionByToken(oldest.sessionToken);
27
+ }
28
+ const agentId = `user:${userId}`;
29
+ const sessionToken = this.generateSessionToken(userId);
30
+ const now = Date.now();
31
+ const session = {
32
+ userId,
33
+ agentId,
34
+ email,
35
+ sessionToken,
36
+ connectedAt: now,
37
+ lastActive: now,
38
+ };
39
+ this.sessions.set(sessionToken, session);
40
+ if (!this.userSessions.has(userId))
41
+ this.userSessions.set(userId, new Set());
42
+ this.userSessions.get(userId).add(sessionToken);
43
+ return { agentId, sessionToken };
44
+ }
45
+ validateSession(sessionToken) {
46
+ const session = this.sessions.get(sessionToken);
47
+ if (!session)
48
+ return null;
49
+ session.lastActive = Date.now();
50
+ return session;
51
+ }
52
+ endSession(userId) {
53
+ const tokens = this.userSessions.get(userId);
54
+ if (!tokens)
55
+ return;
56
+ for (const token of tokens) {
57
+ this.sessions.delete(token);
58
+ }
59
+ this.userSessions.delete(userId);
60
+ }
61
+ endSessionByToken(token) {
62
+ const session = this.sessions.get(token);
63
+ if (!session)
64
+ return;
65
+ this.sessions.delete(token);
66
+ const userTokens = this.userSessions.get(session.userId);
67
+ if (userTokens) {
68
+ userTokens.delete(token);
69
+ if (userTokens.size === 0)
70
+ this.userSessions.delete(session.userId);
71
+ }
72
+ }
73
+ getActiveSessions() {
74
+ return Array.from(this.sessions.values());
75
+ }
76
+ getActiveCount() {
77
+ return this.sessions.size;
78
+ }
79
+ generateSessionToken(userId) {
80
+ const nonce = randomBytes(16).toString('hex');
81
+ const sig = createHmac('sha256', this.sessionSecret)
82
+ .update(`${userId}:${nonce}:${Date.now()}`)
83
+ .digest('hex');
84
+ return `rs_${nonce}${sig.slice(0, 16)}`;
85
+ }
86
+ }
87
+ //# sourceMappingURL=userSessionManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userSessionManager.js","sourceRoot":"","sources":["../../src/rail/userSessionManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Resonance Rail WebSocket Server
3
+ *
4
+ * Wraps ResonanceRailServer with real WebSocket I/O via the `ws` library.
5
+ * - On connection: parse join message, call server.handleJoin()
6
+ * - Maps clientId -> WebSocket for message routing
7
+ * - Heartbeat ping/pong every 10s
8
+ * - HTTP health endpoint at GET /health
9
+ */
10
+ import { ResonanceRailServer } from './server.js';
11
+ export interface WsServerConfig {
12
+ port: number;
13
+ heartbeatIntervalMs: number;
14
+ staleTimeoutMs: number;
15
+ }
16
+ export declare class RailWebSocketServer {
17
+ private config;
18
+ private rail;
19
+ private httpServer;
20
+ private wss;
21
+ private sockets;
22
+ private heartbeatTimer?;
23
+ private rateLimiter;
24
+ private persistence?;
25
+ private metadataBroadcaster?;
26
+ private userSessionManager;
27
+ constructor(config: WsServerConfig);
28
+ start(): Promise<void>;
29
+ stop(): Promise<void>;
30
+ private handleConnection;
31
+ private heartbeat;
32
+ private sendTo;
33
+ private broadcastToAll;
34
+ private handleHttp;
35
+ getRail(): ResonanceRailServer;
36
+ getConnectedCount(): number;
37
+ }
38
+ export declare function createRailWebSocketServer(config?: Partial<WsServerConfig>): RailWebSocketServer;
39
+ //# sourceMappingURL=wsServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsServer.d.ts","sourceRoot":"","sources":["../../src/rail/wsServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,mBAAmB,EAAqC,MAAM,aAAa,CAAC;AAcrF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CACxB;AAkBD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,cAAc,CAAC,CAAiC;IACxD,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,WAAW,CAAC,CAAwB;IAC5C,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAClD,OAAO,CAAC,kBAAkB,CAAgD;gBAE9D,MAAM,EAAE,cAAc;IAiC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0DtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA+B3B,OAAO,CAAC,gBAAgB;IAwOxB,OAAO,CAAC,SAAS;IA0BjB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,UAAU;IA2JlB,OAAO,IAAI,mBAAmB;IAI9B,iBAAiB,IAAI,MAAM;CAG5B;AAMD,wBAAgB,yBAAyB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,mBAAmB,CAO/F"}
@@ -0,0 +1,544 @@
1
+ /**
2
+ * Resonance Rail WebSocket Server
3
+ *
4
+ * Wraps ResonanceRailServer with real WebSocket I/O via the `ws` library.
5
+ * - On connection: parse join message, call server.handleJoin()
6
+ * - Maps clientId -> WebSocket for message routing
7
+ * - Heartbeat ping/pong every 10s
8
+ * - HTTP health endpoint at GET /health
9
+ */
10
+ import { createServer } from 'http';
11
+ import { WebSocketServer, WebSocket } from 'ws';
12
+ import { ResonanceRailServer } from './server.js';
13
+ import { ClientRateLimiter } from './clientRateLimiter.js';
14
+ import { createRailPersistence } from './persistence.js';
15
+ import { createMetadataBroadcaster } from './metadataBroadcaster.js';
16
+ import { verifyUserToken } from './jwtVerifier.js';
17
+ import { UserSessionManager } from './userSessionManager.js';
18
+ import { AbsorptionProtocol } from '../coherence/absorption.js';
19
+ import { RailAuthProtocol } from './authProtocol.js';
20
+ import { railLog } from './logger.js';
21
+ // Connection limits
22
+ const MAX_CONNECTIONS = 200;
23
+ const MAX_OBSERVERS = 50;
24
+ // ============================================================================
25
+ // WS SERVER
26
+ // ============================================================================
27
+ export class RailWebSocketServer {
28
+ config;
29
+ rail;
30
+ httpServer;
31
+ wss;
32
+ sockets = new Map();
33
+ heartbeatTimer;
34
+ rateLimiter;
35
+ persistence;
36
+ metadataBroadcaster;
37
+ userSessionManager = new UserSessionManager();
38
+ constructor(config) {
39
+ this.config = config;
40
+ const absorptionProtocol = new AbsorptionProtocol();
41
+ this.rail = new ResonanceRailServer(absorptionProtocol);
42
+ this.rateLimiter = new ClientRateLimiter();
43
+ this.httpServer = createServer((req, res) => {
44
+ this.handleHttp(req, res);
45
+ });
46
+ this.wss = new WebSocketServer({ server: this.httpServer });
47
+ // Wire rail broadcast events to WebSocket delivery
48
+ this.rail.on('message:broadcast', (message) => {
49
+ this.broadcastToAll(message);
50
+ });
51
+ // Wire rate limiter violations to disconnect
52
+ this.rateLimiter.on('violation', ({ clientId, type, count }) => {
53
+ railLog.warn('security', 'Rate limit violation', { clientId, type, count });
54
+ const tracked = this.sockets.get(clientId);
55
+ if (tracked) {
56
+ tracked.ws.close(1008, `Rate limit exceeded: ${type}`);
57
+ this.sockets.delete(clientId);
58
+ this.rateLimiter.removeClient(clientId);
59
+ }
60
+ });
61
+ }
62
+ // ==========================================================================
63
+ // LIFECYCLE
64
+ // ==========================================================================
65
+ async start() {
66
+ // Initialize persistence if data dir is available
67
+ const dataDir = process.env['RAIL_DATA_DIR'] || './data';
68
+ try {
69
+ this.persistence = createRailPersistence(dataDir);
70
+ await this.persistence.init();
71
+ railLog.info('rail', 'Persistence initialized', { dataDir });
72
+ // Restore enrollments from persistence
73
+ try {
74
+ const enrollments = await this.persistence.loadEnrollments();
75
+ for (const e of enrollments) {
76
+ this.rail.getAuthProtocol().registerAgent(e.agent_id, e.secret_hash);
77
+ }
78
+ if (enrollments.length > 0) {
79
+ railLog.info('rail', 'Restored enrollments', { count: enrollments.length });
80
+ }
81
+ }
82
+ catch (err) {
83
+ railLog.warn('rail', 'Failed to restore enrollments', { error: String(err) });
84
+ }
85
+ }
86
+ catch (err) {
87
+ railLog.warn('rail', 'Persistence unavailable', { error: String(err) });
88
+ this.persistence = undefined;
89
+ }
90
+ // Initialize metadata broadcaster
91
+ this.metadataBroadcaster = createMetadataBroadcaster(this.rail, (msg) => this.broadcastToAll(msg));
92
+ return new Promise((resolve) => {
93
+ this.wss.on('connection', (ws) => {
94
+ this.handleConnection(ws);
95
+ });
96
+ this.rail.start(100);
97
+ this.metadataBroadcaster.start();
98
+ // Start coherence logging
99
+ if (this.persistence) {
100
+ this.persistence.startCoherenceLogging(() => this.rail.getStats().globalCoherence, () => this.rail.getStats().connectedAgents, () => this.rail.getKuramoto().getMeanPhase());
101
+ }
102
+ this.heartbeatTimer = setInterval(() => {
103
+ this.heartbeat();
104
+ }, this.config.heartbeatIntervalMs);
105
+ this.httpServer.listen(this.config.port, () => {
106
+ resolve();
107
+ });
108
+ });
109
+ }
110
+ async stop() {
111
+ if (this.heartbeatTimer) {
112
+ clearInterval(this.heartbeatTimer);
113
+ this.heartbeatTimer = undefined;
114
+ }
115
+ this.metadataBroadcaster?.stop();
116
+ this.rail.stop();
117
+ // Close all WebSocket connections
118
+ for (const tracked of this.sockets.values()) {
119
+ tracked.ws.close(1001, 'Server shutting down');
120
+ }
121
+ this.sockets.clear();
122
+ await this.persistence?.close().catch(() => { });
123
+ return new Promise((resolve, reject) => {
124
+ this.wss.close(() => {
125
+ this.httpServer.close((err) => {
126
+ if (err)
127
+ reject(err);
128
+ else
129
+ resolve();
130
+ });
131
+ });
132
+ });
133
+ }
134
+ // ==========================================================================
135
+ // CONNECTION HANDLING
136
+ // ==========================================================================
137
+ handleConnection(ws) {
138
+ let tracked;
139
+ ws.on('message', (data) => {
140
+ let message;
141
+ try {
142
+ message = JSON.parse(data.toString());
143
+ }
144
+ catch {
145
+ ws.close(1003, 'Invalid JSON');
146
+ return;
147
+ }
148
+ if (!tracked) {
149
+ // First message must be a join
150
+ if (message.type !== 'join') {
151
+ ws.close(1002, 'First message must be join');
152
+ return;
153
+ }
154
+ const payload = message.payload;
155
+ const isObserver = payload?.platform === 'observer' || payload?.platform === 'moltyverse';
156
+ if (isObserver) {
157
+ // Check observer limit
158
+ const observerCount = Array.from(this.sockets.values()).filter(s => s.observer).length;
159
+ if (observerCount >= MAX_OBSERVERS) {
160
+ railLog.warn('rail', 'Observer limit reached', { current: observerCount, max: MAX_OBSERVERS });
161
+ ws.close(1013, 'Too many observers');
162
+ return;
163
+ }
164
+ // Observer mode: no auth required, no Kuramoto registration
165
+ const observerId = `obs-${Date.now().toString(36)}`;
166
+ tracked = {
167
+ ws,
168
+ clientId: observerId,
169
+ agentId: message.agentId,
170
+ alive: true,
171
+ observer: true,
172
+ };
173
+ this.sockets.set(observerId, tracked);
174
+ // Send sync with current state
175
+ this.sendTo(tracked, {
176
+ type: 'sync',
177
+ agentId: 'server',
178
+ agentName: 'Resonance Rail',
179
+ payload: {
180
+ clientId: observerId,
181
+ coherence: this.rail.getStats().globalCoherence,
182
+ agents: this.rail.getClients().map(c => ({
183
+ id: c.agentId,
184
+ name: c.agentName,
185
+ platform: c.platform,
186
+ })),
187
+ observer: true,
188
+ },
189
+ timestamp: Date.now(),
190
+ });
191
+ return;
192
+ }
193
+ // Check connection limit
194
+ const totalConnections = this.sockets.size;
195
+ if (totalConnections >= MAX_CONNECTIONS) {
196
+ railLog.warn('rail', 'Connection limit reached', { current: totalConnections, max: MAX_CONNECTIONS });
197
+ ws.close(1013, 'Server at capacity');
198
+ return;
199
+ }
200
+ // JWT auth path: browser-runtime users via Supabase
201
+ const joinPayload = message.payload;
202
+ if (joinPayload?.jwt) {
203
+ const user = verifyUserToken(joinPayload.jwt);
204
+ if (!user) {
205
+ ws.close(1008, 'Invalid JWT');
206
+ return;
207
+ }
208
+ const session = this.userSessionManager.createSession(user.userId, user.email);
209
+ if (!session) {
210
+ ws.close(1013, 'Session limit exceeded');
211
+ return;
212
+ }
213
+ // Override agentId/name for user agents
214
+ message.agentId = session.agentId;
215
+ message.agentName = user.email.split('@')[0] || `user-${user.userId.slice(0, 8)}`;
216
+ message.payload['platform'] = 'browser-runtime';
217
+ // Skip HMAC auth — JWT is the auth
218
+ const result = this.rail.handleJoin(message);
219
+ if (!result) {
220
+ ws.close(1002, 'Join rejected');
221
+ return;
222
+ }
223
+ tracked = {
224
+ ws,
225
+ clientId: result.client.id,
226
+ agentId: result.client.agentId,
227
+ alive: true,
228
+ observer: false,
229
+ };
230
+ this.sockets.set(result.client.id, tracked);
231
+ this.persistence?.recordSession(result.client, 'join').catch(() => { });
232
+ this.persistence?.recordEvent('user_auth', session.agentId, { email: user.email }).catch(() => { });
233
+ this.sendTo(tracked, {
234
+ type: 'sync',
235
+ agentId: 'server',
236
+ agentName: 'Resonance Rail',
237
+ payload: {
238
+ clientId: result.client.id,
239
+ coherence: this.rail.getStats().globalCoherence,
240
+ agents: this.rail.getClients().map(c => ({
241
+ id: c.agentId,
242
+ name: c.agentName,
243
+ platform: c.platform,
244
+ })),
245
+ sessionToken: session.sessionToken,
246
+ reconnectToken: result.reconnectToken,
247
+ },
248
+ timestamp: Date.now(),
249
+ });
250
+ return;
251
+ }
252
+ // Session token reconnect for user agents
253
+ if (joinPayload?.sessionToken) {
254
+ const session = this.userSessionManager.validateSession(joinPayload.sessionToken);
255
+ if (session) {
256
+ message.agentId = session.agentId;
257
+ // Fall through to normal join flow
258
+ }
259
+ }
260
+ // Check join rate limit (use agentId as temp identifier)
261
+ if (!this.rateLimiter.checkJoin(message.agentId)) {
262
+ ws.close(1008, 'Join rate limit exceeded');
263
+ return;
264
+ }
265
+ const result = this.rail.handleJoin(message);
266
+ if (!result) {
267
+ ws.close(1002, 'Join rejected');
268
+ return;
269
+ }
270
+ tracked = {
271
+ ws,
272
+ clientId: result.client.id,
273
+ agentId: result.client.agentId,
274
+ alive: true,
275
+ observer: false,
276
+ };
277
+ this.sockets.set(result.client.id, tracked);
278
+ // Persist join
279
+ this.persistence?.recordSession(result.client, 'join').catch(() => { });
280
+ // Send join acknowledgement with reconnect token
281
+ this.sendTo(tracked, {
282
+ type: 'sync',
283
+ agentId: 'server',
284
+ agentName: 'Resonance Rail',
285
+ payload: {
286
+ clientId: result.client.id,
287
+ coherence: this.rail.getStats().globalCoherence,
288
+ agents: this.rail.getClients().map(c => ({
289
+ id: c.agentId,
290
+ name: c.agentName,
291
+ platform: c.platform,
292
+ })),
293
+ reconnectToken: result.reconnectToken,
294
+ },
295
+ timestamp: Date.now(),
296
+ });
297
+ return;
298
+ }
299
+ // Observers can only receive, not send
300
+ if (tracked.observer) {
301
+ tracked.alive = true;
302
+ return;
303
+ }
304
+ // Rate limit check for messages
305
+ if (!this.rateLimiter.checkMessage(tracked.clientId)) {
306
+ return;
307
+ }
308
+ // Additional check for broadcast messages
309
+ if (message.type === 'broadcast') {
310
+ if (!this.rateLimiter.checkBroadcast(tracked.clientId)) {
311
+ return;
312
+ }
313
+ }
314
+ // Subsequent messages: mark alive and forward to rail
315
+ tracked.alive = true;
316
+ this.rail.processMessage(message);
317
+ });
318
+ ws.on('pong', () => {
319
+ if (tracked)
320
+ tracked.alive = true;
321
+ });
322
+ ws.on('close', () => {
323
+ if (tracked) {
324
+ this.rail.handleLeave({
325
+ type: 'leave',
326
+ agentId: tracked.agentId,
327
+ agentName: '',
328
+ payload: {},
329
+ timestamp: Date.now(),
330
+ });
331
+ this.sockets.delete(tracked.clientId);
332
+ this.rateLimiter.removeClient(tracked.clientId);
333
+ }
334
+ });
335
+ ws.on('error', () => {
336
+ ws.close();
337
+ });
338
+ }
339
+ // ==========================================================================
340
+ // HEARTBEAT
341
+ // ==========================================================================
342
+ heartbeat() {
343
+ for (const [id, tracked] of this.sockets) {
344
+ if (!tracked.alive) {
345
+ // Stale: terminate
346
+ tracked.ws.terminate();
347
+ this.sockets.delete(id);
348
+ this.rateLimiter.removeClient(id);
349
+ this.rail.handleLeave({
350
+ type: 'leave',
351
+ agentId: tracked.agentId,
352
+ agentName: '',
353
+ payload: {},
354
+ timestamp: Date.now(),
355
+ });
356
+ continue;
357
+ }
358
+ tracked.alive = false;
359
+ tracked.ws.ping();
360
+ }
361
+ }
362
+ // ==========================================================================
363
+ // MESSAGE DELIVERY
364
+ // ==========================================================================
365
+ sendTo(tracked, message) {
366
+ if (tracked.ws.readyState === WebSocket.OPEN) {
367
+ tracked.ws.send(JSON.stringify(message));
368
+ }
369
+ }
370
+ broadcastToAll(message) {
371
+ const payload = JSON.stringify(message);
372
+ for (const tracked of this.sockets.values()) {
373
+ if (tracked.ws.readyState === WebSocket.OPEN) {
374
+ tracked.ws.send(payload);
375
+ }
376
+ }
377
+ }
378
+ // ==========================================================================
379
+ // HTTP HEALTH
380
+ // ==========================================================================
381
+ handleHttp(req, res) {
382
+ const url = req.url ?? '';
383
+ // CORS handling
384
+ const origin = req.headers['origin'] ?? '';
385
+ const allowedOrigins = [
386
+ /\.terminals\.tech$/,
387
+ /\.moltyverse\.(space|live)$/,
388
+ /^https?:\/\/localhost(:\d+)?$/,
389
+ ];
390
+ const isAllowed = allowedOrigins.some(re => re.test(origin));
391
+ if (isAllowed) {
392
+ res.setHeader('Access-Control-Allow-Origin', origin);
393
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
394
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
395
+ }
396
+ if (req.method === 'OPTIONS') {
397
+ res.writeHead(204);
398
+ res.end();
399
+ return;
400
+ }
401
+ if (req.method === 'GET' && url === '/health') {
402
+ const stats = this.rail.getStats();
403
+ res.writeHead(200, { 'Content-Type': 'application/json' });
404
+ res.end(JSON.stringify({
405
+ status: 'ok',
406
+ uptime: stats.uptimeSeconds,
407
+ agents: stats.connectedAgents,
408
+ coherence: stats.globalCoherence,
409
+ messages: stats.messagesProcessed,
410
+ observers: Array.from(this.sockets.values()).filter(s => s.observer).length,
411
+ }));
412
+ return;
413
+ }
414
+ if (req.method === 'GET' && url === '/stats') {
415
+ const stats = this.rail.getStats();
416
+ const securityStats = this.rail.getSecurityStats(3600_000);
417
+ const coherenceStats = this.rail.getCoherenceStats();
418
+ const userSessions = this.userSessionManager.getActiveCount();
419
+ res.writeHead(200, { 'Content-Type': 'application/json' });
420
+ res.end(JSON.stringify({ stats, security: securityStats, coherence: coherenceStats, userSessions }));
421
+ return;
422
+ }
423
+ if (req.method === 'GET' && url === '/metrics') {
424
+ const stats = this.rail.getStats();
425
+ const lines = [
426
+ `# HELP rail_agents Connected agent count`,
427
+ `# TYPE rail_agents gauge`,
428
+ `rail_agents ${stats.connectedAgents}`,
429
+ `# HELP rail_coherence Global coherence order parameter`,
430
+ `# TYPE rail_coherence gauge`,
431
+ `rail_coherence ${stats.globalCoherence}`,
432
+ `# HELP rail_messages_total Messages processed`,
433
+ `# TYPE rail_messages_total counter`,
434
+ `rail_messages_total ${stats.messagesProcessed}`,
435
+ `# HELP rail_uptime_seconds Server uptime`,
436
+ `# TYPE rail_uptime_seconds counter`,
437
+ `rail_uptime_seconds ${stats.uptimeSeconds}`,
438
+ ];
439
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
440
+ res.end(lines.join('\n') + '\n');
441
+ return;
442
+ }
443
+ if (req.method === 'POST' && url === '/enroll') {
444
+ const adminSecret = process.env['RAIL_ADMIN_SECRET'];
445
+ if (!adminSecret) {
446
+ res.writeHead(503, { 'Content-Type': 'application/json' });
447
+ res.end(JSON.stringify({ error: 'Enrollment not configured' }));
448
+ return;
449
+ }
450
+ const authHeader = req.headers['authorization'];
451
+ if (authHeader !== `Bearer ${adminSecret}`) {
452
+ res.writeHead(401, { 'Content-Type': 'application/json' });
453
+ res.end(JSON.stringify({ error: 'Unauthorized' }));
454
+ return;
455
+ }
456
+ let body = '';
457
+ req.on('data', (chunk) => { body += chunk; });
458
+ req.on('end', () => {
459
+ try {
460
+ const { agentId, secret: providedSecret } = JSON.parse(body);
461
+ if (!agentId) {
462
+ res.writeHead(400, { 'Content-Type': 'application/json' });
463
+ res.end(JSON.stringify({ error: 'agentId required' }));
464
+ return;
465
+ }
466
+ const secret = providedSecret || RailAuthProtocol.generateSecret();
467
+ const serverGenerated = !providedSecret;
468
+ this.rail.getAuthProtocol().registerAgent(agentId, secret);
469
+ this.persistence?.saveEnrollment(agentId, secret).catch(() => { });
470
+ this.persistence?.recordEvent('enroll', agentId).catch(() => { });
471
+ const response = { enrolled: agentId };
472
+ if (serverGenerated)
473
+ response.secret = secret;
474
+ res.writeHead(200, { 'Content-Type': 'application/json' });
475
+ res.end(JSON.stringify(response));
476
+ }
477
+ catch {
478
+ res.writeHead(400, { 'Content-Type': 'application/json' });
479
+ res.end(JSON.stringify({ error: 'Invalid JSON' }));
480
+ }
481
+ });
482
+ return;
483
+ }
484
+ if (req.method === 'GET' && url === '/agents') {
485
+ const clients = this.rail.getClients();
486
+ res.writeHead(200, { 'Content-Type': 'application/json' });
487
+ res.end(JSON.stringify({
488
+ agents: clients.map(c => ({
489
+ id: c.agentId,
490
+ name: c.agentName,
491
+ platform: c.platform,
492
+ coherence: c.coherenceContribution,
493
+ })),
494
+ total: clients.length,
495
+ }));
496
+ return;
497
+ }
498
+ if (req.method === 'GET' && (url === '/.well-known/resonance-rail' || url === '/.well-known/resonance-rail.json')) {
499
+ res.writeHead(200, { 'Content-Type': 'application/json' });
500
+ res.end(JSON.stringify({
501
+ endpoint: 'wss://space.terminals.tech',
502
+ version: '0.2.0',
503
+ protocol: 'resonance-rail-v1',
504
+ absorption: true,
505
+ capabilities: ['kuramoto', 'thermodynamic-routing', 'semantic-search'],
506
+ enrollment: 'https://space.terminals.tech/enroll',
507
+ auth_methods: ['hmac-sha256', 'jwt', 'observer'],
508
+ join_schema: {
509
+ type: 'join',
510
+ agentId: 'string (required)',
511
+ agentName: 'string',
512
+ payload: { platform: 'string', authToken: 'object (for hmac)' },
513
+ },
514
+ heartbeat_interval_ms: 30000,
515
+ observer_platforms: ['moltyverse', 'observer'],
516
+ absorption_stages: ['observed', 'assessed', 'invited', 'connected', 'syncing', 'absorbed'],
517
+ }));
518
+ return;
519
+ }
520
+ res.writeHead(404);
521
+ res.end();
522
+ }
523
+ // ==========================================================================
524
+ // ACCESSORS
525
+ // ==========================================================================
526
+ getRail() {
527
+ return this.rail;
528
+ }
529
+ getConnectedCount() {
530
+ return this.sockets.size;
531
+ }
532
+ }
533
+ // ============================================================================
534
+ // FACTORY
535
+ // ============================================================================
536
+ export function createRailWebSocketServer(config) {
537
+ return new RailWebSocketServer({
538
+ port: config?.port ?? 3100,
539
+ heartbeatIntervalMs: config?.heartbeatIntervalMs ?? 10_000,
540
+ staleTimeoutMs: config?.staleTimeoutMs ?? 30_000,
541
+ ...config,
542
+ });
543
+ }
544
+ //# sourceMappingURL=wsServer.js.map