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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.759",
3
+ "version": "1.0.760",
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
@@ -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
- if (pathOnly === '/api/clone' && req.method === 'POST') {
2204
- const body = await parseBody(req);
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,