midscene 0.1.0__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.
Files changed (32) hide show
  1. midscene-0.1.0/LICENSE +21 -0
  2. midscene-0.1.0/PKG-INFO +574 -0
  3. midscene-0.1.0/README.md +518 -0
  4. midscene-0.1.0/pyproject.toml +97 -0
  5. midscene-0.1.0/setup.cfg +4 -0
  6. midscene-0.1.0/src/midscene/__init__.py +73 -0
  7. midscene-0.1.0/src/midscene/_node_driver/android/service/package.json +13 -0
  8. midscene-0.1.0/src/midscene/_node_driver/android/service/service.js +470 -0
  9. midscene-0.1.0/src/midscene/_node_driver/web/service/package.json +14 -0
  10. midscene-0.1.0/src/midscene/_node_driver/web/service/service.js +405 -0
  11. midscene-0.1.0/src/midscene/_pytest_plugin.py +168 -0
  12. midscene-0.1.0/src/midscene/_pytest_support.py +98 -0
  13. midscene-0.1.0/src/midscene/agent_android.py +103 -0
  14. midscene-0.1.0/src/midscene/agent_web.py +60 -0
  15. midscene-0.1.0/src/midscene/base_agent.py +212 -0
  16. midscene-0.1.0/src/midscene/config.py +86 -0
  17. midscene-0.1.0/src/midscene/drivers.py +70 -0
  18. midscene-0.1.0/src/midscene/exceptions.py +34 -0
  19. midscene-0.1.0/src/midscene/node_bootstrap.py +322 -0
  20. midscene-0.1.0/src/midscene/node_service.py +266 -0
  21. midscene-0.1.0/src/midscene/py.typed +0 -0
  22. midscene-0.1.0/src/midscene/runtime.py +373 -0
  23. midscene-0.1.0/src/midscene.egg-info/PKG-INFO +574 -0
  24. midscene-0.1.0/src/midscene.egg-info/SOURCES.txt +30 -0
  25. midscene-0.1.0/src/midscene.egg-info/dependency_links.txt +1 -0
  26. midscene-0.1.0/src/midscene.egg-info/entry_points.txt +2 -0
  27. midscene-0.1.0/src/midscene.egg-info/requires.txt +10 -0
  28. midscene-0.1.0/src/midscene.egg-info/top_level.txt +1 -0
  29. midscene-0.1.0/tests/test_agent_integration.py +563 -0
  30. midscene-0.1.0/tests/test_example_integration.py +89 -0
  31. midscene-0.1.0/tests/test_node_service.py +215 -0
  32. midscene-0.1.0/tests/test_web_service.py +105 -0
