agentgui 1.0.836 → 1.0.837

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,145 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import zlib from 'zlib';
4
+ import { LRUCache } from 'lru-cache';
5
+
6
+ const MIME_TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'application/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml' };
7
+
8
+ export const _assetCache = new LRUCache({ max: 200 });
9
+ export const htmlState = { cache: null, etag: null };
10
+
11
+ export function generateETag(stats) {
12
+ return `"${stats.mtimeMs.toString(36)}-${stats.size.toString(36)}"`;
13
+ }
14
+
15
+ export function warmAssetCache(staticDir) {
16
+ const dirs = ['js', 'css', 'lib', 'vendor'];
17
+ let count = 0;
18
+ for (const dir of dirs) {
19
+ const full = path.join(staticDir, dir);
20
+ if (!fs.existsSync(full)) continue;
21
+ for (const file of fs.readdirSync(full)) {
22
+ const filePath = path.join(full, file);
23
+ try {
24
+ const stats = fs.statSync(filePath);
25
+ if (!stats.isFile()) continue;
26
+ const etag = generateETag(stats);
27
+ if (_assetCache.has(etag)) continue;
28
+ const raw = fs.readFileSync(filePath);
29
+ _assetCache.set(etag, raw.length < 860 ? { raw, gz: null } : { raw, gz: zlib.gzipSync(raw, { level: 6 }) });
30
+ count++;
31
+ } catch (_) {}
32
+ }
33
+ }
34
+ for (const file of ['app.js', 'theme.js']) {
35
+ const filePath = path.join(staticDir, file);
36
+ try {
37
+ const stats = fs.statSync(filePath);
38
+ const etag = generateETag(stats);
39
+ if (!_assetCache.has(etag)) {
40
+ const raw = fs.readFileSync(filePath);
41
+ _assetCache.set(etag, raw.length < 860 ? { raw, gz: null } : { raw, gz: zlib.gzipSync(raw, { level: 6 }) });
42
+ count++;
43
+ }
44
+ } catch (_) {}
45
+ }
46
+ if (count > 0) console.log(`[CACHE] Pre-warmed ${count} static assets`);
47
+ }
48
+
49
+ export function serveFile(filePath, res, req, { compressAndSend, acceptsEncoding, watch, BASE_URL, PKG_VERSION }) {
50
+ const ext = path.extname(filePath).toLowerCase();
51
+ const contentType = MIME_TYPES[ext] || 'application/octet-stream';
52
+
53
+ if (ext !== '.html') {
54
+ fs.stat(filePath, (err, stats) => {
55
+ if (err) { res.writeHead(500); res.end('Server error'); return; }
56
+ const etag = generateETag(stats);
57
+ if (req && req.headers['if-none-match'] === etag) { res.writeHead(304); res.end(); return; }
58
+ const cacheControl = 'public, no-cache';
59
+ const sendCached = (cached) => {
60
+ if (acceptsEncoding(req, 'gzip') && cached.gz) {
61
+ res.writeHead(200, { 'Content-Type': contentType, 'Content-Encoding': 'gzip', 'Content-Length': cached.gz.length, 'ETag': etag, 'Cache-Control': cacheControl });
62
+ res.end(cached.gz);
63
+ } else {
64
+ res.writeHead(200, { 'Content-Type': contentType, 'Content-Length': cached.raw.length, 'ETag': etag, 'Cache-Control': cacheControl });
65
+ res.end(cached.raw);
66
+ }
67
+ };
68
+ const cached = _assetCache.get(etag);
69
+ if (cached) { sendCached(cached); return; }
70
+ fs.readFile(filePath, (err2, raw) => {
71
+ if (err2) { res.writeHead(500); res.end('Server error'); return; }
72
+ if (raw.length < 860) { const entry = { raw, gz: null }; _assetCache.set(etag, entry); sendCached(entry); return; }
73
+ const gz = zlib.gzipSync(raw, { level: 6 });
74
+ const entry = { raw, gz };
75
+ _assetCache.set(etag, entry);
76
+ sendCached(entry);
77
+ });
78
+ });
79
+ return;
80
+ }
81
+
82
+ fs.stat(filePath, (err, stats) => {
83
+ if (err) { res.writeHead(500); res.end('Server error'); return; }
84
+ const etag = generateETag(stats);
85
+ if (!watch && htmlState.cache && htmlState.etag === etag) {
86
+ res.writeHead(200, { 'Content-Type': contentType, 'Cache-Control': 'no-store', 'Content-Encoding': 'gzip', 'Content-Length': htmlState.cache.length });
87
+ res.end(htmlState.cache);
88
+ return;
89
+ }
90
+ fs.readFile(filePath, (err2, data) => {
91
+ if (err2) { res.writeHead(500); res.end('Server error'); return; }
92
+ let content = data.toString();
93
+ const wsToken = process.env.PASSWORD ? `window.__WS_TOKEN='${process.env.PASSWORD.replace(/'/g, "\\'")}';` : '';
94
+ const baseTag = `<script>window.__BASE_URL='${BASE_URL}';window.__SERVER_VERSION='${PKG_VERSION}';${wsToken}</script>`;
95
+ content = content.replace('<head>', `<head>\n <base href="${BASE_URL}/">\n ` + baseTag);
96
+ content = content.replace(/(href|src)="vendor\//g, `$1="${BASE_URL}/vendor/`);
97
+ content = content.replace(/(src)="\/gm\/js\//g, `$1="${BASE_URL}/js/`);
98
+ if (watch) {
99
+ content += `\n<script>(function(){const ws=new WebSocket((location.protocol==='https:'?'wss://':'ws://')+location.host+'${BASE_URL}/hot-reload');ws.onmessage=e=>{if(JSON.parse(e.data).type==='reload')location.reload()};})();</script>`;
100
+ }
101
+ compressAndSend(req, res, 200, contentType, content);
102
+ if (!watch && acceptsEncoding(req, 'gzip')) {
103
+ htmlState.cache = zlib.gzipSync(Buffer.from(content), { level: 6 });
104
+ htmlState.etag = etag;
105
+ }
106
+ });
107
+ });
108
+ }
109
+
110
+ export function createChunkBatcher(queries, debugLog) {
111
+ const pending = [];
112
+ let timer = null;
113
+ const BATCH_SIZE = 10;
114
+ const BATCH_INTERVAL = 50;
115
+
116
+ function flush() {
117
+ if (pending.length === 0) return;
118
+ const batch = pending.splice(0);
119
+ try {
120
+ const tx = queries._db ? queries._db.transaction(() => {
121
+ for (const c of batch) queries.createChunk(c.sessionId, c.conversationId, c.sequence, c.type, c.data);
122
+ }) : null;
123
+ if (tx) { tx(); } else {
124
+ for (const c of batch) {
125
+ try { queries.createChunk(c.sessionId, c.conversationId, c.sequence, c.type, c.data); } catch (e) { debugLog(`[chunk] ${e.message}`); }
126
+ }
127
+ }
128
+ } catch (err) {
129
+ debugLog(`[chunk-batch] Batch write failed: ${err.message}`);
130
+ for (const c of batch) {
131
+ try { queries.createChunk(c.sessionId, c.conversationId, c.sequence, c.type, c.data); } catch (_) {}
132
+ }
133
+ }
134
+ }
135
+
136
+ function add(sessionId, conversationId, sequence, blockType, blockData) {
137
+ pending.push({ sessionId, conversationId, sequence, type: blockType, data: blockData });
138
+ if (pending.length >= BATCH_SIZE) { if (timer) { clearTimeout(timer); timer = null; } flush(); }
139
+ else if (!timer) { timer = setTimeout(() => { timer = null; flush(); }, BATCH_INTERVAL); }
140
+ }
141
+
142
+ function drain() { if (timer) { clearTimeout(timer); timer = null; } flush(); }
143
+
144
+ return { add, drain };
145
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.836",
3
+ "version": "1.0.837",
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
@@ -1706,7 +1706,7 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
1706
1706
  execMachine.send(conversationId, { type: 'START', sessionId });
1707
1707
  queries.setIsStreaming(conversationId, true);
1708
1708
  queries.updateSession(sessionId, { status: 'active' });
1709
- const batcher = createChunkBatcher();
1709
+ const batcher = createChunkBatcher(queries, debugLog);
1710
1710
 
1711
1711
  try {
1712
1712
  debugLog(`[stream] Starting: conversationId=${conversationId}, sessionId=${sessionId}`);
@@ -2757,8 +2757,8 @@ if (watch) {
2757
2757
  fs.watchFile(fp, { interval: 100 }, (curr, prev) => {
2758
2758
  if (curr.mtime > prev.mtime) {
2759
2759
  _assetCache.clear();
2760
- _htmlCache = null;
2761
- _htmlCacheEtag = null;
2760
+ htmlState.cache = null;
2761
+ htmlState.etag = null;
2762
2762
  hotReloadClients.forEach(c => { if (c.readyState === 1) c.send(JSON.stringify({ type: 'reload' })); });
2763
2763
  }
2764
2764
  });
@@ -2821,7 +2821,7 @@ function onServerReady() {
2821
2821
  }
2822
2822
 
2823
2823
  recoverStaleSessions();
2824
- warmAssetCache();
2824
+ warmAssetCache(staticDir);
2825
2825
 
2826
2826
  // Run DB cleanup on startup and every 6 hours
2827
2827
  try { queries.cleanup(); console.log('[cleanup] Initial DB cleanup complete'); } catch (e) { console.error('[cleanup] Error:', e.message); }