mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* perf(paths): guard the no-op regex passes and hoist the loop-invariant root `normalizeRuntimePathForComparison` ran two whole-string regex passes on every call — `/\/+/g` and `/\/+$/` — that cannot change a path with no doubled slash and no trailing slash, which is nearly every path. `parseWslUncPath` likewise folded backslashes and ran an anchored UNC regex over every POSIX path. Substring/char-code probes skip all of them, and a `createRelativePathInsideRootResolver` factory (mirroring the existing `createNormalizedPathInsideOrEqualMatcher`) folds a fan-out's root once instead of once per candidate. Outputs are unchanged; a seeded 200k-path differential fuzz against a pre-guard copy proves it. * perf(paths): drop the root hoist, land the guards alone The three in-module guards are the whole win: 5000-op batches, CPU time, median of 9 --- normalize 541 -> 239 ns/op, relativePathInsideRoot 1778 -> 899, isPathInsideOrEqual 1076 -> 572, parseWslUncPath 57 -> 14. The loop-invariant root hoist added 176 ns/op on top of that (899 -> 723) at 7 hand-picked call sites, and cost a new exported factory whose input contract is the opposite of the one next to it, plus a function substitution in worktree/ownership.ts. Not worth 0.9 ms per storm. Prod diff: 2 files. New ratchet pins the single-factory surface. * docs(paths): point the fixture header at the real guards test
196 lines
7.7 KiB
TypeScript
196 lines
7.7 KiB
TypeScript
export type WslUncPathInfo = {
|
|
distro: string
|
|
linuxPath: string
|
|
}
|
|
|
|
const SLASH_CHAR_CODE = '/'.charCodeAt(0)
|
|
const BACKSLASH_CHAR_CODE = '\\'.charCodeAt(0)
|
|
|
|
function isPathSeparatorCharCode(charCode: number): boolean {
|
|
return charCode === SLASH_CHAR_CODE || charCode === BACKSLASH_CHAR_CODE
|
|
}
|
|
|
|
export function parseWslUncPath(path: string): WslUncPathInfo | null {
|
|
// The match is anchored at `//` after the fold, so only two leading separators can ever reach it.
|
|
// Every POSIX path pays the fold + regex otherwise, and this is on the FS-event storm path.
|
|
if (
|
|
!isPathSeparatorCharCode(path.charCodeAt(0)) ||
|
|
!isPathSeparatorCharCode(path.charCodeAt(1))
|
|
) {
|
|
return null
|
|
}
|
|
const normalized = path.includes('\\') ? path.replace(/\\/g, '/') : path
|
|
const match = normalized.match(/^\/\/(wsl\.localhost|wsl\$)\/([^/]+)(\/.*)?$/i)
|
|
if (!match) {
|
|
return null
|
|
}
|
|
|
|
return {
|
|
distro: match[2],
|
|
linuxPath: match[3] || '/'
|
|
}
|
|
}
|
|
|
|
export function isWslUncPath(path: string): boolean {
|
|
return parseWslUncPath(path) !== null
|
|
}
|
|
|
|
/**
|
|
* Convert a Windows path to a Linux path for commands that will execute inside WSL.
|
|
* Returns the path unchanged if it is already POSIX-style.
|
|
*
|
|
* Why: WSL hook/setup environments may need both the worktree UNC path
|
|
* (\\wsl.localhost\...) and regular Windows install paths (C:\Users\...)
|
|
* translated before passing them to bash. Leaving drive paths untouched
|
|
* breaks scripts that read ORCA_ROOT_PATH or similar env vars inside WSL.
|
|
*/
|
|
export function toLinuxPath(windowsPath: string): string {
|
|
// Why the platform guard: on a POSIX host a literal `//wsl$/x` path is an
|
|
// ordinary directory, not a distro mount, so it must survive unchanged.
|
|
const info = process.platform === 'win32' ? parseWslUncPath(windowsPath) : null
|
|
if (info) {
|
|
return info.linuxPath
|
|
}
|
|
|
|
const driveMatch = windowsPath.match(/^([A-Za-z]):[/\\](.*)$/)
|
|
if (!driveMatch) {
|
|
return windowsPath
|
|
}
|
|
|
|
const driveLetter = driveMatch[1].toLowerCase()
|
|
const rest = driveMatch[2].replace(/\\/g, '/')
|
|
return `/mnt/${driveLetter}/${rest}`
|
|
}
|
|
|
|
/** Convert an absolute Linux path in a known WSL distro to its Windows form. */
|
|
export function toWindowsWslPath(linuxPath: string, distro: string): string {
|
|
return toWindowsWslDrivePath(linuxPath) ?? toWindowsWslUncPath(linuxPath, distro)
|
|
}
|
|
|
|
/** Keep a Linux path addressable through its distro, including drvfs mounts. */
|
|
export function toWindowsWslUncPath(linuxPath: string, distro: string): string {
|
|
return `\\\\wsl.localhost\\${distro}${linuxPath === '/' ? '\\' : linuxPath.replace(/\//g, '\\')}`
|
|
}
|
|
|
|
/**
|
|
* Resolve a repo-scoped worktree base path against the repo's own WSL distro.
|
|
*
|
|
* Why: project setup stores the value verbatim, and for a WSL-backed repo an
|
|
* absolute Linux path like /home/user/trees is the natural spelling of a
|
|
* location inside that distro. Windows path code reads it as drive-relative,
|
|
* so the WSL workspace-mirroring heuristic silently replaced it with
|
|
* ~/orca/workspaces (STA-4772). The repo path pins the distro, making the
|
|
* value unambiguous — translate it to its UNC form. Non-WSL repos and
|
|
* non-POSIX values (UNC, drive, relative) pass through untouched, so native
|
|
* Windows, macOS/Linux, and SSH base paths keep their meaning.
|
|
*
|
|
* Why not toWindowsWslPath: its /mnt/<drive> branch emits a drive-letter path,
|
|
* which the workspace-mirroring heuristic reads as desktop-local and discards —
|
|
* the very bug this resolves. drvfs bases stay on the distro UNC view instead,
|
|
* deliberately trading Windows-side throughput for a distro-addressable path
|
|
* that keeps terminals inside WSL. The single-leading-slash guard is also
|
|
* deliberate: multi-slash (//x) and backslash-rooted spellings are ambiguous
|
|
* with Windows UNC and drive-relative forms and keep their old behavior.
|
|
* Dot segments collapse here because ownership layouts compare paths without
|
|
* resolving them, so creation and classification must see the same spelling.
|
|
*/
|
|
export function resolveWslRepoWorktreeBasePath(repoPath: string, basePath: string): string {
|
|
const repoWsl = parseWslUncPath(repoPath)
|
|
if (!repoWsl || !/^\/(?!\/)/.test(basePath)) {
|
|
return basePath
|
|
}
|
|
const collapsed = collapsePosixDotSegments(basePath)
|
|
return toWindowsWslUncPath(collapsed, repoWsl.distro)
|
|
}
|
|
|
|
function collapsePosixDotSegments(absolutePosixPath: string): string {
|
|
const segments: string[] = []
|
|
for (const segment of absolutePosixPath.split('/')) {
|
|
if (!segment || segment === '.') {
|
|
continue
|
|
}
|
|
if (segment === '..') {
|
|
segments.pop()
|
|
continue
|
|
}
|
|
segments.push(segment)
|
|
}
|
|
return `/${segments.join('/')}`
|
|
}
|
|
|
|
// Why: Windows folds the share (\\wsl$ aliases \\wsl.localhost), the distro, and
|
|
// drvfs /mnt/<drive> tails case-insensitively; the rest of the Linux path is not.
|
|
export function foldWslUncPathCaseInsensitiveParts(path: string): string | null {
|
|
const parsed = parseWslUncPath(path)
|
|
if (!parsed) {
|
|
return null
|
|
}
|
|
// Why: the drvfs automount is literally lowercase /mnt — a case-variant like
|
|
// /MNT is an ordinary case-sensitive Linux dir and must not be folded.
|
|
const linuxPath = /^\/mnt\/[a-zA-Z](?:\/|$)/.test(parsed.linuxPath)
|
|
? parsed.linuxPath.toLowerCase()
|
|
: parsed.linuxPath
|
|
return `//wsl.localhost/${parsed.distro.toLowerCase()}${linuxPath === '/' ? '' : linuxPath}`
|
|
}
|
|
|
|
/**
|
|
* The spelling a WSL-hosted tool answers in for `path`. Git-in-the-distro resolves relative output
|
|
* against the Linux path, not the caller's UNC spelling, and realpath cannot bridge the two spaces.
|
|
*/
|
|
export function toWslExecutionSpace(path: string): string {
|
|
return parseWslUncPath(path)?.linuxPath ?? path
|
|
}
|
|
|
|
/**
|
|
* The drvfs automount is literally lowercase `/mnt/<letter>`; `/MNT` is an ordinary Linux dir.
|
|
* Deliberately looser than `toWindowsWslDrivePath`'s end-anchored matcher below: this one only
|
|
* classifies a prefix, so do not unify them — the anchoring there is what keeps a path carrying a
|
|
* stray line terminator off the drive spelling.
|
|
*/
|
|
const DRVFS_LINUX_PATH = /^\/mnt\/[a-z](?:\/|$)/
|
|
|
|
/** True for a Linux path that is really a Windows drive reached through drvfs. */
|
|
export function isDrvfsLinuxPath(linuxPath: string): boolean {
|
|
return DRVFS_LINUX_PATH.test(linuxPath)
|
|
}
|
|
|
|
/**
|
|
* The Windows drive spelling of a drvfs path, or null when the path is not one. Needs no distro:
|
|
* the bytes sit on the drive whichever distro mounted them.
|
|
*/
|
|
export function toWindowsWslDrivePath(linuxPath: string): string | null {
|
|
// `.` excludes every line terminator, so a drvfs prefix on a stray output line (an rg hit that
|
|
// still carries its CR) stays off the drive spelling.
|
|
const match = linuxPath.match(/^\/mnt\/([a-z])(\/.*)?$/)
|
|
if (!match) {
|
|
return null
|
|
}
|
|
const tail = (match[2] ?? '').replace(/\//g, '\\')
|
|
return `${match[1].toUpperCase()}:${tail || '\\'}`
|
|
}
|
|
|
|
/**
|
|
* The distro whose git would reach `projectPath` across the 9p/drvfs boundary, or null when it
|
|
* would not.
|
|
*
|
|
* Two shapes cross it. A Windows drive path (`C:\...`) crosses it whenever the project's runtime is
|
|
* WSL. The UNC spelling of a distro's own drvfs mount (`\\wsl.localhost\Ubuntu\mnt\c\...`) crosses
|
|
* it however the runtime is set, because the bytes sit on the Windows drive either way.
|
|
*
|
|
* Everything else returns null: a real Linux path inside the distro, a drive path under Windows-host
|
|
* git, a plain UNC share (not mounted in the distro at all), and any POSIX or SSH path.
|
|
*/
|
|
export function getWslFilesystemBoundaryDistro(args: {
|
|
projectPath: string
|
|
wslRuntimeDistro?: string | null
|
|
}): string | null {
|
|
const wsl = parseWslUncPath(args.projectPath)
|
|
if (wsl) {
|
|
return isDrvfsLinuxPath(wsl.linuxPath) ? wsl.distro : null
|
|
}
|
|
if (!/^[A-Za-z]:[\\/]/.test(args.projectPath)) {
|
|
return null
|
|
}
|
|
return args.wslRuntimeDistro || null
|
|
}
|