docs: doc/ 全量同步 dev 现状——API 目录补全(AI 控制台/系统备份/自动发现等)、去 STF 过时口径、补 generic_steps 与配置键速查;确立「功能/配置改动须同步文档」红线

- doc/API.md:补方法/路径标题,权限分层修正,新增 AI 控制台(/api/agent/*)、系统备份(/api/system/backup/*)、设备自动发现(/api/devices/discovery/*)、tap_text/summary/health/devices-apps 等整节端点,去 STF 残留
- doc/TASK_DEV.md:STF 时代描述清理;新增 §2.10 generic_steps(18 节点与必填/嵌套/静默跳过语义)、§2.11 自定义动作与单步测试、/api/jobs 盲存校验语义、resolve_serials/抢占语义、模板构造函数签名修正
- doc/DEPLOY.md:数据备份改为推荐「系统→数据备份」功能并说明重启生效目录,端口表 STF7100→MCP8033,补 start.sh 生产链路与 MCP_PLATFORM_PASS 同步,故障排查去 STF
- doc/MCP.md:加「现状边界」(平台级任务 CRUD 未 MCP 化,规划见 AI_TASK_GEN §9),busy/平台会话说明,MCP_ALLOWED_SERIALS 语义纠正
- doc/MCP_DESIGN.md:加实现现状对照、错误码、独立容器改演进备选、里程碑状态、API 映射表按实现重写
- doc/ARCHITECTURE.md:Tab/子分栏/线程模型/数据表/蓝图表去 STF,补 device_discovery/agent/system_backup/经验巡检等
- doc/DEVELOPMENT.md:新增 §5.6「改动必须同步文档」红线、§2.3 配置键速查、蓝图化新增 API 流程、文档索引补登记
- doc/STF_REMOVAL.md:加历史记录状态横幅
- doc/AI_TASK_GEN.md:新增 AI 建任务设计稿(含 §9 需转 MCP 工具分层)
This commit is contained in:
2026-09-09 16:05:56 +08:00
parent 470c76221e
commit fd829a6063
9 changed files with 1121 additions and 196 deletions
+540 -70
View File
@@ -1,6 +1,6 @@
# API 接口文档
`platform-tools` Web 后台提供 JSON API,所有接口需登录后访问(Flask-Login session 认证)。
`platform-tools` Web 后台提供 JSON API,绝大多数接口需登录后访问(Flask-Login session 认证);免登录例外见下方权限模型。
**Base URL**:`http://localhost:18050`
@@ -15,16 +15,17 @@
---
**权限模型**(v2 起):
- 所有接口需登录;**查看类 GET 接口**(状态/列表/截图)所有登录用户可用
- 所有接口需登录(Flask-Login session);**免登录例外**:`GET /api/health`(探活)、`GET /locate`(设备端定位页,只显示 serial 文本)、`/login` 与静态资源
- **多数查看类 GET**(状态/任务/分组/自定义动作/APK 列表等)仅需登录即可用;设备维护/看屏/元素抓取类 GET 需对应 `devices` 权限
- **写操作按权限位授权**(管理员拥有全部权限):
| 权限位 | 中文 | 覆盖接口 |
|--------|------|---------|
| `tasks` | 任务管理 | 任务/自定义动作/分组的增删改、启停、立即执行 |
| `devices` | 设备控制 | 停止设备、释放占用、清除异常、前台扫描、元素抓取 |
| `devices` | 设备控制 | 停止设备、清除异常、定位、前台扫描、远程看屏/触控、元素抓取、设备池管理、自动发现 |
| `apks` | 应用管理 | APK 上传、安装、删除 |
| `logs` | 日志查看 | `GET /api/logs` |
- **用户管理接口仅管理员可用**(普通用户即使被授予业务权限也无法访问)
- 无权限访问返回 `403 {"ok": false, "error": "无权限执行此操作..."}`
- **仅管理员可用**(普通用户即使被授予业务权限也无法访问):AI 控制台 `/api/agent/*`、系统备份 `/api/system/backup/*`、Tailscale `/api/tailscale/*`、用户管理 `/api/users`、adb 终端 `/api/adb/*`、工具 `/api/tools/*`
- 403 文案两种:业务权限缺失 → `{"ok": false, "error": "无权限执行此操作(需要权限: X)"}`;仅管理员接口被非管理员访问 → `{"ok": false, "error": "仅管理员可执行此操作"}`
- 当前用户权限查询:`GET /api/me`
---
@@ -47,25 +48,55 @@
登出,重定向到登录页。
### GET /api/me
当前登录用户信息(含权限位),前端据此隐藏无权限的功能入口。需登录。
**响应**:
```json
{"ok": true, "user": {
"id": 1, "username": "admin", "is_admin": true,
"perms": ["tasks", "devices", "apks", "logs"]
}}
```
管理员返回全部权限位;普通用户返回其被授予的业务权限数组。
### GET /api/csrf
获取当前会话的 CSRF token(登录后先获取一次;变更类请求需在 `X-CSRF-Token` 请求头携带)。
**响应**:
```json
{"ok": true, "token": "…"}
```
---
## 2. 页面路由
### GET /
单页应用首页(需登录)。响应头设置 `Cache-Control: no-store` 防止缓存。
单页应用首页(需登录)。响应头设置 `Cache-Control: no-store, no-cache, must-revalidate, max-age=0`(并带 `Pragma: no-cache`)防止缓存。
### GET /login
登录页面(GET)。
### GET /wall
监控大屏页面(需登录,全屏深色控制室风格,供挂墙/电视展示):设备卡片网格
(缩略图/型号/状态/当前动作/进度)、顶部统计与时钟;状态每 5s 刷新、缩略图每 2.5s 轮询。
20 台设备整体开销约 0.2 核 CPU + 100KB/s 带宽,普通电脑无压力。
---
## 3. 设备状态
### GET /api/status
获取设备池 + Worker 综合状态(带 5 秒缓存)。
获取设备池 + Worker 综合状态(带 5 秒缓存;worker 状态实时读内存)。需登录。
字段说明:`server_time` 服务端时间戳;`fg_scanning`/`fg_last_scan` 前台 App 扫描状态;`devices` 设备数组。
**响应**:
```json
@@ -81,27 +112,58 @@
"device_name": "测试机1",
"present": true,
"ready": true,
"stf_occupied": false,
"owner": "",
"worker_status": "idle",
"foreground_app": "空闲",
"worker_status": "running",
"foreground_app": "抖音",
"progress": {"done": 5, "total": 80, "unit": "视频", "action_counts": {"like": 3}},
"current_action": "观看视频 6",
"last_error": "",
"last_warning": "",
"running_job": "",
"task_job": "",
"attempt": 0
"attempt": 0,
"end_time": 0
}
]
}
```
**worker_status 取值**:`idle` / `connecting` / `running` / `done` / `error` / `failed` / `released`
`worker_status` 取值:`idle` / `connecting` / `running` / `done` / `error` / `failed`
(权限:设备控制)
### GET /api/summary
失败/异常任务汇总(供监控页"异常汇总"面板),观察设备长期健康度。需登录。
**响应**:
```json
{
"ok": true,
"counts": {"total": 8, "running": 1, "done": 5, "error": 1, "failed": 1, "idle": 0},
"errors": [
{"serial": "192.168.1.100:5555", "model": "Pixel 6", "status": "failed",
"last_error": "重试3次失败", "task": "抖音养号", "attempt": 3, "updated": 1700000000.0}
]
}
```
`counts` 各状态计数;`errors` 为 `error`/`failed` 且有 `last_error` 的异常设备
(按最近心跳倒序,最多 50 条;已不在设备池的陈旧失败记录不展示)。
### GET /api/health
轻量健康检查(免登录,供运维探活):进程存活 + 设备/任务摘要,不暴露敏感信息。
**响应**:
```json
{"ok": true, "status": "up", "time": 1700000000.0, "device_total": 8,
"device_online": 6, "device_running": 2, "device_error": 1, "jobs": 3}
```
### POST /api/scan_foreground
手动触发前台 App 扫描(后台异步执行,不打扰设备)。
(权限:设备控制)
**响应**:
```json
{"ok": true, "msg": "扫描已启动"}
@@ -117,6 +179,18 @@
{"ok": true, "devices": ["192.168.1.100:5555", "192.168.1.101:5555"]}
```
### GET /api/devices/<serial>/apps
获取指定设备上已安装的应用列表(包名 + versionCode/versionName + APK 路径 + 应用名)。需登录。
**响应**:
```json
{"ok": true, "apps": [
{"package": "com.ss.android.ugc.aweme", "path": "/data/app/.../base.apk",
"version_code": 2500, "version_name": "25.0.0", "label": "抖音"}
]}
```
### GET /api/device/screenshot
获取设备当前画面截图(PNG)。
@@ -198,10 +272,12 @@
```
`next_run`:下次真正执行时间(已按运行窗口跳过窗口外触发点,格式 `YYYY-MM-DD HH:MM`);手动任务/已停用为 `null`。
(权限:任务管理)
### POST /api/jobs
创建任务计划。
(权限:任务管理)
**请求**(JSON):
```json
{
@@ -228,10 +304,12 @@
| `stop_cron` | (cron_stop 必填)到点停止本任务 worker |
| `window` | 可选,运行窗口 `{"start": "21:00", "end": "09:00"}`(每天重复,支持跨午夜)。窗口外定时触发和手动执行(`POST /api/jobs/:id/run`)均不启动,手动执行返回错误提示 |
(权限:任务管理)
### PUT /api/jobs/<job_id>
更新任务计划。只需传要更新的字段。
(权限:任务管理)
**请求**(JSON):
```json
{"params": {"watch_count": 100}}
@@ -242,25 +320,29 @@
{"ok": true, "msg": "任务已更新", "job": {"...": "..."}}
```
(权限:任务管理)
### DELETE /api/jobs/<job_id>
删除任务计划。
删除任务计划。不存在返回 404。
(权限:任务管理)
**响应**:
```json
{"ok": true, "msg": "任务已删除"}
```
(权限:任务管理)
### POST /api/jobs/<job_id>/run
立即执行任务(异步,不阻塞)。
(权限:任务管理)
**响应**:
```json
{"ok": true, "msg": "任务 抖音养号 已触发"}
```
(权限:任务管理)
### POST /api/jobs/<job_id>/toggle
启用/停用任务。
@@ -292,36 +374,50 @@
}
```
(权限:任务管理)
### POST /api/groups
创建分组。
创建分组。重名返回 400。
(权限:任务管理)
**请求**(JSON):
```json
{"name": "A组", "serials": ["192.168.1.100:5555"], "description": "测试组"}
```
(权限:任务管理)
**响应**:`{"ok": true, "msg": "分组已创建"}`
更新分组。
### PUT /api/groups/<name>
更新分组(serials/description 按需传字段)。`<name>` 不存在返回 404。
(权限:任务管理)
**请求**(JSON):
```json
{"serials": ["192.168.1.100:5555", "192.168.1.101:5555"], "description": "更新描述"}
```
**响应**:`{"ok": true, "msg": "分组已更新"}`
### DELETE /api/groups/<name>
删除分组。不存在返回 404。
(权限:任务管理)
删除分组。
**响应**:`{"ok": true, "msg": "分组已删除"}`
---
## 7. 运行控制
(权限:设备控制)
### POST /api/stop_device
停止单台设备的 worker(并阻止后续重试)。
(权限:设备控制)
**请求**(JSON):
```json
{"serial": "192.168.1.100:5555"}
@@ -332,67 +428,107 @@
{"ok": true, "msg": "已发送停止信号给 192.168.1.100:5555"}
```
(权限:设备控制)
### POST /api/stop_all
停止所有运行中的 worker。
(权限:设备控制)
**响应**:
```json
{"ok": true, "stopped": ["192.168.1.100:5555", "192.168.1.101:5555"]}
```
> 旧「释放设备占用」端点已随 STF 摘除移除,此接口不再存在;设备互斥由调度器内存锁保证。
### POST /api/device/clear_error
清除单台设备的异常状态(`error`/`failed` → `idle`),供设备列表"清除异常"按钮使用。
设备正在运行或等待重试时返回 400。
(权限:设备控制)
(已随 STF 摘除移除,此接口不再存在;设备互斥由调度器内存锁保证)
**请求**(JSON):
```json
{"serial": "192.168.1.100:5555"}
```
**响应**:
```json
{"ok": true, "released": ["192.168.1.100:5555"]}
{"ok": true, "msg": "已清除 192.168.1.100:5555 的异常状态"}
```
### POST /api/device/clear_all_errors
一键清除所有异常/失败设备(自动跳过正在运行/等待重试的)。
(权限:设备控制)
**响应**:
```json
{"ok": true, "cleared": 2, "msg": "已清除 2 台设备的异常状态"}
```
---
## 8. 用户管理
(仅管理员)
用户管理接口**仅管理员可用**(非管理员返回 403 `仅管理员可执行此操作`)。`uid` 为用户 id(整数)。
### GET /api/users
列出所有用户。
**响应**:
```json
{"ok": true, "users": [{"id": 1, "username": "admin", "is_admin": true}]}
{"ok": true, "users": [
{"id": 1, "username": "admin", "is_admin": true, "perms": []},
{"id": 2, "username": "user1", "is_admin": false, "perms": ["tasks", "devices"]}
]}
```
`perms`:用户被授予的业务权限位数组(存储值;管理员以 `is_admin` 为准,perms 照常保存,取消管理员后按 perms 生效)。
(仅管理员)
### POST /api/users
创建用户。
**请求**(JSON):
```json
{"username": "user1", "password": "pass123", "is_admin": false}
{"username": "user1", "password": "pass123", "is_admin": false, "perms": ["tasks"]}
```
`perms` 可选,默认无业务权限;`is_admin` 默认 false。
(仅管理员)
**响应**:`{"ok": true, "msg": "用户已创建"}`
更新用户(修改密码/管理员权限)。
### PUT /api/users/<uid>
更新用户(改密码 / 管理员权限 / 权限位),只需传要改的字段。`uid` 不存在返回 404。
**请求**(JSON):
```json
{"password": "newpass", "is_admin": true}
```
(仅管理员)
**响应**:`{"ok": true, "msg": "用户已更新"}`
> 不能取消最后一个管理员(返回 400)。
删除用户(不能删除 admin 和当前登录用户)。
### DELETE /api/users/<uid>
删除用户。`uid` 不存在返回 404。
**响应**:`{"ok": true, "msg": "用户已删除"}`
> 不能删除默认管理员 `admin`、不能删除当前登录用户、也不能删除最后一个管理员(均返回 400)。
---
## 9. 日志
(权限:日志查看)
### GET /api/logs
查看日志文件内容。
(权限:日志查看)
**参数**:
| 参数 | 类型 | 默认 | 说明 |
|------|------|------|------|
@@ -407,9 +543,10 @@
"ok": true,
"content": "2026-08-08 10:00:00 [INFO] [core.worker] ...",
"file": "core.log",
"files": {"core": "core.log", "task": "task.log", "web": "web.log", "action": "action.log"}
"files": ["core.log", "task.log", "web.log", "action.log"]
}
```
`files`:可选日志文件名数组。
---
@@ -417,24 +554,49 @@
### GET /api/custom_actions
列出所有自定义动作(步骤打包)。
列出所有自定义动作(步骤打包)。需登录。
(权限:任务管理)
**响应**:
```json
{"ok": true, "actions": [
{"id": "a1b2c3d4", "name": "登录流程", "icon": "📦",
"steps": [{"type": "click", "...": "..."}], "created_at": "2026-08-08 10:00:00"}
]}
```
### POST /api/custom_actions
创建自定义动作。
(权限:任务管理)
**请求**(JSON):
```json
{"name": "登录流程", "icon": "📦", "steps": [{"type": "click", "...": "..."}]}
```
(权限:任务管理)
**响应**:`{"ok": true, "msg": "动作已保存", "action": {...}}`
更新自定义动作。
### PUT /api/custom_actions/<action_id>
更新自定义动作(name/icon/steps,按需传字段)。不存在返回 404。
(权限:任务管理)
删除自定义动作。
**请求**(JSON):
```json
{"name": "登录流程 v2", "steps": [{"type": "click", "...": "..."}]}
```
**响应**:`{"ok": true, "msg": "已更新", "action": {...}}`
### DELETE /api/custom_actions/<action_id>
删除自定义动作。不存在返回 404。
(权限:任务管理)
**响应**:`{"ok": true, "msg": "已删除"}`
---
@@ -449,18 +611,22 @@
{"ok": true, "running": true}
```
(权限:设备控制)
### GET /api/uiauto/devices
获取 uiauto2 已连接的设备列表。
获取 uiauto2 已连接的设备列表。uiautodev 本地服务未运行(或列表获取失败)返回 503。
(权限:设备控制)
**响应**:
```json
{"ok": true, "devices": [{"serial": "192.168.1.100:5555", "model": "Pixel 6"}]}
```
(权限:设备控制)
### GET /api/uiauto/screenshot
通过 uiauto2 获取设备截图(JPEG)。
通过 uiauto2 获取设备截图(JPEG)。uiautodev 本地服务未运行返回 503。
(权限:设备控制)
**参数**:
| 参数 | 类型 | 说明 |
@@ -469,9 +635,11 @@
**响应**:成功返回 `image/jpeg`,失败返回 JSON 错误。
(权限:设备控制)
### GET /api/uiauto/elements
获取设备 UI 元素树。
获取设备 UI 元素树。uiautodev 本地服务未运行返回 503。
(权限:设备控制)
**参数**:
| 参数 | 类型 | 说明 |
@@ -523,10 +691,12 @@
}
```
(权限:应用管理)
### POST /api/apks/upload
上传 APK 文件(自动解析包名/版本/应用名)。
(权限:应用管理)
**请求**(multipart/form-data):
| 字段 | 类型 | 说明 |
|------|------|------|
@@ -537,14 +707,20 @@
{"ok": true, "apk": {"...": "..."}, "msg": "上传成功: 抖音"}
```
(权限:应用管理)
### DELETE /api/apks/<apk_id>
删除 APK 文件和记录。
(权限:应用管理)
**响应**:`{"ok": true, "msg": "..."}`;删除失败返回 400。
### POST /api/apks/install
批量安装 APK 到指定设备。
(权限:应用管理)
**请求**(JSON):
```json
{"apk_id": "abc123", "serials": ["192.168.1.100:5555", "192.168.1.101:5555"]}
@@ -557,13 +733,17 @@
### GET /api/apks/install/devices
可安装设备列表:设备池在线设备(标记 `pool`)+ 本机 adb 设备(**含 USB 有线连接**,serial 无冒号标记 `usb`)。
可安装设备列表:设备池在线设备 + 本机 adb 设备(含 USB 有线连接)。
安装弹窗用此列表,USB 设备安装时跳过 adb connect 直接安装。
`source` 取值:`pool`=设备池在线;`usb`=本机 USB 有线(serial 无冒号);`adb`=本机网络 adb(serial 含冒号)。
(权限:应用管理)
**响应**:
```json
{"ok": true, "devices": [
{"serial": "100.100.10.11:5555", "model": "22120RN86C", "source": "stf"},
{"serial": "100.100.10.11:5555", "model": "22120RN86C", "source": "pool"},
{"serial": "192.168.1.5:5555", "model": "", "source": "adb"},
{"serial": "ZY322ABCDEF", "model": "", "source": "usb"}
]}
```
@@ -595,10 +775,11 @@
---
## 13. 维护 / 工具(仅管理员)
## 13. 维护 / 工具
以下接口都**仅管理员可用**(普通用户即使有业务权限也访问不了,返回 403)。
设备池管理、远程看屏、adb 终端等集中在"工具"页(页内子分栏)。
设备池管理、自动发现、远程看屏/触控、定位、维护终端等集中在"工具"页(页内子分栏)。
**权限说明**:设备池管理、自动发现、远程看屏/触控、定位等接口需 `devices` 权限;
**adb 终端仅管理员可用**(见各节标注)。
### GET /api/devices/pool
@@ -636,6 +817,69 @@ IP:5555 设备添加后立即尝试 adb connect,并后台采集型号(getpro
批量采集池内在线设备的型号(后台执行)。**权限**:`devices`。
### GET /api/devices/discovery
自动发现状态 + 待连接列表(pending)+ 正式池断联设备。前端约 10s 轮询一次。**权限**:`devices`。
**响应**:
```json
{
"ok": true,
"enabled": true, "subnets": ["192.168.1.0/24"], "interval": 60, "port": 5555,
"scanning": false,
"last_scan": "2026-08-08 10:00", "last_result": {"found": 12, "verified": 3, "new": 1},
"last_error": "", "pending_count": 2,
"pending": [{"serial": "192.168.20.5:5555", "source": "lan",
"first_seen": "2026-08-08 10:00", "last_seen": "2026-08-08 10:05", "online": true}],
"pool_offline": [{"serial": "192.168.1.100:5555", "model": "Pixel 6", "name": "测试机1"}]
}
```
`pending` 只列当前在线设备(离线候选不可确认,下轮扫描自动更新);`pool_offline`
为正式池中已断联设备(发现线程每轮自动重连,也可手动触发重连)。
### POST /api/devices/discovery/scan
手动触发一轮扫描(后台执行,约 5-30 秒)。**权限**:`devices`。
扫描只验证并把新设备放进待连接池(pending),**确认后才入正式设备池**。
**响应**:
```json
{"ok": true, "msg": "扫描已启动(后台执行,约 5-30 秒)",
"result": {"found": 12, "verified": 3, "new": 1}}
```
扫描进行中返回 409;未配置网段返回 400 并附原因。
### POST /api/devices/discovery/confirm
确认连接:把待连接设备加入正式设备池并后台 adb connect/采型号。**权限**:`devices`。
**请求**(JSON):`{"serial": "192.168.20.5:5555", "name": "客厅机"}`
**响应**:`{"ok": true, "msg": "已加入设备池", "is_new": true}`
### POST /api/devices/discovery/ignore
忽略:从待连接列表删除(下轮扫描可能再次发现)。**权限**:`devices`。
**请求**(JSON):`{"serial": "..."}`
**响应**:`{"ok": true, "msg": "已忽略"}`
### POST /api/devices/discovery/reconnect
手动立即重连正式池中的断联设备(后台 adb connect + 采型号)。**权限**:`devices`。
日常无需手动——发现线程每轮(默认 60s)自动重连断联设备。
**请求**(JSON):`{"serial": "..."}`
**响应**:`{"ok": true, "msg": "重连已启动(约 5-15 秒生效)"}`
### POST /api/devices/discovery/settings
保存自动发现配置(部分字段更新)。**权限**:`devices`。
**请求**(JSON):`{"enabled": true, "subnets": ["192.168.1.0/24"], "interval": 60, "port": 5555}`
`interval` 需在 10-3600 秒之间;`subnets` 逐项校验 CIDR,非法返回 400。
**响应**:`{"ok": true, "msg": "已保存"}`
### GET /api/screen/stream
远程看屏:MJPEG 实时画面流(`multipart/x-mixed-replace`)。**权限**:`devices`。
@@ -649,12 +893,6 @@ IP:5555 设备添加后立即尝试 adb connect,并后台采集型号(getpro
`?serial=xxx` 指定设备。监控大屏按设备每 ~2.5s 轮询一帧(页面不可见时暂停);
一次性请求(非流),与任务并发安全(与任务截图同走 u2 minicap)。
### GET /wall
监控大屏页面(全屏深色控制室风格,登录后可访问):设备卡片网格(缩略图/型号/
状态/当前动作/进度)、顶部统计与时钟;状态每 5s 刷新、缩略图每 2.5s 轮询。
20 台设备整体开销约 0.2 核 CPU + 100KB/s 带宽,普通电脑无压力。
### POST /api/screen/tap
点击设备屏幕。**权限**:`devices`。
@@ -673,6 +911,26 @@ IP:5555 设备添加后立即尝试 adb connect,并后台采集型号(getpro
输入文字(需焦点在输入框)。**请求**(JSON):`{"serial": "...", "text": "你好"}`
### POST /api/screen/tap_text
按屏幕文字点击:先在 UI 树里做 text/description 子串匹配点元素中心(原生控件);
未命中则截图 OCR 找文字中心(WebView/图片/画布渲染文字)。**权限**:`devices`。
**请求**(JSON):`{"serial": "...", "text": "立即下载"}`
**响应**:
```json
{"ok": true, "found": true, "method": "ui", "matched": "立即下载", "x": 540, "y": 1200}
```
`method`:`ui` 或 `ocr`;`found=false` 表示屏幕确实没有该文字(业务结果,非设备错误)。
### GET /api/screen/size
获取设备屏幕原生分辨率(只读,供坐标换算——截图常是缩放图、操作需原生坐标)。**权限**:`devices`。
离线/不可达返回 503。
`?serial=xxx`
**响应**:`{"ok": true, "width": 1080, "height": 2400}`
### POST /api/device/screen_all
批量亮屏/息屏(并发)。**权限**:`devices`。
@@ -683,9 +941,25 @@ IP:5555 设备添加后立即尝试 adb connect,并后台采集型号(getpro
```
`serials` 可选:指定设备(离线自动过滤);不带则作用于全部在线设备。息屏会中断运行中的任务,前端有确认提示。
### POST /api/device/locate
定位设备:点亮屏幕并解锁(WAKEUP → dismiss-keyguard → MENU 兜底)。
`show=true` 时额外用设备浏览器打开平台 `/locate` 大字定位页(更醒目,但会切换前台,任务运行中慎用)。**权限**:`devices`。
**请求**(JSON):`{"serial": "192.168.1.100:5555", "show": true}`
**响应**:`{"ok": true, "msg": "192.168.1.100:5555 屏幕已点亮;已打开大字定位页(按返回键退出)"}`
### POST /api/device/locate/stop
结束定位:优先 force-stop 定位时启动的浏览器(无论前后台都能关掉);无记录时前台是
浏览器则 force-stop,否则按返回键轻量退出(不误杀任务应用)。**权限**:`devices`。
**请求**(JSON):`{"serial": "..."}`
**响应**:`{"ok": true, "msg": "已关闭浏览器 com.android.chrome"}`
### GET /api/adb/devices
维护终端设备列表:本地 adb 已连接(含 offline)+ 设备池已配置设备(标记 `pool`)。
维护终端设备列表(**仅管理员**):本地 adb 已连接(含 offline)+ 设备池已配置设备(标记 `pool`)。
供终端设备选择器使用——选中后前端自动附加 `-s <serial>`。
**响应**:
@@ -693,13 +967,13 @@ IP:5555 设备添加后立即尝试 adb connect,并后台采集型号(getpro
{"ok": true, "devices": [
{"serial": "100.100.10.11:5555", "state": "device"},
{"serial": "100.100.10.13:5555", "state": "offline"},
{"serial": "100.100.10.12:5555", "state": "stf"}
{"serial": "100.100.10.12:5555", "state": "pool"}
]}
```
### POST /api/adb/cmd
adb 远程终端:用平台 adb 二进制执行任意 adb 命令(20s 超时)。
adb 远程终端(**仅管理员**):用平台 adb 二进制执行任意 adb 命令(20s 超时)。
**请求**(JSON):
```json
@@ -716,7 +990,7 @@ adb 远程终端:用平台 adb 二进制执行任意 adb 命令(20s 超时
---
## 14. 步骤测试(仅管理员可触发,需"设备控制"权限)
## 14. 步骤测试(需"设备控制"权限)
### POST /api/steps/test
@@ -744,7 +1018,9 @@ adb 远程终端:用平台 adb 二进制执行任意 adb 命令(20s 超时
工具页"Tailscale 管理"子分栏,调用 Tailscale 官方 API v2。所有接口仅管理员可用。
前置:`.env` 配置 `TAILSCALE_API_KEY`(Settings → API Access Tokens)与
`TAILSCALE_TAILNET`(tailnet 名,个人账号一般为邮箱前缀);未配置返回 502 并附提示。
`TAILSCALE_TAILNET`(tailnet 名,个人账号一般为邮箱前缀)。
`GET /api/tailscale/status` 未配置时返回 `200 {"ok": true, "configured": false}` 并附 `hint` 提示;
其余接口在未配置/调用失败时返回 `502` 并附错误信息。
设备 IP 由 tailnet 分配,API 不可修改,列表只读展示。
### GET /api/tailscale/status
@@ -814,7 +1090,7 @@ adb 远程终端:用平台 adb 二进制执行任意 adb 命令(20s 超时
## 16. 工具(仅管理员)
工具页(剪贴板注入 / 应用版本管理 / 设备池管理)接口,均仅管理员可用。
工具页(剪贴板注入 / 应用版本管理)接口,均仅管理员可用。
### POST /api/tools/clipboard/set
@@ -825,8 +1101,12 @@ adb 远程终端:用平台 adb 二进制执行任意 adb 命令(20s 超时
{"serials": ["100.100.10.11:5555", "0123456789ABCDEF"], "text": "要注入的文字"}
```
设备来源与维护终端一致(本地 adb 含 USB + 设备池)。
实现:u2 `jsonrpc.setClipboard`(实测 `cmd clipboard` 在 MIUI 上不存在),支持中文/引号/换行;
IP 设备先 adb connect(已连接跳过,绝不 disconnect),USB 设备首次自动推送 atx-agent。
实现:通过 ClipInject(`com.example.clipinject`)透明 Activity 前台聚焦后写入剪贴板
(shell 启动前台 Activity 不受后台启动限制),再用 u2 读回比对校验,支持中文/引号/换行。
不再使用 u2 setClipboard / 自动推送 atx-agent(Android 10+ 禁止后台写剪贴板,旧 u2
调用"成功"但内容被系统静默丢弃)。
IP 设备先 adb connect(已连接跳过,绝不 disconnect);**设备未安装 ClipInject 时** am start
返回 unable to resolve Intent,接口明确报错提示先安装。
**响应**:
```json
@@ -849,3 +1129,193 @@ IP 设备先 adb connect(已连接跳过,绝不 disconnect),USB 设备
"results": {"100.100.10.11:5555": {"installed": true, "version_name": "28.5.0", "version_code": "280500"}}}
```
未安装返回 `installed: false`;查询失败的设备带 `error` 字段。
---
## 17. AI 控制台(仅管理员)
浏览器内 AI 助手(DeepSeek 式多会话):用 MCP 工具操作指定设备、多轮上下文延续、
成功后自动提炼经验记忆。**单实例:同时只允许一个 Agent 运行。** 以下接口均仅管理员可用。
### GET /api/agent/config
读 Agent 配置。`api_key` 打码回显在 `api_key_masked` 字段。
**响应**:
```json
{"ok": true, "api_base": "https://api.deepseek.com", "model": "deepseek-v4-flash-vision-exp",
"api_key_masked": "sk-***abcd", "default_serial": "", "max_steps": "40"}
```
### POST /api/agent/config
保存 Agent 配置(部分更新)。**请求**(JSON):
`{"api_base": "...", "model": "...", "api_key": "...", "default_serial": "...", "max_steps": 40}`
(`max_steps` 1-200,默认 40)
**响应**:`{"ok": true, "msg": "已保存"}`
### GET /api/agent/devices
AI 可用设备列表(在线状态 + 是否有任务运行,前端据此把 busy 设备禁选)。
**响应**:
```json
{"ok": true, "devices": [
{"serial": "192.168.1.100:5555", "model": "Pixel 6", "online": true,
"busy": false, "worker_status": "idle", "task_job": ""}
]}
```
### POST /api/agent/run
启动 Agent(后台线程执行,立即返回 `run_id`)。**请求**(JSON):
`{"prompt": "打开抖音并点赞前 3 条视频", "serial": "192.168.1.100:5555", "conversation_id": "abc..."}`
`serial` 也可省略、用配置的 `default_serial`;`conversation_id` 绑定会话(历史从会话加载)。
**校验**:未配 API Key/模型名 → 400;设备不在池/离线 → 400;设备正有任务运行 → 409;
已有 Agent 运行中 → 409。
**响应**:`{"ok": true, "run_id": "8f3a2c9d"}`
### GET /api/agent/run
当前 Agent 运行状态(多窗口/页面刷新恢复用)。
**响应**:
```json
{"ok": true, "state": "running"|"idle"|"done", "run_id": "...", "prompt": "...",
"serial": "...", "started": "10:00:01", "answer": "...", "error": "",
"history": [{"role": "user", "content": "..."}]}
```
### GET /api/agent/stream?run_id=
订阅事件流(SSE,EventSource)。事件:
- `event: delta` `{text, kind: content|reasoning}` — 流式文本增量
- `event: step` `{tool, args, image?}` — 工具调用完成(MCP 步骤,image 为缩略截图)
- `event: done` `{answer}` — 完成
- `event: error` `{message}` — 失败
- 空闲时每 15s 发一行 `: keepalive` 注释防超时;`done`/`error` 后关流
### POST /api/agent/stop
中断当前运行的 Agent(下一个检查点生效,数秒内)。无运行中任务返回 400。
**响应**:`{"ok": true, "msg": "已请求停止"}`
### POST /api/agent/clear
清空当前对话历史。
**响应**:`{"ok": true, "msg": "已清空"}`
### GET /api/agent/conversations
会话列表(按最近更新倒序)。
**响应**:
```json
{"ok": true, "conversations": [
{"id": "abc...", "title": "打开抖音点赞", "updated_at": "2026-08-08 10:00", "count": 3}
]}
```
`count` 为轮数(用户+助手消息对数)。
### POST /api/agent/conversations
新建会话(空消息)。
**响应**:`{"ok": true, "id": "新会话id", "title": "新会话"}`
### GET /api/agent/conversations/<conv_id>
会话详情(全部消息文本)。不存在返回 404。
**响应**:
```json
{"ok": true, "id": "...", "title": "...", "messages": [{"role": "user", "content": "..."}],
"created_at": "...", "updated_at": "..."}
```
### DELETE /api/agent/conversations/<conv_id>
删除会话(消息一并删除,不可恢复)。
**响应**:`{"ok": true, "msg": "会话已删除"}`
### POST /api/agent/conversations/<conv_id>/rename
重命名会话。**请求**(JSON):`{"title": "新标题"}`
**响应**:`{"ok": true, "msg": "已重命名"}`
### GET /api/agent/experience
经验记忆库列表(自进化,含最近一次巡检结论)。
**响应**:
```json
{"ok": true, "running": false, "last": "2026-08-08 03:47", "last_summary": "评审 5 条,建议删除 1 条(待人工确认)",
"experiences": [{"id": 1, "task_prompt": "打开抖音并点赞", "recipe": "...", "tool_seq": "...",
"hits": 3, "created_at": "...", "audit": {"verdict": "delete", "score": 3,
"reason": "...", "action": "pending", "at": "..."}}]}
```
`audit` 为最近一次 AI 巡检结论(无则 null)。
### POST /api/agent/experience/delete
人工确认删除经验(真删,巡检绝不自动删)。**请求**(JSON):`{"id": 1}`
**响应**:`{"ok": true, "msg": "经验 #1 已删除"}`
### POST /api/agent/experience/audit
手动触发一轮经验巡检(后台线程,AI 评审只建议不删)。巡检进行中返回 409。
**响应**:`{"ok": true, "msg": "巡检已启动,完成后刷新列表查看建议"}`
### POST /api/agent/experience/keep
人工保留经验(撤销"建议删除",后续巡检不再重复建议)。**请求**(JSON):`{"id": 1}`
**响应**:`{"ok": true, "msg": "经验 #1 已保留"}`
---
## 18. 系统备份(仅管理员)
整库备份导出/导入(users.db + 可选 APK 文件)。导入涉及整库替换,仅管理员可用。
### POST /api/system/backup/export
生成导出 zip 并作为附件返回(含 users.db 一致快照 + manifest.json + 可选 `apks/`)。
**请求**(JSON):`{"include_apk": true}`
**响应**:`application/zip` 附件下载(`download_name` 形如 `export_20260808_101000.zip`)。
### POST /api/system/backup/preview
上传备份文件(multipart 字段 `file`,支持 .zip 或 .db)→ 暂存并校验 → 返回预览。
校验失败返回 400。
**响应**:
```json
{"ok": true, "token": "12位hex", "preview": {
"file_name": "export_xxx.zip", "file_size": 123456,
"integrity": "ok", "schema_version": 4, "current_schema_version": 4,
"tables": [{"table": "user", "label": "用户", "rows": 3}],
"missing_optional": [], "warnings": ["备份为全量快照:含敏感信息,请妥善保管"]
}}
```
### POST /api/system/backup/apply
确认应用导入:自动备份当前库到 `BACKUP_DIR/pre_restore_*.db`(安全网),再把暂存库
落为「待生效恢复任务」。**重启 web_server 后生效**。token 无效/过期返回 400。
**请求**(JSON):`{"token": "12位hex"}`
**响应**:
```json
{"ok": true, "backup_name": "pre_restore_20260808_101000.db",
"message": "恢复任务已生成:当前库已自动备份,重启 web_server 后即应用导入的数据"}
```