cc-haha/src/server/services/windowsDrivePath.ts
程序员阿江(Relakkes) fe8aac6b83 fix: normalize Windows drive-root sessions (#601)
Windows folder selection can surface drive roots as D:, which Node treats as drive-relative instead of the absolute D:\ root. Normalize drive-root inputs before resolving launch directories, filesystem access roots, transcript metadata, and workspace paths. Use path.relative containment checks so drive roots and child projects remain distinct in session grouping and file access.

Constraint: Windows drive-root inputs may arrive as C:, D:, or any other single-letter drive prefix
Rejected: Special-case only D: | every Windows drive letter has the same drive-relative semantics
Confidence: high
Scope-risk: moderate
Directive: Keep Windows drive-root normalization centralized; do not reintroduce string-prefix containment checks for workspace roots
Tested: bun test src/server/__tests__/windows-drive-path.test.ts src/server/__tests__/filesystem.test.ts src/server/__tests__/sessions.test.ts src/server/__tests__/workspace-service.test.ts
Tested: cd desktop && bun run test -- --run src/components/layout/Sidebar.test.tsx
Tested: bun run check:server
Tested: cd desktop && bun run lint
Not-tested: Real Windows desktop smoke on a physical Windows machine
Related: #601
2026-05-25 18:16:17 +08:00

31 lines
994 B
TypeScript

import * as path from 'node:path'
export function normalizeDriveRootPathForPlatform(
filePath: string,
platform: NodeJS.Platform = process.platform,
): string {
if (platform !== 'win32') return filePath
const driveRootMatch = filePath.match(/^([a-zA-Z]):$/)
if (!driveRootMatch) return filePath
return `${driveRootMatch[1]}:\\`
}
export function isSameOrInsidePathForPlatform(
targetPath: string,
rootPath: string,
platform: NodeJS.Platform = process.platform,
): boolean {
const pathApi = platform === 'win32' ? path.win32 : path
const normalize = (filePath: string) => {
const resolved = pathApi.resolve(normalizeDriveRootPathForPlatform(filePath, platform))
return platform === 'win32' ? resolved.toLowerCase() : resolved
}
const target = normalize(targetPath)
const root = normalize(rootPath)
const relative = pathApi.relative(root, target)
return relative === '' || (!!relative && !relative.startsWith('..') && !pathApi.isAbsolute(relative))
}