Skip to content

fix: avoid exiting interactive PowerShell in install script#48983

Open
Angrycain wants to merge 2 commits intoopenclaw:mainfrom
Angrycain:codex/fix-install-ps1-exit
Open

fix: avoid exiting interactive PowerShell in install script#48983
Angrycain wants to merge 2 commits intoopenclaw:mainfrom
Angrycain:codex/fix-install-ps1-exit

Conversation

@Angrycain
Copy link
Copy Markdown

@Angrycain Angrycain commented Mar 17, 2026

Summary

  • Problem: scripts/install.ps1 called exit 1 inside Main, which can terminate an interactive PowerShell session when the installer is run via iex or a downloaded scriptblock.
  • Why it matters: a failed install should return control to the user's shell instead of killing the whole session.
  • What changed: failure paths now record an exit code and return from Main; top-level non-zero exit is preserved only for direct script-file execution.
  • What did NOT change (scope boundary): no CLI flags, config schema, or public API changes.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

Failed Windows installs no longer terminate the user's interactive PowerShell session when the installer is run via iex or a downloaded scriptblock. Direct script-file execution still returns a non-zero exit code on failure.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS (authoring/tests), bug affects Windows PowerShell behavior
  • Runtime/container: Node.js / pnpm
  • Model/provider:
  • Integration/channel (if any):
  • Relevant config (redacted):

Steps

  1. Run the Windows installer via iex / downloaded scriptblock.
  2. Trigger a failure path inside Main (for example missing install prerequisites or npm install failure).
  3. Observe shell behavior.

Expected

  • Installer reports the failure and returns control to the current PowerShell session.
  • Direct script-file execution still exits non-zero.

Actual

  • Before this change, failure paths inside Main could terminate the current interactive PowerShell session.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: added regression coverage to ensure Main does not call exit directly and that top-level exit remains guarded by direct script execution.
  • Edge cases checked: failure in execution policy check, Node/Git prerequisite checks, and npm install path all return through the new failure path.
  • What you did not verify: manual execution on a real Windows PowerShell host.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR
  • Files/config to restore: scripts/install.ps1
  • Known bad symptoms reviewers should watch for: installer failures no longer returning a non-zero exit code for direct script-file execution

Risks and Mitigations

  • Risk: changing failure control flow could accidentally suppress the expected non-zero exit behavior for direct script execution.
    • Mitigation: regression test asserts the top-level guarded exit path remains in place.

AI-assisted: yes
Tested: corepack pnpm vitest --run test/scripts/install.ps1.test.ts test/scripts/ui.test.ts

@openclaw-barnacle openclaw-barnacle bot added scripts Repository scripts size: S labels Mar 17, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR fixes a UX bug where running the Windows install script via iex (Invoke-Expression) in an interactive PowerShell session would call exit and terminate the entire shell, not just the script. The fix is clean and well-reasoned: exit calls inside Main are replaced with return (Fail-Install), and a single top-level exit is guarded by $PSCommandPath (which is only populated when the script is run as a file, not via iex).

Key changes:

  • Added Fail-Install helper that sets $script:InstallExitCode and returns $false, avoiding exit from within the function body.
  • All four exit 1 calls inside Main replaced with return (Fail-Install).
  • Success path now explicitly return $true.
  • Top-level caller pattern: $installSucceeded = Main, then exit only when -not $installSucceeded -and $PSCommandPath.
  • New static-analysis Vitest test validates both invariants (no exit inside Main; the guard pattern is present).

Minor concern: The extractFunctionBody parser in the test file counts raw {/} characters and will mis-parse Main if a future string literal inside it contains a bare brace. This doesn't affect current correctness but is a fragility worth noting.

PR description is entirely blank — all template sections (Problem, Why it matters, Security Impact, Evidence, Human Verification, etc.) are unfilled. Per the template this repository uses, these sections are required.

