sam-coder-cli 1.0.15 ā 1.0.17
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/multiplayer-client.js +224 -79
- package/package.json +1 -1
|
@@ -235,59 +235,61 @@ class MultiplayerClient extends EventEmitter {
|
|
|
235
235
|
this.clients = [];
|
|
236
236
|
this.currentTask = null;
|
|
237
237
|
this.taskQueue = [];
|
|
238
|
+
this.pendingTasks = new Map();
|
|
239
|
+
this.taskResults = new Map();
|
|
238
240
|
this.rl = options.rl || readline.createInterface({
|
|
239
241
|
input: process.stdin,
|
|
240
242
|
output: process.stdout
|
|
241
243
|
});
|
|
244
|
+
|
|
245
|
+
// Bind methods
|
|
246
|
+
this.processTaskQueue = this.processTaskQueue.bind(this);
|
|
247
|
+
this.completeTask = this.completeTask.bind(this);
|
|
242
248
|
}
|
|
243
249
|
|
|
244
250
|
async connect() {
|
|
245
251
|
try {
|
|
246
|
-
// Ensure server is running
|
|
247
|
-
|
|
248
|
-
const serverPort = url.port || 8080;
|
|
249
|
-
this.serverUrl = await ensureServerRunning(serverPort);
|
|
252
|
+
// Ensure server is running
|
|
253
|
+
this.serverUrl = await ensureServerRunning(this.port);
|
|
250
254
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
this.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
this.send({
|
|
258
|
-
type: 'client_info',
|
|
259
|
-
clientInfo: {
|
|
260
|
-
name: this.name || `Agent-${Math.random().toString(36).substr(2, 4)}`,
|
|
261
|
-
role: this.role || 'Assistant',
|
|
262
|
-
model: this.model || 'default'
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
this.emit('connected');
|
|
266
|
-
resolve();
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
this.ws.on('message', (data) => {
|
|
270
|
-
try {
|
|
271
|
-
const message = JSON.parse(data);
|
|
272
|
-
this.handleMessage(message);
|
|
273
|
-
} catch (error) {
|
|
274
|
-
console.error('Error parsing message:', error);
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
this.ws.on('close', () => {
|
|
279
|
-
this.connected = false;
|
|
280
|
-
this.emit('disconnected');
|
|
281
|
-
});
|
|
255
|
+
this.ws = new WebSocket(this.serverUrl);
|
|
256
|
+
|
|
257
|
+
this.ws.on('open', () => {
|
|
258
|
+
this.connected = true;
|
|
259
|
+
this.emit('connected');
|
|
260
|
+
console.log(chalk.green('ā
Connected to multiplayer server'));
|
|
282
261
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
262
|
+
// Send client info immediately after connection
|
|
263
|
+
this.updateClientInfo({
|
|
264
|
+
name: this.name,
|
|
265
|
+
role: this.role,
|
|
266
|
+
model: this.model
|
|
286
267
|
});
|
|
287
268
|
});
|
|
269
|
+
|
|
270
|
+
this.ws.on('message', (data) => {
|
|
271
|
+
try {
|
|
272
|
+
const message = JSON.parse(data);
|
|
273
|
+
this.handleMessage(message);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error('Error parsing message:', error);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
this.ws.on('close', () => {
|
|
280
|
+
this.connected = false;
|
|
281
|
+
this.emit('disconnected');
|
|
282
|
+
console.log(chalk.yellow('\nš Disconnected from multiplayer server'));
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
this.ws.on('error', (error) => {
|
|
286
|
+
console.error('WebSocket error:', error);
|
|
287
|
+
this.emit('error', error);
|
|
288
|
+
});
|
|
289
|
+
|
|
288
290
|
} catch (error) {
|
|
289
|
-
console.error('Failed to
|
|
290
|
-
|
|
291
|
+
console.error('Failed to connect to server:', error);
|
|
292
|
+
this.emit('error', error);
|
|
291
293
|
}
|
|
292
294
|
}
|
|
293
295
|
|
|
@@ -398,13 +400,22 @@ class MultiplayerClient extends EventEmitter {
|
|
|
398
400
|
this.emit('work_updated', message);
|
|
399
401
|
}
|
|
400
402
|
|
|
401
|
-
handleTaskAssigned(task) {
|
|
402
|
-
console.log(`\n New
|
|
403
|
-
console.log(`Type: ${task.taskType} | Assigned by: ${this.getClientName(task.assignedBy)}\n`);
|
|
403
|
+
async handleTaskAssigned(task) {
|
|
404
|
+
console.log(chalk.magenta(`\nš New Task: ${task.prompt}`));
|
|
405
|
+
console.log(chalk.dim(`Type: ${task.taskType} | Assigned by: ${this.getClientName(task.assignedBy)}\n`));
|
|
406
|
+
|
|
407
|
+
// Store task in pending tasks
|
|
408
|
+
this.pendingTasks.set(task.id, {
|
|
409
|
+
...task,
|
|
410
|
+
status: 'pending',
|
|
411
|
+
assignedAt: new Date().toISOString()
|
|
412
|
+
});
|
|
404
413
|
|
|
405
|
-
// Add to task queue
|
|
406
|
-
this.
|
|
407
|
-
|
|
414
|
+
// Add to task queue if it's assigned to this client or to all
|
|
415
|
+
if (!task.assignedTo || task.assignedTo === this.clientId) {
|
|
416
|
+
this.taskQueue.push(task);
|
|
417
|
+
this.processTaskQueue();
|
|
418
|
+
}
|
|
408
419
|
|
|
409
420
|
this.emit('task_assigned', task);
|
|
410
421
|
}
|
|
@@ -413,68 +424,196 @@ class MultiplayerClient extends EventEmitter {
|
|
|
413
424
|
if (this.currentTask || this.taskQueue.length === 0) return;
|
|
414
425
|
|
|
415
426
|
this.currentTask = this.taskQueue.shift();
|
|
427
|
+
const taskId = this.currentTask.id;
|
|
416
428
|
|
|
417
429
|
try {
|
|
418
|
-
//
|
|
419
|
-
|
|
430
|
+
// Update task status to in-progress
|
|
431
|
+
this.pendingTasks.set(taskId, {
|
|
432
|
+
...this.pendingTasks.get(taskId),
|
|
433
|
+
status: 'in_progress',
|
|
434
|
+
startedAt: new Date().toISOString()
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// Notify others about task progress
|
|
438
|
+
this.send({
|
|
439
|
+
type: 'task_progress',
|
|
440
|
+
sessionId: this.sessionId,
|
|
441
|
+
taskId,
|
|
442
|
+
status: 'in_progress',
|
|
443
|
+
progress: 0,
|
|
444
|
+
timestamp: new Date().toISOString()
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
console.log(chalk.blue(`\nš [${this.role}] Working on: ${this.currentTask.prompt}`));
|
|
420
448
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
449
|
+
// Process the task using the completeTask method
|
|
450
|
+
const completedTask = await this.completeTask(taskId);
|
|
424
451
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
452
|
+
if (completedTask) {
|
|
453
|
+
// Task completed successfully
|
|
454
|
+
this.send({
|
|
455
|
+
type: 'task_progress',
|
|
456
|
+
sessionId: this.sessionId,
|
|
457
|
+
taskId,
|
|
458
|
+
status: 'completed',
|
|
459
|
+
progress: 100,
|
|
460
|
+
timestamp: new Date().toISOString()
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
console.log(chalk.green(`\nā
[${this.role}] Completed task: ${this.currentTask.prompt}`));
|
|
464
|
+
}
|
|
465
|
+
if (this.taskQueue.length > 0) {
|
|
466
|
+
this.processTaskQueue();
|
|
467
|
+
}
|
|
468
|
+
} catch (error) {
|
|
469
|
+
console.error('Error processing task:', error);
|
|
470
|
+
this.send({
|
|
471
|
+
type: 'task_failed',
|
|
472
|
+
sessionId: this.sessionId,
|
|
473
|
+
taskId,
|
|
474
|
+
error: error.message,
|
|
428
475
|
timestamp: new Date().toISOString()
|
|
476
|
+
});
|
|
477
|
+
} finally {
|
|
478
|
+
this.currentTask = null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async completeTask(taskId) {
|
|
483
|
+
const task = this.pendingTasks.get(taskId);
|
|
484
|
+
if (!task) {
|
|
485
|
+
console.error('Task not found:', taskId);
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
try {
|
|
490
|
+
// Process the task using the same logic as the normal mode
|
|
491
|
+
const { processQuery } = require('./agi-cli');
|
|
492
|
+
const result = await processQuery(task.prompt, [], this.model);
|
|
493
|
+
|
|
494
|
+
// Update task status
|
|
495
|
+
const completedTask = {
|
|
496
|
+
...task,
|
|
497
|
+
status: 'completed',
|
|
498
|
+
completedAt: new Date().toISOString(),
|
|
499
|
+
result: typeof result === 'string' ? result : JSON.stringify(result, null, 2)
|
|
429
500
|
};
|
|
430
501
|
|
|
431
|
-
|
|
502
|
+
this.pendingTasks.set(taskId, completedTask);
|
|
503
|
+
this.taskResults.set(taskId, completedTask.result);
|
|
504
|
+
|
|
505
|
+
// Notify server and other clients
|
|
432
506
|
this.send({
|
|
433
|
-
type: '
|
|
507
|
+
type: 'task_completed',
|
|
434
508
|
sessionId: this.sessionId,
|
|
435
|
-
taskId
|
|
436
|
-
result:
|
|
437
|
-
timestamp:
|
|
509
|
+
taskId,
|
|
510
|
+
result: completedTask.result,
|
|
511
|
+
timestamp: new Date().toISOString()
|
|
438
512
|
});
|
|
439
513
|
|
|
440
|
-
console.log(
|
|
514
|
+
console.log(chalk.green(`\nā
[${this.role}] Completed: ${task.prompt}`));
|
|
515
|
+
this.emit('task_completed', completedTask);
|
|
441
516
|
|
|
517
|
+
return completedTask;
|
|
442
518
|
} catch (error) {
|
|
443
|
-
console.error('Error
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
519
|
+
console.error('Error completing task:', error);
|
|
520
|
+
|
|
521
|
+
// Update task with error status
|
|
522
|
+
const failedTask = {
|
|
523
|
+
...task,
|
|
524
|
+
status: 'failed',
|
|
525
|
+
completedAt: new Date().toISOString(),
|
|
526
|
+
error: error.message
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
this.pendingTasks.set(taskId, failedTask);
|
|
530
|
+
this.emit('task_failed', { taskId, error: error.message });
|
|
531
|
+
|
|
532
|
+
return null;
|
|
450
533
|
}
|
|
451
534
|
}
|
|
452
535
|
|
|
453
536
|
handleTaskCompleted(data) {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
537
|
+
const task = this.pendingTasks.get(data.taskId);
|
|
538
|
+
if (!task) {
|
|
539
|
+
console.error('Task not found for completion:', data.taskId);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
457
542
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
});
|
|
543
|
+
console.log(chalk.green(`\nā
Task completed by all agents!`));
|
|
544
|
+
console.log(chalk.blue(`Task: ${task.prompt || 'Unknown task'}`));
|
|
461
545
|
|
|
462
|
-
|
|
546
|
+
if (data.results && data.results.length > 0) {
|
|
547
|
+
console.log(chalk.cyan('Results:'));
|
|
548
|
+
data.results.forEach(result => {
|
|
549
|
+
console.log(chalk.yellow(`- ${this.getClientName(result.clientId) || 'Unknown'}:`));
|
|
550
|
+
console.log(result.result);
|
|
551
|
+
});
|
|
552
|
+
} else if (data.result) {
|
|
553
|
+
console.log(chalk.cyan('Result:'));
|
|
554
|
+
console.log(data.result);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Update the task with the final result
|
|
558
|
+
const completedTask = {
|
|
559
|
+
...task,
|
|
560
|
+
status: 'completed',
|
|
561
|
+
completedAt: new Date().toISOString(),
|
|
562
|
+
result: data.result || 'No result provided'
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
this.pendingTasks.set(data.taskId, completedTask);
|
|
566
|
+
this.taskResults.set(data.taskId, completedTask.result);
|
|
567
|
+
|
|
568
|
+
this.emit('task_completed', completedTask);
|
|
463
569
|
}
|
|
464
570
|
|
|
465
|
-
updateWork(work) {
|
|
571
|
+
async updateWork(work) {
|
|
466
572
|
const workItem = {
|
|
573
|
+
id: uuidv4(),
|
|
467
574
|
...work,
|
|
468
575
|
clientId: this.clientId,
|
|
469
|
-
|
|
470
|
-
timestamp: new Date().toISOString()
|
|
576
|
+
clientName: this.name,
|
|
577
|
+
timestamp: new Date().toISOString(),
|
|
578
|
+
status: work.status || 'in_progress'
|
|
471
579
|
};
|
|
472
|
-
|
|
580
|
+
|
|
581
|
+
this.workHistory.push(workItem);
|
|
582
|
+
|
|
583
|
+
// Keep only the last 10 work items
|
|
584
|
+
if (this.workHistory.length > 10) {
|
|
585
|
+
this.workHistory = this.workHistory.slice(-10);
|
|
586
|
+
}
|
|
587
|
+
|
|
473
588
|
this.send({
|
|
474
589
|
type: 'work_update',
|
|
475
590
|
sessionId: this.sessionId,
|
|
476
|
-
|
|
591
|
+
...workItem
|
|
477
592
|
});
|
|
593
|
+
|
|
594
|
+
this.emit('work_updated', workItem);
|
|
595
|
+
return workItem;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
async updateClientInfo(updates) {
|
|
599
|
+
// Update local client info
|
|
600
|
+
Object.assign(this, updates);
|
|
601
|
+
|
|
602
|
+
// Notify server about the update
|
|
603
|
+
if (this.connected) {
|
|
604
|
+
this.send({
|
|
605
|
+
type: 'client_update',
|
|
606
|
+
sessionId: this.sessionId,
|
|
607
|
+
clientId: this.clientId,
|
|
608
|
+
updates: {
|
|
609
|
+
name: this.name,
|
|
610
|
+
role: this.role,
|
|
611
|
+
model: this.model,
|
|
612
|
+
updatedAt: new Date().toISOString()
|
|
613
|
+
},
|
|
614
|
+
timestamp: new Date().toISOString()
|
|
615
|
+
});
|
|
616
|
+
}
|
|
478
617
|
}
|
|
479
618
|
|
|
480
619
|
assignTask(task, assigneeClientId) {
|
|
@@ -505,6 +644,12 @@ class MultiplayerClient extends EventEmitter {
|
|
|
505
644
|
this.ws.close();
|
|
506
645
|
}
|
|
507
646
|
}
|
|
647
|
+
|
|
648
|
+
getClientName(clientId) {
|
|
649
|
+
if (!clientId) return 'Unknown';
|
|
650
|
+
const client = this.clients.find(c => c.clientId === clientId || c.id === clientId);
|
|
651
|
+
return client ? (client.name || client.clientInfo?.name || 'Unknown') : 'Unknown';
|
|
652
|
+
}
|
|
508
653
|
}
|
|
509
654
|
|
|
510
655
|
module.exports = MultiplayerClient;
|