feat: add macOS PlayCover/MaaTools support - #1720
Conversation
Automated PR 2026.07.30
There was a problem hiding this comment.
Hey - 我发现了两个问题,并留下了一些整体性的反馈:
- 在
ConnectionAttr.__init__和Connection.__init__中,针对 PlayCover 的提前return分支会跳过常规的 ADB 相关初始化;请再次确认,当is_playcover为 true 时,这些类上其他代码所依赖的所有属性和不变式(例如adb_client、is_over_http、package_name的处理)要么确实不会被使用,要么都被显式初始化为安全值,以避免属性缺失错误或者细微的行为差异。 - 新增的截图模式中,在 PlayCover 专用的
MacBGR和MacSCK之外,还包含了一个通用的RGBA值;建议重命名或限定RGBA的作用范围,使其清晰地表明是 PlayCover 专用(或者仅在is_playcover为 true 时进行校验),以避免在使用非 PlayCover 控制方式时引起混淆或被误选。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- 在 `ConnectionAttr.__init__` 和 `Connection.__init__` 中,针对 PlayCover 的提前 `return` 分支会跳过常规的 ADB 相关初始化;请再次确认,当 `is_playcover` 为 true 时,这些类上其他代码所依赖的所有属性和不变式(例如 `adb_client`、`is_over_http`、`package_name` 的处理)要么确实不会被使用,要么都被显式初始化为安全值,以避免属性缺失错误或者细微的行为差异。
- 新增的截图模式中,在 PlayCover 专用的 `MacBGR` 和 `MacSCK` 之外,还包含了一个通用的 `RGBA` 值;建议重命名或限定 `RGBA` 的作用范围,使其清晰地表明是 PlayCover 专用(或者仅在 `is_playcover` 为 true 时进行校验),以避免在使用非 PlayCover 控制方式时引起混淆或被误选。
## Individual Comments
### Comment 1
<location path="module/device/app_control.py" line_range="14-18" />
<code_context>
_app_u2_family = ['uiautomator2', 'minitouch', 'scrcpy']
def app_is_running(self) -> bool:
+ if self.is_playcover:
+ return True
method = self.config.script.device.control_method
</code_context>
<issue_to_address>
**issue (bug_risk):** 对于 PlayCover,从 `dump_hierarchy` 返回 `None` 可能会破坏那些期望获得 XML 元素的调用方。
函数签名和文档字符串承诺返回 `etree._Element`,但现在 PlayCover 分支返回 `None`,这会在调用方将 `self.hierarchy` 当作元素(例如调用 `.xpath(...)`)时导致运行时错误。如果这是有意为之,要么更新所有调用点以显式处理 `None`,要么返回一个最小的空层级元素,以保持类型契约的一致性。
</issue_to_address>
### Comment 2
<location path="tests/test_playcover_integration.py" line_range="11" />
<code_context>
+ROOT = Path(__file__).resolve().parents[1]
+
+
+class PlayCoverIntegrationTests(unittest.TestCase):
+ def test_template_defaults(self):
+ template = json.loads((ROOT / "config" / "template.json").read_text(encoding="utf-8"))
</code_context>
<issue_to_address>
**suggestion (testing):** 为 AppControl 中 PlayCover 特定行为添加测试覆盖(app_is_running/app_start/app_stop/dump_hierarchy)。
目前有一条 PlayCover 专用路径尚未被覆盖,即当 `is_playcover` 为 `True` 时 `AppControl` 的行为:
- `app_is_running` 应在不调用 ADB/u2 的情况下直接返回 `True`。
- `app_start` 和 `app_stop` 应该是空操作(no-op)。
- `dump_hierarchy` 应返回 `None`。
请添加一个测试,在 `is_playcover=True` 的情况下(通过真实的 `Connection` 或一个简单的桩对象)调用这些方法,并断言预期的返回值,同时确保不会调用任何 ADB/u2 相关方法,类似现有对 `detect_device`/`adb_connect` 的检查。这样可以防止 PlayCover 的提前返回逻辑在未来出现回归。
建议实现:
```python
ROOT = Path(__file__).resolve().parents[1]
class PlayCoverAppControlTests(unittest.TestCase):
def test_playcover_appcontrol_early_return_behavior(self):
# Deferred import to avoid hard dependency if AppControl is not available in some environments
try:
from module.device.control import AppControl
except ImportError:
self.skipTest("AppControl is not available in this environment")
class FakeConnection:
def __init__(self):
# PlayCover-specific flag
self.is_playcover = True
# ADB / u2-like interfaces that we can inspect for calls
self.adb = Mock()
self.u2 = Mock()
conn = FakeConnection()
# Construct AppControl with a PlayCover connection; adjust if your ctor signature differs
app_control = AppControl(conn)
# app_is_running: should return True and not hit ADB/u2
self.assertTrue(app_control.app_is_running())
# app_start/app_stop: should be no-ops and not hit ADB/u2
self.assertIsNone(app_control.app_start())
self.assertIsNone(app_control.app_stop())
# dump_hierarchy: should return None and not hit ADB/u2
self.assertIsNone(app_control.dump_hierarchy())
# Ensure no ADB/u2 calls were made (protect PlayCover early-return paths)
self.assertEqual(conn.adb.method_calls, [])
self.assertEqual(conn.u2.method_calls, [])
```
1. 如果 `AppControl` 不在 `module.device.control` 中,请在 `test_playcover_appcontrol_early_return_behavior` 中更新导入路径,使其匹配 `AppControl` 实际所在的模块(例如 `from module.app_control import AppControl` 等)。
2. 如果 `AppControl` 的构造函数并非只接收一个 `connection` 参数(例如期望关键字参数或不同的参数名),请相应调整 `app_control = AppControl(conn)` 这一行(比如改为 `app_control = AppControl(connection=conn)`)。
3. 如果你的连接对象上 ADB/u2 属性名称不同(例如 `adb_client`、`u2_client` 或 `device`),请更新 `FakeConnection` 桩类以及最后的 `assertEqual(...method_calls, [])` 断言,使其引用正确的属性。
</issue_to_address>帮我变得更有用!请对每条评论点选 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- The early
returnpaths for PlayCover inConnectionAttr.__init__andConnection.__init__skip the usual ADB-related initialization; please double-check that all attributes and invariants other parts of the code expect on these classes (e.g.,adb_client,is_over_http,package_namehandling) are either not used whenis_playcoveris true or are explicitly initialized to safe values to avoid attribute errors or subtle behavior differences. - The new screenshot modes include a generic
RGBAvalue alongside the PlayCover-specificMacBGRandMacSCK; consider renaming or scopingRGBAto be clearly PlayCover-specific (or validating it only whenis_playcoveris true) to avoid confusion or accidental selection when using non-PlayCover control methods.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The early `return` paths for PlayCover in `ConnectionAttr.__init__` and `Connection.__init__` skip the usual ADB-related initialization; please double-check that all attributes and invariants other parts of the code expect on these classes (e.g., `adb_client`, `is_over_http`, `package_name` handling) are either not used when `is_playcover` is true or are explicitly initialized to safe values to avoid attribute errors or subtle behavior differences.
- The new screenshot modes include a generic `RGBA` value alongside the PlayCover-specific `MacBGR` and `MacSCK`; consider renaming or scoping `RGBA` to be clearly PlayCover-specific (or validating it only when `is_playcover` is true) to avoid confusion or accidental selection when using non-PlayCover control methods.
## Individual Comments
### Comment 1
<location path="module/device/app_control.py" line_range="14-18" />
<code_context>
_app_u2_family = ['uiautomator2', 'minitouch', 'scrcpy']
def app_is_running(self) -> bool:
+ if self.is_playcover:
+ return True
method = self.config.script.device.control_method
</code_context>
<issue_to_address>
**issue (bug_risk):** Returning `None` from `dump_hierarchy` for PlayCover may break callers expecting an XML element.
The signature and docstring promise an `etree._Element`, but the PlayCover path now returns `None`, which can cause runtime errors when callers use `self.hierarchy` as an element (e.g., `.xpath(...)`). If this is intentional, either update call sites to handle `None` or return a minimal empty hierarchy element to keep the type contract consistent.
</issue_to_address>
### Comment 2
<location path="tests/test_playcover_integration.py" line_range="11" />
<code_context>
+ROOT = Path(__file__).resolve().parents[1]
+
+
+class PlayCoverIntegrationTests(unittest.TestCase):
+ def test_template_defaults(self):
+ template = json.loads((ROOT / "config" / "template.json").read_text(encoding="utf-8"))
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for PlayCover-specific behavior in AppControl (app_is_running/app_start/app_stop/dump_hierarchy).
One PlayCover-specific path that isn’t covered is `AppControl` behavior when `is_playcover` is `True`:
- `app_is_running` should return `True` without calling ADB/u2.
- `app_start` and `app_stop` should be no-ops.
- `dump_hierarchy` should return `None`.
Please add a test that exercises these methods with `is_playcover=True` (via a real `Connection` or a simple stub) and asserts both the expected return values and that ADB/u2-related methods are not invoked, similar to the `detect_device`/`adb_connect` checks. This will protect the PlayCover early-return logic from regressions.
Suggested implementation:
```python
ROOT = Path(__file__).resolve().parents[1]
class PlayCoverAppControlTests(unittest.TestCase):
def test_playcover_appcontrol_early_return_behavior(self):
# Deferred import to avoid hard dependency if AppControl is not available in some environments
try:
from module.device.control import AppControl
except ImportError:
self.skipTest("AppControl is not available in this environment")
class FakeConnection:
def __init__(self):
# PlayCover-specific flag
self.is_playcover = True
# ADB / u2-like interfaces that we can inspect for calls
self.adb = Mock()
self.u2 = Mock()
conn = FakeConnection()
# Construct AppControl with a PlayCover connection; adjust if your ctor signature differs
app_control = AppControl(conn)
# app_is_running: should return True and not hit ADB/u2
self.assertTrue(app_control.app_is_running())
# app_start/app_stop: should be no-ops and not hit ADB/u2
self.assertIsNone(app_control.app_start())
self.assertIsNone(app_control.app_stop())
# dump_hierarchy: should return None and not hit ADB/u2
self.assertIsNone(app_control.dump_hierarchy())
# Ensure no ADB/u2 calls were made (protect PlayCover early-return paths)
self.assertEqual(conn.adb.method_calls, [])
self.assertEqual(conn.u2.method_calls, [])
```
1. If `AppControl` is not located in `module.device.control`, update the import path in `test_playcover_appcontrol_early_return_behavior` to match the actual module where `AppControl` is defined (e.g. `from module.app_control import AppControl` or similar).
2. If `AppControl`'s constructor does not take a single `connection` argument (e.g. it expects keyword arguments or a different parameter name), adjust the `app_control = AppControl(conn)` line accordingly (for example, `app_control = AppControl(connection=conn)`).
3. If your ADB/u2 attributes are named differently on the connection (for example `adb_client`, `u2_client`, or `device`), update the `FakeConnection` stub and the final `assertEqual(...method_calls, [])` checks so they refer to the correct attributes.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| if self.is_playcover: | ||
| return True | ||
| method = self.config.script.device.control_method | ||
| # if self.is_wsa: | ||
| # package = self.app_current_wsa() |
There was a problem hiding this comment.
issue (bug_risk): 对于 PlayCover,从 dump_hierarchy 返回 None 可能会破坏那些期望获得 XML 元素的调用方。
函数签名和文档字符串承诺返回 etree._Element,但现在 PlayCover 分支返回 None,这会在调用方将 self.hierarchy 当作元素(例如调用 .xpath(...))时导致运行时错误。如果这是有意为之,要么更新所有调用点以显式处理 None,要么返回一个最小的空层级元素,以保持类型契约的一致性。
Original comment in English
issue (bug_risk): Returning None from dump_hierarchy for PlayCover may break callers expecting an XML element.
The signature and docstring promise an etree._Element, but the PlayCover path now returns None, which can cause runtime errors when callers use self.hierarchy as an element (e.g., .xpath(...)). If this is intentional, either update call sites to handle None or return a minimal empty hierarchy element to keep the type contract consistent.
| ROOT = Path(__file__).resolve().parents[1] | ||
|
|
||
|
|
||
| class PlayCoverIntegrationTests(unittest.TestCase): |
There was a problem hiding this comment.
suggestion (testing): 为 AppControl 中 PlayCover 特定行为添加测试覆盖(app_is_running/app_start/app_stop/dump_hierarchy)。
目前有一条 PlayCover 专用路径尚未被覆盖,即当 is_playcover 为 True 时 AppControl 的行为:
app_is_running应在不调用 ADB/u2 的情况下直接返回True。app_start和app_stop应该是空操作(no-op)。dump_hierarchy应返回None。
请添加一个测试,在 is_playcover=True 的情况下(通过真实的 Connection 或一个简单的桩对象)调用这些方法,并断言预期的返回值,同时确保不会调用任何 ADB/u2 相关方法,类似现有对 detect_device/adb_connect 的检查。这样可以防止 PlayCover 的提前返回逻辑在未来出现回归。
建议实现:
ROOT = Path(__file__).resolve().parents[1]
class PlayCoverAppControlTests(unittest.TestCase):
def test_playcover_appcontrol_early_return_behavior(self):
# Deferred import to avoid hard dependency if AppControl is not available in some environments
try:
from module.device.control import AppControl
except ImportError:
self.skipTest("AppControl is not available in this environment")
class FakeConnection:
def __init__(self):
# PlayCover-specific flag
self.is_playcover = True
# ADB / u2-like interfaces that we can inspect for calls
self.adb = Mock()
self.u2 = Mock()
conn = FakeConnection()
# Construct AppControl with a PlayCover connection; adjust if your ctor signature differs
app_control = AppControl(conn)
# app_is_running: should return True and not hit ADB/u2
self.assertTrue(app_control.app_is_running())
# app_start/app_stop: should be no-ops and not hit ADB/u2
self.assertIsNone(app_control.app_start())
self.assertIsNone(app_control.app_stop())
# dump_hierarchy: should return None and not hit ADB/u2
self.assertIsNone(app_control.dump_hierarchy())
# Ensure no ADB/u2 calls were made (protect PlayCover early-return paths)
self.assertEqual(conn.adb.method_calls, [])
self.assertEqual(conn.u2.method_calls, [])- 如果
AppControl不在module.device.control中,请在test_playcover_appcontrol_early_return_behavior中更新导入路径,使其匹配AppControl实际所在的模块(例如from module.app_control import AppControl等)。 - 如果
AppControl的构造函数并非只接收一个connection参数(例如期望关键字参数或不同的参数名),请相应调整app_control = AppControl(conn)这一行(比如改为app_control = AppControl(connection=conn))。 - 如果你的连接对象上 ADB/u2 属性名称不同(例如
adb_client、u2_client或device),请更新FakeConnection桩类以及最后的assertEqual(...method_calls, [])断言,使其引用正确的属性。
Original comment in English
suggestion (testing): Add coverage for PlayCover-specific behavior in AppControl (app_is_running/app_start/app_stop/dump_hierarchy).
One PlayCover-specific path that isn’t covered is AppControl behavior when is_playcover is True:
app_is_runningshould returnTruewithout calling ADB/u2.app_startandapp_stopshould be no-ops.dump_hierarchyshould returnNone.
Please add a test that exercises these methods with is_playcover=True (via a real Connection or a simple stub) and asserts both the expected return values and that ADB/u2-related methods are not invoked, similar to the detect_device/adb_connect checks. This will protect the PlayCover early-return logic from regressions.
Suggested implementation:
ROOT = Path(__file__).resolve().parents[1]
class PlayCoverAppControlTests(unittest.TestCase):
def test_playcover_appcontrol_early_return_behavior(self):
# Deferred import to avoid hard dependency if AppControl is not available in some environments
try:
from module.device.control import AppControl
except ImportError:
self.skipTest("AppControl is not available in this environment")
class FakeConnection:
def __init__(self):
# PlayCover-specific flag
self.is_playcover = True
# ADB / u2-like interfaces that we can inspect for calls
self.adb = Mock()
self.u2 = Mock()
conn = FakeConnection()
# Construct AppControl with a PlayCover connection; adjust if your ctor signature differs
app_control = AppControl(conn)
# app_is_running: should return True and not hit ADB/u2
self.assertTrue(app_control.app_is_running())
# app_start/app_stop: should be no-ops and not hit ADB/u2
self.assertIsNone(app_control.app_start())
self.assertIsNone(app_control.app_stop())
# dump_hierarchy: should return None and not hit ADB/u2
self.assertIsNone(app_control.dump_hierarchy())
# Ensure no ADB/u2 calls were made (protect PlayCover early-return paths)
self.assertEqual(conn.adb.method_calls, [])
self.assertEqual(conn.u2.method_calls, [])- If
AppControlis not located inmodule.device.control, update the import path intest_playcover_appcontrol_early_return_behaviorto match the actual module whereAppControlis defined (e.g.from module.app_control import AppControlor similar). - If
AppControl's constructor does not take a singleconnectionargument (e.g. it expects keyword arguments or a different parameter name), adjust theapp_control = AppControl(conn)line accordingly (for example,app_control = AppControl(connection=conn)). - If your ADB/u2 attributes are named differently on the connection (for example
adb_client,u2_client, ordevice), update theFakeConnectionstub and the finalassertEqual(...method_calls, [])checks so they refer to the correct attributes.
|
有空研究一下,哦对了提到dev分支 |
嗯呢 ,等会改。顺带我申请了加开发群,有空通过一下啦~ |
Walkthrough新增 Poem
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@module/device/method/playcover.py`:
- Around line 125-129: 调整 swipe 方法中的事件顺序:在发送 TOUCH_BEGAN 后等待 duration,再发送
TOUCH_MOVED,最后发送 TOUCH_ENDED;不要在 TOUCH_MOVED
之后再等待。必要时在等待期间按现有输入通道约定发送多个移动事件,以确保不同截图通道和输入方式保持一致的滑动行为。
In `@README.md`:
- Around line 84-92: 完善 README 中 macOS PlayCover 实验性集成的中英文说明:补充经过验证的 OASX app
获取或构建入口,并说明如何启动本地 OAS 服务及确认其配置,使用户能完成选择 MacPlayTools
前的环境准备。若流程仅支持开发者验证,请在两种语言版本中明确标注其范围;保留现有不分发二进制和 Python 依赖安装说明。
In `@requirements-macos-playcover.txt`:
- Line 34: 将 requirements-macos-playcover.txt 中的 requests 依赖升级至
requests>=2.32.0,并在升级前为现有 requests.Session 使用路径补充覆盖 verify=False
后同一主机请求不会复用未验证连接的测试。
- Line 25: Update the macOS dependency requirements around ppocr-onnx==0.0.3.9
to explicitly pin a compatible onnxruntime version for Python 3.10 on both Intel
and Apple Silicon with macOS 11/12 wheel support, then verify the selected
version resolves correctly for those environments.
In `@tasks/Script/config_device.py`:
- Around line 28-37: 在 Device 配置验证中新增截图方法与控制方法的成对校验:ControlMethod.MacPlayTools
仅允许与 MacBGR、RGBA、MacSCK 搭配,且这三种截图方法仅允许与 MacPlayTools
搭配;拒绝其余组合并保持现有配置校验行为不变。为这两类无效组合补充测试,覆盖 PlayCover 截图方法配合非 MacPlayTools,以及
MacPlayTools 配合非 PlayCover 截图方法。
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 8bffd8b3-ed06-4076-9fe6-0b7a911e80a3
📒 Files selected for processing (11)
README.mdmodule/device/app_control.pymodule/device/connection.pymodule/device/connection_attr.pymodule/device/control.pymodule/device/method/playcover.pymodule/device/screenshot.pyrequirements-macos-playcover.txttasks/Script/config_device.pytests/test_playcover_integration.pytests/test_playcover_protocol.py
| def swipe(self, p1, p2, duration=0.2): | ||
| self.touch(self.TOUCH_BEGAN, p1[0], p1[1]) | ||
| self.touch(self.TOUCH_MOVED, p2[0], p2[1]) | ||
| time.sleep(max(0, float(duration))) | ||
| self.touch(self.TOUCH_ENDED, p2[0], p2[1]) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
修正 swipe 的持续时间顺序。
第 127 行在等待前发送 TOUCH_MOVED。因此,坐标位移立即发生,而 duration 只延迟 TOUCH_ENDED。这不能表示从起点到终点的滑动时间,并会降低真实设备上的滑动识别稳定性。
将等待放在开始触控和移动触控之间。必要时发送多个 TOUCH_MOVED 事件。
建议修改
def swipe(self, p1, p2, duration=0.2):
self.touch(self.TOUCH_BEGAN, p1[0], p1[1])
- self.touch(self.TOUCH_MOVED, p2[0], p2[1])
time.sleep(max(0, float(duration)))
+ self.touch(self.TOUCH_MOVED, p2[0], p2[1])
self.touch(self.TOUCH_ENDED, p2[0], p2[1])As per path instructions: “不同截图通道、不同输入方式之间的行为是否仍然一致”。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@module/device/method/playcover.py` around lines 125 - 129, 调整 swipe
方法中的事件顺序:在发送 TOUCH_BEGAN 后等待 duration,再发送 TOUCH_MOVED,最后发送 TOUCH_ENDED;不要在
TOUCH_MOVED 之后再等待。必要时在等待期间按现有输入通道约定发送多个移动事件,以确保不同截图通道和输入方式保持一致的滑动行为。
Source: Path instructions
| 本实验性接入借鉴了 MAA(明日方舟小助手)的 [macOS PlayCover / MaaTools 接入思路](https://docs.maa.plus/zh-cn/manual/device/macos.html),并使用 [OASX](https://github.com/runhey/OASX) 作为界面。我们已从 OASX Flutter 源码编译出一个可在 macOS 本地运行的 app,用于本集成验证;本 PR 不上传或分发该二进制。此功能仍处于试验性阶段,不代表官方 release 已经包含 macOS app。 | ||
|
|
||
| Flutter app 仅是界面,不内置 Python 解释器、OCR、本地 OAS 服务或其依赖。请在 macOS 主机上安装 Python 3.10,创建 venv,并手动安装 [requirements-macos-playcover.txt](requirements-macos-playcover.txt) 中的 pip 包: | ||
|
|
||
| ```bash | ||
| python3.10 -m venv .venv | ||
| source .venv/bin/activate | ||
| python -m pip install --upgrade pip | ||
| python -m pip install -r requirements-macos-playcover.txt |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "README size and relevant sections"
wc -l README.md
sed -n '70,125p' README.md
echo
echo "Search macOS/OASX/playcover/OAS service mentions"
rg -n "OASX|PlayCover|MaaTools|requirements-macos-playcover|MacPlayTools|OAS|OASX|playcover|本地 OAS|macOS" -S . | head -200
echo
echo "Files named requirements-macos-playcover.txt and docs"
fd -a 'requirements-macos-playcover\.txt|README|.*mac.*|.*OASX.*' . | sed 's#^\./##' | head -200Repository: runhey/OnmyojiAutoScript
Length of output: 21602
补齐 OASX app 和本地 OAS 服务的可执行使用说明。
README 现在仅说明 PR 不分发 OASX 二进制,并给出 Python 依赖安装与 PlayCover 配置。但没有经过验证的 OASX 构建/获取入口,也没有启动本地 OAS 服务的命令和配置确认;当前说明无法让用户实际到达选择 MacPlayTools 前的环境准备状态。请补充构建或获取步骤,以及 OAS 服务的启动与配置方式;若仅为开发者验证流程,请在中英文说明中明确标注范围。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 84 - 92, 完善 README 中 macOS PlayCover
实验性集成的中英文说明:补充经过验证的 OASX app 获取或构建入口,并说明如何启动本地 OAS 服务及确认其配置,使用户能完成选择
MacPlayTools 前的环境准备。若流程仅支持开发者验证,请在两种语言版本中明确标注其范围;保留现有不分发二进制和 Python 依赖安装说明。
| numpy==1.24.3 | ||
| opencv-python==4.7.0.72 | ||
| Pillow==10.2.0 | ||
| ppocr-onnx==0.0.3.9 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3.10 -m pip install --dry-run --only-binary=:all: \
-r requirements-macos-playcover.txtRepository: runhey/OnmyojiAutoScript
Length of output: 210
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '--- candidate file ---\n'
wc -l requirements-macos-playcover.txt
sed -n '1,80p' requirements-macos-playcover.txt
printf '\n--- pyproject/setup-related dependency metadata ---\n'
python3 - <<'PY'
import os, re, json
for fn in ['pyproject.toml', 'setup.py', 'setup.cfg']:
if os.path.exists(fn):
print(f'\n## {fn}')
with open(fn) as f:
data=f.read()
print(data[:4000])
PY
printf '\n--- onnxruntime release metadata via pypi JSON API ---\n'
python3 - <<'PY'
import json, urllib.request, re, sys
url='https://pypi.org/pypi/onnxruntime/json'
with urllib.request.urlopen(url, timeout=20) as r:
data=json.load(r)['releases']
samples={}
for v,'info' in data.items():
ok=True
req='python_version <= 3.10'
for x in info.get('requires_python',''):
if '<' in req or '>' in req:
ok=False
if ok:
samples.setdefault(req,0)
samples[req]+=1
for req, count in list(samples.items())[:8]:
print(req, count)
target='0.0.3.9'
proj='https://pypi.org/pypi/ppocr-onnx/json'
raw=urllib.request.urlopen(proj, timeout=20).read()
meta=json.loads(raw)
if target in meta['releases']:
print('\nppocr-onnx target dependencies:')
for rel in meta['releases'][target]:
if rel.get('packagetype'):
print(rel['packagetype'], rel.get('requires_dist'))
else:
print('target not found', target, sorted(meta['releases'].keys())[-10:])
# Print relevant onnxruntime file tags for selected versions
for v in ['1.14.0','1.23.0','1.23.1','1.24.0']:
if v not in data:
print('\nONNXRuntime', v, 'not found')
continue
print('\nONNXRuntime', v)
for f in data[v]:
fn=f['filename']
mac=re.search(r'(macosx_\d+_\d+_\d+)', fn)
py=re.search(r'-cp(\d{2})(\w+)\d*-|^cp(\d{2})(\w+)\d*-|^py', fn)
if mac and fn.endswith('.whl'):
print(fn, 'macos_tag=', mac.group(1), py.group() if py else '')
PYRepository: runhey/OnmyojiAutoScript
Length of output: 253
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- candidate file ---'
wc -l requirements-macos-playcover.txt
sed -n '1,90p' requirements-macos-playcover.txt
printf '%s\n' ''
printf '%s\n' '--- pyproject/setup-related dependency metadata ---'
python3 - <<'PY'
import os
for fn in ['pyproject.toml', 'setup.py', 'setup.cfg']:
if os.path.exists(fn):
print(f'\n## {fn}')
with open(fn, encoding='utf-8') as f:
print(f.read()[:6000])
PY
printf '%s\n' ''
printf '%s\n' '--- onnxruntime/ppocr-onnx pypi release metadata ---'
python3 - <<'PY'
import json, urllib.request, re, sys
try:
with urllib.request.urlopen('https://pypi.org/pypi/onnxruntime/json', timeout=20) as r:
meta=json.loads(r.read())
data=meta['releases']
except Exception as e:
print('ERROR onnxruntime release metadata:', repr(e), file=sys.stderr)
sys.exit(0)
for label, versions in [('-- required_python samples for 3.10 target --',
['python_version <= 3.10', 'python_version < 3.11', 'python_version >= 3.11', '']
)]:
print(label)
samples={}
for v, files in data.items():
req=next((f['requires_python'] for f in files if f['packagetype']=='sdist'), '')
if '3.10' in req or '3.11' in req or not req:
samples[req] = samples.get(req, 0) + 1
for req, count in sorted(samples.items()):
print(f'{req or "(none)"} {count}')
target='0.0.3.9'
try:
raw=urllib.request.urlopen('https://pypi.org/pypi/ppocr-onnx/json', timeout=20).read()
pmeta=json.loads(raw)
except Exception as e:
print('ERROR ppocr-onnx metadata:', repr(e), file=sys.stderr)
sys.exit(0)
print(f'\nppocr-onnx target {target} presence:', target in pmeta['releases'])
if target in pmeta['releases']:
for rel in pmeta['releases'][target]:
if rel.get('packagetype'):
print(rel['packagetype'], rel.get('requires_dist'))
for v in ['1.14.0','1.23.0','1.23.1','1.24.0']:
print(f'\nONNXRuntime {v}')
if v not in data:
print('version not found')
continue
version_files=[f for f in data[v] if f['packagetype']=='bdist_wheel' and 'macosx' in f['filename']]
print(f'macOS wheel count={len(version_files)}')
for f in version_files[:50]:
fn=f['filename']
mac=re.search(r'(\d+_\d+_\d+)', fn)
py_re=re.search(r'-(cp(310|311|312).*|-py\d\.)', fn)
print(fn, 'mac_os=', mac.group(1) if mac else '', 'py=', py_re.group(1) if py_re else '')
print(f'\nTarget onnxruntime range from ppocr-onnx {target}:')
for rel in pmeta.get('releases',{}).get(target,[]):
reqs=rel.get('requires_dist') or []
for req in reqs:
if 'onnxruntime' in req:
print(req)
break
PY
printf '%s\n' ''
printf '%s\n' '--- local requirement references ---'
rg -n "onnxruntime|python_requires|python-version|macos|playcover|python3\.10|ppocr-onnx" -S requirements-macos-playcover.txt README.md pyproject.toml setup.py setup.cfg .github 2>/dev/null || trueRepository: runhey/OnmyojiAutoScript
Length of output: 6458
🌐 Web query:
PyPI pppocr-onnx 0.0.3.9 requires_dist onnxruntime
💡 Result:
The Python package ppocr-onnx version 0.0.3.9 requires the onnxruntime package as a dependency [1][2]. Official PyPI project metadata explicitly lists onnxruntime as a required package for this version, alongside others such as numpy, opencv-python, shapely, pyclipper, pillow, and requests [1][2]. This package is designed to perform text detection and recognition by utilizing models provided by PaddleOCR and the onnxruntime inference engine [1][2]. Users installing this package will typically have onnxruntime automatically resolved and installed by their Python package manager [1][2].
Citations:
🌐 Web query:
PyPI onnxruntime 1.14.0 Python 3.10 macOS wheel requirespython macosx
💡 Result:
The onnxruntime 1.14.0 release encountered known issues with its macOS wheel packaging, specifically involving architecture incompatibility (mismatch between arm64 and x86_64) [1]. These packaging problems were addressed and resolved in the subsequent patch release, onnxruntime 1.14.1 [2][1]. If you are experiencing installation errors or "no matching distribution" messages when attempting to install onnxruntime on macOS, it is recommended to use version 1.14.1 or later [2]. In some environments, if pip still fails to resolve the correct wheel due to older macOS SDK configurations, you may need to upgrade pip or use the following command to assist in correctly identifying compatible wheels [3]: SYSTEM_VERSION_COMPAT=0 pip install --no-cache-dir "onnxruntime>=1.14.1" For users on newer macOS versions or Apple Silicon (arm64), ensuring that you are using a Python environment that matches your hardware architecture is essential to avoid the architecture mismatch errors reported in the v1.14.0 release [1].
Citations:
- 1: incompatible architecture v1.14.0 microsoft/onnxruntime#14663
- 2: https://github.com/microsoft/onnxruntime/releases/tag/v1.14.1
- 3: pip doesn't install latest version of onnxruntime microsoft/onnxruntime#17166
🌐 Web query:
PyPI onnxruntime 1.23.0 Python 3.10 macOS wheel requirespython macosx
💡 Result:
ONNX Runtime version 1.23.0 was released on September 25, 2025 [1][2]. For this specific version, macOS wheels for Python 3.10 were provided, as the explicit removal of Python 3.10 support occurred in subsequent releases [3][4]. The release notes for version 1.23.0 explicitly stated that upcoming changes would include the discontinuation of Python 3.10 wheels [4]. Consequently, while version 1.23.0 and 1.23.1 included these wheels, version 1.23.2 and later releases dropped support for Python 3.10 macOS wheels [3]. Regarding Python version requirements, current versions of the onnxruntime package on PyPI typically specify a minimum Python version of 3.11 [5], though older versions like 1.23.2 indicated a requirement of 3.10 [6]. Users requiring Python 3.10 on macOS should utilize version 1.23.1 or earlier to ensure wheel availability [3].
Citations:
- 1: https://newreleases.io/project/pypi/onnxruntime/release/1.23.0
- 2: https://repology.org/maintainer/onnxruntime%40microsoft.com/feed-for-repo/pypi
- 3: [Build] 1.23.2 release dropped macOS 3.10 wheels microsoft/onnxruntime#26416
- 4: https://github.com/Microsoft/onnxruntime/releases/tag/v1.23.0
- 5: https://pypi.org/project/onnxruntime/
- 6: https://pypi.org/project/onnxruntime/1.23.2/
🌐 Web query:
site:pypi.org/project/onnxruntime/1.23.0/ "macosx" "cp310"
💡 Result:
In Python packaging and wheel filenames, the tag cp310 designates compatibility with CPython version 3.10. Wheels labeled with cp310 are intended for environments running Python 3.10. When combined with macosx in a filename, it indicates a pre-built binary package compiled specifically for macOS systems running CPython 3.10 [1].
Citations:
🌐 Web query:
site:pypi.org/project/onnxruntime/1.23.0/ "cp310" "macosx_*_12_0"
💡 Result:
The identifier cp310 macosx_12_0 refers to a Python wheel file naming convention defined by PEP 425, used to specify the compatibility of a pre-built binary package [1]. In this specific tag: cp310 indicates the wheel is compatible with CPython version 3.10. macosx_12_0 indicates the wheel is built for macOS version 12.0 or newer. These tags are used by pip and the Python packaging ecosystem to ensure that a downloaded package is compatible with the user's operating system and Python interpreter [1]. While specific details about the wheel tag format appear in technical distribution standards, package repositories like PyPI list these tags to confirm whether a user's system meets the requirements for a particular version of a library [1].
Citations:
固定并验证 onnxruntime macOS wheel 选择。
ppocr-onnx==0.0.3.9 传递依赖未固定在清单中,会由 pip 解析到 onnxruntime。如果解析结果不满足 Python 3.10 + Intel/Apple Silicon + macOS 11/12 的 wheel 标签要求,安装可能失败。固定一个已覆盖该环境的 onnxruntime 版本,或者在文档中明确最低 macOS 版本和架构支持范围。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@requirements-macos-playcover.txt` at line 25, Update the macOS dependency
requirements around ppocr-onnx==0.0.3.9 to explicitly pin a compatible
onnxruntime version for Python 3.10 on both Intel and Apple Silicon with macOS
11/12 wheel support, then verify the selected version resolves correctly for
those environments.
Source: MCP tools
| websockets==13.1 | ||
| zerorpc==0.6.3 | ||
| paho-mqtt==1.6.1 | ||
| requests==2.31.0 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
python3.10 -m pip_audit -r requirements-macos-playcover.txt
python3.10 -m pip install --dry-run 'requests>=2.32.2,<3'Repository: runhey/OnmyojiAutoScript
Length of output: 210
🌐 Web query:
CVE-2024-35195 requests 2.31.0 vulnerability fix version 2.32.2
💡 Result:
The vulnerability identified as CVE-2024-35195 affects the Python requests library [1][2]. It involves a flaw where, when using a requests.Session object, if the first request is made with verify=False, certificate verification may remain disabled for all subsequent requests to the same host, regardless of the verify parameter's value in later requests [1][3]. This vulnerability is fixed in version 2.32.0 of the requests library [1][3]. While you mentioned version 2.32.2 as a potential target, the official fix was implemented in version 2.32.0 [1][4]. Any version prior to 2.32.0 is considered affected [5][6]. Users are encouraged to upgrade to 2.32.0 or any later stable version to remediate this issue [1][4].
Citations:
- 1: https://osv.dev/vulnerability/CVE-2024-35195
- 2: https://nvd.nist.gov/vuln/detail/cve-2024-35195
- 3: https://notcve.org/cve/CVE-2024-35195
- 4: https://security-tracker.debian.org/tracker/CVE-2024-35195
- 5: https://www.opencve.io/cve/CVE-2024-35195
- 6: https://security.alpinelinux.org/vuln/CVE-2024-35195
🌐 Web query:
requests 2.32.0 CVE-2024-35195 fix
💡 Result:
CVE-2024-35195 is a security vulnerability in the requests library that was resolved in version 2.32.0 [1][2][3]. The vulnerability occurred when using a requests.Session object [3]. If the first request made with a session included verify=False to disable SSL/TLS certificate verification, all subsequent requests to the same host via that session would also ignore certificate verification, even if verify=True was explicitly set in those later requests [2][4][3]. This persistence of the initial verification setting resulted from the reuse of connections within the session's connection pool [3]. To fix this issue, users should upgrade to requests version 2.32.0 or later [1][3][5]. For environments where an immediate upgrade is not possible, suggested mitigations include avoiding the use of verify=False for the initial request to a host within a session, or explicitly closing the session object to clear the connection pool if verify=False must be used [5].
Citations:
- 1: https://github.com/psf/requests/releases/tag/v2.32.0
- 2: https://osv.dev/vulnerability/CVE-2024-35195
- 3: GHSA-9wx4-h78v-vm56
- 4: https://nvd.nist.gov/vuln/detail/CVE-2024-35195
- 5: CVE-2024-35195 nlohmann/json#4424
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== requirements line =="
sed -n '30,38p' requirements-macos-playcover.txt >/tmp/requirements-macos-playcover-requests.txt
cat /tmp/requirements-macos-playcover-requests.txt
echo
echo "== occurrences of Session/verify/CustomAdapter in repo =="
rg -n "requests\.Session|Session\(|verify\s*=\s*False|verify\s*=\s*True|[vV]erify\s*=" -S . \
-g '!build/**' -g '!dist/**' -g '!.venv/**' -g '!venv/**' -g '!*cache*' \
| head -n 200
echo
echo "== python availability and pip audit if present =="
python3 --version
python3 -m pip --version
python3 -m pip_audit --version >/tmp/pip_audit_version.txt 2>/tmp/pip_audit_error.txt || true
cat /tmp/pip_audit_version.txt
cat /tmp/pip_audit_error.txt
echo
echo "== dry-run upgrade check via pip resolver api =="
python3 - <<'PY'
from urllib.request import urlopen
import json
urls = [
"https://pypi.org/pypi/requests/json",
"https://osv.dev/v1/vulns/CVE-2024-35195",
]
for url in urls:
try:
with urlopen(url, timeout=20) as r:
data = json.load(r)
print(f"--- {url} ---")
if "releases" in data:
print("latest_requests_version", sorted(data["releases"].keys())[-1][:50])
if "versions" in data:
fixed = [v for v, effects in data["versions"].items() if any("requests" in str(v) and "fixed" in str(e.get("fixed_versions") or "") for e in [effects]) or "fixed" in effects]
print("osv_versions", data.get("versions", []))
print("data_keys", list(data.keys()))
print("schema_version", data.get("schema_version"))
print("severity", data.get("severity"))
print("affected", data.get("affected", []))
else:
print(data)
except Exception as e:
print(url, "ERROR", repr(e))
PYRepository: runhey/OnmyojiAutoScript
Length of output: 1674
升级 requests 到修复版本。
requests==2.31.0 存在 CVE-2024-35195:当 requests.Session 先使用 verify=False,后续同一主机的请求可能继续复用未验证证书的连接。升级到 requests>=2.32.0,并在升级前覆盖测试代码中已有的 Session 路径。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@requirements-macos-playcover.txt` at line 34, 将
requirements-macos-playcover.txt 中的 requests 依赖升级至 requests>=2.32.0,并在升级前为现有
requests.Session 使用路径补充覆盖 verify=False 后同一主机请求不会复用未验证连接的测试。
Source: MCP tools
| MacBGR = 'MacBGR' | ||
| RGBA = 'RGBA' | ||
| MacSCK = 'MacSCK' | ||
|
|
||
| class ControlMethod(str, Enum): | ||
| ADB = 'adb' | ||
| UIAUTOMATOR2 = 'uiautomator2' | ||
| MINITOUCH = 'minitouch' | ||
| WINDOW_MESSAGE = 'window_message' | ||
| MacPlayTools = 'MacPlayTools' |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
验证 PlayCover 截图方法和控制方法的组合。
当前枚举允许 MacBGR、RGBA 或 MacSCK 与 minitouch 等控制方法组合。此时 module/device/connection_attr.py 不会初始化 playcover_client,但 module/device/screenshot.py 会调用 self.playcover_client.screenshot(),从而触发 AttributeError。
同样,MacPlayTools 配合 auto 或 ADB 截图方法会创建 PlayCover 连接,但截图会走 ADB 路径。请在 Device 配置验证中要求这两项成对使用:MacPlayTools 只能配合三个 PlayCover 截图方法,三个 PlayCover 截图方法也只能配合 MacPlayTools。同时添加这两个无效组合的测试。
As per path instructions: “不同截图通道、不同输入方式之间的行为是否仍然一致”。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tasks/Script/config_device.py` around lines 28 - 37, 在 Device
配置验证中新增截图方法与控制方法的成对校验:ControlMethod.MacPlayTools 仅允许与 MacBGR、RGBA、MacSCK
搭配,且这三种截图方法仅允许与 MacPlayTools 搭配;拒绝其余组合并保持现有配置校验行为不变。为这两类无效组合补充测试,覆盖 PlayCover
截图方法配合非 MacPlayTools,以及 MacPlayTools 配合非 PlayCover 截图方法。
Source: Path instructions
|
这玩意居然是裸socket,没有这些接口吗app_start,app_stop,app_is_running。 第二点,MacBGR,RGBA,MacSCK这三个太多了,知道是专门给mac处理的就好了,太多用户有心智负担,然后最好可以删到一个,然后我没看懂RGBA是干啥的,好像和MacSCk没区别,我看到MacBGR会多一些校验,这边是有什么性能上的考虑吗,这边速度有测试过吗,还有最好可以在redme上写一下,或者assets/i18n/zh-CN.json这里可以覆盖对应的说明文字。 这种最好打个日志,表示不同的case, 感觉直接return后面会留坑 协议帧校验和坐标值做得扎实 |
|
嗯呢,这个逻辑先是 PlayCover 官方的 PlayTools,然后 MAA 使用的 PlayCover Fork 又接入了 MaaTools。MaaTools 实际是在 PlayTools 里增加了一套本地 TCP 服务,把截图和触控能力提供给 MAA,并不是 HTTP JSON-RPC。 RGBA 的话,请看~
现在的SCK 和 RGBA 一样我copy下来的还没改。rgba你也看到啦是metal,metal的话阴阳师在macos上刚好也就是metal渲染的,我就先用了这个,至少现在图像识别没什么问题 这点我倒是很骄傲,因为我翻了大半个互联网是没找到任何跟这个问题有关的解决方案,纯翻日志翻出来的,而且翻这个日志还得我下了两个不同版本的阴阳师然后花几十个g流量把他们都更新完了然后一点一点看对比出来,codex立大功。指令的话我翻了playcover中文网,他们有个算一丢丢类似的issue吧,洛克王国初始化资源失败,我看了眼觉得很像,虽然逻辑八竿子打不着,我看他映射我就试了试,跑通了。 MAA他们那个MacSCK非常好使,是直接找到窗口,然后ScreenCaptureKit 捕获。 理论上不需要转果子的vimage所以应该效率最高,现在这个bgr老是会掉色变成黑白,饱受其害。 然后的话return确实,这部分最初确实是按照 MAA 的接过来的,当时主要先保证 PlayCover 能正常截图和触控。不过稳定运行的话这些个坑找机会要填了。 其他的就咳咳,前人种树后人乘凉,不敢当! |
|
RGBA 换一个名字,可以和别的一样在前面加上Mac,然后让AI 在这里加上module/daemon/benchmark.py测试。 |
|
你是提这个功能的,既然你的最新阴阳师版本能跑,那就按照你的为准,最多标注上 阴阳师版本大于 1.8.4.4。多留一个选项背后所声明维护量是会变多的 |
中文
本 PR 为 OAS 添加 macOS PlayCover/MaaTools 支持,接入思路参考了 MAA 明日方舟 macOS 使用方案。
主要改动
MacPlayTools。MacBGR、RGBA和MacSCK截图方式。requirements-macos-playcover.txt。使用方式
1280×720。OAS 要求使用该分辨率,否则无法正常识别和点击。control=MacPlayTools)。MacBGR、RGBA或MacSCK。serial,例如localhost:1718。adb或minitouch即可。已使用 OASX Flutter 源码编译并验证 macOS 本地 app。本 PR 不上传该 app,也不内置 Python、OCR 或 OAS 服务。Python 及相关依赖需要按照
requirements-macos-playcover.txt手动安装。测试结果:PlayCover 协议、截图、触控及控制方式切换测试共
13/13通过。English
This PR adds macOS PlayCover/MaaTools support to OAS, following the approach described in the MAA macOS guide.
Changes
MacPlayToolsas a selectable control method in OASX.MacBGR,RGBA, andMacSCKscreenshot methods.requirements-macos-playcover.txtfor manual macOS dependency installation.Usage
1280×720. OAS requires this resolution for recognition and tapping.control=MacPlayTools) in OASX.MacBGR,RGBA, orMacSCKas the screenshot method.serial, for examplelocalhost:1718.adborminitouchwhen using an Android emulator.A local macOS app compiled from the OASX Flutter source was used for validation. The app is not included in this PR and does not bundle Python, OCR, or the OAS service. Python and the required packages must be installed manually using
requirements-macos-playcover.txt.Validation result: all
13/13PlayCover protocol, screenshot, touch, and control-selection tests passed.Summary by Sourcery
添加实验性的 macOS PlayCover/MaaTools 集成为现有基于 Android 的方法之外的备用设备控制路径。
新功能:
文档:
测试:
Original summary in English
Summary by Sourcery
Add experimental macOS PlayCover/MaaTools integration as an alternative device control path alongside existing Android-based methods.
New Features:
Documentation:
Tests:
Summary by CodeRabbit
新功能
文档
测试