Skip to content

fix(src): 强制清理 SRC 残留的 Python 后端进程 - #397

Open
Craun718 wants to merge 4 commits into
AUTO-MAS-Project:devfrom
Craun718:feat/kill-src-new
Open

fix(src): 强制清理 SRC 残留的 Python 后端进程#397
Craun718 wants to merge 4 commits into
AUTO-MAS-Project:devfrom
Craun718:feat/kill-src-new

Conversation

@Craun718

@Craun718 Craun718 commented Aug 21, 2026

Copy link
Copy Markdown
Member
  • 在 SRC 启动前及任务结束、取消、异常中断时,强制终止 src.exe 进程树和安装目录内残留的 Python 后端进程。
  • 持久化 SRC 安装标识与 WebUI 端口,通过安装路径和监听端口复查残留,同时避免误杀其他 Python 进程。
  • 清理失败时将任务标记为异常并保留配置快照,仅在确认进程退出后恢复配置,避免残留后端继续读写配置。
  • 补充进程树清理、端口兜底、取消收尾及清理失败场景的回归测试。

Closes #337

Sourcery 摘要

确保 SRC 任务在启动、结束、取消和异常恢复时可靠清理所有关联进程,并安全维护配置状态。

错误修复:

  • 强制清理 SRC 主进程树、安装目录内的 Python 后端进程及 WebUI 端口残留,降低任务结束后遗留进程继续运行的风险。
  • 改进进程路径、安装实例和端口校验,避免误终止其他 Python 或同名进程。
  • 在启动、取消、异常和上次中断恢复流程中处理残留进程,并在清理失败时将任务标记为异常。

功能增强:

  • 增加 SRC 配置快照、进程状态持久化及可恢复的配置事务,确保仅在确认进程退出后恢复配置。
  • 为脚本任务增加跨调度器的原子资源占用,避免共享或嵌套 SRC 安装目录并发运行。
  • 完善取消收尾、异常通知和最终操作的超时与失败处理。

测试:

  • 补充任务占用、取消收尾、进程扫描与终止、端口兜底及 SRC 清理失败场景的回归测试。
Original summary in English

Sourcery 总结

在每个任务生命周期中,可靠地清理 SRC 进程,并保留安全、可恢复的配置状态。

错误修复:

  • 确保在启动前,以及正常完成、取消、失败和恢复期间,可靠地终止与 SRC 相关的进程。
  • 通过验证可执行文件路径、安装标识、配置范围和 WebUI 端口所有权,防止终止无关进程。
  • 当无法确认 SRC 清理已完成时,将任务标记为异常,并保留可恢复状态。

增强功能:

  • 添加持久化的 SRC 进程和配置快照状态,并实现事务性恢复:仅在进程清理成功后恢复配置。
  • 为共享或重叠 SRC 安装目录的脚本添加跨调度器的原子预留机制。
  • 提高取消最终处理的可靠性,并为清理、通知、模拟器关闭和错误报告操作设置超时限制。

测试:

  • 添加针对任务预留、取消最终处理、进程树终止、路径安全的进程扫描、WebUI 端口回退机制以及清理失败的回归测试。
Original summary in English

Summary by Sourcery

Reliably clean up SRC processes and preserve safe, recoverable configuration state across every task lifecycle.

Bug Fixes:

  • Ensure SRC-related processes are reliably terminated before startup and during normal completion, cancellation, failures, and recovery.
  • Prevent unrelated processes from being terminated by validating executable paths, installation identity, configuration scope, and WebUI port ownership.
  • Mark tasks as abnormal and preserve recoverable state when SRC cleanup cannot be confirmed.

Enhancements:

  • Add durable SRC process and configuration snapshot state with transactional recovery that restores configuration only after process cleanup succeeds.
  • Add atomic cross-scheduler reservations for scripts sharing or overlapping SRC installation directories.
  • Make cancellation finalization reliable and bound cleanup, notification, emulator shutdown, and error reporting operations with timeouts.

