Skip to content

refactor(main): 解除对Win32的强依赖 - #423

Merged
HarcoChen merged 9 commits into
AUTO-MAS-Project:devfrom
HarcoChen:refactor/Win32-Cleanup
Aug 29, 2026
Merged

refactor(main): 解除对Win32的强依赖#423
HarcoChen merged 9 commits into
AUTO-MAS-Project:devfrom
HarcoChen:refactor/Win32-Cleanup

Conversation

@HarcoChen

@HarcoChen HarcoChen commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

解除了后端对Win32的强依赖,现在支持跨平台启动后端

Sourcery 摘要

通过引入平台能力层并隔离 Windows 专属功能,解除后端对 Win32 的强依赖并实现跨平台启动。

新功能:

  • 支持 Linux 等非 Windows 平台启动后端,并提供平台相关的电源操作能力。

错误修复:

  • 避免非 Windows 环境因导入 Win32 专用依赖而无法启动。
  • 在不支持的平台上明确跳过或拒绝 Windows 专属功能,避免执行无效操作。

改进:

  • 引入统一的平台能力抽象,集中管理窗口、输入、进程、电源、自启动和安全存储等平台服务。
  • 延迟加载核心管理器和 MaaFW Win32 适配器,降低启动时的依赖和初始化开销。
  • 将系统服务、终末地登录、模拟器进程管理及配置加密迁移到平台服务接口。

构建:

  • 将 pywin32 和 keyboard 调整为仅在 Windows 平台安装的依赖。

杂项:

  • 移除不再使用的终末地 PC Win32 适配资源和实现。
Original summary in English

Sourcery 总结

将后端启动和核心服务与 Win32 依赖解耦,以支持跨平台运行,同时保留 Windows 特有功能。

新功能:

  • 通过特定平台的功能实现,支持在非 Windows 平台上启动后端。
  • 提供支持平台感知的电源、启动、进程、窗口、协议和机密存储服务接口。

错误修复:

  • 防止因导入仅限 Windows 的依赖而导致非 Windows 平台启动失败。
  • 在不受支持的平台上,明确拒绝或跳过仅限 Windows 的操作。

增强功能:

  • 通过平台服务隔离 Windows 特有功能,并延迟加载重量级管理器和 MaaFW 适配器。
  • 集中处理跨平台的进程和系统服务行为,同时保留兼容性入口点。

构建:

  • 将 pywin32 和 keyboard 依赖限制为 Windows 安装。

测试:

  • 添加平台功能测试,涵盖非 Windows 导入、不受支持的操作以及平台实现的一致性。

杂项:

  • 移除已过时的 Win32 专用包重新导出,以及未使用的 EndField PC 适配器资源。
Original summary in English

Sourcery 摘要

将后端启动和核心服务与 Win32 解耦,以支持跨平台运行,同时保留 Windows 特有的功能。

新功能:

  • 通过基于平台的功能实现,支持在非 Windows 平台上启动后端。
  • 为系统电源、启动、进程、窗口、协议和机密存储操作提供平台服务。

错误修复:

  • 防止因导入仅限 Windows 的依赖项而导致的非 Windows 启动失败。
  • 在不受支持的平台上,明确拒绝或跳过仅限 Windows 的操作。

增强功能:

  • 通过通用平台层和 Windows 特定的平台层,将核心服务和任务集成与 Win32 解耦。
  • 延迟加载重量级管理器和 MaaFW 适配器,以减少启动依赖项和初始化开销。

构建:

  • 将 pywin32 和 keyboard 依赖项限制为 Windows 安装。

测试:

  • 添加平台功能测试,覆盖非 Windows 导入、不受支持的操作,以及不同平台实现之间的一致性。

日常维护:

  • 移除已废弃的 Win32 软件包重新导出内容,以及未使用的 EndField PC Win32 适配器资源。
Original summary in English

Sourcery 摘要

通过引入平台能力层并隔离 Windows 专属功能,使后端支持跨平台启动。

新功能:

  • 支持在非 Windows 平台启动后端,并通过平台能力接口提供进程、窗口、电源、自启动、协议和安全存储等服务。

错误修复:

  • 避免非 Windows 环境因导入 Win32 专用依赖而导致启动失败,并对不支持的平台操作提供明确处理。

改进:

  • 将 Windows 专属实现与通用服务解耦,统一平台相关能力入口并延迟加载重量级管理器和适配器。

构建:

  • 将 pywin32 和 keyboard 限制为仅在 Windows 平台安装。

测试:

  • 新增平台能力测试,验证非 Windows 导入、能力错误和跨平台实现接口一致性。

杂项:

  • 移除 MaaFW 包级 Win32 重新导出,并迁移系统、进程、窗口和安全服务至平台层。
Original summary in English

Sourcery 摘要

通过引入平台能力抽象并隔离 Windows 专属功能,使后端支持跨平台启动,同时保留 Windows 平台能力。

新功能:

  • 支持后端在非 Windows 平台启动,并提供按平台实现的系统、电源、进程、窗口、协议、自启动和安全存储能力。

错误修复:

  • 避免非 Windows 环境因加载 Win32 专用依赖而启动失败,并对不支持的平台操作提供明确处理。

改进:

  • 通过统一的平台能力层隔离 Windows 专属实现,并延迟加载核心管理器及 MaaFW 适配器。

构建:

  • 将 pywin32 和 keyboard 限制为仅在 Windows 平台安装。

测试:

  • 新增平台能力测试,验证非 Windows 导入、能力错误及不同平台实现的接口一致性。

杂项:

  • 移除 MaaFW 包级 Win32 重新导出,并将系统、进程、窗口和安全相关逻辑迁移至平台服务入口。
Original summary in English

Sourcery 摘要

通过平台能力抽象解耦后端与 Win32 依赖,使应用支持跨平台启动并保留 Windows 专属功能。

新功能:

  • 支持后端在非 Windows 平台启动,并提供按平台实现的系统、电源、自启动、进程、窗口、协议和安全存储能力。

错误修复:

  • 避免非 Windows 环境因加载 Win32 专用依赖而无法启动,并对不支持的平台操作提供明确处理。

增强功能:

  • 引入统一的平台能力层,隔离 Windows 专属实现,并将核心管理器、适配器及相关能力改为按需加载。

构建:

  • 将 pywin32 和 keyboard 限制为仅在 Windows 平台安装。

测试:

  • 新增平台能力测试,验证非 Windows 导入、能力错误、窗口降级行为及不同平台实现的一致性。

维护:

  • 移除 MaaFW 包级 Win32 重新导出,并保留系统、进程、窗口和安全服务的兼容入口。
Original summary in English

Sourcery 摘要

