sam-coder-cli 1.0.43 → 1.0.45
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/bin/agi-cli.js +122 -89
- package/package.json +3 -8
- package/MULTIPLAYER.md +0 -112
- package/bin/agi-cli.js.new +0 -328
- package/bin/ai-collaboration.js +0 -175
- package/bin/ai-team.js +0 -234
- package/bin/collaborate.js +0 -48
- package/bin/multiplayer-client.js +0 -905
- package/bin/multiplayer-mode.js +0 -276
- package/bin/multiplayer-server.js +0 -372
|
@@ -1,905 +0,0 @@
|
|
|
1
|
-
const WebSocket = require('ws');
|
|
2
|
-
const http = require('http');
|
|
3
|
-
const EventEmitter = require('events');
|
|
4
|
-
const readline = require('readline');
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
const { v4: uuidv4 } = require('uuid');
|
|
7
|
-
const { spawn, execSync } = require('child_process');
|
|
8
|
-
|
|
9
|
-
async function isPortInUse(port) {
|
|
10
|
-
return new Promise((resolve) => {
|
|
11
|
-
const net = require('net');
|
|
12
|
-
const server = net.createServer()
|
|
13
|
-
.once('error', () => resolve(true))
|
|
14
|
-
.once('listening', () => {
|
|
15
|
-
server.close();
|
|
16
|
-
resolve(false);
|
|
17
|
-
})
|
|
18
|
-
.listen(port);
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function findAvailablePort(startPort = 8080, maxAttempts = 10) {
|
|
23
|
-
let port = startPort;
|
|
24
|
-
let attempts = 0;
|
|
25
|
-
|
|
26
|
-
while (attempts < maxAttempts) {
|
|
27
|
-
const inUse = await isPortInUse(port);
|
|
28
|
-
if (!inUse) {
|
|
29
|
-
return port;
|
|
30
|
-
}
|
|
31
|
-
port++;
|
|
32
|
-
attempts++;
|
|
33
|
-
}
|
|
34
|
-
throw new Error(`Could not find an available port after ${maxAttempts} attempts`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function ensureServerRunning(port = 8080, serverId = null) {
|
|
38
|
-
// First try to connect to an existing server by ID if provided
|
|
39
|
-
if (serverId) {
|
|
40
|
-
try {
|
|
41
|
-
console.log(chalk.blue(`Attempting to connect to existing server ${serverId}...`));
|
|
42
|
-
// Try to connect to the server
|
|
43
|
-
const ws = new WebSocket(`ws://${serverId}`);
|
|
44
|
-
|
|
45
|
-
// Wait for connection or timeout
|
|
46
|
-
const connectionResult = await Promise.race([
|
|
47
|
-
new Promise(resolve => ws.once('open', () => resolve(true))),
|
|
48
|
-
new Promise(resolve => setTimeout(() => resolve(false), 2000))
|
|
49
|
-
]);
|
|
50
|
-
|
|
51
|
-
if (connectionResult) {
|
|
52
|
-
console.log(chalk.green(`✅ Connected to existing server at ${serverId}`));
|
|
53
|
-
ws.terminate();
|
|
54
|
-
return `ws://${serverId}`;
|
|
55
|
-
}
|
|
56
|
-
ws.terminate();
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.log(chalk.yellow(`Could not connect to server ${serverId}, starting a new server...`));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// If no server ID provided or connection failed, start a new server
|
|
63
|
-
try {
|
|
64
|
-
const availablePort = await findAvailablePort(port);
|
|
65
|
-
const isDefaultPort = (availablePort === port);
|
|
66
|
-
|
|
67
|
-
if (!isDefaultPort) {
|
|
68
|
-
console.log(chalk.yellow(`Port ${port} is in use, trying port ${availablePort}...`));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
console.log('Starting local multiplayer server...');
|
|
72
|
-
// Start server in a detached process
|
|
73
|
-
const serverProcess = spawn('node', [__filename, '--server', '--port', availablePort.toString()], {
|
|
74
|
-
detached: true,
|
|
75
|
-
stdio: 'ignore',
|
|
76
|
-
windowsHide: true
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
serverProcess.unref();
|
|
80
|
-
|
|
81
|
-
// Wait for server to start
|
|
82
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
83
|
-
console.log(chalk.green(`Local multiplayer server started on port ${availablePort}`));
|
|
84
|
-
return `ws://localhost:${availablePort}`;
|
|
85
|
-
} catch (error) {
|
|
86
|
-
console.error(chalk.red('Failed to start multiplayer server:'), error.message);
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
class MultiplayerServer {
|
|
92
|
-
constructor(port = 8080, isChildProcess = false) {
|
|
93
|
-
this.port = port;
|
|
94
|
-
this.sessions = new Map();
|
|
95
|
-
this.server = http.createServer();
|
|
96
|
-
this.wss = new WebSocket.Server({ server: this.server });
|
|
97
|
-
this.setupEventHandlers();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
setupEventHandlers() {
|
|
101
|
-
this.wss.on('connection', (ws) => {
|
|
102
|
-
let clientId = uuidv4();
|
|
103
|
-
let sessionId = null;
|
|
104
|
-
let clientInfo = {};
|
|
105
|
-
|
|
106
|
-
ws.on('message', (message) => {
|
|
107
|
-
try {
|
|
108
|
-
const data = JSON.parse(message);
|
|
109
|
-
this.handleMessage(ws, clientId, data);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.error('Error parsing message:', error);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
ws.on('close', () => {
|
|
116
|
-
if (sessionId && this.sessions.has(sessionId)) {
|
|
117
|
-
const session = this.sessions.get(sessionId);
|
|
118
|
-
delete session.clients[clientId];
|
|
119
|
-
this.broadcastToSession(sessionId, {
|
|
120
|
-
type: 'agent_left',
|
|
121
|
-
clientId,
|
|
122
|
-
clientInfo
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
if (Object.keys(session.clients).length === 0) {
|
|
126
|
-
this.sessions.delete(sessionId);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
handleMessage(ws, clientId, data) {
|
|
134
|
-
switch (data.type) {
|
|
135
|
-
case 'create_session':
|
|
136
|
-
this.handleCreateSession(ws, clientId, data);
|
|
137
|
-
break;
|
|
138
|
-
case 'join_session':
|
|
139
|
-
this.handleJoinSession(ws, clientId, data);
|
|
140
|
-
break;
|
|
141
|
-
case 'update_work':
|
|
142
|
-
case 'chat':
|
|
143
|
-
case 'task_update':
|
|
144
|
-
this.broadcastToSession(data.sessionId, {
|
|
145
|
-
...data,
|
|
146
|
-
clientId,
|
|
147
|
-
timestamp: new Date().toISOString()
|
|
148
|
-
});
|
|
149
|
-
break;
|
|
150
|
-
default:
|
|
151
|
-
console.log('Unknown message type:', data.type);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
handleCreateSession(ws, clientId, data) {
|
|
156
|
-
const sessionId = uuidv4();
|
|
157
|
-
this.sessions.set(sessionId, {
|
|
158
|
-
id: sessionId,
|
|
159
|
-
clients: {},
|
|
160
|
-
workHistory: [],
|
|
161
|
-
createdAt: new Date().toISOString()
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
this.handleJoinSession(ws, clientId, {
|
|
165
|
-
...data,
|
|
166
|
-
sessionId,
|
|
167
|
-
isHost: true
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
handleJoinSession(ws, clientId, data) {
|
|
172
|
-
const { sessionId, clientInfo } = data;
|
|
173
|
-
|
|
174
|
-
if (!this.sessions.has(sessionId)) {
|
|
175
|
-
ws.send(JSON.stringify({
|
|
176
|
-
type: 'error',
|
|
177
|
-
message: 'Session not found'
|
|
178
|
-
}));
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const session = this.sessions.get(sessionId);
|
|
183
|
-
|
|
184
|
-
if (Object.keys(session.clients).length >= 4) {
|
|
185
|
-
ws.send(JSON.stringify({
|
|
186
|
-
type: 'error',
|
|
187
|
-
message: 'Session is full (max 4 agents)'
|
|
188
|
-
}));
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Add client to session
|
|
193
|
-
session.clients[clientId] = {
|
|
194
|
-
ws,
|
|
195
|
-
clientId,
|
|
196
|
-
clientInfo: {
|
|
197
|
-
...clientInfo,
|
|
198
|
-
joinedAt: new Date().toISOString(),
|
|
199
|
-
isHost: data.isHost || false
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// Send welcome message with session info
|
|
204
|
-
ws.send(JSON.stringify({
|
|
205
|
-
type: 'session_joined',
|
|
206
|
-
sessionId,
|
|
207
|
-
clientId,
|
|
208
|
-
clients: Object.values(session.clients).map(c => c.clientInfo),
|
|
209
|
-
workHistory: session.workHistory
|
|
210
|
-
}));
|
|
211
|
-
|
|
212
|
-
// Notify other clients
|
|
213
|
-
this.broadcastToSession(sessionId, {
|
|
214
|
-
type: 'agent_joined',
|
|
215
|
-
clientId,
|
|
216
|
-
clientInfo: session.clients[clientId].clientInfo
|
|
217
|
-
}, clientId);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
broadcastToSession(sessionId, message, excludeClientId = null) {
|
|
221
|
-
if (!this.sessions.has(sessionId)) return;
|
|
222
|
-
|
|
223
|
-
const session = this.sessions.get(sessionId);
|
|
224
|
-
const messageStr = JSON.stringify(message);
|
|
225
|
-
|
|
226
|
-
Object.entries(session.clients).forEach(([id, client]) => {
|
|
227
|
-
if (id !== excludeClientId && client.ws.readyState === WebSocket.OPEN) {
|
|
228
|
-
client.ws.send(messageStr);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
start() {
|
|
234
|
-
return new Promise((resolve) => {
|
|
235
|
-
this.server.listen(this.port, () => {
|
|
236
|
-
if (!this.isChildProcess) {
|
|
237
|
-
console.log(`Multiplayer server running on ws://localhost:${this.port}`);
|
|
238
|
-
}
|
|
239
|
-
resolve();
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
static async startAsChildProcess(port = 8080) {
|
|
245
|
-
const server = new MultiplayerServer(port, true);
|
|
246
|
-
await server.start();
|
|
247
|
-
// Keep process alive
|
|
248
|
-
process.on('SIGINT', () => process.exit());
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Start server if this file is run directly
|
|
253
|
-
if (require.main === module && process.argv.includes('--server')) {
|
|
254
|
-
const port = parseInt(process.argv[process.argv.indexOf('--port') + 1]) || 8080;
|
|
255
|
-
MultiplayerServer.startAsChildProcess(port);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
class MultiplayerClient extends EventEmitter {
|
|
259
|
-
static TASK_TYPES = {
|
|
260
|
-
RESEARCH: 'research',
|
|
261
|
-
CODE: 'code',
|
|
262
|
-
DEBUG: 'debug',
|
|
263
|
-
DOCUMENT: 'document'
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
static AGENT_ROLES = {
|
|
267
|
-
LEAD: 'Lead',
|
|
268
|
-
DEVELOPER: 'Developer',
|
|
269
|
-
RESEARCHER: 'Researcher',
|
|
270
|
-
REVIEWER: 'Reviewer'
|
|
271
|
-
};
|
|
272
|
-
constructor(options = {}) {
|
|
273
|
-
super();
|
|
274
|
-
this.serverUrl = options.serverUrl || 'ws://localhost:8080';
|
|
275
|
-
this.clientId = options.clientId || uuidv4();
|
|
276
|
-
this.name = options.name || `Agent-${this.clientId.substr(0, 4)}`;
|
|
277
|
-
this.role = options.role || MultiplayerClient.AGENT_ROLES.DEVELOPER;
|
|
278
|
-
this.model = options.model || 'default';
|
|
279
|
-
this.sessionId = options.sessionId || null;
|
|
280
|
-
this.isHost = options.isHost || false;
|
|
281
|
-
this.connected = false;
|
|
282
|
-
this.ws = null;
|
|
283
|
-
this.workHistory = [];
|
|
284
|
-
this.clients = [];
|
|
285
|
-
this.currentTask = null;
|
|
286
|
-
this.taskQueue = [];
|
|
287
|
-
this.pendingTasks = new Map();
|
|
288
|
-
this.taskResults = new Map();
|
|
289
|
-
this.rl = options.rl || readline.createInterface({
|
|
290
|
-
input: process.stdin,
|
|
291
|
-
output: process.stdout
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
// Bind methods
|
|
295
|
-
this.processTaskQueue = this.processTaskQueue.bind(this);
|
|
296
|
-
this.completeTask = this.completeTask.bind(this);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
async connect(serverId = null) {
|
|
300
|
-
return new Promise((resolve, reject) => {
|
|
301
|
-
const connectWithRetry = async () => {
|
|
302
|
-
try {
|
|
303
|
-
// If no server URL is provided, try to start a local server or connect to the specified server ID
|
|
304
|
-
if (this.serverUrl === 'ws://localhost:8080') {
|
|
305
|
-
try {
|
|
306
|
-
this.serverUrl = await ensureServerRunning(8080, serverId);
|
|
307
|
-
} catch (error) {
|
|
308
|
-
console.error(chalk.red('Failed to start local server:'), error.message);
|
|
309
|
-
reject(error);
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
console.log(chalk.blue(`Connecting to ${this.serverUrl}...`));
|
|
315
|
-
this.ws = new WebSocket(this.serverUrl);
|
|
316
|
-
|
|
317
|
-
const connectionTimeout = setTimeout(() => {
|
|
318
|
-
this.ws.terminate();
|
|
319
|
-
reject(new Error('Connection timeout'));
|
|
320
|
-
}, 10000); // 10 seconds timeout
|
|
321
|
-
|
|
322
|
-
this.ws.on('open', () => {
|
|
323
|
-
clearTimeout(connectionTimeout);
|
|
324
|
-
this.connected = true;
|
|
325
|
-
console.log(chalk.green('✅ Connected to multiplayer server'));
|
|
326
|
-
|
|
327
|
-
// Send client info
|
|
328
|
-
this.send({
|
|
329
|
-
type: 'client_info',
|
|
330
|
-
clientId: this.clientId,
|
|
331
|
-
name: this.name,
|
|
332
|
-
role: this.role,
|
|
333
|
-
model: this.model
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
this.emit('connected');
|
|
337
|
-
resolve();
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
this.ws.on('message', (data) => {
|
|
341
|
-
try {
|
|
342
|
-
const message = JSON.parse(data);
|
|
343
|
-
this.handleMessage(message);
|
|
344
|
-
} catch (error) {
|
|
345
|
-
console.error('Error parsing message:', error);
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
this.ws.on('close', () => {
|
|
350
|
-
this.connected = false;
|
|
351
|
-
this.emit('disconnected');
|
|
352
|
-
console.log(chalk.yellow('\n🔌 Disconnected from multiplayer server'));
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
this.ws.on('error', (error) => {
|
|
356
|
-
clearTimeout(connectionTimeout);
|
|
357
|
-
console.error('WebSocket error:', error.message);
|
|
358
|
-
if (!this.connected) {
|
|
359
|
-
reject(error);
|
|
360
|
-
}
|
|
361
|
-
this.emit('error', error);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
} catch (error) {
|
|
365
|
-
console.error('Connection error:', error.message);
|
|
366
|
-
reject(error);
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
connectWithRetry();
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
handleMessage(message) {
|
|
375
|
-
switch (message.type) {
|
|
376
|
-
case 'session_joined':
|
|
377
|
-
this.handleSessionJoined(message);
|
|
378
|
-
break;
|
|
379
|
-
case 'session_created':
|
|
380
|
-
this.sessionId = message.sessionId;
|
|
381
|
-
this.clientId = message.clientId;
|
|
382
|
-
this.isHost = message.isHost;
|
|
383
|
-
this.emit('session_created', message);
|
|
384
|
-
break;
|
|
385
|
-
case 'client_updated':
|
|
386
|
-
this.handleClientUpdated(message);
|
|
387
|
-
break;
|
|
388
|
-
case 'agent_joined':
|
|
389
|
-
this.handleAgentJoined(message);
|
|
390
|
-
break;
|
|
391
|
-
case 'agent_left':
|
|
392
|
-
this.handleAgentLeft(message);
|
|
393
|
-
break;
|
|
394
|
-
case 'chat':
|
|
395
|
-
this.handleChatMessage(message);
|
|
396
|
-
break;
|
|
397
|
-
case 'work_update':
|
|
398
|
-
this.handleWorkUpdate(message);
|
|
399
|
-
break;
|
|
400
|
-
case 'task_assigned':
|
|
401
|
-
this.handleTaskAssigned(message);
|
|
402
|
-
break;
|
|
403
|
-
case 'task_completed':
|
|
404
|
-
this.handleTaskCompleted(message);
|
|
405
|
-
break;
|
|
406
|
-
case 'task_update':
|
|
407
|
-
this.handleTaskUpdate(message);
|
|
408
|
-
break;
|
|
409
|
-
case 'error':
|
|
410
|
-
console.error('Error:', message.message);
|
|
411
|
-
break;
|
|
412
|
-
default:
|
|
413
|
-
console.log('Unknown message type:', message.type);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
handleSessionJoined(message) {
|
|
418
|
-
this.sessionId = message.sessionId;
|
|
419
|
-
this.clients = message.clients || [];
|
|
420
|
-
this.workHistory = message.workHistory || [];
|
|
421
|
-
|
|
422
|
-
// Update host status if this client is the host
|
|
423
|
-
if (message.isHost !== undefined) {
|
|
424
|
-
this.isHost = message.isHost;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Process any pending tasks
|
|
428
|
-
if (message.pendingTasks && message.pendingTasks.length > 0) {
|
|
429
|
-
message.pendingTasks.forEach(task => {
|
|
430
|
-
this.emit('task_assigned', task);
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Emit session_joined with host status
|
|
435
|
-
this.emit('session_joined', {
|
|
436
|
-
...message,
|
|
437
|
-
isHost: this.isHost
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
handleClientUpdated(data) {
|
|
442
|
-
const clientIndex = this.clients.findIndex(c => c.id === data.clientId);
|
|
443
|
-
if (clientIndex !== -1) {
|
|
444
|
-
this.clients[clientIndex] = { ...this.clients[clientIndex], ...data.clientInfo };
|
|
445
|
-
}
|
|
446
|
-
this.emit('client_updated', data);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
handleAgentJoined(message) {
|
|
450
|
-
this.clients.push({
|
|
451
|
-
...message.clientInfo,
|
|
452
|
-
clientId: message.clientId
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
if (message.clientId !== this.clientId) {
|
|
456
|
-
console.log(chalk.blue(`\n${message.clientInfo.name || 'Agent'} has joined the session`));
|
|
457
|
-
}
|
|
458
|
-
this.emit('agent_joined', message);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
handleAgentLeft(message) {
|
|
462
|
-
const clientIndex = this.clients.findIndex(c => c.clientId === message.clientId);
|
|
463
|
-
if (clientIndex !== -1) {
|
|
464
|
-
console.log(chalk.yellow(`\n${this.clients[clientIndex].name || 'Agent'} has left the session`));
|
|
465
|
-
this.clients.splice(clientIndex, 1);
|
|
466
|
-
}
|
|
467
|
-
this.emit('agent_left', message);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
handleChatMessage(message) {
|
|
471
|
-
if (message.clientId === this.clientId) return;
|
|
472
|
-
|
|
473
|
-
const sender = this.clients.find(c => c.clientId === message.clientId) || { name: 'Unknown' };
|
|
474
|
-
console.log(chalk.cyan(`\n[${sender.name}]: ${message.content}`));
|
|
475
|
-
this.emit('chat', message);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
handleWorkUpdate(message) {
|
|
479
|
-
this.workHistory.push({
|
|
480
|
-
...message.work,
|
|
481
|
-
timestamp: message.timestamp,
|
|
482
|
-
clientId: message.clientId
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
const client = this.clients.find(c => c.clientId === message.clientId) || { name: 'Unknown' };
|
|
486
|
-
console.log(chalk.green(`\n[Work Update] ${client.name}: ${message.work.description}`));
|
|
487
|
-
this.emit('work_updated', message);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
async handleTaskAssigned(task) {
|
|
491
|
-
console.log(chalk.magenta(`\n📋 New Task: ${task.prompt}`));
|
|
492
|
-
console.log(chalk.dim(`Type: ${task.taskType} | Assigned by: ${this.getClientName(task.assignedBy)}\n`));
|
|
493
|
-
|
|
494
|
-
// Store task in pending tasks
|
|
495
|
-
this.pendingTasks.set(task.id, {
|
|
496
|
-
...task,
|
|
497
|
-
status: 'pending',
|
|
498
|
-
assignedAt: new Date().toISOString()
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
// Add to task queue if it's assigned to this client or to all
|
|
502
|
-
if (!task.assignedTo || task.assignedTo === this.clientId) {
|
|
503
|
-
this.taskQueue.push(task);
|
|
504
|
-
this.processTaskQueue();
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
this.emit('task_assigned', task);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
async processTaskQueue() {
|
|
511
|
-
if (this.currentTask || this.taskQueue.length === 0) return;
|
|
512
|
-
|
|
513
|
-
this.currentTask = this.taskQueue.shift();
|
|
514
|
-
const taskId = this.currentTask.id;
|
|
515
|
-
|
|
516
|
-
try {
|
|
517
|
-
// Update task status to in-progress
|
|
518
|
-
this.pendingTasks.set(taskId, {
|
|
519
|
-
...this.pendingTasks.get(taskId),
|
|
520
|
-
status: 'in_progress',
|
|
521
|
-
startedAt: new Date().toISOString()
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
// Notify others about task progress
|
|
525
|
-
this.send({
|
|
526
|
-
type: 'task_progress',
|
|
527
|
-
sessionId: this.sessionId,
|
|
528
|
-
taskId,
|
|
529
|
-
status: 'in_progress',
|
|
530
|
-
progress: 0,
|
|
531
|
-
timestamp: new Date().toISOString()
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
console.log(chalk.blue(`\n🔄 [${this.role}] Working on: ${this.currentTask.prompt}`));
|
|
535
|
-
|
|
536
|
-
// Process the task using the completeTask method
|
|
537
|
-
const completedTask = await this.completeTask(taskId);
|
|
538
|
-
|
|
539
|
-
if (completedTask) {
|
|
540
|
-
// Task completed successfully
|
|
541
|
-
this.send({
|
|
542
|
-
type: 'task_progress',
|
|
543
|
-
sessionId: this.sessionId,
|
|
544
|
-
taskId,
|
|
545
|
-
status: 'completed',
|
|
546
|
-
progress: 100,
|
|
547
|
-
timestamp: new Date().toISOString()
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
console.log(chalk.green(`\n✅ [${this.role}] Completed task: ${this.currentTask.prompt}`));
|
|
551
|
-
}
|
|
552
|
-
if (this.taskQueue.length > 0) {
|
|
553
|
-
this.processTaskQueue();
|
|
554
|
-
}
|
|
555
|
-
} catch (error) {
|
|
556
|
-
console.error('Error processing task:', error);
|
|
557
|
-
this.send({
|
|
558
|
-
type: 'task_failed',
|
|
559
|
-
sessionId: this.sessionId,
|
|
560
|
-
taskId,
|
|
561
|
-
error: error.message,
|
|
562
|
-
timestamp: new Date().toISOString()
|
|
563
|
-
});
|
|
564
|
-
} finally {
|
|
565
|
-
this.currentTask = null;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
async completeTask(taskId) {
|
|
570
|
-
const task = this.pendingTasks.get(taskId);
|
|
571
|
-
if (!task) {
|
|
572
|
-
console.error('Task not found:', taskId);
|
|
573
|
-
return null;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
try {
|
|
577
|
-
// Process the task using the same logic as the normal mode
|
|
578
|
-
const { processQuery } = require('./agi-cli');
|
|
579
|
-
const result = await processQuery(task.prompt, [], this.model);
|
|
580
|
-
|
|
581
|
-
// Update task status
|
|
582
|
-
const completedTask = {
|
|
583
|
-
...task,
|
|
584
|
-
status: 'completed',
|
|
585
|
-
completedAt: new Date().toISOString(),
|
|
586
|
-
result: typeof result === 'string' ? result : JSON.stringify(result, null, 2)
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
this.pendingTasks.set(taskId, completedTask);
|
|
590
|
-
this.taskResults.set(taskId, completedTask.result);
|
|
591
|
-
|
|
592
|
-
// Notify server and other clients
|
|
593
|
-
this.send({
|
|
594
|
-
type: 'task_completed',
|
|
595
|
-
sessionId: this.sessionId,
|
|
596
|
-
taskId,
|
|
597
|
-
result: completedTask.result,
|
|
598
|
-
timestamp: new Date().toISOString()
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
console.log(chalk.green(`\n✅ [${this.role}] Completed: ${task.prompt}`));
|
|
602
|
-
this.emit('task_completed', completedTask);
|
|
603
|
-
|
|
604
|
-
return completedTask;
|
|
605
|
-
} catch (error) {
|
|
606
|
-
console.error('Error completing task:', error);
|
|
607
|
-
|
|
608
|
-
// Update task with error status
|
|
609
|
-
const failedTask = {
|
|
610
|
-
...task,
|
|
611
|
-
status: 'failed',
|
|
612
|
-
completedAt: new Date().toISOString(),
|
|
613
|
-
error: error.message
|
|
614
|
-
};
|
|
615
|
-
|
|
616
|
-
this.pendingTasks.set(taskId, failedTask);
|
|
617
|
-
this.emit('task_failed', { taskId, error: error.message });
|
|
618
|
-
|
|
619
|
-
return null;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
handleTaskCompleted(data) {
|
|
624
|
-
const task = this.pendingTasks.get(data.taskId);
|
|
625
|
-
if (!task) {
|
|
626
|
-
console.error('Task not found for completion:', data.taskId);
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
console.log(chalk.green(`\n✅ Task completed by all agents!`));
|
|
631
|
-
console.log(chalk.blue(`Task: ${task.prompt || 'Unknown task'}`));
|
|
632
|
-
|
|
633
|
-
if (data.results && data.results.length > 0) {
|
|
634
|
-
console.log(chalk.cyan('Results:'));
|
|
635
|
-
data.results.forEach(result => {
|
|
636
|
-
console.log(chalk.yellow(`- ${this.getClientName(result.clientId) || 'Unknown'}:`));
|
|
637
|
-
console.log(result.result);
|
|
638
|
-
});
|
|
639
|
-
} else if (data.result) {
|
|
640
|
-
console.log(chalk.cyan('Result:'));
|
|
641
|
-
console.log(data.result);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// Update the task with the final result
|
|
645
|
-
const completedTask = {
|
|
646
|
-
...task,
|
|
647
|
-
status: 'completed',
|
|
648
|
-
completedAt: new Date().toISOString(),
|
|
649
|
-
result: data.result || 'No result provided'
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
this.pendingTasks.set(data.taskId, completedTask);
|
|
653
|
-
this.taskResults.set(data.taskId, completedTask.result);
|
|
654
|
-
|
|
655
|
-
this.emit('task_completed', completedTask);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
async updateWork(work) {
|
|
659
|
-
const workItem = {
|
|
660
|
-
id: uuidv4(),
|
|
661
|
-
...work,
|
|
662
|
-
clientId: this.clientId,
|
|
663
|
-
clientName: this.name,
|
|
664
|
-
timestamp: new Date().toISOString(),
|
|
665
|
-
status: work.status || 'in_progress'
|
|
666
|
-
};
|
|
667
|
-
|
|
668
|
-
this.workHistory.push(workItem);
|
|
669
|
-
|
|
670
|
-
// Keep only the last 10 work items
|
|
671
|
-
if (this.workHistory.length > 10) {
|
|
672
|
-
this.workHistory = this.workHistory.slice(-10);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
this.send({
|
|
676
|
-
type: 'work_update',
|
|
677
|
-
sessionId: this.sessionId,
|
|
678
|
-
...workItem
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
this.emit('work_updated', workItem);
|
|
682
|
-
return workItem;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
async updateClientInfo(updates) {
|
|
686
|
-
// Update local client info
|
|
687
|
-
Object.assign(this, updates);
|
|
688
|
-
|
|
689
|
-
// Notify server about the update
|
|
690
|
-
if (this.connected) {
|
|
691
|
-
this.send({
|
|
692
|
-
type: 'client_update',
|
|
693
|
-
sessionId: this.sessionId,
|
|
694
|
-
clientId: this.clientId,
|
|
695
|
-
updates: {
|
|
696
|
-
name: this.name,
|
|
697
|
-
role: this.role,
|
|
698
|
-
model: this.model,
|
|
699
|
-
updatedAt: new Date().toISOString()
|
|
700
|
-
},
|
|
701
|
-
timestamp: new Date().toISOString()
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
assignTask(task, assigneeClientId) {
|
|
707
|
-
this.send({
|
|
708
|
-
type: 'task_assigned',
|
|
709
|
-
sessionId: this.sessionId,
|
|
710
|
-
task: {
|
|
711
|
-
...task,
|
|
712
|
-
assignedBy: this.clientId,
|
|
713
|
-
assignedAt: new Date().toISOString()
|
|
714
|
-
},
|
|
715
|
-
assignedTo: assigneeClientId
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
send(message) {
|
|
720
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
721
|
-
this.ws.send(JSON.stringify({
|
|
722
|
-
...message,
|
|
723
|
-
clientId: this.clientId,
|
|
724
|
-
timestamp: new Date().toISOString()
|
|
725
|
-
}));
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
disconnect() {
|
|
730
|
-
if (this.ws) {
|
|
731
|
-
this.ws.close();
|
|
732
|
-
this.connected = false;
|
|
733
|
-
this.ws = null;
|
|
734
|
-
}
|
|
735
|
-
if (this.rl) {
|
|
736
|
-
this.rl.close();
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
sendChatMessage(message) {
|
|
741
|
-
if (!this.connected || !this.ws) {
|
|
742
|
-
console.error('Not connected to server');
|
|
743
|
-
return false;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
try {
|
|
747
|
-
const chatMessage = {
|
|
748
|
-
type: 'chat_message',
|
|
749
|
-
sessionId: this.sessionId,
|
|
750
|
-
clientId: this.clientId,
|
|
751
|
-
sender: this.name,
|
|
752
|
-
message: message,
|
|
753
|
-
timestamp: new Date().toISOString()
|
|
754
|
-
};
|
|
755
|
-
|
|
756
|
-
this.ws.send(JSON.stringify(chatMessage));
|
|
757
|
-
return true;
|
|
758
|
-
} catch (error) {
|
|
759
|
-
console.error('Error sending chat message:', error);
|
|
760
|
-
return false;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
getClientName(clientId) {
|
|
765
|
-
if (!clientId) return 'Unknown';
|
|
766
|
-
const client = this.clients.find(c => c.clientId === clientId || c.id === clientId);
|
|
767
|
-
return client ? (client.name || client.clientInfo?.name || 'Unknown') : 'Unknown';
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
async createSession(sessionId = null) {
|
|
771
|
-
if (!this.connected) {
|
|
772
|
-
await this.connect();
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
this.sessionId = sessionId || uuidv4();
|
|
776
|
-
this.isHost = true;
|
|
777
|
-
|
|
778
|
-
return new Promise((resolve, reject) => {
|
|
779
|
-
if (!this.ws) {
|
|
780
|
-
reject(new Error('Not connected to server'));
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
const onSessionCreated = (data) => {
|
|
785
|
-
if (data.sessionId === this.sessionId) {
|
|
786
|
-
this.off('session_created', onSessionCreated);
|
|
787
|
-
this.off('error', onError);
|
|
788
|
-
console.log(`✅ Session ${this.sessionId} created successfully`);
|
|
789
|
-
resolve(this.sessionId);
|
|
790
|
-
}
|
|
791
|
-
};
|
|
792
|
-
|
|
793
|
-
const onError = (error) => {
|
|
794
|
-
this.off('session_created', onSessionCreated);
|
|
795
|
-
this.off('error', onError);
|
|
796
|
-
console.error('Error creating session:', error.message);
|
|
797
|
-
reject(error);
|
|
798
|
-
};
|
|
799
|
-
|
|
800
|
-
this.once('session_created', onSessionCreated);
|
|
801
|
-
this.once('error', onError);
|
|
802
|
-
|
|
803
|
-
console.log(`Creating new session ${this.sessionId}...`);
|
|
804
|
-
this.send({
|
|
805
|
-
type: 'create_session',
|
|
806
|
-
sessionId: this.sessionId,
|
|
807
|
-
clientInfo: {
|
|
808
|
-
name: this.name,
|
|
809
|
-
role: this.role,
|
|
810
|
-
model: this.model
|
|
811
|
-
}
|
|
812
|
-
});
|
|
813
|
-
});
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
async joinSession(sessionId, createIfNotExists = true) {
|
|
817
|
-
if (!sessionId) {
|
|
818
|
-
throw new Error('Session ID is required');
|
|
819
|
-
}
|
|
820
|
-
if (!this.connected) {
|
|
821
|
-
await this.connect();
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
return new Promise((resolve, reject) => {
|
|
825
|
-
if (!this.ws) {
|
|
826
|
-
reject(new Error('Not connected to server'));
|
|
827
|
-
return;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
const timeout = setTimeout(() => {
|
|
831
|
-
this.off('session_joined', onSessionJoined);
|
|
832
|
-
this.off('session_created', onSessionCreated);
|
|
833
|
-
this.off('error', onError);
|
|
834
|
-
reject(new Error('Session join timed out'));
|
|
835
|
-
}, 15000); // Increased timeout to 15 seconds
|
|
836
|
-
|
|
837
|
-
const onSessionJoined = (data) => {
|
|
838
|
-
if (data.sessionId === sessionId) {
|
|
839
|
-
clearTimeout(timeout);
|
|
840
|
-
this.sessionId = sessionId;
|
|
841
|
-
this.isHost = data.isHost || false;
|
|
842
|
-
this.emit('session_joined', data);
|
|
843
|
-
this.off('session_joined', onSessionJoined);
|
|
844
|
-
this.off('session_created', onSessionCreated);
|
|
845
|
-
this.off('error', onError);
|
|
846
|
-
console.log(`✅ Successfully joined session ${sessionId}`);
|
|
847
|
-
resolve(data);
|
|
848
|
-
}
|
|
849
|
-
};
|
|
850
|
-
|
|
851
|
-
const onSessionCreated = (data) => {
|
|
852
|
-
if (data.sessionId === sessionId) {
|
|
853
|
-
clearTimeout(timeout);
|
|
854
|
-
this.sessionId = sessionId;
|
|
855
|
-
this.isHost = true;
|
|
856
|
-
this.emit('session_joined', { ...data, isHost: true });
|
|
857
|
-
this.off('session_joined', onSessionJoined);
|
|
858
|
-
this.off('session_created', onSessionCreated);
|
|
859
|
-
this.off('error', onError);
|
|
860
|
-
console.log(`✅ Created and joined new session ${sessionId}`);
|
|
861
|
-
resolve({ ...data, isHost: true });
|
|
862
|
-
}
|
|
863
|
-
};
|
|
864
|
-
|
|
865
|
-
const onError = (error) => {
|
|
866
|
-
if (error.message === 'Session not found' && createIfNotExists) {
|
|
867
|
-
// If session doesn't exist and we're allowed to create it
|
|
868
|
-
this.off('session_joined', onSessionJoined);
|
|
869
|
-
this.off('error', onError);
|
|
870
|
-
console.log(`Session ${sessionId} not found, creating new session...`);
|
|
871
|
-
this.createSession(sessionId)
|
|
872
|
-
.then(() => {
|
|
873
|
-
this.once('session_created', onSessionCreated);
|
|
874
|
-
this.once('error', onError);
|
|
875
|
-
})
|
|
876
|
-
.catch(reject);
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
clearTimeout(timeout);
|
|
881
|
-
this.off('session_joined', onSessionJoined);
|
|
882
|
-
this.off('session_created', onSessionCreated);
|
|
883
|
-
this.off('error', onError);
|
|
884
|
-
console.error('Error joining session:', error.message);
|
|
885
|
-
reject(error);
|
|
886
|
-
};
|
|
887
|
-
|
|
888
|
-
this.on('session_joined', onSessionJoined);
|
|
889
|
-
this.on('error', onError);
|
|
890
|
-
|
|
891
|
-
console.log(`Attempting to join session ${sessionId}...`);
|
|
892
|
-
this.send({
|
|
893
|
-
type: 'join_session',
|
|
894
|
-
sessionId: sessionId,
|
|
895
|
-
clientId: this.clientId,
|
|
896
|
-
name: this.name,
|
|
897
|
-
role: this.role
|
|
898
|
-
});
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
// Export MultiplayerClient and MultiplayerServer as direct properties of module.exports
|
|
904
|
-
module.exports.MultiplayerClient = MultiplayerClient;
|
|
905
|
-
module.exports.MultiplayerServer = MultiplayerServer;
|