sam-coder-cli 1.0.43 → 1.0.44
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 +7 -77
- package/package.json +2 -8
- package/MULTIPLAYER.md +0 -112
- 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
package/bin/multiplayer-mode.js
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
const AITeam = require('./ai-team');
|
|
2
|
-
const readline = require('readline');
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
const ui = require('./ui');
|
|
5
|
-
|
|
6
|
-
class MultiplayerMode {
|
|
7
|
-
constructor(options = {}) {
|
|
8
|
-
this.rl = options.rl || readline.createInterface({
|
|
9
|
-
input: process.stdin,
|
|
10
|
-
output: process.stdout
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// Create AI Team instance
|
|
14
|
-
this.aiTeam = new AITeam({
|
|
15
|
-
name: options.agentName || this.generateAgentName(),
|
|
16
|
-
role: options.role || 'DEVELOPER',
|
|
17
|
-
model: options.model || 'deepseek/deepseek-chat-v3-0324:free',
|
|
18
|
-
rl: this.rl,
|
|
19
|
-
serverUrl: options.serverUrl
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
this.agentRole = options.role || 'DEVELOPER';
|
|
23
|
-
this.isWorking = false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
generateAgentName() {
|
|
27
|
-
const adjectives = ['Swift', 'Clever', 'Wise', 'Agile', 'Smart', 'Quick', 'Bright', 'Sharp'];
|
|
28
|
-
const nouns = ['Coder', 'Thinker', 'Solver', 'Creator', 'Builder', 'Planner', 'Strategist'];
|
|
29
|
-
const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
30
|
-
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
31
|
-
return `${randomAdjective}${randomNoun}${Math.floor(100 + Math.random() * 900)}`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
setupEventListeners() {
|
|
35
|
-
// Event listeners are now handled by AICollaboration class
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async start() {
|
|
39
|
-
console.clear();
|
|
40
|
-
ui.showHeader();
|
|
41
|
-
console.log(chalk.blue.bold('=== AI Team Collaboration Mode ==='));
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
// Get session ID to join or create a new one
|
|
45
|
-
const sessionId = await this.askQuestion('\nEnter session ID to join (or press Enter to create a new session): ');
|
|
46
|
-
|
|
47
|
-
await this.aiTeam.start(sessionId || undefined);
|
|
48
|
-
|
|
49
|
-
// Keep the process alive
|
|
50
|
-
this.rl.on('close', () => {
|
|
51
|
-
console.log('\nDisconnecting from collaboration session...');
|
|
52
|
-
process.exit(0);
|
|
53
|
-
});
|
|
54
|
-
} catch (error) {
|
|
55
|
-
console.error(chalk.red(`\nError: ${error.message}`));
|
|
56
|
-
console.error(error.stack);
|
|
57
|
-
this.rl.close();
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async askQuestion(question) {
|
|
63
|
-
return new Promise((resolve) => {
|
|
64
|
-
this.rl.question(question, (answer) => {
|
|
65
|
-
resolve(answer.trim());
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async joinSession(sessionId) {
|
|
71
|
-
this.client.joinSession(sessionId);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async createSession() {
|
|
75
|
-
console.log(chalk.yellow('Creating a new session...'));
|
|
76
|
-
this.client.createSession();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
setupCommandHandlers() {
|
|
80
|
-
this.client.on('command', async (command) => {
|
|
81
|
-
await this.handleCommand(command);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async handleCommand(input) {
|
|
86
|
-
const [command, ...args] = input.slice(1).split(' ');
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
switch (command.toLowerCase()) {
|
|
90
|
-
case 'name':
|
|
91
|
-
if (args.length > 0) {
|
|
92
|
-
const newName = args.join(' ');
|
|
93
|
-
this.client.name = newName;
|
|
94
|
-
await this.client.updateClientInfo({ name: newName });
|
|
95
|
-
console.log(chalk.green(`✅ Name updated to: ${newName}`));
|
|
96
|
-
} else {
|
|
97
|
-
console.log(chalk.yellow(`Current name: ${this.client.name}`));
|
|
98
|
-
}
|
|
99
|
-
break;
|
|
100
|
-
|
|
101
|
-
case 'role':
|
|
102
|
-
if (args.length > 0) {
|
|
103
|
-
const newRole = args.join(' ');
|
|
104
|
-
this.client.role = newRole;
|
|
105
|
-
await this.client.updateClientInfo({ role: newRole });
|
|
106
|
-
console.log(chalk.green(`✅ Role updated to: ${newRole}`));
|
|
107
|
-
} else {
|
|
108
|
-
console.log(chalk.yellow(`Current role: ${this.client.role}`));
|
|
109
|
-
}
|
|
110
|
-
break;
|
|
111
|
-
|
|
112
|
-
case 'model':
|
|
113
|
-
if (args.length > 0) {
|
|
114
|
-
this.client.model = args[0];
|
|
115
|
-
await this.client.updateClientInfo({ model: this.client.model });
|
|
116
|
-
console.log(chalk.green(`✅ Model updated to: ${this.client.model}`));
|
|
117
|
-
} else {
|
|
118
|
-
console.log(chalk.yellow(`Current model: ${this.client.model}`));
|
|
119
|
-
}
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case 'work':
|
|
123
|
-
if (args.length > 0) {
|
|
124
|
-
const workDesc = args.join(' ');
|
|
125
|
-
this.client.updateWork({
|
|
126
|
-
description: workDesc,
|
|
127
|
-
status: 'in_progress',
|
|
128
|
-
timestamp: new Date().toISOString()
|
|
129
|
-
});
|
|
130
|
-
console.log(chalk.green(`✅ Work updated: ${workDesc}`));
|
|
131
|
-
} else {
|
|
132
|
-
console.log(chalk.yellow('Please provide a work description. Example: /work Fixing bugs'));
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
|
|
136
|
-
case 'collab':
|
|
137
|
-
case 'collaborate':
|
|
138
|
-
if (args.length > 0) {
|
|
139
|
-
if (!this.client.isHost) {
|
|
140
|
-
console.log(chalk.yellow('Only the session host can initiate collaboration'));
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
const taskType = args[0].toLowerCase();
|
|
144
|
-
const prompt = args.slice(1).join(' ');
|
|
145
|
-
|
|
146
|
-
if (!Object.values(MultiplayerClient.TASK_TYPES).includes(taskType)) {
|
|
147
|
-
console.log(chalk.yellow(`Invalid task type. Available types: ${Object.values(MultiplayerClient.TASK_TYPES).join(', ')}`));
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (!prompt) {
|
|
152
|
-
console.log(chalk.yellow('Please provide a task prompt. Example: /collab research How does quantum computing work?'));
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
console.log(chalk.blue(`\n🤝 Starting collaborative ${taskType} task: ${prompt}`));
|
|
157
|
-
await this.client.collaborate(prompt, taskType);
|
|
158
|
-
|
|
159
|
-
} else {
|
|
160
|
-
console.log(chalk.yellow('Usage: /collab <type> <prompt>'));
|
|
161
|
-
console.log(chalk.yellow('Types: ' + Object.values(MultiplayerClient.TASK_TYPES).join(', ')));
|
|
162
|
-
}
|
|
163
|
-
break;
|
|
164
|
-
|
|
165
|
-
case 'task':
|
|
166
|
-
if (args.length > 1) {
|
|
167
|
-
const [target, ...taskArgs] = args;
|
|
168
|
-
const taskDesc = taskArgs.join(' ');
|
|
169
|
-
|
|
170
|
-
// Find the target client
|
|
171
|
-
const targetClient = this.client.clients.find(c =>
|
|
172
|
-
c.name.toLowerCase() === target.toLowerCase() ||
|
|
173
|
-
c.clientId === target
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
if (!targetClient) {
|
|
177
|
-
console.log(chalk.red(`❌ Could not find agent: ${target}`));
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// In a real implementation, you would send this to the server
|
|
182
|
-
// and the server would route it to the appropriate client
|
|
183
|
-
console.log(chalk.blue(`\n📝 Task assigned to ${targetClient.name}: ${taskDesc}`));
|
|
184
|
-
|
|
185
|
-
} else {
|
|
186
|
-
console.log(chalk.yellow('Usage: /task <agent> <task description>'));
|
|
187
|
-
console.log(chalk.yellow('Example: /task alice Please review the latest changes'));
|
|
188
|
-
}
|
|
189
|
-
break;
|
|
190
|
-
|
|
191
|
-
case 'list':
|
|
192
|
-
this.listAgents();
|
|
193
|
-
break;
|
|
194
|
-
|
|
195
|
-
case 'exit':
|
|
196
|
-
console.log(chalk.yellow('\n👋 Disconnecting...'));
|
|
197
|
-
process.exit(0);
|
|
198
|
-
break;
|
|
199
|
-
|
|
200
|
-
case 'help':
|
|
201
|
-
this.showHelp();
|
|
202
|
-
break;
|
|
203
|
-
|
|
204
|
-
default:
|
|
205
|
-
console.log(chalk.red(`❌ Unknown command: ${command}. Type /help for available commands.`));
|
|
206
|
-
}
|
|
207
|
-
} catch (error) {
|
|
208
|
-
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
this.prompt();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
showHelp() {
|
|
215
|
-
console.log('\n=== 🆘 Available Commands ===');
|
|
216
|
-
console.log('\n👤 Agent Management:');
|
|
217
|
-
console.log(' /name [new_name] - Set or show your name');
|
|
218
|
-
console.log(' /role [role] - Set or show your role');
|
|
219
|
-
console.log(' /model [model_name] - Set or show your AI model');
|
|
220
|
-
|
|
221
|
-
console.log('\n🤝 Collaboration:');
|
|
222
|
-
console.log(' /work [description] - Update your work status');
|
|
223
|
-
console.log(' /collab <type> <prompt> - Start a collaborative task');
|
|
224
|
-
console.log(' Types: ' + Object.values(MultiplayerClient.TASK_TYPES).join(', '));
|
|
225
|
-
console.log(' /task [agent] [task] - Assign a task to another agent');
|
|
226
|
-
|
|
227
|
-
console.log('\n📋 Session:');
|
|
228
|
-
console.log(' /list - List all agents in the session');
|
|
229
|
-
console.log(' /exit - Leave the session');
|
|
230
|
-
console.log(' /help - Show this help message');
|
|
231
|
-
|
|
232
|
-
console.log('\n💡 Example:');
|
|
233
|
-
console.log(' /collab research How does quantum computing work?');
|
|
234
|
-
console.log(' /work Implementing user authentication');
|
|
235
|
-
console.log(' /task bob Please review my latest changes');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
listAgents() {
|
|
239
|
-
console.log('\n=== 🧑💻 Agents in Session ===');
|
|
240
|
-
if (this.client.clients.length === 0) {
|
|
241
|
-
console.log('No other agents in the session');
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
this.client.clients.forEach(client => {
|
|
246
|
-
const role = client.role ? ` (${client.role})` : '';
|
|
247
|
-
const status = client.isHost ? '👑 ' : '👤 ';
|
|
248
|
-
console.log(`${status}${client.name}${role} [${client.model}]`);
|
|
249
|
-
if (client.work) {
|
|
250
|
-
console.log(` ${client.work.status === 'completed' ? '✅' : '🔄'} ${client.work.description}`);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
prompt() {
|
|
256
|
-
if (this.isWorking) return;
|
|
257
|
-
|
|
258
|
-
this.rl.question(chalk.blue('> '), async (input) => {
|
|
259
|
-
if (!input.trim()) {
|
|
260
|
-
this.prompt();
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Handle commands
|
|
265
|
-
if (input.startsWith('/')) {
|
|
266
|
-
await this.handleCommand(input);
|
|
267
|
-
} else {
|
|
268
|
-
// Regular chat message
|
|
269
|
-
this.client.sendChatMessage(input);
|
|
270
|
-
this.prompt();
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
module.exports = MultiplayerMode;
|
|
@@ -1,372 +0,0 @@
|
|
|
1
|
-
const WebSocket = require('ws');
|
|
2
|
-
const http = require('http');
|
|
3
|
-
const net = require('net');
|
|
4
|
-
const uuid = require('uuid');
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
|
|
7
|
-
async function findAvailablePort(startPort = 8080, maxAttempts = 10) {
|
|
8
|
-
let port = startPort;
|
|
9
|
-
let attempts = 0;
|
|
10
|
-
|
|
11
|
-
while (attempts < maxAttempts) {
|
|
12
|
-
const inUse = await new Promise((resolve) => {
|
|
13
|
-
const server = net.createServer()
|
|
14
|
-
.once('error', () => resolve(true))
|
|
15
|
-
.once('listening', () => {
|
|
16
|
-
server.close();
|
|
17
|
-
resolve(false);
|
|
18
|
-
})
|
|
19
|
-
.listen(port);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (!inUse) {
|
|
23
|
-
return port;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
port++;
|
|
27
|
-
attempts++;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
throw new Error(`Could not find an available port after ${maxAttempts} attempts`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
class MultiplayerServer {
|
|
34
|
-
constructor(port = 8080) {
|
|
35
|
-
this.port = port;
|
|
36
|
-
this.sessions = new Map();
|
|
37
|
-
this.clients = new Map();
|
|
38
|
-
this.tasks = new Map();
|
|
39
|
-
this.httpServer = null;
|
|
40
|
-
this.wss = null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async start() {
|
|
44
|
-
try {
|
|
45
|
-
this.port = await findAvailablePort(this.port);
|
|
46
|
-
|
|
47
|
-
// Create HTTP server
|
|
48
|
-
this.httpServer = http.createServer();
|
|
49
|
-
|
|
50
|
-
// Create WebSocket server
|
|
51
|
-
this.wss = new WebSocket.Server({ server: this.httpServer });
|
|
52
|
-
|
|
53
|
-
// Setup event handlers
|
|
54
|
-
this.setupEventHandlers();
|
|
55
|
-
|
|
56
|
-
// Start listening
|
|
57
|
-
return new Promise((resolve, reject) => {
|
|
58
|
-
this.httpServer.on('error', (error) => {
|
|
59
|
-
console.error(chalk.red('HTTP server error:'), error);
|
|
60
|
-
reject(error);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
this.httpServer.listen(this.port, () => {
|
|
64
|
-
console.log(chalk.green(`Multiplayer server started on port ${this.port}`));
|
|
65
|
-
resolve(this.port);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error(chalk.red('Failed to start multiplayer server:'), error.message);
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
static async startAsChildProcess(port = 8080) {
|
|
75
|
-
const { spawn } = require('child_process');
|
|
76
|
-
const serverProcess = spawn('node', [__filename, '--port', port.toString()], {
|
|
77
|
-
detached: true,
|
|
78
|
-
stdio: 'inherit',
|
|
79
|
-
windowsHide: true
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
serverProcess.on('error', (error) => {
|
|
83
|
-
console.error('Failed to start server process:', error);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
serverProcess.unref();
|
|
87
|
-
console.log(`Multiplayer server starting on port ${port}...`);
|
|
88
|
-
return serverProcess;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
setupEventHandlers() {
|
|
92
|
-
if (!this.wss) {
|
|
93
|
-
throw new Error('WebSocket server not initialized');
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
this.wss.on('connection', (ws) => {
|
|
97
|
-
let clientId = uuid.v4();
|
|
98
|
-
let sessionId = null;
|
|
99
|
-
let clientInfo = {};
|
|
100
|
-
|
|
101
|
-
// Store client
|
|
102
|
-
this.clients.set(clientId, { ws, clientInfo });
|
|
103
|
-
|
|
104
|
-
ws.on('message', (data) => {
|
|
105
|
-
try {
|
|
106
|
-
const message = typeof data === 'string' ? JSON.parse(data) : data;
|
|
107
|
-
this.handleMessage(ws, clientId, message);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.error('Error processing message:', error);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
ws.on('close', () => {
|
|
114
|
-
// Remove client from any session
|
|
115
|
-
if (sessionId && this.sessions.has(sessionId)) {
|
|
116
|
-
const session = this.sessions.get(sessionId);
|
|
117
|
-
if (session && session.clients) {
|
|
118
|
-
delete session.clients[clientId];
|
|
119
|
-
|
|
120
|
-
// Notify other clients
|
|
121
|
-
this.broadcastToSession(sessionId, {
|
|
122
|
-
type: 'agent_left',
|
|
123
|
-
clientId,
|
|
124
|
-
clientInfo,
|
|
125
|
-
timestamp: new Date().toISOString()
|
|
126
|
-
}, clientId);
|
|
127
|
-
|
|
128
|
-
// Clean up empty sessions
|
|
129
|
-
if (Object.keys(session.clients).length === 0) {
|
|
130
|
-
this.sessions.delete(sessionId);
|
|
131
|
-
console.log(`Session ${sessionId} ended (no clients)`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Remove client from clients map
|
|
137
|
-
this.clients.delete(clientId);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
ws.on('error', (error) => {
|
|
141
|
-
console.error('WebSocket error:', error);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
handleMessage(ws, message) {
|
|
147
|
-
try {
|
|
148
|
-
const data = typeof message === 'string' ? JSON.parse(message) : message;
|
|
149
|
-
const client = this.clients.get(ws);
|
|
150
|
-
|
|
151
|
-
switch (data.type) {
|
|
152
|
-
case 'client_info':
|
|
153
|
-
if (client) {
|
|
154
|
-
Object.assign(client, data.clientInfo);
|
|
155
|
-
this.broadcastToSession(client.sessionId, {
|
|
156
|
-
type: 'client_updated',
|
|
157
|
-
clientId: client.id,
|
|
158
|
-
clientInfo: data.clientInfo
|
|
159
|
-
}, ws);
|
|
160
|
-
}
|
|
161
|
-
break;
|
|
162
|
-
|
|
163
|
-
case 'create_session':
|
|
164
|
-
this.handleCreateSession(ws, data);
|
|
165
|
-
break;
|
|
166
|
-
|
|
167
|
-
case 'join_session':
|
|
168
|
-
this.handleJoinSession(ws, data);
|
|
169
|
-
break;
|
|
170
|
-
|
|
171
|
-
case 'collaborate':
|
|
172
|
-
if (client && client.sessionId) {
|
|
173
|
-
const taskId = uuid.v4();
|
|
174
|
-
this.tasks.set(taskId, {
|
|
175
|
-
id: taskId,
|
|
176
|
-
sessionId: client.sessionId,
|
|
177
|
-
prompt: data.prompt,
|
|
178
|
-
status: 'in_progress',
|
|
179
|
-
createdBy: client.id,
|
|
180
|
-
createdAt: new Date().toISOString(),
|
|
181
|
-
results: []
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
this.broadcastToSession(client.sessionId, {
|
|
185
|
-
type: 'task_assigned',
|
|
186
|
-
taskId,
|
|
187
|
-
prompt: data.prompt,
|
|
188
|
-
assignedBy: client.id
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
break;
|
|
192
|
-
|
|
193
|
-
case 'task_result':
|
|
194
|
-
if (client && client.sessionId && data.taskId) {
|
|
195
|
-
const task = this.tasks.get(data.taskId);
|
|
196
|
-
if (task) {
|
|
197
|
-
task.results.push({
|
|
198
|
-
clientId: client.id,
|
|
199
|
-
result: data.result,
|
|
200
|
-
timestamp: new Date().toISOString()
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
// Check if all clients have responded
|
|
204
|
-
const session = this.sessions.get(client.sessionId);
|
|
205
|
-
if (session && task.results.length >= Object.keys(session.clients).length) {
|
|
206
|
-
task.status = 'completed';
|
|
207
|
-
this.broadcastToSession(client.sessionId, {
|
|
208
|
-
type: 'task_completed',
|
|
209
|
-
taskId: task.id,
|
|
210
|
-
results: task.results
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
break;
|
|
216
|
-
|
|
217
|
-
case 'chat':
|
|
218
|
-
case 'work_update':
|
|
219
|
-
case 'task_update':
|
|
220
|
-
this.broadcastToSession(data.sessionId, data);
|
|
221
|
-
break;
|
|
222
|
-
|
|
223
|
-
default:
|
|
224
|
-
console.log('Unknown message type:', data.type);
|
|
225
|
-
}
|
|
226
|
-
} catch (error) {
|
|
227
|
-
console.error('Error handling message:', error);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
handleCreateSession(ws, data) {
|
|
232
|
-
const sessionId = uuid.v4();
|
|
233
|
-
const clientId = uuid.v4();
|
|
234
|
-
|
|
235
|
-
// Store client info
|
|
236
|
-
this.clients.set(ws, {
|
|
237
|
-
id: clientId,
|
|
238
|
-
sessionId,
|
|
239
|
-
ws,
|
|
240
|
-
isHost: true,
|
|
241
|
-
name: data.name || `Agent-${Math.random().toString(36).substr(2, 4)}`,
|
|
242
|
-
role: data.role || 'Assistant',
|
|
243
|
-
model: data.model || 'default'
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
this.sessions.set(sessionId, {
|
|
247
|
-
id: sessionId,
|
|
248
|
-
clients: { [clientId]: true },
|
|
249
|
-
workHistory: [],
|
|
250
|
-
createdAt: new Date().toISOString(),
|
|
251
|
-
hostId: clientId
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// Send session created confirmation
|
|
255
|
-
ws.send(JSON.stringify({
|
|
256
|
-
type: 'session_created',
|
|
257
|
-
sessionId,
|
|
258
|
-
clientId,
|
|
259
|
-
isHost: true
|
|
260
|
-
}));
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
handleJoinSession(ws, data) {
|
|
264
|
-
const { sessionId } = data;
|
|
265
|
-
const clientId = data.clientId || uuid.v4();
|
|
266
|
-
|
|
267
|
-
// If session doesn't exist, create it (this allows for reconnection)
|
|
268
|
-
if (!this.sessions.has(sessionId)) {
|
|
269
|
-
console.log(`Session ${sessionId} not found, creating new session`);
|
|
270
|
-
this.sessions.set(sessionId, {
|
|
271
|
-
id: sessionId,
|
|
272
|
-
clients: {},
|
|
273
|
-
createdAt: new Date().toISOString(),
|
|
274
|
-
hostId: clientId, // First joiner becomes host if session didn't exist
|
|
275
|
-
tasks: []
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const session = this.sessions.get(sessionId);
|
|
280
|
-
|
|
281
|
-
// Check if session is full (max 4 agents)
|
|
282
|
-
if (Object.keys(session.clients).length >= 4 && !session.clients[clientId]) {
|
|
283
|
-
ws.send(JSON.stringify({
|
|
284
|
-
type: 'error',
|
|
285
|
-
message: 'Session is full (max 4 agents)'
|
|
286
|
-
}));
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Store/update client info
|
|
291
|
-
const existingClient = Array.from(this.clients.entries())
|
|
292
|
-
.find(([_, c]) => c.id === clientId)?.[1];
|
|
293
|
-
|
|
294
|
-
const clientInfo = {
|
|
295
|
-
id: clientId,
|
|
296
|
-
sessionId,
|
|
297
|
-
ws,
|
|
298
|
-
isHost: session.hostId === clientId,
|
|
299
|
-
name: data.name || existingClient?.name || `Agent-${clientId.substr(0, 4)}`,
|
|
300
|
-
role: data.role || existingClient?.role || 'Assistant',
|
|
301
|
-
model: data.model || existingClient?.model || 'default',
|
|
302
|
-
joinedAt: new Date().toISOString(),
|
|
303
|
-
lastSeen: new Date().toISOString()
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// Add client to session
|
|
307
|
-
session.clients[clientId] = clientInfo;
|
|
308
|
-
this.clients.set(ws, clientInfo);
|
|
309
|
-
|
|
310
|
-
// Send session joined confirmation
|
|
311
|
-
ws.send(JSON.stringify({
|
|
312
|
-
type: 'session_joined',
|
|
313
|
-
sessionId,
|
|
314
|
-
clientId,
|
|
315
|
-
clientInfo,
|
|
316
|
-
isHost: clientInfo.isHost,
|
|
317
|
-
sessionInfo: {
|
|
318
|
-
id: session.id,
|
|
319
|
-
createdAt: session.createdAt,
|
|
320
|
-
clientCount: Object.keys(session.clients).length,
|
|
321
|
-
hostId: session.hostId
|
|
322
|
-
}
|
|
323
|
-
}));
|
|
324
|
-
|
|
325
|
-
// Notify other clients in the session
|
|
326
|
-
this.broadcastToSession(sessionId, {
|
|
327
|
-
type: 'agent_joined',
|
|
328
|
-
clientId,
|
|
329
|
-
clientInfo,
|
|
330
|
-
timestamp: new Date().toISOString()
|
|
331
|
-
}, clientId);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
broadcastToSession(sessionId, message, excludeClientId = null) {
|
|
335
|
-
if (!this.sessions.has(sessionId)) return;
|
|
336
|
-
|
|
337
|
-
const session = this.sessions.get(sessionId);
|
|
338
|
-
const messageStr = JSON.stringify(message);
|
|
339
|
-
|
|
340
|
-
Object.entries(session.clients).forEach(([id, client]) => {
|
|
341
|
-
if (id !== excludeClientId && client.ws && client.ws.readyState === WebSocket.OPEN) {
|
|
342
|
-
client.ws.send(messageStr);
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
start() {
|
|
348
|
-
return new Promise((resolve, reject) => {
|
|
349
|
-
this.httpServer = http.createServer();
|
|
350
|
-
this.wss = new WebSocket.Server({ server: this.httpServer });
|
|
351
|
-
this.setupEventHandlers();
|
|
352
|
-
|
|
353
|
-
this.httpServer.on('error', (error) => {
|
|
354
|
-
console.error(chalk.red('HTTP server error:'), error);
|
|
355
|
-
reject(error);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
this.httpServer.listen(this.port, () => {
|
|
359
|
-
console.log(chalk.green(`Multiplayer server started on port ${this.port}`));
|
|
360
|
-
resolve(this.port);
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Start server if run directly
|
|
367
|
-
if (require.main === module) {
|
|
368
|
-
const server = new MultiplayerServer();
|
|
369
|
-
server.start();
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
module.exports = MultiplayerServer;
|