mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-11 17:36:24 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5024443c72 |
Generated
+9
-9
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -17782,7 +17782,7 @@
|
||||
},
|
||||
"packages/a2a-server": {
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
"@google-cloud/storage": "7.19.0",
|
||||
@@ -18242,7 +18242,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "0.16.1",
|
||||
@@ -18458,7 +18458,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
@@ -19131,7 +19131,7 @@
|
||||
},
|
||||
"packages/devtools": {
|
||||
"name": "@google/gemini-cli-devtools",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ws": "8.16.0"
|
||||
@@ -19167,7 +19167,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@google/gemini-cli-sdk",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -19506,7 +19506,7 @@
|
||||
},
|
||||
"packages/test-utils": {
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -19524,7 +19524,7 @@
|
||||
},
|
||||
"packages/vscode-ide-companion": {
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"license": "LICENSE",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.23.0",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
@@ -14,7 +14,7 @@
|
||||
"url": "git+https://github.com/google-gemini/gemini-cli.git"
|
||||
},
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.55.1"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.56.0-nightly.20260806.g761f604c1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"description": "Gemini CLI A2A Server",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"description": "Gemini CLI",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -27,7 +27,7 @@
|
||||
"dist"
|
||||
],
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.55.1"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.56.0-nightly.20260806.g761f604c1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "0.16.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"description": "Gemini CLI Core",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -31,6 +31,21 @@ vi.mock('node:fs', async (importOriginal) => {
|
||||
...actual.promises,
|
||||
readFile: vi.fn(),
|
||||
readdir: vi.fn(),
|
||||
realpath: vi.fn((p) => Promise.resolve(p)),
|
||||
stat: vi.fn(() =>
|
||||
Promise.resolve({ uid: process.getuid ? process.getuid() : 1000 }),
|
||||
),
|
||||
open: vi.fn((filePath: string) =>
|
||||
Promise.resolve({
|
||||
stat: () => fs.promises.stat(filePath),
|
||||
readFile: (options?: string | { encoding?: string }) =>
|
||||
fs.promises.readFile(
|
||||
filePath,
|
||||
options as unknown as BufferEncoding | undefined,
|
||||
),
|
||||
close: () => Promise.resolve(),
|
||||
} as unknown as fs.promises.FileHandle),
|
||||
),
|
||||
},
|
||||
realpathSync: (p: string) => p,
|
||||
existsSync: vi.fn(() => false),
|
||||
@@ -430,6 +445,141 @@ describe('ide-connection-utils', () => {
|
||||
|
||||
expect(result).toEqual(config2);
|
||||
});
|
||||
|
||||
it('should NOT filter out config if all found config files are mismatched/invalid workspaces, returning the best sorted match so that the correct Directory Mismatch error is raised downstream', async () => {
|
||||
const invalidConfig1 = {
|
||||
port: '1111',
|
||||
workspacePath: '/invalid/workspace1',
|
||||
};
|
||||
const invalidConfig2 = {
|
||||
port: '2222',
|
||||
workspacePath: '/invalid/workspace2',
|
||||
};
|
||||
vi.mocked(fs.promises.readFile).mockRejectedValueOnce(
|
||||
new Error('not found'),
|
||||
);
|
||||
(
|
||||
vi.mocked(fs.promises.readdir) as Mock<
|
||||
(path: fs.PathLike) => Promise<string[]>
|
||||
>
|
||||
).mockResolvedValue([
|
||||
'gemini-ide-server-12345-111.json',
|
||||
'gemini-ide-server-12345-222.json',
|
||||
]);
|
||||
vi.mocked(fs.promises.readFile)
|
||||
.mockResolvedValueOnce(JSON.stringify(invalidConfig1))
|
||||
.mockResolvedValueOnce(JSON.stringify(invalidConfig2));
|
||||
|
||||
const result = await getConnectionConfigFromFile(12345);
|
||||
|
||||
expect(result).toEqual(invalidConfig1);
|
||||
});
|
||||
|
||||
it('should prioritize the config matching the port from the environment variable when all found config files are mismatched/invalid workspaces', async () => {
|
||||
vi.stubEnv('GEMINI_CLI_IDE_SERVER_PORT', '2222');
|
||||
const invalidConfig1 = {
|
||||
port: '1111',
|
||||
workspacePath: '/invalid/workspace1',
|
||||
};
|
||||
const invalidConfig2 = {
|
||||
port: '2222',
|
||||
workspacePath: '/invalid/workspace2',
|
||||
};
|
||||
vi.mocked(fs.promises.readFile).mockRejectedValueOnce(
|
||||
new Error('not found'),
|
||||
);
|
||||
(
|
||||
vi.mocked(fs.promises.readdir) as Mock<
|
||||
(path: fs.PathLike) => Promise<string[]>
|
||||
>
|
||||
).mockResolvedValue([
|
||||
'gemini-ide-server-12345-111.json',
|
||||
'gemini-ide-server-12345-222.json',
|
||||
]);
|
||||
vi.mocked(fs.promises.readFile)
|
||||
.mockResolvedValueOnce(JSON.stringify(invalidConfig1))
|
||||
.mockResolvedValueOnce(JSON.stringify(invalidConfig2));
|
||||
|
||||
const result = await getConnectionConfigFromFile(12345);
|
||||
|
||||
expect(result).toEqual(invalidConfig2);
|
||||
});
|
||||
|
||||
it.runIf(process.getuid !== undefined)(
|
||||
'should reject and ignore config files owned by a different user UID to prevent hijacking/information disclosure',
|
||||
async () => {
|
||||
const config1 = {
|
||||
port: '1111',
|
||||
workspacePath: '/test/workspace',
|
||||
};
|
||||
vi.mocked(fs.promises.readFile).mockRejectedValueOnce(
|
||||
new Error('not found'),
|
||||
);
|
||||
(
|
||||
vi.mocked(fs.promises.readdir) as Mock<
|
||||
(path: fs.PathLike) => Promise<string[]>
|
||||
>
|
||||
).mockResolvedValue(['gemini-ide-server-12345-111.json']);
|
||||
vi.mocked(fs.promises.readFile).mockResolvedValueOnce(
|
||||
JSON.stringify(config1),
|
||||
);
|
||||
|
||||
const otherUid = (process.getuid ? process.getuid() : 1000) + 1;
|
||||
vi.mocked(fs.promises.stat).mockResolvedValueOnce({
|
||||
uid: otherUid,
|
||||
} as unknown as fs.Stats);
|
||||
|
||||
const result = await getConnectionConfigFromFile(12345);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
},
|
||||
);
|
||||
|
||||
it('should accept and parse config files owned by the current user UID', async () => {
|
||||
const config1 = {
|
||||
port: '1111',
|
||||
workspacePath: '/test/workspace',
|
||||
};
|
||||
vi.mocked(fs.promises.readFile).mockRejectedValueOnce(
|
||||
new Error('not found'),
|
||||
);
|
||||
(
|
||||
vi.mocked(fs.promises.readdir) as Mock<
|
||||
(path: fs.PathLike) => Promise<string[]>
|
||||
>
|
||||
).mockResolvedValue(['gemini-ide-server-12345-111.json']);
|
||||
vi.mocked(fs.promises.readFile).mockResolvedValueOnce(
|
||||
JSON.stringify(config1),
|
||||
);
|
||||
|
||||
const currentUid = process.getuid ? process.getuid() : 1000;
|
||||
vi.mocked(fs.promises.stat).mockResolvedValueOnce({
|
||||
uid: currentUid,
|
||||
} as unknown as fs.Stats);
|
||||
|
||||
const result = await getConnectionConfigFromFile(12345);
|
||||
|
||||
expect(result).toEqual(config1);
|
||||
});
|
||||
|
||||
it('should reject and ignore config files if fs.promises.open throws an error', async () => {
|
||||
vi.mocked(fs.promises.readFile).mockRejectedValueOnce(
|
||||
new Error('not found'),
|
||||
);
|
||||
(
|
||||
vi.mocked(fs.promises.readdir) as Mock<
|
||||
(path: fs.PathLike) => Promise<string[]>
|
||||
>
|
||||
).mockResolvedValue(['gemini-ide-server-12345-111.json']);
|
||||
|
||||
vi.mocked(fs.promises.open).mockRejectedValueOnce(
|
||||
new Error('symlink loop / permission denied'),
|
||||
);
|
||||
|
||||
const result = await getConnectionConfigFromFile(12345);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateWorkspacePath', () => {
|
||||
|
||||
@@ -109,6 +109,26 @@ export function getStdioConfigFromEnv(): StdioConfig | undefined {
|
||||
|
||||
const IDE_SERVER_FILE_REGEX = /^gemini-ide-server-(\d+)-\d+\.json$/;
|
||||
|
||||
async function verifyAndReadFile(
|
||||
filePath: string,
|
||||
): Promise<string | undefined> {
|
||||
let handle: fs.promises.FileHandle | undefined;
|
||||
try {
|
||||
handle = await fs.promises.open(filePath, 'r');
|
||||
const stat = await handle.stat();
|
||||
if (process.getuid && stat.uid !== process.getuid()) {
|
||||
return undefined;
|
||||
}
|
||||
return await handle.readFile('utf8');
|
||||
} catch {
|
||||
return undefined;
|
||||
} finally {
|
||||
if (handle) {
|
||||
await handle.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getConnectionConfigFromFile(
|
||||
pid: number,
|
||||
): Promise<
|
||||
@@ -122,7 +142,10 @@ export async function getConnectionConfigFromFile(
|
||||
'ide',
|
||||
`gemini-ide-server-${pid}.json`,
|
||||
);
|
||||
const portFileContents = await fs.promises.readFile(portFile, 'utf8');
|
||||
const portFileContents = await verifyAndReadFile(portFile);
|
||||
if (!portFileContents) {
|
||||
throw new Error('Verification failed or file not found');
|
||||
}
|
||||
const parsed: unknown = JSON.parse(portFileContents);
|
||||
type ConfigType = ConnectionConfig & {
|
||||
workspacePath?: string;
|
||||
@@ -164,23 +187,21 @@ export async function getConnectionConfigFromFile(
|
||||
|
||||
sortConnectionFiles(matchingFiles, pid);
|
||||
|
||||
let fileContents: string[];
|
||||
try {
|
||||
fileContents = await Promise.all(
|
||||
matchingFiles.map((file) =>
|
||||
fs.promises.readFile(path.join(portFileDir, file), 'utf8'),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
logger.debug('Failed to read IDE connection config file(s):', e);
|
||||
return undefined;
|
||||
}
|
||||
const fileContents = await Promise.all(
|
||||
matchingFiles.map((file) =>
|
||||
verifyAndReadFile(path.join(portFileDir, file)),
|
||||
),
|
||||
);
|
||||
|
||||
const parsedContents = fileContents.map(
|
||||
(
|
||||
content,
|
||||
):
|
||||
| (ConnectionConfig & { workspacePath?: string; ideInfo?: IdeInfo })
|
||||
| undefined => {
|
||||
if (!content) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(content);
|
||||
type ConfigType = ConnectionConfig & {
|
||||
@@ -219,6 +240,31 @@ export async function getConnectionConfigFromFile(
|
||||
);
|
||||
|
||||
if (validWorkspaces.length === 0) {
|
||||
// If no workspace matches the current CWD, but we found and parsed
|
||||
// valid connection config file(s), return the best-sorted config.
|
||||
// This lets downstream connection logic raise a helpful, detailed
|
||||
// "Directory mismatch" warning instead of a generic connection error.
|
||||
let fileIndex = -1;
|
||||
const portFromEnv = getPortFromEnv();
|
||||
if (portFromEnv) {
|
||||
fileIndex = parsedContents.findIndex(
|
||||
(content) =>
|
||||
!!content &&
|
||||
content.port !== undefined &&
|
||||
String(content.port) === portFromEnv,
|
||||
);
|
||||
}
|
||||
if (fileIndex === -1) {
|
||||
fileIndex = parsedContents.findIndex((content) => !!content);
|
||||
}
|
||||
|
||||
if (fileIndex !== -1) {
|
||||
const selected = parsedContents[fileIndex]!;
|
||||
logger.debug(
|
||||
`Selected best mismatched IDE connection file: ${matchingFiles[fileIndex]}`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -234,7 +280,8 @@ export async function getConnectionConfigFromFile(
|
||||
const portFromEnv = getPortFromEnv();
|
||||
if (portFromEnv) {
|
||||
const matchingPortIndex = validWorkspaces.findIndex(
|
||||
(content) => String(content.port) === portFromEnv,
|
||||
(content) =>
|
||||
content.port !== undefined && String(content.port) === portFromEnv,
|
||||
);
|
||||
if (matchingPortIndex !== -1) {
|
||||
const selected = validWorkspaces[matchingPortIndex];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-devtools",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-sdk",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"description": "Gemini CLI SDK",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"private": true,
|
||||
"main": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"displayName": "Gemini CLI Companion",
|
||||
"description": "Enable Gemini CLI with direct access to your IDE workspace.",
|
||||
"version": "0.55.1",
|
||||
"version": "0.56.0-nightly.20260806.g761f604c1",
|
||||
"publisher": "google",
|
||||
"icon": "assets/icon.png",
|
||||
"repository": {
|
||||
|
||||
Reference in New Issue
Block a user