Skip to content

fix(cli): complete Windows cliproxy platform handling + pid probe (#11236) - #11263

Merged
diegosouzapw merged 2 commits into
release/v3.8.50from
fix/11236-windows-cliproxy-residuals
Aug 23, 2026
Merged

fix(cli): complete Windows cliproxy platform handling + pid probe (#11236)#11263
diegosouzapw merged 2 commits into
release/v3.8.50from
fix/11236-windows-cliproxy-residuals

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Closes #11236

Bugs 1, 4 and 5 of the issue are already fixed on the tip (#10371, #10491, and the combos PATCH/CLI work). This PR closes the remaining residuals validated against release/v3.8.50.

1. Dist-fold residuals (bugs 2+3 — root cause of the .exe failures)

#10371 fixed the Windows .exe name in the source, but three runtime reads of the process.platform literal survived in the same paths. The published npm artifact is bundled on Linux, and the bundler constant-folds that literal to the build machine's platform, pruning the win32 branch from the shipped artifact — so installs still copied the binary as extension-less cliproxyapi and spawn still ENOENT'd on Windows. Precedent: b43a212 (#10244/#10293), which converted detectPlatform/detectArch for the same fold class.

Converted to call-time os.platform() reads, matching the module's documented anti-fold pattern:

  • src/lib/versionManager/binaryManager.tsmanagedBinaryName() (install/rollback/getCurrentBinaryPath name)
  • src/lib/services/installers/cliproxy.tsresolveSpawnArgs() executable name (bug 3's spawn path)
  • src/lib/versionManager/processManager.tsgetProcessInfo() per-OS memory probes (same file family, same fold class; the darwin ps branch would be pruned on macOS)

Deliberately not converted: process.platform uses inside test files (binaryManager.test.ts, cliproxy-resolve-spawn-args-6877.test.ts) — tests are never bundled into the artifact.

2. Fold guard (new test)

tests/unit/windows-platform-fold-guard-11236.test.ts — structural guard that fails if process.platform reappears outside a comment in the four artifact runtime files above (plus portProbe.ts). Uses a comment-stripping tokenizer (line + block comments blanked, line numbers preserved) and ships mutation self-checks proving a code occurrence is caught while comment mentions (the fold-explanation comments reference the pattern by name) are let through.

3. Bug 6 — pid: null on Windows while healthy

resolvePortPid() (src/lib/services/portProbe.ts) only probed lsof, ss and net-tools netstat — none exist on a stock Windows host, so an adopted service kept pid: null forever. Added a last-fallback netstat -ano probe with a dedicated parser (parseWindowsNetstatPid) that matches TCP LISTENING rows on the local-address column (: anchor prevents suffix false-positives like 128 vs 20128) and reads the pid from the last column. Unix probes are untouched; on Unix the Windows probe degrades to a no-op (Linux/macOS rows say LISTEN, never LISTENING), and on Windows the Unix probes fail fast via ENOENT/invalid-args. The darwin args branch in the same probe array was also converted to os.platform() (same fold class, same hunk).

Parser unit tests use a realistic Windows 11 netstat -ano sample (CRLF, IPv4/IPv6 listeners, ESTABLISHED foreign-address trap, stateless UDP row).

4. Bug 5 hardening — oauth status out-of-contract 200

bin/cli/commands/oauth.mjs runOAuthStatus: #10491's data.connections ?? envelope still fell through to data itself when the API returns a 200 with no connections/providers/items array (e.g. {"status":"ok"}), crashing on .filter is not a function (+ the libuv teardown assertion reported on Windows). Now guarded with Array.isArray(): coerces to an empty list and writes a sanitized one-line stderr warning — no stack trace, no throw.

TDD evidence (Hard Rule #18)

RED before the fix, GREEN after:

Test RED (before) GREEN (after)
fold guard (#11236) offenders at binaryManager L114, processManager L155/L162, cliproxy L104, portProbe L215 6/6 pass (incl. 2 mutation self-checks)
parseWindowsNetstatPid tests export did not exist 2/2 pass, full portProbePid suite 10/10
oauth out-of-contract test TypeError: ...filter is not a function (the exact bug 5 crash) passes, cli-oauth-commands 10/10

Sibling suites green: binaryManager, processManager, extract-zip-5590, windows-hide-spawns-8131, windows-process-stop-8045, version-manager (×3), db-versionManager (72 tests), ServiceSupervisor, installers (cliproxy ×2, ninerouter, bifrost, mux), ninerouter-embed-6205, cli-process-supervisor (×2) (87 tests).

One test alignment (not masking): cliproxy-resolve-spawn-args-6877.test.ts's win32 case mocked process.platform via defineProperty, which the new runtime read no longer consults; the mock moved to mock.method(os, "platform") — the .exe assertion itself is unchanged.

Checks

  • npm run typecheck:core clean
  • npx eslint on all touched files clean — the new oauth test avoids as any (as unknown as) so cli-oauth-commands.test.ts stays at its frozen suppression count of 20
  • Prettier applied to all touched files

⚠️ base-red inherited: #9985 (release/v3.8.50 tip fails build on the EndpointPageClient JSX defect; fix in review at #11256 — this PR does not touch that file)

backryun and others added 2 commits August 23, 2026 13:01
…1236)

Residuals of #11236 after #10371/#10491 landed on the tip:

- Dist-fold residuals (bugs 2+3): managedBinaryName() (binaryManager),
  resolveSpawnArgs() (installers/cliproxy) and the per-OS memory probes in
  getProcessInfo() (processManager) still read the process.platform literal,
  which the Linux build of the published artifact constant-folds (precedent:
  b43a212 / #10244). Converted to call-time os.platform() reads, matching
  the module's documented anti-fold pattern. Test-side process.platform uses
  are not bundled and stay.
- Fold guard: new tests/unit/windows-platform-fold-guard-11236.test.ts pins
  zero out-of-comment process.platform occurrences in the four artifact
  runtime files, with a comment-stripping tokenizer plus mutation self-checks.
- Bug 6 (pid null on Windows): portProbe.resolvePortPid() only probed
  lsof/ss/net-tools netstat. Added a netstat -ano probe with a dedicated
  LISTENING-row parser (parseWindowsNetstatPid) as the last fallback; Unix
  probes unchanged, and the Windows parser never matches Unix rows (LISTEN vs
  LISTENING). Also converted the darwin args branch in the same array to
  os.platform() (same fold class, same hunk).
- Bug 5 hardening: runOAuthStatus coerces an out-of-contract 200 payload to
  an empty list with a sanitized stderr warning instead of crashing on
  .filter over a non-array.

TDD: guard test, parser tests and the oauth hardening test all failed RED
before the fix and pass GREEN after; sibling suites (binaryManager,
processManager, portProbePid, cli-oauth-commands, installers,
ServiceSupervisor, version-manager) green. The 6877 spawn-args test's win32
mock moved from defineProperty(process.platform) to
mock.method(os, "platform") to match the new runtime read — assertion
unchanged.
@diegosouzapw
diegosouzapw merged commit 578db86 into release/v3.8.50 Aug 23, 2026
16 of 22 checks passed
@diegosouzapw
diegosouzapw deleted the fix/11236-windows-cliproxy-residuals branch August 23, 2026 21:46
@diegosouzapw diegosouzapw mentioned this pull request Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants