Metadata-Version: 2.4
Name: isa-rpa
Version: 0.4.8
Summary: I-Search Agent RPA Python SDK，封装 OCR、验证码、DocAgent 等客户端能力
Keywords: rpa,ocr,captcha,docagent,sap
Author: isa
Author-email: isa <service@i-search.com.cn>
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Dist: gmssl>=3.2.2
Requires-Dist: httpx>=0.23
Requires-Dist: playwright>=1.60.0
Requires-Dist: pycryptodome>=3.20.0
Requires-Dist: pydantic>=2.0
Requires-Dist: requests>=2.28
Requires-Python: >=3.12
Project-URL: Homepage, https://www.i-search.com.cn/
Description-Content-Type: text/markdown

# isa-rpa

I-Search Agent RPA Python SDK，封装 OCR、验证码、DocAgent、浏览器会话、密码箱凭证、短信箱、大模型文本处理等客户端能力。

该包可从官方 PyPI 安装，但其中部分能力依赖 I-Search 内部运行环境、内部网关或平台侧注入的环境变量。公开安装不等于脱离这些运行条件即可直接使用。

> 版本线说明：本目录是 **Python 3.12+** 线（`0.4.x`）。PyPI 上仍存在的 `0.1.x` 是旧 Python 3.8 兼容版本，仅作历史兼容保留、不再维护和上传 PyPI。

## 安装

如果使用方没有修改 `uv` 的默认索引，可以直接从官方 PyPI 安装：

```bash
uv add isa-rpa
```

作为 CLI 工具安装：

```bash
uv tool install isa-rpa
```

也可以使用 `pip`：

```bash
pip install isa-rpa
```

## 打包与发布

本模块位于 monorepo `magicx-app/sdk/isa-rpa/`。

```bash
uv build --no-sources
uv publish
```

构建产物位于 `dist/` 目录：

```text
dist/isa_rpa-0.2.5-py3-none-any.whl
dist/isa_rpa-0.2.5.tar.gz
```

## CLI

查看帮助：

```bash
isa-rpa --help
```

`isa-rpa` 是伞形命令，按命令组分子命令：

- `isa-rpa browser <command>`：浏览器探索（探索 DOM 结构、定位元素、点击/填写、截图、执行 JS 等，每条命令独立连接 CDP，执行后退出）。命令清单见 `isa-rpa browser --help`，详细用法见 [web-exploration Skill](../../plugins/web-exploration/skills/web-exploration/SKILL.md)。
- `isa-rpa secret <command>`：密码箱凭证管理（list/query/exists/create/update/delete）；调用方明文传入 / 获取, SDK 内部 SM2 加密后上送, name 主键、同租户唯一。详见 [magic-toolkit 的 password-vault Skill](../../plugins/magic-toolkit/skills/password-vault/SKILL.md)。
- `isa-rpa rollout <input.jsonl> [-o output.md] [--full]`：把大模型存档的 JSONL 转换为人读的 Markdown（自用调试工具）。

  ```bash
  isa-rpa rollout session.jsonl                 # 紧凑格式，生成 rollout-compact.md
  isa-rpa rollout session.jsonl --full          # 完整格式，生成 rollout-full.md
  isa-rpa rollout session.jsonl -o out.md       # 指定输出路径
  ```

  - 默认输出紧凑格式（`rollout-compact.md`），`--full` 输出完整 Markdown（`rollout-full.md`）。
  - 不指定 `-o` 时输出到当前工作目录，同名文件已存在时自动追加 `(1)`、`(2)` 等序号。

如果在魔术师(magicx) docker 环境中运行，通常需要由运行时注入 `MAGICX_RUNTIME=docker`、`CLIENT_ID`、网关相关环境变量，以及浏览器会话所需的 `MAGICX_CDP_URL`（远程 CDP base，如 `http://192.168.11.23:8080`，不带 `/json/version`）。

