@dongju101/gemini-mcp 0.1.0

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/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # gemini-mcp
2
+
3
+ Gemini CLI를 Subprocess로 호출하는 MCP Server.
4
+ Gemini Pro 구독(Google AI Pro)의 OAuth 인증을 활용하여 API Key 없이 Gemini를 사용한다.
5
+
6
+ ## 사전 조건
7
+
8
+ - Node.js 18+
9
+ - [Gemini CLI](https://github.com/google/gemini-cli) 글로벌 설치 및 OAuth 인증 완료
10
+
11
+ ```bash
12
+ npm install -g @google/gemini-cli
13
+ gemini # 최초 실행 시 OAuth 인증 진행
14
+ ```
15
+
16
+ ## 설치
17
+
18
+ ```bash
19
+ git clone <repository-url>
20
+ cd OAuth-gemini-mcp
21
+ npm install
22
+ npm run build
23
+ ```
24
+
25
+ ## Claude Code 등록
26
+
27
+ ```bash
28
+ claude mcp add gemini-conversation -s user -- \
29
+ node /path/to/OAuth-gemini-mcp/dist/server.js
30
+ ```
31
+
32
+ ## 설정
33
+
34
+ `gemini_workspace/config.json`으로 Server 동작을 제어한다.
35
+ Config 파일이 없거나 잘못된 경우 각 항목의 기본값이 적용된다.
36
+
37
+ ```json
38
+ {
39
+ "file_access": true,
40
+ "model": "gemini-3-pro-preview"
41
+ }
42
+ ```
43
+
44
+ ### 파일 접근 (file_access)
45
+
46
+ Gemini가 프로젝트 파일에 접근할 수 있는지를 제어한다.
47
+
48
+ | 값 | 동작 |
49
+ |------|------|
50
+ | `true` | Project Root에서 실행. Gemini가 프로젝트 파일을 읽고 분석 가능. |
51
+ | `false` (기본값) | 격리된 Workspace에서 실행. 프로젝트 파일 접근 불가. |
52
+
53
+ ### Model 선택 (model)
54
+
55
+ Gemini CLI에서 사용할 Model을 지정한다.
56
+
57
+ | 값 | 동작 |
58
+ |------|------|
59
+ | 미지정 (기본값) | `gemini-3-pro-preview` 사용 |
60
+ | `"gemini-3-flash-preview"` | 단순 질의, 번역, 요약 등 빠른 응답이 우선인 작업 |
61
+ | `"gemini-2.5-pro"` / `"gemini-2.5-flash"` | 3 계열 모델이 quota 초과 또는 오류 시 대체 |
62
+
63
+ Tool 호출 시 `model` Parameter를 직접 지정하면 Config 설정보다 우선 적용된다.
64
+
65
+ ### 설정 예시
66
+
67
+ ```bash
68
+ # 기본 모델 + 파일 접근 활성화
69
+ echo '{"file_access": true}' > gemini_workspace/config.json
70
+
71
+ # 특정 Model 고정 + 파일 접근 활성화
72
+ echo '{"file_access": true, "model": "gemini-3-flash-preview"}' > gemini_workspace/config.json
73
+ ```
74
+
75
+ ## Tool 목록
76
+
77
+ | Tool | Parameter | 용도 |
78
+ |------|-----------|------|
79
+ | `gemini_chat` | `message`, `model`, `session`, `sandbox`, `thinking` | Gemini와 대화형 질의. `--resume`으로 이전 대화 맥락 자동 유지. |
80
+ | `gemini_reset` | `session` | 대화 Session 초기화. 다음 호출부터 새 대화 시작. |
81
+
82
+ ### gemini_chat Parameter
83
+
84
+ | Parameter | 기본값 | 설명 |
85
+ |-----------|--------|------|
86
+ | `message` | (필수) | Gemini에 전달할 메시지 |
87
+ | `model` | `""` (gemini-3-pro-preview) | 사용할 모델 |
88
+ | `session` | `"default"` | Session 이름. 독립적인 대화 맥락 유지 |
89
+ | `sandbox` | `false` | Sandbox mode로 code를 격리 환경에서 실행 |
90
+ | `thinking` | `"low"` | Gemini thinking level (`low` / `medium` / `high`) |
91
+
92
+ ## Multi-Session
93
+
94
+ 여러 Terminal에서 독립적인 대화를 유지할 수 있다.
95
+ `session` Parameter로 Session 이름을 지정하면 각각의 대화 맥락이 격리된다.
96
+
97
+ ```
98
+ Terminal A (frontend 작업):
99
+ gemini_chat(message="React Component 구조 설명해줘", session="frontend")
100
+ gemini_chat(message="위 구조에서 상태 관리는?", session="frontend") -> 이전 맥락 유지
101
+
102
+ Terminal B (backend 작업):
103
+ gemini_chat(message="Express Router 패턴 알려줘", session="backend")
104
+ gemini_chat(message="위 패턴에 인증 추가하려면?", session="backend") -> 독립적 맥락 유지
105
+ ```
106
+
107
+ - `session` 미지정 시 `"default"` Session 사용
108
+ - Session별 Workspace가 `gemini_workspace/sessions/{session_name}/`에 자동 생성
109
+ - Claude Code를 종료했다 재시작해도 `--resume`으로 직전 대화를 이어갈 수 있다
110
+ - `gemini_reset(session="frontend")`: 특정 Session만 초기화
111
+ - `gemini_reset()`: 전체 Session 초기화
112
+
113
+ ## 디렉토리 구조
114
+
115
+ ```
116
+ OAuth-gemini-mcp/
117
+ src/
118
+ server.ts # FastMCP Server 진입점
119
+ runner.ts # Gemini CLI Subprocess 실행기
120
+ types.ts # Type 정의 (GeminiResponse, RunnerEvent 등)
121
+ __tests__/ # Vitest 테스트
122
+ dist/ # TypeScript 빌드 출력
123
+ gemini_workspace/ # CLI 실행용 격리 디렉토리
124
+ config.json # Server 설정 (file_access, model)
125
+ .gemini/
126
+ settings.json # CLI 설정 (thinkingConfig, security.auth)
127
+ sessions/ # Session별 독립 Workspace
128
+ docs/ # 기술 문서
129
+ package.json
130
+ tsconfig.json
131
+ vitest.config.ts
132
+ ```
133
+
134
+ ## 테스트
135
+
136
+ ```bash
137
+ npm test # Vitest 실행
138
+ npm run test:watch # Watch mode
139
+ ```
140
+
141
+ ## 기술 문서
142
+
143
+ - [전략 분석](docs/strategy-analysis.md) -- 5개 접근 전략 비교 및 선정 근거
144
+ - [기술 조사](docs/technical-research.md) -- CLI 동작 분석, stream-json 구조, Cold Start 분석
145
+ - [Architecture](docs/architecture.md) -- 시스템 구조, 데이터 흐름, 구현 설계
146
+ - [성능 분석](docs/performance-token-analysis.md) -- Token 소비 및 Latency 최적화 분석
147
+ - [정책 준수 분석](docs/policy-compliance-analysis.md) -- Gemini ToS 및 사용 정책 준수 분석
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Gemini CLI Headless stream-json 실행기
3
+ *
4
+ * gemini_mcp/runner.py의 TypeScript 전환.
5
+ * child_process.spawn + readline으로 JSONL Stream을 파싱한다.
6
+ */
7
+ import type { RunnerEvent } from "./types";
8
+ export declare const MAX_MESSAGE_LENGTH = 30000;
9
+ export declare const MAX_MESSAGE_BYTES = 100000;
10
+ export declare const MAX_TOTAL_TIMEOUT = 300;
11
+ export declare const FALLBACK_MODEL = "gemini-3-flash-preview";
12
+ /**
13
+ * readline AsyncIterator에서 timeout 적용하여 한 줄을 읽는다.
14
+ * null 반환 시 timeout 발생.
15
+ */
16
+ export declare function readLineWithTimeout(iterator: AsyncIterableIterator<string>, timeoutMs: number): Promise<IteratorResult<string> | null>;
17
+ export declare class GeminiCLIRunner {
18
+ private readonly workspaceDir;
19
+ private readonly timeout;
20
+ private readonly totalTimeout;
21
+ private readonly activeSessions;
22
+ private readonly sessionDirs;
23
+ private readonly sessionChains;
24
+ private readonly cliPath;
25
+ private readonly env;
26
+ constructor(workspaceDir: string, timeout?: number, totalTimeout?: number);
27
+ private buildCmd;
28
+ private static processEvent;
29
+ /**
30
+ * Gemini CLI를 실행하며 JSONL Event를 실시간으로 yield한다.
31
+ * RESOURCE_EXHAUSTED 발생 시 FALLBACK_MODEL로 1회 자동 재시도한다.
32
+ */
33
+ chatStream(message: string, model?: string | null, cwd?: string | null, sessionId?: string, sandbox?: boolean): AsyncGenerator<RunnerEvent>;
34
+ private withSessionLock;
35
+ private chatStreamInner;
36
+ private runCli;
37
+ reset(sessionId?: string, sessionDirs?: string[]): void;
38
+ /**
39
+ * Gemini CLI가 ~/.gemini/tmp/<sha256(cwd)>/chats/ 에 persist하는
40
+ * session 데이터를 삭제한다.
41
+ */
42
+ private clearGeminiChats;
43
+ }
44
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAA4B,WAAW,EAAE,MAAM,SAAS,CAAC;AAErE,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,iBAAiB,SAAU,CAAC;AACzC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAkDvD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,qBAAqB,CAAC,MAAM,CAAC,EACvC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAQxC;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAyB;gBAG3C,YAAY,EAAE,MAAM,EACpB,OAAO,SAAM,EACb,YAAY,SAAoB;IASlC,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,MAAM,CAAC,YAAY;IA+C3B;;;OAGG;IACI,UAAU,CACf,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAM,GAAG,IAAW,EAC3B,GAAG,GAAE,MAAM,GAAG,IAAW,EACzB,SAAS,SAAY,EACrB,OAAO,UAAQ,GACd,cAAc,CAAC,WAAW,CAAC;YAoCf,eAAe;YAoBf,eAAe;YAiCf,MAAM;IAgJrB,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAiBvD;;;OAGG;IACH,OAAO,CAAC,gBAAgB;CASzB"}
package/dist/runner.js ADDED
@@ -0,0 +1,411 @@
1
+ "use strict";
2
+ /**
3
+ * Gemini CLI Headless stream-json 실행기
4
+ *
5
+ * gemini_mcp/runner.py의 TypeScript 전환.
6
+ * child_process.spawn + readline으로 JSONL Stream을 파싱한다.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.GeminiCLIRunner = exports.FALLBACK_MODEL = exports.MAX_TOTAL_TIMEOUT = exports.MAX_MESSAGE_BYTES = exports.MAX_MESSAGE_LENGTH = void 0;
43
+ exports.readLineWithTimeout = readLineWithTimeout;
44
+ const child_process_1 = require("child_process");
45
+ const readline_1 = require("readline");
46
+ const crypto_1 = require("crypto");
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ exports.MAX_MESSAGE_LENGTH = 30_000;
50
+ exports.MAX_MESSAGE_BYTES = 100_000;
51
+ exports.MAX_TOTAL_TIMEOUT = 300;
52
+ exports.FALLBACK_MODEL = "gemini-3-flash-preview";
53
+ const QUOTA_ERROR_PATTERN = "RESOURCE_EXHAUSTED";
54
+ const FILTERED_ENV_PREFIXES = ["CI", "GEMINI_SYSTEM_MD", "GEMINI_WRITE_SYSTEM_MD", "GEMINI_CLI_HOME"];
55
+ function createEmptyResponse(error) {
56
+ return {
57
+ content: "",
58
+ model: "",
59
+ sessionId: "",
60
+ totalTokens: 0,
61
+ toolCalls: [],
62
+ error: error ?? null,
63
+ };
64
+ }
65
+ function safeKill(proc) {
66
+ try {
67
+ proc.kill();
68
+ }
69
+ catch {
70
+ // 이미 종료된 경우 무시
71
+ }
72
+ }
73
+ function findCli() {
74
+ try {
75
+ const result = (0, child_process_1.execFileSync)("which", ["gemini"], { encoding: "utf-8" });
76
+ return result.trim();
77
+ }
78
+ catch {
79
+ throw new Error("Gemini CLI가 설치되지 않았습니다. " +
80
+ "'npm install -g @google/gemini-cli'로 설치하세요.");
81
+ }
82
+ }
83
+ function buildEnv(geminiCliHome) {
84
+ const env = {};
85
+ for (const [k, v] of Object.entries(process.env)) {
86
+ if (v === undefined)
87
+ continue;
88
+ const skip = FILTERED_ENV_PREFIXES.some((prefix) => k.startsWith(prefix));
89
+ if (!skip) {
90
+ env[k] = v;
91
+ }
92
+ }
93
+ if (geminiCliHome) {
94
+ env["GEMINI_CLI_HOME"] = geminiCliHome;
95
+ }
96
+ return env;
97
+ }
98
+ /**
99
+ * readline AsyncIterator에서 timeout 적용하여 한 줄을 읽는다.
100
+ * null 반환 시 timeout 발생.
101
+ */
102
+ async function readLineWithTimeout(iterator, timeoutMs) {
103
+ let timer;
104
+ const timeoutPromise = new Promise((resolve) => {
105
+ timer = setTimeout(() => resolve(null), timeoutMs);
106
+ });
107
+ const result = await Promise.race([iterator.next(), timeoutPromise]);
108
+ clearTimeout(timer);
109
+ return result;
110
+ }
111
+ class GeminiCLIRunner {
112
+ workspaceDir;
113
+ timeout;
114
+ totalTimeout;
115
+ activeSessions = new Set();
116
+ sessionDirs = new Map();
117
+ sessionChains = new Map();
118
+ cliPath;
119
+ env;
120
+ constructor(workspaceDir, timeout = 120, totalTimeout = exports.MAX_TOTAL_TIMEOUT) {
121
+ this.workspaceDir = workspaceDir;
122
+ this.timeout = timeout;
123
+ this.totalTimeout = totalTimeout;
124
+ this.cliPath = findCli();
125
+ this.env = buildEnv(workspaceDir);
126
+ }
127
+ buildCmd(message, model, sessionId, sandbox) {
128
+ const isActive = this.activeSessions.has(sessionId);
129
+ const cmd = [this.cliPath];
130
+ if (isActive)
131
+ cmd.push("--resume");
132
+ if (sandbox)
133
+ cmd.push("-s");
134
+ cmd.push("-p", message);
135
+ if (model)
136
+ cmd.push("-m", model);
137
+ cmd.push("--output-format", "stream-json", "-y");
138
+ return cmd;
139
+ }
140
+ static processEvent(event, response, contentParts, pendingTools) {
141
+ const eventType = event.type;
142
+ if (eventType === "init") {
143
+ response.sessionId = event.session_id || "";
144
+ response.model = event.model || "";
145
+ }
146
+ else if (eventType === "message" && event.role === "assistant") {
147
+ contentParts.push(event.content || "");
148
+ }
149
+ else if (eventType === "tool_use") {
150
+ contentParts.length = 0;
151
+ const toolId = event.tool_id || "";
152
+ const tc = {
153
+ name: event.tool_name || "",
154
+ parameters: event.parameters || {},
155
+ status: "",
156
+ output: "",
157
+ };
158
+ pendingTools.set(toolId, tc);
159
+ response.toolCalls.push(tc);
160
+ }
161
+ else if (eventType === "tool_result") {
162
+ const toolId = event.tool_id || "";
163
+ const pending = pendingTools.get(toolId);
164
+ if (pending) {
165
+ pending.status = event.status || "";
166
+ pending.output = event.output || "";
167
+ }
168
+ contentParts.length = 0;
169
+ }
170
+ else if (eventType === "result") {
171
+ const stats = event.stats || {};
172
+ response.totalTokens = stats.total_tokens || 0;
173
+ if (event.status !== "success") {
174
+ const rawError = event.error;
175
+ response.error =
176
+ typeof rawError === "string"
177
+ ? rawError
178
+ : rawError
179
+ ? JSON.stringify(rawError)
180
+ : "Unknown error";
181
+ }
182
+ }
183
+ }
184
+ /**
185
+ * Gemini CLI를 실행하며 JSONL Event를 실시간으로 yield한다.
186
+ * RESOURCE_EXHAUSTED 발생 시 FALLBACK_MODEL로 1회 자동 재시도한다.
187
+ */
188
+ async *chatStream(message, model = null, cwd = null, sessionId = "default", sandbox = false) {
189
+ // 빈 메시지 검증
190
+ if (!message || !message.trim()) {
191
+ yield { type: "_response", data: createEmptyResponse("Message가 비어 있습니다.") };
192
+ return;
193
+ }
194
+ // 길이 제한 검증
195
+ if (message.length > exports.MAX_MESSAGE_LENGTH) {
196
+ yield {
197
+ type: "_response",
198
+ data: createEmptyResponse(`Message가 최대 길이(${exports.MAX_MESSAGE_LENGTH.toLocaleString()}자)를 초과합니다.`),
199
+ };
200
+ return;
201
+ }
202
+ // 바이트 크기 제한 검증
203
+ const msgBytes = Buffer.byteLength(message, "utf8");
204
+ if (msgBytes > exports.MAX_MESSAGE_BYTES) {
205
+ yield {
206
+ type: "_response",
207
+ data: createEmptyResponse(`Message가 최대 크기(${exports.MAX_MESSAGE_BYTES.toLocaleString()} bytes)를 초과합니다.`),
208
+ };
209
+ return;
210
+ }
211
+ // Per-Session 직렬 실행
212
+ yield* this.withSessionLock(sessionId, () => this.chatStreamInner(message, model, cwd, sessionId, sandbox));
213
+ }
214
+ async *withSessionLock(sessionId, fn) {
215
+ // Promise 체이닝으로 Per-Session 직렬화 구현
216
+ let resolve;
217
+ const gate = new Promise((r) => {
218
+ resolve = r;
219
+ });
220
+ const prev = this.sessionChains.get(sessionId) ?? Promise.resolve();
221
+ this.sessionChains.set(sessionId, gate);
222
+ await prev;
223
+ try {
224
+ yield* fn();
225
+ }
226
+ finally {
227
+ resolve();
228
+ }
229
+ }
230
+ async *chatStreamInner(message, model, cwd, sessionId, sandbox) {
231
+ let responseEvent = null;
232
+ for await (const event of this.runCli(message, model, cwd, sessionId, sandbox)) {
233
+ if (event.type === "_response") {
234
+ responseEvent = event;
235
+ }
236
+ else {
237
+ yield event;
238
+ }
239
+ }
240
+ // Quota 초과 시 FALLBACK_MODEL로 1회 재시도
241
+ if (responseEvent !== null &&
242
+ responseEvent.type === "_response" &&
243
+ responseEvent.data.error &&
244
+ typeof responseEvent.data.error === "string" &&
245
+ responseEvent.data.error.includes(QUOTA_ERROR_PATTERN) &&
246
+ (model || "").toLowerCase() !== exports.FALLBACK_MODEL) {
247
+ yield { type: "_fallback", model: exports.FALLBACK_MODEL };
248
+ yield* this.runCli(message, exports.FALLBACK_MODEL, cwd, sessionId, sandbox);
249
+ }
250
+ else if (responseEvent !== null) {
251
+ yield responseEvent;
252
+ }
253
+ }
254
+ async *runCli(message, model, cwd, sessionId, sandbox) {
255
+ const cmd = this.buildCmd(message, model, sessionId, sandbox);
256
+ const workDir = cwd || this.workspaceDir;
257
+ fs.mkdirSync(workDir, { recursive: true });
258
+ const proc = (0, child_process_1.spawn)(cmd[0], cmd.slice(1), {
259
+ cwd: workDir,
260
+ env: this.env,
261
+ stdio: ["pipe", "pipe", "pipe"],
262
+ });
263
+ // spawn은 ENOENT를 동기 throw하지 않고 error event로 emit한다.
264
+ // 첫 tick에서 error/spawn 중 하나를 대기하여 실행 실패를 감지한다.
265
+ const spawnResult = await new Promise((resolve) => {
266
+ proc.once("error", (err) => resolve(err));
267
+ proc.once("spawn", () => resolve(null));
268
+ });
269
+ if (spawnResult !== null) {
270
+ yield {
271
+ type: "_response",
272
+ data: createEmptyResponse(`프로세스 실행 실패: ${spawnResult.message}`),
273
+ };
274
+ return;
275
+ }
276
+ const response = createEmptyResponse();
277
+ const contentParts = [];
278
+ const pendingTools = new Map();
279
+ const stderrChunks = [];
280
+ // stderr 드레인
281
+ if (proc.stderr) {
282
+ proc.stderr.on("data", (chunk) => {
283
+ stderrChunks.push(chunk);
284
+ });
285
+ }
286
+ try {
287
+ if (!proc.stdout) {
288
+ response.error = "stdout 스트림을 열 수 없습니다.";
289
+ yield { type: "_response", data: response };
290
+ return;
291
+ }
292
+ const rl = (0, readline_1.createInterface)({
293
+ input: proc.stdout,
294
+ crlfDelay: Infinity,
295
+ });
296
+ const iterator = rl[Symbol.asyncIterator]();
297
+ const startTime = Date.now();
298
+ while (true) {
299
+ const elapsedMs = Date.now() - startTime;
300
+ const remainingMs = this.totalTimeout * 1000 - elapsedMs;
301
+ if (remainingMs <= 0) {
302
+ safeKill(proc);
303
+ response.error = `총 응답 시간 초과 (${this.totalTimeout}초)`;
304
+ break;
305
+ }
306
+ const lineTimeoutMs = Math.min(this.timeout * 1000, remainingMs);
307
+ const result = await readLineWithTimeout(iterator, lineTimeoutMs);
308
+ if (result === null) {
309
+ // timeout
310
+ safeKill(proc);
311
+ if (remainingMs <= this.timeout * 1000) {
312
+ response.error = `총 응답 시간 초과 (${this.totalTimeout}초)`;
313
+ }
314
+ else {
315
+ response.error = `응답 시간 초과 (${this.timeout}초)`;
316
+ }
317
+ break;
318
+ }
319
+ if (result.done)
320
+ break;
321
+ const decoded = result.value.trim();
322
+ if (!decoded)
323
+ continue;
324
+ let event;
325
+ try {
326
+ event = JSON.parse(decoded);
327
+ }
328
+ catch {
329
+ continue;
330
+ }
331
+ GeminiCLIRunner.processEvent(event, response, contentParts, pendingTools);
332
+ yield event;
333
+ }
334
+ rl.close();
335
+ }
336
+ finally {
337
+ // Process 정리 대기
338
+ await new Promise((resolve) => {
339
+ if (proc.exitCode !== null) {
340
+ resolve();
341
+ }
342
+ else {
343
+ proc.on("exit", () => resolve());
344
+ // 아직 실행 중이면 강제 종료
345
+ if (proc.exitCode === null) {
346
+ safeKill(proc);
347
+ }
348
+ }
349
+ });
350
+ }
351
+ // stderr 기반 에러 감지
352
+ if (proc.exitCode !== 0 && contentParts.length === 0 && !response.error) {
353
+ const stderr = Buffer.concat(stderrChunks).toString().trim();
354
+ const stderrLower = stderr.toLowerCase();
355
+ if (stderrLower.includes("auth") || stderrLower.includes("token")) {
356
+ response.error = "인증 실패. 'gemini' 명령어로 재인증이 필요합니다.";
357
+ }
358
+ else {
359
+ response.error = stderr || `Exit code: ${proc.exitCode}`;
360
+ }
361
+ }
362
+ response.content = contentParts.join("");
363
+ if (!response.content && !response.error) {
364
+ if (response.toolCalls.length > 0) {
365
+ const summaries = response.toolCalls.map((tc) => `- ${tc.name}: ${tc.status || "completed"}`);
366
+ response.content = "Tool 실행 결과:\n" + summaries.join("\n");
367
+ }
368
+ else {
369
+ response.content = "(응답 없음)";
370
+ }
371
+ }
372
+ if (!response.error) {
373
+ this.activeSessions.add(sessionId);
374
+ this.sessionDirs.set(sessionId, workDir);
375
+ }
376
+ yield { type: "_response", data: response };
377
+ }
378
+ reset(sessionId, sessionDirs) {
379
+ // Gemini CLI disk session 정리
380
+ const dirs = sessionDirs
381
+ ?? (sessionId ? [this.sessionDirs.get(sessionId)] : [...this.sessionDirs.values()]);
382
+ for (const dir of dirs) {
383
+ if (dir)
384
+ this.clearGeminiChats(dir);
385
+ }
386
+ if (sessionId === undefined) {
387
+ this.activeSessions.clear();
388
+ this.sessionDirs.clear();
389
+ }
390
+ else {
391
+ this.activeSessions.delete(sessionId);
392
+ this.sessionDirs.delete(sessionId);
393
+ }
394
+ }
395
+ /**
396
+ * Gemini CLI가 ~/.gemini/tmp/<sha256(cwd)>/chats/ 에 persist하는
397
+ * session 데이터를 삭제한다.
398
+ */
399
+ clearGeminiChats(cwd) {
400
+ const hash = (0, crypto_1.createHash)("sha256").update(cwd).digest("hex");
401
+ const chatsDir = path.join(this.workspaceDir, ".gemini", "tmp", hash, "chats");
402
+ try {
403
+ fs.rmSync(chatsDir, { recursive: true, force: true });
404
+ }
405
+ catch {
406
+ // 디렉토리가 없거나 삭제 실패 시 무시
407
+ }
408
+ }
409
+ }
410
+ exports.GeminiCLIRunner = GeminiCLIRunner;
411
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}