Tests:

  • Add regression coverage for task reservations, cancellation finalization, process-tree termination, path-safe process scanning, WebUI port fallback, and cleanup failures.
Original summary in English

Sourcery 摘要

确保 SRC 任务在启动、结束、取消和异常恢复时可靠清理所有关联进程,并安全维护配置状态。

错误修复:

  • 强制清理 SRC 主进程树、安装目录内的 Python 后端进程及 WebUI 端口残留,降低任务结束后遗留进程继续运行的风险。
  • 改进进程路径、安装实例和端口校验,避免误终止其他 Python 或同名进程。
  • 在启动、取消、异常和上次中断恢复流程中处理残留进程,并在清理失败时将任务标记为异常。

功能增强:

  • 增加 SRC 配置快照、进程状态持久化及可恢复的配置事务,确保仅在确认进程退出后恢复配置。
  • 为脚本任务增加跨调度器的原子资源占用,避免共享或嵌套 SRC 安装目录并发运行。
  • 完善取消收尾、异常通知和最终操作的超时与失败处理。

测试:

  • 补充任务占用、取消收尾、进程扫描与终止、端口兜底及 SRC 清理失败场景的回归测试。
Original summary in English

Sourcery 总结

在每个任务生命周期中,可靠地清理 SRC 进程,并保留安全、可恢复的配置状态。

错误修复:

  • 确保在启动前,以及正常完成、取消、失败和恢复期间,可靠地终止与 SRC 相关的进程。
  • 通过验证可执行文件路径、安装标识、配置范围和 WebUI 端口所有权,防止终止无关进程。
  • 当无法确认 SRC 清理已完成时,将任务标记为异常,并保留可恢复状态。

增强功能:

  • 添加持久化的 SRC 进程和配置快照状态,并实现事务性恢复:仅在进程清理成功后恢复配置。
  • 为共享或重叠 SRC 安装目录的脚本添加跨调度器的原子预留机制。
  • 提高取消最终处理的可靠性,并为清理、通知、模拟器关闭和错误报告操作设置超时限制。

测试:

  • 添加针对任务预留、取消最终处理、进程树终止、路径安全的进程扫描、WebUI 端口回退机制以及清理失败的回归测试。
Original summary in English

Summary by Sourcery

Reliably clean up SRC processes and preserve safe, recoverable configuration state across every task lifecycle.

Bug Fixes:

  • Ensure SRC-related processes are reliably terminated before startup and during normal completion, cancellation, failures, and recovery.
  • Prevent unrelated processes from being terminated by validating executable paths, installation identity, configuration scope, and WebUI port ownership.
  • Mark tasks as abnormal and preserve recoverable state when SRC cleanup cannot be confirmed.

Enhancements:

  • Add durable SRC process and configuration snapshot state with transactional recovery that restores configuration only after process cleanup succeeds.
  • Add atomic cross-scheduler reservations for scripts sharing or overlapping SRC installation directories.
  • Make cancellation finalization reliable and bound cleanup, notification, emulator shutdown, and error reporting operations with timeouts.

Tests:

  • Add regression coverage for task reservations, cancellation finalization, process-tree termination, path-safe process scanning, WebUI port fallback, and cleanup failures.

@sourcery-ai

sourcery-ai Bot commented Aug 21, 2026

Copy link
Copy Markdown

审阅者指南

重构 SRC 任务管理,以原子方式锁定脚本和 SRC 安装根目录,同时新增健壮的 SRC Python 后端进程清理、持久化的安装/WebUI 状态跟踪,以及可靠的 SRC 配置快照处理,包括对取消安全的任务收尾和大量回归测试。

SRC 任务执行与 Python 后端清理的时序图

