手机端点
所有 /v1/phones 端点,涵盖云手机、应用、实时屏幕、直接控制和智能体运行,包括参数、限制、响应和错误。
手机对象#
属性
idstringph_ 后跟 20 个字符。
objectstring始终为 phone。
statusstringstarting(约一分钟,安装启动应用时会更久)、ready、failed 或 deleted。
namestring | null你设置的标签。
userstring | null你自己的终端用户 id(如果设置了)。
metadataobject | null你随手机存储的数据。
startup_appsarray你在 apps 中列出的每个应用对应一项:source、status(pending、installed 或 failed)、packages(安装的包名)和 error。
created_atready_atUnix 时间戳(秒)。
deletes_after_idle_atinteger | null如果在此之前未被使用,手机将在这个时间被删除(最后一次活动后 1 小时)。
awake_secondsinteger截至目前已计费的唤醒时长。
charge_usdnumber手机截至目前的费用。
end_reasonstring | null被删除的原因:deleted(由你删除)、idle、balance 或 failed。
errorstring | null启动失败的原因。
创建手机#
/v1/phones创建一台手机,并在其启动期间返回 201 和手机对象。之后用获取手机加上 wait 等待它就绪。余额中需要至少有 15 分钟的手机时长。每个账户最多同时拥有 3 台手机。
请求体参数
appsarray of strings手机 ready 之前要安装的应用,最多 20 个:指向 .apk、.apks 或 .xapk 文件的 https:// 链接,或已上传 APK 的名称。安装失败不会导致手机启动失败,请查看 startup_apps。
namestring标签,最多 80 个字符。
userstring你的终端用户 id(最多 200 个字符),可用 GET /v1/phones?user= 列出该用户的手机。
metadataobject最多 4 KB 的自定义数据。
waitinteger挂起请求、等待手机就绪的秒数,最多 50。
curl https://lovelyrouter.com/v1/phones \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "content-type: application/json" \
-d '{"apps": ["https://f-droid.org/F-Droid.apk"], "wait": 50}'{
"id": "ph_2758eedebf08496f87e0",
"object": "phone",
"status": "starting",
"name": null,
"user": null,
"metadata": null,
"startup_apps": [{ "source": "https://f-droid.org/F-Droid.apk", "status": "pending" }],
"created_at": 1793308400,
"ready_at": null,
"last_active_at": 1793308400,
"deletes_after_idle_at": 1793312000,
"awake_seconds": 0,
"charge_usd": 0,
"deleted_at": null,
"end_reason": null,
"error": null,
"price_per_hour_usd": 1.20
}列出手机#
/v1/phones返回 {"object": "list", "data": [phone, …]},最新的在前。user= 只保留某个终端用户的手机;all=1 会包含已删除的手机。
删除手机#
/v1/phones/{id}删除手机及其中的应用和数据,停止其上的运行,并结算最后几分钟的费用。返回状态为 deleted 的手机对象。手机在 1 小时没有活动后也会被自动删除。
获取截图#
/v1/phones/{id}/screenshot以 WebP 图片(image/webp,短边 540 像素)返回当前屏幕。带上 format=json 时,返回 {screenshot, width, height, app, elements},其中图片为 base64 编码。
curl https://lovelyrouter.com/v1/phones/ph_2758eedebf08496f87e0/screenshot \
-H "Authorization: Bearer $LOVELY_API_KEY" -o screen.webp列出元素#
/v1/phones/{id}/elements屏幕上的内容,来自 Android 的视图树:{"app", "activity", "width", "height", "data": [element, …]}。每个元素包含 id、type(Button、EditText、TextView…)、text、desc(无障碍标签)、resource(视图 id)、x 和 y(中心点)、w 和 h,以及 flags(clickable、long-clickable、scrollable、editable、checked/unchecked、focused、selected、password、disabled)。不使用视图绘制的屏幕(游戏、部分画布)只会列出很少的元素,这时请使用截图。
发送操作#
/v1/phones/{id}/actions在手机上执行一个操作,返回 {"ok": true}(或 ok: false 加 error)。加上 "screenshot": true 可在屏幕稳定后获得 screenshot(base64 WebP)。智能体运行正在操控手机时返回 409。
操作
{"action": "tap", "x", "y"}点击一个点。
{"action": "long_press", "x", "y", "ms"?}按住不放(默认 800 毫秒)。
{"action": "swipe", "x", "y", "to_x", "to_y", "ms"?}从一个点拖动到另一个点(默认 300 毫秒)。
{"action": "scroll", "direction", "x"?, "y"?}up、down、left 或 right;down 会显示下方的内容。在指定点滚动,或在屏幕中央滚动。
{"action": "type", "text", "clear"?, "submit"?}在当前聚焦的输入框中输入,支持任何语言。clear 先清空内容;submit 输入后按 Enter。
{"action": "key", "key"}back、home、enter、recents、delete、tab、escape、search、menu、power、volume_up、volume_down 或 wakeup。
{"action": "open_url", "url"}打开一个链接:在 Chrome 中打开网页,或打开应用链接(market://、应用自己的 scheme)。
{"action": "launch", "package"}打开一个已安装的应用,例如 org.fdroid.fdroid。
{"action": "stop_app", "package"}强制停止一个应用。
{"action": "wait", "seconds"}最多 60 秒。
{"action": "rotate", "orientation"}旋转手机:portrait(竖屏)或 landscape(横屏)。锁定方向的应用(很多游戏)保持原方向。实时屏幕会跟着变化。
{"action": "shell", "command"}在手机上执行一条 adb shell 命令;结果中包含 output。
curl https://lovelyrouter.com/v1/phones/ph_2758eedebf08496f87e0/actions \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "content-type: application/json" \
-d '{"action": "type", "text": "天气 Tokyo", "submit": true}'获取实时屏幕链接#
/v1/phones/{id}/screen返回 {"url", "expires_at"}:一个显示手机实时屏幕的页面。拿到链接的任何人都可以观看手机,并用鼠标和键盘操控它,所以只把它分享给应该使用这台手机的人。该页面可以用 iframe 嵌入。
请求体参数
ttl_secondsinteger默认 3600链接的有效时长:60 到 86,400。已经打开的屏幕会继续可用。
view_onlyboolean以只读模式打开页面。这只是显示设置,不是权限:观看者可以把它关掉。
上传 APK#
/v1/phones/apks/{name}存储一个 APK,之后可以按 name(字母、数字、_、.、-,最多 80 个字符)在任何手机上安装。把文件作为请求体发送,最大 100 MB;更大的文件(最大 500 MB)请分片上传。.apk、.apks 和 .xapk 都可以;安装 .xapk 时,其中的游戏数据(OBB 文件)会复制到手机上。上传到同一名称会替换原文件。返回 201 和 {name, bytes, sha256}。每个账户最多保存 50 个,超过 1 GB 的存储会计费。
GET /v1/phones/apks 列出已上传的 APK,DELETE /v1/phones/apks/{name} 删除其中一个。
curl -X PUT https://lovelyrouter.com/v1/phones/apks/myapp.apk \
-H "Authorization: Bearer $LOVELY_API_KEY" \
--data-binary @myapp.apk分片上传大 APK#
/v1/phones/apks/{name}/uploads超过 100 MB(最大 500 MB)的文件要分片上传:先告知文件大小,逐个发送分片,最后完成。结果与上传 APK 相同:文件以 name 保存,并替换同名文件。
Body parameters
bytesinteger必填文件大小(字节),最大 524,288,000(500 MB)。
返回 201 和 {id, part_size, parts, parts_done, expires_at}。然后:
- 对从 1 到
parts的每个分片n,调用PUT /v1/phones/apks/{name}/uploads/{id}/{n},请求体是文件从(n-1) × part_size开始的字节。除最后一个分片外,每个分片都是part_size字节(50 MB),最后一个是剩余部分。分片可以按任意顺序或同时发送,重新发送的分片会替换之前的。 POST /v1/phones/apks/{name}/uploads/{id}/complete合并分片,返回201和{name, bytes, sha256};如果还缺分片,返回409并列出缺少的分片。
GET /v1/phones/apks/{name}/uploads/{id} 显示已收到哪些分片(parts_done),对它调用 DELETE 会放弃这次上传。24 小时内未完成的上传会被丢弃。
存储#
上传的 APK 前 1 GB 免费存储。超出部分每 GB 每月 $0.30,每小时从余额中扣除。不再需要的 APK 可以用 DELETE /v1/phones/apks/{name} 删除。上传需要余额大于零。
# 120 MB 的文件:3 个分片
curl https://lovelyrouter.com/v1/phones/apks/game.xapk/uploads \
-H "Authorization: Bearer $LOVELY_API_KEY" \
-H "content-type: application/json" \
-d "{\"bytes\": $(stat -c %s game.xapk)}"
split -b 50M -d -a 2 game.xapk part.
for n in 1 2 3; do
curl -X PUT https://lovelyrouter.com/v1/phones/apks/game.xapk/uploads/upl_.../$n \
-H "Authorization: Bearer $LOVELY_API_KEY" \
--data-binary @part.0$((n-1))
done
curl -X POST https://lovelyrouter.com/v1/phones/apks/game.xapk/uploads/upl_.../complete \
-H "Authorization: Bearer $LOVELY_API_KEY"运行对象#
属性
idstringprun_ 后跟 20 个字符。
objectstring始终为 phone.run。
phone_idstring运行所在的手机。
statusstringqueued(手机正在启动)、running、waiting(它向你提了问题)、succeeded、failed 或 stopped(被你停止,或达到 max_steps)。
goalmodel你发送的内容。
stepsinteger截至目前的步数。
questionstring | null处于 waiting 时:智能体需要你提供什么。
answerstring | null完成后的结果。
errorstring | null失败或停止的原因。
llm_usdnumber模型 token 的费用(运行结束时设置)。
tracearray带上 trace=1 时:每一步的 step、note(智能体看到了什么、为什么这样操作)、action、args、app、ok、error 和 ms;如果操作后屏幕完全没变,还会有 unchanged: true。
启动运行#
/v1/phones/runs创建一台手机并在上面启动智能体,或者用 phone_id 在你已有的某台手机上启动。返回 202 和运行对象。一台手机同一时间只运行一个目标:如果该手机上已有活动的运行,返回 409。
请求体参数
goalstring必填要做什么,用平实的语言描述,并写明任务需要的所有值。最多 10,000 个字符。
phone_idstring在这台手机上运行,而不是新建一台。POST /v1/phones/{id}/runs 效果相同。
appsname用于新手机,同创建手机。
modestring默认 smartsmart:由模型决定每一步(每步几秒钟)。fast:由快速决策模型在不到一秒内选出几乎每一步(它总会行动,适合游戏),model 只用来写答案或向你提问。见快速模式。
planboolean默认 true仅限快速模式:一个教练模型在快速模型旁边运行(费用已包含在手机价格中)。它为目标写一份简短的策略(游戏的话,写明优先走哪些步)和一条当前局面的提示,每隔几步或运行看起来卡住时会重写这两部分。快速模型从不等它:每一步都使用最新的版本。运行对象的 strategy、situation 和 sidecar_updates 字段会显示这些内容。
modelstring默认 gemini-3.8-flashGET /v1/models 中的任意对话模型。其 token 按正常价格计费。
max_stepsinteger默认 401 到 200。达到上限的运行以 stopped 结束,error 为 reached the step limit (N)。
waitinteger挂起请求、等待运行结束或提问的秒数,最多 50。
获取运行#
/v1/phones/runs/{id}返回运行对象。wait(最多 50 秒)会挂起请求,直到运行结束或向你提问;trace=1 会附上每一步;加上 after_step=N 时,运行超过 N 步也会立即返回。GET /v1/phones/runs?phone_id= 列出运行,最新的在前。
错误#
| 状态码 | 何时出现 | | - | - | | 400 | 某个字段缺失或有误,消息会说明是哪个。 | | 402 | 余额不足以启动手机,或已用完。 | | 404 | 你的账户中没有这台手机、这个运行或这个 APK。 | | 409 | 手机仍在启动中,或正忙于某个运行。 | | 410 | 手机已被删除。 | | 413 | APK 太大,无法一次发送(超过 100 MB 请分片上传),或超过 500 MB。 | | 422 | 应用未能安装,error 会说明原因。 | | 429 | 你已经有 3 台手机,或已上传 50 个 APK。 | | 503 | 目前所有手机都在使用中,请几分钟后再试。 |