agentgui 1.0.913 → 1.0.915
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/AGENTS.md +20 -0
- package/CHANGELOG.md +7 -0
- package/lib/db-queries-cleanup.js +4 -3
- package/lib/routes-tools.js +3 -4
- package/lib/tool-version-check.js +2 -2
- package/package.json +1 -1
- package/server.js +10 -5
package/AGENTS.md
CHANGED
|
@@ -11,3 +11,23 @@ Fix: `bun run scripts/capture-screenshots.mjs` (not `node ...`).
|
|
|
11
11
|
Why it works: `process.execPath` becomes bun, so the spawned child server also runs under bun and loads `bun:sqlite` natively — no compiled binding needed.
|
|
12
12
|
|
|
13
13
|
Rule: any CI step that spawns the agentgui server (directly or via a script that inherits `process.execPath`) must invoke it with `bun`.
|
|
14
|
+
|
|
15
|
+
## Plugin Dependencies
|
|
16
|
+
|
|
17
|
+
**`lib/plugins/stream-plugin.js` must not import packages missing from package.json.**
|
|
18
|
+
|
|
19
|
+
The plugin loader runs at startup and logs failures silently if a plugin import fails. If stream plugin fails to load due to missing dependency (e.g., `uuid`), the error cascades: `agents` and `websocket` plugins both declare `stream` as a dependency and fail with "Plugin stream not found in registry", cascading to boot failure.
|
|
20
|
+
|
|
21
|
+
Fix for uuid: replaced `import { v4 as uuidv4 } from 'uuid'` with `import { randomUUID as uuidv4 } from 'crypto'` (Node.js built-in, zero deps).
|
|
22
|
+
|
|
23
|
+
Pattern: all imports in lib/plugins/* must be either built-in (crypto, fs, path, etc.) or already in package.json. No new npm packages should be added to plugins without adding them to dependencies.
|
|
24
|
+
|
|
25
|
+
## Plugin Tool Provisioning on Windows
|
|
26
|
+
|
|
27
|
+
**`lib/tool-spawner.js` must iterate bun → npx fallback and match cross-platform command-not-found errors.**
|
|
28
|
+
|
|
29
|
+
On Windows hosts without bun installed, the auto-provisioner on startup and 6h periodic update checker failed silently when spawnBunxProc tried `bun.cmd` directly. The missing command error came via process stderr+close, not the 'error' event, so simple ENOENT detection was insufficient. Additionally, the error message format differs by OS: Windows shows "'bun.cmd' is not recognized as an internal or external command", Linux/Mac show "command not found" or "cannot find".
|
|
30
|
+
|
|
31
|
+
Fix: `BUNX_RUNNERS` array iterates `['bun', 'npx']` and tries each in sequence. Error detection regex `isMissingCmdError` matches `/not recognized|ENOENT|command not found|cannot find/i` on both `error.message` and captured stdout+stderr. Only falls through to next runner when the `missing` flag is set.
|
|
32
|
+
|
|
33
|
+
Pattern: When a binary might not exist on all platforms, use a runner fallback strategy. Always capture and check both error.message and process output streams. Cross-platform error detection requires regex alternation on common message patterns.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.0.908] - manual audit fixes: server retry idempotency, cli-version detection, cleanup FK ordering, refresh-all path
|
|
2
|
+
|
|
3
|
+
- server.js: `onServerListenStart` one-shot guard prevents `onServerReady` + `loadPluginExtensions` from re-firing when EADDRINUSE retry succeeds — previously every retry re-ran autoProvision/installGMAgentConfigs/setIntervals causing 100+ duplicate provision passes and stacked timers
|
|
4
|
+
- lib/tool-version-check.js: bumped `getCliVersion` execSync timeout from 1000ms to 15000ms; bumped `checkCliInstalled` `where` timeout from 3000ms to 10000ms — Windows cmd.exe + node CLI cold-start regularly exceeds 1s/3s, leaving opencode/gemini/kilo/agent-browser with `installedVersion: null` despite being installed
|
|
5
|
+
- lib/db-queries-cleanup.js: reordered cleanup transaction — child rows (chunks, stream_updates) now deleted before their parent sessions; was causing FOREIGN KEY constraint failed on every `[cleanup] Initial DB cleanup complete` invocation and on the 6h periodic cleanup, silently aborting the whole transaction
|
|
6
|
+
- lib/routes-tools.js: `POST /api/tools/refresh-all` now calls `toolManager.refreshAllToolsAsync()` (which clears statusCache) instead of `getAllTools()` which returned stale cached values — refresh button was a no-op
|
|
7
|
+
|
|
1
8
|
## [Unreleased] - implement 247420 brand-bible chrome on main shell
|
|
2
9
|
|
|
3
10
|
- index.html: header brand line reads `247420 / agentgui / <leaf>`; sidebar brand line reads `247420 / agentgui` — mono slash `/` in muted color, matches canonical app-topbar pattern from c:/dev/design
|
|
@@ -6,15 +6,16 @@ export function addCleanupQueries(q, db, prep, generateId) {
|
|
|
6
6
|
|
|
7
7
|
const cleanupStmt = db.transaction(() => {
|
|
8
8
|
prep('DELETE FROM events WHERE created_at < ?').run(thirtyDaysAgo);
|
|
9
|
-
prep('DELETE FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?').run(thirtyDaysAgo);
|
|
10
9
|
prep('DELETE FROM idempotencyKeys WHERE (created_at + ttl) < ?').run(now);
|
|
11
10
|
|
|
12
|
-
prep('DELETE FROM chunks WHERE sessionId IN (SELECT id FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?)').run(
|
|
13
|
-
prep('DELETE FROM stream_updates WHERE sessionId IN (SELECT id FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?)').run(
|
|
11
|
+
prep('DELETE FROM chunks WHERE sessionId IN (SELECT id FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?)').run(thirtyDaysAgo);
|
|
12
|
+
prep('DELETE FROM stream_updates WHERE sessionId IN (SELECT id FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?)').run(thirtyDaysAgo);
|
|
14
13
|
|
|
15
14
|
prep('DELETE FROM chunks WHERE created_at < ? AND sessionId NOT IN (SELECT id FROM sessions WHERE completed_at IS NULL AND started_at >= ?)').run(sevenDaysAgo, sevenDaysAgo);
|
|
16
15
|
prep('DELETE FROM stream_updates WHERE created_at < ? AND sessionId NOT IN (SELECT id FROM sessions WHERE completed_at IS NULL AND started_at >= ?)').run(sevenDaysAgo, sevenDaysAgo);
|
|
17
16
|
|
|
17
|
+
prep('DELETE FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?').run(thirtyDaysAgo);
|
|
18
|
+
|
|
18
19
|
prep('DELETE FROM voice_cache WHERE expires_at <= ?').run(now);
|
|
19
20
|
|
|
20
21
|
const deletedConvIds = prep("SELECT id FROM conversations WHERE status = 'deleted' AND updated_at < ?").all(sevenDaysAgo).map(r => r.id);
|
package/lib/routes-tools.js
CHANGED
|
@@ -67,12 +67,11 @@ export function register(deps) {
|
|
|
67
67
|
sendJSON(req, res, 200, { refreshing: true, toolCount: 4 });
|
|
68
68
|
broadcastSync({ type: 'tools_refresh_started' });
|
|
69
69
|
setImmediate(async () => {
|
|
70
|
-
const tools = toolManager.
|
|
70
|
+
const tools = await toolManager.refreshAllToolsAsync();
|
|
71
71
|
for (const tool of tools) {
|
|
72
72
|
queries.updateToolStatus(tool.id, { status: tool.installed ? 'installed' : 'not_installed', version: tool.installedVersion, last_check_at: Date.now() });
|
|
73
|
-
if (tool.installed) {
|
|
74
|
-
|
|
75
|
-
if (status?.upgradeNeeded) queries.updateToolStatus(tool.id, { update_available: 1, latest_version: status.publishedVersion });
|
|
73
|
+
if (tool.installed && tool.upgradeNeeded) {
|
|
74
|
+
queries.updateToolStatus(tool.id, { update_available: 1, latest_version: tool.publishedVersion });
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
77
|
broadcastSync({ type: 'tools_refresh_complete', data: tools });
|
|
@@ -141,7 +141,7 @@ export function checkCliInstalled(pkg) {
|
|
|
141
141
|
const cmd = isWindows ? 'where' : 'which';
|
|
142
142
|
const bin = BIN_MAP[pkg];
|
|
143
143
|
if (bin) {
|
|
144
|
-
execSync(`${cmd} ${bin}`, { stdio: 'pipe', timeout:
|
|
144
|
+
execSync(`${cmd} ${bin}`, { stdio: 'pipe', timeout: 10000, windowsHide: true });
|
|
145
145
|
return true;
|
|
146
146
|
}
|
|
147
147
|
} catch (_) {}
|
|
@@ -154,7 +154,7 @@ export function getCliVersion(pkg) {
|
|
|
154
154
|
if (!bin) return null;
|
|
155
155
|
try {
|
|
156
156
|
const versionFlag = pkg === 'agent-browser' ? '-V' : '--version';
|
|
157
|
-
const out = execSync(`${bin} ${versionFlag}`, { stdio: 'pipe', timeout:
|
|
157
|
+
const out = execSync(`${bin} ${versionFlag}`, { stdio: 'pipe', timeout: 15000, encoding: 'utf8', windowsHide: true });
|
|
158
158
|
const match = out.match(/(\d+\.\d+\.\d+)/);
|
|
159
159
|
if (match) {
|
|
160
160
|
console.log(`[tool-manager] CLI ${pkg} (${bin}) version: ${match[1]}`);
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -169,11 +169,19 @@ process.on('SIGINT', () => {
|
|
|
169
169
|
process.exit(0);
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
+
let _serverReadyFired = false;
|
|
173
|
+
const onServerListenStart = () => {
|
|
174
|
+
if (_serverReadyFired) return;
|
|
175
|
+
_serverReadyFired = true;
|
|
176
|
+
onServerReady();
|
|
177
|
+
loadPluginExtensions();
|
|
178
|
+
};
|
|
179
|
+
|
|
172
180
|
server.on('error', (err) => {
|
|
173
181
|
if (err.code === 'EADDRINUSE') {
|
|
174
182
|
console.error(`Port ${PORT} already in use. Waiting 3 seconds before retry...`);
|
|
175
183
|
setTimeout(() => {
|
|
176
|
-
server.listen(PORT,
|
|
184
|
+
server.listen(PORT, onServerListenStart);
|
|
177
185
|
}, 3000);
|
|
178
186
|
} else {
|
|
179
187
|
console.error('[SERVER] Error (contained):', err.message);
|
|
@@ -194,7 +202,4 @@ const { onServerReady, getJsonlWatcher } = createOnServerReady({
|
|
|
194
202
|
performAgentHealthCheck, pm2Manager, pm2Subscribers, recoverStaleSessions
|
|
195
203
|
});
|
|
196
204
|
|
|
197
|
-
server.listen(PORT,
|
|
198
|
-
onServerReady();
|
|
199
|
-
loadPluginExtensions();
|
|
200
|
-
});
|
|
205
|
+
server.listen(PORT, onServerListenStart);
|