agentgui 1.0.916 → 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.
Files changed (53) hide show
  1. package/CLAUDE.md +1 -382
  2. package/database-schema.js +0 -58
  3. package/lib/db-queries-cleanup.js +0 -12
  4. package/lib/db-queries-del.js +0 -1
  5. package/lib/db-queries.js +0 -4
  6. package/lib/http-handler.js +1 -10
  7. package/lib/plugins/database-plugin.js +1 -1
  8. package/lib/process-message.js +2 -2
  9. package/lib/provider-config.js +0 -16
  10. package/lib/recovery.js +2 -12
  11. package/lib/routes-agent-actions.js +2 -58
  12. package/lib/routes-debug.js +1 -7
  13. package/lib/routes-registry.js +5 -17
  14. package/lib/server-startup.js +1 -59
  15. package/lib/stream-event-handler.js +1 -3
  16. package/lib/ws-handlers-session2.js +2 -23
  17. package/lib/ws-handlers-util.js +106 -175
  18. package/lib/ws-legacy-handlers.js +1 -104
  19. package/lib/ws-setup.js +1 -3
  20. package/package.json +1 -15
  21. package/server.js +9 -26
  22. package/test.js +1 -21
  23. package/ecosystem.config.cjs +0 -22
  24. package/lib/checkpoint-manager.js +0 -182
  25. package/lib/db-queries-tools.js +0 -131
  26. package/lib/db-queries-voice.js +0 -85
  27. package/lib/oauth-codex.js +0 -164
  28. package/lib/oauth-common.js +0 -92
  29. package/lib/oauth-gemini.js +0 -199
  30. package/lib/plugins/auth-plugin.js +0 -132
  31. package/lib/plugins/speech-plugin.js +0 -72
  32. package/lib/plugins/tools-plugin.js +0 -120
  33. package/lib/pm2-manager.js +0 -170
  34. package/lib/routes-oauth.js +0 -105
  35. package/lib/routes-speech.js +0 -173
  36. package/lib/routes-tools.js +0 -184
  37. package/lib/speech-manager.js +0 -200
  38. package/lib/speech.js +0 -50
  39. package/lib/tool-install-machine.js +0 -157
  40. package/lib/tool-manager.js +0 -98
  41. package/lib/tool-provisioner.js +0 -98
  42. package/lib/tool-spawner.js +0 -163
  43. package/lib/tool-version-check.js +0 -196
  44. package/lib/tool-version-fetch.js +0 -68
  45. package/lib/ws-handlers-oauth.js +0 -76
  46. package/static/js/agent-auth-oauth.js +0 -159
  47. package/static/js/pm2-monitor.js +0 -151
  48. package/static/js/stt-handler.js +0 -147
  49. package/static/js/tool-install-machine.js +0 -155
  50. package/static/js/tools-manager-ui.js +0 -124
  51. package/static/js/tools-manager.js +0 -172
  52. package/static/js/voice-machine.js +0 -145
  53. package/static/js/voice.js +0 -134