浏览器会话（`BrowserSession` / `isa-rpa browser`）按 `MAGICX_RUNTIME` 解析 CDP 地址，优先级如下：

1. `MAGICX_CDP_URL`（不区分运行环境，注入即优先）：探测 `<base>/json/version` 取 `webSocketDebuggerUrl`。
2. `MAGICX_RUNTIME=docker`：`MAGICX_CDP_URL` 是唯一来源，未注入或不可达直接报错，不本地兜底。
3. `MAGICX_RUNTIME=windows|macos|linux`（native）：客户端不注入 `MAGICX_CDP_URL`，改调本地客户端控制接口 `GET http://127.0.0.1:<MAGICX_CONTROL_PORT>/browser/open` 拉起/复用本地 chrome 单例（`MAGICX_CONTROL_PORT` 默认 `9876`，由客户端 daemon 注入；控制接口内部已等 CDP 就绪）。为兼容旧版客户端，若存在 `RPA_TENANT_ID`，SDK 会继续附带 `tenantId` query；新客户端会忽略该参数，缺失时也不影响浏览器调用。若 native 环境也注入了 `MAGICX_CDP_URL` 则按第 1 条优先用它、失败同样直接报错。
4. 其它（未设置 `MAGICX_RUNTIME` 或非上述值）：注入了 `MAGICX_CDP_URL` 就优先连，探测失败回退本地 9222（探不到再本地拉起 Chromium）；未注入则直接探本地 9222。

### Token 获取方式

统一 token 获取（`get_token()`）按 `MAGICX_RUNTIME` 自动适配运行环境：

- 优先读取 `MAGICAL_ACCESS_TOKEN`；桌面客户端会从当前登录态注入该变量，token 不落盘。
- `MAGICX_RUNTIME=docker`：通过 sidecar `isa-pty-vnc-old`(容器内 `127.0.0.1:10913`) 的 `/api/auth/token` 中转获取（需要 `CLIENT_ID`）。
- 非 docker 环境未注入 `MAGICAL_ACCESS_TOKEN` 时直接抛 `TokenError`，不读取本地 token 文件。

## Python SDK

### 大模型文本处理

使用运行时注入的 `MAGICAL_URL_PREFIX` 与 `MAGICX_LLM_KEY` 调用大模型，可用于自然语言总结、分析、提取或改写。API key 仅通过请求体传递，不需要手动拼装 HTTP 请求：

```python
from isa_rpa import ask_llm

result = ask_llm("请用一句话总结这段文字：...")
print(result)
```

`ask_llm` 会按 `MAGICX_RUNTIME` 自动携带当前流程上下文，使用与 Responses API
一致的 `x-codex-turn-metadata` Header 传给下游 new-api：

- `docker`：流程编号取 Java 注入的 `CLIENT_ID`，流程名称由 Java 后端按编号查询；`CLIENT_ID` 缺失会直接报错。
- `windows|macos|linux`：从入口 `main.py` 所在目录的 `.magic/flow.json` 读取 `appNo` 与 `name`，不依赖当前工作目录。文件缺失、损坏或缺少 `appNo` 时两个字段均按空处理，LLM 调用仍会继续。
- 其它值或未注入 `MAGICX_RUNTIME`：视为异常运行环境并报错。

解析到非空流程编号时，SDK 还会读取 `RPA_TENANT_ID`，并通过统一 token 获取逻辑附带 `tenantno` / `accesstoken` 请求头及 metadata。调用方只需传入提示词，不需要传递流程信息。

### 浏览器会话

统一的 Playwright 浏览器入口，自动处理魔术师(magicx) docker 环境（连接已有 CDP）与本地开发环境（首次会启动有头浏览器并在 `.cache/isa-rpa/chromium-profile/` 保留登录态）。本地启动时按优先级解析可执行文件：用户系统已安装的 Google Chrome → 环境里已安装的 Playwright Chromium；都没有则提示安装 Google Chrome。SDK 不会自行下载浏览器内核。

