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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dailo-devtools
3
- Version: 0.1.3
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` 命令行工具和异步 Python 客户端,可执行脚本、读取文字与控件、保存截图和查看日志。
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,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dailo-devtools"
7
- version = "0.1.3"
7
+ version = "0.1.4"
8
8
  description = "通过代劳 DevTools 调试单台 Android 设备的 Python API 与 CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -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="append", default=[])
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: