agentgui 1.0.5 → 1.0.7

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/database.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { createRequire } from 'module';
4
5
 
6
+ const require = createRequire(import.meta.url);
5
7
  const dbDir = path.join(os.homedir(), '.gmgui');
6
8
  const dbFilePath = path.join(dbDir, 'data.db');
7
9
  const oldJsonPath = path.join(dbDir, 'data.json');
@@ -28,7 +30,7 @@ try {
28
30
  }
29
31
 
30
32
  function initSchema() {
31
- db.run(`
33
+ db.exec(`
32
34
  CREATE TABLE IF NOT EXISTS conversations (
33
35
  id TEXT PRIMARY KEY,
34
36
  agentId TEXT NOT NULL,
@@ -36,13 +38,11 @@ function initSchema() {
36
38
  created_at INTEGER NOT NULL,
37
39
  updated_at INTEGER NOT NULL,
38
40
  status TEXT DEFAULT 'active'
39
- )
40
- `);
41
+ );
41
42
 
42
- db.run(`CREATE INDEX IF NOT EXISTS idx_conversations_agent ON conversations(agentId)`);
43
- db.run(`CREATE INDEX IF NOT EXISTS idx_conversations_updated ON conversations(updated_at DESC)`);
43
+ CREATE INDEX IF NOT EXISTS idx_conversations_agent ON conversations(agentId);
44
+ CREATE INDEX IF NOT EXISTS idx_conversations_updated ON conversations(updated_at DESC);
44
45
 
45
- db.run(`
46
46
  CREATE TABLE IF NOT EXISTS messages (
47
47
  id TEXT PRIMARY KEY,
48
48
  conversationId TEXT NOT NULL,
@@ -50,12 +50,10 @@ function initSchema() {
50
50
  content TEXT NOT NULL,
51
51
  created_at INTEGER NOT NULL,
52
52
  FOREIGN KEY (conversationId) REFERENCES conversations(id)
53
- )
54
- `);
53
+ );
55
54
 
56
- db.run(`CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversationId)`);
55
+ CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversationId);
57
56
 
58
- db.run(`
59
57
  CREATE TABLE IF NOT EXISTS sessions (
60
58
  id TEXT PRIMARY KEY,
61
59
  conversationId TEXT NOT NULL,
@@ -65,13 +63,11 @@ function initSchema() {
65
63
  response TEXT,
66
64
  error TEXT,
67
65
  FOREIGN KEY (conversationId) REFERENCES conversations(id)
68
- )
69
- `);
66
+ );
70
67
 
71
- db.run(`CREATE INDEX IF NOT EXISTS idx_sessions_conversation ON sessions(conversationId)`);
72
- db.run(`CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(conversationId, status)`);
68
+ CREATE INDEX IF NOT EXISTS idx_sessions_conversation ON sessions(conversationId);
69
+ CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(conversationId, status);
73
70
 
74
- db.run(`
75
71
  CREATE TABLE IF NOT EXISTS events (
76
72
  id TEXT PRIMARY KEY,
77
73
  type TEXT NOT NULL,
@@ -81,21 +77,19 @@ function initSchema() {
81
77
  created_at INTEGER NOT NULL,
82
78
  FOREIGN KEY (conversationId) REFERENCES conversations(id),
83
79
  FOREIGN KEY (sessionId) REFERENCES sessions(id)
84
- )
85
- `);
80
+ );
86
81
 
87
- db.run(`CREATE INDEX IF NOT EXISTS idx_events_conversation ON events(conversationId)`);
82
+ CREATE INDEX IF NOT EXISTS idx_events_conversation ON events(conversationId);
88
83
 
89
- db.run(`
90
84
  CREATE TABLE IF NOT EXISTS idempotencyKeys (
91
85
  key TEXT PRIMARY KEY,
92
86
  value TEXT NOT NULL,
93
87
  created_at INTEGER NOT NULL,
94
88
  ttl INTEGER NOT NULL
95
- )
96
- `);
89
+ );
97
90
 
98
- db.run(`CREATE INDEX IF NOT EXISTS idx_idempotency_created ON idempotencyKeys(created_at)`);
91
+ CREATE INDEX IF NOT EXISTS idx_idempotency_created ON idempotencyKeys(created_at);
92
+ `);
99
93
  }
100
94
 
101
95
  function migrateFromJson() {
@@ -393,10 +387,10 @@ export const queries = {
393
387
  const conv = this.getConversation(id);
394
388
  if (!conv) return false;
395
389
 
396
- db.run('DELETE FROM messages WHERE conversationId = ?', [id]);
397
- db.run('DELETE FROM sessions WHERE conversationId = ?', [id]);
398
- db.run('DELETE FROM events WHERE conversationId = ?', [id]);
399
- db.run('DELETE FROM conversations WHERE id = ?', [id]);
390
+ db.prepare('DELETE FROM events WHERE conversationId = ?').run(id);
391
+ db.prepare('DELETE FROM sessions WHERE conversationId = ?').run(id);
392
+ db.prepare('DELETE FROM messages WHERE conversationId = ?').run(id);
393
+ db.prepare('DELETE FROM conversations WHERE id = ?').run(id);
400
394
 
401
395
  return true;
402
396
  },
@@ -404,14 +398,11 @@ export const queries = {
404
398
  cleanup() {
405
399
  const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
406
400
 
407
- db.run('DELETE FROM events WHERE created_at < ?', [thirtyDaysAgo]);
408
- db.run(
409
- 'DELETE FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?',
410
- [thirtyDaysAgo]
411
- );
401
+ db.prepare('DELETE FROM events WHERE created_at < ?').run(thirtyDaysAgo);
402
+ db.prepare('DELETE FROM sessions WHERE completed_at IS NOT NULL AND completed_at < ?').run(thirtyDaysAgo);
412
403
 
413
404
  const now = Date.now();
414
- db.run('DELETE FROM idempotencyKeys WHERE (created_at + ttl) < ?', [now]);
405
+ db.prepare('DELETE FROM idempotencyKeys WHERE (created_at + ttl) < ?').run(now);
415
406
  },
416
407
 
417
408
  setIdempotencyKey(key, value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -26,6 +26,7 @@
26
26
  "test:all": "npm run test:integration && npm run test"
27
27
  },
28
28
  "dependencies": {
29
+ "better-sqlite3": "^12.6.2",
29
30
  "ws": "^8.14.2"
30
31
  }
31
32
  }
package/server.js CHANGED
@@ -11,7 +11,7 @@ import ACPConnection from './acp-launcher.js';
11
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  const PORT = process.env.PORT || 3000;
13
13
  const BASE_URL = (process.env.BASE_URL || '/gm').replace(/\/+$/, '');
14
- const watch = process.argv.includes('--watch');
14
+ const watch = process.argv.includes('--no-watch') ? false : (process.argv.includes('--watch') || process.env.HOT_RELOAD !== 'false');
15
15
 
16
16
  const staticDir = path.join(__dirname, 'static');
17
17
  if (!fs.existsSync(staticDir)) fs.mkdirSync(staticDir, { recursive: true });
@@ -126,6 +126,7 @@ const server = http.createServer(async (req, res) => {
126
126
  if (req.method === 'DELETE') {
127
127
  const deleted = queries.deleteConversation(convMatch[1]);
128
128
  if (!deleted) { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); return; }
129
+ broadcastSync({ type: 'conversation_deleted', conversationId: convMatch[1] });
129
130
  res.writeHead(200, { 'Content-Type': 'application/json' });
130
131
  res.end(JSON.stringify({ deleted: true }));
131
132
  return;
@@ -290,7 +291,7 @@ function serveFile(filePath, res) {
290
291
  if (err) { res.writeHead(500); res.end('Server error'); return; }
291
292
  let content = data.toString();
292
293
  if (ext === '.html') {
293
- const baseTag = `<script>window.__BASE_URL='${BASE_URL}'; window.__AUTH_TOKEN=localStorage.getItem('gmgui-token');</script>`;
294
+ const baseTag = `<script>window.__BASE_URL='${BASE_URL}';</script>`;
294
295
  content = content.replace('<head>', '<head>\n ' + baseTag);
295
296
  if (watch) {
296
297
  content += `\n<script>(function(){const ws=new WebSocket('ws://'+location.host+'${BASE_URL}/hot-reload');ws.onmessage=e=>{if(JSON.parse(e.data).type==='reload')location.reload()};})();</script>`;
@@ -421,6 +422,11 @@ function onServerReady() {
421
422
  console.log(`GMGUI running on http://localhost:${PORT}${BASE_URL}/`);
422
423
  console.log(`Agents: ${discoveredAgents.map(a => a.name).join(', ') || 'none'}`);
423
424
  console.log(`Hot reload: ${watch ? 'on' : 'off'}`);
425
+ // Auto-import Claude Code conversations
426
+ const imported = queries.importClaudeCodeConversations();
427
+ if (imported.length > 0) {
428
+ console.log(`Auto-imported ${imported.filter(i => i.status === 'imported').length} Claude Code conversations`);
429
+ }
424
430
  }
425
431
 
426
432
  server.listen(PORT, onServerReady);
package/static/app.js CHANGED
@@ -98,12 +98,31 @@ class GMGUIApp {
98
98
  this.setupEventListeners();
99
99
  await this.fetchHome();
100
100
  await this.fetchAgents();
101
+ await this.autoImportClaudeCode();
101
102
  await this.fetchConversations();
102
103
  this.connectSyncWebSocket();
103
104
  this.setupCrossTabSync();
105
+ this.startPeriodicSync();
104
106
  this.renderAll();
105
107
  }
106
108
 
109
+ startPeriodicSync() {
110
+ // Rapid sync every 10 seconds: check for new Claude Code conversations and sync
111
+ setInterval(() => {
112
+ this.autoImportClaudeCode().then(() => {
113
+ this.fetchConversations().then(() => this.renderChatHistory());
114
+ });
115
+ }, 10000);
116
+ }
117
+
118
+ async autoImportClaudeCode() {
119
+ try {
120
+ await fetch(BASE_URL + '/api/import/claude-code');
121
+ } catch (e) {
122
+ console.error('autoImportClaudeCode:', e);
123
+ }
124
+ }
125
+
107
126
  connectSyncWebSocket() {
108
127
  const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
109
128
  this.syncWs = new ReconnectingWebSocket(
@@ -259,6 +278,11 @@ class GMGUIApp {
259
278
  }
260
279
 
261
280
  setupEventListeners() {
281
+ window.addEventListener('focus', () => {
282
+ this.autoImportClaudeCode().then(() => {
283
+ this.fetchConversations().then(() => this.renderChatHistory());
284
+ });
285
+ });
262
286
  const input = document.getElementById('messageInput');
263
287
  if (input) {
264
288
  input.addEventListener('keydown', (e) => {
@@ -396,20 +420,24 @@ class GMGUIApp {
396
420
  async deleteConversation(id) {
397
421
  try {
398
422
  const res = await fetch(`${BASE_URL}/api/conversations/${id}`, { method: 'DELETE' });
423
+ if (!res.ok) {
424
+ console.error('deleteConversation failed:', res.status);
425
+ return;
426
+ }
427
+ this.conversations.delete(id);
428
+ if (this.currentConversation === id) {
429
+ this.currentConversation = null;
430
+ const first = Array.from(this.conversations.values())[0];
431
+ if (first) {
432
+ this.displayConversation(first.id);
433
+ } else {
434
+ this.showWelcome();
435
+ }
436
+ }
437
+ this.renderChatHistory();
399
438
  } catch (e) {
400
439
  console.error('deleteConversation:', e);
401
440
  }
402
- this.conversations.delete(id);
403
- if (this.currentConversation === id) {
404
- this.currentConversation = null;
405
- const first = Array.from(this.conversations.values())[0];
406
- if (first) {
407
- this.displayConversation(first.id);
408
- } else {
409
- this.showWelcome();
410
- }
411
- }
412
- this.renderChatHistory();
413
441
  }
414
442
 
415
443
  showWelcome() {