@tjamescouch/agentchat 0.16.5 → 0.17.0

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/agentchat.js CHANGED
@@ -213,109 +213,6 @@ program
213
213
  }
214
214
  });
215
215
 
216
- // Interactive connect command
217
- program
218
- .command('connect <server>')
219
- .description('Interactive connection (for debugging)')
220
- .option('-n, --name <name>', 'Agent name', `agent-${process.pid}`)
221
- .option('-i, --identity <file>', 'Path to identity file')
222
- .option('-j, --join <channels...>', 'Channels to join automatically')
223
- .action(async (server, options) => {
224
- try {
225
- const client = new AgentChatClient({ server, name: options.name, identity: options.identity });
226
- await client.connect();
227
-
228
- console.log(`Connected as ${client.agentId}`);
229
-
230
- // Auto-join channels
231
- if (options.join) {
232
- for (const ch of options.join) {
233
- await client.join(ch);
234
- console.log(`Joined ${ch}`);
235
- }
236
- }
237
-
238
- // Listen for messages
239
- client.on('message', (msg) => {
240
- console.log(`[${msg.to}] ${msg.from}: ${msg.content}`);
241
- });
242
-
243
- client.on('agent_joined', (msg) => {
244
- console.log(`* ${msg.agent} joined ${msg.channel}`);
245
- });
246
-
247
- client.on('agent_left', (msg) => {
248
- console.log(`* ${msg.agent} left ${msg.channel}`);
249
- });
250
-
251
- // Read from stdin
252
- console.log('Type messages as: #channel message or @agent message');
253
- console.log('Commands: /join #channel, /leave #channel, /channels, /quit');
254
-
255
- const readline = await import('readline');
256
- const rl = readline.createInterface({
257
- input: process.stdin,
258
- output: process.stdout
259
- });
260
-
261
- rl.on('line', async (line) => {
262
- line = line.trim();
263
- if (!line) return;
264
-
265
- // Commands
266
- if (line.startsWith('/')) {
267
- const [cmd, ...args] = line.slice(1).split(' ');
268
-
269
- switch (cmd) {
270
- case 'join':
271
- if (args[0]) {
272
- await client.join(args[0]);
273
- console.log(`Joined ${args[0]}`);
274
- }
275
- break;
276
- case 'leave':
277
- if (args[0]) {
278
- await client.leave(args[0]);
279
- console.log(`Left ${args[0]}`);
280
- }
281
- break;
282
- case 'channels':
283
- const channels = await client.listChannels();
284
- for (const ch of channels) {
285
- console.log(` ${ch.name} (${ch.agents})`);
286
- }
287
- break;
288
- case 'quit':
289
- case 'exit':
290
- client.disconnect();
291
- process.exit(0);
292
- break;
293
- default:
294
- console.log('Unknown command');
295
- }
296
- return;
297
- }
298
-
299
- // Messages: #channel msg or @agent msg
300
- const match = line.match(/^([@#][^\s]+)\s+(.+)$/);
301
- if (match) {
302
- await client.send(match[1], match[2]);
303
- } else {
304
- console.log('Format: #channel message or @agent message');
305
- }
306
- });
307
-
308
- rl.on('close', () => {
309
- client.disconnect();
310
- process.exit(0);
311
- });
312
-
313
- } catch (err) {
314
- console.error('Error:', err.message);
315
- process.exit(1);
316
- }
317
- });
318
-
319
216
  // Create channel command
320
217
  program
321
218
  .command('create <server> <channel>')
package/lib/server.js CHANGED
@@ -253,14 +253,36 @@ export class AgentChatServer {
253
253
  this._log('server_start', { port: this.port, host: this.host, tls });
254
254
 
255
255
  this.wss.on('connection', (ws, req) => {
256
- const ip = req.socket.remoteAddress;
257
- this._log('connection', { ip });
256
+ // Get real IP (X-Forwarded-For for proxied connections like Fly.io)
257
+ const forwardedFor = req.headers['x-forwarded-for'];
258
+ const realIp = forwardedFor ? forwardedFor.split(',')[0].trim() : req.socket.remoteAddress;
259
+ const userAgent = req.headers['user-agent'] || 'unknown';
260
+
261
+ // Store connection metadata on ws for later logging
262
+ ws._connectedAt = Date.now();
263
+ ws._realIp = realIp;
264
+ ws._userAgent = userAgent;
265
+
266
+ this._log('connection', {
267
+ ip: realIp,
268
+ proxy_ip: req.socket.remoteAddress,
269
+ user_agent: userAgent
270
+ });
258
271
 
259
272
  ws.on('message', (data) => {
260
273
  this._handleMessage(ws, data.toString());
261
274
  });
262
275
 
263
276
  ws.on('close', () => {
277
+ // Log if connection closed without ever identifying (drive-by)
278
+ if (!this.agents.has(ws)) {
279
+ const duration = ws._connectedAt ? Math.round((Date.now() - ws._connectedAt) / 1000) : 0;
280
+ this._log('connection_closed_unidentified', {
281
+ ip: ws._realIp,
282
+ duration_sec: duration,
283
+ user_agent: ws._userAgent
284
+ });
285
+ }
264
286
  this._handleDisconnect(ws);
265
287
  });
266
288
 
@@ -468,7 +490,19 @@ export class AgentChatServer {
468
490
  this.agents.set(ws, agent);
469
491
  this.agentById.set(id, ws);
470
492
 
471
- this._log('identify', { id, name: msg.name, hasPubkey: !!msg.pubkey });
493
+ // Determine if this is a new or returning identity
494
+ const isReturning = msg.pubkey && this.pubkeyToId.has(msg.pubkey);
495
+ const isEphemeral = !msg.pubkey;
496
+
497
+ this._log('identify', {
498
+ id,
499
+ name: msg.name,
500
+ hasPubkey: !!msg.pubkey,
501
+ returning: isReturning,
502
+ ephemeral: isEphemeral,
503
+ ip: ws._realIp,
504
+ user_agent: ws._userAgent
505
+ });
472
506
 
473
507
  this._send(ws, createMessage(ServerMessageType.WELCOME, {
474
508
  agent_id: `@${id}`,
@@ -1381,7 +1415,17 @@ export class AgentChatServer {
1381
1415
  const agent = this.agents.get(ws);
1382
1416
  if (!agent) return;
1383
1417
 
1384
- this._log('disconnect', { agent: agent.id });
1418
+ // Calculate connection duration
1419
+ const duration = ws._connectedAt ? Math.round((Date.now() - ws._connectedAt) / 1000) : 0;
1420
+ const channelCount = agent.channels.size;
1421
+
1422
+ this._log('disconnect', {
1423
+ agent: agent.id,
1424
+ duration_sec: duration,
1425
+ channels_joined: channelCount,
1426
+ had_pubkey: !!agent.pubkey,
1427
+ ip: ws._realIp
1428
+ });
1385
1429
 
1386
1430
  // Leave all channels
1387
1431
  for (const channelName of agent.channels) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tjamescouch/agentchat",
3
- "version": "0.16.5",
3
+ "version": "0.17.0",
4
4
  "description": "Real-time IRC-like communication protocol for AI agents",
5
5
  "main": "lib/client.js",
6
6
  "files": [