Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
74 changes: 45 additions & 29 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpenOptions>;

export class Commands {
private readonly logger: Logger;
private readonly pathResolver: PathResolver;
Expand Down Expand Up @@ -437,27 +453,19 @@ 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);
if (!agent) {
// 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");
}
Expand Down Expand Up @@ -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<boolean> {
public async open(options: OpenOptions = {}): Promise<boolean> {
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");
Expand All @@ -556,13 +567,11 @@ export class Commands {
return false;
}

return this.openWorkspace(
baseUrl,
workspace,
agent,
return this.openWorkspace(baseUrl, workspace, agent, {
folderPath,
openRecent,
);
useDefaultDirectory,
});
}

/**
Expand Down Expand Up @@ -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<boolean> {
const { openRecent, useDefaultDirectory } = {
...openDefaults,
...options,
};
let { folderPath } = options;
const remoteAuthority = toRemoteAuthority(
baseUrl,
workspace.owner_name,
Expand All @@ -765,7 +781,7 @@ export class Commands {
newWindow = false;
}

if (!folderPath) {
if (!folderPath && useDefaultDirectory) {
folderPath = agent.expanded_directory;
}

Expand Down
13 changes: 7 additions & 6 deletions src/uri/uriHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,14 @@ async function handleOpen(ctx: UriRouteContext): Promise<void> {

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).
Expand Down
24 changes: 16 additions & 8 deletions test/unit/uri/uriHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand All @@ -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 () => {
Expand Down