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