菜单

AI控制云机API

DuoPlus云机自动化控制API

该API为云机内部提供出来的服务,可通过该API接口直接控制云机,执行一些自动化操作,比如:获取屏幕和UI元素、点击和长按、文本输入和键盘、滑动和滚动、应用与页面导航、等待与元素读取等

接入前提:在 DuoPlus 控制台“自动化”→“API”获取 API Key。目标云机须已开机(status == 1),且 HTTP 服务已启用(http_status 为 1 或 "1")。若云机尚未开机,先通过控制台或平台开机接口启动;本文件不包含开机接口。Gateway 使用该 API Key 作为 Bearer Token;部署时须确保设备侧鉴权与该 Key 匹配,否则可能返回 401。

1. 获取云机 IP、地区和自动化支持状态

调用 HTTP Gateway 前,必须先通过平台 list 接口取得目标云机的 ip 和 region,并确认 status == 1、http_status 为 1。

1.1 请求入口

text 复制代码
POST https://openapi.duoplus.cn/api/v1/cloudPhone/list

请求头:

http 复制代码
Content-Type: application/json
Lang: zh
DuoPlus-API-Key: <API_KEY>

1.2 请求参数

按云机 ID 精确查询:

json 复制代码
{
  "image_id": ["IMAGE_ID"],
  "page": 1,
  "pagesize": 100
}

也可按名称查询:

json 复制代码
{
  "name": "phone-name",
  "page": 1,
  "pagesize": 100
}
参数 必填 说明
page 否 页码,从 1 开始;不传默认第 1 页
pagesize 否 每页数量,最大 100;不传默认 10
image_id 否 云机 ID 数组,推荐使用精确 ID
name 否 云机名称;名称可能重复
link_status 否 连接状态数组
group_id 否 分组 ID
proxy_id 否 代理 ID

1.3 自动化必需的返回字段

响应包络示意:

json 复制代码
{
  "code": 200,
  "message": "success",
  "data": {
    "list": [
      {
        "id": "IMAGE_ID",
        "name": "phone-name",
        "status": 1,
        "ip": "10.0.0.10",
        "region": "sg",
        "http_status": 1
      }
    ],
    "total_page": 1
  }
}

上例只展示自动化需要的关键字段,服务端可能返回更多设备信息。

字段 用途
data.list[].id 云机 ID,用于确认选中了正确设备
data.list[].status 1 表示云机已开机;其他状态需先处理
data.list[].ip 作为 Gateway 请求头 CloudIP
data.list[].region 作为 Gateway 请求头 Region;以选中云机的实际返回值为准
data.list[].http_status 1/"1" 表示 HTTP 服务已启用;0/"0" 表示未启用
data.total_page 总页数;未按 ID 精确查询时需要遍历全部分页

成功必须同时满足 HTTP 请求成功和响应顶层 code == 200。

1.4 cURL 示例

bash 复制代码
curl -sS 'https://openapi.duoplus.cn/api/v1/cloudPhone/list' \
  -H 'Content-Type: application/json' \
  -H 'Lang: zh' \
  -H "DuoPlus-API-Key: $DUOPLUS_API_KEY" \
  --data '{"image_id":["IMAGE_ID"],"page":1,"pagesize":100}'

核对匹配项的 id 后,从该项提取:

text 复制代码
CloudIP = matched_phone.ip
Region  = matched_phone.region

只有 status == 1 且 String(http_status) == "1" 时才能继续调用自动化 Gateway。ip 不为空并不代表自动化已启用。若按名称查找,应遍历 total_page 的所有分页,并在名称重复时用 ID 确认目标。平台 OpenAPI 每个接口限 1 QPS;分页请求也应控制频率。

2. 自动化调用入口

所有自动化操作均请求:

text 复制代码
POST https://agent-gateway.duoplus.cn/agent-command
http 复制代码
Content-Type: application/json
Authorization: Bearer <API_KEY>
Region: <REGION>
CloudIP: <CLOUD_IP>
请求头 说明
Authorization Gateway 鉴权,格式为 Bearer <API_KEY>
Region 目标云机 list 返回的 region
CloudIP 云机私网 IP

云机必须已运行并启用 HTTP 服务。Gateway 返回 401 时,应检查 API Key 与设备鉴权配置,不要盲目重试。第 13 节 cURL 中的区域和 IP 示例值必须替换为目标云机的实际返回值。

3. 通用模型

operation 用途
health 检查 Gateway/后端健康状态
ready 检查自动化执行器是否就绪
submit 获取屏幕/UI 或执行 UI 动作
query 查询已保留的命令结果
stop 停止正在运行或卡住的任务