将后端核心启动流程与 Win32 解耦,以支持跨平台运行并保留 Windows 专属功能。

新功能:

  • 支持后端在非 Windows 平台启动,并通过平台能力提供进程、窗口、电源、自启动、协议和安全存储服务。

错误修复:

  • 避免非 Windows 环境因加载 Win32 或桌面专用依赖而无法启动,并对不支持的平台操作提供明确处理。

增强:

  • 引入统一的平台能力抽象,隔离 Windows 专属实现并延迟加载核心管理器及适配器。
  • 将系统服务、进程管理、窗口操作和配置加密迁移到平台服务入口,同时保留兼容接口。

构建:

  • 将 pywin32 和 keyboard 依赖限制为仅在 Windows 平台安装。

测试:

  • 新增平台能力和无头环境导入测试,验证非 Windows 依赖隔离、不支持能力处理及跨平台接口一致性。

杂项:

  • 移除 MaaFW 包级 Win32 重新导出及不再使用的专用适配资源。
Original summary in English

Sourcery 摘要

通过平台能力抽象解耦后端启动流程与 Win32 依赖,同时保留 Windows 专属功能。

新功能:

  • 支持后端在非 Windows 平台启动,并通过平台能力提供电源、进程、窗口、协议和安全存储等服务。

错误修复:

  • 避免非 Windows 环境因导入 Win32 或桌面专用依赖而启动失败,并对不支持的平台操作提供明确处理。

改进:

  • 引入统一的平台能力层,隔离 Windows 专属实现并延迟加载核心管理器、适配器及桌面依赖。
  • 将系统服务、进程管理、窗口操作、自启动和配置加密迁移到平台服务接口,同时保留兼容入口。

构建:

  • 将 pywin32 和 keyboard 依赖限制为仅在 Windows 平台安装。

测试:

  • 新增平台能力和无头环境导入测试,验证依赖隔离、不支持能力处理及跨平台接口一致性。

杂项:

  • 移除 MaaFW 包级 Win32 重新导出及不再使用的专用适配资源。
Original summary in English

Summary by Sourcery

通过平台能力抽象解耦后端启动流程与 Win32 依赖,同时保留 Windows 专属功能。

New Features:

  • 支持后端在非 Windows 平台启动,并通过平台能力提供电源、进程、窗口、协议和安全存储等服务。

Bug Fixes:

  • 避免非 Windows 环境因导入 Win32 或桌面专用依赖而启动失败,并对不支持的平台操作提供明确处理。

Enhancements:

  • 引入统一的平台能力层,隔离 Windows 专属实现并延迟加载核心管理器、适配器及桌面依赖。
  • 将系统服务、进程管理、窗口操作、自启动和配置加密迁移到平台服务接口,同时保留兼容入口。

Build:

  • 将 pywin32 和 keyboard 依赖限制为仅在 Windows 平台安装。

Tests:

  • 新增平台能力和无头环境导入测试,验证依赖隔离、不支持能力处理及跨平台接口一致性。

Chores:

  • 移除 MaaFW 包级 Win32 重新导出及不再使用的专用适配资源。

@HarcoChen
HarcoChen changed the base branch from main to dev August 26, 2026 15:05
@sourcery-ai

sourcery-ai Bot commented Aug 26, 2026

Copy link
Copy Markdown

审查者指南

本 PR 通过平台能力抽象、Windows/common 双实现和按需导入,将后端启动、系统服务、进程/窗口操作及安全存储与 Win32 解耦;Windows 继续使用原生能力,非 Windows 则可启动并对不支持的功能进行安全跳过或明确拒绝,同时将 Windows 依赖改为条件安装并补充平台兼容性测试。

平台特定电源操作的时序图

sequenceDiagram
    participant System as System
    participant Power as power
    participant WindowsPower as WindowsPowerController
    participant CommonPower as CommonPowerController
    participant OS as OperatingSystem

    System->>Power: execute(action)
    alt Windows
        Power->>WindowsPower: execute(action)
        WindowsPower->>OS: subprocess.run(command)
    else non-Windows
        Power->>CommonPower: execute(action)
        CommonPower-->>System: UnsupportedPlatformError
    end
Loading

延迟加载 Windows 适配器的时序图

sequenceDiagram
    participant Main as main
    participant Platform as IS_WINDOWS
    participant Importer as importlib
    participant Adapter as ArknightWin32Toolkit
    participant Timer as MainTimer

    Main->>Platform: check platform
    alt Windows
        Main->>Importer: import_module(app.MaaFW.ArknightWin32)
        Main->>Adapter: init()
        Main->>Timer: start()
    else non-Windows
        Main->>Timer: start()
    end
Loading

文件级变更

变更 详情 文件
引入统一的平台能力分层,并将 Windows 专属实现与通用降级实现隔离。
  • 新增电源、自启动、进程、窗口、协议和机密存储的平台入口及 Windows/common 实现
  • 非 Windows 平台对不支持能力返回明确异常或安全跳过
  • 将系统服务迁移到平台服务,并保留旧系统服务入口兼容调用方
app/services/platform/__init__.py
app/services/platform/common/__init__.py
app/services/platform/common/power.py
app/services/platform/common/startup.py
app/services/platform/common/system.py
app/services/platform/power.py
app/services/platform/startup.py
app/services/platform/windows/__init__.py
app/services/platform/windows/power.py
app/services/platform/windows/startup.py
app/services/system.py
app/utils/platform/__init__.py
app/utils/platform/common/__init__.py
app/utils/platform/common/errors.py
app/utils/platform/common/process_platform.py
app/utils/platform/common/process_runner.py
app/utils/platform/common/secret.py
app/utils/platform/common/window.py
app/utils/platform/process.py
app/utils/platform/secret.py
app/utils/platform/window.py
app/utils/platform/windows/__init__.py
app/utils/platform/windows/process.py
app/utils/platform/windows/secret.py
app/utils/platform/windows/window.py
移除核心启动路径中的 Win32 硬依赖,并通过延迟导入避免非 Windows 环境加载专用模块。
  • 按平台条件导入 win32api、win32gui、win32con、win32process、winreg、keyboard 等依赖
  • 延迟加载 core 管理器、MaaFW 适配器及 DPI/Win32 API
  • 在模拟器可见性、终末地登录和 Arknights PC 任务等 Windows 专属流程中增加平台检查
app/MaaFW/ArknightWin32.py
app/MaaFW/__init__.py
app/core/__init__.py
app/core/config.py
app/core/timer.py
app/task/MaaEnd/tools/login.py
app/utils/emulator/general.py
app/utils/emulator/ldplayer.py
app/utils/emulator/mumu.py
app/utils/emulator/tools.py
main.py
将窗口、进程、脚本启动、OCR 和配置加密逻辑改为使用平台能力接口。
  • 把进程管理和进程运行器迁移到通用平台实现,并由平台对象提供创建参数及协议打开能力
  • 将 OCR 窗口操作和脚本默认打开行为改为平台服务调用
  • 将 DPAPI 实现移入 Windows secret 适配器,非 Windows 明确拒绝
