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.
Files changed (52) hide show
  1. package/database-schema.js +0 -58
  2. package/lib/db-queries-cleanup.js +0 -12
  3. package/lib/db-queries-del.js +0 -1
  4. package/lib/db-queries.js +0 -4
  5. package/lib/http-handler.js +1 -10
  6. package/lib/plugins/database-plugin.js +1 -1
  7. package/lib/process-message.js +2 -2
  8. package/lib/provider-config.js +0 -16
  9. package/lib/recovery.js +2 -12
  10. package/lib/routes-agent-actions.js +2 -58
  11. package/lib/routes-debug.js +1 -7
  12. package/lib/routes-registry.js +5 -17
  13. package/lib/server-startup.js +1 -59
  14. package/lib/stream-event-handler.js +1 -3
  15. package/lib/ws-handlers-session2.js +2 -23
  16. package/lib/ws-handlers-util.js +106 -175
  17. package/lib/ws-legacy-handlers.js +1 -104
  18. package/lib/ws-setup.js +1 -3
  19. package/package.json +1 -15
  20. package/server.js +9 -26
  21. package/test.js +1 -21
  22. package/ecosystem.config.cjs +0 -22
  23. package/lib/checkpoint-manager.js +0 -182
  24. package/lib/db-queries-tools.js +0 -131
  25. package/lib/db-queries-voice.js +0 -85
  26. package/lib/oauth-codex.js +0 -164
  27. package/lib/oauth-common.js +0 -92
  28. package/lib/oauth-gemini.js +0 -199
  29. package/lib/plugins/auth-plugin.js +0 -132
  30. package/lib/plugins/speech-plugin.js +0 -72
  31. package/lib/plugins/tools-plugin.js +0 -120
  32. package/lib/pm2-manager.js +0 -170
  33. package/lib/routes-oauth.js +0 -105
  34. package/lib/routes-speech.js +0 -173
  35. package/lib/routes-tools.js +0 -184
  36. package/lib/speech-manager.js +0 -200
  37. package/lib/speech.js +0 -50
  38. package/lib/tool-install-machine.js +0 -157
  39. package/lib/tool-manager.js +0 -98
  40. package/lib/tool-provisioner.js +0 -98
  41. package/lib/tool-spawner.js +0 -163
  42. package/lib/tool-version-check.js +0 -196
  43. package/lib/tool-version-fetch.js +0 -68
  44. package/lib/ws-handlers-oauth.js +0 -76
  45. package/static/js/agent-auth-oauth.js +0 -159
  46. package/static/js/pm2-monitor.js +0 -151
  47. package/static/js/stt-handler.js +0 -147
  48. package/static/js/tool-install-machine.js +0 -155
  49. package/static/js/tools-manager-ui.js +0 -124
  50. package/static/js/tools-manager.js +0 -172
  51. package/static/js/voice-machine.js +0 -145
  52. 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
- })();