@google/gemini-cli-core 0.1.14 → 0.1.15
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/google-gemini-cli-core-0.1.13.tgz +0 -0
- package/dist/src/code_assist/oauth2.test.js +14 -14
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/config/config.d.ts +3 -0
- package/dist/src/config/config.js +6 -0
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/ide/ide-client.js +11 -2
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/oauth-provider.d.ts +5 -1
- package/dist/src/mcp/oauth-provider.js +36 -11
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +2 -2
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.d.ts +3 -1
- package/dist/src/mcp/oauth-token-storage.js +3 -1
- package/dist/src/mcp/oauth-token-storage.js.map +1 -1
- package/dist/src/prompts/mcp-prompts.d.ts +8 -0
- package/dist/src/prompts/mcp-prompts.js +13 -0
- package/dist/src/prompts/mcp-prompts.js.map +1 -0
- package/dist/src/prompts/prompt-registry.d.ts +26 -0
- package/dist/src/prompts/prompt-registry.js +47 -0
- package/dist/src/prompts/prompt-registry.js.map +1 -0
- package/dist/src/services/fileDiscoveryService.test.js +101 -60
- package/dist/src/services/fileDiscoveryService.test.js.map +1 -1
- package/dist/src/services/gitService.test.js +67 -86
- package/dist/src/services/gitService.test.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +70 -0
- package/dist/src/services/shellExecutionService.js +152 -0
- package/dist/src/services/shellExecutionService.js.map +1 -0
- package/dist/src/services/shellExecutionService.test.d.ts +6 -0
- package/dist/src/services/shellExecutionService.test.js +258 -0
- package/dist/src/services/shellExecutionService.test.js.map +1 -0
- package/dist/src/tools/glob.test.js +7 -4
- package/dist/src/tools/glob.test.js.map +1 -1
- package/dist/src/tools/grep.test.js +4 -4
- package/dist/src/tools/grep.test.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +26 -2
- package/dist/src/tools/mcp-client.js +64 -10
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +53 -1
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +51 -62
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/tools/shell.d.ts +1 -0
- package/dist/src/tools/shell.js +137 -181
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +249 -101
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/tool-registry.js +3 -3
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +2 -2
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/utils/formatters.d.ts +6 -0
- package/dist/src/utils/formatters.js +16 -0
- package/dist/src/utils/formatters.js.map +1 -0
- package/dist/src/utils/shell-utils.d.ts +37 -3
- package/dist/src/utils/shell-utils.js +110 -47
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/shell-utils.test.js +146 -396
- package/dist/src/utils/shell-utils.test.js.map +1 -1
- package/dist/src/utils/textUtils.d.ts +13 -0
- package/dist/src/utils/textUtils.js +28 -0
- package/dist/src/utils/textUtils.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -6,22 +6,13 @@
|
|
|
6
6
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
7
|
import { GitService } from './gitService.js';
|
|
8
8
|
import * as path from 'path';
|
|
9
|
+
import * as fs from 'fs/promises';
|
|
10
|
+
import * as os from 'os';
|
|
11
|
+
import { getProjectHash, GEMINI_DIR } from '../utils/paths.js';
|
|
9
12
|
const hoistedMockExec = vi.hoisted(() => vi.fn());
|
|
10
13
|
vi.mock('node:child_process', () => ({
|
|
11
14
|
exec: hoistedMockExec,
|
|
12
15
|
}));
|
|
13
|
-
const hoistedMockMkdir = vi.hoisted(() => vi.fn());
|
|
14
|
-
const hoistedMockReadFile = vi.hoisted(() => vi.fn());
|
|
15
|
-
const hoistedMockWriteFile = vi.hoisted(() => vi.fn());
|
|
16
|
-
vi.mock('fs/promises', async (importOriginal) => {
|
|
17
|
-
const actual = (await importOriginal());
|
|
18
|
-
return {
|
|
19
|
-
...actual,
|
|
20
|
-
mkdir: hoistedMockMkdir,
|
|
21
|
-
readFile: hoistedMockReadFile,
|
|
22
|
-
writeFile: hoistedMockWriteFile,
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
16
|
const hoistedMockEnv = vi.hoisted(() => vi.fn());
|
|
26
17
|
const hoistedMockSimpleGit = vi.hoisted(() => vi.fn());
|
|
27
18
|
const hoistedMockCheckIsRepo = vi.hoisted(() => vi.fn());
|
|
@@ -44,34 +35,26 @@ const hoistedIsGitRepositoryMock = vi.hoisted(() => vi.fn());
|
|
|
44
35
|
vi.mock('../utils/gitUtils.js', () => ({
|
|
45
36
|
isGitRepository: hoistedIsGitRepositoryMock,
|
|
46
37
|
}));
|
|
47
|
-
const hoistedMockIsNodeError = vi.hoisted(() => vi.fn());
|
|
48
|
-
vi.mock('../utils/errors.js', () => ({
|
|
49
|
-
isNodeError: hoistedMockIsNodeError,
|
|
50
|
-
}));
|
|
51
38
|
const hoistedMockHomedir = vi.hoisted(() => vi.fn());
|
|
52
|
-
vi.mock('os', () =>
|
|
53
|
-
|
|
54
|
-
}));
|
|
55
|
-
const hoistedMockCreateHash = vi.hoisted(() => {
|
|
56
|
-
const mockUpdate = vi.fn().mockReturnThis();
|
|
57
|
-
const mockDigest = vi.fn();
|
|
39
|
+
vi.mock('os', async (importOriginal) => {
|
|
40
|
+
const actual = await importOriginal();
|
|
58
41
|
return {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
digest: mockDigest,
|
|
62
|
-
})),
|
|
63
|
-
mockUpdate,
|
|
64
|
-
mockDigest,
|
|
42
|
+
...actual,
|
|
43
|
+
homedir: hoistedMockHomedir,
|
|
65
44
|
};
|
|
66
45
|
});
|
|
67
|
-
vi.mock('crypto', () => ({
|
|
68
|
-
createHash: hoistedMockCreateHash.createHash,
|
|
69
|
-
}));
|
|
70
46
|
describe('GitService', () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
47
|
+
let testRootDir;
|
|
48
|
+
let projectRoot;
|
|
49
|
+
let homedir;
|
|
50
|
+
let hash;
|
|
51
|
+
beforeEach(async () => {
|
|
52
|
+
testRootDir = await fs.mkdtemp(path.join(os.tmpdir(), 'git-service-test-'));
|
|
53
|
+
projectRoot = path.join(testRootDir, 'project');
|
|
54
|
+
homedir = path.join(testRootDir, 'home');
|
|
55
|
+
await fs.mkdir(projectRoot, { recursive: true });
|
|
56
|
+
await fs.mkdir(homedir, { recursive: true });
|
|
57
|
+
hash = getProjectHash(projectRoot);
|
|
75
58
|
vi.clearAllMocks();
|
|
76
59
|
hoistedIsGitRepositoryMock.mockReturnValue(true);
|
|
77
60
|
hoistedMockExec.mockImplementation((command, callback) => {
|
|
@@ -83,13 +66,7 @@ describe('GitService', () => {
|
|
|
83
66
|
}
|
|
84
67
|
return {};
|
|
85
68
|
});
|
|
86
|
-
|
|
87
|
-
hoistedMockReadFile.mockResolvedValue('');
|
|
88
|
-
hoistedMockWriteFile.mockResolvedValue(undefined);
|
|
89
|
-
hoistedMockIsNodeError.mockImplementation((e) => e instanceof Error);
|
|
90
|
-
hoistedMockHomedir.mockReturnValue(mockHomedir);
|
|
91
|
-
hoistedMockCreateHash.mockUpdate.mockReturnThis();
|
|
92
|
-
hoistedMockCreateHash.mockDigest.mockReturnValue(mockHash);
|
|
69
|
+
hoistedMockHomedir.mockReturnValue(homedir);
|
|
93
70
|
hoistedMockEnv.mockImplementation(() => ({
|
|
94
71
|
checkIsRepo: hoistedMockCheckIsRepo,
|
|
95
72
|
init: hoistedMockInit,
|
|
@@ -113,17 +90,18 @@ describe('GitService', () => {
|
|
|
113
90
|
commit: 'initial',
|
|
114
91
|
});
|
|
115
92
|
});
|
|
116
|
-
afterEach(() => {
|
|
93
|
+
afterEach(async () => {
|
|
117
94
|
vi.restoreAllMocks();
|
|
95
|
+
await fs.rm(testRootDir, { recursive: true, force: true });
|
|
118
96
|
});
|
|
119
97
|
describe('constructor', () => {
|
|
120
|
-
it('should successfully create an instance
|
|
121
|
-
expect(() => new GitService(
|
|
98
|
+
it('should successfully create an instance', () => {
|
|
99
|
+
expect(() => new GitService(projectRoot)).not.toThrow();
|
|
122
100
|
});
|
|
123
101
|
});
|
|
124
102
|
describe('verifyGitAvailability', () => {
|
|
125
103
|
it('should resolve true if git --version command succeeds', async () => {
|
|
126
|
-
const service = new GitService(
|
|
104
|
+
const service = new GitService(projectRoot);
|
|
127
105
|
await expect(service.verifyGitAvailability()).resolves.toBe(true);
|
|
128
106
|
});
|
|
129
107
|
it('should resolve false if git --version command fails', async () => {
|
|
@@ -131,7 +109,7 @@ describe('GitService', () => {
|
|
|
131
109
|
callback(new Error('git not found'));
|
|
132
110
|
return {};
|
|
133
111
|
});
|
|
134
|
-
const service = new GitService(
|
|
112
|
+
const service = new GitService(projectRoot);
|
|
135
113
|
await expect(service.verifyGitAvailability()).resolves.toBe(false);
|
|
136
114
|
});
|
|
137
115
|
});
|
|
@@ -141,11 +119,11 @@ describe('GitService', () => {
|
|
|
141
119
|
callback(new Error('git not found'));
|
|
142
120
|
return {};
|
|
143
121
|
});
|
|
144
|
-
const service = new GitService(
|
|
122
|
+
const service = new GitService(projectRoot);
|
|
145
123
|
await expect(service.initialize()).rejects.toThrow('Checkpointing is enabled, but Git is not installed. Please install Git or disable checkpointing to continue.');
|
|
146
124
|
});
|
|
147
125
|
it('should call setupShadowGitRepository if Git is available', async () => {
|
|
148
|
-
const service = new GitService(
|
|
126
|
+
const service = new GitService(projectRoot);
|
|
149
127
|
const setupSpy = vi
|
|
150
128
|
.spyOn(service, 'setupShadowGitRepository')
|
|
151
129
|
.mockResolvedValue(undefined);
|
|
@@ -154,64 +132,67 @@ describe('GitService', () => {
|
|
|
154
132
|
});
|
|
155
133
|
});
|
|
156
134
|
describe('setupShadowGitRepository', () => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const service = new GitService(mockProjectRoot);
|
|
163
|
-
await service.setupShadowGitRepository();
|
|
164
|
-
const expectedConfigContent = '[user]\n name = Gemini CLI\n email = gemini-cli@google.com\n[commit]\n gpgsign = false\n';
|
|
165
|
-
expect(hoistedMockWriteFile).toHaveBeenCalledWith(gitConfigPath, expectedConfigContent);
|
|
135
|
+
let repoDir;
|
|
136
|
+
let gitConfigPath;
|
|
137
|
+
beforeEach(() => {
|
|
138
|
+
repoDir = path.join(homedir, GEMINI_DIR, 'history', hash);
|
|
139
|
+
gitConfigPath = path.join(repoDir, '.gitconfig');
|
|
166
140
|
});
|
|
167
141
|
it('should create history and repository directories', async () => {
|
|
168
|
-
const service = new GitService(
|
|
142
|
+
const service = new GitService(projectRoot);
|
|
169
143
|
await service.setupShadowGitRepository();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
144
|
+
const stats = await fs.stat(repoDir);
|
|
145
|
+
expect(stats.isDirectory()).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
it('should create a .gitconfig file with the correct content', async () => {
|
|
148
|
+
const service = new GitService(projectRoot);
|
|
149
|
+
await service.setupShadowGitRepository();
|
|
150
|
+
const expectedConfigContent = '[user]\n name = Gemini CLI\n email = gemini-cli@google.com\n[commit]\n gpgsign = false\n';
|
|
151
|
+
const actualConfigContent = await fs.readFile(gitConfigPath, 'utf-8');
|
|
152
|
+
expect(actualConfigContent).toBe(expectedConfigContent);
|
|
173
153
|
});
|
|
174
154
|
it('should initialize git repo in historyDir if not already initialized', async () => {
|
|
175
155
|
hoistedMockCheckIsRepo.mockResolvedValue(false);
|
|
176
|
-
const service = new GitService(
|
|
156
|
+
const service = new GitService(projectRoot);
|
|
177
157
|
await service.setupShadowGitRepository();
|
|
178
158
|
expect(hoistedMockSimpleGit).toHaveBeenCalledWith(repoDir);
|
|
179
159
|
expect(hoistedMockInit).toHaveBeenCalled();
|
|
180
160
|
});
|
|
181
161
|
it('should not initialize git repo if already initialized', async () => {
|
|
182
162
|
hoistedMockCheckIsRepo.mockResolvedValue(true);
|
|
183
|
-
const service = new GitService(
|
|
163
|
+
const service = new GitService(projectRoot);
|
|
184
164
|
await service.setupShadowGitRepository();
|
|
185
165
|
expect(hoistedMockInit).not.toHaveBeenCalled();
|
|
186
166
|
});
|
|
187
167
|
it('should copy .gitignore from projectRoot if it exists', async () => {
|
|
188
|
-
const gitignoreContent =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
return '';
|
|
194
|
-
});
|
|
195
|
-
const service = new GitService(mockProjectRoot);
|
|
168
|
+
const gitignoreContent = 'node_modules/\n.env';
|
|
169
|
+
const visibleGitIgnorePath = path.join(projectRoot, '.gitignore');
|
|
170
|
+
await fs.writeFile(visibleGitIgnorePath, gitignoreContent);
|
|
171
|
+
const service = new GitService(projectRoot);
|
|
196
172
|
await service.setupShadowGitRepository();
|
|
197
|
-
|
|
198
|
-
|
|
173
|
+
const hiddenGitIgnorePath = path.join(repoDir, '.gitignore');
|
|
174
|
+
const copiedContent = await fs.readFile(hiddenGitIgnorePath, 'utf-8');
|
|
175
|
+
expect(copiedContent).toBe(gitignoreContent);
|
|
176
|
+
});
|
|
177
|
+
it('should not create a .gitignore in shadow repo if project .gitignore does not exist', async () => {
|
|
178
|
+
const service = new GitService(projectRoot);
|
|
179
|
+
await service.setupShadowGitRepository();
|
|
180
|
+
const hiddenGitIgnorePath = path.join(repoDir, '.gitignore');
|
|
181
|
+
// An empty string is written if the file doesn't exist.
|
|
182
|
+
const content = await fs.readFile(hiddenGitIgnorePath, 'utf-8');
|
|
183
|
+
expect(content).toBe('');
|
|
199
184
|
});
|
|
200
185
|
it('should throw an error if reading projectRoot .gitignore fails with other errors', async () => {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
});
|
|
208
|
-
hoistedMockIsNodeError.mockImplementation((e) => e instanceof Error);
|
|
209
|
-
const service = new GitService(mockProjectRoot);
|
|
210
|
-
await expect(service.setupShadowGitRepository()).rejects.toThrow('Read permission denied');
|
|
186
|
+
const visibleGitIgnorePath = path.join(projectRoot, '.gitignore');
|
|
187
|
+
// Create a directory instead of a file to cause a read error
|
|
188
|
+
await fs.mkdir(visibleGitIgnorePath);
|
|
189
|
+
const service = new GitService(projectRoot);
|
|
190
|
+
// EISDIR is the expected error code on Unix-like systems
|
|
191
|
+
await expect(service.setupShadowGitRepository()).rejects.toThrow(/EISDIR: illegal operation on a directory, read|EBUSY: resource busy or locked, read/);
|
|
211
192
|
});
|
|
212
193
|
it('should make an initial commit if no commits exist in history repo', async () => {
|
|
213
194
|
hoistedMockCheckIsRepo.mockResolvedValue(false);
|
|
214
|
-
const service = new GitService(
|
|
195
|
+
const service = new GitService(projectRoot);
|
|
215
196
|
await service.setupShadowGitRepository();
|
|
216
197
|
expect(hoistedMockCommit).toHaveBeenCalledWith('Initial commit', {
|
|
217
198
|
'--allow-empty': null,
|
|
@@ -219,7 +200,7 @@ describe('GitService', () => {
|
|
|
219
200
|
});
|
|
220
201
|
it('should not make an initial commit if commits already exist', async () => {
|
|
221
202
|
hoistedMockCheckIsRepo.mockResolvedValue(true);
|
|
222
|
-
const service = new GitService(
|
|
203
|
+
const service = new GitService(projectRoot);
|
|
223
204
|
await service.setupShadowGitRepository();
|
|
224
205
|
expect(hoistedMockCommit).not.toHaveBeenCalled();
|
|
225
206
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitService.test.js","sourceRoot":"","sources":["../../../src/services/gitService.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"gitService.test.js","sourceRoot":"","sources":["../../../src/services/gitService.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/** A structured result from a shell command execution. */
|
|
7
|
+
export interface ShellExecutionResult {
|
|
8
|
+
/** The raw, unprocessed output buffer. */
|
|
9
|
+
rawOutput: Buffer;
|
|
10
|
+
/** The combined, decoded stdout and stderr as a string. */
|
|
11
|
+
output: string;
|
|
12
|
+
/** The decoded stdout as a string. */
|
|
13
|
+
stdout: string;
|
|
14
|
+
/** The decoded stderr as a string. */
|
|
15
|
+
stderr: string;
|
|
16
|
+
/** The process exit code, or null if terminated by a signal. */
|
|
17
|
+
exitCode: number | null;
|
|
18
|
+
/** The signal that terminated the process, if any. */
|
|
19
|
+
signal: NodeJS.Signals | null;
|
|
20
|
+
/** An error object if the process failed to spawn. */
|
|
21
|
+
error: Error | null;
|
|
22
|
+
/** A boolean indicating if the command was aborted by the user. */
|
|
23
|
+
aborted: boolean;
|
|
24
|
+
/** The process ID of the spawned shell. */
|
|
25
|
+
pid: number | undefined;
|
|
26
|
+
}
|
|
27
|
+
/** A handle for an ongoing shell execution. */
|
|
28
|
+
export interface ShellExecutionHandle {
|
|
29
|
+
/** The process ID of the spawned shell. */
|
|
30
|
+
pid: number | undefined;
|
|
31
|
+
/** A promise that resolves with the complete execution result. */
|
|
32
|
+
result: Promise<ShellExecutionResult>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Describes a structured event emitted during shell command execution.
|
|
36
|
+
*/
|
|
37
|
+
export type ShellOutputEvent = {
|
|
38
|
+
/** The event contains a chunk of output data. */
|
|
39
|
+
type: 'data';
|
|
40
|
+
/** The stream from which the data originated. */
|
|
41
|
+
stream: 'stdout' | 'stderr';
|
|
42
|
+
/** The decoded string chunk. */
|
|
43
|
+
chunk: string;
|
|
44
|
+
} | {
|
|
45
|
+
/** Signals that the output stream has been identified as binary. */
|
|
46
|
+
type: 'binary_detected';
|
|
47
|
+
} | {
|
|
48
|
+
/** Provides progress updates for a binary stream. */
|
|
49
|
+
type: 'binary_progress';
|
|
50
|
+
/** The total number of bytes received so far. */
|
|
51
|
+
bytesReceived: number;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* A centralized service for executing shell commands with robust process
|
|
55
|
+
* management, cross-platform compatibility, and streaming output capabilities.
|
|
56
|
+
*
|
|
57
|
+
*/
|
|
58
|
+
export declare class ShellExecutionService {
|
|
59
|
+
/**
|
|
60
|
+
* Executes a shell command using `spawn`, capturing all output and lifecycle events.
|
|
61
|
+
*
|
|
62
|
+
* @param commandToExecute The exact command string to run.
|
|
63
|
+
* @param cwd The working directory to execute the command in.
|
|
64
|
+
* @param onOutputEvent A callback for streaming structured events about the execution, including data chunks and status updates.
|
|
65
|
+
* @param abortSignal An AbortSignal to terminate the process and its children.
|
|
66
|
+
* @returns An object containing the process ID (pid) and a promise that
|
|
67
|
+
* resolves with the complete execution result.
|
|
68
|
+
*/
|
|
69
|
+
static execute(commandToExecute: string, cwd: string, onOutputEvent: (event: ShellOutputEvent) => void, abortSignal: AbortSignal): ShellExecutionHandle;
|
|
70
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import { TextDecoder } from 'util';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import stripAnsi from 'strip-ansi';
|
|
10
|
+
import { getCachedEncodingForBuffer } from '../utils/systemEncoding.js';
|
|
11
|
+
import { isBinary } from '../utils/textUtils.js';
|
|
12
|
+
const SIGKILL_TIMEOUT_MS = 200;
|
|
13
|
+
/**
|
|
14
|
+
* A centralized service for executing shell commands with robust process
|
|
15
|
+
* management, cross-platform compatibility, and streaming output capabilities.
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
export class ShellExecutionService {
|
|
19
|
+
/**
|
|
20
|
+
* Executes a shell command using `spawn`, capturing all output and lifecycle events.
|
|
21
|
+
*
|
|
22
|
+
* @param commandToExecute The exact command string to run.
|
|
23
|
+
* @param cwd The working directory to execute the command in.
|
|
24
|
+
* @param onOutputEvent A callback for streaming structured events about the execution, including data chunks and status updates.
|
|
25
|
+
* @param abortSignal An AbortSignal to terminate the process and its children.
|
|
26
|
+
* @returns An object containing the process ID (pid) and a promise that
|
|
27
|
+
* resolves with the complete execution result.
|
|
28
|
+
*/
|
|
29
|
+
static execute(commandToExecute, cwd, onOutputEvent, abortSignal) {
|
|
30
|
+
const isWindows = os.platform() === 'win32';
|
|
31
|
+
const shell = isWindows ? 'cmd.exe' : 'bash';
|
|
32
|
+
const shellArgs = [isWindows ? '/c' : '-c', commandToExecute];
|
|
33
|
+
const child = spawn(shell, shellArgs, {
|
|
34
|
+
cwd,
|
|
35
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
36
|
+
detached: !isWindows, // Use process groups on non-Windows for robust killing
|
|
37
|
+
env: {
|
|
38
|
+
...process.env,
|
|
39
|
+
GEMINI_CLI: '1',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
const result = new Promise((resolve) => {
|
|
43
|
+
// Use decoders to handle multi-byte characters safely (for streaming output).
|
|
44
|
+
let stdoutDecoder = null;
|
|
45
|
+
let stderrDecoder = null;
|
|
46
|
+
let stdout = '';
|
|
47
|
+
let stderr = '';
|
|
48
|
+
const outputChunks = [];
|
|
49
|
+
let error = null;
|
|
50
|
+
let exited = false;
|
|
51
|
+
let isStreamingRawContent = true;
|
|
52
|
+
const MAX_SNIFF_SIZE = 4096;
|
|
53
|
+
let sniffedBytes = 0;
|
|
54
|
+
const handleOutput = (data, stream) => {
|
|
55
|
+
if (!stdoutDecoder || !stderrDecoder) {
|
|
56
|
+
const encoding = getCachedEncodingForBuffer(data);
|
|
57
|
+
try {
|
|
58
|
+
stdoutDecoder = new TextDecoder(encoding);
|
|
59
|
+
stderrDecoder = new TextDecoder(encoding);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// If the encoding is not supported, fall back to utf-8.
|
|
63
|
+
// This can happen on some platforms for certain encodings like 'utf-32le'.
|
|
64
|
+
stdoutDecoder = new TextDecoder('utf-8');
|
|
65
|
+
stderrDecoder = new TextDecoder('utf-8');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
outputChunks.push(data);
|
|
69
|
+
// Binary detection logic. This only runs until we've made a determination.
|
|
70
|
+
if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
|
|
71
|
+
const sniffBuffer = Buffer.concat(outputChunks.slice(0, 20));
|
|
72
|
+
sniffedBytes = sniffBuffer.length;
|
|
73
|
+
if (isBinary(sniffBuffer)) {
|
|
74
|
+
// Change state to stop streaming raw content.
|
|
75
|
+
isStreamingRawContent = false;
|
|
76
|
+
onOutputEvent({ type: 'binary_detected' });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const decodedChunk = stream === 'stdout'
|
|
80
|
+
? stdoutDecoder.decode(data, { stream: true })
|
|
81
|
+
: stderrDecoder.decode(data, { stream: true });
|
|
82
|
+
const strippedChunk = stripAnsi(decodedChunk);
|
|
83
|
+
if (stream === 'stdout') {
|
|
84
|
+
stdout += strippedChunk;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
stderr += strippedChunk;
|
|
88
|
+
}
|
|
89
|
+
if (isStreamingRawContent) {
|
|
90
|
+
onOutputEvent({ type: 'data', stream, chunk: strippedChunk });
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const totalBytes = outputChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
94
|
+
onOutputEvent({ type: 'binary_progress', bytesReceived: totalBytes });
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
child.stdout.on('data', (data) => handleOutput(data, 'stdout'));
|
|
98
|
+
child.stderr.on('data', (data) => handleOutput(data, 'stderr'));
|
|
99
|
+
child.on('error', (err) => {
|
|
100
|
+
error = err;
|
|
101
|
+
});
|
|
102
|
+
const abortHandler = async () => {
|
|
103
|
+
if (child.pid && !exited) {
|
|
104
|
+
if (isWindows) {
|
|
105
|
+
spawn('taskkill', ['/pid', child.pid.toString(), '/f', '/t']);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
try {
|
|
109
|
+
// Kill the entire process group (negative PID).
|
|
110
|
+
// SIGTERM first, then SIGKILL if it doesn't die.
|
|
111
|
+
process.kill(-child.pid, 'SIGTERM');
|
|
112
|
+
await new Promise((res) => setTimeout(res, SIGKILL_TIMEOUT_MS));
|
|
113
|
+
if (!exited) {
|
|
114
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (_e) {
|
|
118
|
+
// Fall back to killing just the main process if group kill fails.
|
|
119
|
+
if (!exited)
|
|
120
|
+
child.kill('SIGKILL');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
abortSignal.addEventListener('abort', abortHandler, { once: true });
|
|
126
|
+
child.on('exit', (code, signal) => {
|
|
127
|
+
exited = true;
|
|
128
|
+
abortSignal.removeEventListener('abort', abortHandler);
|
|
129
|
+
if (stdoutDecoder) {
|
|
130
|
+
stdout += stripAnsi(stdoutDecoder.decode());
|
|
131
|
+
}
|
|
132
|
+
if (stderrDecoder) {
|
|
133
|
+
stderr += stripAnsi(stderrDecoder.decode());
|
|
134
|
+
}
|
|
135
|
+
const finalBuffer = Buffer.concat(outputChunks);
|
|
136
|
+
resolve({
|
|
137
|
+
rawOutput: finalBuffer,
|
|
138
|
+
output: stdout + (stderr ? `\n${stderr}` : ''),
|
|
139
|
+
stdout,
|
|
140
|
+
stderr,
|
|
141
|
+
exitCode: code,
|
|
142
|
+
signal,
|
|
143
|
+
error,
|
|
144
|
+
aborted: abortSignal.aborted,
|
|
145
|
+
pid: child.pid,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
return { pid: child.pid, result };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=shellExecutionService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shellExecutionService.js","sourceRoot":"","sources":["../../../src/services/shellExecutionService.ts"],"names":[],"mappings":"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"}
|