@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.
- package/LICENSE +26 -0
- package/README.md +203 -0
- package/dist/backends/codex.d.ts +37 -0
- package/dist/backends/codex.d.ts.map +1 -0
- package/dist/backends/codex.js +438 -0
- package/dist/backends/codex.js.map +1 -0
- package/dist/backends/gemini.d.ts +17 -0
- package/dist/backends/gemini.d.ts.map +1 -0
- package/dist/backends/gemini.js +174 -0
- package/dist/backends/gemini.js.map +1 -0
- package/dist/backends/index.d.ts +8 -0
- package/dist/backends/index.d.ts.map +1 -0
- package/dist/backends/index.js +9 -0
- package/dist/backends/index.js.map +1 -0
- package/dist/backends/registry.d.ts +33 -0
- package/dist/backends/registry.d.ts.map +1 -0
- package/dist/backends/registry.js +80 -0
- package/dist/backends/registry.js.map +1 -0
- package/dist/backends/types.d.ts +61 -0
- package/dist/backends/types.d.ts.map +1 -0
- package/dist/backends/types.js +5 -0
- package/dist/backends/types.js.map +1 -0
- package/dist/constants.d.ts +223 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +228 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +192 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/ask.tool.d.ts +4 -0
- package/dist/tools/ask.tool.d.ts.map +1 -0
- package/dist/tools/ask.tool.js +113 -0
- package/dist/tools/ask.tool.js.map +1 -0
- package/dist/tools/brainstorm.tool.d.ts +3 -0
- package/dist/tools/brainstorm.tool.d.ts.map +1 -0
- package/dist/tools/brainstorm.tool.js +250 -0
- package/dist/tools/brainstorm.tool.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +42 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +85 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/review-code.tool.d.ts +3 -0
- package/dist/tools/review-code.tool.d.ts.map +1 -0
- package/dist/tools/review-code.tool.js +279 -0
- package/dist/tools/review-code.tool.js.map +1 -0
- package/dist/tools/test-tool.example.d.ts +13 -0
- package/dist/tools/test-tool.example.d.ts.map +1 -0
- package/dist/tools/test-tool.example.js +32 -0
- package/dist/tools/test-tool.example.js.map +1 -0
- package/dist/tools/timeout-test.tool.d.ts +3 -0
- package/dist/tools/timeout-test.tool.d.ts.map +1 -0
- package/dist/tools/timeout-test.tool.js +38 -0
- package/dist/tools/timeout-test.tool.js.map +1 -0
- package/dist/utils/askSessionManager.d.ts +59 -0
- package/dist/utils/askSessionManager.d.ts.map +1 -0
- package/dist/utils/askSessionManager.js +123 -0
- package/dist/utils/askSessionManager.js.map +1 -0
- package/dist/utils/brainstormSessionManager.d.ts +67 -0
- package/dist/utils/brainstormSessionManager.d.ts.map +1 -0
- package/dist/utils/brainstormSessionManager.js +174 -0
- package/dist/utils/brainstormSessionManager.js.map +1 -0
- package/dist/utils/changeModeInstructions.d.ts +17 -0
- package/dist/utils/changeModeInstructions.d.ts.map +1 -0
- package/dist/utils/changeModeInstructions.js +100 -0
- package/dist/utils/changeModeInstructions.js.map +1 -0
- package/dist/utils/changeModeParser.d.ts +15 -0
- package/dist/utils/changeModeParser.d.ts.map +1 -0
- package/dist/utils/changeModeParser.js +81 -0
- package/dist/utils/changeModeParser.js.map +1 -0
- package/dist/utils/changeModeTranslator.d.ts +4 -0
- package/dist/utils/changeModeTranslator.d.ts.map +1 -0
- package/dist/utils/changeModeTranslator.js +42 -0
- package/dist/utils/changeModeTranslator.js.map +1 -0
- package/dist/utils/commandExecutor.d.ts +2 -0
- package/dist/utils/commandExecutor.d.ts.map +1 -0
- package/dist/utils/commandExecutor.js +76 -0
- package/dist/utils/commandExecutor.js.map +1 -0
- package/dist/utils/envAllowlist.d.ts +17 -0
- package/dist/utils/envAllowlist.d.ts.map +1 -0
- package/dist/utils/envAllowlist.js +54 -0
- package/dist/utils/envAllowlist.js.map +1 -0
- package/dist/utils/geminiExecutor.d.ts +3 -0
- package/dist/utils/geminiExecutor.d.ts.map +1 -0
- package/dist/utils/geminiExecutor.js +94 -0
- package/dist/utils/geminiExecutor.js.map +1 -0
- package/dist/utils/gitStateDetector.d.ts +32 -0
- package/dist/utils/gitStateDetector.d.ts.map +1 -0
- package/dist/utils/gitStateDetector.js +68 -0
- package/dist/utils/gitStateDetector.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +42 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/reviewFormatter.d.ts +35 -0
- package/dist/utils/reviewFormatter.d.ts.map +1 -0
- package/dist/utils/reviewFormatter.js +201 -0
- package/dist/utils/reviewFormatter.js.map +1 -0
- package/dist/utils/reviewPromptBuilder.d.ts +43 -0
- package/dist/utils/reviewPromptBuilder.d.ts.map +1 -0
- package/dist/utils/reviewPromptBuilder.js +170 -0
- package/dist/utils/reviewPromptBuilder.js.map +1 -0
- package/dist/utils/reviewResponseParser.d.ts +20 -0
- package/dist/utils/reviewResponseParser.d.ts.map +1 -0
- package/dist/utils/reviewResponseParser.js +149 -0
- package/dist/utils/reviewResponseParser.js.map +1 -0
- package/dist/utils/reviewSessionCache.d.ts +81 -0
- package/dist/utils/reviewSessionCache.d.ts.map +1 -0
- package/dist/utils/reviewSessionCache.js +220 -0
- package/dist/utils/reviewSessionCache.js.map +1 -0
- package/dist/utils/reviewSessionManager.d.ts +52 -0
- package/dist/utils/reviewSessionManager.d.ts.map +1 -0
- package/dist/utils/reviewSessionManager.js +65 -0
- package/dist/utils/reviewSessionManager.js.map +1 -0
- package/dist/utils/sessionManager.d.ts +95 -0
- package/dist/utils/sessionManager.d.ts.map +1 -0
- package/dist/utils/sessionManager.js +382 -0
- package/dist/utils/sessionManager.js.map +1 -0
- package/dist/utils/sessionSchemas.d.ts +140 -0
- package/dist/utils/sessionSchemas.d.ts.map +1 -0
- package/dist/utils/sessionSchemas.js +2 -0
- package/dist/utils/sessionSchemas.js.map +1 -0
- package/dist/utils/timeoutManager.d.ts +2 -0
- package/dist/utils/timeoutManager.d.ts.map +1 -0
- package/dist/utils/timeoutManager.js +2 -0
- package/dist/utils/timeoutManager.js.map +1 -0
- 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
|