agent-relay 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 (143) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +22 -0
  3. package/PROTOCOL.md +319 -0
  4. package/README.md +791 -0
  5. package/dist/cli/index.d.ts +7 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +1591 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/daemon/connection.d.ts +60 -0
  10. package/dist/daemon/connection.d.ts.map +1 -0
  11. package/dist/daemon/connection.js +245 -0
  12. package/dist/daemon/connection.js.map +1 -0
  13. package/dist/daemon/index.d.ts +4 -0
  14. package/dist/daemon/index.d.ts.map +1 -0
  15. package/dist/daemon/index.js +4 -0
  16. package/dist/daemon/index.js.map +1 -0
  17. package/dist/daemon/router.d.ts +72 -0
  18. package/dist/daemon/router.d.ts.map +1 -0
  19. package/dist/daemon/router.js +183 -0
  20. package/dist/daemon/router.js.map +1 -0
  21. package/dist/daemon/server.d.ts +52 -0
  22. package/dist/daemon/server.d.ts.map +1 -0
  23. package/dist/daemon/server.js +186 -0
  24. package/dist/daemon/server.js.map +1 -0
  25. package/dist/dashboard/public/index.html +690 -0
  26. package/dist/dashboard/server.d.ts +2 -0
  27. package/dist/dashboard/server.d.ts.map +1 -0
  28. package/dist/dashboard/server.js +220 -0
  29. package/dist/dashboard/server.js.map +1 -0
  30. package/dist/games/index.d.ts +2 -0
  31. package/dist/games/index.d.ts.map +1 -0
  32. package/dist/games/index.js +2 -0
  33. package/dist/games/index.js.map +1 -0
  34. package/dist/games/tictactoe.d.ts +24 -0
  35. package/dist/games/tictactoe.d.ts.map +1 -0
  36. package/dist/games/tictactoe.js +160 -0
  37. package/dist/games/tictactoe.js.map +1 -0
  38. package/dist/hooks/inbox-check/hook.d.ts +28 -0
  39. package/dist/hooks/inbox-check/hook.d.ts.map +1 -0
  40. package/dist/hooks/inbox-check/hook.js +97 -0
  41. package/dist/hooks/inbox-check/hook.js.map +1 -0
  42. package/dist/hooks/inbox-check/index.d.ts +8 -0
  43. package/dist/hooks/inbox-check/index.d.ts.map +1 -0
  44. package/dist/hooks/inbox-check/index.js +8 -0
  45. package/dist/hooks/inbox-check/index.js.map +1 -0
  46. package/dist/hooks/inbox-check/types.d.ts +31 -0
  47. package/dist/hooks/inbox-check/types.d.ts.map +1 -0
  48. package/dist/hooks/inbox-check/types.js +5 -0
  49. package/dist/hooks/inbox-check/types.js.map +1 -0
  50. package/dist/hooks/inbox-check/utils.d.ts +44 -0
  51. package/dist/hooks/inbox-check/utils.d.ts.map +1 -0
  52. package/dist/hooks/inbox-check/utils.js +107 -0
  53. package/dist/hooks/inbox-check/utils.js.map +1 -0
  54. package/dist/index.d.ts +10 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +10 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/protocol/framing.d.ts +32 -0
  59. package/dist/protocol/framing.d.ts.map +1 -0
  60. package/dist/protocol/framing.js +71 -0
  61. package/dist/protocol/framing.js.map +1 -0
  62. package/dist/protocol/index.d.ts +3 -0
  63. package/dist/protocol/index.d.ts.map +1 -0
  64. package/dist/protocol/index.js +3 -0
  65. package/dist/protocol/index.js.map +1 -0
  66. package/dist/protocol/types.d.ts +104 -0
  67. package/dist/protocol/types.d.ts.map +1 -0
  68. package/dist/protocol/types.js +6 -0
  69. package/dist/protocol/types.js.map +1 -0
  70. package/dist/state/agent-state.d.ts +40 -0
  71. package/dist/state/agent-state.d.ts.map +1 -0
  72. package/dist/state/agent-state.js +120 -0
  73. package/dist/state/agent-state.js.map +1 -0
  74. package/dist/storage/adapter.d.ts +29 -0
  75. package/dist/storage/adapter.d.ts.map +1 -0
  76. package/dist/storage/adapter.js +2 -0
  77. package/dist/storage/adapter.js.map +1 -0
  78. package/dist/storage/sqlite-adapter.d.ts +15 -0
  79. package/dist/storage/sqlite-adapter.d.ts.map +1 -0
  80. package/dist/storage/sqlite-adapter.js +116 -0
  81. package/dist/storage/sqlite-adapter.js.map +1 -0
  82. package/dist/supervisor/inbox.d.ts +38 -0
  83. package/dist/supervisor/inbox.d.ts.map +1 -0
  84. package/dist/supervisor/inbox.js +162 -0
  85. package/dist/supervisor/inbox.js.map +1 -0
  86. package/dist/supervisor/index.d.ts +10 -0
  87. package/dist/supervisor/index.d.ts.map +1 -0
  88. package/dist/supervisor/index.js +10 -0
  89. package/dist/supervisor/index.js.map +1 -0
  90. package/dist/supervisor/spawner.d.ts +54 -0
  91. package/dist/supervisor/spawner.d.ts.map +1 -0
  92. package/dist/supervisor/spawner.js +282 -0
  93. package/dist/supervisor/spawner.js.map +1 -0
  94. package/dist/supervisor/state.d.ts +132 -0
  95. package/dist/supervisor/state.d.ts.map +1 -0
  96. package/dist/supervisor/state.js +465 -0
  97. package/dist/supervisor/state.js.map +1 -0
  98. package/dist/supervisor/supervisor.d.ts +67 -0
  99. package/dist/supervisor/supervisor.d.ts.map +1 -0
  100. package/dist/supervisor/supervisor.js +263 -0
  101. package/dist/supervisor/supervisor.js.map +1 -0
  102. package/dist/supervisor/types.d.ts +139 -0
  103. package/dist/supervisor/types.d.ts.map +1 -0
  104. package/dist/supervisor/types.js +12 -0
  105. package/dist/supervisor/types.js.map +1 -0
  106. package/dist/utils/index.d.ts +2 -0
  107. package/dist/utils/index.d.ts.map +1 -0
  108. package/dist/utils/index.js +2 -0
  109. package/dist/utils/index.js.map +1 -0
  110. package/dist/utils/name-generator.d.ts +17 -0
  111. package/dist/utils/name-generator.d.ts.map +1 -0
  112. package/dist/utils/name-generator.js +52 -0
  113. package/dist/utils/name-generator.js.map +1 -0
  114. package/dist/webhook/spawner.d.ts +79 -0
  115. package/dist/webhook/spawner.d.ts.map +1 -0
  116. package/dist/webhook/spawner.js +288 -0
  117. package/dist/webhook/spawner.js.map +1 -0
  118. package/dist/wrapper/client.d.ts +72 -0
  119. package/dist/wrapper/client.d.ts.map +1 -0
  120. package/dist/wrapper/client.js +306 -0
  121. package/dist/wrapper/client.js.map +1 -0
  122. package/dist/wrapper/inbox.d.ts +37 -0
  123. package/dist/wrapper/inbox.d.ts.map +1 -0
  124. package/dist/wrapper/inbox.js +73 -0
  125. package/dist/wrapper/inbox.js.map +1 -0
  126. package/dist/wrapper/index.d.ts +4 -0
  127. package/dist/wrapper/index.d.ts.map +1 -0
  128. package/dist/wrapper/index.js +7 -0
  129. package/dist/wrapper/index.js.map +1 -0
  130. package/dist/wrapper/parser.d.ts +94 -0
  131. package/dist/wrapper/parser.d.ts.map +1 -0
  132. package/dist/wrapper/parser.js +360 -0
  133. package/dist/wrapper/parser.js.map +1 -0
  134. package/dist/wrapper/pty-wrapper.d.ts +125 -0
  135. package/dist/wrapper/pty-wrapper.d.ts.map +1 -0
  136. package/dist/wrapper/pty-wrapper.js +494 -0
  137. package/dist/wrapper/pty-wrapper.js.map +1 -0
  138. package/dist/wrapper/tmux-wrapper.d.ts +131 -0
  139. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -0
  140. package/dist/wrapper/tmux-wrapper.js +427 -0
  141. package/dist/wrapper/tmux-wrapper.js.map +1 -0
  142. package/install.sh +69 -0
  143. package/package.json +82 -0
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Webhook Spawner - Spawns agent CLIs when messages arrive
3
+ *
4
+ * Supports: claude, codex, gemini, cursor
5
+ */
6
+ import { spawn } from 'child_process';
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import * as chokidar from 'chokidar';
10
+ /**
11
+ * Get the spawn command for a CLI type
12
+ */
13
+ function getSpawnCommand(cli) {
14
+ switch (cli.toLowerCase()) {
15
+ case 'claude':
16
+ return { cmd: 'claude', args: ['--dangerously-skip-permissions'] };
17
+ case 'codex':
18
+ return { cmd: 'codex', args: [] };
19
+ case 'gemini':
20
+ return { cmd: 'gemini', args: [] };
21
+ case 'cursor':
22
+ return { cmd: 'cursor', args: ['--cli'] };
23
+ default:
24
+ return { cmd: cli, args: [] };
25
+ }
26
+ }
27
+ /**
28
+ * Spawn an agent CLI to check their inbox
29
+ */
30
+ export function spawnAgent(config) {
31
+ const { cmd, args } = getSpawnCommand(config.cli);
32
+ const instructionsPath = path.join(config.dataDir, config.name, 'INSTRUCTIONS.md');
33
+ // Create a prompt that tells the agent to check inbox
34
+ const prompt = `You have new messages! Read ${instructionsPath} and check your inbox immediately using the team-check command. Respond to any messages, then continue your work loop.`;
35
+ try {
36
+ const child = spawn(cmd, [...args, '-p', prompt], {
37
+ cwd: config.projectDir,
38
+ stdio: 'inherit',
39
+ detached: true,
40
+ });
41
+ child.unref();
42
+ return {
43
+ agent: config.name,
44
+ pid: child.pid,
45
+ success: true,
46
+ };
47
+ }
48
+ catch (error) {
49
+ return {
50
+ agent: config.name,
51
+ success: false,
52
+ error: error instanceof Error ? error.message : String(error),
53
+ };
54
+ }
55
+ }
56
+ /**
57
+ * Spawn agent in background and capture output
58
+ */
59
+ export function spawnAgentBackground(config, onOutput, onExit) {
60
+ const { cmd, args } = getSpawnCommand(config.cli);
61
+ const instructionsPath = path.join(config.dataDir, config.name, 'INSTRUCTIONS.md');
62
+ const prompt = `You have new messages! Read ${instructionsPath} and check your inbox using team-check --no-wait. Respond to messages, do one task step, broadcast status, then exit.`;
63
+ try {
64
+ const child = spawn(cmd, [...args, '-p', prompt], {
65
+ cwd: config.projectDir,
66
+ stdio: ['pipe', 'pipe', 'pipe'],
67
+ });
68
+ if (onOutput) {
69
+ child.stdout?.on('data', (data) => onOutput(data.toString()));
70
+ child.stderr?.on('data', (data) => onOutput(data.toString()));
71
+ }
72
+ if (onExit) {
73
+ child.on('exit', onExit);
74
+ }
75
+ return child;
76
+ }
77
+ catch (error) {
78
+ console.error(`Failed to spawn ${config.name}:`, error);
79
+ return null;
80
+ }
81
+ }
82
+ /**
83
+ * Watch for inbox changes and spawn agents
84
+ */
85
+ export class InboxWatcher {
86
+ watcher = null;
87
+ agents = new Map();
88
+ activeAgents = new Map();
89
+ debounceTimers = new Map();
90
+ dataDir;
91
+ debounceMs;
92
+ constructor(dataDir, debounceMs = 2000) {
93
+ this.dataDir = dataDir;
94
+ this.debounceMs = debounceMs;
95
+ }
96
+ /**
97
+ * Register an agent to watch
98
+ */
99
+ registerAgent(config) {
100
+ this.agents.set(config.name, { ...config, enabled: config.enabled ?? true });
101
+ }
102
+ /**
103
+ * Load agents from team.json
104
+ */
105
+ loadFromTeamConfig() {
106
+ const configPath = path.join(this.dataDir, 'team.json');
107
+ if (!fs.existsSync(configPath)) {
108
+ throw new Error(`Team config not found: ${configPath}`);
109
+ }
110
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
111
+ for (const agent of config.agents) {
112
+ this.registerAgent({
113
+ name: agent.name,
114
+ cli: agent.cli,
115
+ projectDir: config.projectDir,
116
+ dataDir: this.dataDir,
117
+ enabled: agent.webhook !== false, // enabled by default
118
+ });
119
+ }
120
+ }
121
+ /**
122
+ * Start watching for inbox changes
123
+ */
124
+ start(onSpawn) {
125
+ const inboxPattern = path.join(this.dataDir, '*/inbox.md');
126
+ this.watcher = chokidar.watch(inboxPattern, {
127
+ persistent: true,
128
+ ignoreInitial: true,
129
+ awaitWriteFinish: {
130
+ stabilityThreshold: 500,
131
+ pollInterval: 100,
132
+ },
133
+ });
134
+ this.watcher.on('change', (filePath) => {
135
+ const agentName = path.basename(path.dirname(filePath));
136
+ this.handleInboxChange(agentName, onSpawn);
137
+ });
138
+ this.watcher.on('add', (filePath) => {
139
+ const agentName = path.basename(path.dirname(filePath));
140
+ // Only trigger if file has content
141
+ const stats = fs.statSync(filePath);
142
+ if (stats.size > 0) {
143
+ this.handleInboxChange(agentName, onSpawn);
144
+ }
145
+ });
146
+ console.log(`Watching for inbox changes in ${this.dataDir}`);
147
+ }
148
+ /**
149
+ * Handle inbox change with debouncing
150
+ */
151
+ handleInboxChange(agentName, onSpawn) {
152
+ const agent = this.agents.get(agentName);
153
+ if (!agent || !agent.enabled) {
154
+ return;
155
+ }
156
+ // Check if agent is already active
157
+ if (this.activeAgents.has(agentName)) {
158
+ console.log(`${agentName} already active, skipping spawn`);
159
+ return;
160
+ }
161
+ // Check if inbox actually has content
162
+ const inboxPath = path.join(this.dataDir, agentName, 'inbox.md');
163
+ if (!fs.existsSync(inboxPath) || fs.statSync(inboxPath).size === 0) {
164
+ return;
165
+ }
166
+ // Debounce rapid changes
167
+ const existingTimer = this.debounceTimers.get(agentName);
168
+ if (existingTimer) {
169
+ clearTimeout(existingTimer);
170
+ }
171
+ const timer = setTimeout(() => {
172
+ this.debounceTimers.delete(agentName);
173
+ this.spawnForAgent(agentName, onSpawn);
174
+ }, this.debounceMs);
175
+ this.debounceTimers.set(agentName, timer);
176
+ }
177
+ /**
178
+ * Spawn agent and track it
179
+ */
180
+ spawnForAgent(agentName, onSpawn) {
181
+ const agent = this.agents.get(agentName);
182
+ if (!agent)
183
+ return;
184
+ console.log(`Spawning ${agentName} (${agent.cli})...`);
185
+ const child = spawnAgentBackground(agent, (output) => {
186
+ process.stdout.write(`[${agentName}] ${output}`);
187
+ }, (code) => {
188
+ console.log(`${agentName} exited with code ${code}`);
189
+ this.activeAgents.delete(agentName);
190
+ });
191
+ if (child) {
192
+ this.activeAgents.set(agentName, child);
193
+ onSpawn?.({
194
+ agent: agentName,
195
+ pid: child.pid,
196
+ success: true,
197
+ });
198
+ }
199
+ else {
200
+ onSpawn?.({
201
+ agent: agentName,
202
+ success: false,
203
+ error: 'Failed to spawn',
204
+ });
205
+ }
206
+ }
207
+ /**
208
+ * Stop watching
209
+ */
210
+ stop() {
211
+ this.watcher?.close();
212
+ this.watcher = null;
213
+ // Clear all debounce timers
214
+ for (const timer of this.debounceTimers.values()) {
215
+ clearTimeout(timer);
216
+ }
217
+ this.debounceTimers.clear();
218
+ // Kill active agents
219
+ for (const [name, child] of this.activeAgents) {
220
+ console.log(`Stopping ${name}...`);
221
+ child.kill();
222
+ }
223
+ this.activeAgents.clear();
224
+ }
225
+ /**
226
+ * Get status of all agents
227
+ */
228
+ getStatus() {
229
+ return Array.from(this.agents.values()).map((agent) => ({
230
+ name: agent.name,
231
+ enabled: agent.enabled ?? true,
232
+ active: this.activeAgents.has(agent.name),
233
+ }));
234
+ }
235
+ }
236
+ /**
237
+ * Trigger webhook for a specific agent (called after team-send)
238
+ */
239
+ export async function triggerWebhook(agentName, dataDir, options) {
240
+ const configPath = path.join(dataDir, 'team.json');
241
+ if (!fs.existsSync(configPath)) {
242
+ return { agent: agentName, success: false, error: 'Team config not found' };
243
+ }
244
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
245
+ const agent = config.agents.find((a) => a.name === agentName);
246
+ if (!agent) {
247
+ return { agent: agentName, success: false, error: 'Agent not found' };
248
+ }
249
+ // HTTP webhook if configured
250
+ if (options?.http || agent.webhookUrl) {
251
+ const url = options?.http || agent.webhookUrl;
252
+ try {
253
+ const response = await fetch(url, {
254
+ method: 'POST',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: JSON.stringify({
257
+ agent: agentName,
258
+ event: 'new_message',
259
+ timestamp: new Date().toISOString(),
260
+ inboxPath: path.join(dataDir, agentName, 'inbox.md'),
261
+ }),
262
+ });
263
+ return {
264
+ agent: agentName,
265
+ success: response.ok,
266
+ error: response.ok ? undefined : `HTTP ${response.status}`,
267
+ };
268
+ }
269
+ catch (error) {
270
+ return {
271
+ agent: agentName,
272
+ success: false,
273
+ error: error instanceof Error ? error.message : String(error),
274
+ };
275
+ }
276
+ }
277
+ // Spawn CLI if requested
278
+ if (options?.spawn !== false) {
279
+ return spawnAgent({
280
+ name: agentName,
281
+ cli: agent.cli,
282
+ projectDir: config.projectDir,
283
+ dataDir,
284
+ });
285
+ }
286
+ return { agent: agentName, success: true };
287
+ }
288
+ //# sourceMappingURL=spawner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawner.js","sourceRoot":"","sources":["../../src/webhook/spawner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Relay Client
3
+ * Connects to the daemon and handles message sending/receiving.
4
+ */
5
+ import { type SendPayload, type PayloadKind } from '../protocol/types.js';
6
+ export type ClientState = 'DISCONNECTED' | 'CONNECTING' | 'HANDSHAKING' | 'READY' | 'BACKOFF';
7
+ export interface ClientConfig {
8
+ socketPath: string;
9
+ agentName: string;
10
+ reconnect: boolean;
11
+ maxReconnectAttempts: number;
12
+ reconnectDelayMs: number;
13
+ reconnectMaxDelayMs: number;
14
+ }
15
+ export declare class RelayClient {
16
+ private config;
17
+ private socket?;
18
+ private parser;
19
+ private _state;
20
+ private sessionId?;
21
+ private resumeToken?;
22
+ private reconnectAttempts;
23
+ private reconnectDelay;
24
+ private reconnectTimer?;
25
+ private _destroyed;
26
+ onMessage?: (from: string, payload: SendPayload) => void;
27
+ onStateChange?: (state: ClientState) => void;
28
+ onError?: (error: Error) => void;
29
+ constructor(config?: Partial<ClientConfig>);
30
+ get state(): ClientState;
31
+ get agentName(): string;
32
+ /**
33
+ * Connect to the relay daemon.
34
+ */
35
+ connect(): Promise<void>;
36
+ /**
37
+ * Disconnect from the relay daemon.
38
+ */
39
+ disconnect(): void;
40
+ /**
41
+ * Permanently destroy the client. Disconnects and prevents any reconnection.
42
+ */
43
+ destroy(): void;
44
+ /**
45
+ * Send a message to another agent.
46
+ */
47
+ sendMessage(to: string, body: string, kind?: PayloadKind, data?: Record<string, unknown>): boolean;
48
+ /**
49
+ * Broadcast a message to all agents.
50
+ */
51
+ broadcast(body: string, kind?: PayloadKind, data?: Record<string, unknown>): boolean;
52
+ /**
53
+ * Subscribe to a topic.
54
+ */
55
+ subscribe(topic: string): boolean;
56
+ /**
57
+ * Unsubscribe from a topic.
58
+ */
59
+ unsubscribe(topic: string): boolean;
60
+ private setState;
61
+ private sendHello;
62
+ private send;
63
+ private handleData;
64
+ private processFrame;
65
+ private handleWelcome;
66
+ private handleDeliver;
67
+ private handlePing;
68
+ private handleDisconnect;
69
+ private handleError;
70
+ private scheduleReconnect;
71
+ }
72
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/wrapper/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAIL,KAAK,WAAW,EAEhB,KAAK,WAAW,EAEjB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,YAAY,GAAG,aAAa,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9F,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAWD,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAAC,CAAa;IAC5B,OAAO,CAAC,MAAM,CAAc;IAE5B,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,UAAU,CAAS;IAG3B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACzD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;gBAErB,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM;IAM9C,IAAI,KAAK,IAAI,WAAW,CAEvB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA4CxB;;OAEG;IACH,UAAU,IAAI,IAAI;IAqBlB;;OAEG;IACH,OAAO,IAAI,IAAI;IAKf;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAuB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAqB7G;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAuB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAI/F;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAajC;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAanC,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,SAAS;IAqBjB,OAAO,CAAC,IAAI;IAaZ,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,iBAAiB;CAiB1B"}
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Relay Client
3
+ * Connects to the daemon and handles message sending/receiving.
4
+ */
5
+ import net from 'node:net';
6
+ import { v4 as uuid } from 'uuid';
7
+ import { PROTOCOL_VERSION, } from '../protocol/types.js';
8
+ import { encodeFrame, FrameParser } from '../protocol/framing.js';
9
+ import { DEFAULT_SOCKET_PATH } from '../daemon/server.js';
10
+ const DEFAULT_CLIENT_CONFIG = {
11
+ socketPath: DEFAULT_SOCKET_PATH,
12
+ agentName: 'agent',
13
+ reconnect: true,
14
+ maxReconnectAttempts: 10,
15
+ reconnectDelayMs: 100,
16
+ reconnectMaxDelayMs: 30000,
17
+ };
18
+ export class RelayClient {
19
+ config;
20
+ socket;
21
+ parser;
22
+ _state = 'DISCONNECTED';
23
+ sessionId;
24
+ resumeToken;
25
+ reconnectAttempts = 0;
26
+ reconnectDelay;
27
+ reconnectTimer;
28
+ _destroyed = false;
29
+ // Event handlers
30
+ onMessage;
31
+ onStateChange;
32
+ onError;
33
+ constructor(config = {}) {
34
+ this.config = { ...DEFAULT_CLIENT_CONFIG, ...config };
35
+ this.parser = new FrameParser();
36
+ this.reconnectDelay = this.config.reconnectDelayMs;
37
+ }
38
+ get state() {
39
+ return this._state;
40
+ }
41
+ get agentName() {
42
+ return this.config.agentName;
43
+ }
44
+ /**
45
+ * Connect to the relay daemon.
46
+ */
47
+ connect() {
48
+ if (this._state !== 'DISCONNECTED' && this._state !== 'BACKOFF') {
49
+ return Promise.resolve();
50
+ }
51
+ return new Promise((resolve, reject) => {
52
+ this.setState('CONNECTING');
53
+ this.socket = net.createConnection(this.config.socketPath, () => {
54
+ this.setState('HANDSHAKING');
55
+ this.sendHello();
56
+ });
57
+ this.socket.on('data', (data) => this.handleData(data));
58
+ this.socket.on('close', () => {
59
+ this.handleDisconnect();
60
+ });
61
+ this.socket.on('error', (err) => {
62
+ if (this._state === 'CONNECTING') {
63
+ reject(err);
64
+ }
65
+ this.handleError(err);
66
+ });
67
+ // Wait for WELCOME
68
+ const checkReady = setInterval(() => {
69
+ if (this._state === 'READY') {
70
+ clearInterval(checkReady);
71
+ resolve();
72
+ }
73
+ }, 10);
74
+ // Timeout
75
+ setTimeout(() => {
76
+ if (this._state !== 'READY') {
77
+ clearInterval(checkReady);
78
+ reject(new Error('Connection timeout'));
79
+ }
80
+ }, 5000);
81
+ });
82
+ }
83
+ /**
84
+ * Disconnect from the relay daemon.
85
+ */
86
+ disconnect() {
87
+ if (this.reconnectTimer) {
88
+ clearTimeout(this.reconnectTimer);
89
+ this.reconnectTimer = undefined;
90
+ }
91
+ if (this.socket) {
92
+ this.send({
93
+ v: PROTOCOL_VERSION,
94
+ type: 'BYE',
95
+ id: uuid(),
96
+ ts: Date.now(),
97
+ payload: {},
98
+ });
99
+ this.socket.end();
100
+ this.socket = undefined;
101
+ }
102
+ this.setState('DISCONNECTED');
103
+ }
104
+ /**
105
+ * Permanently destroy the client. Disconnects and prevents any reconnection.
106
+ */
107
+ destroy() {
108
+ this._destroyed = true;
109
+ this.disconnect();
110
+ }
111
+ /**
112
+ * Send a message to another agent.
113
+ */
114
+ sendMessage(to, body, kind = 'message', data) {
115
+ if (this._state !== 'READY') {
116
+ return false;
117
+ }
118
+ const envelope = {
119
+ v: PROTOCOL_VERSION,
120
+ type: 'SEND',
121
+ id: uuid(),
122
+ ts: Date.now(),
123
+ to,
124
+ payload: {
125
+ kind,
126
+ body,
127
+ data,
128
+ },
129
+ };
130
+ return this.send(envelope);
131
+ }
132
+ /**
133
+ * Broadcast a message to all agents.
134
+ */
135
+ broadcast(body, kind = 'message', data) {
136
+ return this.sendMessage('*', body, kind, data);
137
+ }
138
+ /**
139
+ * Subscribe to a topic.
140
+ */
141
+ subscribe(topic) {
142
+ if (this._state !== 'READY')
143
+ return false;
144
+ return this.send({
145
+ v: PROTOCOL_VERSION,
146
+ type: 'SUBSCRIBE',
147
+ id: uuid(),
148
+ ts: Date.now(),
149
+ topic,
150
+ payload: {},
151
+ });
152
+ }
153
+ /**
154
+ * Unsubscribe from a topic.
155
+ */
156
+ unsubscribe(topic) {
157
+ if (this._state !== 'READY')
158
+ return false;
159
+ return this.send({
160
+ v: PROTOCOL_VERSION,
161
+ type: 'UNSUBSCRIBE',
162
+ id: uuid(),
163
+ ts: Date.now(),
164
+ topic,
165
+ payload: {},
166
+ });
167
+ }
168
+ setState(state) {
169
+ this._state = state;
170
+ if (this.onStateChange) {
171
+ this.onStateChange(state);
172
+ }
173
+ }
174
+ sendHello() {
175
+ const hello = {
176
+ v: PROTOCOL_VERSION,
177
+ type: 'HELLO',
178
+ id: uuid(),
179
+ ts: Date.now(),
180
+ payload: {
181
+ agent: this.config.agentName,
182
+ capabilities: {
183
+ ack: true,
184
+ resume: true,
185
+ max_inflight: 256,
186
+ supports_topics: true,
187
+ },
188
+ session: this.resumeToken ? { resume_token: this.resumeToken } : undefined,
189
+ },
190
+ };
191
+ this.send(hello);
192
+ }
193
+ send(envelope) {
194
+ if (!this.socket)
195
+ return false;
196
+ try {
197
+ const frame = encodeFrame(envelope);
198
+ this.socket.write(frame);
199
+ return true;
200
+ }
201
+ catch (err) {
202
+ this.handleError(err);
203
+ return false;
204
+ }
205
+ }
206
+ handleData(data) {
207
+ try {
208
+ const frames = this.parser.push(data);
209
+ for (const frame of frames) {
210
+ this.processFrame(frame);
211
+ }
212
+ }
213
+ catch (err) {
214
+ this.handleError(err);
215
+ }
216
+ }
217
+ processFrame(envelope) {
218
+ switch (envelope.type) {
219
+ case 'WELCOME':
220
+ this.handleWelcome(envelope);
221
+ break;
222
+ case 'DELIVER':
223
+ this.handleDeliver(envelope);
224
+ break;
225
+ case 'PING':
226
+ this.handlePing(envelope);
227
+ break;
228
+ case 'ERROR':
229
+ console.error('[client] Server error:', envelope.payload);
230
+ break;
231
+ case 'BUSY':
232
+ console.warn('[client] Server busy, backing off');
233
+ break;
234
+ }
235
+ }
236
+ handleWelcome(envelope) {
237
+ this.sessionId = envelope.payload.session_id;
238
+ this.resumeToken = envelope.payload.resume_token;
239
+ this.reconnectAttempts = 0;
240
+ this.reconnectDelay = this.config.reconnectDelayMs;
241
+ this.setState('READY');
242
+ console.log(`[client] Connected as ${this.config.agentName} (session: ${this.sessionId})`);
243
+ }
244
+ handleDeliver(envelope) {
245
+ // Send ACK
246
+ this.send({
247
+ v: PROTOCOL_VERSION,
248
+ type: 'ACK',
249
+ id: uuid(),
250
+ ts: Date.now(),
251
+ payload: {
252
+ ack_id: envelope.id,
253
+ seq: envelope.delivery.seq,
254
+ },
255
+ });
256
+ // Notify handler
257
+ if (this.onMessage && envelope.from) {
258
+ this.onMessage(envelope.from, envelope.payload);
259
+ }
260
+ }
261
+ handlePing(envelope) {
262
+ this.send({
263
+ v: PROTOCOL_VERSION,
264
+ type: 'PONG',
265
+ id: uuid(),
266
+ ts: Date.now(),
267
+ payload: envelope.payload ?? {},
268
+ });
269
+ }
270
+ handleDisconnect() {
271
+ this.parser.reset();
272
+ this.socket = undefined;
273
+ // Don't reconnect if permanently destroyed
274
+ if (this._destroyed) {
275
+ this.setState('DISCONNECTED');
276
+ return;
277
+ }
278
+ if (this.config.reconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {
279
+ this.scheduleReconnect();
280
+ }
281
+ else {
282
+ this.setState('DISCONNECTED');
283
+ }
284
+ }
285
+ handleError(error) {
286
+ console.error('[client] Error:', error.message);
287
+ if (this.onError) {
288
+ this.onError(error);
289
+ }
290
+ }
291
+ scheduleReconnect() {
292
+ this.setState('BACKOFF');
293
+ this.reconnectAttempts++;
294
+ // Exponential backoff with jitter
295
+ const jitter = Math.random() * 0.3 + 0.85; // 0.85 - 1.15
296
+ const delay = Math.min(this.reconnectDelay * jitter, this.config.reconnectMaxDelayMs);
297
+ this.reconnectDelay *= 2;
298
+ console.log(`[client] Reconnecting in ${Math.round(delay)}ms (attempt ${this.reconnectAttempts})`);
299
+ this.reconnectTimer = setTimeout(() => {
300
+ this.connect().catch(() => {
301
+ // Will trigger another reconnect
302
+ });
303
+ }, delay);
304
+ }
305
+ }
306
+ //# sourceMappingURL=client.js.map