@maxanatsko/llm-cli-bridge 3.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.
Files changed (131) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +203 -0
  3. package/dist/backends/codex.d.ts +37 -0
  4. package/dist/backends/codex.d.ts.map +1 -0
  5. package/dist/backends/codex.js +438 -0
  6. package/dist/backends/codex.js.map +1 -0
  7. package/dist/backends/gemini.d.ts +17 -0
  8. package/dist/backends/gemini.d.ts.map +1 -0
  9. package/dist/backends/gemini.js +174 -0
  10. package/dist/backends/gemini.js.map +1 -0
  11. package/dist/backends/index.d.ts +8 -0
  12. package/dist/backends/index.d.ts.map +1 -0
  13. package/dist/backends/index.js +9 -0
  14. package/dist/backends/index.js.map +1 -0
  15. package/dist/backends/registry.d.ts +33 -0
  16. package/dist/backends/registry.d.ts.map +1 -0
  17. package/dist/backends/registry.js +80 -0
  18. package/dist/backends/registry.js.map +1 -0
  19. package/dist/backends/types.d.ts +61 -0
  20. package/dist/backends/types.d.ts.map +1 -0
  21. package/dist/backends/types.js +5 -0
  22. package/dist/backends/types.js.map +1 -0
  23. package/dist/constants.d.ts +223 -0
  24. package/dist/constants.d.ts.map +1 -0
  25. package/dist/constants.js +228 -0
  26. package/dist/constants.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +192 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/tools/ask.tool.d.ts +4 -0
  32. package/dist/tools/ask.tool.d.ts.map +1 -0
  33. package/dist/tools/ask.tool.js +113 -0
  34. package/dist/tools/ask.tool.js.map +1 -0
  35. package/dist/tools/brainstorm.tool.d.ts +3 -0
  36. package/dist/tools/brainstorm.tool.d.ts.map +1 -0
  37. package/dist/tools/brainstorm.tool.js +250 -0
  38. package/dist/tools/brainstorm.tool.js.map +1 -0
  39. package/dist/tools/index.d.ts +5 -0
  40. package/dist/tools/index.d.ts.map +1 -0
  41. package/dist/tools/index.js +13 -0
  42. package/dist/tools/index.js.map +1 -0
  43. package/dist/tools/registry.d.ts +42 -0
  44. package/dist/tools/registry.d.ts.map +1 -0
  45. package/dist/tools/registry.js +85 -0
  46. package/dist/tools/registry.js.map +1 -0
  47. package/dist/tools/review-code.tool.d.ts +3 -0
  48. package/dist/tools/review-code.tool.d.ts.map +1 -0
  49. package/dist/tools/review-code.tool.js +279 -0
  50. package/dist/tools/review-code.tool.js.map +1 -0
  51. package/dist/tools/test-tool.example.d.ts +13 -0
  52. package/dist/tools/test-tool.example.d.ts.map +1 -0
  53. package/dist/tools/test-tool.example.js +32 -0
  54. package/dist/tools/test-tool.example.js.map +1 -0
  55. package/dist/tools/timeout-test.tool.d.ts +3 -0
  56. package/dist/tools/timeout-test.tool.d.ts.map +1 -0
  57. package/dist/tools/timeout-test.tool.js +38 -0
  58. package/dist/tools/timeout-test.tool.js.map +1 -0
  59. package/dist/utils/askSessionManager.d.ts +59 -0
  60. package/dist/utils/askSessionManager.d.ts.map +1 -0
  61. package/dist/utils/askSessionManager.js +123 -0
  62. package/dist/utils/askSessionManager.js.map +1 -0
  63. package/dist/utils/brainstormSessionManager.d.ts +67 -0
  64. package/dist/utils/brainstormSessionManager.d.ts.map +1 -0
  65. package/dist/utils/brainstormSessionManager.js +174 -0
  66. package/dist/utils/brainstormSessionManager.js.map +1 -0
  67. package/dist/utils/changeModeInstructions.d.ts +17 -0
  68. package/dist/utils/changeModeInstructions.d.ts.map +1 -0
  69. package/dist/utils/changeModeInstructions.js +100 -0
  70. package/dist/utils/changeModeInstructions.js.map +1 -0
  71. package/dist/utils/changeModeParser.d.ts +15 -0
  72. package/dist/utils/changeModeParser.d.ts.map +1 -0
  73. package/dist/utils/changeModeParser.js +81 -0
  74. package/dist/utils/changeModeParser.js.map +1 -0
  75. package/dist/utils/changeModeTranslator.d.ts +4 -0
  76. package/dist/utils/changeModeTranslator.d.ts.map +1 -0
  77. package/dist/utils/changeModeTranslator.js +42 -0
  78. package/dist/utils/changeModeTranslator.js.map +1 -0
  79. package/dist/utils/commandExecutor.d.ts +2 -0
  80. package/dist/utils/commandExecutor.d.ts.map +1 -0
  81. package/dist/utils/commandExecutor.js +76 -0
  82. package/dist/utils/commandExecutor.js.map +1 -0
  83. package/dist/utils/envAllowlist.d.ts +17 -0
  84. package/dist/utils/envAllowlist.d.ts.map +1 -0
  85. package/dist/utils/envAllowlist.js +54 -0
  86. package/dist/utils/envAllowlist.js.map +1 -0
  87. package/dist/utils/geminiExecutor.d.ts +3 -0
  88. package/dist/utils/geminiExecutor.d.ts.map +1 -0
  89. package/dist/utils/geminiExecutor.js +94 -0
  90. package/dist/utils/geminiExecutor.js.map +1 -0
  91. package/dist/utils/gitStateDetector.d.ts +32 -0
  92. package/dist/utils/gitStateDetector.d.ts.map +1 -0
  93. package/dist/utils/gitStateDetector.js +68 -0
  94. package/dist/utils/gitStateDetector.js.map +1 -0
  95. package/dist/utils/logger.d.ts +13 -0
  96. package/dist/utils/logger.d.ts.map +1 -0
  97. package/dist/utils/logger.js +42 -0
  98. package/dist/utils/logger.js.map +1 -0
  99. package/dist/utils/reviewFormatter.d.ts +35 -0
  100. package/dist/utils/reviewFormatter.d.ts.map +1 -0
  101. package/dist/utils/reviewFormatter.js +201 -0
  102. package/dist/utils/reviewFormatter.js.map +1 -0
  103. package/dist/utils/reviewPromptBuilder.d.ts +43 -0
  104. package/dist/utils/reviewPromptBuilder.d.ts.map +1 -0
  105. package/dist/utils/reviewPromptBuilder.js +170 -0
  106. package/dist/utils/reviewPromptBuilder.js.map +1 -0
  107. package/dist/utils/reviewResponseParser.d.ts +20 -0
  108. package/dist/utils/reviewResponseParser.d.ts.map +1 -0
  109. package/dist/utils/reviewResponseParser.js +149 -0
  110. package/dist/utils/reviewResponseParser.js.map +1 -0
  111. package/dist/utils/reviewSessionCache.d.ts +81 -0
  112. package/dist/utils/reviewSessionCache.d.ts.map +1 -0
  113. package/dist/utils/reviewSessionCache.js +220 -0
  114. package/dist/utils/reviewSessionCache.js.map +1 -0
  115. package/dist/utils/reviewSessionManager.d.ts +52 -0
  116. package/dist/utils/reviewSessionManager.d.ts.map +1 -0
  117. package/dist/utils/reviewSessionManager.js +65 -0
  118. package/dist/utils/reviewSessionManager.js.map +1 -0
  119. package/dist/utils/sessionManager.d.ts +95 -0
  120. package/dist/utils/sessionManager.d.ts.map +1 -0
  121. package/dist/utils/sessionManager.js +382 -0
  122. package/dist/utils/sessionManager.js.map +1 -0
  123. package/dist/utils/sessionSchemas.d.ts +140 -0
  124. package/dist/utils/sessionSchemas.d.ts.map +1 -0
  125. package/dist/utils/sessionSchemas.js +2 -0
  126. package/dist/utils/sessionSchemas.js.map +1 -0
  127. package/dist/utils/timeoutManager.d.ts +2 -0
  128. package/dist/utils/timeoutManager.d.ts.map +1 -0
  129. package/dist/utils/timeoutManager.js +2 -0
  130. package/dist/utils/timeoutManager.js.map +1 -0
  131. package/package.json +72 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Codex Backend - Executes prompts via OpenAI's Codex CLI
