docs: update SKILL.md / README.md / CLI.md / INTEGRATION.md for v2
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,31 +2,53 @@
|
||||
|
||||
## 核心约束
|
||||
|
||||
业务 skill **不能自己存账号、密码、API Key、Token**。必须通过 account-manager CLI 获取。
|
||||
业务 skill **不能自己散落存储账号、密码、API Key、Token**。应通过 account-manager **CLI**(进程隔离)或 **CLI + `service.rpa_helpers` 同进程库**(浏览器自动化)组合获取。
|
||||
|
||||
## 两种集成方式
|
||||
|
||||
| 方式 | 适用场景 | 调用形态 |
|
||||
|---|---|---|
|
||||
| **A. CLI subprocess(v1 起)** | 任意业务 skill;一次性取账号 JSON / 凭据 / lease | `subprocess.run([sys.executable, f"{skill_root}/scripts/main.py", "account", "pick-web", ...])` |
|
||||
| **B. rpa_helpers 同进程 import(v2)** | 需要 Playwright 上下文内完成登录与页面操作 | `sys.path.insert(0, f"{account_manager_root}/scripts"); from service.rpa_helpers import ensure_logged_in` |
|
||||
|
||||
常见组合:**CLI** 负责 pick / lease / `get-credential --reveal`;**rpa_helpers** 负责 `launch_browser_with_profile` + `ensure_logged_in`。详见 [`RPA_HELPERS.md`](RPA_HELPERS.md)。
|
||||
|
||||
## 推荐调用方式
|
||||
|
||||
使用 **`subprocess`** 调用 `scripts/main.py`,工作目录可为技能根或 `scripts/`。宿主注入的 **`JIANGCHANG_DATA_ROOT` / `JIANGCHANG_USER_ID`** 必须与最终用户会话一致。
|
||||
使用 **`subprocess`** 调用 `scripts/main.py`。宿主注入的 **`JIANGCHANG_DATA_ROOT` / `JIANGCHANG_USER_ID`**(或上层包装的 `CLAW_*`)必须与最终用户会话一致——否则会写到另一套目录。路径调试见 [`RUNTIME.md`](RUNTIME.md)。
|
||||
|
||||
## 机器可读命令摘要
|
||||
|
||||
| 目的 | 命令 | 成功时 stdout |
|
||||
|------|------|----------------|
|
||||
| 取单条账号 JSON | `account get <id>` | 单行 JSON 对象 |
|
||||
| 批量账号 JSON | `account list --platform <p> [--environment <e>]` | 单行 JSON 数组 |
|
||||
| 网页自动化候选 | `account pick-web --platform <p> [--lease]` | 单行 JSON 对象 |
|
||||
| 获取 API Key | `credential pick --platform <p> --type api_key [--reveal]` | 单行 JSON(`success: true` + 字段) |
|
||||
| 兼容入口 | `get <id>` / `list-json <p>` / `pick-web <p>` | 同上 |
|
||||
| 目的 | 命令 | 成功时 stdout(摘要) |
|
||||
|---|---|---|
|
||||
| 枚举平台 | `platform list [--domain ...]` | `{"success": true, "data": {"platforms": [...]}}` |
|
||||
| 平台详情 | `platform get <p>` | `{"success": true, "data": {...}}` |
|
||||
| 取单条账号 JSON | `account get <id>` | **单行 JSON 对象**(无固定 `success` 字段) |
|
||||
| 批量账号 JSON | `account list ...` | **单行 JSON 数组** |
|
||||
| 网页自动化候选 | `account pick-web ...` | **单行 JSON 对象** |
|
||||
| 取加密凭据明文 | `account get-credential <id> --reveal --lease-token <t>` | **单行 JSON**,含 `plaintext`(若可解密) |
|
||||
| 备份加密凭据 | `account export-credentials --plaintext` | **JSON 数组**(stderr 有警告行) |
|
||||
| 导入加密凭据 | `account import-credentials [--replace-all]` | **通常为空**(计数在 stderr) |
|
||||
| 初始化 master key | `account init-master-key [--print\|--from-env\|--rotate]` | **单行 JSON** |
|
||||
| 新增网页账号 | `account add-web ...` | `{"success": true, "data": {...}}` |
|
||||
| 新增凭据 | `account add-secret ...` | `{"success": true, "data": {...}}` |
|
||||
| 获取 API Key | `credential pick ... [--reveal]` | `{"success": true, ...}` |
|
||||
| 兼容入口 | `get <id>` / `list-json <p>` / `pick-web <p>` | 同上对应新式命令 |
|
||||
|
||||
## 解析协议
|
||||
更完整的命令表见 [`CLI.md`](CLI.md)。
|
||||
|
||||
1. 读取子进程 **stdout** 首行。
|
||||
2. 若首行以 **`ERROR:`** 开头 → 失败。
|
||||
3. 否则对首行做 `json.loads`。
|
||||
## 解析协议(稳妥做法)
|
||||
|
||||
1. 读取子进程 **stdout**;若有多行,**集成应以首行为准**(export/import 例外:`export` 整块 stdout 即 JSON;`import` 读 stderr)。
|
||||
2. **优先尝试 `json.loads`**:
|
||||
- 若为 dict 且 `success is False` → 读取 `error.code` / `error.message`。
|
||||
- 若 dict 且 `success is True` 且存在 `data` → 业务字段通常在 `data` 内(`add-web` / `add-secret` / `platform`)。
|
||||
3. 若 JSON 首字段不是上述统一格式(例如 `pick-web`)→ 直接按字段读取。
|
||||
4. **遗留文本失败**:首行以 **`ERROR:`** 开头 → 视为失败(旧别名路径仍可能出现)。
|
||||
|
||||
## JSON 对象字段
|
||||
|
||||
### account get / pick-web
|
||||
### `account pick-web`(典型)
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -34,26 +56,28 @@
|
||||
"platform_key": "maersk",
|
||||
"account_label": "Maersk simulator booking",
|
||||
"login_id": "demo@maersk.com",
|
||||
"login_id_type": "email",
|
||||
"profile_dir": "D:/claw-data/10032/account-manager/profiles/logistics/maersk/Maersk_simulator_booking_demo@maersk.com",
|
||||
"url": "http://localhost:5180/industries/logistics/maersk/login",
|
||||
"tenant_id": "tenant-demo",
|
||||
"environment": "simulator",
|
||||
"role": "booking",
|
||||
"provider_code": "maersk",
|
||||
"profile_dir": "D:/claw-data/10032/account-manager/profiles/logistics/maersk/Maersk_simulator_booking_demo@maersk.com",
|
||||
"url": "http://localhost:5180/industries/logistics/maersk/login",
|
||||
"status": "active",
|
||||
"session_status": "unknown",
|
||||
"last_used_at": "2026-05-01T10:00:00",
|
||||
"created_at": "2026-05-01T09:00:00",
|
||||
"updated_at": "2026-05-01T09:00:00"
|
||||
"auth_strategy": "qr_code_manual",
|
||||
"session_persistent": 1,
|
||||
"device_fingerprint": null,
|
||||
"lease_token": "…………",
|
||||
"lease_expires_at": "2026-05-11T12:34:56"
|
||||
}
|
||||
```
|
||||
|
||||
pick-web 额外字段(带 lease 时):
|
||||
- `lease_token`: 租约 token
|
||||
- `lease_expires_at`: 过期时间
|
||||
`lease_*` 仅在传入 `--lease` 时出现;时间与仓库 `_unix_to_iso_local` 一致。
|
||||
|
||||
### credential pick
|
||||
### `account get`
|
||||
|
||||
输出即数据库映射字典(字段与 [`SCHEMA.md`](SCHEMA.md) `accounts` 一致,`extra_json` 等为结构化 JSON)。
|
||||
|
||||
### `credential pick`
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -69,13 +93,48 @@ pick-web 额外字段(带 lease 时):
|
||||
}
|
||||
```
|
||||
|
||||
**只有 `--reveal` 时才返回 `secret` 字段。业务 skill 不要向最终用户展示 reveal 输出。**
|
||||
仅 `--reveal` 时附加 **`secret`**。请勿向终端用户打印 reveal 结果。
|
||||
|
||||
失败时使用统一 JSON:`{"success": false, "error": {"code": "ERROR:...", "message": "..."}}`(见 [ERRORS.md](ERRORS.md))。
|
||||
### `account get-credential`
|
||||
|
||||
成功示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"account_id": 42,
|
||||
"credential_id": 7,
|
||||
"credential_type": "password",
|
||||
"secret_storage": "local_encrypted",
|
||||
"secret_mask": "****abcd",
|
||||
"plaintext": "……"
|
||||
}
|
||||
```
|
||||
|
||||
失败时使用统一 JSON:`{"success": false, "error": {"code": "...", "message": "..."}}`(亦可能夹杂历史 `ERROR:` 文本命令——见 [`ERRORS.md`](ERRORS.md))。
|
||||
|
||||
## rpa_helpers 同进程使用(摘要)
|
||||
|
||||
```python
|
||||
import sys
|
||||
|
||||
AM_ROOT = r"D:\OpenClaw\client-commons\account-manager"
|
||||
sys.path.insert(0, f"{AM_ROOT}/scripts")
|
||||
|
||||
from service.rpa_helpers import (
|
||||
launch_browser_with_profile,
|
||||
close_browser,
|
||||
ensure_logged_in,
|
||||
)
|
||||
```
|
||||
|
||||
**务必**:`sys.path` 指向 **`account-manager/scripts`**,因此 import 形态为 **`from service.rpa_helpers ...`**(而不是 `from scripts.service.rpa_helpers ...`)。
|
||||
|
||||
完整 API、登录判定 hook、`LoginResult` / `AuthStrategyError` 说明见 [`RPA_HELPERS.md`](RPA_HELPERS.md)。
|
||||
|
||||
## 业务流程示例
|
||||
|
||||
### 物流 RPA
|
||||
### 物流 RPA(CLI)
|
||||
|
||||
1. `account pick-web --platform maersk --environment simulator --role booking --lease --holder my-skill`
|
||||
2. 解析 JSON → 取 `id`、`profile_dir`、`url`、`lease_token`
|
||||
@@ -83,12 +142,71 @@ pick-web 额外字段(带 lease 时):
|
||||
4. 业务完成后:`lease release <lease_token>`
|
||||
5. 如遇登录失效:`account mark-session <id> --session-status needs_login`
|
||||
|
||||
### LLM API 调用
|
||||
### LLM API 调用(CLI)
|
||||
|
||||
1. `credential pick --platform deepseek --type api_key --environment production --reveal`
|
||||
2. 解析 JSON → 取 `secret` 字段
|
||||
3. 在业务进程内存中使用,不写文件、不打日志
|
||||
3. 仅在内存中使用,不写磁盘、不打日志
|
||||
|
||||
### 加密密码 + 自动登录(CLI + rpa_helpers)
|
||||
|
||||
```python
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
AM = r"D:\OpenClaw\client-commons\account-manager"
|
||||
PY = sys.executable
|
||||
MAIN = f"{AM}/scripts/main.py"
|
||||
|
||||
sys.path.insert(0, f"{AM}/scripts")
|
||||
from service.rpa_helpers import launch_browser_with_profile, close_browser, ensure_logged_in
|
||||
|
||||
|
||||
def am_json(argv: list[str]) -> dict:
|
||||
r = subprocess.run([PY, MAIN, *argv], capture_output=True, text=True, check=False)
|
||||
line = (r.stdout or "").splitlines()[0] if r.stdout else ""
|
||||
return json.loads(line)
|
||||
|
||||
|
||||
# 1) pick 账号 + lease
|
||||
acc = am_json(
|
||||
[
|
||||
"account",
|
||||
"pick-web",
|
||||
"--platform",
|
||||
"icbc_sim",
|
||||
"--lease",
|
||||
"--holder",
|
||||
"my-rpa",
|
||||
]
|
||||
)
|
||||
|
||||
# 2) 取明文密码(必须 reveal + lease)
|
||||
cred = am_json(
|
||||
[
|
||||
"account",
|
||||
"get-credential",
|
||||
str(acc["id"]),
|
||||
"--reveal",
|
||||
"--lease-token",
|
||||
acc["lease_token"],
|
||||
]
|
||||
)
|
||||
|
||||
# 3) 浏览器 + 登录
|
||||
ctx, page = launch_browser_with_profile(acc["profile_dir"])
|
||||
try:
|
||||
result = ensure_logged_in(page, acc, credentials={"plaintext": cred["plaintext"]})
|
||||
if not result.ok:
|
||||
print(result.message, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
# …业务逻辑…
|
||||
finally:
|
||||
close_browser(ctx)
|
||||
subprocess.run([PY, MAIN, "lease", "release", acc["lease_token"]], check=False)
|
||||
```
|
||||
|
||||
## 退出码
|
||||
|
||||
请以 **stdout 首行前缀** 与 **JSON 可解析性** 为准;不要仅依赖退出码。
|
||||
请以 **stdout JSON / `ERROR:` 前缀** 为准;不要仅依赖退出码。
|
||||
|
||||
Reference in New Issue
Block a user