Files
account-manager/references/INTEGRATION.md

213 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 与其它 Skill 集成account-manager
## 核心约束
业务 skill **不能自己散落存储账号、密码、API Key、Token**。应通过 account-manager **CLI**(进程隔离)或 **CLI + `service.rpa_helpers` 同进程库**(浏览器自动化)组合获取。
## 两种集成方式
| 方式 | 适用场景 | 调用形态 |
|---|---|---|
| **A. CLI subprocessv1 起)** | 任意业务 skill一次性取账号 JSON / 凭据 / lease | `subprocess.run([sys.executable, f"{skill_root}/scripts/main.py", "account", "pick-web", ...])` |
| **B. rpa_helpers 同进程 importv2** | 需要 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 <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**;若有多行,**集成应以首行为准**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)。
## 业务流程示例
### 物流 RPACLI
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 <lease_token>`
5. 如遇登录失效:`account mark-session <id> --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:` 前缀** 为准;不要仅依赖退出码。