fix(console): sidecar 补 CREATE_NO_WINDOW(不再弹常驻黑窗)
真机复验 verbatim 修复时暴露的既有缺陷:点「检查更新」后屏幕上多出一个不消失的 黑窗(WindowsTerminal 承载,标题即 node.exe 路径)。 根因:控制台自身是 GUI 子系统,spawn 控制台子系统程序(node.exe)时 Windows 会 新建控制台,必须 CREATE_NO_WINDOW 抑制。AgentManager::start 早有此标志, sidecar_command 漏了。修复 verbatim 前 sidecar 秒退、黑窗一闪而过; sidecar 常驻后它才留在屏幕上。 修法:sidecar_command 补 creation_flags;常量提为 sidecar::CREATE_NO_WINDOW 由两处共用,避免值漂移。 本项无自动化守卫(如实记档):creation_flags 只有 setter 没有 getter, 断言不到最终值;扫源码属源码级检查,违反铁律 1。靠真机截图对照把关。 clippy 零告警,test:rust 18/18(行为零变化的注释/常量整理 + 一处新标志)。
This commit is contained in:
1 parent
2437b0e863
commit
9384b64c42
3 files changed
+62
-3
No files matched your search
+32
-1
@@ -490,7 +490,38 @@ Windows 侧只有打包(`console-windows.yml`)。于是「本地全绿 + CI
|
||||
|
||||
> ⚠️ **本项属控制台上壳变更,不是被控端变更**,因此**不触发** `pnpm release patch`。
|
||||
> 但要让用户真正拿到修复,**必须重新构建并分发控制台安装包**。
|
||||
> ⚠️ 修复只净化了「交给 node 的路径」,**尚待在真机上跑通完整链路**(下一版安装包复验)。
|
||||
|
||||
**真机复验(Windows 11 ARM64 VM,装 CI 产出的 arm64 NSIS 包)**:
|
||||
|
||||
| 项 | 修复前(2.0.3) | 修复后 |
|
||||
|---|---|---|
|
||||
| sidecar 进程命令行 | `"\\?\C:\…\node.exe" C:\…\sidecar.mjs`(**program 带 verbatim**) | `"C:\…\node.exe" C:\…\sidecar.mjs`(两侧均干净) |
|
||||
| 点「检查更新」后 | sidecar 0.5 秒即退,界面报「sidecar 已退出(stdout 关闭)」 | sidecar 常驻(pid 恰在点击那一刻出现),界面显示**最新版本 v2.0.3 / 已是最新 / 发布时间 2026-10-13 17:36:54** |
|
||||
| 安装包 | `e7636120…` | `27e41ef8…`(exe)/ `b2b78546…`(随包 sidecar) |
|
||||
|
||||
结论:**缺陷已消除,完整链路真机跑通**。复验同时暴露了一个既有缺陷,见下节。
|
||||
|
||||
### 修复 —— sidecar 拉起时弹出**常驻黑窗**(上项修复后暴露的既有缺陷)
|
||||
|
||||
**现象**:真机复验时「检查更新」能返回结果了,但屏幕上多出一个**不消失的黑窗** ——
|
||||
由 `WindowsTerminal.exe` 承载,标题就是 `C:\Users\…\nodeagent-console\node.exe`。
|
||||
|
||||
**为什么以前没人发现**:修复 verbatim 之前 sidecar 0.5 秒即退,这个黑窗**一闪而过**;
|
||||
sidecar 常驻之后它才留在屏幕上。所以它是**被上一项修复暴露出来的既有缺陷**,不是本次引入。
|
||||
|
||||
**根因**:控制台自身是 **GUI 子系统**程序(不占控制台)。GUI 父进程 spawn 一个
|
||||
**控制台子系统**程序(`node.exe`)时,Windows 会**新建一个控制台** —— 必须靠
|
||||
`CREATE_NO_WINDOW` 抑制。`AgentManager::start` 早已加了这个标志(所以被控端从不弹窗),
|
||||
**`sidecar_command` 漏了**。换句话说:CHANGELOG 里「消灭命令行窗口」那节写的三条措施,
|
||||
sidecar 这条路径一直没被覆盖。
|
||||
|
||||
**修法**:`sidecar_command` 补 `creation_flags(CREATE_NO_WINDOW)`;
|
||||
常量提为 `sidecar::CREATE_NO_WINDOW`,由 sidecar 与 `AgentManager::start` **共用**,避免值漂移。
|
||||
|
||||
> ⚠️ **本项没有自动化守卫**,如实记档原因:`creation_flags` 只有 setter、**没有 getter**
|
||||
> (`std::process::Command` 与 `tokio::process::Command` 都如此),断言不到「到底带没带」;
|
||||
> 而「扫源码看有没有写过这个常量」属源码级检查,违反铁律 1(守卫要落在最终下发值上)。
|
||||
> 故本条靠**真机截图对照**把关(修复前有黑窗 / 修复后无黑窗),并确保 Windows CI 能真编译到这段代码。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -458,10 +458,10 @@ impl AgentManager {
|
||||
cmd.env("NODEAGENT_HOME", &data_dir);
|
||||
|
||||
// ⚠️ 消灭黑窗:Windows 上不加这个标志,每 spawn 一次 node 就弹一个控制台窗口
|
||||
// (与 sidecar 共用同一常量,避免两处硬编码漂移 —— 2026-10-13 sidecar 就是漏了这一行)
|
||||
#[cfg(windows)]
|
||||
{
|
||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
cmd.creation_flags(crate::sidecar::CREATE_NO_WINDOW);
|
||||
}
|
||||
|
||||
let mut child = cmd
|
||||
|
||||
@@ -99,6 +99,27 @@ pub(crate) fn unverbatim(path: PathBuf) -> PathBuf {
|
||||
/// 「必须去掉 verbatim 前缀」这条纪律只在这里守一次;单测直接断言本函数产出的
|
||||
/// `program` / `args`,因此**在 macOS / Linux 上也守得住 Windows 分支**(守的是
|
||||
/// 「最终下发文本」,不是「源码里出现过某个常量」)。
|
||||
/// `CREATE_NO_WINDOW`(`0x0800_0000`):进程创建时不新开控制台窗口。
|
||||
///
|
||||
/// ## 为什么必须显式加(2026-10-13 真机发现的既有缺陷)
|
||||
/// 控制台自身是 **GUI 子系统**程序(不占控制台)。GUI 父进程 spawn 一个**控制台子系统**
|
||||
/// 程序(`node.exe`)时,Windows 会**新建一个控制台**;Windows 11 上它由
|
||||
/// **WindowsTerminal.exe** 承载 → 用户屏幕上多出一个常驻黑窗。
|
||||
///
|
||||
/// 修复 verbatim 缺陷**之前**,sidecar 被拉起后 0.5 秒即退,黑窗一闪而过(所以一直没被发现);
|
||||
/// 修复**之后** sidecar 常驻,黑窗就留在屏幕上不走了 —— 真机截图实证(窗口标题即
|
||||
/// `C:\Users\…\nodeagent-console\node.exe`,进程名 `WindowsTerminal`)。
|
||||
///
|
||||
/// 这与「消灭命令行窗口」那条既有目标直接冲突(`AgentManager::start` 早已加了此标志),
|
||||
/// 故 `sidecar_command` 必须同样带上,两边**共用本常量**以免值漂移。
|
||||
///
|
||||
/// ⚠️ **本项没有单测守卫**,原因如实记档:`creation_flags` 只有 setter、**没有 getter**
|
||||
/// (`std::process::Command` 与 `tokio::process::Command` 都如此),断言不到「到底带没带」;
|
||||
/// 而任何「扫源码看有没有写过这个常量」的做法都违反铁律 1(守卫要落在最终下发值上)。
|
||||
/// 因此这条纪律靠**真机截图对照**把关(修复前有黑窗 / 修复后无黑窗)。
|
||||
#[cfg(windows)]
|
||||
pub(crate) const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||
|
||||
fn sidecar_command(node: &Path, script: &Path) -> Command {
|
||||
let mut cmd = Command::new(unverbatim(node.to_path_buf()));
|
||||
cmd.arg(unverbatim(script.to_path_buf()))
|
||||
@@ -106,6 +127,13 @@ fn sidecar_command(node: &Path, script: &Path) -> Command {
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.kill_on_drop(true); // app 退出时不留孤儿进程
|
||||
|
||||
// ⚠️ 消灭黑窗:不加这一行,用户每点一次「检查更新」都会多出一个常驻黑窗
|
||||
// (详见 `CREATE_NO_WINDOW` 的说明)。
|
||||
#[cfg(windows)]
|
||||
{
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user