feat: open browser with stable CDP port for scrape attach
All checks were successful
技能自动化发布 / release (push) Successful in 11s
All checks were successful
技能自动化发布 / release (push) Successful in 11s
This commit is contained in:
@@ -3,6 +3,10 @@
|
|||||||
本文件对应技能市场 **「更新日志」** Tab。
|
本文件对应技能市场 **「更新日志」** Tab。
|
||||||
发布 CI 会按当前 tag 版本提取匹配的 `##` 小节。
|
发布 CI 会按当前 tag 版本提取匹配的 `##` 小节。
|
||||||
|
|
||||||
|
## 2.0.30
|
||||||
|
|
||||||
|
- 「打开浏览器」为本账号 Profile 附加稳定本机调试口(与科力普等采集技能约定同一端口算法),便于采集挂接已打开的原生窗口;仍使用 `--restore-last-session` 恢复登录态
|
||||||
|
|
||||||
## 2.0.29
|
## 2.0.29
|
||||||
|
|
||||||
- 「打开浏览器」有可恢复会话时改用 `--restore-last-session` 强制恢复标签与 Session Cookie;不再裸写受 Secure Preferences HMAC 保护的 `restore_on_startup`(该写法会被 Chrome 否掉并落到新标签页)
|
- 「打开浏览器」有可恢复会话时改用 `--restore-last-session` 强制恢复标签与 Session Cookie;不再裸写受 Secure Preferences HMAC 保护的 `restore_on_startup`(该写法会被 Chrome 否掉并落到新标签页)
|
||||||
|
|||||||
2
SKILL.md
2
SKILL.md
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: 账号与凭据管理
|
name: 账号与凭据管理
|
||||||
description: "通用账号 & 凭据 & 登录流程管理器。管理网页/RPA 账号、API Key/Token;支持 9 种登录策略(账密自动、扫码、2FA、SSO 等);提供加密本地存储(Fernet);提供 rpa_helpers 库供业务 skill 同进程调用 ensure_logged_in 完成多平台自动登录。涵盖物流、LLM、内容、开发、API 五类平台。"
|
description: "通用账号 & 凭据 & 登录流程管理器。管理网页/RPA 账号、API Key/Token;支持 9 种登录策略(账密自动、扫码、2FA、SSO 等);提供加密本地存储(Fernet);提供 rpa_helpers 库供业务 skill 同进程调用 ensure_logged_in 完成多平台自动登录。涵盖物流、LLM、内容、开发、API 五类平台。"
|
||||||
version: 2.0.29
|
version: 2.0.30
|
||||||
author: 深圳匠厂科技有限公司
|
author: 深圳匠厂科技有限公司
|
||||||
metadata:
|
metadata:
|
||||||
openclaw:
|
openclaw:
|
||||||
|
|||||||
@@ -91,25 +91,46 @@ HUMAN_CHROME_FLAGS = (
|
|||||||
#(该项受 Secure Preferences HMAC 保护,裸写会被 Chrome 否掉)。
|
#(该项受 Secure Preferences HMAC 保护,裸写会被 Chrome 否掉)。
|
||||||
RESTORE_LAST_SESSION_FLAG = "--restore-last-session"
|
RESTORE_LAST_SESSION_FLAG = "--restore-last-session"
|
||||||
|
|
||||||
|
# 与 scrape-product-colipu 等业务技能约定同一算法,便于 CDP 挂接已打开的账号窗口。
|
||||||
|
_CDP_PORT_BASE = 9330
|
||||||
|
_CDP_PORT_SPAN = 60
|
||||||
|
|
||||||
def human_chrome_launch_args(profile_dir, url=None, restore_session=False):
|
|
||||||
|
def cdp_port_for_profile(profile_dir: str) -> int:
|
||||||
|
"""按 profile 绝对路径派生稳定本机调试端口(9330–9389)。"""
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
key = os.path.abspath(profile_dir).replace("/", "\\").lower().encode("utf-8")
|
||||||
|
digest = hashlib.sha1(key).hexdigest()
|
||||||
|
return _CDP_PORT_BASE + (int(digest[:8], 16) % _CDP_PORT_SPAN)
|
||||||
|
|
||||||
|
|
||||||
|
def human_chrome_launch_args(profile_dir, url=None, restore_session=False, *, enable_cdp=True):
|
||||||
"""chrome.exe 之后的参数(Popen 列表形式,路径不额外加引号)。"""
|
"""chrome.exe 之后的参数(Popen 列表形式,路径不额外加引号)。"""
|
||||||
profile_abs = os.path.abspath(profile_dir)
|
profile_abs = os.path.abspath(profile_dir)
|
||||||
args = [f"--user-data-dir={profile_abs}", *HUMAN_CHROME_FLAGS]
|
args = [f"--user-data-dir={profile_abs}", *HUMAN_CHROME_FLAGS]
|
||||||
if restore_session:
|
if restore_session:
|
||||||
args.append(RESTORE_LAST_SESSION_FLAG)
|
args.append(RESTORE_LAST_SESSION_FLAG)
|
||||||
|
if enable_cdp:
|
||||||
|
port = cdp_port_for_profile(profile_abs)
|
||||||
|
args.append(f"--remote-debugging-port={port}")
|
||||||
|
args.append("--remote-debugging-address=127.0.0.1")
|
||||||
cleaned = (url or "").strip()
|
cleaned = (url or "").strip()
|
||||||
if cleaned:
|
if cleaned:
|
||||||
args.append(cleaned)
|
args.append(cleaned)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
def human_chrome_shortcut_arguments(profile_dir, url=None, restore_session=False):
|
def human_chrome_shortcut_arguments(profile_dir, url=None, restore_session=False, *, enable_cdp=True):
|
||||||
"""Windows .lnk Arguments 字符串(user-data-dir 带引号)。"""
|
"""Windows .lnk Arguments 字符串(user-data-dir 带引号)。"""
|
||||||
profile_abs = os.path.abspath(profile_dir)
|
profile_abs = os.path.abspath(profile_dir)
|
||||||
parts = [f'--user-data-dir="{profile_abs}"', *HUMAN_CHROME_FLAGS]
|
parts = [f'--user-data-dir="{profile_abs}"', *HUMAN_CHROME_FLAGS]
|
||||||
if restore_session:
|
if restore_session:
|
||||||
parts.append(RESTORE_LAST_SESSION_FLAG)
|
parts.append(RESTORE_LAST_SESSION_FLAG)
|
||||||
|
if enable_cdp:
|
||||||
|
port = cdp_port_for_profile(profile_abs)
|
||||||
|
parts.append(f"--remote-debugging-port={port}")
|
||||||
|
parts.append("--remote-debugging-address=127.0.0.1")
|
||||||
cleaned = (url or "").strip()
|
cleaned = (url or "").strip()
|
||||||
if cleaned:
|
if cleaned:
|
||||||
parts.append(cleaned)
|
parts.append(cleaned)
|
||||||
@@ -329,21 +350,23 @@ def cmd_open(account_id):
|
|||||||
# 首次/空 Profile:仍打开平台首页便于登录。
|
# 首次/空 Profile:仍打开平台首页便于登录。
|
||||||
has_session = profile_has_restorable_session(profile_abs)
|
has_session = profile_has_restorable_session(profile_abs)
|
||||||
launch_url = None if has_session else url
|
launch_url = None if has_session else url
|
||||||
|
cdp_port = cdp_port_for_profile(profile_abs)
|
||||||
cmd = [
|
cmd = [
|
||||||
browser_exe,
|
browser_exe,
|
||||||
*human_chrome_launch_args(
|
*human_chrome_launch_args(
|
||||||
profile_abs, launch_url, restore_session=has_session
|
profile_abs, launch_url, restore_session=has_session, enable_cdp=True
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
"browser_open_launch exe=%s profile_dir=%s launch_url=%s has_session=%s "
|
"browser_open_launch exe=%s profile_dir=%s launch_url=%s has_session=%s "
|
||||||
"restore_last_session=%s platform=%s",
|
"restore_last_session=%s cdp_port=%s platform=%s",
|
||||||
browser_exe,
|
browser_exe,
|
||||||
profile_abs,
|
profile_abs,
|
||||||
launch_url or "(restore)",
|
launch_url or "(restore)",
|
||||||
has_session,
|
has_session,
|
||||||
has_session,
|
has_session,
|
||||||
|
cdp_port,
|
||||||
target.get("platform_key"),
|
target.get("platform_key"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -396,6 +419,7 @@ def cmd_open(account_id):
|
|||||||
"pid": child.pid,
|
"pid": child.pid,
|
||||||
"has_session": has_session,
|
"has_session": has_session,
|
||||||
"restore_last_session": has_session,
|
"restore_last_session": has_session,
|
||||||
|
"cdp_port": cdp_port,
|
||||||
"session_restore_on_startup": prefs_result.get("restore_after"),
|
"session_restore_on_startup": prefs_result.get("restore_after"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ class TestHumanChromeLaunchArgs:
|
|||||||
for flag in HUMAN_CHROME_FLAGS:
|
for flag in HUMAN_CHROME_FLAGS:
|
||||||
assert flag in args
|
assert flag in args
|
||||||
assert "--restore-last-session" not in args
|
assert "--restore-last-session" not in args
|
||||||
|
assert any(a.startswith("--remote-debugging-port=") for a in args)
|
||||||
assert args[-1] == "https://chat.deepseek.com"
|
assert args[-1] == "https://chat.deepseek.com"
|
||||||
assert "--enable-automation" not in args
|
assert "--enable-automation" not in args
|
||||||
|
|
||||||
@@ -201,6 +202,7 @@ class TestHumanChromeLaunchArgs:
|
|||||||
|
|
||||||
args = human_chrome_launch_args(str(tmp_path / "prof"), None, restore_session=True)
|
args = human_chrome_launch_args(str(tmp_path / "prof"), None, restore_session=True)
|
||||||
assert "--restore-last-session" in args
|
assert "--restore-last-session" in args
|
||||||
|
assert any(a.startswith("--remote-debugging-port=") for a in args)
|
||||||
assert not any(a.startswith("http") for a in args)
|
assert not any(a.startswith("http") for a in args)
|
||||||
|
|
||||||
def test_shortcut_arguments_quote_profile_and_include_url(self, tmp_path):
|
def test_shortcut_arguments_quote_profile_and_include_url(self, tmp_path):
|
||||||
@@ -213,6 +215,7 @@ class TestHumanChromeLaunchArgs:
|
|||||||
assert "--start-maximized" in text
|
assert "--start-maximized" in text
|
||||||
assert "--no-first-run" in text
|
assert "--no-first-run" in text
|
||||||
assert "--restore-last-session" not in text
|
assert "--restore-last-session" not in text
|
||||||
|
assert "--remote-debugging-port=" in text
|
||||||
assert text.endswith("https://example.com")
|
assert text.endswith("https://example.com")
|
||||||
|
|
||||||
def test_shortcut_restore_session(self, tmp_path):
|
def test_shortcut_restore_session(self, tmp_path):
|
||||||
@@ -220,8 +223,16 @@ class TestHumanChromeLaunchArgs:
|
|||||||
|
|
||||||
text = human_chrome_shortcut_arguments(str(tmp_path / "p"), None, restore_session=True)
|
text = human_chrome_shortcut_arguments(str(tmp_path / "p"), None, restore_session=True)
|
||||||
assert "--restore-last-session" in text
|
assert "--restore-last-session" in text
|
||||||
|
assert "--remote-debugging-port=" in text
|
||||||
assert "http" not in text
|
assert "http" not in text
|
||||||
|
|
||||||
|
def test_cdp_port_stable_for_profile(self, tmp_path):
|
||||||
|
from service.browser_service import cdp_port_for_profile
|
||||||
|
|
||||||
|
p = str(tmp_path / "prof")
|
||||||
|
assert cdp_port_for_profile(p) == cdp_port_for_profile(p)
|
||||||
|
assert 9330 <= cdp_port_for_profile(p) < 9390
|
||||||
|
|
||||||
|
|
||||||
class TestResolveChromiumChannel:
|
class TestResolveChromiumChannel:
|
||||||
"""resolve_chromium_channel returns a channel string when browsers exist."""
|
"""resolve_chromium_channel returns a channel string when browsers exist."""
|
||||||
|
|||||||
Reference in New Issue
Block a user