@vybestack/llxprt-code-ide-integration 0.10.0-nightly.260613.1adad3b34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.last_build +0 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/src/ide/constants.d.ts +6 -0
- package/dist/src/ide/constants.js +7 -0
- package/dist/src/ide/constants.js.map +1 -0
- package/dist/src/ide/detect-ide.d.ts +64 -0
- package/dist/src/ide/detect-ide.js +78 -0
- package/dist/src/ide/detect-ide.js.map +1 -0
- package/dist/src/ide/ide-client.d.ts +77 -0
- package/dist/src/ide/ide-client.js +440 -0
- package/dist/src/ide/ide-client.js.map +1 -0
- package/dist/src/ide/ide-installer.d.ts +14 -0
- package/dist/src/ide/ide-installer.js +116 -0
- package/dist/src/ide/ide-installer.js.map +1 -0
- package/dist/src/ide/ideContext.d.ts +415 -0
- package/dist/src/ide/ideContext.js +162 -0
- package/dist/src/ide/ideContext.js.map +1 -0
- package/dist/src/ide/index.d.ts +11 -0
- package/dist/src/ide/index.js +12 -0
- package/dist/src/ide/index.js.map +1 -0
- package/dist/src/ide/process-utils.d.ts +21 -0
- package/dist/src/ide/process-utils.js +198 -0
- package/dist/src/ide/process-utils.js.map +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +9 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lsp/index.d.ts +7 -0
- package/dist/src/lsp/index.js +7 -0
- package/dist/src/lsp/index.js.map +1 -0
- package/dist/src/lsp/lsp-service-client.d.ts +37 -0
- package/dist/src/lsp/lsp-service-client.js +415 -0
- package/dist/src/lsp/lsp-service-client.js.map +1 -0
- package/dist/src/lsp/types.d.ts +45 -0
- package/dist/src/lsp/types.js +3 -0
- package/dist/src/lsp/types.js.map +1 -0
- package/dist/src/utils/paths.d.ts +13 -0
- package/dist/src/utils/paths.js +24 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'node:fs';
|
|
7
|
+
import { isSubpath } from '../utils/paths.js';
|
|
8
|
+
import { detectIde, IDE_DEFINITIONS } from './detect-ide.js';
|
|
9
|
+
import { ideContext, IdeContextNotificationSchema, IdeDiffAcceptedNotificationSchema, IdeDiffRejectedNotificationSchema, IdeDiffClosedNotificationSchema, CloseDiffResponseSchema, } from './ideContext.js';
|
|
10
|
+
import { getIdeProcessInfo } from './process-utils.js';
|
|
11
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
12
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { EnvHttpProxyAgent, } from 'undici';
|
|
16
|
+
import { debugLogger } from '@vybestack/llxprt-code-telemetry/utils/debugLogger.js';
|
|
17
|
+
const logger = {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
debug: (...args) => debugLogger.debug('[DEBUG] [IDEClient]', ...args),
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
error: (...args) => debugLogger.error('[ERROR] [IDEClient]', ...args),
|
|
22
|
+
};
|
|
23
|
+
export var IDEConnectionStatus;
|
|
24
|
+
(function (IDEConnectionStatus) {
|
|
25
|
+
IDEConnectionStatus["Connected"] = "connected";
|
|
26
|
+
IDEConnectionStatus["Disconnected"] = "disconnected";
|
|
27
|
+
IDEConnectionStatus["Connecting"] = "connecting";
|
|
28
|
+
})(IDEConnectionStatus || (IDEConnectionStatus = {}));
|
|
29
|
+
function getRealPath(path) {
|
|
30
|
+
try {
|
|
31
|
+
return fs.realpathSync(path);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Path doesn't exist; return original path.
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Manages the connection to and interaction with the IDE server.
|
|
40
|
+
*/
|
|
41
|
+
export class IdeClient {
|
|
42
|
+
static instance;
|
|
43
|
+
client = undefined;
|
|
44
|
+
state = {
|
|
45
|
+
status: IDEConnectionStatus.Disconnected,
|
|
46
|
+
details: 'IDE integration is currently disabled. To enable it, run /ide enable.',
|
|
47
|
+
};
|
|
48
|
+
currentIde;
|
|
49
|
+
ideProcessInfo;
|
|
50
|
+
connectionConfig;
|
|
51
|
+
authToken;
|
|
52
|
+
diffResponses = new Map();
|
|
53
|
+
statusListeners = new Set();
|
|
54
|
+
trustChangeListeners = new Set();
|
|
55
|
+
constructor() { }
|
|
56
|
+
static async getInstance() {
|
|
57
|
+
if (!IdeClient.instance) {
|
|
58
|
+
const client = new IdeClient();
|
|
59
|
+
client.ideProcessInfo = await getIdeProcessInfo();
|
|
60
|
+
client.connectionConfig = await client.getConnectionConfigFromFile();
|
|
61
|
+
client.currentIde = detectIde(client.ideProcessInfo, client.connectionConfig?.ideInfo);
|
|
62
|
+
IdeClient.instance = client;
|
|
63
|
+
}
|
|
64
|
+
return IdeClient.instance;
|
|
65
|
+
}
|
|
66
|
+
static resetInstance() {
|
|
67
|
+
IdeClient.instance = undefined;
|
|
68
|
+
}
|
|
69
|
+
addStatusChangeListener(listener) {
|
|
70
|
+
this.statusListeners.add(listener);
|
|
71
|
+
}
|
|
72
|
+
removeStatusChangeListener(listener) {
|
|
73
|
+
this.statusListeners.delete(listener);
|
|
74
|
+
}
|
|
75
|
+
addTrustChangeListener(listener) {
|
|
76
|
+
this.trustChangeListeners.add(listener);
|
|
77
|
+
}
|
|
78
|
+
removeTrustChangeListener(listener) {
|
|
79
|
+
this.trustChangeListeners.delete(listener);
|
|
80
|
+
}
|
|
81
|
+
async connect() {
|
|
82
|
+
if (!this.currentIde) {
|
|
83
|
+
this.setState(IDEConnectionStatus.Disconnected, `IDE integration is not supported in your current environment. To use this feature, run LLxprt Code in one of these supported IDEs: ${Object.values(IDE_DEFINITIONS)
|
|
84
|
+
.map((ide) => ide.displayName)
|
|
85
|
+
.join(', ')}`, false);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.setState(IDEConnectionStatus.Connecting);
|
|
89
|
+
this.connectionConfig = await this.getConnectionConfigFromFile();
|
|
90
|
+
this.authToken =
|
|
91
|
+
this.connectionConfig?.authToken ??
|
|
92
|
+
process.env['LLXPRT_CODE_IDE_AUTH_TOKEN'];
|
|
93
|
+
const workspacePath = this.connectionConfig?.workspacePath ??
|
|
94
|
+
process.env['LLXPRT_CODE_IDE_WORKSPACE_PATH'];
|
|
95
|
+
const { isValid, error } = IdeClient.validateWorkspacePath(workspacePath, process.cwd());
|
|
96
|
+
if (!isValid) {
|
|
97
|
+
this.setState(IDEConnectionStatus.Disconnected, error, true);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const portFromFile = this.connectionConfig?.port;
|
|
101
|
+
if (portFromFile) {
|
|
102
|
+
const connected = await this.establishConnection(portFromFile);
|
|
103
|
+
if (connected) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const portFromEnv = this.getPortFromEnv();
|
|
108
|
+
if (portFromEnv) {
|
|
109
|
+
const connected = await this.establishConnection(portFromEnv);
|
|
110
|
+
if (connected) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.setState(IDEConnectionStatus.Disconnected, `Failed to connect to IDE companion extension in ${this.currentIde.displayName}. Please ensure the extension is running. To install the extension, run /ide install.`, true);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A diff is accepted with any modifications if the user performs one of the
|
|
118
|
+
* following actions:
|
|
119
|
+
* - Clicks the checkbox icon in the IDE to accept
|
|
120
|
+
* - Runs `command+shift+p` > "LLxprt Code: Accept Diff in IDE" to accept
|
|
121
|
+
* - Selects "accept" in the CLI UI
|
|
122
|
+
* - Saves the file via `ctrl/command+s`
|
|
123
|
+
*
|
|
124
|
+
* A diff is rejected if the user performs one of the following actions:
|
|
125
|
+
* - Clicks the "x" icon in the IDE
|
|
126
|
+
* - Runs "LLxprt Code: Close Diff in IDE"
|
|
127
|
+
* - Selects "no" in the CLI UI
|
|
128
|
+
* - Closes the file
|
|
129
|
+
*/
|
|
130
|
+
async openDiff(filePath, newContent) {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
this.diffResponses.set(filePath, resolve);
|
|
133
|
+
logger.debug(`openDiff -> tools/call openDiff for ${filePath}`);
|
|
134
|
+
this.client
|
|
135
|
+
?.callTool({
|
|
136
|
+
name: `openDiff`,
|
|
137
|
+
arguments: {
|
|
138
|
+
filePath,
|
|
139
|
+
newContent,
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
.catch((err) => {
|
|
143
|
+
logger.debug(`openDiff callTool for ${filePath} failed:`, err);
|
|
144
|
+
reject(err);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
async closeDiff(filePath, options) {
|
|
149
|
+
try {
|
|
150
|
+
logger.debug(`closeDiff -> tools/call closeDiff for ${filePath}`);
|
|
151
|
+
const result = await this.client?.callTool({
|
|
152
|
+
name: `closeDiff`,
|
|
153
|
+
arguments: {
|
|
154
|
+
filePath,
|
|
155
|
+
suppressNotification: options?.suppressNotification,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
if (result) {
|
|
159
|
+
const parsed = CloseDiffResponseSchema.parse(result);
|
|
160
|
+
return parsed.content;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
logger.debug(`closeDiff callTool for ${filePath} failed:`, err);
|
|
165
|
+
}
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
// Closes the diff. Instead of waiting for a notification,
|
|
169
|
+
// manually resolves the diff resolver as the desired outcome.
|
|
170
|
+
async resolveDiffFromCli(filePath, outcome) {
|
|
171
|
+
const resolver = this.diffResponses.get(filePath);
|
|
172
|
+
const content = await this.closeDiff(filePath, {
|
|
173
|
+
// Suppress notification to avoid race where closing the diff rejects the
|
|
174
|
+
// request.
|
|
175
|
+
suppressNotification: true,
|
|
176
|
+
});
|
|
177
|
+
if (resolver) {
|
|
178
|
+
if (outcome === 'accepted') {
|
|
179
|
+
resolver({ status: 'accepted', content });
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
resolver({ status: 'rejected', content: undefined });
|
|
183
|
+
}
|
|
184
|
+
this.diffResponses.delete(filePath);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async disconnect() {
|
|
188
|
+
if (this.state.status === IDEConnectionStatus.Disconnected) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
for (const filePath of this.diffResponses.keys()) {
|
|
192
|
+
await this.closeDiff(filePath);
|
|
193
|
+
}
|
|
194
|
+
this.diffResponses.clear();
|
|
195
|
+
this.setState(IDEConnectionStatus.Disconnected, 'IDE integration disabled. To enable it again, run /ide enable.');
|
|
196
|
+
void this.client?.close();
|
|
197
|
+
}
|
|
198
|
+
getCurrentIde() {
|
|
199
|
+
return this.currentIde;
|
|
200
|
+
}
|
|
201
|
+
getConnectionStatus() {
|
|
202
|
+
return this.state;
|
|
203
|
+
}
|
|
204
|
+
getDetectedIdeDisplayName() {
|
|
205
|
+
return this.currentIde?.displayName;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if diffing functionality is enabled for this IDE client.
|
|
209
|
+
* Returns true when the client is connected and the IDE supports diff operations.
|
|
210
|
+
*/
|
|
211
|
+
isDiffingEnabled() {
|
|
212
|
+
return this.state.status === IDEConnectionStatus.Connected;
|
|
213
|
+
}
|
|
214
|
+
setState(status, details, logToConsole = false) {
|
|
215
|
+
const isAlreadyDisconnected = this.state.status === IDEConnectionStatus.Disconnected &&
|
|
216
|
+
status === IDEConnectionStatus.Disconnected;
|
|
217
|
+
// Only update details & log to console if the state wasn't already
|
|
218
|
+
// disconnected, so that the first detail message is preserved.
|
|
219
|
+
if (!isAlreadyDisconnected) {
|
|
220
|
+
this.state = { status, details };
|
|
221
|
+
for (const listener of this.statusListeners) {
|
|
222
|
+
listener(this.state);
|
|
223
|
+
}
|
|
224
|
+
if (details) {
|
|
225
|
+
if (logToConsole) {
|
|
226
|
+
logger.error(details);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
// We only want to log disconnect messages to debug
|
|
230
|
+
// if they are not already being logged to the console.
|
|
231
|
+
logger.debug(details);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (status === IDEConnectionStatus.Disconnected) {
|
|
236
|
+
ideContext.clearIdeContext();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
static validateWorkspacePath(ideWorkspacePath, cwd) {
|
|
240
|
+
if (ideWorkspacePath === undefined) {
|
|
241
|
+
return {
|
|
242
|
+
isValid: false,
|
|
243
|
+
error: `Failed to connect to IDE companion extension. Please ensure the extension is running. To install the extension, run /ide install.`,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
if (ideWorkspacePath === '') {
|
|
247
|
+
return {
|
|
248
|
+
isValid: false,
|
|
249
|
+
error: `To use this feature, please open a workspace folder in your IDE and try again.`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const ideWorkspacePaths = ideWorkspacePath.split(path.delimiter);
|
|
253
|
+
const realCwd = getRealPath(cwd);
|
|
254
|
+
const isWithinWorkspace = ideWorkspacePaths.some((workspacePath) => {
|
|
255
|
+
const idePath = getRealPath(workspacePath);
|
|
256
|
+
return isSubpath(idePath, realCwd);
|
|
257
|
+
});
|
|
258
|
+
if (!isWithinWorkspace) {
|
|
259
|
+
return {
|
|
260
|
+
isValid: false,
|
|
261
|
+
error: `Directory mismatch. LLxprt Code is running in a different location than the open workspace in the IDE. Please run the CLI from one of the following directories: ${ideWorkspacePaths.join(', ')}`,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
return { isValid: true };
|
|
265
|
+
}
|
|
266
|
+
getPortFromEnv() {
|
|
267
|
+
const port = process.env['LLXPRT_CODE_IDE_SERVER_PORT'];
|
|
268
|
+
if (!port) {
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
return port;
|
|
272
|
+
}
|
|
273
|
+
async getConnectionConfigFromFile() {
|
|
274
|
+
if (!this.ideProcessInfo) {
|
|
275
|
+
return {};
|
|
276
|
+
}
|
|
277
|
+
// Try new port file location (in subdirectory with port in filename)
|
|
278
|
+
try {
|
|
279
|
+
const portDir = path.join(os.tmpdir(), 'llxprt', 'ide');
|
|
280
|
+
const files = await fs.promises.readdir(portDir);
|
|
281
|
+
const prefix = `llxprt-ide-server-${this.ideProcessInfo.pid}-`;
|
|
282
|
+
const portFile = files.find((file) => file.startsWith(prefix) && file.endsWith('.json'));
|
|
283
|
+
if (portFile) {
|
|
284
|
+
const portFilePath = path.join(portDir, portFile);
|
|
285
|
+
const portFileContents = await fs.promises.readFile(portFilePath, 'utf8');
|
|
286
|
+
const configData = JSON.parse(portFileContents);
|
|
287
|
+
return {
|
|
288
|
+
port: configData?.port?.toString(),
|
|
289
|
+
workspacePath: configData?.workspacePath,
|
|
290
|
+
authToken: configData?.authToken,
|
|
291
|
+
ideInfo: configData?.ideInfo,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Port file in new location not found; try old location.
|
|
297
|
+
}
|
|
298
|
+
// For backwards compatibility, try old port file location
|
|
299
|
+
try {
|
|
300
|
+
const portFile = path.join(os.tmpdir(), `llxprt-ide-server-${this.ideProcessInfo.pid}.json`);
|
|
301
|
+
const portFileContents = await fs.promises.readFile(portFile, 'utf8');
|
|
302
|
+
const configData = JSON.parse(portFileContents);
|
|
303
|
+
return {
|
|
304
|
+
port: configData?.port?.toString(),
|
|
305
|
+
workspacePath: configData?.workspacePath,
|
|
306
|
+
authToken: configData?.authToken,
|
|
307
|
+
ideInfo: configData?.ideInfo,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
// No port file found.
|
|
312
|
+
return {};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
createProxyAwareFetch() {
|
|
316
|
+
// ignore proxy for '127.0.0.1' by default to allow connecting to the ide mcp server
|
|
317
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional falsy coalescing: env var may be empty string, both cases need same handling
|
|
318
|
+
const existingNoProxy = process.env['NO_PROXY'] || '';
|
|
319
|
+
const agent = new EnvHttpProxyAgent({
|
|
320
|
+
noProxy: [existingNoProxy, '127.0.0.1'].filter(Boolean).join(','),
|
|
321
|
+
});
|
|
322
|
+
const undiciPromise = import('undici');
|
|
323
|
+
// Suppress unhandled rejection if the promise is not awaited immediately.
|
|
324
|
+
// If the import fails, the error will be thrown when awaiting undiciPromise below.
|
|
325
|
+
undiciPromise.catch(() => { });
|
|
326
|
+
return async (url, init) => {
|
|
327
|
+
const { fetch: fetchFn } = await undiciPromise;
|
|
328
|
+
const fetchOptions = {
|
|
329
|
+
...init,
|
|
330
|
+
dispatcher: agent,
|
|
331
|
+
};
|
|
332
|
+
const options = fetchOptions;
|
|
333
|
+
const response = await fetchFn(url, options);
|
|
334
|
+
// Convert undici headers to standard headers format
|
|
335
|
+
const headers = {};
|
|
336
|
+
response.headers.forEach((value, key) => {
|
|
337
|
+
headers[key] = value;
|
|
338
|
+
});
|
|
339
|
+
return new Response(response.body, {
|
|
340
|
+
status: response.status,
|
|
341
|
+
statusText: response.statusText,
|
|
342
|
+
headers,
|
|
343
|
+
});
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
registerClientHandlers() {
|
|
347
|
+
if (!this.client) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
this.client.setNotificationHandler(IdeContextNotificationSchema, (notification) => {
|
|
351
|
+
ideContext.setIdeContext(notification.params);
|
|
352
|
+
const isTrusted = notification.params.workspaceState?.isTrusted;
|
|
353
|
+
if (isTrusted !== undefined) {
|
|
354
|
+
for (const listener of this.trustChangeListeners) {
|
|
355
|
+
listener(isTrusted);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
this.client.onerror = (_error) => {
|
|
360
|
+
this.setState(IDEConnectionStatus.Disconnected, `IDE connection error. The connection was lost unexpectedly. Please try reconnecting by running /ide enable`, true);
|
|
361
|
+
};
|
|
362
|
+
this.client.onclose = () => {
|
|
363
|
+
this.setState(IDEConnectionStatus.Disconnected, `IDE connection error. The connection was lost unexpectedly. Please try reconnecting by running /ide enable`, true);
|
|
364
|
+
};
|
|
365
|
+
this.client.setNotificationHandler(IdeDiffAcceptedNotificationSchema, (notification) => {
|
|
366
|
+
const { filePath, content } = notification.params;
|
|
367
|
+
const resolver = this.diffResponses.get(filePath);
|
|
368
|
+
if (resolver) {
|
|
369
|
+
resolver({ status: 'accepted', content });
|
|
370
|
+
this.diffResponses.delete(filePath);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
logger.debug(`No resolver found for ${filePath}`);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
this.client.setNotificationHandler(IdeDiffRejectedNotificationSchema, (notification) => {
|
|
377
|
+
const { filePath } = notification.params;
|
|
378
|
+
const resolver = this.diffResponses.get(filePath);
|
|
379
|
+
if (resolver) {
|
|
380
|
+
resolver({ status: 'rejected', content: undefined });
|
|
381
|
+
this.diffResponses.delete(filePath);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
logger.debug(`No resolver found for ${filePath}`);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
// For backwards compatibility. Newer extension versions will only send
|
|
388
|
+
// IdeDiffRejectedNotificationSchema.
|
|
389
|
+
this.client.setNotificationHandler(IdeDiffClosedNotificationSchema, (notification) => {
|
|
390
|
+
const { filePath } = notification.params;
|
|
391
|
+
const resolver = this.diffResponses.get(filePath);
|
|
392
|
+
if (resolver) {
|
|
393
|
+
resolver({ status: 'rejected', content: undefined });
|
|
394
|
+
this.diffResponses.delete(filePath);
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
logger.debug(`No resolver found for ${filePath}`);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
async establishConnection(port) {
|
|
402
|
+
let transport;
|
|
403
|
+
try {
|
|
404
|
+
this.client = new Client({
|
|
405
|
+
name: 'streamable-http-client',
|
|
406
|
+
// Task(#3487): use the CLI version here.
|
|
407
|
+
version: '1.0.0',
|
|
408
|
+
});
|
|
409
|
+
transport = new StreamableHTTPClientTransport(new URL(`http://${getIdeServerHost()}:${port}/mcp`), {
|
|
410
|
+
fetch: this.createProxyAwareFetch(),
|
|
411
|
+
requestInit: {
|
|
412
|
+
headers: this.authToken
|
|
413
|
+
? { Authorization: `Bearer ${this.authToken}` }
|
|
414
|
+
: {},
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
await this.client.connect(transport);
|
|
418
|
+
this.registerClientHandlers();
|
|
419
|
+
this.setState(IDEConnectionStatus.Connected);
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
// Connection failed; cleanup transport if created.
|
|
424
|
+
if (transport) {
|
|
425
|
+
try {
|
|
426
|
+
await transport.close();
|
|
427
|
+
}
|
|
428
|
+
catch (closeError) {
|
|
429
|
+
logger.debug('Failed to close transport:', closeError);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function getIdeServerHost() {
|
|
437
|
+
const isInContainer = fs.existsSync('/.dockerenv') || fs.existsSync('/run/.containerenv');
|
|
438
|
+
return isInContainer ? 'host.docker.internal' : '127.0.0.1';
|
|
439
|
+
}
|
|
440
|
+
//# sourceMappingURL=ide-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ide-client.js","sourceRoot":"","sources":["../../../src/ide/ide-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { type IdeInfo } from './detect-ide.js';
|
|
7
|
+
export interface IdeInstaller {
|
|
8
|
+
install(): Promise<InstallResult>;
|
|
9
|
+
}
|
|
10
|
+
export interface InstallResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function getIdeInstaller(ide: IdeInfo, platform?: NodeJS.Platform): IdeInstaller | null;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import * as child_process from 'node:child_process';
|
|
7
|
+
import * as process from 'node:process';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import * as os from 'node:os';
|
|
11
|
+
import { IDE_DEFINITIONS } from './detect-ide.js';
|
|
12
|
+
import { LLXPRT_CODE_COMPANION_EXTENSION_NAME } from './constants.js';
|
|
13
|
+
function getVsCodeCommand(platform = process.platform) {
|
|
14
|
+
return platform === 'win32' ? 'code.cmd' : 'code';
|
|
15
|
+
}
|
|
16
|
+
async function findVsCodeCommand(platform = process.platform) {
|
|
17
|
+
// 1. Check PATH first.
|
|
18
|
+
const vscodeCommand = getVsCodeCommand(platform);
|
|
19
|
+
try {
|
|
20
|
+
if (platform === 'win32') {
|
|
21
|
+
// eslint-disable-next-line sonarjs/os-command -- Project intentionally invokes platform tooling at this trusted boundary; arguments remain explicit and behavior is preserved.
|
|
22
|
+
const result = child_process
|
|
23
|
+
.execSync(`where.exe ${vscodeCommand}`)
|
|
24
|
+
.toString()
|
|
25
|
+
.trim();
|
|
26
|
+
// `where.exe` can return multiple paths. Return the first one.
|
|
27
|
+
const firstPath = result.split(/\r?\n/)[0];
|
|
28
|
+
if (firstPath) {
|
|
29
|
+
return firstPath;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// eslint-disable-next-line sonarjs/os-command -- Project intentionally invokes platform tooling at this trusted boundary; arguments remain explicit and behavior is preserved.
|
|
34
|
+
child_process.execSync(`command -v ${vscodeCommand}`, {
|
|
35
|
+
stdio: 'ignore',
|
|
36
|
+
});
|
|
37
|
+
return vscodeCommand;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Not in PATH, continue to check common locations.
|
|
42
|
+
}
|
|
43
|
+
// 2. Check common installation locations.
|
|
44
|
+
const locations = [];
|
|
45
|
+
const homeDir = os.homedir();
|
|
46
|
+
if (platform === 'darwin') {
|
|
47
|
+
// macOS
|
|
48
|
+
locations.push('/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code', path.join(homeDir, 'Library/Application Support/Code/bin/code'));
|
|
49
|
+
}
|
|
50
|
+
else if (platform === 'linux') {
|
|
51
|
+
// Linux
|
|
52
|
+
locations.push('/usr/share/code/bin/code', '/snap/bin/code', path.join(homeDir, '.local/share/code/bin/code'));
|
|
53
|
+
}
|
|
54
|
+
else if (platform === 'win32') {
|
|
55
|
+
// Windows
|
|
56
|
+
locations.push(path.join(
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- intentional falsy coalescing: env var may be empty string, fallback to default path
|
|
58
|
+
process.env.ProgramFiles || 'C:\\Program Files', 'Microsoft VS Code', 'bin', 'code.cmd'), path.join(homeDir, 'AppData', 'Local', 'Programs', 'Microsoft VS Code', 'bin', 'code.cmd'));
|
|
59
|
+
}
|
|
60
|
+
for (const location of locations) {
|
|
61
|
+
if (fs.existsSync(location)) {
|
|
62
|
+
return location;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
class VsCodeInstaller {
|
|
68
|
+
ideInfo;
|
|
69
|
+
platform;
|
|
70
|
+
vsCodeCommand;
|
|
71
|
+
constructor(ideInfo, platform = process.platform) {
|
|
72
|
+
this.ideInfo = ideInfo;
|
|
73
|
+
this.platform = platform;
|
|
74
|
+
this.vsCodeCommand = findVsCodeCommand(platform);
|
|
75
|
+
}
|
|
76
|
+
async install() {
|
|
77
|
+
const commandPath = await this.vsCodeCommand;
|
|
78
|
+
if (!commandPath) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
message: `${this.ideInfo.displayName} CLI not found. Please ensure 'code' is in your system's PATH. For help, see https://code.visualstudio.com/docs/configure/command-line#_code-is-not-recognized-as-an-internal-or-external-command. You can also install the '${LLXPRT_CODE_COMPANION_EXTENSION_NAME}' extension manually from the VS Code marketplace.`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const result = child_process.spawnSync(commandPath, [
|
|
86
|
+
'--install-extension',
|
|
87
|
+
'vybestack.llxprt-code-vscode-ide-companion',
|
|
88
|
+
'--force',
|
|
89
|
+
], { stdio: 'pipe', shell: this.platform === 'win32' });
|
|
90
|
+
if (result.status !== 0) {
|
|
91
|
+
throw new Error(`Failed to install extension: ${result.stderr.toString()}`);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
success: true,
|
|
95
|
+
message: `${this.ideInfo.displayName} companion extension was installed successfully.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Extension installation failed - user must install manually
|
|
100
|
+
return {
|
|
101
|
+
success: false,
|
|
102
|
+
message: `Failed to install ${this.ideInfo.displayName} companion extension. Please try installing '${LLXPRT_CODE_COMPANION_EXTENSION_NAME}' manually from the ${this.ideInfo.displayName} extension marketplace.`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function getIdeInstaller(ide, platform = process.platform) {
|
|
108
|
+
switch (ide.name) {
|
|
109
|
+
case IDE_DEFINITIONS.vscode.name:
|
|
110
|
+
case IDE_DEFINITIONS.firebasestudio.name:
|
|
111
|
+
return new VsCodeInstaller(ide, platform);
|
|
112
|
+
default:
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=ide-installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ide-installer.js","sourceRoot":"","sources":["../../../src/ide/ide-installer.ts"],"names":[],"mappings":"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"}
|