本地兜底启动时，profile 目录按以下顺序解析：

1. 环境变量 `ISA_RPA_CHROME_PROFILE_DIR`（绝对路径，用户自定义）；
2. 入口脚本所在目录下的 `.cache/isa-rpa/chromium-profile/`（即 `main.py` 旁边，不受当前工作目录影响）；
3. 兜底用户家目录 `~/.cache/isa-rpa/chromium-profile/`（用于 REPL、`python -c`、Jupyter、pytest 等拿不到入口脚本路径的场景）。

```python
import asyncio
from isa_rpa import BrowserSession

async def main():
    async with BrowserSession() as context:
        page = context.pages[0] if context.pages else await context.new_page()
        await page.goto("https://example.com")
        print(await page.title())

asyncio.run(main())
```

退出 `async with` 时由 SDK 自行决定是否关闭浏览器：
- 通过 CDP 连接的共享浏览器**不关**
- 本地兜底启动的 Chromium 会被关掉，避免子进程泄漏

### 密码箱凭证

主推: `SecretClient`(对接 `/api/password-credential/*`, 与 web 端共用同一套 endpoint; 调用方明文传入 / 获取, SDK 内部用 SM2 公钥加密后上送, 网络上传输的密码字段始终是密文; name 主键、同租户唯一)。AI 代码统一使用 SDK：`search` 模糊搜索摘要，`query` 按名称精确读取，`create / update / delete` 管理密码。CLI `isa-rpa secret` 保留用于兼容旧脚本和人工运维。

数据模型: 名称 + 账号 + 密码 + 自定义字段 + 备注 + 共享范围; 密码与自定义字段至少其一非空。

```python
from isa_rpa import SecretClient

detail = SecretClient().query(name="github_alice_login")
username = detail.get("username")
password = detail.get("password")
for f in detail.get("customFields") or []:
    print(f["key"], f["value"])
```

模糊搜索时使用 `search`，不要把关键字传给精确查询的 `query`：

```python
matches = SecretClient().search(keyword="github")
for item in matches:
    print(item["credentialName"], item.get("username"))
```

CLI 调试输出:

```bash
ISA_RPA_SECRET_DEBUG=1 isa-rpa secret query --name github_alice_login
```

完整字段、命名规范、工作流程见 [magic-toolkit 的 password-vault Skill](../../plugins/magic-toolkit/skills/password-vault/SKILL.md)。

#### 旧 `CredentialClient`(已下线)

`CredentialClient` 对接的是隔壁旧 RPA 的凭据接口(RSA/DES、`assetName/assetValue` 模型)。旧 RPA 控制台的凭据服务已全部下线, `CredentialClient` 仅保留导入、构造、`close()` 和上下文管理协议的兼容壳; 旧方法名仍在, 但 `query` / `create` / `update` / `delete` / `list` 调用都会抛出 `CredentialError`, 明确提示改用魔术师密码箱。CLI `isa-rpa credential` 子命令组在 0.3.0 起已下线。所有场景一律改用 `SecretClient` / `isa-rpa secret`。

### DocAgent

```python
from isa_rpa import DocAgentClient

client = DocAgentClient(token="your-token")
files = client.list_files(project_code="PRJ001")
bills = client.list_bills(project_code="PRJ001")
```

### 验证码识别

```python
from isa_rpa import CaptchaClient

client = CaptchaClient(
    api_key="your-api-key",
    secret_key="your-secret-key",
)
```

## 适用范围

- OCR、验证码识别等能力可按各自接口配置独立使用。
- 浏览器会话、密码箱凭证、DocAgent、统一 token 获取等能力通常依赖 I-Search 平台环境。`BrowserSession` 的 CDP 解析按 `MAGICX_RUNTIME` 分流（详见上文「浏览器会话」环境变量说明）：纯本地开发（未设置 `MAGICX_RUNTIME`）下优先 `MAGICX_CDP_URL`、其次本地 9222、再探不到自动拉起 Chromium；docker 只连 `MAGICX_CDP_URL`、native（windows/macos/linux）改调客户端控制接口 `/browser/open`，两者探不到都直接报错、不本地兜底。
- 若你的运行环境不提供对应的网关、认证或共享组件，这些能力可能无法直接工作。