3
+ *
4
+ * Uses `codex exec` for non-interactive execution mode.
5
+ * Supports native session resume via thread_id from JSON output.
6
+ * Codex CLI does not support @file syntax, so files must be read and inlined.
7
+ */
8
+ import { spawn } from 'child_process';
9
+ import * as fs from 'fs/promises';
10
+ import { constants as fsConstants } from 'fs';
11
+ import * as path from 'path';
12
+ import { Logger } from '../utils/logger.js';
13
+ import { CODEX_CLI, CODEX_FILE_REF, CODEX_OUTPUT, CODEX_MODELS, ERROR_MESSAGES } from '../constants.js';
14
+ import { getAllowedEnv } from '../utils/envAllowlist.js';
15
+ import { getChangeModeInstructionsCondensed } from '../utils/changeModeInstructions.js';
16
+ export class CodexBackend {
17
+ name = 'codex';
18
+ async execute(prompt, config, onProgress) {
19
+ // Security: Validate model name to prevent argument injection
20
+ if (config.model && config.model.startsWith('-')) {
21
+ throw new Error(`Invalid model name: model cannot start with '-'`);
22
+ }
23
+ if (config.codexThreadId && config.codexThreadId.startsWith('-')) {
24
+ throw new Error(`Invalid codex thread id: thread id cannot start with '-'`);
25
+ }
26
+ // Translate @file references to inline content since Codex doesn't support them
27
+ const processedPrompt = await this.translateFileRefs(prompt, config.cwd);
28
+ // Apply changeMode instructions if enabled
29
+ const finalPrompt = config.changeMode
30
+ ? this.applyChangeModeInstructions(processedPrompt)
31
+ : processedPrompt;
32
+ // Build args - use resume if we have an existing threadId
33
+ const args = this.buildArgs(config);
34
+ // Execute and parse JSON output
35
+ const result = await this.executeCommand(args, finalPrompt, onProgress, config.cwd);
36
+ return {
37
+ response: result.response,
38
+ backend: this.name,
39
+ model: config.model ?? (config.codexThreadId ? undefined : CODEX_MODELS.DEFAULT),
40
+ codexThreadId: result.threadId,
41
+ };
42
+ }
43
+ async isAvailable() {
44
+ return new Promise((resolve) => {
45
+ const checker = process.platform === 'win32' ? 'where' : 'which';
46
+ const child = spawn(checker, ['codex']);
47
+ child.on('close', (code) => resolve(code === 0));
48
+ child.on('error', () => resolve(false));
49
+ });
50
+ }
51
+ getModels() {
52
+ return [
53
+ CODEX_MODELS.GPT_5_4,
54
+ CODEX_MODELS.GPT_5_4_MINI,
55
+ CODEX_MODELS.GPT_5_3_CODEX,
56
+ CODEX_MODELS.GPT_5_2_CODEX,
57
+ CODEX_MODELS.GPT_5_2,
58
+ ];
59
+ }
60
+ supportsFileRefs() {
61
+ return false; // Codex reads files directly, doesn't use @ syntax
62
+ }
63
+ getFileRefSyntax() {
64
+ return ''; // No file ref syntax
65
+ }
66
+ buildArgs(config) {
67
+ const args = [];
68
+ // Codex parses approval/sandbox options as global flags; place before subcommands.
69
+ // On resume, prefer Codex's thread-associated model unless the user explicitly overrides `model`.
70
+ const modelToUse = config.model ?? (config.codexThreadId ? undefined : CODEX_MODELS.DEFAULT);
71
+ if (modelToUse) {
72
+ args.push(CODEX_CLI.FLAGS.MODEL, modelToUse);
73
+ }
74
+ // Approval mode
75
+ if (config.approvalMode) {
76
+ args.push(CODEX_CLI.FLAGS.APPROVAL, config.approvalMode);
77
+ }
78
+ else if (config.fullAuto) {
79
+ args.push(CODEX_CLI.FLAGS.FULL_AUTO);
80
+ }
81
+ else {
82
+ // Default to on-request for safety
83
+ args.push(CODEX_CLI.FLAGS.APPROVAL, CODEX_CLI.APPROVAL_MODES.ON_REQUEST);
84
+ }
85
+ // Sandbox mode
86
+ const sandboxMode = config.sandboxMode ??
87
+ (config.sandbox ? CODEX_CLI.SANDBOX_MODES.WORKSPACE_WRITE : CODEX_CLI.SANDBOX_MODES.READ_ONLY);
88
+ if (sandboxMode === CODEX_CLI.SANDBOX_MODES.FULL_ACCESS) {
89
+ Logger.warn('⚠️ SECURITY: Codex full filesystem access enabled (danger-full-access)');
90
+ }
91
+ args.push(CODEX_CLI.FLAGS.SANDBOX, sandboxMode);
92
+ // Reasoning effort is configured via --config (not --reasoning-effort) in current Codex CLI.
93
+ if (config.reasoningEffort) {
94
+ args.push(CODEX_CLI.FLAGS.CONFIG, `model_reasoning_effort="${config.reasoningEffort}"`);
95
+ }
96
+ // Use `codex exec` and `codex exec resume <threadId>` for non-interactive mode.
97
+ args.push(CODEX_CLI.COMMANDS.EXEC);
98
+ if (config.codexThreadId) {
99
+ args.push(CODEX_CLI.COMMANDS.RESUME, config.codexThreadId);
100
+ }
101
+ // Enable JSON output to capture thread_id
102
+ args.push(CODEX_CLI.FLAGS.JSON);
103
+ // Read prompt from stdin
104
+ args.push(CODEX_CLI.FLAGS.STDIN);
105
+ return args;
106
+ }
107
+ /**
108
+ * Validate that a resolved path is within the allowed workspace
109
+ * Prevents path traversal attacks including Windows drive letter escapes
110
+ */
111
+ isPathWithinWorkspace(resolvedPath, workingDir) {
112
+ const normalizedPath = path.normalize(resolvedPath);
113
+ const normalizedWorkDir = path.normalize(workingDir);
114
+ const relative = path.relative(normalizedWorkDir, normalizedPath);
115
+ // Check: empty string (workspace root) is allowed, doesn't escape via '..', not absolute (handles Windows drive letters)
116
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
117
+ }
118
+ /**
119
+ * Translate @file references to inline content
120
+ * Codex doesn't support @ syntax, so we read files and include their content
121
+ * Handles paths with dots, slashes, dashes, underscores, and relative paths like @../src/file.ts
122
+ * Includes path traversal protection to prevent reading files outside workspace
123
+ */
124
+ async translateFileRefs(prompt, cwd) {
125
+ const workingDir = cwd || process.cwd();
126
+ const lexicalWorkDir = path.resolve(workingDir);
127
+ const canonicalWorkDir = await fs.realpath(workingDir).catch(() => lexicalWorkDir);
128
+ // Match @file references - handles:
129
+ // - Relative paths: @../src/file.ts, @./file.ts
130
+ // - Absolute paths: @/home/user/file.ts
131
+ // - Paths with special chars: @src/file-name.test.ts
132
+ // Stops at whitespace or another @ symbol
133
+ const fileRefs = prompt.match(/@(?:\.\.?\/)?[^\s@]+/g) || [];
134
+ if (fileRefs.length === 0) {
135
+ return prompt;
136
+ }
137
+ let translated = prompt;
138
+ const missingFiles = [];
139
+ const deniedFiles = [];
140
+ // Max file size: 10MB to prevent memory exhaustion
141
+ let totalInlinedBytes = 0;
142
+ const alreadyProcessedRefs = new Set();
143
+ const alreadyProcessedTargets = new Map();
144
+ for (const ref of fileRefs) {
145
+ const filePath = ref.substring(1); // Remove @ prefix
146
+ // Avoid re-reading/re-inlining the same @reference multiple times.
147
+ // Replace duplicates with a small pointer to the first inlined instance.
148
+ if (alreadyProcessedRefs.has(ref)) {
149
+ translated = translated.replace(ref, `\n--- Duplicate @reference: ${filePath} (see earlier in prompt) ---\n`);
150
+ continue;
151
+ }
152
+ alreadyProcessedRefs.add(ref);
153
+ const absolutePath = path.isAbsolute(filePath)
154
+ ? filePath
155
+ : path.join(workingDir, filePath);
156
+ // Resolve for basic path normalization (does not follow symlinks)
157
+ const resolvedPath = path.resolve(absolutePath);
158
+ // Security check: Ensure path is within workspace
159
+ if (!this.isPathWithinWorkspace(resolvedPath, lexicalWorkDir)) {
160
+ deniedFiles.push(filePath);
161
+ Logger.warn(`Path traversal blocked for @reference: ${filePath} (resolved to ${resolvedPath})`);
162
+ translated = translated.replace(ref, `${ERROR_MESSAGES.ACCESS_DENIED_OUTSIDE_WORKSPACE} (${filePath})`);
163
+ continue;
164
+ }
165
+ try {
166
+ try {
167
+ await fs.access(absolutePath, fsConstants.F_OK);
168
+ }
169
+ catch {
170
+ // Extra security: if file doesn't exist and path contains .., deny access.
171
+ // This prevents potential TOCTOU attacks where file is created after check.
172
+ if (filePath.includes('..')) {
173
+ deniedFiles.push(filePath);
174
+ Logger.warn(`Path traversal blocked for non-existent path with ..: ${filePath}`);
175
+ translated = translated.replace(ref, ERROR_MESSAGES.ACCESS_DENIED_PATH_TRAVERSAL);
176
+ continue;
177
+ }
178
+ missingFiles.push(filePath);
179
+ Logger.warn(`File not found for @reference: ${filePath}`);
180
+ translated = translated.replace(ref, `${ERROR_MESSAGES.FILE_NOT_FOUND}: ${filePath}`);
181
+ continue;
182
+ }
183
+ // Canonicalize to prevent parent-directory symlink traversal (e.g., workspace/subdir -> /etc)
184
+ const canonicalTargetPath = await fs.realpath(absolutePath);
185
+ const isSymlinkedPath = path.normalize(canonicalTargetPath) !== path.normalize(path.resolve(absolutePath));
186
+ if (!this.isPathWithinWorkspace(canonicalTargetPath, canonicalWorkDir)) {
187
+ deniedFiles.push(filePath);
188
+ Logger.warn(`Symlink traversal blocked for @reference: ${filePath} (realpath: ${canonicalTargetPath})`);
189
+ translated = translated.replace(ref, `${isSymlinkedPath ? ERROR_MESSAGES.ACCESS_DENIED_SYMLINK_OUTSIDE_WORKSPACE : ERROR_MESSAGES.ACCESS_DENIED_OUTSIDE_WORKSPACE} (${filePath})`);
190
+ continue;
191
+ }
192
+ if (alreadyProcessedTargets.has(canonicalTargetPath)) {
193
+ translated = translated.replace(ref, `\n--- Duplicate @reference: ${filePath} (see earlier in prompt) ---\n`);
194
+ continue;
195
+ }
196
+ const stat = await fs.stat(canonicalTargetPath);
197
+ if (stat.isDirectory()) {
198
+ // For directories, list files but don't inline all content (bounded)
199
+ const fileNames = [];
200
+ let truncated = false;
201
+ const dir = await fs.opendir(canonicalTargetPath);
202
+ try {
203
+ while (true) {
204
+ const dirent = await dir.read();
205
+ if (!dirent)
206
+ break;
207
+ if (fileNames.length < CODEX_FILE_REF.MAX_DIR_ENTRIES) {
208
+ fileNames.push(dirent.name);
209
+ continue;
210
+ }
211
+ truncated = true;
212
+ break;
213
+ }
214
+ }
215
+ finally {
216
+ await dir.close();
217
+ }
218
+ const suffix = truncated ? `, ... (showing first ${CODEX_FILE_REF.MAX_DIR_ENTRIES})` : '';
219
+ const directoryListing = `\n--- Directory: ${filePath} ---\nFiles: ${fileNames.join(', ')}${suffix}\n--- end directory ---\n`;
220
+ const listingBytes = Buffer.byteLength(directoryListing, 'utf8');
221
+ if (totalInlinedBytes + listingBytes > CODEX_FILE_REF.MAX_TOTAL_BYTES) {
222
+ Logger.warn(`Inline limit reached while listing directory: ${filePath}`);
223
+ translated = translated.replace(ref, `${ERROR_MESSAGES.INLINE_LIMIT_REACHED}: ${filePath}`);
224
+ continue;
225
+ }
226
+ totalInlinedBytes += listingBytes;
227
+ alreadyProcessedTargets.set(canonicalTargetPath, directoryListing);
228
+ translated = translated.replace(ref, directoryListing);
229
+ continue;
230
+ }
231
+ // Check file size before reading
232
+ if (stat.size > CODEX_FILE_REF.MAX_FILE_BYTES) {
233
+ Logger.warn(`File too large for @reference: ${filePath} (${(stat.size / 1024 / 1024).toFixed(2)}MB > 10MB limit)`);
234
+ translated = translated.replace(ref, `${ERROR_MESSAGES.FILE_TOO_LARGE}: ${filePath} (${(stat.size / 1024 / 1024).toFixed(2)}MB exceeds 10MB limit)`);
235
+ continue;
236
+ }
237
+ if (totalInlinedBytes + stat.size > CODEX_FILE_REF.MAX_TOTAL_BYTES) {
238
+ Logger.warn(`Inline limit reached; skipping file: ${filePath} (${stat.size} bytes)`);
239
+ translated = translated.replace(ref, `${ERROR_MESSAGES.INLINE_LIMIT_REACHED}: ${filePath}`);
240
+ continue;
241
+ }
242
+ const content = await fs.readFile(canonicalTargetPath, 'utf-8');
243
+ const fileBlock = `\n--- File: ${filePath} ---\n${content}\n--- end file: ${filePath} ---\n`;
244
+ totalInlinedBytes += stat.size;
245
+ alreadyProcessedTargets.set(canonicalTargetPath, fileBlock);
246
+ translated = translated.replace(ref, fileBlock);
247
+ }
248
+ catch (error) {
249
+ const errMsg = error instanceof Error ? error.message : String(error);
250
+ Logger.error(`Error reading file ${filePath}: ${errMsg}`);
251
+ translated = translated.replace(ref, `${ERROR_MESSAGES.ERROR_READING_FILE}: ${filePath}`);
252
+ }
253
+ }
254
+ // Log warnings for security and missing files
255
+ if (deniedFiles.length > 0) {
256
+ Logger.warn(`Security: Blocked access to ${deniedFiles.length} file(s) outside workspace`);
257
+ }
258
+ if (missingFiles.length > 0) {
259
+ Logger.warn(`Missing file references: ${missingFiles.join(', ')}`);
260
+ }
261
+ return translated;
262
+ }
263
+ applyChangeModeInstructions(prompt) {
264
+ return getChangeModeInstructionsCondensed(prompt);
265
+ }
266
+ /**
267
+ * Parse JSONL output from Codex CLI
268
+ * Extracts thread_id from thread.started event and response text from message events
269
+ */
270
+ parseJsonOutput(jsonlOutput) {
271
+ const allLines = jsonlOutput.trim().split('\n');
272
+ // Defensive line limit to prevent DoS via massive JSONL output
273
+ const lines = allLines.slice(0, CODEX_OUTPUT.MAX_JSONL_LINES);
274
+ if (allLines.length >= CODEX_OUTPUT.MAX_JSONL_LINES) {
275
+ Logger.warn(`Truncated JSONL output to ${CODEX_OUTPUT.MAX_JSONL_LINES} lines`);
276
+ }
277
+ let threadId;
278
+ const responseChunks = [];
279
+ for (const line of lines) {
280
+ if (!line.trim())
281
+ continue;
282
+ try {
283
+ const event = JSON.parse(line);
284
+ // Extract thread_id from thread.started event
285
+ if (event.type === 'thread.started' && event.thread_id) {
286
+ threadId = event.thread_id;
287
+ Logger.debug(`Codex thread started: ${threadId}`);
288
+ }
289
+ // Extract response text from various event types
290
+ // Agent messages contain the actual response
291
+ if (event.type === 'item.agent_message' && event.content) {
292
+ responseChunks.push(event.content);
293
+ }
294
+ // Newer Codex JSONL emits completed items with nested payload.
295
+ // Example: {"type":"item.completed","item":{"type":"agent_message","text":"..."}}
296
+ if (event.type === 'item.completed' && event.item) {
297
+ const item = event.item;
298
+ if (item.type === 'agent_message') {
299
+ if (typeof item.text === 'string') {
300
+ responseChunks.push(item.text);
301
+ }
302
+ else if (typeof item.content === 'string') {
303
+ responseChunks.push(item.content);
304
+ }
305
+ else if (Array.isArray(item.content)) {
306
+ for (const part of item.content) {
307
+ if (part?.type === 'text' && part.text) {
308
+ responseChunks.push(part.text);
309
+ }
310
+ }
311
+ }
312
+ }
313
+ }
314
+ // Also check for message content in turn.completed
315
+ if (event.type === 'turn.completed' && event.output) {
316
+ if (typeof event.output === 'string') {
317
+ responseChunks.push(event.output);
318
+ }
319
+ else if (event.output.content) {
320
+ responseChunks.push(event.output.content);
321
+ }
322
+ }
323
+ // Handle item.message for direct message content
324
+ if (event.type === 'item.message' && event.content) {
325
+ if (Array.isArray(event.content)) {
326
+ for (const part of event.content) {
327
+ if (part.type === 'text' && part.text) {
328
+ responseChunks.push(part.text);
329
+ }
330
+ }
331
+ }
332
+ else if (typeof event.content === 'string') {
333
+ responseChunks.push(event.content);
334
+ }
335
+ }
336
+ }
337
+ catch (parseError) {
338
+ // Not all lines may be valid JSON, skip them
339
+ Logger.debug(`Skipping non-JSON line: ${line.substring(0, 50)}...`);
340
+ }
341
+ }
342
+ // Join all response chunks
343
+ const response = responseChunks.join('\n').trim();
344
+ // If no response extracted from events, use raw output minus JSON structure
345
+ if (!response) {
346
+ Logger.warn('No structured response found in Codex JSON output, using raw text extraction');
347
+ // Try to extract any text content from the raw output
348
+ const textMatch = jsonlOutput.match(/"text"\s*:\s*"([^"]+)"/g);
349
+ if (textMatch) {
350
+ const extractedTexts = textMatch.map(m => {
351
+ const match = m.match(/"text"\s*:\s*"([^"]+)"/);
352
+ return match ? match[1] : '';
353
+ }).filter(Boolean);
354
+ return { response: extractedTexts.join('\n'), threadId };
355
+ }
356
+ return { response: jsonlOutput, threadId };
357
+ }
358
+ return { response, threadId };
359
+ }
360
+ executeCommand(args, prompt, onProgress, cwd) {
361
+ return new Promise((resolve, reject) => {
362
+ const startTime = Date.now();
363
+ Logger.commandExecution('codex', args, startTime);
364
+ const childProcess = spawn('codex', args, {
365
+ env: getAllowedEnv(),
366
+ shell: false,
367
+ stdio: ['pipe', 'pipe', 'pipe'],
368
+ cwd: cwd || process.cwd(),
369
+ });
370
+ // Write prompt to stdin
371
+ childProcess.stdin.write(prompt);
372
+ childProcess.stdin.end();
373
+ let stdout = '';
374
+ let stderr = '';
375
+ let isResolved = false;
376
+ let outputSizeExceeded = false;
377
+ childProcess.stdout.on('data', (data) => {
378
+ // Security: Prevent memory exhaustion from massive output
379
+ if (outputSizeExceeded)
380
+ return;
381
+ const chunk = data.toString();
382
+ if (stdout.length + chunk.length > CODEX_OUTPUT.MAX_OUTPUT_SIZE) {
383
+ Logger.warn(`Output exceeds ${CODEX_OUTPUT.MAX_OUTPUT_SIZE / 1024 / 1024}MB limit, killing process`);
384
+ outputSizeExceeded = true;
385
+ childProcess.kill('SIGTERM');
386
+ return;
387
+ }
388
+ stdout += chunk;
389
+ // For JSON output, try to parse and report progress from events
390
+ if (onProgress) {
391
+ const newLines = chunk.split('\n');
392
+ for (const line of newLines) {
393
+ if (!line.trim())
394
+ continue;
395
+ try {
396
+ const event = JSON.parse(line);
397
+ // Report agent messages as progress
398
+ if (event.type === 'item.agent_message' && event.content) {
399
+ onProgress(event.content);
400
+ }
401
+ }
402
+ catch {
403
+ // Skip non-JSON lines
404
+ }
405
+ }
406
+ }
407
+ });
408
+ childProcess.stderr.on('data', (data) => {
409
+ stderr += data.toString();
410
+ });
411
+ childProcess.on('error', (error) => {
412
+ if (!isResolved) {
413
+ isResolved = true;
414
+ Logger.error('Process error:', error);
415
+ reject(new Error(`Failed to spawn codex command: ${error.message}`));
416
+ }
417
+ });
418
+ childProcess.on('close', (code) => {
419
+ if (!isResolved) {
420
+ isResolved = true;
421
+ if (code === 0) {
422
+ Logger.commandComplete(startTime, code, stdout.length);
423
+ // Parse JSON output to extract thread_id and response
424
+ const result = this.parseJsonOutput(stdout);
425
+ resolve(result);
426
+ }
427
+ else {
428
+ Logger.commandComplete(startTime, code);
429
+ Logger.error(`Codex failed with exit code ${code}`);
430
+ const errorMessage = stderr.trim() || 'Unknown error';
431
+ reject(new Error(`Codex command failed with exit code ${code}: ${errorMessage}`));
432
+ }
433
+ }
434
+ });
435
+ });
436
+ }
437
+ }
438
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/backends/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Gemini Backend - Executes prompts via Google's Gemini CLI
3
+ */
4
+ import { BackendExecutor, BackendConfig, BackendType, BackendResult } from './types.js';
5
+ export declare class GeminiBackend implements BackendExecutor {
6
+ name: BackendType;
7
+ private resolveModel;
8
+ execute(prompt: string, config: BackendConfig, onProgress?: (output: string) => void): Promise<BackendResult>;
9
+ isAvailable(): Promise<boolean>;
10
+ getModels(): string[];
11
+ supportsFileRefs(): boolean;
12
+ getFileRefSyntax(): string;
13
+ private buildArgs;
14
+ private applyChangeModeInstructions;
15
+ private executeCommand;
16
+ }
17
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/backends/gemini.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAYxF,qBAAa,aAAc,YAAW,eAAe;IACnD,IAAI,EAAE,WAAW,CAAY;IAE7B,OAAO,CAAC,YAAY;IAYd,OAAO,CACX,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa,EACrB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GACpC,OAAO,CAAC,aAAa,CAAC;IA0DnB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC,SAAS,IAAI,MAAM,EAAE;IASrB,gBAAgB,IAAI,OAAO;IAI3B,gBAAgB,IAAI,MAAM;IAI1B,OAAO,CAAC,SAAS;IA4BjB,OAAO,CAAC,2BAA2B;IAKnC,OAAO,CAAC,cAAc;CAgEvB"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Gemini Backend - Executes prompts via Google's Gemini CLI
3
+ */
4
+ import { spawn } from 'child_process';
5
+ import { Logger } from '../utils/logger.js';
6
+ import { ERROR_MESSAGES, STATUS_MESSAGES, MODELS, CLI, GEMINI_MODEL_ALIASES } from '../constants.js';
7
+ import { getAllowedEnv } from '../utils/envAllowlist.js';
8
+ import { getChangeModeInstructions } from '../utils/changeModeInstructions.js';
9
+ export class GeminiBackend {
10
+ name = 'gemini';
11
+ resolveModel(requestedModel) {
12
+ if (!requestedModel || requestedModel.trim().length === 0) {
13
+ return MODELS.PRO_3;
14
+ }
15
+ const normalized = GEMINI_MODEL_ALIASES[requestedModel] || requestedModel;
16
+ if (normalized !== requestedModel) {
17
+ Logger.warn(`Gemini model '${requestedModel}' is deprecated; using '${normalized}' instead.`);
18
+ }
19
+ return normalized;
20
+ }
21
+ async execute(prompt, config, onProgress) {
22
+ // Security: Validate model name to prevent argument injection
23
+ if (config.model && config.model.startsWith('-')) {
24
+ throw new Error(`Invalid model name: model cannot start with '-'`);
25
+ }
26
+ let processedPrompt = prompt;
27
+ const model = this.resolveModel(config.model);
28
+ const primaryModel = model;
29
+ let usedModel = model;
30
+ // Apply changeMode instructions if enabled
31
+ if (config.changeMode) {
32
+ processedPrompt = this.applyChangeModeInstructions(prompt);
33
+ }
34
+ const args = this.buildArgs(processedPrompt, { ...config, model });
35
+ try {
36
+ const response = await this.executeCommand(args, onProgress, config.cwd);
37
+ return {
38
+ response,
39
+ backend: this.name,
40
+ model: usedModel,
41
+ };
42
+ }
43
+ catch (error) {
44
+ // Handle quota exceeded with fallback to Flash model
45
+ const errorMessage = error instanceof Error ? error.message : String(error);
46
+ if (errorMessage.includes(ERROR_MESSAGES.QUOTA_EXCEEDED) && model !== MODELS.FLASH) {
47
+ Logger.warn(`${ERROR_MESSAGES.QUOTA_EXCEEDED}. Falling back to ${MODELS.FLASH}.`);
48
+ onProgress?.(STATUS_MESSAGES.FLASH_RETRY);
49
+ const fallbackConfig = { ...config, model: MODELS.FLASH };
50
+ const fallbackArgs = this.buildArgs(processedPrompt, fallbackConfig);
51
+ usedModel = MODELS.FLASH;
52
+ try {
53
+ const response = await this.executeCommand(fallbackArgs, onProgress, config.cwd);
54
+ Logger.warn(`Successfully executed with ${MODELS.FLASH} fallback.`);
55
+ onProgress?.(STATUS_MESSAGES.FLASH_SUCCESS);
56
+ return {
57
+ response,
58
+ backend: this.name,
59
+ model: usedModel,
60
+ };
61
+ }
62
+ catch (fallbackError) {
63
+ const fallbackErrorMessage = fallbackError instanceof Error
64
+ ? fallbackError.message
65
+ : String(fallbackError);
66
+ throw new Error(`${primaryModel} quota exceeded, ${MODELS.FLASH} fallback also failed: ${fallbackErrorMessage}`);
67
+ }
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+ async isAvailable() {
73
+ return new Promise((resolve) => {
74
+ const checker = process.platform === 'win32' ? 'where' : 'which';
75
+ const child = spawn(checker, ['gemini']);
76
+ child.on('close', (code) => resolve(code === 0));
77
+ child.on('error', () => resolve(false));
78
+ });
79
+ }
80
+ getModels() {
81
+ return [
82
+ MODELS.PRO_3,
83
+ MODELS.FLASH_3,
84
+ MODELS.PRO,
85
+ MODELS.FLASH
86
+ ];
87
+ }
88
+ supportsFileRefs() {
89
+ return true;
90
+ }
91
+ getFileRefSyntax() {
92
+ return '@';
93
+ }
94
+ buildArgs(prompt, config) {
95
+ const args = [];
96
+ if (config.model) {
97
+ args.push(CLI.FLAGS.MODEL, config.model);
98
+ }
99
+ if (config.sandbox) {
100
+ args.push(CLI.FLAGS.SANDBOX);
101
+ }
102
+ // Add allowed tools for auto-approval
103
+ if (config.allowedTools && config.allowedTools.length > 0) {
104
+ for (const tool of config.allowedTools) {
105
+ args.push(CLI.FLAGS.ALLOWED_TOOLS, tool);
106
+ }
107
+ }
108
+ // Ensure @ symbols work cross-platform by wrapping in quotes if needed
109
+ const finalPrompt = prompt.includes('@') && !prompt.startsWith('"')
110
+ ? `"${prompt}"`
111
+ : prompt;
112
+ args.push(CLI.FLAGS.PROMPT, finalPrompt);
113
+ return args;
114
+ }
115
+ applyChangeModeInstructions(prompt) {
116
+ const processedPrompt = prompt.replace(/file:(\S+)/g, '@$1');
117
+ return getChangeModeInstructions(processedPrompt);
118
+ }
119
+ executeCommand(args, onProgress, cwd) {
120
+ return new Promise((resolve, reject) => {
121
+ const startTime = Date.now();
122
+ Logger.commandExecution(CLI.COMMANDS.GEMINI, args, startTime);
123
+ const childProcess = spawn(CLI.COMMANDS.GEMINI, args, {
124
+ env: getAllowedEnv(),
125
+ shell: false,
126
+ stdio: ['ignore', 'pipe', 'pipe'],
127
+ cwd: cwd || process.cwd(),
128
+ });
129
+ let stdout = '';
130
+ let stderr = '';
131
+ let isResolved = false;
132
+ let lastReportedLength = 0;
133
+ childProcess.stdout.on('data', (data) => {
134
+ stdout += data.toString();
135
+ if (onProgress && stdout.length > lastReportedLength) {
136
+ const newContent = stdout.substring(lastReportedLength);
137
+ lastReportedLength = stdout.length;
138
+ onProgress(newContent);
139
+ }
140
+ });
141
+ childProcess.stderr.on('data', (data) => {
142
+ stderr += data.toString();
143
+ if (stderr.includes('RESOURCE_EXHAUSTED')) {
144
+ const modelMatch = stderr.match(/Quota exceeded for quota metric '([^']+)'/);
145
+ const model = modelMatch ? modelMatch[1] : 'Unknown Model';
146
+ Logger.error(`Gemini Quota Error: Quota exceeded for ${model}`);
147
+ }
148
+ });
149
+ childProcess.on('error', (error) => {
150
+ if (!isResolved) {
151
+ isResolved = true;
152
+ Logger.error('Process error:', error);
153
+ reject(new Error(`Failed to spawn gemini command: ${error.message}`));
154
+ }
155
+ });
156
+ childProcess.on('close', (code) => {
157
+ if (!isResolved) {
158
+ isResolved = true;
159
+ if (code === 0) {
160
+ Logger.commandComplete(startTime, code, stdout.length);
161
+ resolve(stdout.trim());
162
+ }
163
+ else {
164
+ Logger.commandComplete(startTime, code);
165
+ Logger.error(`Failed with exit code ${code}`);
166
+ const errorMessage = stderr.trim() || 'Unknown error';
167
+ reject(new Error(`Gemini command failed with exit code ${code}: ${errorMessage}`));
168
+ }
169
+ }
170
+ });
171
+ });
172
+ }
173
+ }
174
+ //# sourceMappingURL=gemini.js.map