agentgui 1.0.274 → 1.0.275
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/CLAUDE.md +280 -280
- package/IPFS_DOWNLOADER.md +277 -277
- package/TASK_2C_COMPLETION.md +334 -334
- package/bin/gmgui.cjs +54 -54
- package/build-portable.js +3 -42
- package/database.js +1422 -1406
- package/lib/claude-runner.js +1130 -1130
- package/lib/ipfs-downloader.js +459 -459
- package/lib/speech.js +152 -152
- package/package.json +1 -1
- package/readme.md +76 -76
- package/server.js +3787 -3794
- package/setup-npm-token.sh +68 -68
- package/static/app.js +773 -773
- package/static/event-rendering-showcase.html +708 -708
- package/static/index.html +3178 -3180
- package/static/js/agent-auth.js +298 -298
- package/static/js/audio-recorder-processor.js +18 -18
- package/static/js/client.js +2656 -2656
- package/static/js/conversations.js +583 -583
- package/static/js/dialogs.js +267 -267
- package/static/js/event-consolidator.js +101 -101
- package/static/js/event-filter.js +311 -311
- package/static/js/event-processor.js +452 -452
- package/static/js/features.js +413 -413
- package/static/js/kalman-filter.js +67 -67
- package/static/js/progress-dialog.js +130 -130
- package/static/js/script-runner.js +219 -219
- package/static/js/streaming-renderer.js +2123 -2120
- package/static/js/syntax-highlighter.js +269 -269
- package/static/js/tts-websocket-handler.js +152 -152
- package/static/js/ui-components.js +431 -431
- package/static/js/voice.js +849 -849
- package/static/js/websocket-manager.js +596 -596
- package/static/templates/INDEX.html +465 -465
- package/static/templates/README.md +190 -190
- package/static/templates/agent-capabilities.html +56 -56
- package/static/templates/agent-metadata-panel.html +44 -44
- package/static/templates/agent-status-badge.html +30 -30
- package/static/templates/code-annotation-panel.html +155 -155
- package/static/templates/code-suggestion-panel.html +184 -184
- package/static/templates/command-header.html +77 -77
- package/static/templates/command-output-scrollable.html +118 -118
- package/static/templates/elapsed-time.html +54 -54
- package/static/templates/error-alert.html +106 -106
- package/static/templates/error-history-timeline.html +160 -160
- package/static/templates/error-recovery-options.html +109 -109
- package/static/templates/error-stack-trace.html +95 -95
- package/static/templates/error-summary.html +80 -80
- package/static/templates/event-counter.html +48 -48
- package/static/templates/execution-actions.html +97 -97
- package/static/templates/execution-progress-bar.html +80 -80
- package/static/templates/execution-stepper.html +120 -120
- package/static/templates/file-breadcrumb.html +118 -118
- package/static/templates/file-diff-viewer.html +121 -121
- package/static/templates/file-metadata.html +133 -133
- package/static/templates/file-read-panel.html +66 -66
- package/static/templates/file-write-panel.html +120 -120
- package/static/templates/git-branch-remote.html +107 -107
- package/static/templates/git-diff-list.html +101 -101
- package/static/templates/git-log-visualization.html +153 -153
- package/static/templates/git-status-panel.html +115 -115
- package/static/templates/quality-metrics-display.html +170 -170
- package/static/templates/terminal-output-panel.html +87 -87
- package/static/templates/test-results-display.html +144 -144
- package/static/theme.js +72 -72
- package/test-download-progress.js +223 -223
- package/test-websocket-broadcast.js +147 -147
- package/tests/ipfs-downloader.test.js +370 -370
package/lib/speech.js
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
import { createRequire } from 'module';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
import http from 'http';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
|
|
8
|
-
const require = createRequire(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const ROOT = path.dirname(__dirname);
|
|
11
|
-
|
|
12
|
-
const serverSTT = require('webtalk/server-stt');
|
|
13
|
-
const serverTTS = require('webtalk/server-tts');
|
|
14
|
-
|
|
15
|
-
const EXTRA_VOICE_DIRS = [path.join(ROOT, 'voices')];
|
|
16
|
-
|
|
17
|
-
const POCKET_TTS_VOICES = [
|
|
18
|
-
{ id: 'default', name: 'Default', gender: 'female', accent: 'French' },
|
|
19
|
-
{ id: 'alba', name: 'Alba', gender: 'female', accent: 'French' },
|
|
20
|
-
{ id: 'marius', name: 'Marius', gender: 'male', accent: 'French' },
|
|
21
|
-
{ id: 'javert', name: 'Javert', gender: 'male', accent: 'French' },
|
|
22
|
-
{ id: 'jean', name: 'Jean', gender: 'male', accent: 'French' },
|
|
23
|
-
{ id: 'fantine', name: 'Fantine', gender: 'female', accent: 'French' },
|
|
24
|
-
{ id: 'cosette', name: 'Cosette', gender: 'female', accent: 'French' },
|
|
25
|
-
{ id: 'eponine', name: 'Eponine', gender: 'female', accent: 'French' },
|
|
26
|
-
{ id: 'azelma', name: 'Azelma', gender: 'female', accent: 'French' },
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const PREDEFINED_IDS = new Set(POCKET_TTS_VOICES.filter(v => v.id !== 'default').map(v => v.id));
|
|
30
|
-
const POCKET_PORT = 8787;
|
|
31
|
-
|
|
32
|
-
function safeGetVoices(extraDirs) {
|
|
33
|
-
if (typeof serverTTS.getVoices === 'function') {
|
|
34
|
-
return serverTTS.getVoices(extraDirs || []);
|
|
35
|
-
}
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const needsPatch = !safeGetVoices(EXTRA_VOICE_DIRS).some(v => v.id === 'alba' && !v.isCustom);
|
|
40
|
-
|
|
41
|
-
function synthesizeDirect(text, voiceId) {
|
|
42
|
-
const voicePath = serverTTS.findVoiceFile(voiceId, EXTRA_VOICE_DIRS);
|
|
43
|
-
const isPredefined = voiceId && PREDEFINED_IDS.has(voiceId);
|
|
44
|
-
const boundary = '----PocketTTS' + Date.now();
|
|
45
|
-
const parts = [];
|
|
46
|
-
parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="text"\r\n\r\n${text}\r\n`);
|
|
47
|
-
if (voicePath) {
|
|
48
|
-
const data = fs.readFileSync(voicePath);
|
|
49
|
-
const name = path.basename(voicePath);
|
|
50
|
-
parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="voice_wav"; filename="${name}"\r\nContent-Type: audio/wav\r\n\r\n`);
|
|
51
|
-
parts.push(data);
|
|
52
|
-
parts.push('\r\n');
|
|
53
|
-
} else if (isPredefined) {
|
|
54
|
-
parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="voice_url"\r\n\r\n${voiceId}\r\n`);
|
|
55
|
-
}
|
|
56
|
-
parts.push(`--${boundary}--\r\n`);
|
|
57
|
-
const body = Buffer.concat(parts.map(p => Buffer.isBuffer(p) ? p : Buffer.from(p)));
|
|
58
|
-
return new Promise((resolve, reject) => {
|
|
59
|
-
const req = http.request({
|
|
60
|
-
hostname: '127.0.0.1', port: POCKET_PORT, path: '/tts', method: 'POST',
|
|
61
|
-
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}`, 'Content-Length': body.length },
|
|
62
|
-
timeout: 60000,
|
|
63
|
-
}, res => {
|
|
64
|
-
if (res.statusCode !== 200) {
|
|
65
|
-
let e = '';
|
|
66
|
-
res.on('data', d => e += d);
|
|
67
|
-
res.on('end', () => reject(new Error(`pocket-tts HTTP ${res.statusCode}: ${e}`)));
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const chunks = [];
|
|
71
|
-
res.on('data', d => chunks.push(d));
|
|
72
|
-
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
73
|
-
});
|
|
74
|
-
req.on('error', reject);
|
|
75
|
-
req.on('timeout', () => { req.destroy(); reject(new Error('pocket-tts timeout')); });
|
|
76
|
-
req.write(body);
|
|
77
|
-
req.end();
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function transcribe(audioBuffer) {
|
|
82
|
-
return serverSTT.transcribe(audioBuffer);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function getSTT() {
|
|
86
|
-
return serverSTT.getSTT();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function synthesize(text, voiceId) {
|
|
90
|
-
if (needsPatch && voiceId && PREDEFINED_IDS.has(voiceId)) {
|
|
91
|
-
return synthesizeDirect(text, voiceId);
|
|
92
|
-
}
|
|
93
|
-
return serverTTS.synthesize(text, voiceId, EXTRA_VOICE_DIRS);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function synthesizeStream(text, voiceId) {
|
|
97
|
-
if (needsPatch && voiceId && PREDEFINED_IDS.has(voiceId)) {
|
|
98
|
-
return (async function* () {
|
|
99
|
-
yield await synthesizeDirect(text, voiceId);
|
|
100
|
-
})();
|
|
101
|
-
}
|
|
102
|
-
return serverTTS.synthesizeStream(text, voiceId, EXTRA_VOICE_DIRS);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function getVoices() {
|
|
106
|
-
const upstream = safeGetVoices(EXTRA_VOICE_DIRS);
|
|
107
|
-
const custom = upstream.filter(v => v.isCustom);
|
|
108
|
-
return [...POCKET_TTS_VOICES, ...custom];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function getStatus() {
|
|
112
|
-
const sttStatus = serverSTT.getStatus();
|
|
113
|
-
const ttsStatus = serverTTS.getStatus();
|
|
114
|
-
return {
|
|
115
|
-
sttReady: sttStatus.ready,
|
|
116
|
-
ttsReady: ttsStatus.ready,
|
|
117
|
-
sttLoading: sttStatus.loading,
|
|
118
|
-
ttsLoading: false,
|
|
119
|
-
sttError: sttStatus.error,
|
|
120
|
-
ttsError: ttsStatus.ready ? null : (ttsStatus.lastError || 'pocket-tts not running'),
|
|
121
|
-
pocketTts: ttsStatus,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function preloadTTS() {
|
|
126
|
-
if (typeof serverTTS.findVoiceFile !== 'function' || typeof serverTTS.start !== 'function') {
|
|
127
|
-
console.log('[TTS] pocket-tts functions not available');
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const defaultVoice = serverTTS.findVoiceFile('custom_cleetus', EXTRA_VOICE_DIRS) || '/config/voices/cleetus.wav';
|
|
131
|
-
const voicePath = fs.existsSync(defaultVoice) ? defaultVoice : null;
|
|
132
|
-
serverTTS.start(voicePath, {}).then(ok => {
|
|
133
|
-
if (ok) console.log('[TTS] pocket-tts sidecar started');
|
|
134
|
-
else console.log('[TTS] pocket-tts failed to start');
|
|
135
|
-
}).catch(err => {
|
|
136
|
-
console.error('[TTS] pocket-tts start error:', err.message);
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function ttsCacheKey(text, voiceId) {
|
|
141
|
-
return typeof serverTTS.ttsCacheKey === 'function' ? serverTTS.ttsCacheKey(text, voiceId) : null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function ttsCacheGet(key) {
|
|
145
|
-
return typeof serverTTS.ttsCacheGet === 'function' ? serverTTS.ttsCacheGet(key) : null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function splitSentences(text) {
|
|
149
|
-
return typeof serverTTS.splitSentences === 'function' ? serverTTS.splitSentences(text) : [text];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export { transcribe, synthesize, synthesizeStream, getSTT, getStatus, getVoices, preloadTTS, ttsCacheKey, ttsCacheGet, splitSentences };
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const ROOT = path.dirname(__dirname);
|
|
11
|
+
|
|
12
|
+
const serverSTT = require('webtalk/server-stt');
|
|
13
|
+
const serverTTS = require('webtalk/server-tts');
|
|
14
|
+
|
|
15
|
+
const EXTRA_VOICE_DIRS = [path.join(ROOT, 'voices')];
|
|
16
|
+
|
|
17
|
+
const POCKET_TTS_VOICES = [
|
|
18
|
+
{ id: 'default', name: 'Default', gender: 'female', accent: 'French' },
|
|
19
|
+
{ id: 'alba', name: 'Alba', gender: 'female', accent: 'French' },
|
|
20
|
+
{ id: 'marius', name: 'Marius', gender: 'male', accent: 'French' },
|
|
21
|
+
{ id: 'javert', name: 'Javert', gender: 'male', accent: 'French' },
|
|
22
|
+
{ id: 'jean', name: 'Jean', gender: 'male', accent: 'French' },
|
|
23
|
+
{ id: 'fantine', name: 'Fantine', gender: 'female', accent: 'French' },
|
|
24
|
+
{ id: 'cosette', name: 'Cosette', gender: 'female', accent: 'French' },
|
|
25
|
+
{ id: 'eponine', name: 'Eponine', gender: 'female', accent: 'French' },
|
|
26
|
+
{ id: 'azelma', name: 'Azelma', gender: 'female', accent: 'French' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const PREDEFINED_IDS = new Set(POCKET_TTS_VOICES.filter(v => v.id !== 'default').map(v => v.id));
|
|
30
|
+
const POCKET_PORT = 8787;
|
|
31
|
+
|
|
32
|
+
function safeGetVoices(extraDirs) {
|
|
33
|
+
if (typeof serverTTS.getVoices === 'function') {
|
|
34
|
+
return serverTTS.getVoices(extraDirs || []);
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const needsPatch = !safeGetVoices(EXTRA_VOICE_DIRS).some(v => v.id === 'alba' && !v.isCustom);
|
|
40
|
+
|
|
41
|
+
function synthesizeDirect(text, voiceId) {
|
|
42
|
+
const voicePath = serverTTS.findVoiceFile(voiceId, EXTRA_VOICE_DIRS);
|
|
43
|
+
const isPredefined = voiceId && PREDEFINED_IDS.has(voiceId);
|
|
44
|
+
const boundary = '----PocketTTS' + Date.now();
|
|
45
|
+
const parts = [];
|
|
46
|
+
parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="text"\r\n\r\n${text}\r\n`);
|
|
47
|
+
if (voicePath) {
|
|
48
|
+
const data = fs.readFileSync(voicePath);
|
|
49
|
+
const name = path.basename(voicePath);
|
|
50
|
+
parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="voice_wav"; filename="${name}"\r\nContent-Type: audio/wav\r\n\r\n`);
|
|
51
|
+
parts.push(data);
|
|
52
|
+
parts.push('\r\n');
|
|
53
|
+
} else if (isPredefined) {
|
|
54
|
+
parts.push(`--${boundary}\r\nContent-Disposition: form-data; name="voice_url"\r\n\r\n${voiceId}\r\n`);
|
|
55
|
+
}
|
|
56
|
+
parts.push(`--${boundary}--\r\n`);
|
|
57
|
+
const body = Buffer.concat(parts.map(p => Buffer.isBuffer(p) ? p : Buffer.from(p)));
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const req = http.request({
|
|
60
|
+
hostname: '127.0.0.1', port: POCKET_PORT, path: '/tts', method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}`, 'Content-Length': body.length },
|
|
62
|
+
timeout: 60000,
|
|
63
|
+
}, res => {
|
|
64
|
+
if (res.statusCode !== 200) {
|
|
65
|
+
let e = '';
|
|
66
|
+
res.on('data', d => e += d);
|
|
67
|
+
res.on('end', () => reject(new Error(`pocket-tts HTTP ${res.statusCode}: ${e}`)));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const chunks = [];
|
|
71
|
+
res.on('data', d => chunks.push(d));
|
|
72
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
73
|
+
});
|
|
74
|
+
req.on('error', reject);
|
|
75
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('pocket-tts timeout')); });
|
|
76
|
+
req.write(body);
|
|
77
|
+
req.end();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function transcribe(audioBuffer) {
|
|
82
|
+
return serverSTT.transcribe(audioBuffer);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function getSTT() {
|
|
86
|
+
return serverSTT.getSTT();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function synthesize(text, voiceId) {
|
|
90
|
+
if (needsPatch && voiceId && PREDEFINED_IDS.has(voiceId)) {
|
|
91
|
+
return synthesizeDirect(text, voiceId);
|
|
92
|
+
}
|
|
93
|
+
return serverTTS.synthesize(text, voiceId, EXTRA_VOICE_DIRS);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function synthesizeStream(text, voiceId) {
|
|
97
|
+
if (needsPatch && voiceId && PREDEFINED_IDS.has(voiceId)) {
|
|
98
|
+
return (async function* () {
|
|
99
|
+
yield await synthesizeDirect(text, voiceId);
|
|
100
|
+
})();
|
|
101
|
+
}
|
|
102
|
+
return serverTTS.synthesizeStream(text, voiceId, EXTRA_VOICE_DIRS);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getVoices() {
|
|
106
|
+
const upstream = safeGetVoices(EXTRA_VOICE_DIRS);
|
|
107
|
+
const custom = upstream.filter(v => v.isCustom);
|
|
108
|
+
return [...POCKET_TTS_VOICES, ...custom];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function getStatus() {
|
|
112
|
+
const sttStatus = serverSTT.getStatus();
|
|
113
|
+
const ttsStatus = serverTTS.getStatus();
|
|
114
|
+
return {
|
|
115
|
+
sttReady: sttStatus.ready,
|
|
116
|
+
ttsReady: ttsStatus.ready,
|
|
117
|
+
sttLoading: sttStatus.loading,
|
|
118
|
+
ttsLoading: false,
|
|
119
|
+
sttError: sttStatus.error,
|
|
120
|
+
ttsError: ttsStatus.ready ? null : (ttsStatus.lastError || 'pocket-tts not running'),
|
|
121
|
+
pocketTts: ttsStatus,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function preloadTTS() {
|
|
126
|
+
if (typeof serverTTS.findVoiceFile !== 'function' || typeof serverTTS.start !== 'function') {
|
|
127
|
+
console.log('[TTS] pocket-tts functions not available');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const defaultVoice = serverTTS.findVoiceFile('custom_cleetus', EXTRA_VOICE_DIRS) || '/config/voices/cleetus.wav';
|
|
131
|
+
const voicePath = fs.existsSync(defaultVoice) ? defaultVoice : null;
|
|
132
|
+
serverTTS.start(voicePath, {}).then(ok => {
|
|
133
|
+
if (ok) console.log('[TTS] pocket-tts sidecar started');
|
|
134
|
+
else console.log('[TTS] pocket-tts failed to start');
|
|
135
|
+
}).catch(err => {
|
|
136
|
+
console.error('[TTS] pocket-tts start error:', err.message);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function ttsCacheKey(text, voiceId) {
|
|
141
|
+
return typeof serverTTS.ttsCacheKey === 'function' ? serverTTS.ttsCacheKey(text, voiceId) : null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function ttsCacheGet(key) {
|
|
145
|
+
return typeof serverTTS.ttsCacheGet === 'function' ? serverTTS.ttsCacheGet(key) : null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function splitSentences(text) {
|
|
149
|
+
return typeof serverTTS.splitSentences === 'function' ? serverTTS.splitSentences(text) : [text];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { transcribe, synthesize, synthesizeStream, getSTT, getStatus, getVoices, preloadTTS, ttsCacheKey, ttsCacheGet, splitSentences };
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
# AgentGUI
|
|
2
|
-
|
|
3
|
-
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
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npx agentgui
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Or install and run manually:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
git clone https://github.com/AnEntrypoint/agentgui.git
|
|
15
|
-
cd agentgui
|
|
16
|
-
npm install
|
|
17
|
-
npm run dev
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Open `http://localhost:3000` in your browser.
|
|
21
|
-
|
|
22
|
-
## What It Does
|
|
23
|
-
|
|
24
|
-
- Auto-discovers AI coding agents installed on your system (Claude Code, Gemini CLI, OpenCode, Goose, Codex, Kiro, etc.)
|
|
25
|
-
- Runs agents with streaming JSON output and displays results in real-time via WebSocket
|
|
26
|
-
- Manages conversations with SQLite persistence
|
|
27
|
-
- Supports concurrent agent sessions
|
|
28
|
-
- Provides file browsing and upload for agent working directories
|
|
29
|
-
- Includes speech-to-text and text-to-speech
|
|
30
|
-
|
|
31
|
-
## Architecture
|
|
32
|
-
|
|
33
|
-
- `server.js` - HTTP server, REST API, WebSocket endpoint, static file serving
|
|
34
|
-
- `database.js` - SQLite database (WAL mode) at `~/.gmgui/data.db`
|
|
35
|
-
- `lib/claude-runner.js` - Agent runner framework, spawns CLI processes and parses streaming output
|
|
36
|
-
- `lib/speech.js` - Speech processing via Hugging Face transformers
|
|
37
|
-
- `static/` - Browser client with streaming renderer, WebSocket manager, and HTML templates
|
|
38
|
-
- `bin/gmgui.cjs` - CLI entry point for `npx agentgui`
|
|
39
|
-
|
|
40
|
-
## Text-to-Speech on Windows
|
|
41
|
-
|
|
42
|
-
On Windows, AgentGUI automatically sets up pocket-tts (text-to-speech) on your first TTS request. No manual setup required.
|
|
43
|
-
|
|
44
|
-
### What Happens
|
|
45
|
-
1. Server detects Python 3.9+ installation
|
|
46
|
-
2. Creates virtual environment at `~/.gmgui/pocket-venv`
|
|
47
|
-
3. Installs pocket-tts via pip
|
|
48
|
-
4. All subsequent TTS requests use cached installation
|
|
49
|
-
|
|
50
|
-
### Requirements
|
|
51
|
-
- Python 3.9+ (check with `python --version`)
|
|
52
|
-
- ~200 MB free disk space
|
|
53
|
-
- Internet connection for first setup
|
|
54
|
-
|
|
55
|
-
### Troubleshooting
|
|
56
|
-
- **Python not found**: Download from https://www.python.org and ensure "Add Python to PATH" is checked
|
|
57
|
-
- **Setup fails**: Check that you have write access to your home directory (~/.gmgui/)
|
|
58
|
-
- **Manual cleanup**: Delete `%USERPROFILE%\.gmgui\pocket-venv` and try again
|
|
59
|
-
|
|
60
|
-
For manual setup or detailed troubleshooting, see the setup instructions in the code or check `/api/speech-status` endpoint for error details.
|
|
61
|
-
|
|
62
|
-
## Configuration
|
|
63
|
-
|
|
64
|
-
| Variable | Default | Description |
|
|
65
|
-
|----------|---------|-------------|
|
|
66
|
-
| `PORT` | 3000 | Server port |
|
|
67
|
-
| `BASE_URL` | /gm | URL prefix |
|
|
68
|
-
| `HOT_RELOAD` | true | Watch mode for development |
|
|
69
|
-
|
|
70
|
-
## License
|
|
71
|
-
|
|
72
|
-
MIT
|
|
73
|
-
|
|
74
|
-
## Repository
|
|
75
|
-
|
|
76
|
-
https://github.com/AnEntrypoint/agentgui
|
|
1
|
+
# AgentGUI
|
|
2
|
+
|
|
3
|
+
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
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx agentgui
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install and run manually:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/AnEntrypoint/agentgui.git
|
|
15
|
+
cd agentgui
|
|
16
|
+
npm install
|
|
17
|
+
npm run dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Open `http://localhost:3000` in your browser.
|
|
21
|
+
|
|
22
|
+
## What It Does
|
|
23
|
+
|
|
24
|
+
- Auto-discovers AI coding agents installed on your system (Claude Code, Gemini CLI, OpenCode, Goose, Codex, Kiro, etc.)
|
|
25
|
+
- Runs agents with streaming JSON output and displays results in real-time via WebSocket
|
|
26
|
+
- Manages conversations with SQLite persistence
|
|
27
|
+
- Supports concurrent agent sessions
|
|
28
|
+
- Provides file browsing and upload for agent working directories
|
|
29
|
+
- Includes speech-to-text and text-to-speech
|
|
30
|
+
|
|
31
|
+
## Architecture
|
|
32
|
+
|
|
33
|
+
- `server.js` - HTTP server, REST API, WebSocket endpoint, static file serving
|
|
34
|
+
- `database.js` - SQLite database (WAL mode) at `~/.gmgui/data.db`
|
|
35
|
+
- `lib/claude-runner.js` - Agent runner framework, spawns CLI processes and parses streaming output
|
|
36
|
+
- `lib/speech.js` - Speech processing via Hugging Face transformers
|
|
37
|
+
- `static/` - Browser client with streaming renderer, WebSocket manager, and HTML templates
|
|
38
|
+
- `bin/gmgui.cjs` - CLI entry point for `npx agentgui`
|
|
39
|
+
|
|
40
|
+
## Text-to-Speech on Windows
|
|
41
|
+
|
|
42
|
+
On Windows, AgentGUI automatically sets up pocket-tts (text-to-speech) on your first TTS request. No manual setup required.
|
|
43
|
+
|
|
44
|
+
### What Happens
|
|
45
|
+
1. Server detects Python 3.9+ installation
|
|
46
|
+
2. Creates virtual environment at `~/.gmgui/pocket-venv`
|
|
47
|
+
3. Installs pocket-tts via pip
|
|
48
|
+
4. All subsequent TTS requests use cached installation
|
|
49
|
+
|
|
50
|
+
### Requirements
|
|
51
|
+
- Python 3.9+ (check with `python --version`)
|
|
52
|
+
- ~200 MB free disk space
|
|
53
|
+
- Internet connection for first setup
|
|
54
|
+
|
|
55
|
+
### Troubleshooting
|
|
56
|
+
- **Python not found**: Download from https://www.python.org and ensure "Add Python to PATH" is checked
|
|
57
|
+
- **Setup fails**: Check that you have write access to your home directory (~/.gmgui/)
|
|
58
|
+
- **Manual cleanup**: Delete `%USERPROFILE%\.gmgui\pocket-venv` and try again
|
|
59
|
+
|
|
60
|
+
For manual setup or detailed troubleshooting, see the setup instructions in the code or check `/api/speech-status` endpoint for error details.
|
|
61
|
+
|
|
62
|
+
## Configuration
|
|
63
|
+
|
|
64
|
+
| Variable | Default | Description |
|
|
65
|
+
|----------|---------|-------------|
|
|
66
|
+
| `PORT` | 3000 | Server port |
|
|
67
|
+
| `BASE_URL` | /gm | URL prefix |
|
|
68
|
+
| `HOT_RELOAD` | true | Watch mode for development |
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
73
|
+
|
|
74
|
+
## Repository
|
|
75
|
+
|
|
76
|
+
https://github.com/AnEntrypoint/agentgui
|