sam-coder-cli 1.0.29 → 1.0.31

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.
@@ -779,44 +779,50 @@ class MultiplayerClient extends EventEmitter {
779
779
  if (!sessionId) {
780
780
  throw new Error('Session ID is required');
781
781
  }
782
-
783
782
  if (!this.connected) {
784
- try {
785
- await this.connect();
786
- } catch (error) {
787
- throw new Error(`Failed to connect to server: ${error.message}`);
788
- }
783
+ await this.connect();
789
784
  }
790
785
 
791
786
  return new Promise((resolve, reject) => {
792
- this.sessionId = sessionId;
793
- this.isHost = false;
787
+ if (!this.ws) {
788
+ reject(new Error('Not connected to server'));
789
+ return;
790
+ }
791
+
792
+ const timeout = setTimeout(() => {
793
+ this.off('session_joined', onSessionJoined);
794
+ this.off('error', onError);
795
+ reject(new Error('Session join timed out'));
796
+ }, 10000); // 10 second timeout
794
797
 
795
798
  const onSessionJoined = (data) => {
796
- if (data.sessionId === this.sessionId) {
799
+ if (data.sessionId === sessionId) {
800
+ clearTimeout(timeout);
801
+ this.sessionId = sessionId;
802
+ this.isHost = data.isHost || false;
803
+ this.emit('session_joined', data);
797
804
  this.off('session_joined', onSessionJoined);
798
805
  this.off('error', onError);
799
- resolve();
806
+ resolve(data);
800
807
  }
801
808
  };
802
809
 
803
810
  const onError = (error) => {
811
+ clearTimeout(timeout);
804
812
  this.off('session_joined', onSessionJoined);
805
813
  this.off('error', onError);
806
814
  reject(error);
807
815
  };
808
816
 
809
- this.once('session_joined', onSessionJoined);
810
- this.once('error', onError);
817
+ this.on('session_joined', onSessionJoined);
818
+ this.on('error', onError);
811
819
 
812
820
  this.send({
813
821
  type: 'join_session',
814
- sessionId: this.sessionId,
815
- clientInfo: {
816
- name: this.name,
817
- role: this.role,
818
- model: this.model
819
- }
822
+ sessionId: sessionId,
823
+ clientId: this.clientId,
824
+ name: this.name,
825
+ role: this.role
820
826
  });
821
827
  });
822
828
  }
@@ -263,17 +263,23 @@ class MultiplayerServer {
263
263
  handleJoinSession(ws, data) {
264
264
  const { sessionId } = data;
265
265
  const clientId = data.clientId || uuid.v4();
266
- const session = this.sessions.get(sessionId);
267
-
268
- if (!session) {
269
- ws.send(JSON.stringify({
270
- type: 'error',
271
- message: 'Session not found'
272
- }));
273
- return;
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
+ });
274
277
  }
275
278
 
276
- if (Object.keys(session.clients).length >= 4) {
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]) {
277
283
  ws.send(JSON.stringify({
278
284
  type: 'error',
279
285
  message: 'Session is full (max 4 agents)'
@@ -292,45 +298,37 @@ class MultiplayerServer {
292
298
  isHost: session.hostId === clientId,
293
299
  name: data.name || existingClient?.name || `Agent-${clientId.substr(0, 4)}`,
294
300
  role: data.role || existingClient?.role || 'Assistant',
295
- model: data.model || existingClient?.model || 'default'
301
+ model: data.model || existingClient?.model || 'default',
302
+ joinedAt: new Date().toISOString(),
303
+ lastSeen: new Date().toISOString()
296
304
  };
297
305
 
306
+ // Add client to session
307
+ session.clients[clientId] = clientInfo;
298
308
  this.clients.set(ws, clientInfo);
299
- session.clients[clientId] = true;
300
-
301
- // Get all client infos for the session
302
- const sessionClients = Array.from(this.clients.values())
303
- .filter(c => c.sessionId === sessionId)
304
- .map(({ id, name, role, isHost }) => ({
305
- id,
306
- name,
307
- role,
308
- isHost
309
- }));
310
309
 
311
- // Send welcome message with session info
310
+ // Send session joined confirmation
312
311
  ws.send(JSON.stringify({
313
312
  type: 'session_joined',
314
313
  sessionId,
315
314
  clientId,
315
+ clientInfo,
316
316
  isHost: clientInfo.isHost,
317
- clients: sessionClients,
318
- workHistory: session.workHistory,
319
- pendingTasks: Array.from(this.tasks.values())
320
- .filter(t => t.sessionId === sessionId && t.status === 'in_progress')
317
+ sessionInfo: {
318
+ id: session.id,
319
+ createdAt: session.createdAt,
320
+ clientCount: Object.keys(session.clients).length,
321
+ hostId: session.hostId
322
+ }
321
323
  }));
322
324
 
323
- // Notify other clients
325
+ // Notify other clients in the session
324
326
  this.broadcastToSession(sessionId, {
325
327
  type: 'agent_joined',
326
328
  clientId,
327
- clientInfo: {
328
- id: clientId,
329
- name: clientInfo.name,
330
- role: clientInfo.role,
331
- isHost: clientInfo.isHost
332
- }
333
- }, ws);
329
+ clientInfo,
330
+ timestamp: new Date().toISOString()
331
+ }, clientId);
334
332
  }
335
333
 
336
334
  broadcastToSession(sessionId, message, excludeClientId = null) {
@@ -340,15 +338,27 @@ class MultiplayerServer {
340
338
  const messageStr = JSON.stringify(message);
341
339
 
342
340
  Object.entries(session.clients).forEach(([id, client]) => {
343
- if (id !== excludeClientId && client.ws.readyState === WebSocket.OPEN) {
341
+ if (id !== excludeClientId && client.ws && client.ws.readyState === WebSocket.OPEN) {
344
342
  client.ws.send(messageStr);
345
343
  }
346
344
  });
347
345
  }
348
346
 
349
347
  start() {
350
- this.server.listen(this.port, () => {
351
- console.log(`Multiplayer server running on ws://localhost:${this.port}`);
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
+ });
352
362
  });
353
363
  }
354
364
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sam-coder-cli",
3
- "version": "1.0.29",
3
+ "version": "1.0.31",
4
4
  "description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
5
5
  "main": "bin/agi-cli.js",
6
6
  "bin": {