agentgui 1.0.766 → 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 +4 -72
|
@@ -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
|
@@ -23,6 +23,7 @@ import { register as registerOAuthRoutes } from './lib/routes-oauth.js';
|
|
|
23
23
|
import { register as registerUtilRoutes } from './lib/routes-util.js';
|
|
24
24
|
import { register as registerToolRoutes } from './lib/routes-tools.js';
|
|
25
25
|
import { register as registerThreadRoutes } from './lib/routes-threads.js';
|
|
26
|
+
import { register as registerDebugRoutes } from './lib/routes-debug.js';
|
|
26
27
|
import { startCodexOAuth, exchangeCodexOAuthCode, handleCodexOAuthCallback, getCodexOAuthStatus, getCodexOAuthState, CODEX_HOME, CODEX_AUTH_FILE } from './lib/oauth-codex.js';
|
|
27
28
|
import { WSOptimizer } from './lib/ws-optimizer.js';
|
|
28
29
|
import { WsRouter } from './lib/ws-protocol.js';
|
|
@@ -1205,79 +1206,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
1205
1206
|
}
|
|
1206
1207
|
|
|
1207
1208
|
|
|
1208
|
-
if (pathOnly === '/api/debug' && req.method === 'GET') {
|
|
1209
|
-
const execSnap = {};
|
|
1210
|
-
for (const [k, v] of activeExecutions) execSnap[k] = { pid: v.pid, startTime: v.startTime, sessionId: v.sessionId, lastActivity: v.lastActivity };
|
|
1211
|
-
const queueSnap = {};
|
|
1212
|
-
for (const [k, v] of messageQueues) queueSnap[k] = { length: v.length, messageIds: v.map(m => m.messageId) };
|
|
1213
|
-
let streamingConvs = [];
|
|
1214
|
-
try { streamingConvs = queries.getConversationsList().filter(c => c.isStreaming).map(c => ({ id: c.id, isStreaming: c.isStreaming })); } catch (_) {}
|
|
1215
|
-
const clientSubs = [];
|
|
1216
|
-
for (const ws of syncClients) clientSubs.push({ clientId: ws.clientId, subs: ws.subscriptions ? [...ws.subscriptions] : [] });
|
|
1217
|
-
let recentErrors = [];
|
|
1218
|
-
try {
|
|
1219
|
-
const raw = fs.readFileSync(_errLogPath, 'utf8');
|
|
1220
|
-
recentErrors = raw.trim().split('\n').slice(-20).map(l => { try { return JSON.parse(l); } catch { return l; } });
|
|
1221
|
-
} catch (_) {}
|
|
1222
|
-
sendJSON(req, res, 200, { activeExecutions: execSnap, messageQueues: queueSnap, streamingConversations: streamingConvs, wsClients: clientSubs, recentErrors });
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
if (pathOnly === '/api/backup' && req.method === 'GET') {
|
|
1227
|
-
const dbPath = path.join(os.homedir(), '.gmgui', 'data.db');
|
|
1228
|
-
if (!fs.existsSync(dbPath)) { sendJSON(req, res, 404, { error: 'Database not found' }); return; }
|
|
1229
|
-
const stat = fs.statSync(dbPath);
|
|
1230
|
-
res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename="agentgui-backup.db"', 'Content-Length': stat.size });
|
|
1231
|
-
fs.createReadStream(dbPath).pipe(res);
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
1209
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
const backupPath = dbPath + '.bak-' + Date.now();
|
|
1238
|
-
const chunks = [];
|
|
1239
|
-
req.on('data', (chunk) => chunks.push(chunk));
|
|
1240
|
-
req.on('end', () => {
|
|
1241
|
-
try {
|
|
1242
|
-
const body = Buffer.concat(chunks);
|
|
1243
|
-
if (body.length < 100 || body.slice(0, 16).toString() !== 'SQLite format 3\0') {
|
|
1244
|
-
sendJSON(req, res, 400, { error: 'Invalid SQLite database file' });
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
fs.copyFileSync(dbPath, backupPath);
|
|
1248
|
-
fs.writeFileSync(dbPath, body);
|
|
1249
|
-
sendJSON(req, res, 200, { success: true, backupPath, size: body.length });
|
|
1250
|
-
} catch (e) {
|
|
1251
|
-
sendJSON(req, res, 500, { error: e.message });
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
|
-
return;
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
if (pathOnly === '/api/debug/machines' && req.method === 'GET' && process.env.DEBUG) {
|
|
1258
|
-
const toolSnap = {};
|
|
1259
|
-
for (const [id, actor] of toolInstallMachine.getMachineActors()) {
|
|
1260
|
-
const s = actor.getSnapshot();
|
|
1261
|
-
toolSnap[id] = { state: s.value, context: s.context };
|
|
1262
|
-
}
|
|
1263
|
-
const execSnap = {};
|
|
1264
|
-
const allExecConvIds = [...activeExecutions.keys()];
|
|
1265
|
-
for (const id of allExecConvIds) {
|
|
1266
|
-
const s = execMachine.snapshot(id);
|
|
1267
|
-
if (s) execSnap[id] = { state: s.value, context: { pid: s.context.pid, sessionId: s.context.sessionId, queueLen: s.context.queue?.length } };
|
|
1268
|
-
}
|
|
1269
|
-
sendJSON(req, res, 200, { toolInstall: toolSnap, execution: execSnap });
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
const toolHandler = _toolRoutes._match(req.method, pathOnly);
|
|
1274
|
-
if (toolHandler) { await toolHandler(req, res); return; }
|
|
1275
|
-
|
|
1276
|
-
if (pathOnly === '/api/ws-stats' && req.method === 'GET') {
|
|
1277
|
-
const stats = wsOptimizer.getStats();
|
|
1278
|
-
sendJSON(req, res, 200, stats);
|
|
1279
|
-
return;
|
|
1280
|
-
}
|
|
1210
|
+
const debugHandler = _debugRoutes._match(req.method, pathOnly);
|
|
1211
|
+
if (debugHandler) { await debugHandler(req, res); return; }
|
|
1281
1212
|
|
|
1282
1213
|
if (pathOnly === '/api/agents/search' && req.method === 'POST') {
|
|
1283
1214
|
const body = await parseBody(req);
|
|
@@ -2918,6 +2849,7 @@ const _oauthRoutes = registerOAuthRoutes({ sendJSON, parseBody, PORT, BASE_URL,
|
|
|
2918
2849
|
const _utilRoutes = registerUtilRoutes({ sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION });
|
|
2919
2850
|
const _toolRoutes = registerToolRoutes({ sendJSON, parseBody, queries, broadcastSync, logError, toolManager });
|
|
2920
2851
|
const _threadRoutes = registerThreadRoutes({ sendJSON, parseBody, queries });
|
|
2852
|
+
const _debugRoutes = registerDebugRoutes({ sendJSON, queries, activeExecutions, messageQueues, syncClients, wsOptimizer, _errLogPath });
|
|
2921
2853
|
|
|
2922
2854
|
registerConvHandlers(wsRouter, {
|
|
2923
2855
|
queries, activeExecutions, rateLimitState,
|