app/utils/OCR/OCRtool.py
app/utils/ProcessManager.py
app/utils/platform/common/process.py
app/utils/security.py
app/utils/tools.py
app/task/general/tools/ExecuteScript.py
调整 Windows 专属依赖与历史适配资源,支持跨平台安装和启动。
  • 将 pywin32 和 keyboard 限制为 Windows 环境安装
  • 移除 MaaFW 包级 Win32 re-export 及不再使用的 EndField PC 适配实现/资源
  • 保留兼容导出并更新进程创建标志和更新流程
requirements.txt
app/utils/constants.py
app/services/update.py
res/version.json
增加平台能力的非 Windows 导入、能力错误和接口一致性测试。
  • 验证公共入口不加载 Win32 依赖且非 Windows 创建参数正确
  • 验证不支持窗口能力抛出带能力名称的异常
  • 静态检查 common 与 Windows 平台实现的导出一致性并覆盖 OCR 导入
tests/AGENTS.md
tests/platform/__init__.py
tests/platform/test_entries.py

提示和命令

与 Sourcery 交互

  • 触发新的审查: 在拉取请求中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 回复审查评论,请 Sourcery 根据该评论创建 issue。也可以回复审查评论并使用 @sourcery-ai issue,从该评论创建 issue。
  • 生成拉取请求标题: 在拉取请求标题的任意位置写入 @sourcery-ai,即可随时生成标题。也可以在拉取请求中评论 @sourcery-ai title,以随时(重新)生成标题。
  • 生成拉取请求摘要: 在拉取请求正文中任意位置写入 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。也可以在拉取请求中评论 @sourcery-ai summary,以随时(重新)生成摘要。
  • 生成审查者指南: 在拉取请求中评论 @sourcery-ai guide,即可随时(重新)生成审查者指南。
  • 解决所有 Sourcery 评论: 在拉取请求中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这会很有用。
  • 忽略所有 Sourcery 审查: 在拉取请求中评论 @sourcery-ai dismiss,即可忽略所有现有的 Sourcery 审查。如果你想从新的审查开始,这尤其有用——别忘了评论 @sourcery-ai review 以触发新的审查!

自定义使用体验

访问你的控制面板

  • 启用或禁用审查功能,例如 Sourcery 生成的拉取请求摘要、审查者指南及其他功能。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

获取帮助

Original review guide in English

Reviewer's Guide

本 PR 通过平台能力抽象、Windows/common 双实现和按需导入,将后端启动、系统服务、进程/窗口操作及安全存储与 Win32 解耦;Windows 继续使用原生能力,非 Windows 则可启动并对不支持的功能进行安全跳过或明确拒绝,同时将 Windows 依赖改为条件安装并补充平台兼容性测试。

Sequence diagram for platform-specific power operations

sequenceDiagram
    participant System as System
    participant Power as power
    participant WindowsPower as WindowsPowerController
    participant CommonPower as CommonPowerController
    participant OS as OperatingSystem

    System->>Power: execute(action)
    alt Windows
        Power->>WindowsPower: execute(action)
        WindowsPower->>OS: subprocess.run(command)
    else non-Windows
        Power->>CommonPower: execute(action)
        CommonPower-->>System: UnsupportedPlatformError
    end
Loading

Sequence diagram for deferred Windows adapter loading

sequenceDiagram
    participant Main as main
    participant Platform as IS_WINDOWS
    participant Importer as importlib
    participant Adapter as ArknightWin32Toolkit
    participant Timer as MainTimer

    Main->>Platform: check platform
    alt Windows
        Main->>Importer: import_module(app.MaaFW.ArknightWin32)
        Main->>Adapter: init()
        Main->>Timer: start()
    else non-Windows
        Main->>Timer: start()
    end
Loading

File-Level Changes

Change Details Files
引入统一的平台能力分层,并将 Windows 专属实现与通用降级实现隔离。
  • 新增电源、自启动、进程、窗口、协议和机密存储的平台入口及 Windows/common 实现
  • 非 Windows 平台对不支持能力返回明确异常或安全跳过
  • 将系统服务迁移到平台服务,并保留旧系统服务入口兼容调用方
app/services/platform/__init__.py
app/services/platform/common/__init__.py
app/services/platform/common/power.py
app/services/platform/common/startup.py
app/services/platform/common/system.py
app/services/platform/power.py
app/services/platform/startup.py
app/services/platform/windows/__init__.py
app/services/platform/windows/power.py
app/services/platform/windows/startup.py
app/services/system.py
app/utils/platform/__init__.py
app/utils/platform/common/__init__.py
app/utils/platform/common/errors.py
app/utils/platform/common/process_platform.py
app/utils/platform/common/process_runner.py
app/utils/platform/common/secret.py
app/utils/platform/common/window.py
app/utils/platform/process.py
app/utils/platform/secret.py
app/utils/platform/window.py
app/utils/platform/windows/__init__.py
app/utils/platform/windows/process.py
app/utils/platform/windows/secret.py
app/utils/platform/windows/window.py
移除核心启动路径中的 Win32 硬依赖,并通过延迟导入避免非 Windows 环境加载专用模块。
  • 按平台条件导入 win32api、win32gui、win32con、win32process、winreg、keyboard 等依赖
  • 延迟加载 core 管理器、MaaFW 适配器及 DPI/Win32 API
  • 在模拟器可见性、终末地登录和 Arknights PC 任务等 Windows 专属流程中增加平台检查
app/MaaFW/ArknightWin32.py
app/MaaFW/__init__.py
app/core/__init__.py
app/core/config.py
app/core/timer.py
app/task/MaaEnd/tools/login.py
app/utils/emulator/general.py
app/utils/emulator/ldplayer.py
app/utils/emulator/mumu.py
app/utils/emulator/tools.py
main.py
将窗口、进程、脚本启动、OCR 和配置加密逻辑改为使用平台能力接口。
  • 把进程管理和进程运行器迁移到通用平台实现,并由平台对象提供创建参数及协议打开能力
  • 将 OCR 窗口操作和脚本默认打开行为改为平台服务调用
  • 将 DPAPI 实现移入 Windows secret 适配器,非 Windows 明确拒绝
