* perf(windows): stop the capability poll respawning blocking wsl.exe probes
#11295 added a 30s renderer interval to `useWindowsTerminalCapabilities` whose
early-return only fires when WSL is available with at least one distro, so on the
common Windows host (no WSL) it re-ran a full capability read forever. Each read
IPCs four probes whose main-process handlers were synchronous `execFileSync` calls
to wsl.exe/pwsh.exe, blocking the Electron main event loop for up to 5s a time.
The un-latching intent is kept: a host that answers "no WSL" is still re-checked,
now on an exponential backoff (30s, +60s, +120s) that parks once the answer stops
moving, re-arms on window focus, is shared by all consumers of an owner key, and
stops entirely when the last consumer unmounts. The wsl/pwsh IPC handlers now use
async twins that share the existing caches and back off identically.
* fix(windows): classify async wsl/pwsh probe failures with the execFile error shape
The async twins feed `execFile` callback errors into classifiers written for
`execFileSync`: a non-zero exit lands on `error.code` as a number rather than
`error.status`, and a timeout is a SIGTERM kill rather than ETIMEDOUT.
So a Windows host without WSL (wsl.exe ships in System32, so it exits non-zero
instead of ENOENT) was cached as retryable, shrinking the shared window from
10min to 45s and making the still-sync callers re-pay their blocking spawn ~13x
more often; and a pwsh cold start past 5s cached "pwsh missing" for 30s,
demoting the user's PowerShell 7 preference — the exact case the ETIMEDOUT
branch exists to prevent.
Also drops a literal NUL byte from the new re-probe module's signature
separator, which made the file binary to git, and seeds `lastProbeAt` at
registration so focus churn right after mount cannot defer the first re-probe
indefinitely.
Co-authored-by: Orca <help@stably.ai>
* perf(windows): route relay host-capability probes through the async wsl/pwsh twins
A paired web/mobile client resolves `useWindowsTerminalCapabilities` to a local
target (TabBar's `isWebClient` gate, and `useSettingsNavigationMetadata` forces
`{kind:'local'}`), so the new re-probe arms there too. But `window.api.wsl/pwsh`
on a web client is not the ipc/app.ts channel — it is `host.wsl.*`/`host.pwsh.*`
over the runtime RPC, which still ran the sync probes and blocked the desktop
main event loop on `execFileSync('wsl.exe' | 'pwsh.exe')` for up to 5s per call.
Switch those handlers and the relay preflight capability probe to the async
twins added here; they share the same caches, dedupe and backoff, so remote
callers see no behavior change.
* fix(windows): harden async capability reprobes
* fix(windows): dedupe PowerShell shell probes
---------
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(preflight): resolve WSL/SSH agent paths past shell aliases
LeanCTX and similar tools wrap claude/codex as interactive shell aliases.
command -v then returns alias text, which fails absolute-path detection and
hides installed agents (#7816).
Prefer bash type -P, then zsh type -p, then command -v for dash/sh fallback
in WSL agent discovery, WSL isCommandOnPath, and remote relay probes.
* fix(preflight): harden alias-safe PATH lookup chain
Require non-empty results between type -P, type -p, and command -v so bash
type -p empty success cannot skip later lookups.
* fix(preflight): resolve agent executables directly from PATH
---------
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
* chore(lint): upgrade oxlint to 1.71 and enable 7 new rules
Upgrade oxlint 1.67.0 -> 1.71.0 (1.72 was blocked by the repo's 3-day
minimum-release-age supply-chain guard; nothing here needs it). The
bump is a no-op on the existing config.
Enable 3 error rules (backlog autofixed to zero in this commit) and
4 warn rules (surface signal without gating CI):
error (autofixed, behavior-preserving):
- unicorn/prefer-node-protocol (~1531 sites: bare builtin -> node:)
- typescript/no-import-type-side-effects (~36: all-inline-type -> import type)
- unicorn/no-array-reverse (19: copy-then-reverse -> toReversed)
warn (real signal, current fires are test-only/correct):
- unicorn/no-array-fill-with-reference-type (aliasing footgun guard)
- typescript/no-unsafe-function-type (bans bare Function type)
- unicorn/prefer-array-flat-map (map().flat() -> flatMap())
- unicorn/prefer-regexp-test (.match() in bool ctx -> .test())
mobile/.oxlintrc.json extends root, so it inherits all 7; the autofix
ran from root and covered mobile/ too.
Verification (all green): oxlint 0 errors (root+mobile+aux configs),
oxfmt clean, typecheck (node+cli+web), vitest 22795 passed / 0 failed,
builds (electron-vite + web + cli) succeed. node: rewrites confirmed to
skip embedded SSH/CLI string payloads (AST-only); all toReversed sites
verified to operate on fresh copies or write-once locals.
* chore(lint): bump mobile oxlint to 1.71 so inherited rules parse
mobile/ is a standalone pnpm project pinning its own oxlint@1.67, which
lacks unicorn/no-array-fill-with-reference-type (needs >=1.70). Since
mobile/.oxlintrc.json extends the root config, mobile CI's 'cd mobile &&
oxlint' failed to parse the new rule. Bump mobile to match root (1.71).
Verified in mobile/: oxlint 0 errors, oxfmt --check clean, tsc --noEmit
pass, vitest 978 passed / 0 failed.
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
* fix(terminal): support Windows shell selection for SSH hosts
* fix(runtime): register SSH Windows capability preflight RPC
* fix(terminal): honor remote Git Bash and split preflight IPC seams
* fix(terminal): keep SSH Windows shell state tied to the selected host
* fix(terminal): preserve Windows SSH shell selection
---------
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
* feat: add windows ssh relay base support
* feat: support windows ssh relay runtime services
* fix: default windows ssh pty cwd to user profile
* fix: support windows hosts over system ssh
* fix: preserve degraded windows relay native deps
* fix: gate windows shell args by relay platform
* fix: preserve windows relay fallback pipes
* test: align windows native deps relay fixture
* fix: build valid windows install lock command
* fix: address windows SSH relay review findings
Resolve correctness, efficiency, and reuse issues found reviewing the
Windows SSH native-host support:
- GC liveness on Windows now probes the actual named pipe (via node
net.connect against markers + deterministic candidates) instead of
substring-matching Win32_Process command lines, which could remove a
live relay dir. Reports ALIVE conservatively only when there is no
liveness signal at all (no markers and no seed pipes).
- Resolve the remote node path once per deploy and thread it through
install/repair/launch instead of re-resolving 3-7x.
- Replace the 200ms node -e poll loop with a single long-lived remote
wait process during Windows relay startup.
- Skip the no-op executable command on Windows in uploadRelay.
- Make the Windows fallback pipe name deterministic and recoverable
(drop the global counter), with an extra reconnect attempt.
- Normalize the prepended node bin dir to backslashes on Windows PATH.
- Batch the system-SSH Windows directory upload into a single streamed
JSON package instead of one ssh process per file.
- Extract relay endpoint/marker helpers into ssh-relay-endpoints.ts and
consolidate the PowerShell EncodedCommand encoding into the shared
powershell-command-encoding module.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Support cancellation and timeouts in Windows port scanning
- Propagate the request AbortSignal and a 5-second timeout to both
PowerShell and netstat child processes during Windows port scanning.
- Avoid spawning the netstat fallback process if the port scan has
already been aborted.
- Wrap the .NET OSArchitecture check in a try/catch block during SSH
Windows platform detection to robustly fall back to environment
variables if needed.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>