agentgui 1.0.759 → 1.0.760
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-util.js +106 -0
- package/package.json +1 -1
- package/server.js +4 -103
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
|
|
6
|
+
export function register(deps) {
|
|
7
|
+
const { sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION } = deps;
|
|
8
|
+
const routes = {};
|
|
9
|
+
|
|
10
|
+
routes['GET /api/import/claude-code'] = async (req, res) => {
|
|
11
|
+
const result = queries.importClaudeCodeConversations();
|
|
12
|
+
sendJSON(req, res, 200, { imported: result });
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
routes['GET /api/discover/claude-code'] = async (req, res) => {
|
|
16
|
+
const discovered = queries.discoverClaudeCodeConversations();
|
|
17
|
+
sendJSON(req, res, 200, { discovered });
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
routes['GET /api/home'] = async (req, res) => {
|
|
21
|
+
sendJSON(req, res, 200, { home: os.homedir(), cwd: STARTUP_CWD });
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
routes['GET /api/version'] = async (req, res) => {
|
|
25
|
+
sendJSON(req, res, 200, { version: PKG_VERSION });
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
routes['POST /api/clone'] = async (req, res) => {
|
|
29
|
+
const body = await parseBody(req);
|
|
30
|
+
const repo = (body.repo || '').trim();
|
|
31
|
+
if (!repo || !/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo)) {
|
|
32
|
+
sendJSON(req, res, 400, { error: 'Invalid repo format. Use org/repo or user/repo' });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const cloneDir = STARTUP_CWD || os.homedir();
|
|
36
|
+
const repoName = repo.split('/')[1];
|
|
37
|
+
const targetPath = path.join(cloneDir, repoName);
|
|
38
|
+
if (fs.existsSync(targetPath)) {
|
|
39
|
+
sendJSON(req, res, 409, { error: `Directory already exists: ${repoName}`, path: targetPath });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const isWindows = os.platform() === 'win32';
|
|
44
|
+
execSync('git clone https://github.com/' + repo + '.git', {
|
|
45
|
+
cwd: cloneDir, encoding: 'utf-8', timeout: 120000,
|
|
46
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
47
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
48
|
+
shell: isWindows
|
|
49
|
+
});
|
|
50
|
+
sendJSON(req, res, 200, { ok: true, repo, path: targetPath, name: repoName });
|
|
51
|
+
} catch (err) {
|
|
52
|
+
sendJSON(req, res, 500, { error: (err.stderr || err.message || 'Clone failed').trim() });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
routes['POST /api/folders'] = async (req, res) => {
|
|
57
|
+
const body = await parseBody(req);
|
|
58
|
+
const folderPath = body.path || STARTUP_CWD;
|
|
59
|
+
try {
|
|
60
|
+
const expandedPath = folderPath.startsWith('~') ? folderPath.replace('~', os.homedir()) : folderPath;
|
|
61
|
+
const entries = fs.readdirSync(expandedPath, { withFileTypes: true });
|
|
62
|
+
const folders = entries
|
|
63
|
+
.filter(e => e.isDirectory() && !e.name.startsWith('.'))
|
|
64
|
+
.map(e => ({ name: e.name }))
|
|
65
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
66
|
+
sendJSON(req, res, 200, { folders });
|
|
67
|
+
} catch (err) {
|
|
68
|
+
sendJSON(req, res, 400, { error: err.message });
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
routes['GET /api/git/check-remote-ownership'] = async (req, res) => {
|
|
73
|
+
try {
|
|
74
|
+
const isWindows = os.platform() === 'win32';
|
|
75
|
+
const result = execSync('git remote get-url origin' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
76
|
+
const remoteUrl = result.trim();
|
|
77
|
+
const statusResult = execSync('git status --porcelain' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
78
|
+
const hasChanges = statusResult.trim().length > 0;
|
|
79
|
+
const unpushedResult = execSync('git rev-list --count --not --remotes' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
80
|
+
const hasUnpushed = parseInt(unpushedResult.trim() || '0', 10) > 0;
|
|
81
|
+
const ownsRemote = !remoteUrl.includes('github.com/') || remoteUrl.includes(process.env.GITHUB_USER || '');
|
|
82
|
+
sendJSON(req, res, 200, { ownsRemote, hasChanges, hasUnpushed, remoteUrl });
|
|
83
|
+
} catch {
|
|
84
|
+
sendJSON(req, res, 200, { ownsRemote: false, hasChanges: false, hasUnpushed: false, remoteUrl: '' });
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
routes['POST /api/git/push'] = async (req, res) => {
|
|
89
|
+
try {
|
|
90
|
+
const isWindows = os.platform() === 'win32';
|
|
91
|
+
const gitCommand = isWindows
|
|
92
|
+
? 'git add -A & git commit -m "Auto-commit" & git push'
|
|
93
|
+
: 'git add -A && git commit -m "Auto-commit" && git push';
|
|
94
|
+
execSync(gitCommand, { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
95
|
+
sendJSON(req, res, 200, { success: true });
|
|
96
|
+
} catch (err) {
|
|
97
|
+
sendJSON(req, res, 500, { error: err.message });
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
routes['_match'] = (method, pathOnly) => {
|
|
102
|
+
return routes[`${method} ${pathOnly}`] || null;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return routes;
|
|
106
|
+
}
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -21,6 +21,7 @@ import { getGeminiOAuthCreds, startGeminiOAuth, exchangeGeminiOAuthCode, handleG
|
|
|
21
21
|
import { initSpeechManager, getSpeech, ensurePocketTtsSetup, voiceCacheManager, modelDownloadState, broadcastModelProgress, ensureModelsDownloaded, eagerTTS } from './lib/speech-manager.js';
|
|
22
22
|
import { register as registerSpeechRoutes } from './lib/routes-speech.js';
|
|
23
23
|
import { register as registerOAuthRoutes } from './lib/routes-oauth.js';
|
|
24
|
+
import { register as registerUtilRoutes } from './lib/routes-util.js';
|
|
24
25
|
import { startCodexOAuth, exchangeCodexOAuthCode, handleCodexOAuthCallback, getCodexOAuthStatus, getCodexOAuthState, CODEX_HOME, CODEX_AUTH_FILE } from './lib/oauth-codex.js';
|
|
25
26
|
import { WSOptimizer } from './lib/ws-optimizer.js';
|
|
26
27
|
import { WsRouter } from './lib/ws-protocol.js';
|
|
@@ -2175,113 +2176,12 @@ const server = http.createServer(async (req, res) => {
|
|
|
2175
2176
|
return;
|
|
2176
2177
|
}
|
|
2177
2178
|
|
|
2178
|
-
if (pathOnly === '/api/import/claude-code' && req.method === 'GET') {
|
|
2179
|
-
const result = queries.importClaudeCodeConversations();
|
|
2180
|
-
sendJSON(req, res, 200, { imported: result });
|
|
2181
|
-
return;
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
if (pathOnly === '/api/discover/claude-code' && req.method === 'GET') {
|
|
2185
|
-
const discovered = queries.discoverClaudeCodeConversations();
|
|
2186
|
-
sendJSON(req, res, 200, { discovered });
|
|
2187
|
-
return;
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
if (pathOnly === '/api/home' && req.method === 'GET') {
|
|
2191
|
-
sendJSON(req, res, 200, { home: os.homedir(), cwd: STARTUP_CWD });
|
|
2192
|
-
return;
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
if (pathOnly === '/api/version' && req.method === 'GET') {
|
|
2196
|
-
sendJSON(req, res, 200, { version: PKG_VERSION });
|
|
2197
|
-
return;
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
2179
|
const speechHandler = _speechRoutes._match(req.method, pathOnly);
|
|
2201
2180
|
if (speechHandler) { await speechHandler(req, res, pathOnly); return; }
|
|
2202
2181
|
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
const repo = (body.repo || '').trim();
|
|
2206
|
-
if (!repo || !/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(repo)) {
|
|
2207
|
-
sendJSON(req, res, 400, { error: 'Invalid repo format. Use org/repo or user/repo' });
|
|
2208
|
-
return;
|
|
2209
|
-
}
|
|
2210
|
-
const cloneDir = STARTUP_CWD || os.homedir();
|
|
2211
|
-
const repoName = repo.split('/')[1];
|
|
2212
|
-
const targetPath = path.join(cloneDir, repoName);
|
|
2213
|
-
if (fs.existsSync(targetPath)) {
|
|
2214
|
-
sendJSON(req, res, 409, { error: `Directory already exists: ${repoName}`, path: targetPath });
|
|
2215
|
-
return;
|
|
2216
|
-
}
|
|
2217
|
-
try {
|
|
2218
|
-
const isWindows = os.platform() === 'win32';
|
|
2219
|
-
execSync('git clone https://github.com/' + repo + '.git', {
|
|
2220
|
-
cwd: cloneDir,
|
|
2221
|
-
encoding: 'utf-8',
|
|
2222
|
-
timeout: 120000,
|
|
2223
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
2224
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
2225
|
-
shell: isWindows
|
|
2226
|
-
});
|
|
2227
|
-
sendJSON(req, res, 200, { ok: true, repo, path: targetPath, name: repoName });
|
|
2228
|
-
} catch (err) {
|
|
2229
|
-
const stderr = err.stderr || err.message || 'Clone failed';
|
|
2230
|
-
sendJSON(req, res, 500, { error: stderr.trim() });
|
|
2231
|
-
}
|
|
2232
|
-
return;
|
|
2233
|
-
}
|
|
2182
|
+
const utilHandler = _utilRoutes._match(req.method, pathOnly);
|
|
2183
|
+
if (utilHandler) { await utilHandler(req, res); return; }
|
|
2234
2184
|
|
|
2235
|
-
if (pathOnly === '/api/folders' && req.method === 'POST') {
|
|
2236
|
-
const body = await parseBody(req);
|
|
2237
|
-
const folderPath = body.path || STARTUP_CWD;
|
|
2238
|
-
try {
|
|
2239
|
-
const expandedPath = folderPath.startsWith('~') ?
|
|
2240
|
-
folderPath.replace('~', os.homedir()) : folderPath;
|
|
2241
|
-
const entries = fs.readdirSync(expandedPath, { withFileTypes: true });
|
|
2242
|
-
const folders = entries
|
|
2243
|
-
.filter(e => e.isDirectory() && !e.name.startsWith('.'))
|
|
2244
|
-
.map(e => ({ name: e.name }))
|
|
2245
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
2246
|
-
sendJSON(req, res, 200, { folders });
|
|
2247
|
-
} catch (err) {
|
|
2248
|
-
sendJSON(req, res, 400, { error: err.message });
|
|
2249
|
-
}
|
|
2250
|
-
return;
|
|
2251
|
-
}
|
|
2252
|
-
|
|
2253
|
-
if (pathOnly === '/api/git/check-remote-ownership' && req.method === 'GET') {
|
|
2254
|
-
try {
|
|
2255
|
-
const isWindows = os.platform() === 'win32';
|
|
2256
|
-
const result = execSync('git remote get-url origin' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
2257
|
-
const remoteUrl = result.trim();
|
|
2258
|
-
const statusResult = execSync('git status --porcelain' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
2259
|
-
const hasChanges = statusResult.trim().length > 0;
|
|
2260
|
-
const unpushedResult = execSync('git rev-list --count --not --remotes' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
2261
|
-
const hasUnpushed = parseInt(unpushedResult.trim() || '0', 10) > 0;
|
|
2262
|
-
const ownsRemote = !remoteUrl.includes('github.com/') || remoteUrl.includes(process.env.GITHUB_USER || '');
|
|
2263
|
-
sendJSON(req, res, 200, { ownsRemote, hasChanges, hasUnpushed, remoteUrl });
|
|
2264
|
-
} catch {
|
|
2265
|
-
sendJSON(req, res, 200, { ownsRemote: false, hasChanges: false, hasUnpushed: false, remoteUrl: '' });
|
|
2266
|
-
}
|
|
2267
|
-
return;
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
if (pathOnly === '/api/git/push' && req.method === 'POST') {
|
|
2271
|
-
try {
|
|
2272
|
-
const isWindows = os.platform() === 'win32';
|
|
2273
|
-
const gitCommand = isWindows
|
|
2274
|
-
? 'git add -A & git commit -m "Auto-commit" & git push'
|
|
2275
|
-
: 'git add -A && git commit -m "Auto-commit" && git push';
|
|
2276
|
-
execSync(gitCommand, { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
2277
|
-
sendJSON(req, res, 200, { success: true });
|
|
2278
|
-
} catch (err) {
|
|
2279
|
-
sendJSON(req, res, 500, { error: err.message });
|
|
2280
|
-
}
|
|
2281
|
-
return;
|
|
2282
|
-
}
|
|
2283
|
-
|
|
2284
|
-
// ============================================================
|
|
2285
2185
|
// THREAD API ENDPOINTS (ACP v0.2.3)
|
|
2286
2186
|
// ============================================================
|
|
2287
2187
|
|
|
@@ -3425,6 +3325,7 @@ const wsRouter = new WsRouter();
|
|
|
3425
3325
|
initSpeechManager({ broadcastSync, syncClients, queries });
|
|
3426
3326
|
const _speechRoutes = registerSpeechRoutes({ sendJSON, parseBody, broadcastSync, debugLog });
|
|
3427
3327
|
const _oauthRoutes = registerOAuthRoutes({ sendJSON, parseBody, PORT, BASE_URL, rootDir });
|
|
3328
|
+
const _utilRoutes = registerUtilRoutes({ sendJSON, parseBody, queries, STARTUP_CWD, PKG_VERSION });
|
|
3428
3329
|
|
|
3429
3330
|
registerConvHandlers(wsRouter, {
|
|
3430
3331
|
queries, activeExecutions, rateLimitState,
|