sequenceDiagram
    participant TaskManager
    participant Task
    participant SrcManager
    participant AutoProxyTask
    participant kill_src_processes
    participant System

    TaskManager->>Task: add_task(...)
    Task->>SrcManager: spawn(SrcManager)
    SrcManager->>SrcManager: _recover_previous_run()
    SrcManager->>kill_src_processes: kill_src_processes(ProcessManager, src_exe_path, src_root_path, src_set_path, webui_port, expected_installation_id)
    kill_src_processes->>System: kill_process(src_exe_path)
    kill_src_processes->>System: kill_process_by_pid(pid)
    kill_src_processes->>kill_src_webui_process: kill_src_webui_process(src_root_path, src_set_path, webui_port, listener_wait_timeout)
    kill_src_processes-->>SrcManager: bool (cleanup_success)
    SrcManager->>AutoProxyTask: spawn(AutoProxyTask)
    AutoProxyTask->>kill_src_processes: kill_src_processes(...)
    AutoProxyTask-->>SrcManager: process_cleanup_success
    SrcManager->>SrcManager: final_task()
    SrcManager->>SrcManager: _restore_src_config_from_temp()
    SrcManager->>SrcManager: _retire_src_config_snapshot()
    SrcManager->>System: kill_process(src_exe_path)
Loading

文件级变更

Change Details Files
为每个脚本和每个 SRC 根目录引入预留机制,防止并发调度器运行相互冲突的 SRC 任务。
  • 添加 _ScriptTaskReservations,提供按脚本 UUID 和规范化 SRC 根路径键控的原子、带所有者意识的预留,并对嵌套根目录做重叠检查。
  • 将 Task 和 _TaskManager 接入预留机制,在任务创建、执行、取消和清理时获取/释放预留,确保在出错时也能释放预留。
  • 更新 SrcManager 构造函数以接收已经预留的 SRC 根路径,以及在恢复期间扩展预留到历史根目录的回调。
app/core/task_manager.py
tests/core/test_task_manager_locking.py
让 TaskExecuteBase 感知取消状态,从而在不丢失取消状态的前提下等待 finalizer。
  • 添加 wait_for_finalizer_on_cancel 标志和 _run_final_task 辅助方法,在 final_task 完成前延后传播 CancelledError。
  • 确保外层根任务始终通过 done_callback 和 _execute_task 的 finally 代码块设置 accomplish Event。
  • 增加回归测试,覆盖默认行为、在主逻辑执行期间取消以及在收尾阶段取消这三种场景。
app/models/task.py
tests/models/test_task_execute_base.py
加固 System 的进程终止能力,引入基于 PID 的控制、路径扫描及不确定性报告。
  • 引入 _ProcessPathScan,在按可执行文件路径扫描进程时区分已确认和不确定的 PID。
  • 重构 kill_process,使用 _scan_processes_by_path 和 kill_process_by_pid,聚合成功/失败结果并暴露首个错误。
  • 实现 kill_process_by_pid,处理 taskkill 结果、基于 psutil 的存活性检查以及结构化日志;添加针对访问被拒绝、进程已退出以及同名进程存在歧义等情况的测试。
app/services/system.py
tests/services/test_system_process.py
添加 SRC 专用的进程和配置工具,用于安全识别安装、管理快照以及清理 Python 后端进程。
  • 实现配置工具,用于从 src.exe 计算安装 ID、校验安装、管理 Temp/Temp.ready 快照状态、事务性保存用户配置以及对 SRC 配置更新进行分阶段/提升处理。
  • 实现进程工具,用于校验清理路径、以 WebUI 端口和配置所有者持久化/读取 SrcProcessState、从 deploy.yaml 读取 WebUI 端口、在遵守 SRC 根目录边界的前提下终止 SRC 主进程、辅助进程和 WebUI 进程,并扫描 SRC 根目录下的可执行文件。
  • 通过 app.task.SRC.tools.all 导出新的辅助函数,供各类 SRC 任务复用。
