From 0b9a8b2107880a34f8991a48e7226005a146c87f Mon Sep 17 00:00:00 2001 From: chendelian <116870791@qq.com> Date: Sat, 18 Jul 2026 09:31:40 +0800 Subject: [PATCH] Release v1.0.50: web RPA profile/headed hard rules (POLICY-RPA-004) --- .env.example | 4 +- CHANGELOG.md | 5 +++ README.md | 6 ++- SKILL.md | 2 +- TUTORIAL.md | 4 +- development/ADAPTER.md | 1 + development/CONFIG.md | 8 ++-- development/DEVELOPMENT.md | 4 +- development/POLICY_MATRIX.md | 1 + development/REQUIREMENTS.md | 6 +++ development/RPA.md | 36 +++++++++++++--- development/TESTING.md | 3 +- examples/real_browser_rpa/README.md | 3 ++ examples/simulator_browser_rpa/README.md | 6 +++ scripts/util/constants.py | 2 +- tests/test_development_policy_guard.py | 54 ++++++++++++++++++++++++ tests/test_docs_standards.py | 11 +++++ 17 files changed, 136 insertions(+), 20 deletions(-) diff --git a/.env.example b/.env.example index c8c80bb..d3c2d8b 100644 --- a/.env.example +++ b/.env.example @@ -7,8 +7,8 @@ TARGET_BASE_URL=https://sandbox.jc2009.com # 填写完整网址(含 https:// # 默认登录账号:仅填写非敏感的账号标识(如工号/登录名) DEFAULT_LOGIN_ID=04110001 # 只填账号标识,不要填密码;密码请在「账号管理」中登记 -# 是否显示浏览器窗口:打开后能否看见自动化操作过程 -OPENCLAW_BROWSER_HEADLESS=0 # 0=显示窗口(推荐,便于排查),1=后台静默运行 +# 是否显示浏览器窗口:网页自动化时是否弹出可见浏览器 +OPENCLAW_BROWSER_HEADLESS=0 # 0=显示窗口(必须,便于介入与排查);1=后台静默(勿作日常用法) # 浏览器防检测:降低被网站识别为自动化工具的概率 OPENCLAW_PLAYWRIGHT_STEALTH=1 # 1=开启(推荐),0=关闭;一般保持默认 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c0e97f..d79f6c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ - 小节正文写用户能看懂的变化(新能力、修复、注意事项),避免堆砌内部实现细节 - 打 tag 前**必须**为即将发布的版本新增一节;找不到匹配小节时,本次发布不会更新 changelog 字段 +## 1.0.50 + +- 沉淀网页 RPA 硬规则:必须先经账号管理取得 `profile_dir` 再开浏览器;生产有头;站点登录按平台约定(不全局强制);新增 `POLICY-RPA-004` 自动检测 +- 用户教程 / 技能说明占位强调「先登记账号」;需求模板增加登录策略三选一(required / optional / not_needed) + ## 1.0.49 - 明确 Skill Action 心智:默认按 sync;仅长任务 / RPA 显式 async 进任务中心;匠厂宿主省略 `executionProfile` 时按 sync;新技能仍须显式声明 diff --git a/README.md b/README.md index 85469c1..54b2acc 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,11 @@ description: "用用户能理解的方式说明这个技能能做什么、适合 说明用户在使用前要准备的东西,例如: -- 相关账号已登录,且具备查询、导出或提交权限 +- **网页自动化类:** 先在「账号管理」登记本技能对应平台的账号(浏览器会话由系统隔离管理)。未登记账号不要直接跑真实网站 +- **是否要登录目标站:** 按本技能说明(有的平台必须登录,有的公开页可不登录);与「是否登记账号」不是一回事——登记账号是前置,登录目标站看平台 +- 相关业务权限(查询、导出、提交等)已具备 - 需要处理的文件、时间范围、订单号、客户名称、项目名称等信息 -- 如果涉及外部系统,请确认网络、账号、验证码或审批流程可正常使用 +- 如果涉及验证码或人工确认,请保证能及时在弹出的浏览器窗口中操作 --- diff --git a/SKILL.md b/SKILL.md index 0020521..fc7692a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: 技能开发模板(通用业务版) description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" -version: 1.0.49 +version: 1.0.50 author: 深圳匠厂科技有限公司 metadata: openclaw: diff --git a/TUTORIAL.md b/TUTORIAL.md index 1771e6d..a14c039 100644 --- a/TUTORIAL.md +++ b/TUTORIAL.md @@ -16,7 +16,9 @@ 1. 已安装并登录匠厂客户端 2. 已在技能市场安装本技能(或更新到最新版) -3. 【如需】相关账号已登录、权限可用;需要的文件 / 时间范围 / 单号等已准备好 +3. 【网页自动化类技能必做】在「账号管理」中为本技能所需平台**先登记账号**,并确认浏览器会话可用;**不要**在未配置账号时直接跑真实网站(会弄脏本机会话、加重风控) +4. 【按平台】若本技能说明「需要先登录目标站」,请按界面提示完成登录或扫码;若说明「可不登录也能用」,仍须完成上一步账号登记 +5. 【如需】需要的文件 / 时间范围 / 单号等已准备好 --- diff --git a/development/ADAPTER.md b/development/ADAPTER.md index 3129316..a0ae9ed 100644 --- a/development/ADAPTER.md +++ b/development/ADAPTER.md @@ -101,6 +101,7 @@ def get_adapter(): 3. **account-manager 账号/租约能力**是例外:可参考 `examples/real_browser_rpa/scripts/service/account_client.py` 与 `examples/simulator_browser_rpa/scripts/service/account_client.py`,封装为**单一** `account_client.py`;允许在该文件内部集中通过 subprocess 调 account-manager CLI。**禁止** import `rpa_helpers` 等 account-manager 内部模块;`simulator_rpa` 与 `real_rpa` 均走同一 `pick_web_account` / `release_lease` 模式。 4. **不允许**直接 `import account-manager` 的内部 Python 模块(如 `service/`、`util/`、`db/`)。 5. **pick lease 后必须 `finally release lease`**;进程被 kill 后可能残留 lease,需在运维文档说明排查方式(查 account-manager lease 列表 / 手动释放)。 +6. **网页 RPA 硬规则**(详见 [`RPA.md`](RPA.md) §0.2):必须先 `pick_web_account` 拿到 `profile_dir` 再开浏览器;拿不到则失败退出;生产有头;**是否站点登录**按平台约定,但**不等于**可以跳过 account-manager。 ```python # 普通兄弟技能 — 走 sibling_bridge diff --git a/development/CONFIG.md b/development/CONFIG.md index cc7c862..3f6ab80 100644 --- a/development/CONFIG.md +++ b/development/CONFIG.md @@ -50,8 +50,8 @@ # 运行模式:决定本技能用模拟数据还是真实浏览器/接口去执行 OPENCLAW_TEST_TARGET=mock # 可选:mock=本地模拟,simulator_rpa=仿真站点,real_api=真实接口,real_rpa=真实浏览器;模板默认 mock,日常正式使用请改为 real_rpa -# 是否显示浏览器窗口:打开后能否看见自动化操作过程 -OPENCLAW_BROWSER_HEADLESS=0 # 0=显示窗口(推荐,便于排查),1=后台静默运行 +# 是否显示浏览器窗口:网页自动化时是否弹出可见浏览器 +OPENCLAW_BROWSER_HEADLESS=0 # 0=显示窗口(必须,便于介入与排查);1=后台静默(勿作日常用法) ``` ### 反例(禁止) @@ -81,8 +81,8 @@ TARGET_BASE_URL=https://sandbox.jc2009.com # 填写完整网址(含 https:// # 默认登录账号:仅填写非敏感的账号标识(如工号/登录名) DEFAULT_LOGIN_ID=04110001 # 只填账号标识,不要填密码;密码请在「账号管理」中登记 -# 是否显示浏览器窗口:打开后能否看见自动化操作过程 -OPENCLAW_BROWSER_HEADLESS=0 # 0=显示窗口(推荐,便于排查),1=后台静默运行 +# 是否显示浏览器窗口:网页自动化时是否弹出可见浏览器 +OPENCLAW_BROWSER_HEADLESS=0 # 0=显示窗口(必须,便于介入与排查);1=后台静默(勿作日常用法) # 浏览器防检测:降低被网站识别为自动化工具的概率 OPENCLAW_PLAYWRIGHT_STEALTH=1 # 1=开启(推荐),0=关闭;一般保持默认 diff --git a/development/DEVELOPMENT.md b/development/DEVELOPMENT.md index 1ece340..b7f53aa 100644 --- a/development/DEVELOPMENT.md +++ b/development/DEVELOPMENT.md @@ -170,7 +170,7 @@ scripts/ 若 skill 需要浏览器/桌面/手机自动化,按以下顺序落地: -1. **先读三份标准**:`RPA.md`(三端范式与反反爬)、`CONFIG.md`(`.env` 落盘与读取)、`ADAPTER.md`(四档 adapter)。 +1. **先读三份标准**:`RPA.md`(含 §0.2:Profile 强制 / 有头 / 登录分平台)、`CONFIG.md`(`.env` 落盘与读取)、`ADAPTER.md`(四档 adapter)。 2. **从 examples 选择性复制**(不要整包照搬 `examples//`,见各 example README 的 copy map): - 若是 **真实浏览器 RPA**(真实网站、登录态、验证码、滚动采集),**必须先读** `examples/real_browser_rpa/README.md`,再按需复制到 `scripts/service/`: - `examples/real_browser_rpa/scripts/service/browser_session.py` @@ -185,6 +185,7 @@ scripts/ - `examples/simulator_browser_rpa/scripts/service/task_service.py`(async 编排) - `sandbox/demo_app.html` 仅留在 examples,不进入生产 skill - **禁止**:`rpa_helpers`、sync Playwright 用于完整技能 RPA 主路径、`task_service` 散落 account-manager subprocess + - **网页硬规则**:未拿到 `profile_dir` 禁止开浏览器;生产 `OPENCLAW_BROWSER_HEADLESS=0`;站点是否登录写进 REQUIREMENTS / 用户教程,**不要**写成「可不接 account-manager」 3. **只用共享库,不在 skill 里重写反反爬**: ```python from jiangchang_skill_core import config @@ -868,6 +869,7 @@ uses: client-jiangchang/jiangchang-platform-kit/.github/workflows/reusable-relea - [ ] 本仓库**不是** skill-template 的误复制(根目录**无** `.openclaw-skill-template`) - [ ] `git remote -v` 指向**本技能**远端,URL 不含 skill-template 仓库名 - [ ] `git log` 首条提交属于本技能(非模板历史) +- [ ] 网页 RPA:先 account-manager `profile_dir` 再开浏览器;`.env` 默认有头;REQUIREMENTS 写明登录策略(required/optional/not_needed);用户 README/TUTORIAL 强调先登记账号(`RPA.md` §0.2 / `POLICY-RPA-004`) ## 17. 常见错误 diff --git a/development/POLICY_MATRIX.md b/development/POLICY_MATRIX.md index 84376e2..1fe61a5 100644 --- a/development/POLICY_MATRIX.md +++ b/development/POLICY_MATRIX.md @@ -19,6 +19,7 @@ | POLICY-RPA-001 | 不得 import/use account-manager 内部 `rpa_helpers` / `inject_account_manager_scripts_path` / `get_account_credential` | development/ADAPTER.md §兄弟依赖;development/RPA.md §1.7 | hard | 扫描 `scripts/**/*.py` 禁止模式 | `tests/test_development_policy_guard.py::TestPolicyRpa001`(另见 `tests/test_no_rpa_helpers_import.py`) | | POLICY-RPA-002 | RPA 交付代码不得直接执行 ffmpeg 拼 MP4;应使用 `RpaVideoSession`(允许 `ffmpeg_path` 等诊断字段) | development/RPA.md §5.3 录屏成片标准 | hard | 扫描 `scripts/**/*.py` 禁止 `subprocess` / `os.system` / `os.popen` 直接调用 ffmpeg(`task_run_support.py` 保留豁免) | `tests/test_development_policy_guard.py::TestPolicyRpa002` | | POLICY-RPA-003 | 即使默认 `OPENCLAW_RECORD_VIDEO=0`,RPA / 长任务模板也必须保留录屏能力:`RpaVideoSession`、`video.add_step`、video artifact merge 到 `result_summary` | development/RPA.md §5.3;development/LOGGING.md;development/CONFIG.md | hard | 扫描 `scripts/service/*.py`(service 层整体须含上述接入) | `tests/test_development_policy_guard.py::TestPolicyRpa003` | +| POLICY-RPA-004 | 网页 RPA:`.env.example` 若声明 `OPENCLAW_BROWSER_HEADLESS` 则默认必须为 `0`;`scripts/` 若使用 `launch_persistent_*` 则必须同时存在 `pick_web_account`(先 Profile 再开浏览器);登录不全局强制,但不得跳过 account-manager | development/RPA.md §0.2;development/ADAPTER.md §兄弟依赖 | hard | 解析 `.env.example` + 扫描 `scripts/**/*.py` | `tests/test_development_policy_guard.py::TestPolicyRpa004` | | POLICY-PACKAGING-001 | `scripts/**/*.py` 单文件 < 1000 行 | development/DEVELOPMENT.md §3.4 发布打包约束;development/RUNTIME.md §发布打包约束 | hard | 行数统计 | **已有测试覆盖**:`tests/test_release_packaging_constraints.py::test_scripts_py_files_under_pyarmor_line_limit` | | POLICY-PACKAGING-002 | 文本文件 UTF-8 without BOM,无 `U+FEFF` | development/DEVELOPMENT.md §3.4;development/RUNTIME.md §编码与输出 | hard | BOM / 解码检查 | **已有测试覆盖**:`tests/test_release_packaging_constraints.py::test_text_files_are_utf8_without_bom` | | POLICY-DOCS-001 | 本矩阵存在且包含上述全部 `policy_id` | 本次规范化约定 | hard | 解析 `development/POLICY_MATRIX.md` | `tests/test_development_policy_guard.py::TestPolicyDocs001` | diff --git a/development/REQUIREMENTS.md b/development/REQUIREMENTS.md index 7b048a7..b18a19f 100644 --- a/development/REQUIREMENTS.md +++ b/development/REQUIREMENTS.md @@ -22,6 +22,11 @@ - 中文 description(一句话): - account-manager platform_key(如有): - 命名形态:标准型 / scope 型 +- **网页 RPA(如有)— 登录策略(三选一,须写明):** + - `required`:目标站必须登录才能闭环(用户须先登记账号,并在有头浏览器中完成/保持登录) + - `optional`:登录可提升能力,但不登录也能完成主路径 + - `not_needed`:公开页即可,不要求站点登录 + - 无论上列哪一种:**都必须**经 account-manager 取得 `profile_dir`(见 `development/RPA.md` §0.2);不得使用系统默认浏览器用户目录 ## 1. 文档目标 @@ -103,6 +108,7 @@ - Windows 环境下需保证 UTF-8 输出兼容 - 必须具备基本日志能力;敏感字段脱敏 - RPA 类 skill:Playwright 由宿主共享 runtime 提供;技能侧不要 `playwright install`;URL 不要放进 `launch_persistent_context` 的 `args` +- 网页 RPA:启动前必须已 pick 到 `profile_dir`;生产有头;站点登录策略见 §0(required / optional / not_needed) ## 6. 输入输出要求 diff --git a/development/RPA.md b/development/RPA.md index 3ad3794..dfc70ce 100644 --- a/development/RPA.md +++ b/development/RPA.md @@ -2,7 +2,7 @@ > 本文是团队 RPA 开发的**统一标准**。任何需要"自动操作软件界面"的 skill,都应先读这份文档,按这里的选型和范式落地,不要每个项目重新踩坑。 -我们开发的各类 skill,本质上都是在替人操作三类界面:**浏览器、桌面软件、手机软件**。三类的底层技术不同,但**工程范式相同**:保持登录态、有头运行、拟人操作、失败存证、人工兜底。 +我们开发的各类 skill,本质上都是在替人操作三类界面:**浏览器、桌面软件、手机软件**。三类的底层技术不同,但**工程范式相同**:可控会话(Profile)、有头运行、拟人操作、失败存证、人工兜底。 --- @@ -12,8 +12,8 @@ | 约定 | 说明 | |------|------| -| **保持登录态** | 复用持久化 Profile / session,避免每次重新登录触发风控;账号由 account-manager 下发,不硬编码 | -| **有头运行** | 默认有头(headless 易被识别 / 难人工介入);`OPENCLAW_BROWSER_HEADLESS=1` 仅给 CI | +| **可控会话 / Profile** | 网页版见下方 **§0.2**(必须走 account-manager 的 `profile_dir`);桌面/手机用对应持久会话,不硬编码密码 | +| **有头运行** | 生产 RPA **必须有头**(见 §0.2);禁止把无头当交付/联调常态 | | **拟人操作** | 真实事件(isTrusted=true),逐字输入、随机延迟、贝塞尔鼠标轨迹;严禁 JS 直接设值/JS 点击/JS 跳转 | | **步骤间随机等待** | 每两步操作之间 `random_delay(min,max)`,区间由 `.env` 配置(默认 1~5s) | | **人工兜底(HITL)** | 滑块 / 短信验证码 / 人脸 / U盾 / 动态口令 → **停下来轮询等人工**,超时报 `ERROR:XXX_NEED_HUMAN`,绝不自动硬闯 | @@ -44,6 +44,27 @@ async def open_login(page): 规范权威定义:[`LOGGING.md`](LOGGING.md) §2.5。金样代码:[`scripts/service/task_service.py`](../scripts/service/task_service.py)。 +### 0.2 网页版 RPA 硬规则(Profile / 有头 / 登录分平台) + +凡**打开网页、用浏览器自动化**的技能(真实站或仿真站),必须遵守(`POLICY-RPA-004`): + +| 规则 | 要求 | +|------|------| +| **Profile 强制** | 启动浏览器前必须已从 **account-manager**(经本技能 `account_client`)拿到可用的 **`profile_dir`(及租约约定)**。只用该目录做 `launch_persistent_context`。 | +| **拿不到就停** | 未配置账号、未拿到 `profile_dir`、租约失败 → **立即失败退出,禁止开浏览器**。不得静默落到系统默认 Chrome/Edge 用户目录。 | +| **有头强制** | 生产与用户侧联调:**`OPENCLAW_BROWSER_HEADLESS=0`(有头)**。禁止把无头(`=1`)当作交付或真实站联调常态(无头易风控、难人工介入,且易污染身份)。 | +| **登录不全局强制** | **是否要在目标站完成登录**由平台决定(如部分公开页可不登录也能闭环)。由做该技能的同事在 `REQUIREMENTS.md` / 用户教程中约定;**模板不强制「所有网页 RPA 必须先登录」**。 | +| **登录 ≠ Profile** | 「可不登录」**绝不等于**「可以不接 account-manager」。无论目标站要不要登录,网页操作都依赖账号管理下发的 **Profile**。 | + +**反模式(禁止):** + +- 未 `pick` 到 `profile_dir` 仍 `launch` / 仍打开页面 +- 用系统默认用户数据目录或随意空目录反复跑真实站 → **污染本机 Profile、加重风控** +- 把「本站可不登录」写成「可以不走 account-manager」 +- 真实站联调默认无头狂跑 + +**参考实现:** `examples/real_browser_rpa/`、`examples/simulator_browser_rpa/`(`pick_web_account` → RPA → `finally release_lease`)。 + --- ## 1. 浏览器(标准已成熟) @@ -55,21 +76,22 @@ async def open_login(page): | 项 | 标准 | |----|------| | 浏览器 | **优先系统 Chrome/Edge** + `launch_persistent_context`,`channel="chrome"` 或 Edge;不用内置 Chromium,**技能内不要 `playwright install`** | -| 登录态 | 持久 Profile 目录;账号、profile、lease **统一走 account-manager**(或对应兄弟技能),不硬编码密码 | +| Profile / 账号 | **必须**经 account-manager 取得 `profile_dir`(见 §0.2);密码不进 `.env`;**是否站点登录**按平台在需求/教程约定 | | CDP | **仅作诊断 / 桌面宿主类场景**;**不要**作为强风控站点的默认生产路径 | | 行为 | 模拟真实用户:真实点击、键盘、鼠标、地址栏输入;**不要**拼接搜索结果 URL、DOM 注入、`el.value=`、JS 跳转 | -| 模式 | 默认有头 `OPENCLAW_BROWSER_HEADLESS=0`;无头仅 CI | +| 模式 | **有头** `OPENCLAW_BROWSER_HEADLESS=0`(生产强制,见 §0.2) | | 反检测 | stealth 默认开 `OPENCLAW_PLAYWRIGHT_STEALTH=1`(见 1.1) | ### 1.1 Playwright 启动标准 -1. **默认有头**:`OPENCLAW_BROWSER_HEADLESS=0`(`.env.example` 默认值)。 +1. **有头**:`OPENCLAW_BROWSER_HEADLESS=0`(`.env.example` 默认;网页 RPA 生产路径禁止改成无头常态,见 §0.2)。 2. **stealth 默认开**:`OPENCLAW_PLAYWRIGHT_STEALTH=1`;通过 `add_init_script` 注入指纹淡化脚本。 3. **不要在技能里自行安装 playwright**;由宿主共享 runtime 提供。 4. **不要默认传 `--no-sandbox`**(除非特定容器环境且已评估风险)。 5. **不要默认传 `--disable-blink-features=AutomationControlled`**;platform-kit stealth 已覆盖,额外 flag 可能适得其反。 6. **可以** `ignore_default_args=["--enable-automation"]`(platform-kit `launch_persistent_browser` 已处理)。 -7. **强风控平台**:优先真实点击、键盘、鼠标、地址栏、持久 profile;**不要**直接拼接搜索结果 URL 或 DOM 注入。 +7. **强风控平台**:优先真实点击、键盘、鼠标、地址栏、account-manager 持久 profile;**不要**直接拼接搜索结果 URL 或 DOM 注入。 +8. **先 Profile 再开浏览器**:`user_data_dir` 必须来自 pick 到的 `profile_dir`;未拿到则不得进入本节后续步骤。 指纹淡化(stealth)典型项:`navigator.webdriver=undefined`、`chrome.runtime`、`permissions.query`、`plugins`、`languages` 等。共享实现见 `jiangchang_skill_core.rpa`(platform-kit **>= 1.2.0**)。 diff --git a/development/TESTING.md b/development/TESTING.md index 66461f5..a75ab46 100644 --- a/development/TESTING.md +++ b/development/TESTING.md @@ -137,7 +137,8 @@ Golden fixture 流程同理([`tests/samples/test_golden_cases.py.sample`](../t 浏览器 RPA 走 `simulator_rpa` 档位联调前,逐项确认: - [ ] 数据目录 `.env` 中 `OPENCLAW_TEST_TARGET=simulator_rpa`(非 `mock` / `unit`) -- [ ] account-manager:`platform ensure` + 账号 `active` + 有 `profile_dir` +- [ ] account-manager:`platform ensure` + 账号 `active` + 有 `profile_dir`(无 profile 禁止开浏览器,见 `RPA.md` §0.2) +- [ ] `OPENCLAW_BROWSER_HEADLESS=0`(联调有头;勿默认无头) - [ ] 目标 sandbox UI 已部署(跨团队;本地可用 `sandbox/demo_app.html`) - [ ] 失败先查 `rpa-artifacts/` 截图,再查 Chrome profile 缓存(`--user-data-dir` 手工打开清站点数据) - [ ] 默认 `python tests/run_tests.py -v` 仍全部通过(mock 离线);example 内 `pytest` 不启真实浏览器 diff --git a/examples/real_browser_rpa/README.md b/examples/real_browser_rpa/README.md index 2478f5b..cfbaebd 100644 --- a/examples/real_browser_rpa/README.md +++ b/examples/real_browser_rpa/README.md @@ -79,6 +79,9 @@ real_browser_rpa/ **浏览器与启动约束:** +- **先 Profile 再开浏览器**:必须 `pick_web_account` 拿到 `profile_dir`;拿不到则失败退出,禁止落到系统默认用户目录(见模板 `development/RPA.md` §0.2) +- **有头**:生产 / 联调 `OPENCLAW_BROWSER_HEADLESS=0`;勿把无头当交付常态 +- **登录按平台**:是否要求站点登录由本技能约定;「可不登录」≠「可不接 account-manager」 - **不要**在技能内安装 Playwright;浏览器与 Python 包由宿主/runtime 提供 - `launch_persistent_context` 的 `args` **只放 Chrome 参数**;**不要把 URL 放进 `args`** - 页面必须通过 `new_page()` + `goto()`,或通过真实地址栏/点击进入 diff --git a/examples/simulator_browser_rpa/README.md b/examples/simulator_browser_rpa/README.md index 32b9cf9..7ae6c29 100644 --- a/examples/simulator_browser_rpa/README.md +++ b/examples/simulator_browser_rpa/README.md @@ -57,6 +57,12 @@ simulator_browser_rpa/ | `task_service.py` | async 校验输入 → 选 adapter → `await submit_batch` | | `sandbox/demo_app.html` | 可控 DOM;含可选门户门闩 + 原批量提交流程 | +## 网页 RPA 硬规则(与模板 `development/RPA.md` §0.2 一致) + +- **先 Profile 再开浏览器**:`simulator_rpa` 档必须 `pick_web_account` 拿到 `profile_dir`;拿不到则失败,禁止系统默认用户目录 +- **有头**:联调 / 交付默认 `OPENCLAW_BROWSER_HEADLESS=0` +- **登录按平台约定**:本示例需要仿真登录;其他技能可为 optional / not_needed,但**仍须**走 account-manager + ## 核心流程 1. `await run_batch_submit(target, items)` 校验参数 diff --git a/scripts/util/constants.py b/scripts/util/constants.py index f64f54f..ebc62ec 100644 --- a/scripts/util/constants.py +++ b/scripts/util/constants.py @@ -1,6 +1,6 @@ """技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger)。""" SKILL_SLUG = "your-skill-slug" -SKILL_VERSION = "1.0.49" +SKILL_VERSION = "1.0.50" LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug" PLATFORM_KIT_MIN_VERSION = "1.2.0" diff --git a/tests/test_development_policy_guard.py b/tests/test_development_policy_guard.py index c74e2da..9de846f 100644 --- a/tests/test_development_policy_guard.py +++ b/tests/test_development_policy_guard.py @@ -35,6 +35,7 @@ POLICY_IDS = ( "POLICY-RPA-001", "POLICY-RPA-002", "POLICY-RPA-003", + "POLICY-RPA-004", "POLICY-PACKAGING-001", "POLICY-PACKAGING-002", "POLICY-DOCS-001", @@ -142,6 +143,18 @@ RPA_003_SOURCE = ( "development/RPA.md §5.3; development/TESTING.md §12; development/POLICY_MATRIX.md" ) +RPA_004_SOURCE = ( + "development/RPA.md §0.2; development/ADAPTER.md §兄弟依赖; development/POLICY_MATRIX.md" +) +RPA_004_HEADLESS_KEY_RE = re.compile( + r"(?m)^\s*OPENCLAW_BROWSER_HEADLESS\s*=\s*([^\s#]+)" +) +RPA_004_LAUNCH_MARKERS = ( + "launch_persistent_context", + "launch_persistent_browser", +) +RPA_004_PICK_MARKER = "pick_web_account" + LOGGING_001_SOURCE = "development/LOGGING.md; development/RUNTIME.md" LOGGING_002_SOURCE = "development/LOGGING.md; development/DEVELOPMENT.md" LOGGING_003_SOURCE = "development/LOGGING.md" @@ -615,6 +628,47 @@ class TestPolicyRpa003(unittest.TestCase): ) +class TestPolicyRpa004(unittest.TestCase): + def test_env_example_browser_headless_defaults_to_headed(self) -> None: + skill_root = get_skill_root() + env_path = os.path.join(skill_root, ".env.example") + self.assertTrue(os.path.isfile(env_path), msg=".env.example missing") + text = _read_text(env_path) + match = RPA_004_HEADLESS_KEY_RE.search(text) + if match is None: + return + value = match.group(1).strip().strip("\"'") + self.assertEqual( + value, + "0", + msg=_policy_msg( + "POLICY-RPA-004", + RPA_004_SOURCE, + f".env.example OPENCLAW_BROWSER_HEADLESS must default to 0 (headed), got {value!r}", + ), + ) + + def test_launch_persistent_requires_pick_web_account(self) -> None: + skill_root = get_skill_root() + script_texts = [ + _read_text(path) for path in _walk_files(skill_root, "scripts", suffix=".py") + ] + combined = "\n".join(script_texts) + uses_launch = any(marker in combined for marker in RPA_004_LAUNCH_MARKERS) + if not uses_launch: + return + self.assertIn( + RPA_004_PICK_MARKER, + combined, + msg=_policy_msg( + "POLICY-RPA-004", + RPA_004_SOURCE, + "scripts/ uses launch_persistent_* but missing pick_web_account " + "(must obtain profile_dir before opening browser)", + ), + ) + + def _scan_sensitive_logging_assignments(skill_root: str) -> list[str]: offenders: list[str] = [] for path in _walk_files(skill_root, "scripts", suffix=".py"): diff --git a/tests/test_docs_standards.py b/tests/test_docs_standards.py index c74bdf6..5fc19f8 100644 --- a/tests/test_docs_standards.py +++ b/tests/test_docs_standards.py @@ -238,6 +238,17 @@ class TestDocsStandards(unittest.TestCase): msg="RPA.md must mention jc2009 / industry simulator / portal gate", ) + def test_rpa_md_covers_web_profile_headed_and_login_split(self) -> None: + text = self._read("development/RPA.md") + self.assertIn("0.2", text) + self.assertIn("profile_dir", text) + self.assertIn("POLICY-RPA-004", text) + self.assertIn("有头", text) + self.assertTrue( + "登录不全局强制" in text or "登录 ≠ Profile" in text or "登录≠Profile" in text, + msg="RPA.md §0.2 must separate site-login from Profile requirement", + ) + def test_simulator_example_readme_covers_async_and_account_manager(self) -> None: text = self._read("examples/simulator_browser_rpa/README.md") self.assertIn("async", text.lower())