Path: blob/main/src/vs/platform/agentHost/common/tunnelAgentHost.ts
13394 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Event } from '../../../base/common/event.js';6import { createDecorator } from '../../instantiation/common/instantiation.js';7import type { IAgentHostSocketInfo } from './agentService.js';89export const ITunnelAgentHostService = createDecorator<ITunnelAgentHostService>('tunnelAgentHostService');1011/**12* IPC channel name for the shared-process tunnel service.13*/14export const TUNNEL_AGENT_HOST_CHANNEL = 'tunnelAgentHost';1516/** Configuration key for the list of manually configured tunnel names. */17export const TunnelAgentHostsSettingId = 'chat.remoteAgentTunnels';1819/** Minimum protocol version required for agent host connections. */20export const TUNNEL_MIN_PROTOCOL_VERSION = 5;2122/** Well-known port for the agent host on tunnel machines. */23export const TUNNEL_AGENT_HOST_PORT = 31546;2425/** Label used to identify VS Code server launcher tunnels. */26export const TUNNEL_LAUNCHER_LABEL = 'vscode-server-launcher';2728/** Address prefix for tunnel-backed connections (e.g. `tunnel:myTunnelId`). */29export const TUNNEL_ADDRESS_PREFIX = 'tunnel:';3031/** Prefix for protocol version tags. */32export const PROTOCOL_VERSION_TAG_PREFIX = 'protocolv';3334/**35* Parse tunnel tags to extract display name and protocol version.36* Follows the convention from the vscode-remote-tunnels SDK: the37* first label that is not `vscode-server-launcher`, does not start38* with `_`, and is not a `protocolvN` tag is the display name.39*/40export class TunnelTags {41public readonly protocolVersion: number = 2;42public readonly name: string | undefined;4344constructor(readonly value: readonly string[] | undefined) {45if (value) {46let protocolVersion: number | undefined;47let name: string | undefined;48for (const tag of value) {49if (tag.startsWith(PROTOCOL_VERSION_TAG_PREFIX)) {50const parsed = Number(tag.slice(PROTOCOL_VERSION_TAG_PREFIX.length));51if (!isNaN(parsed)) {52protocolVersion = parsed;53}54} else if (!tag.startsWith('_') && tag !== TUNNEL_LAUNCHER_LABEL && !name) {55name = tag;56}57}58if (protocolVersion !== undefined) {59this.protocolVersion = protocolVersion;60}61if (name !== undefined) {62this.name = name;63}64}65}66}6768/** A recently used tunnel cached in storage. */69export interface ICachedTunnel {70readonly tunnelId: string;71readonly clusterId: string;72readonly name: string;73readonly authProvider?: 'github' | 'microsoft';74}7576/** Information about a discovered dev tunnel with an agent host. */77export interface ITunnelInfo {78/** The tunnel's unique identifier. */79readonly tunnelId: string;80/** The cluster region where the tunnel is hosted. */81readonly clusterId: string;82/** Display name derived from tunnel tags or tunnel name. */83readonly name: string;84/** All tags/labels on the tunnel. */85readonly tags: readonly string[];86/** Parsed protocol version from tags. */87readonly protocolVersion: number;88/** Number of hosts currently accepting connections (0 = offline). */89readonly hostConnectionCount: number;90}9192/**93* Serializable result from a successful tunnel connect operation.94* Returned over IPC from the shared process.95*/96export interface ITunnelConnectResult {97/** Unique identifier for this connection's relay channel. */98readonly connectionId: string;99/** Display-friendly address (e.g. "tunnel:myTunnel"). */100readonly address: string;101/** Display name for the tunnel. */102readonly name: string;103/** Connection token derived from the tunnel ID. */104readonly connectionToken: string;105}106107/**108* A message relayed from a remote agent host through the tunnel.109* The shared process acts as a WebSocket proxy, forwarding JSON110* messages bidirectionally between the tunnel and the renderer via IPC.111*/112export interface ITunnelRelayMessage {113readonly connectionId: string;114readonly data: string;115}116117/**118* Main-process (shared process) service that manages dev tunnel119* connections. The renderer calls this over IPC and handles registration120* with {@link IRemoteAgentHostService} locally.121*/122export const ITunnelAgentHostMainService = createDecorator<ITunnelAgentHostMainService>('tunnelAgentHostMainService');123124export interface ITunnelAgentHostMainService {125readonly _serviceBrand: undefined;126127/** Fires when a message is received from a remote agent host via the tunnel relay. */128readonly onDidRelayMessage: Event<ITunnelRelayMessage>;129130/** Fires when a relay connection to a remote agent host closes. */131readonly onDidRelayClose: Event<string /* connectionId */>;132133/**134* List dev tunnels associated with the user's account that have135* the `vscode-server-launcher` label and a protocol version tag136* of at least {@link TUNNEL_MIN_PROTOCOL_VERSION}.137*138* @param token The user's access token (GitHub or Microsoft).139* @param authProvider The auth provider that issued the token.140* @param additionalTunnelNames Optional tunnel names to look up141* in addition to the account-wide enumeration.142*/143listTunnels(token: string, authProvider: 'github' | 'microsoft', additionalTunnelNames?: string[]): Promise<ITunnelInfo[]>;144145/**146* Connect to a tunnel's agent host via the dev tunnels relay and147* begin relaying WebSocket messages through IPC.148*149* @param token The user's access token (GitHub or Microsoft).150* @param authProvider The auth provider that issued the token.151* @param tunnelId The tunnel ID to connect to.152* @param clusterId The cluster region of the tunnel.153*/154connect(token: string, authProvider: 'github' | 'microsoft', tunnelId: string, clusterId: string): Promise<ITunnelConnectResult>;155156/**157* Send a message to a remote agent host through the tunnel relay.158*/159relaySend(connectionId: string, message: string): Promise<void>;160161/**162* Disconnect a tunnel relay connection.163*/164disconnect(connectionId: string): Promise<void>;165}166167/**168* Renderer-side service that manages dev tunnel agent host connections.169* Uses the shared-process {@link ITunnelAgentHostMainService} for170* actual tunnel SDK operations and registers connections with171* {@link IRemoteAgentHostService}.172*/173export interface ITunnelAgentHostService {174readonly _serviceBrand: undefined;175176/** Fires when the set of available tunnels changes. */177readonly onDidChangeTunnels: Event<void>;178179/**180* Enumerate available dev tunnels with agent host support.181* When {@link options.silent} is `true`, uses cached tokens without182* prompting the user. Returns an empty array if no cached token.183*/184listTunnels(options?: { silent?: boolean }): Promise<ITunnelInfo[]>;185186/**187* Connect to a tunnel's agent host and register the connection188* with {@link IRemoteAgentHostService}.189*190* @param tunnel The tunnel to connect to.191* @param authProvider Optional auth provider to use. If omitted, uses cached/last known.192*/193connect(tunnel: ITunnelInfo, authProvider?: 'github' | 'microsoft'): Promise<void>;194195/**196* Disconnect from a tunnel agent host.197*/198disconnect(address: string): Promise<void>;199200/** Get the list of recently used (cached) tunnels. */201getCachedTunnels(): ICachedTunnel[];202203/** Cache a tunnel as recently used. */204cacheTunnel(tunnel: ITunnelInfo, authProvider?: 'github' | 'microsoft'): void;205206/** Remove a tunnel from the cache. */207removeCachedTunnel(tunnelId: string): void;208209/**210* Determine which auth provider has an existing cached session.211* When {@link silent} is true, does not prompt the user.212* Returns `undefined` if no cached session is available.213*/214getAuthProvider(options?: { silent?: boolean }): Promise<'github' | 'microsoft' | undefined>;215}216217// ---- Tunnel hosting (exposing the local agent host to remote clients) --------218219/** IPC channel name for the tunnel host service. */220export const TUNNEL_HOST_CHANNEL = 'tunnelHost';221222/** Output channel ID for the tunnel host logs. */223export const TUNNEL_HOST_LOG_ID = 'tunnelHostService';224225/** Information about an actively hosted tunnel. */226export interface ITunnelHostInfo {227readonly tunnelName: string;228readonly tunnelId: string;229readonly clusterId: string;230readonly domain: string;231}232233/** Status of the tunnel host. */234export type TunnelHostStatus =235| { readonly active: false }236| { readonly active: true; readonly info: ITunnelHostInfo };237238/**239* Shared-process service that hosts a dev tunnel using `TunnelRelayTunnelHost`240* and pipes incoming connections to the local agent host.241*/242export const ITunnelAgentHostHostingService = createDecorator<ITunnelAgentHostHostingService>('tunnelAgentHostHostingService');243244export interface ITunnelAgentHostHostingService {245readonly _serviceBrand: undefined;246247/** Fires when the hosting status changes. */248readonly onDidChangeStatus: Event<TunnelHostStatus>;249250/**251* Start hosting a dev tunnel that forwards connections to the local252* agent host. Creates a tunnel with the appropriate labels and port253* configuration, then connects a `TunnelRelayTunnelHost`.254*255* @param token The user's access token.256* @param authProvider The auth provider that issued the token.257* @param socketInfo Socket path for the local agent host.258*/259startHosting(token: string, authProvider: 'github' | 'microsoft', socketInfo: IAgentHostSocketInfo): Promise<ITunnelHostInfo>;260261/** Stop hosting and clean up the tunnel. */262stopHosting(): Promise<void>;263264/** Get the current hosting status. */265getStatus(): Promise<TunnelHostStatus>;266}267268269