app/task/SRC/tools/config.py
app/task/SRC/tools/process.py
app/task/SRC/tools/__init__.py
tests/task/test_src_process_cleanup.py
重构 SRC AutoProxy 和 ScriptConfig 任务,在取消情况下也能强制进行 SRC 进程清理、维护持久状态并安全地执行配置事务。
  • AutoProxyTask 现会跟踪 src_installation_id、src_webui_port 和 process_cleanup_success,在取消时等待 final_task,在启动 src.exe 之前写入 SrcProcessState,并在每一次 SRC 启动或停止(正常、错误和最终路径)时都使用 kill_src_processes。
  • ScriptConfigTask 同样会跟踪安装 ID、进程状态和配置会话生命周期,通过 stage_src_config_update/promote_src_config_update 分阶段提交配置变更,以事务方式保存用户配置,并在清理失败时抛出异常,同时在模拟器关闭和通知发送周围添加超时。
  • 两个任务都使用 recover_src_user_config/save_src_user_config 来恢复或持久化用户特定配置,并通过有限时的 wait_for 超时和对崩溃安全的日志记录来强化 websocket/通知的上报。
app/task/SRC/AutoProxy.py
app/task/SRC/ScriptConfig.py
app/task/SRC/ManualReview.py
重构 SrcManager,使其在多次运行间协调整个 SRC 的恢复、清理和快照还原,同时保持脚本配置正确加锁。
  • 在启动时,recover_previous_run 会清理未提交的快照,读取进程/快照状态,预留历史 SRC 根目录,回滚中断的交换操作,终止残留的 SRC 进程(包括历史根目录中的),并根据快照可用性和路径变更情况来恢复或隔离快照。
  • 在 prepare 阶段,cleanup_current_src_root 会重新校验当前安装,通过 backup_src_config_to_temp 将实时配置备份到 Temp,并确保按用户的默认配置事务得到恢复。
  • final_task 会根据 process_cleanup_success 决定恢复或保留快照,通过 Config.ScriptConfig.UserData 写回 user_config,安全解锁配置,并在带超时和错误上报的前提下发送汇总通知。
app/task/SRC/manager.py
tests/task/test_src_process_cleanup.py

可能关联的问题


技巧和指令

与 Sourcery 交互

  • 触发新的审查: 在 pull request 上评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在审查评论下回复,要求 Sourcery 从该评论创建一个 issue。你也可以直接回复审查评论 @sourcery-ai issue 来从中创建一个 issue。
  • 生成 pull request 标题: 在 pull request 标题中的任意位置写上 @sourcery-ai 即可随时生成标题。你也可以在 pull request 上评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文的任意位置写上 @sourcery-ai summary,即可在指定位置生成 PR 摘要。你也可以在 pull request 上评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审阅者指南: 在 pull request 上评论 @sourcery-ai guide,即可随时(重新)生成审阅者指南。
  • 解决所有 Sourcery 评论: 在 pull request 上评论 @sourcery-ai resolve,即可标记所有 Sourcery 评论为已解决。适用于你已经处理完所有评论且不希望继续看到它们的情况。
  • 关闭所有 Sourcery 审查: 在 pull request 上评论 @sourcery-ai dismiss,即可关闭所有现有的 Sourcery 审查。特别适合希望以一次全新的审查开始——别忘了再评论 @sourcery-ai review 来触发新的审查!

自定义你的体验

访问你的 控制面板 以:

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

获取帮助

Original review guide in English

Reviewer's Guide

Refactors SRC task management to atomically lock scripts and SRC installation roots while adding robust SRC Python backend process cleanup, persistent installation/WebUI state tracking, and resilient SRC config snapshot handling, including new cancellation-safe task finalization and extensive regression tests.

Sequence diagram for SRC task execution and Python backend cleanup

