Path: blob/main/src/vs/sessions/contrib/remoteAgentHost/browser/manageRemoteAgentHosts.ts
13401 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 { Codicon } from '../../../../base/common/codicons.js';6import { DisposableStore } from '../../../../base/common/lifecycle.js';7import { ThemeIcon } from '../../../../base/common/themables.js';8import { autorun } from '../../../../base/common/observable.js';9import { localize, localize2 } from '../../../../nls.js';10import { Action2, IMenuService, MenuItemAction, registerAction2 } from '../../../../platform/actions/common/actions.js';11import { ICommandService } from '../../../../platform/commands/common/commands.js';12import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';13import { ServicesAccessor, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';14import { IRemoteAgentHostService, RemoteAgentHostsEnabledSettingId } from '../../../../platform/agentHost/common/remoteAgentHostService.js';15import { TUNNEL_ADDRESS_PREFIX } from '../../../../platform/agentHost/common/tunnelAgentHost.js';16import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js';17import { Menus } from '../../../browser/menus.js';18import { SessionsCategories } from '../../../common/categories.js';19import { IAgentHostSessionsProvider, isAgentHostProvider } from '../../../common/agentHostSessionsProvider.js';20import { ISessionsProvidersService } from '../../../services/sessions/browser/sessionsProvidersService.js';21import { getStatusLabel, showRemoteHostOptions } from './remoteHostOptions.js';22import { RemoteAgentHostCommandIds } from './remoteAgentHostActions.js';2324interface IRemoteHostQuickPickItem extends IQuickPickItem {25readonly kind: 'remote';26readonly provider: IAgentHostSessionsProvider;27}2829interface IMenuActionQuickPickItem extends IQuickPickItem {30readonly kind: 'menu-action';31readonly action: MenuItemAction;32}3334type ManageHostsPickItem = IRemoteHostQuickPickItem | IMenuActionQuickPickItem;3536registerAction2(class extends Action2 {37constructor() {38super({39id: RemoteAgentHostCommandIds.manageRemoteAgentHosts,40title: localize2('manageRemoteAgentHosts', "Manage Remote Agent Hosts..."),41category: SessionsCategories.Sessions,42f1: true,43precondition: ContextKeyExpr.equals(`config.${RemoteAgentHostsEnabledSettingId}`, true),44});45}4647override async run(accessor: ServicesAccessor): Promise<void> {48const quickInputService = accessor.get(IQuickInputService);49const sessionsProvidersService = accessor.get(ISessionsProvidersService);50const remoteAgentHostService = accessor.get(IRemoteAgentHostService);51const menuService = accessor.get(IMenuService);52const contextKeyService = accessor.get(IContextKeyService);53const commandService = accessor.get(ICommandService);54const instantiationService = accessor.get(IInstantiationService);5556const removeButton: IQuickInputButton = {57iconClass: ThemeIcon.asClassName(Codicon.close),58tooltip: localize('manageHosts.removeTooltip', "Remove"),59};6061const buildItems = (): (ManageHostsPickItem | IQuickPickSeparator)[] => {62const remoteProviders: IAgentHostSessionsProvider[] = sessionsProvidersService.getProviders()63.filter(isAgentHostProvider)64.filter((p: IAgentHostSessionsProvider) => !!p.remoteAddress);6566const remoteItems: IRemoteHostQuickPickItem[] = remoteProviders.map((p: IAgentHostSessionsProvider) => {67const isTunnel = p.remoteAddress?.startsWith(TUNNEL_ADDRESS_PREFIX);68const status = p.connectionStatus?.get();69const item: IRemoteHostQuickPickItem = {70kind: 'remote',71provider: p,72label: `$(${isTunnel ? 'cloud' : 'remote'}) ${p.label}`,73description: status !== undefined ? getStatusLabel(status) : undefined,74detail: p.remoteAddress,75};76if (!isTunnel) {77(item as IRemoteHostQuickPickItem & { buttons?: IQuickInputButton[] }).buttons = [removeButton];78}79return item;80});8182const menuActionItems: IMenuActionQuickPickItem[] = [];83const menuActions = menuService.getMenuActions(Menus.SessionWorkspaceManage, contextKeyService, { renderShortTitle: true });84for (const [, actions] of menuActions) {85for (const action of actions) {86if (action instanceof MenuItemAction) {87const icon = ThemeIcon.isThemeIcon(action.item.icon) ? action.item.icon : undefined;88menuActionItems.push({89kind: 'menu-action',90action,91label: icon ? `$(${icon.id}) ${action.label}` : action.label,92description: action.tooltip || undefined,93});94}95}96}9798const items: (ManageHostsPickItem | IQuickPickSeparator)[] = [];99if (remoteItems.length > 0) {100items.push({ type: 'separator', label: localize('manageHosts.remoteHostsHeader', "Remote Agent Hosts") });101items.push(...remoteItems);102}103if (menuActionItems.length > 0) {104items.push({ type: 'separator', label: localize('manageHosts.actionsHeader', "Add or Manage") });105items.push(...menuActionItems);106}107return items;108};109110const showManagePicker = () => {111const store = new DisposableStore();112const picker = quickInputService.createQuickPick<ManageHostsPickItem>({ useSeparators: true });113store.add(picker);114picker.title = localize('manageHosts.title', "Manage Remote Agent Hosts");115picker.placeholder = localize('manageHosts.placeholder', "Select a remote to manage or pick an action");116picker.matchOnDescription = true;117picker.matchOnDetail = true;118119let lastFilter = '';120const refresh = () => {121lastFilter = picker.value;122picker.items = buildItems();123picker.value = lastFilter;124};125refresh();126127// Refresh when providers/connection status change128store.add(sessionsProvidersService.onDidChangeProviders(() => refresh()));129const observerStore = store.add(new DisposableStore());130const subscribeToProviders = () => {131observerStore.clear();132for (const p of sessionsProvidersService.getProviders()) {133if (isAgentHostProvider(p) && p.connectionStatus) {134observerStore.add(autorun(reader => {135p.connectionStatus!.read(reader);136refresh();137}));138}139}140};141subscribeToProviders();142store.add(sessionsProvidersService.onDidChangeProviders(() => subscribeToProviders()));143144store.add(picker.onDidTriggerItemButton(async e => {145if (e.item.kind === 'remote' && e.button === removeButton) {146const address = e.item.provider.remoteAddress;147if (address) {148await remoteAgentHostService.removeRemoteAgentHost(address);149// onDidChangeProviders will refresh150}151}152}));153154store.add(picker.onDidAccept(() => {155const selected = picker.selectedItems[0];156picker.hide();157if (!selected) {158return;159}160if (selected.kind === 'remote') {161void instantiationService.invokeFunction(a => showRemoteHostOptions(a, selected.provider, { showBackButton: true })).then(result => {162if (result === 'back') {163showManagePicker();164}165});166} else if (selected.kind === 'menu-action') {167commandService.executeCommand(selected.action.id, () => showManagePicker());168}169}));170171store.add(picker.onDidHide(() => store.dispose()));172picker.show();173};174175showManagePicker();176}177});178179180