mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 08:03:12 +00:00
Selecting ~100 changed files in a WSL worktree and hitting Stage All did nothing: the files stayed unstaged and the operation reported a failure. Bulk stage/unstage/discard chunked pathspecs 100 at a time, a count picked against a raw argv. A WSL-routed write is not a raw argv -- it is folded into one login-shell command line that shell-quotes every pathspec, quotes the result again, and embeds it three times (one branch per guest shell), so the finished line runs ~3.4x the raw pathspec bytes. Realistic project paths blew past the 32767-character CreateProcess cap at 100 paths and wsl.exe refused to spawn, with nothing staged. Chunking now measures the finished command line through the real resolver, so the wrapper's quoting rules live in one place and native, WSL and SSH hosts each get the budget of the host that actually spawns. A pathspec too long to fit alone still ships alone rather than being dropped, and no chunk is ever emitted empty -- a pathspec-free `clean -ffdx` would have swept the whole worktree. The tracked-path listing behind that discard also fences the WSL login shell now. Its stdout was parsed NUL-delimited without a fence, so Ubuntu's interactive rc banner glued itself onto the first record: that path failed to match anything git reported and was treated as untracked, sending a tracked file to `git clean` instead of `git restore`. Not observing a path in ls-files output is not evidence the path is untracked. The Windows command-line cap and its libuv-aware length estimate move out of the WSL runner into src/shared/windows-command-line-budget.ts, shared by both callers.
29 lines
1.4 KiB
TypeScript
29 lines
1.4 KiB
TypeScript
/**
|
|
* How long a command line may get before `CreateProcess` refuses it.
|
|
*
|
|
* Windows caps a command line at 32767 characters, and *everything* shares that
|
|
* one budget: the binary, `wsl.exe -d <distro> --exec`, the login-shell wrapper
|
|
* and the payload. So the number only means anything when it is measured on the
|
|
* FINISHED line. Two shipped defects came from measuring a part instead: a
|
|
* script-only threshold in the WSL runner, where a multi-KB login PATH pushed a
|
|
* legal-looking script over the real limit, and count-only chunking of bulk git
|
|
* pathspecs, where the login-shell wrapper tripled the line behind our back.
|
|
*
|
|
* The 2767-character margin absorbs what we do not model exactly (the distro
|
|
* name, libuv's requoting of the outer argv).
|
|
*/
|
|
export const MAX_COMMAND_LINE_CHARS = 30_000
|
|
|
|
/**
|
|
* What `CreateProcess` will count.
|
|
*
|
|
* libuv escapes every `"` and doubles a backslash run before a quote, so a
|
|
* quote-dense script costs more than its length. Charging one extra character
|
|
* per `"` or `\\` keeps the estimate on the safe side of the cap; an earlier
|
|
* version claimed to over-count and in fact under-counted, which put a
|
|
* quote-heavy ~26KB script on argv and over the real limit.
|
|
*/
|
|
export function commandLineLength(args: readonly string[]): number {
|
|
return args.reduce((total, arg) => total + arg.length + 3 + (arg.match(/["\\]/g)?.length ?? 0), 0)
|
|
}
|