@@ -265,7 +265,7 @@ async def _initialize_page_logic(browser: AsyncBrowser):
265265 launch_mode = os .environ .get ('LAUNCH_MODE' , 'debug' )
266266 logger .info (f" 检测到启动模式: { launch_mode } " )
267267 loop = asyncio .get_running_loop ()
268-
268+
269269 if launch_mode == 'headless' or launch_mode == 'virtual_headless' :
270270 auth_filename = os .environ .get ('ACTIVE_AUTH_JSON_PATH' )
271271 if auth_filename :
@@ -293,7 +293,7 @@ async def _initialize_page_logic(browser: AsyncBrowser):
293293 logger .info (" direct_debug_no_browser 模式:不加载 storage_state,不进行浏览器操作。" )
294294 else :
295295 logger .warning (f" ⚠️ 警告: 未知的启动模式 '{ launch_mode } '。不加载 storage_state。" )
296-
296+
297297 try :
298298 logger .info ("创建新的浏览器上下文..." )
299299 context_options : Dict [str , Any ] = {'viewport' : {'width' : 460 , 'height' : 800 }}
@@ -302,18 +302,18 @@ async def _initialize_page_logic(browser: AsyncBrowser):
302302 logger .info (f" (使用 storage_state='{ os .path .basename (storage_state_path_to_use )} ')" )
303303 else :
304304 logger .info (" (不使用 storage_state)" )
305-
305+
306306 # 代理设置需要从server模块中获取
307307 import server
308308 if server .PLAYWRIGHT_PROXY_SETTINGS :
309309 context_options ['proxy' ] = server .PLAYWRIGHT_PROXY_SETTINGS
310310 logger .info (f" (浏览器上下文将使用代理: { server .PLAYWRIGHT_PROXY_SETTINGS ['server' ]} )" )
311311 else :
312312 logger .info (" (浏览器上下文不使用显式代理配置)" )
313-
313+
314314 context_options ['ignore_https_errors' ] = True
315315 logger .info (" (浏览器上下文将忽略 HTTPS 错误)" )
316-
316+
317317 temp_context = await browser .new_context (** context_options )
318318
319319 # 设置网络拦截和脚本注入
@@ -325,10 +325,10 @@ async def _initialize_page_logic(browser: AsyncBrowser):
325325 target_full_url = f"{ target_url_base } prompts/new_chat"
326326 login_url_pattern = 'accounts.google.com'
327327 current_url = ""
328-
328+
329329 # 导入_handle_model_list_response - 需要延迟导入避免循环引用
330330 from .operations import _handle_model_list_response
331-
331+
332332 for p_iter in pages :
333333 try :
334334 page_url_to_check = p_iter .url
@@ -346,7 +346,7 @@ async def _initialize_page_logic(browser: AsyncBrowser):
346346 logger .warning (f" 检查页面 URL 时出现属性错误: { attr_err_url } " )
347347 except Exception as e_url_check :
348348 logger .warning (f" 检查页面 URL 时出现其他未预期错误: { e_url_check } (类型: { type (e_url_check ).__name__ } )" )
349-
349+
350350 if not found_page :
351351 logger .info (f"-> 未找到合适的现有页面,正在打开新页面并导航到 { target_full_url } ..." )
352352 found_page = await temp_context .new_page ()
@@ -374,18 +374,22 @@ async def _initialize_page_logic(browser: AsyncBrowser):
374374 logger .error (" 5. 系统资源问题: 确保系统有足够的内存和 CPU 资源。" )
375375 logger .error ("=" * 74 + "\n " )
376376 raise RuntimeError (f"导航新页面失败: { new_page_nav_err } " ) from new_page_nav_err
377-
377+
378378 if login_url_pattern in current_url :
379379 if launch_mode == 'headless' :
380380 logger .error ("无头模式下检测到重定向至登录页面,认证可能已失效。请更新认证文件。" )
381381 raise RuntimeError ("无头模式认证失败,需要更新认证文件。" )
382382 else :
383383 print (f"\n { '=' * 20 } 需要操作 { '=' * 20 } " , flush = True )
384384 login_prompt = " 检测到可能需要登录。如果浏览器显示登录页面,请在浏览器窗口中完成 Google 登录,然后在此处按 Enter 键继续..."
385- print (USER_INPUT_START_MARKER_SERVER , flush = True )
386- await loop .run_in_executor (None , input , login_prompt )
387- print (USER_INPUT_END_MARKER_SERVER , flush = True )
388- logger .info (" 用户已操作,正在检查登录状态..." )
385+ # NEW: If SUPPRESS_LOGIN_WAIT is set, skip waiting for user input.
386+ if os .environ .get ("SUPPRESS_LOGIN_WAIT" , "" ).lower () in ("1" , "true" , "yes" ):
387+ logger .info ("检测到 SUPPRESS_LOGIN_WAIT 标志,跳过等待用户输入。" )
388+ else :
389+ print (USER_INPUT_START_MARKER_SERVER , flush = True )
390+ await loop .run_in_executor (None , input , login_prompt )
391+ print (USER_INPUT_END_MARKER_SERVER , flush = True )
392+ logger .info (" 正在检查登录状态..." )
389393 try :
390394 await found_page .wait_for_url (f"**/{ AI_STUDIO_URL_PATTERN } **" , timeout = 180000 )
391395 current_url = found_page .url
@@ -394,36 +398,39 @@ async def _initialize_page_logic(browser: AsyncBrowser):
394398 raise RuntimeError ("手动登录尝试后仍在登录页面。" )
395399 logger .info (" ✅ 登录成功!请不要操作浏览器窗口,等待后续提示。" )
396400
397- # 等待模型列表响应,确认登录成功
398- await _wait_for_model_list_and_handle_auth_save (temp_context , launch_mode , loop )
401+ # 登录成功后,调用认证保存逻辑
402+ if os .environ .get ('AUTO_SAVE_AUTH' , 'false' ).lower () == 'true' :
403+ await _wait_for_model_list_and_handle_auth_save (temp_context , launch_mode , loop )
404+
399405 except Exception as wait_login_err :
400406 from .operations import save_error_snapshot
401407 await save_error_snapshot ("init_login_wait_fail" )
402408 logger .error (f"登录提示后未能检测到 AI Studio URL 或保存状态时出错: { wait_login_err } " , exc_info = True )
403409 raise RuntimeError (f"登录提示后未能检测到 AI Studio URL: { wait_login_err } " ) from wait_login_err
410+
404411 elif target_url_base not in current_url or "/prompts/" not in current_url :
405412 from .operations import save_error_snapshot
406413 await save_error_snapshot ("init_unexpected_page" )
407414 logger .error (f"初始导航后页面 URL 意外: { current_url } 。期望包含 '{ target_url_base } ' 和 '/prompts/'。" )
408415 raise RuntimeError (f"初始导航后出现意外页面: { current_url } 。" )
409-
416+
410417 logger .info (f"-> 确认当前位于 AI Studio 对话页面: { current_url } " )
411418 await found_page .bring_to_front ()
412-
419+
413420 try :
414421 input_wrapper_locator = found_page .locator ('ms-prompt-input-wrapper' )
415422 await expect_async (input_wrapper_locator ).to_be_visible (timeout = 35000 )
416423 await expect_async (found_page .locator (INPUT_SELECTOR )).to_be_visible (timeout = 10000 )
417424 logger .info ("-> ✅ 核心输入区域可见。" )
418-
425+
419426 model_name_locator = found_page .locator ('mat-select[data-test-ms-model-selector] div.model-option-content span.gmat-body-medium' )
420427 try :
421428 model_name_on_page = await model_name_locator .first .inner_text (timeout = 5000 )
422429 logger .info (f"-> 🤖 页面检测到的当前模型: { model_name_on_page } " )
423430 except PlaywrightAsyncError as e :
424431 logger .error (f"获取模型名称时出错 (model_name_locator): { e } " )
425432 raise
426-
433+
427434 result_page_instance = found_page
428435 result_page_ready = True
429436
@@ -504,6 +511,19 @@ async def _wait_for_model_list_and_handle_auth_save(temp_context, launch_mode, l
504511 except asyncio .TimeoutError :
505512 logger .warning (" ⚠️ 等待模型列表响应超时,但继续处理认证保存..." )
506513
514+ # 检查是否有预设的文件名用于保存
515+ save_auth_filename = os .environ .get ('SAVE_AUTH_FILENAME' , '' ).strip ()
516+ if save_auth_filename :
517+ logger .info (f" 检测到 SAVE_AUTH_FILENAME 环境变量: '{ save_auth_filename } '。将自动保存认证文件。" )
518+ await _handle_auth_file_save_with_filename (temp_context , save_auth_filename )
519+ return
520+
521+ # If not auto-saving, proceed with interactive prompts
522+ await _interactive_auth_save (temp_context , launch_mode , loop )
523+
524+
525+ async def _interactive_auth_save (temp_context , launch_mode , loop ):
526+ """处理认证文件保存的交互式提示"""
507527 # 检查是否启用自动确认
508528 if AUTO_CONFIRM_LOGIN :
509529 print ("\n " + "=" * 50 , flush = True )
@@ -562,7 +582,6 @@ async def _handle_auth_file_save(temp_context, loop):
562582 finally :
563583 print (USER_INPUT_END_MARKER_SERVER , flush = True )
564584
565- # 检查用户是否选择取消
566585 if chosen_auth_filename .strip ().lower () == 'cancel' :
567586 print (" 用户选择取消保存认证状态。" , flush = True )
568587 return
@@ -575,12 +594,33 @@ async def _handle_auth_file_save(temp_context, loop):
575594
576595 try :
577596 await temp_context .storage_state (path = auth_save_path )
597+ logger .info (f" 认证状态已成功保存到: { auth_save_path } " )
578598 print (f" ✅ 认证状态已成功保存到: { auth_save_path } " , flush = True )
579599 except Exception as save_state_err :
580600 logger .error (f" ❌ 保存认证状态失败: { save_state_err } " , exc_info = True )
581601 print (f" ❌ 保存认证状态失败: { save_state_err } " , flush = True )
582602
583603
604+ async def _handle_auth_file_save_with_filename (temp_context , filename : str ):
605+ """处理认证文件保存(使用提供的文件名)"""
606+ os .makedirs (SAVED_AUTH_DIR , exist_ok = True )
607+
608+ # Clean the filename and add .json if needed
609+ final_auth_filename = filename .strip ()
610+ if not final_auth_filename .endswith (".json" ):
611+ final_auth_filename += ".json"
612+
613+ auth_save_path = os .path .join (SAVED_AUTH_DIR , final_auth_filename )
614+
615+ try :
616+ await temp_context .storage_state (path = auth_save_path )
617+ print (f" ✅ 认证状态已自动保存到: { auth_save_path } " , flush = True )
618+ logger .info (f" 自动保存认证状态成功: { auth_save_path } " )
619+ except Exception as save_state_err :
620+ logger .error (f" ❌ 自动保存认证状态失败: { save_state_err } " , exc_info = True )
621+ print (f" ❌ 自动保存认证状态失败: { save_state_err } " , flush = True )
622+
623+
584624async def _handle_auth_file_save_auto (temp_context ):
585625 """处理认证文件保存(自动模式)"""
586626 os .makedirs (SAVED_AUTH_DIR , exist_ok = True )
@@ -592,8 +632,8 @@ async def _handle_auth_file_save_auto(temp_context):
592632
593633 try :
594634 await temp_context .storage_state (path = auth_save_path )
595- print (f" ✅ 认证状态已自动保存到 : { auth_save_path } " , flush = True )
596- logger . info (f" 自动保存认证状态成功 : { auth_save_path } " )
635+ logger . info (f" 认证状态已成功保存到 : { auth_save_path } " )
636+ print (f" ✅ 认证状态已成功保存到 : { auth_save_path } " , flush = True )
597637 except Exception as save_state_err :
598- logger .error (f" ❌ 自动保存认证状态失败 : { save_state_err } " , exc_info = True )
599- print (f" ❌ 自动保存认证状态失败 : { save_state_err } " , flush = True )
638+ logger .error (f" ❌ 保存认证状态失败 : { save_state_err } " , exc_info = True )
639+ print (f" ❌ 保存认证状态失败 : { save_state_err } " , flush = True )
0 commit comments