执行动作的通用请求:

json 复制代码
{
  "operation": "submit",
  "command_id": "click-element-1730000000000-a1b2c3d4",
  "task_id": "click-element-task-1730000000000-a1b2c3d4",
  "action": "execute",
  "payload": {
    "task_type": "ai",
    "task_id": "click-element-task-1730000000000-a1b2c3d4",
    "action": "execute",
    "action_name": "CLICK_ELEMENT",
    "params": {
      "text": "Search",
      "wait_after": 500
    }
  }
}
  • command_id 与 task_id 应唯一。
  • action_name 使用大写动作名。
  • 动作参数放入 payload.params。
  • 可在顶层加入 Unix 毫秒时间戳 deadline_at。
  • 同步动作最长约 300 秒,客户端超时可设为约 310 秒。

任意动作的 params 均可带:

参数 说明
wait_before 动作前等待时间,毫秒
wait_after 动作后等待时间,毫秒;导航/加载建议 500–1500

4. 健康与就绪检查

健康检查:

json 复制代码
{"operation":"health"}

就绪检查:

json 复制代码
{"operation":"ready"}

响应满足 executor == "ready" 或 ready == true 即表示执行器就绪。收到 503 或 NOT_READY 时,可在启动超时内继续轮询。

5. 获取屏幕和 UI 元素

5.1 请求

json 复制代码
{
  "operation": "submit",
  "command_id": "ui-state-1730000000000-a1b2c3d4",
  "task_id": "ui-state-task-1730000000000-a1b2c3d4",
  "action": "get_ui_state",
  "payload": {
    "task_type": "ai",
    "task_id": "ui-state-task-1730000000000-a1b2c3d4",
    "action": "get_ui_state",
    "lang": "zh"
  }
}

5.2 响应解析

result_json 是 JSON 字符串,需要二次解析:

javascript 复制代码
const outer = await response.json();
const result = JSON.parse(outer.result_json);

解析后的对象可能包含:

  • success:动作是否成功;
  • UI 元素树:用于按文本、资源 ID、内容描述等定位控件;
  • screenshot:Base64 编码的当前屏幕截图。

截图解码:

javascript 复制代码
const encoded = result.screenshot.replace(/^data:[^,]+,/, "");
const bytes = Buffer.from(encoded, "base64");

第一次操作前读取 UI;每次点击、输入、滑动或导航后再次读取,以真实屏幕变化验证结果。

6. 点击与长按

6.1 元素点击 CLICK_ELEMENT

json 复制代码
{"text":"Continue","wait_after":800}
参数 说明
resource_id Android 资源 ID,通常最稳定
content_desc 无障碍内容描述
text 元素显示文本
class_name Android 控件类名
element_order 多个元素匹配时的序号,从 0 开始
json 复制代码
{"resource_id":"com.example:id/login"}
json 复制代码
{"content_desc":"Search"}
json 复制代码
{"text":"Item","element_order":1}

优先选择:resource_id → content_desc/text → 坐标。

6.2 元素长按 LONG_ELEMENT

json 复制代码
{"text":"Message","duration":1200}

选择字段同 CLICK_ELEMENT,duration 单位为毫秒。

6.3 坐标动作

点击 CLICK_COORDINATE:

json 复制代码
{"x":500,"y":420,"wait_after":500}

长按 LONG_COORDINATE:

json 复制代码
{"x":500,"y":420,"duration":1200}

双击 DOUBLE_TAP_COORDINATE:

json 复制代码
{"x":500,"y":420}

坐标为 0..1000 的相对值,左上角 (0,0),右下角 (1000,1000):

text 复制代码
relative_x = round(pixel_x / screenshot_width  * 1000)
relative_y = round(pixel_y / screenshot_height * 1000)

坐标动作前应立即截图并取目标中心;页面跳转或旋转后不能复用旧坐标。

7. 文本输入和键盘

7.1 输入 INPUT_CONTENT

json 复制代码
{"content":"hello world","clear_first":true}

输入前必须先聚焦输入框:获取 UI → 点击输入框 → INPUT_CONTENT → 必要时发送 Enter → 再次获取 UI 验证。

7.2 键盘 KEYBOARD_OPERATION

json 复制代码
{"key":"enter"}

支持:enter、delete、tab、escape、space。

8. 滑动与滚动

动作名:SLIDE_PAGE。

默认手势:

json 复制代码
{"direction":"up","wait_after":600}

支持 up、down、left、right。向下浏览内容时,手指向上滑,因此使用 up。

自定义轨迹:

