diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aeeccd8..082d5846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## Unreleased + +### Fixed + +- URI handler no longer falls back to the agent's `expanded_directory` when the `folder` + query parameter is absent. An absent `folder` now opens a bare remote window, restoring + pre-v1.10.0 behavior. + ## [v1.14.2-pre](https://github.com/coder/vscode-coder/releases/tag/v1.14.2-pre) 2026-03-26 ### Added diff --git a/src/commands.ts b/src/commands.ts index a7ad0332..1791b3a7 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -36,6 +36,22 @@ import { WorkspaceTreeItem, } from "./workspace/workspacesProvider"; +interface OpenOptions { + workspaceOwner?: string; + workspaceName?: string; + agentName?: string; + folderPath?: string; + openRecent?: boolean; + /** When false, an absent folderPath opens a bare remote window instead of + * falling back to the agent's expanded_directory. Defaults to true. */ + useDefaultDirectory?: boolean; +} + +const openDefaults = { + openRecent: false, + useDefaultDirectory: true, +} as const satisfies Partial; + export class Commands { private readonly logger: Logger; private readonly pathResolver: PathResolver; @@ -437,13 +453,9 @@ export class Commands { throw new Error("You are not logged in"); } if (item instanceof AgentTreeItem) { - await this.openWorkspace( - baseUrl, - item.workspace, - item.agent, - undefined, - true, - ); + await this.openWorkspace(baseUrl, item.workspace, item.agent, { + openRecent: true, + }); } else if (item instanceof WorkspaceTreeItem) { const agents = await this.extractAgentsWithFallback(item.workspace); const agent = await maybeAskAgent(agents); @@ -451,13 +463,9 @@ export class Commands { // User declined to pick an agent. return; } - await this.openWorkspace( - baseUrl, - item.workspace, - agent, - undefined, - true, - ); + await this.openWorkspace(baseUrl, item.workspace, agent, { + openRecent: true, + }); } else { throw new TypeError("Unable to open unknown sidebar item"); } @@ -523,13 +531,16 @@ export class Commands { * Throw if not logged into a deployment or if a matching workspace or agent * cannot be found. */ - public async open( - workspaceOwner?: string, - workspaceName?: string, - agentName?: string, - folderPath?: string, - openRecent?: boolean, - ): Promise { + public async open(options: OpenOptions = {}): Promise { + const { + workspaceOwner, + workspaceName, + agentName, + folderPath, + openRecent, + useDefaultDirectory, + } = { ...openDefaults, ...options }; + const baseUrl = this.extensionClient.getAxiosInstance().defaults.baseURL; if (!baseUrl) { throw new Error("You are not logged in"); @@ -556,13 +567,11 @@ export class Commands { return false; } - return this.openWorkspace( - baseUrl, - workspace, - agent, + return this.openWorkspace(baseUrl, workspace, agent, { folderPath, openRecent, - ); + useDefaultDirectory, + }); } /** @@ -749,9 +758,16 @@ export class Commands { baseUrl: string, workspace: Workspace, agent: WorkspaceAgent, - folderPath: string | undefined, - openRecent = false, + options: Pick< + OpenOptions, + "folderPath" | "openRecent" | "useDefaultDirectory" + > = {}, ): Promise { + const { openRecent, useDefaultDirectory } = { + ...openDefaults, + ...options, + }; + let { folderPath } = options; const remoteAuthority = toRemoteAuthority( baseUrl, workspace.owner_name, @@ -765,7 +781,7 @@ export class Commands { newWindow = false; } - if (!folderPath) { + if (!folderPath && useDefaultDirectory) { folderPath = agent.expanded_directory; } diff --git a/src/uri/uriHandler.ts b/src/uri/uriHandler.ts index f03e2690..703cae4e 100644 --- a/src/uri/uriHandler.ts +++ b/src/uri/uriHandler.ts @@ -92,13 +92,14 @@ async function handleOpen(ctx: UriRouteContext): Promise { let opened = false; try { - opened = await commands.open( - owner, - workspace, - agent ?? undefined, - folder ?? undefined, + opened = await commands.open({ + workspaceOwner: owner, + workspaceName: workspace, + agentName: agent ?? undefined, + folderPath: folder ?? undefined, openRecent, - ); + useDefaultDirectory: false, + }); } finally { // Clear the pending chat ID if commands.open() did not // actually open a window (user cancelled, or it threw). diff --git a/test/unit/uri/uriHandler.test.ts b/test/unit/uri/uriHandler.test.ts index e2671ed4..849f94e0 100644 --- a/test/unit/uri/uriHandler.test.ts +++ b/test/unit/uri/uriHandler.test.ts @@ -134,7 +134,14 @@ describe("uriHandler", () => { ); expect(deploymentManager.setDeployment).toHaveBeenCalled(); - expect(commands.open).toHaveBeenCalledWith("o", "w", "a", "/f", true); + expect(commands.open).toHaveBeenCalledWith({ + workspaceOwner: "o", + workspaceName: "w", + agentName: "a", + folderPath: "/f", + openRecent: true, + useDefaultDirectory: false, + }); }); it.each([ @@ -146,13 +153,14 @@ describe("uriHandler", () => { const { handleUri, commands } = createTestContext(); const query = `owner=o&workspace=w&${param}&url=${encodeURIComponent(TEST_URL)}`; await handleUri(createMockUri("/open", query)); - expect(commands.open).toHaveBeenCalledWith( - "o", - "w", - undefined, - undefined, - expected, - ); + expect(commands.open).toHaveBeenCalledWith({ + workspaceOwner: "o", + workspaceName: "w", + agentName: undefined, + folderPath: undefined, + openRecent: expected, + useDefaultDirectory: false, + }); }); it("opens chat when chatId is present and open succeeds", async () => {