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.
- package/lib/routes-debug.js +80 -0
- package/package.json +1 -1
- package/server.js +7 -76
- package/static/js/client.js +2 -0
|
@@ -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
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 {
|
|
20
|
-
import {
|
|
21
|
-
import { initSpeechManager, getSpeech,
|
|
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
|
-
|
|
1237
|
-
|
|
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,
|
package/static/js/client.js
CHANGED
|
@@ -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">
|