Metadata-Version: 2.5
Name: dailo-sdk
Version: 0.1.0
Summary: 代劳 Android 自动化脚本 API
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: dacite<2,>=1.9
Requires-Dist: httpx<1,>=0.28
Requires-Dist: loguru<1,>=0.7
Description-Content-Type: text/markdown

# 代劳 Python API

`pip install dailo-sdk`，从 `dailo` 导入 API。编辑器可补全、查看文档和跳转源码；脚本在代劳设备中执行。

`execution()` 返回当前执行 ID 和 `retry_of_execution_id`。持久化连续失败计数时，只在重跑来源与上次执行 ID 一致时继承计数；手动重启、停止后再启动或更换账号应重新计数。

默认诊断记录达到 1 秒的 API 调用或状态驻留，包含近期操作和同帧图文；每次驻留去重，状态改变后重新记录，异常即时记录。单次 `wait_until` 的等待过程最多报告一次耗时，最终调用诊断仍保留。应用安装、权限、启动和停止也纳入耗时记录。

统计等待诊断时区分过程与最终异常；两者的 `operation` 都可能是 `wait_until`，`diagnostic` 都是 `slow_api`。按 `reason` 和原执行异常识别终态，不以总条数大于1判节流失败。

诊断补采画面不再次执行业务观察守卫，避免同一次异常重复触发延期等状态修改；正常观察和动作仍执行守卫。

拖拽轨迹包含起止坐标、移动交付完成、申请松手及松手完成的耗时；移动交付后才开始画面稳定判断，稳定等待失败仍执行松手。系统交付不代表游戏已处理输入，动作结果仍按真实画面确认。

守卫实际返回False并阻塞到预算耗尽时才抛出 `ObservationGuardTimeout`，其 `observation` 保留最后拒绝的同帧画面。守卫允许画面但调用预算已耗尽时抛普通`TimeoutError`；不能把普通定位超时记为网络失败或账号延期。

定位器的一次调用共用守卫、动作、until结果等待与诊断预算；`wait_until`、画面变化等待与守卫嵌套时继承较早deadline，内部等待只取得剩余时间。复制执行上下文的deadline互相隔离；晚到条件成功仍按超时处理。等待诊断使用本次操作提供的画面及缓存文字；无本次画面时，仅在剩余预算内补采新帧，不额外OCR。采帧/编码截图的时间计入同一预算；耗尽后不再补采，未取得字段为空。日志以`collection=budgeted`和`frameSource=operation / diagnostic / unavailable`说明来源。Python回调和原生调用不会因此被强制中断，不能把这个预算当作硬截止时间。

`text` 使用当前画面文字，`region` 对指定区域执行局部文字块识别。数字字段可用 `text(region=区域, line=True)` 读取指定区域的一行文字；同帧区域识别使用 `frame.matches(..., region=区域)`。

列表翻页可用 `text(".+", region=名单区域).drag(x1, y1, x2, y2)`，等待名单稳定后松手，避免惯性跨页。`settle=False` 等待系统完成手势，不等待画面稳定；侧视相机或过渡按钮仍需确认可操作状态。`until=目标界面` 按需等待动作结果。

`image(path, size=(宽, 高))` 按画面中的尺寸匹配原图，尺寸使用 720p 坐标；缩放和缓存由宿主处理。

`color_at(x, y)` 读取 RGB，可判断按钮高亮或可操作区域，不需要图像处理库。

```python
from dailo import app, get_config, node, text
from dataclasses import dataclass

@dataclass
class Settings:
    rounds: int = 5

settings = get_config(Settings)  # 显式读取 UI 配置，属性可补全

app("com.example.game").launch()
node(text="登录").click()  # 默认隐式等待 30 秒
print(text())             # 读取画面文字，输出显示在设备左下角
```

`text("登录")` 定位包含文字的行，`exact=True` 匹配整行；`node`、`image`、`color` 定位控件、图片、颜色，PNG 透明区域不参与匹配。定位结果支持 `click()`、`wait()`、`disappear()`、`is_visible()`；`all()` 立即读取全部目标，需要滚动稳定时显式使用 `settled()`。`入口.click(until=目标界面)` 等目标可操作后点击一次，再等目标界面；`click(offset=(0, 30))` 点击目标中心下方 30 像素。`wait_until(lambda: 条件)` 支持多个合法出口，默认 30 秒。使用 `httpx` 下载文件，`install_app(path)` 安装或更新并保留应用数据。`app(package)` 查询版本、启动、停止及授予权限。`stage(message)` 同步关键阶段；普通 `print` / loguru 日志更新设备，结束时同步结果。`status_position(x, y)` 调整状态行位置（左、下边距，dp）。

`settled()`（含 `drag` 的 `settle`）只确认匹配位置稳定，结果可能为空，不保证目标存在或可操作。需要目标存在时先调用 `目标.wait()`；缩放或页面过渡期间，应先等待同帧实际字段或图像出现，再执行手势，并用 `until` 或实际画面条件确认结果。

`frame = observe()` 固定一份画面；`frame.text()`、`frame.matches(pattern)`、`frame.images({名称: image(...)})` 和 `frame.pixel(x, y)` 读取同一帧。图像批量匹配可指定 `grayscale=True`，返回边界及相关度。匹配结果保存文字、边界、中心与相关度；取证时帧ID和时间使用所属 `frame.observation_id`、`frame.captured_at_us`。`observe(text=False)` 延后 OCR；`observe(source=path, text=False)` 读取保存的图像。`frame.crop(path, region)` 保存同帧模板，`log(message, image=frame.screenshot)` 保存对应图文日志。

数字字段可用 `frame.text(region=区域, line=True, allow_chars="0123456789")`：在原识别模型每步分数上仅选择允许字符与CTC空白，再按原CTC解码；不删除或替换解码后的文字。默认 `allow_chars=None` 不限制字符，空字符串无效。调用方仍须校验完整字段；数字约束不保证业务数值正确。

`set_observation_guard(callback)` 在动作前检查同帧画面；返回 False 阻止动作并等画面变化。SDK 默认记录慢调用和超过一秒的状态驻留，包括阶段、实际文字、最近调用及同帧截图。`defer_until(带时区的datetime, reason=原因)` 结束本次任务并表达最早重跑时间。

点击、定位器滑动/拖动、填充及按键输入另留一条 `input_action` 文字日志，记录单调时钟微秒的开始/返回时间、目标和 returned/raised；不额外采帧。返回只代表输入 API 完成，业务成效仍需实际后续画面确认。

`notifications(after=提交前的time.time(), package_name=包名)` 读取本次提交后的瞬时 Toast / 通知，每项包含文字、应用及 Unix 时间；服务最多保留最近32条，重启清空。提示属于独立事件，不代表当前截图内容。

`network_proxy()` 读取 Android `ConnectivityManager` 当前实际生效的代理，返回 host、port、exclusions、pac_url；无代理时返回 `None`。系统设置字段为空不代表内存中的代理已清除。