json 复制代码
{
  "direction": "up",
  "start_x": 520,
  "start_y": 780,
  "end_x": 500,
  "end_y": 260,
  "wait_after": 600
}

先尝试默认手势;每次滑动后重新读取屏幕。连续无效时轻微调整轨迹,同一目标通常最多尝试 3 次。

9. 应用与页面导航

打开应用 OPEN_APP:

json 复制代码
{"package_name":"com.android.settings","wait_after":1200}

返回桌面 GO_TO_HOME:

json 复制代码
{}

返回上一页 PAGE_BACK:

json 复制代码
{}

10. 等待与元素读取

固定等待 WAIT_TIME:

json 复制代码
{"wait_time":1500}

等待元素 WAIT_FOR_SELECTOR:

json 复制代码
{"resource_id":"com.example:id/result","timeout":15}

或:

json 复制代码
{"text":"Completed","timeout":10}

timeout 默认 10 秒。

读取单个元素文本 GET_SINGLE_ELEMENT_TEXT:

json 复制代码
{"resource_id":"com.example:id/title"}

可使用 text、resource_id、class_name 等选择字段。

11. 查询结果与停止任务

查询命令结果:

json 复制代码
{"operation":"query","command_id":"<COMMAND_ID>"}

停止卡住的任务:

json 复制代码
{
  "operation": "stop",
  "command_id": "stop-1730000000000-a1b2c3d4",
  "task_id": "<TASK_ID>",
  "reason": "operation timeout"
}

stop 仅用于长时间运行或卡住的任务,不用于正常完成操作。

12. 成功判定

对 submit 的 UI 动作,不能仅根据 HTTP 200 判断成功。必须同时满足:

  1. HTTP 请求成功;
  2. 外层 state == "SUCCEEDED";
  3. 二次解析后的 result_json.success == true;
  4. 重新获取 UI 后,屏幕状态符合预期。
javascript 复制代码
const outer = await response.json();
if (outer.state !== "SUCCEEDED") throw new Error("Gateway command failed");

const result = JSON.parse(outer.result_json);
if (result.success !== true) throw new Error("UI action failed");

// 再次调用 get_ui_state,验证真实界面结果。

13. 完整 cURL 示例

13.1 获取当前屏幕

bash 复制代码
curl -sS 'https://agent-gateway.duoplus.cn/agent-command' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $DUOPLUS_API_KEY" \
  -H "Region: $DUOPLUS_REGION" \
  -H "CloudIP: $DUOPLUS_CLOUD_IP" \
  --data '{
    "operation":"submit",
    "command_id":"ui-state-UNIQUE_ID",
    "task_id":"ui-state-task-UNIQUE_ID",
    "action":"get_ui_state",
    "payload":{
      "task_type":"ai",
      "task_id":"ui-state-task-UNIQUE_ID",
      "action":"get_ui_state",
      "lang":"zh"
    }
  }'

13.2 点击元素

bash 复制代码
curl -sS 'https://agent-gateway.duoplus.cn/agent-command' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $DUOPLUS_API_KEY" \
  -H "Region: $DUOPLUS_REGION" \
  -H "CloudIP: $DUOPLUS_CLOUD_IP" \
  --data '{
    "operation":"submit",
    "command_id":"click-UNIQUE_ID",
    "task_id":"click-task-UNIQUE_ID",
    "action":"execute",
    "payload":{
      "task_type":"ai",
      "task_id":"click-task-UNIQUE_ID",
      "action":"execute",
      "action_name":"CLICK_ELEMENT",
      "params":{"text":"Continue","wait_after":800}
    }
  }'

执行第 13 节示例前,设置 DUOPLUS_API_KEY、DUOPLUS_REGION、DUOPLUS_CLOUD_IP,并将每次请求中重复出现的 UNIQUE_ID 换成新的唯一值;同一次请求的顶层和 payload 中的 task_id 必须一致。示例为 Bash 语法;PowerShell 中环境变量写法不同。

14. 自动化建议

  • 每次动作前读取 UI,每次动作后重新读取并验证。
  • 优先使用元素选择器;WebView、Canvas 或无法识别的控件再使用坐标。
  • 导航或加载动作加入约 500–1500 ms 的 wait_after。
  • 同一策略最多重试 3 次;无变化时更换选择器、坐标或导航方式。
  • 验证码、支付、账户恢复或破坏性确认应暂停,由用户决定。
  • 截图只是观察结果,不能代替任务完成验证。

15. 执行 Shell 命令

动作名:SHELL_COMMAND。

