Skip to content

fix(windows): hide console window during gateway restart#54120

Closed
zhiyuan1i wants to merge 3 commits intoopenclaw:mainfrom
zhiyuan1i:fix/windows-restart-visible-console
Closed

fix(windows): hide console window during gateway restart#54120
zhiyuan1i wants to merge 3 commits intoopenclaw:mainfrom
zhiyuan1i:fix/windows-restart-visible-console

Conversation

@zhiyuan1i
Copy link
Copy Markdown

Summary

Fixes #39 — On Windows, the gateway restart script (generated during openclaw update) shows a visible console window running findstr /R /C":18789 .*LISTEN" that persists until the port is released.

Root Cause

runRestartScript() in src/cli/update-cli/restart-helper.ts spawns cmd.exe with windowsHide: true, but this flag doesn't prevent child processes within the batch script (specifically the netstat -ano | findstr pipeline) from creating visible console windows.

Fix

On Windows, instead of directly spawning cmd.exe, we now:

  1. Write a small VBScript wrapper (.vbs) alongside the .bat file
  2. The VBScript uses WScript.Shell.Run with intWindowStyle = 0 (hidden) to launch cmd.exe /c script.bat
  3. The VBScript self-deletes after launch

This guarantees no console window is ever visible to the user, regardless of what commands the batch script runs internally. The WScript.Shell.Run(..., 0, False) approach is the standard Windows way to run batch scripts silently.

Changes

  • restart-helper.ts: Refactored runRestartScript() to use VBScript wrapper on Windows
  • restart-helper.test.ts: Updated tests to match new Windows behavior (wscript.exe + VBS wrapper instead of direct cmd.exe)
  • Removed unused quoteCmdScriptArg import

Testing

  • Verified the fix locally on Windows 11 (10.0.26200)
  • The restart script now executes completely hidden with no visible console window

On Windows, the update-cli restart helper spawns a .bat script that
polls for gateway port release using 'netstat | findstr'. Despite
'windowsHide: true' on the parent spawn(), the piped commands inside
the batch script create a visible console window that persists until
the port is released.

Fix: On Windows, launch the batch script via a VBScript wrapper using
WScript.Shell.Run with window style 0 (hidden). This guarantees no
console window is ever visible to the user, regardless of what the
batch script does internally. The VBScript self-deletes after launch.

Also removes the now-unused quoteCmdScriptArg import from the
restart-helper module.

