diff --git a/.openclaw-skill-template b/.openclaw-skill-template new file mode 100644 index 0000000..5464d21 --- /dev/null +++ b/.openclaw-skill-template @@ -0,0 +1,5 @@ +# 此文件仅存在于 skill-template 源仓库。 +# 用 tools/scaffold_skill 创建新技能时,脚本会从目标目录删除本文件。 +# 若业务技能仓库中仍存在本文件,说明未正确 scaffold,tests 会失败。 +template_slug: your-skill-slug +template_version: "1.0.32" diff --git a/SKILL.md b/SKILL.md index 7783100..c7c2ffb 100644 --- a/SKILL.md +++ b/SKILL.md @@ -79,11 +79,13 @@ python {baseDir}/scripts/main.py version ## 定制入口 -1. 复制目录为你的新 skill 仓库,全局替换 `your-skill-slug` 等占位词。 -2. 按 `development/DEVELOPMENT.md` 完成开发与文档定制。 -3. 用户市场说明写入根 `README.md`;Agent 调用契约见 `references/CLI.md`、`references/SCHEMA.md`。 -4. 同步修改 `scripts/util/constants.py` 中的 `SKILL_SLUG` / `SKILL_VERSION`。 -5. 本地 SQLite 的中文表名/字段名、标准时间字段与字段顺序规范见 `references/SCHEMA.md`(元数据表 `_jiangchang_*` + `CREATE TABLE` 物理顺序)。 +1. 从本模板创建新业务技能时,**不得保留模板 `.git`**;优先使用 [`tools/scaffold_skill.ps1`](tools/scaffold_skill.ps1)(见 [`tools/README.md`](tools/README.md))。 +2. 业务技能仓库中**不得**存在 `.openclaw-skill-template`(仅 template 源仓库保留)。 +3. 复制目录为你的新 skill 仓库,全局替换 `your-skill-slug` 等占位词。 +4. 按 `development/DEVELOPMENT.md` 完成开发与文档定制。 +5. 用户市场说明写入根 `README.md`;Agent 调用契约见 `references/CLI.md`、`references/SCHEMA.md`。 +6. 同步修改 `scripts/util/constants.py` 中的 `SKILL_SLUG` / `SKILL_VERSION`。 +7. 本地 SQLite 的中文表名/字段名、标准时间字段与字段顺序规范见 `references/SCHEMA.md`(元数据表 `_jiangchang_*` + `CREATE TABLE` 物理顺序)。 ## 技能命名与 slug diff --git a/development/ADAPTER.md b/development/ADAPTER.md index 1cb66a2..3129316 100644 --- a/development/ADAPTER.md +++ b/development/ADAPTER.md @@ -37,17 +37,22 @@ scripts/service/_adapter/ base.py # 数据契约(dataclass)+ AdapterBase 接口 mock.py # 离线仿真,给 CI/单测 real_api.py # 真实系统 API - sim_rpa.py # 仿真平台 RPA + sim_rpa.py # 薄仿真平台 RPA adapter(委托 *_playwright.py) real_rpa.py # 真实系统 RPA(占位,谨慎实现) + +scripts/service/ + _playwright.py # RPA 主流程(厚);与薄 sim_rpa.py 并列 + account_client.py # account-manager subprocess 唯一封装 + browser_session.py # async persistent context 启动 ``` 模板示例(均在 `examples/`,**不在** `scripts/`): -- **仿真浏览器 RPA 完整案例**:`examples/simulator_browser_rpa/` — mock + simulator_rpa + sandbox 页面 + adapter 分层 + 测试 -- **真实浏览器 RPA 完整案例**:`examples/real_browser_rpa/` — 登录/验证码/滚动采集 +- **仿真浏览器 RPA 完整案例**:`examples/simulator_browser_rpa/` — async mock + simulator_rpa + `simulator_playwright.py` + sandbox 页面 + adapter 分层 + 测试 +- **真实浏览器 RPA 完整案例**:`examples/real_browser_rpa/` — 登录/验证码/滚动采集 + `account_client.py` - **真实 API / 仿真 API**:`examples/real_api/`、`examples/simulator_api/` — 当前为规划占位,尚未沉淀可复制实现 -复制 adapter 分层时,以 `examples/simulator_browser_rpa/scripts/service/adapter/` 为权威参考,在新技能中创建 `scripts/service/_adapter/` 并按 README copy map 选择性复制。 +复制 adapter 分层时,以修正后的 `examples/simulator_browser_rpa/`(薄 adapter + `simulator_playwright.py`)与 `examples/real_browser_rpa/scripts/service/account_client.py` 为权威参考,在新技能中创建 `scripts/service/_adapter/` 并按 README copy map 选择性复制。 > 先判断属于四象限哪一种(`real_browser_rpa` / `real_api` / `simulator_browser_rpa` / `simulator_api`),再读对应 `examples/*/README.md`。示例是**参考架构与边界**,不是业务代码原样复制。 @@ -93,7 +98,7 @@ def get_adapter(): 1. 在 `SKILL.md` 的 `metadata.openclaw.dependencies.required` 声明。 2. **普通兄弟技能调用**,优先走统一 **`service.sibling_bridge`**(`call_sibling_json`),**不要**在 `task_service.py`、`task_rpa.py` 等业务流程文件中到处散落 `subprocess.run`。 -3. **account-manager 账号/租约能力**是例外:可参考 `examples/real_browser_rpa/scripts/service/account_client.py`,封装为**单一** `account_client.py`;允许在该文件内部集中通过 subprocess 调 account-manager CLI。 +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 列表 / 手动释放)。 diff --git a/development/CONFIG.md b/development/CONFIG.md index 745ad22..61bc5e0 100644 --- a/development/CONFIG.md +++ b/development/CONFIG.md @@ -23,6 +23,7 @@ OPENCLAW_TEST_TARGET=mock # mock | simulator_rpa | real_api | real_ TARGET_BASE_URL=https://sandbox.jc2009.com # ── 默认账号(仅非敏感标识;密码走 account-manager)── +# 浏览器 RPA simulator_rpa 联调账号来自 account-manager(platform + profile_dir),不是此处的 DEFAULT_LOGIN_ID DEFAULT_LOGIN_ID=04110001 # ── 浏览器 / RPA ── diff --git a/development/DEVELOPMENT.md b/development/DEVELOPMENT.md index a059c87..9a996bb 100644 --- a/development/DEVELOPMENT.md +++ b/development/DEVELOPMENT.md @@ -80,11 +80,13 @@ 你不应该直接在这个仓库里开发业务,而应该: 0. 按 [`NAMING.md`](NAMING.md) 确定 slug(`{verb}-{noun-phrase}-{platform}`) -1. 复制这个目录 -2. 改成新 skill 的目录名(与 slug 一致) +1. **优先**用 [`tools/scaffold_skill.ps1`](../tools/scaffold_skill.ps1) 创建新目录(见 [`tools/README.md`](../tools/README.md)) +2. 在新目录内 `git init` 并绑定**本技能**远端(**不得**保留模板 `.git`) 3. 把占位内容替换掉 4. 再开始写业务逻辑 +> **Git 红线**:禁止资源管理器整文件夹复制后保留模板 `.git`;`git remote -v` 必须指向新技能仓库,不能仍是 skill-template。 + ## 2. 新 skill 的标准目录结构 复制模板后,你应该保留下面这套结构: @@ -168,10 +170,13 @@ scripts/ - `examples/real_browser_rpa/scripts/service/task_rpa.py` - `examples/real_browser_rpa/scripts/service/account_client.py` - 若是 **仿真浏览器 RPA**(自有 sandbox、表单批量提交、可控 DOM),**必须先读** `examples/simulator_browser_rpa/README.md`,再按需复制到 `scripts/service/`: - - `examples/simulator_browser_rpa/scripts/service/browser_session.py` - - `examples/simulator_browser_rpa/scripts/service/adapter/` - - `examples/simulator_browser_rpa/scripts/service/task_service.py` + - `examples/simulator_browser_rpa/scripts/service/browser_session.py`(async) + - `examples/simulator_browser_rpa/scripts/service/account_client.py`(subprocess,对齐 `real_browser_rpa`) + - `examples/simulator_browser_rpa/scripts/service/simulator_playwright.py`(RPA 主流程,复制后改名为 `_playwright.py`) + - `examples/simulator_browser_rpa/scripts/service/adapter/`(薄 `simulator_rpa` + mock) + - `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 3. **只用共享库,不在 skill 里重写反反爬**: ```python from jiangchang_skill_core import config @@ -244,12 +249,52 @@ release workflow 会对 `scripts/` 下的 Python 源码做加密/打包。当前 ### 第一步:复制模板并改目录名 -例如你要开发 `your-skill-slug`(或 `disburse-payroll-icbc` 一类领域 skill): +例如你要开发 `disburse-payroll-icbc` 一类领域 skill(目录名 = slug): -1. 复制 `skill-template` -2. 新目录改成与 `slug` 一致的名称(如 `your-skill-slug`) -3. 初始化为独立 git 仓库 -4. 关联它自己的远端仓库 +#### 推荐方式(首选) + +在 **skill-template 仓库根目录**执行: + +```powershell +.\tools\scaffold_skill.ps1 -Slug disburse-payroll-icbc -Destination D:\OpenClaw\client-gdcm\disburse-payroll-icbc +cd D:\OpenClaw\client-gdcm\disburse-payroll-icbc +git init +git remote add origin <你的新技能 Gitea/Git 仓库 URL> +git remote -v # 确认 origin 不是 skill-template +``` + +跨平台可用 `python tools/scaffold_skill.py --slug ... --destination ...`(见 [`tools/README.md`](../tools/README.md))。 + +脚手架会排除 `.git`、缓存与 `.env`,并删除 `.openclaw-skill-template` 标记;**不会**自动 `git init`。 + +#### 禁止方式 + +| 做法 | 后果 | +|------|------| +| ❌ 资源管理器整文件夹复制后不做 Git 清理 | 隐藏 `.git` 被带走,push 串到 template | +| ❌ 保留模板 `.git` 只改 `remote url` | 历史、分支、对象库仍属 template | +| ❌ 未删 `.git` 就 `git init` | 嵌套/混乱仓库,难以排查 | + +#### 若已手工复制(补救) + +```powershell +cd <新技能目录> +Remove-Item -Recurse -Force .git +Remove-Item -Recurse -Force .pytest_cache -ErrorAction SilentlyContinue +Remove-Item -Force .openclaw-skill-template -ErrorAction SilentlyContinue +git init +git remote add origin <新技能仓库 URL> +git remote -v +``` + +#### AI / 编程代理复制红线 + +| 禁止 | 说明 | +|------|------| +| 保留模板 `.git` | 必须先 `Remove-Item -Recurse -Force .git` 再 `git init` | +| `origin` 仍指向 skill-template | 复制后必须 `git remote -v` 自检 | +| 保留 `.openclaw-skill-template` | 仅 template 源仓库可有;业务技能不得保留 | +| 复制 `.pytest_cache` / `__pycache__` | 用 scaffold 已排除;手工复制应删 | 目录名要和 skill slug 对齐,后面很多地方都依赖这个命名。 @@ -699,6 +744,9 @@ uses: client-jiangchang/jiangchang-platform-kit/.github/workflows/reusable-relea - [ ] 源码/文档/配置为 **UTF-8 without BOM**(Python 文件不得含 `U+FEFF`) - [ ] 没有新增默认必跑测试访问真实网络或浏览器 - [ ] 如有 integration 测试需求,已写在 `tests/integration/` 下并保持 `.sample` 后缀 +- [ ] 本仓库**不是** skill-template 的误复制(根目录**无** `.openclaw-skill-template`) +- [ ] `git remote -v` 指向**本技能**远端,URL 不含 skill-template 仓库名 +- [ ] `git log` 首条提交属于本技能(非模板历史) ## 17. 常见错误 @@ -758,12 +806,12 @@ uses: client-jiangchang/jiangchang-platform-kit/.github/workflows/reusable-relea 表现: -- slug 形如 `amazon-download-settlement-report` +- slug 形如 `erp-download-settlement-report`(平台段错误地放在最前) - 语义校验或 `test_slug_naming` 失败 要改: -- 动词放首段,平台放末段,例如 `download-settlement-report-amazon` +- 动词放首段,平台放末段,例如 `download-settlement-report-erp` - 见 [`NAMING.md`](NAMING.md) ### 错误 6:noun 段过长导致超 48 字符 @@ -789,6 +837,25 @@ uses: client-jiangchang/jiangchang-platform-kit/.github/workflows/reusable-relea - `SKILL.md` 的 `metadata.openclaw.slug` - `scripts/util/constants.py` 的 `SKILL_SLUG` +### 错误 8:复制模板时带走了 `.git` + +表现: + +- `git remote -v` 仍显示 skill-template 的 origin +- push 到错误仓库,或 log 里全是模板历史 +- 新技能目录里仍有 `.openclaw-skill-template` + +修复: + +```powershell +Remove-Item -Recurse -Force .git +Remove-Item -Force .openclaw-skill-template -ErrorAction SilentlyContinue +git init +git remote add origin <新技能仓库 URL> +``` + +以后复制请优先用 `tools/scaffold_skill.ps1`(见 [`tools/README.md`](../tools/README.md))。 + ## 18. 推荐开发顺序总结 如果让一个新人照着做,我建议他按这个顺序: diff --git a/development/NAMING.md b/development/NAMING.md index 022e4a1..d057f68 100644 --- a/development/NAMING.md +++ b/development/NAMING.md @@ -165,10 +165,11 @@ slug **末段 platform** 与 `account-manager` 中的 `platform_key` **语义对 ## 9. 复制新技能流程 1. **先定 slug** — 按本文规范拟定 `{verb}-{noun-phrase}-{platform}`,确认在白名单内且 ≤ 48 字符 -2. **复制目录** — 将 `skill-template` 复制为新目录,**目录名 = slug** +2. **复制目录** — **推荐** [`tools/scaffold_skill.ps1`](../tools/scaffold_skill.ps1)(`-Slug` 与目录名一致);勿整包复制带走 `.git` +2.5. **清理 Git(若未用 scaffold)** — 删除 `.git`、`.openclaw-skill-template`、`.pytest_cache`;再 `git init` 与 `git remote add origin` 3. **全局替换** — `your-skill-slug` → 新 slug;更新 `SKILL.md`、`constants.py`、文档中的占位 4. **填写 REQUIREMENTS** — 完成 [`REQUIREMENTS.md`](REQUIREMENTS.md) §0 技能标识 -5. **开发 & 自检** — `python tests/run_tests.py -v`,含 slug 语义测试 +5. **开发 & 自检** — `python tests/run_tests.py -v`,含 slug 语义测试;`git remote -v` 确认非 template --- diff --git a/development/README.md b/development/README.md index 5d26ed6..862ce89 100644 --- a/development/README.md +++ b/development/README.md @@ -11,5 +11,7 @@ 7. [`CONFIG.md`](CONFIG.md) — `.env` 规范与 bootstrap 机制 8. [`RUNTIME.md`](RUNTIME.md) — 共享 runtime、数据路径、发布打包与编码约定 +脚手架与 Git 防串库:[`../tools/README.md`](../tools/README.md)(`scaffold_skill.ps1`)。 + Agent 调用契约见 [`../references/CLI.md`](../references/CLI.md)、[`../references/SCHEMA.md`](../references/SCHEMA.md)。 用户市场说明见根目录 [`README.md`](../README.md),不要写进本目录。 diff --git a/development/RPA.md b/development/RPA.md index 6de9214..c29258d 100644 --- a/development/RPA.md +++ b/development/RPA.md @@ -138,7 +138,7 @@ from jiangchang_skill_core.rpa.stealth import stealth_enabled, STEALTH_INIT_SCRI - `examples/real_browser_rpa/scripts/service/task_rpa.py` — RPA 主流程 - `examples/real_browser_rpa/scripts/service/account_client.py` — account-manager CLI 封装 -仿真浏览器 RPA 的 adapter 分层权威参考见 `examples/simulator_browser_rpa/scripts/service/adapter/`,**不能**替代真实浏览器 RPA 规范。 +仿真浏览器 RPA 的 adapter 分层权威参考见 `examples/simulator_browser_rpa/`(含 async `simulator_playwright.py`、薄 `simulator_rpa` adapter、`account_client.py` subprocess),**不能**替代真实浏览器 RPA 规范。 ### 1.6 仿真浏览器 RPA 示例(必读) @@ -146,12 +146,32 @@ from jiangchang_skill_core.rpa.stealth import stealth_enabled, STEALTH_INIT_SCRI 1. **必须先阅读** `examples/simulator_browser_rpa/README.md` 2. 再参考: - - `examples/simulator_browser_rpa/scripts/service/browser_session.py` - - `examples/simulator_browser_rpa/scripts/service/adapter/`(base / mock / simulator_rpa / dispatch) - - `examples/simulator_browser_rpa/scripts/service/task_service.py` + - `examples/simulator_browser_rpa/scripts/service/browser_session.py` — async persistent context(**不含 goto**) + - `examples/simulator_browser_rpa/scripts/service/simulator_playwright.py` — RPA 主流程(厚) + - `examples/simulator_browser_rpa/scripts/service/adapter/`(薄 `simulator_rpa` + mock + dispatch) + - `examples/simulator_browser_rpa/scripts/service/account_client.py` — account-manager subprocess + - `examples/simulator_browser_rpa/scripts/service/task_service.py` — async 编排 - `examples/simulator_browser_rpa/sandbox/demo_app.html` -> **先选类型再写代码**:真实第三方网站 → `real_browser_rpa`;自有 sandbox → `simulator_browser_rpa`。不要跨类型照抄 selector 或业务流程。 +> **先选类型再写代码**:真实第三方网站 → `real_browser_rpa`;自有 sandbox / 行业仿真 → `simulator_browser_rpa`。不要跨类型照抄 selector 或业务流程。 + +### 1.7 行业仿真平台浏览器 RPA(jc2009 类) + +适用:**自有或共享 `sandbox.jc2009.com` 等行业仿真平台**,不是真实高风控第三方站。 + +工程范式与 §1.5 相同: + +| 项 | 标准 | +|----|------| +| Playwright | **async** 贯穿;禁止 sync Playwright 用于完整 RPA 主路径 | +| 分层 | **薄 adapter** + `{domain}_playwright.py`(示例:`simulator_playwright.py`)+ `account_client.py` subprocess | +| 登录 | **双层**:门户 HITL(`#portal-user` / `#portal-pass` 类泛化 DOM)+ 业务系统登录(技能内自写) | +| 账号 | `url` 用行业根,不用 `/login`;`auth_strategy=per_session_manual`;`pick_web_account` + `release_lease` | +| 禁止 | **不要** `import account-manager` 的 `rpa_helpers` 等内部模块 | +| Selector | 用户可见文案 / `get_by_role` / `name` 优先;`data-testid` 有则用、无则 fallback;共享 sandbox **不要求**为技能加 testid | +| Profile | Chrome persistent profile 可能缓存旧 SPA → 联调排障:手工 `--user-data-dir` 清站点数据 | + +权威 example:**修正后的** `examples/simulator_browser_rpa/`(不是任何生产技能仓库)。 --- diff --git a/development/TESTING.md b/development/TESTING.md index 8820f7a..030c603 100644 --- a/development/TESTING.md +++ b/development/TESTING.md @@ -130,6 +130,20 @@ Golden fixture 流程同理([`tests/samples/test_golden_cases.py.sample`](../t --- +## 7.1 simulator_rpa 联调检查表 + +浏览器 RPA 走 `simulator_rpa` 档位联调前,逐项确认: + +- [ ] 数据目录 `.env` 中 `OPENCLAW_TEST_TARGET=simulator_rpa`(非 `mock` / `unit`) +- [ ] account-manager:`platform ensure` + 账号 `active` + 有 `profile_dir` +- [ ] 目标 sandbox UI 已部署(跨团队;本地可用 `sandbox/demo_app.html`) +- [ ] 失败先查 `rpa-artifacts/` 截图,再查 Chrome profile 缓存(`--user-data-dir` 手工打开清站点数据) +- [ ] 默认 `python tests/run_tests.py -v` 仍全部通过(mock 离线);example 内 `pytest` 不启真实浏览器 + +参考实现:`examples/simulator_browser_rpa/README.md`。 + +--- + ## 8. 新 skill 的最小测试清单 以下清单 **原文摘自 [`tests/README.md`](../tests/README.md) 「新技能最小测试清单」**(复制新仓库后逐项勾选): diff --git a/examples/README.md b/examples/README.md index 4970950..f7e2bfb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -19,13 +19,23 @@ ## 两类浏览器 RPA 的区别 -| | real_browser_rpa | simulator_browser_rpa | +| | real_browser_rpa | simulator_browser_rpa(修正后) | |---|---|---| -| 目标 | 真实第三方网页 | 自有仿真页面 / sandbox | +| 目标 | 真实第三方网页 | 自有仿真页面 / sandbox / jc2009 类 | | 风控 | 高(验证码、登录态、反爬) | 低(DOM 可控) | -| 账号 | 通常需 account-manager + profile | 示例可用 env / demo 账号 | +| 账号 | account-manager + profile | account-manager + profile(`simulator_rpa` 档) | | 用途 | 生产级真实采集/操作参考 | adapter 分层 + 表单 RPA 教学 | +### 工程范式对比 + +| 工程范式 | real_browser_rpa | simulator_browser_rpa(修正后) | +|---|---|---| +| async Playwright | ✅ | ✅ | +| 薄 adapter + `*_playwright.py` | `task_rpa.py` | `simulator_playwright.py` | +| account-manager subprocess | ✅ | ✅(`simulator_rpa` 档位) | + +两者工程范式一致,差异在目标系统(真实站 vs 仿真站/本地 demo)。 + ## 如何选择示例 | 你的场景 | 参考目录 | diff --git a/examples/simulator_browser_rpa/README.md b/examples/simulator_browser_rpa/README.md index f3fcb82..32b9cf9 100644 --- a/examples/simulator_browser_rpa/README.md +++ b/examples/simulator_browser_rpa/README.md @@ -1,20 +1,20 @@ # Simulator Browser RPA Example -这是一个**仿真浏览器 RPA** 的可复制参考实现:在可控 sandbox 页面上,通过 Playwright 完成登录、表单填写、弹窗确认、结果解析。 +这是一个**仿真浏览器 RPA** 的可复制参考实现:在可控 sandbox 页面上,通过 **async Playwright** 完成门户门闩、两步登录、表单填写、PIN 弹窗、结果解析。 -本示例抽象自已跑通的「批量提交类」浏览器自动化 skill,但已去业务化,不包含真实银行/真实资金语义。 +本示例抽象自已跑通的「工行批量提交类」浏览器自动化 skill 的工程范式,但已**去业务化**——不包含真实银行/真实资金语义,也**不是** Amazon 或任何生产技能的复制。 ## 适合什么场景 -- 自有 **仿真系统 / sandbox / training 页面** +- 自有 **行业仿真系统** / `sandbox.jc2009.com` / 本地 `sandbox/demo_app.html` - 需要 **浏览器 RPA** 做端到端演示或回归 -- 表单较复杂:多步登录、Tab 切换、动态增行、PIN 弹窗、成功页解析 -- 需要 **adapter 分层**:mock(CI)与 simulator_rpa(半集成)可切换 +- 表单较复杂:门户 HITL、多步登录、Tab 切换、动态增行、PIN 弹窗、成功页解析 +- 需要 **adapter 分层**:`mock`(CI)与 `simulator_rpa`(半集成)可切换 ## 不适合什么场景 - **真实第三方网站 + 高风控 + 人工验证码** → 优先看 [`../real_browser_rpa/README.md`](../real_browser_rpa/README.md) -- 目标系统有稳定 **官方 API** → 不要硬写浏览器 RPA,后续参考 `real_api` +- 目标系统有稳定 **官方 API** → 不要硬写浏览器 RPA - **不要把本示例的业务字段、selector、sandbox URL 原样复制**到你的 skill ## 目录结构 @@ -23,22 +23,24 @@ simulator_browser_rpa/ ├── README.md ├── sandbox/ -│ └── demo_app.html # 本地仿真页面(稳定 data-testid / name) +│ └── demo_app.html # 本地仿真页面(门户门闩 + 批量提交教学 DOM) ├── scripts/ │ ├── service/ -│ │ ├── browser_session.py # 浏览器启动(URL 仅 page.goto) -│ │ ├── account_client.py # 账号来源集中封装 -│ │ ├── task_service.py # 编排层 +│ │ ├── browser_session.py # async 启动 persistent context(不含 goto) +│ │ ├── account_client.py # account-manager subprocess(对齐 real_browser_rpa) +│ │ ├── simulator_playwright.py # RPA 主流程(厚) +│ │ ├── task_service.py # async 编排层 │ │ └── adapter/ -│ │ ├── base.py # BatchItem / BatchSubmitResult 契约 -│ │ ├── mock.py # mock/unit 档 -│ │ ├── simulator_rpa.py -│ │ └── __init__.py # select_adapter +│ │ ├── base.py # BatchItem / BatchSubmitResult 契约 +│ │ ├── mock.py # mock/unit 档 +│ │ ├── simulator_rpa.py # 薄 adapter:pick / lease / 委托 RPA +│ │ └── __init__.py # select_adapter │ └── util/ │ ├── constants.py │ └── logging.py └── tests/ ├── test_adapter_dispatch.py + ├── test_no_rpa_helpers_import.py ├── test_simulator_rpa_unit.py └── test_stop_reason.py ``` @@ -47,91 +49,95 @@ simulator_browser_rpa/ | 文件 | 职责 | |---|---| -| `browser_session.py` | 系统 Chrome/Edge + launch args 规范;支持 profile / 无 profile | -| `account_client.py` | 示例从环境变量读账号;真实 skill 可在此集中对接 account-manager | -| `adapter/base.py` | 数据契约与 `BatchAdapterBase` | +| `browser_session.py` | async 系统 Chrome/Edge + launch args;**不在此 goto** | +| `account_client.py` | **唯一** account-manager subprocess 封装(`pick_web_account` / `release_lease`) | +| `simulator_playwright.py` | 门户轮询、登录、批量表单、PIN、解析 `batch_id`、截图 | +| `adapter/simulator_rpa.py` | **薄** adapter:pick 账号、lease、委托 RPA、`finally release_lease` | | `adapter/mock.py` | 不启浏览器,供 unit/mock/CI | -| `adapter/simulator_rpa.py` | 仿真 RPA 主流程 | -| `task_service.py` | 校验输入 → 选 adapter → 提交 → 返回 summary | -| `sandbox/demo_app.html` | 可控 DOM,配合 selector 稳定 | -| `tests/` | dispatch、URL 不进 launch args、失败转结果 | +| `task_service.py` | async 校验输入 → 选 adapter → `await submit_batch` | +| `sandbox/demo_app.html` | 可控 DOM;含可选门户门闩 + 原批量提交流程 | ## 核心流程 -1. `task_service.run_batch_submit(target, items)` 校验参数 +1. `await run_batch_submit(target, items)` 校验参数 2. `select_adapter()` 按 `OPENCLAW_TEST_TARGET` 选择 mock 或 simulator RPA -3. simulator 档:`pick_simulator_account()` → `set_credentials()` → `submit_batch()` -4. RPA:登录两步 → 批量页 → 填表 → 提交 → PIN → 解析 `batch_id` +3. `simulator_rpa` 档:`SimulatorBrowserRpaAdapter.submit_batch` 内 `pick_web_account(sim_batch)` → `submit_batch_rpa` → `release_lease` +4. RPA:门户门闩(若可见则 HITL 轮询)→ 登录两步 → 批量页 → 填表 → 提交 → PIN → 解析 `batch_id` 5. 失败截图(有 `artifacts_dir` 时)→ 返回 `BatchSubmitResult` -## 复制到新 skill 时怎么做 +## 两档仿真目标 -按 copy map **选择性复制**到 `scripts/`;**不要**整包照搬本目录,**不要**把本 README 当作新技能根 README。 +| 档位 | 目标 | 说明 | +|---|---|---| +| 本地教学 | `sandbox/demo_app.html`(`file://`) | 结构教学 + mock 单测默认 | +| 线上联调 | `https://sandbox.jc2009.com/...` | 设 `SIMULATOR_BASE_URL` + account-manager `sim_batch` 账号 | + +联调时账号 `url` 用行业根路径,**不要**写 `/login` 子路径;`auth_strategy=per_session_manual`。 + +## account-manager 注册示例(泛化) + +```bash +python .../account-manager/scripts/main.py account add-web \ + --platform sim_batch \ + --login-id demo001 \ + --url file:///.../demo_app.html \ + --environment simulator \ + --auth-strategy per_session_manual +``` + +线上 sandbox 时将 `--url` 换为 `https://sandbox.jc2009.com/...` 占位路径(按团队部署填写)。 + +**PIN/密码演示值**:RPA 从 account 的 `login_id` 取账号;`password` / `token_pin` 允许从 account 字段或环境变量 `SIMULATOR_PASSWORD` / `SIMULATOR_TOKEN_PIN` 回退;验证码固定演示值 `0000`(仅 sandbox 用)。 ## Copy map(复制到新技能 `scripts/`) | 来源(examples) | 目标(新技能) | 说明 | |---|---|---| -| `examples/simulator_browser_rpa/scripts/service/browser_session.py` | `scripts/service/browser_session.py` | URL 仅 `page.goto`,不进 launch args | -| `examples/simulator_browser_rpa/scripts/service/account_client.py` | `scripts/service/account_client.py` | 账号来源集中封装 | -| `examples/simulator_browser_rpa/scripts/service/task_service.py` | `scripts/service/task_service.py` | 编排层,合并业务逻辑 | -| `examples/simulator_browser_rpa/scripts/service/adapter/` | `scripts/service/_adapter/` | **adapter 分层权威参考** | -| `examples/simulator_browser_rpa/scripts/util/constants.py` | `scripts/util/constants.py` | 须替换业务常量 | -| `examples/simulator_browser_rpa/scripts/util/logging.py` | `scripts/util/logging.py` | 可选 | -| `examples/simulator_browser_rpa/tests/` | `tests/` | 可按需复制 dispatch / unit 测试 | -| `examples/simulator_browser_rpa/sandbox/demo_app.html` | — | **仅仿真测试用**,不进入生产 skill | -| `examples/simulator_browser_rpa/README.md` | — | **只读参考**,不复制为新技能根 README | - -**边界说明:** - -- `sandbox/` 只用于仿真测试与本地回归,**不进入**真实生产 skill -- `adapter/` 分层(base / mock / simulator_rpa)是仿真浏览器 RPA 的权威参考 -- `mock` / `simulator_rpa` 只用于测试与仿真,**不要**混入 `real_browser_rpa` 的真实站点逻辑 -- `demo_app.html` 是可运行沙箱页面,**不是**真实业务系统 - -**保留:** - -- adapter 分层(base / mock / simulator_rpa / `select_adapter`) -- `browser_session.py` 启动规范(**URL 不进 launch args**) -- `account_client.py` 集中封装模式 -- 关键节点结构化日志 + 失败截图 -- 测试:dispatch、launch args、错误转 `BatchSubmitResult` - -**替换:** - -- `BatchItem` 字段与校验规则 -- `sandbox/demo_app.html` 或 `SIMULATOR_BASE_URL` 指向的页面 -- 全部 DOM selector(必须 F12 实测) -- `task_service.py` 输入输出与 DB/任务日志 -- mock 返回字段、成功页 `batch_id` 解析逻辑 +| `browser_session.py` | `scripts/service/browser_session.py` | async;URL 仅 `page.goto`,不进 launch args | +| `account_client.py` | `scripts/service/account_client.py` | subprocess,对齐 `real_browser_rpa` | +| `simulator_playwright.py` | `scripts/service/_playwright.py` | RPA 主流程(厚) | +| `adapter/` | `scripts/service/_adapter/` | 薄 `simulator_rpa` + mock | +| `task_service.py` | `scripts/service/task_service.py` | async 编排 | +| `util/constants.py` | `scripts/util/constants.py` | 须替换 `TARGET_PLATFORM` 等业务常量 | +| `tests/` | `tests/` | 可按需复制 dispatch / unit / 禁止 import 守护 | +| `sandbox/demo_app.html` | — | **仅仿真测试**,不进入生产 skill | ## 明确禁止 -- 不要复制 `__pycache__`、`.pytest_cache`、`artifacts/`、`recordings/` 等生成物 -- **不要在技能侧**执行 `playwright install` / `pip install playwright`(Python 包由宿主共享 runtime 提供) -- **不要把 URL 放进** `launch_persistent_context(args=[...])` +- **不要** `import account-manager` 内部模块(`rpa_helpers`、`inject_account_manager_scripts_path`、`get_account_credential`) +- **不要** sync Playwright 用于完整 RPA 主路径 - **不要在** `task_service.py` / RPA 主流程里散落 account-manager subprocess -- **不要把真实生产系统**配置到 `simulator_rpa` 档位 -- 不要照抄 slug、logger 名、演示账号到生产 skill +- **不要在技能侧**执行 `playwright install` / `pip install playwright` +- **不要把 URL 放进** `launch_persistent_context(args=[...])` +- **不要** vendor `scripts/jiangchang_skill_core/` +- **不要**照抄 slug、logger 名、演示账号到生产 skill + +> 旧版 `pick_simulator_account()` / `SimulatorAccount` / `set_credentials()` 已废弃;账号统一由 account-manager 下发。 + +## 联调检查表 + +- [ ] 数据目录 `.env` 中 `OPENCLAW_TEST_TARGET=simulator_rpa`(非 mock) +- [ ] account-manager:`platform ensure` + 账号 `active` + 有 `profile_dir` +- [ ] 目标 sandbox UI 已部署(跨团队) +- [ ] 失败先查 `rpa-artifacts/` 截图,再查 Chrome profile 缓存(可用 `--user-data-dir` 手工打开清站点数据) +- [ ] 默认 `tests/run_tests.py` / 本 example `pytest` 仍全部通过(mock 离线) ## 依赖说明 - Python 3.10+ -- Python 包 `playwright`:由**宿主共享 runtime** 提供;技能侧不要写入 `requirements.txt`,不要 `pip install playwright` -- 默认使用**系统 Chrome/Edge**,不使用 Playwright 内置 Chromium -- 技能侧不要执行 `playwright install` / `playwright install chromium` -- 本地独立开发若缺 Playwright Python 包,仅作开发机临时补装;生产由宿主解决 -- `jiangchang_skill_core.runtime_env.find_chrome_executable`(可选,用于查找系统浏览器) +- Python 包 `playwright`:由**宿主共享 runtime** 提供;技能侧不要 `pip install playwright` +- 默认使用**系统 Chrome/Edge**;技能侧不要 `playwright install` +- `jiangchang_skill_core.runtime_env.find_chrome_executable`(可选) ## 环境变量(示例) | 变量 | 说明 | |---|---| -| `OPENCLAW_TEST_TARGET` | `mock`/`unit` → Mock;默认 → Simulator RPA | -| `SIMULATOR_BASE_URL` | 仿真页 URL;默认本地 `sandbox/demo_app.html` 的 file URI | -| `SIMULATOR_LOGIN_ID` / `PASSWORD` / `TOKEN_PIN` | 演示账号 | -| `SIMULATOR_PROFILE_DIR` | 可选 persistent profile | -| `OPENCLAW_BROWSER_HEADLESS` | `0` 默认有头;`1` 无头模式(CI 可用) | +| `OPENCLAW_TEST_TARGET` | `mock`/`unit` → Mock;`simulator_rpa` → 仿真 RPA | +| `SIMULATOR_BASE_URL` | 仿真页 URL;默认本地 `demo_app.html` 的 file URI | +| `SIMULATOR_PASSWORD` / `SIMULATOR_TOKEN_PIN` | 演示密码/PIN 回退(账号字段优先) | +| `PORTAL_LOGIN_WAIT_SEC` | 门户 HITL 超时(默认 120) | +| `OPENCLAW_BROWSER_HEADLESS` | `0` 默认有头;`1` 无头(CI 可用) | ## 运行测试 @@ -140,9 +146,9 @@ cd examples/simulator_browser_rpa python -m pytest tests/ -v ``` -测试为 mock/纯函数,**不需要启动真实浏览器**。 +测试为 mock/纯函数,**不启动真实浏览器**。 ## 当前状态 -- 示例代码与 sandbox 页面已就绪,可作为仿真浏览器 RPA 复制起点。 +- async Playwright + 薄 adapter + `simulator_playwright.py` 工程范式已对齐 `real_browser_rpa`。 - 未接入完整 CLI、数据库与 entitlement。 diff --git a/examples/simulator_browser_rpa/sandbox/demo_app.html b/examples/simulator_browser_rpa/sandbox/demo_app.html index 721eb5b..52ee286 100644 --- a/examples/simulator_browser_rpa/sandbox/demo_app.html +++ b/examples/simulator_browser_rpa/sandbox/demo_app.html @@ -15,7 +15,16 @@

仿真业务系统 · 批量提交(示例)

-
+
+

平台门户登录(示例)

+
+ + + +
+
+ +