agentgui 1.0.765 → 1.0.767

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.
@@ -0,0 +1,80 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import * as toolInstallMachine from './tool-install-machine.js';
5
+ import * as execMachine from './execution-machine.js';
6
+
7
+ export function register(deps) {
8
+ const { sendJSON, queries, activeExecutions, messageQueues, syncClients, wsOptimizer, _errLogPath } = deps;
9
+ const routes = {};
10
+
11
+ routes['GET /api/debug'] = async (req, res) => {
12
+ const execSnap = {};
13
+ for (const [k, v] of activeExecutions) execSnap[k] = { pid: v.pid, startTime: v.startTime, sessionId: v.sessionId, lastActivity: v.lastActivity };
14
+ const queueSnap = {};
15
+ for (const [k, v] of messageQueues) queueSnap[k] = { length: v.length, messageIds: v.map(m => m.messageId) };
16
+ let streamingConvs = [];
17
+ try { streamingConvs = queries.getConversationsList().filter(c => c.isStreaming).map(c => ({ id: c.id, isStreaming: c.isStreaming })); } catch (_) {}
18
+ const clientSubs = [];
19
+ for (const ws of syncClients) clientSubs.push({ clientId: ws.clientId, subs: ws.subscriptions ? [...ws.subscriptions] : [] });
20
+ let recentErrors = [];
21
+ try {
22
+ const raw = fs.readFileSync(_errLogPath, 'utf8');
23
+ recentErrors = raw.trim().split('\n').slice(-20).map(l => { try { return JSON.parse(l); } catch { return l; } });
24
+ } catch (_) {}
25
+ sendJSON(req, res, 200, { activeExecutions: execSnap, messageQueues: queueSnap, streamingConversations: streamingConvs, wsClients: clientSubs, recentErrors });
26
+ };
27
+
28
+ routes['GET /api/backup'] = async (req, res) => {
29
+ const dbPath = path.join(os.homedir(), '.gmgui', 'data.db');
30
+ if (!fs.existsSync(dbPath)) { sendJSON(req, res, 404, { error: 'Database not found' }); return; }
31
+ const stat = fs.statSync(dbPath);
32
+ res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename="agentgui-backup.db"', 'Content-Length': stat.size });
33
+ fs.createReadStream(dbPath).pipe(res);
34
+ };
35
+
36
+ routes['POST /api/restore'] = async (req, res) => {
37
+ const dbPath = path.join(os.homedir(), '.gmgui', 'data.db');
38
+ const backupPath = dbPath + '.bak-' + Date.now();
39
+ const chunks = [];
40
+ req.on('data', (chunk) => chunks.push(chunk));
41
+ req.on('end', () => {
42
+ try {
43
+ const body = Buffer.concat(chunks);
44
+ if (body.length < 100 || body.slice(0, 16).toString() !== 'SQLite format 3\0') {
45
+ sendJSON(req, res, 400, { error: 'Invalid SQLite database file' }); return;
46
+ }
47
+ fs.copyFileSync(dbPath, backupPath);
48
+ fs.writeFileSync(dbPath, body);
49
+ sendJSON(req, res, 200, { success: true, backupPath, size: body.length });
50
+ } catch (e) {
51
+ sendJSON(req, res, 500, { error: e.message });
52
+ }
53
+ });
54
+ };
55
+
56
+ routes['GET /api/debug/machines'] = async (req, res) => {
57
+ if (!process.env.DEBUG) { sendJSON(req, res, 404, { error: 'Not found' }); return; }
58
+ const toolSnap = {};
59
+ for (const [id, actor] of toolInstallMachine.getMachineActors()) {
60
+ const s = actor.getSnapshot();
61
+ toolSnap[id] = { state: s.value, context: s.context };
62
+ }
63
+ const execSnap = {};
64
+ for (const id of activeExecutions.keys()) {
65
+ const s = execMachine.snapshot(id);
66
+ if (s) execSnap[id] = { state: s.value, context: { pid: s.context.pid, sessionId: s.context.sessionId, queueLen: s.context.queue?.length } };
67
+ }
68
+ sendJSON(req, res, 200, { toolInstall: toolSnap, execution: execSnap });
69
+ };
70
+
71
+ routes['GET /api/ws-stats'] = async (req, res) => {
72
+ sendJSON(req, res, 200, wsOptimizer.getStats());
73
+ };
74
+
75
+ routes['_match'] = (method, pathOnly) => {
76
+ return routes[`${method} ${pathOnly}`] || null;
77
+ };
78
+
79
+ return routes;
80
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.765",
3
+ "version": "1.0.767",
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
@@ -3,7 +3,6 @@ import fs from 'fs';
3
3
  import path from 'path';
4
4
  import os from 'os';
5
5
  import zlib from 'zlib';
6
- import crypto from 'crypto';
7
6
  import { fileURLToPath } from 'url';
8
7
  import { WebSocketServer } from 'ws';
9
8
  import { execSync, spawn } from 'child_process';
@@ -16,14 +15,15 @@ import fsbrowse from 'fsbrowse';
16
15
  import { queries } from './database.js';
17
16
  import { runClaudeWithStreaming } from './lib/claude-runner.js';
18
17
  import { initializeDescriptors, getAgentDescriptor } from './lib/agent-descriptors.js';
19
- import { findCommand, queryACPServerAgents, discoverAgents, discoverExternalACPServers, initializeAgentDiscovery } from './lib/agent-discovery.js';
20
- import { getGeminiOAuthCreds, startGeminiOAuth, exchangeGeminiOAuthCode, handleGeminiOAuthCallback, getGeminiOAuthStatus, getGeminiOAuthState } from './lib/oauth-gemini.js';
21
- import { initSpeechManager, getSpeech, ensurePocketTtsSetup, voiceCacheManager, modelDownloadState, broadcastModelProgress, ensureModelsDownloaded, eagerTTS } from './lib/speech-manager.js';
18
+ import { discoverExternalACPServers, initializeAgentDiscovery } from './lib/agent-discovery.js';
19
+ import { startGeminiOAuth, exchangeGeminiOAuthCode, handleGeminiOAuthCallback, getGeminiOAuthStatus, getGeminiOAuthState } from './lib/oauth-gemini.js';
20
+ import { initSpeechManager, getSpeech, voiceCacheManager, modelDownloadState, ensureModelsDownloaded, eagerTTS } from './lib/speech-manager.js';
22
21
  import { register as registerSpeechRoutes } from './lib/routes-speech.js';
23
22
  import { register as registerOAuthRoutes } from './lib/routes-oauth.js';
24
23
  import { register as registerUtilRoutes } from './lib/routes-util.js';
25
24
  import { register as registerToolRoutes } from './lib/routes-tools.js';
26
25
  import { register as registerThreadRoutes } from './lib/routes-threads.js';
26
+ import { register as registerDebugRoutes } from './lib/routes-debug.js';
27
27
  import { startCodexOAuth, exchangeCodexOAuthCode, handleCodexOAuthCallback, getCodexOAuthStatus, getCodexOAuthState, CODEX_HOME, CODEX_AUTH_FILE } from './lib/oauth-codex.js';
28
28
  import { WSOptimizer } from './lib/ws-optimizer.js';
29
29
  import { WsRouter } from './lib/ws-protocol.js';
@@ -1206,79 +1206,9 @@ const server = http.createServer(async (req, res) => {
1206
1206
  }
1207
1207
 
1208
1208
 
1209
- if (pathOnly === '/api/debug' && req.method === 'GET') {
1210
- const execSnap = {};
1211
- for (const [k, v] of activeExecutions) execSnap[k] = { pid: v.pid, startTime: v.startTime, sessionId: v.sessionId, lastActivity: v.lastActivity };
1212
- const queueSnap = {};
1213
- for (const [k, v] of messageQueues) queueSnap[k] = { length: v.length, messageIds: v.map(m => m.messageId) };
1214
- let streamingConvs = [];
1215
- try { streamingConvs = queries.getConversationsList().filter(c => c.isStreaming).map(c => ({ id: c.id, isStreaming: c.isStreaming })); } catch (_) {}
1216
- const clientSubs = [];
1217
- for (const ws of syncClients) clientSubs.push({ clientId: ws.clientId, subs: ws.subscriptions ? [...ws.subscriptions] : [] });
1218
- let recentErrors = [];
1219
- try {
1220
- const raw = fs.readFileSync(_errLogPath, 'utf8');
1221
- recentErrors = raw.trim().split('\n').slice(-20).map(l => { try { return JSON.parse(l); } catch { return l; } });
1222
- } catch (_) {}
1223
- sendJSON(req, res, 200, { activeExecutions: execSnap, messageQueues: queueSnap, streamingConversations: streamingConvs, wsClients: clientSubs, recentErrors });
1224
- return;
1225
- }
1226
-
1227
- if (pathOnly === '/api/backup' && req.method === 'GET') {
1228
- const dbPath = path.join(os.homedir(), '.gmgui', 'data.db');
1229
- if (!fs.existsSync(dbPath)) { sendJSON(req, res, 404, { error: 'Database not found' }); return; }
1230
- const stat = fs.statSync(dbPath);
1231
- res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename="agentgui-backup.db"', 'Content-Length': stat.size });
1232
- fs.createReadStream(dbPath).pipe(res);
1233
- return;
1234
- }
1235
1209
 
1236
- if (pathOnly === '/api/restore' && req.method === 'POST') {
1237
- const dbPath = path.join(os.homedir(), '.gmgui', 'data.db');
1238
- const backupPath = dbPath + '.bak-' + Date.now();
1239
- const chunks = [];
1240
- req.on('data', (chunk) => chunks.push(chunk));
1241
- req.on('end', () => {
1242
- try {
1243
- const body = Buffer.concat(chunks);
1244
- if (body.length < 100 || body.slice(0, 16).toString() !== 'SQLite format 3\0') {
1245
- sendJSON(req, res, 400, { error: 'Invalid SQLite database file' });
1246
- return;
1247
- }
1248
- fs.copyFileSync(dbPath, backupPath);
1249
- fs.writeFileSync(dbPath, body);
1250
- sendJSON(req, res, 200, { success: true, backupPath, size: body.length });
1251
- } catch (e) {
1252
- sendJSON(req, res, 500, { error: e.message });
1253
- }
1254
- });
1255
- return;
1256
- }
1257
-
1258
- if (pathOnly === '/api/debug/machines' && req.method === 'GET' && process.env.DEBUG) {
1259
- const toolSnap = {};
1260
- for (const [id, actor] of toolInstallMachine.getMachineActors()) {
1261
- const s = actor.getSnapshot();
1262
- toolSnap[id] = { state: s.value, context: s.context };
1263
- }
1264
- const execSnap = {};
1265
- const allExecConvIds = [...activeExecutions.keys()];
1266
- for (const id of allExecConvIds) {
1267
- const s = execMachine.snapshot(id);
1268
- if (s) execSnap[id] = { state: s.value, context: { pid: s.context.pid, sessionId: s.context.sessionId, queueLen: s.context.queue?.length } };
1269
- }
1270
- sendJSON(req, res, 200, { toolInstall: toolSnap, execution: execSnap });
1271
- return;
1272
- }
1273
-
1274
- const toolHandler = _toolRoutes._match(req.method, pathOnly);
1275
- if (toolHandler) { await toolHandler(req, res); return; }
1276
-
1277
- if (pathOnly === '/api/ws-stats' && req.method === 'GET') {
1278
- const stats = wsOptimizer.getStats();
1279
- sendJSON(req, res, 200, stats);
1280
- return;
1281
- }
1210
+ const debugHandler = _debugRoutes._match(req.method, pathOnly);
1211
+ if (debugHandler) { await debugHandler(req, res); return; }
1282
1212
 
1283
1213
  if (pathOnly === '/api/agents/search' && req.method === 'POST') {
1284
1214
  const body = await parseBody(req);
@@ -2919,6 +2849,7 @@ const _oauthRoutes = registerOAuthRoutes({ sendJSON, parseBody, PORT, BASE_URL,
2919
2849
  const _utilRoutes = registerUtilRoutes({ sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION });
2920
2850
  const _toolRoutes = registerToolRoutes({ sendJSON, parseBody, queries, broadcastSync, logError, toolManager });
2921
2851
  const _threadRoutes = registerThreadRoutes({ sendJSON, parseBody, queries });
2852
+ const _debugRoutes = registerDebugRoutes({ sendJSON, queries, activeExecutions, messageQueues, syncClients, wsOptimizer, _errLogPath });
2922
2853
 
2923
2854
  registerConvHandlers(wsRouter, {
2924
2855
  queries, activeExecutions, rateLimitState,
@@ -878,6 +878,8 @@ class AgentGUIClient {
878
878
  const wdInfo = conv?.workingDirectory ? `${this.escapeHtml(conv.workingDirectory)}` : '';
879
879
  const timestamp = new Date(conv?.created_at || Date.now()).toLocaleDateString();
880
880
  const metaParts = [timestamp];
881
+ if (conv?.agentType || conv?.agentId) metaParts.push(this.escapeHtml(conv.agentType || conv.agentId));
882
+ if (conv?.messageCount > 0) metaParts.push(`${conv.messageCount} msgs`);
881
883
  if (wdInfo) metaParts.push(wdInfo);
882
884
  outputEl.innerHTML = `
883
885
  <div class="conversation-header">