agentgui 1.0.842 → 1.0.844

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/CHANGELOG.md CHANGED
@@ -1,6 +1,7 @@
1
1
  ## [Unreleased]
2
2
 
3
3
  ### Refactor
4
+ - Extract express upload + fsbrowse setup from server.js to lib/routes-upload.js (79L) exporting createExpressApp; server.js imports createExpressApp and no longer contains Busboy/fsbrowse/express inline code
4
5
  - Extract maskKey, getProviderConfigs, saveProviderConfig, buildSystemPrompt, PROVIDER_CONFIGS from server.js to lib/provider-config.js (151L); extract logError, makeCleanupExecution, makeGetModelsForAgent, errLogPath from server.js to lib/server-utils.js (61L); server.js imports all via named imports; cleanupExecution wired after broadcastSync; _debugRoutes receives errLogPath
5
6
  - Extract parseBody, acceptsEncoding, compressAndSend, sendJSON from server.js to lib/http-utils.js (43L); server.js imports from new module; zlib import removed from server.js
6
7
  - Extract message/stream/queue routes (messagesMatch, streamMatch, queueMatch handlers) to lib/routes-messages.js (140L) and session/chunk/full/execution routes to lib/routes-sessions.js (145L); server.js reduced from 2406L to 2127L; both files ≤200L; wired via _messagesRoutes._match and _sessionsRoutes._match in request handler