@@ -1,151 +0,0 @@
1
- (function() {
2
- const ACTIVE_STATES = new Set(['online', 'launching', 'stopping', 'waiting restart']);
3
-
4
- window.pm2Monitor = { processes: [], logsModalOpen: null, initialized: false };
5
-
6
- const panel = document.getElementById('pm2MonitorPanel');
7
- const list = document.getElementById('pm2ProcessList');
8
- const refreshBtn = document.getElementById('pm2RefreshBtn');
9
-
10
- function send(msg) {
11
- if (window.wsManager && window.wsManager.isConnected) window.wsManager.sendMessage(msg);
12
- }
13
-
14
- function setPanelVisible(visible) {
15
- if (!panel) return;
16
- panel.style.display = visible ? 'flex' : 'none';
17
- }
18
-
19
- function renderProcessList(processes) {
20
- if (!list) return;
21
- const active = processes.filter(p => ACTIVE_STATES.has(p.status));
22
- const inactive = processes.filter(p => !ACTIVE_STATES.has(p.status));
23
- const ordered = [...active, ...inactive];
24
- list.innerHTML = ordered.map(proc => {
25
- const isActive = ACTIVE_STATES.has(proc.status);
26
- const uptimeSec = proc.uptime ? Math.floor((Date.now() - proc.uptime) / 1000) : null;
27
- return `<div class="pm2-process-item ${proc.status}" data-pm2-name="${escAttr(proc.name)}">
28
- <div class="pm2-process-name">${escHtml(proc.name)} <span class="pm2-status-dot pm2-status-${proc.status}"></span></div>
29
- <div class="pm2-process-meta">
30
- <span>${proc.status}</span>
31
- <span>${(proc.cpu || 0).toFixed(1)}% CPU</span>
32
- <span>${fmtBytes(proc.memory)}</span>
33
- ${uptimeSec !== null ? `<span>${fmtUptime(uptimeSec)}</span>` : ''}
34
- <span>#${proc.pid || '-'}</span>
35
- ${proc.restarts > 0 ? `<span>${proc.restarts}x restarts</span>` : ''}
36
- </div>
37
- <div class="pm2-process-actions">
38
- ${!isActive ? `<button class="pm2-btn" onclick="window.pm2Monitor.startProcess('${escAttr(proc.name)}')">Start</button>` : ''}
39
- ${proc.status === 'online' ? `<button class="pm2-btn danger" onclick="window.pm2Monitor.stopProcess('${escAttr(proc.name)}')">Stop</button>` : ''}
40
- <button class="pm2-btn" onclick="window.pm2Monitor.restartProcess('${escAttr(proc.name)}')">Restart</button>
41
- <button class="pm2-btn" onclick="window.pm2Monitor.showLogs('${escAttr(proc.name)}')">Logs</button>
42
- <button class="pm2-btn danger" onclick="window.pm2Monitor.deleteProcess('${escAttr(proc.name)}')">Delete</button>
43
- </div>
44
- </div>`;
45
- }).join('');
46
- }
47
-
48
- function handleMessage(msg) {
49
- if (msg.type === 'pm2_monit_update') {
50
- const procs = msg.processes || [];
51
- window.pm2Monitor.processes = procs;
52
- const hasActive = msg.hasActive || procs.some(p => ACTIVE_STATES.has(p.status));
53
- setPanelVisible(hasActive);
54
- if (hasActive) renderProcessList(procs);
55
- } else if (msg.type === 'pm2_list_response') {
56
- const procs = msg.processes || [];
57
- window.pm2Monitor.processes = procs;
58
- const hasActive = procs.some(p => ACTIVE_STATES.has(p.status));
59
- setPanelVisible(hasActive);
60
- if (hasActive) renderProcessList(procs);
61
- } else if (msg.type === 'pm2_unavailable') {
62
- setPanelVisible(false);
63
- } else if (msg.type === 'pm2_start_response' || msg.type === 'pm2_stop_response' ||
64
- msg.type === 'pm2_restart_response' || msg.type === 'pm2_delete_response') {
65
- const action = msg.type.replace('pm2_', '').replace('_response', '');
66
- if (msg.success) {
67
- toast(`PM2 ${action} ${msg.name} succeeded`, 'success');
68
- setTimeout(() => send({ type: 'pm2_list' }), 500);
69
- } else {
70
- toast(`PM2 ${action} ${msg.name} failed: ${msg.error}`, 'error');
71
- }
72
- } else if (msg.type === 'pm2_logs_response') {
73
- if (msg.success) showLogsModal(msg.name, msg.logs);
74
- else toast(`Logs error: ${msg.error}`, 'error');
75
- } else if (msg.type === 'pm2_flush_logs_response') {
76
- toast(msg.success ? 'Logs flushed' : `Flush failed: ${msg.error}`, msg.success ? 'success' : 'error');
77
- }
78
- }
79
-
80
- function init() {
81
- if (window.pm2Monitor.initialized) return;
82
- window.pm2Monitor.initialized = true;
83
- setPanelVisible(false);
84
- if (refreshBtn) refreshBtn.addEventListener('click', () => send({ type: 'pm2_list' }));
85
- document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && window.pm2Monitor.logsModalOpen) window.pm2Monitor.closeLogsModal(); });
86
- if (window.wsManager) {
87
- window.wsManager.on('message', handleMessage);
88
- window.wsManager.on('connected', () => {
89
- send({ type: 'pm2_start_monitoring' });
90
- send({ type: 'pm2_list' });
91
- });
92
- if (window.wsManager.isConnected) {
93
- send({ type: 'pm2_start_monitoring' });
94
- send({ type: 'pm2_list' });
95
- }
96
- }
97
- }
98
-
99
- function showLogsModal(name, logs) {
100
- if (window.pm2Monitor.logsModalOpen) window.pm2Monitor.closeLogsModal();
101
- const modal = document.createElement('div');
102
- modal.className = 'pm2-logs-modal';
103
- modal.innerHTML = `<div class="pm2-logs-content">
104
- <div class="pm2-logs-header"><span>Logs: ${escHtml(name)}</span><button class="pm2-btn" onclick="window.pm2Monitor.closeLogsModal()">Close</button></div>
105
- <div class="pm2-logs-body">${escHtml(logs || 'No logs available')}</div>
106
- <div class="pm2-logs-footer">
107
- <button class="pm2-btn" onclick="window.pm2Monitor.flushLogs('${escAttr(name)}')">Flush</button>
108
- <button class="pm2-btn" onclick="window.pm2Monitor.closeLogsModal()">Close</button>
109
- </div></div>`;
110
- document.body.appendChild(modal);
111
- window.pm2Monitor.logsModalOpen = name;
112
- modal.addEventListener('click', (e) => { if (e.target === modal) window.pm2Monitor.closeLogsModal(); });
113
- }
114
-
115
- function toast(msg, type) {
116
- const t = document.createElement('div');
117
- t.className = 'pm2-toast';
118
- t.textContent = msg;
119
- const bg = type === 'error' ? 'var(--color-error)' : type === 'success' ? 'var(--color-success)' : 'var(--color-primary)';
120
- t.style.cssText = `position:fixed;top:1rem;right:1rem;padding:0.5rem 1rem;border-radius:0.375rem;font-size:0.8rem;color:white;z-index:10000;background:${bg}`;
121
- document.body.appendChild(t);
122
- setTimeout(() => { t.style.opacity = '0'; t.style.transition = 'opacity 0.3s'; setTimeout(() => t.remove(), 300); }, 3000);
123
- }
124
-
125
- function escHtml(s) { if (!s) return ''; const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
126
- function escAttr(s) { return (s || '').replace(/"/g, '&quot;').replace(/'/g, '&#039;'); }
127
- function fmtBytes(b) {
128
- if (!b || typeof b !== 'number') return '0 B';
129
- const u = ['B','KB','MB','GB'], i = Math.min(Math.floor(Math.log(b) / Math.log(1024)), 3);
130
- return (b / Math.pow(1024, i)).toFixed(1) + ' ' + u[i];
131
- }
132
- function fmtUptime(s) {
133
- if (!s || s < 0) return '0s';
134
- const d = Math.floor(s / 86400), h = Math.floor((s % 86400) / 3600), m = Math.floor((s % 3600) / 60), sec = s % 60;
135
- if (d > 0) return `${d}d ${h}h`;
136
- if (h > 0) return `${h}h ${m}m`;
137
- if (m > 0) return `${m}m ${sec}s`;
138
- return `${sec}s`;
139
- }
140
-
141
- window.pm2Monitor.closeLogsModal = () => { const m = document.querySelector('.pm2-logs-modal'); if (m) m.remove(); window.pm2Monitor.logsModalOpen = null; };
142
- window.pm2Monitor.startProcess = (name) => send({ type: 'pm2_start', name });
143
- window.pm2Monitor.stopProcess = (name) => send({ type: 'pm2_stop', name });
144
- window.pm2Monitor.restartProcess = (name) => send({ type: 'pm2_restart', name });
145
- window.pm2Monitor.deleteProcess = (name) => { if (confirm(`Delete PM2 process "${name}"?`)) send({ type: 'pm2_delete', name }); };
146
- window.pm2Monitor.showLogs = (name) => send({ type: 'pm2_logs', name, lines: 200 });
147
- window.pm2Monitor.flushLogs = (name) => send({ type: 'pm2_flush_logs', name });
148
-
149
- if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => setTimeout(init, 150));
150
- else setTimeout(init, 150);
151
- })();
@@ -1,147 +0,0 @@
1
- (function() {
2
- var BASE = window.__BASE_URL || '';
3
- var isRecording = false;
4
- var mediaStream = null;
5
- var audioContext = null;
6
- var workletNode = null;
7
- var recordedChunks = [];
8
- var TARGET_SAMPLE_RATE = 16000;
9
-
10
- function rmApi() { return window.recordingMachineAPI; }
11
-
12
- window.STTHandler = {
13
- isRecording: function() { return rmApi()?.isRecording() || isRecording; },
14
-
15
- startRecording: async function() {
16
- if (isRecording || rmApi()?.isRecording()) return;
17
- try {
18
- mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
19
- audioContext = new (window.AudioContext || window.webkitAudioContext)();
20
- var source = audioContext.createMediaStreamSource(mediaStream);
21
- recordedChunks = [];
22
- await audioContext.audioWorklet.addModule(BASE + '/js/audio-recorder-processor.js');
23
- workletNode = new AudioWorkletNode(audioContext, 'recorder-processor');
24
- workletNode.port.onmessage = function(e) {
25
- recordedChunks.push(e.data);
26
- };
27
- source.connect(workletNode);
28
- isRecording = true;
29
- if (rmApi()) rmApi().send({ type: 'START' });
30
- return { success: true };
31
- } catch (e) {
32
- isRecording = false;
33
- if (rmApi()) rmApi().send({ type: 'ERROR', error: e.message });
34
- return { success: false, error: e.message };
35
- }
36
- },
37
-
38
- stopRecording: async function() {
39
- if (!isRecording && !rmApi()?.isRecording()) return { success: false, error: 'Not recording' };
40
- isRecording = false;
41
- if (rmApi()) rmApi().send({ type: 'STOP' });
42
-
43
- if (workletNode) {
44
- workletNode.port.postMessage('stop');
45
- workletNode.disconnect();
46
- workletNode = null;
47
- }
48
-
49
- if (mediaStream) {
50
- mediaStream.getTracks().forEach(function(t) { t.stop(); });
51
- mediaStream = null;
52
- }
53
-
54
- var sourceSampleRate = audioContext ? audioContext.sampleRate : 48000;
55
- if (audioContext) {
56
- audioContext.close().catch(function() {});
57
- audioContext = null;
58
- }
59
-
60
- if (recordedChunks.length === 0) {
61
- return { success: false, error: 'No audio recorded' };
62
- }
63
-
64
- var totalLen = 0;
65
- for (var i = 0; i < recordedChunks.length; i++) totalLen += recordedChunks[i].length;
66
- var merged = new Float32Array(totalLen);
67
- var offset = 0;
68
- for (var j = 0; j < recordedChunks.length; j++) {
69
- merged.set(recordedChunks[j], offset);
70
- offset += recordedChunks[j].length;
71
- }
72
- recordedChunks = [];
73
-
74
- var resampled = resampleBuffer(merged, sourceSampleRate, TARGET_SAMPLE_RATE);
75
- var wavBuffer = encodeWav(resampled, TARGET_SAMPLE_RATE);
76
-
77
- try {
78
- var resp = await fetch(BASE + '/api/stt', {
79
- method: 'POST',
80
- headers: { 'Content-Type': 'audio/wav' },
81
- body: wavBuffer
82
- });
83
- var data = await resp.json();
84
- if (data.text) {
85
- if (rmApi()) rmApi().send({ type: 'COMPLETE' });
86
- return { success: true, text: data.text };
87
- } else if (data.error) {
88
- if (rmApi()) rmApi().send({ type: 'ERROR', error: data.error });
89
- return { success: false, error: data.error };
90
- } else {
91
- if (rmApi()) rmApi().send({ type: 'ERROR', error: 'No transcription returned' });
92
- return { success: false, error: 'No transcription returned' };
93
- }
94
- } catch (e) {
95
- if (rmApi()) rmApi().send({ type: 'ERROR', error: e.message });
96
- return { success: false, error: 'Transcription failed: ' + e.message };
97
- }
98
- }
99
- };
100
-
101
- function resampleBuffer(inputBuffer, fromRate, toRate) {
102
- if (fromRate === toRate) return inputBuffer;
103
- var ratio = fromRate / toRate;
104
- var newLen = Math.round(inputBuffer.length / ratio);
105
- var result = new Float32Array(newLen);
106
- for (var i = 0; i < newLen; i++) {
107
- var srcIdx = i * ratio;
108
- var lo = Math.floor(srcIdx);
109
- var hi = Math.min(lo + 1, inputBuffer.length - 1);
110
- var frac = srcIdx - lo;
111
- result[i] = inputBuffer[lo] * (1 - frac) + inputBuffer[hi] * frac;
112
- }
113
- return result;
114
- }
115
-
116
- function encodeWav(float32Audio, sampleRate) {
117
- var numSamples = float32Audio.length;
118
- var bytesPerSample = 2;
119
- var dataSize = numSamples * bytesPerSample;
120
- var buffer = new ArrayBuffer(44 + dataSize);
121
- var view = new DataView(buffer);
122
-
123
- function writeStr(off, str) {
124
- for (var i = 0; i < str.length; i++) view.setUint8(off + i, str.charCodeAt(i));
125
- }
126
-
127
- writeStr(0, 'RIFF');
128
- view.setUint32(4, 36 + dataSize, true);
129
- writeStr(8, 'WAVE');
130
- writeStr(12, 'fmt ');
131
- view.setUint32(16, 16, true);
132
- view.setUint16(20, 1, true);
133
- view.setUint16(22, 1, true);
134
- view.setUint32(24, sampleRate, true);
135
- view.setUint32(28, sampleRate * bytesPerSample, true);
136
- view.setUint16(32, bytesPerSample, true);
137
- view.setUint16(34, 16, true);
138
- writeStr(36, 'data');
139
- view.setUint32(40, dataSize, true);
140
-
141
- for (var i = 0; i < numSamples; i++) {
142
- var s = Math.max(-1, Math.min(1, float32Audio[i]));
143
- view.setInt16(44 + i * 2, s < 0 ? s * 32768 : s * 32767, true);
144
- }
145
- return buffer;
146
- }
147
- })();
@@ -1,155 +0,0 @@
1
- (function() {
2
- const { createMachine, createActor, assign } = XState;
3
-
4
- const toolInstallMachine = createMachine({
5
- id: 'tool-install-ui',
6
- initial: 'idle',
7
- context: {
8
- version: null,
9
- error: null,
10
- progress: 0,
11
- installedVersion: null,
12
- publishedVersion: null,
13
- },
14
- states: {
15
- idle: {
16
- entry: assign({ error: null, progress: 0 }),
17
- on: {
18
- INSTALL: 'installing',
19
- UPDATE: 'updating',
20
- SET_INSTALLED: {
21
- target: 'installed',
22
- actions: assign(({ event }) => ({
23
- installedVersion: event.installedVersion || null,
24
- publishedVersion: event.publishedVersion || null,
25
- version: event.version || event.installedVersion || null,
26
- })),
27
- },
28
- SET_NEEDS_UPDATE: {
29
- target: 'needs_update',
30
- actions: assign(({ event }) => ({
31
- installedVersion: event.installedVersion || null,
32
- publishedVersion: event.publishedVersion || null,
33
- })),
34
- },
35
- SET_FAILED: {
36
- target: 'failed',
37
- actions: assign(({ event }) => ({ error: event.error || null })),
38
- },
39
- },
40
- },
41
- installing: {
42
- entry: assign({ error: null, progress: 0 }),
43
- on: {
44
- PROGRESS: {
45
- actions: assign(({ context, event }) => ({
46
- progress: Math.min(event.progress || context.progress + 5, 90),
47
- })),
48
- },
49
- COMPLETE: {
50
- target: 'installed',
51
- actions: assign(({ event }) => ({
52
- version: event.version || null,
53
- installedVersion: event.installedVersion || null,
54
- publishedVersion: event.publishedVersion || null,
55
- progress: 100,
56
- error: null,
57
- })),
58
- },
59
- FAILED: {
60
- target: 'failed',
61
- actions: assign(({ event }) => ({ error: event.error || null, progress: 0 })),
62
- },
63
- },
64
- },
65
- installed: {
66
- on: {
67
- UPDATE: 'updating',
68
- SET_NEEDS_UPDATE: {
69
- target: 'needs_update',
70
- actions: assign(({ event }) => ({
71
- installedVersion: event.installedVersion || null,
72
- publishedVersion: event.publishedVersion || null,
73
- })),
74
- },
75
- },
76
- },
77
- updating: {
78
- entry: assign({ error: null, progress: 0 }),
79
- on: {
80
- PROGRESS: {
81
- actions: assign(({ context, event }) => ({
82
- progress: Math.min(event.progress || context.progress + 5, 90),
83
- })),
84
- },
85
- COMPLETE: {
86
- target: 'installed',
87
- actions: assign(({ event }) => ({
88
- version: event.version || null,
89
- installedVersion: event.installedVersion || null,
90
- publishedVersion: event.publishedVersion || null,
91
- progress: 100,
92
- error: null,
93
- })),
94
- },
95
- FAILED: {
96
- target: 'failed',
97
- actions: assign(({ event }) => ({ error: event.error || null, progress: 0 })),
98
- },
99
- },
100
- },
101
- needs_update: {
102
- on: {
103
- UPDATE: 'updating',
104
- SET_INSTALLED: {
105
- target: 'installed',
106
- actions: assign(({ event }) => ({
107
- installedVersion: event.installedVersion || null,
108
- publishedVersion: event.publishedVersion || null,
109
- })),
110
- },
111
- },
112
- },
113
- failed: {
114
- on: {
115
- INSTALL: 'installing',
116
- UPDATE: 'updating',
117
- },
118
- },
119
- },
120
- });
121
-
122
- const toolInstallMachines = new Map();
123
-
124
- function getOrCreate(toolId) {
125
- if (toolInstallMachines.has(toolId)) return toolInstallMachines.get(toolId);
126
- const actor = createActor(toolInstallMachine);
127
- actor.start();
128
- toolInstallMachines.set(toolId, actor);
129
- return actor;
130
- }
131
-
132
- function sendEvent(toolId, event) {
133
- const actor = getOrCreate(toolId);
134
- actor.send(event);
135
- return actor.getSnapshot();
136
- }
137
-
138
- function getState(toolId) {
139
- const actor = toolInstallMachines.get(toolId);
140
- return actor ? actor.getSnapshot().value : 'idle';
141
- }
142
-
143
- function isLocked(toolId) {
144
- const s = getState(toolId);
145
- return s === 'installing' || s === 'updating';
146
- }
147
-
148
- function remove(toolId) {
149
- const actor = toolInstallMachines.get(toolId);
150
- if (actor) { actor.stop(); toolInstallMachines.delete(toolId); }
151
- }
152
-
153
- window.__toolInstallMachines = toolInstallMachines;
154
- window.toolInstallMachineAPI = { getOrCreate, send: sendEvent, getState, isLocked, remove };
155
- })();
@@ -1,124 +0,0 @@
1
- (function() {
2
- window.toolsManagerUI = {
3
- getStatusColor: function(tool) {
4
- var ms = window.toolInstallMachineAPI ? window.toolInstallMachineAPI.getState(tool.id) : null;
5
- if (ms === 'installing' || ms === 'updating') return '#3b82f6';
6
- if (ms === 'needs_update') return '#f59e0b';
7
- if (ms === 'installed') return '#10b981';
8
- if (ms === 'failed') return '#ef4444';
9
- if (tool.status === 'installing' || tool.status === 'updating') return '#3b82f6';
10
- if (tool.status === 'needs_update' || (tool.status === 'installed' && tool.hasUpdate)) return '#f59e0b';
11
- if (tool.status === 'installed') return '#10b981';
12
- if (tool.status === 'failed') return '#ef4444';
13
- return '#6b7280';
14
- },
15
-
16
- getStatusText: function(tool) {
17
- var ms = window.toolInstallMachineAPI ? window.toolInstallMachineAPI.getState(tool.id) : null;
18
- if (ms === 'installing') return 'Installing...';
19
- if (ms === 'updating') return 'Updating...';
20
- if (ms === 'needs_update') return 'Update available';
21
- if (ms === 'installed') return tool.hasUpdate ? 'Update available' : 'Up-to-date';
22
- if (ms === 'failed') return 'Installation failed';
23
- if (tool.status === 'installing') return 'Installing...';
24
- if (tool.status === 'updating') return 'Updating...';
25
- if (tool.status === 'needs_update') return 'Update available';
26
- if (tool.status === 'installed') return tool.hasUpdate ? 'Update available' : 'Up-to-date';
27
- if (tool.status === 'failed') return 'Installation failed';
28
- return 'Not installed';
29
- },
30
-
31
- getStatusClass: function(tool) {
32
- var ms = window.toolInstallMachineAPI ? window.toolInstallMachineAPI.getState(tool.id) : null;
33
- if (ms === 'installing' || ms === 'updating') return 'installing';
34
- if (ms === 'needs_update') return 'updating';
35
- if (ms === 'installed') return 'installed';
36
- if (ms === 'failed') return 'failed';
37
- if (tool.status === 'installing' || tool.status === 'updating') return 'installing';
38
- if (tool.status === 'needs_update' || (tool.status === 'installed' && tool.hasUpdate)) return 'updating';
39
- if (tool.status === 'installed') return 'installed';
40
- if (tool.status === 'failed') return 'failed';
41
- return 'not-installed';
42
- },
43
-
44
- renderToolCard: function(tool, isRefreshing) {
45
- var h = window.webjsx && window.webjsx.createElement;
46
- if (!h) return null;
47
- var ui = window.toolsManagerUI;
48
- var statusClass = ui.getStatusClass(tool);
49
- var ms = window.toolInstallMachineAPI ? window.toolInstallMachineAPI.getState(tool.id) : null;
50
- var isInstalling = ms === 'installing' || ms === 'updating';
51
- var snap = window.__toolInstallMachines && window.__toolInstallMachines.get(tool.id) ? window.__toolInstallMachines.get(tool.id).getSnapshot() : null;
52
- var progress = snap ? snap.context.progress : 0;
53
- var locked = window.toolInstallMachineAPI ? window.toolInstallMachineAPI.isLocked(tool.id) : false;
54
- var canInstall = ms === 'idle' || ms === 'failed' || tool.status === 'not_installed' || tool.status === 'failed';
55
- var canUpdate = ms === 'needs_update' || tool.hasUpdate || tool.status === 'needs_update';
56
- var iv = (snap && snap.context.installedVersion) || tool.installedVersion;
57
- var pv = (snap && snap.context.publishedVersion) || tool.publishedVersion;
58
- var versionChildren = [];
59
- if (iv) versionChildren.push(h('span', { class: 'tool-version-item' }, 'v' + iv));
60
- if (pv && iv !== pv) versionChildren.push(h('span', { class: 'tool-version-item' }, '(v' + pv + ' available)'));
61
- var actionBtn = canInstall
62
- ? h('button', { class: 'tool-btn tool-btn-primary', disabled: locked, onClick: function() { window.toolsManager.install(tool.id); } }, 'Install')
63
- : canUpdate
64
- ? h('button', { class: 'tool-btn tool-btn-primary', disabled: locked, onClick: function() { window.toolsManager.update(tool.id); } }, 'Update')
65
- : h('button', { class: 'tool-btn tool-btn-secondary', disabled: isRefreshing, onClick: function() { window.toolsManager.refresh(); } }, '\u2713');
66
- return h('div', { class: 'tool-item' },
67
- h('div', { style: 'display:flex;flex-direction:column;gap:0.3rem;' },
68
- h('div', { class: 'tool-header' }, h('span', { class: 'tool-name' }, tool.name || tool.id)),
69
- h('div', { class: 'tool-status-indicator ' + statusClass },
70
- h('span', { class: 'tool-status-dot' }),
71
- h('span', {}, ui.getStatusText(tool))
72
- ),
73
- versionChildren.length ? h('div', { class: 'tool-versions' }, ...versionChildren) : null,
74
- isInstalling && progress !== undefined
75
- ? h('div', { class: 'tool-progress-container' }, h('div', { class: 'tool-progress-bar' }, h('div', { class: 'tool-progress-fill', style: 'width:' + Math.min(progress, 100) + '%' })))
76
- : null,
77
- tool.error_message ? h('div', { class: 'tool-error-message' }, 'Error: ' + tool.error_message.substring(0, 40)) : null
78
- ),
79
- h('div', { class: 'tool-actions' }, actionBtn)
80
- );
81
- },
82
-
83
- esc: function(s) {
84
- var d = document.createElement('div');
85
- d.textContent = s;
86
- return d.innerHTML;
87
- },
88
-
89
- updateVoiceSelector: function(voices) {
90
- var voiceSelector = document.getElementById('toolsVoiceSelector');
91
- if (!voiceSelector || !voices || !Array.isArray(voices)) return;
92
- var currentValue = voiceSelector.value || localStorage.getItem('toolsVoice') || 'default';
93
- voiceSelector.innerHTML = '';
94
- var builtIn = voices.filter(function(v) { return !v.isCustom; });
95
- var custom = voices.filter(function(v) { return v.isCustom; });
96
- if (builtIn.length) {
97
- var grp1 = document.createElement('optgroup');
98
- grp1.label = 'Built-in Voices';
99
- builtIn.forEach(function(voice) {
100
- var opt = document.createElement('option');
101
- opt.value = voice.id;
102
- var parts = [];
103
- if (voice.gender && voice.gender !== 'custom') parts.push(voice.gender);
104
- if (voice.accent && voice.accent !== 'custom') parts.push(voice.accent);
105
- opt.textContent = voice.name + (parts.length ? ' (' + parts.join(', ') + ')' : '');
106
- grp1.appendChild(opt);
107
- });
108
- voiceSelector.appendChild(grp1);
109
- }
110
- if (custom.length) {
111
- var grp2 = document.createElement('optgroup');
112
- grp2.label = 'Custom Voices';
113
- custom.forEach(function(voice) {
114
- var opt = document.createElement('option');
115
- opt.value = voice.id;
116
- opt.textContent = voice.name;
117
- grp2.appendChild(opt);
118
- });
119
- voiceSelector.appendChild(grp2);
120
- }
121
- if (voiceSelector.querySelector('option[value="' + currentValue + '"]')) voiceSelector.value = currentValue;
122
- }
123
- };
124
- })();