app/utils/OCR/OCRtool.py
app/utils/ProcessManager.py
app/utils/platform/common/process.py
app/utils/security.py
app/utils/tools.py
app/task/general/tools/ExecuteScript.py
调整 Windows 专属依赖与历史适配资源,支持跨平台安装和启动。
  • 将 pywin32 和 keyboard 限制为 Windows 环境安装
  • 移除 MaaFW 包级 Win32 re-export 及不再使用的 EndField PC 适配实现/资源
  • 保留兼容导出并更新进程创建标志和更新流程
requirements.txt
app/utils/constants.py
app/services/update.py
res/version.json
增加平台能力的非 Windows 导入、能力错误和接口一致性测试。
  • 验证公共入口不加载 Win32 依赖且非 Windows 创建参数正确
  • 验证不支持窗口能力抛出带能力名称的异常
  • 静态检查 common 与 Windows 平台实现的导出一致性并覆盖 OCR 导入
tests/AGENTS.md
tests/platform/__init__.py
tests/platform/test_entries.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@HarcoChen
HarcoChen force-pushed the refactor/Win32-Cleanup branch from 2d482e3 to 1087b38 Compare August 26, 2026 16:11
@HarcoChen
HarcoChen force-pushed the refactor/Win32-Cleanup branch from 1087b38 to fe9bd17 Compare August 27, 2026 16:32
@HarcoChen
HarcoChen marked this pull request as ready for review August 28, 2026 01:36

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嘿——我发现了 1 个安全问题,以及另外 4 个问题

安全问题

  • 检测到子进程函数 run 未使用静态字符串。如果此数据可由恶意行为者控制,则可能构成命令注入。请审查此调用的使用方式,确保它不受外部资源控制。可以考虑使用 shlex.escape()。(链接
面向 AI Agent 的提示
请处理本次代码审查中的评论:

## 单独评论

### 评论 1
<location path="main.py" line_range="182" />
<code_context>
-                )
-                from app.MaaFW import ArknightWin32Toolkit
+                if IS_WINDOWS:
+                    for adapter in (
+                        "app.MaaFW.ArknightWin32",
+                        "app.MaaFW.EndFieldPCWin32",
+                    ):
+                        await asyncio.to_thread(importlib.import_module, adapter)

-                await ArknightWin32Toolkit.init()
</code_context>
<issue_to_address>
**issue (broader_impact):** Windows 启动时会导入 `app.MaaFW.EndFieldPCWin32`,尽管该 Win32 适配器已在重构中移除,因此 `initialize_background_services()` 会在后端完成启动前引发 `ModuleNotFoundError`**触发条件:** Windows 上进行后台服务初始化时。

**建议修复:** 从适配器列表中移除 `app.MaaFW.EndFieldPCWin32`,或者在所有启动导入都移除之前保留该模块。

```suggestion

```
</issue_to_address>

### 评论 2
<location path="app/services/platform/common/system.py" line_range="265-270" />
<code_context>
+        """
+
+        logger.info(f"开始中止进程 PID: {pid}")
+        args = ["taskkill", "/F"]
+        if kill_tree:
+            args.append("/T")
+        args.extend(["/PID", str(pid)])
+        result = await ProcessRunner.run_process(
+            *args,
+        )
+        if result.returncode != 0:
</code_context>
<issue_to_address>
**issue (bug_risk):** `kill_process_by_pid` 始终调用仅限 Windows 使用的 `taskkill` 可执行文件,因此在 Linux 上进行进程清理时会引发 `FileNotFoundError`,而不是终止指定的进程。

**触发条件:** 在非 Windows 平台上调用 `System.kill_process_by_pid``System.kill_process` 时。

**建议修复:** 实现平台特定的进程终止逻辑,例如在非 Windows 平台上使用 `psutil.Process(pid).kill()`。
</issue_to_address>

### 评论 3
<location path="app/utils/platform/common/process.py" line_range="77" />
<code_context>
+        with suppress(psutil.NoSuchProcess, psutil.AccessDenied):
+            if proc.info.get("name") == process_name:
+                for hwnd in get_window_handles(proc.pid):
+                    if window_service.is_visible(hwnd):
+                        return True
+    return False
</code_context>
<issue_to_address>
**issue (bug_risk):** 非 Windows 实现仅在赋值 `window` 时捕获 `UnsupportedPlatformError`,但 `window_service.is_visible(hwnd)` 在该保护范围之外调用,并会引发 `UnsupportedPlatformError`;因此,在不受支持的平台上,`is_process_running` 会崩溃,而不是返回 `False`**触发条件:** 在非 Windows 平台上找到匹配的进程时。

**建议修复:** 在窗口可见性调用周围捕获 `UnsupportedPlatformError`,或者在窗口功能不受支持时立即返回 `False````suggestion
                    try:
                        if window_service.is_visible(hwnd):
                            return True
                    except UnsupportedPlatformError:
                        return False
```
</issue_to_address>

### 评论 4
<location path="app/utils/platform/windows/window.py" line_range="146-156" />
<code_context>
-            except Exception:
-                # 某些系统策略下 SetForegroundWindow 可能被拒绝, 尝试焦点切换降级路径
-                try:
-                    win32gui.SetWindowPos(
-                        hwnd,
                        win32con.HWND_TOPMOST,
                        0,
</code_context>
<issue_to_address>
**issue (bug_risk):** `activate_window` 的回退逻辑在两次 `SetWindowPos` 调用中都将 `hwnd` 作为 `hWndInsertAfter` 参数传入,而不是分别使用 `HWND_TOPMOST``HWND_NOTOPMOST`,因此无法执行预期的置顶切换;当 `SetForegroundWindow` 被拒绝时,激活操作仍可能失败。

**触发条件:** Windows 拒绝初始的 `SetForegroundWindow` 调用并执行回退路径时。

**建议修复:** 第一次调用传入 `win32con.HWND_TOPMOST`,第二次调用传入 `win32con.HWND_NOTOPMOST`。
</issue_to_address>

### 评论 5
<location path="app/services/platform/windows/power.py" line_range="19" />
<code_context>
        subprocess.run(commands[action])
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** 检测到子进程函数 `run` 未使用静态字符串。如果此数据可由恶意行为者控制,则可能构成命令注入。请审查此调用的使用方式,确保它不受外部资源控制。可以考虑使用 `shlex.escape()`*来源:opengrep*
</issue_to_address>

Sourcery 评估

需要人工审查。 首先需要处理 5 个发现的问题;此外,该重构还改变了启动任务、电源命令、进程终止以及基于 DPAPI 的密钥存储的平台分派。错误的分派可能创建或遗留自动启动任务、终止模拟器进程,或关闭/重启机器,而这些影响无法通过还原代码完全撤销。

阻塞性发现:main.py:182app/services/platform/common/system.py:270app/utils/platform/common/process.py:77app/utils/platform/windows/window.py:156app/services/platform/windows/power.py:19


Sourcery 对开源项目免费——如果您喜欢我们的审查结果,请考虑分享 ✨
请帮助我变得更有用!请在每条评论上点击 👍 或 👎,我会利用反馈来改进审查结果。
Original comment in English

Hey - I've found 1 security issue, and 4 other issues

Security issues:

  • Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="main.py" line_range="182" />
<code_context>
-                )
-                from app.MaaFW import ArknightWin32Toolkit
+                if IS_WINDOWS:
+                    for adapter in (
+                        "app.MaaFW.ArknightWin32",
+                        "app.MaaFW.EndFieldPCWin32",
+                    ):
+                        await asyncio.to_thread(importlib.import_module, adapter)

-                await ArknightWin32Toolkit.init()
</code_context>
<issue_to_address>
**issue (broader_impact):** Windows startup imports `app.MaaFW.EndFieldPCWin32` even though this Win32 adapter is removed by the refactor, so `initialize_background_services()` raises `ModuleNotFoundError` before the backend finishes starting.

**Triggers:** On Windows during background-service initialization.

**Suggested fix:** Remove `app.MaaFW.EndFieldPCWin32` from the adapter list, or retain the module until all startup imports are removed.

```suggestion

```
</issue_to_address>

### Comment 2
<location path="app/services/platform/common/system.py" line_range="265-270" />
<code_context>
+        """
+
+        logger.info(f"开始中止进程 PID: {pid}")
+        args = ["taskkill", "/F"]
+        if kill_tree:
+            args.append("/T")
+        args.extend(["/PID", str(pid)])
+        result = await ProcessRunner.run_process(
+            *args,
+        )
+        if result.returncode != 0:
</code_context>
<issue_to_address>
**issue (bug_risk):** `kill_process_by_pid` always invokes the Windows-only `taskkill` executable, so process cleanup on Linux raises `FileNotFoundError` instead of terminating the requested process.

