11import { isAxiosError } from "axios" ;
2- import { type Api } from "coder/site/src/api/api" ;
3- import {
4- type Workspace ,
5- type WorkspaceAgent ,
6- } from "coder/site/src/api/typesGenerated" ;
72import * as fs from "node:fs/promises" ;
83import * as os from "node:os" ;
94import * as path from "node:path" ;
@@ -20,18 +15,11 @@ import { extractAgents } from "../api/api-helper";
2015import { AuthInterceptor } from "../api/authInterceptor" ;
2116import { CoderApi } from "../api/coderApi" ;
2217import { needToken } from "../api/utils" ;
23- import { type Commands } from "../commands" ;
2418import {
2519 CONFIG_CHANGE_DEBOUNCE_MS ,
2620 watchConfigurationChanges ,
2721} from "../configWatcher" ;
2822import { version as cliVersion } from "../core/cliExec" ;
29- import { type CliManager } from "../core/cliManager" ;
30- import { type ServiceContainer } from "../core/container" ;
31- import { type ContextManager } from "../core/contextManager" ;
32- import { type StartupMode } from "../core/mementoManager" ;
33- import { type PathResolver } from "../core/pathResolver" ;
34- import { type SecretsManager } from "../core/secretsManager" ;
3523import { toError } from "../error/errorUtils" ;
3624import { featureSetForVersion , type FeatureSet } from "../featureSet" ;
3725import { Inbox } from "../inbox" ;
@@ -40,8 +28,6 @@ import {
4028 RemoteSetupTelemetry ,
4129 type RemoteSetupTracer ,
4230} from "../instrumentation/remoteSetup" ;
43- import { type Logger } from "../logging/logger" ;
44- import { type LoginCoordinator } from "../login/loginCoordinator" ;
4531import { OAuthSessionManager } from "../oauth/sessionManager" ;
4632import {
4733 type CliAuth ,
@@ -58,12 +44,11 @@ import {
5844 expandPath ,
5945 parseRemoteAuthority ,
6046} from "../util" ;
61- import { showDismissibleNotification } from "../util/notifications" ;
6247import { vscodeProposed } from "../vscodeProposed" ;
6348import { WorkspaceMonitor } from "../workspace/workspaceMonitor" ;
6449
6550import {
66- applySshProxyEnvironment ,
51+ applySshEnvironment ,
6752 getSshProxyEnvironment ,
6853 SSH_PROXY_SETTINGS ,
6954} from "./environment" ;
@@ -79,6 +64,23 @@ import { SshProcessMonitor } from "./sshProcess";
7964import { computeSshProperties , sshSupportsSetEnv } from "./sshSupport" ;
8065import { WorkspaceStateMachine } from "./workspaceStateMachine" ;
8166
67+ import type { Api } from "coder/site/src/api/api" ;
68+ import type {
69+ Workspace ,
70+ WorkspaceAgent ,
71+ } from "coder/site/src/api/typesGenerated" ;
72+
73+ import type { Commands } from "../commands" ;
74+ import type { CliManager } from "../core/cliManager" ;
75+ import type { ServiceContainer } from "../core/container" ;
76+ import type { ContextManager } from "../core/contextManager" ;
77+ import type { DismissibleNotifier } from "../core/dismissibleNotifier" ;
78+ import type { StartupMode } from "../core/mementoManager" ;
79+ import type { PathResolver } from "../core/pathResolver" ;
80+ import type { SecretsManager } from "../core/secretsManager" ;
81+ import type { Logger } from "../logging/logger" ;
82+ import type { LoginCoordinator } from "../login/loginCoordinator" ;
83+
8284export interface RemoteDetails extends vscode . Disposable {
8385 safeHostname : string ;
8486 url : string ;
@@ -109,6 +111,7 @@ export class Remote {
109111 private readonly contextManager : ContextManager ;
110112 private readonly secretsManager : SecretsManager ;
111113 private readonly loginCoordinator : LoginCoordinator ;
114+ private readonly dismissibleNotifier : DismissibleNotifier ;
112115 private readonly setupTelemetry : RemoteSetupTelemetry ;
113116 private readonly authTelemetry : AuthTelemetry ;
114117
@@ -123,6 +126,7 @@ export class Remote {
123126 this . contextManager = serviceContainer . getContextManager ( ) ;
124127 this . secretsManager = serviceContainer . getSecretsManager ( ) ;
125128 this . loginCoordinator = serviceContainer . getLoginCoordinator ( ) ;
129+ this . dismissibleNotifier = serviceContainer . getDismissibleNotifier ( ) ;
126130 this . setupTelemetry = new RemoteSetupTelemetry (
127131 serviceContainer . getTelemetryService ( ) ,
128132 ) ;
@@ -209,9 +213,9 @@ export class Remote {
209213
210214 try {
211215 disposables . push (
212- applySshProxyEnvironment ( baseUrl , vscode . workspace . getConfiguration ( ) ) ,
216+ applySshEnvironment ( baseUrl , vscode . workspace . getConfiguration ( ) ) ,
213217 ) ;
214- void this . warnIfProxyEnvNotInherited ( baseUrl ) ;
218+ await this . warnIfProxyEnvNotInherited ( baseUrl ) ;
215219 // Create OAuth session manager for this remote deployment
216220 const remoteOAuthManager = OAuthSessionManager . create (
217221 { url : baseUrl , safeHostname : parts . safeHostname } ,
@@ -837,7 +841,10 @@ export class Remote {
837841 * MS VS Code with `remote.SSH.useLocalServer=false` spawns ssh without
838842 * inheriting process.env, so the proxy variables never reach it. Warn once and
839843 * offer to enable the local server when a proxy applies to this deployment.
840- * Catches internally so the caller can safely fire-and-forget.
844+ *
845+ * Blocks setup with a modal: the write must land before ssh spawns (which
846+ * happens after setup returns) for it to apply without a reload. Catches
847+ * internally so a failure here never aborts the connection.
841848 */
842849 private async warnIfProxyEnvNotInherited ( baseUrl : string ) : Promise < void > {
843850 try {
@@ -851,27 +858,24 @@ export class Remote {
851858 }
852859
853860 const ENABLE = "Enable Local Server" ;
854- const choice = await showDismissibleNotification (
861+ const choice = await this . dismissibleNotifier . showDismissible (
862+ "coder.proxyUseLocalServerWarningDismissed" ,
855863 "Your proxy settings may not reach the SSH connection because `remote.SSH.useLocalServer` is disabled. Enable it so Coder can apply the proxy to the connection." ,
856- this . extensionContext . globalState ,
857- { key : "coder.proxyUseLocalServerWarningDismissed" , actions : [ ENABLE ] } ,
864+ { actions : [ ENABLE ] , modal : true } ,
858865 ) ;
859866 if ( choice !== ENABLE ) {
860867 return ;
861868 }
862869
863- await cfg . update (
864- "remote.SSH.useLocalServer" ,
865- true ,
866- vscode . ConfigurationTarget . Global ,
867- ) ;
868- const RELOAD = "Reload" ;
869- const reload = await vscode . window . showInformationMessage (
870- "Local server enabled. Reload the window to apply." ,
871- RELOAD ,
870+ // Use the jsonc writer, not cfg.update which can hang during remote setup.
871+ // No reload needed: ssh hasn't spawned yet, so it picks this up on connect.
872+ const ok = await applySettingOverrides (
873+ this . pathResolver . getUserSettingsPath ( ) ,
874+ [ { key : "remote.SSH.useLocalServer" , value : true } ] ,
875+ this . logger ,
872876 ) ;
873- if ( reload === RELOAD ) {
874- await vscode . commands . executeCommand ( "workbench.action.reloadWindow ") ;
877+ if ( ! ok ) {
878+ this . logger . warn ( "Failed to enable remote.SSH.useLocalServer ") ;
875879 }
876880 } catch ( error ) {
877881 this . logger . debug (
0 commit comments