sequenceDiagram
    participant TaskManager
    participant Task
    participant SrcManager
    participant AutoProxyTask
    participant kill_src_processes
    participant System

    TaskManager->>Task: add_task(...)
    Task->>SrcManager: spawn(SrcManager)
    SrcManager->>SrcManager: _recover_previous_run()
    SrcManager->>kill_src_processes: kill_src_processes(ProcessManager, src_exe_path, src_root_path, src_set_path, webui_port, expected_installation_id)
    kill_src_processes->>System: kill_process(src_exe_path)
    kill_src_processes->>System: kill_process_by_pid(pid)
    kill_src_processes->>kill_src_webui_process: kill_src_webui_process(src_root_path, src_set_path, webui_port, listener_wait_timeout)
    kill_src_processes-->>SrcManager: bool (cleanup_success)
    SrcManager->>AutoProxyTask: spawn(AutoProxyTask)
    AutoProxyTask->>kill_src_processes: kill_src_processes(...)
    AutoProxyTask-->>SrcManager: process_cleanup_success
    SrcManager->>SrcManager: final_task()
    SrcManager->>SrcManager: _restore_src_config_from_temp()
    SrcManager->>SrcManager: _retire_src_config_snapshot()
    SrcManager->>System: kill_process(src_exe_path)
Loading

File-Level Changes

Change Details Files
Introduce per-script and per-SRC-root reservations to prevent concurrent schedulers from running conflicting SRC tasks.
  • Add _ScriptTaskReservations to provide atomic, owner-aware reservations keyed by script UUID and normalized SRC root paths, with overlap checks for nested roots.
  • Wire Task and _TaskManager to acquire/release reservations on task creation, execution, cancellation, and cleanup, ensuring reservations are freed on errors.
  • Update SrcManager construction to receive a reserved SRC root path and a callback for extending reservations to historical roots used during recovery.
app/core/task_manager.py
tests/core/test_task_manager_locking.py
Make TaskExecuteBase cancellation-aware so finalizers can be awaited without losing cancellation state.
  • Add wait_for_finalizer_on_cancel flag and _run_final_task helper to defer CancelledError propagation until final_task completes.
  • Ensure accomplish Event is always set by the outer root task via a done_callback and within _execute_task finally blocks.
  • Add regression tests that cover default behavior, cancel-during-main, and cancel-during-final scenarios.
app/models/task.py
tests/models/test_task_execute_base.py
Harden System process termination with PID-level control, path scanning, and uncertainty reporting.
  • Introduce _ProcessPathScan to distinguish between confirmed and uncertain PIDs when scanning processes by executable path.
  • Refactor kill_process to use _scan_processes_by_path and kill_process_by_pid, aggregating success/failure and surfacing first error.
  • Implement kill_process_by_pid with taskkill result handling, psutil-based existence checks, and structured logging; add tests for access denied, already-exited processes, and ambiguous same-name processes.
app/services/system.py
tests/services/test_system_process.py
Add SRC-specific process and configuration utilities to safely identify installations, manage snapshots, and clean Python backend processes.
  • Implement config utilities to compute installation IDs from src.exe, validate installations, manage Temp/Temp.ready snapshot state, transactional user config saves, and staging/promotion of SRC config updates.
  • Implement process utilities to validate cleanup paths, persist/read SrcProcessState with WebUI ports and config owners, read WebUI ports from deploy.yaml, kill SRC main, helper, and WebUI processes while respecting SRC root boundaries, and scan SRC root executables.
  • Export new helpers via app.task.SRC.tools.all for reuse across SRC tasks.
app/task/SRC/tools/config.py
app/task/SRC/tools/process.py
app/task/SRC/tools/__init__.py
tests/task/test_src_process_cleanup.py
Refactor SRC AutoProxy and ScriptConfig tasks to enforce SRC process cleanup, persistent state, and safe config transactions under cancellation.
  • AutoProxyTask now tracks src_installation_id, src_webui_port, and process_cleanup_success, waits for final_task on cancel, writes SrcProcessState before launching src.exe, and uses kill_src_processes everywhere SRC is started or stopped (normal, error, and final paths).
  • ScriptConfigTask similarly tracks installation ID, process state, and config session lifecycle, stages config changes via stage_src_config_update/promote_src_config_update, saves user configs transactionally, and raises when cleanup fails, with timeouts around emulator closure and notifications.
  • Both tasks use recover_src_user_config/save_src_user_config to restore or persist user-specific configs, and harden websocket/notification reporting with bounded wait_for timeouts and crash-safe logging.