**Triggers:** When `System.kill_process_by_pid` or `System.kill_process` is called on a non-Windows platform.

**Suggested fix:** Implement platform-specific process termination, for example with `psutil.Process(pid).kill()` on non-Windows platforms.
</issue_to_address>

### Comment 3
<location path="app/utils/platform/common/process.py" line_range="77" />
<code_context>
+        with suppress(psutil.NoSuchProcess, psutil.AccessDenied):
+            if proc.info.get("name") == process_name:
+                for hwnd in get_window_handles(proc.pid):
+                    if window_service.is_visible(hwnd):
+                        return True
+    return False
</code_context>
<issue_to_address>
**issue (bug_risk):** The non-Windows implementation catches `UnsupportedPlatformError` only while assigning `window`, but `window_service.is_visible(hwnd)` is called outside that protection and raises `UnsupportedPlatformError`; therefore `is_process_running` crashes instead of returning `False` on unsupported platforms.

**Triggers:** When a matching process is found on a non-Windows platform.

**Suggested fix:** Catch `UnsupportedPlatformError` around the window-visibility call or return `False` immediately when the window capability is unsupported.

```suggestion
                    try:
                        if window_service.is_visible(hwnd):
                            return True
                    except UnsupportedPlatformError:
                        return False
```
</issue_to_address>

