fix: handle Windows drive-letter paths across the codebase (#148)
* Add metrics collection and terminal performance tests
* fix: handle Windows drive-letter paths across the codebase
Windows paths like C:\Users\foo\project were broken in multiple places:
- agent-storage slugified D:\MyProject as D:-MyProject (illegal colon)
- terminal-manager rejected all non-/ paths as relative
- bootstrap parser misparsed drive colons as TCP host:port
- daemon/client connection helpers misclassified Windows paths
- CLI cwd filtering used hardcoded / separators
- checkout-git worktree detection used hardcoded / in path checks
- worktree archive used split("/").pop() instead of path.basename()
All path helpers now normalize separators and handle Windows
drive letters with case-insensitive comparison where needed.
This commit is contained in:
@@ -73,6 +73,12 @@ describe("TerminalManager", () => {
|
||||
await expect(manager.getTerminals("tmp")).rejects.toThrow("cwd must be absolute path");
|
||||
});
|
||||
|
||||
it("accepts Windows absolute paths", async () => {
|
||||
manager = createTerminalManager();
|
||||
await expect(manager.getTerminals("C:\\Users\\foo\\project")).resolves.not.toThrow();
|
||||
await expect(manager.getTerminals("D:\\MyProject")).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it("creates separate terminals for different cwds", async () => {
|
||||
manager = createTerminalManager();
|
||||
const tmpTerminals = [await manager.createTerminal({ cwd: "/tmp" })];
|
||||
@@ -121,6 +127,18 @@ describe("TerminalManager", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("does not reject Windows absolute paths as relative", async () => {
|
||||
manager = createTerminalManager();
|
||||
// Should pass path validation (not throw "cwd must be absolute path").
|
||||
// The terminal may or may not spawn successfully on non-Windows hosts,
|
||||
// so we only assert the validation error is absent.
|
||||
try {
|
||||
await manager.createTerminal({ cwd: "C:\\Users\\foo\\project" });
|
||||
} catch (error) {
|
||||
expect((error as Error).message).not.toBe("cwd must be absolute path");
|
||||
}
|
||||
});
|
||||
|
||||
it("inherits registered env for the worktree root cwd", async () => {
|
||||
await withShell("/bin/sh", async () => {
|
||||
manager = createTerminalManager();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createTerminal, type TerminalSession } from "./terminal.js";
|
||||
import { resolve, sep } from "node:path";
|
||||
import { resolve, sep, win32, posix } from "node:path";
|
||||
|
||||
export interface TerminalListItem {
|
||||
id: string;
|
||||
@@ -37,7 +37,7 @@ export function createTerminalManager(): TerminalManager {
|
||||
const defaultEnvByRootCwd = new Map<string, Record<string, string>>();
|
||||
|
||||
function assertAbsolutePath(cwd: string): void {
|
||||
if (!cwd.startsWith("/")) {
|
||||
if (!posix.isAbsolute(cwd) && !win32.isAbsolute(cwd)) {
|
||||
throw new Error("cwd must be absolute path");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user