app/task/SRC/AutoProxy.py
app/task/SRC/ScriptConfig.py
app/task/SRC/ManualReview.py
Rework SrcManager to orchestrate SRC recovery, cleanup, and snapshot restoration across runs while keeping script configs locked correctly.
  • On startup, recover_previous_run clears uncommitted snapshots, reads process/snapshot state, reserves historical SRC roots, rolls back interrupted swaps, kills residual SRC processes (including historical roots), and restores or quarantines snapshots depending on availability and path changes.
  • During prepare, cleanup_current_src_root revalidates current installation, backs up live config to Temp via backup_src_config_to_temp, and ensures per-user default config transactions are recovered.
  • final_task now restores or preserves snapshots based on process_cleanup_success, writes back user_config via Config.ScriptConfig.UserData, unlocks configs safely, and sends summary notifications with timeouts and error reporting.
app/task/SRC/manager.py
tests/task/test_src_process_cleanup.py

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

@Craun718
Craun718 marked this pull request as ready for review August 21, 2026 06:05

@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 @Craun718, your pull request is larger than the review limit of 150000 diff characters

@1w1w11w1 1w1w11w1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

审查结论

建议暂缓合并。当前仍有两个可复现的 P1 行为问题:

  1. 日志误判仍未修复,Closes #337 目前不成立

    • app/task/SRC/AutoProxy.py:559 仍对拼接后的日志做裸字符串匹配:"Request human takeover" in log
    • app/utils/LogMonitor.py:157-169 在识别到本轮起始时间后,会把后续没有时间戳的 traceback 续行全部追加到当前日志。因此 traceback 中展示的源码行 logger.critical('Request human takeover') 仍会被当成真实人工接管。
    • 我在 PR 提交 6df81dc9 上用最小输入复现,check_log() 将该日志标记为 SRC 无法继续执行任务, 需要用户接管。请改为只匹配结构化的真实日志记录,或明确排除 traceback/源码行,并补一条回归测试。
  2. 残留进程清理范围过宽,可能误杀非 SRC 后端进程

    • app/task/SRC/tools/process.py:263-312 会枚举 SRC 根目录下所有 .exe,随后只要运行中进程的 exe 路径位于该根目录内就调用 taskkill /T
    • 这会把 updater、ADB 或用户在安装目录内启动的其他工具及其子进程一并终止;最小 mock 验证中,<SRC_ROOT>/updater.exe 会被直接选中清理。
    • 这与 PR 正文“避免误杀其他 Python 进程”的目标矛盾。建议限定为 toolkit 后端特征、已记录的进程树/启动 PID,或经过端口与安装身份双重确认的目标。

其他问题

  • tests/task/test_src_process_cleanup.py 新增 3,847 行、85 个用例,超出 tests/AGENTS.md 要求的专项最小回归范围,建议压缩为覆盖本次修复边界的少量测试。
  • .vscode/extensions.json 与 SRC 修复无关,建议移出本 PR。
  • PR 正文重复 Sourcery 摘要和英文 <details>,建议保留 1-4 条中文摘要即可。

验证

  • 针对性 pytest:105 passed, 2 subtests passed
  • python -m compileall -q app:通过
  • Ruff:通过
  • 现有测试没有覆盖 traceback 关键词误判,也没有真实 Windows 进程树/端口清理验证。

@Craun718
Craun718 requested a review from 1w1w11w1 August 22, 2026 07:07
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.

2 participants