Confidence Score: 4/5

  • Safe to merge — the fix is logically correct and well-tested; only a minor test-parser fragility and an empty PR description template hold it back from a perfect score.
  • The core logic ($PSCommandPath guard) correctly distinguishes iex/interactive invocations from script-file invocations, replacing exit calls inside Main with return to avoid killing interactive sessions. The success and failure paths both behave correctly. The new Vitest tests validate the structural invariants. The one minor concern is the brace-counting parser in the test being brittle to future string literals with bare braces, and the completely blank PR description template.
  • No files require special attention — test/scripts/install.ps1.test.ts has a minor parser fragility but does not affect current correctness.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: test/scripts/install.ps1.test.ts
Line: 5-29

Comment:
**Brace parser is fragile to `{`/`}` in string literals**

`extractFunctionBody` counts `{` and `}` characters unconditionally. If a future change adds a PowerShell string literal containing a bare brace inside `Main` (e.g., `Write-Host "See docs at https://example.com/{path}"`) the brace counter will go out of sync and either return a truncated body or throw, causing the test to fail in a confusing way.

Consider anchoring on PowerShell's actual block boundaries (e.g. split on lines and track `function`/`if`/`else` nesting) or, at minimum, add a comment warning future editors that string-literal braces inside `Main` will break this parser.

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

Last reviewed commit: 8ccfaa2

Comment on lines +5 to +29
function extractFunctionBody(source: string, name: string): string {
const signature = `function ${name} {`;
const start = source.indexOf(signature);
expect(start).toBeGreaterThanOrEqual(0);

let depth = 0;
let bodyStart = -1;
for (let i = start; i < source.length; i += 1) {
const char = source[i];
if (char === "{") {
depth += 1;
if (bodyStart === -1) {
bodyStart = i + 1;
}
continue;
}
if (char === "}") {
depth -= 1;
if (depth === 0 && bodyStart !== -1) {
return source.slice(bodyStart, i);
}
}
}

throw new Error(`Could not extract body for ${name}`);
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 Brace parser is fragile to {/} in string literals

extractFunctionBody counts { and } characters unconditionally. If a future change adds a PowerShell string literal containing a bare brace inside Main (e.g., Write-Host "See docs at https://example.com/{path}") the brace counter will go out of sync and either return a truncated body or throw, causing the test to fail in a confusing way.

Consider anchoring on PowerShell's actual block boundaries (e.g. split on lines and track function/if/else nesting) or, at minimum, add a comment warning future editors that string-literal braces inside Main will break this parser.

Prompt To Fix With AI
This is a comment left during a code review.
Path: test/scripts/install.ps1.test.ts
Line: 5-29

Comment:
**Brace parser is fragile to `{`/`}` in string literals**

`extractFunctionBody` counts `{` and `}` characters unconditionally. If a future change adds a PowerShell string literal containing a bare brace inside `Main` (e.g., `Write-Host "See docs at https://example.com/{path}"`) the brace counter will go out of sync and either return a truncated body or throw, causing the test to fail in a confusing way.

Consider anchoring on PowerShell's actual block boundaries (e.g. split on lines and track `function`/`if`/`else` nesting) or, at minimum, add a comment warning future editors that string-literal braces inside `Main` will break this parser.

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: 8ccfaa2c11

ℹ️ 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 +374 to +375
if (-not $installSucceeded -and $PSCommandPath) {
exit $script:InstallExitCode
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 Preserve non-zero status for scriptblock installs

The new top-level guard only calls exit when $PSCommandPath is set, but $PSCommandPath is empty when this installer is executed as a scriptblock (for example the documented iwr ... | iex flow), so failed installs now return from Main without a terminating error or process exit code. In non-interactive contexts that invoke the script this way, a failed install can be reported as success (exit code 0), which breaks automation that depends on shell status.

Useful? React with 👍 / 👎.

@andyzhang88888
Copy link
Copy Markdown

Review

Good fix. Replacing exit 1 with Fail-Install + $PSCommandPath guard correctly prevents killing interactive PowerShell sessions when the installer is run via iex.

The new test (install.ps1.test.ts) verifying Main has no exit and the top-level guard pattern is a nice safety net.

LGTM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scripts Repository scripts size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Install script (install.ps1) forcefully exits PowerShell process on systems without winget (e.g., Windows LTSC)

2 participants