### Comment 4
<location path="app/utils/platform/windows/window.py" line_range="146-156" />
<code_context>
-            except Exception:
-                # 某些系统策略下 SetForegroundWindow 可能被拒绝, 尝试焦点切换降级路径
-                try:
-                    win32gui.SetWindowPos(
-                        hwnd,
-                        win32con.HWND_TOPMOST,
-                        0,
</code_context>
<issue_to_address>
**issue (bug_risk):** The `activate_window` fallback passes `hwnd` as the `hWndInsertAfter` argument to both `SetWindowPos` calls instead of `HWND_TOPMOST` and `HWND_NOTOPMOST`, so the intended topmost toggle is not performed and activation can still fail when `SetForegroundWindow` is rejected.

**Triggers:** When Windows refuses the initial `SetForegroundWindow` call and the fallback path runs.

**Suggested fix:** Pass `win32con.HWND_TOPMOST` for the first call and `win32con.HWND_NOTOPMOST` for the second call.
</issue_to_address>

### Comment 5
<location path="app/services/platform/windows/power.py" line_range="19" />
<code_context>
        subprocess.run(commands[action])
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

*Source: opengrep*
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 5 findings to address first, and the refactor changes platform dispatch for startup tasks, power commands, process termination, and DPAPI-backed secret storage. A wrong dispatch can create or leave behind an auto-start task, terminate emulator processes, or shut down/reboot the machine, and those effects are not fully undone by reverting the code.

Blocking findings: main.py:182, app/services/platform/common/system.py:270, app/utils/platform/common/process.py:77, app/utils/platform/windows/window.py:156, app/services/platform/windows/power.py:19


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread main.py
Comment thread app/services/platform/common/system.py Outdated
Comment thread app/utils/platform/common/process.py Outdated
Comment thread app/utils/platform/windows/window.py
Comment thread app/services/platform/windows/power.py
- pywin32/keyboard 改为 sys_platform == "win32" 条件依赖,代码已不
  硬依赖 Win32,非 Windows 环境安装依赖不应再失败
- 修复 res/version.json 中 AUTO-MAS-Project#433 引入的缺失逗号;该文件被
  frontend/vite.config.ts require,非法 JSON 会导致前端构建失败
- 补充平台层相关更新日志条目
- tests/AGENTS.md 补充 tests/platform/ 目录归属说明
@HarcoChen
HarcoChen force-pushed the refactor/Win32-Cleanup branch from 50ab8b3 to cac5e67 Compare August 28, 2026 02:05
@HarcoChen

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review

@sourcery-ai

sourcery-ai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Sorry @HarcoChen, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 2 days and 11 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@1w1w11w1
1w1w11w1 requested a review from ClozyA August 28, 2026 04:36
@1w1w11w1

Copy link
Copy Markdown
Contributor

审查结论

本次改动方向合理,但目前仍有两项会阻断“Linux 等非 Windows 平台启动后端”目标的问题,建议合并前处理。

需要修复

  1. [P1] Linux 无图形环境仍可能无法启动后端

    main.py 在平台判断前导入 TaskManager,其导入链最终加载 app/task/MaaEnd/tools/login.py 顶层的 pyautogui。无图形会话的 Linux 缺少 DISPLAY 时会在导入阶段失败。建议仅在 Windows 或实际执行账号切换时导入桌面登录依赖,并增加 Ubuntu 无图形环境的启动导入测试。

  2. [P1] core/service 循环依赖只是延迟,未真正解除

    当前仍存在动态依赖环:

    app.core.config -> app.services.System
                    -> app.services.platform.common.system
                    -> app.core.Config
    

    函数内导入避免了当前初始化顺序下的直接异常,但平台层仍反向依赖 core。建议只将电源、自启动、进程等 OS 能力放入平台模块,将 Config.serverpower_sign、WebSocket 和定时任务编排保留在 app/services/system.py 或 core,使依赖保持 core -> services -> platform

  3. [P2] 非 Windows 电源能力与 PR 描述不一致

    app/services/platform/common/power.pysupported_actions 为空,所有操作均抛出 UnsupportedPlatformError。若本 PR 只保证跨平台启动,应收窄 PR 描述;若要提供 Linux 电源能力,则需要补充相应实现。

  4. [P2] 无效的循环导入规避代码

    app/services/platform/common/system.py 的模块级 __getattr__ 不会在模块内部解析裸变量 Config 时触发;当前实际依赖方法内的局部导入。该代码没有消除循环且容易误导维护者,建议删除。

  5. [P2] PR 描述与实现、版本记录不一致

    • PR 称移除终末地 PC Win32 适配实现,但 main.py 仍主动导入 EndFieldPCWin32,对应文件也未删除。
    • res/version.json 为本 PR 新增了两条记录,仓库规则要求一条 PR 只贡献一条 changelog,应合并为一条。
  6. [P2] 排水任务静默吞掉未知异常

    app/utils/platform/common/process.py_drain() 捕获未知 Exception 后直接忽略。若排水任务提前退出,子进程管道可能重新产生背压且没有诊断信息。建议删除宽泛捕获,或至少记录异常与降级原因。

循环导入验证

静态依赖图显示,PR 与 dev 均存在相同的两个既有强连通分量:

  • core.config <-> core.emulator_manager
  • models <-> models.config

PR 没有新增静态可见的强连通分量。另以 app.coreapp.servicesTaskManager、平台 SystemUpdater 等作为首个入口执行了 6 种独立进程导入顺序,均未复现 partially initialized modulecannot import name;风险主要来自上述被局部导入隐藏的动态反向依赖。

验证结果

  • python -m pytest tests/platform/test_entries.py -q3 passed, 4 skipped
  • python -m pytest tests --collect-only -q:成功收集 108

当前 Windows 环境会跳过真正的非 Windows 导入路径,因此以上结果不能覆盖 Linux 无 DISPLAY 的启动场景。

@1w1w11w1

Copy link
Copy Markdown
Contributor

循环依赖重构建议

当前通过函数内导入和模块级 __getattr__ 延迟触发依赖,只是避免了现有初始化顺序下的直接报错,没有消除 core <-> services 的双向依赖。建议将目标依赖方向固定为:

core -> services.system -> services/platform + utils/platform
                         ↓
                    不允许反向导入 core

建议方案

  1. 平台层只保留 OS 能力

    app/services/platform/**app/utils/platform/** 只负责电源、自启动、进程、窗口和安全存储等操作,不应导入 app.core,也不应承载 WebSocket、全局配置或任务调度逻辑。

  2. _SystemHandler 编排移回 app/services/system.py

    当前 app/services/platform/common/system.py 混合了平台能力和应用编排,不属于 common 平台实现。建议删除该层级,将兼容入口恢复为真正的应用服务,由它调用平台能力对象。

  3. System 通过参数接收状态,不再读取 Config

    例如将:

    await System.start_power_task()

    调整为:

    await System.start_power_task(Config.power_sign)
    Config.power_sign = "NoAction"

    System 只负责执行和管理电源任务,配置读取与状态复位由 core 调用方负责。

  4. KillSelf 移出平台电源能力

    KillSelf 是应用生命周期操作,不是操作系统电源能力。发送关闭 WebSocket、设置 Config.server.should_exit 应由 core 或 API 编排;平台电源接口只处理 shutdown、reboot、sleep 等真实系统操作。

  5. 逐步停止使用包级业务聚合导入

    将:

    from app.core import Config
    from app.services import System

    逐步替换为:

    from app.core.config import Config
    from app.services.system import System

    最终让 app/core/__init__.pyapp/services/__init__.py 不再主动加载业务模块,避免导入结果依赖包初始化顺序。

  6. 增加依赖方向回归测试

    可使用一个轻量 AST 测试,禁止平台模块导入 app.core 或上层业务 service,无需引入新的依赖分析库。

推荐落地顺序

拆回 System 编排
-> 参数化 power_sign
-> 移走 KillSelf
-> 删除平台层对 core 的导入
-> 删除无效 __getattr__
-> 后续 PR 清理包级聚合导入和既有 SCC

前五步即可从根本上消除本 PR 涉及的动态循环依赖;core.config <-> core.emulator_managermodels <-> models.config 两个既有强连通分量可另开 PR 分阶段处理,避免扩大本次改动范围。

@HarcoChen

Copy link
Copy Markdown
Contributor Author

后续计划:

阶段三:参数化 power_sign

System 不再自己读 Config,改由调用方传入,消除 services → core 的剩余延迟导入。
波及:core / api / update 一批调用方。

阶段四:KillSelf 移出电源能力

关闭 WebSocket、设置 should_exit 是应用生命周期,不是操作系统电源能力。平台电源接口只保留 shutdown / reboot / sleep。
波及:api/core.py、update.py。

阶段五:包级聚合导入改为具体模块

from app.core import Config 改为 from app.core.config import Config,让 init.py 不再主动加载业务模块,导入结果不再取决于包初始化顺序。

记录:两个更大的环

models ↔ utils双向有顶层导入(models→utils 4 条,utils→models 6 条)现在不炸只是因为初始化顺序恰好成立。

app.utils.emulator.{general,ldplayer,mumu} → app.models.{config,emulator} [顶层]
app.models.{ConfigBase,config} → app.utils{,.constants,.io} [顶层]

task ↔ core 有 32 条反向边,但只需断一条。 task → core 有 32 条顶层导入,方向极其一致;反向只有 core/task_manager.py 一条。真要治,断那一条就够,不必动 32 条。task ↔ models 同理:93 对 2。

@HarcoChen HarcoChen linked an issue Aug 28, 2026 that may be closed by this pull request
@qiyinxi

qiyinxi commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

对本 PR 做了一轮较深入的审查(多角度查找 + 每条结论独立复核,含对 CPython 行为的实测验证)。整体重构思路和平台分层是好的,迁移过程也很克制,但目前跨平台启动这个核心目标尚未真正达成,另有若干需要处理的问题。按严重程度排列:

阻断核心目标的问题

  1. 无显示环境的 Linux 上后端仍然无法启动app/task/MaaEnd/tools/login.py:39 只对 win32 三件套加了 IS_WINDOWS 门控,顶层的 import pyautogui(及 :37import cv2)原样保留,而该模块在启动必经链上:main.py lifespan → app/core/task_manager.py:57app/task/__init__.py:25 → MaaEnd/manager.py:38 → tools/init.py:22 → login.py。无 DISPLAY 时 pyautogui 导入即抛 KeyError: 'DISPLAY'。requirements.txt 也只给 pywin32/keyboard 加了 sys_platform == "win32" 标记,pyautogui/pynput/mss 均未加。新增测试只覆盖了 OCRtool 的导入,没覆盖这条链。

  2. Linux 电源操作被删除而非移植,且失败完全静默 — 旧 app/services/system.py 里可用的 Linux 分支(shutdown -h nowsystemctl hibernate/suspendloginctl terminate-user)被整体删除,替换为 supported_actions = frozenset()CommonPowerControllerapp/services/platform/common/power.py:5)。非 Windows 上所有真实电源模式都会在 system.py:121-122 抛 RuntimeError,而这个异常发生在 system.py:152 一个无 done-callback、也不被 await 的 asyncio.create_task 里:前端已收到倒计时提示、power_sign 已被重置,最终既不关机也无日志。建议把删掉的分支移植成 ~15 行的 LinuxPowerController

  3. Linux 上加密配置读写全面受损app/utils/platform/common/secret.py:4-9 对任何非空值都抛 UnsupportedPlatformError,而调用点在通用配置层:ConfigBase.setValue:731 使得 Linux 上保存任何凭据(SMTP 授权码、MirrorChyanCDK、Info/Password)都返回 code=500;更严重的是从 Windows 迁移来的非空密文在加载时会被 EncryptValidator(bare except → False)+ load 的 normalize-commit(ConfigBase.py:956-971静默清空并写回磁盘——无日志的数据丢失。

其他正确性问题

  1. kill_process 遇首个 AccessDenied 即中止app/utils/platform/common/process_platform.py:36:进程树中任一成员权限不足就提前 return,连排在末位的主进程都不再尝试,也跳过 wait_procs 校验;与 Windows 端 taskkill /F /T 的语义不一致。建议收集失败继续遍历、最后汇总。

  2. (旧 bug,但在本 PR 触碰的函数内)IsWindowVisible 传入的是 PIDapp/utils/emulator/general.py:150main_pid 传给需要 HWND 的 win32gui.IsWindowVisiblemain_hwnd 属性就是为此存在的):隐藏窗口时立即假成功、窗口留在原地;显示窗口时每 0.5s 反复按老板键直至超时报错。ldplayer.py:339top_hwnd 的写法是正确的。

  3. (旧 bug,但本 PR 改过该函数)DPI 缩放检测结果被丢弃OCRtool.__init__:74 写的是 self.zoom,而 classmethod get_screenshot_region:183-188 读的是 cls.zoom(恒为默认 1.5);所有实际调用路径都是 classmethod,平台层新做的 get_dpi_scaling() 算完即弃,非 150% 缩放的机器上截图裁剪一直是错的。

  4. OCR 查窗违反自身契约OCRtool.py:263 在非 Windows 上 platform_window.find_window_by_title 直接抛 UnsupportedPlatformError,违背 docstring 的 int | None 约定与 WindowsNotFoundException 错误流,也与同一 PR 在 process.py:84/101 做的 []/None 降级自相矛盾(影响有限,但建议统一)。

  5. MaaFW 适配器注册时机后移(潜在隐患) — 自定义 action/recognition 的装饰器注册从"服务前必然执行"移入后台初始化任务(main.py:179-184);当前尚无可达的触发路径,但共享 try 块前段(MCP 挂载、get_stage 网络请求)一旦抛异常,整个会话适配器都不会注册,而服务器照常对外服务、任务接口不检查 background_status

设计与一致性

  1. IS_WINDOWS 静默守卫架空了能力层system.pyset_SelfStart:81/set_Sleep:73/is_startup:193 提前判平台后静默返回,使 Common 桩的"显式报错"成为不可达死代码(grep 确认为唯一调用方),与本 PR 写入 changelog 的「不支持的能力显式报错而非静默降级」直接矛盾;Linux 用户切"开机自启"界面显示成功、实际无事发生,未来接入 Linux 实现也会被这些守卫拦住。建议统一用 set_power 那种 supported_actions 能力查询(唯一真正落实了显式报错设计的地方)。

  2. "平台不支持"缺乏统一契约 — 四种处理方式并存:底层抛 UnsupportedPlatformError、进程包装层吞成 []/None、System 层静默 no-op 或抛普通 RuntimeError、业务代码手写守卫(general.py:135、ldplayer.py:324、login.py:386)或直接用 win32gui(mumu.py:580+)。连查找类操作之间都不一致;粗算移植 macOS 需改 ≥12 处现有文件。建议在能力层定义一种契约并只在 API 边界转换一次。

  3. 新增的非 Windows 测试没有任何环境会执行 — 仓库没有任何 workflow 跑 pytest,Windows 开发机上四个 skipif(IS_WINDOWS) 测试全部跳过——防回归断言只是"看起来有覆盖"。补一个最小的 Linux CI job 即可(它会立刻暴露第 1 条)。

  4. decode_bytes 每次调用都执行 locale.getpreferredencoding()process_runner.py:21:旧 ENCODINGS 在导入时算一次;已核对 CPython 3.12 Lib/locale.py:667-690,POSIX 分支每次调用做一轮进程级 setlocale 往返(文档标注线程不安全),而该函数位于日志逐行解码与 ADB 轮询热路径。建议提回模块级或 functools.cache

  5. 窗口激活逻辑三份近似副本windows/window.pyactivate_window:125force_activate_window:233 骨架相同(附着方向等差异是有意行为,可参数化保留),login.py:116 还有第三份手写副本未迁移。

  6. 新模块带入的死代码platform/common/system.py:37 的 Config 模块级 __getattr__(唯一导入方只取 System;且 PEP 562 的模块 __getattr__ 对模块内函数的裸名字查找不生效——实测确认);:197-209 注释掉的 get_window_info(引用了本文件未导入的 win32gui);process.py:73 永不可达的 suppress(UnsupportedPlatformError)。建议全部删除。

规范

  1. changelog 违反「一条 PR 只贡献一条记录」res/version.json 在程序优化下新增了两条条目,AGENTS.md 写作约束要求合并为一条。另注:append-version-contributor workflow 解析的是基线分支的 version.json,而基线恰好缺本 PR 补上的那个逗号,合并时归属 job 预计会失败、条目将无人署名。

附注(基线相关): 本 PR 的基线快照里,task_manager.py/update.py 仅通过模块级 __getattr__ 提供 System/Config56885edd / #428 引入)——模块 __getattr__ 对模块自身函数内的裸名字查找不生效(Python 3.12 实测),当时 task_manager.py:534 等引用是运行时 NameError。该问题已由 8717cf01 / #436(LazyProxy)在 dev 上修复,因此:① 建议本 PR rebase 到包含 #436 的最新 dev;② rebase 之后上面第 2 条的静默失败路径将立即可触发(基线上它曾被该 NameError 抢先掩盖);③ 本 PR 新增的 platform/common/system.py 里那个同款模块级 __getattr__(见第 14 条)在 #436 之后同样是应删除的死代码。

本审查由 AI(Claude)辅助完成;以上每条均经独立验证(代码交叉核对,关键语义在 CPython 3.12 上实测)。

qiyinxi added a commit that referenced this pull request Aug 29, 2026
关联 #295(本 PR 将 dev_v2 已合入的 #296 重构移植到 `dev`,是否随本 PR 关闭该 issue 请维护者定夺)。

## 摘要

1. **后端**新增 `app/core/ws/`(MainConnection 唯一主连接、Dispatcher 按 id+type
分发、Publisher 统一出口、强类型 WSEnvelope/WS*Data);`@ws_command`
反射执行器换为显式参数注册表;出站 Koishi 客户端改用协议层 ping/pong,命令执行器由 main.py 显式注入;新增
`/api/core/ws_meta` 与任务运行时、电源倒计时、更新下载三个 HTTP 初始快照端点。
2. **前端**重写连接层(状态机、单一重连计时器、有限退避、id+type 分发、请求响应关联)与订阅注册表;新增
`useAppLifecycle` 生命周期协调器(常驻订阅、正常关闭 30s taskkill 兜底、异常自动重启后端上限 3
次、后端驱动电源倒计时、休眠恢复检查);Electron 主进程改为协调退出(`app-close-requested` → renderer
closeApp → 25s 兜底 scoped 强杀),**删除 `taskkill /f /im python.exe` 全量误杀**。
3. **删除**应用层心跳、Broadcast(全仓零订阅者)、消息缓存/重放/历史、反射式
ws_command、`app/api/ws_debug.py` 全部调试路由与 WSdev
页面、连接原因白名单、`schedulerHandlers` localStorage 重放。
4. **dev 独有面补齐迁移**(#296 未覆盖):HSR/OkNte/Okww 专项共 21 处调用、GameSign 结果广播(新增
`id=GameSign` / `gamesign.result.updated` 路由,并顺带修复该调用在 dev_v2
上已静默失效的问题)、托盘 TrayAction/全局停止快捷键与协调退出汇合、首页卫星状态改由任务运行时常驻订阅驱动。
5. 已包含 dev_v2 上 #296 的全部后续修复(30s 关闭超时、模拟器/明日方舟错误通知展示、断线后任务停止契约、/stop
完成事件窗口)。

## 范围调整(相对 dev_v2 版本)

- **不移植**:插件系统/插件市场全部通道(dev 无该子系统)、MaaFW 前端适配、uv/pyproject 工具链。
- **弹窗 Dialogs 通道未移植**:dev 已移除人工排查模式(其唯一生产方),且 dev 上旧 `Message/Question`
通道全仓无发送方;对应 `WebSocketMessageListener` 一并删除。
- **开发模式判定改用 `AUTO_MAS_ENV`**:dev 分支前端拉起的后端始终携带
`AUTO_MAS_DEV=1`(跳过自行提权的宿主标记),不能沿用 dev_v2 的判定,否则生产环境后端会被误判为开发模式导致关闭流程失效。
- 行为说明:Koishi 远程 `core.close` 现执行完整 teardown 并经主 WS 发
`backend.shutdown.ready`(原为关闭 socket + KillSelf);如需远程关闭前端仍可走电源 KillSelf
路径(`frontend.close.requested`)。
- `yarn openapi` 再生成同时收敛了此前后端 schema 变更后未再生的存量差异(森空岛内置签到、人工排查移除、OkNte
`IfUseMasConfig`、MAA 游戏更新配置等字段),生成目录未手改。

## 验证

- 后端 pytest 135 passed(2 项失败为既存基线:telemetry 测试断言旧 `_start_sentry` 签名、SRC
测试依赖的系统临时目录残留,均与本 PR 无关);合并门槛 `pytest --collect-only` 退出码 0。
- 前端 vitest 120/120;`yarn typecheck` 0 错误;vite build 与 electron
主进程构建通过;改动文件 ESLint 全绿。
- 真实主 WS 冒烟:单连接替换旧连接、非法信封丢弃不掉线、`/close` 后依次收到 `power.sign.updated` 与
`backend.shutdown.ready`、开发模式后端保留复用、三个快照端点返回正确。
- 全仓搜索
`send_websocket_message`/`Config.websocket`/`Broadcast`/`sendRaw`/`ExternalWSHandlers`/`scheduler-pending-tabs`
等旧机制 0 残留。
- 待人工复核(需图形界面实机):Electron 正常/异常退出全路径(✕、托盘退出/重启、25s
兜底、失败保留前端)、休眠恢复、任务全流程与音频、断线自动重启弹窗。

## 协作提示

- 与在途 PR #423(main.py Win32 依赖)与 #417(通知中间层)存在相邻改动,合并顺序靠后的一方需 rebase。
- WS 调试 REST 路由删除会同步影响 MCP 工具面(自动生成),属预期的对外契约变化。
- 协议速查文档新增于 `res/docs/WebSocket管理器快速上手.md`。

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@HarcoChen

Copy link
Copy Markdown
Contributor Author

对本 PR 做了一轮较深入的审查(多角度查找 + 每条结论独立复核,含对 CPython 行为的实测验证)。整体重构思路和平台分层是好的,迁移过程也很克制,但目前跨平台启动这个核心目标尚未真正达成,另有若干需要处理的问题。按严重程度排列:

P0-1,Headless Linux前端怎么启动,而且和架构改动关系不大
P0-2\3,都是后续补充的内容
P1-4,权限不足?管理员启动诶。
P1-5、6,本PR不是来修BUG的,修BUG污染PR
P1-7 有问题,但是ocr.py已经弃用了

P2-10 确实是问题,后面提PR改
P2-11 测试文件后面全删了
P2其他问题稍后修复

…-phase1

# Conflicts:
#	app/core/__init__.py
#	app/services/system.py
#	app/services/update.py
#	res/version.json
@HarcoChen
HarcoChen merged commit 1e6bc81 into AUTO-MAS-Project:dev Aug 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux平台是否有可能

4 participants