agentgui 1.0.274 → 1.0.275
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +280 -280
- package/IPFS_DOWNLOADER.md +277 -277
- package/TASK_2C_COMPLETION.md +334 -334
- package/bin/gmgui.cjs +54 -54
- package/build-portable.js +3 -42
- package/database.js +1422 -1406
- package/lib/claude-runner.js +1130 -1130
- package/lib/ipfs-downloader.js +459 -459
- package/lib/speech.js +152 -152
- package/package.json +1 -1
- package/readme.md +76 -76
- package/server.js +3787 -3794
- package/setup-npm-token.sh +68 -68
- package/static/app.js +773 -773
- package/static/event-rendering-showcase.html +708 -708
- package/static/index.html +3178 -3180
- package/static/js/agent-auth.js +298 -298
- package/static/js/audio-recorder-processor.js +18 -18
- package/static/js/client.js +2656 -2656
- package/static/js/conversations.js +583 -583
- package/static/js/dialogs.js +267 -267
- package/static/js/event-consolidator.js +101 -101
- package/static/js/event-filter.js +311 -311
- package/static/js/event-processor.js +452 -452
- package/static/js/features.js +413 -413
- package/static/js/kalman-filter.js +67 -67
- package/static/js/progress-dialog.js +130 -130
- package/static/js/script-runner.js +219 -219
- package/static/js/streaming-renderer.js +2123 -2120
- package/static/js/syntax-highlighter.js +269 -269
- package/static/js/tts-websocket-handler.js +152 -152
- package/static/js/ui-components.js +431 -431
- package/static/js/voice.js +849 -849
- package/static/js/websocket-manager.js +596 -596
- package/static/templates/INDEX.html +465 -465
- package/static/templates/README.md +190 -190
- package/static/templates/agent-capabilities.html +56 -56
- package/static/templates/agent-metadata-panel.html +44 -44
- package/static/templates/agent-status-badge.html +30 -30
- package/static/templates/code-annotation-panel.html +155 -155
- package/static/templates/code-suggestion-panel.html +184 -184
- package/static/templates/command-header.html +77 -77
- package/static/templates/command-output-scrollable.html +118 -118
- package/static/templates/elapsed-time.html +54 -54
- package/static/templates/error-alert.html +106 -106
- package/static/templates/error-history-timeline.html +160 -160
- package/static/templates/error-recovery-options.html +109 -109
- package/static/templates/error-stack-trace.html +95 -95
- package/static/templates/error-summary.html +80 -80
- package/static/templates/event-counter.html +48 -48
- package/static/templates/execution-actions.html +97 -97
- package/static/templates/execution-progress-bar.html +80 -80
- package/static/templates/execution-stepper.html +120 -120
- package/static/templates/file-breadcrumb.html +118 -118
- package/static/templates/file-diff-viewer.html +121 -121
- package/static/templates/file-metadata.html +133 -133
- package/static/templates/file-read-panel.html +66 -66
- package/static/templates/file-write-panel.html +120 -120
- package/static/templates/git-branch-remote.html +107 -107
- package/static/templates/git-diff-list.html +101 -101
- package/static/templates/git-log-visualization.html +153 -153
- package/static/templates/git-status-panel.html +115 -115
- package/static/templates/quality-metrics-display.html +170 -170
- package/static/templates/terminal-output-panel.html +87 -87
- package/static/templates/test-results-display.html +144 -144
- package/static/theme.js +72 -72
- package/test-download-progress.js +223 -223
- package/test-websocket-broadcast.js +147 -147
- package/tests/ipfs-downloader.test.js +370 -370
package/static/js/agent-auth.js
CHANGED
|
@@ -1,298 +1,298 @@
|
|
|
1
|
-
(function() {
|
|
2
|
-
var BASE = window.__BASE_URL || '';
|
|
3
|
-
var btn = document.getElementById('agentAuthBtn');
|
|
4
|
-
var dropdown = document.getElementById('agentAuthDropdown');
|
|
5
|
-
var agents = [], providers = {}, authRunning = false, editingProvider = null;
|
|
6
|
-
var AUTH_CONV_ID = '__agent_auth__';
|
|
7
|
-
|
|
8
|
-
function init() {
|
|
9
|
-
if (!btn || !dropdown) return;
|
|
10
|
-
btn.style.display = 'flex';
|
|
11
|
-
btn.addEventListener('click', toggleDropdown);
|
|
12
|
-
document.addEventListener('click', function(e) {
|
|
13
|
-
if (!btn.contains(e.target) && !dropdown.contains(e.target)) closeDropdown();
|
|
14
|
-
});
|
|
15
|
-
window.addEventListener('conversation-selected', function() { refresh(); });
|
|
16
|
-
window.addEventListener('ws-message', onWsMessage);
|
|
17
|
-
refresh();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function refresh() { fetchAuthStatus(); fetchProviderConfigs(); }
|
|
21
|
-
|
|
22
|
-
function fetchAuthStatus() {
|
|
23
|
-
fetch(BASE + '/api/agents/auth-status').then(function(r) { return r.json(); })
|
|
24
|
-
.then(function(data) { agents = data.agents || []; updateButton(); renderDropdown(); })
|
|
25
|
-
.catch(function() {});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function fetchProviderConfigs() {
|
|
29
|
-
fetch(BASE + '/api/auth/configs').then(function(r) { return r.json(); })
|
|
30
|
-
.then(function(data) { providers = data || {}; updateButton(); renderDropdown(); })
|
|
31
|
-
.catch(function() {});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function updateButton() {
|
|
35
|
-
btn.style.display = 'flex';
|
|
36
|
-
var agentOk = agents.length === 0 || agents.every(function(a) { return a.authenticated; });
|
|
37
|
-
var pkeys = Object.keys(providers);
|
|
38
|
-
var provOk = pkeys.length === 0 || pkeys.some(function(k) { return providers[k].hasKey; });
|
|
39
|
-
var anyWarn = agents.some(function(a) { return !a.authenticated; }) ||
|
|
40
|
-
pkeys.some(function(k) { return !providers[k].hasKey; });
|
|
41
|
-
btn.classList.toggle('auth-ok', agentOk && provOk && (agents.length > 0 || pkeys.length > 0));
|
|
42
|
-
btn.classList.toggle('auth-warn', anyWarn);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function renderDropdown() {
|
|
46
|
-
dropdown.innerHTML = '';
|
|
47
|
-
if (agents.length > 0) {
|
|
48
|
-
appendHeader('Agent CLI Auth');
|
|
49
|
-
agents.forEach(function(agent) {
|
|
50
|
-
var dotClass = agent.authenticated ? 'ok' : (agent.detail === 'unknown' ? 'unknown' : 'missing');
|
|
51
|
-
var item = makeItem(dotClass, agent.name, agent.detail);
|
|
52
|
-
item.addEventListener('click', function(e) { e.stopPropagation(); closeDropdown(); triggerAuth(agent.id); });
|
|
53
|
-
dropdown.appendChild(item);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
var pkeys = Object.keys(providers);
|
|
57
|
-
if (pkeys.length > 0) {
|
|
58
|
-
if (agents.length > 0) appendSep();
|
|
59
|
-
appendHeader('Provider Keys');
|
|
60
|
-
pkeys.forEach(function(pid) {
|
|
61
|
-
var p = providers[pid];
|
|
62
|
-
var item = makeItem(p.hasKey ? 'ok' : 'missing', p.name || pid, p.hasKey ? p.apiKey : 'not set');
|
|
63
|
-
item.style.flexWrap = 'wrap';
|
|
64
|
-
item.addEventListener('click', function(e) { e.stopPropagation(); toggleEdit(pid); });
|
|
65
|
-
dropdown.appendChild(item);
|
|
66
|
-
if (editingProvider === pid) dropdown.appendChild(makeEditForm(pid));
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function appendHeader(text) {
|
|
72
|
-
var h = document.createElement('div');
|
|
73
|
-
h.className = 'agent-auth-section-header';
|
|
74
|
-
h.textContent = text;
|
|
75
|
-
dropdown.appendChild(h);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function appendSep() {
|
|
79
|
-
var s = document.createElement('div');
|
|
80
|
-
s.style.cssText = 'height:1px;background:var(--color-border);margin:0.25rem 0;';
|
|
81
|
-
dropdown.appendChild(s);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function makeItem(dotClass, name, detail) {
|
|
85
|
-
var el = document.createElement('button');
|
|
86
|
-
el.className = 'agent-auth-item';
|
|
87
|
-
el.innerHTML = '<span class="agent-auth-dot ' + dotClass + '"></span><span>' + esc(name) +
|
|
88
|
-
'</span><span style="margin-left:auto;font-size:0.7rem;color:var(--color-text-secondary)">' + esc(detail) + '</span>';
|
|
89
|
-
return el;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function makeEditForm(pid) {
|
|
93
|
-
var form = document.createElement('div');
|
|
94
|
-
form.style.cssText = 'width:100%;padding:0.375rem 0.75rem;display:flex;gap:0.375rem;';
|
|
95
|
-
var input = document.createElement('input');
|
|
96
|
-
input.type = 'password'; input.placeholder = 'API key';
|
|
97
|
-
input.style.cssText = 'flex:1;min-width:0;padding:0.25rem 0.5rem;font-size:0.75rem;border:1px solid var(--color-border);border-radius:0.25rem;background:var(--color-bg-primary);color:var(--color-text-primary);outline:none;';
|
|
98
|
-
input.addEventListener('click', function(e) { e.stopPropagation(); });
|
|
99
|
-
var saveBtn = document.createElement('button');
|
|
100
|
-
saveBtn.textContent = 'Save';
|
|
101
|
-
saveBtn.style.cssText = 'padding:0.25rem 0.5rem;font-size:0.7rem;font-weight:600;background:var(--color-primary);color:white;border:none;border-radius:0.25rem;cursor:pointer;flex-shrink:0;';
|
|
102
|
-
saveBtn.addEventListener('click', function(e) {
|
|
103
|
-
e.stopPropagation();
|
|
104
|
-
var key = input.value.trim();
|
|
105
|
-
if (!key) return;
|
|
106
|
-
saveBtn.disabled = true; saveBtn.textContent = '...';
|
|
107
|
-
saveProviderKey(pid, key);
|
|
108
|
-
});
|
|
109
|
-
form.appendChild(input); form.appendChild(saveBtn);
|
|
110
|
-
setTimeout(function() { input.focus(); }, 50);
|
|
111
|
-
return form;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function toggleEdit(pid) { editingProvider = editingProvider === pid ? null : pid; renderDropdown(); }
|
|
115
|
-
|
|
116
|
-
function saveProviderKey(providerId, apiKey) {
|
|
117
|
-
fetch(BASE + '/api/auth/save-config', {
|
|
118
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
119
|
-
body: JSON.stringify({ providerId: providerId, apiKey: apiKey, defaultModel: '' })
|
|
120
|
-
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
121
|
-
if (data.success) { editingProvider = null; fetchProviderConfigs(); }
|
|
122
|
-
}).catch(function() { editingProvider = null; renderDropdown(); });
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function toggleDropdown(e) {
|
|
126
|
-
e.stopPropagation();
|
|
127
|
-
if (!dropdown.classList.contains('open')) { editingProvider = null; refresh(); }
|
|
128
|
-
dropdown.classList.toggle('open');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function closeDropdown() { dropdown.classList.remove('open'); editingProvider = null; }
|
|
132
|
-
|
|
133
|
-
var oauthPollInterval = null, oauthPollTimeout = null, oauthFallbackTimer = null;
|
|
134
|
-
|
|
135
|
-
function cleanupOAuthPolling() {
|
|
136
|
-
if (oauthPollInterval) { clearInterval(oauthPollInterval); oauthPollInterval = null; }
|
|
137
|
-
if (oauthPollTimeout) { clearTimeout(oauthPollTimeout); oauthPollTimeout = null; }
|
|
138
|
-
if (oauthFallbackTimer) { clearTimeout(oauthFallbackTimer); oauthFallbackTimer = null; }
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function showOAuthWaitingModal() {
|
|
142
|
-
removeOAuthModal();
|
|
143
|
-
var overlay = document.createElement('div');
|
|
144
|
-
overlay.id = 'oauthWaitingModal';
|
|
145
|
-
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:9999;';
|
|
146
|
-
overlay.innerHTML = '<div style="background:var(--color-bg-secondary,#1f2937);border-radius:1rem;padding:2rem;max-width:28rem;width:calc(100% - 2rem);box-shadow:0 25px 50px rgba(0,0,0,0.5);color:var(--color-text-primary,white);font-family:system-ui,sans-serif;" onclick="event.stopPropagation()">' +
|
|
147
|
-
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">' +
|
|
148
|
-
'<h2 style="font-size:1.125rem;font-weight:700;margin:0;">Google Sign-In</h2>' +
|
|
149
|
-
'<button id="oauthWaitingClose" style="background:none;border:none;color:var(--color-text-secondary,#9ca3af);font-size:1.5rem;cursor:pointer;padding:0;line-height:1;">\u00d7</button></div>' +
|
|
150
|
-
'<div id="oauthWaitingContent" style="text-align:center;padding:1.5rem 0;">' +
|
|
151
|
-
'<div style="font-size:2rem;margin-bottom:1rem;animation:pulse 2s infinite;">⏳</div>' +
|
|
152
|
-
'<p style="font-size:0.85rem;color:var(--color-text-secondary,#d1d5db);margin:0 0 0.5rem;">Waiting for Google sign-in to complete...</p>' +
|
|
153
|
-
'<p style="font-size:0.75rem;color:var(--color-text-secondary,#6b7280);margin:0;">Complete the sign-in in the tab that just opened.</p>' +
|
|
154
|
-
'<p style="font-size:0.75rem;color:var(--color-text-secondary,#6b7280);margin:0.25rem 0 0;">This dialog will close automatically when done.</p></div>' +
|
|
155
|
-
'<div id="oauthPasteFallback" style="display:none;">' +
|
|
156
|
-
'<div style="margin-bottom:1rem;padding:1rem;background:var(--color-bg-tertiary,rgba(255,255,255,0.05));border-radius:0.5rem;">' +
|
|
157
|
-
'<p style="font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin:0 0 0.5rem;">The automatic relay did not complete. This can happen when accessing the server remotely.</p>' +
|
|
158
|
-
'<p style="font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin:0;">Copy the <span style="color:white;font-weight:600;">entire URL</span> from the sign-in tab and paste it below.</p></div>' +
|
|
159
|
-
'<input type="text" id="oauthPasteInput" placeholder="http://localhost:3000/gm/oauth2callback?code=..." style="width:100%;box-sizing:border-box;padding:0.75rem 1rem;background:var(--color-bg-primary,#374151);border:1px solid var(--color-border,#4b5563);border-radius:0.5rem;color:var(--color-text-primary,white);font-size:0.8rem;font-family:monospace;outline:none;" />' +
|
|
160
|
-
'<p id="oauthPasteError" style="font-size:0.75rem;color:#ef4444;margin:0.5rem 0 0;display:none;"></p></div>' +
|
|
161
|
-
'<div style="display:flex;gap:0.75rem;margin-top:1.25rem;">' +
|
|
162
|
-
'<button id="oauthWaitingCancel" style="flex:1;padding:0.625rem;border-radius:0.5rem;border:1px solid var(--color-border,#4b5563);background:transparent;color:var(--color-text-primary,white);font-size:0.8rem;cursor:pointer;font-weight:600;">Cancel</button>' +
|
|
163
|
-
'<button id="oauthPasteSubmit" style="flex:1;padding:0.625rem;border-radius:0.5rem;border:none;background:var(--color-primary,#3b82f6);color:white;font-size:0.8rem;cursor:pointer;font-weight:600;display:none;">Complete Sign-In</button></div>' +
|
|
164
|
-
'<style>@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}</style></div>';
|
|
165
|
-
document.body.appendChild(overlay);
|
|
166
|
-
var dismiss = function() { cleanupOAuthPolling(); authRunning = false; removeOAuthModal(); };
|
|
167
|
-
document.getElementById('oauthWaitingClose').addEventListener('click', dismiss);
|
|
168
|
-
document.getElementById('oauthWaitingCancel').addEventListener('click', dismiss);
|
|
169
|
-
document.getElementById('oauthPasteSubmit').addEventListener('click', submitOAuthPasteUrl);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function showOAuthPasteFallback() {
|
|
173
|
-
var fallback = document.getElementById('oauthPasteFallback');
|
|
174
|
-
var waitContent = document.getElementById('oauthWaitingContent');
|
|
175
|
-
var submitBtn = document.getElementById('oauthPasteSubmit');
|
|
176
|
-
if (fallback) fallback.style.display = 'block';
|
|
177
|
-
if (waitContent) waitContent.style.display = 'none';
|
|
178
|
-
if (submitBtn) submitBtn.style.display = 'block';
|
|
179
|
-
var input = document.getElementById('oauthPasteInput');
|
|
180
|
-
if (input) {
|
|
181
|
-
input.addEventListener('keydown', function(e) { if (e.key === 'Enter') submitOAuthPasteUrl(); });
|
|
182
|
-
setTimeout(function() { input.focus(); }, 100);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function removeOAuthModal() {
|
|
187
|
-
var el = document.getElementById('oauthWaitingModal');
|
|
188
|
-
if (el) el.remove();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function submitOAuthPasteUrl() {
|
|
192
|
-
var input = document.getElementById('oauthPasteInput');
|
|
193
|
-
var errorEl = document.getElementById('oauthPasteError');
|
|
194
|
-
var submitBtn = document.getElementById('oauthPasteSubmit');
|
|
195
|
-
if (!input) return;
|
|
196
|
-
var url = input.value.trim();
|
|
197
|
-
if (!url) {
|
|
198
|
-
if (errorEl) { errorEl.textContent = 'Please paste the URL from the redirected page.'; errorEl.style.display = 'block'; }
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Verifying...'; }
|
|
202
|
-
if (errorEl) errorEl.style.display = 'none';
|
|
203
|
-
|
|
204
|
-
fetch(BASE + '/api/gemini-oauth/complete', {
|
|
205
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
206
|
-
body: JSON.stringify({ url: url })
|
|
207
|
-
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
208
|
-
if (data.success) {
|
|
209
|
-
cleanupOAuthPolling();
|
|
210
|
-
authRunning = false;
|
|
211
|
-
removeOAuthModal();
|
|
212
|
-
refresh();
|
|
213
|
-
} else {
|
|
214
|
-
if (errorEl) { errorEl.textContent = data.error || 'Failed to complete authentication.'; errorEl.style.display = 'block'; }
|
|
215
|
-
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Complete Sign-In'; }
|
|
216
|
-
}
|
|
217
|
-
}).catch(function(e) {
|
|
218
|
-
if (errorEl) { errorEl.textContent = e.message; errorEl.style.display = 'block'; }
|
|
219
|
-
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Complete Sign-In'; }
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function triggerAuth(agentId) {
|
|
224
|
-
if (authRunning) return;
|
|
225
|
-
fetch(BASE + '/api/agents/' + agentId + '/auth', {
|
|
226
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}'
|
|
227
|
-
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
228
|
-
if (data.ok) {
|
|
229
|
-
authRunning = true; showTerminalTab(); switchToTerminalView();
|
|
230
|
-
var term = getTerminal();
|
|
231
|
-
if (term) { term.clear(); term.writeln('\x1b[36m[authenticating ' + agentId + ']\x1b[0m\r\n'); }
|
|
232
|
-
if (data.authUrl) {
|
|
233
|
-
window.open(data.authUrl, '_blank');
|
|
234
|
-
if (agentId === 'gemini') {
|
|
235
|
-
showOAuthWaitingModal();
|
|
236
|
-
cleanupOAuthPolling();
|
|
237
|
-
oauthPollInterval = setInterval(function() {
|
|
238
|
-
fetch(BASE + '/api/gemini-oauth/status').then(function(r) { return r.json(); }).then(function(status) {
|
|
239
|
-
if (status.status === 'success') {
|
|
240
|
-
cleanupOAuthPolling();
|
|
241
|
-
authRunning = false;
|
|
242
|
-
removeOAuthModal();
|
|
243
|
-
refresh();
|
|
244
|
-
} else if (status.status === 'error') {
|
|
245
|
-
cleanupOAuthPolling();
|
|
246
|
-
authRunning = false;
|
|
247
|
-
removeOAuthModal();
|
|
248
|
-
}
|
|
249
|
-
}).catch(function() {});
|
|
250
|
-
}, 1500);
|
|
251
|
-
oauthFallbackTimer = setTimeout(function() {
|
|
252
|
-
if (authRunning) showOAuthPasteFallback();
|
|
253
|
-
}, 30000);
|
|
254
|
-
oauthPollTimeout = setTimeout(function() {
|
|
255
|
-
cleanupOAuthPolling();
|
|
256
|
-
if (authRunning) { authRunning = false; removeOAuthModal(); }
|
|
257
|
-
}, 5 * 60 * 1000);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}).catch(function() {});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function onWsMessage(e) {
|
|
265
|
-
var data = e.detail;
|
|
266
|
-
if (!data || data.conversationId !== AUTH_CONV_ID) return;
|
|
267
|
-
if (data.type === 'script_started') {
|
|
268
|
-
authRunning = true; showTerminalTab(); switchToTerminalView();
|
|
269
|
-
var term = getTerminal();
|
|
270
|
-
if (term) { term.clear(); term.writeln('\x1b[36m[authenticating ' + (data.agentId || '') + ']\x1b[0m\r\n'); }
|
|
271
|
-
} else if (data.type === 'script_output') {
|
|
272
|
-
showTerminalTab();
|
|
273
|
-
var term = getTerminal();
|
|
274
|
-
if (term) term.write(data.data);
|
|
275
|
-
} else if (data.type === 'script_stopped') {
|
|
276
|
-
authRunning = false;
|
|
277
|
-
removeOAuthModal();
|
|
278
|
-
cleanupOAuthPolling();
|
|
279
|
-
var term = getTerminal();
|
|
280
|
-
var msg = data.error ? data.error : ('exited with code ' + (data.code || 0));
|
|
281
|
-
if (term) term.writeln('\r\n\x1b[90m[auth ' + msg + ']\x1b[0m');
|
|
282
|
-
setTimeout(refresh, 1000);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function showTerminalTab() { var t = document.getElementById('terminalTabBtn'); if (t) t.style.display = ''; }
|
|
287
|
-
function switchToTerminalView() {
|
|
288
|
-
var bar = document.getElementById('viewToggleBar');
|
|
289
|
-
if (!bar) return;
|
|
290
|
-
var t = bar.querySelector('[data-view="terminal"]'); if (t) t.click();
|
|
291
|
-
}
|
|
292
|
-
function getTerminal() { return window.scriptRunner ? window.scriptRunner.getTerminal() : null; }
|
|
293
|
-
function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
|
294
|
-
|
|
295
|
-
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
296
|
-
else init();
|
|
297
|
-
window.agentAuth = { refresh: refresh };
|
|
298
|
-
})();
|
|
1
|
+
(function() {
|
|
2
|
+
var BASE = window.__BASE_URL || '';
|
|
3
|
+
var btn = document.getElementById('agentAuthBtn');
|
|
4
|
+
var dropdown = document.getElementById('agentAuthDropdown');
|
|
5
|
+
var agents = [], providers = {}, authRunning = false, editingProvider = null;
|
|
6
|
+
var AUTH_CONV_ID = '__agent_auth__';
|
|
7
|
+
|
|
8
|
+
function init() {
|
|
9
|
+
if (!btn || !dropdown) return;
|
|
10
|
+
btn.style.display = 'flex';
|
|
11
|
+
btn.addEventListener('click', toggleDropdown);
|
|
12
|
+
document.addEventListener('click', function(e) {
|
|
13
|
+
if (!btn.contains(e.target) && !dropdown.contains(e.target)) closeDropdown();
|
|
14
|
+
});
|
|
15
|
+
window.addEventListener('conversation-selected', function() { refresh(); });
|
|
16
|
+
window.addEventListener('ws-message', onWsMessage);
|
|
17
|
+
refresh();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function refresh() { fetchAuthStatus(); fetchProviderConfigs(); }
|
|
21
|
+
|
|
22
|
+
function fetchAuthStatus() {
|
|
23
|
+
fetch(BASE + '/api/agents/auth-status').then(function(r) { return r.json(); })
|
|
24
|
+
.then(function(data) { agents = data.agents || []; updateButton(); renderDropdown(); })
|
|
25
|
+
.catch(function() {});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function fetchProviderConfigs() {
|
|
29
|
+
fetch(BASE + '/api/auth/configs').then(function(r) { return r.json(); })
|
|
30
|
+
.then(function(data) { providers = data || {}; updateButton(); renderDropdown(); })
|
|
31
|
+
.catch(function() {});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function updateButton() {
|
|
35
|
+
btn.style.display = 'flex';
|
|
36
|
+
var agentOk = agents.length === 0 || agents.every(function(a) { return a.authenticated; });
|
|
37
|
+
var pkeys = Object.keys(providers);
|
|
38
|
+
var provOk = pkeys.length === 0 || pkeys.some(function(k) { return providers[k].hasKey; });
|
|
39
|
+
var anyWarn = agents.some(function(a) { return !a.authenticated; }) ||
|
|
40
|
+
pkeys.some(function(k) { return !providers[k].hasKey; });
|
|
41
|
+
btn.classList.toggle('auth-ok', agentOk && provOk && (agents.length > 0 || pkeys.length > 0));
|
|
42
|
+
btn.classList.toggle('auth-warn', anyWarn);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function renderDropdown() {
|
|
46
|
+
dropdown.innerHTML = '';
|
|
47
|
+
if (agents.length > 0) {
|
|
48
|
+
appendHeader('Agent CLI Auth');
|
|
49
|
+
agents.forEach(function(agent) {
|
|
50
|
+
var dotClass = agent.authenticated ? 'ok' : (agent.detail === 'unknown' ? 'unknown' : 'missing');
|
|
51
|
+
var item = makeItem(dotClass, agent.name, agent.detail);
|
|
52
|
+
item.addEventListener('click', function(e) { e.stopPropagation(); closeDropdown(); triggerAuth(agent.id); });
|
|
53
|
+
dropdown.appendChild(item);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
var pkeys = Object.keys(providers);
|
|
57
|
+
if (pkeys.length > 0) {
|
|
58
|
+
if (agents.length > 0) appendSep();
|
|
59
|
+
appendHeader('Provider Keys');
|
|
60
|
+
pkeys.forEach(function(pid) {
|
|
61
|
+
var p = providers[pid];
|
|
62
|
+
var item = makeItem(p.hasKey ? 'ok' : 'missing', p.name || pid, p.hasKey ? p.apiKey : 'not set');
|
|
63
|
+
item.style.flexWrap = 'wrap';
|
|
64
|
+
item.addEventListener('click', function(e) { e.stopPropagation(); toggleEdit(pid); });
|
|
65
|
+
dropdown.appendChild(item);
|
|
66
|
+
if (editingProvider === pid) dropdown.appendChild(makeEditForm(pid));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function appendHeader(text) {
|
|
72
|
+
var h = document.createElement('div');
|
|
73
|
+
h.className = 'agent-auth-section-header';
|
|
74
|
+
h.textContent = text;
|
|
75
|
+
dropdown.appendChild(h);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function appendSep() {
|
|
79
|
+
var s = document.createElement('div');
|
|
80
|
+
s.style.cssText = 'height:1px;background:var(--color-border);margin:0.25rem 0;';
|
|
81
|
+
dropdown.appendChild(s);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function makeItem(dotClass, name, detail) {
|
|
85
|
+
var el = document.createElement('button');
|
|
86
|
+
el.className = 'agent-auth-item';
|
|
87
|
+
el.innerHTML = '<span class="agent-auth-dot ' + dotClass + '"></span><span>' + esc(name) +
|
|
88
|
+
'</span><span style="margin-left:auto;font-size:0.7rem;color:var(--color-text-secondary)">' + esc(detail) + '</span>';
|
|
89
|
+
return el;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function makeEditForm(pid) {
|
|
93
|
+
var form = document.createElement('div');
|
|
94
|
+
form.style.cssText = 'width:100%;padding:0.375rem 0.75rem;display:flex;gap:0.375rem;';
|
|
95
|
+
var input = document.createElement('input');
|
|
96
|
+
input.type = 'password'; input.placeholder = 'API key';
|
|
97
|
+
input.style.cssText = 'flex:1;min-width:0;padding:0.25rem 0.5rem;font-size:0.75rem;border:1px solid var(--color-border);border-radius:0.25rem;background:var(--color-bg-primary);color:var(--color-text-primary);outline:none;';
|
|
98
|
+
input.addEventListener('click', function(e) { e.stopPropagation(); });
|
|
99
|
+
var saveBtn = document.createElement('button');
|
|
100
|
+
saveBtn.textContent = 'Save';
|
|
101
|
+
saveBtn.style.cssText = 'padding:0.25rem 0.5rem;font-size:0.7rem;font-weight:600;background:var(--color-primary);color:white;border:none;border-radius:0.25rem;cursor:pointer;flex-shrink:0;';
|
|
102
|
+
saveBtn.addEventListener('click', function(e) {
|
|
103
|
+
e.stopPropagation();
|
|
104
|
+
var key = input.value.trim();
|
|
105
|
+
if (!key) return;
|
|
106
|
+
saveBtn.disabled = true; saveBtn.textContent = '...';
|
|
107
|
+
saveProviderKey(pid, key);
|
|
108
|
+
});
|
|
109
|
+
form.appendChild(input); form.appendChild(saveBtn);
|
|
110
|
+
setTimeout(function() { input.focus(); }, 50);
|
|
111
|
+
return form;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function toggleEdit(pid) { editingProvider = editingProvider === pid ? null : pid; renderDropdown(); }
|
|
115
|
+
|
|
116
|
+
function saveProviderKey(providerId, apiKey) {
|
|
117
|
+
fetch(BASE + '/api/auth/save-config', {
|
|
118
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
119
|
+
body: JSON.stringify({ providerId: providerId, apiKey: apiKey, defaultModel: '' })
|
|
120
|
+
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
121
|
+
if (data.success) { editingProvider = null; fetchProviderConfigs(); }
|
|
122
|
+
}).catch(function() { editingProvider = null; renderDropdown(); });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function toggleDropdown(e) {
|
|
126
|
+
e.stopPropagation();
|
|
127
|
+
if (!dropdown.classList.contains('open')) { editingProvider = null; refresh(); }
|
|
128
|
+
dropdown.classList.toggle('open');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function closeDropdown() { dropdown.classList.remove('open'); editingProvider = null; }
|
|
132
|
+
|
|
133
|
+
var oauthPollInterval = null, oauthPollTimeout = null, oauthFallbackTimer = null;
|
|
134
|
+
|
|
135
|
+
function cleanupOAuthPolling() {
|
|
136
|
+
if (oauthPollInterval) { clearInterval(oauthPollInterval); oauthPollInterval = null; }
|
|
137
|
+
if (oauthPollTimeout) { clearTimeout(oauthPollTimeout); oauthPollTimeout = null; }
|
|
138
|
+
if (oauthFallbackTimer) { clearTimeout(oauthFallbackTimer); oauthFallbackTimer = null; }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function showOAuthWaitingModal() {
|
|
142
|
+
removeOAuthModal();
|
|
143
|
+
var overlay = document.createElement('div');
|
|
144
|
+
overlay.id = 'oauthWaitingModal';
|
|
145
|
+
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);display:flex;align-items:center;justify-content:center;z-index:9999;';
|
|
146
|
+
overlay.innerHTML = '<div style="background:var(--color-bg-secondary,#1f2937);border-radius:1rem;padding:2rem;max-width:28rem;width:calc(100% - 2rem);box-shadow:0 25px 50px rgba(0,0,0,0.5);color:var(--color-text-primary,white);font-family:system-ui,sans-serif;" onclick="event.stopPropagation()">' +
|
|
147
|
+
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">' +
|
|
148
|
+
'<h2 style="font-size:1.125rem;font-weight:700;margin:0;">Google Sign-In</h2>' +
|
|
149
|
+
'<button id="oauthWaitingClose" style="background:none;border:none;color:var(--color-text-secondary,#9ca3af);font-size:1.5rem;cursor:pointer;padding:0;line-height:1;">\u00d7</button></div>' +
|
|
150
|
+
'<div id="oauthWaitingContent" style="text-align:center;padding:1.5rem 0;">' +
|
|
151
|
+
'<div style="font-size:2rem;margin-bottom:1rem;animation:pulse 2s infinite;">⏳</div>' +
|
|
152
|
+
'<p style="font-size:0.85rem;color:var(--color-text-secondary,#d1d5db);margin:0 0 0.5rem;">Waiting for Google sign-in to complete...</p>' +
|
|
153
|
+
'<p style="font-size:0.75rem;color:var(--color-text-secondary,#6b7280);margin:0;">Complete the sign-in in the tab that just opened.</p>' +
|
|
154
|
+
'<p style="font-size:0.75rem;color:var(--color-text-secondary,#6b7280);margin:0.25rem 0 0;">This dialog will close automatically when done.</p></div>' +
|
|
155
|
+
'<div id="oauthPasteFallback" style="display:none;">' +
|
|
156
|
+
'<div style="margin-bottom:1rem;padding:1rem;background:var(--color-bg-tertiary,rgba(255,255,255,0.05));border-radius:0.5rem;">' +
|
|
157
|
+
'<p style="font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin:0 0 0.5rem;">The automatic relay did not complete. This can happen when accessing the server remotely.</p>' +
|
|
158
|
+
'<p style="font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin:0;">Copy the <span style="color:white;font-weight:600;">entire URL</span> from the sign-in tab and paste it below.</p></div>' +
|
|
159
|
+
'<input type="text" id="oauthPasteInput" placeholder="http://localhost:3000/gm/oauth2callback?code=..." style="width:100%;box-sizing:border-box;padding:0.75rem 1rem;background:var(--color-bg-primary,#374151);border:1px solid var(--color-border,#4b5563);border-radius:0.5rem;color:var(--color-text-primary,white);font-size:0.8rem;font-family:monospace;outline:none;" />' +
|
|
160
|
+
'<p id="oauthPasteError" style="font-size:0.75rem;color:#ef4444;margin:0.5rem 0 0;display:none;"></p></div>' +
|
|
161
|
+
'<div style="display:flex;gap:0.75rem;margin-top:1.25rem;">' +
|
|
162
|
+
'<button id="oauthWaitingCancel" style="flex:1;padding:0.625rem;border-radius:0.5rem;border:1px solid var(--color-border,#4b5563);background:transparent;color:var(--color-text-primary,white);font-size:0.8rem;cursor:pointer;font-weight:600;">Cancel</button>' +
|
|
163
|
+
'<button id="oauthPasteSubmit" style="flex:1;padding:0.625rem;border-radius:0.5rem;border:none;background:var(--color-primary,#3b82f6);color:white;font-size:0.8rem;cursor:pointer;font-weight:600;display:none;">Complete Sign-In</button></div>' +
|
|
164
|
+
'<style>@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}</style></div>';
|
|
165
|
+
document.body.appendChild(overlay);
|
|
166
|
+
var dismiss = function() { cleanupOAuthPolling(); authRunning = false; removeOAuthModal(); };
|
|
167
|
+
document.getElementById('oauthWaitingClose').addEventListener('click', dismiss);
|
|
168
|
+
document.getElementById('oauthWaitingCancel').addEventListener('click', dismiss);
|
|
169
|
+
document.getElementById('oauthPasteSubmit').addEventListener('click', submitOAuthPasteUrl);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function showOAuthPasteFallback() {
|
|
173
|
+
var fallback = document.getElementById('oauthPasteFallback');
|
|
174
|
+
var waitContent = document.getElementById('oauthWaitingContent');
|
|
175
|
+
var submitBtn = document.getElementById('oauthPasteSubmit');
|
|
176
|
+
if (fallback) fallback.style.display = 'block';
|
|
177
|
+
if (waitContent) waitContent.style.display = 'none';
|
|
178
|
+
if (submitBtn) submitBtn.style.display = 'block';
|
|
179
|
+
var input = document.getElementById('oauthPasteInput');
|
|
180
|
+
if (input) {
|
|
181
|
+
input.addEventListener('keydown', function(e) { if (e.key === 'Enter') submitOAuthPasteUrl(); });
|
|
182
|
+
setTimeout(function() { input.focus(); }, 100);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function removeOAuthModal() {
|
|
187
|
+
var el = document.getElementById('oauthWaitingModal');
|
|
188
|
+
if (el) el.remove();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function submitOAuthPasteUrl() {
|
|
192
|
+
var input = document.getElementById('oauthPasteInput');
|
|
193
|
+
var errorEl = document.getElementById('oauthPasteError');
|
|
194
|
+
var submitBtn = document.getElementById('oauthPasteSubmit');
|
|
195
|
+
if (!input) return;
|
|
196
|
+
var url = input.value.trim();
|
|
197
|
+
if (!url) {
|
|
198
|
+
if (errorEl) { errorEl.textContent = 'Please paste the URL from the redirected page.'; errorEl.style.display = 'block'; }
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (submitBtn) { submitBtn.disabled = true; submitBtn.textContent = 'Verifying...'; }
|
|
202
|
+
if (errorEl) errorEl.style.display = 'none';
|
|
203
|
+
|
|
204
|
+
fetch(BASE + '/api/gemini-oauth/complete', {
|
|
205
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
206
|
+
body: JSON.stringify({ url: url })
|
|
207
|
+
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
208
|
+
if (data.success) {
|
|
209
|
+
cleanupOAuthPolling();
|
|
210
|
+
authRunning = false;
|
|
211
|
+
removeOAuthModal();
|
|
212
|
+
refresh();
|
|
213
|
+
} else {
|
|
214
|
+
if (errorEl) { errorEl.textContent = data.error || 'Failed to complete authentication.'; errorEl.style.display = 'block'; }
|
|
215
|
+
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Complete Sign-In'; }
|
|
216
|
+
}
|
|
217
|
+
}).catch(function(e) {
|
|
218
|
+
if (errorEl) { errorEl.textContent = e.message; errorEl.style.display = 'block'; }
|
|
219
|
+
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Complete Sign-In'; }
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function triggerAuth(agentId) {
|
|
224
|
+
if (authRunning) return;
|
|
225
|
+
fetch(BASE + '/api/agents/' + agentId + '/auth', {
|
|
226
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}'
|
|
227
|
+
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
228
|
+
if (data.ok) {
|
|
229
|
+
authRunning = true; showTerminalTab(); switchToTerminalView();
|
|
230
|
+
var term = getTerminal();
|
|
231
|
+
if (term) { term.clear(); term.writeln('\x1b[36m[authenticating ' + agentId + ']\x1b[0m\r\n'); }
|
|
232
|
+
if (data.authUrl) {
|
|
233
|
+
window.open(data.authUrl, '_blank');
|
|
234
|
+
if (agentId === 'gemini') {
|
|
235
|
+
showOAuthWaitingModal();
|
|
236
|
+
cleanupOAuthPolling();
|
|
237
|
+
oauthPollInterval = setInterval(function() {
|
|
238
|
+
fetch(BASE + '/api/gemini-oauth/status').then(function(r) { return r.json(); }).then(function(status) {
|
|
239
|
+
if (status.status === 'success') {
|
|
240
|
+
cleanupOAuthPolling();
|
|
241
|
+
authRunning = false;
|
|
242
|
+
removeOAuthModal();
|
|
243
|
+
refresh();
|
|
244
|
+
} else if (status.status === 'error') {
|
|
245
|
+
cleanupOAuthPolling();
|
|
246
|
+
authRunning = false;
|
|
247
|
+
removeOAuthModal();
|
|
248
|
+
}
|
|
249
|
+
}).catch(function() {});
|
|
250
|
+
}, 1500);
|
|
251
|
+
oauthFallbackTimer = setTimeout(function() {
|
|
252
|
+
if (authRunning) showOAuthPasteFallback();
|
|
253
|
+
}, 30000);
|
|
254
|
+
oauthPollTimeout = setTimeout(function() {
|
|
255
|
+
cleanupOAuthPolling();
|
|
256
|
+
if (authRunning) { authRunning = false; removeOAuthModal(); }
|
|
257
|
+
}, 5 * 60 * 1000);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}).catch(function() {});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function onWsMessage(e) {
|
|
265
|
+
var data = e.detail;
|
|
266
|
+
if (!data || data.conversationId !== AUTH_CONV_ID) return;
|
|
267
|
+
if (data.type === 'script_started') {
|
|
268
|
+
authRunning = true; showTerminalTab(); switchToTerminalView();
|
|
269
|
+
var term = getTerminal();
|
|
270
|
+
if (term) { term.clear(); term.writeln('\x1b[36m[authenticating ' + (data.agentId || '') + ']\x1b[0m\r\n'); }
|
|
271
|
+
} else if (data.type === 'script_output') {
|
|
272
|
+
showTerminalTab();
|
|
273
|
+
var term = getTerminal();
|
|
274
|
+
if (term) term.write(data.data);
|
|
275
|
+
} else if (data.type === 'script_stopped') {
|
|
276
|
+
authRunning = false;
|
|
277
|
+
removeOAuthModal();
|
|
278
|
+
cleanupOAuthPolling();
|
|
279
|
+
var term = getTerminal();
|
|
280
|
+
var msg = data.error ? data.error : ('exited with code ' + (data.code || 0));
|
|
281
|
+
if (term) term.writeln('\r\n\x1b[90m[auth ' + msg + ']\x1b[0m');
|
|
282
|
+
setTimeout(refresh, 1000);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function showTerminalTab() { var t = document.getElementById('terminalTabBtn'); if (t) t.style.display = ''; }
|
|
287
|
+
function switchToTerminalView() {
|
|
288
|
+
var bar = document.getElementById('viewToggleBar');
|
|
289
|
+
if (!bar) return;
|
|
290
|
+
var t = bar.querySelector('[data-view="terminal"]'); if (t) t.click();
|
|
291
|
+
}
|
|
292
|
+
function getTerminal() { return window.scriptRunner ? window.scriptRunner.getTerminal() : null; }
|
|
293
|
+
function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
|
294
|
+
|
|
295
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
296
|
+
else init();
|
|
297
|
+
window.agentAuth = { refresh: refresh };
|
|
298
|
+
})();
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
class RecorderProcessor extends AudioWorkletProcessor {
|
|
2
|
-
constructor() {
|
|
3
|
-
super();
|
|
4
|
-
this._stopped = false;
|
|
5
|
-
this.port.onmessage = (e) => {
|
|
6
|
-
if (e.data === 'stop') this._stopped = true;
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
process(inputs) {
|
|
10
|
-
if (this._stopped) return false;
|
|
11
|
-
const input = inputs[0];
|
|
12
|
-
if (input && input[0] && input[0].length > 0) {
|
|
13
|
-
this.port.postMessage(new Float32Array(input[0]));
|
|
14
|
-
}
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
registerProcessor('recorder-processor', RecorderProcessor);
|
|
1
|
+
class RecorderProcessor extends AudioWorkletProcessor {
|
|
2
|
+
constructor() {
|
|
3
|
+
super();
|
|
4
|
+
this._stopped = false;
|
|
5
|
+
this.port.onmessage = (e) => {
|
|
6
|
+
if (e.data === 'stop') this._stopped = true;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
process(inputs) {
|
|
10
|
+
if (this._stopped) return false;
|
|
11
|
+
const input = inputs[0];
|
|
12
|
+
if (input && input[0] && input[0].length > 0) {
|
|
13
|
+
this.port.postMessage(new Float32Array(input[0]));
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
registerProcessor('recorder-processor', RecorderProcessor);
|