agentgui 1.0.212 → 1.0.214
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/lib/claude-runner.js +11 -2
- package/package.json +1 -1
- package/server.js +106 -18
- package/static/js/agent-auth.js +47 -30
package/lib/claude-runner.js
CHANGED
|
@@ -208,7 +208,7 @@ class AgentRunner {
|
|
|
208
208
|
} = config;
|
|
209
209
|
|
|
210
210
|
const cmd = this.requiresAdapter && this.adapterCommand ? this.adapterCommand : this.command;
|
|
211
|
-
const baseArgs = this.requiresAdapter && this.adapterCommand ? this.adapterArgs :
|
|
211
|
+
const baseArgs = this.requiresAdapter && this.adapterCommand ? this.adapterArgs : this.buildArgs(prompt, config);
|
|
212
212
|
const args = [...baseArgs];
|
|
213
213
|
|
|
214
214
|
const proc = spawn(cmd, args, { cwd });
|
|
@@ -351,6 +351,15 @@ class AgentRunner {
|
|
|
351
351
|
return;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
if (message.id === promptId && message.error) {
|
|
355
|
+
originalHandler(message);
|
|
356
|
+
completed = true;
|
|
357
|
+
clearTimeout(timeoutHandle);
|
|
358
|
+
proc.kill();
|
|
359
|
+
reject(new Error(message.error.message || 'ACP prompt error'));
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
354
363
|
originalHandler(message);
|
|
355
364
|
};
|
|
356
365
|
|
|
@@ -726,7 +735,7 @@ registry.register({
|
|
|
726
735
|
protocol: 'acp',
|
|
727
736
|
supportsStdin: false,
|
|
728
737
|
supportedFeatures: ['streaming', 'resume', 'acp-protocol'],
|
|
729
|
-
buildArgs: () => ['acp'],
|
|
738
|
+
buildArgs: () => ['--experimental-acp', '--yolo'],
|
|
730
739
|
protocolHandler: acpProtocolHandler
|
|
731
740
|
});
|
|
732
741
|
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -336,6 +336,67 @@ function geminiOAuthResultPage(title, message, success) {
|
|
|
336
336
|
</div></body></html>`;
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
+
function encodeOAuthState(csrfToken, relayUrl) {
|
|
340
|
+
const payload = JSON.stringify({ t: csrfToken, r: relayUrl });
|
|
341
|
+
return Buffer.from(payload).toString('base64url');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function decodeOAuthState(stateStr) {
|
|
345
|
+
try {
|
|
346
|
+
const payload = JSON.parse(Buffer.from(stateStr, 'base64url').toString());
|
|
347
|
+
return { csrfToken: payload.t, relayUrl: payload.r };
|
|
348
|
+
} catch (_) {
|
|
349
|
+
return { csrfToken: stateStr, relayUrl: null };
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function geminiOAuthRelayPage(code, state, error) {
|
|
354
|
+
const stateData = decodeOAuthState(state || '');
|
|
355
|
+
const relayUrl = stateData.relayUrl || '';
|
|
356
|
+
const escapedCode = (code || '').replace(/['"\\]/g, '');
|
|
357
|
+
const escapedState = (state || '').replace(/['"\\]/g, '');
|
|
358
|
+
const escapedError = (error || '').replace(/['"\\]/g, '');
|
|
359
|
+
const escapedRelay = relayUrl.replace(/['"\\]/g, '');
|
|
360
|
+
return `<!DOCTYPE html><html><head><title>Completing sign-in...</title></head>
|
|
361
|
+
<body style="margin:0;display:flex;align-items:center;justify-content:center;min-height:100vh;background:#111827;font-family:system-ui,sans-serif;color:white;">
|
|
362
|
+
<div id="status" style="text-align:center;max-width:400px;padding:2rem;">
|
|
363
|
+
<div id="spinner" style="font-size:2rem;margin-bottom:1rem;">⌛</div>
|
|
364
|
+
<h1 id="title" style="font-size:1.5rem;margin-bottom:0.5rem;">Completing sign-in...</h1>
|
|
365
|
+
<p id="msg" style="color:#9ca3af;">Relaying authentication to server...</p>
|
|
366
|
+
</div>
|
|
367
|
+
<script>
|
|
368
|
+
(function() {
|
|
369
|
+
var code = '${escapedCode}';
|
|
370
|
+
var state = '${escapedState}';
|
|
371
|
+
var error = '${escapedError}';
|
|
372
|
+
var relayUrl = '${escapedRelay}';
|
|
373
|
+
function show(icon, title, msg, color) {
|
|
374
|
+
document.getElementById('spinner').textContent = icon;
|
|
375
|
+
document.getElementById('spinner').style.color = color;
|
|
376
|
+
document.getElementById('title').textContent = title;
|
|
377
|
+
document.getElementById('msg').textContent = msg;
|
|
378
|
+
}
|
|
379
|
+
if (error) { show('\\u2717', 'Authentication Failed', error, '#ef4444'); return; }
|
|
380
|
+
if (!code) { show('\\u2717', 'Authentication Failed', 'No authorization code received.', '#ef4444'); return; }
|
|
381
|
+
if (!relayUrl) { show('\\u2713', 'Authentication Successful', 'Credentials saved. You can close this tab.', '#10b981'); return; }
|
|
382
|
+
fetch(relayUrl, {
|
|
383
|
+
method: 'POST',
|
|
384
|
+
headers: { 'Content-Type': 'application/json' },
|
|
385
|
+
body: JSON.stringify({ code: code, state: state })
|
|
386
|
+
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
387
|
+
if (data.success) {
|
|
388
|
+
show('\\u2713', 'Authentication Successful', data.email ? 'Signed in as ' + data.email + '. You can close this tab.' : 'Credentials saved. You can close this tab.', '#10b981');
|
|
389
|
+
} else {
|
|
390
|
+
show('\\u2717', 'Authentication Failed', data.error || 'Unknown error', '#ef4444');
|
|
391
|
+
}
|
|
392
|
+
}).catch(function(e) {
|
|
393
|
+
show('\\u2717', 'Relay Failed', 'Could not reach server: ' + e.message + '. You may need to paste the URL manually.', '#ef4444');
|
|
394
|
+
});
|
|
395
|
+
})();
|
|
396
|
+
</script>
|
|
397
|
+
</body></html>`;
|
|
398
|
+
}
|
|
399
|
+
|
|
339
400
|
function isRemoteRequest(req) {
|
|
340
401
|
return !!(req && (req.headers['x-forwarded-for'] || req.headers['x-forwarded-host'] || req.headers['x-forwarded-proto']));
|
|
341
402
|
}
|
|
@@ -353,7 +414,10 @@ async function startGeminiOAuth(req) {
|
|
|
353
414
|
redirectUri = `http://localhost:${PORT}${BASE_URL}/oauth2callback`;
|
|
354
415
|
}
|
|
355
416
|
|
|
356
|
-
const
|
|
417
|
+
const csrfToken = crypto.randomBytes(32).toString('hex');
|
|
418
|
+
const relayUrl = req ? `${buildBaseUrl(req)}${BASE_URL}/api/gemini-oauth/relay` : null;
|
|
419
|
+
const state = encodeOAuthState(csrfToken, relayUrl);
|
|
420
|
+
|
|
357
421
|
const client = new OAuth2Client({
|
|
358
422
|
clientId: creds.clientId,
|
|
359
423
|
clientSecret: creds.clientSecret,
|
|
@@ -367,7 +431,7 @@ async function startGeminiOAuth(req) {
|
|
|
367
431
|
});
|
|
368
432
|
|
|
369
433
|
const mode = useCustomClient ? 'custom' : (remote ? 'cli-remote' : 'cli-local');
|
|
370
|
-
geminiOAuthPending = { client, redirectUri, state };
|
|
434
|
+
geminiOAuthPending = { client, redirectUri, state: csrfToken };
|
|
371
435
|
geminiOAuthState = { status: 'pending', error: null, email: null };
|
|
372
436
|
|
|
373
437
|
setTimeout(() => {
|
|
@@ -380,12 +444,13 @@ async function startGeminiOAuth(req) {
|
|
|
380
444
|
return { authUrl, mode };
|
|
381
445
|
}
|
|
382
446
|
|
|
383
|
-
async function exchangeGeminiOAuthCode(code,
|
|
447
|
+
async function exchangeGeminiOAuthCode(code, stateParam) {
|
|
384
448
|
if (!geminiOAuthPending) throw new Error('No pending OAuth flow. Please start authentication again.');
|
|
385
449
|
|
|
386
|
-
const { client, redirectUri, state:
|
|
450
|
+
const { client, redirectUri, state: expectedCsrf } = geminiOAuthPending;
|
|
451
|
+
const { csrfToken } = decodeOAuthState(stateParam);
|
|
387
452
|
|
|
388
|
-
if (
|
|
453
|
+
if (csrfToken !== expectedCsrf) {
|
|
389
454
|
geminiOAuthState = { status: 'error', error: 'State mismatch', email: null };
|
|
390
455
|
geminiOAuthPending = null;
|
|
391
456
|
throw new Error('State mismatch - possible CSRF attack.');
|
|
@@ -423,6 +488,23 @@ async function exchangeGeminiOAuthCode(code, state) {
|
|
|
423
488
|
|
|
424
489
|
async function handleGeminiOAuthCallback(req, res) {
|
|
425
490
|
const reqUrl = new URL(req.url, `http://localhost:${PORT}`);
|
|
491
|
+
const code = reqUrl.searchParams.get('code');
|
|
492
|
+
const state = reqUrl.searchParams.get('state');
|
|
493
|
+
const error = reqUrl.searchParams.get('error');
|
|
494
|
+
const errorDesc = reqUrl.searchParams.get('error_description');
|
|
495
|
+
|
|
496
|
+
if (error) {
|
|
497
|
+
const desc = errorDesc || error;
|
|
498
|
+
geminiOAuthState = { status: 'error', error: desc, email: null };
|
|
499
|
+
geminiOAuthPending = null;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const stateData = decodeOAuthState(state || '');
|
|
503
|
+
if (stateData.relayUrl) {
|
|
504
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
505
|
+
res.end(geminiOAuthRelayPage(code, state, errorDesc || error));
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
426
508
|
|
|
427
509
|
if (!geminiOAuthPending) {
|
|
428
510
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
@@ -431,20 +513,8 @@ async function handleGeminiOAuthCallback(req, res) {
|
|
|
431
513
|
}
|
|
432
514
|
|
|
433
515
|
try {
|
|
434
|
-
|
|
435
|
-
if (error) {
|
|
436
|
-
const desc = reqUrl.searchParams.get('error_description') || error;
|
|
437
|
-
geminiOAuthState = { status: 'error', error: desc, email: null };
|
|
438
|
-
geminiOAuthPending = null;
|
|
439
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
440
|
-
res.end(geminiOAuthResultPage('Authentication Failed', desc, false));
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const code = reqUrl.searchParams.get('code');
|
|
445
|
-
const state = reqUrl.searchParams.get('state');
|
|
516
|
+
if (error) throw new Error(errorDesc || error);
|
|
446
517
|
const email = await exchangeGeminiOAuthCode(code, state);
|
|
447
|
-
|
|
448
518
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
449
519
|
res.end(geminiOAuthResultPage('Authentication Successful', email ? `Signed in as ${email}` : 'Gemini CLI credentials saved.', true));
|
|
450
520
|
} catch (e) {
|
|
@@ -1165,6 +1235,24 @@ const server = http.createServer(async (req, res) => {
|
|
|
1165
1235
|
return;
|
|
1166
1236
|
}
|
|
1167
1237
|
|
|
1238
|
+
if (pathOnly === '/api/gemini-oauth/relay' && req.method === 'POST') {
|
|
1239
|
+
try {
|
|
1240
|
+
const body = await parseBody(req);
|
|
1241
|
+
const { code, state: stateParam } = body;
|
|
1242
|
+
if (!code || !stateParam) {
|
|
1243
|
+
sendJSON(req, res, 400, { error: 'Missing code or state' });
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
const email = await exchangeGeminiOAuthCode(code, stateParam);
|
|
1247
|
+
sendJSON(req, res, 200, { success: true, email });
|
|
1248
|
+
} catch (e) {
|
|
1249
|
+
geminiOAuthState = { status: 'error', error: e.message, email: null };
|
|
1250
|
+
geminiOAuthPending = null;
|
|
1251
|
+
sendJSON(req, res, 400, { error: e.message });
|
|
1252
|
+
}
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1168
1256
|
if (pathOnly === '/api/gemini-oauth/complete' && req.method === 'POST') {
|
|
1169
1257
|
try {
|
|
1170
1258
|
const body = await parseBody(req);
|
package/static/js/agent-auth.js
CHANGED
|
@@ -130,46 +130,61 @@
|
|
|
130
130
|
|
|
131
131
|
function closeDropdown() { dropdown.classList.remove('open'); editingProvider = null; }
|
|
132
132
|
|
|
133
|
-
var oauthPollInterval = null, oauthPollTimeout = null;
|
|
133
|
+
var oauthPollInterval = null, oauthPollTimeout = null, oauthFallbackTimer = null;
|
|
134
134
|
|
|
135
135
|
function cleanupOAuthPolling() {
|
|
136
136
|
if (oauthPollInterval) { clearInterval(oauthPollInterval); oauthPollInterval = null; }
|
|
137
137
|
if (oauthPollTimeout) { clearTimeout(oauthPollTimeout); oauthPollTimeout = null; }
|
|
138
|
+
if (oauthFallbackTimer) { clearTimeout(oauthFallbackTimer); oauthFallbackTimer = null; }
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
function
|
|
141
|
-
|
|
141
|
+
function showOAuthWaitingModal() {
|
|
142
|
+
removeOAuthModal();
|
|
142
143
|
var overlay = document.createElement('div');
|
|
143
|
-
overlay.id = '
|
|
144
|
+
overlay.id = 'oauthWaitingModal';
|
|
144
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;';
|
|
145
|
-
var s = function(c) { return 'font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin:0 0 ' + (c ? '0' : '0.5rem') + ';'; };
|
|
146
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
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;">
|
|
149
|
-
'<button id="
|
|
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;">' +
|
|
150
156
|
'<div style="margin-bottom:1rem;padding:1rem;background:var(--color-bg-tertiary,rgba(255,255,255,0.05));border-radius:0.5rem;">' +
|
|
151
|
-
'<p style="
|
|
152
|
-
'<p style="
|
|
153
|
-
'<p style="' + s() + '">3. After signing in, you will be redirected to a page that <span style="color:#facc15;font-weight:600;">may not load</span> (this is expected).</p>' +
|
|
154
|
-
'<p style="' + s(1) + '">4. Copy the <span style="color:white;font-weight:600;">entire URL</span> from the address bar and paste it below.</p></div>' +
|
|
155
|
-
'<label style="display:block;font-size:0.8rem;color:var(--color-text-secondary,#d1d5db);margin-bottom:0.5rem;">Paste the redirect URL here:</label>' +
|
|
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>' +
|
|
156
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;" />' +
|
|
157
|
-
'<p id="oauthPasteError" style="font-size:0.75rem;color:#ef4444;margin:0.5rem 0 0;display:none;"></p>' +
|
|
160
|
+
'<p id="oauthPasteError" style="font-size:0.75rem;color:#ef4444;margin:0.5rem 0 0;display:none;"></p></div>' +
|
|
158
161
|
'<div style="display:flex;gap:0.75rem;margin-top:1.25rem;">' +
|
|
159
|
-
'<button id="
|
|
160
|
-
'<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;">Complete Sign-In</button></div>' +
|
|
161
|
-
'<
|
|
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>';
|
|
162
165
|
document.body.appendChild(overlay);
|
|
163
|
-
var dismiss = function() { cleanupOAuthPolling(); authRunning = false;
|
|
164
|
-
document.getElementById('
|
|
165
|
-
document.getElementById('
|
|
166
|
+
var dismiss = function() { cleanupOAuthPolling(); authRunning = false; removeOAuthModal(); };
|
|
167
|
+
document.getElementById('oauthWaitingClose').addEventListener('click', dismiss);
|
|
168
|
+
document.getElementById('oauthWaitingCancel').addEventListener('click', dismiss);
|
|
166
169
|
document.getElementById('oauthPasteSubmit').addEventListener('click', submitOAuthPasteUrl);
|
|
167
|
-
document.getElementById('oauthPasteInput').addEventListener('keydown', function(e) { if (e.key === 'Enter') submitOAuthPasteUrl(); });
|
|
168
|
-
setTimeout(function() { var i = document.getElementById('oauthPasteInput'); if (i) i.focus(); }, 100);
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
function
|
|
172
|
-
var
|
|
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');
|
|
173
188
|
if (el) el.remove();
|
|
174
189
|
}
|
|
175
190
|
|
|
@@ -193,7 +208,7 @@
|
|
|
193
208
|
if (data.success) {
|
|
194
209
|
cleanupOAuthPolling();
|
|
195
210
|
authRunning = false;
|
|
196
|
-
|
|
211
|
+
removeOAuthModal();
|
|
197
212
|
refresh();
|
|
198
213
|
} else {
|
|
199
214
|
if (errorEl) { errorEl.textContent = data.error || 'Failed to complete authentication.'; errorEl.style.display = 'block'; }
|
|
@@ -217,26 +232,28 @@
|
|
|
217
232
|
if (data.authUrl) {
|
|
218
233
|
window.open(data.authUrl, '_blank');
|
|
219
234
|
if (agentId === 'gemini') {
|
|
220
|
-
|
|
221
|
-
if (needsPaste) showOAuthPasteModal();
|
|
235
|
+
showOAuthWaitingModal();
|
|
222
236
|
cleanupOAuthPolling();
|
|
223
237
|
oauthPollInterval = setInterval(function() {
|
|
224
238
|
fetch(BASE + '/api/gemini-oauth/status').then(function(r) { return r.json(); }).then(function(status) {
|
|
225
239
|
if (status.status === 'success') {
|
|
226
240
|
cleanupOAuthPolling();
|
|
227
241
|
authRunning = false;
|
|
228
|
-
|
|
242
|
+
removeOAuthModal();
|
|
229
243
|
refresh();
|
|
230
244
|
} else if (status.status === 'error') {
|
|
231
245
|
cleanupOAuthPolling();
|
|
232
246
|
authRunning = false;
|
|
233
|
-
|
|
247
|
+
removeOAuthModal();
|
|
234
248
|
}
|
|
235
249
|
}).catch(function() {});
|
|
236
250
|
}, 1500);
|
|
251
|
+
oauthFallbackTimer = setTimeout(function() {
|
|
252
|
+
if (authRunning) showOAuthPasteFallback();
|
|
253
|
+
}, 30000);
|
|
237
254
|
oauthPollTimeout = setTimeout(function() {
|
|
238
255
|
cleanupOAuthPolling();
|
|
239
|
-
if (authRunning) { authRunning = false;
|
|
256
|
+
if (authRunning) { authRunning = false; removeOAuthModal(); }
|
|
240
257
|
}, 5 * 60 * 1000);
|
|
241
258
|
}
|
|
242
259
|
}
|
|
@@ -257,7 +274,7 @@
|
|
|
257
274
|
if (term) term.write(data.data);
|
|
258
275
|
} else if (data.type === 'script_stopped') {
|
|
259
276
|
authRunning = false;
|
|
260
|
-
|
|
277
|
+
removeOAuthModal();
|
|
261
278
|
cleanupOAuthPolling();
|
|
262
279
|
var term = getTerminal();
|
|
263
280
|
var msg = data.error ? data.error : ('exited with code ' + (data.code || 0));
|