Fixes openclaw#39
@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes size: S labels Mar 25, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24f050ee72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +183 to +187
const child = spawn("wscript.exe", [vbsPath], {
detached: true,
stdio: "ignore",
windowsHide: true,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add cmd.exe fallback when wscript.exe is unavailable

This changes Windows restart execution to depend entirely on wscript.exe; in hardened Windows environments where Windows Script Host is disabled or blocked, spawn("wscript.exe", ...) fails and the update path no longer performs an automatic restart (it only falls back to a warning in maybeRestartService). Previously, direct cmd.exe execution still restarted the gateway, so this is a behavioral regression for those setups; consider catching this failure and falling back to the old cmd.exe /d /s /c ... path.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 25, 2026

Greptile Summary

This PR fixes a visible console window during gateway restart on Windows by replacing direct cmd.exe spawning with a VBScript wrapper that uses WScript.Shell.Run with intWindowStyle = 0. The approach is sound — windowsHide: true on a cmd.exe spawn does not suppress windows created by child processes, and the VBScript technique is the standard Windows workaround for this.

Key issues found:

  • Incorrect backslash escaping (restart-helper.ts:174): VBScript strings do not treat \ as an escape character, so .replace(/\\/g, "\\\\") produces double backslashes in the generated .vbs file. This works for typical local temp paths due to Windows path normalization, but is semantically wrong and will break for UNC paths (e.g. \\server\share\...).
  • Off-by-one in VBScript quote scheme (restart-helper.ts:177): The template uses 4 quotes before and 7 quotes after the path. After VBScript's "" decoding this gives cmd.exe the argument ""path""", and with /s /c stripping the outer pair, cmd.exe sees "path"" — a stray trailing ". For typical batch scripts without positional parameter usage this is harmless, but it is technically incorrect.
  • No error handling around the new fs.writeFile call (restart-helper.ts:182–189): Previously runRestartScript could not throw. The new VBScript file write adds a failure mode; if the write fails, the entire restart silently fails with an unhandled rejection.
  • The test suite drops the metacharacter quoting test without adding equivalent coverage for paths containing spaces or special characters via the VBScript path.

Confidence Score: 3/5

  • The overall fix direction is correct, but there are two concrete quoting bugs and a new unhandled failure mode that should be resolved before merging.
  • The VBScript approach is the right solution for hiding child-process console windows on Windows. However, the backslash doubling is wrong for VBScript (breaks UNC paths), the "" quote count is off by one pair (trailing stray " in the cmd.exe argument), and the new async file write introduces an unhandled failure mode that didn't exist before. These are concrete, fixable bugs rather than style nits, warranting a 3.
  • src/cli/update-cli/restart-helper.ts — specifically the quotedBat escaping and the quote scheme in the ws.Run line.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/cli/update-cli/restart-helper.ts
Line: 174

Comment:
**Backslash doubling is wrong for VBScript**

VBScript strings do not use `\` as an escape character — backslashes are treated literally. The `.replace(/\\/g, "\\\\")` call writes `C:\\Users\\...\\script.bat` into the `.vbs` file instead of `C:\Users\...\script.bat`. While Windows path APIs often normalise double-backslashes, this is technically incorrect and **breaks for UNC paths** (e.g. `\\server\share\script.bat` becomes `\\\\server\\share\\script.bat`, which is a different UNC root).

Only the quote-doubling step is needed for VBScript string embedding:

```suggestion
    const quotedBat = scriptPath.replace(/"/g, '""');
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/cli/update-cli/restart-helper.ts
Line: 177

Comment:
**Off-by-one in VBScript quote scheme produces a trailing stray `"` in the cmd.exe argument**

The template literal has **4 `"`** before `${quotedBat}` and **7 `"`** after it. After VBScript's `""``"` decoding the `Run` argument becomes:

```
cmd.exe /d /s /c ""<path>"""
```

With `cmd.exe /d /s /c`, the `/s` flag strips the **first and last** `"` of the argument string, yielding `"<path>""`. The extra trailing `"` is passed as an empty argument to the batch script. While in practice most batch scripts ignore unexpected arguments, this is still incorrect.

The correct pattern to have cmd.exe receive `"<path>"` (i.e., one `""` pair on each side in the VBScript string) needs **4 before + 5 after**:

```suggestion
      `ws.Run "cmd.exe /d /s /c """"${quotedBat}""""", 0, False`,
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/cli/update-cli/restart-helper.ts
Line: 182-189

Comment:
**No error handling around VBScript file write**

Previously, `runRestartScript` only called `spawn` (synchronous, never throws). Adding `await fs.writeFile(...)` introduces a new failure mode: if the write fails (disk full, permissions), the function rejects and the gateway restart silently fails. Consider wrapping the Windows path in a try/catch to fall back gracefully, similar to how `prepareRestartScript` already does:

```typescript
try {
  await fs.writeFile(vbsPath, vbsContent, "utf8");
} catch {
  // Fall back: run the bat directly (console flash may be visible but restart succeeds)
  const child = spawn("cmd.exe", ["/d", "/s", "/c", scriptPath], {
    detached: true,
    stdio: "ignore",
    windowsHide: true,
  });
  child.unref();
  return;
}
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(windows): hide console window during..." | Re-trigger Greptile

Comment thread src/cli/update-cli/restart-helper.ts Outdated
if (process.platform === "win32") {
// Write a VBScript wrapper that runs the batch script fully hidden.
const vbsPath = scriptPath.replace(/\.bat$/i, ".vbs");
const quotedBat = scriptPath.replace(/\\/g, "\\\\").replace(/"/g, '""');
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 Backslash doubling is wrong for VBScript

VBScript strings do not use \ as an escape character — backslashes are treated literally. The .replace(/\\/g, "\\\\") call writes C:\\Users\\...\\script.bat into the .vbs file instead of C:\Users\...\script.bat. While Windows path APIs often normalise double-backslashes, this is technically incorrect and breaks for UNC paths (e.g. \\server\share\script.bat becomes \\\\server\\share\\script.bat, which is a different UNC root).

Only the quote-doubling step is needed for VBScript string embedding:

Suggested change
const quotedBat = scriptPath.replace(/\\/g, "\\\\").replace(/"/g, '""');
const quotedBat = scriptPath.replace(/"/g, '""');
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cli/update-cli/restart-helper.ts
Line: 174

Comment:
**Backslash doubling is wrong for VBScript**

VBScript strings do not use `\` as an escape character — backslashes are treated literally. The `.replace(/\\/g, "\\\\")` call writes `C:\\Users\\...\\script.bat` into the `.vbs` file instead of `C:\Users\...\script.bat`. While Windows path APIs often normalise double-backslashes, this is technically incorrect and **breaks for UNC paths** (e.g. `\\server\share\script.bat` becomes `\\\\server\\share\\script.bat`, which is a different UNC root).

Only the quote-doubling step is needed for VBScript string embedding:

```suggestion
    const quotedBat = scriptPath.replace(/"/g, '""');
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread src/cli/update-cli/restart-helper.ts Outdated
const quotedBat = scriptPath.replace(/\\/g, "\\\\").replace(/"/g, '""');
const vbsContent = [
'Set ws = CreateObject("WScript.Shell")',
`ws.Run "cmd.exe /d /s /c """"${quotedBat}""""""", 0, False`,
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 Off-by-one in VBScript quote scheme produces a trailing stray " in the cmd.exe argument

The template literal has 4 " before ${quotedBat} and 7 " after it. After VBScript's """ decoding the Run argument becomes:

cmd.exe /d /s /c ""<path>"""

With cmd.exe /d /s /c, the /s flag strips the first and last " of the argument string, yielding "<path>"". The extra trailing " is passed as an empty argument to the batch script. While in practice most batch scripts ignore unexpected arguments, this is still incorrect.

The correct pattern to have cmd.exe receive "<path>" (i.e., one "" pair on each side in the VBScript string) needs 4 before + 5 after:

Suggested change
`ws.Run "cmd.exe /d /s /c """"${quotedBat}""""""", 0, False`,
`ws.Run "cmd.exe /d /s /c """"${quotedBat}""""", 0, False`,
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cli/update-cli/restart-helper.ts
Line: 177

Comment:
**Off-by-one in VBScript quote scheme produces a trailing stray `"` in the cmd.exe argument**

The template literal has **4 `"`** before `${quotedBat}` and **7 `"`** after it. After VBScript's `""``"` decoding the `Run` argument becomes:

```
cmd.exe /d /s /c ""<path>"""
```

With `cmd.exe /d /s /c`, the `/s` flag strips the **first and last** `"` of the argument string, yielding `"<path>""`. The extra trailing `"` is passed as an empty argument to the batch script. While in practice most batch scripts ignore unexpected arguments, this is still incorrect.

The correct pattern to have cmd.exe receive `"<path>"` (i.e., one `""` pair on each side in the VBScript string) needs **4 before + 5 after**:

```suggestion
      `ws.Run "cmd.exe /d /s /c """"${quotedBat}""""", 0, False`,
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread src/cli/update-cli/restart-helper.ts Outdated
Comment on lines +182 to +189
await fs.writeFile(vbsPath, vbsContent, "utf8");
const child = spawn("wscript.exe", [vbsPath], {
detached: true,
stdio: "ignore",
windowsHide: true,
});
child.unref();
return;
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.

P2 No error handling around VBScript file write

Previously, runRestartScript only called spawn (synchronous, never throws). Adding await fs.writeFile(...) introduces a new failure mode: if the write fails (disk full, permissions), the function rejects and the gateway restart silently fails. Consider wrapping the Windows path in a try/catch to fall back gracefully, similar to how prepareRestartScript already does:

try {
  await fs.writeFile(vbsPath, vbsContent, "utf8");
} catch {
  // Fall back: run the bat directly (console flash may be visible but restart succeeds)
  const child = spawn("cmd.exe", ["/d", "/s", "/c", scriptPath], {
    detached: true,
    stdio: "ignore",
    windowsHide: true,
  });
  child.unref();
  return;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cli/update-cli/restart-helper.ts
Line: 182-189

Comment:
**No error handling around VBScript file write**

Previously, `runRestartScript` only called `spawn` (synchronous, never throws). Adding `await fs.writeFile(...)` introduces a new failure mode: if the write fails (disk full, permissions), the function rejects and the gateway restart silently fails. Consider wrapping the Windows path in a try/catch to fall back gracefully, similar to how `prepareRestartScript` already does:

```typescript
try {
  await fs.writeFile(vbsPath, vbsContent, "utf8");
} catch {
  // Fall back: run the bat directly (console flash may be visible but restart succeeds)
  const child = spawn("cmd.exe", ["/d", "/s", "/c", scriptPath], {
    detached: true,
    stdio: "ignore",
    windowsHide: true,
  });
  child.unref();
  return;
}
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 538f310fe4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Write a VBScript wrapper that runs the batch script fully hidden.
// VBScript does not use \ as an escape character, so only quotes need doubling.
const vbsPath = scriptPath.replace(/\.bat$/i, ".vbs");
const quotedBat = scriptPath.replace(/"/g, '""');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Escape cmd expansion chars in Windows restart path

The new Windows wrapper only escapes double quotes (scriptPath.replace(/"/g, '""')) before embedding the batch path into cmd.exe /d /s /c, but cmd.exe still expands %VAR% inside quoted strings. If the temp/script path contains a literal % (valid on NTFS and possible via custom TEMP), the command line is rewritten and the .bat path can no longer be resolved, so auto-restart silently fails. The prior implementation used quoteCmdScriptArg to escape %/!; that escaping needs to be preserved for the VBS command string (and fallback cmd.exe path).

Useful? React with 👍 / 👎.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 26, 2026

Closing this as implemented after Codex automated review.

Current main already removed the reported Windows update-restart netstat | findstr batch path and keeps the restart handoff detached/hidden with windowsHide: true. The current implementation uses a guarded .cmd wrapper with embedded PowerShell, bounded Scheduled Task stop/run calls, log capture, and tests that explicitly reject the old findstr/netstat -ano | script shape. PR #54120’s VBScript approach was useful against the older helper but is now obsolete on main.

Best possible solution:

Close PR #54120 as implemented on main and keep the current PowerShell-backed Windows restart helper. If users still reproduce a console flash after a release containing d1e5f4bd3c62f7b6d38717a2036a2fc56a199475, handle it as a fresh Windows restart bug with current logs and reproduction details rather than merging this older VBScript branch.

What I checked:

  • Current Windows restart helper: Windows update restarts now generate an openclaw-restart-*.cmd wrapper that invokes embedded PowerShell, logs to the gateway restart log, uses bounded schtasks.exe calls via System.Diagnostics.ProcessStartInfo with UseShellExecute = false, and polls listeners with Get-NetTCPConnection plus a parsed netstat.exe -ano -p tcp fallback rather than the old batch findstr pipeline. (src/cli/update-cli/restart-helper.ts:152, 06d409dc2738)
  • Detached hidden handoff remains covered: runRestartScript() still launches the prepared Windows restart script through cmd.exe /d /s /c with detached: true, stdio: "ignore", and windowsHide: true, using quoteCmdScriptArg() for metacharacter-safe paths. (src/cli/update-cli/restart-helper.ts:352, 06d409dc2738)
  • Regression tests reject old visible-console path: The Windows restart-helper tests assert ordering for the guarded PowerShell/Scheduled Task path and explicitly assert the generated script does not contain findstr, netstat -ano |, or direct batch schtasks /End /TN commands. Separate tests verify the Windows spawn uses windowsHide: true and quotes metacharacter paths. (src/cli/update-cli/restart-helper.test.ts:71, 06d409dc2738)
  • Fixing history: Commit d1e5f4bd3c62f7b6d38717a2036a2fc56a199475 replaced the older Windows batch restart script that used netstat -ano | findstr polling and direct schtasks batch commands with the current PowerShell-backed implementation and updated tests. (src/cli/update-cli/restart-helper.ts:142, d1e5f4bd3c62)
  • PR review context: PR fix(windows): hide console window during gateway restart #54120 targeted the older runRestartScript()/batch behavior with a VBScript wrapper. The discussion usefully identified VBScript quoting, fallback, and error-handling concerns, but current main no longer has the findstr batch script that motivated the PR, so merging this branch would duplicate and partially bypass newer restart-helper hardening. (538f310fe4f7)

So I’m closing this as already implemented rather than keeping a duplicate issue open.

Codex Review notes: model gpt-5.5, reasoning high; reviewed against 06d409dc2738; fix evidence: commit d1e5f4bd3c62.

@clawsweeper clawsweeper Bot closed this Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant