mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
* Allow Windows SSH directory browsing to recover The remote project picker runs before the relay filesystem ACLs exist, so it uses a raw SSH exec channel with POSIX shell commands. Windows OpenSSH targets whose default shell is cmd.exe reject Orca's POSIX exec wrapper, which prevented browsing any remote directories. Keep the existing POSIX path as the first attempt, then fall back to a narrowly scoped PowerShell listing that emits the same line-based format. Constraint: Add Remote Project needs raw SSH browsing before relay roots are registered Constraint: Windows OpenSSH may use cmd.exe as the remote command shell Rejected: Replace the POSIX command for all hosts | riskier for existing Linux/macOS SSH targets Confidence: medium Scope-risk: narrow Directive: Keep POSIX browsing as the primary path; Windows fallback exists only for shell-wrapper rejection errors Tested: npx --yes pnpm@10.24.0 exec vitest run --config config/vitest.config.ts src/main/ipc/ssh-browse.test.ts Tested: npx --yes pnpm@10.24.0 exec oxlint src/main/ipc/ssh-browse.ts src/main/ipc/ssh-browse.test.ts Tested: npx --yes pnpm@10.24.0 exec oxfmt --check src/main/ipc/ssh-browse.ts src/main/ipc/ssh-browse.test.ts Tested: npx --yes pnpm@10.24.0 run typecheck:node Not-tested: Live Windows OpenSSH host with cmd.exe default shell * fix(ssh-browse): strip CRLF in Windows PowerShell browse output Windows OpenSSH exec emits CRLF, but the browse parser split on \n only and never stripped the trailing \r. Every directory line then failed the endsWith('/') check and was misclassified as a file with a stray CR in its name (and resolvedPath kept a trailing \r) — breaking the exact Windows path this PR adds. Split on /\r?\n/ to match the existing ssh-relay-versioned-install convention; POSIX (LF-only) output and filenames with legitimate leading/trailing spaces are unaffected. The added fallback test used \n-only fixtures that no real Windows host produces, masking the bug; switch it to realistic CRLF output so it now guards the regression. Co-authored-by: Orca <help@stably.ai> * fix(ssh-browse): locale-independent Windows fallback + UTF-8 output - Trigger the PowerShell fallback on cmd.exe's locale-independent 9009 exit code, not just English/Spanish stderr text, so non-English Windows hosts actually recover. - Pin [Console]::OutputEncoding to UTF-8 in the PowerShell script so non-ASCII names (e.g. C:\Users\José, CJK, Cyrillic) aren't mojibake'd when decoded. - Rethrow the original POSIX error when the PowerShell retry also fails, so a false-positive predicate match doesn't mask the real failure. - Tests: decode the -EncodedCommand payload to guard injection-safe escaping and the UTF-8 pin; add a 9009-locale fallback case and a negative-predicate (permission denied must not retry) case. - Drop an incorrect sentence from the CRLF-split comment. Co-authored-by: Orca <help@stably.ai> * fix(ssh-browse): surface PowerShell error on proven-Windows fallback When the fallback was triggered by cmd.exe's 9009 exit, the host is provably Windows and PowerShell genuinely ran, so its error ('Cannot find path' / 'Access is denied') is the real cause — surface it instead of the misleading cmd.exe "exec is not recognized" prose. Only the string-heuristic path (a possible POSIX false positive) still rethrows the original error. Also: note the system-ssh transport's 8-bit exit-code truncation caveat in the 9009 comment, and add tests for both double-failure paths (9009 -> surface PowerShell error; heuristic -> surface original). Co-authored-by: Orca <help@stably.ai> * test(ssh-browse): guard the ~ -> $HOME PowerShell fallback branch The tilde expansion in powerShellPathExpression (~ is the default browse path) was unguarded — collapsing it to a literal would pass the whole suite. Add a fallback test asserting the decoded script contains $dir = $HOME. Also tighten the 9009 comment: the locale-independent trigger only holds for a cmd.exe DefaultShell on the ssh2 transport; a powershell.exe DefaultShell exits 1 (not 9009) and, like the system-ssh transport, relies on the stderr heuristics. Co-authored-by: Orca <help@stably.ai> * fix(ssh-browse): emit forward-slash resolvedPath from Windows fallback Get-Location.ProviderPath returns a native backslash path (C:\Users\alice), but the renderer's parentPath/joinPath only split on '/': backslash paths make the browser's 'Up' button a no-op and produce mixed separators when descending. Normalize the emitted resolvedPath to forward slashes (matching the POSIX branch) while keeping the native $resolved for Get-ChildItem -LiteralPath. Update the fallback-test fixtures to the real forward-slash output and assert the -replace normalization is present in the generated script. Co-authored-by: Orca <help@stably.ai> * fix(ssh-browse): root Windows drive paths in the PowerShell fallback The forward-slash resolvedPath means the renderer rebuilds Windows paths with POSIX helpers, so it hands back drive paths Set-Location mishandles: the breadcrumb prepends a spurious leading '/' (/C:/Users -> current drive's root), and 'Up' from a first-level dir yields a bare drive letter ('C:' is drive-relative, not C:\). Normalize both to a rooted drive path in powerShellPathExpression so navigation lands where the user clicked. POSIX, UNC, and relative paths are left untouched. Add parametrized tests for /C:/Users and C: -> rooted $dir literals. Co-authored-by: Orca <help@stably.ai> * test(ssh-browse): cover combined /C: drive-path normalization Guards the strip-then-root ordering in normalizeWindowsDrivePath so a future refactor can't regress the combined leading-slash + bare-drive case. Co-authored-by: Orca <help@stably.ai> * fix(ssh-browse): trigger Windows fallback on non-zero exit, not exit 9009 Verified on real Windows OpenSSH + cmd.exe that a rejected POSIX exec wrapper arrives over SSH as process exit code 1, not 9009 (cmd.exe's 9009 ERRORLEVEL never crosses its process boundary; sshd forwards the process exit code). Confirmed on both the ssh2 and system-ssh transports. The old trigger keyed off exit 9009 (dead code) with an English/Spanish stderr string fallback, so non-English cmd.exe hosts (German/French/ Japanese/etc.) never fell back and directory browsing failed. Fix: retry via PowerShell whenever the POSIX attempt fails with a RemoteBrowseError (command ran, exited non-zero) - locale-independent and covers every cmd.exe locale. Transport errors/timeouts aren't RemoteBrowseErrors, so dropped connections aren't mis-retried. Pick the error to surface via the POSIX "command not found" exit 127 (no powershell.exe means the host isn't Windows, so surface the original POSIX error). Removes the fragile 9009/stderr-string heuristics. Tests: correct injected exit codes to the real value (1), add a Japanese-locale regression test, and lock the retry/no-mask contract. --------- Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com> Co-authored-by: Orca <help@stably.ai> Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>