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.
- package/CHANGELOG.md +11 -0
- package/LICENSE +22 -0
- package/PROTOCOL.md +319 -0
- package/README.md +791 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1591 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/daemon/connection.d.ts +60 -0
- package/dist/daemon/connection.d.ts.map +1 -0
- package/dist/daemon/connection.js +245 -0
- package/dist/daemon/connection.js.map +1 -0
- package/dist/daemon/index.d.ts +4 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +4 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/router.d.ts +72 -0
- package/dist/daemon/router.d.ts.map +1 -0
- package/dist/daemon/router.js +183 -0
- package/dist/daemon/router.js.map +1 -0
- package/dist/daemon/server.d.ts +52 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +186 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/dashboard/public/index.html +690 -0
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +220 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/games/index.d.ts +2 -0
- package/dist/games/index.d.ts.map +1 -0
- package/dist/games/index.js +2 -0
- package/dist/games/index.js.map +1 -0
- package/dist/games/tictactoe.d.ts +24 -0
- package/dist/games/tictactoe.d.ts.map +1 -0
- package/dist/games/tictactoe.js +160 -0
- package/dist/games/tictactoe.js.map +1 -0
- package/dist/hooks/inbox-check/hook.d.ts +28 -0
- package/dist/hooks/inbox-check/hook.d.ts.map +1 -0
- package/dist/hooks/inbox-check/hook.js +97 -0
- package/dist/hooks/inbox-check/hook.js.map +1 -0
- package/dist/hooks/inbox-check/index.d.ts +8 -0
- package/dist/hooks/inbox-check/index.d.ts.map +1 -0
- package/dist/hooks/inbox-check/index.js +8 -0
- package/dist/hooks/inbox-check/index.js.map +1 -0
- package/dist/hooks/inbox-check/types.d.ts +31 -0
- package/dist/hooks/inbox-check/types.d.ts.map +1 -0
- package/dist/hooks/inbox-check/types.js +5 -0
- package/dist/hooks/inbox-check/types.js.map +1 -0
- package/dist/hooks/inbox-check/utils.d.ts +44 -0
- package/dist/hooks/inbox-check/utils.d.ts.map +1 -0
- package/dist/hooks/inbox-check/utils.js +107 -0
- package/dist/hooks/inbox-check/utils.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/framing.d.ts +32 -0
- package/dist/protocol/framing.d.ts.map +1 -0
- package/dist/protocol/framing.js +71 -0
- package/dist/protocol/framing.js.map +1 -0
- package/dist/protocol/index.d.ts +3 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +3 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/types.d.ts +104 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +6 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/state/agent-state.d.ts +40 -0
- package/dist/state/agent-state.d.ts.map +1 -0
- package/dist/state/agent-state.js +120 -0
- package/dist/state/agent-state.js.map +1 -0
- package/dist/storage/adapter.d.ts +29 -0
- package/dist/storage/adapter.d.ts.map +1 -0
- package/dist/storage/adapter.js +2 -0
- package/dist/storage/adapter.js.map +1 -0
- package/dist/storage/sqlite-adapter.d.ts +15 -0
- package/dist/storage/sqlite-adapter.d.ts.map +1 -0
- package/dist/storage/sqlite-adapter.js +116 -0
- package/dist/storage/sqlite-adapter.js.map +1 -0
- package/dist/supervisor/inbox.d.ts +38 -0
- package/dist/supervisor/inbox.d.ts.map +1 -0
- package/dist/supervisor/inbox.js +162 -0
- package/dist/supervisor/inbox.js.map +1 -0
- package/dist/supervisor/index.d.ts +10 -0
- package/dist/supervisor/index.d.ts.map +1 -0
- package/dist/supervisor/index.js +10 -0
- package/dist/supervisor/index.js.map +1 -0
- package/dist/supervisor/spawner.d.ts +54 -0
- package/dist/supervisor/spawner.d.ts.map +1 -0
- package/dist/supervisor/spawner.js +282 -0
- package/dist/supervisor/spawner.js.map +1 -0
- package/dist/supervisor/state.d.ts +132 -0
- package/dist/supervisor/state.d.ts.map +1 -0
- package/dist/supervisor/state.js +465 -0
- package/dist/supervisor/state.js.map +1 -0
- package/dist/supervisor/supervisor.d.ts +67 -0
- package/dist/supervisor/supervisor.d.ts.map +1 -0
- package/dist/supervisor/supervisor.js +263 -0
- package/dist/supervisor/supervisor.js.map +1 -0
- package/dist/supervisor/types.d.ts +139 -0
- package/dist/supervisor/types.d.ts.map +1 -0
- package/dist/supervisor/types.js +12 -0
- package/dist/supervisor/types.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/name-generator.d.ts +17 -0
- package/dist/utils/name-generator.d.ts.map +1 -0
- package/dist/utils/name-generator.js +52 -0
- package/dist/utils/name-generator.js.map +1 -0
- package/dist/webhook/spawner.d.ts +79 -0
- package/dist/webhook/spawner.d.ts.map +1 -0
- package/dist/webhook/spawner.js +288 -0
- package/dist/webhook/spawner.js.map +1 -0
- package/dist/wrapper/client.d.ts +72 -0
- package/dist/wrapper/client.d.ts.map +1 -0
- package/dist/wrapper/client.js +306 -0
- package/dist/wrapper/client.js.map +1 -0
- package/dist/wrapper/inbox.d.ts +37 -0
- package/dist/wrapper/inbox.d.ts.map +1 -0
- package/dist/wrapper/inbox.js +73 -0
- package/dist/wrapper/inbox.js.map +1 -0
- package/dist/wrapper/index.d.ts +4 -0
- package/dist/wrapper/index.d.ts.map +1 -0
- package/dist/wrapper/index.js +7 -0
- package/dist/wrapper/index.js.map +1 -0
- package/dist/wrapper/parser.d.ts +94 -0
- package/dist/wrapper/parser.d.ts.map +1 -0
- package/dist/wrapper/parser.js +360 -0
- package/dist/wrapper/parser.js.map +1 -0
- package/dist/wrapper/pty-wrapper.d.ts +125 -0
- package/dist/wrapper/pty-wrapper.d.ts.map +1 -0
- package/dist/wrapper/pty-wrapper.js +494 -0
- package/dist/wrapper/pty-wrapper.js.map +1 -0
- package/dist/wrapper/tmux-wrapper.d.ts +131 -0
- package/dist/wrapper/tmux-wrapper.d.ts.map +1 -0
- package/dist/wrapper/tmux-wrapper.js +427 -0
- package/dist/wrapper/tmux-wrapper.js.map +1 -0
- package/install.sh +69 -0
- 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
|