Skip to content

fix(ws): WebSocket 加固清单里的两处输入容错与事件构造 - #837

Open
beichen24a1 wants to merge 12 commits into
AUTO-MAS-Project:devfrom
beichen24a1:fix/ws-hardening-misc
Open

beichen24a1 wants to merge 12 commits into
AUTO-MAS-Project:devfrom
beichen24a1:fix/ws-hardening-misc

Conversation

@beichen24a1

@beichen24a1 beichen24a1 commented Sep 16, 2026

Copy link
Copy Markdown

Closes #443(清单中落点最明确的两条)

#443 是一份 17 条的加固清单,这里挑出边界最清晰、风险最低的两条,其余留给维护者按优先级排。

  • app/api/ws_command.pyparams 不是对象(列表、字符串)时 **(params or {}) 直接抛 TypeError,只捕 ValidationError 的写法把它漏到外层变成 500——而 dev 基线对外是 400。改为同时捕 TypeError,仍按 参数错误 回 400。
  • app/services/update.py:三处 UPDATE_FAILED 用裸 dict 构造事件,绕过 WSUpdateFailedData 模型。字段名本来是对的,但模型是这类事件的唯一契约,统一走它更稳。

没动的部分(都在 #443 里,按优先级留给维护者)

  • 后端:stop-ALL 等待期间新任务挂死且锁传染 teardown;10 秒启动窗口内的连接顶替让启动队列静默丢失;倒计时归零后前端挂死导致电源操作静默放弃;二进制帧断开主连接;Publisher.send 对僵死对端可阻塞 20–40s —— 都涉及调度语义或发送策略。
  • Electron/前端生命周期 与托盘「退出」不武装 25s 兜底;relaunchAfterQuit 置位后永不复位;断线状态下点关闭空等 30–35 秒;5 个用户编辑页配置会话订阅无卸载释放 —— 都涉及生命周期设计,改动面比本次大一个量级。

验证

  • python -m py_compile app/services/update.py app/api/ws_command.py:通过
  • python -m pytest tests/services -q:66 passed
  • python -m pytest tests --collect-only -q:1412 collected,exit 0
  • ruff check / ruff format --check:通过

Sourcery 总结

增强 WebSocket 处理、应用启动、配置流程、部署更新和模拟器诊断功能,使其能够抵御格式错误的输入、生命周期竞态和部分失败。

错误修复:

  • 将 WebSocket 命令参数类型错误作为面向客户端的 400 参数错误返回,而不是产生未捕获的 500 响应。
  • 防止无效的第三方 BetterGI 清单和缺失的脚本配置文件导致原本有效的流程失败。
  • 改进模拟器命令失败处理,提供可操作的返回码、stdout 和 stderr 详细信息。
  • 在窗口或渲染器销毁后,阻止 Electron 渲染器事件继续传递。
  • 确保可选的启动集成和维护任务不会阻止主调度器启动。
  • 通过原子方式暂存替换内容,并在失败时保留之前的版本,使仓库部署更加安全。
  • 在下载依赖项期间显示进度,避免进度指示器停滞不动。

增强功能:

  • 构造更新失败事件时,统一使用 WebSocket 更新失败数据模型。

部署:

  • 加强仓库部署替换机制,确保中断的更新不会留下不完整的应用目录。

测试:

  • 增加对依赖项下载进度、仓库原子替换、格式错误的 BetterGI 清单以及详细进程失败消息的测试覆盖。
Original summary in English

Summary by Sourcery

Harden WebSocket handling, application startup, configuration workflows, deployment updates, and emulator diagnostics against malformed input, lifecycle races, and partial failures.

Bug Fixes:

  • Return WebSocket command parameter type errors as client-facing 400 parameter errors instead of uncaught 500 responses.
  • Prevent invalid third-party BetterGI manifests and missing script configuration files from failing otherwise valid workflows.
  • Improve emulator command failures with actionable return-code, stdout, and stderr details.
  • Guard Electron renderer event delivery after a window or renderer has been destroyed.
  • Ensure optional startup integrations and maintenance tasks cannot prevent the main scheduler from starting.
  • Make repository deployments safer by staging replacements atomically and preserving the previous version on failure.
  • Show progress during dependency downloads instead of leaving the progress indicator stalled.

Enhancements:

  • Use the WebSocket update-failure data model consistently when constructing update failure events.

Deployment:

  • Harden repository deployment replacement so interrupted updates do not leave incomplete application directories.

Tests:

  • Add coverage for dependency download progress, atomic repository replacement, malformed BetterGI manifests, and detailed process failure messages.
Original summary in English
Original summary in English

同一套「按 ConfigPathMode 在两个目录间拷贝」的逻辑在通用脚本里写了 4 份,只有
ScriptConfig.set_general 做了存在性判断,另外三处源路径不存在时直接抛异常:

- AutoProxy.set_general(下发):新建用户后没跑过「脚本设置」,data/<脚本>/<用户>/
  ConfigFile 还不存在,自动代理一启动就崩、任务判成异常(Sentry AUTO-MAS-BACKEND-4Q)。
  ScriptConfig.set_general 在同样情况下会跳过拷贝、让脚本用自己的配置 —— 两条路径
  对同一件事行为不一致,有判断的那份是预期行为。
- AutoProxy.update_config(回写):脚本配置路径不存在时抛异常;File 分支还缺目标目录,
  copy 不像 copytree 会自建目录,会再抛一次。
- ScriptConfig.final_task(回写):先 rmtree 掉 MAS 侧副本再拷,源不存在时异常发生在
  清空之后,用户会以为配置丢了(4Y / 4W)。

三处统一成「先判源存在,不存在就跳过并给一条可读 warning」,与 ScriptConfig.set_general
同口径。

验证:
- 三处源不存在路径的前后走查:修复前分别是 swap_in_dir 的 RuntimeError 与 shutil 的
  FileNotFoundError,修复后都是 warning + 跳过
- python -m pytest tests/task -q:804 passed / 17 failed,与改前基线逐条一致(既有失败)
- python -m pytest tests --collect-only -q:1406 collected,exit 0
- ruff check / ruff format --check:通过
copyToRoot() 对每个条目先 rmSync 掉 appRoot 下的目标、再逐文件 copyFileSync。删完之后、
复制完成之前的任何中断(进程被杀、断电、单个文件撞杀软或占用)都会留下残缺的 app/,
缺哪些模块取决于复制停在哪一步:落在 lifespan 里的让后端完全起不来,落在
initialize_background_services 里的静默失效(Sentry AUTO-MAS-BACKEND-56 / 51)。

改为 replaceItem():先整体复制到 <目标>.new,再改名换入,成功后删掉旧副本;换入失败把
旧目标改名回去 —— 宁可停在旧版本,也不让目标处于缺失或半新半旧的状态。

顺带把 .git 挪到复制列表最后:它是「当前部署的是哪个版本」的依据,先复制会让中断后的
残缺源码看起来已经是最新版,标题栏不再提示更新,用户也就失去重新拉取代码的入口。

验证:
- 新增 4 个 vitest 用例(真实临时目录):整目录替换后无 .new/.old 残留、目标不存在时
  自建(顺带暴露并修掉 copyFileSync 不自建父目录的边界)、清掉上次中断残留、源不存在
  时抛错且目标原样保留
- yarn vitest run electron/services/repositoryService.test.ts:4 passed
- yarn tsc -p tsconfig.electron.json --noEmit:通过
- yarn lint:0 errors
list_js_scripts() 逐个读取 {RootPath}/User/JsScript/*/manifest.json 取显示名,任意一个
解析失败都会让函数抛出、接口 500(Sentry AUTO-MAS-BACKEND-9B 的现场是一个尾逗号),
一条龙表单的「JS 脚本」候选列表整体拿不到 —— 而那个坏文件只属于某一个脚本。

manifest 是玩家从社区订阅或手工放置的第三方文件,格式未必严格。解析失败改为记一条
warning、显示名退回目录名、继续列下一个;函数本来就有 display = folder 的兜底,只是
没兜住异常。

验证:
- 新增 4 个纯逻辑用例:坏 manifest 只影响自己(用线上那条事件的尾逗号形态)、manifest
  缺 name、目录没有 manifest、JS 脚本目录不存在
- python -m pytest tests/task/test_bettergi_js_script_manifest.py -q:4 passed
- python -m pytest tests --collect-only -q:exit 0
- ruff check:通过
initializationHandlers.ts 里 12 处 event.sender.send(...) 都没有守卫。这些回调活得比
一次 IPC 调用长:窗口关闭、渲染进程被 kill 之后,Runtime 子进程还在往 stdout 吐进度
(Sentry AUTO-MAS-DESKTOP-16),后端子进程随后仍会退出并触发状态回调
(AUTO-MAS-DESKTOP-11),拿着已销毁的 WebContents 去 send 就是主进程的未捕获异常
—— 抛在 Node 的 exit 回调里,没有任何 try/catch 兜住,level 直接是 fatal。

统一走新的 sendToRenderer():sender 已销毁就跳过。main.ts 里一直有这道守卫(12 处),
这个文件是漏的。

验证:
- yarn tsc -p tsconfig.electron.json --noEmit:通过
- yarn lint:0 errors
- 12 处调用点全部改到统一出口,文件内不再有裸 event.sender.send
雷电的 dnconsole.exe 崩溃时返回码是 3221225480,而 stdout / stderr 都是空的,原来
只回 stdout 的写法让界面上只剩「命令执行失败: 」加一个空串,用户分不清是路径配置错、
实例不存在,还是模拟器自身挂了。

给 ProcessResult 加 failure_detail()(returncode + repr 过的 stdout/stderr),ldplayer
与 mumu 里 8 处 raise RuntimeError(f"命令执行失败: ...") 统一改用它。

验证:
- 新增 2 个纯逻辑用例:线上那条事件的形态(返回码非零、两个输出都为空)与多行输出
- python -m pytest tests/tools/test_process_result_failure_detail.py -q:2 passed
- ruff check / ruff format --check:通过
dependencyService 只在「Installing collected packages:」与「Successfully installed」两个
节点推进进度:下载阶段解析出的 Collecting 只累加 totalPackages,从不推进
installedPackages。而下载恰恰是耗时最长的一段 —— 非大陆用户实测初始化 7 分 36 秒里有
7 分 27 秒停在 40%,体感是彻底卡死(issue AUTO-MAS-Project#499)。

pip 在这个阶段并不需要知道总包数,所以给出 estimateDownloadProgress():按已见到的包数
渐近推进(40% + 30% × (1 - e^(-n/25))),越接近 70% 越慢、封顶 70%,把 70-80 留给「开始
安装」那一刻的跳动。

验证:
- 新增 4 个 vitest 用例:起点 40%、单调不减、封顶 70% 且能到 69% 以上、见到 20 个包时
  已经过半
- yarn vitest run electron/services/dependencyService.test.ts:4 passed
initialize_background_services() 把 MCP 挂载、历史清理、适配器导入、显示输出守卫、通知
管理器、Koishi 全放在一个 try 里,前面任何一步抛异常就整体结束,排在其后的
MainTimer.start() 一起没起 —— 界面一切正常、手动运行队列也正常,定时任务却整夜不触发;
/api/core/health 虽然回了 backgroundStatus: failed 与 backgroundError,但前端不消费这
两个字段,用户看不到任何提示(issue AUTO-MAS-Project#738)。

改成 _optional_step() 逐个包住:失败只记一条 exception 日志、继续下一步。主定时器仍排在
显示输出守卫之后(原有顺序要求不变),它自己的失败依旧算整体失败。各步骤之间的先后顺序
没有调整。

验证:
- 真机启动(把 MainTimer.start() 临时换成一行日志,避免触发用户的计划任务)并在「后台
  清理」里注入 RuntimeError:日志依序出现「MCP 服务已挂载」→「后台清理失败, 已跳过:
  模拟可选步骤失败」(带完整 traceback)→「主业务定时器启动」→「后端后台初始化完成」,
  即可选步骤失败后流程照常走到定时器;修复前同样的注入会让这一步永远不执行
- python -m py_compile main.py:通过
- python -m pytest tests --collect-only -q:1412 collected,exit 0
- ruff format --check main.py:通过(ruff check 报的 7 处 import 排序 / E402 是既有问题,
  不在本次改动范围)
来自 AUTO-MAS-Project#443 清单中落点最明确的两条:

- app/api/ws_command.py:`params` 不是对象时 `**(params or {})` 直接抛 TypeError,只捕
  ValidationError 的写法把它漏到外层变成 500(dev 基线是 400)。改为同时捕 TypeError。
- app/services/update.py:三处 UPDATE_FAILED 用裸 dict 构造事件,绕过 WSUpdateFailedData
  模型。字段名本来是对的,但模型是这类事件的唯一契约,统一走它更稳。

清单里其余条目(stop-ALL 等待期间新任务挂死、启动窗口内的连接顶替、倒计时归零后电源
操作静默放弃、「✕ 与托盘退出不武装兜底」、5 个用户编辑页缺 onUnmounted、二进制帧断开
主连接等)要么涉及调度语义、要么涉及生命周期设计,没有一并动。

验证:
- python -m py_compile:通过
- python -m pytest tests/services -q:66 passed
- python -m pytest tests --collect-only -q:1412 collected,exit 0
- ruff check / ruff format --check:通过

@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.

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

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

@sourcery-ai

sourcery-ai Bot commented Sep 16, 2026

Copy link
Copy Markdown

审查者指南

本 PR 集中修复多类边界容错与可靠性问题:隔离后台初始化失败、增强部署替换和 IPC 生命周期安全、改善依赖与模拟器错误反馈,并补强脚本清单、用户配置、WebSocket 参数及更新事件的数据契约;同时新增针对关键行为的 Python/TypeScript 测试和变更日志。

受保护的渲染进程事件传递时序图

sequenceDiagram
    participant Renderer as Renderer
    participant Main as ElectronMain
    participant Runtime as RuntimeOrBackend
    Runtime->>Main: progress callback
    Main->>Main: WebContents.isDestroyed()
    alt renderer still exists
        Main->>Renderer: WebContents.send(channel, payload)
    else renderer destroyed
        Main-->>Runtime: omit event
    end
Loading

容错 WebSocket 命令验证时序图

sequenceDiagram
    participant Client as WebSocketClient
    participant API as execute_ws_command
    participant Model as params_model
    Client->>API: execute_ws_command(endpoint, params)
    API->>Model: params_model(**(params or {}))
    alt valid object parameters
        Model-->>API: param_instance
        API-->>Client: command result
    else TypeError or ValidationError
        Model-->>API: validation exception
        API-->>Client: success=false, 参数错误, HTTP 400
    end
Loading

故障隔离后台初始化流程图

flowchart TD
    Start[后台初始化] --> MCP[_mount_mcp]
    MCP --> Cleanup[_cleanup]
    Cleanup --> Arknight[_init_arknight]
    Arknight --> Guard[DesktopGuard.start]
    Guard --> Timer[MainTimer.start]
    Timer --> Claw[_start_openclaw]
    Claw --> Koishi[_init_koishi]
    Koishi --> Done[初始化完成]
    MCP -.失败.-> Continue1[记录日志并继续]
    Cleanup -.失败.-> Continue2[记录日志并继续]
    Arknight -.失败.-> Continue3[记录日志并继续]
    Guard -.失败.-> Continue4[记录日志并继续]
    Claw -.失败.-> Continue5[记录日志并继续]
    Koishi -.失败.-> Continue6[记录日志并继续]
    Continue1 --> Cleanup
    Continue2 --> Arknight
    Continue3 --> Guard
    Continue4 --> Timer
    Continue5 --> Koishi
    Continue6 --> Done
Loading

原子部署替换流程图

flowchart TD
    Start[replaceItem] --> Cleanup[清理目标.new和目标.old]
    Cleanup --> Copy[复制源到目标.new]
    Copy --> RenameOld{目标存在}
    RenameOld -->|是| Backup[目标改名为目标.old]
    RenameOld -->|否| Swap[目标.new改名为目标]
    Backup --> Swap
    Swap --> Remove[清理目标.old]
    Remove --> Done[替换完成]
    Copy -.失败.-> Preserve[保留旧目标并清理目标.new]
    Swap -.失败.-> Restore[恢复目标.old]
    Restore --> Preserve
Loading

文件级变更

变更 详情 文件
将多个后台初始化步骤隔离为可选步骤,单步失败仅记录日志并继续启动主定时器。
  • 为 MCP、清理、适配器、桌面守卫、通知管理器和 Koishi 初始化增加统一异常隔离
  • 保留取消异常传播,避免吞掉任务取消信号
main.py
增强部署替换的中断安全性,避免更新过程中留下残缺程序。
  • 先复制到 .new 暂存路径,再通过重命名切换目标并在失败时恢复 .old
  • 清理上次中断残留,并将 .git 延后复制
  • 为目录替换、首次部署、残留清理和源不存在场景增加测试
frontend/electron/services/repositoryService.ts
frontend/electron/services/repositoryService.test.ts
改善依赖安装期间的进度反馈,使下载阶段从 40% 平滑推进至 70%。
  • 根据已发现的包数使用渐近公式估算下载进度
  • 增加起点、单调性和上限测试
frontend/electron/services/dependencyService.ts
frontend/electron/services/dependencyService.test.ts
防止渲染进程销毁后异步初始化回调发送 IPC 导致主进程异常。
  • 集中封装 WebContents 销毁检查
  • 替换初始化、安装、后端状态及更新进度回调中的直接 send
frontend/electron/ipc/initializationHandlers.ts
提高用户配置同步对缺失路径和目标目录的容错能力。
  • 回写前检查脚本配置源是否存在,避免先删除 MAS 副本
  • 文件模式复制前创建目标目录
  • 下发配置前检查用户配置目录或文件,不存在时跳过并告警
app/task/general/AutoProxy.py
app/task/general/ScriptConfig.py
增强第三方 BetterGI 脚本清单解析的容错性。
  • 单个 manifest 解析失败时记录告警并回退到目录名
  • 增加损坏、缺少名称、缺失目录和无 manifest 场景测试
app/task/BetterGI/tools/one_dragon.py
tests/task/test_bettergi_js_script_manifest.py
补充模拟器命令失败信息,统一展示返回码、标准输出和错误输出。
  • 新增 ProcessResult.failure_detail() 格式化完整失败上下文
  • 更新 LDPlayer 与 MuMu 的失败异常使用该信息
  • 覆盖空输出和多行输出测试
app/utils/platform/common/process_runner.py
app/utils/emulator/ldplayer.py
app/utils/emulator/mumu.py
tests/tools/test_process_result_failure_detail.py
修复 WebSocket 命令参数类型错误的错误映射,并统一更新失败事件的数据模型。
  • 同时捕获 TypeError 与 ValidationError,将非对象 params 按参数错误返回 400
  • 三处 UPDATE_FAILED 事件改用 WSUpdateFailedData 构造
app/api/ws_command.py
app/services/update.py
补充各项用户可见修复的变更日志条目。
  • 记录后台初始化、脚本清单、依赖进度、部署替换、模拟器错误、配置复制、IPC 防护和 WebSocket 参数修复
changelog.d/fix-background-init-independent.fix.md
changelog.d/fix-bettergi-js-manifest-robust.fix.md
changelog.d/fix-dependency-progress-advance.fix.md
changelog.d/fix-deploy-atomic-replace.fix.md
changelog.d/fix-emulator-failure-detail.fix.md
changelog.d/fix-general-config-copy-exists.fix.md
changelog.d/fix-ipc-send-destroyed-guard.fix.md
changelog.d/fix-ws-hardening-misc.fix.md

可能相关的问题


提示和命令

与 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 集中修复多类边界容错与可靠性问题:隔离后台初始化失败、增强部署替换和 IPC 生命周期安全、改善依赖与模拟器错误反馈,并补强脚本清单、用户配置、WebSocket 参数及更新事件的数据契约;同时新增针对关键行为的 Python/TypeScript 测试和变更日志。

Sequence diagram for guarded renderer event delivery

sequenceDiagram
    participant Renderer as Renderer
    participant Main as ElectronMain
    participant Runtime as RuntimeOrBackend
    Runtime->>Main: progress callback
    Main->>Main: WebContents.isDestroyed()
    alt renderer still exists
        Main->>Renderer: WebContents.send(channel, payload)
    else renderer destroyed
        Main-->>Runtime: omit event
    end
Loading

Sequence diagram for tolerant WebSocket command validation

sequenceDiagram
    participant Client as WebSocketClient
    participant API as execute_ws_command
    participant Model as params_model
    Client->>API: execute_ws_command(endpoint, params)
    API->>Model: params_model(**(params or {}))
    alt valid object parameters
        Model-->>API: param_instance
        API-->>Client: command result
    else TypeError or ValidationError
        Model-->>API: validation exception
        API-->>Client: success=false, 参数错误, HTTP 400
    end
Loading

Flow diagram for fault-isolated backend initialization

flowchart TD
    Start[后台初始化] --> MCP[_mount_mcp]
    MCP --> Cleanup[_cleanup]
    Cleanup --> Arknight[_init_arknight]
    Arknight --> Guard[DesktopGuard.start]
    Guard --> Timer[MainTimer.start]
    Timer --> Claw[_start_openclaw]
    Claw --> Koishi[_init_koishi]
    Koishi --> Done[初始化完成]
    MCP -.失败.-> Continue1[记录日志并继续]
    Cleanup -.失败.-> Continue2[记录日志并继续]
    Arknight -.失败.-> Continue3[记录日志并继续]
    Guard -.失败.-> Continue4[记录日志并继续]
    Claw -.失败.-> Continue5[记录日志并继续]
    Koishi -.失败.-> Continue6[记录日志并继续]
    Continue1 --> Cleanup
    Continue2 --> Arknight
    Continue3 --> Guard
    Continue4 --> Timer
    Continue5 --> Koishi
    Continue6 --> Done
Loading

Flow diagram for atomic deployment replacement

flowchart TD
    Start[replaceItem] --> Cleanup[清理目标.new和目标.old]
    Cleanup --> Copy[复制源到目标.new]
    Copy --> RenameOld{目标存在}
    RenameOld -->|是| Backup[目标改名为目标.old]
    RenameOld -->|否| Swap[目标.new改名为目标]
    Backup --> Swap
    Swap --> Remove[清理目标.old]
    Remove --> Done[替换完成]
    Copy -.失败.-> Preserve[保留旧目标并清理目标.new]
    Swap -.失败.-> Restore[恢复目标.old]
    Restore --> Preserve
Loading

File-Level Changes

Change Details Files
将多个后台初始化步骤隔离为可选步骤,单步失败仅记录日志并继续启动主定时器。
  • 为 MCP、清理、适配器、桌面守卫、通知管理器和 Koishi 初始化增加统一异常隔离
  • 保留取消异常传播,避免吞掉任务取消信号
main.py
增强部署替换的中断安全性,避免更新过程中留下残缺程序。
  • 先复制到 .new 暂存路径,再通过重命名切换目标并在失败时恢复 .old
  • 清理上次中断残留,并将 .git 延后复制
  • 为目录替换、首次部署、残留清理和源不存在场景增加测试
frontend/electron/services/repositoryService.ts
frontend/electron/services/repositoryService.test.ts
改善依赖安装期间的进度反馈,使下载阶段从 40% 平滑推进至 70%。
  • 根据已发现的包数使用渐近公式估算下载进度
  • 增加起点、单调性和上限测试
frontend/electron/services/dependencyService.ts
frontend/electron/services/dependencyService.test.ts
防止渲染进程销毁后异步初始化回调发送 IPC 导致主进程异常。
  • 集中封装 WebContents 销毁检查
  • 替换初始化、安装、后端状态及更新进度回调中的直接 send
frontend/electron/ipc/initializationHandlers.ts
提高用户配置同步对缺失路径和目标目录的容错能力。
  • 回写前检查脚本配置源是否存在,避免先删除 MAS 副本
  • 文件模式复制前创建目标目录
  • 下发配置前检查用户配置目录或文件,不存在时跳过并告警
app/task/general/AutoProxy.py
app/task/general/ScriptConfig.py
增强第三方 BetterGI 脚本清单解析的容错性。
  • 单个 manifest 解析失败时记录告警并回退到目录名
  • 增加损坏、缺少名称、缺失目录和无 manifest 场景测试
app/task/BetterGI/tools/one_dragon.py
tests/task/test_bettergi_js_script_manifest.py
补充模拟器命令失败信息,统一展示返回码、标准输出和错误输出。
  • 新增 ProcessResult.failure_detail() 格式化完整失败上下文
  • 更新 LDPlayer 与 MuMu 的失败异常使用该信息
  • 覆盖空输出和多行输出测试
app/utils/platform/common/process_runner.py
app/utils/emulator/ldplayer.py
app/utils/emulator/mumu.py
tests/tools/test_process_result_failure_detail.py
修复 WebSocket 命令参数类型错误的错误映射,并统一更新失败事件的数据模型。
  • 同时捕获 TypeError 与 ValidationError,将非对象 params 按参数错误返回 400
  • 三处 UPDATE_FAILED 事件改用 WSUpdateFailedData 构造
app/api/ws_command.py
app/services/update.py
补充各项用户可见修复的变更日志条目。
  • 记录后台初始化、脚本清单、依赖进度、部署替换、模拟器错误、配置复制、IPC 防护和 WebSocket 参数修复
changelog.d/fix-background-init-independent.fix.md
changelog.d/fix-bettergi-js-manifest-robust.fix.md
changelog.d/fix-dependency-progress-advance.fix.md
changelog.d/fix-deploy-atomic-replace.fix.md
changelog.d/fix-emulator-failure-detail.fix.md
changelog.d/fix-general-config-copy-exists.fix.md
changelog.d/fix-ipc-send-destroyed-guard.fix.md
changelog.d/fix-ws-hardening-misc.fix.md

Possibly linked issues


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

@qiyinxi qiyinxi added the skip-changelog 本 PR 没有用户可见改动,不需要 changelog.d 碎片 label Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changelog 本 PR 没有用户可见改动,不需要 changelog.d 碎片

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants