archive: 紫鸟/HubStudio 文档快照(3889 篇 Markdown)
来源:紫鸟帮助中心/博客/主站资讯 + HubStudio API 文档/帮助中心 + 三个官方 skill 仓库。 只入库 Markdown 与元数据;原始 HTML 与 tarball 留在本机。
This commit is contained in:
commit
322b7737eb
3976 files changed
+695714
No files matched your search
@@ -0,0 +1,151 @@
|
||||
# HubStudio ADB Connection Skill
|
||||
|
||||
This guide standardizes ADB connection workflows for HubStudio cloud phones.
|
||||
|
||||
## Scope
|
||||
|
||||
- Android 12 / Android 15: direct ADB connection
|
||||
- Android 13 / Android 14 / Android 15A: SSH tunnel + local ADB connection
|
||||
|
||||
## Preconditions
|
||||
|
||||
- HubStudio local API is reachable (`http://127.0.0.1:6873`)
|
||||
- Cloud phone is powered on
|
||||
- ADB access is enabled
|
||||
- Local `adb` command is installed and available in PATH
|
||||
|
||||
### If ADB is not installed
|
||||
|
||||
macOS (Homebrew):
|
||||
|
||||
```bash
|
||||
brew install android-platform-tools
|
||||
adb version
|
||||
```
|
||||
|
||||
Windows (winget):
|
||||
|
||||
```powershell
|
||||
winget install Google.PlatformTools
|
||||
adb version
|
||||
```
|
||||
|
||||
Manual install (all platforms):
|
||||
|
||||
- Download Android SDK Platform Tools from Google
|
||||
- Add `platform-tools` directory to PATH
|
||||
- Re-open terminal and run `adb version`
|
||||
|
||||
## Step 0: Enable ADB and Query Connection Info
|
||||
|
||||
1) Enable ADB:
|
||||
|
||||
```bash
|
||||
node hubstudio.js adbEnable <mobileId>
|
||||
```
|
||||
|
||||
2) Query ADB info:
|
||||
|
||||
```bash
|
||||
node hubstudio.js adbInfo <mobileId>
|
||||
```
|
||||
|
||||
Expected fields from response:
|
||||
- `adbIp`
|
||||
- `adbPort`
|
||||
- `adbPassword` (connection code)
|
||||
- For some versions/platforms, tunnel command fields may also be returned
|
||||
|
||||
## Workflow A: Android 12 / Android 15
|
||||
|
||||
### A1. Connect directly to cloud phone
|
||||
|
||||
```bash
|
||||
adb connect <adbIp>:<adbPort>
|
||||
```
|
||||
|
||||
### A2. Authenticate with connection code
|
||||
|
||||
```bash
|
||||
adb shell <adbPassword>
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```bash
|
||||
adb -s <adbIp>:<adbPort> shell <adbPassword>
|
||||
```
|
||||
|
||||
### A3. Verify connection
|
||||
|
||||
```bash
|
||||
adb devices
|
||||
```
|
||||
|
||||
## Workflow B: Android 13 / Android 14 / Android 15A
|
||||
|
||||
### B1. Create SSH tunnel (first terminal)
|
||||
|
||||
```bash
|
||||
ssh -oHostKeyAlgorithms=+ssh-rsa <sshUser>@<sshHost> -p <sshPort> -L <localPort>:adb-proxy:<remoteAdbPort> -Nf
|
||||
```
|
||||
|
||||
When prompted, input the SSH password from platform response.
|
||||
|
||||
### B2. Connect local forwarded ADB port (second terminal)
|
||||
|
||||
```bash
|
||||
adb connect localhost:<localPort>
|
||||
```
|
||||
|
||||
### B3. Verify connection
|
||||
|
||||
```bash
|
||||
adb devices
|
||||
```
|
||||
|
||||
Expected output:
|
||||
|
||||
```text
|
||||
List of devices attached
|
||||
localhost:<localPort> device
|
||||
```
|
||||
|
||||
## Complete Operation Checklist
|
||||
|
||||
- [ ] Cloud phone is powered on
|
||||
- [ ] `enableAdb=true` applied successfully
|
||||
- [ ] `list-adb` returns non-null ADB fields
|
||||
- [ ] Chosen proper workflow by Android version
|
||||
- [ ] `adb devices` shows connected device
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
adb devices
|
||||
adb shell
|
||||
adb shell pm list packages
|
||||
adb install app.apk
|
||||
adb uninstall <package.name>
|
||||
adb reboot
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `adb connect` timeout or refused
|
||||
|
||||
- Confirm cloud phone status is running
|
||||
- Confirm ADB is enabled
|
||||
- Re-query `list-adb` to ensure `adbIp/adbPort` is valid
|
||||
- Check local firewall/network policy
|
||||
|
||||
### SSH tunnel mode cannot connect
|
||||
|
||||
- Verify tunnel process is alive
|
||||
- Confirm `<localPort>` is not occupied
|
||||
- Rebuild SSH tunnel and retry `adb connect localhost:<localPort>`
|
||||
|
||||
### Connected but shell auth fails
|
||||
|
||||
- Recheck `adbPassword` from latest `list-adb` response
|
||||
- Retry on specific device with `adb -s ... shell <code>`
|
||||
@@ -0,0 +1,606 @@
|
||||
# OpenClaw + HubStudio + Playwright 实操教程(完整可执行)
|
||||
|
||||
---
|
||||
|
||||
## 1. 三个工具怎么分工
|
||||
|
||||
- `OpenClaw`:作为统一入口和运行环境(网关、控制台、任务入口)
|
||||
- `HubStudio`:提供本地 API,负责启动/关闭浏览器环境
|
||||
- `Playwright`:连接 HubStudio 打开的浏览器并执行自动化动作
|
||||
|
||||
一句话理解:**OpenClaw 管流程,HubStudio 管环境,Playwright 管页面操作。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 官方文档入口
|
||||
|
||||
- OpenClaw 入门与安装:<https://docs.openclaw.ai/start/getting-started>
|
||||
- HubStudio 接口文档:<https://api-docs.hubstudio.cn/>
|
||||
- github HubStudio Skill 项目地址:<https://github.com/hubstudio-Max/hubstudio-skill>
|
||||
---
|
||||
|
||||
## 3. 第一次配置(只需做一次)
|
||||
|
||||
在终端中执行:
|
||||
|
||||
```bash
|
||||
cd "/Users/kxc/Documents/workspace/projects/skills/hubstudio-skill"
|
||||
```
|
||||
|
||||
### 3.1 检查基础工具
|
||||
|
||||
```bash
|
||||
node --version
|
||||
openclaw --version
|
||||
node hubstudio.js help
|
||||
```
|
||||
|
||||
### 3.2 初始化 OpenClaw(关键)
|
||||
|
||||
按 OpenClaw 官方推荐,先做 onboarding:
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
如果网关正常,你会看到网关在线状态。
|
||||
|
||||
### 3.3 安装 Playwright
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm install playwright
|
||||
```
|
||||
|
||||
校验:
|
||||
|
||||
```bash
|
||||
node -e "const { chromium } = require('playwright'); console.log('playwright ok:', !!chromium)"
|
||||
```
|
||||
|
||||
输出 `playwright ok: true` 即可继续。
|
||||
|
||||
---
|
||||
|
||||
## 4. 结合 OpenClaw 的标准执行流程
|
||||
|
||||
本项目已经提供脚本:
|
||||
|
||||
- `playwright_hubstudio_baidu_demo.js`
|
||||
|
||||
推荐按下面顺序执行,这样就体现了 OpenClaw 的结合方式。
|
||||
|
||||
### Step 1:先确认 OpenClaw 网关与状态
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
openclaw status
|
||||
```
|
||||
|
||||
### Step 2:可选,打开 OpenClaw 控制台(便于观察)
|
||||
|
||||
```bash
|
||||
openclaw dashboard
|
||||
```
|
||||
|
||||
> 这一步是可选的,主要用于你在浏览器中查看 OpenClaw 控制界面。
|
||||
|
||||
### Step 3:执行自动化任务脚本
|
||||
|
||||
```bash
|
||||
node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
如果你要指定其他环境编号:
|
||||
|
||||
```bash
|
||||
CONTAINER_CODE="1502764609" node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 这个脚本里 OpenClaw 是怎么“参与”的
|
||||
|
||||
很多同学会问:脚本里没写 `openclaw run ...`,怎么算结合?
|
||||
|
||||
这里的结合点是:
|
||||
|
||||
1. 你先用 `openclaw onboard` 和 `openclaw gateway status` 建立统一运行入口
|
||||
2. 你可通过 `openclaw dashboard` 观察控制面
|
||||
3. 业务自动化脚本在同一工程环境执行,OpenClaw 负责“控制层”和“入口层”
|
||||
4. 具体 API 调用与浏览器操作由 `hubstudio.js + Playwright` 完成
|
||||
|
||||
这是一种稳定、常见的“控制层与执行层分离”方式。
|
||||
|
||||
---
|
||||
|
||||
## 6. 成功后你会看到什么
|
||||
|
||||
终端会输出类似:
|
||||
|
||||
- `== [1/6] 启动 HubStudio 环境 ==`
|
||||
- `环境启动成功,CDP 端口:xxxxx`
|
||||
- `== [4/6] 点击第一条搜索结果 ==`
|
||||
- `== [5/6] 提取页面内容并生成摘要 ==`
|
||||
- `摘要文件已生成:.../summary_hubstudio_baidu_playwright.txt`
|
||||
- `== [6/6] 关闭 HubStudio 环境 ==`
|
||||
|
||||
---
|
||||
|
||||
## 7. 结果文件说明
|
||||
|
||||
脚本会生成:
|
||||
|
||||
- `summary_hubstudio_baidu_playwright.txt`
|
||||
|
||||
文件内包含:
|
||||
|
||||
- 首条搜索结果标题/链接
|
||||
- 跳转后页面标题/URL
|
||||
- 页面摘要(中文)
|
||||
- 抓取正文片段(前 1500 字符)
|
||||
|
||||
---
|
||||
|
||||
## 8. 一条命令跑全流程(给同事复制)
|
||||
|
||||
```bash
|
||||
cd "/Users/kxc/Documents/workspace/projects/skills/hubstudio-skill" && \
|
||||
openclaw gateway status && \
|
||||
node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 常见问题
|
||||
|
||||
### Q1:`openclaw gateway status` 显示异常
|
||||
|
||||
先重新执行:
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
### Q2:脚本启动环境失败
|
||||
|
||||
检查:
|
||||
|
||||
- HubStudio 客户端是否在线并登录
|
||||
- 环境编号 `1502764609` 是否属于当前账号
|
||||
- 本地 API 是否可访问(默认 `http://127.0.0.1:6873`)
|
||||
|
||||
### Q3:脚本提示缺少 Playwright
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm install playwright
|
||||
```
|
||||
|
||||
### Q4:第一条链接点击后没跳转
|
||||
|
||||
这是百度页面结构或弹窗变化导致,通常重跑一次即可。
|
||||
如果仍失败,可把脚本中的等待时间适当调大(如 `3500` 调到 `5000`)。
|
||||
|
||||
---
|
||||
|
||||
## 10. 推荐的日常使用顺序
|
||||
|
||||
每天执行任务时,按这个顺序最稳:
|
||||
|
||||
1. `openclaw gateway status`
|
||||
2. `node playwright_hubstudio_baidu_demo.js`
|
||||
3. 检查 `summary_hubstudio_baidu_playwright.txt`
|
||||
|
||||
这样就实现了“通过 OpenClaw 统一入口 + HubStudio 环境 + Playwright 执行”的完整闭环。
|
||||
# OpenClaw + HubStudio + Playwright 完整示例教程(零基础版)
|
||||
|
||||
> 本教程是“只保留可成功路径”的版本。
|
||||
> 你只需要照着复制命令,就能完成:
|
||||
> 启动 HubStudio 环境 `1502764609` -> 打开百度搜索 `HubStudio` -> 点击第一条 -> 抓取内容并生成摘要文件 -> 关闭环境。
|
||||
|
||||
---
|
||||
|
||||
## 1. 你要准备什么
|
||||
|
||||
### 1.1 已安装软件
|
||||
|
||||
- HubStudio 客户端(并且可正常登录)
|
||||
- Node.js(建议 22+)
|
||||
- OpenClaw CLI
|
||||
|
||||
### 1.2 官方文档入口(建议先收藏)
|
||||
|
||||
- OpenClaw 安装与入门:<https://docs.openclaw.ai/start/getting-started>
|
||||
- HubStudio 接口文档:<https://api-docs.hubstudio.cn/>
|
||||
- github <https://github.com/hubstudio-Max/hubstudio-skill>
|
||||
---
|
||||
|
||||
## 2. 第一次操作前检查(3 分钟)
|
||||
|
||||
在终端里执行下面 4 条命令,确认环境是通的:
|
||||
|
||||
```bash
|
||||
cd "/Users/kxc/Documents/workspace/projects/skills/hubstudio-skill"
|
||||
node --version
|
||||
openclaw --version
|
||||
node hubstudio.js help
|
||||
```
|
||||
|
||||
看到版本号和帮助信息就可以继续。
|
||||
|
||||
---
|
||||
|
||||
## 3. 安装 Playwright(本教程用它做自动化)
|
||||
|
||||
> 注意:本教程不使用 `agent-browser`,统一改为 `Playwright`。
|
||||
|
||||
在项目目录执行:
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm install playwright
|
||||
```
|
||||
|
||||
安装完成后,测试一下:
|
||||
|
||||
```bash
|
||||
node -e "const { chromium } = require('playwright'); console.log('playwright ok:', !!chromium)"
|
||||
```
|
||||
|
||||
输出 `playwright ok: true` 即表示安装成功。
|
||||
|
||||
---
|
||||
|
||||
## 4. 一次性创建自动化脚本(推荐)
|
||||
|
||||
本项目里已经放好脚本文件:
|
||||
|
||||
- `playwright_hubstudio_baidu_demo.js`
|
||||
|
||||
这个脚本会自动做 6 件事:
|
||||
|
||||
1. 调用 HubStudio API 启动环境 `1502764609`
|
||||
2. 读取返回里的 `debuggingPort`
|
||||
3. 用 Playwright 通过 CDP 连接到该环境
|
||||
4. 打开百度,搜索 `HubStudio`,点击第一条结果
|
||||
5. 抓取结果页文本,并生成摘要文件
|
||||
6. 调用 HubStudio API 关闭环境
|
||||
|
||||
---
|
||||
|
||||
## 5. 直接运行(复制即用)
|
||||
|
||||
在项目目录执行:
|
||||
|
||||
```bash
|
||||
node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
如果你要换环境编号,可以这样执行:
|
||||
|
||||
```bash
|
||||
CONTAINER_CODE="1502764609" node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 运行成功后你会看到什么
|
||||
|
||||
终端会依次打印:
|
||||
|
||||
- `== [1/6] 启动 HubStudio 环境 ==`
|
||||
- `环境启动成功,CDP 端口:xxxx`
|
||||
- `== [3/6] 打开百度并搜索 HubStudio ==`
|
||||
- `== [4/6] 点击第一条搜索结果 ==`
|
||||
- `== [5/6] 提取页面内容并生成摘要 ==`
|
||||
- `摘要文件已生成:.../summary_hubstudio_baidu_playwright.txt`
|
||||
- `== [6/6] 关闭 HubStudio 环境 ==`
|
||||
|
||||
---
|
||||
|
||||
## 7. 结果文件在哪里
|
||||
|
||||
脚本会在当前目录生成:
|
||||
|
||||
- `summary_hubstudio_baidu_playwright.txt`
|
||||
|
||||
文件内包含:
|
||||
|
||||
- 首条结果标题
|
||||
- 首条结果链接
|
||||
- 跳转后页面标题
|
||||
- 跳转后页面 URL
|
||||
- 自动生成的中文摘要
|
||||
- 抓取到的正文片段(前 1500 字符)
|
||||
|
||||
---
|
||||
|
||||
## 8. 本教程对应的真实示例结果(已验证)
|
||||
|
||||
按本教程实际执行后,典型结果如下:
|
||||
|
||||
- 环境 `1502764609` 启动成功(`code: 0`)
|
||||
- 成功拿到 `debuggingPort` 并完成 CDP 连接
|
||||
- 成功执行百度搜索并点击第一条
|
||||
- 成功提取页面文本并输出摘要文件
|
||||
- 环境关闭成功(`code: 0`)
|
||||
|
||||
---
|
||||
|
||||
## 9. 常见问题(新手友好)
|
||||
|
||||
### Q1:运行时提示 `Cannot find module 'playwright'`
|
||||
|
||||
说明还没安装依赖,回到第 3 节执行:
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm install playwright
|
||||
```
|
||||
|
||||
### Q2:脚本卡在“启动 HubStudio 环境”
|
||||
|
||||
请确认:
|
||||
|
||||
- HubStudio 客户端处于打开状态
|
||||
- 账号已登录并已开通VIP
|
||||
- 环境编号 `1502764609` 在当前账号下可用
|
||||
|
||||
### Q3:百度第一页结构变化,点不到第一条
|
||||
|
||||
这是网页结构偶尔变化导致,脚本里已经用了多个候选选择器。
|
||||
通常重试一次就可以;如仍失败,可把脚本中的等待时间 `3500` 改成 `5000` 再试。
|
||||
|
||||
### Q4:我想给同事复用
|
||||
|
||||
只要把这 3 个文件给同事并按教程执行即可:
|
||||
|
||||
- `hubstudio.js`
|
||||
- `commands.generated.json`
|
||||
- `playwright_hubstudio_baidu_demo.js`
|
||||
|
||||
---
|
||||
|
||||
## 10. 你可以复制给同事的“最短步骤”
|
||||
|
||||
```bash
|
||||
cd "/Users/kxc/Documents/workspace/projects/skills/hubstudio-skill"
|
||||
npm init -y
|
||||
npm install playwright
|
||||
node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
如果终端出现摘要文件路径,说明流程成功。
|
||||
# OpenClaw + HubStudio + agent-browser 新手实操教程
|
||||
|
||||
> 目标:按这份文档一步一步操作,完成以下任务:
|
||||
> 启动环境 `1502764609` -> 打开百度搜索 `HubStudio` -> 点击第一条结果 -> 提取页面内容并给出总结 -> 关闭环境。
|
||||
|
||||
---
|
||||
|
||||
## 1) 这份文档适合谁
|
||||
|
||||
- 第一次接触命令行、只会复制粘贴也能操作
|
||||
- 已经安装好 `OpenClaw`、`HubStudio`、`agent-browser`
|
||||
- 希望有一条“从开始到结束”的固定流程,不需要自己拼命令
|
||||
|
||||
---
|
||||
|
||||
## 2) 你会得到什么结果
|
||||
|
||||
跑完后你会拿到 4 类结果:
|
||||
|
||||
- 环境是否成功启动(会看到 `code: 0`)
|
||||
- 当前连接的页面标题和网址
|
||||
- 目标页面的正文片段(用于总结)
|
||||
- 自动关闭环境,避免环境一直占用
|
||||
|
||||
---
|
||||
|
||||
## 3) 开始前准备(只做一次)
|
||||
|
||||
### 3.1 打开 HubStudio 客户端
|
||||
|
||||
请先确认:
|
||||
|
||||
- HubStudio 客户端是打开状态
|
||||
- 当前账号已登录并已开通VIP
|
||||
- 环境 `1502764609` 在你的账号下可见
|
||||
|
||||
### 3.2 打开终端并进入项目目录
|
||||
|
||||
复制执行:
|
||||
|
||||
```bash
|
||||
cd "/Users/kxc/Documents/workspace/projects/skills/hubstudio-skill"
|
||||
```
|
||||
|
||||
### 3.3 快速检查三个工具
|
||||
|
||||
复制执行:
|
||||
|
||||
```bash
|
||||
openclaw --version
|
||||
agent-browser --version
|
||||
node hubstudio.js help
|
||||
```
|
||||
|
||||
看到版本号或帮助信息即可,不用纠结输出细节。
|
||||
|
||||
---
|
||||
|
||||
## 4) 一键执行脚本(最推荐)
|
||||
|
||||
这一节是给“希望最稳最省事”的用法。
|
||||
你只需要创建脚本一次,后面每次运行一条命令就行。
|
||||
|
||||
### 4.1 创建脚本文件
|
||||
|
||||
在项目目录新建文件 `examples_baidu_hubstudio.sh`,内容如下:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CONTAINER_CODE="1502764609"
|
||||
|
||||
echo "========== [1/6] 启动 HubStudio 环境 =========="
|
||||
START_JSON=$(node hubstudio.js browserStart "$CONTAINER_CODE")
|
||||
echo "$START_JSON" | jq '{code,msg,debuggingPort:(.payload.data.debuggingPort // null)}'
|
||||
|
||||
CDP_PORT=$(echo "$START_JSON" | jq -r '.payload.data.debuggingPort // empty')
|
||||
if [ -z "$CDP_PORT" ]; then
|
||||
echo "未拿到 debuggingPort,流程终止"
|
||||
exit 1
|
||||
fi
|
||||
echo "CDP_PORT=$CDP_PORT"
|
||||
|
||||
echo "========== [2/6] OpenClaw 状态确认 =========="
|
||||
openclaw status || true
|
||||
|
||||
echo "========== [3/6] 打开百度搜索 HubStudio =========="
|
||||
agent-browser --cdp "$CDP_PORT" open "https://www.baidu.com/s?wd=HubStudio"
|
||||
agent-browser --cdp "$CDP_PORT" wait 3500
|
||||
|
||||
echo "========== [4/6] 点击第一条搜索结果 =========="
|
||||
CLICK_JSON=$(agent-browser --cdp "$CDP_PORT" --json eval "(() => {
|
||||
const a = document.querySelector('#content_left .result h3 a, #content_left .c-container h3 a, h3 a');
|
||||
if (!a) return { ok:false, reason:'no-result-link' };
|
||||
const href = a.href;
|
||||
const title = a.innerText.trim();
|
||||
a.click();
|
||||
return { ok:true, title, href };
|
||||
})()")
|
||||
echo "$CLICK_JSON" | jq .
|
||||
|
||||
agent-browser --cdp "$CDP_PORT" wait 6000
|
||||
|
||||
echo "========== [5/6] 提取页面信息并生成摘要 =========="
|
||||
TITLE_JSON=$(agent-browser --cdp "$CDP_PORT" --json get title)
|
||||
URL_JSON=$(agent-browser --cdp "$CDP_PORT" --json get url)
|
||||
CONTENT_JSON=$(agent-browser --cdp "$CDP_PORT" --json eval "(() => {
|
||||
const text = (document.body?.innerText || '').replace(/\\s+/g, ' ').trim();
|
||||
return text.slice(0, 3500);
|
||||
})()")
|
||||
|
||||
echo "$TITLE_JSON" | jq .
|
||||
echo "$URL_JSON" | jq .
|
||||
|
||||
TITLE=$(echo "$TITLE_JSON" | jq -r '.data.title // "unknown"')
|
||||
URL=$(echo "$URL_JSON" | jq -r '.data.url // "unknown"')
|
||||
EXCERPT=$(echo "$CONTENT_JSON" | jq -r '.data.result // ""')
|
||||
|
||||
SUMMARY_FILE="summary_hubstudio_baidu.txt"
|
||||
{
|
||||
echo "页面标题: $TITLE"
|
||||
echo "页面地址: $URL"
|
||||
echo ""
|
||||
echo "页面内容摘要:"
|
||||
echo "- 该页面是 Hubstudio 相关的下载/介绍页。"
|
||||
echo "- 主要强调多账号环境、指纹浏览器、云手机等能力。"
|
||||
echo "- 页面通常包含版本信息、下载入口和安装说明。"
|
||||
echo ""
|
||||
echo "抓取片段(前3500字符):"
|
||||
echo "$EXCERPT"
|
||||
} > "$SUMMARY_FILE"
|
||||
|
||||
echo "摘要已写入: $SUMMARY_FILE"
|
||||
|
||||
echo "========== [6/6] 关闭环境 =========="
|
||||
node hubstudio.js browserStop "$CONTAINER_CODE" | jq '{code,msg}'
|
||||
|
||||
echo "全部完成。"
|
||||
```
|
||||
|
||||
### 4.2 给脚本执行权限
|
||||
|
||||
```bash
|
||||
chmod +x examples_baidu_hubstudio.sh
|
||||
```
|
||||
|
||||
### 4.3 运行脚本
|
||||
|
||||
```bash
|
||||
./examples_baidu_hubstudio.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) 每一步成功的判断标准
|
||||
|
||||
- 启动成功:输出里有 `code: 0`,且出现 `CDP_PORT=xxxx`
|
||||
- 点击成功:`CLICK_JSON` 里 `ok: true`
|
||||
- 跳转成功:`get title` 和 `get url` 不是百度搜索页
|
||||
- 提取成功:生成文件 `summary_hubstudio_baidu.txt`
|
||||
- 收尾成功:`browserStop` 输出里 `code: 0`
|
||||
|
||||
---
|
||||
|
||||
## 6) 本文档对应的实测结果(已通过)
|
||||
|
||||
同样流程已经在本机实际跑通,关键结果如下:
|
||||
|
||||
- 环境 `1502764609` 启动成功,返回 `code: 0`
|
||||
- 返回过可用的 `debuggingPort`(示例中曾拿到 `60343`)
|
||||
- 成功打开百度并点击首条结果
|
||||
- 实际进入页面标题:`Hubstudio下载-Hubstudio最新版下载V3.51.0`
|
||||
- 成功提取页面正文并生成摘要
|
||||
- 最后环境成功关闭,返回 `code: 0`
|
||||
|
||||
---
|
||||
|
||||
## 7) 常见问题(小白版)
|
||||
|
||||
### 7.1 运行脚本时提示命令不存在
|
||||
|
||||
说明工具没有安装到终端环境里。分别执行:
|
||||
|
||||
```bash
|
||||
openclaw --version
|
||||
agent-browser --version
|
||||
node --version
|
||||
```
|
||||
|
||||
哪个报错就先安装哪个。
|
||||
|
||||
### 7.2 启动环境拿不到 `CDP_PORT`
|
||||
|
||||
优先检查:
|
||||
|
||||
- HubStudio 客户端是否在线并已登录
|
||||
- 环境编号是否是 `1502764609`
|
||||
- 是否是当前账号下的环境
|
||||
|
||||
### 7.3 点击第一条失败(`no-result-link`)
|
||||
|
||||
这是网页加载慢或页面结构变化导致。
|
||||
直接重新运行脚本,通常可恢复;也可把等待时间从 `3500` 调到 `5000`。
|
||||
|
||||
### 7.4 结果页不是你预期的网站
|
||||
|
||||
百度首条结果会随地区/时间变化,这是正常现象。
|
||||
流程目标是“点击首条并提取内容”,不是固定某个域名。
|
||||
|
||||
---
|
||||
|
||||
## 8) 你可以直接复制的最短命令版
|
||||
|
||||
如果你不想建脚本,直接按顺序执行以下命令也可以:
|
||||
|
||||
```bash
|
||||
cd "/Users/kxc/Documents/workspace/projects/skills/hubstudio-skill"
|
||||
START_JSON=$(node hubstudio.js browserStart 1502764609)
|
||||
CDP_PORT=$(echo "$START_JSON" | jq -r '.payload.data.debuggingPort // empty')
|
||||
agent-browser --cdp "$CDP_PORT" open "https://www.baidu.com/s?wd=HubStudio"
|
||||
agent-browser --cdp "$CDP_PORT" wait 3500
|
||||
agent-browser --cdp "$CDP_PORT" --json eval "(() => { const a = document.querySelector('#content_left .result h3 a, #content_left .c-container h3 a, h3 a'); if(!a) return {ok:false}; a.click(); return {ok:true}; })()"
|
||||
agent-browser --cdp "$CDP_PORT" wait 6000
|
||||
agent-browser --cdp "$CDP_PORT" --json get title
|
||||
agent-browser --cdp "$CDP_PORT" --json get url
|
||||
node hubstudio.js browserStop 1502764609
|
||||
```
|
||||
@@ -0,0 +1,340 @@
|
||||
# HubStudio OpenAPI Skill
|
||||
|
||||
这是一个面向 `HubStudio` 本地 API 的技能项目,目标是让你可以用统一方式完成:
|
||||
|
||||
- API 能力检索(有哪些接口、每个接口做什么)
|
||||
- 请求参数校验(必填项、类型、约束)
|
||||
- 自动化调用(浏览器环境、云手机、账号、分组等)
|
||||
- 常见问题排查(HTTP 成功但业务失败、环境无法启动等)
|
||||
|
||||
项目核心定位:**把 `openapi.yaml` 的接口能力,转成可查询、可执行、可自动化的工程化工具链。**
|
||||
|
||||
---
|
||||
|
||||
## 项目提供的 Skill 能力
|
||||
|
||||
## 1) 全量接口查询能力
|
||||
|
||||
- 基于 `openapi.yaml` 提供 HubStudio 全量接口定义
|
||||
- 当前生成命令总数:`56`(见 `commands.generated.json`)
|
||||
- 覆盖业务域:
|
||||
- 浏览器环境
|
||||
- 云手机
|
||||
- 环境管理
|
||||
- 平台账号管理
|
||||
- 分组管理
|
||||
|
||||
你可以快速回答类似问题:
|
||||
|
||||
- “打开环境接口叫什么?请求体怎么写?”
|
||||
- “批量开启云手机需要哪些字段?”
|
||||
- “某接口返回字段 `debuggingPort` 是什么含义?”
|
||||
|
||||
---
|
||||
|
||||
## 2) 请求/响应字段解释能力
|
||||
|
||||
Skill 可以按接口粒度解释:
|
||||
|
||||
- 请求参数(query/header/body)
|
||||
- 必填字段与可选字段
|
||||
- 字段类型与含义
|
||||
- 响应 `code/msg/data` 结构
|
||||
|
||||
这使得你可以在自动化流程里先做参数检查,再发请求,减少无效调用。
|
||||
|
||||
---
|
||||
|
||||
## 3) 本地 CLI 执行能力(开箱即用)
|
||||
|
||||
项目内置 `hubstudio.js`,可直接调用 HubStudio 本地 API:
|
||||
|
||||
```bash
|
||||
node hubstudio.js help
|
||||
node hubstudio.js list
|
||||
node hubstudio.js browserStart 1502764609
|
||||
node hubstudio.js browserStatus 1502764609
|
||||
node hubstudio.js browserStop 1502764609
|
||||
```
|
||||
|
||||
也支持生成命令直调:
|
||||
|
||||
```bash
|
||||
node hubstudio.js postV1BrowserStart --body '{"containerCode":"1502764609"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) 浏览器自动化接入能力(Playwright)
|
||||
|
||||
项目已提供端到端示例脚本:`playwright_hubstudio_baidu_demo.js`,可完成:
|
||||
|
||||
1. 启动 HubStudio 环境
|
||||
2. 获取 `debuggingPort`
|
||||
3. 使用 Playwright 通过 CDP 连接该浏览器
|
||||
4. 打开百度并搜索 `HubStudio`
|
||||
5. 点击第一条结果并提取正文
|
||||
6. 生成摘要文件并关闭环境
|
||||
|
||||
执行命令:
|
||||
|
||||
```bash
|
||||
node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
输出结果示例文件:
|
||||
|
||||
- `summary_hubstudio_baidu_playwright.txt`
|
||||
|
||||
---
|
||||
|
||||
## 5) ADB 连接辅助能力(云手机)
|
||||
|
||||
项目提供云手机 ADB 连接专项说明:`ADB_CONNECTION_GUIDE.md`,用于:
|
||||
|
||||
- 开启 ADB
|
||||
- 查询连接信息
|
||||
- 根据 Android 版本选择连接模式
|
||||
- 验证 `adb devices`
|
||||
|
||||
---
|
||||
|
||||
## 项目文件说明
|
||||
|
||||
- `SKILL.md`:Skill 主说明(使用指引、调用建议、FAQ)
|
||||
- `openapi.yaml`:HubStudio OpenAPI 源定义
|
||||
- `reference.md`:从 OpenAPI 生成的本地接口参考文档
|
||||
- `commands.generated.json`:从 OpenAPI 生成的 CLI 命令映射
|
||||
- `hubstudio.js`:统一命令行入口(别名 + 生成命令)
|
||||
- `playwright_hubstudio_baidu_demo.js`:Playwright 端到端自动化示例
|
||||
- `ADB_CONNECTION_GUIDE.md`:云手机 ADB 接入指南
|
||||
- `OPENCLAW_AGENT_BROWSER_TUTORIAL.md`:面向新手的实操教程(当前已切换为 Playwright 流程)
|
||||
|
||||
---
|
||||
|
||||
## 快速开始(建议流程)
|
||||
|
||||
### 1. 环境准备
|
||||
|
||||
```bash
|
||||
# 先进入项目根目录(README 所在目录)
|
||||
cd ./
|
||||
node --version
|
||||
openclaw --version
|
||||
```
|
||||
|
||||
### 2. 安装依赖(如需运行 Playwright 示例)
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm install playwright
|
||||
```
|
||||
|
||||
### 3. 验证 HubStudio API 可用
|
||||
|
||||
```bash
|
||||
node hubstudio.js browserStatus 1502764609
|
||||
```
|
||||
|
||||
若返回 `code: 0` 说明 API 可正常调用。
|
||||
|
||||
### 4. 运行示例
|
||||
|
||||
```bash
|
||||
node playwright_hubstudio_baidu_demo.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安装 HubStudio Skill 到 OpenClaw(详细步骤)
|
||||
|
||||
这一节专门说明:如何把当前项目作为一个 Skill 安装到 OpenClaw 并让 OpenClaw 识别。
|
||||
|
||||
### 1) 先确认 OpenClaw 已初始化
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
### 2) 创建 OpenClaw 的 workspace skills 目录
|
||||
|
||||
```bash
|
||||
mkdir -p "$HOME/.openclaw/workspace/skills"
|
||||
```
|
||||
|
||||
### 3) 安装方式二选一
|
||||
|
||||
#### 方式 A(推荐):软链接,方便你持续更新
|
||||
|
||||
适合开发调试:你修改当前项目文件,OpenClaw 立即读取最新内容。
|
||||
|
||||
```bash
|
||||
ln -sfn "./" "$HOME/.openclaw/workspace/skills/hubstudio"
|
||||
```
|
||||
|
||||
#### 方式 B:复制一份稳定版本
|
||||
|
||||
适合“先固定版本再使用”。
|
||||
|
||||
```bash
|
||||
rm -rf "$HOME/.openclaw/workspace/skills/hubstudio"
|
||||
cp -R "./" "$HOME/.openclaw/workspace/skills/hubstudio"
|
||||
```
|
||||
|
||||
### 4) 让 OpenClaw 刷新技能状态
|
||||
|
||||
```bash
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
### 5) 验证 Skill 是否安装成功
|
||||
|
||||
```bash
|
||||
openclaw skills check
|
||||
openclaw skills list
|
||||
openclaw skills info hubstudio-openapi
|
||||
```
|
||||
|
||||
成功时你应看到:
|
||||
|
||||
- `hubstudio-openapi` 状态为 `ready`
|
||||
- 来源显示为 `openclaw-workspace`
|
||||
- 路径类似:`~/.openclaw/workspace/skills/hubstudio/SKILL.md`
|
||||
|
||||
### 6) 安装后建议做一次能力验证
|
||||
|
||||
```bash
|
||||
cd ./
|
||||
node hubstudio.js list
|
||||
node hubstudio.js browserStatus 1502764609
|
||||
```
|
||||
|
||||
如果你在 OpenClaw 中调用与 HubStudio API 相关任务时,模型能够命中 `hubstudio-openapi` 这个 skill,即说明集成生效。
|
||||
|
||||
---
|
||||
|
||||
## 响应判定规范(非常重要)
|
||||
|
||||
HubStudio 常见返回结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "Success",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
判定规则:
|
||||
|
||||
- `HTTP 200` 不代表业务成功
|
||||
- 以 `code` 为准:
|
||||
- `code = 0`:成功
|
||||
- `code != 0`:业务失败(参数、权限、资源状态等)
|
||||
|
||||
---
|
||||
|
||||
## 参数校验模式(团队统一建议)
|
||||
|
||||
`hubstudio.js` 已内置 OpenAPI 驱动的参数强校验,支持两种模式:
|
||||
|
||||
- `strict=true`(默认)
|
||||
- 开启必填、类型、范围、格式、枚举校验
|
||||
- 拒绝未知字段(如拼错字段名)
|
||||
- `strict=false`
|
||||
- 必填、类型、范围、格式、枚举仍然校验
|
||||
- 仅放过未知字段(用于调试或灰度联调)
|
||||
|
||||
### 1) 默认严格模式(推荐生产)
|
||||
|
||||
```bash
|
||||
node hubstudio.js postV1BrowserStart --body '{"containerCode":"1502764609"}'
|
||||
```
|
||||
|
||||
### 2) 临时切换宽松模式(仅用于排障)
|
||||
|
||||
```bash
|
||||
node hubstudio.js postV1BrowserStart --strict false --body '{"containerCode":"1502764609","x":1}'
|
||||
```
|
||||
|
||||
### 3) 通过环境变量统一设置模式
|
||||
|
||||
```bash
|
||||
# 关闭严格模式(当前 shell 会话生效)
|
||||
export HUBSTUDIO_STRICT_VALIDATION=false
|
||||
node hubstudio.js postV1BrowserStart --body '{"containerCode":"1502764609","x":1}'
|
||||
|
||||
# 恢复严格模式
|
||||
export HUBSTUDIO_STRICT_VALIDATION=true
|
||||
```
|
||||
|
||||
### 4) 校验失败返回格式(标准化)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Request validation failed",
|
||||
"command": "postV1BrowserStart",
|
||||
"fieldErrors": [
|
||||
{
|
||||
"field": "body.containerCode",
|
||||
"reason": "required field missing or empty",
|
||||
"expected": "required",
|
||||
"actualType": "missing"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5) 团队使用建议
|
||||
|
||||
- 日常开发与生产执行统一用默认严格模式
|
||||
- 只在定位线上疑难问题时,短时使用 `--strict false`
|
||||
- 排障结束后务必恢复严格模式,避免脏字段长期进入调用链
|
||||
|
||||
---
|
||||
|
||||
## 典型使用场景
|
||||
|
||||
- 批量管理浏览器环境(开关、状态、窗口排列)
|
||||
- 云手机应用安装/启动/停止自动化
|
||||
- ADB 开启与连接状态查询
|
||||
- 将 HubStudio 接口能力接入 OpenClaw/脚本工作流
|
||||
- 用 Playwright 基于 HubStudio 环境做网页自动化
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1) 接口返回 200 但任务没成功
|
||||
|
||||
请检查返回体中的 `code` 和 `msg`,不要只看 HTTP 状态码。
|
||||
|
||||
### 2) 启动环境失败
|
||||
|
||||
优先检查:
|
||||
|
||||
- HubStudio 客户端是否登录
|
||||
- 是否开通VIP
|
||||
- `containerCode` 是否存在且属于当前账号
|
||||
- 本地服务地址是否是 `http://127.0.0.1:6873` 端口可能存在变动需要检查
|
||||
|
||||
### 3) 自动化脚本连接不上浏览器
|
||||
|
||||
请确认:
|
||||
|
||||
- `browserStart` 后返回了 `debuggingPort`
|
||||
- 环境未提前关闭
|
||||
- 本机端口未被拦截
|
||||
|
||||
---
|
||||
|
||||
## 参考文档
|
||||
|
||||
- OpenClaw Getting Started: <https://docs.openclaw.ai/start/getting-started>
|
||||
- HubStudio API Docs: <https://api-docs.hubstudio.cn/>
|
||||
- 项目内接口参考:`reference.md`
|
||||
@@ -0,0 +1,312 @@
|
||||
---
|
||||
name: hubstudio-openapi
|
||||
description: HubStudio OpenAPI skill for full endpoint lookup, request/response field explanation, and parameter constraint checking. Use when querying HubStudio API interfaces, validating request payloads, or building automation against openapi.yaml.
|
||||
---
|
||||
|
||||
# HubStudio Integration Guide
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
2. [Installation](#installation)
|
||||
3. [Configure HubStudio](#configure-hubstudio)
|
||||
4. [Using API/CLI](#using-apicli)
|
||||
5. [Connection Details](#connection-details)
|
||||
6. [Automation Examples](#automation-examples)
|
||||
7. [FAQ](#faq)
|
||||
8. [Advanced Usage](#advanced-usage)
|
||||
9. [ADB Connection Skill](#adb-connection-skill)
|
||||
10. [Resources](#resources)
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
HubStudio provides browser environment automation, cloud phone operations, environment management, account management, and group management through local APIs.
|
||||
|
||||
This Skill is designed to:
|
||||
- Locate all HubStudio endpoints quickly
|
||||
- Explain request/response fields and constraints
|
||||
- Provide safe call patterns for automation
|
||||
- Standardize API testing and troubleshooting
|
||||
|
||||
Core docs:
|
||||
- Official API docs: [https://api-docs.hubstudio.cn/](https://api-docs.hubstudio.cn/)
|
||||
- Full local generated reference: [reference.md](reference.md)
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Install HubStudio Desktop
|
||||
|
||||
Install and open HubStudio client on your machine, then log in.
|
||||
|
||||
### 2. Verify Local API Service
|
||||
|
||||
The current OpenAPI file uses:
|
||||
|
||||
```bash
|
||||
http://127.0.0.1:6873
|
||||
```
|
||||
|
||||
Verify service health with:
|
||||
|
||||
```bash
|
||||
curl -s -X POST "http://127.0.0.1:6873/api/v1/browser/all-browser-status" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### 3. Install Optional Tooling
|
||||
|
||||
```bash
|
||||
# API calls and JSON inspection
|
||||
brew install curl jq
|
||||
|
||||
# Python validation scripts
|
||||
python3 -m pip install --user pyyaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configure HubStudio
|
||||
|
||||
### 1. Login and Prepare Data
|
||||
|
||||
1. Login to HubStudio client
|
||||
2. Ensure browser environments/cloud phones exist
|
||||
3. Confirm local API is reachable
|
||||
|
||||
### 2. Configure Runtime Variables (Optional)
|
||||
|
||||
```bash
|
||||
export HUBSTUDIO_BASE_URL="http://127.0.0.1:6873"
|
||||
export HUBSTUDIO_AUTH_TOKEN="<your-token-if-needed>"
|
||||
```
|
||||
|
||||
### 3. Timing Notes
|
||||
|
||||
- Browser environment startup typically takes **3-5 seconds**
|
||||
- Cloud phone startup typically takes **30-90 seconds**
|
||||
- Build retry/timeout logic based on those windows
|
||||
|
||||
---
|
||||
|
||||
## Using API/CLI
|
||||
|
||||
HubStudio OpenAPI in this project contains 56 operations across these domains:
|
||||
- Browser Environment
|
||||
- Cloud Phone
|
||||
- Environment Management
|
||||
- Account Management
|
||||
- Group Management
|
||||
|
||||
### Recommended Call Pattern
|
||||
|
||||
```bash
|
||||
curl -s -X POST "$HUBSTUDIO_BASE_URL/<endpoint>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '<json-body>'
|
||||
```
|
||||
|
||||
### Node CLI (Direct Execution)
|
||||
|
||||
This project includes `hubstudio.js`, so you can execute capabilities directly:
|
||||
|
||||
```bash
|
||||
node hubstudio.js help
|
||||
node hubstudio.js list
|
||||
node hubstudio.js browserCreate
|
||||
node hubstudio.js browserStart 1474900026
|
||||
node hubstudio.js browserStatus 1474900026
|
||||
node hubstudio.js browserForeground 1474900026
|
||||
node hubstudio.js browserArrange
|
||||
node hubstudio.js browserStop 1474900026
|
||||
node hubstudio.js testAll
|
||||
```
|
||||
|
||||
`hubstudio.js` now supports all OpenAPI endpoints via generated commands in `commands.generated.json`.
|
||||
Example generated command:
|
||||
|
||||
```bash
|
||||
node hubstudio.js postV1BrowserStart --body '{"containerCode":"1474900026"}'
|
||||
```
|
||||
|
||||
### Cloud Phone ADB Commands
|
||||
|
||||
```bash
|
||||
# Enable ADB
|
||||
node hubstudio.js postV1CloudMobileBatchUpdateAdb --body '{"mobileIds":["<mobileId>"],"enableAdb":true}'
|
||||
|
||||
# Query ADB connection info
|
||||
node hubstudio.js postV1CloudMobileListAdb --body '{"mobileIds":["<mobileId>"]}'
|
||||
```
|
||||
|
||||
### Common Browser Environment APIs
|
||||
|
||||
```bash
|
||||
# Open environment
|
||||
curl -s -X POST "http://127.0.0.1:6873/api/v1/browser/start" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"containerCode":"1474900026"}'
|
||||
|
||||
# Close environment
|
||||
curl -s -X POST "http://127.0.0.1:6873/api/v1/browser/stop" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"containerCode":"1474900026"}'
|
||||
|
||||
# Get all open environment status
|
||||
curl -s -X POST "http://127.0.0.1:6873/api/v1/browser/all-browser-status" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### Discover Full Endpoint Details
|
||||
|
||||
- Open [reference.md](reference.md) for:
|
||||
- All endpoints and methods
|
||||
- Request/response field descriptions
|
||||
- Required fields and constraints
|
||||
- Grouped view by tags
|
||||
|
||||
---
|
||||
|
||||
## Connection Details
|
||||
|
||||
### Base URL
|
||||
|
||||
From `openapi.yaml`:
|
||||
|
||||
```bash
|
||||
http://127.0.0.1:6873
|
||||
```
|
||||
|
||||
### Response Convention
|
||||
|
||||
Most endpoints return:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "Success",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
Interpretation:
|
||||
- `code = 0`: success
|
||||
- non-zero code: business error (missing field, not found, permission, precondition)
|
||||
|
||||
---
|
||||
|
||||
## Automation Examples
|
||||
|
||||
### Example 1: Open Browser Environment
|
||||
|
||||
```bash
|
||||
node hubstudio.js browserStart 1474900026
|
||||
```
|
||||
|
||||
### Example 2: Query Running Browser Environments
|
||||
|
||||
```bash
|
||||
node hubstudio.js browserStatus
|
||||
```
|
||||
|
||||
### Example 3: Smoke Test All OpenAPI Endpoints
|
||||
|
||||
```bash
|
||||
# Current smoke test report (generated previously):
|
||||
cat scripts/endpoint_test_report.json | jq '.total, .reachable, .transport_fail_count'
|
||||
```
|
||||
|
||||
### Example 4: Validate Skill Completeness
|
||||
|
||||
```bash
|
||||
python3 scripts/validate_completeness.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q1: API Returns HTTP 200 But Business Failure
|
||||
|
||||
This is expected for many endpoints. Check response body `code` and `msg` for business-level result.
|
||||
|
||||
### Q2: Environment Open Failed
|
||||
|
||||
Check:
|
||||
1. `containerCode` is correct
|
||||
2. Environment exists in current account/workspace
|
||||
3. HubStudio client is logged in and local service is running
|
||||
|
||||
### Q3: Why Some Endpoint Tests Fail with Missing Parameters?
|
||||
|
||||
Endpoint smoke tests are designed for reachability first. Business failures due to required params/resources are normal unless strict case data is provided.
|
||||
|
||||
### Q4: How to Find Required Fields?
|
||||
|
||||
Use [reference.md](reference.md). Each endpoint lists:
|
||||
- Required parameters
|
||||
- Request body required fields
|
||||
- Type and constraint details
|
||||
|
||||
---
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### 1. OpenClaw Integration
|
||||
|
||||
In OpenClaw workflows, call HubStudio endpoints directly with structured payloads:
|
||||
|
||||
```bash
|
||||
# Example: open environment
|
||||
openclaw hubstudio call --path "/api/v1/browser/start" \
|
||||
--method POST \
|
||||
--body '{"containerCode":"1474900026"}'
|
||||
```
|
||||
|
||||
### 2. Retry Strategy
|
||||
|
||||
- Browser environment APIs: retry 1-2 times, interval 2-3 seconds
|
||||
- Cloud phone APIs: retry 2-4 times, interval 5-10 seconds
|
||||
|
||||
### 3. Batch Operations
|
||||
|
||||
For batch tasks, iterate IDs with per-item result logging and partial-failure tolerance.
|
||||
|
||||
### 4. Validation Loop
|
||||
|
||||
1. Update data in `openapi.yaml`
|
||||
2. Re-generate `reference.md`
|
||||
3. Run `python3 scripts/validate_completeness.py`
|
||||
4. Run endpoint smoke tests and inspect `scripts/endpoint_test_report.json`
|
||||
|
||||
---
|
||||
|
||||
## ADB Connection Skill
|
||||
|
||||
Use this capability when the user asks to connect cloud phones through ADB.
|
||||
|
||||
- Android 12 / Android 15: direct `adb connect <ip:port>` mode
|
||||
- Android 13 / Android 14 / Android 15A: SSH tunnel + `adb connect localhost:<port>` mode
|
||||
- Full guide and command templates: [ADB_CONNECTION_GUIDE.md](ADB_CONNECTION_GUIDE.md)
|
||||
|
||||
Execution rule:
|
||||
1. Ensure cloud phone is powered on
|
||||
2. Enable ADB via `postV1CloudMobileBatchUpdateAdb`
|
||||
3. Query ADB info via `postV1CloudMobileListAdb`
|
||||
4. Choose direct or tunnel workflow by Android version
|
||||
5. Validate with `adb devices`
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
- HubStudio API Docs: [https://api-docs.hubstudio.cn/](https://api-docs.hubstudio.cn/)
|
||||
- Full Generated API Reference: [reference.md](reference.md)
|
||||
- ADB workflow details: [ADB_CONNECTION_GUIDE.md](ADB_CONNECTION_GUIDE.md)
|
||||
- Completeness Checker: `scripts/validate_completeness.py`
|
||||
- Endpoint Smoke Test Report: `scripts/endpoint_test_report.json`
|
||||
@@ -0,0 +1,634 @@
|
||||
{
|
||||
"baseUrl": "http://127.0.0.1:6873",
|
||||
"count": 56,
|
||||
"commands": [
|
||||
{
|
||||
"command": "postV1CloudMobileAppPage",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/app/page",
|
||||
"summary": "APP列表(分页)\\查询可安装应用列表",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"productId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileAppInstalledlist",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/app/installedList",
|
||||
"summary": "已安装应用列表查询",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"01KAJXGA2GNQ7XY54ZZHV6XN0G": "test",
|
||||
"mobileId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileGroupAppCreate",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/group/app/create",
|
||||
"summary": "新增团队应用",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {
|
||||
"packageName": "test",
|
||||
"versionCode": 0
|
||||
},
|
||||
"defaultBody": {
|
||||
"01KAJXGA2GNQ7XY54ZZHV6XN0G": "test",
|
||||
"mobileId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileAppInstall",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/app/install",
|
||||
"summary": "app应用安装",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileAppStart",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/app/start",
|
||||
"summary": "APP启动",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0,
|
||||
"packageName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileAppRestart",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/app/restart",
|
||||
"summary": "APP重启",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0,
|
||||
"packageName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileAppStop",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/app/stop",
|
||||
"summary": "APP停止",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0,
|
||||
"packageName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileAppUninstall",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/app/uninstall",
|
||||
"summary": "APP卸载",
|
||||
"tag": "云手机/应用管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0,
|
||||
"packageName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileUploadFile",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/upload-file",
|
||||
"summary": "公网文件上传文件到云手机",
|
||||
"tag": "云手机/文件管理",
|
||||
"defaultQuery": {
|
||||
"downloadDest": "test",
|
||||
"fileName": "test",
|
||||
"fileUrl": "test"
|
||||
},
|
||||
"defaultBody": {
|
||||
"mobileId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV2CloudMobileUploadFile",
|
||||
"method": "POST",
|
||||
"path": "/api/v2/cloud-mobile/upload-file",
|
||||
"summary": "选择本地上传文件到云手机",
|
||||
"tag": "云手机/文件管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileSetkeybox",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/setKeyBox",
|
||||
"summary": "设置keyBox",
|
||||
"tag": "云手机/文件管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0,
|
||||
"filePath": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileMobileProductList",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/mobile-product-list",
|
||||
"summary": "云手机商品列表",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileMobilePage",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/mobile-page",
|
||||
"summary": "云手机分页列表",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileAddMobile",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/add-mobile",
|
||||
"summary": "添加云手机",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"productId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobilePowerOnMobile",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/power-on-mobile",
|
||||
"summary": "批量开启云手机",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileShutdownMobile",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/shutdown-mobile",
|
||||
"summary": "批量关闭云手机",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileDelMobileBatch",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/del-mobile-batch",
|
||||
"summary": "批量删除云手机",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileUpdate",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/update",
|
||||
"summary": "修改云手机信息",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {
|
||||
"mobileId": 0
|
||||
},
|
||||
"defaultBody": {
|
||||
"mobileId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileSetTag",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/set-tag",
|
||||
"summary": "批量设置云手机分组",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileIds": [],
|
||||
"tagName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileGetCountryTimeZoneLanguageList",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/get-country-time-zone-language-list",
|
||||
"summary": "国家时区语言列表",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileUpdateProxy",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/update-proxy",
|
||||
"summary": " 更新代理",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {
|
||||
"mobileId": 0,
|
||||
"proxyTypeId": 0,
|
||||
"proxyHost": "test",
|
||||
"proxyPort": "test"
|
||||
},
|
||||
"defaultBody": {
|
||||
"proxyPort": 0,
|
||||
"proxyTypeId": 0,
|
||||
"mobileId": 0,
|
||||
"proxyHost": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileNewMachineStatus",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/new-machine-status",
|
||||
"summary": "获取一键新机状态及可用数量",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileBrandModels",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/brand/models",
|
||||
"summary": "查询品牌机型",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"productId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileNewMachine",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/new-machine",
|
||||
"summary": "一键新机",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileBatchUpdateAdb",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/batch-update-adb",
|
||||
"summary": "批量更新云手机ADB状态",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"enableAdb": false,
|
||||
"mobileIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileListAdb",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/list-adb",
|
||||
"summary": "批量获取云手机ADB状态",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileExeCommand",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/exe-command",
|
||||
"summary": "执行shell命令",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"mobileId": 0,
|
||||
"command": "test",
|
||||
"01KAJYBK6R36JW8F2K4M6F3MGX": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1CloudMobileSimulatesendsms",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cloud-mobile/simulateSendSms",
|
||||
"summary": "发送短信到云手机",
|
||||
"tag": "云手机",
|
||||
"defaultQuery": {
|
||||
"mobileId": 0,
|
||||
"senderNumber": "test",
|
||||
"smsContent": "test"
|
||||
},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserStart",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/start",
|
||||
"summary": "打开环境",
|
||||
"tag": "浏览器环境",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCode": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserStop",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/stop",
|
||||
"summary": "关闭环境",
|
||||
"tag": "浏览器环境",
|
||||
"defaultQuery": {
|
||||
"containerCode": "test"
|
||||
},
|
||||
"defaultBody": {
|
||||
"containerCode": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserAllBrowserStatus",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/all-browser-status",
|
||||
"summary": "获取所有打开环境",
|
||||
"tag": "浏览器环境",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserForeground",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/foreground",
|
||||
"summary": "切换浏览器窗口",
|
||||
"tag": "浏览器环境",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCode": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserStopAll",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/stop-all",
|
||||
"summary": "关闭所有环境接口",
|
||||
"tag": "浏览器环境",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1DisplayAll",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/display/all",
|
||||
"summary": "获取全部屏幕(物理机的屏幕)",
|
||||
"tag": "浏览器环境",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserArrange",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/arrange",
|
||||
"summary": "浏览器窗口自定义排列",
|
||||
"tag": "浏览器环境",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvList",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/list",
|
||||
"summary": "获取环境列表",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvCreate",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/create",
|
||||
"summary": "创建环境",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerName": "test",
|
||||
"asDynamicType": 0,
|
||||
"proxyTypeName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvUpdate",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/update",
|
||||
"summary": "更新环境",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCode": 0,
|
||||
"containerName": "test",
|
||||
"tagName": "test",
|
||||
"coreVersion": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvProxyUpdate",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/proxy/update",
|
||||
"summary": "更新环境代理",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCode": 0,
|
||||
"asDynamicType": 0,
|
||||
"proxyTypeName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvDel",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/del",
|
||||
"summary": "删除环境",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCodes": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvImportCookie",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/import-cookie",
|
||||
"summary": "导入Cookie",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCode": "test",
|
||||
"cookie": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvExportCookie",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/export-cookie",
|
||||
"summary": "导出Cookie",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvRandomUa",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/random-ua",
|
||||
"summary": "获取随机UA",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1CacheClear",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/cache/clear",
|
||||
"summary": "清除环境本地缓存",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserResetExtension",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/reset-extension",
|
||||
"summary": "清理环境内插件缓存",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"browserOauth": 0,
|
||||
"pluginIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1BrowserDownloadCore",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/browser/download-core",
|
||||
"summary": "下载内核",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"Cores": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1EnvRefreshFingerprint",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/env/refresh-fingerprint",
|
||||
"summary": "刷新指纹",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"containerCode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1ContainerBatchUpdateRemark",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/container/batch-update-remark",
|
||||
"summary": "批量修改备注",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1ContainerWebglRendererList",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/container/webgl-renderer-list",
|
||||
"summary": "查询webglVendor和webglRenderer",
|
||||
"tag": "环境管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1AccountList",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/account/list",
|
||||
"summary": "账号分页列表",
|
||||
"tag": "平台账号管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1AccountUpdate",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/account/update",
|
||||
"summary": "账号更新",
|
||||
"tag": "平台账号管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1ContainerAddAccount",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/container/add-account",
|
||||
"summary": "添加环境账号",
|
||||
"tag": "平台账号管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"accountName": "test",
|
||||
"siteName": "test",
|
||||
"accountPassword": "test",
|
||||
"containerCode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1AccountDel",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/account/del",
|
||||
"summary": "账号删除",
|
||||
"tag": "平台账号管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"accountIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1GroupList",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/group/list",
|
||||
"summary": "获取环境分组列表",
|
||||
"tag": "分组管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {}
|
||||
},
|
||||
{
|
||||
"command": "postV1GroupCreate",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/group/create",
|
||||
"summary": "新建环境分组",
|
||||
"tag": "分组管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"tagName": "test"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "postV1GroupDel",
|
||||
"method": "POST",
|
||||
"path": "/api/v1/group/del",
|
||||
"summary": "删除环境分组",
|
||||
"tag": "分组管理",
|
||||
"defaultQuery": {},
|
||||
"defaultBody": {
|
||||
"tagCode": "test"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const YAML = require("yaml");
|
||||
|
||||
const GENERATED_PATH = path.join(__dirname, "commands.generated.json");
|
||||
const OPENAPI_PATH = path.join(__dirname, "openapi.yaml");
|
||||
const generated = JSON.parse(fs.readFileSync(GENERATED_PATH, "utf8"));
|
||||
const BASE_URL = process.env.HUBSTUDIO_BASE_URL || generated.baseUrl || "http://127.0.0.1:6873";
|
||||
const COMMANDS = generated.commands || [];
|
||||
const COMMAND_MAP = new Map(COMMANDS.map((c) => [c.command, c]));
|
||||
|
||||
const ALIASES = {
|
||||
browserCreate: "postV1EnvCreate",
|
||||
browserStart: "postV1BrowserStart",
|
||||
browserStop: "postV1BrowserStop",
|
||||
browserStatus: "postV1BrowserAllBrowserStatus",
|
||||
browserForeground: "postV1BrowserForeground",
|
||||
browserStopAll: "postV1BrowserStopAll",
|
||||
displayAll: "postV1DisplayAll",
|
||||
browserArrange: "postV1BrowserArrange",
|
||||
};
|
||||
|
||||
class InputValidationError extends Error {
|
||||
constructor(message, fieldErrors = [], command = null) {
|
||||
super(message);
|
||||
this.name = "InputValidationError";
|
||||
this.code = "VALIDATION_ERROR";
|
||||
this.fieldErrors = fieldErrors;
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
|
||||
function loadOpenApi() {
|
||||
try {
|
||||
const content = fs.readFileSync(OPENAPI_PATH, "utf8");
|
||||
return YAML.parse(content) || {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const OPENAPI = loadOpenApi();
|
||||
|
||||
function resolveSchema(schema) {
|
||||
if (!schema || typeof schema !== "object") {
|
||||
return schema;
|
||||
}
|
||||
if (!schema.$ref || typeof schema.$ref !== "string") {
|
||||
return schema;
|
||||
}
|
||||
if (!schema.$ref.startsWith("#/")) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
const parts = schema.$ref
|
||||
.replace(/^#\//, "")
|
||||
.split("/")
|
||||
.map((x) => x.replace(/~1/g, "/").replace(/~0/g, "~"));
|
||||
let current = OPENAPI;
|
||||
for (const part of parts) {
|
||||
if (!current || typeof current !== "object" || !(part in current)) {
|
||||
return schema;
|
||||
}
|
||||
current = current[part];
|
||||
}
|
||||
if (!current || typeof current !== "object") {
|
||||
return schema;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function getOperationSchema(method, endpointPath) {
|
||||
const methodKey = String(method || "post").toLowerCase();
|
||||
const op = OPENAPI && OPENAPI.paths && OPENAPI.paths[endpointPath] && OPENAPI.paths[endpointPath][methodKey];
|
||||
if (!op) {
|
||||
return { querySchema: null, bodySchema: null, requestBodyRequired: false };
|
||||
}
|
||||
|
||||
const queryParams = Array.isArray(op.parameters) ? op.parameters.filter((p) => p && p.in === "query") : [];
|
||||
const queryProperties = {};
|
||||
const queryRequired = [];
|
||||
for (const param of queryParams) {
|
||||
if (!param || !param.name) continue;
|
||||
queryProperties[param.name] = resolveSchema(param.schema || {});
|
||||
if (param.required) {
|
||||
queryRequired.push(param.name);
|
||||
}
|
||||
}
|
||||
|
||||
const querySchema = queryParams.length
|
||||
? {
|
||||
type: "object",
|
||||
properties: queryProperties,
|
||||
required: queryRequired,
|
||||
}
|
||||
: null;
|
||||
|
||||
const jsonBodySchema =
|
||||
op.requestBody &&
|
||||
op.requestBody.content &&
|
||||
op.requestBody.content["application/json"] &&
|
||||
op.requestBody.content["application/json"].schema
|
||||
? resolveSchema(op.requestBody.content["application/json"].schema)
|
||||
: null;
|
||||
|
||||
return {
|
||||
querySchema,
|
||||
bodySchema: jsonBodySchema,
|
||||
requestBodyRequired: Boolean(op.requestBody && op.requestBody.required),
|
||||
};
|
||||
}
|
||||
|
||||
function isPlainObject(value) {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function isEmptyValue(value) {
|
||||
if (value === undefined || value === null) return true;
|
||||
if (typeof value === "string") return value.trim().length === 0;
|
||||
if (Array.isArray(value)) return value.length === 0;
|
||||
if (isPlainObject(value)) return Object.keys(value).length === 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
function pushValidationError(errors, field, reason, expected, actual) {
|
||||
errors.push({
|
||||
field,
|
||||
reason,
|
||||
expected,
|
||||
actualType: actual,
|
||||
});
|
||||
}
|
||||
|
||||
function validateStringFormat(value, schema, fieldPath, errors) {
|
||||
const format = schema.format;
|
||||
if (!format) return;
|
||||
|
||||
if (format === "email") {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(value)) {
|
||||
pushValidationError(errors, fieldPath, "invalid email format", "email", "string");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (format === "uri" || format === "url") {
|
||||
try {
|
||||
new URL(value);
|
||||
} catch {
|
||||
pushValidationError(errors, fieldPath, "invalid URL format", "url", "string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateBySchema(value, schema, fieldPath, errors, options = {}) {
|
||||
const resolved = resolveSchema(schema || {});
|
||||
if (!resolved || typeof resolved !== "object") return;
|
||||
|
||||
if (value === undefined) return;
|
||||
if (value === null) {
|
||||
if (resolved.nullable || resolved.type === "null") return;
|
||||
pushValidationError(errors, fieldPath, "value cannot be null", resolved.type || "non-null", "null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(resolved.type)) {
|
||||
const typePass = resolved.type.some((oneType) => {
|
||||
const localErrors = [];
|
||||
validateBySchema(value, { ...resolved, type: oneType }, fieldPath, localErrors, options);
|
||||
return localErrors.length === 0;
|
||||
});
|
||||
if (!typePass) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", resolved.type.join("|"), typeof value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const inferredType = resolved.type || (resolved.properties ? "object" : undefined);
|
||||
if (!inferredType) return;
|
||||
|
||||
if (inferredType === "string") {
|
||||
if (typeof value !== "string") {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "string", Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
if (typeof resolved.minLength === "number" && value.length < resolved.minLength) {
|
||||
pushValidationError(errors, fieldPath, "string too short", `minLength=${resolved.minLength}`, "string");
|
||||
}
|
||||
if (typeof resolved.maxLength === "number" && value.length > resolved.maxLength) {
|
||||
pushValidationError(errors, fieldPath, "string too long", `maxLength=${resolved.maxLength}`, "string");
|
||||
}
|
||||
if (resolved.pattern) {
|
||||
try {
|
||||
const reg = new RegExp(resolved.pattern);
|
||||
if (!reg.test(value)) {
|
||||
pushValidationError(errors, fieldPath, "string pattern mismatch", resolved.pattern, "string");
|
||||
}
|
||||
} catch {
|
||||
// Ignore invalid server-side regex pattern definitions.
|
||||
}
|
||||
}
|
||||
validateStringFormat(value, resolved, fieldPath, errors);
|
||||
} else if (inferredType === "number" || inferredType === "integer") {
|
||||
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", inferredType, Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
if (inferredType === "integer" && !Number.isInteger(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "integer", "number");
|
||||
}
|
||||
if (typeof resolved.minimum === "number" && value < resolved.minimum) {
|
||||
pushValidationError(errors, fieldPath, "number below minimum", `>=${resolved.minimum}`, "number");
|
||||
}
|
||||
if (typeof resolved.maximum === "number" && value > resolved.maximum) {
|
||||
pushValidationError(errors, fieldPath, "number above maximum", `<=${resolved.maximum}`, "number");
|
||||
}
|
||||
if (typeof resolved.exclusiveMinimum === "number" && value <= resolved.exclusiveMinimum) {
|
||||
pushValidationError(errors, fieldPath, "number below exclusiveMinimum", `>${resolved.exclusiveMinimum}`, "number");
|
||||
}
|
||||
if (typeof resolved.exclusiveMaximum === "number" && value >= resolved.exclusiveMaximum) {
|
||||
pushValidationError(errors, fieldPath, "number above exclusiveMaximum", `<${resolved.exclusiveMaximum}`, "number");
|
||||
}
|
||||
} else if (inferredType === "boolean") {
|
||||
if (typeof value !== "boolean") {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "boolean", Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
} else if (inferredType === "array") {
|
||||
if (!Array.isArray(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "array", typeof value);
|
||||
return;
|
||||
}
|
||||
if (typeof resolved.minItems === "number" && value.length < resolved.minItems) {
|
||||
pushValidationError(errors, fieldPath, "array too short", `minItems=${resolved.minItems}`, "array");
|
||||
}
|
||||
if (typeof resolved.maxItems === "number" && value.length > resolved.maxItems) {
|
||||
pushValidationError(errors, fieldPath, "array too long", `maxItems=${resolved.maxItems}`, "array");
|
||||
}
|
||||
if (resolved.items) {
|
||||
value.forEach((item, idx) => {
|
||||
validateBySchema(item, resolved.items, `${fieldPath}[${idx}]`, errors, options);
|
||||
});
|
||||
}
|
||||
} else if (inferredType === "object") {
|
||||
if (!isPlainObject(value)) {
|
||||
pushValidationError(errors, fieldPath, "type mismatch", "object", Array.isArray(value) ? "array" : typeof value);
|
||||
return;
|
||||
}
|
||||
const properties = resolved.properties || {};
|
||||
const required = Array.isArray(resolved.required) ? resolved.required : [];
|
||||
|
||||
for (const reqKey of required) {
|
||||
const reqPath = fieldPath ? `${fieldPath}.${reqKey}` : reqKey;
|
||||
if (!(reqKey in value) || isEmptyValue(value[reqKey])) {
|
||||
pushValidationError(errors, reqPath, "required field missing or empty", "required", "missing");
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(value)) {
|
||||
const childPath = fieldPath ? `${fieldPath}.${key}` : key;
|
||||
if (!(key in properties)) {
|
||||
if (options.allowUnknownFields) {
|
||||
continue;
|
||||
}
|
||||
pushValidationError(errors, childPath, "unknown field", "defined property", "unknown");
|
||||
continue;
|
||||
}
|
||||
validateBySchema(value[key], properties[key], childPath, errors, options);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(resolved.enum) && resolved.enum.length > 0) {
|
||||
const enumPass = resolved.enum.some((x) => x === value);
|
||||
if (!enumPass) {
|
||||
pushValidationError(errors, fieldPath, "enum mismatch", JSON.stringify(resolved.enum), typeof value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateCommandInputs(commandName, generatedCmd, body, query, strictValidation = true) {
|
||||
const errors = [];
|
||||
const { querySchema, bodySchema, requestBodyRequired } = getOperationSchema(generatedCmd.method, generatedCmd.path);
|
||||
const options = { allowUnknownFields: !strictValidation };
|
||||
|
||||
if (requestBodyRequired && isEmptyValue(body)) {
|
||||
pushValidationError(errors, "body", "required request body missing or empty", "non-empty object", "missing");
|
||||
}
|
||||
|
||||
if (querySchema) {
|
||||
validateBySchema(query || {}, querySchema, "query", errors, options);
|
||||
} else if (query && isPlainObject(query) && Object.keys(query).length > 0) {
|
||||
pushValidationError(errors, "query", "unexpected query parameters", "no query params", "object");
|
||||
}
|
||||
|
||||
if (bodySchema) {
|
||||
validateBySchema(body || {}, bodySchema, "body", errors, options);
|
||||
} else if (body && isPlainObject(body) && Object.keys(body).length > 0) {
|
||||
pushValidationError(errors, "body", "unexpected request body", "no body", "object");
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new InputValidationError("Request validation failed", errors, commandName);
|
||||
}
|
||||
}
|
||||
|
||||
function printHelp() {
|
||||
console.log(`HubStudio CLI
|
||||
|
||||
Usage:
|
||||
node hubstudio.js <command> [args] [--body '{"k":"v"}'] [--query '{"k":"v"}'] [--strict true|false]
|
||||
|
||||
Commands:
|
||||
browserCreate [name] Create browser environment
|
||||
browserStart <containerCode> Start browser environment
|
||||
browserStop <containerCode> Stop browser environment
|
||||
browserStatus [containerCode] Query running browser status
|
||||
browserForeground <containerCode> Bring browser window to front
|
||||
browserStopAll [clearOpening] Close all environments
|
||||
displayAll Get all physical screens
|
||||
browserArrange [screenId] Arrange browser windows
|
||||
list List all generated commands
|
||||
testAll Test all generated endpoints
|
||||
help Show this help
|
||||
|
||||
Generated commands:
|
||||
${COMMANDS.length} commands are available, e.g.:
|
||||
- postV1BrowserStart
|
||||
- postV1BrowserStop
|
||||
- postV1EnvCreate
|
||||
|
||||
Env:
|
||||
HUBSTUDIO_BASE_URL Default: http://127.0.0.1:6873
|
||||
HUBSTUDIO_STRICT_VALIDATION Default: true (set false to allow unknown fields)
|
||||
`);
|
||||
}
|
||||
|
||||
function parseJsonArg(flag, value) {
|
||||
if (!value) {
|
||||
throw new Error(`Missing value for ${flag}`);
|
||||
}
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (err) {
|
||||
throw new Error(`Invalid JSON for ${flag}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseCommandArgs(rawArgs) {
|
||||
const positional = [];
|
||||
let bodyOverride = null;
|
||||
let queryOverride = null;
|
||||
let strictValidation = process.env.HUBSTUDIO_STRICT_VALIDATION !== "false";
|
||||
|
||||
for (let i = 0; i < rawArgs.length; i += 1) {
|
||||
const token = rawArgs[i];
|
||||
if (token === "--body") {
|
||||
bodyOverride = parseJsonArg("--body", rawArgs[i + 1]);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (token === "--query") {
|
||||
queryOverride = parseJsonArg("--query", rawArgs[i + 1]);
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
if (token === "--strict") {
|
||||
const next = rawArgs[i + 1];
|
||||
if (!next || next.startsWith("--")) {
|
||||
strictValidation = true;
|
||||
} else {
|
||||
const normalized = String(next).toLowerCase();
|
||||
if (normalized === "true") {
|
||||
strictValidation = true;
|
||||
} else if (normalized === "false") {
|
||||
strictValidation = false;
|
||||
} else {
|
||||
throw new InputValidationError("Request validation failed", [
|
||||
{
|
||||
field: "--strict",
|
||||
reason: "invalid boolean flag value",
|
||||
expected: "true|false",
|
||||
actualType: typeof next,
|
||||
},
|
||||
]);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
positional.push(token);
|
||||
}
|
||||
|
||||
return { positional, bodyOverride, queryOverride, strictValidation };
|
||||
}
|
||||
|
||||
async function post(endpointPath, body = {}, query = null) {
|
||||
const url = new URL(endpointPath, BASE_URL);
|
||||
if (query && typeof query === "object") {
|
||||
for (let [k, v] of Object.entries(query)) {
|
||||
if (v !== undefined && v !== null) {
|
||||
url.searchParams.set(k, String(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
let payload = null;
|
||||
try {
|
||||
payload = await response.json();
|
||||
} catch {
|
||||
payload = { raw: await response.text() };
|
||||
}
|
||||
return { status: response.status, payload };
|
||||
}
|
||||
|
||||
function printResult(result) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
|
||||
function requiredArg(name, value) {
|
||||
if (!value) {
|
||||
throw new InputValidationError("Request validation failed", [
|
||||
{
|
||||
field: name,
|
||||
reason: "required argument missing or empty",
|
||||
expected: "non-empty",
|
||||
actualType: "missing",
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function getGeneratedCommand(commandName) {
|
||||
const resolved = ALIASES[commandName] || commandName;
|
||||
const cmd = COMMAND_MAP.get(resolved);
|
||||
if (!cmd) {
|
||||
throw new Error(`Unknown command: ${commandName}`);
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
function resolveCallInputs(commandName, positional, bodyOverride, queryOverride) {
|
||||
const generatedCmd = getGeneratedCommand(commandName);
|
||||
const body = { ...(generatedCmd.defaultBody || {}) };
|
||||
const query = { ...(generatedCmd.defaultQuery || {}) };
|
||||
|
||||
if (commandName === "browserCreate") {
|
||||
const name = positional[0] || `NodeAuto-${Date.now()}`;
|
||||
body.containerName = name;
|
||||
body.asDynamicType = 1;
|
||||
body.proxyTypeName = "不使用代理";
|
||||
} else if (commandName === "browserStart" || commandName === "browserForeground") {
|
||||
requiredArg("containerCode", positional[0]);
|
||||
body.containerCode = positional[0];
|
||||
} else if (commandName === "browserStop") {
|
||||
requiredArg("containerCode", positional[0]);
|
||||
body.containerCode = positional[0];
|
||||
query.containerCode = positional[0];
|
||||
} else if (commandName === "browserStatus") {
|
||||
if (positional[0]) {
|
||||
body.containerCode = [positional[0]];
|
||||
}
|
||||
} else if (commandName === "browserStopAll") {
|
||||
body.clearOpening = positional[0] ? positional[0] === "true" : false;
|
||||
} else if (commandName === "browserArrange" && positional[0]) {
|
||||
body.screenId = Number(positional[0]);
|
||||
}
|
||||
|
||||
const finalBody = bodyOverride && typeof bodyOverride === "object" ? bodyOverride : body;
|
||||
const finalQuery = queryOverride && typeof queryOverride === "object" ? queryOverride : query;
|
||||
return { generatedCmd, finalBody, finalQuery };
|
||||
}
|
||||
|
||||
function normalizeResult(command, generatedCmd, result, body, query) {
|
||||
return {
|
||||
command,
|
||||
method: generatedCmd.method,
|
||||
path: generatedCmd.path,
|
||||
status: result.status,
|
||||
code: result.payload && Object.prototype.hasOwnProperty.call(result.payload, "code") ? result.payload.code : null,
|
||||
msg: result.payload && Object.prototype.hasOwnProperty.call(result.payload, "msg") ? result.payload.msg : null,
|
||||
success:
|
||||
result.status < 400 &&
|
||||
(!result.payload || !Object.prototype.hasOwnProperty.call(result.payload, "code") || result.payload.code === 0),
|
||||
request: { body, query },
|
||||
payload: result.payload,
|
||||
};
|
||||
}
|
||||
|
||||
async function runTestAll() {
|
||||
const results = [];
|
||||
for (const cmd of COMMANDS) {
|
||||
try {
|
||||
const result = await post(cmd.path, cmd.defaultBody || {}, cmd.defaultQuery || {});
|
||||
results.push(normalizeResult(cmd.command, cmd, result, cmd.defaultBody || {}, cmd.defaultQuery || {}));
|
||||
} catch (err) {
|
||||
results.push({
|
||||
command: cmd.command,
|
||||
method: cmd.method,
|
||||
path: cmd.path,
|
||||
status: null,
|
||||
code: null,
|
||||
msg: err.message,
|
||||
success: false,
|
||||
request: { body: cmd.defaultBody || {}, query: cmd.defaultQuery || {} },
|
||||
payload: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const summary = {
|
||||
total: results.length,
|
||||
success: results.filter((x) => x.success).length,
|
||||
failed: results.filter((x) => !x.success).length,
|
||||
};
|
||||
const report = {
|
||||
baseUrl: BASE_URL,
|
||||
testedAt: new Date().toISOString(),
|
||||
summary,
|
||||
results,
|
||||
};
|
||||
|
||||
const reportPath = path.join(__dirname, "scripts", "node_test_all_report.json");
|
||||
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf8");
|
||||
return { summary, reportPath, report };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [, , command, ...rawArgs] = process.argv;
|
||||
|
||||
if (!command || command === "help" || command === "--help" || command === "-h") {
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "list") {
|
||||
const items = COMMANDS.map((c) => ({
|
||||
command: c.command,
|
||||
method: c.method,
|
||||
path: c.path,
|
||||
summary: c.summary,
|
||||
}));
|
||||
printResult({ total: items.length, commands: items });
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "testAll") {
|
||||
const out = await runTestAll();
|
||||
printResult({
|
||||
summary: out.summary,
|
||||
reportPath: out.reportPath,
|
||||
});
|
||||
if (out.summary.failed > 0) {
|
||||
process.exitCode = 2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const { positional, bodyOverride, queryOverride } = parseCommandArgs(rawArgs);
|
||||
const { generatedCmd, finalBody, finalQuery } = resolveCallInputs(
|
||||
command,
|
||||
positional,
|
||||
bodyOverride,
|
||||
queryOverride
|
||||
);
|
||||
validateCommandInputs(command, generatedCmd, finalBody, finalQuery);
|
||||
const result = await post(generatedCmd.path, finalBody, finalQuery);
|
||||
const normalized = normalizeResult(command, generatedCmd, result, finalBody, finalQuery);
|
||||
printResult(normalized);
|
||||
|
||||
if (normalized.status >= 400) {
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
if (normalized.code !== null && normalized.code !== 0) {
|
||||
process.exitCode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
if (err instanceof InputValidationError) {
|
||||
const payload = {
|
||||
success: false,
|
||||
error: {
|
||||
code: err.code,
|
||||
message: err.message,
|
||||
command: err.command || null,
|
||||
fieldErrors: err.fieldErrors || [],
|
||||
},
|
||||
};
|
||||
console.error(JSON.stringify(payload, null, 2));
|
||||
process.exit(3);
|
||||
return;
|
||||
}
|
||||
console.error(err.message || String(err));
|
||||
process.exit(1);
|
||||
});
|
||||
File diff suppressed because it is too large.
Load diff
Generated
+60
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "hubstudio-skill",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "hubstudio-skill",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"playwright": "^1.58.2"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
|
||||
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.58.2"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.58.2",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
|
||||
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "hubstudio-skill",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "hubstudio.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"playwright": "^1.58.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { execSync } = require("child_process");
|
||||
const { chromium } = require("playwright");
|
||||
|
||||
const CONTAINER_CODE = process.env.CONTAINER_CODE || "1502764609";
|
||||
function runHubstudio(command, containerCode) {
|
||||
const cmd = `node hubstudio.js ${command} ${containerCode}`;
|
||||
const output = execSync(cmd, { encoding: "utf8" });
|
||||
return JSON.parse(output);
|
||||
}
|
||||
|
||||
function summarizeFromText(text) {
|
||||
const clean = (text || "").replace(/\s+/g, " ").trim();
|
||||
const excerpt = clean.slice(0, 1500);
|
||||
return [
|
||||
"1) 页面是 HubStudio 相关下载/介绍页。",
|
||||
"2) 重点介绍多账号环境、指纹浏览器、云手机等能力。",
|
||||
"3) 页面通常包含版本、下载入口与安装提示信息。",
|
||||
"",
|
||||
"抓取片段(前1500字符):",
|
||||
excerpt,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("== [1/6] 启动 HubStudio 环境 ==");
|
||||
const startResult = runHubstudio("browserStart", CONTAINER_CODE);
|
||||
if (startResult.code !== 0) {
|
||||
console.error("启动失败:", JSON.stringify(startResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const cdpPort = startResult.payload?.data?.debuggingPort;
|
||||
if (!cdpPort) {
|
||||
console.error("启动成功但未拿到 debuggingPort:", JSON.stringify(startResult, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`环境启动成功,CDP 端口:${cdpPort}`);
|
||||
|
||||
let browser;
|
||||
try {
|
||||
console.log("== [2/6] 连接 Playwright 到 HubStudio 浏览器 ==");
|
||||
browser = await chromium.connectOverCDP(`http://127.0.0.1:${cdpPort}`);
|
||||
|
||||
const context = browser.contexts()[0] || (await browser.newContext());
|
||||
const page = context.pages()[0] || (await context.newPage());
|
||||
|
||||
console.log("== [3/6] 打开百度并搜索 HubStudio ==");
|
||||
await page.goto("https://www.baidu.com/s?wd=HubStudio", { waitUntil: "domcontentloaded" });
|
||||
await page.waitForTimeout(3500);
|
||||
|
||||
console.log("== [4/6] 点击第一条搜索结果 ==");
|
||||
const firstResult = page.locator("#content_left .result h3 a, #content_left .c-container h3 a, h3 a").first();
|
||||
await firstResult.waitFor({ timeout: 15000 });
|
||||
const firstTitle = ((await firstResult.innerText()) || "").trim();
|
||||
const firstHref = await firstResult.getAttribute("href");
|
||||
console.log("首条标题:", firstTitle);
|
||||
console.log("首条链接:", firstHref || "(empty)");
|
||||
|
||||
let targetPage = page;
|
||||
const maybePopup = context.waitForEvent("page", { timeout: 5000 }).catch(() => null);
|
||||
await firstResult.click();
|
||||
const popupPage = await maybePopup;
|
||||
if (popupPage) {
|
||||
targetPage = popupPage;
|
||||
await targetPage.waitForLoadState("domcontentloaded");
|
||||
} else {
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
}
|
||||
await targetPage.waitForTimeout(5000);
|
||||
|
||||
console.log("== [5/6] 提取页面内容并生成摘要 ==");
|
||||
const finalTitle = await targetPage.title();
|
||||
const finalUrl = targetPage.url();
|
||||
const bodyText = await targetPage.locator("body").innerText();
|
||||
const summaryText = summarizeFromText(bodyText);
|
||||
|
||||
const report = [
|
||||
`任务时间: ${new Date().toISOString()}`,
|
||||
`环境编号: ${CONTAINER_CODE}`,
|
||||
`首条结果标题: ${firstTitle}`,
|
||||
`首条结果链接: ${firstHref || ""}`,
|
||||
`落地页标题: ${finalTitle}`,
|
||||
`落地页地址: ${finalUrl}`,
|
||||
"",
|
||||
"页面摘要:",
|
||||
summaryText,
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
const outputPath = path.join(process.cwd(), "summary_hubstudio_baidu_playwright.txt");
|
||||
fs.writeFileSync(outputPath, report, "utf8");
|
||||
console.log(`摘要文件已生成:${outputPath}`);
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close();
|
||||
}
|
||||
console.log("== [6/6] 关闭 HubStudio 环境 ==");
|
||||
const stopResult = runHubstudio("browserStop", CONTAINER_CODE);
|
||||
console.log(JSON.stringify({ code: stopResult.code, msg: stopResult.msg }, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("执行失败:", err.message || String(err));
|
||||
process.exit(1);
|
||||
});
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,84 @@
|
||||
# Skill Hub
|
||||
|
||||
[紫鸟开放平台](https://open.ziniao.com/) 的 **Agent Skills** 仓库,面向所有支持 Agent Skill 规范的 AI 编程助手与 Agent 运行时。
|
||||
|
||||
仓库内有两类 Skill:
|
||||
|
||||
- **文档型 Skill**:用 `SKILL.md` 提供定位、能力概览与按需深入的导航,用 `reference/` 存放可被精确检索的接口细节。Agent 在需要理解某个产品"是什么、能做什么、怎么对接"时加载,再按需深入到具体接口参数,既掌握全局又不被细节淹没。
|
||||
- **执行型 Skill**:面向普通用户直接完成任务,无需关心内部调用细节。Agent 按契约直接调用开放平台接口拿到最终结果。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
skills/
|
||||
├── ziniao-erp-api-doc/ # 紫鸟 ERP API 文档(文档型)
|
||||
├── ziniao-sso-doc/ # 紫鸟单点登录 OpenAPI 文档(文档型)
|
||||
├── ziniao-webdriver-doc/ # 紫鸟浏览器 WebDriver 自动化接口文档(文档型)
|
||||
└── linkfoxai-image-tool/ # LinkFoxAI 图片处理工具(执行型)
|
||||
```
|
||||
|
||||
各类型 Skill 的结构:
|
||||
|
||||
```
|
||||
# 文档型
|
||||
<skill-name>/
|
||||
├── SKILL.md # 入口:定位、能力边界、架构流程、按需深入导航
|
||||
└── reference/ # 细节:按模块拆分的接口请求/响应参数、示例
|
||||
|
||||
# 执行型
|
||||
<skill-name>/
|
||||
└── SKILL.md # 入口:触发条件、执行流程、能力分组、调用契约
|
||||
```
|
||||
|
||||
## 已收录 Skills
|
||||
|
||||
| Skill | 类型 | 说明 | 适用场景 |
|
||||
|-------|------|------|----------|
|
||||
| `ziniao-erp-api-doc` | 文档型 | 紫鸟 ERP REST API(Bearer API Key 认证,72 个接口) | ERP 系统对接规划、集成设计、接口权限评估 |
|
||||
| `ziniao-sso-doc` | 文档型 | 紫鸟单点登录 OpenAPI | ERP 系统 SSO 对接、身份集成设计 |
|
||||
| `ziniao-webdriver-doc` | 文档型 | 紫鸟浏览器 WebDriver 本地自动化接口 | 自动化集成选型、Selenium/Puppeteer/Playwright/DrissionPage 对接 |
|
||||
| `linkfoxai-image-tool` | 执行型 | LinkFoxAI 图片处理(图片上传、场景裂变、商品替换、自动抠图) | 用户要求直接完成图片处理任务并拿到最终结果 |
|
||||
|
||||
## 兼容的 Agent
|
||||
|
||||
本仓库遵循 [Agent Skill 规范](https://docs.claude.com/en/docs/agents-and-tools/agent-skills),`SKILL.md` 的 frontmatter(`name` + `description`)可被任何支持该规范的工具识别并按需加载。已验证/适用的 Agent 包括(不限于):
|
||||
|
||||
- **Claude Code**(Anthropic 官方 CLI)
|
||||
- **Codex**(OpenAI)
|
||||
- **Cursor**
|
||||
- **OpenClaw**
|
||||
- 其他兼容 Agent Skill 规范的运行时
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 安装 Skill
|
||||
|
||||
通过 `skills` CLI 安装。仓库地址为 `ziniao-open/skill-hub`:
|
||||
|
||||
```bash
|
||||
# 安装全部 Skill
|
||||
npx skills add ziniao-open/skill-hub
|
||||
|
||||
# 安装指定 Skill(@ 后跟 skills/ 下的目录名)
|
||||
npx skills add ziniao-open/skill-hub@ziniao-erp-api-doc
|
||||
npx skills add ziniao-open/skill-hub@ziniao-sso-doc
|
||||
npx skills add ziniao-open/skill-hub@ziniao-webdriver-doc
|
||||
npx skills add ziniao-open/skill-hub@linkfoxai-image-tool
|
||||
```
|
||||
|
||||
> Tip: 使用 `--yes`(`-y`)与 `--global`(`-g`)参数可跳过确认提示并全局安装:
|
||||
> ```bash
|
||||
> npx skills add ziniao-open/skill-hub -y -g
|
||||
> ```
|
||||
|
||||
安装完成后,Agent 会在匹配到对应 `description` 的场景下自动加载并参考相应 Skill。
|
||||
|
||||
### 阅读约定
|
||||
|
||||
- **先读 `SKILL.md`**:了解 Skill 的定位、能力边界与执行/深入流程。
|
||||
- **文档型**:通过 `SKILL.md` 的"按需深入"导航定位到具体文件,仅在需要查阅接口请求/响应参数、示例代码时读取对应 `reference/` 文件,避免一次性加载全部细节。
|
||||
- **执行型**:按 `SKILL.md` 中的能力分组与调用契约,优先复用本地脚本(若存在),否则直接调用开放平台接口完成任务并返回结果。
|
||||
|
||||
## 许可证
|
||||
|
||||
本仓库由紫鸟开放平台维护,内容包含接口文档与执行型 Skill,供开发者集成对接与自动化使用。使用本仓库即表示你已阅读并同意紫鸟开放平台的相关服务条款;接口的实际调用受开放平台的鉴权、计费与限流策略约束。
|
||||
@@ -0,0 +1,301 @@
|
||||
---
|
||||
name: linkfoxai-image-tool
|
||||
description: 这是紫鸟开放平台Skill,面向普通用户直接执行任务,无需关心内部调用细节;当用户要求直接完成图片上传、场景裂变、商品替换、自动抠图等当前 Skill 覆盖能力,并希望直接拿到最终结果时使用本 Skill。执行时优先复用本地脚本(若存在),否则通过开放平台接口完成调用。
|
||||
---
|
||||
|
||||
# linkfoxai-image-tool
|
||||
|
||||
## Trigger Conditions
|
||||
|
||||
- 当用户目标属于当前 Skill 覆盖的能力分组,且希望直接拿到最终结果时,可使用本 Skill。
|
||||
- 当前 Skill 覆盖的能力分组:
|
||||
- `linkfox-image-upload`: 图片上传
|
||||
- `linkfox-scene-fission`: 场景裂变
|
||||
- `linkfox-shop-replace`: 商品替换
|
||||
- `linkfox-auto-image-matting`: 自动抠图
|
||||
- 若用户目标是查询当前 Skill 覆盖任务的状态、处理进度或最终结果,也属于可直接执行的适用场景。
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- 当用户只需要开放平台接入说明、参数解释、代码开发建议或集成设计时,不要使用本 Skill。
|
||||
- 当请求不属于当前 Skill 覆盖的能力范围时,不要强行复用本 Skill。
|
||||
- 当需要你猜测未在契约中声明的参数、字段或调用行为时,不要继续执行,应以现有契约为准。
|
||||
|
||||
## Execution Flow
|
||||
|
||||
- `Step 1`: 读取当前 skill 的 `scripts/` 目录,确认是否存在可用的本地脚本及 `脚本使用指导.md`。
|
||||
- `Step 2`: 若存在脚本,先读取 `scripts/脚本使用指导.md`,确认脚本的 payload、成功/失败语义与退出码规则。
|
||||
- `Step 3`: 根据当前执行环境属于 `Win`、`macOS` 还是 `Linux`,按 `tool分组名_平台` 规则组合目标脚本名;`Win` 对应 `.ps1`,`macOS` 与 `Linux` 对应 `.sh`。
|
||||
- `Step 4`: 若脚本存在且执行成功,直接使用脚本标准输出作为最终结果。
|
||||
- `Step 5`: 若脚本不存在,或脚本执行失败并返回非 `0` 退出码,则改用对应的执行型 tool 直接调用开放平台,按 `requestUrl`、`Raw Request Contract` 与 `Raw Response Contract` 完成调用。
|
||||
|
||||
## Execution Prerequisites
|
||||
|
||||
- `callDomain`: `https://sbappstoreapi.ziniao.com/openapi-router`
|
||||
- `apiKey`: 先确认 `ZNOPEN_API_KEY` 已可用;默认从 `~/.znopen/config.json` 读取,缺失时先提示用户提供,提供后回填文件,完成配置。
|
||||
- `Authorization`: 无需让用户手工拼接请求头,tool 在调用开放平台时会基于可用的 `apiKey` 自动注入该头。
|
||||
- `requestUrl`: 统一由 `callDomain + sourceOpenPath` 组成,调用前应直接使用已生成结果,不建议手工改写。
|
||||
|
||||
## Capability Groups
|
||||
|
||||
### linkfox-image-upload
|
||||
- 适用请求:图片上传
|
||||
- 开放平台执行 tool:`postImageV2UploadByBase64`
|
||||
|
||||
### linkfox-scene-fission
|
||||
- 适用请求:场景裂变
|
||||
- 开放平台执行 tool:`postImageV2MakeSceneFission`、`postImageV2MakeInfo`
|
||||
|
||||
### linkfox-shop-replace
|
||||
- 适用请求:商品替换
|
||||
- 开放平台执行 tool:`postImageV2MakeShopReplace`、`postImageV2MakeInfo`
|
||||
|
||||
### linkfox-auto-image-matting
|
||||
- 适用请求:自动抠图
|
||||
- 开放平台执行 tool:`postImageV2MakeCutout`、`postImageV2MakeInfo`
|
||||
|
||||
## Call Limits
|
||||
|
||||
- tool 发起开放平台请求时,必须自动带上请求头:`Authorization: Bearer {ApiKey}`。
|
||||
- `Bearer` 和 `ApiKey` 之间必须有一个空格,不能省略、不能连写、也不能替换为其他前缀。
|
||||
- 请求方法必须与对应 tool 的 `sourceMethod` 保持一致,不允许自行切换调用方式。
|
||||
- 请求参数必须严格遵循 `Raw Request Contract`,响应解析与错误处理必须严格遵循 `Raw Response Contract`。
|
||||
- 当前置条件未满足、凭证缺失或开放平台返回错误时,不应继续盲目重试或猜测字段含义。
|
||||
|
||||
## Tool Group Definitions
|
||||
|
||||
### linkfox-image-upload
|
||||
|
||||
#### `postImageV2UploadByBase64`
|
||||
|
||||
- Capability Group: `linkfox-image-upload`
|
||||
- Use This Tool When: 当用户当前目标属于“图片上传”场景时使用;通过开放平台接口完成“图片上传”。
|
||||
- Required Inputs: fileName、base64
|
||||
- Returns: 重点查看返回字段:msg、traceId、code、viewUrl
|
||||
- Tool Goal: 图片上传
|
||||
|
||||
##### Definition
|
||||
|
||||
```yaml
|
||||
tool: postImageV2UploadByBase64
|
||||
sourceApiName: 图片上传
|
||||
sourceMethod: POST
|
||||
sourceOpenPath: /linkfox-ai/image/v2/uploadByBase64
|
||||
requestUrl: https://sbappstoreapi.ziniao.com/openapi-router/linkfox-ai/image/v2/uploadByBase64
|
||||
```
|
||||
|
||||
##### Raw Request Contract
|
||||
|
||||
```text
|
||||
{"headers":[{"isRoot":false,"name":"Content-Type","id":"w8pqf70e","type":"","value":"application/json","required":1,"desc":""}],"query":[],"body":[{"isRoot":true,"children":[{"isRoot":false,"name":"fileName","id":"bzxlv5tv","type":"string","required":1,"desc":"文件名(带扩展名)支持格式:jpeg、jpg、png、webp","value":"1.png"},{"isRoot":false,"name":"base64","id":"6bzgux9i","type":"string","required":1,"desc":"图片数据base64编码 文件大小:不超过20MB","value":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"UploadByBase64ReqVo"}]}
|
||||
```
|
||||
|
||||
##### Raw Response Contract
|
||||
|
||||
```text
|
||||
[{"isRoot":true,"children":[{"isRoot":false,"name":"msg","id":"474ze4ft","type":"string","required":0,"desc":"结果描述"},{"isRoot":false,"name":"traceId","id":"gcq4vu8w","type":"string","required":0,"desc":"链路追踪id"},{"isRoot":false,"name":"code","id":"6r3j5kog","type":"integer","required":0,"desc":"结果编码 200成功 非200报错"},{"isRoot":false,"children":[{"isRoot":false,"name":"viewUrl","id":"ohy4rmpb","type":"string","required":0,"desc":"上传成功后的图片访问地址"}],"name":"data","id":"k0lmm7mt","type":"object","required":0},{"isRoot":false,"name":"msgKey","id":"46vvp4b4","type":"string","required":0,"desc":"错误编码"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ResponseDto«ApiImageUploadResVo»"}]
|
||||
```
|
||||
|
||||
### linkfox-scene-fission
|
||||
|
||||
#### `postImageV2MakeSceneFission`
|
||||
|
||||
- Capability Group: `linkfox-scene-fission`
|
||||
- Use This Tool When: 当用户当前目标属于“场景裂变”场景时使用;通过开放平台接口完成“场景裂变”。
|
||||
- Required Inputs: imageUrl、segImageUrl、strength、prompt
|
||||
- Returns: 重点查看返回字段:msg、traceId、code、id
|
||||
- Tool Goal: 场景裂变
|
||||
|
||||
##### Definition
|
||||
|
||||
```yaml
|
||||
tool: postImageV2MakeSceneFission
|
||||
sourceApiName: 场景裂变
|
||||
sourceMethod: POST
|
||||
sourceOpenPath: /linkfox-ai/image/v2/make/sceneFission
|
||||
requestUrl: https://sbappstoreapi.ziniao.com/openapi-router/linkfox-ai/image/v2/make/sceneFission
|
||||
```
|
||||
|
||||
##### Input Rules
|
||||
|
||||
- Image Input Rule: 当前契约只看到 URL 类图片字段,请按契约中的 `https` 图片地址字段传参。
|
||||
- 不要自行追加 `base64` 字段,除非它已经明确出现在 `Raw Request Contract` 中。
|
||||
|
||||
##### Raw Request Contract
|
||||
|
||||
```text
|
||||
{"headers":[{"isRoot":false,"name":"Content-Type","id":"w8pqf70e","type":"","value":"application/json","required":1,"desc":""}],"query":[],"body":[{"isRoot":true,"children":[{"isRoot":false,"name":"imageUrl","id":"bzxlv5tv","type":"string","required":1,"desc":"原图","value":"https://test-file-ai.linkfox.com/UPLOAD/MANAGE/CASE/SCENE_FISSION/1744309376725860352/image.jpg"},{"isRoot":false,"name":"segImageUrl","id":"6bzgux9i","type":"string","required":0,"desc":"保留区域图,不传会自动调用智能抠图"},{"id":"r8mecvca","name":"strength","type":"number","desc":"生成图与原图相似度 默认值:0.5 值越大相似度度越高 浮点数 小数点后只有一位 可选范围 [0.1,1]","isInit":true,"required":0},{"id":"grl5efmb","name":"prompt","type":"string","desc":"强化内容描述,不传会自动根据参数图提取 最长600字符,超出会截断","isInit":true,"required":0},{"id":"87njvdrs","name":"imageOutputWidth","type":"integer","desc":"输出尺寸 宽度 最长不能超过2048,最短不能低于32","isInit":true,"required":0},{"id":"ukpogp3d","name":"imageOutputHeight","type":"integer","desc":"输出尺寸 高度 最长不能超过2048,最短不能低于32","isInit":true,"required":0},{"id":"ob2j7568","name":"provider","type":"string","desc":"场景裂变模式 SCENE_FISSION_REALISTIC:写实(默认值) SCENE_FISSION_SIMPLE:简约 SCENE_FISSION_INTELLIGENT:智能","isInit":true,"value":"SCENE_FISSION_REALISTIC"},{"id":"g017pou2","name":"outputNum","type":"integer","desc":"输出张数 取值范围:[1,4] 默认值:1","isInit":true,"required":0}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"SceneFissionParamsVo"}]}
|
||||
```
|
||||
|
||||
##### Raw Response Contract
|
||||
|
||||
```text
|
||||
[{"isRoot":true,"children":[{"isRoot":false,"name":"msg","id":"474ze4ft","type":"string","required":0,"desc":"结果描述"},{"isRoot":false,"name":"traceId","id":"gcq4vu8w","type":"string","required":0,"desc":"链路追踪id"},{"isRoot":false,"name":"code","id":"6r3j5kog","type":"integer","required":0,"desc":"结果编码 200成功 非200报错"},{"isRoot":false,"children":[{"isRoot":false,"name":"id","id":"ohy4rmpb","type":"integer","required":0,"desc":"作图任务ID"}],"name":"data","id":"k0lmm7mt","type":"object","required":0},{"isRoot":false,"name":"msgKey","id":"46vvp4b4","type":"string","required":0,"desc":"错误编码"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ResponseDto«OnlyIdVo»"}]
|
||||
```
|
||||
|
||||
#### `postImageV2MakeInfo`
|
||||
|
||||
- Capability Group: `linkfox-scene-fission`
|
||||
- Use This Tool When: 当用户当前目标属于“场景裂变”场景时使用;通过开放平台接口完成“获取AI作图结果”。
|
||||
- Required Inputs: id
|
||||
- Returns: 重点查看返回字段:msg、traceId、code、id
|
||||
- Reuse Note: 该 tool 名称在多个能力分组中复用;当前块表示它在 `linkfox-scene-fission` 场景下的使用入口,按当前分组目标理解即可。
|
||||
- Tool Goal: 获取AI作图结果
|
||||
|
||||
##### Definition
|
||||
|
||||
```yaml
|
||||
tool: postImageV2MakeInfo
|
||||
sourceApiName: 获取AI作图结果
|
||||
sourceMethod: POST
|
||||
sourceOpenPath: /linkfox-ai/image/v2/make/info
|
||||
requestUrl: https://sbappstoreapi.ziniao.com/openapi-router/linkfox-ai/image/v2/make/info
|
||||
```
|
||||
|
||||
##### Raw Request Contract
|
||||
|
||||
```text
|
||||
{"headers":[{"isRoot":false,"name":"Content-Type","id":"w8pqf70e","type":"","value":"application/json","required":1,"desc":""}],"query":[],"body":[{"isRoot":true,"children":[{"isRoot":false,"name":"id","id":"bzxlv5tv","type":"string","required":1,"desc":"作图任务ID","value":"111"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"OnlyIdVo"}]}
|
||||
```
|
||||
|
||||
##### Raw Response Contract
|
||||
|
||||
```text
|
||||
[{"isRoot":true,"children":[{"isRoot":false,"name":"msg","id":"474ze4ft","type":"string","required":0,"desc":"结果描述"},{"isRoot":false,"name":"traceId","id":"gcq4vu8w","type":"string","required":0,"desc":"链路追踪id"},{"isRoot":false,"name":"code","id":"6r3j5kog","type":"integer","required":0,"desc":"结果编码 200成功 非200报错"},{"isRoot":false,"children":[{"isRoot":false,"name":"id","id":"ohy4rmpb","type":"integer","required":0,"desc":"作图任务ID"},{"isRoot":false,"name":"status","id":"gcq4vu8w","type":"integer","required":0,"desc":"状态 1.排队中 2.生成中 3.成功 4.失败"},{"isRoot":false,"name":"errorCode","id":"6r3j5kog","type":"string","required":0,"desc":"错误码"},{"isRoot":false,"name":"errorMsg","id":"46vvp4b4","type":"string","required":0,"desc":"错误文字描述"},{"isRoot":false,"name":"resultList","id":"k0lmm7mt","type":"array","required":0,"desc":"输出结果图片数组","children":[{"isRoot":false,"name":"id","id":"item1","type":"integer","required":0,"desc":"图片ID"},{"isRoot":false,"name":"status","id":"item2","type":"integer","required":0,"desc":"状态 0.生成中 1.成功 2.失败"},{"isRoot":false,"name":"url","id":"item3","type":"string","required":0,"desc":"高清图"},{"isRoot":false,"name":"width","id":"item4","type":"integer","required":0,"desc":"宽"},{"isRoot":false,"name":"height","id":"item5","type":"integer","required":0,"desc":"高"},{"isRoot":false,"name":"format","id":"item6","type":"string","required":0,"desc":"格式"}]}],"name":"data","id":"k0lmm7mt","type":"object","required":0},{"isRoot":false,"name":"msgKey","id":"46vvp4b4","type":"string","required":0,"desc":"错误编码"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ResponseDto«ApiImageMakeResVo»"}]
|
||||
```
|
||||
|
||||
### linkfox-shop-replace
|
||||
|
||||
#### `postImageV2MakeShopReplace`
|
||||
|
||||
- Capability Group: `linkfox-shop-replace`
|
||||
- Use This Tool When: 当用户当前目标属于“商品替换”场景时使用;通过开放平台接口完成“商品替换”。
|
||||
- Required Inputs: imageUrl、sourceImageUrl、targetOriginUrl、targetImageUrl
|
||||
- Returns: 重点查看返回字段:msg、traceId、code、id
|
||||
- Tool Goal: 商品替换
|
||||
|
||||
##### Definition
|
||||
|
||||
```yaml
|
||||
tool: postImageV2MakeShopReplace
|
||||
sourceApiName: 商品替换
|
||||
sourceMethod: POST
|
||||
sourceOpenPath: /linkfox-ai/image/v2/make/shopReplace
|
||||
requestUrl: https://sbappstoreapi.ziniao.com/openapi-router/linkfox-ai/image/v2/make/shopReplace
|
||||
```
|
||||
|
||||
##### Input Rules
|
||||
|
||||
- Image Input Rule: 当前契约只看到 URL 类图片字段,请按契约中的 `https` 图片地址字段传参。
|
||||
- 不要自行追加 `base64` 字段,除非它已经明确出现在 `Raw Request Contract` 中。
|
||||
|
||||
##### Raw Request Contract
|
||||
|
||||
```text
|
||||
{"headers":[{"isRoot":false,"name":"Content-Type","id":"w8pqf70e","type":"","value":"application/json","required":1,"desc":""}],"query":[],"body":[{"isRoot":true,"children":[{"isRoot":false,"name":"imageUrl","id":"bzxlv5tv","type":"string","required":1,"desc":"商品原图","value":"https://linkfoxai-ailab.oss-cn-shenzhen.aliyuncs.com/UPLOAD/1701144666432344064/2024/11/18/96a7a42fc965491dbc625d2db09f2478.png"},{"isRoot":false,"name":"sourceImageUrl","id":"6bzgux9i","type":"string","required":0,"desc":"原图抠出来的商品图"},{"id":"r8mecvca","name":"targetOriginUrl","type":"string","desc":"替换的目标原图","isInit":true,"required":1,"value":"https://linkfoxai-ailab.oss-cn-shenzhen.aliyuncs.com/UPLOAD/1701144666432344064/2024/11/18/9778678b4724467485346248032387ec.png"},{"id":"grl5efmb","name":"targetImageUrl","type":"string","desc":"替换的目标原图抠图结果","isInit":true,"required":0},{"id":"87njvdrs","name":"denoiseStrength","type":"string","desc":"生成图变化程度 默认值:0.5 越大越高 浮点数 可选范围 [0,1]","isInit":true,"required":0},{"id":"ukpogp3d","name":"imageOutputWidth","type":"integer","desc":"输出尺寸 宽度 最长边不能超过4096,最短边不能低于32","isInit":true,"required":0},{"id":"g017pou2","name":"imageOutputHeight","type":"integer","desc":"输出尺寸 高度 最长边不能超过4096,最短边不能低于32","isInit":true,"required":0},{"id":"vtarf9ph","name":"outputNum","type":"integer","desc":"输出张数 取值范围:[1,4] 默认值:1","isInit":true,"required":0}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ShopReplaceParamsVo"}]}
|
||||
```
|
||||
|
||||
##### Raw Response Contract
|
||||
|
||||
```text
|
||||
[{"isRoot":true,"children":[{"isRoot":false,"name":"msg","id":"474ze4ft","type":"string","required":0,"desc":"结果描述"},{"isRoot":false,"name":"traceId","id":"gcq4vu8w","type":"string","required":0,"desc":"链路追踪id"},{"isRoot":false,"name":"code","id":"6r3j5kog","type":"integer","required":0,"desc":"结果编码 200成功 非200报错"},{"isRoot":false,"children":[{"isRoot":false,"name":"id","id":"ohy4rmpb","type":"integer","required":0,"desc":"作图任务ID"}],"name":"data","id":"k0lmm7mt","type":"object","required":0},{"isRoot":false,"name":"msgKey","id":"46vvp4b4","type":"string","required":0,"desc":"错误编码"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ResponseDto«OnlyIdVo»"}]
|
||||
```
|
||||
|
||||
#### `postImageV2MakeInfo`
|
||||
|
||||
- Capability Group: `linkfox-shop-replace`
|
||||
- Use This Tool When: 当用户当前目标属于“商品替换”场景时使用;通过开放平台接口完成“获取AI作图结果”。
|
||||
- Required Inputs: id
|
||||
- Returns: 重点查看返回字段:msg、traceId、code、id
|
||||
- Reuse Note: 该 tool 名称在多个能力分组中复用;当前块表示它在 `linkfox-shop-replace` 场景下的使用入口,按当前分组目标理解即可。
|
||||
- Tool Goal: 获取AI作图结果
|
||||
|
||||
##### Definition
|
||||
|
||||
```yaml
|
||||
tool: postImageV2MakeInfo
|
||||
sourceApiName: 获取AI作图结果
|
||||
sourceMethod: POST
|
||||
sourceOpenPath: /linkfox-ai/image/v2/make/info
|
||||
requestUrl: https://sbappstoreapi.ziniao.com/openapi-router/linkfox-ai/image/v2/make/info
|
||||
```
|
||||
|
||||
##### Raw Request Contract
|
||||
|
||||
```text
|
||||
{"headers":[{"isRoot":false,"name":"Content-Type","id":"w8pqf70e","type":"","value":"application/json","required":1,"desc":""}],"query":[],"body":[{"isRoot":true,"children":[{"isRoot":false,"name":"id","id":"bzxlv5tv","type":"string","required":1,"desc":"作图任务ID","value":"111"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"OnlyIdVo"}]}
|
||||
```
|
||||
|
||||
##### Raw Response Contract
|
||||
|
||||
```text
|
||||
[{"isRoot":true,"children":[{"isRoot":false,"name":"msg","id":"474ze4ft","type":"string","required":0,"desc":"结果描述"},{"isRoot":false,"name":"traceId","id":"gcq4vu8w","type":"string","required":0,"desc":"链路追踪id"},{"isRoot":false,"name":"code","id":"6r3j5kog","type":"integer","required":0,"desc":"结果编码 200成功 非200报错"},{"isRoot":false,"children":[{"isRoot":false,"name":"id","id":"ohy4rmpb","type":"integer","required":0,"desc":"作图任务ID"},{"isRoot":false,"name":"status","id":"gcq4vu8w","type":"integer","required":0,"desc":"状态 1.排队中 2.生成中 3.成功 4.失败"},{"isRoot":false,"name":"errorCode","id":"6r3j5kog","type":"string","required":0,"desc":"错误码"},{"isRoot":false,"name":"errorMsg","id":"46vvp4b4","type":"string","required":0,"desc":"错误文字描述"},{"isRoot":false,"name":"resultList","id":"k0lmm7mt","type":"array","required":0,"desc":"输出结果图片数组","children":[{"isRoot":false,"name":"id","id":"item1","type":"integer","required":0,"desc":"图片ID"},{"isRoot":false,"name":"status","id":"item2","type":"integer","required":0,"desc":"状态 0.生成中 1.成功 2.失败"},{"isRoot":false,"name":"url","id":"item3","type":"string","required":0,"desc":"高清图"},{"isRoot":false,"name":"width","id":"item4","type":"integer","required":0,"desc":"宽"},{"isRoot":false,"name":"height","id":"item5","type":"integer","required":0,"desc":"高"},{"isRoot":false,"name":"format","id":"item6","type":"string","required":0,"desc":"格式"}]}],"name":"data","id":"k0lmm7mt","type":"object","required":0},{"isRoot":false,"name":"msgKey","id":"46vvp4b4","type":"string","required":0,"desc":"错误编码"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ResponseDto«ApiImageMakeResVo»"}]
|
||||
```
|
||||
|
||||
### linkfox-auto-image-matting
|
||||
|
||||
#### `postImageV2MakeCutout`
|
||||
|
||||
- Capability Group: `linkfox-auto-image-matting`
|
||||
- Use This Tool When: 当用户当前目标属于“自动抠图”场景时使用;通过开放平台接口完成“自动抠图”。
|
||||
- Required Inputs: imageUrl、subType、clothClass
|
||||
- Returns: 重点查看返回字段:msg、traceId、code、id
|
||||
- Tool Goal: 自动抠图
|
||||
|
||||
##### Definition
|
||||
|
||||
```yaml
|
||||
tool: postImageV2MakeCutout
|
||||
sourceApiName: 自动抠图
|
||||
sourceMethod: POST
|
||||
sourceOpenPath: /linkfox-ai/image/v2/make/cutout
|
||||
requestUrl: https://sbappstoreapi.ziniao.com/openapi-router/linkfox-ai/image/v2/make/cutout
|
||||
```
|
||||
|
||||
##### Input Rules
|
||||
|
||||
- Image Input Rule: 当前契约只看到 URL 类图片字段,请按契约中的 `https` 图片地址字段传参。
|
||||
- 不要自行追加 `base64` 字段,除非它已经明确出现在 `Raw Request Contract` 中。
|
||||
|
||||
##### Raw Request Contract
|
||||
|
||||
```text
|
||||
{"headers":[{"isRoot":false,"name":"Content-Type","id":"w8pqf70e","type":"","value":"application/json","required":1,"desc":""}],"query":[],"body":[{"isRoot":true,"children":[{"isRoot":false,"name":"imageUrl","id":"bzxlv5tv","type":"string","required":1,"desc":"原图","value":"https://linkfoxai-ailab.oss-cn-shenzhen.aliyuncs.com/UPLOAD/1701144666432344064/2024/11/13/bec32c4461644befb202f86b23502583.png"},{"isRoot":false,"name":"subType","id":"6bzgux9i","type":"integer","required":1,"desc":"抠图类型 1: 通用 2: 人像 3: 商品 9: 服饰 12: 头发 13:人脸","value":"1"},{"id":"r8mecvca","name":"clothClass","type":"string","desc":"服饰分类,只有抠图类型=9(服饰抠图)时才会生效, 可多选,以','号分割","isInit":true,"required":0}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"CutoutParamsVo"}]}
|
||||
```
|
||||
|
||||
##### Raw Response Contract
|
||||
|
||||
```text
|
||||
[{"isRoot":true,"children":[{"isRoot":false,"name":"msg","id":"474ze4ft","type":"string","required":0,"desc":"结果描述"},{"isRoot":false,"name":"traceId","id":"gcq4vu8w","type":"string","required":0,"desc":"链路追踪id"},{"isRoot":false,"name":"code","id":"6r3j5kog","type":"integer","required":0,"desc":"结果编码 200成功 非200报错"},{"isRoot":false,"children":[{"isRoot":false,"name":"id","id":"ohy4rmpb","type":"integer","required":0,"desc":"作图任务ID"}],"name":"data","id":"k0lmm7mt","type":"object","required":0},{"isRoot":false,"name":"msgKey","id":"46vvp4b4","type":"string","required":0,"desc":"错误编码"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ResponseDto«OnlyIdVo»"}]
|
||||
```
|
||||
|
||||
#### `postImageV2MakeInfo`
|
||||
|
||||
- Capability Group: `linkfox-auto-image-matting`
|
||||
- Use This Tool When: 当用户当前目标属于“自动抠图”场景时使用;通过开放平台接口完成“获取AI作图结果”。
|
||||
- Required Inputs: id
|
||||
- Returns: 重点查看返回字段:msg、traceId、code、id
|
||||
- Reuse Note: 该 tool 名称在多个能力分组中复用;当前块表示它在 `linkfox-auto-image-matting` 场景下的使用入口,按当前分组目标理解即可。
|
||||
- Tool Goal: 获取AI作图结果
|
||||
|
||||
##### Definition
|
||||
|
||||
```yaml
|
||||
tool: postImageV2MakeInfo
|
||||
sourceApiName: 获取AI作图结果
|
||||
sourceMethod: POST
|
||||
sourceOpenPath: /linkfox-ai/image/v2/make/info
|
||||
requestUrl: https://sbappstoreapi.ziniao.com/openapi-router/linkfox-ai/image/v2/make/info
|
||||
```
|
||||
|
||||
##### Raw Request Contract
|
||||
|
||||
```text
|
||||
{"headers":[{"isRoot":false,"name":"Content-Type","id":"w8pqf70e","type":"","value":"application/json","required":1,"desc":""}],"query":[],"body":[{"isRoot":true,"children":[{"isRoot":false,"name":"id","id":"bzxlv5tv","type":"string","required":1,"desc":"作图任务ID","value":"111"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"OnlyIdVo"}]}
|
||||
```
|
||||
|
||||
##### Raw Response Contract
|
||||
|
||||
```text
|
||||
[{"isRoot":true,"children":[{"isRoot":false,"name":"msg","id":"474ze4ft","type":"string","required":0,"desc":"结果描述"},{"isRoot":false,"name":"traceId","id":"gcq4vu8w","type":"string","required":0,"desc":"链路追踪id"},{"isRoot":false,"name":"code","id":"6r3j5kog","type":"integer","required":0,"desc":"结果编码 200成功 非200报错"},{"isRoot":false,"children":[{"isRoot":false,"name":"id","id":"ohy4rmpb","type":"integer","required":0,"desc":"作图任务ID"},{"isRoot":false,"name":"status","id":"gcq4vu8w","type":"integer","required":0,"desc":"状态 1.排队中 2.生成中 3.成功 4.失败"},{"isRoot":false,"name":"errorCode","id":"6r3j5kog","type":"string","required":0,"desc":"错误码"},{"isRoot":false,"name":"errorMsg","id":"46vvp4b4","type":"string","required":0,"desc":"错误文字描述"},{"isRoot":false,"name":"resultList","id":"k0lmm7mt","type":"array","required":0,"desc":"输出结果图片数组","children":[{"isRoot":false,"name":"id","id":"item1","type":"integer","required":0,"desc":"图片ID"},{"isRoot":false,"name":"status","id":"item2","type":"integer","required":0,"desc":"状态 0.生成中 1.成功 2.失败"},{"isRoot":false,"name":"url","id":"item3","type":"string","required":0,"desc":"高清图"},{"isRoot":false,"name":"width","id":"item4","type":"integer","required":0,"desc":"宽"},{"isRoot":false,"name":"height","id":"item5","type":"integer","required":0,"desc":"高"},{"isRoot":false,"name":"format","id":"item6","type":"string","required":0,"desc":"格式"}]}],"name":"data","id":"k0lmm7mt","type":"object","required":0},{"isRoot":false,"name":"msgKey","id":"46vvp4b4","type":"string","required":0,"desc":"错误编码"}],"name":"","id":0,"type":"object","value":"","required":0,"desc":"ResponseDto«ApiImageMakeResVo»"}]
|
||||
```
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: ziniao-erp-api-doc
|
||||
description: >
|
||||
Use when 需要理解紫鸟ERP API 的定位、能力、权限体系或调用流程,
|
||||
用于 ERP 系统对接规划、集成设计或接口评估。
|
||||
不要用于直接查阅具体接口参数——查阅时读取 reference/ 下对应文件。
|
||||
---
|
||||
|
||||
# ziniao-erp-api-doc(紫鸟ERP API 文档)
|
||||
|
||||
## 何时使用 / 不使用
|
||||
|
||||
| 场景 | 做法 |
|
||||
|------|------|
|
||||
| 需要了解紫鸟 API 的能力和模块 | 读本 Skill |
|
||||
| 需要了解接口权限点以申请开通 | 读本 Skill §权限点汇总 |
|
||||
| 需要做 ERP 对接的集成设计 | 读本 Skill |
|
||||
| 需要查阅某个接口的详细参数 | 读本 Skill §按需深入,再读 reference/ 文件 |
|
||||
|
||||
---
|
||||
|
||||
## Level 0:定位与边界
|
||||
|
||||
### 是什么
|
||||
|
||||
紫鸟ERP REST API,面向 ERP/第三方系统,通过"简单通用模式"(Bearer API Key)认证,实现紫鸟浏览器的企业级管理——涵盖账号、设备、访问策略、角色权限、部门员工五大模块,共 72 个接口。
|
||||
|
||||
### 能力概览
|
||||
|
||||
| 能力域 | 接口数 | 说明 |
|
||||
|--------|--------|------|
|
||||
| 账号管理 | 22 | 账号 CRUD、授权管理、标签体系、缓存清除 |
|
||||
| 设备管理 | 11 | 套餐/设备购买续费、绑定解绑、自有设备管理 |
|
||||
| 访问策略 | 22 | 网页/分组管理、访问规则 CRUD、账号策略绑定 |
|
||||
| 角色和权限 | 7 | 角色 CRUD、权限列表、用户角色调整 |
|
||||
| 部门员工 | 10 | 部门 CRUD/移动、员工新增/修改/查询/启禁用 |
|
||||
|
||||
### 不做什么(边界)
|
||||
|
||||
- 不包含 OAuth 授权模式(仅覆盖简单通用模式)
|
||||
- 不包含浏览器指纹配置或窗口管理
|
||||
- 不包含账单/支付明细(仅含设备购买/续费操作)
|
||||
- 不包含企业创建/注销等顶级管理
|
||||
|
||||
---
|
||||
|
||||
## Level 1:架构与流程
|
||||
|
||||
### 核心概念
|
||||
|
||||
**API Key 认证**:在开放平台创建"卖家自研应用"并选择"简单通用模式"获取 API Key。请求头:`Authorization: Bearer {API_Key}`。
|
||||
|
||||
**双层响应结构**:网关层(request_id / code / msg / data(业务层))+ 业务层(ret / status / msg + 业务字段)。网关 code="0" 到达业务层;业务 ret=0 操作成功。失败时额外返回 sub_code / sub_msg。注意:业务数据不一定在业务层的 data 字段中,部分接口的业务字段直接位于业务层根级(参见各 reference 文件中的响应说明)。
|
||||
|
||||
**核心 ID 体系**:companyId(紫鸟企业ID,几乎所有接口必传,可在开放平台首页右侧获取)、storeId(账号ID)、userId/staffId(用户ID)、proxyId(设备ID)、tagId/tid(标签ID)、roleId(角色ID)、ruleId(规则ID)。
|
||||
|
||||
**权限点体系**:每个接口归属一个"所属权限点",应用需在开放平台申请开通对应权限后才能调用。多个接口可共享同一权限点。
|
||||
|
||||
### 调用流程
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[获取 API Key] --> B[申请权限点]
|
||||
B --> C[构造请求头]
|
||||
C --> D[POST 请求]
|
||||
D --> E{网关 code=0?}
|
||||
E -- 是 --> F{业务 ret=0?}
|
||||
E -- 否 --> G[检查 Key/权限]
|
||||
F -- 是 --> H[解析业务字段]
|
||||
F -- 否 --> I[读取 msg]
|
||||
```
|
||||
|
||||
Base URL:`https://sbappstoreapi.ziniao.com/openapi-router`
|
||||
|
||||
### 权限点汇总
|
||||
|
||||
应用需在开放平台申请开通以下权限点,才能调用对应接口:
|
||||
|
||||
| 模块 | 需申请的权限点 |
|
||||
|------|-------------|
|
||||
| 账号管理 | ERP-创建与删除账号权限、ERP-账号查看权限、ERP-编辑账号基础信息、ERP-账号授权权限、ERP-清除账号授权、ERP-清除账号缓存、ERP-查询某用户有权限的账号列表、ERP-标签列表、账号标签管理权限 |
|
||||
| 设备管理 | ERP-设备套餐列表查询权限、ERP-设备购买与续费权限、ERP-设备绑定权限、ERP-设备查询、ERP-开关自动续费、ERP-查询已购设备价格接口、ERP-解绑设备、ERP-添加自有设备(新)、ERP-修改自有设备信息(新) |
|
||||
| 访问策略 | ERP-网页访问权限(覆盖全部 22 个访问策略接口) |
|
||||
| 角色权限 | ERP-角色添加、修改权限、ERP-角色列表查询、ERP-角色详情、ERP-权限列表 |
|
||||
| 部门员工 | ERP-部门与员工接口、ERP-用户的部门变更 |
|
||||
|
||||
每个 reference 文件中标注了各接口的具体权限点归属。
|
||||
|
||||
### 公共响应结构
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "全局请求追踪ID",
|
||||
"code": "0",
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"ret": 0,
|
||||
"status": "success",
|
||||
"data": [],
|
||||
"msg": "",
|
||||
"count": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:业务数据位置因接口而异。多数接口的业务数据在 `data.data` 中,但部分接口的业务字段直接在业务层根级(与 ret/status 同级)。例如"ERP-员工新增v2"返回的 id、password 直接在业务层,不在 data 字段内。请以各 reference 文件中"响应 data"或"响应"的标注为准——标注"响应 data"表示数据在 data 字段中,标注"响应"则表示字段在业务层根级。
|
||||
|
||||
### 环境约定
|
||||
|
||||
- 请求编码:UTF-8,Content-Type:application/json
|
||||
- 域名:`https://sbappstoreapi.ziniao.com/openapi-router`
|
||||
- 几乎所有接口均为 POST + JSON body
|
||||
- 前提:创建"卖家自研应用",选择"简单通用模式",申请所需权限点,配置 IP 白名单
|
||||
|
||||
---
|
||||
|
||||
## 按需深入(Level 2 路由表)
|
||||
|
||||
> **规则:以下内容不要主动加载。**
|
||||
> 仅在需要了解某个细节时,按路由表读取对应的 reference/ 文件。
|
||||
|
||||
| 你需要了解 | 读取文件 |
|
||||
|-----------|---------|
|
||||
| 认证方式、请求构造、代码示例(Java/Python/JS)、常见错误 | `reference/api-guide.md` |
|
||||
| 账号创建/删除/编辑/列表查询/标签列表/缓存清除 | `reference/account-crud.md` |
|
||||
| 账号授权新增/删除/查询/清除/授权用户列表 | `reference/account-auth.md` |
|
||||
| 标签添加/删除/重命名/绑定/解绑/替换/移除/清空/列表 | `reference/account-tags.md` |
|
||||
| 设备套餐/购买/续费/绑定/解绑/查询/自有设备管理 | `reference/device-management.md` |
|
||||
| 网页增删改查/网页分组管理 | `reference/access-policy-web.md` |
|
||||
| 访问规则新建/编辑/删除/启禁用/详情/成员账号设置 | `reference/access-policy-rules.md` |
|
||||
| 角色增改查/权限列表/用户角色调整 | `reference/roles-permissions.md` |
|
||||
| 部门增删改查移动/员工增改查启禁用/部门变更 | `reference/department-staff.md` |
|
||||
@@ -0,0 +1,219 @@
|
||||
# 访问策略 - 访问规则与账号策略接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅访问规则的新建、编辑、删除、启禁用、详情,以及规则成员/账号设置、账号策略绑定接口时加载。
|
||||
|
||||
所有接口**权限点**均为 **ERP-网页访问权限**,方法均为 POST。
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则新建
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/add`
|
||||
|
||||
> addIds 与 addNames 可任传其一,都传取并集。
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| name | 规则名称 | 是 | string |
|
||||
| desc | 规则描述 | 是 | string |
|
||||
| activeUser | 生效成员(1除boss外所有/2指定成员/3指定部门/4指定角色) | 是 | number |
|
||||
| userChange | 指定成员对象 | 是 | object |
|
||||
| userChange.addIds | 新增成员id列表(空值传[]) | 否 | string[] |
|
||||
| userChange.addNames | 新增成员用户名列表 | 否 | string[] |
|
||||
| userChange.departmentIds | 指定部门列表 | 否 | number[] |
|
||||
| userChange.roleIds | 指定角色列表 | 否 | number[] |
|
||||
| activeAccount | 生效账号(0所有/1指定) | 是 | number |
|
||||
| accountChange | 指定账号对象 | 是 | object |
|
||||
| accountChange.addIds | 账号id列表 | 否 | string[] |
|
||||
| activeRange | 生效范围(1所有网站/2黑名单/3白名单) | 是 | string |
|
||||
| configs | 配置详情数组 | 是 | array |
|
||||
| configs[].urlId | 网页ID(activeRange=1时传0) | 是 | number |
|
||||
| configs[].isAccessible | 是否可访问 | 是 | boolean |
|
||||
| configs[].isLog | 是否记录日志 | 是 | boolean |
|
||||
| configs[].functions | 操作控制配置数组 | 是 | array |
|
||||
| configs[].functions[].function | clipboard/file_upload/file_download/print/console/password/mouse_click/keyboard_input | 是 | string |
|
||||
| configs[].functions[].permission | 1允许/2允许且记录/3限制且记录/6限制 | 是 | number |
|
||||
| isSkipRuleConflictCheck | 是否跳过冲突检测 | 是 | boolean |
|
||||
| effectiveTime | 生效时间(可选) | 否 | object |
|
||||
| effectiveTime.timeType | 0永久 1限制时间 | 是 | string |
|
||||
| effectiveTime.startTime | 开始时间戳(秒),永久填0 | 是 | number |
|
||||
| effectiveTime.endTime | 结束时间戳(秒),永久填0 | 是 | number |
|
||||
| isAllowRequest | 是否允许申请访问(默认true) | 否 | boolean |
|
||||
| shareCompanyIds | 合作公司id列表 | 否 | number[] |
|
||||
| domIds | dom列表 | 否 | number[] |
|
||||
|
||||
**响应 data**:含 id(number)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则编辑
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/edit`
|
||||
|
||||
> addIds/addNames 可任传其一取并集;deleteIds/deleteNames 不能与 add 重复。
|
||||
|
||||
参数结构与「规则新建」基本一致,差异:
|
||||
- 新增 `ruleId`(number, 必须) — 规则ID
|
||||
- `userChange` / `accountChange` 增加 `deleteIds`(string[]) 和 `deleteNames`(string[]) 用于删除成员
|
||||
- 无 `activeRange`、`isSkipRuleConflictCheck` 参数
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则删除
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/delete`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| ruleIds | 规则ID列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则启用禁用
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/active`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| ruleIds | 规则ID列表 | 是 | number[] |
|
||||
| isActive | 0禁用 1启用 | 是 | string |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/list`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| search | 搜索关键字 | 否 | string |
|
||||
| activeRange | 1所有网站/2黑名单/3白名单 | 否 | number |
|
||||
| isActive | 0禁用 1启用 | 否 | number |
|
||||
| ruleListType | 0全部 1账号可绑定 | 否 | number |
|
||||
| accountId | 账号id(获取可绑定规则时传) | 否 | number |
|
||||
| page | 页码 | 是 | number |
|
||||
| limit | 每页条数 | 是 | number |
|
||||
|
||||
**响应 data**:含 list 数组(每项含 id、name、activeUser、activeRange、isActive、description、users[]、roles[]、departments[]、accounts[]、presetType、canChangeUser/Account/Url/Active/canDelete/canCopy)和 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则详情
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/detail`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| ruleId | 规则ID | 是 | number |
|
||||
|
||||
**响应 data**:含 id、name、description、activeUser、activeRange、activeAccount、users[{id,name,username}]、roles[{id,name}]、departments[{id,name}]、accounts[{id,name}]、configs[{urlId,urlName,groupId,groupName,tType,isAccessible,isLog,urlPattern,functions[{function,permission}]}]、effectiveTime{timeType,startTime,endTime}、shareCompanys[{id,name}]、domList[]、domIds[]。
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则设置生效成员
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/change_user`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| ruleId | 规则ID | 是 | number |
|
||||
| activeUser | 1所有/2指定成员/3指定部门/4指定角色 | 是 | number |
|
||||
| userChange.addIds | 新增成员id(空传[]) | 是 | string[] |
|
||||
| userChange.addNames | 新增成员用户名 | 否 | string[] |
|
||||
| userChange.deleteIds | 删除成员id(空传[]) | 是 | string[] |
|
||||
| userChange.deleteNames | 删除成员用户名 | 否 | string[] |
|
||||
| userChange.departmentIds | 指定部门 | 是 | number[] |
|
||||
| userChange.roleIds | 指定角色 | 是 | number[] |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-访问规则设置生效账号
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/change_account`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| ruleId | 规则ID | 是 | number |
|
||||
| activeAccount | 0所有 1指定 | 是 | number |
|
||||
| accountChange.addIds | 新增账号id | 是 | string[] |
|
||||
| accountChange.deleteIds | 删除账号id | 是 | string[] |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-规则可绑定的账号列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/account/bindable_list`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| page | 页码 | 是 | number |
|
||||
| limit | 每页条数 | 是 | number |
|
||||
| ruleId | 规则id | 否 | number |
|
||||
| platformIds | 平台id列表 | 否 | number[] |
|
||||
| tagIds | 标签id列表 | 否 | number[] |
|
||||
| accountName | 账号名称 | 否 | string |
|
||||
|
||||
**响应 data**:含 list(每项含 id、name、collocationRelation、sitePlatformName、storePlatformId、tags[{id,name}])、count(number)、bindAccountIds(number[])。
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号添加访问策略
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/account_ref/add_to_account`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| accountId | 账号id | 是 | string |
|
||||
| ruleIds | 访问策略id列表 | 是 | number[] |
|
||||
| isSkipRuleConflictCheck | 是否跳过冲突检测 | 是 | boolean |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号移除访问策略
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/account_ref/remove_from_account`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| accountId | 账号id | 是 | string |
|
||||
| ruleIds | 访问策略id列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号绑定的访问策略列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule/list/one_account`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| accountId | 账号id | 是 | string |
|
||||
| page | 页码 | 是 | number |
|
||||
| limit | 每页条数 | 是 | number |
|
||||
|
||||
**响应 data**:含 list 数组(每项含 id、name、activeRange、activeUser、isActive、users[{id,name,username}]、urlCount、isInvalid、canDelete)和 count。
|
||||
@@ -0,0 +1,152 @@
|
||||
# 访问策略 - 网页与分组管理接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅网页增删改查、网页分组管理接口的详细参数时加载。
|
||||
|
||||
所有接口**权限点**均为 **ERP-网页访问权限**,方法均为 POST。
|
||||
|
||||
---
|
||||
|
||||
## ERP-添加网页
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url/add`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| name | 网页名称 | 是 | string |
|
||||
| groupId | 分组ID | 是 | number |
|
||||
| desc | 描述 | 否 | string |
|
||||
| urlPattern | URL | 是 | string |
|
||||
|
||||
**响应 data**:含 id(number)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-编辑网页
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url/edit`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| urlId | 网页ID | 是 | number |
|
||||
| name | 网页名称 | 是 | string |
|
||||
| groupId | 分组ID | 是 | number |
|
||||
| desc | 描述 | 否 | string |
|
||||
| urlPattern | URL | 是 | string |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-删除网页
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url/delete`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| urlIds | 网页ID列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-网页列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url/list`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| search | 搜索关键字 | 否 | string |
|
||||
| groupIds | 筛选分组ID | 否 | number[] |
|
||||
| tType | 1系统预置 2自定义 | 否 | number |
|
||||
| page | 页码 | 是 | number |
|
||||
| limit | 每页条数 | 是 | number |
|
||||
|
||||
**响应 data**:含 list 数组(每项含 id、name、groupId、groupName、tType、urlPattern、description)和 count(number)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-网页详情
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url/detail`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| urlId | 网页ID | 是 | number |
|
||||
| urlPattern | 网页url(按url查详情) | 否 | string |
|
||||
|
||||
**响应 data**:含 id、name、groupId、groupName、tType、urlPattern、description。
|
||||
|
||||
---
|
||||
|
||||
## ERP-网页修改分组
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url/change_group`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| groupId | 目标分组ID | 是 | number |
|
||||
| urlIds | 网页ID列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-添加网页分组
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url_group/add`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| name | 分组名称 | 是 | string |
|
||||
| groupIdParent | 父级分组ID(0为顶级,默认0) | 否 | number |
|
||||
|
||||
**响应 data**:含 id(number)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-编辑网页分组
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url_group/edit`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| groupId | 分组ID | 是 | number |
|
||||
| name | 分组名称 | 是 | string |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-删除网页分组
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url_group/delete`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| groupId | 分组ID | 是 | number |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-网页分组列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/security/access_rule_url_group/list`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| groupIdParent | 父级id(0为顶级,默认0) | 否 | number |
|
||||
| isAll | 是否返回全部数据(默认false) | 否 | boolean |
|
||||
|
||||
**响应 data**:含 list 数组,每项含 id(number)、name(string)、tType(1系统/2自定义)、urlCount(number)、childrenCount(number)、groupIdParent(number)、children(array,isAll=true 时返回,结构同父节点)。
|
||||
@@ -0,0 +1,76 @@
|
||||
# 账号授权管理接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅账号授权新增、删除、查询、清除、授权用户列表接口的详细参数时加载。
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号授权新增
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/auth/add` **方法**:POST **权限点**:ERP-账号授权权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeIdList | 账号id列表 | 是 | string[] |
|
||||
| staffId | 授权用户id | 是 | string |
|
||||
| isAuthAddtion | 是否授权附加账号(默认授权)0否 1是 | 否 | number |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号授权删除
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/auth/delete` **方法**:POST **权限点**:ERP-账号授权权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeIdList | 账号id列表 | 是 | string[] |
|
||||
| staffId | 授权用户id | 是 | string |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号授权查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/auth/query` **方法**:POST **权限点**:ERP-账号查看权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| userId | 用户id | 是 | number |
|
||||
|
||||
**响应 data**:数组,每项含 storeId(number)、storeName(string)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-清除账号授权
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/auth/clean` **方法**:POST **权限点**:ERP-清除账号授权
|
||||
|
||||
> 清除指定账号上的所有授权关系(不指定用户)。
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeIdList | 账号id数组 | 是 | string[] |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号授权用户列表查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/user/list` **方法**:POST **权限点**:ERP-账号查看权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeIdList | 账号id列表 | 否 | string[] |
|
||||
| page | 页码 | 否 | string |
|
||||
| limit | 每页条数 | 否 | string |
|
||||
|
||||
**响应 data**:数组,每项含 storeId(number)、userId(number)、username(string)。另有 count。
|
||||
@@ -0,0 +1,134 @@
|
||||
# 账号增删改查与状态接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅账号创建、删除、编辑、列表查询、标签列表、缓存清除接口的详细参数时加载。
|
||||
|
||||
---
|
||||
|
||||
## ERP-创建账号
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/create` **方法**:POST **权限点**:ERP-创建与删除账号权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeData | 账号数据数组 | 是 | array |
|
||||
| storeData[].name | 账号名 | 是 | string |
|
||||
| storeData[].username | 平台登录账号 | 否 | string |
|
||||
| storeData[].password | 平台登录密码 | 否 | string |
|
||||
| storeData[].siteId | 站点id | 否 | string |
|
||||
| storeData[].tagIds | 标签列表(不超过5个) | 否 | number[] |
|
||||
| proxyId | 设备id | 否 | number |
|
||||
| enterpriseName | 企业简称 | 否 | string |
|
||||
| defyWarning | 是否无视风险添加:1是 0否(默认1) | 否 | string |
|
||||
|
||||
**响应 data**:数组,每项含 storeId(string)、name(string)、siteId(string)、msg(string)、riskMsgList(string[])
|
||||
|
||||
---
|
||||
|
||||
## ERP-删除账号
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/delete` **方法**:POST **权限点**:ERP-创建与删除账号权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeIds | 账号id数组 | 是 | number[] |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-编辑账号基础信息
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/update/base` **方法**:POST **权限点**:ERP-编辑账号基础信息
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| id | 账号id | 是 | number |
|
||||
| name | 账号名(≤64位,不传不更新) | 否 | string |
|
||||
| siteId | 平台id(不传不更新) | 否 | number |
|
||||
| tagIds | 标签id列表(不传不更新,传空清空,≤5个) | 否 | number[] |
|
||||
| defyWarning | 是否无视风险:1是 0否(默认1) | 否 | integer |
|
||||
| customerUrl | 自定义平台站点url | 否 | string |
|
||||
|
||||
**响应**:ret / msg / status
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号列表查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/list` **方法**:POST **权限点**:ERP-账号查看权限
|
||||
|
||||
> 私有化账号建议一次最多查询 20 条。
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeName | 账号名称 | 否 | string |
|
||||
| ip | 设备ip | 否 | string |
|
||||
| page | 页码 | 否 | number |
|
||||
| limit | 每页条数 | 否 | number |
|
||||
|
||||
**响应 data**:数组,每项含 id、name、ip、proxyId、siteId、siteName、platform、platformName、username、createtime、lastuserId、lastusetime、packageName (均 string)。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-用户的账号列表查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/list/by/user` **方法**:POST **权限点**:ERP-账号查看权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| userId | 用户id | 是 | number |
|
||||
| storeName | 账号名称 | 否 | string |
|
||||
| page | 页码 | 否 | number |
|
||||
| limit | 每页条数 | 否 | number |
|
||||
|
||||
**响应 data**:数组,每项含 storeId、storeName、proxyId、ipExpiryTime、platformName (均 string)。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-查询某用户有权限的账号列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/user/stores` **方法**:POST **权限点**:ERP-查询某用户有权限的账号列表
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| userId | 用户id | 是 | number |
|
||||
| storeName | 账号名称 | 否 | string |
|
||||
| isAccurate | 0模糊 1精确(默认精确) | 否 | number |
|
||||
| page | 页码 | 否 | number |
|
||||
| limit | 每页条数 | 否 | number |
|
||||
|
||||
**响应 data**:数组,每项含 id(number)、name、ipId(number)、ip、platform、platformCategory、userName、lastUserId、lastUserName、lastOpenUsername、lastUsetime、ipExpiryTime、tags(string[])、regionInfo。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-标签列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/tag/list` **方法**:POST **权限点**:ERP-标签列表
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
|
||||
**响应 data**:对象,含 list 数组,每项含 id(string)、name(string)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-清除账号缓存
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/store/deletecookie` **方法**:POST **权限点**:ERP-清除账号缓存
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| userId | 用户id | 是 | string |
|
||||
| removeStoreId | 账号id | 是 | number |
|
||||
| removeType | 1清除所有 2仅保留cookie 3仅保留二步验证状态 | 是 | number |
|
||||
|
||||
**响应**:ret / msg / status
|
||||
@@ -0,0 +1,119 @@
|
||||
# 账号标签管理接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅标签的添加、删除、重命名、绑定、解绑、替换、移除、清空、列表接口时加载。
|
||||
|
||||
所有标签接口**权限点**均为 **账号标签管理权限**。标签操作路径统一为 `/superbrowser/rest/v1/store-tag/{action}`,方法均为 POST。每个账号最多绑定 5 个标签。
|
||||
|
||||
**操作模式**:绑定/解绑为增量操作;替换为全量覆盖;清空移除所有标签。
|
||||
|
||||
---
|
||||
|
||||
## 添加账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/add`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 否 | string |
|
||||
| userId | 用户id | 否 | string |
|
||||
| tagName | 标签名称 | 否 | string |
|
||||
|
||||
**响应 data**:对象,含 tag_id(string)。
|
||||
|
||||
---
|
||||
|
||||
## 删除账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/delete`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 否 | string |
|
||||
| tagIds | 标签id列表 | 否 | string[] |
|
||||
| userId | 用户id | 否 | string |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## 重命名账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/rename`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 否 | string |
|
||||
| tid | 标签id | 否 | string |
|
||||
| userId | 用户id | 否 | string |
|
||||
| name | 新标签名称 | 否 | string |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## 绑定账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/bind`(增量追加)
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 否 | string |
|
||||
| storeIds | 账号id列表 | 否 | string[] |
|
||||
| tagIds | 标签id列表 | 否 | string[] |
|
||||
| userId | 用户id | 否 | string |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## 解绑账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/unbind`(增量移除)
|
||||
|
||||
请求参数同「绑定账号标签」。**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## 替换账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/replace`(全量覆盖)
|
||||
|
||||
请求参数同「绑定账号标签」。**响应**:ret / msg / status
|
||||
|
||||
---
|
||||
|
||||
## 移除账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/remove`
|
||||
|
||||
请求参数同「绑定账号标签」。**响应**:ret / msg / status
|
||||
|
||||
---
|
||||
|
||||
## 清空账号标签
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/clear`(移除账号所有标签)
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 否 | string |
|
||||
| storeIds | 账号id列表 | 否 | string[] |
|
||||
| userId | 用户id | 否 | string |
|
||||
|
||||
**响应**:ret / msg
|
||||
|
||||
---
|
||||
|
||||
## 账号的标签列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/store-tag/list`
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | string |
|
||||
| storeId | 店铺id | 是 | string |
|
||||
| userId | 用户id | 是 | string |
|
||||
| withSystem | 1带系统标签 0不带(默认0) | 否 | string |
|
||||
|
||||
**响应 data**:含 id(string)、name(string)、is_system(string)、store_count(string)。
|
||||
@@ -0,0 +1,108 @@
|
||||
# API 调用指南
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要了解认证方式、请求构造方法、代码示例或常见错误时加载。
|
||||
|
||||
## 概述
|
||||
|
||||
紫鸟开放平台使用"简单通用模式"进行 API 认证,通过 API Key 作为 Bearer Token 传递。
|
||||
|
||||
## 1. 获取 companyId(紫鸟企业ID)
|
||||
|
||||
在开放平台首页右侧可获取紫鸟企业ID(即 companyId),几乎所有接口都需要传此参数。
|
||||
|
||||
## 2. 获取 API Key
|
||||
|
||||
前提:在紫鸟开放平台创建"卖家自研应用",API 身份验证方式选择**简单通用模式**。在控制台中可以找到 API Key。
|
||||
|
||||
## 3. 配置 IP 白名单
|
||||
|
||||
在紫鸟开放平台的**应用管理**中,点击【设置】进入该应用的【应用概览】页面,点击【安全中心】下的【IP 白名单设置】,添加调用接口的服务器 IP。建议使用一台 IP 固定的服务器发起 API 请求。
|
||||
|
||||
## 4. 构造请求头
|
||||
|
||||
```
|
||||
Authorization: Bearer {你的API_Key}
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
`Bearer` 和 API Key 之间必须有一个空格。
|
||||
|
||||
## 5. 请求地址
|
||||
|
||||
Base URL:`https://sbappstoreapi.ziniao.com/openapi-router`
|
||||
|
||||
完整请求地址 = Base URL + 接口路径。例如:
|
||||
`https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/erp/store/list`
|
||||
|
||||
## 6. 响应结构
|
||||
|
||||
### 网关层(外层)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| request_id | string | 全局请求ID |
|
||||
| code | string | "0" 表示成功 |
|
||||
| msg | string | 返回码描述 |
|
||||
| sub_code | string | 业务返回码(成功时不返回) |
|
||||
| sub_msg | string | 业务返回码描述(成功时不返回) |
|
||||
| data | object | 业务响应参数 |
|
||||
|
||||
### 业务层(data 内部)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| ret | number | 0 成功,其他失败 |
|
||||
| status | string | "success" 或 "faile" |
|
||||
| data | array/object | 业务数据(部分接口有) |
|
||||
| msg | string | 错误信息 |
|
||||
| count | number | 列表查询时的总条数 |
|
||||
| 其他字段 | - | 部分接口的业务字段直接在此层级 |
|
||||
|
||||
> **关键提醒**:业务数据位置因接口而异,不要假设所有业务数据都在 `data` 字段中。各 reference 文件对每个接口标注了"响应 data"或"响应"——"响应 data"表示数据在业务层的 data 字段中;"响应"后直接列出的字段(如 id、password)表示它们与 ret/status 同级,直接位于业务层根级。
|
||||
|
||||
## 7. 代码示例
|
||||
|
||||
### Java(OkHttp)
|
||||
|
||||
```java
|
||||
OkHttpClient client = new OkHttpClient().newBuilder().build();
|
||||
MediaType mediaType = MediaType.parse("application/json");
|
||||
RequestBody body = RequestBody.create(mediaType, "{\"companyId\": 123456}");
|
||||
Request request = new Request.Builder()
|
||||
.url("https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/erp/store/list")
|
||||
.method("POST", body)
|
||||
.addHeader("Authorization", "Bearer 你的API_Key")
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.build();
|
||||
Response response = client.newCall(request).execute();
|
||||
```
|
||||
|
||||
### Python(requests)
|
||||
|
||||
```python
|
||||
import requests, json
|
||||
url = "https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/erp/store/list"
|
||||
headers = {"Authorization": "Bearer 你的API_Key", "Content-Type": "application/json"}
|
||||
response = requests.post(url, headers=headers, data=json.dumps({"companyId": 123456}))
|
||||
print(response.json())
|
||||
```
|
||||
|
||||
### JavaScript(Fetch)
|
||||
|
||||
```javascript
|
||||
fetch("https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/erp/store/list", {
|
||||
method: "POST",
|
||||
headers: { "Authorization": "Bearer 你的API_Key", "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ companyId: 123456 }),
|
||||
})
|
||||
.then(r => r.json()).then(console.log).catch(console.error);
|
||||
```
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
**401 错误**:检查 API Key 是否正确、Authorization 格式是否含空格、接口是否支持通用模式、应用是否已申请对应权限点。
|
||||
|
||||
**code 非 "0"**:请求未到达业务层,通常是认证或权限问题。
|
||||
|
||||
**ret 非 0**:业务操作失败,读取 msg 或 sub_msg 获取错误详情。
|
||||
@@ -0,0 +1,172 @@
|
||||
# 部门员工管理接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅部门增删改查移动、员工新增/修改/查询/启禁用、部门变更接口时加载。
|
||||
|
||||
所有接口方法均为 POST。
|
||||
|
||||
---
|
||||
|
||||
## ERP-部门查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/department/list` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
|
||||
**响应 data**:数组,每项含 id(number)、name(string)、hierarchy(number 层级)、parentId(number)、ordering(number)。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-部门新增
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/department/add` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| name | 部门名称 | 是 | string |
|
||||
| hierarchy | 部门级别 | 是 | string |
|
||||
| parentId | 父级部门id | 是 | string |
|
||||
|
||||
**响应 data**:含 id(string 部门id)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-部门修改
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/department/update` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| id | 部门id | 是 | number |
|
||||
| name | 部门新名称 | 否 | string |
|
||||
| parentId | 父部门id | 否 | number |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-部门删除
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/department/delete` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | string |
|
||||
| id | 部门id | 是 | string |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-部门移动
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/department/order` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| parentId | 父部门id | 是 | string |
|
||||
| order | 更新后的部门顺序(部门id数组) | 是 | string[] |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-员工查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/staff/list` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| page | 页码 | 是 | number |
|
||||
| limit | 每页条数 | 是 | number |
|
||||
| username | 用户账号 | 否 | string |
|
||||
| name | 真实姓名 | 否 | string |
|
||||
| departmentIds | 部门id数组 | 否 | number[] |
|
||||
| level | 角色类型(0boss/1经理/2员工/3超管) | 否 | string |
|
||||
| delflag | 0正常 1禁用 2删除(不传全部) | 否 | string |
|
||||
| isAccurate | 0模糊 1精确(默认0) | 否 | number |
|
||||
|
||||
**响应 data**:含 username、userId、name、level、mobile、delflag、authPhone (均 string)。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-员工新增v2
|
||||
|
||||
- **路径**:`/superbrowser/rest/v2/erp/staff/add` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | string |
|
||||
| nickname | 用户账号 | 是 | string |
|
||||
| name | 真实姓名 | 是 | string |
|
||||
| password | 密码 | 是 | string |
|
||||
| mobile | 手机号 | 否 | string |
|
||||
| departmentIds | 部门id数组 | 是 | string[] |
|
||||
| roleId | 角色id | 是 | number |
|
||||
| authDevide | 设备授权方式(1/2/3/4) | 是 | number |
|
||||
| isUpdatePersonInfo | 1允许修改个人信息 0不允许 | 是 | number |
|
||||
| isLimitLogin | 0否 1是 | 是 | number |
|
||||
| isTwoStepVerify | 0否 1是 | 是 | number |
|
||||
| authClient | 登录终端控制对象 | 是 | object |
|
||||
| authClient.allowLoginWindows | 1允许 0不允许 | 是 | string |
|
||||
| authClient.allowLoginMac | 1允许 0不允许 | 是 | string |
|
||||
| authClient.allowLoginAndroid | 1允许 0不允许 | 是 | string |
|
||||
| authClient.allowLoginWeb | 1允许 0不允许 | 是 | string |
|
||||
| authClient.allowLoginIos | 1允许 0不允许 | 是 | string |
|
||||
| authClient.allowLoginMiniprog | 1允许 0不允许 | 是 | string |
|
||||
| authClient.allowLoginLinux | 1允许 0不允许 | 否 | string |
|
||||
| loginLimitStartTime | HH:mm(分钟仅00或30) | 否 | string |
|
||||
| loginLimitEndTime | HH:mm | 否 | string |
|
||||
| loginLimitStartDate | yyyy-MM-dd | 否 | string |
|
||||
| loginLimitEndDate | yyyy-MM-dd | 否 | string |
|
||||
| enablePhoneLogin | 0否 1是 | 否 | number |
|
||||
|
||||
**响应**:含 id(用户id)、password(string)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-员工修改v2
|
||||
|
||||
- **路径**:`/superbrowser/rest/v2/erp/staff/modify` **权限点**:ERP-部门与员工接口
|
||||
|
||||
参数结构与「员工新增v2」基本一致,差异:
|
||||
- 新增 `id`(string, 必须) — 用户id
|
||||
- 多数字段变为非必须(不传不更新)
|
||||
- 新增 `isResetAuthPhone`(number) — 1重置登录手机号 0不重置
|
||||
- `areaCode` 和 `authPhone` 已废弃
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-员工启用禁用接口
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/staff/status` **权限点**:ERP-部门与员工接口
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| staffIds | 用户id数组 | 是 | string[] |
|
||||
| status | 0启用 1禁用 2删除 | 是 | string |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-用户的部门变更
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/staff/department` **权限点**:ERP-用户的部门变更
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | string |
|
||||
| staffIds | 用户id列表 | 是 | number[] |
|
||||
| departmentIds | 部门id列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / msg / status
|
||||
@@ -0,0 +1,180 @@
|
||||
# 设备管理接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅设备套餐、购买、续费、绑定、解绑、查询、自有设备管理接口时加载。
|
||||
|
||||
所有接口方法均为 POST,Content-Type 为 application/json。
|
||||
|
||||
---
|
||||
|
||||
## ERP-套餐列表查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/package/new` **权限点**:ERP-设备套餐列表查询权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
|
||||
**响应 data**:数组,每项含 regionCloudId(套餐ID)、ctype(0站群/1云平台/2自有/3本地)、networkType、areaId、areaName、city、priceOriginal、deviceConfigName、deviceType(1标准/2节能/3高配)、deviceOs、deviceSupportRemoteLogin、deviceCpuSize、deviceMemorySize、deviceBandWidth、deviceDiskSize、platformName (均 string)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-购买设备
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/purchase` **权限点**:ERP-设备购买与续费权限
|
||||
|
||||
> 需要紫鸟账号上有余额。
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | string |
|
||||
| purchase | 购买信息对象 | 是 | object |
|
||||
| purchase.packageId | 套餐id | 是 | number |
|
||||
| purchase.periodId | 时长id(1:1月/2:3月/3:6月/4:12月) | 是 | number |
|
||||
| purchase.num | 数量(单次最多5个) | 是 | number |
|
||||
| storeIds | 要绑定的账号id(未绑设备的) | 否 | number[] |
|
||||
|
||||
**响应 data**:含 order_id(string)、create_time(string)、proxyIdList(number[] 设备ID列表)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-续费设备
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/renew` **权限点**:ERP-设备购买与续费权限
|
||||
|
||||
> 仅支持紫鸟 IP 续费;自有设备请通过开启自动续费。
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | string |
|
||||
| renew | 续费信息对象 | 是 | object |
|
||||
| renew.periodId | 时长id | 是 | string |
|
||||
| renew.ipIds | proxy_id 数组 | 是 | integer[] |
|
||||
|
||||
**响应 data**:含 id(设备id)、ip(设备ip)、expire(过期时间)。另有 orderId、createTime。
|
||||
|
||||
---
|
||||
|
||||
## ERP-开关自动续费
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/ip/renewal` **权限点**:ERP-开关自动续费
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| ids | proxy_id 数组 | 是 | number[] |
|
||||
| isOpen | 0关闭 1开启 | 是 | number |
|
||||
|
||||
**响应**:ret / msg / status
|
||||
|
||||
---
|
||||
|
||||
## ERP-设备查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/ip/page` **权限点**:ERP-设备查询
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| ipAddr | 设备地址 | 否 | string |
|
||||
| page | 页码 | 否 | string |
|
||||
| limit | 每页条数 | 否 | string |
|
||||
|
||||
**响应 data**:数组,每项含 id(number)、ip、proxyName、proxyType(0站群/1云平台/2自有/3vps/4虚拟机/5本地)、port、ptype(0http/1https/2socks5)、expiry、isRenewal(0关/1开)、createtime、delflag(0正常/2回收站) (均 string)。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-绑定设备
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/ip/bind` **权限点**:ERP-设备绑定权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| proxyId | 设备id | 是 | number |
|
||||
| storeIdList | 账号id列表 | 是 | number[] |
|
||||
| defyWarning | 是否无视风险:1是 0否(默认1) | 否 | number |
|
||||
|
||||
**响应**:ret / status / msg / riskMsgList(string[])
|
||||
|
||||
---
|
||||
|
||||
## ERP-解绑设备
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/ip/unbind` **权限点**:ERP-解绑设备
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| storeId | 账号ID | 是 | number |
|
||||
|
||||
**响应**:ret / msg / status
|
||||
|
||||
---
|
||||
|
||||
## ERP-查询已购设备价格接口
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/proxy/purchased_package_info` **权限点**:ERP-查询已购设备价格接口
|
||||
|
||||
> 仅支持查询云平台设备。
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司ID | 是 | number |
|
||||
| proxyId | 设备ID | 是 | number |
|
||||
|
||||
**响应 data**:含 proxyId(number)、regionCloudId(number 套餐ID)、price(number 续费价格)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-设备历史绑定记录
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/ip/historybind` **权限点**:ERP-设备查询
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| proxyId | 设备id | 是 | number |
|
||||
|
||||
**响应 data**:对象含 list 数组,每项含 storeId、storeName、proxyName、unBindTime、unbindUserId、unbindUsername、cloudType (均 string)。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-添加自有设备(新)
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/ip/self/add/new` **权限点**:ERP-添加自有设备(新)
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司ID | 是 | string |
|
||||
| defyWarning | 1无视风险 0风险提示 | 是 | number |
|
||||
| base | 基础信息对象 | 是 | object |
|
||||
| base.deviceName | 设备名称 | 是 | string |
|
||||
| base.isDynamic | 是否动态网络(false静态/true动态) | 是 | boolean |
|
||||
| proxy | 代理信息对象 | 是 | object |
|
||||
| proxy.type | 0http/1https/2sock5/3ssh/4ssl | 是 | string |
|
||||
| proxy.addr | 代理地址 | 是 | string |
|
||||
| proxy.port | 代理端口 | 是 | string |
|
||||
| proxy.username | 用户名 | 否 | string |
|
||||
| proxy.passwd | 密码 | 否 | string |
|
||||
| proxy.isSetAccount | 是否设密码 0否 1是 | 是 | number |
|
||||
|
||||
**响应 data**:含 id(number 设备ID)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-修改自有设备信息(新)
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/ip/self/edit/new` **权限点**:ERP-修改自有设备信息(新)
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司ID | 是 | string |
|
||||
| defyWarning | 1无视风险 0风险提示 | 是 | number |
|
||||
| proxyId | 设备ID | 是 | string |
|
||||
| base | 基础信息对象 | 是 | object |
|
||||
| base.deviceName | 设备名称(不传不更新) | 否 | string |
|
||||
| base.isDynamic | 是否动态网络 | 是 | boolean |
|
||||
| proxy | 代理信息对象(字段同添加接口) | 是 | object |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
@@ -0,0 +1,107 @@
|
||||
# 角色和权限管理接口
|
||||
|
||||
> 本文件是 ziniao-erp-api-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅角色增改查、权限列表、用户角色调整接口的详细参数时加载。
|
||||
|
||||
所有接口方法均为 POST。
|
||||
|
||||
---
|
||||
|
||||
## ERP-添加角色
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/per/role/add` **权限点**:ERP-角色添加、修改权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| roleName | 角色名称 | 是 | string |
|
||||
| identityId | 角色身份id(1管理员/2普通成员/3超级管理员) | 是 | number |
|
||||
| desc | 描述 | 否 | string |
|
||||
| permissionIds | 权限id列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-修改角色
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/per/role/edit` **权限点**:ERP-角色添加、修改权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| roleId | 角色id | 是 | string |
|
||||
| roleName | 角色名称 | 是 | string |
|
||||
| identityId | 角色身份id | 是 | number |
|
||||
| desc | 描述 | 否 | string |
|
||||
| permissionIds | 权限id列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-角色列表查询
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/per/role/list` **权限点**:ERP-角色列表查询
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| page | 页码 | 否 | number |
|
||||
| limit | 每页条数 | 否 | number |
|
||||
| filterKeyword | 搜索关键字 | 否 | string |
|
||||
|
||||
**响应 data**:含角色信息 id、name、desc、identityType(1管理/2员工/3超管)、allPermission、users[{userId,name,username}]、permissions[{id,name,groupId,groupName,dependOn[],requiredBy[]}]。另有 count。
|
||||
|
||||
---
|
||||
|
||||
## ERP-角色详情
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/per/role/detail` **权限点**:ERP-角色详情
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| roleId | 角色id | 是 | number |
|
||||
|
||||
**响应 data**:含 id、companyId、name、desc、permissionList[{id,name,groupId,groupName}] (均 string)。
|
||||
|
||||
---
|
||||
|
||||
## ERP-权限列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/per/permission/list` **权限点**:ERP-权限列表
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| identityId | 角色身份id(1管理员/2普通成员/3超级管理员) | 是 | number |
|
||||
|
||||
**响应 data**:数组,每项含 id(number)、name(string)、desc(string)、order(number)、groupId(number)、groupName(string)、groupOrder(number)、dependOn(number[])、requiredBy(number[])。
|
||||
|
||||
---
|
||||
|
||||
## ERP-调整角色
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/staff/change/role` **权限点**:ERP-角色添加、修改权限
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| roleId | 角色id | 是 | number |
|
||||
| staffIds | 用户id列表 | 是 | number[] |
|
||||
|
||||
**响应**:ret / status / msg
|
||||
|
||||
---
|
||||
|
||||
## ERP-用户角色列表
|
||||
|
||||
- **路径**:`/superbrowser/rest/v1/erp/staff/role/list` **权限点**:ERP-角色列表查询
|
||||
|
||||
| 参数 | 说明 | 必须 | 类型 |
|
||||
|------|------|------|------|
|
||||
| companyId | 公司id | 是 | number |
|
||||
| staffId | 用户id | 是 | number |
|
||||
|
||||
**响应 data**:对象含 roleList 数组,每项含 id(string)、name(string)。
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
name: ziniao-sso-doc
|
||||
description: >
|
||||
Use when 需要理解紫鸟单点登录 OpenAPI 的定位、能力、集成流程或接口清单,
|
||||
用于 ERP 系统 SSO 对接规划、集成设计或接口评估。
|
||||
不要用于直接查阅具体接口参数——查阅时读取 reference/ 下对应文件。
|
||||
---
|
||||
|
||||
# ziniao-sso-doc(紫鸟单点登录 API 文档)
|
||||
|
||||
## 何时使用 / 不使用
|
||||
|
||||
| 场景 | 做法 |
|
||||
|------|------|
|
||||
| 需要了解紫鸟单点登录能做什么 | 读本 Skill |
|
||||
| 需要做 ERP SSO 对接的集成设计 | 读本 Skill |
|
||||
| 需要查阅某个接口的请求/响应参数 | 读本 Skill §按需深入 获取路径,再读 reference/ 文件 |
|
||||
| 需要 Schema URL 协议详细参数 | 读 `reference/client-schema.md` |
|
||||
|
||||
---
|
||||
|
||||
## Level 0:定位与边界
|
||||
|
||||
### 是什么
|
||||
|
||||
紫鸟单点登录 OpenAPI,面向 ERP/第三方系统,通过服务端 API 获取认证令牌,结合 `superbrowser://` Schema URL 协议拉起紫鸟浏览器并自动登录指定账号,实现免密单点登录。支持两种鉴权模式:简单鉴权(直接用 API Key,无需调用 get_app_token)和复杂鉴权(需额外获取 appAuthToken)。
|
||||
|
||||
### 能力概览
|
||||
|
||||
| 能力域 | 说明 |
|
||||
|--------|------|
|
||||
| 应用认证 | 获取应用级 Token(appAuthToken)——仅复杂鉴权模式需要,简单鉴权模式直接用 API Key |
|
||||
| 企业信息 | 获取自建应用关联的公司 ID(companyId) |
|
||||
|
||||
| 员工查询 | 分页查询公司员工列表,支持按角色/状态/部门筛选 |
|
||||
| 账号查询 | 查询公司全部账号或指定用户有权限的账号列表 |
|
||||
| 用户登录 | 获取指定用户的登录 Token(openapiToken),用于 SSO 鉴权 |
|
||||
| 客户端控制 | 通过 Schema URL 打开/关闭账号、退出浏览器(仅 Windows) |
|
||||
|
||||
### 不做什么(边界)
|
||||
|
||||
- 不包含账号 CRUD(创建/删除/编辑)——见 ziniao-erp-api-doc
|
||||
- 不包含标签、授权、设备管理接口——见 ziniao-erp-api-doc
|
||||
- 不提供浏览器自动化(RPA/脚本注入)能力
|
||||
- Schema URL 仅支持 Windows 客户端
|
||||
|
||||
---
|
||||
|
||||
## Level 1:架构与流程
|
||||
|
||||
### 核心概念
|
||||
|
||||
**两种鉴权模式**:**简单鉴权模式**——直接用 API Key 放入 `Authorization: Bearer {API_Key}` 请求头,无需调用 `get_app_token`;**复杂鉴权模式**——需先调用 `get_app_token` 获取 appAuthToken 再进行后续调用。大多数场景使用简单鉴权即可。
|
||||
|
||||
**用户登录 Token**:openapiToken,通过 `user-login` 接口获取,有过期时长,用于 Schema URL 拉起浏览器时的用户身份鉴权。
|
||||
|
||||
**Schema URL 协议**:以 `superbrowser://` 为前缀的自定义 URL 协议,支持三个 Action:OpenStrore(打开账号)、CloseStrore(关闭账号)、exit(退出浏览器)。可通过浏览器链接或命令行 `SuperBrowser.exe "url"` 调用。
|
||||
|
||||
**双层响应结构**:网关层(request_id / code / msg)+ 业务层(ret / status / data / msg)。网关 code="0" 表示请求到达业务层;业务 ret=0 表示操作成功。
|
||||
|
||||
### SSO 集成全流程
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[创建应用<br/>获取 API Key] --> B[获取 companyId]
|
||||
B --> C[查询员工列表]
|
||||
C --> D[查询用户有权限的账号]
|
||||
D --> E[获取用户 openapiToken]
|
||||
E --> F[Schema URL<br/>打开账号]
|
||||
```
|
||||
|
||||
1. 在开放平台创建自建应用,获取 API Key(简单鉴权模式)
|
||||
2. 调用 `builtin/company` 获取 companyId(固定值,获取一次即可)
|
||||
3. 调用 `staff/list` 查询员工,获取 userId
|
||||
4. 调用 `user/stores` 查询该用户有权限的账号,获取 storeId
|
||||
5. 调用 `user-login` 获取用户的 openapiToken(需提前申请接口权限)
|
||||
6. 拼接 Schema URL:`superbrowser://OpenStrore?storeId={storeId}&openapiToken={token}&userId={userId}`
|
||||
|
||||
> 如使用复杂鉴权模式,需在步骤 1 之后额外调用 `get_app_token` 获取 appAuthToken。
|
||||
|
||||
### 接口摘要表
|
||||
|
||||
| 分组 | 接口名称 | 路径 | 方法 |
|
||||
|------|---------|------|------|
|
||||
| 认证 | 获取应用 token(仅复杂鉴权模式) | /auth/get_app_token | POST |
|
||||
| 企业 | 获取公司信息 | /app/builtin/company | GET |
|
||||
| 员工 | ERP-员工查询 | /superbrowser/rest/v1/erp/staff/list | POST |
|
||||
| 账号 | ERP-账号列表查询 | /superbrowser/rest/v1/erp/store/list | POST |
|
||||
| 账号 | ERP-查询用户有权限的账号 | /superbrowser/rest/v1/erp/user/stores | POST |
|
||||
| 登录 | 获取用户登录 token | /superbrowser/rest/v1/token/user-login | POST |
|
||||
|
||||
### 公共响应结构
|
||||
|
||||
> **所有接口返回双层嵌套结构,解析时必须先剥离网关层再读业务层。**
|
||||
> **异常处理:请求异常时,必须将 request_id、msg 和 sub_msg 都打印出来,便于追踪定位。**
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "全局请求追踪ID",
|
||||
"code": "0", // ← 网关层:code="0" 表示请求到达业务层
|
||||
"msg": "SUCCESS",
|
||||
"sub_code": "", // ← 业务返回码,成功时不返回
|
||||
"sub_msg": "", // ← 业务返回码描述,成功时不返回
|
||||
"data": { ... } // ← 业务层:各接口文档中的响应参数均在此内部
|
||||
}
|
||||
```
|
||||
|
||||
### 环境约定
|
||||
|
||||
- Base URL:`https://sbappstoreapi.ziniao.com/openapi-router`
|
||||
- 请求编码:UTF-8,Content-Type:application/json
|
||||
- 前提:在开放平台创建"卖家自研应用",获取 API Key,申请所需权限点,配置 IP 白名单
|
||||
- `user-login` 接口需联系紫鸟客服申请权限(需 Boss 验证)
|
||||
|
||||
---
|
||||
|
||||
## 按需深入(Level 2 路由表)
|
||||
|
||||
> **规则:以下内容不要主动加载。**
|
||||
> 仅在你明确需要了解某个细节时,按路由表读取对应的 reference/ 文件。
|
||||
|
||||
| 你需要了解 | 读取文件 |
|
||||
|-----------|---------|
|
||||
| 认证接口详细参数(get_app_token + user-login) | `reference/account-auth.md` |
|
||||
| 企业信息、员工查询、账号查询接口的详细参数 | `reference/account-crud.md` |
|
||||
| Schema URL 协议详细参数(OpenStrore / CloseStrore / exit) | `reference/client-schema.md` |
|
||||
| API 使用指南(基地址、公共 Headers、公共响应、curl 示例) | `reference/api-guide.md` |
|
||||
@@ -0,0 +1,129 @@
|
||||
# 认证鉴权接口
|
||||
|
||||
> 本文件是 ziniao-sso-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅 get_app_token 或 user-login 接口的详细参数时加载。
|
||||
>
|
||||
> **响应说明**:以下各接口的"响应参数"均为业务层字段,实际位于网关响应的 `data` 内部。完整响应结构见 `api-guide.md` 公共响应参数。
|
||||
|
||||
## 获取应用 Token
|
||||
|
||||
> 仅在"复杂鉴权模式"下需要调用此接口。如使用"简单鉴权模式"(直接用 API Key),无需调用此接口,跳过本节即可。
|
||||
|
||||
### 基本信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 路径 | `/auth/get_app_token` |
|
||||
| 方法 | POST |
|
||||
| Content-Type | application/json |
|
||||
|
||||
### 请求 Headers
|
||||
|
||||
| 参数名称 | 参数值 | 是否必须 |
|
||||
|----------|--------|----------|
|
||||
| Content-Type | application/json | true |
|
||||
| Authorization | Bearer {API_Key} | true |
|
||||
|
||||
### 请求 Body
|
||||
|
||||
无需请求体参数。
|
||||
|
||||
### 响应参数
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| appAuthToken | 应用授权令牌 | string |
|
||||
| expiresIn | 令牌有效期(秒),-1 表示永久有效 | string |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl --location 'https://sbappstoreapi.ziniao.com/openapi-router/auth/get_app_token' \
|
||||
--header 'Authorization: Bearer {API_Key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw ''
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"appAuthToken": "xxxxxxxxxxxxxxxx",
|
||||
"expiresIn": "-1"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 获取指定用户登录 Token
|
||||
|
||||
> 需联系紫鸟在线客服申请接口权限(走特殊申请 OA 单,Boss 验证后生效)。
|
||||
> 目前仅支持 Windows 版客户端。
|
||||
|
||||
### 基本信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 路径 | `/superbrowser/rest/v1/token/user-login` |
|
||||
| 方法 | POST |
|
||||
| Content-Type | application/json |
|
||||
|
||||
### 请求 Headers
|
||||
|
||||
| 参数名称 | 参数值 | 是否必须 |
|
||||
|----------|--------|----------|
|
||||
| Content-Type | application/json | true |
|
||||
| Authorization | Bearer {API_Key} | true |
|
||||
|
||||
### 请求 Body
|
||||
|
||||
| 参数名称 | 参数说明 | 是否必须 | 数据类型 |
|
||||
|----------|----------|----------|----------|
|
||||
| companyId | 公司 ID | false | number |
|
||||
| userId | 用户 ID | false | number |
|
||||
|
||||
### 响应参数
|
||||
|
||||
> 注意:此接口业务层使用 `code`/`msg` 结构,与其他接口的 `ret`/`status`/`msg`/`count` 结构不同,判断成功应检查 `code == 0` 而非 `ret == 0`。
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| code | 0 表示成功 | number |
|
||||
| msg | 状态描述(成功时为 "SUCCESS") | string |
|
||||
| data | 响应数据对象 | object |
|
||||
| data.token | 用户登录 token(即 openapiToken) | string |
|
||||
| data.expireTime | 过期时长(秒) | number |
|
||||
| data.timeUnit | 时间单位(SECONDS) | string |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl --location 'https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/token/user-login' \
|
||||
--header 'Authorization: Bearer {API_Key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"companyId":"15393571083459","userId":"15393571087094"}'
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "SUCCESS",
|
||||
"data": {
|
||||
"token": "79db5b0c8d16d5d7eecded06101a7e39",
|
||||
"expireTime": 1800,
|
||||
"timeUnit": "SECONDS"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用说明
|
||||
|
||||
获取到的 token 即为 Schema URL 中的 `openapiToken` 参数,拼接格式为:
|
||||
|
||||
```
|
||||
superbrowser://OpenStrore?storeId={账号ID}&openapiToken={token}&userId={用户ID}
|
||||
```
|
||||
|
||||
如果本地已登录紫鸟客户端,且 userId 与本地登录用户一致,可不传 openapiToken(客户端版本 v5.290 或 v6.25 以上)。
|
||||
@@ -0,0 +1,256 @@
|
||||
# 企业信息与员工/账号查询接口
|
||||
|
||||
> 本文件是 ziniao-sso-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅企业信息、员工查询或账号查询接口的详细参数时加载。
|
||||
|
||||
> **响应说明**:以下各接口的"响应参数"均为业务层字段,实际位于网关响应的 `data` 内部。完整响应结构见 `api-guide.md` 公共响应参数。
|
||||
|
||||
## 获取当前自建应用的公司信息
|
||||
|
||||
### 基本信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 路径 | `/app/builtin/company` |
|
||||
| 方法 | GET |
|
||||
| Content-Type | application/json |
|
||||
|
||||
### 请求 Headers
|
||||
|
||||
| 参数名称 | 参数值 | 是否必须 |
|
||||
|----------|--------|----------|
|
||||
| Content-Type | application/json | true |
|
||||
| Authorization | Bearer {API_Key} | true |
|
||||
|
||||
### 请求 Body
|
||||
|
||||
无。
|
||||
|
||||
### 响应参数
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| companyId | 企业 ID(固定值,获取一次后复用) | integer |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl --location 'https://sbappstoreapi.ziniao.com/openapi-router/app/builtin/company' \
|
||||
--header 'Authorization: Bearer {API_Key}'
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"companyId": 15393571083459
|
||||
}
|
||||
```
|
||||
|
||||
> companyId 为固定值,也可在开放平台首页右侧直接获取。
|
||||
|
||||
---
|
||||
|
||||
## ERP-员工查询
|
||||
|
||||
### 基本信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 路径 | `/superbrowser/rest/v1/erp/staff/list` |
|
||||
| 方法 | POST |
|
||||
| Content-Type | application/json |
|
||||
|
||||
### 请求 Headers
|
||||
|
||||
| 参数名称 | 参数值 | 是否必须 |
|
||||
|----------|--------|----------|
|
||||
| Content-Type | application/json | true |
|
||||
| Authorization | Bearer {API_Key} | true |
|
||||
|
||||
### 请求 Body
|
||||
|
||||
| 参数名称 | 参数说明 | 是否必须 | 数据类型 |
|
||||
|----------|----------|----------|----------|
|
||||
| companyId | 公司 ID | true | number |
|
||||
| page | 页码 | true | number |
|
||||
| limit | 每页条数 | true | number |
|
||||
| username | 用户账号 | false | string |
|
||||
| name | 用户真实姓名 | false | string |
|
||||
| departmentIds | 部门 ID 数组 | false | array\<number\> |
|
||||
| level | 角色类型:0=Boss,1=经理,2=员工,3=超级管理员 | false | string |
|
||||
| delflag | 删除标志:0=正常,1=禁用,2=删除(不传返回全部) | false | string |
|
||||
| isAccurate | 查询模式:0=模糊查询,1=精确查询(默认 0) | false | number |
|
||||
|
||||
### 响应参数
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| ret | 状态码,0=成功 | number |
|
||||
| status | success / faile | string |
|
||||
| data | 员工列表 | array |
|
||||
| data[].username | 用户名 | string |
|
||||
| data[].userId | 用户 ID | number |
|
||||
| data[].name | 联系人姓名 | string |
|
||||
| data[].level | 角色类型:0=Boss,1=经理,2=员工,3=超级管理员 | string |
|
||||
| data[].mobile | 手机号 | string |
|
||||
| data[].delflag | 状态:0=正常,1=禁用,2=删除 | string |
|
||||
| data[].authPhone | 登录手机号 | string |
|
||||
| msg | 错误信息 | string |
|
||||
| count | 总条数 | string |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl --location 'https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/erp/staff/list' \
|
||||
--header 'Authorization: Bearer {API_Key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"companyId": "15393571083459",
|
||||
"page": "1",
|
||||
"limit": "10",
|
||||
"level": "",
|
||||
"isAccurate": "",
|
||||
"departmentIds": [],
|
||||
"delflag": "",
|
||||
"name": "",
|
||||
"username": ""
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ERP-账号列表查询
|
||||
|
||||
### 基本信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 路径 | `/superbrowser/rest/v1/erp/store/list` |
|
||||
| 方法 | POST |
|
||||
| Content-Type | application/json |
|
||||
|
||||
### 请求 Headers
|
||||
|
||||
| 参数名称 | 参数值 | 是否必须 |
|
||||
|----------|--------|----------|
|
||||
| Content-Type | application/json | true |
|
||||
| Authorization | Bearer {API_Key} | true |
|
||||
|
||||
### 请求 Body
|
||||
|
||||
| 参数名称 | 参数说明 | 是否必须 | 数据类型 |
|
||||
|----------|----------|----------|----------|
|
||||
| companyId | 公司 ID | true | number |
|
||||
| storeName | 账号名称 | false | string |
|
||||
| ip | 设备 IP | false | string |
|
||||
| page | 页码 | false | number |
|
||||
| limit | 每页条数(私有化账号建议传 20) | false | number |
|
||||
|
||||
### 响应参数
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| ret | 状态码,0=成功 | number |
|
||||
| status | success / faile | string |
|
||||
| data | 账号列表 | array |
|
||||
| data[].id | 账号 ID(即 storeId) | number |
|
||||
| data[].ip | 设备 IP | string |
|
||||
| data[].lastuserId | 最后登录用户 ID | number |
|
||||
| data[].lastusetime | 最后登录时间 | string |
|
||||
| data[].name | 账号名 | string |
|
||||
| data[].platform | 账号所属平台(如"美国亚马逊") | string |
|
||||
| data[].proxyId | 设备 ID | string |
|
||||
| data[].siteId | 平台 ID | string |
|
||||
| data[].username | 平台登录账号 | string |
|
||||
| data[].createtime | 账号创建时间 | string |
|
||||
| data[].siteName | 站点名称 | string |
|
||||
| data[].packageName | 套餐名称 | string |
|
||||
| data[].platformName | 平台名称 | string |
|
||||
| msg | 错误信息 | string |
|
||||
| count | 总条数 | string |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl --location 'https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/erp/store/list' \
|
||||
--header 'Authorization: Bearer {API_Key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"companyId": "15393571083459",
|
||||
"storeName": "",
|
||||
"ip": "",
|
||||
"page": "1",
|
||||
"limit": "10"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ERP-查询某用户有权限的账号列表
|
||||
|
||||
### 基本信息
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|------|
|
||||
| 路径 | `/superbrowser/rest/v1/erp/user/stores` |
|
||||
| 方法 | POST |
|
||||
| Content-Type | application/json |
|
||||
|
||||
### 请求 Headers
|
||||
|
||||
| 参数名称 | 参数值 | 是否必须 |
|
||||
|----------|--------|----------|
|
||||
| Content-Type | application/json | true |
|
||||
| Authorization | Bearer {API_Key} | true |
|
||||
|
||||
### 请求 Body
|
||||
|
||||
| 参数名称 | 参数说明 | 是否必须 | 数据类型 |
|
||||
|----------|----------|----------|----------|
|
||||
| companyId | 公司 ID | true | number |
|
||||
| userId | 用户 ID | true | number |
|
||||
| storeName | 账号名称 | false | string |
|
||||
| isAccurate | 查询模式:0=模糊,1=精确(默认精确) | false | number |
|
||||
| page | 页码 | false | number |
|
||||
| limit | 每页条数 | false | number |
|
||||
|
||||
### 响应参数
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| ret | 状态码,0=成功 | number |
|
||||
| status | success / faile | string |
|
||||
| data | 账号列表 | array |
|
||||
| data[].id | 账号 ID(即 storeId) | number |
|
||||
| data[].ipId | 设备 ID | number |
|
||||
| data[].ip | 设备 IP | string |
|
||||
| data[].platform | 账号所属平台 | string |
|
||||
| data[].name | 账号名 | string |
|
||||
| data[].lastUserId | 最后登录用户 ID | string |
|
||||
| data[].lastUserName | 最后登录用户姓名 | string |
|
||||
| data[].lastOpenUsername | 最后登录用户名称 | string |
|
||||
| data[].lastUsetime | 最后登录时间 | string |
|
||||
| data[].ipExpiryTime | 设备到期时间 | string |
|
||||
| data[].tags | 账号标签列表 | array\<string\> |
|
||||
| data[].regionInfo | 套餐信息 | string |
|
||||
| data[].platformCategory | 平台类目 | string |
|
||||
| data[].userName | 登录凭证 | string |
|
||||
| msg | 错误信息 | string |
|
||||
| count | 总条数 | number |
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl --location 'https://sbappstoreapi.ziniao.com/openapi-router/superbrowser/rest/v1/erp/user/stores' \
|
||||
--header 'Authorization: Bearer {API_Key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"companyId": "15393571083459",
|
||||
"userId": "15393571087094",
|
||||
"storeName": "",
|
||||
"isAccurate": "",
|
||||
"page": "1",
|
||||
"limit": "10"
|
||||
}'
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
# API 使用指南
|
||||
|
||||
> 本文件是 ziniao-sso-doc 的 Level 2 参考文档。
|
||||
> 仅在需要了解 API 基地址、公共 Headers、公共响应结构或 curl 调用示例时加载。
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
https://sbappstoreapi.ziniao.com/openapi-router
|
||||
```
|
||||
|
||||
所有服务端接口路径均以此为前缀拼接。
|
||||
|
||||
## 公共请求 Headers
|
||||
|
||||
| Header | 值 | 说明 |
|
||||
|--------|------|------|
|
||||
| Content-Type | application/json | 所有接口统一使用 JSON 格式 |
|
||||
| Authorization | Bearer {API_Key} | API Key 从开放平台自建应用获取 |
|
||||
|
||||
## 认证方式
|
||||
|
||||
支持两种鉴权模式:
|
||||
|
||||
### 简单鉴权模式(推荐)
|
||||
|
||||
1. 在紫鸟开放平台创建"卖家自研应用"
|
||||
2. API 身份验证选择"简单通用模式"
|
||||
3. 获取 API Key
|
||||
4. 每次请求在 Header 中携带 `Authorization: Bearer {API_Key}`
|
||||
|
||||
无需调用 `get_app_token` 接口,直接用 API Key 即可调用所有业务接口。
|
||||
|
||||
### 复杂鉴权模式
|
||||
|
||||
1. 创建应用后获取 API Key
|
||||
2. 调用 `/auth/get_app_token` 获取 appAuthToken
|
||||
3. 后续请求使用 appAuthToken 进行身份验证
|
||||
|
||||
## 公共响应参数
|
||||
|
||||
所有接口的网关层返回以下统一结构:
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| request_id | 全局请求 ID,用于追踪定位 | string |
|
||||
| code | 网关返回码,`"0"` 表示成功 | string |
|
||||
| msg | 网关返回码描述 | string |
|
||||
| sub_code | 业务返回码,成功时不返回 | string |
|
||||
| sub_msg | 业务返回码描述,成功时不返回 | string |
|
||||
| data | 业务响应参数 | object |
|
||||
|
||||
### 业务层响应(data 内部)
|
||||
|
||||
部分接口(员工查询、账号查询等)的 data 内部有二级结构:
|
||||
|
||||
| 参数名称 | 参数说明 | 类型 |
|
||||
|----------|----------|------|
|
||||
| ret | 状态码,0=成功,其他=失败 | number |
|
||||
| status | `"success"` 或 `"faile"` | string |
|
||||
| data | 业务数据(数组或对象) | array/object |
|
||||
| msg | 错误信息 | string |
|
||||
| count | 总条数 | string/number |
|
||||
|
||||
### 响应判断逻辑
|
||||
|
||||
```
|
||||
1. 检查网关层 code 是否为 "0"
|
||||
├── 否 → 打印 request_id、msg、sub_msg,检查 API Key 和权限配置
|
||||
└── 是 → 检查业务层是否成功
|
||||
├── 否 → 打印响应获取错误原因
|
||||
└── 是 → 解析 data 获取业务数据
|
||||
```
|
||||
|
||||
> **异常处理要求**:请求异常(包括状态码异常)时,必须将 `request_id`、`msg` 和 `sub_msg` 都打印出来,便于追踪定位问题。
|
||||
|
||||
## 前置准备
|
||||
|
||||
1. 在紫鸟开放平台注册并创建"卖家自研应用"
|
||||
2. 选择鉴权模式(简单鉴权或复杂鉴权)
|
||||
3. 获取 API Key
|
||||
4. 申请所需权限点(如需使用 `user-login` 接口,需联系紫鸟在线客服走特殊申请 OA 单,Boss 验证后生效)
|
||||
5. 配置 IP 白名单
|
||||
6. 获取 companyId(调用 `/app/builtin/company` 或在开放平台首页右侧查看)
|
||||
@@ -0,0 +1,88 @@
|
||||
# 客户端 Schema URL 协议
|
||||
|
||||
> 本文件是 ziniao-sso-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅 superbrowser:// Schema URL 的详细参数与用法时加载。
|
||||
|
||||
## 概述
|
||||
|
||||
通过 `superbrowser://` 自定义 URL 协议控制紫鸟浏览器,支持打开/关闭账号和退出程序。
|
||||
调用方式有两种:
|
||||
|
||||
1. **浏览器链接**:在浏览器地址栏打开 `superbrowser://...`(需通过安装程序安装紫鸟)
|
||||
2. **命令行**:`SuperBrowser.exe "superbrowser://..."` (需主程序路径正确)
|
||||
|
||||
---
|
||||
|
||||
## Action: OpenStrore(打开账号)
|
||||
|
||||
### 完整示例
|
||||
|
||||
```
|
||||
superbrowser://OpenStrore?storeId=xxxxxxx&openapiToken=xxxxxxxxx&userId=xxxxxxx&lanuchUrl=aHR0cHM6Ly93d3cuYmFpZHUuY29t&autoopen=true&debuggPort=60001¬PromptForDownload=1&forceDownloadPath=Rjpc5a6J6KOF5YyF --disable-gpu start-maximized
|
||||
```
|
||||
|
||||
### 必须参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| storeId | 账号 ID |
|
||||
| userId | 紫鸟的用户 ID(如果发生变化需要重新登录) |
|
||||
| openapiToken | 鉴权 token(通过 `user-login` 接口获取) |
|
||||
|
||||
> 如果本地已登录紫鸟客户端,且 userId 与本地登录用户一致,openapiToken 可不传(客户端版本 v5.290 或 v6.25 以上)。
|
||||
|
||||
### 可选参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| lanuchUrl | 将要打开的目标地址,需 Base64 编码(如 `https://www.baidu.com` → `aHR0cHM6Ly93d3cuYmFpZHUuY29t`) |
|
||||
| autoopen | 设为 `true` 时自动打开 lanuchUrl 配置的地址 |
|
||||
| debuggPort | 指定 debug 端口号 |
|
||||
| notPromptForDownload | `1`=下载文件不弹出选择路径提示框,直接下载;`0` 或不传则弹出选择路径提示框 |
|
||||
| forceDownloadPath | 设置默认下载路径,路径需 Base64 编码 |
|
||||
|
||||
### 命令行参数
|
||||
|
||||
命令参数添加在链接后面,用空格隔开,多个参数也用空格隔开。
|
||||
|
||||
> 添加的命令参数需要向管理员申请使用权限,否则不生效。
|
||||
|
||||
### 响应结果
|
||||
|
||||
启动紫鸟浏览器并打开对应账号。
|
||||
|
||||
---
|
||||
|
||||
## Action: exit(退出浏览器)
|
||||
|
||||
### 请求格式
|
||||
|
||||
```
|
||||
superbrowser://exit
|
||||
```
|
||||
|
||||
### 说明
|
||||
|
||||
退出紫鸟浏览器程序,关闭所有账号。
|
||||
|
||||
> 如果上次打开的程序未完全退出,下次调用打开账号将会发生异常。建议在重新打开前先调用 exit。
|
||||
|
||||
---
|
||||
|
||||
## Action: CloseStrore(关闭账号)
|
||||
|
||||
### 请求格式
|
||||
|
||||
```
|
||||
superbrowser://CloseStrore?storeId=268682156400
|
||||
```
|
||||
|
||||
### 参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| storeId | 要关闭的账号 ID |
|
||||
|
||||
### 说明
|
||||
|
||||
关闭指定账号,每次只能关闭 1 个账号。如需关闭多个,需多次调用。
|
||||
@@ -0,0 +1,163 @@
|
||||
---
|
||||
name: ziniao-webdriver-doc
|
||||
description: >
|
||||
Use when 需要理解紫鸟浏览器 WebDriver 自动化接口的定位、能力、架构或对接流程,
|
||||
用于设计讨论、集成规划或工具评估。
|
||||
不要用于执行具体自动化脚本编写——执行时直接参考 reference/ 中的接口与示例。
|
||||
---
|
||||
|
||||
# ziniao-webdriver-doc(紫鸟 WebDriver 文档)
|
||||
|
||||
## 何时使用 / 不使用
|
||||
|
||||
| 场景 | 做法 |
|
||||
|------|------|
|
||||
| 需要理解紫鸟 WebDriver 是什么、能做什么 | 读本 Skill |
|
||||
| 需要做自动化集成/框架选型的设计决策 | 读本 Skill |
|
||||
| 需要查阅某个接口的请求/响应格式 | 读本 Skill 的 §按需深入 获取路径,再读对应 reference/ 文件 |
|
||||
| 需要查看各语言/框架示例代码 | 不读本 Skill → 直接读 `reference/framework-examples.md` |
|
||||
|
||||
---
|
||||
|
||||
## Level 0:定位与边界
|
||||
|
||||
### 是什么
|
||||
|
||||
紫鸟浏览器 WebDriver 是紫鸟浏览器提供的本地自动化控制接口。通过命令行参数启动紫鸟浏览器进入 WebDriver 模式后,浏览器在本地开启 HTTP 服务,开发者可通过 JSON API 管理店铺窗口,并使用 Selenium / Puppeteer / Playwright / DrissionPage 等框架对接调试端口,实现对店铺浏览器的自动化控制。
|
||||
|
||||
### 能力概览
|
||||
|
||||
| 能力域 | 说明 |
|
||||
|--------|------|
|
||||
| 客户端启动控制 | 命令行参数启动/退出紫鸟浏览器进入 WebDriver 模式 |
|
||||
| 认证与登录 | 通过 API 完成设备授权、账号登录、获取店铺列表 |
|
||||
| 店铺窗口管理 | 启动/关闭指定店铺窗口,获取调试端口(debuggingPort) |
|
||||
| 自动化框架对接 | 返回调试端口供 Selenium/Puppeteer/Playwright/DrissionPage 连接 |
|
||||
| 内核管理 | 检测/下载/更新浏览器内核 |
|
||||
| 缓存与会话 | 清理本地/在线缓存、管理 Cookie 策略 |
|
||||
| 插件管理 | 启动店铺时加载指定插件、查询插件安装状态 |
|
||||
|
||||
### 不做什么(边界)
|
||||
|
||||
- 不是通用浏览器自动化方案——仅适用于紫鸟浏览器客户端
|
||||
- 不提供 WebDriver 驱动本身——需根据返回的 `core_version` 自行下载匹配版本的 ChromeDriver
|
||||
- 不处理电商平台业务逻辑——仅提供浏览器窗口控制能力
|
||||
- 不提供云端部署——仅支持本地运行(通过 `--listen_ip` 可开放远程控制)
|
||||
|
||||
---
|
||||
|
||||
## Level 1:架构与流程
|
||||
|
||||
### 核心概念
|
||||
|
||||
**WebDriver 模式**:通过 `--run_type=web_driver` 参数启动紫鸟浏览器,使其进入自动化控制模式。此模式下浏览器在本地指定端口开启 HTTP 服务,接受 JSON 格式的 API 调用。启动前必须关闭紫鸟主进程。
|
||||
|
||||
**HTTP IPC 通信**:所有 API 均为 POST 请求,JSON 格式,UTF-8 编码。通过 `--port` 参数指定通信端口,地址为 `http://127.0.0.1:{port}`。每个请求需携带 `action` 字段标识操作类型、`requestId` 做全局唯一标识。超时建议 120 秒以上。
|
||||
|
||||
**店铺窗口(browserOauth / browserId)**:紫鸟以"店铺"为最小管理单位。调用 `getBrowserList` 获取店铺列表及加密 ID(`browserOauth`),然后通过 `startBrowser` 启动指定店铺窗口,返回 `debuggingPort` 用于自动化框架连接。若直接使用 `getBrowserList` 返回值打开店铺,只需传 `browserOauth`;仅当需要以明文店铺 ID 打开时才赋值 `browserId`。
|
||||
|
||||
**内核版本匹配**:`startBrowser` 返回 `core_type`(如 Chromium)和 `core_version`(如 119.1.0.16),开发者必须使用对应大版本号的 ChromeDriver(如 119.x)连接,否则自动化将失败。
|
||||
|
||||
### 流程全景
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[申请权限] --> B["启动客户端<br>(先清理残留进程)"]
|
||||
B --> C["updateCore<br>检测内核<br>⚠ 需凭据"]
|
||||
C --> D["getBrowserList<br>获取店铺列表<br>⚠ 需凭据"]
|
||||
D --> E["startBrowser<br>启动店铺窗口<br>⚠ 需凭据"]
|
||||
E --> F["下载匹配的<br>ChromeDriver"]
|
||||
F --> G["连接自动化框架<br>Selenium等"]
|
||||
G --> H[执行自动化操作]
|
||||
H --> I["stopBrowser<br>关闭店铺<br>⚠ 需凭据"]
|
||||
I --> J[exit<br>退出客户端]
|
||||
```
|
||||
|
||||
1. **申请权限**:在紫鸟开放平台开通 WebDriver 权限
|
||||
2. **启动客户端**:先清理残留进程(见§关键陷阱),再命令行启动紫鸟浏览器,必须参数:`--run_type=web_driver --ipc_type=http --port={端口}`
|
||||
3. **updateCore**:**必须携带 company/username/password**,轮询检测内核是否就绪(statusCode==0),缺失时自动下载
|
||||
4. **getBrowserList**:传入 company/username/password,获取店铺列表
|
||||
5. **startBrowser**:传入 company/username/password + browserOauth,返回 debuggingPort 和 core_version
|
||||
6. **下载 ChromeDriver**:根据 `core_version` 大版本号下载匹配的 ChromeDriver(**不是系统 Chrome 版本**)
|
||||
7. **连接框架**:用 `127.0.0.1:{debuggingPort}` + 匹配版本的 ChromeDriver 连接 Selenium 等
|
||||
8. **关闭**:`stopBrowser`(需凭据)关闭店铺窗口,`exit` 退出客户端
|
||||
|
||||
### 能力摘要表
|
||||
|
||||
| 接口 Action | 职责 | 关键输入 | 关键输出 | 需凭据 |
|
||||
|-------------|------|---------|---------|:------:|
|
||||
| updateCore | 检测/下载内核 | **company, username, password** | statusCode, msg(进度) | **是** |
|
||||
| applyAuth | 设备授权 | company, username, password | statusCode | **是** |
|
||||
| getBrowserList | 获取店铺列表 | company, username, password | browserList[{browserOauth, browserName, siteId...}] | **是** |
|
||||
| startBrowser | 启动店铺窗口 | company, username, password, browserOauth/browserId | debuggingPort, core_version, core_type | **是** |
|
||||
| stopBrowser | 关闭店铺窗口 | company, username, password, browserOauth/browserId | statusCode | **是** |
|
||||
| logout | 退出登录 | 无 | statusCode | 否 |
|
||||
| exit | 退出客户端进程 | 无 | statusCode | 否 |
|
||||
| ClearCache | 清理本地缓存 | browserOauths[] | statusCode | 否 |
|
||||
| ClearOnline | 清理在线缓存 | company, username, password, browserOauth, type(1/2/3) | statusCode | **是** |
|
||||
| getPluginInstalled | 查询插件安装状态 | browserId, pluginIds | install_status | 否 |
|
||||
| getRunningInfo | 获取已开启的店铺 | 无 | browsers[] | 否 |
|
||||
|
||||
### 关键陷阱
|
||||
|
||||
> **以下均为实战中高频踩坑点,务必在编写自动化脚本时严格遵守。**
|
||||
|
||||
#### 陷阱 1:凭据一致性
|
||||
|
||||
所有标记"需凭据"的接口(`updateCore`、`getBrowserList`、`startBrowser`、`stopBrowser`、`ClearOnline`)**每次调用都必须携带完整的 company/username/password**,不只是"登录类"接口。`updateCore` 尤其容易遗漏——它看似只是内核检测,但紫鸟服务端需要凭据验证身份后才返回内核状态。
|
||||
|
||||
#### 陷阱 2:错误信息处理
|
||||
|
||||
API 返回错误时,**优先读取 `err` 字段的内容来定位问题**,而非仅依赖 `statusCode` 数字。`err` 字段包含具体的中文错误描述,是最有价值的排障信息。
|
||||
|
||||
编码注意:`err` 中的中文可能因响应体未正确按 UTF-8 解码而出现乱码,导致无法定位根因。**务必确保 HTTP 客户端以 UTF-8 解码响应体**(Python `requests` 库需设置 `response.encoding = 'utf-8'`,或使用 `response.content.decode('utf-8')`)。
|
||||
|
||||
#### 陷阱 3:进程残留清理
|
||||
|
||||
紫鸟启动失败或异常退出后,可能残留多个子进程(主进程 + 内核进程),如果不清理就重启会导致端口占用、反复失败。**每次启动前和失败后重试前,必须确保所有紫鸟相关进程已终止。**
|
||||
|
||||
清理命令:
|
||||
|
||||
```bash
|
||||
# Windows (PowerShell)
|
||||
Get-Process | Where-Object { $_.ProcessName -match 'ziniao|ZiNiao' } | Stop-Process -Force
|
||||
|
||||
# macOS / Linux
|
||||
pkill -f ziniao || true
|
||||
```
|
||||
|
||||
#### 陷阱 4:ChromeDriver 版本必须匹配紫鸟内核
|
||||
|
||||
紫鸟浏览器的内核版本(`core_version`)**与系统安装的 Chrome 浏览器版本无关**。必须根据 `startBrowser` 返回的 `core_version` 提取大版本号(如 `119.1.0.16` → `119`),下载对应版本的 ChromeDriver。
|
||||
|
||||
建议策略:
|
||||
1. 首次获取 `core_version` 后,检查本地缓存目录是否已有该大版本的 ChromeDriver
|
||||
2. 若无,从 https://googlechromelabs.github.io/chrome-for-testing/ 下载对应版本
|
||||
3. 缓存到本地目录(如 `./chromedriver_cache/{major_version}/chromedriver`),后续复用
|
||||
|
||||
### 环境约定
|
||||
|
||||
| 项目 | 要求 |
|
||||
|------|------|
|
||||
| Windows | 紫鸟 V5 (5.X.X.X) 或 V6 (6.16.0.126+) |
|
||||
| macOS | 紫鸟 V6 (6.15.0.44+) |
|
||||
| Linux | 紫鸟 V6 (6.25.3.3+) |
|
||||
| 通信协议 | HTTP POST, JSON, UTF-8 |
|
||||
| 超时设置 | ≥ 120 秒 |
|
||||
| 启动前提 | 紫鸟主进程必须已完全关闭 |
|
||||
| 推荐框架 | Selenium(Playwright/Puppeteer 会被检测为自动化) |
|
||||
|
||||
---
|
||||
|
||||
## 按需深入(Level 2 路由表)
|
||||
|
||||
> **规则:以下内容不要主动加载。**
|
||||
> 仅在你明确需要了解某个细节时,按路由表读取对应的 reference/ 文件。
|
||||
|
||||
| 你需要了解 | 读取文件 |
|
||||
|-----------|---------|
|
||||
| 核心工作流接口(applyAuth/getBrowserList/startBrowser) | `reference/api-core.md` |
|
||||
| 辅助管理接口(stopBrowser/logout/exit/缓存/插件/updateCore) | `reference/api-auxiliary.md` |
|
||||
| 启动客户端的命令行参数详细说明 | `reference/startup-params.md` |
|
||||
| 各语言/框架的示例代码与下载链接 | `reference/framework-examples.md` |
|
||||
| 权限开通、账号配置、常见问题排查 | `reference/prerequisites.md` |
|
||||
@@ -0,0 +1,226 @@
|
||||
# 辅助管理接口
|
||||
|
||||
> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅 stopBrowser / logout / exit / 缓存清理 / 插件查询 / updateCore 等辅助接口时加载。
|
||||
|
||||
## 4. stopBrowser — 关闭店铺窗口
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"company": "公司",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"action": "stopBrowser",
|
||||
"browserOauth": "店铺ID",
|
||||
"browserId": "店铺数字ID(优先取此值)",
|
||||
"requestId": "全局唯一标识",
|
||||
"duplicate": 0
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"err": "异常信息",
|
||||
"action": "stopBrowser",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**状态码:** 0=成功,-10000=未知异常,-10002=参数非法,-10003=登录失败,-10013=需设备认证
|
||||
|
||||
---
|
||||
|
||||
## 5. logout — 退出登录
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"action": "logout",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. exit — 退出客户端进程
|
||||
|
||||
正常退出紫鸟浏览器主进程,会自动关闭已启动店铺并保持 Cookie 等信息。
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"action": "exit",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"err": "异常信息",
|
||||
"action": "exit",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**状态码:** 0=成功,-10005=登出失败
|
||||
|
||||
---
|
||||
|
||||
## 7. ClearCache — 清理本地缓存
|
||||
|
||||
不需要登录即可调用。
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"action": "ClearCache",
|
||||
"browserOauths": ["店铺id1", "店铺id2"],
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
`browserOauths` 为空则删除所有本地缓存。
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"err": "异常信息",
|
||||
"action": "ClearCache",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**状态码:** 0=成功,-10000=未知异常
|
||||
|
||||
---
|
||||
|
||||
## 8. ClearOnline — 清理在线缓存
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"company": "公司",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"action": "ClearOnline",
|
||||
"browserOauth": "店铺ID",
|
||||
"type": 2,
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
`type` 取值:1=清除所有缓存,2=仅保留cookie,3=仅保留二步认证cookie
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"statusMsg": "具体描述",
|
||||
"err": "异常信息",
|
||||
"action": "ClearOnline",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**状态码:** 0=成功,-10000=没有找到记录
|
||||
|
||||
---
|
||||
|
||||
## 9. getPluginInstalled — 查询插件安装状态
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"action": "getPluginInstalled",
|
||||
"requestId": "全局唯一标识",
|
||||
"browserId": "店铺ID",
|
||||
"duplicate": 0,
|
||||
"pluginIds": "插件id,插件id"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"statusMsg": "",
|
||||
"err": "异常信息",
|
||||
"action": "getPluginInstalled",
|
||||
"requestId": "全局唯一标识",
|
||||
"install_status": true
|
||||
}
|
||||
```
|
||||
|
||||
`install_status`:true=已通知安装,false=未通知
|
||||
|
||||
**状态码:** 0=成功,-10000=没有找到记录
|
||||
|
||||
---
|
||||
|
||||
## 10. getRunningInfo — 获取当前已开启的店铺
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"action": "getRunningInfo",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"browsers": [],
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**状态码:** 0=成功
|
||||
|
||||
---
|
||||
|
||||
## 11. updateCore — 更新内核
|
||||
|
||||
> **⚠ 此接口必须携带 company/username/password 凭据。** 遗漏凭据会导致请求失败——这是最常见的对接错误之一,因为 updateCore 看似只是"内核检测",容易被误认为不需要登录态。
|
||||
|
||||
因 HTTP 有超时限制,此接口适合循环调用直到返回成功。
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"company": "公司",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"action": "updateCore",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"err": "异常信息",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**状态码:** 0=成功,-10000=处理中
|
||||
|
||||
**调用机制:**
|
||||
1. 客户端启动后调用 updateCore,若连接失败说明客户端还未启动成功,轮询(每2秒)直到连接成功
|
||||
2. statusCode==0 → 内核就绪,继续后续操作
|
||||
3. statusCode!=0 → 内核缺失/下载中,继续轮询并展示 `msg` 中的下载进度
|
||||
4. 建议设置总超时 300 秒,避免无限等待
|
||||
|
||||
**错误排查:**
|
||||
- 返回错误时**优先读取 `err` 字段**获取中文错误描述,而非仅看 statusCode 数字
|
||||
- 若 `err` 内容出现乱码,说明 HTTP 响应体未正确按 UTF-8 解码——务必确保客户端以 UTF-8 解码响应
|
||||
- 常见失败原因:未携带凭据、凭据错误、客户端尚未完全启动
|
||||
@@ -0,0 +1,201 @@
|
||||
# 核心工作流接口
|
||||
|
||||
> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查阅 applyAuth / getBrowserList / startBrowser 的请求/响应格式与状态码时加载。
|
||||
|
||||
## 概述
|
||||
|
||||
紫鸟 WebDriver 共提供 11 个 HTTP API,均为 POST 请求,JSON 格式,UTF-8 编码。
|
||||
请求地址:`http://127.0.0.1:{port}`(port 为启动参数 `--port` 指定的端口)。
|
||||
|
||||
通用字段说明:
|
||||
- `action`:接口标识(必须)
|
||||
- `requestId`:全局唯一请求标识(必须)
|
||||
- `statusCode`:返回状态码,`0` 表示成功
|
||||
- `err`:异常信息
|
||||
|
||||
---
|
||||
|
||||
## 1. applyAuth — 申请设备授权
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"company": "公司",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"action": "applyAuth",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"err": "异常信息",
|
||||
"action": "applyAuth",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**状态码:**
|
||||
| 码值 | 说明 |
|
||||
|------|------|
|
||||
| 0 | 成功 |
|
||||
| -10000 | 未知异常 |
|
||||
| -10002 | Socket参数非法 |
|
||||
| -10003 | 申请流程异常 |
|
||||
| -10004 | 申请失败 |
|
||||
|
||||
---
|
||||
|
||||
## 2. getBrowserList — 获取店铺列表
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"company": "公司",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"action": "getBrowserList",
|
||||
"requestId": "全局唯一标识"
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"err": "异常信息",
|
||||
"action": "getBrowserList",
|
||||
"requestId": "全局唯一标识",
|
||||
"browserList": [{
|
||||
"browserOauth": "店铺ID(加密)",
|
||||
"browserName": "店铺名称",
|
||||
"browserIp": "店铺IP",
|
||||
"siteId": "店铺所属站点",
|
||||
"isExpired": false,
|
||||
"platform_id": "平台id",
|
||||
"platform_name": "平台名称"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
SiteId 说明文档:https://cdn-superbrowser-attachment.ziniao.com/webdriver/doc/SiteId%E8%AF%B4%E6%98%8E20250703.txt
|
||||
|
||||
**状态码:**
|
||||
| 码值 | 说明 |
|
||||
|------|------|
|
||||
| 0 | 成功 |
|
||||
| -10000 | 未知异常 |
|
||||
| -10002 | Socket参数非法 |
|
||||
| -10003 | 登录失败(需开通webdriver权限/关闭登录验证) |
|
||||
| -10004 | 获取店铺列表时服务器返回异常 |
|
||||
| -10013 | 需要设备认证 |
|
||||
|
||||
---
|
||||
|
||||
## 3. startBrowser — 启动店铺窗口
|
||||
|
||||
连续两次调用 startBrowser 会视为重启。关闭店铺需调用 stopBrowser。
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"company": "公司",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"action": "startBrowser",
|
||||
"browserOauth": "店铺ID(加密)",
|
||||
"browserId": "店铺ID(未加密,优先取此值)",
|
||||
"isHeadless": true,
|
||||
"isWaitPluginUpdate": true,
|
||||
"privacyMode": true,
|
||||
"requestId": "全局唯一标识",
|
||||
"runMode": "运行模式 1轻量/2均衡(默认)/3极速",
|
||||
"pluginIdList": "id1,id2,id3",
|
||||
"pluginIdType": 0,
|
||||
"proxyTagFiter": "name1,name2,name3",
|
||||
"cookieTypeLoad": 0,
|
||||
"cookieTypeSave": 0,
|
||||
"injectJsInfo": "{\"url\":\"http://...\",\"username\":\"用户名\"}",
|
||||
"notPromptForDownload": 1,
|
||||
"isLoadUserPlugin": true,
|
||||
"windowRatio": 100,
|
||||
"forceDownloadPath": "文件下载路径(绝对路径)",
|
||||
"preSetting": "{\"profile.managed_default_content_settings.images\": 2}"
|
||||
}
|
||||
```
|
||||
|
||||
参数说明:
|
||||
- `browserOauth` / `browserId`:二选一指定目标店铺。如果直接使用 `getBrowserList` 返回的数据来打开店铺,**只需传 `browserOauth` 即可**(它已包含加密店铺标识);仅当用户需要使用明文店铺 ID 打开时,才需要赋值 `browserId`。两者同时存在时 `browserId` 优先。
|
||||
- `isHeadless`:无头模式(开启后需自行检测网络)
|
||||
- `isWaitPluginUpdate`:等待插件热更新完成(仅 V5)
|
||||
- `runMode`:仅 V5 支持
|
||||
- `pluginIdType`:0=插件版本id,1=插件id
|
||||
- `cookieTypeLoad`:固定传 0
|
||||
- `cookieTypeSave`:0=默认,1=不提交
|
||||
- `notPromptForDownload`:1=不弹窗,0=弹窗
|
||||
- `windowRatio`:0-100,0=不控制,100=全屏
|
||||
- `preSetting`:浏览器配置 JSON,支持无图模式
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"statusCode": "状态码",
|
||||
"err": "异常信息",
|
||||
"action": "startBrowser",
|
||||
"browserOauth": "店铺ID",
|
||||
"requestId": "全局唯一标识",
|
||||
"LastError": "内部错误信息",
|
||||
"ip": "代理IP",
|
||||
"isDynamicIp": "是否动态IP",
|
||||
"browserPath": "允许的下载路径",
|
||||
"launcherPage": "平台默认启动页面",
|
||||
"ipDetectionPage": "IP检测页地址",
|
||||
"debuggingPort": "调试端口",
|
||||
"reportPluginId": "报表插件id",
|
||||
"duplicate": "副本标志",
|
||||
"proxyTag": "当前启动的代理标志",
|
||||
"proxyType": 5,
|
||||
"mainHandle": "窗口句柄",
|
||||
"core_version": "内核版本",
|
||||
"core_type": "内核类型",
|
||||
"coreVersion": "内核版本",
|
||||
"coreType": "内核类型",
|
||||
"downloadPath": "文件下载路径"
|
||||
}
|
||||
```
|
||||
|
||||
`proxyType` 取值:-1=错误数据,0=站群,1=云平台,2=自有IP,5=本地IP
|
||||
|
||||
使用 Selenium 连接时需根据 `core_type` 及 `core_version` 选取对应版本 ChromeDriver:
|
||||
```java
|
||||
ChromeOptions options = new ChromeOptions();
|
||||
options.setExperimentalOption("debuggerAddress", "127.0.0.1:" + debuggingPort);
|
||||
```
|
||||
|
||||
**状态码:**
|
||||
| 码值 | 说明 |
|
||||
|------|------|
|
||||
| 0 | 成功 |
|
||||
| -10000 | 未知异常 |
|
||||
| -10001 | 内核窗口创建失败 |
|
||||
| -10002 | Socket参数非法 |
|
||||
| -10003 | 登录失败 |
|
||||
| -10004 | browserOauth缺失 |
|
||||
| -10006 | 上次 startBrowser 还未执行结束 |
|
||||
| -10013 | 需要设备认证 |
|
||||
| 1 | 初始化数据失败 |
|
||||
| 2 | 当前IP无法正常使用 |
|
||||
| 4 | 初始化时区失败 |
|
||||
| 5 | 初始化代理失败 |
|
||||
| 6 | 初始化黑白名单失败 |
|
||||
| 7 | 启动内核失败 |
|
||||
| 8 | 初始化浏览器个人目录失败 |
|
||||
| 9 | 初始化Cookies失败 |
|
||||
| 11 | 初始化浏览器设置文件失败 |
|
||||
| 13 | 初始化代理信息配置失败 |
|
||||
|
||||
`LastError` 字段包含具体错误信息。
|
||||
@@ -0,0 +1,159 @@
|
||||
# 自动化框架示例
|
||||
|
||||
> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
|
||||
> 仅在需要查看各语言/框架的示例代码与下载链接时加载。
|
||||
|
||||
## 概述
|
||||
|
||||
紫鸟 WebDriver 支持多种自动化框架和编程语言。所有示例代码均需替换以下信息后运行:
|
||||
- 驱动程序路径(ChromeDriver 等)
|
||||
- 客户端可执行程序路径
|
||||
- Socket 端口号
|
||||
- 用户登录信息(company, username, password)
|
||||
|
||||
**推荐使用 Selenium**:Playwright 和 Puppeteer 会被平台检测为自动化,需自行处理特征问题。
|
||||
|
||||
## 框架支持矩阵
|
||||
|
||||
| 框架 | 语言 | 示例文件 | 下载链接 | 备注 |
|
||||
|------|------|---------|---------|------|
|
||||
| Selenium | Python | ziniao_webdriver_http_py3.py | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/ziniao_webdriver_http_py3.py) | 基础店铺访问示例,适合初学者 |
|
||||
| Selenium | Python | ziniao_webdriver_demo | [GitHub](https://github.com/ziniao-open/ziniao_webdriver_demo) | 开源示例集:订单拉取、报表下载、评价导出 |
|
||||
| Selenium | Java | webdriver-java-demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/webdriver-java-demo.zip) | 用 IDEA 打开后运行 |
|
||||
| Selenium | C#.NET | webdriver-C#.net-demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/webdriver-CSharp-demo.zip) | Windows 环境 |
|
||||
| Puppeteer | JavaScript | puppeteer_js_demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/puppeteer_js_demo.zip) | 通过 Puppeteer API 控制 |
|
||||
| Playwright | Python | ziniao_playwright_http_py3.py | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/ziniao_playwright_http_py3.py) | 会被检测为自动化,建议用 Selenium |
|
||||
| Playwright | Java | playwright-java-demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/playwright-java-demo.zip) | 会被检测为自动化,建议用 Selenium |
|
||||
| Playwright | JavaScript | playwright_js_demo.zip | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/playwright_js_demo.zip) | 会被检测为自动化,建议用 Selenium |
|
||||
| DrissionPage | Python | ziniao_drissionpage_http_py3.py | [下载](https://cdn-superbrowser-attachment.ziniao.com/webdriver/demo/20260119/ziniao_drissionpage_http_py3.py) | 通过 DrissionPage API 控制 |
|
||||
|
||||
## 连接示例(以 Selenium 为例)
|
||||
|
||||
### Python + Selenium
|
||||
|
||||
```python
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
|
||||
# startBrowser 返回的 debuggingPort 和 core_version
|
||||
debugging_port = 12345
|
||||
core_version = "119.1.0.16"
|
||||
|
||||
# 根据 core_version 的大版本号选择对应 ChromeDriver
|
||||
chrome_driver_path = "/path/to/chromedriver-119"
|
||||
|
||||
options = Options()
|
||||
options.add_experimental_option("debuggerAddress", f"127.0.0.1:{debugging_port}")
|
||||
|
||||
service = Service(executable_path=chrome_driver_path)
|
||||
driver = webdriver.Chrome(service=service, options=options)
|
||||
|
||||
# 现在可以操作店铺浏览器窗口
|
||||
print(driver.title)
|
||||
```
|
||||
|
||||
### Java + Selenium
|
||||
|
||||
```java
|
||||
ChromeOptions options = new ChromeOptions();
|
||||
options.setExperimentalOption("debuggerAddress", "127.0.0.1:" + debuggingPort);
|
||||
|
||||
// 根据 core_version 选择对应版本 ChromeDriver
|
||||
System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver-" + majorVersion);
|
||||
|
||||
WebDriver driver = new ChromeDriver(options);
|
||||
System.out.println(driver.getTitle());
|
||||
```
|
||||
|
||||
## ChromeDriver 版本匹配
|
||||
|
||||
> **⚠ 紫鸟内核版本 ≠ 系统 Chrome 版本。** 不要使用系统安装的 Chrome 浏览器对应的 ChromeDriver,必须根据紫鸟 `startBrowser` 返回的 `core_version` 来匹配。
|
||||
|
||||
`startBrowser` 响应中的 `core_type` 和 `core_version` 决定了需要使用的 ChromeDriver 版本:
|
||||
- `core_type: "Chromium"`, `core_version: "119.1.0.16"` → 使用 ChromeDriver 119.x
|
||||
- 下载地址:https://googlechromelabs.github.io/chrome-for-testing/
|
||||
- JSON API(自动化脚本可直接解析):https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json
|
||||
|
||||
版本不匹配会导致连接失败,务必检查。
|
||||
|
||||
### 自动下载与缓存策略(Python 伪代码)
|
||||
|
||||
```python
|
||||
import os, re, json, zipfile, io, platform
|
||||
import requests
|
||||
|
||||
CACHE_DIR = "./chromedriver_cache"
|
||||
|
||||
def get_chromedriver_path(core_version: str) -> str:
|
||||
"""根据紫鸟 core_version 获取匹配的 ChromeDriver 路径,自动下载缓存。"""
|
||||
major = core_version.split(".")[0] # "119.1.0.16" → "119"
|
||||
|
||||
exe_name = "chromedriver.exe" if platform.system() == "Windows" else "chromedriver"
|
||||
cached = os.path.join(CACHE_DIR, major, exe_name)
|
||||
|
||||
if os.path.exists(cached):
|
||||
return cached
|
||||
|
||||
# 查询可用版本
|
||||
resp = requests.get(
|
||||
"https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"
|
||||
)
|
||||
resp.encoding = "utf-8"
|
||||
data = resp.json()
|
||||
|
||||
# 找到该大版本的最新 chromedriver
|
||||
plat = {
|
||||
"Windows": "win64", "Darwin": "mac-x64", "Linux": "linux64"
|
||||
}[platform.system()]
|
||||
|
||||
download_url = None
|
||||
for v in reversed(data["versions"]):
|
||||
if v["version"].startswith(major + ".") and "chromedriver" in v["downloads"]:
|
||||
for d in v["downloads"]["chromedriver"]:
|
||||
if d["platform"] == plat:
|
||||
download_url = d["url"]
|
||||
break
|
||||
if download_url:
|
||||
break
|
||||
|
||||
if not download_url:
|
||||
raise RuntimeError(f"未找到大版本 {major} 的 ChromeDriver 下载地址")
|
||||
|
||||
# 下载并解压
|
||||
os.makedirs(os.path.join(CACHE_DIR, major), exist_ok=True)
|
||||
zip_resp = requests.get(download_url)
|
||||
with zipfile.ZipFile(io.BytesIO(zip_resp.content)) as zf:
|
||||
for name in zf.namelist():
|
||||
if name.endswith(exe_name):
|
||||
with open(cached, "wb") as f:
|
||||
f.write(zf.read(name))
|
||||
break
|
||||
|
||||
if platform.system() != "Windows":
|
||||
os.chmod(cached, 0o755)
|
||||
|
||||
return cached
|
||||
```
|
||||
|
||||
### 缓存目录结构
|
||||
|
||||
```
|
||||
chromedriver_cache/
|
||||
├── 119/
|
||||
│ └── chromedriver.exe
|
||||
├── 120/
|
||||
│ └── chromedriver.exe
|
||||
└── ...
|
||||
```
|
||||
|
||||
## GitHub 开源示例项目
|
||||
|
||||
仓库地址:https://github.com/ziniao-open/ziniao_webdriver_demo
|
||||
|
||||
包含以下场景示例:
|
||||
1. **Demo 1**:电商订单自动拉取(热门推荐)
|
||||
2. **Demo 2**:销售与流量业务报表下载
|
||||
3. **Demo 3**:电商平台用户评价导出
|
||||
|
||||
克隆后替换配置信息即可运行。
|
||||
@@ -0,0 +1,141 @@
|
||||
# 前置条件与排障
|
||||
|
||||
> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
|
||||
> 仅在需要了解权限开通、账号配置、常见问题排查时加载。
|
||||
|
||||
## 概述
|
||||
|
||||
使用紫鸟 WebDriver 前需完成权限开通和环境准备。本文档覆盖权限申请、账号配置和常见问题排查。
|
||||
|
||||
## 1. 开通 WebDriver 权限
|
||||
|
||||
首次使用 WebDriver 时,需先在紫鸟开放平台开通权限。
|
||||
|
||||
详细步骤见官方文档:[如何开通webdriver权限](https://open.ziniao.com/docSupport?docId=99)
|
||||
|
||||
## 2. 客户端版本要求
|
||||
|
||||
| 操作系统 | 最低版本 |
|
||||
|---------|---------|
|
||||
| Windows | 紫鸟 V5 (5.X.X.X) 或 V6 (6.16.0.126+) |
|
||||
| macOS | 紫鸟 V6 (6.15.0.44+) |
|
||||
| Linux | 紫鸟 V6 (6.25.3.3+) |
|
||||
|
||||
## 3. 环境准备清单
|
||||
|
||||
### 3.1 关闭主进程
|
||||
|
||||
启动 WebDriver 前**务必**确保紫鸟浏览器主进程已完全关闭,避免端口占用或冲突。
|
||||
|
||||
### 3.2 清理残留进程
|
||||
|
||||
紫鸟启动失败或异常退出后,可能残留多个子进程(主进程 + 内核进程),不清理直接重启会导致端口占用、反复失败。**每次启动前和失败后重试前,都必须执行清理。**
|
||||
|
||||
已知的紫鸟相关进程名:`ziniao`、`ZiNiao`、`ziniao.exe`、`ZiNiaoBrowser` 等(不同版本命名可能不同,建议用模糊匹配)。
|
||||
|
||||
清理命令:
|
||||
|
||||
```bash
|
||||
# Windows (PowerShell)
|
||||
Get-Process | Where-Object { $_.ProcessName -match 'ziniao|ZiNiao' } | Stop-Process -Force
|
||||
|
||||
# macOS / Linux
|
||||
pkill -f ziniao || true
|
||||
```
|
||||
|
||||
验证清理结果:
|
||||
|
||||
```bash
|
||||
# Windows (PowerShell) — 应无输出
|
||||
Get-Process | Where-Object { $_.ProcessName -match 'ziniao|ZiNiao' }
|
||||
|
||||
# macOS / Linux — 应无输出
|
||||
pgrep -f ziniao
|
||||
```
|
||||
|
||||
### 3.3 创建自动化专用账号
|
||||
|
||||
- 创建专用的自动化成员账号(命名建议:`自动化_Robot`)
|
||||
- 正式使用前验证该账号能否正常登录紫鸟浏览器客户端
|
||||
|
||||
### 3.4 配置账号权限
|
||||
|
||||
**问题场景**:自动化账号登录后无法获取店铺列表。
|
||||
|
||||
**解决方案**:
|
||||
1. 登录企业管理后台
|
||||
2. 检查该账号的权限配置
|
||||
3. 授予查看对应账号/店铺的权限
|
||||
4. 建议新建包含此权限的自定义角色并分配给自动化账号
|
||||
|
||||
### 3.5 超时设置
|
||||
|
||||
将所有 HTTP 请求的超时时间设为 **120 秒或以上**,因为启动店铺环境可能耗时较长。
|
||||
|
||||
### 3.6 系统资源管理
|
||||
|
||||
- 启动店铺窗口 CPU 消耗较大
|
||||
- 根据设备配置合理控制并发启动数量
|
||||
- 建议错开各任务的启动时间,避免瞬时负载过高
|
||||
|
||||
### 3.7 ChromeDriver 版本管理
|
||||
|
||||
> **关键:紫鸟内核版本 ≠ 系统 Chrome 版本。** 必须根据 `startBrowser` 返回的 `core_version` 匹配 ChromeDriver,不能使用系统 Chrome 对应的版本。
|
||||
|
||||
**匹配规则:**
|
||||
- `core_version` 格式为 `X.Y.Z.W`(如 `119.1.0.16`),取第一段 `X` 作为大版本号
|
||||
- 下载该大版本号对应的 ChromeDriver(如大版本 `119` → ChromeDriver 119.x)
|
||||
|
||||
**下载地址:**
|
||||
- Chrome for Testing:https://googlechromelabs.github.io/chrome-for-testing/
|
||||
- JSON API(便于自动化脚本解析):https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json
|
||||
|
||||
**本地缓存建议:**
|
||||
|
||||
```
|
||||
chromedriver_cache/
|
||||
├── 119/
|
||||
│ └── chromedriver.exe # 对应 core_version 119.x
|
||||
├── 120/
|
||||
│ └── chromedriver.exe # 对应 core_version 120.x
|
||||
└── ...
|
||||
```
|
||||
|
||||
自动化脚本应实现以下逻辑:
|
||||
1. 从 `startBrowser` 响应中提取 `core_version` 大版本号
|
||||
2. 检查缓存目录 `chromedriver_cache/{major_version}/` 是否存在
|
||||
3. 若不存在,从 Chrome for Testing 下载对应版本并解压到缓存目录
|
||||
4. 使用缓存中的 ChromeDriver 路径连接 Selenium
|
||||
|
||||
## 4. 通信注意事项
|
||||
|
||||
1. 请求/响应均为 JSON 格式,收发消息统一 UTF-8 编码
|
||||
2. HTTP 模式下超时时长必须 ≥120 秒
|
||||
3. 调用任何接口后务必检查 `statusCode`(0 表示成功),**出错时优先读取 `err` 字段获取中文错误描述**,这是最有价值的排障信息
|
||||
4. **启动后必须首先调用 `updateCore` 接口**(需携带凭据),否则打开店铺可能出现内核缺失
|
||||
5. **务必确保 HTTP 客户端以 UTF-8 解码响应体**,否则 `err` 中的中文会乱码,严重影响错误定位(Python `requests` 库需设置 `response.encoding = 'utf-8'`)
|
||||
6. 所有标记"需凭据"的接口(`updateCore`、`getBrowserList`、`startBrowser`、`stopBrowser`、`ClearOnline`)每次调用都必须携带 company/username/password
|
||||
|
||||
## 5. 常见问题
|
||||
|
||||
| 现象 | 可能原因 | 解决方案 |
|
||||
|------|---------|---------|
|
||||
| 连接接口失败 | 客户端未启动完成 | 轮询 updateCore 直到连接成功 |
|
||||
| getBrowserList 返回 -10003 | 未开通权限或未关闭登录验证 | 检查权限开通状态 |
|
||||
| getBrowserList 返回 -10013 | 新设备未授权 | 调用 applyAuth 进行设备授权 |
|
||||
| 店铺列表为空 | 账号无店铺权限 | 在企业管理后台配置角色权限 |
|
||||
| startBrowser 返回 -10006 | 上次启动未完成 | 等待或调用 stopBrowser 后重试 |
|
||||
| startBrowser 返回 7 | 内核缺失 | 确保先调用 updateCore 等待成功 |
|
||||
| Selenium 连接失败 | ChromeDriver 版本不匹配 | 根据 core_version 下载匹配版本 |
|
||||
| 请求超时 | 超时设置过短 | 设置超时 ≥120 秒 |
|
||||
| 多账号冲突 | 端口/缓存冲突 | V5 使用 `--multip` + `--enforce-cache-path` |
|
||||
| updateCore 返回错误 | 未携带凭据(company/username/password) | 所有需凭据的接口必须携带完整凭据 |
|
||||
| err 字段内容乱码 | HTTP 响应体未以 UTF-8 解码 | 设置 `response.encoding = 'utf-8'` 或用 `response.content.decode('utf-8')` |
|
||||
| 重启失败 / 端口占用 | 上次失败后残留紫鸟进程 | 清理所有紫鸟相关进程后重试(见§3.2) |
|
||||
| Selenium 连接失败(版本不匹配) | 使用了系统 Chrome 对应的 ChromeDriver | 必须根据紫鸟 `core_version` 大版本号下载匹配的 ChromeDriver(见§3.7) |
|
||||
|
||||
## 6. 技术支持
|
||||
|
||||
如在对接过程中遇到问题,可通过以下渠道获取支持:
|
||||
- 微信添加紫鸟生态管家,备注 "紫鸟webdriver"
|
||||
- 官方 FAQ:https://open.ziniao.com/docSupport?docId=257
|
||||
@@ -0,0 +1,122 @@
|
||||
# 启动参数详细说明
|
||||
|
||||
> 本文件是 ziniao-webdriver-doc 的 Level 2 参考文档。
|
||||
> 仅在需要了解紫鸟浏览器 WebDriver 模式的启动命令行参数时加载。
|
||||
|
||||
## 概述
|
||||
|
||||
通过命令行参数启动紫鸟浏览器客户端,使其进入 WebDriver 模式。
|
||||
启动前**必须**关闭紫鸟浏览器的主进程,否则可能端口占用或冲突。
|
||||
|
||||
## 必选参数
|
||||
|
||||
| 参数 | 版本支持 | 说明 |
|
||||
|------|---------|------|
|
||||
| `--run_type=web_driver` | V5 / V6 全平台 | 指定以 WebDriver 模式运行主进程 |
|
||||
| `--ipc_type=http` | V5 / V6 全平台 | 约定通信方式为 HTTP,超时建议 ≥120 秒 |
|
||||
| `--port=端口号` | V5 / V6 全平台 | 指定通讯端口,紫鸟在本机以此端口启动 HTTP 服务 |
|
||||
|
||||
## 可选参数
|
||||
|
||||
| 参数 | 版本支持 | 说明 |
|
||||
|------|---------|------|
|
||||
| `--port_exception=1886,1567` | V5 only | 店铺内允许连接到本地的端口号,逗号分隔 |
|
||||
| `--multip` | V5 only | 去除干扰自动化的功能,多账号建议与 `--enforce-cache-path` 一起使用 |
|
||||
| `--enforce-cache-path=路径` | V5 only | 指定独立缓存目录,允许多账号同时运行,需自行调整 `--port` 避免冲突 |
|
||||
| `--listen_ip=IP` | V5 only | 设置监听地址,默认 127.0.0.1。非本机时允许远程控制 |
|
||||
| `--dns=DNS服务器` | V5 only | 设置 DNS,默认使用本机 DNS。示例:`--dns=223.5.5.5` |
|
||||
|
||||
## 启动命令示例
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import subprocess, platform, time
|
||||
|
||||
client_path = "紫鸟浏览器可执行文件路径"
|
||||
socket_port = 9515
|
||||
|
||||
is_windows = platform.system() == 'Windows'
|
||||
is_mac = platform.system() == 'Darwin'
|
||||
is_linux = platform.system() == 'Linux'
|
||||
|
||||
def start_browser():
|
||||
try:
|
||||
if is_windows:
|
||||
cmd = [client_path,
|
||||
'--run_type=web_driver',
|
||||
'--ipc_type=http',
|
||||
'--port=' + str(socket_port)]
|
||||
elif is_mac:
|
||||
cmd = ['open', '-a', client_path, '--args',
|
||||
'--run_type=web_driver',
|
||||
'--ipc_type=http',
|
||||
'--port=' + str(socket_port)]
|
||||
elif is_linux:
|
||||
cmd = [client_path, '--no-sandbox',
|
||||
'--run_type=web_driver',
|
||||
'--ipc_type=http',
|
||||
'--port=' + str(socket_port)]
|
||||
else:
|
||||
exit()
|
||||
subprocess.Popen(cmd)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
import traceback
|
||||
print('start browser process failed: ' + traceback.format_exc())
|
||||
exit()
|
||||
```
|
||||
|
||||
### JavaScript (Node.js)
|
||||
|
||||
```javascript
|
||||
const childProcess = require('child_process');
|
||||
const util = require('util');
|
||||
const os = require('os');
|
||||
const execFile = util.promisify(childProcess.execFile);
|
||||
|
||||
const clientPath = '紫鸟浏览器可执行文件路径';
|
||||
const socketPort = 9515;
|
||||
|
||||
const is_windows = os.platform() === 'win32';
|
||||
const is_mac = os.platform() === 'darwin';
|
||||
const is_linux = os.platform() === 'linux';
|
||||
|
||||
async function startBrowser() {
|
||||
try {
|
||||
if (is_windows) {
|
||||
const cmd = [clientPath,
|
||||
'--run_type=web_driver',
|
||||
'--ipc_type=http',
|
||||
`--port=${socketPort}`];
|
||||
execFile(cmd[0], cmd.slice(1));
|
||||
} else if (is_mac) {
|
||||
const cmd = ['open', '-a', clientPath, '--args',
|
||||
'--run_type=web_driver',
|
||||
'--ipc_type=http',
|
||||
`--port=${socketPort}`];
|
||||
execFile(cmd[0], cmd.slice(1));
|
||||
} else if (is_linux) {
|
||||
const cmd = [clientPath, '--no-sandbox',
|
||||
'--run_type=web_driver',
|
||||
'--ipc_type=http',
|
||||
`--port=${socketPort}`];
|
||||
execFile(cmd[0], cmd.slice(1));
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
} catch (error) {
|
||||
console.log(`start browser process failed: ${error}`);
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 各操作系统差异
|
||||
|
||||
| 操作系统 | 启动方式 | 特殊参数 |
|
||||
|---------|---------|---------|
|
||||
| Windows | 直接调用可执行文件 | 无 |
|
||||
| macOS | 通过 `open -a` 启动,`--args` 传参 | 无 |
|
||||
| Linux | 直接调用可执行文件 | 需追加 `--no-sandbox` |
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 ziniao-open
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,244 @@
|
||||
# ziniao-skills
|
||||
|
||||
紫鸟浏览器 AI Agent Skill 包,覆盖 ERP 管理(员工/角色/设备/标签/部门)和店铺自动化操作,适配 ziniao-cli 与 ZClaw 本地接口。
|
||||
|
||||
## Skill 安装
|
||||
|
||||
以后 Skill 内置在 `ziniao-cli` 里安装,不再单独走 `npx skills add`:
|
||||
|
||||
```bash
|
||||
ziniao-cli skills install
|
||||
```
|
||||
|
||||
先安装 CLI 后再执行上面的命令。备选(不推荐):`npx skills add ziniao-open/skills -y -g`
|
||||
|
||||
## ziniao-cli 安装
|
||||
|
||||
```bash
|
||||
npm install -g @ziniao-open/cli
|
||||
```
|
||||
|
||||
安装后验证:
|
||||
|
||||
```bash
|
||||
ziniao-cli --version
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 初始化配置
|
||||
|
||||
```bash
|
||||
ziniao-cli config init
|
||||
```
|
||||
|
||||
按提示选择"新建应用"或"手动输入 API Key",完成后凭证自动存入系统 Keychain。
|
||||
|
||||
### 2. AI Agent 初始化流程
|
||||
|
||||
AI Agent 应使用非交互模式:
|
||||
|
||||
```bash
|
||||
ziniao-cli config init --new
|
||||
```
|
||||
|
||||
该命令会阻塞直到审核通过、被拒绝或超时(1 小时),Agent 的处理流程:
|
||||
|
||||
1. 后台执行 `config init --new`
|
||||
2. 从 stderr 输出中提取 URL(包含 `memberAuth?cliRequestId=` 的行)
|
||||
3. 将链接展示给用户,提示在浏览器中打开完成应用创建
|
||||
4. 等待命令完成(审核通过/拒绝/超时)
|
||||
|
||||
输出示例:
|
||||
|
||||
```
|
||||
请在浏览器中打开以下链接完成应用创建:
|
||||
|
||||
https://open.ziniao.com/memberAuth?cliRequestId=a1b2c3d4-...&from=cli
|
||||
|
||||
⏳ 等待应用创建及审核... (按 Ctrl+C 取消)
|
||||
✓ 审核已通过
|
||||
✓ 企业 ID: 15393*****3459
|
||||
✓ 配置已保存 (profile: zhangsan)
|
||||
```
|
||||
|
||||
初始化完成后,`isBoss` 字段决定账号权限范围:
|
||||
|
||||
| 账号类型 | 服务端 API | ZClaw Bridge(本地浏览器) |
|
||||
|---------|-----------|--------------------------|
|
||||
| Boss | 全部可用 | 全部可用 |
|
||||
| 成员 | 不可用 | 全部可用 |
|
||||
|
||||
可通过 `ziniao-cli config show` 查看当前账号类型。
|
||||
|
||||
### 3. 多账号管理
|
||||
|
||||
支持多个账号配置(profile),通过 `config use` 切换:
|
||||
|
||||
```bash
|
||||
# 列出所有 profile
|
||||
ziniao-cli config list
|
||||
|
||||
# 切换到指定 profile
|
||||
ziniao-cli config use <name>
|
||||
|
||||
# 重命名 profile
|
||||
ziniao-cli config rename <old> <new>
|
||||
|
||||
# 删除 profile(需确认,--yes 跳过)
|
||||
ziniao-cli config remove --profile <name>
|
||||
```
|
||||
|
||||
### 4. 验证连通性
|
||||
|
||||
```bash
|
||||
ziniao-cli doctor
|
||||
```
|
||||
|
||||
### 5. 开始使用
|
||||
|
||||
```bash
|
||||
# 查看员工列表
|
||||
ziniao-cli staff list --format table
|
||||
|
||||
# 查看部门树
|
||||
ziniao-cli department list --tree
|
||||
|
||||
# 查看角色列表
|
||||
ziniao-cli role list --format table
|
||||
```
|
||||
|
||||
## 命令概览
|
||||
|
||||
### 配置管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `config init` | 初始化配置 |
|
||||
| `config show` | 显示当前配置 |
|
||||
| `config list` | 列出所有 profile |
|
||||
| `config use <name>` | 切换当前 profile |
|
||||
| `config rename <old> <new>` | 重命名 profile |
|
||||
| `config remove` | 删除 profile(需确认) |
|
||||
|
||||
### 部门管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `department list` | 查询部门列表 |
|
||||
| `department create` | 新增部门 |
|
||||
| `department update` | 修改部门 |
|
||||
| `department delete` | 删除部门 |
|
||||
| `department reorder` | 部门排序 |
|
||||
|
||||
### 员工管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `staff list` | 查询员工列表 |
|
||||
| `staff create` | 新增员工 |
|
||||
| `staff update` | 修改员工信息 |
|
||||
| `staff enable` | 启用员工 |
|
||||
| `staff disable` | 禁用员工 |
|
||||
| `staff remove` | 删除员工 |
|
||||
| `staff transfer` | 员工调岗 |
|
||||
|
||||
### 角色管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `role list` | 查询角色列表 |
|
||||
| `role detail` | 查看角色详情 |
|
||||
| `role create` | 创建角色 |
|
||||
| `role update` | 修改角色 |
|
||||
| `role assign` | 分配角色给员工 |
|
||||
| `role staff-roles` | 查看员工已分配角色 |
|
||||
| `role permissions` | 查看可用权限列表 |
|
||||
|
||||
### 账号(店铺)管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `account list` | 查询账号列表 |
|
||||
| `account create` | 创建账号 |
|
||||
| `account update` | 修改账号信息 |
|
||||
| `account delete` | 删除账号 |
|
||||
| `account auth-add` | 授权员工访问店铺 |
|
||||
| `account auth-remove` | 撤销员工授权 |
|
||||
| `account auth-clean` | 清空店铺所有授权 |
|
||||
| `account auth-list` | 查询员工被授权的店铺 |
|
||||
| `account auth-users` | 查询店铺已授权的员工 |
|
||||
| `account tag-list` | 查询企业标签列表 |
|
||||
| `account tag-create` | 创建标签 |
|
||||
| `account tag-rename` | 重命名标签 |
|
||||
| `account tag-delete` | 删除标签 |
|
||||
| `account tag-bind` | 绑定标签到店铺 |
|
||||
| `account tag-unbind` | 解绑标签 |
|
||||
| `account tag-replace` | 全量替换店铺标签 |
|
||||
| `account tag-clear` | 清空店铺标签 |
|
||||
| `account tag-remove` | 从店铺移除标签 |
|
||||
| `account tag-of-store` | 查询店铺绑定的标签 |
|
||||
|
||||
### 设备管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `device list` | 查询设备列表 |
|
||||
| `device add-custom` | 添加自有设备 |
|
||||
| `device update-custom` | 修改自有设备 |
|
||||
| `device plan-list` | 查询可购买套餐 |
|
||||
| `device bind` | 绑定设备到店铺 |
|
||||
| `device unbind` | 解绑设备 |
|
||||
| `device auto-renew` | 设置自动续费 |
|
||||
| `device purchase` | 购买设备套餐 |
|
||||
| `device renew` | 续费设备 |
|
||||
|
||||
### 浏览器控制(ZClaw)
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `store list` | 列出已打开的店铺浏览器 |
|
||||
| `store open` | 打开店铺浏览器 |
|
||||
| `store close` | 关闭店铺浏览器 |
|
||||
| `page visit` | 导航到指定 URL |
|
||||
| `page screenshot` | 页面截图 |
|
||||
| `page automation` | 页面自动化操作 |
|
||||
|
||||
### 通用 API 调用
|
||||
|
||||
对于未提供快捷命令的接口(共 73 个接口中 shortcut 覆盖 45 个),可使用通用 `api` 命令:
|
||||
|
||||
```bash
|
||||
ziniao-cli api <path> [--data '{}'] [--format table] [--jq '.field']
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/store/deletecookie --data '{"storeId":"xxx"}'
|
||||
```
|
||||
|
||||
## Skill 模块
|
||||
|
||||
| Skill | 说明 |
|
||||
|-------|------|
|
||||
| `ziniao-shared` | 共享基础:配置初始化、认证、输出格式、权限点 |
|
||||
| `ziniao-staff` | 员工管理 |
|
||||
| `ziniao-role` | 角色与权限管理 |
|
||||
| `ziniao-department` | 部门管理 |
|
||||
| `ziniao-account` | 账号(店铺)管理与标签 |
|
||||
| `ziniao-device` | 设备管理 |
|
||||
| `ziniao-store` | 浏览器店铺控制(ZClaw) |
|
||||
| `ziniao-page` | 浏览器页面操作(ZClaw) |
|
||||
| `ziniao-access-policy` | 访问策略管理 |
|
||||
| `ziniao-openapi-explorer` | API 路径清单与探索指引 |
|
||||
| `ziniao-workflow-batch-account` | 批量账号授权工作流 |
|
||||
| `ziniao-skill-maker` | 自定义 Skill 创建指引 |
|
||||
|
||||
## 适用环境
|
||||
|
||||
这些 Skill 文档适用于任何支持工具调用的 AI Agent 环境:
|
||||
|
||||
- Claude Code
|
||||
- Cursor
|
||||
- OpenClaw
|
||||
- 其他支持 Skill 协议的 Agent
|
||||
|
||||
## License
|
||||
|
||||
CC BY 4.0
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
name: ziniao-access-policy
|
||||
version: 1.0.0
|
||||
description: "紫鸟访问策略管理:网页访问规则、网页/网页分组、规则生效成员/账号、账号绑定策略的查询和维护。当前无快捷命令,全部通过 ziniao-cli api 直调。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 访问策略管理
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md)**
|
||||
|
||||
访问策略接口用于管理 ERP 网页访问权限,包括访问规则、网页配置、网页分组,以及账号与访问策略的绑定关系。
|
||||
|
||||
当前没有 `access-policy` 快捷命令,全部使用通用 `api` 命令直调。所有接口均为 `POST`、`application/json`,请求体需要显式传 `companyId`。
|
||||
|
||||
## 关键枚举
|
||||
|
||||
| 字段 | 值 | 说明 |
|
||||
|------|----|------|
|
||||
| `activeUser` | `1` | 除 boss 外所有成员 |
|
||||
| `activeUser` | `2` | 指定成员 |
|
||||
| `activeUser` | `3` | 指定部门 |
|
||||
| `activeUser` | `4` | 指定角色 |
|
||||
| `activeAccount` | `0` | 所有账号 |
|
||||
| `activeAccount` | `1` | 指定账号 |
|
||||
| `activeRange` | `1` | 所有网站 |
|
||||
| `activeRange` | `2` | 指定网址-黑名单 |
|
||||
| `activeRange` | `3` | 指定网址-白名单 |
|
||||
| `isActive` | `0` | 禁用 |
|
||||
| `isActive` | `1` | 启用 |
|
||||
|
||||
## 访问规则
|
||||
|
||||
| 操作 | API |
|
||||
|------|-----|
|
||||
| 规则列表 | `/superbrowser/rest/v1/erp/security/access_rule/list` |
|
||||
| 规则详情 | `/superbrowser/rest/v1/erp/security/access_rule/detail` |
|
||||
| 新建规则 | `/superbrowser/rest/v1/erp/security/access_rule/add` |
|
||||
| 编辑规则 | `/superbrowser/rest/v1/erp/security/access_rule/edit` |
|
||||
| 删除规则 | `/superbrowser/rest/v1/erp/security/access_rule/delete` |
|
||||
| 启用/禁用规则 | `/superbrowser/rest/v1/erp/security/access_rule/active` |
|
||||
| 设置生效成员 | `/superbrowser/rest/v1/erp/security/access_rule/change_user` |
|
||||
| 设置生效账号 | `/superbrowser/rest/v1/erp/security/access_rule/change_account` |
|
||||
| 规则可绑定账号列表 | `/superbrowser/rest/v1/erp/security/access_rule/account/bindable_list` |
|
||||
|
||||
```bash
|
||||
# 查询访问规则
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/list \
|
||||
--data '{"companyId":123,"page":1,"limit":20}'
|
||||
|
||||
# 查看规则详情
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/detail \
|
||||
--data '{"companyId":123,"ruleId":456}'
|
||||
|
||||
# 删除规则,ruleIds 是数组
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/delete \
|
||||
--data '{"companyId":123,"ruleIds":[456]}'
|
||||
|
||||
# 启用规则,禁用时 isActive 传 "0"
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/active \
|
||||
--data '{"companyId":123,"ruleIds":[456],"isActive":"1"}'
|
||||
```
|
||||
|
||||
设置生效成员接口无快捷命令,需通过 `api` 直调。请求体字段必须按接口文档发送:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `ruleId` | number | 规则 ID |
|
||||
| `activeUser` | number | `1` 除 boss 外所有成员;`2` 指定成员;`3` 指定部门;`4` 指定角色 |
|
||||
| `userChange.addIds` | string[] | 新增成员 ID 列表;无新增传 `[]` |
|
||||
| `userChange.addNames` | string[] | 新增成员用户名列表;无新增传 `[]` |
|
||||
| `userChange.deleteIds` | string[] | 删除成员 ID 列表;无删除传 `[]` |
|
||||
| `userChange.deleteNames` | string[] | 删除成员用户名列表;无删除传 `[]` |
|
||||
| `departmentIds` | number[] | 指定部门列表;非部门模式也传 `[]` |
|
||||
| `roleIds` | number[] | 指定角色列表;非角色模式也传 `[]` |
|
||||
|
||||
`addIds` 和 `addNames` 可任选一个传,两者都传时取并集;`deleteIds/deleteNames` 不能与新增列表重复。`companyId` 由 CLI 自动注入,不要在 `--data` 中手动传入公共参数。
|
||||
|
||||
```bash
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/change_user \
|
||||
--data '{"companyId":123,"ruleId":456,"activeUser":2,"userChange":{"addIds":["1001"],"deleteIds":[],"addNames":[],"deleteNames":[]},"departmentIds":[],"roleIds":[]}'
|
||||
```
|
||||
|
||||
设置生效账号时,`accountChange.addIds` 和 `accountChange.deleteIds` 传账号 ID 字符串数组。
|
||||
|
||||
```bash
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/change_account \
|
||||
--data '{"companyId":123,"ruleId":456,"activeAccount":1,"accountChange":{"addIds":["2001"],"deleteIds":[]}}'
|
||||
```
|
||||
|
||||
新建/编辑规则的请求体较复杂,核心结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"companyId": 123,
|
||||
"name": "限制文件下载",
|
||||
"desc": "运营角色适用",
|
||||
"activeUser": 2,
|
||||
"userChange": { "addIds": ["1001"], "addNames": [] },
|
||||
"departmentIds": [],
|
||||
"roleIds": [],
|
||||
"activeAccount": 0,
|
||||
"accountChange": { "addIds": [] },
|
||||
"activeRange": "3",
|
||||
"configs": [
|
||||
{
|
||||
"urlId": 789,
|
||||
"isAccessible": true,
|
||||
"isLog": true,
|
||||
"functions": [
|
||||
{ "function": "file_download", "permission": 3 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"isSkipRuleConflictCheck": false,
|
||||
"effectiveTime": { "timeType": "0", "startTime": 0, "endTime": 0 }
|
||||
}
|
||||
```
|
||||
|
||||
## 网页与网页分组
|
||||
|
||||
| 操作 | API |
|
||||
|------|-----|
|
||||
| 网页列表 | `/superbrowser/rest/v1/erp/security/access_rule_url/list` |
|
||||
| 网页详情 | `/superbrowser/rest/v1/erp/security/access_rule_url/detail` |
|
||||
| 添加网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/add` |
|
||||
| 编辑网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/edit` |
|
||||
| 删除网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/delete` |
|
||||
| 网页修改分组 | `/superbrowser/rest/v1/erp/security/access_rule_url/change_group` |
|
||||
| 网页分组列表 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/list` |
|
||||
| 添加网页分组 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/add` |
|
||||
| 编辑网页分组 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/edit` |
|
||||
| 删除网页分组 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/delete` |
|
||||
|
||||
```bash
|
||||
# 查询网页列表
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule_url/list \
|
||||
--data '{"companyId":123,"page":1,"limit":20}'
|
||||
|
||||
# 添加网页
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule_url/add \
|
||||
--data '{"companyId":123,"name":"Amazon后台","groupId":10,"urlPattern":"https://sellercentral.amazon.com/*","desc":"亚马逊后台"}'
|
||||
|
||||
# 移动网页到分组,urlIds 是数组
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule_url/change_group \
|
||||
--data '{"companyId":123,"groupId":10,"urlIds":[789]}'
|
||||
|
||||
# 查询分组树,isAll=true 时返回 children
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule_url_group/list \
|
||||
--data '{"companyId":123,"groupIdParent":0,"isAll":true}'
|
||||
```
|
||||
|
||||
## 账号绑定策略
|
||||
|
||||
| 操作 | API |
|
||||
|------|-----|
|
||||
| 账号绑定的访问策略列表 | `/superbrowser/rest/v1/erp/security/access_rule/list/one_account` |
|
||||
| 账号添加访问策略 | `/superbrowser/rest/v1/erp/security/access_rule/account_ref/add_to_account` |
|
||||
| 账号移除访问策略 | `/superbrowser/rest/v1/erp/security/access_rule/account_ref/remove_from_account` |
|
||||
|
||||
```bash
|
||||
# 查询某账号已绑定的策略
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/list/one_account \
|
||||
--data '{"companyId":123,"accountId":"2001","page":1,"limit":20}'
|
||||
|
||||
# 给账号添加访问策略
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/account_ref/add_to_account \
|
||||
--data '{"companyId":123,"accountId":"2001","ruleIds":[456],"isSkipRuleConflictCheck":false}'
|
||||
|
||||
# 从账号移除访问策略
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/security/access_rule/account_ref/remove_from_account \
|
||||
--data '{"companyId":123,"accountId":"2001","ruleIds":[456]}'
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- `ruleIds`、`urlIds`、`platformIds`、`tagIds`、`departmentIds`、`roleIds` 都是数组,单个值也要包装成数组。
|
||||
- 成员相关 `addIds/deleteIds/addNames/deleteNames` 是字符串数组;账号相关 `accountChange.addIds/deleteIds` 也是字符串数组。
|
||||
- 新建规则文档说明 `userChange.addIds` 和 `userChange.addNames` 可任选一个传,两者都传时取并集。
|
||||
- `configs.functions.permission` 常用值:`1` 允许、`2` 允许并记录、`3` 限制并记录、`6` 限制。`password`、`mouse_click`、`keyboard_input` 只支持 `1`、`6`。
|
||||
- `request_id` 会由 CLI 输出到 `meta.requestId`,排查接口问题时优先记录该值。
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
name: ziniao-account
|
||||
version: 1.1.0
|
||||
description: "紫鸟账号(店铺)管理:店铺账号的查询、创建、修改、删除、授权和标签管理。当用户需要查看店铺账号列表、创建/修改/删除账号、管理账号授权或标签时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 账号(店铺)管理
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md)**
|
||||
**CRITICAL — Shortcuts 执行前,务必先读取对应的 references/ 说明文档。**
|
||||
|
||||
紫鸟的"账号"指浏览器店铺账号(store),不是员工用户。本模块共覆盖 22 个接口,全部已有快捷命令。
|
||||
|
||||
> **注意区分:** `account` 命令通过**服务端 API** 管理店铺账号(创建、删除、授权、标签)。如需控制本地浏览器窗口(打开/关闭/导航/截图),请使用 `store` + `page` 命令(走本地 ZClaw Bridge)。
|
||||
|
||||
> **获取店铺列表的选择:**
|
||||
> - 成员账号没有服务端 `account list` 权限,应使用 `store list`(本地 ZClaw Bridge)获取店铺列表。
|
||||
> - Boss 账号如果目的是打开店铺浏览器,也应优先使用 `store list` 获取本地可用店铺,而非 `account list`。
|
||||
> - `account list` 适用于需要管理店铺元数据(标签、授权、批量操作)的场景。
|
||||
> - 成员类型可通过 `ziniao-cli config show` 结果中的 `isBoss` 字段判断。
|
||||
|
||||
## Shortcuts — 账号 CRUD
|
||||
|
||||
| 命令 | 说明 | 详细文档 |
|
||||
|------|------|---------|
|
||||
| `account list` | 查询账号列表 | [`references/ziniao-account-list.md`](references/ziniao-account-list.md) |
|
||||
| `account create` | 创建账号 | [`references/ziniao-account-create.md`](references/ziniao-account-create.md) |
|
||||
| `account update` | 编辑账号基础信息 | 只发修改字段:`--id xxx --name "新名"` |
|
||||
| `account delete` | 删除账号(高风险) | `--id xxx`,逗号分隔可批量,需 `--yes` |
|
||||
|
||||
```bash
|
||||
# 查询
|
||||
ziniao-cli account list --name "某店" --format table
|
||||
ziniao-cli account list --page-all
|
||||
|
||||
# 创建
|
||||
ziniao-cli account create --name "我的店铺" --site-id 1 --remark "测试"
|
||||
|
||||
# 修改
|
||||
ziniao-cli account update --id 123 --name "新名称" --site-id 1
|
||||
|
||||
# 删除(批量)
|
||||
ziniao-cli account delete --id "101,102,103" --yes
|
||||
```
|
||||
|
||||
## Shortcuts — 授权管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `account auth-add` | 授权员工访问店铺(`--store-id` 和 `--staff-id` 均支持逗号分隔批量) |
|
||||
| `account auth-remove` | 撤销员工访问权限(`--store-id` 和 `--staff-id` 均支持逗号分隔批量) |
|
||||
| `account auth-clean` | 清空店铺所有员工授权(高风险) |
|
||||
| `account auth-list` | 查询某员工被授权的店铺列表 |
|
||||
| `account auth-users` | 查询某店铺已授权的员工列表 |
|
||||
|
||||
```bash
|
||||
# 批量授权(把 3 个店铺同时授权给 3 个员工)
|
||||
ziniao-cli account auth-add --store-id "111,222,333" --staff-id "201,202,203"
|
||||
|
||||
# 撤销单人
|
||||
ziniao-cli account auth-remove --store-id 111 --staff-id 201
|
||||
|
||||
# 查看某员工被授权的所有店铺
|
||||
ziniao-cli account auth-list --user-id 201 --page-all --format table
|
||||
|
||||
# 清空授权(高风险)
|
||||
ziniao-cli account auth-clean --store-id 111 --yes
|
||||
```
|
||||
|
||||
## Shortcuts — 标签管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `account tag-list` | 查询企业下所有标签 |
|
||||
| `account tag-of-store --store-id` | 查询某店铺绑定的标签列表 |
|
||||
| `account tag-create --name` | 创建标签 |
|
||||
| `account tag-rename --id --name` | 重命名标签 |
|
||||
| `account tag-delete --id` | 删除标签(高风险) |
|
||||
| `account tag-bind --tag-id --store-id` | 绑定标签到店铺(`--store-id` 支持批量) |
|
||||
| `account tag-unbind --tag-id --store-id` | 解绑(`--store-id` 支持批量) |
|
||||
| `account tag-replace --store-id --tag-ids` | 全量替换店铺的标签集合 |
|
||||
| `account tag-clear --store-id` | 清空店铺所有标签(高风险) |
|
||||
| `account tag-remove --tag-id --store-id` | 从指定店铺移除该标签(高风险,`--store-id` 支持批量) |
|
||||
|
||||
底层接口与请求体字段必须严格按接口文档发送,不要补发未记录的别名字段。
|
||||
|
||||
| 命令 | API | 请求体字段(不含公共 `companyId`) |
|
||||
|------|-----|------------------------------------------|
|
||||
| `account tag-list` | `/superbrowser/rest/v1/erp/tag/list` | 无 |
|
||||
| `account tag-of-store --store-id` | `/superbrowser/rest/v1/store-tag/list` | `storeId`, 可选 `withSystem` |
|
||||
| `account tag-create --name` | `/superbrowser/rest/v1/store-tag/add` | `tagName` |
|
||||
| `account tag-rename --id --name` | `/superbrowser/rest/v1/store-tag/rename` | `tid`, `name` |
|
||||
| `account tag-delete --id` | `/superbrowser/rest/v1/store-tag/delete` | `tagIds` |
|
||||
| `account tag-bind --tag-id --store-id` | `/superbrowser/rest/v1/store-tag/bind` | `tagIds`, `storeIds` |
|
||||
| `account tag-unbind --tag-id --store-id` | `/superbrowser/rest/v1/store-tag/unbind` | `tagIds`, `storeIds` |
|
||||
| `account tag-replace --store-id --tag-ids` | `/superbrowser/rest/v1/store-tag/replace` | `tagIds`, `storeIds` |
|
||||
| `account tag-clear --store-id` | `/superbrowser/rest/v1/store-tag/clear` | `storeIds` |
|
||||
| `account tag-remove --tag-id --store-id` | `/superbrowser/rest/v1/store-tag/remove` | `tagIds`, `storeIds` |
|
||||
|
||||
```bash
|
||||
# 查看所有标签
|
||||
ziniao-cli account tag-list --format table
|
||||
|
||||
# 查看某店铺已有标签
|
||||
ziniao-cli account tag-of-store --store-id 101
|
||||
|
||||
# 创建标签后批量绑定到多个店铺
|
||||
ziniao-cli account tag-create --name "重点店铺"
|
||||
ziniao-cli account tag-bind --tag-id 99 --store-id "101,102,103"
|
||||
|
||||
# 全量替换(将店铺 101 的标签换成 99 和 100)
|
||||
ziniao-cli account tag-replace --store-id 101 --tag-ids "99,100"
|
||||
|
||||
# 从多个店铺移除某个标签
|
||||
ziniao-cli account tag-remove --tag-id 99 --store-id "101,102" --yes
|
||||
```
|
||||
|
||||
## 通用 api 覆盖(仅剩的接口)
|
||||
|
||||
```bash
|
||||
# 缓存清理(仅此接口无快捷命令)
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/store/deletecookie --data '{"removeStoreId":"xxx","removeType":1}'
|
||||
# removeType: 1=清除所有缓存, 2=仅保留 cookie, 3=仅保留二步验证状态
|
||||
|
||||
# 账号授权用户列表 / 用户有权限的账号列表
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/store/user/list --data '{"storeIdList":["xxx"],"page":1,"limit":10}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/user/stores --data '{"userId":"xxx","page":1,"limit":10}'
|
||||
```
|
||||
@@ -0,0 +1,43 @@
|
||||
# account create
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。
|
||||
|
||||
创建店铺账号。写入操作。CLI 将嵌套的 `storeData` 数组展平为命名 flag。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
ziniao-cli account create --name "新店铺"
|
||||
ziniao-cli account create --name "US Store" --site-id 123 --proxy-id 456
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--name` | 是 | 账号名称 |
|
||||
| `--site-id` | 否 | 平台 ID |
|
||||
| `--proxy-id` | 否 | 代理设备 ID |
|
||||
| `--group-id` | 否 | 分组 ID |
|
||||
| `--remark` | 否 | 备注 |
|
||||
|
||||
## 底层 API(快捷命令不可用时的兜底)
|
||||
|
||||
```bash
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/store/create \
|
||||
--data '{"storeData":[{"name":"新店铺","siteId":123}]}'
|
||||
```
|
||||
|
||||
**注意事项:**
|
||||
- `storeData` 必须是**数组**,即使只创建一个店铺
|
||||
- 字段名是 `name`(不是 `storeName`)
|
||||
- 在 **Windows Git Bash** 中,以 `/` 开头的路径会被自动转换为本地文件路径,需加环境变量:
|
||||
```bash
|
||||
MSYS_NO_PATHCONV=1 ziniao-cli api /superbrowser/rest/v1/erp/store/create \
|
||||
--data '{"storeData":[{"name":"新店铺"}]}'
|
||||
```
|
||||
- 在 **PowerShell** 或 **CMD** 中无此问题
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-account](../SKILL.md) — 账号管理全部命令
|
||||
@@ -0,0 +1,30 @@
|
||||
# account list
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。
|
||||
|
||||
查询账号列表。只读操作,支持分页和按名称搜索。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
ziniao-cli account list
|
||||
ziniao-cli account list --format table
|
||||
ziniao-cli account list --name "Rosehut"
|
||||
ziniao-cli account list --page-all --page-size 50
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--name` | 否 | 按名称搜索 |
|
||||
| `--page-size` | 否 | 每页条数(默认 20) |
|
||||
| `--page-all` | 否 | 自动翻页获取全部 |
|
||||
| `--page-limit` | 否 | 最大翻页数(默认 10,0 为不限) |
|
||||
| `--page-delay` | 否 | 翻页间隔毫秒数(默认 200) |
|
||||
| `--format` | 否 | json \| table \| csv |
|
||||
| `--jq` | 否 | jq 过滤表达式 |
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-account](../SKILL.md) — 账号管理全部命令
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: ziniao-department
|
||||
version: 1.0.0
|
||||
description: "紫鸟部门管理:部门的查询、创建、修改、删除和排序。当用户需要查看组织架构、创建/修改/删除部门、调整部门排序时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 部门管理
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md),其中包含认证、错误处理和安全规则**
|
||||
**CRITICAL — 所有 Shortcuts 在执行之前,务必先用 Read 工具读取其对应的 references/ 说明文档,禁止直接盲目调用命令。**
|
||||
|
||||
## 核心场景
|
||||
|
||||
### 查看组织架构
|
||||
|
||||
用户想了解公司部门结构时,使用 `--tree` 展示层级关系:
|
||||
|
||||
```bash
|
||||
ziniao-cli department list --tree
|
||||
```
|
||||
|
||||
输出树形结构:
|
||||
```
|
||||
├── 技术部 (id: 15868464646076)
|
||||
│ ├── 前端组 (id: 15954943062431)
|
||||
│ └── 后端组 (id: 15954943062432)
|
||||
└── 市场部 (id: 15868464646077)
|
||||
```
|
||||
|
||||
### 创建部门
|
||||
|
||||
`hierarchy` 由 CLI 自动推算(查父部门层级 +1),用户无需关心:
|
||||
|
||||
```bash
|
||||
# 在已有部门下创建子部门(parentId 必须为已存在的部门 ID,不支持传 0)
|
||||
ziniao-cli department create --name "华东区" --parent-id 15868464646076
|
||||
```
|
||||
|
||||
> ⚠ `--parent-id` 不能传 0,必须传已存在的父部门 ID。如需查询可用的父部门 ID,先执行 `department list --tree`。
|
||||
|
||||
### 危险操作
|
||||
|
||||
删除部门是 `high-risk-write`,会要求确认。**删除父部门会级联删除所有子部门,且不可恢复。** 删除前建议先检查是否有子部门或员工:
|
||||
|
||||
```bash
|
||||
ziniao-cli department list --tree # 检查子部门
|
||||
ziniao-cli staff list --department-id xxx # 检查是否有员工
|
||||
ziniao-cli department delete --id xxx # 执行删除(需确认)
|
||||
```
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| 命令 | 说明 | 详细文档 |
|
||||
|------|------|---------|
|
||||
| `department list` | 查询部门列表 | [`references/ziniao-department-list.md`](references/ziniao-department-list.md) |
|
||||
| `department create` | 新增部门 | [`references/ziniao-department-create.md`](references/ziniao-department-create.md) |
|
||||
| `department update` | 修改部门 | 只发修改字段:`--id xxx --name "新名"` |
|
||||
| `department delete` | 删除部门(子部门及员工归属将被清除) | `high-risk-write`,`--yes` 跳过确认 |
|
||||
| `department reorder` | 调整排序 | `--parent-id xxx --order "id1,id2,id3"` |
|
||||
|
||||
## 通用 api 覆盖
|
||||
|
||||
所有部门接口也可通过通用 api 命令调用:
|
||||
|
||||
```bash
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/department/list
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/department/add --data '{"name":"xxx","parentId":"<父部门ID>","hierarchy":"0"}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/department/update --data '{"id":"xxx","name":"新名"}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/department/delete --data '{"id":"xxx"}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/department/order --data '{"parentId":"xxx","order":["id1","id2"]}'
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
# department create
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md) 了解认证和安全规则。
|
||||
|
||||
新增部门。写入操作。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 创建顶级部门
|
||||
ziniao-cli department create --name "市场部"
|
||||
|
||||
# 创建子部门(hierarchy 自动推算)
|
||||
ziniao-cli department create --name "华东区" --parent-id 15868464646076
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--name` | 是 | 部门名称 |
|
||||
| `--parent-id` | 否 | 父部门 ID(默认 "0" 表示顶级部门) |
|
||||
|
||||
## 智能推算
|
||||
|
||||
- `hierarchy` 由 CLI 自动推算:无 `--parent-id` 则 `hierarchy=0`;有则查父部门层级 +1
|
||||
- 用户无需关心 `hierarchy` 参数
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-department](../SKILL.md) — 部门管理全部命令
|
||||
@@ -0,0 +1,54 @@
|
||||
# department list
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md) 了解认证和安全规则。
|
||||
|
||||
查询部门列表。只读操作。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 默认 JSON 输出
|
||||
ziniao-cli department list
|
||||
|
||||
# 树形展示
|
||||
ziniao-cli department list --tree
|
||||
|
||||
# 表格输出
|
||||
ziniao-cli department list --format table
|
||||
|
||||
# jq 过滤顶级部门
|
||||
ziniao-cli department list --jq '.[] | select(.parentId == 0)'
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--tree` | 否 | 以树形结构展示部门层级(按 parentId 关系渲染) |
|
||||
| `--format` | 否 | 输出格式:json(默认)\| table \| csv |
|
||||
| `--jq` / `-q` | 否 | jq 过滤表达式 |
|
||||
|
||||
## 输出字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | 部门 ID |
|
||||
| `name` | 部门名称 |
|
||||
| `parentId` | 父部门 ID(0 表示顶级) |
|
||||
| `hierarchy` | 层级深度 |
|
||||
|
||||
## 树形输出格式
|
||||
|
||||
当使用 `--tree` 时,按层级渲染:
|
||||
|
||||
```
|
||||
├── 部门名 (id: 123)
|
||||
│ ├── 子部门 (id: 456)
|
||||
│ └── 子部门 (id: 789)
|
||||
└── 其他部门 (id: 101)
|
||||
```
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-department](../SKILL.md) — 部门管理全部命令
|
||||
- [ziniao-shared](../../ziniao-shared/SKILL.md) — 认证和全局参数
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
name: ziniao-device
|
||||
version: 1.1.0
|
||||
description: "紫鸟设备管理:代理设备(IP)的查询、添加、修改、绑定/解绑、购买续费和自动续费。当用户需要查看代理设备列表、添加/修改自有代理、绑定设备到店铺或管理设备费用时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 设备管理
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md)**
|
||||
|
||||
管理紫鸟浏览器使用的代理设备(IP)。常用 9 个接口已有快捷命令覆盖;价格查询、历史绑定记录使用通用 `api` 命令。
|
||||
|
||||
## 代理类型映射
|
||||
|
||||
| CLI 值 | API 值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| http | 0 | HTTP 代理 |
|
||||
| https | 1 | HTTPS 代理 |
|
||||
| socks5 | 2 | SOCKS5 代理 |
|
||||
| ssh | 3 | SSH 隧道 |
|
||||
| ssl | 4 | SSL 代理 |
|
||||
|
||||
## Shortcuts — 查询与自有设备
|
||||
|
||||
| 命令 | 说明 | 详细文档 |
|
||||
|------|------|---------|
|
||||
| `device list` | 查询设备列表 | [`references/ziniao-device-list.md`](references/ziniao-device-list.md) |
|
||||
| `device add-custom` | 添加自有设备 | [`references/ziniao-device-add-custom.md`](references/ziniao-device-add-custom.md) |
|
||||
| `device update-custom` | 修改自有设备 | 部分更新:`--id xxx --addr "1.2.3.4"` |
|
||||
|
||||
```bash
|
||||
ziniao-cli device list --format table
|
||||
ziniao-cli device list --page-all
|
||||
|
||||
ziniao-cli device add-custom --name "我的代理" --proxy-type socks5 \
|
||||
--addr "1.2.3.4" --port 1080 --proxy-user admin --proxy-pass secret
|
||||
|
||||
ziniao-cli device update-custom --id 123 --addr "5.6.7.8"
|
||||
```
|
||||
|
||||
底层接口:设备列表 `/superbrowser/rest/v1/erp/ip/page`;添加自有设备 `/superbrowser/rest/v1/erp/ip/self/add/new`;修改自有设备 `/superbrowser/rest/v1/erp/ip/self/edit/new`。
|
||||
|
||||
## Shortcuts — 绑定管理
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `device bind --id --store-id` | 将设备绑定到店铺(`--store-id` 支持批量) |
|
||||
| `device unbind --store-id` | 按店铺 ID 解绑设备(`--store-id` 支持批量) |
|
||||
|
||||
```bash
|
||||
# 将设备 10 绑定到三个店铺
|
||||
ziniao-cli device bind --id 10 --store-id "101,102,103"
|
||||
|
||||
# 批量解绑
|
||||
ziniao-cli device unbind --store-id "101,102,103"
|
||||
```
|
||||
|
||||
> ⚠ 批量解绑为逐个执行,部分失败不影响已成功项,也不会回滚。
|
||||
|
||||
底层接口:绑定 `/superbrowser/rest/v1/erp/ip/bind`;解绑 `/superbrowser/rest/v1/erp/ip/unbind`。
|
||||
|
||||
## Shortcuts — 费用相关(涉及真实扣费)
|
||||
|
||||
> ⚠ `purchase` 和 `renew` 为 `high-risk-write`,执行前会要求确认或需传 `--yes`。
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `device plan-list` | 查询可购买的套餐列表(先看这里) |
|
||||
| `device purchase --plan-id --period-id --count` | 购买套餐(涉及费用,不可撤销) |
|
||||
| `device renew --id --period-id` | 续费设备(`--id` 支持批量,涉及费用) |
|
||||
| `device auto-renew --id --enable` | 批量开启/关闭自动续费 |
|
||||
|
||||
```bash
|
||||
# 先查套餐,再购买
|
||||
ziniao-cli device plan-list --format table
|
||||
ziniao-cli device purchase --plan-id "plan_xxx" --period-id 1 --count 3
|
||||
|
||||
# 续费单台
|
||||
ziniao-cli device renew --id 10 --period-id 1
|
||||
|
||||
# 批量开启自动续费
|
||||
ziniao-cli device auto-renew --id "10,11,12" --enable true
|
||||
|
||||
# 关闭自动续费(bool 类型必须用等号连接)
|
||||
ziniao-cli device auto-renew --id "10,11,12" --enable=false
|
||||
|
||||
# 关闭自动续费
|
||||
ziniao-cli device auto-renew --id 10 --enable=false
|
||||
```
|
||||
|
||||
底层接口:套餐 `/superbrowser/rest/v1/erp/package/new`;购买 `/superbrowser/rest/v1/erp/purchase`;续费 `/superbrowser/rest/v1/erp/renew`;自动续费 `/superbrowser/rest/v1/erp/ip/renewal`。
|
||||
|
||||
## 通用 api 覆盖
|
||||
|
||||
```bash
|
||||
# 查询已购设备价格
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/proxy/purchased_package_info --data '{"proxyId":"123"}'
|
||||
|
||||
# 查询设备历史绑定记录
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/ip/historybind --data '{"proxyId":"123"}'
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
# device add-custom
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。
|
||||
|
||||
添加自有代理设备。CLI 将嵌套的 `proxy` 对象展平为命名 flag。
|
||||
|
||||
底层 API:`/superbrowser/rest/v1/erp/ip/self/add/new`
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 基础用法
|
||||
ziniao-cli device add-custom \
|
||||
--name "美国代理1" \
|
||||
--proxy-type socks5 \
|
||||
--addr "1.2.3.4" \
|
||||
--port 1080
|
||||
|
||||
# 带认证
|
||||
ziniao-cli device add-custom \
|
||||
--name "美国代理1" \
|
||||
--proxy-type socks5 \
|
||||
--addr "1.2.3.4" \
|
||||
--port 1080 \
|
||||
--proxy-user admin \
|
||||
--proxy-pass secret
|
||||
|
||||
# 动态 IP
|
||||
ziniao-cli device add-custom \
|
||||
--name "动态代理" \
|
||||
--proxy-type http \
|
||||
--addr "proxy.example.com" \
|
||||
--port 8080 \
|
||||
--dynamic
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--name` | 是 | 设备名称 |
|
||||
| `--proxy-type` | 是 | http \| https \| socks5 \| ssh \| ssl |
|
||||
| `--addr` | 是 | 代理地址 |
|
||||
| `--port` | 是 | 代理端口 |
|
||||
| `--proxy-user` | 否 | 代理用户名 |
|
||||
| `--proxy-pass` | 否 | 代理密码 |
|
||||
| `--dynamic` | 否 | 是否动态 IP |
|
||||
| `--defy-warning` | 否 | 是否无视风险导入:1=是,0=否,默认 1 |
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-device](../SKILL.md) — 设备管理全部命令
|
||||
@@ -0,0 +1,29 @@
|
||||
# device list
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。
|
||||
|
||||
查询设备列表。只读操作。
|
||||
|
||||
底层 API:`/superbrowser/rest/v1/erp/ip/page`
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
ziniao-cli device list
|
||||
ziniao-cli device list --format table
|
||||
ziniao-cli device list --page-all
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--page-size` | 否 | 每页条数(默认 20) |
|
||||
| `--page-all` | 否 | 自动翻页获取全部 |
|
||||
| `--page-limit` | 否 | 最大翻页数(默认 10,0 为不限) |
|
||||
| `--page-delay` | 否 | 翻页间隔毫秒数(默认 200) |
|
||||
| `--format` | 否 | json \| table \| csv |
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-device](../SKILL.md) — 设备管理全部命令
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
name: ziniao-openapi-explorer
|
||||
version: 1.1.0
|
||||
description: "紫鸟 API 探索:当现有快捷命令无法满足需求时,通过通用 api 命令和已知路径清单探索紫鸟全部 73 个 API 接口。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# API 探索
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md)。
|
||||
|
||||
当用户的需求**无法被现有快捷命令覆盖**时,使用本技能查找对应的 API 路径并通过 `ziniao-cli api` 裸调。
|
||||
|
||||
## 探索流程
|
||||
|
||||
### Step 1: 确认现有命令不足
|
||||
|
||||
```bash
|
||||
ziniao-cli department --help
|
||||
ziniao-cli staff --help
|
||||
ziniao-cli account --help
|
||||
ziniao-cli device --help
|
||||
ziniao-cli role --help
|
||||
```
|
||||
|
||||
如果已有对应快捷命令,直接使用。
|
||||
|
||||
### Step 2: 从路径清单定位 API
|
||||
|
||||
**禁止猜测 API 路径**,必须从以下清单中查找。
|
||||
|
||||
#### 部门(5 个)✅ 全部已有快捷命令
|
||||
|
||||
| 操作 | 路径 | 快捷命令 |
|
||||
|------|------|---------|
|
||||
| 列表 | `/superbrowser/rest/v1/erp/department/list` | `department list` |
|
||||
| 新增 | `/superbrowser/rest/v1/erp/department/add` | `department create` |
|
||||
| 修改 | `/superbrowser/rest/v1/erp/department/update` | `department update` |
|
||||
| 删除 | `/superbrowser/rest/v1/erp/department/delete` | `department delete` |
|
||||
| 排序 | `/superbrowser/rest/v1/erp/department/order` | `department reorder` |
|
||||
|
||||
#### 员工(5 个)✅ 全部已有快捷命令
|
||||
|
||||
| 操作 | 路径 | 快捷命令 |
|
||||
|------|------|---------|
|
||||
| 列表 | `/superbrowser/rest/v1/erp/staff/list` | `staff list` |
|
||||
| 新增 | `/superbrowser/rest/v2/erp/staff/add` | `staff create` |
|
||||
| 修改 | `/superbrowser/rest/v2/erp/staff/modify` | `staff update` |
|
||||
| 状态 | `/superbrowser/rest/v1/erp/staff/status` | `staff enable/disable/remove` |
|
||||
| 调岗 | `/superbrowser/rest/v1/erp/staff/department` | `staff transfer` |
|
||||
|
||||
#### 账号(23 个)— 仍有 4 个需用 `api`
|
||||
|
||||
| 分类 | 路径前缀 | 快捷命令 |
|
||||
|------|---------|---------|
|
||||
| CRUD | `/superbrowser/rest/v1/erp/store/list\|create\|update/base\|delete` | `account list/create/update/delete` |
|
||||
| 缓存 | `/superbrowser/rest/v1/erp/store/deletecookie` | `api` |
|
||||
| 授权 | `/superbrowser/rest/v1/erp/store/auth/add\|delete\|clean`、`/superbrowser/rest/v1/erp/user/stores`、`/superbrowser/rest/v1/erp/store/user/list` | `account auth-*` |
|
||||
| 授权查询 | `/superbrowser/rest/v1/erp/store/auth/query` | `api` |
|
||||
| 用户账号 | `/superbrowser/rest/v1/erp/store/list/by/user` | `api` |
|
||||
| 附加信息 | `/superbrowser/rest/v1/erp/store/addtion` | `api` |
|
||||
| 标签 | `/superbrowser/rest/v1/erp/tag/list`、`/superbrowser/rest/v1/store-tag/add\|delete\|rename\|bind\|unbind\|replace\|clear\|remove\|list` | `account tag-*` |
|
||||
|
||||
#### 设备(11 个)— 仍有 2 个需用 `api`
|
||||
|
||||
| 分类 | 路径前缀 | 快捷命令 |
|
||||
|------|---------|---------|
|
||||
| 查询 | `/superbrowser/rest/v1/erp/ip/page`、`/superbrowser/rest/v1/erp/proxy/purchased_package_info`、`/superbrowser/rest/v1/erp/ip/historybind` | `device list`(price/history 用 `api`)|
|
||||
| 套餐 | `/superbrowser/rest/v1/erp/package/new` | `device plan-list` |
|
||||
| 购买 | `/superbrowser/rest/v1/erp/purchase\|renew` | `device purchase/renew` |
|
||||
| 自有 | `/superbrowser/rest/v1/erp/ip/self/add/new\|edit/new` | `device add-custom/update-custom` |
|
||||
| 绑定 | `/superbrowser/rest/v1/erp/ip/bind\|unbind\|renewal` | `device bind/unbind/auto-renew` |
|
||||
|
||||
#### 角色(7 个)✅ 全部已有快捷命令
|
||||
|
||||
| 操作 | 路径 | 快捷命令 |
|
||||
|------|------|---------|
|
||||
| 列表 | `/superbrowser/rest/v1/erp/per/role/list` | `role list` |
|
||||
| 详情 | `/superbrowser/rest/v1/erp/per/role/detail` | `role detail` |
|
||||
| 新增 | `/superbrowser/rest/v1/erp/per/role/add` | `role create` |
|
||||
| 修改 | `/superbrowser/rest/v1/erp/per/role/edit` | `role update` |
|
||||
| 分配 | `/superbrowser/rest/v1/erp/staff/change/role` | `role assign` |
|
||||
| 用户角色 | `/superbrowser/rest/v1/erp/staff/role/list` | `role staff-roles` |
|
||||
| 权限列表 | `/superbrowser/rest/v1/erp/per/permission/list` | `role permissions` |
|
||||
|
||||
#### 访问策略(22 个)— 全部需用 `api`
|
||||
|
||||
| 分类 | 路径 |
|
||||
|------|------|
|
||||
| 规则 | `/superbrowser/rest/v1/erp/security/access_rule/list` |
|
||||
| 规则 | `/superbrowser/rest/v1/erp/security/access_rule/detail` |
|
||||
| 规则 | `/superbrowser/rest/v1/erp/security/access_rule/add` |
|
||||
| 规则 | `/superbrowser/rest/v1/erp/security/access_rule/edit` |
|
||||
| 规则 | `/superbrowser/rest/v1/erp/security/access_rule/delete` |
|
||||
| 规则 | `/superbrowser/rest/v1/erp/security/access_rule/active` |
|
||||
| 规则成员 | `/superbrowser/rest/v1/erp/security/access_rule/change_user` |
|
||||
| 规则账号 | `/superbrowser/rest/v1/erp/security/access_rule/change_account` |
|
||||
| 规则账号 | `/superbrowser/rest/v1/erp/security/access_rule/account/bindable_list` |
|
||||
| 网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/list` |
|
||||
| 网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/detail` |
|
||||
| 网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/add` |
|
||||
| 网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/edit` |
|
||||
| 网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/delete` |
|
||||
| 网页 | `/superbrowser/rest/v1/erp/security/access_rule_url/change_group` |
|
||||
| 网页分组 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/list` |
|
||||
| 网页分组 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/add` |
|
||||
| 网页分组 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/edit` |
|
||||
| 网页分组 | `/superbrowser/rest/v1/erp/security/access_rule_url_group/delete` |
|
||||
| 账号策略 | `/superbrowser/rest/v1/erp/security/access_rule/list/one_account` |
|
||||
| 账号策略 | `/superbrowser/rest/v1/erp/security/access_rule/account_ref/add_to_account` |
|
||||
| 账号策略 | `/superbrowser/rest/v1/erp/security/access_rule/account_ref/remove_from_account` |
|
||||
|
||||
### Step 3: 使用 dry-run 探索
|
||||
|
||||
```bash
|
||||
# 先预览请求
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/role/list --dry-run
|
||||
|
||||
# 确认后执行
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/role/list
|
||||
```
|
||||
|
||||
## 安全规则
|
||||
|
||||
- **禁止猜测** API 路径或参数,必须从清单中查找
|
||||
- 写入/删除类操作前确认用户意图
|
||||
- 建议先用 `--dry-run` 预览
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-shared](../ziniao-shared/SKILL.md) — 认证和全局参数
|
||||
- [ziniao-account](../ziniao-account/SKILL.md) — 账号/授权/标签快捷命令
|
||||
- [ziniao-device](../ziniao-device/SKILL.md) — 设备管理快捷命令
|
||||
- [ziniao-role](../ziniao-role/SKILL.md) — 角色管理快捷命令
|
||||
- [ziniao-skill-maker](../ziniao-skill-maker/SKILL.md) — 如需将常用 API 固化为新 Skill
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
name: ziniao-page
|
||||
description: "Use when a request concerns a webpage running inside an already-opened 紫鸟/ZClaw store browser, including navigation, inspection, interaction, extraction, or screenshots. If the task starts by identifying or opening a store, use ziniao-store first."
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 紫鸟页面能力
|
||||
|
||||
本技能负责已打开紫鸟店铺浏览器中的通用网页资源操作,不负责店铺生命周期,也不定义用户的业务流程。
|
||||
|
||||
## 负责范围
|
||||
|
||||
- 对当前店铺浏览器中的网页进行通用操作或读取。
|
||||
- 在页面操作中复用已确认的 `store-id`,必要时指定 `target-id`。
|
||||
- 根据页面状态选择 CLI 已提供的页面命令;参数以 `ziniao-cli page <command> --help` 为准。
|
||||
- 页面操作需要复杂编排时,按需读取 automation reference。
|
||||
|
||||
## 路由边界
|
||||
|
||||
- 已有店铺浏览器并且目标是网页资源:直接使用本技能。
|
||||
- 任务尚未确定或打开店铺:转到 `ziniao-store`,不要自行猜测店铺 ID。
|
||||
- 不要把用户的业务目标或任务参数写入技能;这些是用户任务,由模型组合通用页面能力完成。
|
||||
- 配置、认证、Bridge、客户端或网络异常:按需读取 `ziniao-shared` 的诊断规则,不在本技能重复维护环境修复流程。
|
||||
|
||||
## 最小执行规则
|
||||
|
||||
1. 确认店铺已打开,并取得有效 `store-id`;页面命令通常都需要它。
|
||||
2. 先确认当前页面状态,再执行必要的页面操作;页面变化后不要复用失效的目标或定位信息。
|
||||
3. 对导航、等待、读取、交互、提取和截图等能力,直接使用 CLI 的对应命令;不确定参数时先看 `--help`。
|
||||
4. 涉及附件或图片上传时,在下载、路径转换或上传调用前,必须完整读取 [page upload 安全规则](references/ziniao-page-upload.md);未读取不得执行上传。
|
||||
5. 只在当前任务需要时读取 reference,不要默认读取全部页面文档。
|
||||
6. 报错时区分页面/目标定位问题和环境问题;环境问题再转 `ziniao-shared`/`doctor`。
|
||||
|
||||
## 页面读取与定位顺序
|
||||
|
||||
1. 页面状态、`target-id` 和稳定 CSS selector 已知时,直接复用并执行必要操作;不要为每一步重复读取页面。
|
||||
2. 需要理解页面或验证导航、搜索、提交结果时,优先使用 `page content --content-format structured`;只需 URL/title 时也可使用 `page exec`。
|
||||
3. selector 未知时,先使用 `page content`、`page query` 或 `page exec` 分析 DOM,再使用 `page input` / `page click --selector`。
|
||||
4. 只有上传控件、自定义或图标控件、多个相似控件存在歧义,或上述 DOM 方法仍无法唯一定位时,才使用 `page snapshot` 和当前页面 ref。
|
||||
|
||||
`page snapshot` 不是通用页面状态检查命令。不得为了验证店铺窗口、读取 URL/title/`target-id`、操作已知 selector、普通搜索或结果验证而调用 snapshot;页面未变化时也不得重复创建 snapshot/ref。snapshot ref 只属于当前店铺、tab 和页面,页面导航或刷新后不得复用。
|
||||
|
||||
输入后提交只能选择一种方式:使用 `page input --submit` 后等待并验证结果,或不传 `--submit` 再执行提交按钮的 `page click --wait-nav`;不得重复提交。
|
||||
|
||||
## 按需参考
|
||||
|
||||
- [page visit](references/ziniao-page-visit.md):需要导航时读取。
|
||||
- [page screenshot](references/ziniao-page-screenshot.md):需要截图时读取。
|
||||
- [page upload 安全规则](references/ziniao-page-upload.md):涉及附件、图片、文件输入框或 `page upload` 时必须读取。
|
||||
- [automation run](references/ziniao-page-automation.md):需要多步骤自动化时读取。
|
||||
- `ziniao-store`:需要确定或打开店铺时使用。
|
||||
- `ziniao-shared`:仅在环境、配置、认证或 Bridge 诊断时读取。
|
||||
@@ -0,0 +1,132 @@
|
||||
# automation run
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。紫鸟浏览器必须已启动,且店铺已打开。
|
||||
|
||||
执行多步骤自动化流程。将多个页面操作编排为一条命令执行,减少多次调用的开销。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
ziniao-cli automation run --steps '[
|
||||
{"type": "visit", "url": "https://example.com", "waitUntil": "networkidle"},
|
||||
{"type": "wait", "selector": "#login"},
|
||||
{"type": "click", "selector": "#login"},
|
||||
{"type": "input", "selector": "#username", "text": "admin", "clear": true},
|
||||
{"type": "input", "selector": "#password", "text": "pass123", "clear": true},
|
||||
{"type": "click", "selector": "#submit", "waitForNavigation": true},
|
||||
{"type": "screenshot", "fullPage": true, "path": "./result.png"}
|
||||
]'
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--steps` | 是 | 步骤 JSON 数组 |
|
||||
|
||||
## 步骤类型及字段
|
||||
|
||||
### visit — 导航到 URL
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `url` | 是 | 目标 URL |
|
||||
| `waitUntil` | 否 | 等待条件: domcontentloaded / load / networkidle |
|
||||
| `timeoutMs` | 否 | 超时毫秒数 |
|
||||
|
||||
### click — 点击元素
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `selector` | 是 | CSS 选择器 |
|
||||
| `waitForNavigation` | 否 | 点击后等待导航完成 |
|
||||
| `timeoutMs` | 否 | 超时毫秒数 |
|
||||
|
||||
### input — 输入文本
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `selector` | 是 | CSS 选择器 |
|
||||
| `text` | 是 | 要输入的文本 |
|
||||
| `clear` | 否 | 输入前清空已有内容 |
|
||||
| `submit` | 否 | 输入后提交表单 |
|
||||
| `timeoutMs` | 否 | 超时毫秒数 |
|
||||
|
||||
### scroll — 滚动页面
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `x` | 否 | 水平滚动像素 |
|
||||
| `y` | 否 | 垂直滚动像素 |
|
||||
| `selector` | 否 | 滚动目标元素选择器 |
|
||||
| `behavior` | 否 | 滚动行为: auto / smooth |
|
||||
|
||||
### screenshot — 截图
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `fullPage` | 否 | 全页截图 |
|
||||
| `path` | 否 | 保存路径 |
|
||||
|
||||
### wait — 等待
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `selector` | 否* | 等待元素出现(与 timeout 二选一) |
|
||||
| `timeout` | 否* | 延时毫秒数(与 selector 二选一) |
|
||||
| `timeoutMs` | 否 | 等待超时上限 |
|
||||
|
||||
### exec — 执行 JavaScript
|
||||
|
||||
| 字段 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `script` | 是 | JavaScript 代码 |
|
||||
| `timeoutMs` | 否 | 超时毫秒数 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 登录流程
|
||||
|
||||
```bash
|
||||
ziniao-cli automation run --steps '[
|
||||
{"type": "visit", "url": "https://sellercentral.amazon.com", "waitUntil": "networkidle"},
|
||||
{"type": "input", "selector": "#ap_email", "text": "seller@example.com", "clear": true},
|
||||
{"type": "input", "selector": "#ap_password", "text": "password123", "clear": true},
|
||||
{"type": "click", "selector": "#signInSubmit", "waitForNavigation": true},
|
||||
{"type": "screenshot", "fullPage": true}
|
||||
]'
|
||||
```
|
||||
|
||||
### 滚动加载 + 截图
|
||||
|
||||
```bash
|
||||
ziniao-cli automation run --steps '[
|
||||
{"type": "visit", "url": "https://www.amazon.com/dp/B0XXXXX", "waitUntil": "networkidle"},
|
||||
{"type": "scroll", "y": 500, "behavior": "smooth"},
|
||||
{"type": "wait", "timeout": 1000},
|
||||
{"type": "scroll", "y": 1000, "behavior": "smooth"},
|
||||
{"type": "wait", "timeout": 1000},
|
||||
{"type": "screenshot", "fullPage": true, "path": "./listing-full.png"}
|
||||
]'
|
||||
```
|
||||
|
||||
### 提取数据后截图确认
|
||||
|
||||
```bash
|
||||
ziniao-cli automation run --steps '[
|
||||
{"type": "visit", "url": "https://example.com/dashboard", "waitUntil": "networkidle"},
|
||||
{"type": "wait", "selector": ".data-table"},
|
||||
{"type": "exec", "script": "document.querySelector('.data-table').style.border = '2px solid red'"},
|
||||
{"type": "screenshot", "path": "./dashboard.png"}
|
||||
]'
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- steps 必须为合法 JSON 数组,建议用单引号包裹避免转义问题
|
||||
- 步骤按顺序执行,任何一步失败会终止后续步骤
|
||||
- 复杂的条件逻辑(if/else)不适合用 automation run,应拆分为多条单独命令由 Agent 控制
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-page](../SKILL.md) — 页面操作全部命令
|
||||
@@ -0,0 +1,35 @@
|
||||
# page screenshot
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。店铺必须已打开。
|
||||
|
||||
对当前页面截图。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 视口截图
|
||||
ziniao-cli page screenshot --store-id abc123
|
||||
|
||||
# 全页截图
|
||||
ziniao-cli page screenshot --store-id abc123 --full-page
|
||||
|
||||
# 保存到指定路径
|
||||
ziniao-cli page screenshot --store-id abc123 --path "./output.png"
|
||||
|
||||
# 指定超时
|
||||
ziniao-cli page screenshot --store-id abc123 --timeout 10000
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--store-id` | 是 | 店铺 ID |
|
||||
| `--full-page` | 否 | 全页截图(默认仅视口) |
|
||||
| `--path` | 否 | 保存路径 |
|
||||
| `--timeout` | 否 | 超时毫秒数 |
|
||||
| `--target-id` | 否 | 目标页面 ID(多 tab 时指定) |
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-page](../SKILL.md) — 页面操作全部命令
|
||||
@@ -0,0 +1,55 @@
|
||||
# page upload 安全规则
|
||||
|
||||
页面附件或图片上传属于写操作。执行任何下载、路径转换、文件选择或上传调用前,先完成来源、页面目标和用户确认检查。
|
||||
|
||||
## 来源边界
|
||||
|
||||
- `page upload` 只接受用户提供的本地文件路径。
|
||||
- 若附件来源是 `http://` 或 `https://` URL,立即停止上传并原样回复:
|
||||
|
||||
> 暂不支持从 URL 上传附件,请提供本地文件路径。
|
||||
|
||||
- 不得先下载、复制、缓存或转换 URL 内容,再把生成的本地文件当作可信来源上传。用户要求跳过该限制也不能改变来源边界。
|
||||
- 不得把导航 URL、页面链接或下载链接解释为附件路径。
|
||||
|
||||
## 页面与控件定位
|
||||
|
||||
1. 上传前确认当前 `store-id`、当前页面和唯一 `target-id`。多个可用标签页而用户未指定目标时,停止并让用户确认,不得任意选择。
|
||||
2. 只使用当前页面快照产生的 ref。页面 reload、URL 变化、标签页切换或 ref 失效后,重新执行 `page snapshot`。
|
||||
3. 上传控件不唯一时,根据当前快照的可访问名称和上下文,通过 `--trigger-ref` 或 `--hint` 消歧;不要先单独点击上传入口,也不要猜测泛选择器。
|
||||
|
||||
错误处理:
|
||||
|
||||
- `PAGE_TARGET_AMBIGUOUS`:重新获取当前页面快照并确定唯一 `target-id`;不得任意选择其他标签页。
|
||||
- `PAGE_TARGET_NOT_FOUND`:原目标已失效;重新获取当前店铺快照,不得回退到其他标签页。
|
||||
- `UPLOAD_CONTROL_NOT_DISCOVERED`:重新快照检查动态上传入口;仍未发现时停止并报告。
|
||||
- `UPLOAD_CONTROL_AMBIGUOUS`:使用当前快照的 `--trigger-ref` 或 `--hint` 消歧;仍无法唯一确认时停止并让用户选择。
|
||||
|
||||
## 外部本地路径确认
|
||||
|
||||
当下载目录外的本地路径返回 `confirmationRequired` 时:
|
||||
|
||||
本轮最终用户可见回复必须是一个不可拆分的确认包,并按以下顺序同时包含:
|
||||
|
||||
1. 原样输出完整 `confirmationMessage`,不得摘要、改写或删除字段。
|
||||
2. 紧接着实际显示 `screenshotFilePath` 指向的截图。
|
||||
3. 明确说明文件尚未上传。
|
||||
4. 最后等待用户按照 `confirmationMessage` 的要求明确确认。
|
||||
|
||||
仅调用图片读取工具、仅在中间进度消息或折叠区域显示图片、或只输出截图路径,均不算已经向用户展示截图。结束本轮前必须确认最终回复中同时可见完整确认文字和实际截图。
|
||||
|
||||
截图无法读取或无法在最终回复中展示时,本轮不得索要确认,也不得重试上传。用户明确确认后,只以相同的 `store-id`、`target-id`、文件来源、目标控件和上传意图,追加原响应的 `--confirmation-token <token>` 重试一次。不得换标签页、换文件、换控件或改写来源。先前的笼统授权或“无需再确认”不能替代本次确认。
|
||||
|
||||
页面、店铺会话或任一上传参数发生变化时,原 token 不再用于新的上传意图;重新开始定位和确认流程。
|
||||
|
||||
## 结果判定与重试
|
||||
|
||||
上传命令的结果只描述文件控件状态,不能单独证明网站业务提交完成:
|
||||
|
||||
- `confirmationRequired=true` 或 `stage=confirmation_pending`:尚未获得上传授权;不得声称已上传。
|
||||
- `attached=true, stage=input_attached`:文件仍在网页文件输入控件中;继续根据页面可见文件名、缩略图、进度或错误提示验证。
|
||||
- `attached=true, stage=input_consumed`:网页组件已接收并消费或清空输入;仍需等待页面可见的处理结果。
|
||||
- `attached=false, stage=input_unverified`:CDP 已执行但页面是否接收无法确认;不得报告成功或失败,也不得自动重试。先通过新快照、截图、文件名、进度或错误提示确认;只有页面明确失败,或把本次不确定结果告知用户后用户再次明确要求时才重试。任务开始前的笼统授权不构成这次重试授权。
|
||||
- `UPLOAD_ATTACH_VERIFY_FAILED`:重新快照并检查页面可见状态;不得盲目重试或声称成功。
|
||||
|
||||
只有页面出现与目标文件对应的可见业务结果,才能报告网站侧上传完成。文件进入控件、命令退出码为 0、`attached=true` 或 `input_consumed` 都不是业务完成证明。
|
||||
@@ -0,0 +1,37 @@
|
||||
# page visit
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。店铺必须已打开。
|
||||
|
||||
导航到指定 URL。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
ziniao-cli page visit --store-id abc123 --url "https://www.amazon.com"
|
||||
|
||||
# 等待 networkidle 后再返回
|
||||
ziniao-cli page visit --store-id abc123 --url "https://www.amazon.com" --wait-until networkidle
|
||||
|
||||
# 指定超时
|
||||
ziniao-cli page visit --store-id abc123 --url "https://www.amazon.com" --timeout 60000
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--store-id` | 是 | 店铺 ID |
|
||||
| `--url` | 是 | 目标 URL |
|
||||
| `--wait-until` | 否 | 等待条件: domcontentloaded \| load \| networkidle |
|
||||
| `--timeout` | 否 | 超时毫秒数 |
|
||||
| `--target-id` | 否 | 目标页面 ID(多 tab 时指定) |
|
||||
|
||||
## 提示
|
||||
|
||||
- 导航后建议用 `page wait-nav` 等待页面加载完成
|
||||
- 可用 `page content` 获取页面内容确认是否到达目标页面
|
||||
- `--wait-until networkidle` 适合 SPA 页面,等待所有网络请求完成
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-page](../SKILL.md) — 页面操作全部命令
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: ziniao-role
|
||||
version: 1.0.0
|
||||
description: "紫鸟角色管理:角色的查询、创建、修改、分配和权限查看。当用户需要查看角色列表、创建/修改角色、将角色分配给员工或查看角色成员时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 角色管理
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md)**
|
||||
|
||||
管理紫鸟 ERP 中的角色(权限组)。角色决定员工能看到哪些功能、能操作哪些店铺。
|
||||
创建员工时需要传入 `--role-id`,可通过 `role list` 获取。
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `role list` | 查询所有角色 |
|
||||
| `role detail --id` | 查看角色详情(含权限列表) |
|
||||
| `role create --name --identity-id --permission-ids` | 创建角色 |
|
||||
| `role update --id --name --identity-id --permission-ids` | 修改角色(需全量提交,不传的字段可能被清空) |
|
||||
| `role assign --role-id --staff-id` | 将角色分配给员工(`--staff-id` 支持批量) |
|
||||
| `role staff-roles --staff-id` | 查看某员工已分配的角色列表 |
|
||||
| `role permissions --identity-id` | 查看指定身份的可用权限项 |
|
||||
|
||||
## 核心场景
|
||||
|
||||
### 查看角色与权限
|
||||
|
||||
```bash
|
||||
# 查询所有角色(先获取 role-id)
|
||||
ziniao-cli role list --format table
|
||||
|
||||
# 查看某角色详情
|
||||
ziniao-cli role detail --id 123
|
||||
|
||||
# 查看系统支持的全部权限项(创建角色前先查)
|
||||
ziniao-cli role permissions --identity-id 2
|
||||
```
|
||||
|
||||
### 创建与修改角色
|
||||
|
||||
```bash
|
||||
# 创建角色(权限使用权限 ID,多个权限用逗号分隔)
|
||||
ziniao-cli role create --name "店铺运营" --identity-id 2 --permission-ids "100,101" --desc "负责日常店铺操作"
|
||||
|
||||
# 修改角色(需全量提交,建议先 role detail 查看当前值)
|
||||
ziniao-cli role update --id 123 --name "高级运营" --identity-id 2 --permission-ids "100,101,102"
|
||||
```
|
||||
|
||||
### 分配角色给员工
|
||||
|
||||
```bash
|
||||
# 将角色分配给单个员工
|
||||
ziniao-cli role assign --role-id 123 --staff-id 456
|
||||
|
||||
# 批量分配给多人
|
||||
ziniao-cli role assign --role-id 123 --staff-id "456,789,101"
|
||||
|
||||
# 查看某员工已分配的角色
|
||||
ziniao-cli role staff-roles --staff-id 456 --format table
|
||||
```
|
||||
|
||||
### 典型工作流:新建角色并批量分配
|
||||
|
||||
```bash
|
||||
# 1. 查看系统已有角色,避免重复创建
|
||||
ziniao-cli role list --format table
|
||||
|
||||
# 2. 查看普通成员可用权限项
|
||||
ziniao-cli role permissions --identity-id 2 --jq '.[] | .id'
|
||||
|
||||
# 3. 创建新角色
|
||||
ziniao-cli role create --name "跨境运营" --identity-id 2 --permission-ids "100,101,102"
|
||||
|
||||
# 4. 获取新角色 ID(从上一步输出的 data.id 取)
|
||||
# 5. 批量分配给员工
|
||||
ziniao-cli role assign --role-id <new-id> --staff-id "201,202,203"
|
||||
```
|
||||
|
||||
## 通用 api 覆盖
|
||||
|
||||
```bash
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/role/list
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/role/detail --data '{"roleId":"xxx"}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/role/add --data '{"roleName":"xxx","identityId":2,"permissionIds":[100,101]}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/role/edit --data '{"roleId":"xxx","roleName":"yyy","identityId":2,"permissionIds":[100,101]}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/staff/change/role --data '{"roleId":"xxx","staffIds":["yyy"]}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/staff/role/list --data '{"staffId":"yyy"}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/permission/list --data '{"identityId":2}'
|
||||
```
|
||||
@@ -0,0 +1,401 @@
|
||||
---
|
||||
name: ziniao-shared
|
||||
version: 1.0.0
|
||||
description: "紫鸟 CLI 共享基础:应用配置初始化、ZClaw 客户端与 Bridge 就绪、统一 apiKey 认证、错误处理、输出格式和安全规则。当用户需要首次使用 ziniao-cli、配置 API Key、启动紫鸟客户端、登录 ZClaw 或排查认证/Bridge 问题时触发。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# ziniao-cli 共享规则
|
||||
|
||||
本技能指导你如何通过 ziniao-cli 操作紫鸟开放平台资源和控制紫鸟浏览器,以及有哪些注意事项。
|
||||
|
||||
## 配置初始化
|
||||
|
||||
首次使用需运行 `ziniao-cli config init` 完成应用配置。
|
||||
|
||||
### 初始化模式
|
||||
|
||||
| 命令 | 场景 | 行为 |
|
||||
|------|------|------|
|
||||
| `ziniao-cli config init --new` | **AI Agent(推荐)** | 直接进入新建应用流程,输出浏览器链接后轮询等待 |
|
||||
| `ziniao-cli config init` | 人类用户交互 | 菜单选择:[1] 新建应用 [2] 手动输入 Key |
|
||||
| `ziniao-cli config init --api-key-stdin` | CI/CD 管道 | 从 stdin 读取已有 API Key |
|
||||
| `ziniao-cli config init --api-key-stdin --member` | 成员账号 CI | 跳过企业信息获取,仅用于控制浏览器 |
|
||||
| `ziniao-cli config init --profile <name>` | 多账号 | 指定 profile 名称(不传则自动命名) |
|
||||
|
||||
### AI Agent 初始化流程
|
||||
|
||||
使用 background 方式执行以下命令,启动后读取 stderr 输出,从中提取浏览器链接并展示给用户:
|
||||
|
||||
```bash
|
||||
# 直接进入新建应用流程(该命令会阻塞直到审核通过、被拒绝或超时 1 小时)
|
||||
ziniao-cli config init --new
|
||||
```
|
||||
|
||||
输出示例(Boss 账号):
|
||||
```
|
||||
请在浏览器中打开以下链接完成应用创建:
|
||||
|
||||
https://open.ziniao.com/memberAuth?cliRequestId=a1b2c3d4-...&from=cli
|
||||
|
||||
⏳ 等待应用创建及审核... (按 Ctrl+C 取消)
|
||||
⏳ 等待中... 已等待 30s
|
||||
✓ 审核已通过
|
||||
⏳ 正在获取企业信息...
|
||||
✓ 企业 ID: 15393571083459
|
||||
✓ 配置已保存
|
||||
```
|
||||
|
||||
输出示例(成员账号):
|
||||
```
|
||||
...
|
||||
✓ 审核已通过
|
||||
✓ 成员账号,跳过企业信息获取
|
||||
✓ 配置已保存
|
||||
```
|
||||
|
||||
Agent 应该:
|
||||
1. 后台执行 `config init --new`
|
||||
2. 从输出中提取 URL(包含 `memberAuth?cliRequestId=` 的行)
|
||||
3. 将链接展示给用户,提示在浏览器中打开完成应用创建
|
||||
4. 等待命令完成(审核通过/拒绝/超时)
|
||||
5. 如果审核被拒绝,告知用户联系 Boss 审批
|
||||
|
||||
apiKey 加密后写入本地。沙箱/容器可直接使用,无需系统钥匙串。
|
||||
|
||||
### Boss 与成员账号
|
||||
|
||||
初始化时服务端返回 `isBoss` 标识,决定账号权限范围:
|
||||
|
||||
| 账号类型 | 服务端 API | ZClaw Bridge(本地浏览器) |
|
||||
|---------|-----------|--------------------------|
|
||||
| Boss | ✓ 全部可用 | ✓ 全部可用 |
|
||||
| 成员 | ✗ 不可用(返回 auth 错误) | ✓ 全部可用 |
|
||||
|
||||
服务端 API 权限仅 Boss 账号可申请和调用。成员账号不能开通这些权限点,也不存储 companyId;所有通过 `api` 命令或服务端快捷命令(account/staff/department/role/device)的请求会被拦截并提示"需要 Boss 权限"。
|
||||
|
||||
### 检查配置
|
||||
|
||||
```bash
|
||||
ziniao-cli config show # 查看当前配置(含 profile 名称)
|
||||
ziniao-cli config list # 列出所有 profile
|
||||
ziniao-cli doctor # 全面自检(配置 + apiKey + 网络 + ZClaw Bridge)
|
||||
```
|
||||
|
||||
## ZClaw 客户端与 Bridge 就绪
|
||||
|
||||
ZClaw 本地浏览器操作依赖紫鸟客户端和本地 Bridge。本次任务首次执行店铺或页面命令前,先运行以下命令检查环境:
|
||||
|
||||
### 登录命令硬约束
|
||||
|
||||
本节所有“登录”“自动登录”“先登录”都只表示逐字执行 `ziniao-cli zclaw login`。不存在可替代的 `ziniao-cli login`,也不得省略 `zclaw`。在输出或执行登录步骤前先核对完整命令。
|
||||
|
||||
```bash
|
||||
ziniao-cli doctor
|
||||
```
|
||||
|
||||
如果 `doctor` 各项明细确认配置、API Key、服务端连通性、Bridge 连通且客户端账号与当前 profile 一致,直接执行原店铺或页面命令;不要只依据最后的汇总文字。一次任务内已经检查通过且认证、profile 和客户端状态未变化时,不要为每个后续命令重复检查。
|
||||
|
||||
当 `doctor` 的客户端明细明确显示“紫鸟客户端已启动,但尚未登录”或 `[CLIENT_LOGGED_OUT]`,且 Bridge 已连通、没有 API Key、终端绑定或账号不一致错误时,这已经是确定的未登录状态。未登录恢复命令固定且只能是 `ziniao-cli zclaw login`;`ziniao-cli login` 不是有效命令,不得缩写或替换。必须在任何 `store`、`page` 业务命令之前按以下顺序执行:
|
||||
|
||||
```bash
|
||||
ziniao-cli zclaw login
|
||||
ziniao-cli doctor
|
||||
# 仅当 doctor 明细全部通过时,执行一次原业务命令
|
||||
```
|
||||
|
||||
禁止先调用 `store list`、`store resolve`、`store open` 或 `page` 命令来再次探测登录状态,即使 `doctor` 退出码为 0 或 Bridge 显示正常也不例外。登录失败或复查仍未通过时立即停止并报告实际错误。
|
||||
|
||||
仅当 `doctor` 的客户端检查明确显示“紫鸟客户端未启动或本地服务未就绪”时,才可尝试启动客户端。调用前按**当前 Shell**选择一种命令检查 `ziniao` 是否可解析,不得把某一种检测命令用于所有平台,也不得依次执行多种检测命令:
|
||||
|
||||
| 当前 Shell | 命令检测 |
|
||||
|------------|----------|
|
||||
| Windows PowerShell | `Get-Command ziniao -ErrorAction SilentlyContinue` |
|
||||
| Windows CMD | `where.exe ziniao` |
|
||||
| macOS/Linux Bash、Zsh、sh,或 Windows Git Bash | `command -v ziniao` |
|
||||
|
||||
若当前 Shell 找不到 `ziniao`,不得调用或重试 `ziniao start`。macOS 例外:如果 `/Applications/ziniao.app` 存在,可执行一次 `open -a "/Applications/ziniao.app"` 启动桌面客户端,然后重新运行 `ziniao-cli doctor`;不得把 `open` 当作 Bridge 就绪证明。其他平台提示用户手动启动紫鸟客户端,用户完成后重新运行 `ziniao-cli doctor`。只有找到 `ziniao` 命令时,才执行一次:
|
||||
|
||||
```bash
|
||||
ziniao start
|
||||
ziniao-cli doctor
|
||||
```
|
||||
|
||||
`ziniao start` 只用于启动或唤起单实例紫鸟客户端,不能修复 API Key、终端绑定、账号不一致或登录会话问题。Bridge 可能在客户端启动后才就绪;启动后可短间隔重新运行 `doctor`,总等待时间最多 30 秒。若 `ziniao start` 返回 Windows 错误 1113 或其他启动错误,或命令成功返回但 macOS 上 `doctor` 仍明确显示客户端未启动,可在 `/Applications/ziniao.app` 存在时额外执行一次 `open -a "/Applications/ziniao.app"`,随后重新运行 `doctor`;不得再次重试 `ziniao start`。仍未通过则报告启动错误和 doctor 明细后停止。
|
||||
|
||||
如果 ZClaw Bridge 暂时不可连接,不要再次执行 `ziniao start`,也不要执行 `zclaw login`;只短间隔重新运行 `doctor`,总等待时间最多 30 秒,仍未恢复则停止并报告。即使同一次 `doctor` 还显示客户端未登录,也必须先等 Bridge 恢复。Bridge 恢复后的下一步只能是读取最新一次 `doctor` 明细并重新分流:仍未登录才执行 `ziniao-cli zclaw login`,全部通过才执行原业务命令;禁止在复查前预先执行或承诺执行 `store open`。不要把启动或登录当作状态探测。
|
||||
|
||||
独立的运行中会话失效分支:如果此前 `doctor` 明细已经通过,但原始 `store`、`page` 或其他 ZClaw 本地命令随后明确返回 `Ziniao user is not logged in`,下一条命令必须逐字为:
|
||||
|
||||
```bash
|
||||
ziniao-cli zclaw login
|
||||
```
|
||||
|
||||
这里同样只能使用完整命令 `ziniao-cli zclaw login`,不得写成不存在的 `ziniao-cli login`。登录成功后,使用相同参数只重试刚才失败的原命令一次;不要为了这次恢复额外执行 `doctor`。不得在尚未尝试 `zclaw login` 前要求用户手工登录。
|
||||
|
||||
若 `zclaw login` 失败,直接报告其实际错误。API Key 无效或被拒绝、终端未绑定、账号不一致,或 Bridge 不可连接时,必须立即停止,禁止重复登录、重复发送店铺请求,或笼统要求用户“先登录客户端”。只有原始错误不是该精确未登录错误、或登录命令本身无法恢复时,才按对应原因要求用户处理前置条件。
|
||||
|
||||
只有 `doctor` 显示配置、API Key、服务端连通性、ZClaw Bridge 和客户端账号一致时,才继续浏览器操作:
|
||||
|
||||
- 店铺窗口操作使用 `ziniao-store` Skill。
|
||||
- 页面浏览器自动化使用 `ziniao-page` Skill。
|
||||
- 查询可用 Bridge 工具使用 `ziniao-cli zclaw tools`;仅在没有快捷命令或用户明确要求调试原始工具时,才使用 `ziniao-cli zclaw invoke <tool>`。
|
||||
|
||||
| 现象 | 处理 |
|
||||
|------|------|
|
||||
| `config init --new` 提示客户端未登录 | 启动并登录紫鸟客户端,再重新执行初始化。 |
|
||||
| `doctor` 提示客户端未启动或本地服务未就绪 | 按当前 Shell 使用 PowerShell `Get-Command`、CMD `where.exe` 或 POSIX/Git Bash `command -v` 检测;找到命令才执行一次 `ziniao start`。若 macOS 上 `ziniao start` 失败或执行后仍未启动,且存在 `/Applications/ziniao.app`,可再执行一次 `open -a "/Applications/ziniao.app"`,随后重新运行 `doctor`;若 macOS 找不到命令但应用存在,也直接使用一次 `open`;其他情况提示用户手动启动。 |
|
||||
| `doctor` 明确显示客户端已启动但未登录,且 Bridge 已连通 | 直接执行一次 `ziniao-cli zclaw login`,不得先发店铺或页面命令探测;登录后重新运行 `doctor`,明细全部通过后才执行原业务命令。 |
|
||||
| `127.0.0.1:9481` 暂不可连接(无论客户端是否同时显示未登录) | 不启动、不登录;短间隔重新运行 `doctor`,最多等待 30 秒。Bridge 恢复后再按最新客户端明细分流。 |
|
||||
| `ziniao start` 返回 Windows 错误 1113 或其他启动错误 | 不重试启动;立即重新运行一次 `doctor`,明细通过则继续,否则同时报告启动错误和 doctor 明细后停止。 |
|
||||
| `zclaw login` 提示无法连接 ZClaw Bridge | 不把登录当作启动探测;回到 `doctor` 判断 9481 不可连接的具体原因。 |
|
||||
| API Key 无效或未配置 | 使用正确的 Key 重新执行 `ziniao-cli config init`,或重新运行 `ziniao-cli config init --new`。 |
|
||||
| 初始化后 ZClaw 仍提示 API Key 认证失败 | 前往 https://open.ziniao.com 的用户应用「终端管理」,绑定当前终端识别码(在紫鸟浏览器设置中查看),然后重新执行 `ziniao-cli zclaw login` 和 `ziniao-cli doctor`。 |
|
||||
| `doctor` 提示客户端登录用户与当前 profile 不一致 | 明确说明当前 profile 与客户端登录账号及其不一致,询问用户是否切换到当前 profile 对应账号并继续原请求;不要只要求用户手工切换。用户确认后按既有登录流程继续。 |
|
||||
|
||||
### 多账号切换
|
||||
|
||||
支持多个账号配置(profile),通过 `config use` 切换:
|
||||
|
||||
```bash
|
||||
# 列出所有 profile
|
||||
ziniao-cli config list
|
||||
# * zhangsan
|
||||
# staging
|
||||
|
||||
# 切换到指定 profile
|
||||
ziniao-cli config use staging
|
||||
|
||||
# 重命名 profile
|
||||
ziniao-cli config rename staging production
|
||||
```
|
||||
|
||||
初始化时通过 `--profile` 指定名称,浏览器创建流程会自动用账号用户名命名。
|
||||
|
||||
### 删除配置
|
||||
|
||||
删除操作会弹出确认提示,`--yes` 可跳过:
|
||||
|
||||
```bash
|
||||
ziniao-cli config remove # 删除当前 profile(需确认)
|
||||
ziniao-cli config remove --profile staging # 删除指定 profile(需确认)
|
||||
ziniao-cli config remove --yes # 跳过确认直接删除
|
||||
```
|
||||
|
||||
## 认证
|
||||
|
||||
### 认证模型
|
||||
|
||||
ziniao-cli 使用**统一 apiKey**(Bearer Token),一个 Key 同时用于:
|
||||
|
||||
| 用途 | 地址 | 说明 |
|
||||
|------|------|------|
|
||||
| 服务端 API | `sbappstoreapi.ziniao.com` | 部门/员工/账号/设备等业务接口 |
|
||||
| 本地 ZClaw Bridge | `127.0.0.1:9481` | 紫鸟浏览器店铺/页面操控 |
|
||||
|
||||
**没有** OAuth、token 刷新、双身份(user/bot)等复杂机制。apiKey 是静态凭证,不过期。
|
||||
|
||||
### ISV 应用权限点
|
||||
|
||||
调用服务端 API 前,需在紫鸟开放平台为应用开通对应的权限点。这些权限仅 Boss 账号可申请和调用。以下是各模块所需的权限点:
|
||||
|
||||
| 模块 | 权限点 | 覆盖接口 |
|
||||
|------|--------|---------|
|
||||
| 部门员工 | ERP-部门与员工接口 | 部门 CRUD + 员工查询/新增/修改/启禁用(9 个) |
|
||||
| 部门员工 | ERP-用户的部门变更 | 员工调岗(1 个) |
|
||||
| 角色权限 | ERP-角色列表查询 | 角色列表 + 用户角色列表(2 个) |
|
||||
| 角色权限 | ERP-角色详情 | 角色详情(1 个) |
|
||||
| 角色权限 | ERP-权限列表 | 权限项列表(1 个) |
|
||||
| 角色权限 | ERP-角色添加、修改权限 | 添加/修改/调整角色(3 个) |
|
||||
| 设备管理 | ERP-设备查询 | 设备列表 + 历史绑定记录(2 个) |
|
||||
| 设备管理 | ERP-设备套餐列表查询权限 | 套餐列表(1 个) |
|
||||
| 设备管理 | ERP-设备绑定权限 | 绑定设备(1 个) |
|
||||
| 设备管理 | ERP-解绑设备 | 解绑设备(1 个) |
|
||||
| 设备管理 | ERP-开关自动续费 | 自动续费开关(1 个) |
|
||||
| 设备管理 | ERP-设备购买与续费权限 | 购买 + 续费(2 个) |
|
||||
| 设备管理 | ERP-添加自有设备(新) | 添加自有设备(1 个) |
|
||||
| 设备管理 | ERP-修改自有设备信息(新) | 修改自有设备(1 个) |
|
||||
| 设备管理 | ERP-查询已购设备价格接口 | 已购设备价格(1 个) |
|
||||
| 账号管理 | ERP-账号查看权限 | 账号列表/授权查询/用户账号列表/授权用户列表(4 个) |
|
||||
| 账号管理 | ERP-创建与删除账号权限 | 创建 + 删除账号(2 个) |
|
||||
| 账号管理 | ERP-编辑账号基础信息 | 编辑账号信息(1 个) |
|
||||
| 账号管理 | ERP-账号授权权限 | 授权新增 + 授权删除(2 个) |
|
||||
| 账号管理 | ERP-清除账号授权 | 清除全部授权(1 个) |
|
||||
| 账号管理 | ERP-清除账号缓存 | 清除缓存(1 个) |
|
||||
| 账号管理 | ERP-标签列表 | 企业标签列表(1 个) |
|
||||
| 账号管理 | ERP-查询某用户有权限的账号列表 | 用户有权限的账号(1 个) |
|
||||
| 账号管理 | ERP-获取附加网站信息 | 附加网站信息(1 个) |
|
||||
| 账号管理 | 账号标签管理权限 | 标签 CRUD + 绑定/解绑/替换/清空/移除(9 个) |
|
||||
| 访问策略 | ERP-网页访问权限 | 访问规则/网页/网页分组全部操作(22 个) |
|
||||
|
||||
> 如果调用接口返回 `isv.invalid-method`(不存在的方法名),通常是该权限点未开通。前往 [紫鸟开放平台](https://open.ziniao.com) → 应用管理 → 权限管理 中开通。
|
||||
|
||||
### 公共参数
|
||||
|
||||
每个服务端 API 请求都需要 `companyId`,**CLI 自动注入并强制使用配置值**,无需也不应手动传递。该值在 `config init` 时通过 `/app/builtin/company` 接口自动获取并写入配置;即使 `--data` 中显式传入 `companyId`,CLI 也会用配置值覆盖。
|
||||
|
||||
## 两层命令体系
|
||||
|
||||
### 第一层:通用 api 命令(覆盖全部 73 个接口)
|
||||
|
||||
任何紫鸟服务端 API 都可以通过 `api` 命令调用,无需专门的快捷命令:
|
||||
|
||||
```bash
|
||||
ziniao-cli api <path> [--data '{}'] [--format table] [--jq '.data[]']
|
||||
ziniao-cli api GET /app/builtin/company
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/department/list
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/staff/list --data '{"page":1,"limit":10}' --format table
|
||||
```
|
||||
|
||||
- 默认 POST 方法,支持 GET/POST/PUT/DELETE
|
||||
- `companyId` 由框架自动注入,并强制覆盖 `--data` 中的同名字段
|
||||
- `--page-all` 自动翻页(默认最多 10 页,需取全部时配合 `--page-limit 0`)
|
||||
- `--page-size N` 每页条数(默认 20)
|
||||
- `--page-limit N` 最大翻页数(默认 10,0 为不限,配合 `--page-all`)
|
||||
- `--page-delay MS` 翻页间隔毫秒数(默认 200,配合 `--page-all`)
|
||||
- `--dry-run` 预览请求不执行
|
||||
- `--jq` 内置 jq 过滤
|
||||
|
||||
### 第二层:快捷命令(高频场景优化)
|
||||
|
||||
为复杂接口提供命名 flag + 智能默认值:
|
||||
|
||||
```bash
|
||||
ziniao-cli department list --tree
|
||||
ziniao-cli staff create --username "zhangsan" --name "张三" --password "Pass123!" --role-id 16691047257645
|
||||
ziniao-cli store list --format table
|
||||
```
|
||||
|
||||
## `account` 与 `store` 的区别
|
||||
|
||||
两组命令都涉及"店铺",但职责和通道完全不同:
|
||||
|
||||
| | `account` 命令 | `store` 命令 |
|
||||
|--|---------------|-------------|
|
||||
| 通道 | 服务端 API(`sbappstoreapi.ziniao.com`) | 本地 ZClaw Bridge(`127.0.0.1:9481`) |
|
||||
| 职责 | 店铺账号的 CRUD、授权、标签等**管理操作** | 控制已打开的浏览器实例:列出、打开、关闭 |
|
||||
| 前置条件 | 只需 apiKey + 网络 | 紫鸟浏览器客户端必须已启动 |
|
||||
| 典型场景 | 创建店铺、批量授权员工、管理标签 | 打开店铺浏览器 → 导航 → 截图 → 自动化操作 |
|
||||
|
||||
**简单记忆:** `account` = 管理后台增删改查,`store` = 控制本地浏览器窗口。
|
||||
|
||||
## 命令优先级
|
||||
|
||||
AI Agent 调用时按优先级选择:
|
||||
|
||||
1. **快捷命令** -- `staff list`、`department create`、`store open` 等(参数简化,体验最好)
|
||||
2. **通用 api 命令** -- `api <path>` 兜底(任意接口都能调,需要手写 JSON body)
|
||||
3. **zclaw invoke** -- `zclaw invoke <tool>` 兜底(任意 ZClaw 工具都能调)
|
||||
|
||||
### ZClaw 快捷命令硬约束
|
||||
|
||||
若某个 ZClaw 工具已有对应快捷命令,**必须只调用快捷命令**,禁止再调用
|
||||
`ziniao-cli zclaw invoke <tool>`。两者最终都会请求同一个 Bridge 工具;重复调用会重复执行操作。
|
||||
|
||||
`zclaw invoke` 仅用于没有快捷命令的 Bridge 工具,或用户明确要求按原始工具名和 JSON 参数调试。
|
||||
|
||||
例如 `visit_page` 已由 `page visit` 封装,必须使用 `ziniao-cli page visit ...`。
|
||||
|
||||
### 店铺环境结果硬约束
|
||||
|
||||
当用户要求店铺浏览器、店铺代理或该店铺登录态下的结果时,结果只能来自该店铺环境。
|
||||
店铺页面操作失败后,禁止改用公开网页、搜索引擎、其他店铺、服务端 API 或供应来源来“替代”结果;除非用户明确将任务范围改为公开信息检索。
|
||||
|
||||
发生 `ERR_SOCKS_CONNECTION_FAILED` 时,保持当前店铺打开,等待 5 秒后以相同参数重试;最多 3 次总尝试。仍失败时,报告该店铺环境暂时无法访问及已尝试次数,并停止该店铺任务;不得声称已取得店铺环境结果。
|
||||
|
||||
## 输出格式
|
||||
|
||||
所有命令支持 `--format json|table|csv` 和 `--jq` 过滤:
|
||||
|
||||
```bash
|
||||
ziniao-cli staff list --format table
|
||||
ziniao-cli staff list --jq '.[].name'
|
||||
ziniao-cli department list --format csv
|
||||
```
|
||||
|
||||
### 输出结构
|
||||
|
||||
成功(stdout):
|
||||
```json
|
||||
{"ok": true, "data": ..., "meta": {"count": 10}}
|
||||
```
|
||||
|
||||
失败(stderr):
|
||||
```json
|
||||
{"ok": false, "error": {"type": "gateway|business|auth|validation", "code": 1001, "message": "...", "hint": "..."}}
|
||||
```
|
||||
|
||||
### 错误类型与处理
|
||||
|
||||
| 错误类型 | 含义 | AI Agent 应该做什么 |
|
||||
|---------|------|-------------------|
|
||||
| `auth` | apiKey 缺失或无效 | 提示用户运行 `ziniao-cli config init` |
|
||||
| `gateway` | 网关层错误 (code != "0") | 报告错误,检查网络/apiKey |
|
||||
| `business` | 业务层错误 (ret != 0) | 报告错误信息,根据 msg 判断原因 |
|
||||
| `validation` | 参数校验失败 | 检查命令参数是否正确 |
|
||||
| `network` | 网络不通/Bridge 未启动 | ZClaw 相关:回到 `doctor` 明细路由,只有客户端未启动时才进入启动流程;API 相关:检查网络 |
|
||||
|
||||
## 更新检查
|
||||
|
||||
ziniao-cli 命令执行后,如果检测到新版本,JSON 输出中会包含 `_notice.update` 字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"data": ...,
|
||||
"_notice": {
|
||||
"update": {
|
||||
"current": "1.0.0",
|
||||
"latest": "1.1.0",
|
||||
"message": "ziniao-cli 1.1.0 可用,当前 1.0.0,运行 npm update -g @ziniao-open/cli 更新"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**当你在输出中看到 `_notice.update` 时:**
|
||||
1. 先完成用户当前请求
|
||||
2. 然后将 `message` 字段内容展示给用户,提议帮其更新
|
||||
3. 若用户同意,执行 `npm update -g @ziniao-open/cli`
|
||||
|
||||
更新提示仅通过 stdout JSON 的 `_notice` 字段传递,不会输出到 stderr。可通过环境变量 `ZINIAO_CLI_NO_UPDATE_CHECK=1` 禁用检查。
|
||||
|
||||
## 环境兼容说明
|
||||
|
||||
### Windows Git Bash 路径转义问题
|
||||
|
||||
在 **Git Bash** 中,以 `/` 开头的字符串会被 MSYS 自动转换为 Windows 本地路径(如 `/superbrowser/...` → `C:/Program Files/Git/superbrowser/...`),导致 `api` 命令的路径参数被破坏。
|
||||
|
||||
**PowerShell 和 CMD 无此问题。**
|
||||
|
||||
**解决方式一(推荐):写入 `.bashrc` 永久生效**
|
||||
|
||||
```bash
|
||||
echo 'export MSYS_NO_PATHCONV=1' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
**解决方式二:每次命令前加前缀**
|
||||
|
||||
```bash
|
||||
MSYS_NO_PATHCONV=1 ziniao-cli api /superbrowser/rest/v1/erp/store/create \
|
||||
--data '{"storeData":[{"name":"新店铺"}]}'
|
||||
```
|
||||
|
||||
## 安全规则
|
||||
|
||||
- **API Key 是静态敏感凭证**:不得请求用户在聊天中发送完整 Key,也不得在回复、终端输出或错误信息中展示完整 Key
|
||||
- **禁止把完整 API Key 写入命令行参数、Shell 历史、脚本、截图、录屏、聊天消息、日志或 Git**
|
||||
- 人工交互环境使用 `ziniao-cli config init` 输入 Key;输入期间避免屏幕共享、录屏和终端日志采集
|
||||
- CI/CD 使用 `ziniao-cli config init --api-key-stdin`,由 Secret Manager 或受保护的标准输入提供 Key;不得把 Key 直接写在流水线命令中
|
||||
- **写入/删除操作前必须确认用户意图**
|
||||
- `high-risk-write` 操作(department delete、staff remove)会要求交互式确认,可用 `--yes` 跳过
|
||||
- 建议先用 `--dry-run` 预览危险请求
|
||||
|
||||
## 重要行为规则
|
||||
|
||||
- **ZClaw 本地接口必须通过 ziniao-cli 调用**:调用紫鸟浏览器本地接口(store/page/zclaw 命令)时,必须使用本技能体系中的 ziniao-cli 能力,不要使用 ziniao-assistant 技能自行调用 ZClaw Bridge。
|
||||
- **店铺列表优先使用本地接口**:如果用户要求获取店铺列表,应优先使用 `store list` 快捷命令(走本地 ZClaw Bridge),因为普通成员没有服务端 `account list` 接口权限。成员类型可通过 `ziniao-cli config show` 结果中的 `isBoss` 字段判断。
|
||||
- **ZClaw 认证失败排查**:如果帮用户初始化应用(`config init`)之后,请求 ZClaw 接口仍返回 API Key 认证失败,应提醒用户前往紫鸟开放平台 https://open.ziniao.com 查看自己的用户应用里「终端管理」是否已绑定当前终端识别码(识别码在紫鸟浏览器设置中查看)。
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
name: ziniao-skill-maker
|
||||
version: 1.0.0
|
||||
description: "创建 ziniao-cli 的自定义 Skill。当用户需要把紫鸟 API 操作封装成可复用的 Skill(包装原子 API 或编排多步流程)时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# Skill Maker
|
||||
|
||||
基于 ziniao-cli 创建新 Skill。Skill = 一份 `SKILL.md`,教 AI 用 CLI 命令完成任务。
|
||||
|
||||
## CLI 核心能力
|
||||
|
||||
```bash
|
||||
ziniao-cli <service> <command> # 快捷命令
|
||||
ziniao-cli api [METHOD] <path> [--data/--format] # 任意服务端 API
|
||||
ziniao-cli zclaw invoke <tool> [--args] # 任意 ZClaw 工具
|
||||
ziniao-cli store/page <command> # ZClaw 快捷命令
|
||||
```
|
||||
|
||||
优先级:快捷命令 > 通用 api/zclaw invoke。
|
||||
|
||||
## 调研 API
|
||||
|
||||
```bash
|
||||
# 1. 查看已有快捷命令
|
||||
ziniao-cli department --help
|
||||
ziniao-cli staff --help
|
||||
|
||||
# 2. 查看 ZClaw 工具
|
||||
ziniao-cli zclaw tools
|
||||
|
||||
# 3. 通用 api 裸调
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/per/role/list
|
||||
```
|
||||
|
||||
如果命令不够用,使用 [ziniao-openapi-explorer](../ziniao-openapi-explorer/SKILL.md) 查找 API 路径。
|
||||
|
||||
## SKILL.md 模板
|
||||
|
||||
文件放在 `skills/ziniao-<name>/SKILL.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: ziniao-<name>
|
||||
version: 1.0.0
|
||||
description: "<功能描述>。当用户需要<触发场景>时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
|
||||
# <标题>
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md)**
|
||||
|
||||
## 命令
|
||||
|
||||
\```bash
|
||||
# 单步操作
|
||||
ziniao-cli api POST /superbrowser/rest/v1/erp/xxx --data '{...}'
|
||||
|
||||
# 多步编排
|
||||
# Step 1: ...(记录返回的 xxx_id)
|
||||
# Step 2: 使用 Step 1 的 xxx_id
|
||||
\```
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-shared](../ziniao-shared/SKILL.md) — 认证和全局参数
|
||||
```
|
||||
|
||||
## 关键原则
|
||||
|
||||
- **description 决定触发** — 包含功能关键词 + "当用户需要...时使用"
|
||||
- **CRITICAL 依赖** — 所有 Skill 必须强制读取 ziniao-shared
|
||||
- **安全** — 写入操作前确认用户意图
|
||||
- **编排** — 说明步骤间的数据传递和失败处理
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: ziniao-staff
|
||||
version: 1.0.0
|
||||
description: "紫鸟员工管理:员工的查询、创建、修改、启用/禁用/删除和调岗。当用户需要查询员工信息、创建新员工、修改员工信息、启用/禁用/删除员工或调整员工部门时使用。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 员工管理
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md),其中包含认证、错误处理和安全规则**
|
||||
**CRITICAL — 所有 Shortcuts 在执行之前,务必先用 Read 工具读取其对应的 references/ 说明文档,禁止直接盲目调用命令。**
|
||||
|
||||
## 核心场景
|
||||
|
||||
### 查询员工
|
||||
|
||||
支持多种筛选条件,枚举值已映射为人类可读。注意:默认返回包含已删除员工,取 ID 做后续操作时建议加 `--status active`。
|
||||
|
||||
```bash
|
||||
ziniao-cli staff list --name "张三" --status active --format table
|
||||
ziniao-cli staff list --role boss --page-all
|
||||
ziniao-cli staff list --department-id 15868464646076
|
||||
```
|
||||
|
||||
### 创建员工
|
||||
|
||||
原始 API 需要 20+ 字段的嵌套 JSON,CLI 简化为核心 flag + 智能默认值(`--department-id` 必填):
|
||||
|
||||
```bash
|
||||
ziniao-cli staff create --username "zhangsan" --name "张三" --password "Pass123!" --role-id 16691047257645 --department-id 15868464646076
|
||||
```
|
||||
|
||||
智能默认值:`authDevide=1`(全设备授权)、`authClient=全部允许`、`isLimitLogin=0`。
|
||||
登录日期默认发送有效日期:`loginLimitStartDate=今天`、`loginLimitEndDate=2099-12-31`,也可通过 `--login-start-date` / `--login-end-date` 指定。
|
||||
|
||||
### 启用/禁用/删除
|
||||
|
||||
原始 API 是一个接口通过 `status` 字段区分,CLI 拆为 3 个语义化命令:
|
||||
|
||||
```bash
|
||||
ziniao-cli staff enable --id 16524289555087
|
||||
ziniao-cli staff disable --id "id1,id2,id3" # 支持批量
|
||||
ziniao-cli staff remove --id 16524289555087 # high-risk-write
|
||||
```
|
||||
|
||||
## 枚举映射
|
||||
|
||||
| flag | 可选值 | 对应 API 值 |
|
||||
|------|--------|-----------|
|
||||
| `--status` | active / disabled / deleted | delflag: 0/1/2 |
|
||||
| `--role` | boss / manager / employee / admin | level: 0/1/2/3 |
|
||||
| `--device-auth` | auto / first / approve / phone | authDevide: 1/2/3/4 |
|
||||
| `--allow-clients` | windows,mac,web,android,ios,linux,miniapp 或 all | authClient 嵌套对象 |
|
||||
|
||||
## Shortcuts
|
||||
|
||||
| 命令 | 说明 | 详细文档 |
|
||||
|------|------|---------|
|
||||
| `staff list` | 查询员工列表 | [`references/ziniao-staff-list.md`](references/ziniao-staff-list.md) |
|
||||
| `staff create` | 新增员工 | [`references/ziniao-staff-create.md`](references/ziniao-staff-create.md) |
|
||||
| `staff update` | 修改员工 | 只发修改字段:`--id xxx --name "新名"`,底层路径 `/superbrowser/rest/v2/erp/staff/modify` |
|
||||
| `staff enable` | 启用员工 | [`references/ziniao-staff-enable-disable.md`](references/ziniao-staff-enable-disable.md) |
|
||||
| `staff disable` | 禁用员工 | 同上 |
|
||||
| `staff remove` | 删除员工 | `high-risk-write` |
|
||||
| `staff transfer` | 调岗 | `--staff-id xxx --department-id yyy` |
|
||||
|
||||
## 通用 api 覆盖
|
||||
|
||||
```bash
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/staff/list --data '{"page":1,"limit":10}'
|
||||
ziniao-cli api /superbrowser/rest/v2/erp/staff/add --data '{...}'
|
||||
ziniao-cli api /superbrowser/rest/v2/erp/staff/modify --data '{...}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/staff/status --data '{"staffIds":["xxx"],"status":"1"}'
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/staff/department --data '{"staffIds":["xxx"],"departmentIds":["yyy"]}'
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
# staff create
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。
|
||||
|
||||
新增员工。写入操作。原始 API 需要 20+ 字段,CLI 简化为核心 flag,并补齐服务端需要的登录日期字段。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 最简用法(5 个必填参数)
|
||||
ziniao-cli staff create \
|
||||
--username "zhangsan" \
|
||||
--name "张三" \
|
||||
--password "Pass123!" \
|
||||
--role-id 16691047257645 \
|
||||
--department-id 15868464646076
|
||||
|
||||
# 指定部门 + 限制登录终端
|
||||
ziniao-cli staff create \
|
||||
--username "zhangsan" \
|
||||
--name "张三" \
|
||||
--password "Pass123!" \
|
||||
--role-id 16691047257645 \
|
||||
--department-id 15868464646076 \
|
||||
--allow-clients windows,mac,web
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `--username` | 是 | — | 登录账号 |
|
||||
| `--name` | 是 | — | 姓名 |
|
||||
| `--password` | 是 | — | 密码 |
|
||||
| `--role-id` | 是 | — | 角色 ID(用 `ziniao-cli role list` 或 `ziniao-cli api /superbrowser/rest/v1/erp/per/role/list` 查询) |
|
||||
| `--department-id` | 是 | — | 部门 ID |
|
||||
| `--mobile` | 否 | 无 | 手机号 |
|
||||
| `--allow-clients` | 否 | all | 允许登录终端(windows,mac,web,android,ios,linux,miniapp) |
|
||||
| `--device-auth` | 否 | auto | 设备授权模式(auto\|first\|approve\|phone) |
|
||||
| `--login-start-date` | 否 | 今天 | 可登录起始日期,格式 yyyy-MM-dd |
|
||||
| `--login-end-date` | 否 | 2099-12-31 | 可登录结束日期,格式 yyyy-MM-dd |
|
||||
|
||||
## 智能默认值
|
||||
|
||||
以下字段使用智能默认值,用户无需指定:
|
||||
|
||||
| 原始 API 字段 | 默认值 | 说明 |
|
||||
|--------------|--------|------|
|
||||
| `authDevide` | 1 | 全设备自动永久授权 |
|
||||
| `isUpdatePersonInfo` | 1 | 允许修改个人信息 |
|
||||
| `isLimitLogin` | 0 | 不限制登录时间 |
|
||||
| `isTwoStepVerify` | 0 | 不开启二步验证 |
|
||||
| `enablePhoneLogin` | 0 | 不开启手机号登录 |
|
||||
| `authClient` | 全部 "1" | 允许所有终端 |
|
||||
| `loginLimitStartDate` | 今天 | 服务端需要非空有效日期 |
|
||||
| `loginLimitEndDate` | 2099-12-31 | 服务端需要非空有效日期 |
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-staff](../SKILL.md) — 员工管理全部命令
|
||||
@@ -0,0 +1,47 @@
|
||||
# staff enable / disable / remove
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。
|
||||
|
||||
启用、禁用或删除员工。原始 API 是一个接口(`/erp/staff/status`),CLI 拆为 3 个语义化命令。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 启用
|
||||
ziniao-cli staff enable --id 16524289555087
|
||||
|
||||
# 禁用(支持批量)
|
||||
ziniao-cli staff disable --id 16524289555087
|
||||
ziniao-cli staff disable --id "id1,id2,id3"
|
||||
|
||||
# 删除(高风险,需确认)
|
||||
ziniao-cli staff remove --id 16524289555087
|
||||
ziniao-cli staff remove --id 16524289555087 --yes # 跳过确认
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--id` | 是 | 员工 ID(逗号分隔可批量) |
|
||||
| `--yes` | 否 | 跳过确认(仅 remove) |
|
||||
|
||||
## 风险等级
|
||||
|
||||
| 命令 | 风险等级 | 是否需确认 |
|
||||
|------|---------|----------|
|
||||
| `staff enable` | write | 否 |
|
||||
| `staff disable` | write | 否 |
|
||||
| `staff remove` | high-risk-write | 是(交互式确认) |
|
||||
|
||||
## 内部映射
|
||||
|
||||
| 命令 | API status 值 |
|
||||
|------|--------------|
|
||||
| enable | "0" |
|
||||
| disable | "1" |
|
||||
| remove | "2" |
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-staff](../SKILL.md) — 员工管理全部命令
|
||||
@@ -0,0 +1,64 @@
|
||||
# staff list
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md)。
|
||||
|
||||
查询员工列表。只读操作。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 查询全部
|
||||
ziniao-cli staff list
|
||||
|
||||
# 按姓名搜索(模糊)
|
||||
ziniao-cli staff list --name "张三"
|
||||
|
||||
# 精确匹配
|
||||
ziniao-cli staff list --name "张三" --exact
|
||||
|
||||
# 按状态筛选 + 表格输出
|
||||
ziniao-cli staff list --status active --format table
|
||||
|
||||
# 按角色筛选
|
||||
ziniao-cli staff list --role boss
|
||||
|
||||
# 按部门筛选
|
||||
ziniao-cli staff list --department-id 15868464646076
|
||||
|
||||
# 自动翻页
|
||||
ziniao-cli staff list --page-all --page-size 50
|
||||
|
||||
# jq 过滤
|
||||
ziniao-cli staff list --jq '.[].name'
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--name` | 否 | 按姓名搜索 |
|
||||
| `--username` | 否 | 按账号搜索 |
|
||||
| `--department-id` | 否 | 按部门 ID 筛选(逗号分隔) |
|
||||
| `--status` | 否 | active \| disabled \| deleted |
|
||||
| `--role` | 否 | boss \| manager \| employee \| admin |
|
||||
| `--exact` | 否 | 精确匹配(默认模糊) |
|
||||
| `--page-size` | 否 | 每页条数(默认 20) |
|
||||
| `--page-all` | 否 | 自动翻页获取全部 |
|
||||
| `--page-limit` | 否 | 最大翻页数(默认 10,0 为不限) |
|
||||
| `--page-delay` | 否 | 翻页间隔毫秒数(默认 200) |
|
||||
| `--format` | 否 | json \| table \| csv |
|
||||
| `--jq` / `-q` | 否 | jq 过滤表达式 |
|
||||
|
||||
## 输出字段翻译
|
||||
|
||||
表格输出时,枚举值自动翻译:
|
||||
- `level "0"` -> `role: "Boss"`
|
||||
- `delflag "0"` -> `statusText: "正常"`
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 默认返回所有状态的员工(含已禁用和已删除)。如果需要取员工 ID 做后续操作(分配角色、授权店铺等),建议加 `--status active` 筛选,避免使用已删除员工 ID 导致"无效的 staffId"错误。
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-staff](../SKILL.md) — 员工管理全部命令
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: ziniao-store
|
||||
description: "Use when a request concerns the local 紫鸟/ZClaw browser, a store name or ID, listing/resolving/opening/reusing/closing a store browser, or a store-scoped task that starts from a store. Do not use for server-side account CRUD or for a webpage task on an already-open store."
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 紫鸟店铺入口
|
||||
|
||||
本技能负责“店铺”这一资源及其本地浏览器生命周期,不负责定义用户的业务目标,也不复制 CLI 帮助文档。
|
||||
|
||||
## 负责范围
|
||||
|
||||
- 列出、解析、打开、复用、关闭本地紫鸟店铺浏览器。
|
||||
- 保存本次任务确认过的 `store-id`,供后续步骤复用。
|
||||
- 用户从某个店铺开始并继续操作网页时,按需使用 `ziniao-page`。
|
||||
- 任务涉及配置、认证、Bridge、客户端或网络异常时,按需读取 `ziniao-shared` 的诊断规则。
|
||||
|
||||
## 路由边界
|
||||
|
||||
- 请求以店铺名称/ID或“本地紫鸟店铺”为起点:先处理店铺,再判断是否需要页面能力。
|
||||
- 已有明确且正在运行的店铺,只需网页操作:使用 `ziniao-page`,不要重复打开店铺。
|
||||
- 店铺后台账号的创建、删除、授权、标签等服务端管理:使用 `ziniao-account`,不是本技能。
|
||||
- 不要把用户的业务目标或任务参数写入本技能;这些属于任务内容,不是店铺能力。
|
||||
|
||||
## 最小执行规则
|
||||
|
||||
1. 需要店铺对象时,使用 CLI 的 `store list` 或 `store resolve` 获取并确认目标。
|
||||
2. 需要浏览器窗口时,使用 `store open`;已有有效 `store-id` 且店铺仍运行时直接复用。
|
||||
3. 需要网页资源操作时,继续使用 `ziniao-page`;不要在本技能中重述页面命令。
|
||||
4. 店铺命令失败时,根据错误按需运行 `ziniao-cli doctor`;不要无条件登录、启动或重复重试。
|
||||
5. 具体参数不确定时,使用 `ziniao-cli store <command> --help`,复杂命令再读取对应 reference。
|
||||
|
||||
## 打开后的验证边界
|
||||
|
||||
- 用户只要求打开店铺时,精确解析店铺并成功执行带 `--expected-name` 的 `store open` 即可结束;不得为了证明窗口存在而追加 `page snapshot`。
|
||||
- 用户同时给出目标 URL 时,首次打开优先使用同一次 `store open --url`,不要再对同一 URL 重复执行 `page visit`。
|
||||
- 用户要求确认最终页面或业务状态时,转到 `ziniao-page`,使用导航返回值、`page content` 或 `page exec` 验证 URL/title/正文;不得用 snapshot 代替普通页面验证。
|
||||
- 只有后续页面任务本身满足上传、自定义控件或复杂歧义定位条件时,才由 `ziniao-page` 决定使用 snapshot。
|
||||
|
||||
## 按需参考
|
||||
|
||||
- [store list](references/ziniao-store-list.md):需要列出店铺时读取。
|
||||
- [store open](references/ziniao-store-open.md):需要打开或复用店铺时读取。
|
||||
- `ziniao-shared`:仅在配置、认证、Bridge 或诊断问题出现时读取。
|
||||
- `ziniao-page`:仅在任务继续操作已打开店铺内网页时读取。
|
||||
@@ -0,0 +1,50 @@
|
||||
# store list
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md),再检查店铺操作所需状态:
|
||||
>
|
||||
> ```bash
|
||||
> ziniao-cli doctor
|
||||
> ```
|
||||
>
|
||||
> `doctor` 明细确认通过后直接执行 `store list`,不得无条件启动或登录。检查未通过时,严格按照 `ziniao-shared` 的「ZClaw 客户端与 Bridge 就绪」流程处理;只有客户端未登录或 `store list` 明确返回会话缺失/过期时,才允许登录一次,随后重新检查并只重试一次。API Key、终端或账号认证失败时立即停止。
|
||||
|
||||
列出所有店铺。只读操作。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
ziniao-cli store list
|
||||
ziniao-cli store list --format table
|
||||
ziniao-cli store list --keyword "Rose" --limit 10
|
||||
ziniao-cli store list --all
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--page` | 否 | 页码 |
|
||||
| `--limit` | 否 | 每页数量 |
|
||||
| `--all` | 否 | 获取全部店铺 |
|
||||
| `--keyword` | 否 | 按关键词筛选 |
|
||||
| `--type` | 否 | 店铺列表类型 |
|
||||
| `--format` | 否 | 输出格式: json \| table |
|
||||
| `--jq` | 否 | jq 过滤表达式 |
|
||||
|
||||
## 输出字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| storeId | 店铺 ID(后续操作需要) |
|
||||
| storeName | 店铺名称 |
|
||||
| platformName | 平台名称 |
|
||||
| ip | 绑定的 IP |
|
||||
|
||||
## 提示
|
||||
|
||||
- storeId 是后续所有页面操作(page visit/click/screenshot 等)的必填参数
|
||||
- 建议先用 `store list --format table` 查看所有店铺
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-store](../SKILL.md) — 店铺管理全部命令
|
||||
@@ -0,0 +1,48 @@
|
||||
# store open
|
||||
|
||||
> **前置条件:** 先阅读 [`../ziniao-shared/SKILL.md`](../../ziniao-shared/SKILL.md),再检查店铺操作所需状态:
|
||||
>
|
||||
> ```bash
|
||||
> ziniao-cli doctor
|
||||
> ```
|
||||
>
|
||||
> `doctor` 明细确认通过后直接执行 `store open`,不得无条件启动或登录。检查未通过时,严格按照 `ziniao-shared` 的「ZClaw 客户端与 Bridge 就绪」流程处理;只有客户端未登录或 `store open` 明确返回会话缺失/过期时,才允许登录一次,随后重新检查并只重试一次。API Key、终端或账号认证失败时立即停止。
|
||||
|
||||
打开或复用店铺浏览器窗口;带 `--url` 时导航到目标页面。
|
||||
|
||||
> 首次进入目标页面时,优先使用 `store open --url <url>`,即使店铺为 `reused: true` 也会直接导航当前页面。已建立会话后的后续 URL 跳转使用 `page visit`。同一个 URL 不得连续执行 `store open --url` 与 `page visit`。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
# 按名称打开
|
||||
ziniao-cli store open --name "Rosehut"
|
||||
|
||||
# 按 ID 打开
|
||||
ziniao-cli store open --id abc123
|
||||
|
||||
# 打开并直接访问 URL
|
||||
ziniao-cli store open --name "Rosehut" --url "https://www.amazon.com"
|
||||
|
||||
# 无头模式打开
|
||||
ziniao-cli store open --name "Rosehut" --headless
|
||||
|
||||
# 隐私模式打开
|
||||
ziniao-cli store open --name "Rosehut" --privacy
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
| 参数 | 必填 | 说明 |
|
||||
|------|------|------|
|
||||
| `--name` | 否* | 店铺名称(与 --id 二选一) |
|
||||
| `--id` | 否* | 店铺 ID(与 --name 二选一) |
|
||||
| `--expected-name` | 否 | 期望匹配的名称(用于验证) |
|
||||
| `--url` | 否 | 打开后直接导航到的 URL(对应 launchUrl) |
|
||||
| `--headless` | 否 | 无头模式 |
|
||||
| `--privacy` | 否 | 隐私模式 |
|
||||
| `--window-ratio` | 否 | 窗口比例 |
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-store](../SKILL.md) — 店铺管理全部命令
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
name: ziniao-workflow-batch-account
|
||||
version: 1.0.0
|
||||
description: "批量账号授权工作流:编排 account list + staff list + api 调用完成批量授权店铺给员工。适用于新员工入职时批量分配店铺权限。"
|
||||
metadata:
|
||||
requires:
|
||||
bins: ["ziniao-cli"]
|
||||
---
|
||||
|
||||
# 批量账号授权工作流
|
||||
|
||||
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../ziniao-shared/SKILL.md`](../ziniao-shared/SKILL.md)**
|
||||
|
||||
## 适用场景
|
||||
|
||||
- "给新员工分配店铺权限"
|
||||
- "批量授权店铺给某个员工"
|
||||
- "新人入职,需要授权所有店铺"
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 已配置 apiKey(`ziniao-cli config init`)
|
||||
|
||||
## 工作流
|
||||
|
||||
```
|
||||
staff list ──► 找到目标员工 ID
|
||||
account list ──► 找到目标店铺 ID 列表
|
||||
│
|
||||
├──► 对每个店铺:
|
||||
│ api /superbrowser/rest/v1/erp/store/auth/add { storeIdList, staffId }
|
||||
│
|
||||
└──► 汇总授权结果
|
||||
```
|
||||
|
||||
### Step 1: 查找员工
|
||||
|
||||
```bash
|
||||
# 按姓名查找
|
||||
ziniao-cli staff list --name "张三" --format table
|
||||
# 记录 userId
|
||||
```
|
||||
|
||||
### Step 2: 查找要授权的店铺
|
||||
|
||||
```bash
|
||||
# 列出所有店铺
|
||||
ziniao-cli account list --format table --page-all
|
||||
# 或按名称搜索
|
||||
ziniao-cli account list --name "US" --format table
|
||||
```
|
||||
|
||||
### Step 3: 逐个授权
|
||||
|
||||
对每个店铺执行:
|
||||
|
||||
```bash
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/store/auth/add \
|
||||
--data '{"storeIdList":["<storeId>"],"staffId":"<staffId>"}'
|
||||
```
|
||||
|
||||
### Step 4: 验证
|
||||
|
||||
```bash
|
||||
# 查看该员工已授权的店铺
|
||||
ziniao-cli api /superbrowser/rest/v1/erp/user/stores \
|
||||
--data '{"userId":"<userId>","page":1,"limit":10}'
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 授权是写入操作,执行前确认用户意图
|
||||
- 如果需要授权大量店铺,建议先列出计划并确认
|
||||
- 授权失败时记录错误并继续下一个
|
||||
|
||||
## 参考
|
||||
|
||||
- [ziniao-account](../ziniao-account/SKILL.md) — 账号管理
|
||||
- [ziniao-staff](../ziniao-staff/SKILL.md) — 员工管理
|
||||
- [ziniao-shared](../ziniao-shared/SKILL.md) — 认证和全局参数
|
||||
Reference in new issue
Block a user