mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-30 07:51:07 -07:00
feat(ide): Read IDE info from discovery file (#8760)
This commit is contained in:
@@ -80,3 +80,35 @@ describe('detectIde', () => {
|
||||
expect(detectIde(ideProcessInfoNoCode)).toBe(IDE_DEFINITIONS.vscodefork);
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectIde with ideInfoFromFile', () => {
|
||||
const ideProcessInfo = { pid: 123, command: 'some/path/to/code' };
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
it('should use the name and displayName from the file', () => {
|
||||
const ideInfoFromFile = {
|
||||
name: 'custom-ide',
|
||||
displayName: 'Custom IDE',
|
||||
};
|
||||
expect(detectIde(ideProcessInfo, ideInfoFromFile)).toEqual(ideInfoFromFile);
|
||||
});
|
||||
|
||||
it('should fall back to env detection if name is missing', () => {
|
||||
const ideInfoFromFile = { displayName: 'Custom IDE' };
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe(
|
||||
IDE_DEFINITIONS.vscode,
|
||||
);
|
||||
});
|
||||
|
||||
it('should fall back to env detection if displayName is missing', () => {
|
||||
const ideInfoFromFile = { name: 'custom-ide' };
|
||||
vi.stubEnv('TERM_PROGRAM', 'vscode');
|
||||
expect(detectIde(ideProcessInfo, ideInfoFromFile)).toBe(
|
||||
IDE_DEFINITIONS.vscode,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,10 +16,8 @@ export const IDE_DEFINITIONS = {
|
||||
vscodefork: { name: 'vscodefork', displayName: 'IDE' },
|
||||
} as const;
|
||||
|
||||
export type IdeName = keyof typeof IDE_DEFINITIONS;
|
||||
|
||||
export interface IdeInfo {
|
||||
name: IdeName;
|
||||
name: string;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
@@ -64,10 +62,20 @@ function verifyVSCode(
|
||||
return IDE_DEFINITIONS.vscodefork;
|
||||
}
|
||||
|
||||
export function detectIde(ideProcessInfo: {
|
||||
pid: number;
|
||||
command: string;
|
||||
}): IdeInfo | undefined {
|
||||
export function detectIde(
|
||||
ideProcessInfo: {
|
||||
pid: number;
|
||||
command: string;
|
||||
},
|
||||
ideInfoFromFile?: { name?: string; displayName?: string },
|
||||
): IdeInfo | undefined {
|
||||
if (ideInfoFromFile?.name && ideInfoFromFile.displayName) {
|
||||
return {
|
||||
name: ideInfoFromFile.name,
|
||||
displayName: ideInfoFromFile.displayName,
|
||||
};
|
||||
}
|
||||
|
||||
// Only VSCode-based integrations are currently supported.
|
||||
if (process.env['TERM_PROGRAM'] !== 'vscode') {
|
||||
return undefined;
|
||||
|
||||
@@ -367,12 +367,10 @@ describe('IdeClient', () => {
|
||||
expect(result).toEqual(validConfig);
|
||||
expect(validateSpy).toHaveBeenCalledWith(
|
||||
'/invalid/workspace',
|
||||
'VS Code',
|
||||
'/test/workspace/sub-dir',
|
||||
);
|
||||
expect(validateSpy).toHaveBeenCalledWith(
|
||||
'/test/workspace',
|
||||
'VS Code',
|
||||
'/test/workspace/sub-dir',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -60,8 +60,8 @@ type StdioConfig = {
|
||||
|
||||
type ConnectionConfig = {
|
||||
port?: string;
|
||||
stdio?: StdioConfig;
|
||||
authToken?: string;
|
||||
stdio?: StdioConfig;
|
||||
};
|
||||
|
||||
function getRealPath(path: string): string {
|
||||
@@ -87,6 +87,9 @@ export class IdeClient {
|
||||
};
|
||||
private currentIde: IdeInfo | undefined;
|
||||
private ideProcessInfo: { pid: number; command: string } | undefined;
|
||||
private connectionConfig:
|
||||
| (ConnectionConfig & { workspacePath?: string; ideInfo?: IdeInfo })
|
||||
| undefined;
|
||||
private authToken: string | undefined;
|
||||
private diffResponses = new Map<string, (result: DiffUpdateResult) => void>();
|
||||
private statusListeners = new Set<(state: IDEConnectionState) => void>();
|
||||
@@ -106,7 +109,11 @@ export class IdeClient {
|
||||
IdeClient.instancePromise = (async () => {
|
||||
const client = new IdeClient();
|
||||
client.ideProcessInfo = await getIdeProcessInfo();
|
||||
client.currentIde = detectIde(client.ideProcessInfo);
|
||||
client.connectionConfig = await client.getConnectionConfigFromFile();
|
||||
client.currentIde = detectIde(
|
||||
client.ideProcessInfo,
|
||||
client.connectionConfig?.ideInfo,
|
||||
);
|
||||
return client;
|
||||
})();
|
||||
}
|
||||
@@ -141,17 +148,16 @@ export class IdeClient {
|
||||
|
||||
this.setState(IDEConnectionStatus.Connecting);
|
||||
|
||||
const configFromFile = await this.getConnectionConfigFromFile();
|
||||
if (configFromFile?.authToken) {
|
||||
this.authToken = configFromFile.authToken;
|
||||
this.connectionConfig = await this.getConnectionConfigFromFile();
|
||||
if (this.connectionConfig?.authToken) {
|
||||
this.authToken = this.connectionConfig.authToken;
|
||||
}
|
||||
const workspacePath =
|
||||
configFromFile?.workspacePath ??
|
||||
this.connectionConfig?.workspacePath ??
|
||||
process.env['GEMINI_CLI_IDE_WORKSPACE_PATH'];
|
||||
|
||||
const { isValid, error } = IdeClient.validateWorkspacePath(
|
||||
workspacePath,
|
||||
this.currentIde.displayName,
|
||||
process.cwd(),
|
||||
);
|
||||
|
||||
@@ -160,18 +166,18 @@ export class IdeClient {
|
||||
return;
|
||||
}
|
||||
|
||||
if (configFromFile) {
|
||||
if (configFromFile.port) {
|
||||
if (this.connectionConfig) {
|
||||
if (this.connectionConfig.port) {
|
||||
const connected = await this.establishHttpConnection(
|
||||
configFromFile.port,
|
||||
this.connectionConfig.port,
|
||||
);
|
||||
if (connected) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (configFromFile.stdio) {
|
||||
if (this.connectionConfig.stdio) {
|
||||
const connected = await this.establishStdioConnection(
|
||||
configFromFile.stdio,
|
||||
this.connectionConfig.stdio,
|
||||
);
|
||||
if (connected) {
|
||||
return;
|
||||
@@ -494,20 +500,19 @@ export class IdeClient {
|
||||
|
||||
static validateWorkspacePath(
|
||||
ideWorkspacePath: string | undefined,
|
||||
currentIdeDisplayName: string | undefined,
|
||||
cwd: string,
|
||||
): { isValid: boolean; error?: string } {
|
||||
if (ideWorkspacePath === undefined) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Failed to connect to IDE companion extension in ${currentIdeDisplayName}. Please ensure the extension is running. To install the extension, run /ide install.`,
|
||||
error: `Failed to connect to IDE companion extension. Please ensure the extension is running. To install the extension, run /ide install.`,
|
||||
};
|
||||
}
|
||||
|
||||
if (ideWorkspacePath === '') {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `To use this feature, please open a workspace folder in ${currentIdeDisplayName} and try again.`,
|
||||
error: `To use this feature, please open a workspace folder in your IDE and try again.`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -521,7 +526,7 @@ export class IdeClient {
|
||||
if (!isWithinWorkspace) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Directory mismatch. Gemini CLI is running in a different location than the open workspace in ${currentIdeDisplayName}. Please run the CLI from one of the following directories: ${ideWorkspacePaths.join(
|
||||
error: `Directory mismatch. Gemini CLI 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(
|
||||
', ',
|
||||
)}`,
|
||||
};
|
||||
@@ -564,7 +569,8 @@ export class IdeClient {
|
||||
}
|
||||
|
||||
private async getConnectionConfigFromFile(): Promise<
|
||||
(ConnectionConfig & { workspacePath?: string }) | undefined
|
||||
| (ConnectionConfig & { workspacePath?: string; ideInfo?: IdeInfo })
|
||||
| undefined
|
||||
> {
|
||||
if (!this.ideProcessInfo) {
|
||||
return undefined;
|
||||
@@ -594,6 +600,10 @@ export class IdeClient {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!portFiles) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const fileRegex = new RegExp(
|
||||
`^gemini-ide-server-${this.ideProcessInfo.pid}-\\d+\\.json$`,
|
||||
);
|
||||
@@ -630,7 +640,6 @@ export class IdeClient {
|
||||
}
|
||||
const { isValid } = IdeClient.validateWorkspacePath(
|
||||
content.workspacePath,
|
||||
this.currentIde?.displayName,
|
||||
process.cwd(),
|
||||
);
|
||||
return isValid;
|
||||
|
||||
Reference in New Issue
Block a user