agentgui 1.0.763 → 1.0.765

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.763",
3
+ "version": "1.0.765",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
package/server.js CHANGED
@@ -527,6 +527,10 @@ const server = http.createServer(async (req, res) => {
527
527
  }
528
528
 
529
529
  if (pathOnly === '/api/health' && req.method === 'GET') {
530
+ let dbStatus = { ok: true };
531
+ try { queries._db.prepare('SELECT 1').get(); } catch (e) { dbStatus = { ok: false, error: e.message }; }
532
+ const queueSizes = {};
533
+ for (const [k, v] of messageQueues) queueSizes[k] = v.length;
530
534
  sendJSON(req, res, 200, {
531
535
  status: 'ok',
532
536
  version: PKG_VERSION,
@@ -535,7 +539,9 @@ const server = http.createServer(async (req, res) => {
535
539
  activeExecutions: activeExecutions.size,
536
540
  wsClients: wss.clients.size,
537
541
  memory: process.memoryUsage(),
538
- acp: getACPStatus()
542
+ acp: getACPStatus(),
543
+ db: dbStatus,
544
+ queueSizes
539
545
  });
540
546
  return;
541
547
  }
@@ -1189,7 +1195,7 @@ const server = http.createServer(async (req, res) => {
1189
1195
  }
1190
1196
 
1191
1197
  if (pathOnly === '/api/agents' && req.method === 'GET') {
1192
- console.log(`[API /api/agents] Returning ${discoveredAgents.length} agents:`, discoveredAgents.map(a => a.id).join(', '));
1198
+ debugLog(`[API /api/agents] Returning ${discoveredAgents.length} agents`);
1193
1199
  sendJSON(req, res, 200, { agents: discoveredAgents });
1194
1200
  return;
1195
1201
  }
@@ -1199,21 +1205,6 @@ const server = http.createServer(async (req, res) => {
1199
1205
  return;
1200
1206
  }
1201
1207
 
1202
- if (pathOnly === '/api/health' && req.method === 'GET') {
1203
- let dbStatus = { ok: true };
1204
- try { queries._db.prepare('SELECT 1').get(); } catch (e) { dbStatus = { ok: false, error: e.message }; }
1205
- const queueSizes = {};
1206
- for (const [k, v] of messageQueues) queueSizes[k] = v.length;
1207
- sendJSON(req, res, 200, {
1208
- uptime: process.uptime(),
1209
- db: dbStatus,
1210
- activeExecutionCount: activeExecutions.size,
1211
- queueSizes,
1212
- wsClientCount: syncClients.size,
1213
- memory: process.memoryUsage()
1214
- });
1215
- return;
1216
- }
1217
1208
 
1218
1209
  if (pathOnly === '/api/debug' && req.method === 'GET') {
1219
1210
  const execSnap = {};
@@ -2093,7 +2084,8 @@ function serveFile(filePath, res, req) {
2093
2084
  fs.readFile(filePath, (err2, data) => {
2094
2085
  if (err2) { res.writeHead(500); res.end('Server error'); return; }
2095
2086
  let content = data.toString();
2096
- const baseTag = `<script>window.__BASE_URL='${BASE_URL}';window.__SERVER_VERSION='${PKG_VERSION}';</script>`;
2087
+ const wsToken = process.env.PASSWORD ? `window.__WS_TOKEN='${process.env.PASSWORD.replace(/'/g, "\\'")}';` : '';
2088
+ const baseTag = `<script>window.__BASE_URL='${BASE_URL}';window.__SERVER_VERSION='${PKG_VERSION}';${wsToken}</script>`;
2097
2089
  content = content.replace('<head>', `<head>\n <base href="${BASE_URL}/">\n ` + baseTag);
2098
2090
  content = content.replace(/(href|src)="vendor\//g, `$1="${BASE_URL}/vendor/`);
2099
2091
  content = content.replace(/(src)="\/gm\/js\//g, `$1="${BASE_URL}/js/`);
@@ -2821,12 +2813,18 @@ const subscriptionIndex = new Map();
2821
2813
  const pm2Subscribers = new Set();
2822
2814
 
2823
2815
  wss.on('connection', (ws, req) => {
2824
- // req.url in WebSocket is just the path (e.g., '/gm/sync'), not a full URL
2825
- const wsPath = req.url.startsWith(BASE_URL) ? req.url.slice(BASE_URL.length) : req.url;
2826
- if (wsPath === '/hot-reload') {
2816
+ const _pwd = process.env.PASSWORD;
2817
+ if (_pwd) {
2818
+ const url = new URL(req.url, 'http://localhost');
2819
+ const token = url.searchParams.get('token');
2820
+ if (token !== _pwd) { ws.close(4001, 'Unauthorized'); return; }
2821
+ }
2822
+ const wsPath = req.url.split('?')[0];
2823
+ const wsRoute = wsPath.startsWith(BASE_URL) ? wsPath.slice(BASE_URL.length) : wsPath;
2824
+ if (wsRoute === '/hot-reload') {
2827
2825
  hotReloadClients.push(ws);
2828
2826
  ws.on('close', () => { const i = hotReloadClients.indexOf(ws); if (i > -1) hotReloadClients.splice(i, 1); });
2829
- } else if (wsPath === '/sync') {
2827
+ } else if (wsRoute === '/sync') {
2830
2828
  syncClients.add(ws);
2831
2829
  ws.isAlive = true;
2832
2830
  ws.subscriptions = new Set();
@@ -2857,7 +2855,7 @@ wss.on('connection', (ws, req) => {
2857
2855
  if (ws.pm2Subscribed) {
2858
2856
  pm2Subscribers.delete(ws);
2859
2857
  }
2860
- console.log(`[WebSocket] Client ${ws.clientId} disconnected`);
2858
+ debugLog(`[WebSocket] Client ${ws.clientId} disconnected`);
2861
2859
  });
2862
2860
  }
2863
2861
  });
@@ -2935,13 +2933,13 @@ registerMsgHandlers(wsRouter, {
2935
2933
 
2936
2934
  registerQueueHandlers(wsRouter, { queries, messageQueues, broadcastSync });
2937
2935
 
2938
- console.log('[INIT] About to call registerSessionHandlers, discoveredAgents.length:', discoveredAgents.length);
2936
+ debugLog('[INIT] registerSessionHandlers, agents: ' + discoveredAgents.length);
2939
2937
  registerSessionHandlers(wsRouter, {
2940
2938
  db: queries, discoveredAgents, modelCache,
2941
2939
  getAgentDescriptor, activeScripts, broadcastSync,
2942
2940
  startGeminiOAuth: (req) => startGeminiOAuth(req, { PORT, BASE_URL, rootDir }), geminiOAuthState: getGeminiOAuthState
2943
2941
  });
2944
- console.log('[INIT] registerSessionHandlers completed');
2942
+ debugLog('[INIT] registerSessionHandlers completed');
2945
2943
 
2946
2944
  registerRunHandlers(wsRouter, {
2947
2945
  queries, discoveredAgents, activeExecutions, activeProcessesByRunId,
@@ -3014,7 +3012,7 @@ wsRouter.onLegacy((data, ws) => {
3014
3012
  if (data.conversationId && checkpointManager.hasPendingCheckpoint(data.conversationId)) {
3015
3013
  const checkpoint = checkpointManager.getPendingCheckpoint(data.conversationId);
3016
3014
  if (checkpoint) {
3017
- console.log(`[checkpoint] Injecting ${checkpoint.events.length} events to client for ${data.conversationId}`);
3015
+ debugLog(`[checkpoint] Injecting ${checkpoint.events.length} events to client for ${data.conversationId}`);
3018
3016
 
3019
3017
  const latestSession = queries.getLatestSession(data.conversationId);
3020
3018
  if (latestSession) {
@@ -276,7 +276,8 @@
276
276
  }
277
277
 
278
278
  .conversation-item-delete,
279
- .conversation-item-archive {
279
+ .conversation-item-archive,
280
+ .conversation-item-export {
280
281
  flex-shrink: 0;
281
282
  width: 28px;
282
283
  height: 28px;
@@ -294,7 +295,8 @@
294
295
  }
295
296
 
296
297
  .conversation-item:hover .conversation-item-delete,
297
- .conversation-item:hover .conversation-item-archive {
298
+ .conversation-item:hover .conversation-item-archive,
299
+ .conversation-item:hover .conversation-item-export {
298
300
  opacity: 1;
299
301
  }
300
302
 
@@ -303,18 +305,25 @@
303
305
  color: white;
304
306
  }
305
307
 
308
+ .conversation-item-export:hover {
309
+ background-color: #3b82f6;
310
+ color: white;
311
+ }
312
+
306
313
  .conversation-item-archive:hover {
307
314
  background-color: #f59e0b;
308
315
  color: white;
309
316
  }
310
317
 
311
318
  .conversation-item.active .conversation-item-delete,
312
- .conversation-item.active .conversation-item-archive {
319
+ .conversation-item.active .conversation-item-archive,
320
+ .conversation-item.active .conversation-item-export {
313
321
  color: rgba(255,255,255,0.8);
314
322
  }
315
323
 
316
324
  .conversation-item.active .conversation-item-delete:hover,
317
- .conversation-item.active .conversation-item-archive:hover {
325
+ .conversation-item.active .conversation-item-archive:hover,
326
+ .conversation-item.active .conversation-item-export:hover {
318
327
  background-color: rgba(255,255,255,0.2);
319
328
  color: white;
320
329
  }
@@ -117,6 +117,12 @@ class ConversationManager {
117
117
 
118
118
  setupDelegatedListeners() {
119
119
  this.listEl.addEventListener('click', (e) => {
120
+ const exportBtn = e.target.closest('[data-export-conv]');
121
+ if (exportBtn) {
122
+ e.stopPropagation();
123
+ this.exportConversation(exportBtn.dataset.exportConv);
124
+ return;
125
+ }
120
126
  const archiveBtn = e.target.closest('[data-archive-conv]');
121
127
  if (archiveBtn) {
122
128
  e.stopPropagation();
@@ -483,6 +489,13 @@ class ConversationManager {
483
489
  h('div', { class: 'conversation-item-title' }, ...(badge ? [badge, title] : [title])),
484
490
  h('div', { class: 'conversation-item-meta' }, metaParts.join(' \u2022 '))
485
491
  ),
492
+ h('button', { class: 'conversation-item-export', title: 'Export as markdown', 'data-export-conv': conv.id },
493
+ h('svg', { width: '14', height: '14', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2' },
494
+ h('path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' }),
495
+ h('polyline', { points: '7 10 12 15 17 10' }),
496
+ h('line', { x1: '12', y1: '15', x2: '12', y2: '3' })
497
+ )
498
+ ),
486
499
  h('button', { class: 'conversation-item-archive', title: 'Archive conversation', 'data-archive-conv': conv.id },
487
500
  h('svg', { width: '14', height: '14', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2' },
488
501
  h('path', { d: 'M21 8v13H3V8' }),
@@ -597,6 +610,21 @@ class ConversationManager {
597
610
  this.render();
598
611
  }
599
612
 
613
+ async exportConversation(convId) {
614
+ try {
615
+ const result = await window.wsClient.rpc('conv.export', { id: convId, format: 'markdown' });
616
+ const blob = new Blob([result.markdown], { type: 'text/markdown' });
617
+ const url = URL.createObjectURL(blob);
618
+ const a = document.createElement('a');
619
+ a.href = url;
620
+ a.download = (result.title || 'conversation').replace(/[^a-zA-Z0-9_-]/g, '_') + '.md';
621
+ a.click();
622
+ URL.revokeObjectURL(url);
623
+ } catch (e) {
624
+ console.error('[export] Failed:', e.message);
625
+ }
626
+ }
627
+
600
628
  async archiveConversation(convId) {
601
629
  try {
602
630
  const resp = await fetch(`${window.__BASE_URL || ''}/api/conversations/${convId}/archive`, { method: 'POST' });
@@ -113,7 +113,9 @@ class WebSocketManager {
113
113
  getWebSocketURL() {
114
114
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
115
115
  const baseURL = window.__BASE_URL || '/gm';
116
- return `${protocol}//${window.location.host}${baseURL}/sync`;
116
+ let url = `${protocol}//${window.location.host}${baseURL}/sync`;
117
+ if (window.__WS_TOKEN) url += `?token=${encodeURIComponent(window.__WS_TOKEN)}`;
118
+ return url;
117
119
  }
118
120
 
119
121
  async connect() {