dailo-devtools 0.1.3__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dailo_devtools-0.1.3/src/dailo_devtools.egg-info → dailo_devtools-0.1.4}/PKG-INFO +42 -2
- dailo_devtools-0.1.4/README.md +135 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/pyproject.toml +1 -1
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools/__init__.py +4 -1
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools/cli.py +34 -2
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools/device.py +818 -697
- dailo_devtools-0.1.4/src/dailo_devtools/windows.py +80 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4/src/dailo_devtools.egg-info}/PKG-INFO +42 -2
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools.egg-info/SOURCES.txt +1 -0
- dailo_devtools-0.1.3/README.md +0 -95
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/LICENSE +0 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/setup.cfg +0 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools/py.typed +0 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools.egg-info/dependency_links.txt +0 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools.egg-info/entry_points.txt +0 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools.egg-info/requires.txt +0 -0
- {dailo_devtools-0.1.3 → dailo_devtools-0.1.4}/src/dailo_devtools.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dailo-devtools
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: 通过代劳 DevTools 调试单台 Android 设备的 Python API 与 CLI
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Script SDK, https://pypi.org/project/dailo-sdk/
|
|
@@ -19,7 +19,7 @@ Dynamic: license-file
|
|
|
19
19
|
|
|
20
20
|
# Dailo DevTools
|
|
21
21
|
|
|
22
|
-
在电脑上运行和调试代劳 Android 自动化脚本。提供 `dailo-dev`
|
|
22
|
+
在电脑上运行和调试代劳 Android 与 Windows 自动化脚本。提供 `dailo-dev` 命令行工具与 Python 客户端,可执行脚本、读取文字与控件、保存截图和查看日志。
|
|
23
23
|
|
|
24
24
|
- 命令行执行 Python 文件或短代码。
|
|
25
25
|
- Python API 支持连接设备、截图、观察和停止运行。
|
|
@@ -55,6 +55,28 @@ dailo-dev serve --connection connection.json
|
|
|
55
55
|
|
|
56
56
|
保持服务运行,在另一个终端执行后续命令。连接文件包含访问设备的凭据,请保存在本机;链接过期或重新连接时,从 App 获取新链接。
|
|
57
57
|
|
|
58
|
+
## Windows 本机调试
|
|
59
|
+
|
|
60
|
+
Windows 使用代劳安装目录中的 `runtime/windows-x64`,复用产品的 Rust 宿主和脚本执行通路,无需安装 Python 系统或视觉库。`--hwnd` 或 `--executable` 选择目标;省略时观察当前显示桌面。
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" applications
|
|
64
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" desktops
|
|
65
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" --hwnd 123456 inspect
|
|
66
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" --hwnd 123456 screenshot screen.png
|
|
67
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" --hwnd 123456 exec --file main.py --timeout 300
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Python 客户端提供同一能力;`cancel()` 可由调用方其他线程停止正在执行的脚本:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from dailo_devtools import Windows
|
|
74
|
+
|
|
75
|
+
device = Windows(r"C:\Apps\代劳\runtime\windows-x64", hwnd=123456)
|
|
76
|
+
print(device.inspect())
|
|
77
|
+
device.run_file("main.py", config={"rounds": 5})
|
|
78
|
+
```
|
|
79
|
+
|
|
58
80
|
## 运行脚本
|
|
59
81
|
|
|
60
82
|
执行一段代码:
|
|
@@ -71,6 +93,13 @@ dailo-dev exec --file main.py --config settings.json
|
|
|
71
93
|
|
|
72
94
|
脚本可使用 [dailo-sdk](https://pypi.org/project/dailo-sdk/) 的文字、控件和图片定位 API。文件模式会自动上传脚本目录中的 Python 模块和 `assets/`;`--timeout` 设置执行时限,单位为秒,默认 300。
|
|
73
95
|
|
|
96
|
+
只携带本次所需资源时,使用 `--assets` 指定相对脚本目录的文件或目录;设备上保留相同相对路径。省略此参数时携带整个 `assets/`,单独使用 `--assets` 时不携带资源。Python 模块始终自动携带。
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
dailo-dev exec --file main.py --assets assets/battle.png assets/maps
|
|
100
|
+
dailo-dev exec --code "from dailo import text; print(text())" --assets
|
|
101
|
+
```
|
|
102
|
+
|
|
74
103
|
## 常用命令
|
|
75
104
|
|
|
76
105
|
| 命令 | 用途 |
|
|
@@ -109,6 +138,17 @@ asyncio.run(main())
|
|
|
109
138
|
|
|
110
139
|
`Device` 还提供 `run_file(path, config=...)`、`inspect()` 和 `cancel()`。使用 Python 客户端时无需启动 `dailo-dev serve`;异步上下文管理器负责建立和关闭连接。
|
|
111
140
|
|
|
141
|
+
`run_file()` 和 `execute()` 的 `assets=None` 默认携带 `assets/`。传入显式列表会只携带选定资源,`assets=[]` 不携带资源,例如:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
await device.run_file("main.py", assets=["assets/battle.png", "assets/maps"])
|
|
145
|
+
await device.execute("from dailo import text; print(text())", assets=[])
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Windows 的 `run_file()`、`execute()` 每次生成独立 `devtools:windows:…` 执行 ID,脚本通过 `execution()` 读取。明确续接某次失败时传入 `retry_of_execution_id=原执行ID`;新调用默认不关联旧父。调试 ID 不代表产品任务记录。
|
|
149
|
+
|
|
150
|
+
Android 的同名参数沿用Host提供的本父真实执行ID,只设置本次重试来源,退出后恢复原上下文。续接连续延期时,传入上一父脚本中`dailo.execution().execution_id`的完整原值;不要传请求短ID、sessionId或自行拼接ID。CLI使用`--retry-of-execution-id`,持久服务的`execute`/`run_file`请求使用`retry_of_execution_id`。省略参数表示新调用,不沿用旧延期链;该参数不修改原延期时间,也不绕过最早执行时间。
|
|
151
|
+
|
|
112
152
|
## 许可
|
|
113
153
|
|
|
114
154
|
MIT。
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Dailo DevTools
|
|
2
|
+
|
|
3
|
+
在电脑上运行和调试代劳 Android 与 Windows 自动化脚本。提供 `dailo-dev` 命令行工具与 Python 客户端,可执行脚本、读取文字与控件、保存截图和查看日志。
|
|
4
|
+
|
|
5
|
+
- 命令行执行 Python 文件或短代码。
|
|
6
|
+
- Python API 支持连接设备、截图、观察和停止运行。
|
|
7
|
+
- 自动携带脚本目录中的 Python 模块与 `assets/`。
|
|
8
|
+
- 图文日志保存到电脑,可离线查看。
|
|
9
|
+
|
|
10
|
+
## 安装
|
|
11
|
+
|
|
12
|
+
需要 Python 3.11 或更新版本,以及已安装代劳 App、开启 DevTools 的 Android 设备。
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
python -m pip install dailo-devtools
|
|
16
|
+
dailo-dev install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`dailo-dev install` 安装后台连接所需的 Chromium 运行时,只需执行一次。
|
|
20
|
+
|
|
21
|
+
## 连接设备
|
|
22
|
+
|
|
23
|
+
从代劳 App 的 DevTools 入口复制连接链接,保存为 `connection.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"url": "<从代劳 App 复制的 DevTools 链接>"
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
启动连接服务:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
dailo-dev serve --connection connection.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
保持服务运行,在另一个终端执行后续命令。连接文件包含访问设备的凭据,请保存在本机;链接过期或重新连接时,从 App 获取新链接。
|
|
38
|
+
|
|
39
|
+
## Windows 本机调试
|
|
40
|
+
|
|
41
|
+
Windows 使用代劳安装目录中的 `runtime/windows-x64`,复用产品的 Rust 宿主和脚本执行通路,无需安装 Python 系统或视觉库。`--hwnd` 或 `--executable` 选择目标;省略时观察当前显示桌面。
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" applications
|
|
45
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" desktops
|
|
46
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" --hwnd 123456 inspect
|
|
47
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" --hwnd 123456 screenshot screen.png
|
|
48
|
+
dailo-dev windows --runtime "C:\Apps\代劳\runtime\windows-x64" --hwnd 123456 exec --file main.py --timeout 300
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Python 客户端提供同一能力;`cancel()` 可由调用方其他线程停止正在执行的脚本:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from dailo_devtools import Windows
|
|
55
|
+
|
|
56
|
+
device = Windows(r"C:\Apps\代劳\runtime\windows-x64", hwnd=123456)
|
|
57
|
+
print(device.inspect())
|
|
58
|
+
device.run_file("main.py", config={"rounds": 5})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 运行脚本
|
|
62
|
+
|
|
63
|
+
执行一段代码:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
dailo-dev exec --code "from dailo import text; print(text())"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
执行 Python 文件并传入任务设置:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
dailo-dev exec --file main.py --config settings.json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
脚本可使用 [dailo-sdk](https://pypi.org/project/dailo-sdk/) 的文字、控件和图片定位 API。文件模式会自动上传脚本目录中的 Python 模块和 `assets/`;`--timeout` 设置执行时限,单位为秒,默认 300。
|
|
76
|
+
|
|
77
|
+
只携带本次所需资源时,使用 `--assets` 指定相对脚本目录的文件或目录;设备上保留相同相对路径。省略此参数时携带整个 `assets/`,单独使用 `--assets` 时不携带资源。Python 模块始终自动携带。
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
dailo-dev exec --file main.py --assets assets/battle.png assets/maps
|
|
81
|
+
dailo-dev exec --code "from dailo import text; print(text())" --assets
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 常用命令
|
|
85
|
+
|
|
86
|
+
| 命令 | 用途 |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `dailo-dev status` | 查看连接和运行状态 |
|
|
89
|
+
| `dailo-dev inspect` | 读取设备画面的文字与控件 |
|
|
90
|
+
| `dailo-dev screenshot screen.png` | 保存设备截图 |
|
|
91
|
+
| `dailo-dev cancel` | 停止当前脚本 |
|
|
92
|
+
| `dailo-dev reconnect` | 使用持久连接配置重新连接设备 |
|
|
93
|
+
| `dailo-dev logs <日志目录>` | 离线查看图文日志 |
|
|
94
|
+
|
|
95
|
+
运行 `dailo-dev --help` 或 `dailo-dev exec --help` 查看完整选项。
|
|
96
|
+
|
|
97
|
+
## Python API
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import asyncio
|
|
101
|
+
import json
|
|
102
|
+
from pathlib import Path
|
|
103
|
+
|
|
104
|
+
from dailo_devtools import Device, read_logs
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async def main():
|
|
108
|
+
connection = json.loads(Path("connection.json").read_text(encoding="utf-8"))
|
|
109
|
+
|
|
110
|
+
async with Device(connection) as device:
|
|
111
|
+
result = await device.execute("from dailo import text; print(text())")
|
|
112
|
+
await device.screenshot("screen.png")
|
|
113
|
+
print(result)
|
|
114
|
+
print(read_logs(result["logDirectory"]))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
asyncio.run(main())
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`Device` 还提供 `run_file(path, config=...)`、`inspect()` 和 `cancel()`。使用 Python 客户端时无需启动 `dailo-dev serve`;异步上下文管理器负责建立和关闭连接。
|
|
121
|
+
|
|
122
|
+
`run_file()` 和 `execute()` 的 `assets=None` 默认携带 `assets/`。传入显式列表会只携带选定资源,`assets=[]` 不携带资源,例如:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
await device.run_file("main.py", assets=["assets/battle.png", "assets/maps"])
|
|
126
|
+
await device.execute("from dailo import text; print(text())", assets=[])
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Windows 的 `run_file()`、`execute()` 每次生成独立 `devtools:windows:…` 执行 ID,脚本通过 `execution()` 读取。明确续接某次失败时传入 `retry_of_execution_id=原执行ID`;新调用默认不关联旧父。调试 ID 不代表产品任务记录。
|
|
130
|
+
|
|
131
|
+
Android 的同名参数沿用Host提供的本父真实执行ID,只设置本次重试来源,退出后恢复原上下文。续接连续延期时,传入上一父脚本中`dailo.execution().execution_id`的完整原值;不要传请求短ID、sessionId或自行拼接ID。CLI使用`--retry-of-execution-id`,持久服务的`execute`/`run_file`请求使用`retry_of_execution_id`。省略参数表示新调用,不沿用旧延期链;该参数不修改原延期时间,也不绕过最早执行时间。
|
|
132
|
+
|
|
133
|
+
## 许可
|
|
134
|
+
|
|
135
|
+
MIT。
|
|
@@ -4,10 +4,13 @@ from typing import TYPE_CHECKING
|
|
|
4
4
|
if TYPE_CHECKING:
|
|
5
5
|
from .device import Device, DevtoolsError, read_logs
|
|
6
6
|
|
|
7
|
-
__all__ = ["Device", "DevtoolsError", "read_logs"]
|
|
7
|
+
__all__ = ["Device", "DevtoolsError", "read_logs", "Windows"]
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def __getattr__(name: str):
|
|
11
|
+
if name == 'Windows':
|
|
12
|
+
from .windows import Windows
|
|
13
|
+
return Windows
|
|
11
14
|
if name not in __all__:
|
|
12
15
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
13
16
|
from .device import Device, DevtoolsError, read_logs
|
|
@@ -76,12 +76,44 @@ def main():
|
|
|
76
76
|
source.add_argument("--code")
|
|
77
77
|
source.add_argument("--file")
|
|
78
78
|
execute.add_argument("--config", help="脚本配置 JSON 文件")
|
|
79
|
-
execute.add_argument("--assets", action="
|
|
79
|
+
execute.add_argument("--assets", action="extend", nargs="*", default=None,
|
|
80
|
+
help="仅携带指定资源;省略时携带 assets/,不提供路径时不携带资源")
|
|
80
81
|
execute.add_argument("--directory", default=".", help="内联代码所属仓库,默认当前目录")
|
|
81
82
|
execute.add_argument("--timeout", type=float, default=300, help="整个开发运行的传输时限,单位秒")
|
|
83
|
+
execute.add_argument("--retry-of-execution-id", help="明确续接的上一父真实执行ID;省略时新建延期链")
|
|
82
84
|
raw = sub.add_parser("request", help="直接发送当前 DevTools 协议 JSON")
|
|
83
85
|
raw.add_argument("body")
|
|
86
|
+
windows = sub.add_parser('windows', help='通过代劳随包 Rust 宿主调试本机 Windows')
|
|
87
|
+
windows.add_argument('--runtime', required=True, help='代劳安装目录中的 runtime/windows-x64')
|
|
88
|
+
windows.add_argument('--executable')
|
|
89
|
+
windows.add_argument('--hwnd', type=lambda value: int(value, 0))
|
|
90
|
+
commands = windows.add_subparsers(dest='windows_command', required=True)
|
|
91
|
+
commands.add_parser('applications')
|
|
92
|
+
commands.add_parser('desktops')
|
|
93
|
+
commands.add_parser('inspect')
|
|
94
|
+
commands.add_parser('screenshot').add_argument('path')
|
|
95
|
+
run = commands.add_parser('exec')
|
|
96
|
+
source = run.add_mutually_exclusive_group(required=True)
|
|
97
|
+
source.add_argument('--file')
|
|
98
|
+
source.add_argument('--code')
|
|
99
|
+
run.add_argument('--directory', default='.')
|
|
100
|
+
run.add_argument('--config')
|
|
101
|
+
run.add_argument('--timeout', type=float, default=300)
|
|
84
102
|
args = parser.parse_args()
|
|
103
|
+
if args.command == 'windows':
|
|
104
|
+
from .windows import Windows
|
|
105
|
+
device = Windows(args.runtime, executable=args.executable, hwnd=args.hwnd)
|
|
106
|
+
if args.windows_command == 'exec':
|
|
107
|
+
config = json.loads(Path(args.config).read_text(encoding='utf-8')) if args.config else {}
|
|
108
|
+
callback = lambda value: print(json.dumps(value, ensure_ascii=False), flush=True)
|
|
109
|
+
events = (device.run_file(args.file, config=config, timeout=args.timeout, on_event=callback)
|
|
110
|
+
if args.file else device.execute(args.code, directory=args.directory,
|
|
111
|
+
config=config, timeout=args.timeout, on_event=callback))
|
|
112
|
+
sys.exit(0 if events[-1].get('status') == 'success' else 1)
|
|
113
|
+
result = (device.screenshot(args.path) if args.windows_command == 'screenshot'
|
|
114
|
+
else getattr(device, args.windows_command)())
|
|
115
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
116
|
+
return
|
|
85
117
|
if args.command == "logs":
|
|
86
118
|
from .device import read_logs
|
|
87
119
|
print(json.dumps(read_logs(args.directory), ensure_ascii=False, indent=2))
|
|
@@ -108,7 +140,7 @@ def main():
|
|
|
108
140
|
elif args.command == "inspect":
|
|
109
141
|
body.update(pixels=args.pixels, nodes=not args.no_nodes, text=not args.no_text, raw=args.raw)
|
|
110
142
|
elif args.command == "exec":
|
|
111
|
-
body.update(timeout=args.timeout, assets=args.assets)
|
|
143
|
+
body.update(timeout=args.timeout, assets=args.assets, retry_of_execution_id=args.retry_of_execution_id)
|
|
112
144
|
if args.config:
|
|
113
145
|
body["config"] = json.loads(Path(args.config).read_text(encoding="utf-8"))
|
|
114
146
|
if args.file:
|