Skip to content

Commit 612df81

Browse files
committed
Add configurable autostart behavior for SSH connections
1 parent 8f7c748 commit 612df81

File tree

7 files changed

+109
-25
lines changed

7 files changed

+109
-25
lines changed

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,21 @@
120120
"type": "boolean",
121121
"default": false
122122
},
123+
"coder.disableAutostart": {
124+
"markdownDescription": "Disable starting the workspace automatically when connecting via SSH.",
125+
"type": "string",
126+
"enum": [
127+
"auto",
128+
"always",
129+
"never"
130+
],
131+
"markdownEnumDescriptions": [
132+
"Disables autostart on macOS only (recommended to avoid sleep/wake issues)",
133+
"Disables on all platforms",
134+
"Keeps autostart enabled on all platforms"
135+
],
136+
"default": "auto"
137+
},
123138
"coder.globalFlags": {
124139
"markdownDescription": "Global flags to pass to every Coder CLI invocation. Enter each flag as a separate array item; values are passed verbatim and in order. Do **not** include the `coder` command itself. See the [CLI reference](https://coder.com/docs/reference/cli) for available global flags.\n\nNote that for `--header-command`, precedence is: `#coder.headerCommand#` setting, then `CODER_HEADER_COMMAND` environment variable, then the value specified here. The `--global-config` flag is explicitly ignored.",
125140
"type": "array",

src/api/workspace.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {
88
import { spawn } from "node:child_process";
99
import * as vscode from "vscode";
1010

11+
import { getGlobalFlags } from "../cliConfig";
1112
import { type FeatureSet } from "../featureSet";
12-
import { getGlobalFlags } from "../globalFlags";
1313
import { escapeCommandArg } from "../util";
1414
import { type UnidirectionalStream } from "../websocket/eventStreamConnection";
1515

src/cliConfig.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { type WorkspaceConfiguration } from "vscode";
2+
3+
import { getHeaderArgs } from "./headers";
4+
import { escapeCommandArg } from "./util";
5+
6+
/**
7+
* Returns global configuration flags for Coder CLI commands.
8+
* Always includes the `--global-config` argument with the specified config directory.
9+
*/
10+
export function getGlobalFlags(
11+
configs: WorkspaceConfiguration,
12+
configDir: string,
13+
): string[] {
14+
// Last takes precedence/overrides previous ones
15+
return [
16+
...(configs.get<string[]>("coder.globalFlags") || []),
17+
"--global-config",
18+
escapeCommandArg(configDir),
19+
...getHeaderArgs(configs),
20+
];
21+
}
22+
23+
type DisableAutostartSetting = "auto" | "always" | "never";
24+
25+
/**
26+
* Determines whether autostart should be disabled based on the setting and platform.
27+
* - "always": disable on all platforms
28+
* - "never": never disable
29+
* - "auto": disable only on macOS (due to sleep/wake issues)
30+
*/
31+
export function shouldDisableAutostart(
32+
configs: WorkspaceConfiguration,
33+
platform: NodeJS.Platform,
34+
): boolean {
35+
const setting = configs.get<DisableAutostartSetting>(
36+
"coder.disableAutostart",
37+
"auto",
38+
);
39+
return setting === "always" || (setting === "auto" && platform === "darwin");
40+
}

src/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import * as vscode from "vscode";
1010
import { createWorkspaceIdentifier, extractAgents } from "./api/api-helper";
1111
import { CoderApi } from "./api/coderApi";
1212
import { needToken } from "./api/utils";
13+
import { getGlobalFlags } from "./cliConfig";
1314
import { type CliManager } from "./core/cliManager";
1415
import { type ServiceContainer } from "./core/container";
1516
import { type ContextManager } from "./core/contextManager";
1617
import { type MementoManager } from "./core/mementoManager";
1718
import { type PathResolver } from "./core/pathResolver";
1819
import { type SecretsManager } from "./core/secretsManager";
1920
import { CertificateError } from "./error";
20-
import { getGlobalFlags } from "./globalFlags";
2121
import { type Logger } from "./logging/logger";
2222
import { maybeAskAgent, maybeAskUrl } from "./promptUtils";
2323
import { escapeCommandArg, toRemoteAuthority, toSafeHost } from "./util";

src/globalFlags.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/remote/remote.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ import {
2222
import { extractAgents } from "../api/api-helper";
2323
import { CoderApi } from "../api/coderApi";
2424
import { needToken } from "../api/utils";
25+
import { getGlobalFlags, shouldDisableAutostart } from "../cliConfig";
2526
import { type Commands } from "../commands";
2627
import { type CliManager } from "../core/cliManager";
2728
import * as cliUtils from "../core/cliUtils";
2829
import { type ServiceContainer } from "../core/container";
2930
import { type ContextManager } from "../core/contextManager";
3031
import { type PathResolver } from "../core/pathResolver";
3132
import { featureSetForVersion, type FeatureSet } from "../featureSet";
32-
import { getGlobalFlags } from "../globalFlags";
3333
import { Inbox } from "../inbox";
3434
import { type Logger } from "../logging/logger";
3535
import {
@@ -674,7 +674,7 @@ export class Remote {
674674
const globalConfigs = this.globalConfigs(label);
675675

676676
const proxyCommand = featureSet.wildcardSSH
677-
? `${escapeCommandArg(binaryPath)}${globalConfigs} ssh --stdio --usage-app=vscode --disable-autostart --network-info-dir ${escapeCommandArg(this.pathResolver.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
677+
? `${escapeCommandArg(binaryPath)}${globalConfigs} ssh --stdio --usage-app=vscode${this.disableAutostartConfig()} --network-info-dir ${escapeCommandArg(this.pathResolver.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
678678
: `${escapeCommandArg(binaryPath)}${globalConfigs} vscodessh --network-info-dir ${escapeCommandArg(
679679
this.pathResolver.getNetworkInfoPath(),
680680
)}${await this.formatLogArg(logDir)} --session-token-file ${escapeCommandArg(this.pathResolver.getSessionTokenPath(label))} --url-file ${escapeCommandArg(
@@ -741,6 +741,13 @@ export class Remote {
741741
return ` ${args.join(" ")}`;
742742
}
743743

744+
private disableAutostartConfig(): string {
745+
const configs = vscode.workspace.getConfiguration();
746+
return shouldDisableAutostart(configs, process.platform)
747+
? " --disable-autostart"
748+
: "";
749+
}
750+
744751
// showNetworkUpdates finds the SSH process ID that is being used by this
745752
// workspace and reads the file being created by the Coder CLI.
746753
private showNetworkUpdates(sshPid: number): vscode.Disposable {
Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { it, expect, describe } from "vitest";
22
import { type WorkspaceConfiguration } from "vscode";
33

4-
import { getGlobalFlags } from "@/globalFlags";
4+
import { getGlobalFlags, shouldDisableAutostart } from "@/cliConfig";
55

66
import { isWindows } from "../utils/platform";
77

@@ -86,3 +86,45 @@ function quoteCommand(value: string): string {
8686
const quote = isWindows() ? '"' : "'";
8787
return `${quote}${value}${quote}`;
8888
}
89+
90+
describe("shouldDisableAutostart", () => {
91+
const mockConfig = (setting: string) =>
92+
({
93+
get: (key: string) =>
94+
key === "coder.disableAutostart" ? setting : undefined,
95+
}) as unknown as WorkspaceConfiguration;
96+
97+
it("returns true when setting is 'always' regardless of platform", () => {
98+
const config = mockConfig("always");
99+
expect(shouldDisableAutostart(config, "darwin")).toBe(true);
100+
expect(shouldDisableAutostart(config, "linux")).toBe(true);
101+
expect(shouldDisableAutostart(config, "win32")).toBe(true);
102+
});
103+
104+
it("returns false when setting is 'never' regardless of platform", () => {
105+
const config = mockConfig("never");
106+
expect(shouldDisableAutostart(config, "darwin")).toBe(false);
107+
expect(shouldDisableAutostart(config, "linux")).toBe(false);
108+
expect(shouldDisableAutostart(config, "win32")).toBe(false);
109+
});
110+
111+
it("returns true when setting is 'auto' and platform is darwin", () => {
112+
const config = mockConfig("auto");
113+
expect(shouldDisableAutostart(config, "darwin")).toBe(true);
114+
});
115+
116+
it("returns false when setting is 'auto' and platform is not darwin", () => {
117+
const config = mockConfig("auto");
118+
expect(shouldDisableAutostart(config, "linux")).toBe(false);
119+
expect(shouldDisableAutostart(config, "win32")).toBe(false);
120+
expect(shouldDisableAutostart(config, "freebsd")).toBe(false);
121+
});
122+
123+
it("defaults to 'auto' when setting is not configured", () => {
124+
const config = {
125+
get: (_key: string, defaultValue: unknown) => defaultValue,
126+
} as unknown as WorkspaceConfiguration;
127+
expect(shouldDisableAutostart(config, "darwin")).toBe(true);
128+
expect(shouldDisableAutostart(config, "linux")).toBe(false);
129+
});
130+
});

0 commit comments

Comments
 (0)