该动作在目标云机中执行一条非交互式 Shell 命令,并在命令成功时通过
result_json.result_text 返回标准输出。它适合读取系统属性、查询包信息和执行经过审核的
设备维护命令。

15.1 参数

参数 必填 说明
command 是 要执行的非空 Shell 命令
need_screenshot 否 是否在命令执行后返回截图,默认 true;纯命令操作建议传 false
wait_before 否 执行命令前等待的时间,单位毫秒
wait_after 否 执行命令后等待的时间,单位毫秒

参数示例:

json 复制代码
{
  "command": "getprop ro.build.version.release",
  "need_screenshot": false
}

当前设备执行器对单条 Shell 命令的内部等待时间约为 20 秒,不支持通过请求参数修改。
不要提交交互式命令、持续运行的前台进程或无法在超时时间内自行结束的命令。

15.2 完整请求

bash 复制代码
UNIQUE_ID="$(date +%s%3N)-$RANDOM"

curl --max-time 30 -sS \
  'https://agent-gateway.duoplus.cn/agent-command' \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $DUOPLUS_API_KEY" \
  -H "Region: $DUOPLUS_REGION" \
  -H "CloudIP: $DUOPLUS_CLOUD_IP" \
  --data "{
    \"operation\": \"submit\",
    \"command_id\": \"shell-${UNIQUE_ID}\",
    \"task_id\": \"shell-task-${UNIQUE_ID}\",
    \"action\": \"execute\",
    \"payload\": {
      \"task_type\": \"ai\",
      \"task_id\": \"shell-task-${UNIQUE_ID}\",
      \"action\": \"execute\",
      \"action_name\": \"SHELL_COMMAND\",
      \"params\": {
        \"command\": \"getprop ro.build.version.release\",
        \"need_screenshot\": false
      }
    }
  }"

每次请求必须生成新的 command_id 和 task_id;同一次请求的顶层 task_id 与
payload.task_id 必须一致。

15.3 响应与输出

Gateway 同步返回命令终态。外层响应示意:

json 复制代码
{
  "command_id": "shell-1730000000000-1234",
  "task_id": "shell-task-1730000000000-1234",
  "state": "SUCCEEDED",
  "success": true,
  "message": "Execute shell command: getprop ro.build.version.release - Success",
  "result_json": "{\"task_id\":\"shell-task-1730000000000-1234\",\"success\":true,\"message\":\"Execute shell command: getprop ro.build.version.release - Success\",\"result_text\":\"15\\n\",\"screenshot\":\"\",\"current_app\":\"\"}"
}

result_json 是 JSON 字符串,需要二次解析:

javascript 复制代码
const outer = await response.json();
if (outer.state !== "SUCCEEDED" || outer.success !== true) {
  throw new Error(outer.message || "Shell command failed");
}

const result = JSON.parse(outer.result_json);
if (result.success !== true) {
  throw new Error(result.message || "Shell command failed");
}

console.log(result.result_text); // 命令成功时的标准输出

成功必须同时满足:

  1. HTTP 请求成功;
  2. 外层 state == "SUCCEEDED";
  3. 外层 success == true;
  4. 二次解析后的 result_json.success == true。

result_text 只保证包含命令成功时捕获到的标准输出。命令失败时不要依赖它获取 stderr,
应以 success 和 message 判断结果。不要执行会产生大量输出的命令,以免超过 Gateway/Binder
结果大小限制。

15.4 安全要求

SHELL_COMMAND 具备高权限和高风险。调用方必须遵守以下规则:

  • 获取 Gateway API Key 的调用方可能获得目标云机的 Shell 执行能力,应按高敏感凭证保存,
    禁止写入日志、前端代码或公开仓库。
  • 只能执行调用方预先审核或加入白名单的命令,不得直接拼接用户输入、网页内容、AI 输出或
    其他非可信数据。
  • 命令以设备自动化执行器实际拥有的权限运行;不同系统镜像权限可能不同,不应假设它处于
    普通应用沙箱内。
  • 涉及删除文件、修改系统配置、停止系统服务、安装或卸载应用、重启设备等操作前,必须由
    用户明确确认。
  • 不要通过该接口读取、回传或记录密码、令牌、Cookie、私钥及其他敏感信息。
  • 推荐默认传递 "need_screenshot": false,仅在确实需要验证界面变化时请求截图。

建议后端针对 SHELL_COMMAND 单独配置权限范围、调用审计、速率限制和命令白名单,而不是仅
依赖通用 Gateway Bearer Token。

上一个
如何永久开启APP的无障碍权限
下一个
云手机RPA文档
最近修改: 2026-10-14Powered by