gemini-proxy-client 1.0.21 → 1.0.22

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.
@@ -191,36 +191,80 @@ export class BrowserManager {
191
191
  }
192
192
  /**
193
193
  * 检查是否已登录 Google
194
+ * 使用多重检测策略,避免误判
194
195
  */
195
196
  async checkGoogleLogin() {
196
197
  if (!this.page)
197
198
  return false;
198
199
  try {
199
- // 检查页面 URL 是否在 AI Studio 域名下且没有登录重定向
200
200
  const url = this.page.url();
201
- // 如果在 accounts.google.com,说明需要登录
202
- if (url.includes('accounts.google.com')) {
201
+ // 明确的未登录状态:在 Google 登录页面
202
+ if (url.includes('accounts.google.com/v3/signin') ||
203
+ url.includes('accounts.google.com/ServiceLogin')) {
204
+ console.log(chalk.gray(`[${new Date().toLocaleTimeString()}] 检测到登录页面: ${url.substring(0, 60)}...`));
203
205
  return false;
204
206
  }
205
- // 如果在 AI Studio,检查是否有登录相关的元素
207
+ // 如果在 AI Studio 域名下,进一步检查
206
208
  if (url.includes(GOOGLE_LOGGED_IN_INDICATOR)) {
207
- // 检查是否有用户头像或登录状态指示器
208
- const isLoggedIn = await this.page.evaluate(() => {
209
- // 检查是否有登录提示或错误
210
- const loginPrompt = document.querySelector('[data-login-required]');
211
- if (loginPrompt)
212
- return false;
213
- // 检查是否有用户相关元素
214
- const userAvatar = document.querySelector('img[alt*="Account"], img[alt*="Profile"]');
215
- return !!userAvatar || !document.body.textContent?.includes('Sign in');
209
+ const loginStatus = await this.page.evaluate(() => {
210
+ // 策略1: 检查是否有明确的登录按钮/链接
211
+ const signInButtons = document.querySelectorAll('a[href*="accounts.google.com"], button');
212
+ for (const el of signInButtons) {
213
+ const text = el.textContent?.toLowerCase() || '';
214
+ // 只有当按钮文字明确是 "sign in" 或 "登录" 时才认为未登录
215
+ if (text === 'sign in' || text === '登录' || text === 'log in') {
216
+ return { loggedIn: false, reason: 'found sign-in button' };
217
+ }
218
+ }
219
+ // 策略2: 检查页面是否有重定向到登录的 meta 标签
220
+ const metaRefresh = document.querySelector('meta[http-equiv="refresh"]');
221
+ if (metaRefresh) {
222
+ const content = metaRefresh.getAttribute('content') || '';
223
+ if (content.includes('accounts.google.com')) {
224
+ return { loggedIn: false, reason: 'meta refresh to login' };
225
+ }
226
+ }
227
+ // 策略3: 检查是否有用户菜单/头像(正向确认已登录)
228
+ const userIndicators = [
229
+ 'img[alt*="Account"]',
230
+ 'img[alt*="Profile"]',
231
+ 'img[alt*="Google Account"]',
232
+ '[aria-label*="Account"]',
233
+ '[aria-label*="Google Account"]',
234
+ '[data-ogsr-up]', // Google 用户面板
235
+ ];
236
+ for (const selector of userIndicators) {
237
+ if (document.querySelector(selector)) {
238
+ return { loggedIn: true, reason: `found: ${selector}` };
239
+ }
240
+ }
241
+ // 策略4: 如果页面正常加载了 AI Studio 内容,默认认为已登录
242
+ // 检查是否有 AI Studio 特有的元素
243
+ const studioIndicators = [
244
+ 'iframe[src*="scf.usercontent.goog"]',
245
+ 'iframe[src*="blob:"]',
246
+ '[class*="studio"]',
247
+ '[class*="gemini"]',
248
+ ];
249
+ for (const selector of studioIndicators) {
250
+ if (document.querySelector(selector)) {
251
+ return { loggedIn: true, reason: `studio content: ${selector}` };
252
+ }
253
+ }
254
+ // 默认:如果在 AI Studio 域名下且没有明确的登录提示,认为已登录
255
+ return { loggedIn: true, reason: 'default (no sign-in prompt)' };
216
256
  });
217
- return isLoggedIn;
257
+ console.log(chalk.gray(`[${new Date().toLocaleTimeString()}] 登录检测: ${loginStatus.loggedIn ? '已登录' : '未登录'} (${loginStatus.reason})`));
258
+ return loginStatus.loggedIn;
218
259
  }
260
+ // 不在 AI Studio 域名下,可能是其他页面
261
+ console.log(chalk.gray(`[${new Date().toLocaleTimeString()}] 当前页面不在 AI Studio: ${url.substring(0, 60)}...`));
219
262
  return false;
220
263
  }
221
264
  catch (error) {
222
265
  console.error('检查登录状态失败:', error);
223
- return false;
266
+ // 出错时默认认为已登录,避免误触发重新登录
267
+ return true;
224
268
  }
225
269
  }
226
270
  /**
@@ -601,11 +645,32 @@ export class BrowserManager {
601
645
  }
602
646
  /**
603
647
  * 处理登录过期
648
+ * 先以无头模式重启检查,如果确实需要登录再切换到有界面模式
604
649
  */
605
650
  async handleLoginExpired() {
651
+ const originalHeadless = this.options.headless;
606
652
  // 关闭当前浏览器
607
653
  await this.close();
608
- // 以有界面模式重新启动
654
+ // 先以无头模式重新启动,检查是否真的需要登录
655
+ console.log(chalk.yellow(`[${new Date().toLocaleTimeString()}] 检测到可能的登录问题,正在验证...`));
656
+ this.options.headless = true;
657
+ await this.launch();
658
+ await this.openBuildApp();
659
+ // 等待页面加载
660
+ await sleep(3000);
661
+ // 再次检查登录状态
662
+ const isLoggedIn = await this.checkGoogleLogin();
663
+ if (isLoggedIn) {
664
+ // 实际上已登录,是误判,继续无头模式运行
665
+ console.log(chalk.green(`[${new Date().toLocaleTimeString()}] ✅ 验证通过,登录状态正常,继续无头模式运行`));
666
+ await this.saveCookies();
667
+ await this.clickConnectButton();
668
+ await this.startKeepAlive();
669
+ return;
670
+ }
671
+ // 确实需要登录,切换到有界面模式
672
+ console.log(chalk.yellow(`[${new Date().toLocaleTimeString()}] 确认需要重新登录,切换到有界面模式...`));
673
+ await this.close();
609
674
  this.options.headless = false;
610
675
  await this.launch();
611
676
  await this.openBuildApp();
@@ -615,8 +680,14 @@ export class BrowserManager {
615
680
  console.log(chalk.green('✅ 重新登录成功!'));
616
681
  // 重新连接
617
682
  await this.clickConnectButton();
618
- // 切回无头模式 (下次启动时)
683
+ // 登录成功后,重新以无头模式启动
684
+ console.log(chalk.gray(`[${new Date().toLocaleTimeString()}] 登录完成,切换回无头模式...`));
685
+ await this.close();
619
686
  this.options.headless = true;
687
+ await this.launch();
688
+ await this.openBuildApp();
689
+ await this.clickConnectButton();
690
+ await this.startKeepAlive();
620
691
  }
621
692
  /**
622
693
  * 检查 WebSocket 连接状态
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemini-proxy-client",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "Gemini Proxy Build App 客户端 - 使用 Camoufox 自动保持连接",
5
5
  "main": "dist/index.js",
6
6
  "bin": {