mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 00:03:45 +00:00
fix(pty): preserve byte-exact reservation paths
This commit is contained in:
@@ -9,6 +9,20 @@ import {
|
||||
} from './stable-pane-id'
|
||||
|
||||
const LEAF_ID = '11111111-1111-4111-8111-111111111111'
|
||||
const PANE_KEY = makePaneKey('tab-1', LEAF_ID)
|
||||
|
||||
function reservationKey(
|
||||
workspaceId: string,
|
||||
scope: { connectionId?: string; executionRuntime?: string } = {}
|
||||
) {
|
||||
return makePaneSpawnReservationKey({
|
||||
paneKey: PANE_KEY,
|
||||
providerId: 'provider-1',
|
||||
workspaceId,
|
||||
sessionId: 'session-1',
|
||||
...scope
|
||||
})
|
||||
}
|
||||
|
||||
describe('stable pane ids', () => {
|
||||
it('recognizes UUID leaf ids as stable pane ids', () => {
|
||||
@@ -57,30 +71,50 @@ describe('stable pane ids', () => {
|
||||
expect(parseLegacyNumericPaneKey('tab:1:12')).toBeNull()
|
||||
})
|
||||
|
||||
it('canonicalizes worktree aliases without collapsing independent PTY scopes', () => {
|
||||
const paneKey = makePaneKey('tab-1', LEAF_ID)
|
||||
it.each([
|
||||
['local', { executionRuntime: 'native' }],
|
||||
['SSH', { connectionId: 'ssh-1' }]
|
||||
])('keeps NFC and NFD POSIX workspace paths distinct on %s', (_label, scope) => {
|
||||
expect(reservationKey('repo-1::/workspaces/café', scope)).not.toBe(
|
||||
reservationKey('repo-1::/workspaces/cafe\u0301', scope)
|
||||
)
|
||||
})
|
||||
|
||||
it.each([
|
||||
['drive', 'repo-1::C:\\Work\\Project\\', 'repo-1::c:/work/project'],
|
||||
['UNC', 'repo-1::\\\\Server\\Share\\Project\\', 'repo-1:://server/share/project'],
|
||||
[
|
||||
'WSL UNC',
|
||||
'repo-1::\\\\wsl.localhost\\Ubuntu\\home\\Project',
|
||||
'repo-1:://wsl$/ubuntu/home/Project'
|
||||
]
|
||||
])('canonicalizes equivalent Windows %s paths', (_label, left, right) => {
|
||||
expect(reservationKey(left)).toBe(reservationKey(right))
|
||||
})
|
||||
|
||||
it('canonicalizes raw and worktree-scoped aliases', () => {
|
||||
expect(reservationKey('repo-1::/workspaces/project')).toBe(
|
||||
reservationKey('worktree:repo-1::/workspaces/project')
|
||||
)
|
||||
})
|
||||
|
||||
it('does not collapse independent PTY scopes', () => {
|
||||
const base = {
|
||||
paneKey,
|
||||
paneKey: PANE_KEY,
|
||||
providerId: 'provider-1',
|
||||
connectionId: 'ssh-1',
|
||||
executionRuntime: 'wsl:Ubuntu',
|
||||
workspaceId: 'worktree:repo-1::C:\\Work\\Project\\',
|
||||
workspaceId: 'worktree:repo-1::/workspaces/project',
|
||||
sessionId: 'session-1'
|
||||
}
|
||||
const canonical = makePaneSpawnReservationKey(base)
|
||||
|
||||
expect(canonical).toBe(
|
||||
makePaneSpawnReservationKey({
|
||||
...base,
|
||||
workspaceId: 'repo-1::c:/work/project'
|
||||
})
|
||||
)
|
||||
for (const distinct of [
|
||||
{ providerId: 'provider-2' },
|
||||
{ connectionId: 'ssh-2' },
|
||||
{ executionRuntime: 'native' },
|
||||
{ workspaceId: 'repo-1::C:\\Work\\Other' },
|
||||
{ workspaceId: 'folder:repo-1::C:\\Work\\Project' },
|
||||
{ workspaceId: 'repo-1::/workspaces/other' },
|
||||
{ workspaceId: 'folder:repo-1::/workspaces/project' },
|
||||
{ sessionId: 'session-2' }
|
||||
]) {
|
||||
expect(makePaneSpawnReservationKey({ ...base, ...distinct })).not.toBe(canonical)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { normalizeRuntimePathForComparison } from './cross-platform-path'
|
||||
import { isWindowsAbsolutePathLike, normalizeRuntimePathSeparators } from './cross-platform-path'
|
||||
import { parseWorkspaceKey } from './workspace-scope'
|
||||
import { splitWorktreeId } from './worktree-id'
|
||||
|
||||
@@ -37,22 +37,32 @@ export function makePaneKey(tabId: string, stableLeafId: string): PaneKey {
|
||||
return `${tabId}:${stableLeafId}` as PaneKey
|
||||
}
|
||||
|
||||
function canonicalPaneReservationWorkspacePath(worktreePath: string): string {
|
||||
// Why: POSIX and SSH paths can be byte-sensitive; fold only syntactically proven Windows paths.
|
||||
if (!isWindowsAbsolutePathLike(worktreePath)) {
|
||||
return worktreePath
|
||||
}
|
||||
const normalized = normalizeRuntimePathSeparators(worktreePath)
|
||||
const trimmed = /^[A-Za-z]:\/$/.test(normalized) ? normalized : normalized.replace(/\/+$/, '')
|
||||
const wslUnc = trimmed.match(/^\/\/(?:wsl\.localhost|wsl\$)\/([^/]+)(\/[\s\S]*)?$/i)
|
||||
return wslUnc ? `//wsl/${wslUnc[1].toLowerCase()}${wslUnc[2] ?? ''}` : trimmed.toLowerCase()
|
||||
}
|
||||
|
||||
function canonicalWorkspaceIdentity(workspaceId: string): readonly string[] | null {
|
||||
const trimmed = workspaceId.trim()
|
||||
if (!trimmed || trimmed.length > 1024) {
|
||||
if (!workspaceId.trim() || workspaceId.length > 1024) {
|
||||
return null
|
||||
}
|
||||
const scope = parseWorkspaceKey(trimmed)
|
||||
const scope = parseWorkspaceKey(workspaceId)
|
||||
const kind = scope?.type ?? 'worktree'
|
||||
const unscopedId =
|
||||
scope?.type === 'worktree'
|
||||
? scope.worktreeId
|
||||
: scope?.type === 'folder'
|
||||
? scope.folderWorkspaceId
|
||||
: trimmed
|
||||
: workspaceId
|
||||
const parsed = splitWorktreeId(unscopedId)
|
||||
return parsed
|
||||
? [kind, parsed.repoId, normalizeRuntimePathForComparison(parsed.worktreePath)]
|
||||
? [kind, parsed.repoId, canonicalPaneReservationWorkspacePath(parsed.worktreePath)]
|
||||
: [kind, unscopedId]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user