agentgui 1.0.801 → 1.0.802
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 +2 -4
- package/lib/acp-sdk-manager.js +14 -2
- package/lib/speech-manager.js +3 -31
- package/lib/speech.js +50 -67
- package/lib/tool-manager.js +0 -1
- package/lib/tool-spawner.js +0 -27
- package/lib/tool-version.js +4 -20
- package/package.json +16 -1
package/CLAUDE.md
CHANGED
|
@@ -39,7 +39,7 @@ lib/oauth-codex.js Codex CLI OAuth flow (PKCE S256, token exchange, callback
|
|
|
39
39
|
lib/plugin-interface.js Plugin interface contract definition
|
|
40
40
|
lib/plugin-loader.js Plugin discovery and loading (EventEmitter-based)
|
|
41
41
|
lib/pm2-manager.js PM2 process management wrapper
|
|
42
|
-
lib/speech.js Speech-to-text and text-to-speech via
|
|
42
|
+
lib/speech.js Speech-to-text and text-to-speech via @huggingface/transformers
|
|
43
43
|
lib/speech-manager.js TTS orchestration (eager TTS, voice cache, model download, broadcastModelProgress)
|
|
44
44
|
lib/tool-install-machine.js XState v5 machine per tool: unchecked/checking/idle/installing/installed/updating/needs_update/failed states
|
|
45
45
|
lib/tool-manager.js Tool facade - re-exports from tool-version, tool-spawner, tool-provisioner
|
|
@@ -223,13 +223,11 @@ Tool updates are managed through a complete pipeline:
|
|
|
223
223
|
|
|
224
224
|
## Tool Detection System
|
|
225
225
|
|
|
226
|
-
TOOLS array in `lib/tool-manager.js` —
|
|
226
|
+
TOOLS array in `lib/tool-manager.js` — two categories:
|
|
227
227
|
- **`cli`**: `{ id, name, pkg, category: 'cli' }` — detected via `which <bin>` + `<bin> --version`
|
|
228
228
|
- **`plugin`**: `{ id, name, pkg, installPkg, pluginId, category: 'plugin', frameWork }` — detected via plugin.json files
|
|
229
|
-
- **`npm-local`**: `{ id, name, pkg, category: 'npm-local' }` — detected via `node_modules/<pkg>/package.json`, installed via `npm install --save` in project root
|
|
230
229
|
|
|
231
230
|
Current tools:
|
|
232
|
-
- `speech-engine`: pkg=`webtalk`, category=`npm-local` — STT/TTS (Whisper + ONNX), not a hard npm dep; install from tools tab
|
|
233
231
|
- `cli-claude`: bin=`claude`, pkg=`@anthropic-ai/claude-code`
|
|
234
232
|
- `cli-opencode`: bin=`opencode`, pkg=`opencode-ai`
|
|
235
233
|
- `cli-gemini`: bin=`gemini`, pkg=`@google/gemini-cli`
|
package/lib/acp-sdk-manager.js
CHANGED
|
@@ -179,8 +179,20 @@ export async function queryModels(agentId) {
|
|
|
179
179
|
const port = await ensureRunning(agentId);
|
|
180
180
|
if (!port) return [];
|
|
181
181
|
try {
|
|
182
|
-
const res = await fetch('http://127.0.0.1:' + port + '/
|
|
183
|
-
|
|
182
|
+
const res = await fetch('http://127.0.0.1:' + port + '/provider', { signal: AbortSignal.timeout(5000) });
|
|
183
|
+
if (!res.ok) return [];
|
|
184
|
+
const data = await res.json();
|
|
185
|
+
const connected = data.connected || [];
|
|
186
|
+
const allProvs = Object.fromEntries((data.all || []).map(p => [p.id, p]));
|
|
187
|
+
const src = connected.length ? connected : Object.keys(allProvs);
|
|
188
|
+
const models = [];
|
|
189
|
+
for (const pid of src) {
|
|
190
|
+
const prov = allProvs[pid] || {};
|
|
191
|
+
for (const [, m] of Object.entries(prov.models || {})) {
|
|
192
|
+
models.push({ id: (m.providerID || pid) + '/' + m.id, label: m.name || m.id });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return models;
|
|
184
196
|
} catch (_) { return []; }
|
|
185
197
|
}
|
|
186
198
|
|
package/lib/speech-manager.js
CHANGED
|
@@ -14,38 +14,14 @@ export function initSpeechManager({ broadcastSync, syncClients, queries }) {
|
|
|
14
14
|
_queries = queries;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function isSpeechInstalled() {
|
|
18
|
-
try {
|
|
19
|
-
const r = createRequire(import.meta.url);
|
|
20
|
-
r.resolve('webtalk/speech');
|
|
21
|
-
return true;
|
|
22
|
-
} catch (_) { return false; }
|
|
23
|
-
}
|
|
24
|
-
|
|
25
17
|
export async function ensurePocketTtsSetup(onProgress) {
|
|
26
18
|
const r = createRequire(import.meta.url);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return serverTTS.ensureInstalled(onProgress);
|
|
30
|
-
} catch (err) {
|
|
31
|
-
if (err.code === 'MODULE_NOT_FOUND' || err.code === 'SPEECH_NOT_INSTALLED') return false;
|
|
32
|
-
throw err;
|
|
33
|
-
}
|
|
19
|
+
const serverTTS = r('webtalk/server-tts');
|
|
20
|
+
return serverTTS.ensureInstalled(onProgress);
|
|
34
21
|
}
|
|
35
22
|
|
|
36
23
|
export async function getSpeech() {
|
|
37
|
-
if (!speechModule)
|
|
38
|
-
try {
|
|
39
|
-
speechModule = await import('./speech.js');
|
|
40
|
-
} catch (err) {
|
|
41
|
-
if (err.code === 'MODULE_NOT_FOUND' || err.code === 'SPEECH_NOT_INSTALLED') {
|
|
42
|
-
const e = new Error('speech_not_installed: Install Speech Engine from the Tools tab.');
|
|
43
|
-
e.code = 'SPEECH_NOT_INSTALLED';
|
|
44
|
-
throw e;
|
|
45
|
-
}
|
|
46
|
-
throw err;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
24
|
+
if (!speechModule) speechModule = await import('./speech.js');
|
|
49
25
|
return speechModule;
|
|
50
26
|
}
|
|
51
27
|
|
|
@@ -140,10 +116,6 @@ async function validateAndCleanupModels(modelsDir) {
|
|
|
140
116
|
}
|
|
141
117
|
|
|
142
118
|
export async function ensureModelsDownloaded() {
|
|
143
|
-
if (!isSpeechInstalled()) {
|
|
144
|
-
console.log('[MODELS] Speech Engine not installed, skipping model download.');
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
119
|
if (modelDownloadState.downloading) {
|
|
148
120
|
return new Promise(resolve => { modelDownloadState.waiters.push(resolve); });
|
|
149
121
|
}
|
package/lib/speech.js
CHANGED
|
@@ -1,67 +1,50 @@
|
|
|
1
|
-
import { createRequire } from 'module';
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function synthesizeStream(...args) { return loadSpeech().synthesizeStream(...args); }
|
|
54
|
-
export function getSTT() { return loadSpeech().getSTT(); }
|
|
55
|
-
export function getStatus() {
|
|
56
|
-
try { return loadSpeech().getStatus(); }
|
|
57
|
-
catch (_) { return { sttReady: false, ttsReady: false, sttLoading: false, ttsLoading: false, speechNotInstalled: true }; }
|
|
58
|
-
}
|
|
59
|
-
export function getVoices() {
|
|
60
|
-
try { return loadSpeech().getVoices(); }
|
|
61
|
-
catch (_) { return []; }
|
|
62
|
-
}
|
|
63
|
-
export function preloadTTS() { try { loadSpeech().preloadTTS?.(); } catch (_) {} }
|
|
64
|
-
export function resetSTTError() { try { loadSpeech().resetSTTError?.(); } catch (_) {} }
|
|
65
|
-
export function clearCorruptedSTTCache() { try { return loadSpeech().clearCorruptedSTTCache?.(); } catch (_) {} }
|
|
66
|
-
export function getSttOptions() { try { return loadSpeech().getSttOptions?.(); } catch (_) { return {}; } }
|
|
67
|
-
export const VOICE_DIRS = (() => { try { return loadSpeech().VOICE_DIRS; } catch (_) { return []; } })();
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
const speech = require('webtalk/speech');
|
|
4
|
+
|
|
5
|
+
const ttsMemCache = new Map();
|
|
6
|
+
const TTS_CACHE_MAX_BYTES = 10 * 1024 * 1024;
|
|
7
|
+
let ttsCacheBytes = 0;
|
|
8
|
+
|
|
9
|
+
function ttsCacheKey(text, voiceId) {
|
|
10
|
+
return (voiceId || 'default') + ':' + text;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function ttsCacheGet(key) {
|
|
14
|
+
return ttsMemCache.get(key) || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ttsCacheSet(key, wav) {
|
|
18
|
+
if (ttsMemCache.has(key)) return;
|
|
19
|
+
const size = wav ? wav.length : 0;
|
|
20
|
+
while (ttsCacheBytes + size > TTS_CACHE_MAX_BYTES && ttsMemCache.size > 0) {
|
|
21
|
+
const oldest = ttsMemCache.keys().next().value;
|
|
22
|
+
const old = ttsMemCache.get(oldest);
|
|
23
|
+
ttsCacheBytes -= old ? old.length : 0;
|
|
24
|
+
ttsMemCache.delete(oldest);
|
|
25
|
+
}
|
|
26
|
+
ttsMemCache.set(key, wav);
|
|
27
|
+
ttsCacheBytes += size;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function synthesizeWithCache(text, voiceId) {
|
|
31
|
+
const key = ttsCacheKey(text, voiceId);
|
|
32
|
+
const cached = ttsCacheGet(key);
|
|
33
|
+
if (cached) return cached;
|
|
34
|
+
const wav = await speech.synthesize(text, voiceId);
|
|
35
|
+
ttsCacheSet(key, wav);
|
|
36
|
+
return wav;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const transcribe = speech.transcribe;
|
|
40
|
+
export const synthesize = synthesizeWithCache;
|
|
41
|
+
export const synthesizeStream = speech.synthesizeStream;
|
|
42
|
+
export const getSTT = speech.getSTT;
|
|
43
|
+
export const getStatus = speech.getStatus;
|
|
44
|
+
export const getVoices = speech.getVoices;
|
|
45
|
+
export const preloadTTS = speech.preloadTTS;
|
|
46
|
+
export { ttsCacheKey, ttsCacheGet, ttsCacheSet };
|
|
47
|
+
export const resetSTTError = speech.resetSTTError;
|
|
48
|
+
export const clearCorruptedSTTCache = speech.clearCorruptedSTTCache;
|
|
49
|
+
export const getSttOptions = speech.getSttOptions;
|
|
50
|
+
export const VOICE_DIRS = speech.VOICE_DIRS;
|
package/lib/tool-manager.js
CHANGED
|
@@ -3,7 +3,6 @@ import { createInstaller } from './tool-spawner.js';
|
|
|
3
3
|
import { autoProvision as _autoProvision, startPeriodicUpdateCheck as _startPeriodicUpdateCheck, stopPeriodicUpdateCheck } from './tool-provisioner.js';
|
|
4
4
|
|
|
5
5
|
const TOOLS = [
|
|
6
|
-
{ id: 'speech-engine', name: 'Speech Engine', pkg: 'webtalk', category: 'npm-local', description: 'STT/TTS support (Whisper + ONNX)' },
|
|
7
6
|
{ id: 'cli-claude', name: 'Claude Code', pkg: '@anthropic-ai/claude-code', category: 'cli' },
|
|
8
7
|
{ id: 'cli-opencode', name: 'OpenCode', pkg: 'opencode-ai', category: 'cli' },
|
|
9
8
|
{ id: 'cli-gemini', name: 'Gemini CLI', pkg: '@google/gemini-cli', category: 'cli' },
|
package/lib/tool-spawner.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
3
|
import { getCliVersion, getInstalledVersion, clearVersionCache, checkToolViaBunx } from './tool-version.js';
|
|
6
4
|
import * as toolInstallMachine from './tool-install-machine.js';
|
|
7
5
|
|
|
@@ -75,33 +73,8 @@ const spawnBunxProc = (pkg, onProgress) => new Promise((resolve) => {
|
|
|
75
73
|
});
|
|
76
74
|
});
|
|
77
75
|
|
|
78
|
-
const spawnNpmLocalInstall = (pkg, onProgress) => new Promise((resolve) => {
|
|
79
|
-
const cmd = isWindows ? 'npm.cmd' : 'npm';
|
|
80
|
-
let completed = false, stderr = '', stdout = '';
|
|
81
|
-
let proc;
|
|
82
|
-
const rootDir = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
83
|
-
try {
|
|
84
|
-
proc = spawn(cmd, ['install', '--save', pkg], { stdio: ['pipe', 'pipe', 'pipe'], timeout: 300000, shell: isWindows, windowsHide: true, cwd: rootDir });
|
|
85
|
-
} catch (err) {
|
|
86
|
-
return resolve({ success: false, error: `Failed to spawn npm install: ${err.message}` });
|
|
87
|
-
}
|
|
88
|
-
if (!proc) return resolve({ success: false, error: 'Failed to spawn npm process' });
|
|
89
|
-
const timer = setTimeout(() => { if (!completed) { completed = true; try { proc.kill('SIGKILL'); } catch (_) {} resolve({ success: false, error: 'Timeout (5min)' }); } }, 300000);
|
|
90
|
-
const onData = (d) => { if (onProgress) onProgress({ type: 'progress', data: d.toString() }); };
|
|
91
|
-
if (proc.stdout) proc.stdout.on('data', (d) => { stdout += d.toString(); onData(d); });
|
|
92
|
-
if (proc.stderr) proc.stderr.on('data', (d) => { stderr += d.toString(); onData(d); });
|
|
93
|
-
proc.on('close', (code) => {
|
|
94
|
-
clearTimeout(timer);
|
|
95
|
-
if (completed) return;
|
|
96
|
-
completed = true;
|
|
97
|
-
resolve(code === 0 ? { success: true, error: null, pkg } : { success: false, error: (stdout + stderr).substring(0, 1000) || 'Failed' });
|
|
98
|
-
});
|
|
99
|
-
proc.on('error', (err) => { clearTimeout(timer); if (!completed) { completed = true; resolve({ success: false, error: err.message }); } });
|
|
100
|
-
});
|
|
101
|
-
|
|
102
76
|
function spawnForTool(tool, onProgress) {
|
|
103
77
|
const pkg = tool.installPkg || tool.pkg;
|
|
104
|
-
if (tool.category === 'npm-local') return spawnNpmLocalInstall(pkg, onProgress);
|
|
105
78
|
return tool.category === 'cli' ? spawnNpmInstall(pkg, onProgress) : spawnBunxProc(pkg, onProgress);
|
|
106
79
|
}
|
|
107
80
|
|
package/lib/tool-version.js
CHANGED
|
@@ -216,26 +216,11 @@ export function clearVersionCache() {
|
|
|
216
216
|
versionCache.clear();
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
const ROOT_DIR = path.dirname(new URL(import.meta.url).pathname).replace(/\/lib$/, '');
|
|
220
|
-
|
|
221
|
-
export function checkNpmLocalInstalled(pkg) {
|
|
222
|
-
return fs.existsSync(path.join(ROOT_DIR, 'node_modules', pkg, 'package.json'));
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function getNpmLocalVersion(pkg) {
|
|
226
|
-
try {
|
|
227
|
-
const p = path.join(ROOT_DIR, 'node_modules', pkg, 'package.json');
|
|
228
|
-
if (fs.existsSync(p)) return JSON.parse(fs.readFileSync(p, 'utf8')).version;
|
|
229
|
-
} catch (_) {}
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
219
|
export async function checkToolViaBunx(pkg, pluginId = null, category = 'plugin', frameWork = null, skipPublishedVersion = false, tools = []) {
|
|
234
220
|
try {
|
|
235
|
-
const isNpmLocal = category === 'npm-local';
|
|
236
221
|
const isCli = category === 'cli';
|
|
237
|
-
const installed =
|
|
238
|
-
const installedVersion =
|
|
222
|
+
const installed = isCli ? checkCliInstalled(pkg) : checkToolInstalled(pluginId || pkg, frameWork);
|
|
223
|
+
const installedVersion = isCli ? getCliVersion(pkg) : getInstalledVersion(pkg, pluginId, frameWork, tools);
|
|
239
224
|
let publishedVersion = null;
|
|
240
225
|
if (!skipPublishedVersion) {
|
|
241
226
|
publishedVersion = await fetchPublishedVersion(pkg);
|
|
@@ -245,10 +230,9 @@ export async function checkToolViaBunx(pkg, pluginId = null, category = 'plugin'
|
|
|
245
230
|
return { installed, isUpToDate, upgradeNeeded: needsUpdate, output: 'version-check', installedVersion, publishedVersion };
|
|
246
231
|
} catch (err) {
|
|
247
232
|
console.log(`[tool-manager] Error checking ${pkg}:`, err.message);
|
|
248
|
-
const isNpmLocal = category === 'npm-local';
|
|
249
233
|
const isCli = category === 'cli';
|
|
250
|
-
const installed =
|
|
251
|
-
const installedVersion =
|
|
234
|
+
const installed = isCli ? checkCliInstalled(pkg) : checkToolInstalled(pluginId || pkg, frameWork);
|
|
235
|
+
const installedVersion = isCli ? getCliVersion(pkg) : getInstalledVersion(pkg, pluginId, frameWork, tools);
|
|
252
236
|
return { installed, isUpToDate: false, upgradeNeeded: false, output: '', installedVersion, publishedVersion: null };
|
|
253
237
|
}
|
|
254
238
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentgui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.802",
|
|
4
4
|
"description": "Multi-agent ACP client with real-time communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "electron/main.js",
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@agentclientprotocol/sdk": "^0.4.1",
|
|
27
|
+
"@anthropic-ai/claude-code": "^2.1.37",
|
|
28
|
+
"@google/gemini-cli": "latest",
|
|
29
|
+
"@huggingface/transformers": "^3.8.1",
|
|
30
|
+
"@kilocode/cli": "latest",
|
|
31
|
+
"audio-decode": "^2.2.3",
|
|
27
32
|
"better-sqlite3": "^12.6.2",
|
|
28
33
|
"busboy": "^1.6.0",
|
|
29
34
|
"execa": "^9.6.1",
|
|
@@ -33,9 +38,13 @@
|
|
|
33
38
|
"google-auth-library": "^10.5.0",
|
|
34
39
|
"lru-cache": "^11.2.7",
|
|
35
40
|
"msgpackr": "^1.11.8",
|
|
41
|
+
"onnxruntime-node": "1.21.0",
|
|
42
|
+
"opencode-ai": "^1.2.15",
|
|
36
43
|
"p-retry": "^7.1.1",
|
|
37
44
|
"pm2": "^5.4.3",
|
|
45
|
+
"puppeteer-core": "^24.37.5",
|
|
38
46
|
"webjsx": "^0.0.73",
|
|
47
|
+
"webtalk": "^1.0.31",
|
|
39
48
|
"ws": "^8.14.2",
|
|
40
49
|
"xstate": "^5.28.0",
|
|
41
50
|
"zod": "^4.3.6"
|
|
@@ -43,6 +52,12 @@
|
|
|
43
52
|
"optionalDependencies": {
|
|
44
53
|
"node-pty": "^1.0.0"
|
|
45
54
|
},
|
|
55
|
+
"overrides": {
|
|
56
|
+
"onnxruntime-web": "npm:empty-npm-package@1.0.0",
|
|
57
|
+
"sharp": "npm:empty-npm-package@1.0.0",
|
|
58
|
+
"onnxruntime-common": "1.21.0",
|
|
59
|
+
"onnxruntime-node": "1.21.0"
|
|
60
|
+
},
|
|
46
61
|
"devDependencies": {
|
|
47
62
|
"electron": "^35.0.0"
|
|
48
63
|
}
|