agentgui 1.0.406 → 1.0.408
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 +4 -22
- package/docs/index.html +104 -0
- package/docs/script.js +38 -0
- package/docs/stats.json +4 -0
- package/docs/styles.css +301 -0
- package/lib/claude-runner.js +2 -4
- package/package.json +1 -1
- package/readme.md +20 -4
- package/server.js +8 -1
- package/static/index.html +18 -13
- package/static/js/client.js +0 -1
- package/static/js/event-consolidator.js +32 -33
- package/static/js/voice.js +3 -3
package/database.js
CHANGED
|
@@ -559,7 +559,7 @@ export const queries = {
|
|
|
559
559
|
|
|
560
560
|
getConversationsList() {
|
|
561
561
|
const stmt = prep(
|
|
562
|
-
'SELECT id, title, agentType, created_at, updated_at, messageCount, workingDirectory, isStreaming, model FROM conversations WHERE status != ? ORDER BY updated_at DESC'
|
|
562
|
+
'SELECT id, agentId, title, agentType, created_at, updated_at, messageCount, workingDirectory, isStreaming, model FROM conversations WHERE status != ? ORDER BY updated_at DESC'
|
|
563
563
|
);
|
|
564
564
|
return stmt.all('deleted');
|
|
565
565
|
},
|
|
@@ -619,13 +619,13 @@ export const queries = {
|
|
|
619
619
|
},
|
|
620
620
|
|
|
621
621
|
getStreamingConversations() {
|
|
622
|
-
const stmt = prep('SELECT id, title, claudeSessionId, agentType FROM conversations WHERE isStreaming = 1');
|
|
622
|
+
const stmt = prep('SELECT id, title, claudeSessionId, agentId, agentType, model FROM conversations WHERE isStreaming = 1');
|
|
623
623
|
return stmt.all();
|
|
624
624
|
},
|
|
625
625
|
|
|
626
626
|
getResumableConversations() {
|
|
627
627
|
const stmt = prep(
|
|
628
|
-
"SELECT id, title, claudeSessionId, agentType, workingDirectory FROM conversations WHERE isStreaming = 1 AND claudeSessionId IS NOT NULL AND claudeSessionId != ''"
|
|
628
|
+
"SELECT id, title, claudeSessionId, agentId, agentType, workingDirectory, model FROM conversations WHERE isStreaming = 1 AND claudeSessionId IS NOT NULL AND claudeSessionId != ''"
|
|
629
629
|
);
|
|
630
630
|
return stmt.all();
|
|
631
631
|
},
|
|
@@ -1399,25 +1399,7 @@ export const queries = {
|
|
|
1399
1399
|
},
|
|
1400
1400
|
|
|
1401
1401
|
permanentlyDeleteConversation(id) {
|
|
1402
|
-
|
|
1403
|
-
if (!conv) return false;
|
|
1404
|
-
|
|
1405
|
-
// Delete associated Claude Code session file if it exists
|
|
1406
|
-
if (conv.claudeSessionId) {
|
|
1407
|
-
this.deleteClaudeSessionFile(conv.claudeSessionId);
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
const deleteStmt = db.transaction(() => {
|
|
1411
|
-
prep('DELETE FROM stream_updates WHERE conversationId = ?').run(id);
|
|
1412
|
-
prep('DELETE FROM chunks WHERE conversationId = ?').run(id);
|
|
1413
|
-
prep('DELETE FROM events WHERE conversationId = ?').run(id);
|
|
1414
|
-
prep('DELETE FROM sessions WHERE conversationId = ?').run(id);
|
|
1415
|
-
prep('DELETE FROM messages WHERE conversationId = ?').run(id);
|
|
1416
|
-
prep('DELETE FROM conversations WHERE id = ?').run(id);
|
|
1417
|
-
});
|
|
1418
|
-
|
|
1419
|
-
deleteStmt();
|
|
1420
|
-
return true;
|
|
1402
|
+
return this.deleteConversation(id);
|
|
1421
1403
|
},
|
|
1422
1404
|
|
|
1423
1405
|
cleanupEmptyConversations() {
|
package/docs/index.html
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>AgentGUI - Multi-Agent AI Client</title>
|
|
7
|
+
<link rel="stylesheet" href="styles.css">
|
|
8
|
+
<link rel="icon" type="image/x-icon" href="https://github.com/AnEntrypoint/agentgui/raw/main/agentgui.ico">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div class="container">
|
|
12
|
+
<header>
|
|
13
|
+
<div class="logo">
|
|
14
|
+
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
15
|
+
<rect width="64" height="64" rx="12" fill="url(#gradient)"/>
|
|
16
|
+
<path d="M32 16L44 32L32 48L20 32L32 16Z" stroke="white" stroke-width="3" fill="none"/>
|
|
17
|
+
<circle cx="32" cy="32" r="8" fill="white"/>
|
|
18
|
+
<defs>
|
|
19
|
+
<linearGradient id="gradient" x1="0" y1="0" x2="64" y2="64">
|
|
20
|
+
<stop offset="0%" stop-color="#6366f1"/>
|
|
21
|
+
<stop offset="100%" stop-color="#8b5cf6"/>
|
|
22
|
+
</linearGradient>
|
|
23
|
+
</defs>
|
|
24
|
+
</svg>
|
|
25
|
+
</div>
|
|
26
|
+
<h1>AgentGUI</h1>
|
|
27
|
+
<p class="subtitle">Multi-agent ACP client with real-time communication</p>
|
|
28
|
+
<div class="stats">
|
|
29
|
+
<div class="stat-item">
|
|
30
|
+
<span class="stat-value" id="weekly-downloads">Loading...</span>
|
|
31
|
+
<span class="stat-label">Weekly Downloads</span>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="cta-buttons">
|
|
35
|
+
<a href="https://github.com/AnEntrypoint/agentgui" target="_blank" class="btn btn-primary" id="star-btn">
|
|
36
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
37
|
+
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
|
38
|
+
</svg>
|
|
39
|
+
Star on GitHub
|
|
40
|
+
</a>
|
|
41
|
+
<a href="https://github.com/AnEntrypoint/agentgui#readme" target="_blank" class="btn btn-secondary">
|
|
42
|
+
View Documentation
|
|
43
|
+
</a>
|
|
44
|
+
</div>
|
|
45
|
+
</header>
|
|
46
|
+
|
|
47
|
+
<section class="features">
|
|
48
|
+
<h2>Features</h2>
|
|
49
|
+
<div class="feature-grid">
|
|
50
|
+
<div class="feature-card">
|
|
51
|
+
<div class="feature-icon">⚡</div>
|
|
52
|
+
<h3>Real-Time Streaming</h3>
|
|
53
|
+
<p>Live WebSocket sync with instant updates across multiple agents</p>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="feature-card">
|
|
56
|
+
<div class="feature-icon">🤖</div>
|
|
57
|
+
<h3>Multi-Agent Support</h3>
|
|
58
|
+
<p>Use Claude Code, Gemini CLI, OpenCode, Goose, and more</p>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="feature-card">
|
|
61
|
+
<div class="feature-icon">💾</div>
|
|
62
|
+
<h3>SQLite Persistence</h3>
|
|
63
|
+
<p>All conversations and events safely stored locally</p>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="feature-card">
|
|
66
|
+
<div class="feature-icon">🎤</div>
|
|
67
|
+
<h3>Voice Integration</h3>
|
|
68
|
+
<p>Speech-to-text and text-to-speech with HuggingFace</p>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="feature-card">
|
|
71
|
+
<div class="feature-icon">🔧</div>
|
|
72
|
+
<h3>Portable Executable</h3>
|
|
73
|
+
<p>Windows build with bundled models, no installation needed</p>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="feature-card">
|
|
76
|
+
<div class="feature-icon">🌐</div>
|
|
77
|
+
<h3>Web Interface</h3>
|
|
78
|
+
<p>Modern browser-based UI accessible from any device</p>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</section>
|
|
82
|
+
|
|
83
|
+
<section class="quick-start">
|
|
84
|
+
<h2>Quick Start</h2>
|
|
85
|
+
<div class="code-block">
|
|
86
|
+
<code>npm install -g agentgui</code>
|
|
87
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
88
|
+
</div>
|
|
89
|
+
<p class="note">Then run <code>agentgui</code> and open <a href="http://localhost:3000/gm/">http://localhost:3000/gm/</a></p>
|
|
90
|
+
</section>
|
|
91
|
+
|
|
92
|
+
<footer>
|
|
93
|
+
<p>Built with ❤️ by the AgentGUI team</p>
|
|
94
|
+
<p class="footer-links">
|
|
95
|
+
<a href="https://github.com/AnEntrypoint/agentgui">GitHub</a> •
|
|
96
|
+
<a href="https://github.com/AnEntrypoint/agentgui/issues">Issues</a> •
|
|
97
|
+
<a href="https://github.com/AnEntrypoint/agentgui#readme">Docs</a>
|
|
98
|
+
</p>
|
|
99
|
+
</footer>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<script src="script.js"></script>
|
|
103
|
+
</body>
|
|
104
|
+
</html>
|
package/docs/script.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
2
|
+
const statsElement = document.getElementById('weekly-downloads');
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
const response = await fetch('stats.json');
|
|
6
|
+
if (!response.ok) {
|
|
7
|
+
throw new Error(`HTTP ${response.status}`);
|
|
8
|
+
}
|
|
9
|
+
const data = await response.json();
|
|
10
|
+
const count = data.downloads ?? data.weeklyDownloads ?? 0;
|
|
11
|
+
statsElement.textContent = formatNumber(count);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.warn('Failed to load stats:', error);
|
|
14
|
+
statsElement.textContent = 'N/A';
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function formatNumber(num) {
|
|
19
|
+
if (num >= 1000000) {
|
|
20
|
+
return (num / 1000000).toFixed(1) + 'M';
|
|
21
|
+
}
|
|
22
|
+
if (num >= 1000) {
|
|
23
|
+
return (num / 1000).toFixed(1) + 'K';
|
|
24
|
+
}
|
|
25
|
+
return num.toLocaleString();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function copyCode(button) {
|
|
29
|
+
const codeBlock = button.parentElement;
|
|
30
|
+
const code = codeBlock.querySelector('code').textContent;
|
|
31
|
+
navigator.clipboard.writeText(code).then(() => {
|
|
32
|
+
const originalText = button.textContent;
|
|
33
|
+
button.textContent = 'Copied!';
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
button.textContent = originalText;
|
|
36
|
+
}, 2000);
|
|
37
|
+
});
|
|
38
|
+
}
|
package/docs/stats.json
ADDED
package/docs/styles.css
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--primary: #6366f1;
|
|
3
|
+
--primary-dark: #4f46e5;
|
|
4
|
+
--secondary: #10b981;
|
|
5
|
+
--bg: #0f172a;
|
|
6
|
+
--bg-card: #1e293b;
|
|
7
|
+
--text: #f1f5f9;
|
|
8
|
+
--text-muted: #94a3b8;
|
|
9
|
+
--border: #334155;
|
|
10
|
+
--gradient: linear-gradient(135deg, #6366f1, #8b5cf6);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
* {
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
body {
|
|
20
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
21
|
+
background: var(--bg);
|
|
22
|
+
color: var(--text);
|
|
23
|
+
line-height: 1.6;
|
|
24
|
+
min-height: 100vh;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.container {
|
|
28
|
+
max-width: 1200px;
|
|
29
|
+
margin: 0 auto;
|
|
30
|
+
padding: 2rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
header {
|
|
34
|
+
text-align: center;
|
|
35
|
+
padding: 3rem 0;
|
|
36
|
+
border-bottom: 1px solid var(--border);
|
|
37
|
+
margin-bottom: 3rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.logo {
|
|
41
|
+
margin-bottom: 1rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.logo svg {
|
|
45
|
+
filter: drop-shadow(0 4px 6px rgba(99, 102, 241, 0.3));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
h1 {
|
|
49
|
+
font-size: 3rem;
|
|
50
|
+
font-weight: 800;
|
|
51
|
+
background: var(--gradient);
|
|
52
|
+
-webkit-background-clip: text;
|
|
53
|
+
-webkit-text-fill-color: transparent;
|
|
54
|
+
background-clip: text;
|
|
55
|
+
margin-bottom: 0.5rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.subtitle {
|
|
59
|
+
font-size: 1.25rem;
|
|
60
|
+
color: var(--text-muted);
|
|
61
|
+
margin-bottom: 2rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.stats {
|
|
65
|
+
display: inline-flex;
|
|
66
|
+
gap: 2rem;
|
|
67
|
+
padding: 1.5rem 2rem;
|
|
68
|
+
background: var(--bg-card);
|
|
69
|
+
border: 1px solid var(--border);
|
|
70
|
+
border-radius: 12px;
|
|
71
|
+
margin: 2rem 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.stat-item {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
align-items: center;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.stat-value {
|
|
81
|
+
font-size: 2.5rem;
|
|
82
|
+
font-weight: 700;
|
|
83
|
+
color: var(--primary);
|
|
84
|
+
line-height: 1;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.stat-label {
|
|
88
|
+
font-size: 0.875rem;
|
|
89
|
+
color: var(--text-muted);
|
|
90
|
+
margin-top: 0.5rem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.cta-buttons {
|
|
94
|
+
display: flex;
|
|
95
|
+
gap: 1rem;
|
|
96
|
+
justify-content: center;
|
|
97
|
+
flex-wrap: wrap;
|
|
98
|
+
margin: 2rem 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.btn {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 0.5rem;
|
|
105
|
+
padding: 0.875rem 1.75rem;
|
|
106
|
+
border-radius: 8px;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
text-decoration: none;
|
|
109
|
+
transition: all 0.2s;
|
|
110
|
+
border: none;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
font-size: 1rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.btn-primary {
|
|
116
|
+
background: var(--gradient);
|
|
117
|
+
color: white;
|
|
118
|
+
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.btn-primary:hover {
|
|
122
|
+
transform: translateY(-2px);
|
|
123
|
+
box-shadow: 0 6px 16px rgba(99, 102, 241, 0.4);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.btn-secondary {
|
|
127
|
+
background: var(--bg-card);
|
|
128
|
+
color: var(--text);
|
|
129
|
+
border: 1px solid var(--border);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.btn-secondary:hover {
|
|
133
|
+
border-color: var(--primary);
|
|
134
|
+
color: var(--primary);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
section {
|
|
138
|
+
margin: 4rem 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
h2 {
|
|
142
|
+
font-size: 2rem;
|
|
143
|
+
margin-bottom: 2rem;
|
|
144
|
+
text-align: center;
|
|
145
|
+
color: var(--text);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.feature-grid {
|
|
149
|
+
display: grid;
|
|
150
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
151
|
+
gap: 1.5rem;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.feature-card {
|
|
155
|
+
background: var(--bg-card);
|
|
156
|
+
border: 1px solid var(--border);
|
|
157
|
+
border-radius: 12px;
|
|
158
|
+
padding: 1.5rem;
|
|
159
|
+
transition: transform 0.2s, border-color 0.2s;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.feature-card:hover {
|
|
163
|
+
transform: translateY(-4px);
|
|
164
|
+
border-color: var(--primary);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.feature-icon {
|
|
168
|
+
font-size: 2.5rem;
|
|
169
|
+
margin-bottom: 1rem;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.feature-card h3 {
|
|
173
|
+
font-size: 1.25rem;
|
|
174
|
+
margin-bottom: 0.5rem;
|
|
175
|
+
color: var(--text);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.feature-card p {
|
|
179
|
+
color: var(--text-muted);
|
|
180
|
+
font-size: 0.95rem;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.quick-start {
|
|
184
|
+
background: var(--bg-card);
|
|
185
|
+
border: 1px solid var(--border);
|
|
186
|
+
border-radius: 12px;
|
|
187
|
+
padding: 2rem;
|
|
188
|
+
text-align: center;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.code-block {
|
|
192
|
+
position: relative;
|
|
193
|
+
background: var(--bg);
|
|
194
|
+
border: 1px solid var(--border);
|
|
195
|
+
border-radius: 8px;
|
|
196
|
+
padding: 1.25rem;
|
|
197
|
+
margin: 1.5rem auto;
|
|
198
|
+
max-width: 600px;
|
|
199
|
+
font-family: 'Fira Code', 'Consolas', monospace;
|
|
200
|
+
font-size: 1rem;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.code-block code {
|
|
204
|
+
color: var(--secondary);
|
|
205
|
+
word-break: break-all;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.copy-btn {
|
|
209
|
+
position: absolute;
|
|
210
|
+
top: 0.5rem;
|
|
211
|
+
right: 0.5rem;
|
|
212
|
+
background: var(--border);
|
|
213
|
+
color: var(--text-muted);
|
|
214
|
+
border: none;
|
|
215
|
+
padding: 0.25rem 0.75rem;
|
|
216
|
+
border-radius: 4px;
|
|
217
|
+
font-size: 0.75rem;
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
transition: all 0.2s;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.copy-btn:hover {
|
|
223
|
+
background: var(--primary);
|
|
224
|
+
color: white;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.note {
|
|
228
|
+
color: var(--text-muted);
|
|
229
|
+
margin-top: 1rem;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.note code {
|
|
233
|
+
background: var(--bg);
|
|
234
|
+
padding: 0.125rem 0.375rem;
|
|
235
|
+
border-radius: 4px;
|
|
236
|
+
font-family: 'Fira Code', 'Consolas', monospace;
|
|
237
|
+
color: var(--secondary);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.note a {
|
|
241
|
+
color: var(--primary);
|
|
242
|
+
text-decoration: none;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.note a:hover {
|
|
246
|
+
text-decoration: underline;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
footer {
|
|
250
|
+
text-align: center;
|
|
251
|
+
padding-top: 3rem;
|
|
252
|
+
border-top: 1px solid var(--border);
|
|
253
|
+
margin-top: 4rem;
|
|
254
|
+
color: var(--text-muted);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.footer-links {
|
|
258
|
+
margin-top: 0.5rem;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.footer-links a {
|
|
262
|
+
color: var(--text-muted);
|
|
263
|
+
text-decoration: none;
|
|
264
|
+
margin: 0 0.5rem;
|
|
265
|
+
transition: color 0.2s;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.footer-links a:hover {
|
|
269
|
+
color: var(--primary);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@media (max-width: 768px) {
|
|
273
|
+
h1 {
|
|
274
|
+
font-size: 2rem;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.stats {
|
|
278
|
+
flex-direction: column;
|
|
279
|
+
gap: 1rem;
|
|
280
|
+
padding: 1rem;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.stat-value {
|
|
284
|
+
font-size: 2rem;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.cta-buttons {
|
|
288
|
+
flex-direction: column;
|
|
289
|
+
align-items: center;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.btn {
|
|
293
|
+
width: 100%;
|
|
294
|
+
max-width: 300px;
|
|
295
|
+
justify-content: center;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.feature-grid {
|
|
299
|
+
grid-template-columns: 1fr;
|
|
300
|
+
}
|
|
301
|
+
}
|
package/lib/claude-runner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from 'child_process';
|
|
1
|
+
import { spawn, spawnSync } from 'child_process';
|
|
2
2
|
|
|
3
3
|
const isWindows = process.platform === 'win32';
|
|
4
4
|
|
|
@@ -319,7 +319,7 @@ class AgentRunner {
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
};
|
|
322
|
-
proc.stdin.on('error', () => {});
|
|
322
|
+
proc.stdin.on('error', () => {});
|
|
323
323
|
proc.stdin.write(JSON.stringify(initRequest) + '\n');
|
|
324
324
|
|
|
325
325
|
let sessionCreated = false;
|
|
@@ -499,8 +499,6 @@ class AgentRegistry {
|
|
|
499
499
|
}
|
|
500
500
|
|
|
501
501
|
listACPAvailable() {
|
|
502
|
-
const { spawnSync } = require('child_process');
|
|
503
|
-
const isWindows = process.platform === 'win32';
|
|
504
502
|
return this.list().filter(agent => {
|
|
505
503
|
try {
|
|
506
504
|
const whichCmd = isWindows ? 'where' : 'which';
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
# AgentGUI
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# AgentGUI
|
|
2
|
+
|
|
3
|
+
[](https://anentrypoint.github.io/agentgui/)
|
|
4
|
+
[](https://www.npmjs.com/package/agentgui)
|
|
5
|
+
|
|
6
|
+
A multi-agent GUI for AI coding assistants. Connects to CLI-based agents (Claude Code, Gemini CLI, OpenCode, Goose, and others) and provides a web interface with real-time streaming output.
|
|
4
7
|
|
|
5
8
|
## Quick Start
|
|
6
9
|
|
|
10
|
+
|
|
7
11
|
```bash
|
|
8
12
|
npx agentgui
|
|
9
13
|
```
|
|
@@ -17,7 +21,18 @@ npm install
|
|
|
17
21
|
npm run dev
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
Open `http://localhost:3000` in your browser.
|
|
24
|
+
Open `http://localhost:3000` in your browser.
|
|
25
|
+
|
|
26
|
+
## 📊 Project Stats
|
|
27
|
+
|
|
28
|
+
Check out our **[GitHub Pages site](https://anentrypoint.github.io/agentgui/)** for:
|
|
29
|
+
|
|
30
|
+
- 📈 Live weekly download statistics
|
|
31
|
+
- ⭐ Star the project button
|
|
32
|
+
- ✨ Feature highlights
|
|
33
|
+
- 🚀 Quick start guide
|
|
34
|
+
|
|
35
|
+
The site automatically updates on every push to main with the latest npm download data.
|
|
21
36
|
|
|
22
37
|
## What It Does
|
|
23
38
|
|
|
@@ -74,3 +89,4 @@ MIT
|
|
|
74
89
|
## Repository
|
|
75
90
|
|
|
76
91
|
https://github.com/AnEntrypoint/agentgui
|
|
92
|
+
|
package/server.js
CHANGED
|
@@ -34,6 +34,11 @@ process.on('unhandledRejection', (reason, promise) => {
|
|
|
34
34
|
if (reason instanceof Error) console.error(reason.stack);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
process.on('SIGINT', () => { console.log('[SIGNAL] SIGINT received (ignored - uncrashable)'); });
|
|
38
|
+
process.on('SIGHUP', () => { console.log('[SIGNAL] SIGHUP received (ignored - uncrashable)'); });
|
|
39
|
+
process.on('beforeExit', (code) => { console.log('[PROCESS] beforeExit with code:', code); });
|
|
40
|
+
process.on('exit', (code) => { console.log('[PROCESS] exit with code:', code); });
|
|
41
|
+
|
|
37
42
|
const ttsTextAccumulators = new Map();
|
|
38
43
|
|
|
39
44
|
let speechModule = null;
|
|
@@ -1047,6 +1052,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1047
1052
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
1048
1053
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
1049
1054
|
if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
|
|
1055
|
+
if (req.headers.upgrade && req.headers.upgrade.toLowerCase() === 'websocket') return;
|
|
1050
1056
|
|
|
1051
1057
|
const pathOnly = req.url.split('?')[0];
|
|
1052
1058
|
|
|
@@ -4044,7 +4050,8 @@ if (watch) {
|
|
|
4044
4050
|
} catch (e) { console.error('Watch error:', e.message); }
|
|
4045
4051
|
}
|
|
4046
4052
|
|
|
4047
|
-
process.on('SIGTERM',
|
|
4053
|
+
process.on('SIGTERM', () => {
|
|
4054
|
+
console.log('[SIGNAL] SIGTERM received - graceful shutdown');
|
|
4048
4055
|
wss.close(() => server.close(() => process.exit(0)));
|
|
4049
4056
|
});
|
|
4050
4057
|
|
package/static/index.html
CHANGED
|
@@ -491,10 +491,10 @@
|
|
|
491
491
|
color: var(--color-text-secondary); user-select: none;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
.terminal-container {
|
|
495
|
-
flex: 1; display: flex; flex-direction: column; overflow: hidden; background: #1e1e1e;
|
|
496
|
-
}
|
|
497
|
-
.terminal-output { flex: 1; overflow: hidden; position: relative; }
|
|
494
|
+
.terminal-container {
|
|
495
|
+
flex: 1; display: flex; flex-direction: column; overflow: hidden; background: #1e1e1e; min-height: 0;
|
|
496
|
+
}
|
|
497
|
+
.terminal-output { flex: 1; overflow: hidden; position: relative; min-height: 0; }
|
|
498
498
|
|
|
499
499
|
/* --- View toggle bar --- */
|
|
500
500
|
.view-toggle-bar {
|
|
@@ -1324,15 +1324,20 @@
|
|
|
1324
1324
|
.sidebar-list { contain: strict; content-visibility: auto; }
|
|
1325
1325
|
.message { contain: layout style; content-visibility: auto; contain-intrinsic-size: auto 80px; }
|
|
1326
1326
|
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1327
|
+
.voice-block .voice-result-stats {
|
|
1328
|
+
font-size: 0.8rem;
|
|
1329
|
+
color: var(--color-text-secondary);
|
|
1330
|
+
margin-top: 0.5rem;
|
|
1331
|
+
padding-top: 0.5rem;
|
|
1332
|
+
border-top: 1px solid var(--color-border);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
.voice-block-content {
|
|
1336
|
+
white-space: pre-wrap;
|
|
1337
|
+
display: block;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
.voice-reread-btn {
|
|
1336
1341
|
position: absolute;
|
|
1337
1342
|
top: 0.5rem;
|
|
1338
1343
|
right: 0.5rem;
|
package/static/js/client.js
CHANGED
|
@@ -166,7 +166,6 @@ class AgentGUIClient {
|
|
|
166
166
|
|
|
167
167
|
this.wsManager.on('error', (data) => {
|
|
168
168
|
console.error('WebSocket error:', data);
|
|
169
|
-
this.showError('Connection error: ' + (data.error?.message || 'unknown'));
|
|
170
169
|
});
|
|
171
170
|
|
|
172
171
|
this.wsManager.on('latency_update', (data) => {
|
|
@@ -34,39 +34,38 @@ class EventConsolidator {
|
|
|
34
34
|
return { consolidated: result, stats };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
37
|
+
_mergeTextBlocks(chunks, stats) {
|
|
38
|
+
const result = [];
|
|
39
|
+
let pending = null;
|
|
40
|
+
const MAX_MERGE = 50 * 1024;
|
|
41
|
+
|
|
42
|
+
for (const c of chunks) {
|
|
43
|
+
if (c.block?.type === 'text') {
|
|
44
|
+
if (pending) {
|
|
45
|
+
const pendingText = pending.block.text || '';
|
|
46
|
+
const newText = c.block.text || '';
|
|
47
|
+
const combined = pendingText + newText;
|
|
48
|
+
if (combined.length <= MAX_MERGE) {
|
|
49
|
+
pending = {
|
|
50
|
+
...pending,
|
|
51
|
+
block: { ...pending.block, text: combined },
|
|
52
|
+
created_at: c.created_at,
|
|
53
|
+
_mergedSequences: [...(pending._mergedSequences || [pending.sequence]), c.sequence]
|
|
54
|
+
};
|
|
55
|
+
stats.textMerged++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (pending) result.push(pending);
|
|
60
|
+
pending = { ...c, _mergedSequences: [c.sequence] };
|
|
61
|
+
} else {
|
|
62
|
+
if (pending) { result.push(pending); pending = null; }
|
|
63
|
+
result.push(c);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (pending) result.push(pending);
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
70
69
|
|
|
71
70
|
_collapseToolPairs(chunks, stats) {
|
|
72
71
|
const toolUseMap = {};
|
package/static/js/voice.js
CHANGED
|
@@ -578,9 +578,9 @@
|
|
|
578
578
|
}
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
-
function stripHtml(text) {
|
|
582
|
-
return text.replace(/<[^>]*>/g, '').replace(
|
|
583
|
-
}
|
|
581
|
+
function stripHtml(text) {
|
|
582
|
+
return text.replace(/<[^>]*>/g, '').replace(/[ \t]+/g, ' ').trim();
|
|
583
|
+
}
|
|
584
584
|
|
|
585
585
|
function addVoiceBlock(text, isUser) {
|
|
586
586
|
var container = document.getElementById('voiceMessages');
|