midscene-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zypdominate
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,574 @@
1
+ Metadata-Version: 2.4
2
+ Name: midscene
3
+ Version: 0.1.0
4
+ Summary: AI-powered Android & web UI automation via Midscene.js, bridged into Python
5
+ Author: zypdominate
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 zypdominate
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/zypdominate/midscene-python
29
+ Project-URL: Repository, https://github.com/zypdominate/midscene-python
30
+ Project-URL: Issues, https://github.com/zypdominate/midscene-python/issues
31
+ Project-URL: Changelog, https://github.com/zypdominate/midscene-python/blob/master/CHANGELOG.md
32
+ Keywords: android,web,browser,puppeteer,automation,testing,ai,midscene
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.9
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Topic :: Software Development :: Testing
43
+ Requires-Python: >=3.9
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE
46
+ Requires-Dist: python-dotenv>=1.2.1
47
+ Requires-Dist: requests>=2.28
48
+ Requires-Dist: pytest>=8.4.2
49
+ Provides-Extra: dev
50
+ Requires-Dist: build; extra == "dev"
51
+ Requires-Dist: twine; extra == "dev"
52
+ Requires-Dist: ruff; extra == "dev"
53
+ Requires-Dist: mypy; extra == "dev"
54
+ Requires-Dist: types-requests; extra == "dev"
55
+ Dynamic: license-file
56
+
57
+ # Midscene Python
58
+
59
+ [![PyPI version](https://img.shields.io/pypi/v/midscene.svg)](https://pypi.org/project/midscene/)
60
+ [![Python](https://img.shields.io/pypi/pyversions/midscene.svg)](https://pypi.org/project/midscene/)
61
+ [![CI](https://github.com/zypdominate/midscene-python/actions/workflows/ci.yml/badge.svg)](https://github.com/zypdominate/midscene-python/actions/workflows/ci.yml)
62
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
63
+
64
+ 将 [Midscene.js](https://github.com/web-infra-dev/midscene) AI 驱动的 UI 自动化能力桥接到 Python 测试框架。
65
+
66
+ 无需自行安装 Node.js;无需维护 UI 选择器——用自然语言描述操作,AI 负责定位和执行。
67
+
68
+ 单一 `midscene` 包,工程代码按模块划分,同时支持:
69
+
70
+ | 模块 | 能力 | 入口 |
71
+ |------|------|------|
72
+ | `agent_android` | Android 自动化(ADB + `@midscene/android`) | `from midscene import MidsceneAgent` |
73
+ | `agent_web` / `drivers` | 网页自动化(Puppeteer + `@midscene/web`,预留 Playwright/Bridge) | `from midscene import MidsceneWebAgent` |
74
+ | 共享底层 | 配置、异常、Node 运行时桥接、RPC 服务管理、`BaseAgent` | `from midscene import MidsceneConfig, BaseAgent` |
75
+
76
+ > 本 README 以 Android 为主线说明;网页用法见 [网页自动化](#网页自动化)。
77
+
78
+ ---
79
+
80
+ ## 目录
81
+
82
+ - [架构](#架构)
83
+ - [安装](#安装)
84
+ - [快速开始](#快速开始)
85
+ - [网页自动化](#网页自动化)
86
+ - [pytest 插件](#pytest-插件)
87
+ - [配置](#配置)
88
+ - [API 参考](#api-参考)
89
+ - [异常处理](#异常处理)
90
+ - [开发者指南](#开发者指南)
91
+
92
+ ---
93
+
94
+ ## 架构
95
+
96
+ ```
97
+ Python 测试代码
98
+ └── agent.ai_action("点击登录按钮")
99
+
100
+ │ JSON-RPC 2.0(本地回环,无网络开销)
101
+
102
+ midscene(共享底层)
103
+ ├── Node 运行时(首次使用自动下载到 ~/.midscene/node_runtime/,android/web 共享)
104
+ ├── NodeServiceManager(按平台 ServiceSpec 多实例:android / web 各一个 Node 进程)
105
+ └── BaseAgent(跨平台 ai_action / ai_tap / ai_query / ai_assert …)
106
+
107
+ ├── MidsceneAgent → @midscene/android → ADB → Android 设备 / 模拟器
108
+ └── MidsceneWebAgent → @midscene/web → Puppeteer → Chromium 浏览器
109
+ ```
110
+
111
+ **关键特性:**
112
+
113
+ - 每个平台的 Python 进程与其 Node 进程 1:1;同平台多个 Agent 共享一个 Node 进程(通过 sessionId 隔离)
114
+ - android 与 web 各自拥有独立的 Node 服务与缓存命名空间,可并存
115
+ - Python 进程退出时 Node 子进程自动清理
116
+ - 无需系统已安装 `node` 或 `npm`
117
+
118
+ ---
119
+
120
+ ## 安装
121
+
122
+ ```bash
123
+ pip install midscene
124
+ ```
125
+
126
+ > **首次使用**:会按需自动执行 `npm install`(Android 用 `@midscene/android`,Web 用 `@midscene/web` + puppeteer),需要访问 npm registry。只有实际使用的平台才会触发对应安装。
127
+
128
+ **系统要求:**
129
+
130
+ | 条件 | 说明 |
131
+ |------|------|
132
+ | Python | 3.9 及以上 |
133
+ | ADB | 已安装并在 PATH 中(`adb devices` 能看到目标设备) |
134
+ | AI API | 支持 OpenAI 兼容接口的视觉模型(如 qwen-vl-max、GPT-4o) |
135
+ | Node.js | **无需安装**,wheel 内已内置 |
136
+
137
+ ---
138
+
139
+ ## 快速开始
140
+
141
+ ### 1. 配置 AI 模型
142
+
143
+ 推荐使用 `.env` 文件(自动加载,无需 `export`):
144
+
145
+ ```ini
146
+ # .env
147
+ MIDSCENE_MODEL_BASE_URL=https://ark.cn-beijing.volces.com/api/v3/
148
+ MIDSCENE_MODEL_API_KEY=ark-your-api-key
149
+ MIDSCENE_MODEL_NAME=doubao-seed-1-6-vision-250815
150
+ MIDSCENE_MODEL_FAMILY=doubao-seed
151
+ ```
152
+
153
+ ### 2. 编写测试
154
+
155
+ ```python
156
+ from midscene import MidsceneAgent
157
+
158
+ # 从 .env 或环境变量自动读取配置
159
+ agent = MidsceneAgent("emulator-5556")
160
+
161
+ with MidsceneAgent("emulator-5556") as agent:
162
+ agent.ai_action("等待应用首页加载完成")
163
+ agent.ai_tap("登录按钮")
164
+ agent.ai_input("用户名输入框", "testuser")
165
+ agent.ai_input("密码输入框", "Test@123456")
166
+ agent.ai_tap("确认登录")
167
+ agent.ai_wait_for("登录成功,显示用户首页", timeout_ms=10000)
168
+ agent.ai_assert("当前页面是用户首页")
169
+ ```
170
+
171
+ ### 3. 在 pytest 中使用
172
+
173
+ ```python
174
+ # conftest.py
175
+ import pytest
176
+ from midscene import MidsceneAgent
177
+
178
+ @pytest.fixture
179
+ def agent():
180
+ ag = MidsceneAgent("emulator-5556")
181
+ yield ag
182
+ ag.destroy()
183
+
184
+
185
+ # test_login.py
186
+ def test_login(agent: MidsceneAgent):
187
+ agent.ai_action("打开登录页面")
188
+ agent.ai_input("用户名", "testuser")
189
+ agent.ai_input("密码", "password")
190
+ agent.ai_tap("登录")
191
+ agent.ai_assert("登录成功,显示用户名 testuser")
192
+ ```
193
+
194
+ ---
195
+
196
+ ## 网页自动化
197
+
198
+ 网页自动化由 `MidsceneWebAgent` 提供,默认使用 Puppeteer 驱动(首次使用会自动安装 `@midscene/web` 与 puppeteer,并下载 Chromium)。
199
+
200
+ ```python
201
+ from midscene import MidsceneWebAgent
202
+
203
+ agent = MidsceneWebAgent("https://example.com")
204
+ agent.ai_action("在搜索框输入 midscene 并回车")
205
+ agent.ai_assert("搜索结果已展示")
206
+ agent.destroy()
207
+ ```
208
+
209
+ 选择 / 配置驱动:
210
+
211
+ ```python
212
+ from midscene import MidsceneWebAgent, PuppeteerDriver
213
+
214
+ # 有头模式 + 指定视口
215
+ agent = MidsceneWebAgent(
216
+ "https://example.com",
217
+ driver=PuppeteerDriver(headless=False, viewport={"width": 1440, "height": 900}),
218
+ )
219
+
220
+ # 连接已运行的 Chrome(chrome --remote-debugging-port=9222)
221
+ agent = MidsceneWebAgent(driver=PuppeteerDriver(cdp_endpoint="http://127.0.0.1:9222"))
222
+ ```
223
+
224
+ 网页专有方法:`goto(url)`、`new_tab(url=None)`、`set_viewport(width, height)`、`ai_hover(locate)`,以及全部跨平台 `ai_*` 方法。`PlaywrightDriver` / `BridgeDriver` 为占位,后续接入。
225
+
226
+ ---
227
+
228
+ ## pytest 插件
229
+
230
+ 安装 `midscene` 后,pytest 会**自动加载**内置插件,无需额外配置。提供 `midscene_agent`(Android)与 `midscene_web_agent`(Web)两个 fixture。
231
+
232
+ ### 内置 fixture:`midscene_agent`
233
+
234
+ 无需在 `conftest.py` 中自行声明 fixture,直接在测试函数参数中使用即可:
235
+
236
+ ```python
237
+ # test_my_app.py
238
+ import pytest
239
+
240
+ @pytest.mark.device
241
+ def test_login(midscene_agent):
242
+ midscene_agent.ai_action("点击登录按钮")
243
+ midscene_agent.ai_input("用户名", "testuser")
244
+ midscene_agent.ai_input("密码", "Test@123456")
245
+ midscene_agent.ai_assert("登录成功,显示用户首页")
246
+ ```
247
+
248
+ 设备 ID 解析顺序:
249
+
250
+ | 优先级 | 来源 |
251
+ |--------|------|
252
+ | 1 | `--midscene-device` CLI 参数 |
253
+ | 2 | `MIDSCENE_DEVICE_ID` 环境变量 |
254
+ | 3 | `ANDROID_DEVICE_ID` 环境变量 |
255
+ | 4 | 自动选取第一台已连接设备 |
256
+
257
+ ### 失败自动截图与报告
258
+
259
+ 使用 `midscene_agent` 的测试用例失败时,插件会自动:
260
+
261
+ 1. 调用 `get_screenshot()` 将当前屏幕保存为 PNG。
262
+ 2. 调用 `get_report_file()` 获取 Midscene HTML 报告路径。
263
+ 3. 将上述路径附加到 pytest 报告的 sections(终端 `-v` 输出和 pytest-html 均可见)。
264
+
265
+ 截图保存路径示例:
266
+
267
+ ```
268
+ midscene_artifacts/tests__test_login__test_login.png
269
+ ```
270
+
271
+ ### CLI 选项
272
+
273
+ ```bash
274
+ # 指定设备
275
+ pytest --midscene-device emulator-5556 tests/ -m device
276
+
277
+ # 自定义截图保存目录
278
+ pytest --midscene-artifact-dir /tmp/ci_artifacts tests/ -m device
279
+ ```
280
+
281
+ | 选项 | 默认值 | 说明 |
282
+ |------|--------|------|
283
+ | `--midscene-device` | 自动检测 | `midscene_agent` 使用的 Android 设备 ID |
284
+ | `--midscene-url` | 无 | `midscene_web_agent` 的起始页面 URL(或 `MIDSCENE_WEB_URL`) |
285
+ | `--midscene-headed` | 关闭 | `midscene_web_agent` 以有头模式启动浏览器 |
286
+ | `--midscene-artifact-dir` | `midscene_artifacts/` | 失败截图与报告的保存目录 |
287
+
288
+ `midscene_web_agent` 用法:
289
+
290
+ ```python
291
+ def test_search(midscene_web_agent):
292
+ midscene_web_agent.goto("https://example.com")
293
+ midscene_web_agent.ai_action("点击更多信息链接")
294
+ midscene_web_agent.ai_assert("页面已跳转")
295
+ ```
296
+
297
+ ---
298
+
299
+ ## 配置
300
+
301
+ ### MidsceneConfig
302
+
303
+ 可以通过环境变量、`.env` 文件或代码直接传入配置:
304
+
305
+ ```python
306
+ from midscene import MidsceneAgent, MidsceneConfig
307
+
308
+ # 方式一:从 .env / 环境变量自动读取(推荐)
309
+ agent = MidsceneAgent("emulator-5556")
310
+
311
+ # 方式二:代码直接传入
312
+ config = MidsceneConfig(
313
+ base_url="https://ark.cn-beijing.volces.com/api/v3/",
314
+ api_key="ark-your-api-key",
315
+ model_name="doubao-seed-1-6-vision-250815",
316
+ model_family="doubao-seed",
317
+ )
318
+ agent = MidsceneAgent("emulator-5556", config)
319
+ ```
320
+
321
+ ### 支持的模型家族
322
+
323
+ | `model_family` | 适用模型 | 示例 `model_name` |
324
+ |---|---|---|
325
+ | `openai` | OpenAI GPT 系列 | `gpt-4o` |
326
+ | `qwen` | 阿里通义千问 | `qwen-vl-max` |
327
+ | `doubao` | 字节豆包 | `doubao-vision-pro-32k` |
328
+ | `gemini` | Google Gemini | `gemini-2.0-flash` |
329
+ | `claude` | Anthropic Claude | `claude-opus-4-5` |
330
+
331
+ ### 环境变量说明
332
+
333
+ | 变量 | 必填 | 说明 |
334
+ |------|------|------|
335
+ | `MIDSCENE_MODEL_BASE_URL` | ✅ | AI API 的 base URL |
336
+ | `MIDSCENE_MODEL_API_KEY` | ✅ | API Key |
337
+ | `MIDSCENE_MODEL_NAME` | ✅ | 模型名称 |
338
+ | `MIDSCENE_MODEL_FAMILY` | ❌ | 模型家族 |
339
+
340
+ ---
341
+
342
+ ## API 参考
343
+
344
+ 所有方法均为同步调用,内部通过 JSON-RPC 与 Node.js 微服务通信。
345
+
346
+ ### 初始化与销毁
347
+
348
+ ```python
349
+ agent = MidsceneAgent(device_id, config=None)
350
+ # device_id : ADB 设备 ID,如 "emulator-5556" 或 "192.168.1.100:5555"
351
+ # config : MidsceneConfig 实例,可选,默认从环境变量读取
352
+
353
+ agent.destroy() # 释放 session,进程退出时自动调用
354
+ agent.is_closed() # 返回 True/False
355
+ ```
356
+
357
+ ### Auto Planning
358
+
359
+ ```python
360
+ agent.ai_action(prompt: str) -> None
361
+ ```
362
+
363
+ AI 自动规划并执行多步操作。适合描述复合目标:
364
+
365
+ ```python
366
+ agent.ai_action("打开设置,找到蓝牙选项并开启")
367
+ agent.ai_action("滑动到页面底部,点击'加载更多'")
368
+ ```
369
+
370
+ ### Instant Actions — 精确单步操作
371
+
372
+ 比 `ai_action` 更快、更稳定,适合已知元素的直接操作:
373
+
374
+ ```python
375
+ agent.ai_tap(locate: str) -> None
376
+ # 点击元素
377
+ agent.ai_tap("屏幕右上角的关闭按钮")
378
+ agent.ai_tap("文字为'立即购买'的按钮")
379
+
380
+ agent.ai_input(locate: str, value: str) -> None
381
+ # 在指定输入框中输入文本(先清空再输入)
382
+ agent.ai_input("搜索框", "midscene python")
383
+
384
+ agent.ai_clear_input(locate: str) -> None
385
+ # 清空输入框内容
386
+ agent.ai_clear_input("用户名输入框")
387
+
388
+ agent.ai_scroll(
389
+ locate: str = None,
390
+ direction: str = "down", # "up" | "down" | "left" | "right"
391
+ scroll_type: str = None,
392
+ distance: int = None,
393
+ ) -> None
394
+ # 滚动操作
395
+ agent.ai_scroll("商品列表", direction="down", distance=3)
396
+
397
+ agent.ai_long_press(locate: str, duration: int = None) -> None
398
+ # 长按,duration 单位毫秒
399
+ agent.ai_long_press("消息列表第一条")
400
+
401
+ agent.ai_double_click(locate: str) -> None
402
+ # 双击
403
+ agent.ai_double_click("图片预览区域")
404
+
405
+ agent.ai_keyboard_press(key_name: str, locate: str = None) -> None
406
+ # 模拟按键,如 "Enter"、"Back"、"Home"
407
+ agent.ai_keyboard_press("Enter")
408
+ agent.ai_keyboard_press("Back")
409
+
410
+ agent.ai_pinch(
411
+ direction: str, # "in"(缩小)| "out"(放大)
412
+ locate: str = None,
413
+ distance: int = None,
414
+ duration: int = None,
415
+ ) -> None
416
+ # 捏合/张开手势
417
+ agent.ai_pinch("out", locate="地图区域")
418
+ ```
419
+
420
+ ### Utility — 断言与数据提取
421
+
422
+ ```python
423
+ agent.ai_assert(assertion: str) -> None
424
+ # AI 视觉断言,失败时抛出 AssertionError
425
+ agent.ai_assert("当前页面显示用户名 testuser")
426
+ agent.ai_assert("购物车商品数量为 3")
427
+
428
+ agent.ai_wait_for(assertion: str, timeout_ms: int = 15000) -> None
429
+ # 等待条件满足,超时抛出异常
430
+ agent.ai_wait_for("加载动画消失", timeout_ms=10000)
431
+ agent.ai_wait_for("弹窗出现", timeout_ms=5000)
432
+
433
+ agent.ai_query(data_demand) -> Any
434
+ # 从当前屏幕提取结构化数据
435
+ products = agent.ai_query({"name": "string", "price": "number", "in_stock": "boolean"})
436
+ title = agent.ai_query("页面标题文字")
437
+
438
+ agent.ai_ask(prompt: str) -> Any
439
+ # 自由问答,返回 AI 对当前屏幕的理解
440
+ answer = agent.ai_ask("当前页面的主要功能是什么?")
441
+
442
+ agent.ai_boolean(prompt: str) -> bool
443
+ # 返回布尔值
444
+ is_logged_in = agent.ai_boolean("用户是否已登录?")
445
+
446
+ agent.ai_number(prompt: str) -> Any
447
+ # 返回数字
448
+ count = agent.ai_number("购物车中的商品数量")
449
+
450
+ agent.ai_string(prompt: str) -> str
451
+ # 返回字符串
452
+ username = agent.ai_string("当前登录的用户名")
453
+
454
+ agent.ai_locate(locate_prompt: str) -> Any
455
+ # 定位元素,返回位置信息(坐标等)
456
+ pos = agent.ai_locate("确认按钮")
457
+ ```
458
+
459
+ ### 原生 ADB
460
+
461
+ ```python
462
+ agent.run_adb_shell(command: str, timeout_ms: int = None) -> str
463
+ # 执行 adb shell 命令,返回输出文本;timeout_ms 单位为毫秒
464
+ output = agent.run_adb_shell("dumpsys activity top | grep 'ACTIVITY'")
465
+ output = agent.run_adb_shell("pm list packages | grep com.example", timeout_ms=5000)
466
+ ```
467
+
468
+ ---
469
+
470
+ ## 异常处理
471
+
472
+ ```python
473
+ from midscene import MidsceneRPCError
474
+
475
+ try:
476
+ agent.ai_assert("某个不存在的条件")
477
+ except AssertionError as e:
478
+ # AI 断言失败(pass=False),包含失败原因
479
+ print(f"断言失败: {e}")
480
+
481
+ try:
482
+ agent.ai_action("点击某个按钮")
483
+ except MidsceneRPCError as e:
484
+ # Node.js 侧报错(如设备断开、ADB 错误)
485
+ print(f"RPC 错误 [code={e.code}]: {e}")
486
+ ```
487
+
488
+ | 异常 | 触发场景 |
489
+ |------|---------|
490
+ | `AssertionError` | `ai_assert()` 条件不满足 |
491
+ | `MidsceneRPCError` | Node.js 侧业务错误(ADB 断开、元素找不到等) |
492
+ | `MidsceneSetupError` | Node 二进制缺失、npm install 失败、环境变量未配置 |
493
+ | `MidsceneNodeServiceError` | Node.js 服务启动失败或意外退出 |
494
+ | `MidsceneError` | 使用已 `destroy()` 的 Agent |
495
+
496
+ ---
497
+
498
+ ## 开发者指南
499
+
500
+ ### 本地开发
501
+
502
+ ```bash
503
+ git clone https://github.com/zypdominate/midscene-python
504
+ cd midscene_python
505
+
506
+ # 以可编辑方式安装(含开发工具)
507
+ pip install -e ".[dev]"
508
+
509
+ # 首次运行测试会自动下载 Node 到 ~/.midscene/node_runtime/
510
+ ```
511
+
512
+ 根目录 `pyproject.toml` 同时承载打包元数据与 ruff / pytest / mypy 配置,并把 `src` 加入 `pythonpath`,因此在根目录直接执行 `pytest` / `ruff check .` / `mypy` 即可。
513
+
514
+ ### 运行测试
515
+
516
+ ```bash
517
+ # 全部非真机/非浏览器测试(首次需联网下载 Node + npm 依赖)
518
+ pytest -m "not device and not web" -v
519
+
520
+ # 需要真实 Android 设备的测试(需配置好 .env 并连接设备)
521
+ pytest tests/ -m device -v -s
522
+
523
+ # 需要真实浏览器 + AI Key 的网页测试
524
+ pytest tests/ -m web -v -s
525
+ ```
526
+
527
+ ### 构建与发布
528
+
529
+ ```bash
530
+ # 构建 py3-none-any wheel + sdist
531
+ python tools/build_wheel.py --clean
532
+
533
+ # 检查并上传 PyPI
534
+ python tools/upload_pypi.py --dry-run
535
+ python tools/upload_pypi.py --require-all --yes
536
+ ```
537
+
538
+ ### 项目结构
539
+
540
+ ```
541
+ src/midscene/
542
+ ├── __init__.py # 顶层导出(MidsceneAgent / MidsceneWebAgent / 驱动 / 异常 …)
543
+ ├── config.py # MidsceneConfig(环境变量 / .env 支持)
544
+ ├── exceptions.py
545
+ ├── node_bootstrap.py # 首次运行从 nodejs.org 下载 Node/npm
546
+ ├── runtime.py # ServiceSpec + 按平台参数化的 npm install / 缓存
547
+ ├── node_service.py # NodeServiceManager(按 spec 名称多实例)
548
+ ├── base_agent.py # BaseAgent(跨平台 ai_* 方法 + RPC)
549
+ ├── agent_android.py # MidsceneAgent(BaseAgent) + 设备/系统方法
550
+ ├── agent_web.py # MidsceneWebAgent(BaseAgent) + goto/new_tab/set_viewport
551
+ ├── drivers.py # PuppeteerDriver(实现)+ Playwright/Bridge(占位)
552
+ ├── _pytest_plugin.py # midscene_agent / midscene_web_agent fixture + pytest11 入口
553
+ ├── _pytest_support.py # pytest 插件共享逻辑(失败截图/报告)
554
+ ├── py.typed
555
+ └── _node_driver/
556
+ ├── android/service/ # package.json(@midscene/android) + service.js
557
+ └── web/service/ # package.json(@midscene/web + puppeteer) + service.js
558
+
559
+ # 运行时缓存(不在 pip 包内,android/web 共享 Node 运行时):
560
+ # ~/.midscene/node_runtime/ ← Node + npm
561
+ # ~/.midscene/android/node_service/ ← npm install @midscene/android
562
+ # ~/.midscene/web/node_service/ ← npm install @midscene/web + puppeteer
563
+
564
+ tests/ # Android + Web 测试集中在根目录
565
+ tools/
566
+ ├── build_wheel.py # 构建 py3-none-any wheel + sdist
567
+ └── upload_pypi.py # 检查 dist/ 并上传 PyPI
568
+ ```
569
+
570
+ ---
571
+
572
+ ## License
573
+
574
+ MIT