Skip to content

Commit ead3b59

Browse files
committed
feat: add "Apply Recommended SSH Settings" command and set defaults for Cursor/reconnection
Add a command palette entry (Coder: Apply Recommended SSH Settings) that writes all four recommended SSH settings globally. Also automatically set serverShutdownTimeout (Cursor) and maxReconnectionAttempts when undefined during connection setup, matching the existing reconnectionGraceTime behavior.
1 parent 5b09d2b commit ead3b59

File tree

5 files changed

+84
-12
lines changed

5 files changed

+84
-12
lines changed

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@
320320
"title": "Refresh Tasks",
321321
"category": "Coder",
322322
"icon": "$(refresh)"
323+
},
324+
{
325+
"command": "coder.applyRecommendedSettings",
326+
"title": "Apply Recommended SSH Settings",
327+
"category": "Coder"
323328
}
324329
],
325330
"menus": {
@@ -386,6 +391,9 @@
386391
{
387392
"command": "coder.tasks.refresh",
388393
"when": "false"
394+
},
395+
{
396+
"command": "coder.applyRecommendedSettings"
389397
}
390398
],
391399
"view/title": [

src/commands.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { type Logger } from "./logging/logger";
2424
import { type LoginCoordinator } from "./login/loginCoordinator";
2525
import { withProgress } from "./progress";
2626
import { maybeAskAgent, maybeAskUrl } from "./promptUtils";
27+
import { RECOMMENDED_SSH_SETTINGS } from "./remote/userSettings";
2728
import { escapeCommandArg, toRemoteAuthority, toSafeHost } from "./util";
2829
import { vscodeProposed } from "./vscodeProposed";
2930
import {
@@ -310,6 +311,25 @@ export class Commands {
310311
}
311312
}
312313

314+
/**
315+
* Apply recommended SSH settings for reliable Coder workspace connections.
316+
*/
317+
public async applyRecommendedSettings(): Promise<void> {
318+
const config = vscode.workspace.getConfiguration();
319+
const entries = Object.entries(RECOMMENDED_SSH_SETTINGS);
320+
for (const [key, setting] of entries) {
321+
await config.update(
322+
key,
323+
setting.value,
324+
vscode.ConfigurationTarget.Global,
325+
);
326+
}
327+
const summary = entries.map(([, s]) => s.label).join(", ");
328+
vscode.window.showInformationMessage(
329+
`Applied recommended SSH settings: ${summary}`,
330+
);
331+
}
332+
313333
/**
314334
* Create a new workspace for the currently logged-in deployment.
315335
*

src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,10 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
271271
"coder.manageCredentials",
272272
commands.manageCredentials.bind(commands),
273273
),
274+
vscode.commands.registerCommand(
275+
"coder.applyRecommendedSettings",
276+
commands.applyRecommendedSettings.bind(commands),
277+
),
274278
);
275279

276280
const remote = new Remote(serviceContainer, commands, ctx);

src/remote/userSettings.ts

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,30 @@ export interface SettingOverride {
1010
value: unknown;
1111
}
1212

13+
interface RecommendedSetting {
14+
readonly value: number | null;
15+
readonly label: string;
16+
}
17+
18+
export const RECOMMENDED_SSH_SETTINGS = {
19+
"remote.SSH.connectTimeout": {
20+
value: 1800,
21+
label: "Connect Timeout: 1800s (30 min)",
22+
},
23+
"remote.SSH.reconnectionGraceTime": {
24+
value: 28800,
25+
label: "Reconnection Grace Time: 28800s (8 hours)",
26+
},
27+
"remote.SSH.serverShutdownTimeout": {
28+
value: 28800,
29+
label: "Server Shutdown Timeout: 28800s (8 hours)",
30+
},
31+
"remote.SSH.maxReconnectionAttempts": {
32+
value: null,
33+
label: "Max Reconnection Attempts: max allowed",
34+
},
35+
} as const satisfies Record<string, RecommendedSetting>;
36+
1337
/**
1438
* Build the list of VS Code setting overrides needed for a remote SSH
1539
* connection to a Coder workspace.
@@ -21,7 +45,7 @@ export function buildSshOverrides(
2145
): SettingOverride[] {
2246
const overrides: SettingOverride[] = [];
2347

24-
// Bypass the platform prompt by setting the remote platform for this host.
48+
// Set the remote platform for this host to bypass the platform prompt.
2549
const remotePlatforms = config.get<Record<string, string>>(
2650
"remote.SSH.remotePlatform",
2751
{},
@@ -33,9 +57,9 @@ export function buildSshOverrides(
3357
});
3458
}
3559

36-
// VS Code's default connect timeout of 15s is too short when waiting for
37-
// startup scripts. Enforce a minimum.
38-
const minConnTimeout = 1800;
60+
// Default 15s is too short for startup scripts; enforce a minimum.
61+
const minConnTimeout =
62+
RECOMMENDED_SSH_SETTINGS["remote.SSH.connectTimeout"].value;
3963
const connTimeout = config.get<number>("remote.SSH.connectTimeout");
4064
if (!connTimeout || connTimeout < minConnTimeout) {
4165
overrides.push({
@@ -44,14 +68,16 @@ export function buildSshOverrides(
4468
});
4569
}
4670

47-
// VS Code's default reconnection grace time (ProtocolConstants.ReconnectionGraceTime)
48-
// is 3 hours (10800s). Coder workspaces commonly go offline overnight, so we
49-
// bump to 8 hours. See https://github.com/microsoft/vscode/blob/main/src/vs/base/parts/ipc/common/ipc.net.ts
50-
if (config.get<number>("remote.SSH.reconnectionGraceTime") === undefined) {
51-
overrides.push({
52-
key: "remote.SSH.reconnectionGraceTime",
53-
value: 28800, // 8 hours in seconds
54-
});
71+
// Set recommended defaults for settings the user hasn't configured.
72+
const setIfUndefined = [
73+
"remote.SSH.reconnectionGraceTime",
74+
"remote.SSH.serverShutdownTimeout",
75+
"remote.SSH.maxReconnectionAttempts",
76+
] as const;
77+
for (const key of setIfUndefined) {
78+
if (config.get(key) === undefined) {
79+
overrides.push({ key, value: RECOMMENDED_SSH_SETTINGS[key].value });
80+
}
5581
}
5682

5783
return overrides;

test/unit/remote/userSettings.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,25 @@ describe("buildSshOverrides", () => {
124124
});
125125
});
126126

127+
it.each([
128+
{ key: "remote.SSH.serverShutdownTimeout", expected: 28800 },
129+
{ key: "remote.SSH.maxReconnectionAttempts", expected: null },
130+
])("defaults $key when not configured", ({ key, expected }) => {
131+
const overrides = buildSshOverrides(
132+
new MockConfigurationProvider(),
133+
"host",
134+
"linux",
135+
);
136+
expect(findOverride(overrides, key)).toBe(expected);
137+
});
138+
127139
it("produces no overrides when all settings are already correct", () => {
128140
const config = new MockConfigurationProvider();
129141
config.set("remote.SSH.remotePlatform", { "my-host": "linux" });
130142
config.set("remote.SSH.connectTimeout", 3600);
131143
config.set("remote.SSH.reconnectionGraceTime", 7200);
144+
config.set("remote.SSH.serverShutdownTimeout", 600);
145+
config.set("remote.SSH.maxReconnectionAttempts", 4);
132146
expect(buildSshOverrides(config, "my-host", "linux")).toHaveLength(0);
133147
});
134148
});

0 commit comments

Comments
 (0)