src/vs/platform/agentHost/common/sshRemoteAgentHost.ts

285 LOC · 285 covered · 0 uncovered · 1 ranges · 100 concepts · 1 introducers · 62 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- sshRemoteAgentHostService.ts ×49
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Event } from '../../../base/common/event.js';
7 > import { IDisposable } from '../../../base/common/lifecycle.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { createDecorator } from '../../instantiation/common/instantiation.js';
10 > import type { IRelayMessage } from './relayTransport.js';
11 >
12 > export type { IRelayMessage } from './relayTransport.js';
13 >
14 > export const ISSHRemoteAgentHostService = createDecorator<ISSHRemoteAgentHostService>('sshRemoteAgentHostService');
15 >
16 > /**
17 > * IPC channel name for the main-process SSH service.
18 > */
19 > export const SSH_REMOTE_AGENT_HOST_CHANNEL = 'sshRemoteAgentHost';
20 >
21 > export const enum SSHAuthMethod {
22 > /** Use the local SSH agent for key-based auth. */
23 > Agent = 'agent',
24 > /** Authenticate with an explicit private key file. */
25 > KeyFile = 'keyFile',
26 > /** Authenticate with a password. */
27 > Password = 'password',
28 > }
29 >
30 > export interface ISSHAgentHostConfig {
31 > /** Remote hostname or IP. */
32 > readonly host: string;
33 > /** SSH port (default 22). */
34 > readonly port?: number;
35 > /** Username on the remote machine. */
36 > readonly username: string;
37 > /** Authentication method. */
38 > readonly authMethod: SSHAuthMethod;
39 > /** Path to the private key file (when {@link authMethod} is KeyFile). */
40 > readonly privateKeyPath?: string;
41 > /** Raw IdentityAgent value from resolved SSH config; may be a socket path, `none`, `SSH_AUTH_SOCK`, or an environment reference. */
42 > readonly identityAgent?: string;
43 > /** Password string (when {@link authMethod} is Password). */
44 > readonly password?: string;
45 > /** Display name for this connection. */
46 > readonly name: string;
47 > /** SSH config host alias (e.g. "robfast2") for reconnection on restart. */
48 > readonly sshConfigHost?: string;
49 > /** Dev override: custom command to start the remote agent host instead of the default CLI. */
50 > readonly remoteAgentHostCommand?: string;
51 > /** When true, enables OpenSSH agent forwarding ([email protected]) for this connection. Requires {@link authMethod} to be Agent. */
52 > readonly agentForward?: boolean;
53 > }
54 >
55 > /**
56 > * A sanitized view of the SSH config that omits secret material
57 > * (password, private key path). Exposed on active connections so
58 > * consumers can inspect connection metadata without accessing credentials.
59 > */
60 > export type ISSHAgentHostConfigSanitized = Omit<ISSHAgentHostConfig, 'password' | 'privateKeyPath'>;
61 >
62 > export interface ISSHAgentHostConnection extends IDisposable {
63 > /** The SSH config used to establish this connection (secrets stripped). */
64 > readonly config: ISSHAgentHostConfigSanitized;
65 > /** The connection address (e.g. `ssh:myhost` or `user@host:22`) registered with IRemoteAgentHostService. */
66 > readonly localAddress: string;
67 > /** The display name. */
68 > readonly name: string;
69 > /** Fires when this SSH connection is closed or lost. */
70 > readonly onDidClose: Event<void>;
71 > }
72 >
73 > /**
74 > * Manages SSH connections that bootstrap a remote agent host process.
75 > *
76 > * Each connection SSHs into a remote machine, ensures the VS Code CLI
77 > * is installed, starts `code agent-host`, and creates a WebSocket relay
78 > * over the SSH channel. Messages are forwarded between the renderer and
79 > * the remote agent host via IPC through the shared process.
80 > */
81 > export interface ISSHRemoteAgentHostService {
82 > readonly _serviceBrand: undefined;
83 >
84 > /** Fires when the set of active SSH connections changes. */
85 > readonly onDidChangeConnections: Event<void>;
86 >
87 > /** Progress messages during connect. */
88 > readonly onDidReportConnectProgress: Event<ISSHConnectProgress>;
89 >
90 > /** Currently active SSH-bootstrapped connections. */
91 > readonly connections: readonly ISSHAgentHostConnection[];
92 >
93 > /**
94 > * Bootstrap a remote agent host over SSH.
95 > *
96 > * 1. Opens an SSH connection to the remote host
97 > * 2. Downloads and installs the VS Code CLI if needed
98 > * 3. Starts `code agent-host`
99 > * 4. Creates a WebSocket relay over the SSH channel
100 > * 5. Registers the connection with {@link IRemoteAgentHostService}
101 > *
102 > * Resolves with the connection handle once the agent host is reachable.
103 > */
104 > connect(config: ISSHAgentHostConfig): Promise<ISSHAgentHostConnection>;
105 >
106 > /**
107 > * Disconnect an SSH-bootstrapped connection by host address.
108 > * Tears down the SSH tunnel, stops the remote agent host, and
109 > * removes the entry from {@link IRemoteAgentHostService}.
110 > */
111 > disconnect(host: string): Promise<void>;
112 >
113 > /** List SSH config host aliases (excluding wildcards). */
114 > listSSHConfigHosts(): Promise<string[]>;
115 >
116 > /**
117 > * Ensure `~/.ssh/config` exists (creating it with the right permissions if
118 > * missing) and return its URI. The parent `~/.ssh` directory is created
119 > * with mode 0700 and the config file with mode 0600 on POSIX systems.
120 > */
121 > ensureUserSSHConfig(): Promise<URI>;
122 >
123 > /**
124 > * List the known SSH configuration file URIs in priority order — typically the
125 > * per-user `~/.ssh/config` (always returned, even if it does not yet exist) and
126 > * the system-wide `/etc/ssh/ssh_config` (only when present on disk).
127 > */
128 > listSSHConfigFiles(): Promise<URI[]>;
129 >
130 > /** Resolve full SSH config for a host via `ssh -G`. */
131 > resolveSSHConfig(host: string): Promise<ISSHResolvedConfig>;
132 >
133 > /**
134 > * Re-establish an SSH tunnel on startup for a previously connected host.
135 > * Returns the new local forwarded address and registers it.
136 > */
137 > reconnect(sshConfigHost: string, name: string): Promise<ISSHAgentHostConnection>;
138 > }
139 > /**
140 > * Serializable result from a successful SSH connect operation.
141 > * Returned over IPC from the main process.
142 > */
143 > export interface ISSHConnectResult {
144 > /** Unique identifier for this connection's relay channel. */
145 > readonly connectionId: string;
146 > /** Display-friendly address (e.g. "ssh:robfast2"). */
147 > readonly address: string;
148 > readonly name: string;
149 > readonly connectionToken: string | undefined;
150 > readonly config: ISSHAgentHostConfigSanitized;
151 > /** SSH config host alias for reconnection on restart. */
152 > readonly sshConfigHost?: string;
153 > }
154 >
155 > /**
156 > * Resolved SSH configuration for a host, obtained from `ssh -G`.
157 > */
158 > export interface ISSHResolvedConfig {
159 > readonly hostname: string;
160 > readonly user: string | undefined;
161 > readonly port: number;
162 > readonly identityFile: string[];
163 > readonly identityAgent: string | undefined;
164 > readonly forwardAgent: boolean;
165 > }
166 >
167 > export interface ISSHConnectProgress {
168 > readonly connectionKey: string;
169 > readonly message: string;
170 > }
171 >
172 > /**
173 > * A single prompt within a keyboard-interactive authentication request.
174 > * Mirrors the shape ssh2 hands us — `echo: false` means the user input
175 > * should be hidden (typically a password).
176 > */
177 > export interface ISSHKeyboardInteractivePrompt {
178 > readonly prompt: string;
179 > readonly echo: boolean;
180 > }
181 >
182 > /**
183 > * Request from the main process for the renderer to gather responses to
184 > * a keyboard-interactive auth challenge from the SSH server. The renderer
185 > * is expected to respond with {@link ISSHRemoteAgentHostMainService.respondKeyboardInteractive}
186 > * within a reasonable time, or the underlying SSH connect attempt will time out.
187 > */
188 > export interface ISSHKeyboardInteractiveRequest {
189 > readonly requestId: string;
190 > readonly connectionKey: string;
191 > /** Display-friendly host (e.g. SSH config alias or `user@host`). */
192 > readonly displayHost: string;
193 > readonly username: string;
194 > /** Optional name field from the server (often empty). */
195 > readonly name: string;
196 > /** Optional instructions field from the server (often empty). */
197 > readonly instructions: string;
198 > readonly prompts: readonly ISSHKeyboardInteractivePrompt[];
199 > }
200 >
201 > /**
202 > * Main-process service that performs the actual SSH work.
203 > * The renderer calls this over IPC and handles registration
204 > * with {@link IRemoteAgentHostService} locally.
205 > */
206 > export const ISSHRemoteAgentHostMainService = createDecorator<ISSHRemoteAgentHostMainService>('sshRemoteAgentHostMainService');
207 >
208 > export interface ISSHRemoteAgentHostMainService {
209 > readonly _serviceBrand: undefined;
210 >
211 > /** Fires when the set of active SSH connections changes. */
212 > readonly onDidChangeConnections: Event<void>;
213 >
214 > /** Fires when a connection is closed from the shared process side. */
215 > readonly onDidCloseConnection: Event<string /* connectionId */>;
216 >
217 > /** Progress messages during connect (e.g. "Installing CLI..."). */
218 > readonly onDidReportConnectProgress: Event<ISSHConnectProgress>;
219 >
220 > /** Fires when a message is received from a remote agent host via the SSH relay. */
221 > readonly onDidRelayMessage: Event<IRelayMessage>;
222 >
223 > /** Fires when a relay connection to a remote agent host closes. */
224 > readonly onDidRelayClose: Event<string /* connectionId */>;
225 >
226 > /**
227 > * Fires when the SSH server requests keyboard-interactive auth (typically
228 > * a password prompt). The renderer must answer via {@link respondKeyboardInteractive}
229 > * with the same `requestId`, otherwise the auth attempt will hang until the
230 > * SSH `readyTimeout` elapses.
231 > */
232 > readonly onDidRequestKeyboardInteractive: Event<ISSHKeyboardInteractiveRequest>;
233 >
234 > /**
235 > * Fires when a previously requested keyboard-interactive prompt is no
236 > * longer needed (e.g. the underlying SSH connect attempt failed or was
237 > * aborted). The renderer should dismiss any UI it opened for `requestId`.
238 > */
239 > readonly onDidCancelKeyboardInteractive: Event<string /* requestId */>;
240 >
241 > /**
242 > * Provide responses for a previously fired keyboard-interactive request.
243 > * Pass `undefined` when the user cancels the prompt; this aborts the
244 > * owning SSH connection attempt.
245 > */
246 > respondKeyboardInteractive(requestId: string, responses: readonly string[] | undefined): Promise<void>;
247 >
248 > /**
249 > * Bootstrap a remote agent host over SSH. Returns serializable
250 > * connection info for the renderer to register.
251 > */
252 > connect(config: ISSHAgentHostConfig): Promise<ISSHConnectResult>;
253 >
254 > /**
255 > * Send a message to a remote agent host through the SSH relay.
256 > */
257 > relaySend(connectionId: string, message: string): Promise<void>;
258 >
259 > /**
260 > * Disconnect an SSH-bootstrapped connection by host address.
261 > */
262 > disconnect(host: string): Promise<void>;
263 >
264 > /** List SSH config host aliases (excluding wildcards). */
265 > listSSHConfigHosts(): Promise<string[]>;
266 >
267 > /**
268 > * Ensure `~/.ssh/config` exists (creating it with the right permissions if
269 > * missing) and return its URI.
270 > */
271 > ensureUserSSHConfig(): Promise<URI>;
272 >
273 > /** List the known SSH configuration file URIs (user config always included). */
274 > listSSHConfigFiles(): Promise<URI[]>;
275 >
276 > /** Resolve full SSH config for a host via `ssh -G`. */
277 > resolveSSHConfig(host: string): Promise<ISSHResolvedConfig>;
278 >
279 > /**
280 > * Re-establish an SSH tunnel for a previously connected host.
281 > * Resolves the SSH config alias, connects, and returns fresh
282 > * connection info with a new local forwarded port.
283 > */
284 > reconnect(sshConfigHost: string, name: string, remoteAgentHostCommand?: string, agentForward?: boolean): Promise<ISSHConnectResult>;
285 > }