chrome-ai-bridge 1.0.10 → 1.0.12
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.
|
@@ -209,31 +209,54 @@ export async function getLoginStatus(page, provider) {
|
|
|
209
209
|
return await getGeminiStatus(page);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Default login timeout: 5 minutes (for 2FA, SMS auth, Authenticator apps)
|
|
214
|
+
*/
|
|
215
|
+
const LOGIN_TIMEOUT_MS = 300000;
|
|
212
216
|
/**
|
|
213
217
|
* Wait for login with auto-polling and backoff
|
|
214
218
|
* Returns when user logs in or timeout occurs
|
|
219
|
+
*
|
|
220
|
+
* Improvements (v0.x.x):
|
|
221
|
+
* - Extended timeout: 5 minutes (2FA/SMS/Authenticator support)
|
|
222
|
+
* - Browser brought to front for visibility
|
|
223
|
+
* - Progress updates with remaining time (every 15 seconds)
|
|
224
|
+
* - Clear success feedback on login detection
|
|
215
225
|
*/
|
|
216
|
-
export async function waitForLoginStatus(page, provider, timeoutMs =
|
|
226
|
+
export async function waitForLoginStatus(page, provider, timeoutMs = LOGIN_TIMEOUT_MS, onStatusUpdate) {
|
|
217
227
|
const log = onStatusUpdate || console.error;
|
|
218
228
|
const start = Date.now();
|
|
219
229
|
let delay = 500;
|
|
220
|
-
|
|
230
|
+
let lastProgressReport = 0;
|
|
231
|
+
// Bring browser to front so user can see login page
|
|
232
|
+
try {
|
|
233
|
+
await page.bringToFront();
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Ignore errors (page might be closed)
|
|
237
|
+
}
|
|
221
238
|
while (Date.now() - start < timeoutMs) {
|
|
222
239
|
const status = await getLoginStatus(page, provider);
|
|
223
240
|
if (status === LoginStatus.LOGGED_IN) {
|
|
224
|
-
log('✅
|
|
241
|
+
log('✅ ログイン検出!処理を続行します');
|
|
225
242
|
return status;
|
|
226
243
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
244
|
+
// Progress update every 15 seconds with remaining time (minutes:seconds format)
|
|
245
|
+
const elapsed = Date.now() - start;
|
|
246
|
+
if (elapsed - lastProgressReport >= 15000) {
|
|
247
|
+
const remainingMs = timeoutMs - elapsed;
|
|
248
|
+
const mins = Math.floor(remainingMs / 60000);
|
|
249
|
+
const secs = Math.ceil((remainingMs % 60000) / 1000);
|
|
250
|
+
const timeStr = mins > 0 ? `${mins}分${secs}秒` : `${secs}秒`;
|
|
251
|
+
log(`⏳ ログイン待機中... 残り ${timeStr}`);
|
|
252
|
+
lastProgressReport = elapsed;
|
|
230
253
|
}
|
|
231
254
|
await new Promise(r => setTimeout(r, delay));
|
|
232
255
|
// Backoff with jitter (ChatGPT recommendation: ±10-20% randomization)
|
|
233
256
|
const jitter = 0.9 + Math.random() * 0.2; // 0.9 to 1.1
|
|
234
257
|
delay = Math.min(3000, Math.floor(delay * 1.5 * jitter));
|
|
235
258
|
}
|
|
236
|
-
log('❌
|
|
259
|
+
log('❌ ログインタイムアウト(5分)');
|
|
237
260
|
return LoginStatus.NEEDS_LOGIN;
|
|
238
261
|
}
|
|
239
262
|
/**
|
|
@@ -281,14 +281,13 @@ export const askChatGPTWeb = defineTool({
|
|
|
281
281
|
// Step 3: Check login status
|
|
282
282
|
const loginStatus = await getLoginStatus(page, 'chatgpt');
|
|
283
283
|
if (loginStatus === LoginStatus.NEEDS_LOGIN) {
|
|
284
|
-
response.appendResponseLine('\n
|
|
284
|
+
response.appendResponseLine('\n🔐 ログインが必要です');
|
|
285
|
+
response.appendResponseLine('📱 ブラウザウィンドウを開きました。ログインしてください');
|
|
286
|
+
response.appendResponseLine('⏳ ログイン完了を自動検出します(最大5分待機)');
|
|
287
|
+
response.appendResponseLine('💡 二段階認証もゆっくり対応できます');
|
|
285
288
|
response.appendResponseLine('');
|
|
286
|
-
|
|
287
|
-
response.appendResponseLine(
|
|
288
|
-
response.appendResponseLine(' 2. メールアドレスまたはGoogleアカウントでログイン');
|
|
289
|
-
response.appendResponseLine('');
|
|
290
|
-
// Auto-poll for login completion (max 2 minutes)
|
|
291
|
-
const finalStatus = await waitForLoginStatus(page, 'chatgpt', 120000, msg => response.appendResponseLine(msg));
|
|
289
|
+
// Auto-poll for login completion (max 5 minutes for 2FA support)
|
|
290
|
+
const finalStatus = await waitForLoginStatus(page, 'chatgpt', 300000, msg => response.appendResponseLine(msg));
|
|
292
291
|
if (finalStatus !== LoginStatus.LOGGED_IN) {
|
|
293
292
|
response.appendResponseLine('❌ ログインがタイムアウトしました。再度お試しください。');
|
|
294
293
|
return;
|
|
@@ -136,11 +136,14 @@ export const askGeminiImage = defineTool({
|
|
|
136
136
|
// Check login
|
|
137
137
|
const loginStatus = await getLoginStatus(page, 'gemini');
|
|
138
138
|
if (loginStatus === LoginStatus.NEEDS_LOGIN) {
|
|
139
|
-
response.appendResponseLine('\n
|
|
140
|
-
response.appendResponseLine('📱
|
|
141
|
-
|
|
139
|
+
response.appendResponseLine('\n🔐 ログインが必要です');
|
|
140
|
+
response.appendResponseLine('📱 ブラウザウィンドウを開きました。Googleアカウントでログインしてください');
|
|
141
|
+
response.appendResponseLine('⏳ ログイン完了を自動検出します(最大5分待機)');
|
|
142
|
+
response.appendResponseLine('💡 二段階認証もゆっくり対応できます');
|
|
143
|
+
// Auto-poll for login completion (max 5 minutes for 2FA support)
|
|
144
|
+
const finalStatus = await waitForLoginStatus(page, 'gemini', 300000, msg => response.appendResponseLine(msg));
|
|
142
145
|
if (finalStatus !== LoginStatus.LOGGED_IN) {
|
|
143
|
-
response.appendResponseLine('❌
|
|
146
|
+
response.appendResponseLine('❌ ログインタイムアウト(5分)');
|
|
144
147
|
return;
|
|
145
148
|
}
|
|
146
149
|
}
|
|
@@ -282,6 +285,9 @@ export const askGeminiImage = defineTool({
|
|
|
282
285
|
downloadManager.on('started', (filename) => {
|
|
283
286
|
response.appendResponseLine(`📥 ダウンロード開始: ${filename}`);
|
|
284
287
|
});
|
|
288
|
+
// Get existing Gemini images BEFORE clicking download button
|
|
289
|
+
const existingFiles = await fs.promises.readdir(userDownloadsDir);
|
|
290
|
+
const existingGeminiImages = new Set(existingFiles.filter(f => f.startsWith('Gemini_Generated_Image_') && f.endsWith('.png')));
|
|
285
291
|
// Click download button - Gemini uses "フルサイズの画像をダウンロード" button
|
|
286
292
|
// Improved selector: prioritize aria-describedby for more reliable detection
|
|
287
293
|
const downloadClicked = await page.evaluate(() => {
|
|
@@ -325,25 +331,41 @@ export const askGeminiImage = defineTool({
|
|
|
325
331
|
response.appendResponseLine('ヒント: ブラウザで画像を右クリックして保存してください');
|
|
326
332
|
return;
|
|
327
333
|
}
|
|
328
|
-
// Wait for download to complete using
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
334
|
+
// Wait for download to complete using hybrid approach:
|
|
335
|
+
// 1. Try CDP events first (reliable for standard downloads)
|
|
336
|
+
// 2. Fall back to filesystem monitoring (for blob/JS downloads like Gemini)
|
|
337
|
+
response.appendResponseLine('⏳ ダウンロード完了を待機中...');
|
|
338
|
+
let downloadedPath = null;
|
|
339
|
+
const downloadStartTime = Date.now();
|
|
340
|
+
const downloadTimeout = 60000; // 60 seconds
|
|
341
|
+
// Try CDP-based detection with filesystem fallback
|
|
342
|
+
while (Date.now() - downloadStartTime < downloadTimeout) {
|
|
343
|
+
// Check for new Gemini image files (filesystem fallback)
|
|
344
|
+
const currentFiles = await fs.promises.readdir(userDownloadsDir);
|
|
345
|
+
const newGeminiImages = currentFiles.filter(f => f.startsWith('Gemini_Generated_Image_') &&
|
|
346
|
+
f.endsWith('.png') &&
|
|
347
|
+
!existingGeminiImages.has(f));
|
|
348
|
+
if (newGeminiImages.length > 0) {
|
|
349
|
+
// Found new image file
|
|
350
|
+
const newestImage = newGeminiImages.sort().pop();
|
|
351
|
+
downloadedPath = path.join(userDownloadsDir, newestImage);
|
|
352
|
+
// Wait a bit for file to be fully written
|
|
353
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
354
|
+
response.appendResponseLine(`✅ ダウンロード完了: ${newestImage}`);
|
|
355
|
+
break;
|
|
340
356
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
357
|
+
// Also check CDP events (for standard downloads)
|
|
358
|
+
const completedDownloads = Array.from(downloadManager.getPendingDownloads()).filter((d) => d.state === 'completed');
|
|
359
|
+
if (completedDownloads.length > 0) {
|
|
360
|
+
// CDP detected completion - but Gemini uses blob downloads so this rarely fires
|
|
361
|
+
break;
|
|
346
362
|
}
|
|
363
|
+
// Short wait before next check
|
|
364
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
365
|
+
}
|
|
366
|
+
if (!downloadedPath) {
|
|
367
|
+
response.appendResponseLine('❌ ダウンロードタイムアウト (60秒)');
|
|
368
|
+
response.appendResponseLine('💡 ヒント: ブラウザで画像を右クリックして「画像を保存」してください');
|
|
347
369
|
return;
|
|
348
370
|
}
|
|
349
371
|
// Ensure output directory exists
|
|
@@ -244,14 +244,13 @@ export const askGeminiWeb = defineTool({
|
|
|
244
244
|
// Check login using ARIA-based detection (multi-language support)
|
|
245
245
|
const loginStatus = await getLoginStatus(page, 'gemini');
|
|
246
246
|
if (loginStatus === LoginStatus.NEEDS_LOGIN) {
|
|
247
|
-
response.appendResponseLine('\n
|
|
247
|
+
response.appendResponseLine('\n🔐 ログインが必要です');
|
|
248
|
+
response.appendResponseLine('📱 ブラウザウィンドウを開きました。Googleアカウントでログインしてください');
|
|
249
|
+
response.appendResponseLine('⏳ ログイン完了を自動検出します(最大5分待機)');
|
|
250
|
+
response.appendResponseLine('💡 二段階認証もゆっくり対応できます');
|
|
248
251
|
response.appendResponseLine('');
|
|
249
|
-
|
|
250
|
-
response.appendResponseLine(
|
|
251
|
-
response.appendResponseLine(' 2. パスワードを入力してログイン');
|
|
252
|
-
response.appendResponseLine('');
|
|
253
|
-
// Auto-poll for login completion (max 2 minutes)
|
|
254
|
-
const finalStatus = await waitForLoginStatus(page, 'gemini', 120000, msg => response.appendResponseLine(msg));
|
|
252
|
+
// Auto-poll for login completion (max 5 minutes for 2FA support)
|
|
253
|
+
const finalStatus = await waitForLoginStatus(page, 'gemini', 300000, msg => response.appendResponseLine(msg));
|
|
255
254
|
if (finalStatus !== LoginStatus.LOGGED_IN) {
|
|
256
255
|
response.appendResponseLine('❌ ログインがタイムアウトしました。再度お試しください。');
|
|
257
256
|
return;
|
package/package.json
CHANGED