sam-coder-cli 1.0.14 → 1.0.15
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 +134 -63
- package/bin/multiplayer-mode.js +217 -114
- package/bin/multiplayer-server.js +155 -56
- package/package.json +1 -1
|
@@ -207,17 +207,34 @@ if (require.main === module && process.argv.includes('--server')) {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
class MultiplayerClient extends EventEmitter {
|
|
210
|
+
static TASK_TYPES = {
|
|
211
|
+
RESEARCH: 'research',
|
|
212
|
+
CODE: 'code',
|
|
213
|
+
DEBUG: 'debug',
|
|
214
|
+
DOCUMENT: 'document'
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
static AGENT_ROLES = {
|
|
218
|
+
LEAD: 'Lead',
|
|
219
|
+
DEVELOPER: 'Developer',
|
|
220
|
+
RESEARCHER: 'Researcher',
|
|
221
|
+
REVIEWER: 'Reviewer'
|
|
222
|
+
};
|
|
210
223
|
constructor(options = {}) {
|
|
211
224
|
super();
|
|
212
225
|
this.serverUrl = options.serverUrl || 'ws://localhost:8080';
|
|
213
|
-
this.clientId = uuidv4();
|
|
214
|
-
this.
|
|
215
|
-
this.
|
|
226
|
+
this.clientId = options.clientId || uuidv4();
|
|
227
|
+
this.name = options.name || `Agent-${this.clientId.substr(0, 4)}`;
|
|
228
|
+
this.role = options.role || MultiplayerClient.AGENT_ROLES.DEVELOPER;
|
|
216
229
|
this.model = options.model || 'default';
|
|
217
|
-
this.
|
|
218
|
-
this.
|
|
219
|
-
this.
|
|
230
|
+
this.sessionId = options.sessionId || null;
|
|
231
|
+
this.isHost = options.isHost || false;
|
|
232
|
+
this.connected = false;
|
|
220
233
|
this.ws = null;
|
|
234
|
+
this.workHistory = [];
|
|
235
|
+
this.clients = [];
|
|
236
|
+
this.currentTask = null;
|
|
237
|
+
this.taskQueue = [];
|
|
221
238
|
this.rl = options.rl || readline.createInterface({
|
|
222
239
|
input: process.stdin,
|
|
223
240
|
output: process.stdout
|
|
@@ -227,7 +244,8 @@ class MultiplayerClient extends EventEmitter {
|
|
|
227
244
|
async connect() {
|
|
228
245
|
try {
|
|
229
246
|
// Ensure server is running and get the correct URL
|
|
230
|
-
const
|
|
247
|
+
const url = new URL(this.serverUrl);
|
|
248
|
+
const serverPort = url.port || 8080;
|
|
231
249
|
this.serverUrl = await ensureServerRunning(serverPort);
|
|
232
250
|
|
|
233
251
|
return new Promise((resolve, reject) => {
|
|
@@ -235,6 +253,15 @@ class MultiplayerClient extends EventEmitter {
|
|
|
235
253
|
|
|
236
254
|
this.ws.on('open', () => {
|
|
237
255
|
this.connected = true;
|
|
256
|
+
// Send client info immediately after connection
|
|
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
|
+
});
|
|
238
265
|
this.emit('connected');
|
|
239
266
|
resolve();
|
|
240
267
|
});
|
|
@@ -269,6 +296,15 @@ class MultiplayerClient extends EventEmitter {
|
|
|
269
296
|
case 'session_joined':
|
|
270
297
|
this.handleSessionJoined(message);
|
|
271
298
|
break;
|
|
299
|
+
case 'session_created':
|
|
300
|
+
this.sessionId = message.sessionId;
|
|
301
|
+
this.clientId = message.clientId;
|
|
302
|
+
this.isHost = message.isHost;
|
|
303
|
+
this.emit('session_created', message);
|
|
304
|
+
break;
|
|
305
|
+
case 'client_updated':
|
|
306
|
+
this.handleClientUpdated(message);
|
|
307
|
+
break;
|
|
272
308
|
case 'agent_joined':
|
|
273
309
|
this.handleAgentJoined(message);
|
|
274
310
|
break;
|
|
@@ -284,30 +320,45 @@ class MultiplayerClient extends EventEmitter {
|
|
|
284
320
|
case 'task_assigned':
|
|
285
321
|
this.handleTaskAssigned(message);
|
|
286
322
|
break;
|
|
323
|
+
case 'task_completed':
|
|
324
|
+
this.handleTaskCompleted(message);
|
|
325
|
+
break;
|
|
326
|
+
case 'task_update':
|
|
327
|
+
this.handleTaskUpdate(message);
|
|
328
|
+
break;
|
|
287
329
|
case 'error':
|
|
288
|
-
|
|
330
|
+
console.error('Error:', message.message);
|
|
289
331
|
break;
|
|
290
332
|
default:
|
|
291
|
-
|
|
333
|
+
console.log('Unknown message type:', message.type);
|
|
292
334
|
}
|
|
293
335
|
}
|
|
294
336
|
|
|
295
337
|
handleSessionJoined(message) {
|
|
296
338
|
this.sessionId = message.sessionId;
|
|
297
|
-
this.
|
|
298
|
-
|
|
299
|
-
// Update local client list
|
|
300
|
-
this.clients.clear();
|
|
301
|
-
message.clients.forEach(client => {
|
|
302
|
-
this.clients.set(client.clientId, client);
|
|
303
|
-
});
|
|
304
|
-
|
|
339
|
+
this.clients = message.clients || [];
|
|
305
340
|
this.workHistory = message.workHistory || [];
|
|
341
|
+
|
|
342
|
+
// Process any pending tasks
|
|
343
|
+
if (message.pendingTasks && message.pendingTasks.length > 0) {
|
|
344
|
+
message.pendingTasks.forEach(task => {
|
|
345
|
+
this.emit('task_assigned', task);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
306
349
|
this.emit('session_joined', message);
|
|
307
350
|
}
|
|
308
351
|
|
|
352
|
+
handleClientUpdated(data) {
|
|
353
|
+
const clientIndex = this.clients.findIndex(c => c.id === data.clientId);
|
|
354
|
+
if (clientIndex !== -1) {
|
|
355
|
+
this.clients[clientIndex] = { ...this.clients[clientIndex], ...data.clientInfo };
|
|
356
|
+
}
|
|
357
|
+
this.emit('client_updated', data);
|
|
358
|
+
}
|
|
359
|
+
|
|
309
360
|
handleAgentJoined(message) {
|
|
310
|
-
this.clients.
|
|
361
|
+
this.clients.push({
|
|
311
362
|
...message.clientInfo,
|
|
312
363
|
clientId: message.clientId
|
|
313
364
|
});
|
|
@@ -319,10 +370,10 @@ class MultiplayerClient extends EventEmitter {
|
|
|
319
370
|
}
|
|
320
371
|
|
|
321
372
|
handleAgentLeft(message) {
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
console.log(chalk.yellow(`\n${
|
|
325
|
-
this.clients.
|
|
373
|
+
const clientIndex = this.clients.findIndex(c => c.clientId === message.clientId);
|
|
374
|
+
if (clientIndex !== -1) {
|
|
375
|
+
console.log(chalk.yellow(`\n${this.clients[clientIndex].name || 'Agent'} has left the session`));
|
|
376
|
+
this.clients.splice(clientIndex, 1);
|
|
326
377
|
}
|
|
327
378
|
this.emit('agent_left', message);
|
|
328
379
|
}
|
|
@@ -330,7 +381,7 @@ class MultiplayerClient extends EventEmitter {
|
|
|
330
381
|
handleChatMessage(message) {
|
|
331
382
|
if (message.clientId === this.clientId) return;
|
|
332
383
|
|
|
333
|
-
const sender = this.clients.
|
|
384
|
+
const sender = this.clients.find(c => c.clientId === message.clientId) || { name: 'Unknown' };
|
|
334
385
|
console.log(chalk.cyan(`\n[${sender.name}]: ${message.content}`));
|
|
335
386
|
this.emit('chat', message);
|
|
336
387
|
}
|
|
@@ -342,59 +393,79 @@ class MultiplayerClient extends EventEmitter {
|
|
|
342
393
|
clientId: message.clientId
|
|
343
394
|
});
|
|
344
395
|
|
|
345
|
-
const client = this.clients.
|
|
396
|
+
const client = this.clients.find(c => c.clientId === message.clientId) || { name: 'Unknown' };
|
|
346
397
|
console.log(chalk.green(`\n[Work Update] ${client.name}: ${message.work.description}`));
|
|
347
398
|
this.emit('work_updated', message);
|
|
348
399
|
}
|
|
349
400
|
|
|
350
|
-
handleTaskAssigned(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
this.emit('error', new Error(message.message));
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
createSession() {
|
|
363
|
-
this.send({
|
|
364
|
-
type: 'create_session',
|
|
365
|
-
clientInfo: {
|
|
366
|
-
name: this.agentName,
|
|
367
|
-
model: this.model,
|
|
368
|
-
isHost: true
|
|
369
|
-
}
|
|
370
|
-
});
|
|
401
|
+
handleTaskAssigned(task) {
|
|
402
|
+
console.log(`\n New task assigned: ${task.prompt}`);
|
|
403
|
+
console.log(`Type: ${task.taskType} | Assigned by: ${this.getClientName(task.assignedBy)}\n`);
|
|
404
|
+
|
|
405
|
+
// Add to task queue
|
|
406
|
+
this.taskQueue.push(task);
|
|
407
|
+
this.processTaskQueue();
|
|
408
|
+
|
|
409
|
+
this.emit('task_assigned', task);
|
|
371
410
|
}
|
|
372
411
|
|
|
373
|
-
|
|
374
|
-
this.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
412
|
+
async processTaskQueue() {
|
|
413
|
+
if (this.currentTask || this.taskQueue.length === 0) return;
|
|
414
|
+
|
|
415
|
+
this.currentTask = this.taskQueue.shift();
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
// Simulate working on the task
|
|
419
|
+
console.log(`\n Working on task: ${this.currentTask.prompt}`);
|
|
420
|
+
|
|
421
|
+
// Here you would integrate with your AI to process the task
|
|
422
|
+
// For now, we'll just simulate work
|
|
423
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
424
|
+
|
|
425
|
+
const result = {
|
|
426
|
+
taskId: this.currentTask.id,
|
|
427
|
+
result: `Completed task: ${this.currentTask.prompt}`,
|
|
428
|
+
timestamp: new Date().toISOString()
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// Send result back to server
|
|
432
|
+
this.send({
|
|
433
|
+
type: 'task_result',
|
|
434
|
+
sessionId: this.sessionId,
|
|
435
|
+
taskId: this.currentTask.id,
|
|
436
|
+
result: result.result,
|
|
437
|
+
timestamp: result.timestamp
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
console.log(` Completed task: ${this.currentTask.prompt}`);
|
|
441
|
+
|
|
442
|
+
} catch (error) {
|
|
443
|
+
console.error('Error processing task:', error);
|
|
444
|
+
} finally {
|
|
445
|
+
this.currentTask = null;
|
|
446
|
+
// Process next task if available
|
|
447
|
+
if (this.taskQueue.length > 0) {
|
|
448
|
+
this.processTaskQueue();
|
|
381
449
|
}
|
|
382
|
-
}
|
|
450
|
+
}
|
|
383
451
|
}
|
|
384
452
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
453
|
+
handleTaskCompleted(data) {
|
|
454
|
+
console.log(`\n Task completed by all agents!`);
|
|
455
|
+
console.log(`Task: ${this.tasks.get(data.taskId)?.prompt || 'Unknown task'}`);
|
|
456
|
+
console.log('Results:');
|
|
457
|
+
|
|
458
|
+
data.results.forEach(result => {
|
|
459
|
+
console.log(`- ${this.getClientName(result.clientId)}: ${result.result}`);
|
|
391
460
|
});
|
|
461
|
+
|
|
462
|
+
this.emit('task_completed', data);
|
|
392
463
|
}
|
|
393
464
|
|
|
394
465
|
updateWork(work) {
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
466
|
+
const workItem = {
|
|
467
|
+
...work,
|
|
468
|
+
clientId: this.clientId,
|
|
398
469
|
status: work.status || 'in_progress',
|
|
399
470
|
timestamp: new Date().toISOString()
|
|
400
471
|
};
|
package/bin/multiplayer-mode.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const MultiplayerClient = require('./multiplayer-client');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
2
|
const readline = require('readline');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
4
|
const { v4: uuidv4 } = require('uuid');
|
|
5
5
|
|
|
6
6
|
class MultiplayerMode {
|
|
@@ -86,142 +86,245 @@ class MultiplayerMode {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
async start() {
|
|
89
|
-
|
|
89
|
+
console.clear();
|
|
90
|
+
this.printHeader();
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
// Set agent name
|
|
93
|
+
this.client.name = await this.askQuestion('Enter your name (or press Enter for a random one): ') ||
|
|
94
|
+
`Agent-${Math.random().toString(36).substr(2, 4)}`;
|
|
95
|
+
|
|
96
|
+
// Set agent role
|
|
97
|
+
console.log('\nAvailable roles:');
|
|
98
|
+
Object.entries(MultiplayerClient.AGENT_ROLES).forEach(([key, value]) => {
|
|
99
|
+
console.log(`- ${key}: ${value}`);
|
|
98
100
|
});
|
|
101
|
+
this.client.role = await this.askQuestion('\nChoose your role (or press Enter for Developer): ') ||
|
|
102
|
+
MultiplayerClient.AGENT_ROLES.DEVELOPER;
|
|
103
|
+
|
|
104
|
+
// Get session ID from user
|
|
105
|
+
const sessionId = await this.askQuestion('\nEnter session ID to join or press Enter to create a new session: ');
|
|
106
|
+
|
|
107
|
+
if (sessionId) {
|
|
108
|
+
await this.joinSession(sessionId);
|
|
109
|
+
} else {
|
|
110
|
+
await this.createSession();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.setupCommandHandlers();
|
|
114
|
+
this.prompt();
|
|
99
115
|
}
|
|
100
116
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
117
|
+
async askQuestion(question) {
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
this.rl.question(question, (answer) => {
|
|
120
|
+
resolve(answer.trim());
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
109
124
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
async joinSession(sessionId) {
|
|
126
|
+
this.client.joinSession(sessionId);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async createSession() {
|
|
130
|
+
console.log(chalk.yellow('Creating a new session...'));
|
|
131
|
+
this.client.createSession();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
setupCommandHandlers() {
|
|
135
|
+
this.client.on('command', async (command) => {
|
|
136
|
+
await this.handleCommand(command);
|
|
118
137
|
});
|
|
119
138
|
}
|
|
120
139
|
|
|
121
|
-
async handleCommand(
|
|
122
|
-
const [
|
|
140
|
+
async handleCommand(input) {
|
|
141
|
+
const [command, ...args] = input.slice(1).split(' ');
|
|
123
142
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
143
|
+
try {
|
|
144
|
+
switch (command.toLowerCase()) {
|
|
145
|
+
case 'name':
|
|
146
|
+
if (args.length > 0) {
|
|
147
|
+
const newName = args.join(' ');
|
|
148
|
+
this.client.name = newName;
|
|
149
|
+
await this.client.updateClientInfo({ name: newName });
|
|
150
|
+
console.log(chalk.green(`✅ Name updated to: ${newName}`));
|
|
151
|
+
} else {
|
|
152
|
+
console.log(chalk.yellow(`Current name: ${this.client.name}`));
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
|
|
156
|
+
case 'role':
|
|
157
|
+
if (args.length > 0) {
|
|
158
|
+
const newRole = args.join(' ');
|
|
159
|
+
this.client.role = newRole;
|
|
160
|
+
await this.client.updateClientInfo({ role: newRole });
|
|
161
|
+
console.log(chalk.green(`✅ Role updated to: ${newRole}`));
|
|
162
|
+
} else {
|
|
163
|
+
console.log(chalk.yellow(`Current role: ${this.client.role}`));
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
|
|
167
|
+
case 'model':
|
|
168
|
+
if (args.length > 0) {
|
|
169
|
+
this.client.model = args[0];
|
|
170
|
+
await this.client.updateClientInfo({ model: this.client.model });
|
|
171
|
+
console.log(chalk.green(`✅ Model updated to: ${this.client.model}`));
|
|
172
|
+
} else {
|
|
173
|
+
console.log(chalk.yellow(`Current model: ${this.client.model}`));
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
|
|
177
|
+
case 'work':
|
|
178
|
+
if (args.length > 0) {
|
|
179
|
+
const workDesc = args.join(' ');
|
|
180
|
+
this.client.updateWork({
|
|
181
|
+
description: workDesc,
|
|
182
|
+
status: 'in_progress',
|
|
183
|
+
timestamp: new Date().toISOString()
|
|
184
|
+
});
|
|
185
|
+
console.log(chalk.green(`✅ Work updated: ${workDesc}`));
|
|
186
|
+
} else {
|
|
187
|
+
console.log(chalk.yellow('Please provide a work description. Example: /work Fixing bugs'));
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
168
190
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
191
|
+
case 'collab':
|
|
192
|
+
case 'collaborate':
|
|
193
|
+
if (args.length > 0) {
|
|
194
|
+
if (!this.client.isHost) {
|
|
195
|
+
console.log(chalk.yellow('Only the session host can initiate collaboration'));
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
const taskType = args[0].toLowerCase();
|
|
199
|
+
const prompt = args.slice(1).join(' ');
|
|
200
|
+
|
|
201
|
+
if (!Object.values(MultiplayerClient.TASK_TYPES).includes(taskType)) {
|
|
202
|
+
console.log(chalk.yellow(`Invalid task type. Available types: ${Object.values(MultiplayerClient.TASK_TYPES).join(', ')}`));
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!prompt) {
|
|
207
|
+
console.log(chalk.yellow('Please provide a task prompt. Example: /collab research How does quantum computing work?'));
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
console.log(chalk.blue(`\n🤝 Starting collaborative ${taskType} task: ${prompt}`));
|
|
212
|
+
await this.client.collaborate(prompt, taskType);
|
|
213
|
+
|
|
214
|
+
} else {
|
|
215
|
+
console.log(chalk.yellow('Usage: /collab <type> <prompt>'));
|
|
216
|
+
console.log(chalk.yellow('Types: ' + Object.values(MultiplayerClient.TASK_TYPES).join(', ')));
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
|
|
220
|
+
case 'task':
|
|
221
|
+
if (args.length > 1) {
|
|
222
|
+
const [target, ...taskArgs] = args;
|
|
223
|
+
const taskDesc = taskArgs.join(' ');
|
|
224
|
+
|
|
225
|
+
// Find the target client
|
|
226
|
+
const targetClient = this.client.clients.find(c =>
|
|
227
|
+
c.name.toLowerCase() === target.toLowerCase() ||
|
|
228
|
+
c.clientId === target
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
if (!targetClient) {
|
|
232
|
+
console.log(chalk.red(`❌ Could not find agent: ${target}`));
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// In a real implementation, you would send this to the server
|
|
237
|
+
// and the server would route it to the appropriate client
|
|
238
|
+
console.log(chalk.blue(`\n📝 Task assigned to ${targetClient.name}: ${taskDesc}`));
|
|
172
239
|
|
|
173
|
-
if (targetClient) {
|
|
174
|
-
this.client.assignTask({
|
|
175
|
-
description: taskDescription,
|
|
176
|
-
status: 'pending'
|
|
177
|
-
}, targetClient.clientId);
|
|
178
|
-
console.log(chalk.green(`Task assigned to ${targetClient.name}`));
|
|
179
240
|
} else {
|
|
180
|
-
console.log(chalk.
|
|
241
|
+
console.log(chalk.yellow('Usage: /task <agent> <task description>'));
|
|
242
|
+
console.log(chalk.yellow('Example: /task alice Please review the latest changes'));
|
|
181
243
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
244
|
+
break;
|
|
245
|
+
|
|
246
|
+
case 'list':
|
|
247
|
+
this.listAgents();
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
case 'exit':
|
|
251
|
+
console.log(chalk.yellow('\n👋 Disconnecting...'));
|
|
252
|
+
process.exit(0);
|
|
253
|
+
break;
|
|
254
|
+
|
|
255
|
+
case 'help':
|
|
256
|
+
this.showHelp();
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
default:
|
|
260
|
+
console.log(chalk.red(`❌ Unknown command: ${command}. Type /help for available commands.`));
|
|
261
|
+
}
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
199
264
|
}
|
|
200
265
|
|
|
201
266
|
this.prompt();
|
|
202
267
|
}
|
|
203
268
|
|
|
204
269
|
showHelp() {
|
|
205
|
-
console.log(
|
|
206
|
-
console.log('
|
|
207
|
-
console.log(' /name [new_name]
|
|
208
|
-
console.log(' /role [role]
|
|
209
|
-
console.log(' /
|
|
210
|
-
|
|
211
|
-
console.log('
|
|
212
|
-
console.log(' /
|
|
270
|
+
console.log('\n=== 🆘 Available Commands ===');
|
|
271
|
+
console.log('\n👤 Agent Management:');
|
|
272
|
+
console.log(' /name [new_name] - Set or show your name');
|
|
273
|
+
console.log(' /role [role] - Set or show your role');
|
|
274
|
+
console.log(' /model [model_name] - Set or show your AI model');
|
|
275
|
+
|
|
276
|
+
console.log('\n🤝 Collaboration:');
|
|
277
|
+
console.log(' /work [description] - Update your work status');
|
|
278
|
+
console.log(' /collab <type> <prompt> - Start a collaborative task');
|
|
279
|
+
console.log(' Types: ' + Object.values(MultiplayerClient.TASK_TYPES).join(', '));
|
|
280
|
+
console.log(' /task [agent] [task] - Assign a task to another agent');
|
|
281
|
+
|
|
282
|
+
console.log('\n📋 Session:');
|
|
283
|
+
console.log(' /list - List all agents in the session');
|
|
284
|
+
console.log(' /exit - Leave the session');
|
|
285
|
+
console.log(' /help - Show this help message');
|
|
286
|
+
|
|
287
|
+
console.log('\n💡 Example:');
|
|
288
|
+
console.log(' /collab research How does quantum computing work?');
|
|
289
|
+
console.log(' /work Implementing user authentication');
|
|
290
|
+
console.log(' /task bob Please review my latest changes');
|
|
213
291
|
}
|
|
214
292
|
|
|
215
293
|
listAgents() {
|
|
216
|
-
console.log(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const hostInfo = client.isHost ? ' [HOST]' : '';
|
|
221
|
-
console.log(` ${index}. ${chalk.green(client.name)}${roleInfo}${hostInfo}`);
|
|
222
|
-
index++;
|
|
294
|
+
console.log('\n=== 🧑💻 Agents in Session ===');
|
|
295
|
+
if (this.client.clients.length === 0) {
|
|
296
|
+
console.log('No other agents in the session');
|
|
297
|
+
return;
|
|
223
298
|
}
|
|
224
|
-
|
|
299
|
+
|
|
300
|
+
this.client.clients.forEach(client => {
|
|
301
|
+
const role = client.role ? ` (${client.role})` : '';
|
|
302
|
+
const status = client.isHost ? '👑 ' : '👤 ';
|
|
303
|
+
console.log(`${status}${client.name}${role} [${client.model}]`);
|
|
304
|
+
if (client.work) {
|
|
305
|
+
console.log(` ${client.work.status === 'completed' ? '✅' : '🔄'} ${client.work.description}`);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
prompt() {
|
|
311
|
+
if (this.isWorking) return;
|
|
312
|
+
|
|
313
|
+
this.rl.question(chalk.blue('> '), async (input) => {
|
|
314
|
+
if (!input.trim()) {
|
|
315
|
+
this.prompt();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Handle commands
|
|
320
|
+
if (input.startsWith('/')) {
|
|
321
|
+
await this.handleCommand(input);
|
|
322
|
+
} else {
|
|
323
|
+
// Regular chat message
|
|
324
|
+
this.client.sendChatMessage(input);
|
|
325
|
+
this.prompt();
|
|
326
|
+
}
|
|
327
|
+
});
|
|
225
328
|
}
|
|
226
329
|
}
|
|
227
330
|
|
|
@@ -6,24 +6,20 @@ class MultiplayerServer {
|
|
|
6
6
|
constructor(port = 8080) {
|
|
7
7
|
this.port = port;
|
|
8
8
|
this.sessions = new Map();
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
9
|
+
this.clients = new Map();
|
|
10
|
+
this.tasks = new Map();
|
|
11
|
+
this.server = new WebSocket.Server({ port });
|
|
11
12
|
this.setupEventHandlers();
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
setupEventHandlers() {
|
|
15
|
-
this.
|
|
16
|
+
this.server.on('connection', (ws) => {
|
|
16
17
|
let clientId = uuid.v4();
|
|
17
18
|
let sessionId = null;
|
|
18
19
|
let clientInfo = {};
|
|
19
20
|
|
|
20
21
|
ws.on('message', (message) => {
|
|
21
|
-
|
|
22
|
-
const data = JSON.parse(message);
|
|
23
|
-
this.handleMessage(ws, clientId, data);
|
|
24
|
-
} catch (error) {
|
|
25
|
-
console.error('Error parsing message:', error);
|
|
26
|
-
}
|
|
22
|
+
this.handleMessage(ws, message);
|
|
27
23
|
});
|
|
28
24
|
|
|
29
25
|
ws.on('close', () => {
|
|
@@ -35,7 +31,7 @@ class MultiplayerServer {
|
|
|
35
31
|
clientId,
|
|
36
32
|
clientInfo
|
|
37
33
|
});
|
|
38
|
-
|
|
34
|
+
|
|
39
35
|
if (Object.keys(session.clients).length === 0) {
|
|
40
36
|
this.sessions.delete(sessionId);
|
|
41
37
|
}
|
|
@@ -44,48 +40,129 @@ class MultiplayerServer {
|
|
|
44
40
|
});
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
handleMessage(ws,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
43
|
+
handleMessage(ws, message) {
|
|
44
|
+
try {
|
|
45
|
+
const data = typeof message === 'string' ? JSON.parse(message) : message;
|
|
46
|
+
const client = this.clients.get(ws);
|
|
47
|
+
|
|
48
|
+
switch (data.type) {
|
|
49
|
+
case 'client_info':
|
|
50
|
+
if (client) {
|
|
51
|
+
Object.assign(client, data.clientInfo);
|
|
52
|
+
this.broadcastToSession(client.sessionId, {
|
|
53
|
+
type: 'client_updated',
|
|
54
|
+
clientId: client.id,
|
|
55
|
+
clientInfo: data.clientInfo
|
|
56
|
+
}, ws);
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
|
|
60
|
+
case 'create_session':
|
|
61
|
+
this.handleCreateSession(ws, data);
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'join_session':
|
|
65
|
+
this.handleJoinSession(ws, data);
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case 'collaborate':
|
|
69
|
+
if (client && client.sessionId) {
|
|
70
|
+
const taskId = uuid.v4();
|
|
71
|
+
this.tasks.set(taskId, {
|
|
72
|
+
id: taskId,
|
|
73
|
+
sessionId: client.sessionId,
|
|
74
|
+
prompt: data.prompt,
|
|
75
|
+
status: 'in_progress',
|
|
76
|
+
createdBy: client.id,
|
|
77
|
+
createdAt: new Date().toISOString(),
|
|
78
|
+
results: []
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
this.broadcastToSession(client.sessionId, {
|
|
82
|
+
type: 'task_assigned',
|
|
83
|
+
taskId,
|
|
84
|
+
prompt: data.prompt,
|
|
85
|
+
assignedBy: client.id
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
case 'task_result':
|
|
91
|
+
if (client && client.sessionId && data.taskId) {
|
|
92
|
+
const task = this.tasks.get(data.taskId);
|
|
93
|
+
if (task) {
|
|
94
|
+
task.results.push({
|
|
95
|
+
clientId: client.id,
|
|
96
|
+
result: data.result,
|
|
97
|
+
timestamp: new Date().toISOString()
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Check if all clients have responded
|
|
101
|
+
const session = this.sessions.get(client.sessionId);
|
|
102
|
+
if (session && task.results.length >= Object.keys(session.clients).length) {
|
|
103
|
+
task.status = 'completed';
|
|
104
|
+
this.broadcastToSession(client.sessionId, {
|
|
105
|
+
type: 'task_completed',
|
|
106
|
+
taskId: task.id,
|
|
107
|
+
results: task.results
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
case 'chat':
|
|
115
|
+
case 'work_update':
|
|
116
|
+
case 'task_update':
|
|
117
|
+
this.broadcastToSession(data.sessionId, data);
|
|
118
|
+
break;
|
|
119
|
+
|
|
120
|
+
default:
|
|
121
|
+
console.log('Unknown message type:', data.type);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('Error handling message:', error);
|
|
66
125
|
}
|
|
67
126
|
}
|
|
68
127
|
|
|
69
|
-
handleCreateSession(ws,
|
|
128
|
+
handleCreateSession(ws, data) {
|
|
70
129
|
const sessionId = uuid.v4();
|
|
130
|
+
const clientId = uuid.v4();
|
|
131
|
+
|
|
132
|
+
// Store client info
|
|
133
|
+
this.clients.set(ws, {
|
|
134
|
+
id: clientId,
|
|
135
|
+
sessionId,
|
|
136
|
+
ws,
|
|
137
|
+
isHost: true,
|
|
138
|
+
name: data.name || `Agent-${Math.random().toString(36).substr(2, 4)}`,
|
|
139
|
+
role: data.role || 'Assistant',
|
|
140
|
+
model: data.model || 'default'
|
|
141
|
+
});
|
|
142
|
+
|
|
71
143
|
this.sessions.set(sessionId, {
|
|
72
144
|
id: sessionId,
|
|
73
|
-
clients: {},
|
|
145
|
+
clients: { [clientId]: true },
|
|
74
146
|
workHistory: [],
|
|
75
|
-
createdAt: new Date().toISOString()
|
|
147
|
+
createdAt: new Date().toISOString(),
|
|
148
|
+
hostId: clientId
|
|
76
149
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
150
|
+
|
|
151
|
+
// Send session created confirmation
|
|
152
|
+
ws.send(JSON.stringify({
|
|
153
|
+
type: 'session_created',
|
|
80
154
|
sessionId,
|
|
155
|
+
clientId,
|
|
81
156
|
isHost: true
|
|
82
|
-
});
|
|
157
|
+
}));
|
|
83
158
|
}
|
|
84
159
|
|
|
85
|
-
handleJoinSession(ws,
|
|
86
|
-
const { sessionId
|
|
87
|
-
|
|
88
|
-
|
|
160
|
+
handleJoinSession(ws, data) {
|
|
161
|
+
const { sessionId } = data;
|
|
162
|
+
const clientId = data.clientId || uuid.v4();
|
|
163
|
+
const session = this.sessions.get(sessionId);
|
|
164
|
+
|
|
165
|
+
if (!session) {
|
|
89
166
|
ws.send(JSON.stringify({
|
|
90
167
|
type: 'error',
|
|
91
168
|
message: 'Session not found'
|
|
@@ -93,8 +170,6 @@ class MultiplayerServer {
|
|
|
93
170
|
return;
|
|
94
171
|
}
|
|
95
172
|
|
|
96
|
-
const session = this.sessions.get(sessionId);
|
|
97
|
-
|
|
98
173
|
if (Object.keys(session.clients).length >= 4) {
|
|
99
174
|
ws.send(JSON.stringify({
|
|
100
175
|
type: 'error',
|
|
@@ -103,40 +178,64 @@ class MultiplayerServer {
|
|
|
103
178
|
return;
|
|
104
179
|
}
|
|
105
180
|
|
|
106
|
-
//
|
|
107
|
-
|
|
181
|
+
// Store/update client info
|
|
182
|
+
const existingClient = Array.from(this.clients.entries())
|
|
183
|
+
.find(([_, c]) => c.id === clientId)?.[1];
|
|
184
|
+
|
|
185
|
+
const clientInfo = {
|
|
186
|
+
id: clientId,
|
|
187
|
+
sessionId,
|
|
108
188
|
ws,
|
|
109
|
-
clientId,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
isHost: data.isHost || false
|
|
114
|
-
}
|
|
189
|
+
isHost: session.hostId === clientId,
|
|
190
|
+
name: data.name || existingClient?.name || `Agent-${clientId.substr(0, 4)}`,
|
|
191
|
+
role: data.role || existingClient?.role || 'Assistant',
|
|
192
|
+
model: data.model || existingClient?.model || 'default'
|
|
115
193
|
};
|
|
116
194
|
|
|
195
|
+
this.clients.set(ws, clientInfo);
|
|
196
|
+
session.clients[clientId] = true;
|
|
197
|
+
|
|
198
|
+
// Get all client infos for the session
|
|
199
|
+
const sessionClients = Array.from(this.clients.values())
|
|
200
|
+
.filter(c => c.sessionId === sessionId)
|
|
201
|
+
.map(({ id, name, role, isHost }) => ({
|
|
202
|
+
id,
|
|
203
|
+
name,
|
|
204
|
+
role,
|
|
205
|
+
isHost
|
|
206
|
+
}));
|
|
207
|
+
|
|
117
208
|
// Send welcome message with session info
|
|
118
209
|
ws.send(JSON.stringify({
|
|
119
210
|
type: 'session_joined',
|
|
120
211
|
sessionId,
|
|
121
212
|
clientId,
|
|
122
|
-
|
|
123
|
-
|
|
213
|
+
isHost: clientInfo.isHost,
|
|
214
|
+
clients: sessionClients,
|
|
215
|
+
workHistory: session.workHistory,
|
|
216
|
+
pendingTasks: Array.from(this.tasks.values())
|
|
217
|
+
.filter(t => t.sessionId === sessionId && t.status === 'in_progress')
|
|
124
218
|
}));
|
|
125
219
|
|
|
126
220
|
// Notify other clients
|
|
127
221
|
this.broadcastToSession(sessionId, {
|
|
128
222
|
type: 'agent_joined',
|
|
129
223
|
clientId,
|
|
130
|
-
clientInfo:
|
|
131
|
-
|
|
224
|
+
clientInfo: {
|
|
225
|
+
id: clientId,
|
|
226
|
+
name: clientInfo.name,
|
|
227
|
+
role: clientInfo.role,
|
|
228
|
+
isHost: clientInfo.isHost
|
|
229
|
+
}
|
|
230
|
+
}, ws);
|
|
132
231
|
}
|
|
133
232
|
|
|
134
233
|
broadcastToSession(sessionId, message, excludeClientId = null) {
|
|
135
234
|
if (!this.sessions.has(sessionId)) return;
|
|
136
|
-
|
|
235
|
+
|
|
137
236
|
const session = this.sessions.get(sessionId);
|
|
138
237
|
const messageStr = JSON.stringify(message);
|
|
139
|
-
|
|
238
|
+
|
|
140
239
|
Object.entries(session.clients).forEach(([id, client]) => {
|
|
141
240
|
if (id !== excludeClientId && client.ws.readyState === WebSocket.OPEN) {
|
|
142
241
|
client.ws.send(messageStr);
|