-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy pathinstallExtensionTool.tsx
More file actions
106 lines (92 loc) · 4.71 KB
/
installExtensionTool.tsx
File metadata and controls
106 lines (92 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as l10n from '@vscode/l10n';
import type * as vscode from 'vscode';
import { IRunCommandExecutionService } from '../../../platform/commands/common/runCommandExecutionService';
import { IEnvService } from '../../../platform/env/common/envService';
import { IExtensionsService } from '../../../platform/extensions/common/extensionsService';
import { timeout } from '../../../util/vs/base/common/async';
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
import { StopWatch } from '../../../util/vs/base/common/stopwatch';
import { LanguageModelTextPart, LanguageModelToolResult, MarkdownString } from '../../../vscodeTypes';
import { ToolName } from '../common/toolNames';
import { ToolRegistry } from '../common/toolsRegistry';
import { IToolsService } from '../common/toolsService';
export interface IInstallExtensionToolInput {
id: string;
name: string;
}
class InstallExtensionTool implements vscode.LanguageModelTool<IInstallExtensionToolInput> {
public static readonly toolName = ToolName.InstallExtension;
constructor(
@IRunCommandExecutionService private readonly _commandService: IRunCommandExecutionService,
@IExtensionsService private readonly _extensionsService: IExtensionsService,
@IEnvService private readonly envService: IEnvService,
@IToolsService private readonly toolsService: IToolsService,
) { }
async invoke(options: vscode.LanguageModelToolInvocationOptions<IInstallExtensionToolInput>, token: CancellationToken): Promise<vscode.LanguageModelToolResult> {
const extensionId = options.input.id;
const existingExtension = this._extensionsService.getExtension(extensionId);
if (existingExtension) {
return new LanguageModelToolResult([new LanguageModelTextPart(`${options.input.name} extension is already installed`)]);
}
const insiders = this.envService.getEditorInfo().version.includes('insider');
const args = [extensionId, { enable: true, installPreReleaseVersion: insiders ? true : false }];
const exe = this._commandService.executeCommand('workbench.extensions.installExtension', ...args);
try {
await this.waitForExtensionInstall(exe, extensionId);
return new LanguageModelToolResult([new LanguageModelTextPart(`Installed ${options.input.name} extension successfully`)]);
} catch (error) {
return new LanguageModelToolResult([new LanguageModelTextPart(`Failed to install ${options.input.name} extension.`)]);
}
}
private async waitForExtensionInstall(prom: Promise<void>, extensionId: string) {
await prom;
let extension: vscode.Extension<any> | undefined;
const maxTime = 2_000;
const stopWatch = new StopWatch();
do {
extension = this._extensionsService.getExtension(extensionId);
if (extension) {
// If extension contributes any tools, then wait for the tools to be registered.
const languageModelTools = extension.packageJSON.contributes?.languageModelTools;
if (languageModelTools && Array.isArray(languageModelTools) && languageModelTools.length) {
if (languageModelTools.every((tool) => this.toolsService.getTool(tool.name))) {
return;
}
} else {
return;
}
}
await timeout(100);
} while (stopWatch.elapsed() < maxTime);
if (!extension) {
throw new Error(`Failed to install extension ${extensionId}.`);
}
}
async prepareInvocation(options: vscode.LanguageModelToolInvocationPrepareOptions<IInstallExtensionToolInput>, token: vscode.CancellationToken): Promise<vscode.PreparedToolInvocation> {
const extensionId = options.input.id;
if (!extensionId) {
throw new Error('No extension ID provided');
}
const existingExtension = this._extensionsService.getExtension(extensionId);
if (existingExtension) {
return {
invocationMessage: l10n.t`${options.input.name} extension is already installed`
};
}
const query = encodeURIComponent(JSON.stringify([[extensionId]]));
const markdownString = new MarkdownString(l10n.t(`Copilot will install the extension [{0}](command:workbench.extensions.action.showExtensionsWithIds?{1}) and its dependencies.`, options.input.name, query));
markdownString.isTrusted = { enabledCommands: ['workbench.extensions.action.showExtensionsWithIds'] };
return {
invocationMessage: l10n.t`Installing extension ${options.input.name}`,
confirmationMessages: {
title: l10n.t`Install Extension ${options.input.name}?`,
message: markdownString,
},
};
}
}
ToolRegistry.registerTool(InstallExtensionTool);