## 开发

```bash
uv sync
uv run pytest
uv run ruff check src/
```

## 短信箱（0.4.4 起）

通过一个 SDK 接口查询并消费最新有效短信，无需手机号、登录 Token 或签名：

```python
from isa_rpa import SmsInboxClient

# 直接填写用户提供的短信箱令牌，无需存入密码箱。
sms_key = "sms-xxxxxx"
with SmsInboxClient() as client:
    # 先建立服务端时间 anchor，再在业务页面触发发送短信。
    client.consume(sms_key=sms_key, key=["验证码", "京东"])
    # 触发发送后，使用同一个客户端轮询。
    message = client.wait_and_consume(sms_key=sms_key, key=["验证码", "京东"])
if message is not None:
    content = message["smsContent"]
```

`consume(sms_key, key=None)` 请求 `POST /api/v1/sms-codes/consume`，请求体包含
`smsKey`、自动解析到的 `flowId` 和可选的 `key`；无需租户号，也不依赖
`RPA_TENANT_ID`。`flowId` 在 Docker 环境取 `CLIENT_ID`，在
`windows|macos|linux` 环境取入口 `main.py` 同目录下
`.magic/flow.json` 的 `appNo`。非 Magic 运行环境或元数据缺失时暂不发送该字段，
兼容后端过渡期行为。

后端第一次收到 `(smsKey, flowId)` 时只建立时间 anchor，并固定返回
`CODE_NOT_FOUND`。因此正确顺序是：

1. 先调用一次 `consume()` 建立 anchor，并确认返回 `None`；
2. 再在业务页面触发发送短信；
3. 使用同一个 `SmsInboxClient` 调用 `wait_and_consume()` 轮询。

`key` 可传单个字符串，也可传字符串序列。多个关键字为 AND 语义，必须同时出现在
同一条短信中；不传关键字时消费该短信箱下最新一条有效短信。
返回含 `smsContent`、`createdAt`、`expireAt` 的字典；HTTP 404 / `CODE_NOT_FOUND`
返回 `None`。其他网络、业务或响应格式错误抛 `SmsInboxError`，含 `code`、`status_code`。
成功立即消费；`consume()` 不自动重试。网络超时可能已经消费，应停止确认结果。

从 0.4.5 起，需要等待短信时可使用：

```python
with SmsInboxClient() as client:
    message = client.wait_and_consume(
        sms_key, key="验证码", wait_timeout=60, poll_interval=2,
    )
```

`wait_and_consume(sms_key, key=None, *, wait_timeout=60, poll_interval=2)` 的 `key`
同样支持单个字符串或字符串序列。方法首次立即查询，
仅在暂无短信时继续轮询；成功立即返回，等待到期仍无短信返回 `None`。
`wait_timeout` 必须为有限正数，`poll_interval` 必须为至少 2 秒的有限数。
网络、业务及响应格式错误直接抛 `SmsInboxError`，不自动重试。
请求耗时计入等待预算，每次请求的各阶段 HTTP 超时取客户端配置与剩余预算的较小值。
到期不再发起请求；HTTP 超时按阶段生效，在途请求可能超过期限，已消费成功的短信仍返回。

服务前缀默认复用 `MAGICAL_URL_PREFIX`，也可显式传
`base_url="https://192.168.14.83/magical"`；`timeout=20` 为 HTTP 超时。
默认 `verify=False` 与现有内部网关 SDK 一致，可信证书环境可传 `verify=True`。

使用规范见 [短信箱 Skill](../../plugins/magic-toolkit/skills/sms-inbox/SKILL.md)。
