chore: auto release commit (2026-04-06 16:44:58)
All checks were successful
技能自动化发布 / release (push) Successful in 39s

This commit is contained in:
2026-04-06 16:44:59 +08:00
parent d938885a49
commit c83841cf90
12 changed files with 486 additions and 198 deletions

View File

@@ -1,12 +1,33 @@
"""Playwright 登录检测子进程:由 browser_service.cmd_login 以独立解释器启动argv[1] 为 JSON 配置路径。"""
import json
import logging
import os
import sys
import time
from urllib.parse import urlparse
_scripts_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _scripts_dir not in sys.path:
sys.path.insert(0, _scripts_dir)
from jiangchang_skill_core.unified_logging import attach_unified_file_handler
from playwright.sync_api import sync_playwright
try:
from playwright.sync_api import Error as PlaywrightError
except ImportError:
PlaywrightError = Exception # type: ignore[misc,assignment]
def _is_context_closed_error(ex: BaseException) -> bool:
s = str(ex).lower()
if "target closed" in s or "browser has been closed" in s or "context was destroyed" in s:
return True
if isinstance(ex, PlaywrightError) and (
"closed" in s or "destroyed" in s
):
return True
return False
def page_location_href(p):
# Prefer location.href over page.url for SPA (e.g. Sohu shell path lag).
@@ -117,21 +138,16 @@ def main():
t0 = time.time()
deadline = t0 + float(c["timeout_sec"])
interactive_ok = False
end_reason = "timeout"
result_path = c.get("result_path") or ""
log_path = (c.get("log_file") or "").strip()
lg = logging.getLogger("openclaw.skill.account_manager.login_child")
lg = None
if log_path:
lg.handlers.clear()
lg.setLevel(logging.DEBUG)
_fh = logging.FileHandler(log_path, encoding="utf-8")
_fh.setFormatter(
logging.Formatter(
"%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
)
lg = attach_unified_file_handler(
log_path,
skill_slug="account-manager",
logger_name="openclaw.skill.account_manager.login_child",
)
lg.addHandler(_fh)
lg.propagate = False
last_eval_detail = ""
with sync_playwright() as p:
ctx = p.chromium.launch_persistent_context(
@@ -146,7 +162,7 @@ def main():
page.goto(c["url"], wait_until="domcontentloaded", timeout=60000)
if dom_grace > 0:
time.sleep(dom_grace)
if lg.handlers:
if lg is not None:
lg.info(
"goto_done initial_pages=%s poll_interval_sec=%s dom_grace_sec=%s",
len(ctx.pages or []),
@@ -159,7 +175,7 @@ def main():
still_logged_out, detail = evaluate_logged_out_dom(ctx, page, bundle)
ok = not still_logged_out
last_eval_detail = detail
if lg.handlers:
if lg is not None:
parts = []
for tab in list(ctx.pages or []):
try:
@@ -181,16 +197,22 @@ def main():
)
if ok:
interactive_ok = True
if lg.handlers:
end_reason = "login_ok"
if lg is not None:
lg.info("login_detected_ok %s", detail)
break
except Exception as ex:
if lg.handlers:
if _is_context_closed_error(ex):
end_reason = "user_closed"
if lg is not None:
lg.warning("login_poll_browser_closed err=%s", ex)
break
if lg is not None:
lg.warning("poll_exception err=%s", ex, exc_info=True)
spent = time.time() - iter_start
time.sleep(max(0.0, poll - spent))
if not interactive_ok:
if lg.handlers:
if not interactive_ok and end_reason != "user_closed":
if lg is not None:
lg.warning("login_poll_exhausted detail=%s", last_eval_detail)
rem = max(0.0, deadline - time.time())
if rem > 0:
@@ -199,18 +221,38 @@ def main():
except Exception:
pass
else:
if lg.handlers:
lg.info("login_success_closing_browser_immediately")
if lg is not None:
lg.info("login_dom_ok_preparing_to_release_profile_dir")
if interactive_ok:
# 给 Cookie/会话落盘一点时间;随后必须关闭上下文,否则 llm-manager 无法再对同一 profile 启动持久化浏览器
print(
"INFO:LOGIN_DOM_OK 页面检测已判定登录成功;约 2 秒后关闭本窗口以释放用户目录,供后续大模型网页会话使用。",
flush=True,
)
time.sleep(2.0)
except Exception as e:
if lg.handlers:
lg.exception("login_runner_fatal err=%s", e)
print(e, file=sys.stderr)
raise
if _is_context_closed_error(e):
end_reason = "user_closed"
interactive_ok = False
if lg is not None:
lg.warning("login_runner_browser_closed err=%s", e)
else:
if lg is not None:
lg.exception("login_runner_fatal err=%s", e)
print(e, file=sys.stderr)
raise
finally:
if result_path:
try:
with open(result_path, "w", encoding="utf-8") as rf:
json.dump({"interactive_ok": interactive_ok}, rf, ensure_ascii=False)
json.dump(
{
"interactive_ok": interactive_ok,
"end_reason": end_reason,
},
rf,
ensure_ascii=False,
)
except Exception:
pass
try: