agentgui 1.0.917 → 1.0.918
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-schema.js +0 -58
- package/lib/db-queries-cleanup.js +0 -12
- package/lib/db-queries-del.js +0 -1
- package/lib/db-queries.js +0 -4
- package/lib/http-handler.js +1 -10
- package/lib/plugins/database-plugin.js +1 -1
- package/lib/process-message.js +2 -2
- package/lib/provider-config.js +0 -16
- package/lib/recovery.js +2 -12
- package/lib/routes-agent-actions.js +2 -58
- package/lib/routes-debug.js +1 -7
- package/lib/routes-registry.js +5 -17
- package/lib/server-startup.js +1 -59
- package/lib/stream-event-handler.js +1 -3
- package/lib/ws-handlers-session2.js +2 -23
- package/lib/ws-handlers-util.js +106 -175
- package/lib/ws-legacy-handlers.js +1 -104
- package/lib/ws-setup.js +1 -3
- package/package.json +1 -15
- package/server.js +9 -26
- package/test.js +1 -21
- package/ecosystem.config.cjs +0 -22
- package/lib/checkpoint-manager.js +0 -182
- package/lib/db-queries-tools.js +0 -131
- package/lib/db-queries-voice.js +0 -85
- package/lib/oauth-codex.js +0 -164
- package/lib/oauth-common.js +0 -92
- package/lib/oauth-gemini.js +0 -199
- package/lib/plugins/auth-plugin.js +0 -132
- package/lib/plugins/speech-plugin.js +0 -72
- package/lib/plugins/tools-plugin.js +0 -120
- package/lib/pm2-manager.js +0 -170
- package/lib/routes-oauth.js +0 -105
- package/lib/routes-speech.js +0 -173
- package/lib/routes-tools.js +0 -184
- package/lib/speech-manager.js +0 -200
- package/lib/speech.js +0 -50
- package/lib/tool-install-machine.js +0 -157
- package/lib/tool-manager.js +0 -98
- package/lib/tool-provisioner.js +0 -98
- package/lib/tool-spawner.js +0 -163
- package/lib/tool-version-check.js +0 -196
- package/lib/tool-version-fetch.js +0 -68
- package/lib/ws-handlers-oauth.js +0 -76
- package/static/js/agent-auth-oauth.js +0 -159
- package/static/js/pm2-monitor.js +0 -151
- package/static/js/stt-handler.js +0 -147
- package/static/js/tool-install-machine.js +0 -155
- package/static/js/tools-manager-ui.js +0 -124
- package/static/js/tools-manager.js +0 -172
- package/static/js/voice-machine.js +0 -145
- package/static/js/voice.js +0 -134
package/lib/speech-manager.js
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import { createRequire } from 'module';
|
|
5
|
-
let speechModule = null;
|
|
6
|
-
let _broadcastSync = null;
|
|
7
|
-
let _syncClients = null;
|
|
8
|
-
let _queries = null;
|
|
9
|
-
|
|
10
|
-
export function initSpeechManager({ broadcastSync, syncClients, queries }) {
|
|
11
|
-
_broadcastSync = broadcastSync;
|
|
12
|
-
_syncClients = syncClients;
|
|
13
|
-
_queries = queries;
|
|
14
|
-
}
|
|
15
|
-
export async function ensurePocketTtsSetup(onProgress) {
|
|
16
|
-
const r = createRequire(import.meta.url);
|
|
17
|
-
const serverTTS = r('webtalk/server-tts');
|
|
18
|
-
return serverTTS.ensureInstalled(onProgress);
|
|
19
|
-
}
|
|
20
|
-
export async function getSpeech() {
|
|
21
|
-
if (!speechModule) speechModule = await import('./speech.js');
|
|
22
|
-
return speechModule;
|
|
23
|
-
}
|
|
24
|
-
const ttsTextAccumulators = new Map();
|
|
25
|
-
|
|
26
|
-
export const voiceCacheManager = {
|
|
27
|
-
generating: new Map(),
|
|
28
|
-
maxCacheSize: 10 * 1024 * 1024,
|
|
29
|
-
async getOrGenerateCache(conversationId, text) {
|
|
30
|
-
const cacheKey = `${conversationId}:${text}`;
|
|
31
|
-
if (this.generating.has(cacheKey)) {
|
|
32
|
-
return new Promise((resolve) => {
|
|
33
|
-
const checkInterval = setInterval(() => {
|
|
34
|
-
const cached = _queries.getVoiceCache(conversationId, text);
|
|
35
|
-
if (cached) { clearInterval(checkInterval); resolve(cached); }
|
|
36
|
-
}, 50);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
const cached = _queries.getVoiceCache(conversationId, text);
|
|
40
|
-
if (cached) return cached;
|
|
41
|
-
this.generating.set(cacheKey, true);
|
|
42
|
-
try {
|
|
43
|
-
const speech = await getSpeech();
|
|
44
|
-
const audioBlob = await speech.synthesize(text, 'default');
|
|
45
|
-
const saved = _queries.saveVoiceCache(conversationId, text, audioBlob);
|
|
46
|
-
const totalSize = _queries.getVoiceCacheSize(conversationId);
|
|
47
|
-
if (totalSize > this.maxCacheSize) {
|
|
48
|
-
const needed = totalSize - this.maxCacheSize;
|
|
49
|
-
_queries.deleteOldestVoiceCache(conversationId, needed);
|
|
50
|
-
}
|
|
51
|
-
return saved;
|
|
52
|
-
} finally {
|
|
53
|
-
this.generating.delete(cacheKey);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
export const modelDownloadState = {
|
|
58
|
-
downloading: false,
|
|
59
|
-
progress: null,
|
|
60
|
-
error: null,
|
|
61
|
-
complete: false,
|
|
62
|
-
startTime: null,
|
|
63
|
-
downloadMetrics: new Map(),
|
|
64
|
-
waiters: []
|
|
65
|
-
};
|
|
66
|
-
export function broadcastModelProgress(progress) {
|
|
67
|
-
modelDownloadState.progress = progress;
|
|
68
|
-
const broadcastData = {
|
|
69
|
-
type: 'model_download_progress',
|
|
70
|
-
modelId: progress.type || 'unknown',
|
|
71
|
-
bytesDownloaded: progress.bytesDownloaded || 0,
|
|
72
|
-
bytesRemaining: progress.bytesRemaining || 0,
|
|
73
|
-
totalBytes: progress.totalBytes || 0,
|
|
74
|
-
downloadSpeed: progress.downloadSpeed || 0,
|
|
75
|
-
eta: progress.eta || 0,
|
|
76
|
-
retryCount: progress.retryCount || 0,
|
|
77
|
-
currentGateway: progress.currentGateway || '',
|
|
78
|
-
status: progress.status || (progress.done ? 'completed' : progress.downloading ? 'downloading' : 'paused'),
|
|
79
|
-
percentComplete: progress.percentComplete || 0,
|
|
80
|
-
completedFiles: progress.completedFiles || 0,
|
|
81
|
-
totalFiles: progress.totalFiles || 0,
|
|
82
|
-
timestamp: Date.now(),
|
|
83
|
-
...progress
|
|
84
|
-
};
|
|
85
|
-
_broadcastSync(broadcastData);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function validateAndCleanupModels(modelsDir) {
|
|
89
|
-
try {
|
|
90
|
-
const manifestPath = path.join(modelsDir, '.manifests.json');
|
|
91
|
-
if (fs.existsSync(manifestPath)) {
|
|
92
|
-
try {
|
|
93
|
-
const content = fs.readFileSync(manifestPath, 'utf8');
|
|
94
|
-
JSON.parse(content);
|
|
95
|
-
} catch (e) {
|
|
96
|
-
console.error('[MODELS] Manifest corrupted, removing:', e.message);
|
|
97
|
-
fs.unlinkSync(manifestPath);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
const files = fs.readdirSync(modelsDir);
|
|
101
|
-
for (const file of files) {
|
|
102
|
-
if (file.endsWith('.tmp')) {
|
|
103
|
-
try { fs.unlinkSync(path.join(modelsDir, file)); console.log('[MODELS] Cleaned up temp file:', file); }
|
|
104
|
-
catch (e) { console.warn('[MODELS] Failed to clean:', file); }
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
} catch (e) {
|
|
108
|
-
console.warn('[MODELS] Cleanup check failed:', e.message);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export async function ensureModelsDownloaded() {
|
|
113
|
-
if (modelDownloadState.downloading) {
|
|
114
|
-
return new Promise(resolve => { modelDownloadState.waiters.push(resolve); });
|
|
115
|
-
}
|
|
116
|
-
modelDownloadState.downloading = true;
|
|
117
|
-
modelDownloadState.error = null;
|
|
118
|
-
try {
|
|
119
|
-
const r = createRequire(import.meta.url);
|
|
120
|
-
const { createConfig } = r('webtalk/config');
|
|
121
|
-
const { ensureModel } = r('webtalk/whisper-models');
|
|
122
|
-
const { ensureTTSModels } = r('webtalk/tts-models');
|
|
123
|
-
const gmguiModels = path.join(os.homedir(), '.gmgui', 'models');
|
|
124
|
-
const modelsBase = process.env.PORTABLE_EXE_DIR
|
|
125
|
-
? (fs.existsSync(path.join(process.env.PORTABLE_EXE_DIR, 'models', 'onnx-community')) ? path.join(process.env.PORTABLE_EXE_DIR, 'models') : gmguiModels)
|
|
126
|
-
: gmguiModels;
|
|
127
|
-
await validateAndCleanupModels(modelsBase);
|
|
128
|
-
const config = createConfig({ modelsDir: modelsBase, ttsModelsDir: path.join(modelsBase, 'tts') });
|
|
129
|
-
const onProgress = (progress) => { broadcastModelProgress({ ...progress, started: true, done: false, downloading: true }); };
|
|
130
|
-
broadcastModelProgress({ started: true, done: false, downloading: true, type: 'whisper', status: 'starting' });
|
|
131
|
-
await ensureModel('onnx-community/whisper-base', config, onProgress);
|
|
132
|
-
broadcastModelProgress({ started: true, done: false, downloading: true, type: 'tts', status: 'starting' });
|
|
133
|
-
await ensureTTSModels(config, onProgress);
|
|
134
|
-
modelDownloadState.complete = true;
|
|
135
|
-
broadcastModelProgress({ started: true, done: true, complete: true, downloading: false });
|
|
136
|
-
return true;
|
|
137
|
-
} catch (err) {
|
|
138
|
-
console.error('[MODELS] Download error:', err.message);
|
|
139
|
-
modelDownloadState.error = err.message;
|
|
140
|
-
broadcastModelProgress({ done: true, error: err.message });
|
|
141
|
-
return false;
|
|
142
|
-
} finally {
|
|
143
|
-
modelDownloadState.downloading = false;
|
|
144
|
-
const result = modelDownloadState.complete;
|
|
145
|
-
for (const resolve of modelDownloadState.waiters) resolve(result);
|
|
146
|
-
modelDownloadState.waiters = [];
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function eagerTTS(text, conversationId, sessionId) {
|
|
151
|
-
const key = `${conversationId}:${sessionId}`;
|
|
152
|
-
let acc = ttsTextAccumulators.get(key);
|
|
153
|
-
if (!acc) { acc = { text: '', timer: null }; ttsTextAccumulators.set(key, acc); }
|
|
154
|
-
acc.text += text;
|
|
155
|
-
if (acc.timer) clearTimeout(acc.timer);
|
|
156
|
-
acc.timer = setTimeout(() => flushTTSaccumulator(key, conversationId, sessionId), 600);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function flushTTSaccumulator(key, conversationId, sessionId) {
|
|
160
|
-
const acc = ttsTextAccumulators.get(key);
|
|
161
|
-
if (!acc || !acc.text) return;
|
|
162
|
-
const text = acc.text.trim();
|
|
163
|
-
acc.text = '';
|
|
164
|
-
ttsTextAccumulators.delete(key);
|
|
165
|
-
getSpeech().then(speech => {
|
|
166
|
-
const status = speech.getStatus();
|
|
167
|
-
if (!status.ttsReady || status.ttsError) return;
|
|
168
|
-
const voices = new Set();
|
|
169
|
-
for (const ws of _syncClients) {
|
|
170
|
-
const vid = ws.ttsVoiceId || 'default';
|
|
171
|
-
const convKey = `conv-${conversationId}`;
|
|
172
|
-
if (ws.subscriptions && (ws.subscriptions.has(sessionId) || ws.subscriptions.has(convKey))) {
|
|
173
|
-
voices.add(vid);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (voices.size === 0) return;
|
|
177
|
-
for (const vid of voices) {
|
|
178
|
-
const cacheKey = speech.ttsCacheKey(text, vid);
|
|
179
|
-
const cached = speech.ttsCacheGet(cacheKey);
|
|
180
|
-
if (cached) { pushTTSAudio(cacheKey, cached, conversationId, sessionId, vid); continue; }
|
|
181
|
-
speech.synthesize(text, vid).then(wav => {
|
|
182
|
-
if (speech.ttsCacheSet) speech.ttsCacheSet(cacheKey, wav);
|
|
183
|
-
pushTTSAudio(cacheKey, wav, conversationId, sessionId, vid);
|
|
184
|
-
}).catch(() => {});
|
|
185
|
-
}
|
|
186
|
-
}).catch(() => {});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function pushTTSAudio(cacheKey, wav, conversationId, sessionId, voiceId) {
|
|
190
|
-
const b64 = wav.toString('base64');
|
|
191
|
-
_broadcastSync({
|
|
192
|
-
type: 'tts_audio',
|
|
193
|
-
cacheKey,
|
|
194
|
-
audio: b64,
|
|
195
|
-
voiceId,
|
|
196
|
-
conversationId,
|
|
197
|
-
sessionId,
|
|
198
|
-
timestamp: Date.now()
|
|
199
|
-
});
|
|
200
|
-
}
|
package/lib/speech.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
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;
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { createMachine, createActor, assign } from 'xstate';
|
|
2
|
-
|
|
3
|
-
const machine = createMachine({
|
|
4
|
-
id: 'tool-install',
|
|
5
|
-
initial: 'unchecked',
|
|
6
|
-
context: {
|
|
7
|
-
version: null,
|
|
8
|
-
error: null,
|
|
9
|
-
installedAt: null,
|
|
10
|
-
lastCheckedAt: null,
|
|
11
|
-
},
|
|
12
|
-
states: {
|
|
13
|
-
unchecked: {
|
|
14
|
-
on: {
|
|
15
|
-
CHECK_START: 'checking',
|
|
16
|
-
INSTALL_START: 'installing',
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
checking: {
|
|
20
|
-
entry: assign({ lastCheckedAt: () => Date.now() }),
|
|
21
|
-
on: {
|
|
22
|
-
IDLE: {
|
|
23
|
-
target: 'idle',
|
|
24
|
-
actions: assign(({ event }) => ({
|
|
25
|
-
version: event.version || null,
|
|
26
|
-
lastCheckedAt: Date.now(),
|
|
27
|
-
})),
|
|
28
|
-
},
|
|
29
|
-
INSTALLED: {
|
|
30
|
-
target: 'installed',
|
|
31
|
-
actions: assign(({ event }) => ({
|
|
32
|
-
version: event.version || null,
|
|
33
|
-
installedAt: event.installedAt || Date.now(),
|
|
34
|
-
lastCheckedAt: Date.now(),
|
|
35
|
-
})),
|
|
36
|
-
},
|
|
37
|
-
NEEDS_UPDATE: {
|
|
38
|
-
target: 'needs_update',
|
|
39
|
-
actions: assign(({ event }) => ({
|
|
40
|
-
version: event.version || null,
|
|
41
|
-
lastCheckedAt: Date.now(),
|
|
42
|
-
})),
|
|
43
|
-
},
|
|
44
|
-
FAILED: {
|
|
45
|
-
target: 'failed',
|
|
46
|
-
actions: assign(({ event }) => ({ error: event.error || 'check failed' })),
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
idle: {
|
|
51
|
-
on: {
|
|
52
|
-
CHECK_START: 'checking',
|
|
53
|
-
INSTALL_START: 'installing',
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
installing: {
|
|
57
|
-
entry: assign({ error: null }),
|
|
58
|
-
on: {
|
|
59
|
-
INSTALL_COMPLETE: {
|
|
60
|
-
target: 'installed',
|
|
61
|
-
actions: assign(({ event }) => ({
|
|
62
|
-
version: event.version || null,
|
|
63
|
-
installedAt: Date.now(),
|
|
64
|
-
error: null,
|
|
65
|
-
})),
|
|
66
|
-
},
|
|
67
|
-
FAILED: {
|
|
68
|
-
target: 'failed',
|
|
69
|
-
actions: assign(({ event }) => ({ error: event.error || 'install failed' })),
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
installed: {
|
|
74
|
-
on: {
|
|
75
|
-
CHECK_START: 'checking',
|
|
76
|
-
UPDATE_START: 'updating',
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
updating: {
|
|
80
|
-
entry: assign({ error: null }),
|
|
81
|
-
on: {
|
|
82
|
-
UPDATE_COMPLETE: {
|
|
83
|
-
target: 'installed',
|
|
84
|
-
actions: assign(({ event }) => ({
|
|
85
|
-
version: event.version || null,
|
|
86
|
-
error: null,
|
|
87
|
-
})),
|
|
88
|
-
},
|
|
89
|
-
FAILED: {
|
|
90
|
-
target: 'failed',
|
|
91
|
-
actions: assign(({ event }) => ({ error: event.error || 'update failed' })),
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
needs_update: {
|
|
96
|
-
on: {
|
|
97
|
-
UPDATE_START: 'updating',
|
|
98
|
-
CHECK_START: 'checking',
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
failed: {
|
|
102
|
-
on: {
|
|
103
|
-
CHECK_START: 'checking',
|
|
104
|
-
INSTALL_START: 'installing',
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const actors = new Map();
|
|
111
|
-
|
|
112
|
-
export function getOrCreate(toolId) {
|
|
113
|
-
if (actors.has(toolId)) return actors.get(toolId);
|
|
114
|
-
const actor = createActor(machine);
|
|
115
|
-
actor.start();
|
|
116
|
-
actors.set(toolId, actor);
|
|
117
|
-
return actor;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function get(toolId) {
|
|
121
|
-
return actors.get(toolId) || null;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function send(toolId, event) {
|
|
125
|
-
const actor = getOrCreate(toolId);
|
|
126
|
-
actor.send(event);
|
|
127
|
-
return actor.getSnapshot();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function snapshot(toolId) {
|
|
131
|
-
const actor = actors.get(toolId);
|
|
132
|
-
return actor ? actor.getSnapshot() : null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function isLocked(toolId) {
|
|
136
|
-
const s = snapshot(toolId);
|
|
137
|
-
return s ? (s.value === 'installing' || s.value === 'updating') : false;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function getState(toolId) {
|
|
141
|
-
const s = snapshot(toolId);
|
|
142
|
-
return s ? s.value : 'unchecked';
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function remove(toolId) {
|
|
146
|
-
const actor = actors.get(toolId);
|
|
147
|
-
if (actor) { actor.stop(); actors.delete(toolId); }
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function stopAll() {
|
|
151
|
-
for (const [, actor] of actors) actor.stop();
|
|
152
|
-
actors.clear();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export function getMachineActors() {
|
|
156
|
-
return actors;
|
|
157
|
-
}
|
package/lib/tool-manager.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { checkToolViaBunx, clearVersionCache } from './tool-version-fetch.js';
|
|
2
|
-
import { createInstaller } from './tool-spawner.js';
|
|
3
|
-
import { autoProvision as _autoProvision, startPeriodicUpdateCheck as _startPeriodicUpdateCheck, stopPeriodicUpdateCheck } from './tool-provisioner.js';
|
|
4
|
-
|
|
5
|
-
const TOOLS = [
|
|
6
|
-
{ id: 'cli-claude', name: 'Claude Code', pkg: '@anthropic-ai/claude-code', category: 'cli' },
|
|
7
|
-
{ id: 'cli-opencode', name: 'OpenCode', pkg: 'opencode-ai', category: 'cli' },
|
|
8
|
-
{ id: 'cli-gemini', name: 'Gemini CLI', pkg: '@google/gemini-cli', category: 'cli' },
|
|
9
|
-
{ id: 'cli-kilo', name: 'Kilo Code', pkg: '@kilocode/cli', category: 'cli' },
|
|
10
|
-
{ id: 'cli-codex', name: 'Codex CLI', pkg: '@openai/codex', category: 'cli' },
|
|
11
|
-
{ id: 'cli-agent-browser', name: 'Agent Browser', pkg: 'agent-browser', category: 'cli' },
|
|
12
|
-
{ id: 'gm-cc', name: 'GM Claude', pkg: 'gm-cc', installPkg: 'gm-cc@latest', pluginId: 'gm-cc', category: 'plugin', frameWork: 'claude' },
|
|
13
|
-
{ id: 'gm-oc', name: 'GM OpenCode', pkg: 'gm-oc', installPkg: 'gm-oc@latest', pluginId: 'gm', category: 'plugin', frameWork: 'opencode' },
|
|
14
|
-
{ id: 'gm-gc', name: 'GM Gemini', pkg: 'gm-gc', installPkg: 'gm-gc@latest', pluginId: 'gm', category: 'plugin', frameWork: 'gemini' },
|
|
15
|
-
{ id: 'gm-kilo', name: 'GM Kilo', pkg: 'gm-kilo', installPkg: 'gm-kilo@latest', pluginId: 'gm', category: 'plugin', frameWork: 'kilo' },
|
|
16
|
-
{ id: 'gm-codex', name: 'GM Codex', pkg: 'gm-codex', installPkg: 'gm-codex@latest', pluginId: 'gm', category: 'plugin', frameWork: 'codex' },
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
const statusCache = new Map();
|
|
20
|
-
|
|
21
|
-
const getTool = (id) => TOOLS.find(t => t.id === id);
|
|
22
|
-
|
|
23
|
-
export function checkToolStatus(toolId) {
|
|
24
|
-
const tool = getTool(toolId);
|
|
25
|
-
if (!tool) return null;
|
|
26
|
-
const cached = statusCache.get(toolId);
|
|
27
|
-
if (cached && Date.now() - cached.timestamp < 1800000) {
|
|
28
|
-
return { toolId, installed: cached.installed, isUpToDate: cached.isUpToDate, upgradeNeeded: cached.upgradeNeeded, timestamp: cached.timestamp };
|
|
29
|
-
}
|
|
30
|
-
return { toolId, installed: false, isUpToDate: false, upgradeNeeded: false, timestamp: Date.now() };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function checkToolStatusAsync(toolId, skipPublishedVersion = true) {
|
|
34
|
-
const tool = getTool(toolId);
|
|
35
|
-
if (!tool) return null;
|
|
36
|
-
const cached = statusCache.get(toolId);
|
|
37
|
-
if (cached && Date.now() - cached.timestamp < 1800000) {
|
|
38
|
-
return { toolId, installed: cached.installed, isUpToDate: cached.isUpToDate, upgradeNeeded: cached.upgradeNeeded, installedVersion: cached.installedVersion, publishedVersion: cached.publishedVersion, timestamp: cached.timestamp };
|
|
39
|
-
}
|
|
40
|
-
const result = await checkToolViaBunx(tool.pkg, tool.pluginId, tool.category, tool.frameWork, skipPublishedVersion, TOOLS);
|
|
41
|
-
const status = { toolId, category: tool.category, installed: result.installed, isUpToDate: result.isUpToDate, upgradeNeeded: result.upgradeNeeded, installedVersion: result.installedVersion, publishedVersion: result.publishedVersion, timestamp: Date.now() };
|
|
42
|
-
statusCache.set(toolId, status);
|
|
43
|
-
return status;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export async function checkForUpdates(toolId) {
|
|
47
|
-
const tool = getTool(toolId);
|
|
48
|
-
if (!tool) return { needsUpdate: false };
|
|
49
|
-
const status = await checkToolStatusAsync(toolId);
|
|
50
|
-
return { needsUpdate: status.upgradeNeeded && status.installed };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const { install, update } = createInstaller(getTool, statusCache, checkToolStatusAsync);
|
|
54
|
-
export { install, update };
|
|
55
|
-
|
|
56
|
-
export function getAllTools() {
|
|
57
|
-
return TOOLS.map(tool => {
|
|
58
|
-
const cached = statusCache.get(tool.id);
|
|
59
|
-
return { ...tool, toolId: tool.id, installed: cached?.installed ?? false, isUpToDate: cached?.isUpToDate ?? false, upgradeNeeded: cached?.upgradeNeeded ?? false, installedVersion: cached?.installedVersion ?? null, publishedVersion: cached?.publishedVersion ?? null, timestamp: cached?.timestamp ?? 0 };
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export async function getAllToolsAsync(skipPublishedVersion = false) {
|
|
64
|
-
const results = await Promise.all(TOOLS.map(tool => checkToolStatusAsync(tool.id, skipPublishedVersion)));
|
|
65
|
-
return results.map((status, idx) => ({ ...TOOLS[idx], ...status }));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function clearStatusCache() {
|
|
69
|
-
statusCache.clear();
|
|
70
|
-
clearVersionCache();
|
|
71
|
-
console.log('[tool-manager] Caches cleared, forcing fresh tool detection');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export async function refreshAllToolsAsync() {
|
|
75
|
-
clearStatusCache();
|
|
76
|
-
return getAllToolsAsync();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function getAllToolsSync() {
|
|
80
|
-
return TOOLS.map(tool => {
|
|
81
|
-
const cached = statusCache.get(tool.id);
|
|
82
|
-
return { ...tool, ...cached };
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function getToolConfig(toolId) {
|
|
87
|
-
return getTool(toolId) || null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export async function autoProvision(broadcast) {
|
|
91
|
-
return _autoProvision(TOOLS, statusCache, install, broadcast);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function startPeriodicUpdateCheck(broadcast) {
|
|
95
|
-
return _startPeriodicUpdateCheck(TOOLS, statusCache, update, broadcast);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export { stopPeriodicUpdateCheck };
|
package/lib/tool-provisioner.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { checkToolViaBunx } from './tool-version-fetch.js';
|
|
2
|
-
import * as toolInstallMachine from './tool-install-machine.js';
|
|
3
|
-
|
|
4
|
-
let updateCheckInterval = null;
|
|
5
|
-
const UPDATE_CHECK_INTERVAL = 6 * 60 * 60 * 1000;
|
|
6
|
-
|
|
7
|
-
export async function autoProvision(TOOLS, statusCache, install, broadcast) {
|
|
8
|
-
const log = (msg) => console.log('[TOOLS-AUTO] ' + msg);
|
|
9
|
-
log('Starting background tool provisioning...');
|
|
10
|
-
for (const tool of TOOLS) {
|
|
11
|
-
try {
|
|
12
|
-
toolInstallMachine.send(tool.id, { type: 'CHECK_START' });
|
|
13
|
-
const status = await checkToolViaBunx(tool.pkg, tool.pluginId, tool.category, tool.frameWork, true, TOOLS);
|
|
14
|
-
statusCache.set(tool.id, { ...status, toolId: tool.id, timestamp: Date.now() });
|
|
15
|
-
if (!status.installed) {
|
|
16
|
-
toolInstallMachine.send(tool.id, { type: 'IDLE' });
|
|
17
|
-
log(`${tool.id} not installed, installing...`);
|
|
18
|
-
broadcast({ type: 'tool_install_started', toolId: tool.id });
|
|
19
|
-
const result = await install(tool.id, (msg) => {
|
|
20
|
-
broadcast({ type: 'tool_install_progress', toolId: tool.id, data: msg });
|
|
21
|
-
});
|
|
22
|
-
if (result.success) {
|
|
23
|
-
log(`${tool.id} installed v${result.version}`);
|
|
24
|
-
broadcast({ type: 'tool_install_complete', toolId: tool.id, data: result });
|
|
25
|
-
} else {
|
|
26
|
-
log(`${tool.id} install failed: ${result.error}`);
|
|
27
|
-
broadcast({ type: 'tool_install_failed', toolId: tool.id, data: result });
|
|
28
|
-
}
|
|
29
|
-
} else if (status.upgradeNeeded) {
|
|
30
|
-
toolInstallMachine.send(tool.id, { type: 'NEEDS_UPDATE', version: status.installedVersion });
|
|
31
|
-
log(`${tool.id} needs update (${status.installedVersion} -> ${status.publishedVersion})`);
|
|
32
|
-
broadcast({ type: 'tool_install_started', toolId: tool.id });
|
|
33
|
-
const result = await install(tool.id, (msg) => {
|
|
34
|
-
broadcast({ type: 'tool_update_progress', toolId: tool.id, data: msg });
|
|
35
|
-
});
|
|
36
|
-
if (result.success) {
|
|
37
|
-
log(`${tool.id} updated to v${result.version}`);
|
|
38
|
-
broadcast({ type: 'tool_update_complete', toolId: tool.id, data: result });
|
|
39
|
-
} else {
|
|
40
|
-
log(`${tool.id} update failed: ${result.error}`);
|
|
41
|
-
broadcast({ type: 'tool_update_failed', toolId: tool.id, data: result });
|
|
42
|
-
}
|
|
43
|
-
} else {
|
|
44
|
-
toolInstallMachine.send(tool.id, { type: 'INSTALLED', version: status.installedVersion });
|
|
45
|
-
log(`${tool.id} v${status.installedVersion || 'unknown'} up-to-date`);
|
|
46
|
-
broadcast({ type: 'tool_status_update', toolId: tool.id, data: { installed: true, isUpToDate: true, installedVersion: status.installedVersion, status: 'installed' } });
|
|
47
|
-
}
|
|
48
|
-
} catch (err) {
|
|
49
|
-
toolInstallMachine.send(tool.id, { type: 'FAILED', error: err.message });
|
|
50
|
-
log(`${tool.id} error: ${err.message}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
log('Provisioning complete');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function startPeriodicUpdateCheck(TOOLS, statusCache, update, broadcast) {
|
|
57
|
-
const log = (msg) => console.log('[TOOLS-PERIODIC] ' + msg);
|
|
58
|
-
if (updateCheckInterval) { log('Update check already running'); return; }
|
|
59
|
-
log('Starting periodic tool update checker (every 6 hours)');
|
|
60
|
-
|
|
61
|
-
const check = async () => {
|
|
62
|
-
log('Checking for tool updates...');
|
|
63
|
-
for (const tool of TOOLS) {
|
|
64
|
-
try {
|
|
65
|
-
const status = await checkToolViaBunx(tool.pkg, tool.pluginId, tool.category, tool.frameWork, false, TOOLS);
|
|
66
|
-
if (status.upgradeNeeded) {
|
|
67
|
-
log(`Update available for ${tool.id}: ${status.installedVersion} -> ${status.publishedVersion}`);
|
|
68
|
-
broadcast({ type: 'tool_update_available', toolId: tool.id, data: { installedVersion: status.installedVersion, publishedVersion: status.publishedVersion } });
|
|
69
|
-
log(`Auto-updating ${tool.id}...`);
|
|
70
|
-
const result = await update(tool.id, (msg) => {
|
|
71
|
-
broadcast({ type: 'tool_update_progress', toolId: tool.id, data: msg });
|
|
72
|
-
});
|
|
73
|
-
if (result.success) {
|
|
74
|
-
log(`${tool.id} auto-updated to v${result.version}`);
|
|
75
|
-
broadcast({ type: 'tool_update_complete', toolId: tool.id, data: { ...result, autoUpdated: true } });
|
|
76
|
-
} else {
|
|
77
|
-
log(`${tool.id} auto-update failed: ${result.error}`);
|
|
78
|
-
broadcast({ type: 'tool_update_failed', toolId: tool.id, data: { ...result, autoUpdated: true } });
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
} catch (err) {
|
|
82
|
-
log(`Error checking ${tool.id}: ${err.message}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
log('Update check complete');
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
setImmediate(() => check().catch(err => log(`Initial check failed: ${err.message}`)));
|
|
89
|
-
updateCheckInterval = setInterval(() => check().catch(err => log(`Periodic check failed: ${err.message}`)), UPDATE_CHECK_INTERVAL);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function stopPeriodicUpdateCheck() {
|
|
93
|
-
if (updateCheckInterval) {
|
|
94
|
-
clearInterval(updateCheckInterval);
|
|
95
|
-
updateCheckInterval = null;
|
|
96
|
-
console.log('[TOOLS-PERIODIC] Update check stopped');
|
|
97
|
-
}
|
|
98
|
-
}
|