agentgui 1.0.727 → 1.0.729

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.
@@ -1,435 +1,164 @@
1
- (function() {
2
- var btn = document.getElementById('toolsManagerBtn');
3
- var popup = document.getElementById('toolsPopup');
4
- var tools = [];
5
- var isRefreshing = false;
6
- var operationInProgress = new Set();
7
-
8
- function init() {
9
- if (!btn || !popup) return;
10
- btn.style.display = 'flex';
11
- btn.addEventListener('click', togglePopup);
12
- document.addEventListener('click', function(e) {
13
- if (!btn.contains(e.target) && !popup.contains(e.target)) closePopup();
14
- });
15
- window.addEventListener('ws-message', onWsMessage);
16
-
17
- // Initialize voice controls
18
- initVoiceControls();
19
- refresh();
20
- }
21
-
22
- function initVoiceControls() {
23
- var autoSpeakToggle = document.getElementById('toolsAutoSpeakToggle');
24
- var voiceSelector = document.getElementById('toolsVoiceSelector');
25
-
26
- if (!autoSpeakToggle || !voiceSelector) return;
27
-
28
- var savedAutoSpeak = localStorage.getItem('toolsAutoSpeak') === 'true';
29
- autoSpeakToggle.checked = savedAutoSpeak;
30
-
31
- window.addEventListener('ws-message', function(e) {
32
- var data = e.detail;
33
- if (data && data.type === 'voice_list') updateVoiceSelector(data.voices);
34
- });
35
-
36
- function trySubscribeVsManager() {
37
- if (window.wsManager && window.wsManager.subscribeToVoiceList) {
38
- window.wsManager.subscribeToVoiceList(updateVoiceSelector);
39
- } else {
40
- var BASE = window.__BASE_URL || '';
41
- fetch(BASE + '/api/voices').then(function(r) { return r.json(); }).then(function(d) {
42
- if (d.ok && Array.isArray(d.voices)) updateVoiceSelector(d.voices);
43
- }).catch(function() {});
44
- setTimeout(function() {
45
- if (window.wsManager && window.wsManager.subscribeToVoiceList) {
46
- window.wsManager.subscribeToVoiceList(updateVoiceSelector);
47
- }
48
- }, 2000);
49
- }
50
- }
51
- trySubscribeVsManager();
52
-
53
- autoSpeakToggle.addEventListener('change', function() {
54
- localStorage.setItem('toolsAutoSpeak', this.checked);
55
- if (window.voiceModule) window.voiceModule.setAutoSpeak(this.checked);
56
- });
57
-
58
- voiceSelector.addEventListener('change', function() {
59
- localStorage.setItem('toolsVoice', this.value);
60
- if (window.voiceModule) window.voiceModule.setVoice(this.value);
61
- });
62
- }
63
-
64
- function updateVoiceSelector(voices) {
65
- var voiceSelector = document.getElementById('toolsVoiceSelector');
66
- if (!voiceSelector || !voices || !Array.isArray(voices)) return;
67
-
68
- var currentValue = voiceSelector.value || localStorage.getItem('toolsVoice') || 'default';
69
- voiceSelector.innerHTML = '';
70
-
71
- var builtIn = voices.filter(function(v) { return !v.isCustom; });
72
- var custom = voices.filter(function(v) { return v.isCustom; });
73
-
74
- if (builtIn.length) {
75
- var grp1 = document.createElement('optgroup');
76
- grp1.label = 'Built-in Voices';
77
- builtIn.forEach(function(voice) {
78
- var opt = document.createElement('option');
79
- opt.value = voice.id;
80
- var parts = [];
81
- if (voice.gender && voice.gender !== 'custom') parts.push(voice.gender);
82
- if (voice.accent && voice.accent !== 'custom') parts.push(voice.accent);
83
- opt.textContent = voice.name + (parts.length ? ' (' + parts.join(', ') + ')' : '');
84
- grp1.appendChild(opt);
85
- });
86
- voiceSelector.appendChild(grp1);
87
- }
88
-
89
- if (custom.length) {
90
- var grp2 = document.createElement('optgroup');
91
- grp2.label = 'Custom Voices';
92
- custom.forEach(function(voice) {
93
- var opt = document.createElement('option');
94
- opt.value = voice.id;
95
- opt.textContent = voice.name;
96
- grp2.appendChild(opt);
97
- });
98
- voiceSelector.appendChild(grp2);
99
- }
100
-
101
- if (voiceSelector.querySelector('option[value="' + currentValue + '"]')) {
102
- voiceSelector.value = currentValue;
103
- }
104
- }
105
-
106
- function refresh() {
107
- fetchTools();
108
- }
109
-
110
- function fetchTools() {
111
- window.wsClient.rpc('tools.list')
112
- .then(function(data) {
113
- tools = data.tools || [];
114
- render();
115
- })
116
- .catch(function() {
117
- fetch('/gm/api/tools')
118
- .then(r => r.json())
119
- .then(d => {
120
- tools = d.tools || [];
121
- render();
122
- })
123
- .catch(function(e) {
124
- console.error('[TOOLS-MGR]', e.message);
125
- });
126
- });
127
- }
128
-
129
- function getStatusColor(tool) {
130
- if (tool.status === 'installing' || tool.status === 'updating') return '#3b82f6';
131
- if (tool.status === 'needs_update' || (tool.status === 'installed' && tool.hasUpdate)) return '#f59e0b';
132
- if (tool.status === 'installed') return '#10b981';
133
- if (tool.status === 'failed') return '#ef4444';
134
- return '#6b7280';
135
- }
136
-
137
- function getStatusText(tool) {
138
- if (tool.status === 'installing') return 'Installing...';
139
- if (tool.status === 'updating') return 'Updating...';
140
- if (tool.status === 'needs_update') return 'Update available';
141
- if (tool.status === 'installed') {
142
- return tool.hasUpdate ? 'Update available' : 'Up-to-date';
143
- }
144
- if (tool.status === 'failed') return 'Installation failed';
145
- return 'Not installed';
146
- }
147
-
148
- function getStatusClass(tool) {
149
- if (tool.status === 'installing' || tool.status === 'updating') return 'installing';
150
- if (tool.status === 'needs_update' || (tool.status === 'installed' && tool.hasUpdate)) return 'updating';
151
- if (tool.status === 'installed') return 'installed';
152
- if (tool.status === 'failed') return 'failed';
153
- return 'not-installed';
154
- }
155
-
156
- function install(toolId) {
157
- if (operationInProgress.has(toolId)) return;
158
- operationInProgress.add(toolId);
159
- var tool = tools.find(t => t.id === toolId);
160
- if (tool) {
161
- tool.status = 'installing';
162
- tool.progress = 0;
163
- render();
164
- }
165
- fetch(`/gm/api/tools/${toolId}/install`, { method: 'POST' })
166
- .then(r => r.json())
167
- .then(d => {
168
- if (!d.success) {
169
- alert(`Install failed: ${d.error || 'Unknown error'}`);
170
- operationInProgress.delete(toolId);
171
- if (tool) {
172
- tool.status = 'failed';
173
- render();
174
- }
175
- }
176
- })
177
- .catch(e => {
178
- alert(`Install failed: ${e.message}`);
179
- operationInProgress.delete(toolId);
180
- if (tool) {
181
- tool.status = 'failed';
182
- render();
183
- }
184
- });
185
- }
186
-
187
- function update(toolId) {
188
- if (operationInProgress.has(toolId)) return;
189
- operationInProgress.add(toolId);
190
- var tool = tools.find(t => t.id === toolId);
191
- if (tool) {
192
- tool.status = 'updating';
193
- tool.progress = 0;
194
- render();
195
- }
196
- fetch(`/gm/api/tools/${toolId}/update`, { method: 'POST' })
197
- .then(r => r.json())
198
- .then(d => {
199
- if (!d.success) {
200
- alert(`Update failed: ${d.error || 'Unknown error'}`);
201
- operationInProgress.delete(toolId);
202
- if (tool) {
203
- tool.status = 'failed';
204
- render();
205
- }
206
- }
207
- })
208
- .catch(e => {
209
- alert(`Update failed: ${e.message}`);
210
- operationInProgress.delete(toolId);
211
- if (tool) {
212
- tool.status = 'failed';
213
- render();
214
- }
215
- });
216
- }
217
-
218
- function togglePopup(e) {
219
- e.stopPropagation();
220
- if (!popup.classList.contains('open')) {
221
- isRefreshing = false;
222
- refresh();
223
- }
224
- popup.classList.toggle('open');
225
- }
226
-
227
- function closePopup() {
228
- popup.classList.remove('open');
229
- }
230
-
231
- function isAutoSpeakOn() {
232
- var toggle = document.getElementById('toolsAutoSpeakToggle');
233
- return toggle ? toggle.checked : false;
234
- }
235
-
236
- function onWsMessage(e) {
237
- var data = e.detail;
238
- if (!data) return;
239
-
240
- if (data.type === 'streaming_progress' && data.block && data.block.type === 'text' && data.block.text) {
241
- if (isAutoSpeakOn() && (!data.blockRole || data.blockRole === 'assistant')) {
242
- if (window.voiceModule && typeof window.voiceModule.speakText === 'function') {
243
- window.voiceModule.speakText(data.block.text);
244
- }
245
- }
246
- }
247
-
248
- if (data.type === 'tools_update_started') {
249
- var updateTools = data.tools || [];
250
- updateTools.forEach(function(toolId) {
251
- var tool = tools.find(t => t.id === toolId);
252
- if (tool) {
253
- tool.status = 'updating';
254
- tool.progress = 5;
255
- }
256
- });
257
- render();
258
- } else if (data.type === 'tool_install_started' || data.type === 'tool_install_progress' || data.type === 'tool_update_progress') {
259
- var tool = tools.find(t => t.id === data.toolId);
260
- if (tool) {
261
- tool.status = (data.type === 'tool_install_started' || data.type === 'tool_install_progress') ? 'installing' : 'updating';
262
- tool.progress = (tool.progress || 0) + 5;
263
- if (tool.progress > 90) tool.progress = 90;
264
- render();
265
- }
266
- } else if (data.type === 'tool_install_complete' || data.type === 'tool_update_complete') {
267
- var tool = tools.find(t => t.id === data.toolId);
268
- if (tool) {
269
- tool.status = data.data?.isUpToDate ? 'installed' : 'needs_update';
270
- tool.version = data.data?.version || tool.version;
271
- tool.installedVersion = data.data?.installedVersion || tool.installedVersion;
272
- tool.publishedVersion = data.data?.publishedVersion || tool.publishedVersion;
273
- tool.isUpToDate = data.data?.isUpToDate ?? false;
274
- tool.upgradeNeeded = data.data?.upgradeNeeded ?? false;
275
- tool.hasUpdate = (data.data?.upgradeNeeded && data.data?.installed) ?? false;
276
- tool.progress = 100;
277
- operationInProgress.delete(data.toolId);
278
- render();
279
- setTimeout(fetchTools, 1000);
280
- }
281
- } else if (data.type === 'tool_install_failed' || data.type === 'tool_update_failed') {
282
- var tool = tools.find(t => t.id === data.toolId);
283
- if (tool) {
284
- tool.status = 'failed';
285
- tool.error_message = data.data?.error;
286
- tool.progress = 0;
287
- operationInProgress.delete(data.toolId);
288
- render();
289
- }
290
- } else if (data.type === 'tool_status_update') {
291
- var tool = tools.find(t => t.id === data.toolId);
292
- if (tool && data.data) {
293
- if (data.data.installed) {
294
- tool.status = data.data.isUpToDate ? 'installed' : 'needs_update';
295
- tool.installed = true;
296
- tool.isUpToDate = data.data.isUpToDate ?? true;
297
- tool.installedVersion = data.data.installedVersion || tool.installedVersion;
298
- }
299
- render();
300
- }
301
- } else if (data.type === 'tools_update_complete') {
302
- fetchTools();
303
- } else if (data.type === 'tools_refresh_complete') {
304
- isRefreshing = false;
305
- fetchTools();
306
- }
307
- }
308
-
309
- function render() {
310
- var scroll = popup.querySelector('.tools-popup-scroll');
311
- if (!scroll) return;
312
-
313
- if (tools.length === 0) {
314
- scroll.innerHTML = '<div class="tool-empty-state" style="grid-column: 1 / -1;"><div class="tool-empty-state-icon">⚙️</div><div class="tool-empty-state-text">No tools available</div></div>';
315
- return;
316
- }
317
-
318
- var cliTools = tools.filter(function(t) { return t.category === 'cli'; });
319
- var pluginTools = tools.filter(function(t) { return t.category === 'plugin'; });
320
- var uncategorized = tools.filter(function(t) { return !t.category; });
321
-
322
- function renderToolCard(tool) {
323
- var statusClass = getStatusClass(tool);
324
- var isInstalling = tool.status === 'installing' || tool.status === 'updating';
325
- var versionInfo = '';
326
- if (tool.installedVersion || tool.publishedVersion) {
327
- versionInfo = '<div class="tool-versions">';
328
- if (tool.installedVersion) versionInfo += '<span class="tool-version-item">v' + esc(tool.installedVersion) + '</span>';
329
- if (tool.publishedVersion && tool.installedVersion !== tool.publishedVersion) versionInfo += '<span class="tool-version-item">(v' + esc(tool.publishedVersion) + ' available)</span>';
330
- versionInfo += '</div>';
331
- }
332
- return '<div class="tool-item">' +
333
- '<div style="display: flex; flex-direction: column; gap: 0.3rem;">' +
334
- '<div class="tool-header"><span class="tool-name">' + esc(tool.name || tool.id) + '</span></div>' +
335
- '<div class="tool-status-indicator ' + statusClass + '"><span class="tool-status-dot"></span><span>' + getStatusText(tool) + '</span></div>' +
336
- versionInfo +
337
- (isInstalling && tool.progress !== undefined ? '<div class="tool-progress-container"><div class="tool-progress-bar"><div class="tool-progress-fill" style="width:' + Math.min(tool.progress, 100) + '%"></div></div></div>' : '') +
338
- (tool.error_message ? '<div class="tool-error-message">Error: ' + esc(tool.error_message.substring(0, 40)) + '</div>' : '') +
339
- '</div>' +
340
- '<div class="tool-actions">' +
341
- (tool.status === 'not_installed' ? '<button class="tool-btn tool-btn-primary" onclick="window.toolsManager.install(\'' + tool.id + '\')" ' + (operationInProgress.has(tool.id) ? 'disabled' : '') + '>Install</button>' :
342
- (tool.hasUpdate || tool.status === 'needs_update') ? '<button class="tool-btn tool-btn-primary" onclick="window.toolsManager.update(\'' + tool.id + '\')" ' + (operationInProgress.has(tool.id) ? 'disabled' : '') + '>Update</button>' :
343
- tool.status === 'failed' ? '<button class="tool-btn tool-btn-primary" onclick="window.toolsManager.install(\'' + tool.id + '\')" ' + (operationInProgress.has(tool.id) ? 'disabled' : '') + '>Retry</button>' :
344
- '<button class="tool-btn tool-btn-secondary" onclick="window.toolsManager.refresh()" ' + (isRefreshing ? 'disabled' : '') + '>✓</button>') +
345
- '</div></div>';
346
- }
347
-
348
- var html = '';
349
- if (cliTools.length) html += '<div class="tool-section-header">CLI Agents</div>' + cliTools.map(renderToolCard).join('');
350
- if (pluginTools.length) html += '<div class="tool-section-header">GM Plugins</div>' + pluginTools.map(renderToolCard).join('');
351
- if (uncategorized.length) html += uncategorized.map(renderToolCard).join('');
352
- scroll.innerHTML = html;
353
- }
354
-
355
- function esc(s) {
356
- var d = document.createElement('div');
357
- d.textContent = s;
358
- return d.innerHTML;
359
- }
360
-
361
- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
362
- else init();
363
-
364
- function updateAll() {
365
- var toolsWithUpdates = tools.filter(function(t) {
366
- return t.hasUpdate || t.status === 'needs_update' || t.status === 'failed';
367
- });
368
-
369
- if (toolsWithUpdates.length === 0) {
370
- alert('All tools are up-to-date');
371
- return;
372
- }
373
-
374
- for (var i = 0; i < toolsWithUpdates.length; i++) {
375
- operationInProgress.add(toolsWithUpdates[i].id);
376
- var tool = tools.find(function(t) { return t.id === toolsWithUpdates[i].id; });
377
- if (tool) {
378
- tool.status = 'updating';
379
- tool.progress = 0;
380
- }
381
- }
382
- render();
383
-
384
- fetch('/gm/api/tools/update', { method: 'POST' })
385
- .then(function(r) { return r.json(); })
386
- .then(function(d) {
387
- if (!d.updating) {
388
- alert('Update started, but response unexpected');
389
- for (var i = 0; i < toolsWithUpdates.length; i++) {
390
- operationInProgress.delete(toolsWithUpdates[i].id);
391
- }
392
- }
393
- })
394
- .catch(function(e) {
395
- alert('Update failed: ' + e.message);
396
- for (var i = 0; i < toolsWithUpdates.length; i++) {
397
- operationInProgress.delete(toolsWithUpdates[i].id);
398
- }
399
- });
400
- }
401
-
402
- window.toolsManager = {
403
- refresh: function() {
404
- isRefreshing = true;
405
- render();
406
- fetch('/gm/api/tools/refresh-all', { method: 'POST' })
407
- .catch(function(e) { console.error('[TOOLS-MGR]', e.message); });
408
- },
409
- install: function(toolId) { install(toolId); },
410
- update: function(toolId) { update(toolId); },
411
- updateAll: function() { updateAll(); },
412
- getAutoSpeak: function() {
413
- var toggle = document.getElementById('toolsAutoSpeakToggle');
414
- return toggle ? toggle.checked : false;
415
- },
416
- getVoice: function() {
417
- var selector = document.getElementById('toolsVoiceSelector');
418
- return selector ? selector.value : 'default';
419
- },
420
- setAutoSpeak: function(value) {
421
- var toggle = document.getElementById('toolsAutoSpeakToggle');
422
- if (toggle) {
423
- toggle.checked = value;
424
- localStorage.setItem('toolsAutoSpeak', value);
425
- }
426
- },
427
- setVoice: function(value) {
428
- var selector = document.getElementById('toolsVoiceSelector');
429
- if (selector && Array.from(selector.options).some(opt => opt.value === value)) {
430
- selector.value = value;
431
- localStorage.setItem('toolsVoice', value);
432
- }
433
- }
434
- };
435
- })();
1
+ (function() {
2
+ var btn = document.getElementById('toolsManagerBtn');
3
+ var popup = document.getElementById('toolsPopup');
4
+ var tools = [];
5
+ var isRefreshing = false;
6
+
7
+ function init() {
8
+ if (!btn || !popup) return;
9
+ btn.style.display = 'flex';
10
+ btn.addEventListener('click', togglePopup);
11
+ document.addEventListener('click', function(e) {
12
+ if (!btn.contains(e.target) && !popup.contains(e.target)) closePopup();
13
+ });
14
+ window.addEventListener('ws-message', onWsMessage);
15
+ initVoiceControls();
16
+ refresh();
17
+ }
18
+
19
+ function initVoiceControls() {
20
+ var autoSpeakToggle = document.getElementById('toolsAutoSpeakToggle');
21
+ var voiceSelector = document.getElementById('toolsVoiceSelector');
22
+ if (!autoSpeakToggle || !voiceSelector) return;
23
+ autoSpeakToggle.checked = localStorage.getItem('toolsAutoSpeak') === 'true';
24
+ window.addEventListener('ws-message', function(e) {
25
+ var data = e.detail;
26
+ if (data && data.type === 'voice_list') window.toolsManagerUI.updateVoiceSelector(data.voices);
27
+ });
28
+ function trySubscribe() {
29
+ if (window.wsManager && window.wsManager.subscribeToVoiceList) {
30
+ window.wsManager.subscribeToVoiceList(window.toolsManagerUI.updateVoiceSelector);
31
+ } else {
32
+ var BASE = window.__BASE_URL || '';
33
+ fetch(BASE + '/api/voices').then(r => r.json()).then(d => {
34
+ if (d.ok && Array.isArray(d.voices)) window.toolsManagerUI.updateVoiceSelector(d.voices);
35
+ }).catch(() => {});
36
+ setTimeout(function() {
37
+ if (window.wsManager && window.wsManager.subscribeToVoiceList)
38
+ window.wsManager.subscribeToVoiceList(window.toolsManagerUI.updateVoiceSelector);
39
+ }, 2000);
40
+ }
41
+ }
42
+ trySubscribe();
43
+ autoSpeakToggle.addEventListener('change', function() {
44
+ localStorage.setItem('toolsAutoSpeak', this.checked);
45
+ if (window.voiceModule) window.voiceModule.setAutoSpeak(this.checked);
46
+ });
47
+ voiceSelector.addEventListener('change', function() {
48
+ localStorage.setItem('toolsVoice', this.value);
49
+ if (window.voiceModule) window.voiceModule.setVoice(this.value);
50
+ });
51
+ }
52
+
53
+ function fetchTools() {
54
+ window.wsClient.rpc('tools.list')
55
+ .then(d => { tools = d.tools || []; syncMachineStates(); render(); })
56
+ .catch(() => {
57
+ fetch('/gm/api/tools').then(r => r.json()).then(d => { tools = d.tools || []; syncMachineStates(); render(); })
58
+ .catch(e => console.error('[TOOLS-MGR]', e.message));
59
+ });
60
+ }
61
+
62
+ function syncMachineStates() {
63
+ if (!window.toolInstallMachineAPI) return;
64
+ tools.forEach(function(tool) {
65
+ var ms = window.toolInstallMachineAPI.getState(tool.id);
66
+ if (ms !== 'installing' && ms !== 'updating') {
67
+ if (tool.status === 'installed') window.toolInstallMachineAPI.send(tool.id, { type: 'SET_INSTALLED', installedVersion: tool.installedVersion, publishedVersion: tool.publishedVersion });
68
+ else if (tool.status === 'needs_update') window.toolInstallMachineAPI.send(tool.id, { type: 'SET_NEEDS_UPDATE', installedVersion: tool.installedVersion, publishedVersion: tool.publishedVersion });
69
+ else if (tool.status === 'failed') window.toolInstallMachineAPI.send(tool.id, { type: 'SET_FAILED', error: tool.error_message });
70
+ }
71
+ });
72
+ }
73
+
74
+ function install(toolId) {
75
+ if (window.toolInstallMachineAPI && window.toolInstallMachineAPI.isLocked(toolId)) return;
76
+ if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(toolId, { type: 'INSTALL' });
77
+ render();
78
+ fetch('/gm/api/tools/' + toolId + '/install', { method: 'POST' }).then(r => r.json()).then(d => {
79
+ if (!d.success) { alert('Install failed: ' + (d.error || 'Unknown error')); if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(toolId, { type: 'FAILED', error: d.error }); render(); }
80
+ }).catch(e => { alert('Install failed: ' + e.message); if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(toolId, { type: 'FAILED', error: e.message }); render(); });
81
+ }
82
+
83
+ function update(toolId) {
84
+ if (window.toolInstallMachineAPI && window.toolInstallMachineAPI.isLocked(toolId)) return;
85
+ if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(toolId, { type: 'UPDATE' });
86
+ render();
87
+ fetch('/gm/api/tools/' + toolId + '/update', { method: 'POST' }).then(r => r.json()).then(d => {
88
+ if (!d.success) { alert('Update failed: ' + (d.error || 'Unknown error')); if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(toolId, { type: 'FAILED', error: d.error }); render(); }
89
+ }).catch(e => { alert('Update failed: ' + e.message); if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(toolId, { type: 'FAILED', error: e.message }); render(); });
90
+ }
91
+
92
+ function onWsMessage(e) {
93
+ var data = e.detail;
94
+ if (!data) return;
95
+ if (data.type === 'streaming_progress' && data.block && data.block.type === 'text' && data.block.text) {
96
+ var toggle = document.getElementById('toolsAutoSpeakToggle');
97
+ if (toggle && toggle.checked && (!data.blockRole || data.blockRole === 'assistant'))
98
+ if (window.voiceModule) window.voiceModule.speakText(data.block.text);
99
+ }
100
+ var api = window.toolInstallMachineAPI, tid = data.toolId;
101
+ if (data.type === 'tools_update_started') { (data.tools || []).forEach(t => api && api.send(t, { type: 'UPDATE' })); render(); }
102
+ else if (data.type === 'tool_install_started' || data.type === 'tool_install_progress') { if (api) api.send(tid, { type: 'PROGRESS' }); render(); }
103
+ else if (data.type === 'tool_update_progress') { if (api) api.send(tid, { type: 'PROGRESS' }); render(); }
104
+ else if (data.type === 'tool_install_complete' || data.type === 'tool_update_complete') {
105
+ var d = data.data || {};
106
+ if (api) api.send(tid, { type: 'COMPLETE', version: d.version, installedVersion: d.installedVersion, publishedVersion: d.publishedVersion });
107
+ var tool = tools.find(t => t.id === tid);
108
+ if (tool) { tool.version = d.version || tool.version; tool.installedVersion = d.installedVersion || tool.installedVersion; tool.publishedVersion = d.publishedVersion || tool.publishedVersion; tool.isUpToDate = d.isUpToDate ?? false; tool.upgradeNeeded = d.upgradeNeeded ?? false; tool.hasUpdate = (d.upgradeNeeded && d.installed) ?? false; }
109
+ render(); setTimeout(fetchTools, 1000);
110
+ } else if (data.type === 'tool_install_failed' || data.type === 'tool_update_failed') {
111
+ if (api) api.send(tid, { type: 'FAILED', error: (data.data || {}).error });
112
+ var tool = tools.find(t => t.id === tid); if (tool) tool.error_message = (data.data || {}).error;
113
+ render();
114
+ } else if (data.type === 'tool_status_update') {
115
+ var tool = tools.find(t => t.id === tid);
116
+ if (tool && data.data && data.data.installed) {
117
+ tool.installed = true; tool.isUpToDate = data.data.isUpToDate ?? true; tool.installedVersion = data.data.installedVersion || tool.installedVersion;
118
+ if (api) api.send(tid, data.data.isUpToDate ? { type: 'SET_INSTALLED', installedVersion: tool.installedVersion } : { type: 'SET_NEEDS_UPDATE', installedVersion: tool.installedVersion });
119
+ render();
120
+ }
121
+ } else if (data.type === 'tools_update_complete' || data.type === 'tools_refresh_complete') { isRefreshing = false; fetchTools(); }
122
+ }
123
+
124
+ function render() {
125
+ var scroll = popup.querySelector('.tools-popup-scroll');
126
+ if (!scroll) return;
127
+ if (!tools.length) { scroll.innerHTML = '<div class="tool-empty-state" style="grid-column: 1 / -1;"><div class="tool-empty-state-text">No tools available</div></div>'; return; }
128
+ var cli = tools.filter(t => t.category === 'cli'), plugin = tools.filter(t => t.category === 'plugin'), other = tools.filter(t => !t.category);
129
+ var ui = window.toolsManagerUI;
130
+ var html = '';
131
+ if (cli.length) html += '<div class="tool-section-header">CLI Agents</div>' + cli.map(t => ui.renderToolCard(t, isRefreshing)).join('');
132
+ if (plugin.length) html += '<div class="tool-section-header">GM Plugins</div>' + plugin.map(t => ui.renderToolCard(t, isRefreshing)).join('');
133
+ if (other.length) html += other.map(t => ui.renderToolCard(t, isRefreshing)).join('');
134
+ scroll.innerHTML = html;
135
+ }
136
+
137
+ function togglePopup(e) { e.stopPropagation(); if (!popup.classList.contains('open')) { isRefreshing = false; refresh(); } popup.classList.toggle('open'); }
138
+ function closePopup() { popup.classList.remove('open'); }
139
+ function refresh() { fetchTools(); }
140
+
141
+ function updateAll() {
142
+ var toUpdate = tools.filter(t => t.hasUpdate || t.status === 'needs_update' || t.status === 'failed');
143
+ if (!toUpdate.length) { alert('All tools are up-to-date'); return; }
144
+ toUpdate.forEach(t => { if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(t.id, { type: 'UPDATE' }); });
145
+ render();
146
+ fetch('/gm/api/tools/update', { method: 'POST' }).then(r => r.json()).then(d => {
147
+ if (!d.updating) { alert('Update started, but response unexpected'); toUpdate.forEach(t => { if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(t.id, { type: 'FAILED', error: 'unexpected response' }); }); }
148
+ }).catch(e => { alert('Update failed: ' + e.message); toUpdate.forEach(t => { if (window.toolInstallMachineAPI) window.toolInstallMachineAPI.send(t.id, { type: 'FAILED', error: e.message }); }); });
149
+ }
150
+
151
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
152
+ else init();
153
+
154
+ window.toolsManager = {
155
+ refresh: function() { isRefreshing = true; render(); fetch('/gm/api/tools/refresh-all', { method: 'POST' }).catch(e => console.error('[TOOLS-MGR]', e.message)); },
156
+ install: function(toolId) { install(toolId); },
157
+ update: function(toolId) { update(toolId); },
158
+ updateAll: function() { updateAll(); },
159
+ getAutoSpeak: function() { var t = document.getElementById('toolsAutoSpeakToggle'); return t ? t.checked : false; },
160
+ getVoice: function() { var s = document.getElementById('toolsVoiceSelector'); return s ? s.value : 'default'; },
161
+ setAutoSpeak: function(v) { var t = document.getElementById('toolsAutoSpeakToggle'); if (t) { t.checked = v; localStorage.setItem('toolsAutoSpeak', v); } },
162
+ setVoice: function(v) { var s = document.getElementById('toolsVoiceSelector'); if (s && Array.from(s.options).some(o => o.value === v)) { s.value = v; localStorage.setItem('toolsVoice', v); } }
163
+ };
164
+ })();