# 与其它 Skill 集成(account-manager) ## 核心约束 业务 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`。宿主注入的 **`JIANGCHANG_DATA_ROOT` / `JIANGCHANG_USER_ID`**(或上层包装的 `CLAW_*`)必须与最终用户会话一致——否则会写到另一套目录。路径调试见 [`RUNTIME.md`](RUNTIME.md)。 ## 机器可读命令摘要 | 目的 | 命令 | 成功时 stdout(摘要) | |---|---|---| | 枚举平台 | `platform list [--domain ...]` | `{"success": true, "data": {"platforms": [...]}}` | | 平台详情 | `platform get

` | `{"success": true, "data": {...}}` | | 取单条账号 JSON | `account get ` | **单行 JSON 对象**(无固定 `success` 字段) | | 批量账号 JSON | `account list ...` | **单行 JSON 数组** | | 网页自动化候选 | `account pick-web ...` | **单行 JSON 对象** | | 取加密凭据明文 | `account get-credential --reveal --lease-token ` | **单行 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 ` / `list-json

` / `pick-web

` | 同上对应新式命令 | 更完整的命令表见 [`CLI.md`](CLI.md)。 ## 解析协议(稳妥做法) 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 pick-web`(典型) ```json { "id": 1, "platform_key": "maersk", "account_label": "Maersk simulator booking", "login_id": "demo@maersk.com", "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", "session_status": "unknown", "auth_strategy": "qr_code_manual", "session_persistent": 1, "device_fingerprint": null, "lease_token": "…………", "lease_expires_at": "2026-05-11T12:34:56" } ``` `lease_*` 仅在传入 `--lease` 时出现;时间与仓库 `_unix_to_iso_local` 一致。 ### `account get` 输出即数据库映射字典(字段与 [`SCHEMA.md`](SCHEMA.md) `accounts` 一致,`extra_json` 等为结构化 JSON)。 ### `credential pick` ```json { "success": true, "id": 1, "platform_key": "deepseek", "credential_label": "DeepSeek production key", "credential_type": "api_key", "secret_storage": "env", "secret_ref": "DEEPSEEK_API_KEY", "secret_mask": "****7890", "status": "active" } ``` 仅 `--reveal` 时附加 **`secret`**。请勿向终端用户打印 reveal 结果。 ### `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(CLI) 1. `account pick-web --platform maersk --environment simulator --role booking --lease --holder my-skill` 2. 解析 JSON → 取 `id`、`profile_dir`、`url`、`lease_token` 3. 用 `profile_dir` 启动持久化浏览器上下文 4. 业务完成后:`lease release ` 5. 如遇登录失效:`account mark-session --session-status needs_login` ### LLM API 调用(CLI) 1. `credential pick --platform deepseek --type api_key --environment production --reveal` 2. 解析 JSON → 取 `secret` 字段 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 / `ERROR:` 前缀** 为准;不要仅依赖退出码。