package/CLAUDE.md CHANGED
@@ -60,6 +60,7 @@ lib/routes-runs.js Runs HTTP route handlers (POST /api/runs, runs search, ru
60
60
  lib/routes-scripts.js Scripts/cancel/resume/inject HTTP route handlers (conversation scripts, run-script, stop-script, cancel, resume, inject)
61
61
  lib/routes-agent-actions.js Agent auth and update HTTP route handlers (POST /api/agents/:id/auth, POST /api/agents/:id/update)
62
62
  lib/routes-auth-config.js Auth config HTTP route handlers (GET /api/auth/configs, POST /api/auth/save-config)
63
+ lib/routes-upload.js Express sub-app: POST /api/upload/:conversationId (Busboy file upload) + GET /files/:conversationId fsbrowse router; createExpressApp(deps) factory
63
64
  lib/http-utils.js HTTP utility functions (parseBody, acceptsEncoding, compressAndSend, sendJSON)
64
65
  lib/provider-config.js Provider config helpers (buildSystemPrompt, maskKey, getProviderConfigs, saveProviderConfig, PROVIDER_CONFIGS)
65
66
  lib/server-utils.js Server utility functions (logError, errLogPath, makeCleanupExecution, makeGetModelsForAgent)
@@ -0,0 +1,79 @@
1
+ import express from 'express';
2
+ import Busboy from 'busboy';
3
+ import fsbrowse from 'fsbrowse';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+
7
+ export function createExpressApp({ queries, BASE_URL }) {
8
+ const app = express();
9
+ const fsbrowseRouters = new Map();
10
+
11
+ app.post(BASE_URL + '/api/upload/:conversationId', (req, res) => {
12
+ try {
13
+ const conv = queries.getConversation(req.params.conversationId);
14
+ if (!conv) return res.status(404).json({ error: 'Conversation not found' });
15
+ if (!conv.workingDirectory) return res.status(400).json({ error: 'No working directory set for this conversation' });
16
+
17
+ const uploadDir = conv.workingDirectory;
18
+ if (!fs.existsSync(uploadDir)) {
19
+ fs.mkdirSync(uploadDir, { recursive: true });
20
+ }
21
+
22
+ const bb = Busboy({ headers: req.headers });
23
+ const fileNames = [];
24
+ const writePromises = [];
25
+
26
+ bb.on('file', (fieldname, file, info) => {
27
+ const safeName = path.basename(info.filename);
28
+ const filePath = path.join(uploadDir, safeName);
29
+ fileNames.push(safeName);
30
+ const p = new Promise((resolve) => {
31
+ const writeStream = fs.createWriteStream(filePath);
32
+ file.pipe(writeStream);
33
+ writeStream.on('finish', resolve);
34
+ writeStream.on('error', () => { file.resume(); resolve(); });
35
+ });
36
+ writePromises.push(p);
37
+ });
38
+
39
+ bb.on('finish', () => {
40
+ Promise.all(writePromises).then(() => {
41
+ res.json({ ok: true, files: fileNames, count: fileNames.length });
42
+ }).catch(() => {
43
+ res.json({ ok: true, files: fileNames, count: fileNames.length });
44
+ });
45
+ });
46
+
47
+ bb.on('error', (err) => {
48
+ res.status(500).json({ error: 'Upload failed: ' + err.message });
49
+ });
50
+
51
+ req.pipe(bb);
52
+ } catch (err) {
53
+ res.status(500).json({ error: err.message });
54
+ }
55
+ });
56
+
57
+ app.use(BASE_URL + '/files/:conversationId', (req, res, next) => {
58
+ const convId = req.params.conversationId;
59
+ const conv = queries.getConversation(convId);
60
+ if (!conv || !conv.workingDirectory) {
61
+ return res.status(404).json({ error: 'Conversation not found or no working directory' });
62
+ }
63
+
64
+ const normalizedWorkingDir = path.resolve(conv.workingDirectory);
65
+
66
+ let router = fsbrowseRouters.get(convId);
67
+ if (!router) {
68
+ router = fsbrowse({ baseDir: normalizedWorkingDir, name: 'Files' });
69
+ fsbrowseRouters.set(convId, router);
70
+ }
71
+
72
+ req.baseUrl = BASE_URL + '/files/' + convId;
73
+ req.url = req.url.replace(new RegExp(`^${BASE_URL}/files/${convId}`), '');
74
+
75
+ router(req, res, next);
76
+ });
77
+
78
+ return app;
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.842",
3
+ "version": "1.0.844",
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
@@ -9,9 +9,7 @@ import { LRUCache } from 'lru-cache';
9
9
  import { createRequire } from 'module';
10
10
  import crypto from 'crypto';
11
11
  const PKG_VERSION = JSON.parse(fs.readFileSync(new URL('./package.json', import.meta.url), 'utf8')).version;
12
- import express from 'express';
13
- import Busboy from 'busboy';
14
- import fsbrowse from 'fsbrowse';
12
+ import { createExpressApp } from './lib/routes-upload.js';
15
13
  import { queries } from './database.js';
16
14
  import { runClaudeWithStreaming } from './lib/claude-runner-run.js';
17
15
  import { initializeDescriptors, getAgentDescriptor } from './lib/agent-descriptors.js';
@@ -111,85 +109,7 @@ const STARTUP_CWD = (() => {
111
109
  const staticDir = path.join(rootDir, 'static');
112
110
  if (!fs.existsSync(staticDir)) fs.mkdirSync(staticDir, { recursive: true });
113
111
 
114
- // Express sub-app for fsbrowse file browser and file upload
115
- const expressApp = express();
116
-
117
-
118
- // File upload endpoint - copies dropped files to conversation workingDirectory
119
- expressApp.post(BASE_URL + '/api/upload/:conversationId', (req, res) => {
120
- try {
121
- const conv = queries.getConversation(req.params.conversationId);
122
- if (!conv) return res.status(404).json({ error: 'Conversation not found' });
123
- if (!conv.workingDirectory) return res.status(400).json({ error: 'No working directory set for this conversation' });
124
-
125
- const uploadDir = conv.workingDirectory;
126
- if (!fs.existsSync(uploadDir)) {
127
- fs.mkdirSync(uploadDir, { recursive: true });
128
- }
129
-
130
- const bb = Busboy({ headers: req.headers });
131
- const fileNames = [];
132
- const writePromises = [];
133
-
134
- bb.on('file', (fieldname, file, info) => {
135
- const safeName = path.basename(info.filename);
136
- const filePath = path.join(uploadDir, safeName);
137
- fileNames.push(safeName);
138
- const p = new Promise((resolve) => {
139
- const writeStream = fs.createWriteStream(filePath);
140
- file.pipe(writeStream);
141
- writeStream.on('finish', resolve);
142
- writeStream.on('error', () => { file.resume(); resolve(); });
143
- });
144
- writePromises.push(p);
145
- });
146
-
147
- bb.on('finish', () => {
148
- Promise.all(writePromises).then(() => {
149
- res.json({ ok: true, files: fileNames, count: fileNames.length });
150
- }).catch(() => {
151
- res.json({ ok: true, files: fileNames, count: fileNames.length });
152
- });
153
- });
154
-
155
- bb.on('error', (err) => {
156
- res.status(500).json({ error: 'Upload failed: ' + err.message });
157
- });
158
-
159
- req.pipe(bb);
160
- } catch (err) {
161
- res.status(500).json({ error: err.message });
162
- }
163
- });
164
-
165
- // Cache fsbrowse routers per conversation to ensure API calls work
166
- const fsbrowseRouters = new Map();
167
-
168
- // fsbrowse file browser - mounted per conversation workingDirectory
169
- // Route: /gm/files/:conversationId/*
170
- expressApp.use(BASE_URL + '/files/:conversationId', (req, res, next) => {
171
- const convId = req.params.conversationId;
172
- const conv = queries.getConversation(convId);
173
- if (!conv || !conv.workingDirectory) {
174
- return res.status(404).json({ error: 'Conversation not found or no working directory' });
175
- }
176
-
177
- // Normalize the working directory path to avoid Windows path duplication issues
178
- const normalizedWorkingDir = path.resolve(conv.workingDirectory);
179
-
180
- // Get or create cached fsbrowse router for this conversation
181
- let router = fsbrowseRouters.get(convId);
182
- if (!router) {
183
- router = fsbrowse({ baseDir: normalizedWorkingDir, name: 'Files' });
184
- fsbrowseRouters.set(convId, router);
185
- }
186
-
187
- // Set baseUrl before calling the router
188
- req.baseUrl = BASE_URL + '/files/' + convId;
189
- req.url = req.url.replace(new RegExp(`^${BASE_URL}/files/${convId}`), '');
190
-
191
- router(req, res, next);
192
- });
112
+ const expressApp = createExpressApp({ queries, BASE_URL });
193
113
 
194
114
  let discoveredAgents = [];
195
115
  initializeDescriptors(discoveredAgents);