llm-cli-gateway 1.0.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/CHANGELOG.md +541 -0
- package/LICENSE +21 -0
- package/README.md +545 -0
- package/dist/approval-manager.d.ts +43 -0
- package/dist/approval-manager.js +156 -0
- package/dist/async-job-manager.d.ts +57 -0
- package/dist/async-job-manager.js +334 -0
- package/dist/claude-mcp-config.d.ts +8 -0
- package/dist/claude-mcp-config.js +161 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.js +56 -0
- package/dist/db.d.ts +48 -0
- package/dist/db.js +170 -0
- package/dist/executor.d.ts +30 -0
- package/dist/executor.js +315 -0
- package/dist/health.d.ts +20 -0
- package/dist/health.js +32 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +1503 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.js +5 -0
- package/dist/metrics.d.ts +23 -0
- package/dist/metrics.js +57 -0
- package/dist/migrate-sessions.d.ts +12 -0
- package/dist/migrate-sessions.js +145 -0
- package/dist/migrate.d.ts +2 -0
- package/dist/migrate.js +100 -0
- package/dist/model-registry.d.ts +10 -0
- package/dist/model-registry.js +346 -0
- package/dist/optimizer.d.ts +3 -0
- package/dist/optimizer.js +183 -0
- package/dist/process-monitor.d.ts +54 -0
- package/dist/process-monitor.js +146 -0
- package/dist/request-helpers.d.ts +25 -0
- package/dist/request-helpers.js +32 -0
- package/dist/resources.d.ts +26 -0
- package/dist/resources.js +201 -0
- package/dist/retry.d.ts +72 -0
- package/dist/retry.js +146 -0
- package/dist/review-integrity.d.ts +50 -0
- package/dist/review-integrity.js +283 -0
- package/dist/session-manager-pg.d.ts +76 -0
- package/dist/session-manager-pg.js +383 -0
- package/dist/session-manager.d.ts +62 -0
- package/dist/session-manager.js +223 -0
- package/dist/stream-json-parser.d.ts +35 -0
- package/dist/stream-json-parser.js +94 -0
- package/package.json +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# llm-cli-gateway
|
|
2
|
+
|
|
3
|
+
> *"Without consultation, plans are frustrated, but with many counselors they succeed."*
|
|
4
|
+
> — Proverbs 15:22 (LSB)
|
|
5
|
+
|
|
6
|
+
A Model Context Protocol (MCP) server providing unified access to Claude Code, Codex, and Gemini CLIs with session management, retry logic, and async job orchestration.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
### Core Capabilities
|
|
11
|
+
- **Multi-LLM Orchestration**: Unified interface for Claude Code, Codex, and Gemini CLIs
|
|
12
|
+
- **Session Management**: Track and resume conversations across all CLIs with persistent storage
|
|
13
|
+
- **Token Optimization**: Automatic 44% reduction on prompts, 37% on responses (opt-in)
|
|
14
|
+
- **Correlation ID Tracking**: Full request tracing across all LLM interactions
|
|
15
|
+
- **Cross-Tool Collaboration**: LLMs can use each other via MCP (validated through dogfooding)
|
|
16
|
+
|
|
17
|
+
### Reliability & Performance
|
|
18
|
+
- **Retry Logic**: Exponential backoff with circuit breaker for transient failures
|
|
19
|
+
- **Atomic File Writes**: Process-specific temp files with fsync for data integrity
|
|
20
|
+
- **Memory Limits**: 50MB cap on CLI output prevents DoS attacks
|
|
21
|
+
- **NVM Path Caching**: Eliminates I/O overhead on every request
|
|
22
|
+
- **Long-Running Jobs**: Non-time-bound async execution via `*_request_async` + polling tools
|
|
23
|
+
|
|
24
|
+
### Security & Quality
|
|
25
|
+
- **Comprehensive Testing**: 221 tests covering unit, integration, and regression scenarios
|
|
26
|
+
- **Input Validation**: Zod schemas prevent injection attacks
|
|
27
|
+
- **No Secret Leakage**: Generic session descriptions only (file permissions 0o600)
|
|
28
|
+
- **No ReDoS**: Bounded regex patterns prevent catastrophic backtracking
|
|
29
|
+
- **Type Safety**: Strict TypeScript with comprehensive error handling
|
|
30
|
+
- **221 Tests**: Unit, integration, and regression tests with real CLI execution
|
|
31
|
+
|
|
32
|
+
## Prerequisites
|
|
33
|
+
|
|
34
|
+
Before using this gateway, you need to install the CLI tools you want to use:
|
|
35
|
+
|
|
36
|
+
### Claude Code CLI
|
|
37
|
+
```bash
|
|
38
|
+
# Installation instructions for Claude Code
|
|
39
|
+
# Visit: https://docs.anthropic.com/claude-code
|
|
40
|
+
npm install -g @anthropic-ai/claude-code
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Codex CLI
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g @openai/codex
|
|
46
|
+
codex login
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Gemini CLI
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g @google/gemini-cli
|
|
52
|
+
# Or: https://github.com/google-gemini/gemini-cli
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
### As an MCP server (npm)
|
|
58
|
+
```bash
|
|
59
|
+
npm install -g llm-cli-gateway
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or use directly with `npx`:
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"llm-gateway": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["-y", "llm-cli-gateway"]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### From source
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/verivus-oss/llm-cli-gateway.git
|
|
77
|
+
cd llm-cli-gateway
|
|
78
|
+
npm install
|
|
79
|
+
npm run build
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
### As an MCP Server
|
|
85
|
+
|
|
86
|
+
Add to your MCP client configuration (e.g., Claude Desktop):
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"llm-cli-gateway": {
|
|
92
|
+
"command": "node",
|
|
93
|
+
"args": ["/path/to/llm-cli-gateway/dist/index.js"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Available Tools
|
|
100
|
+
|
|
101
|
+
#### LLM Request Tools
|
|
102
|
+
|
|
103
|
+
##### `claude_request`
|
|
104
|
+
Execute a Claude Code request with optional session management.
|
|
105
|
+
|
|
106
|
+
**Parameters:**
|
|
107
|
+
- `prompt` (string, required): The prompt to send (1-100,000 chars)
|
|
108
|
+
- `model` (string, optional): Model name or alias (use `list_models` for available values; supports `latest`)
|
|
109
|
+
- `outputFormat` (string, optional): Output format ("text" or "json"), default: "text"
|
|
110
|
+
- `sessionId` (string, optional): Specific session ID to use
|
|
111
|
+
- `continueSession` (boolean, optional): Continue the active session
|
|
112
|
+
- `createNewSession` (boolean, optional): Always create a new session
|
|
113
|
+
- `allowedTools` (string[], optional): Restrict Claude tools to this allow-list
|
|
114
|
+
- `disallowedTools` (string[], optional): Explicitly deny listed Claude tools
|
|
115
|
+
- `dangerouslySkipPermissions` (boolean, optional): Request CLI-side permission bypass (legacy mode only)
|
|
116
|
+
- `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
|
|
117
|
+
- `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
|
|
118
|
+
- `mcpServers` (string[], optional): Claude MCP servers to expose (default: `["sqry","exa","ref_tools"]`; `"trstr"` available as opt-in)
|
|
119
|
+
- `strictMcpConfig` (boolean, optional): Require Claude to use only supplied MCP config, default: true (request fails if any requested server is unavailable)
|
|
120
|
+
- `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency (44% reduction), default: false
|
|
121
|
+
- `optimizeResponse` (boolean, optional): Optimize response for token efficiency (37% reduction), default: false
|
|
122
|
+
- `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
|
|
123
|
+
|
|
124
|
+
**Response extras:**
|
|
125
|
+
- `approval`: Approval decision record when `approvalStrategy="mcp_managed"`
|
|
126
|
+
- `mcpServers`: Requested/enabled/missing MCP servers for this call
|
|
127
|
+
|
|
128
|
+
**Example:**
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"prompt": "Write a Python function to calculate fibonacci numbers",
|
|
132
|
+
"model": "sonnet",
|
|
133
|
+
"continueSession": true,
|
|
134
|
+
"optimizePrompt": true,
|
|
135
|
+
"optimizeResponse": true
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
##### `codex_request`
|
|
140
|
+
Execute a Codex request with optional session tracking.
|
|
141
|
+
|
|
142
|
+
**Parameters:**
|
|
143
|
+
- `prompt` (string, required): The prompt to send (1-100,000 chars)
|
|
144
|
+
- `model` (string, optional): Model name or alias (use `list_models` for available values; supports `latest`, recommended: `gpt-5.4`)
|
|
145
|
+
- `fullAuto` (boolean, optional): Enable full-auto mode, default: false
|
|
146
|
+
- `dangerouslyBypassApprovalsAndSandbox` (boolean, optional): Request Codex bypass flags
|
|
147
|
+
- `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
|
|
148
|
+
- `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
|
|
149
|
+
- `mcpServers` (string[], optional): MCP servers expected for Codex execution context
|
|
150
|
+
- `sessionId` (string, optional): Session identifier for tracking
|
|
151
|
+
- `createNewSession` (boolean, optional): Always create a new session
|
|
152
|
+
- `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency, default: false
|
|
153
|
+
- `optimizeResponse` (boolean, optional): Optimize response for token efficiency, default: false
|
|
154
|
+
- `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
|
|
155
|
+
- `idleTimeoutMs` (number, optional): Kill a stuck Codex process after output inactivity; 30,000 to 3,600,000 ms
|
|
156
|
+
|
|
157
|
+
**Response extras:**
|
|
158
|
+
- `approval`: Approval decision record when `approvalStrategy="mcp_managed"`
|
|
159
|
+
- `mcpServers`: Requested MCP servers for this call
|
|
160
|
+
|
|
161
|
+
**Example:**
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"prompt": "Create a REST API endpoint",
|
|
165
|
+
"model": "gpt-5.4",
|
|
166
|
+
"fullAuto": true,
|
|
167
|
+
"optimizePrompt": true
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
##### `gemini_request`
|
|
172
|
+
Execute a Gemini CLI request with session support.
|
|
173
|
+
|
|
174
|
+
**Parameters:**
|
|
175
|
+
- `prompt` (string, required): The prompt to send (1-100,000 chars)
|
|
176
|
+
- `model` (string, optional): Model name or alias (use `list_models` for available values; supports `latest`, `pro`, `flash`)
|
|
177
|
+
- `sessionId` (string, optional): Session ID to resume
|
|
178
|
+
- `resumeLatest` (boolean, optional): Resume the latest session automatically
|
|
179
|
+
- `createNewSession` (boolean, optional): Always create a new session
|
|
180
|
+
- `approvalMode` (string, optional): Gemini approval mode (`default|auto_edit|yolo`) in legacy mode
|
|
181
|
+
- `approvalStrategy` (string, optional): `"legacy"` (default) or `"mcp_managed"`
|
|
182
|
+
- `approvalPolicy` (string, optional): `"strict"`, `"balanced"`, or `"permissive"`
|
|
183
|
+
- `mcpServers` (string[], optional): Allowed Gemini MCP server names
|
|
184
|
+
- `allowedTools` (string[], optional): Restrict Gemini tools to this allow-list
|
|
185
|
+
- `includeDirs` (string[], optional): Additional workspace directories for Gemini
|
|
186
|
+
- `optimizePrompt` (boolean, optional): Optimize prompt for token efficiency, default: false
|
|
187
|
+
- `optimizeResponse` (boolean, optional): Optimize response for token efficiency, default: false
|
|
188
|
+
- `correlationId` (string, optional): Request trace ID (auto-generated if omitted)
|
|
189
|
+
|
|
190
|
+
**Response extras:**
|
|
191
|
+
- `approval`: Approval decision record when `approvalStrategy="mcp_managed"`
|
|
192
|
+
- `mcpServers`: Requested MCP servers for this call
|
|
193
|
+
|
|
194
|
+
**Example:**
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"prompt": "Explain quantum computing",
|
|
198
|
+
"model": "latest",
|
|
199
|
+
"resumeLatest": true,
|
|
200
|
+
"optimizePrompt": true
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
##### `claude_request_async` / `codex_request_async`
|
|
205
|
+
Start a long-running Claude or Codex request without waiting for completion in the same MCP call.
|
|
206
|
+
|
|
207
|
+
Use this flow when analysis/runtime can exceed client tool-call limits:
|
|
208
|
+
1. Start job with `*_request_async`
|
|
209
|
+
2. Poll with `llm_job_status`
|
|
210
|
+
3. Fetch output with `llm_job_result`
|
|
211
|
+
4. Optionally stop with `llm_job_cancel`
|
|
212
|
+
|
|
213
|
+
Async request tools accept the same approval strategy fields as their sync variants:
|
|
214
|
+
- `approvalStrategy`: `"legacy"` (default) or `"mcp_managed"`
|
|
215
|
+
- `approvalPolicy`: `"strict"|"balanced"|"permissive"` override
|
|
216
|
+
- `mcpServers`: Requested MCP servers (`sqry`, `exa`, `ref_tools`, `trstr`)
|
|
217
|
+
- `claude_request_async` also supports `strictMcpConfig` and fails fast when requested servers are unavailable
|
|
218
|
+
|
|
219
|
+
##### `llm_job_status`
|
|
220
|
+
Return lifecycle status (`running`, `completed`, `failed`, `canceled`) and metadata for an async job.
|
|
221
|
+
|
|
222
|
+
##### `llm_job_result`
|
|
223
|
+
Return captured stdout/stderr for an async job (with configurable max chars per stream).
|
|
224
|
+
|
|
225
|
+
##### `llm_job_cancel`
|
|
226
|
+
Cancel a running async job.
|
|
227
|
+
|
|
228
|
+
##### `approval_list`
|
|
229
|
+
List recent MCP-managed approval decisions recorded by the gateway.
|
|
230
|
+
|
|
231
|
+
**Parameters:**
|
|
232
|
+
- `limit` (number, optional): Max records (1-500), default: 50
|
|
233
|
+
- `cli` (string, optional): Filter by `"claude"`, `"codex"`, or `"gemini"`
|
|
234
|
+
|
|
235
|
+
Approval records are persisted to `~/.llm-cli-gateway/approvals.jsonl`.
|
|
236
|
+
|
|
237
|
+
#### Session Management Tools
|
|
238
|
+
|
|
239
|
+
##### `session_create`
|
|
240
|
+
Create a new session for a specific CLI.
|
|
241
|
+
|
|
242
|
+
**Parameters:**
|
|
243
|
+
- `cli` (string, required): CLI to create session for ("claude", "codex", "gemini")
|
|
244
|
+
- `description` (string, optional): Description for the session
|
|
245
|
+
- `setAsActive` (boolean, optional): Set as active session, default: true
|
|
246
|
+
|
|
247
|
+
**Example:**
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"cli": "claude",
|
|
251
|
+
"description": "Code review session",
|
|
252
|
+
"setAsActive": true
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
##### `session_list`
|
|
257
|
+
List all sessions, optionally filtered by CLI.
|
|
258
|
+
|
|
259
|
+
**Parameters:**
|
|
260
|
+
- `cli` (string, optional): Filter by CLI ("claude", "codex", "gemini")
|
|
261
|
+
|
|
262
|
+
**Response includes:**
|
|
263
|
+
- Total session count
|
|
264
|
+
- Session details (ID, CLI, description, timestamps, active status)
|
|
265
|
+
- Active session IDs for each CLI
|
|
266
|
+
|
|
267
|
+
##### `session_set_active`
|
|
268
|
+
Set the active session for a specific CLI.
|
|
269
|
+
|
|
270
|
+
**Parameters:**
|
|
271
|
+
- `cli` (string, required): CLI to set active session for
|
|
272
|
+
- `sessionId` (string, required): Session ID to activate (or null to clear)
|
|
273
|
+
|
|
274
|
+
##### `session_get`
|
|
275
|
+
Retrieve details for a specific session.
|
|
276
|
+
|
|
277
|
+
**Parameters:**
|
|
278
|
+
- `sessionId` (string, required): Session ID to retrieve
|
|
279
|
+
|
|
280
|
+
##### `session_delete`
|
|
281
|
+
Delete a specific session.
|
|
282
|
+
|
|
283
|
+
**Parameters:**
|
|
284
|
+
- `sessionId` (string, required): Session ID to delete
|
|
285
|
+
|
|
286
|
+
##### `session_clear_all`
|
|
287
|
+
Clear all sessions, optionally for a specific CLI.
|
|
288
|
+
|
|
289
|
+
**Parameters:**
|
|
290
|
+
- `cli` (string, optional): Clear sessions for specific CLI only
|
|
291
|
+
|
|
292
|
+
#### Utility Tools
|
|
293
|
+
|
|
294
|
+
##### `list_models`
|
|
295
|
+
List available models for each CLI.
|
|
296
|
+
|
|
297
|
+
**Parameters:**
|
|
298
|
+
- `cli` (string, optional): Specific CLI to list models for ("claude", "codex", "gemini")
|
|
299
|
+
|
|
300
|
+
**Response includes:**
|
|
301
|
+
- Model names and descriptions
|
|
302
|
+
- Best use cases for each model
|
|
303
|
+
- CLI-specific information
|
|
304
|
+
|
|
305
|
+
## Session Management
|
|
306
|
+
|
|
307
|
+
### How It Works
|
|
308
|
+
|
|
309
|
+
1. **Automatic Session Tracking**: By default, the gateway automatically tracks sessions for each CLI
|
|
310
|
+
2. **Active Sessions**: Each CLI can have one active session that's used by default
|
|
311
|
+
3. **Persistent Storage**: Sessions are stored in `~/.llm-cli-gateway/sessions.json`
|
|
312
|
+
4. **Context Reuse**: Using sessions maintains conversation history and context
|
|
313
|
+
|
|
314
|
+
### Session Workflow
|
|
315
|
+
|
|
316
|
+
```javascript
|
|
317
|
+
// 1. Create a new session
|
|
318
|
+
await callTool("session_create", {
|
|
319
|
+
cli: "claude",
|
|
320
|
+
description: "Debugging session",
|
|
321
|
+
setAsActive: true
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// 2. Make requests (automatically uses active session)
|
|
325
|
+
await callTool("claude_request", {
|
|
326
|
+
prompt: "What's the bug in this code?",
|
|
327
|
+
// sessionId is automatically used
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// 3. Continue the conversation
|
|
331
|
+
await callTool("claude_request", {
|
|
332
|
+
prompt: "Can you explain that fix in more detail?",
|
|
333
|
+
continueSession: true
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// 4. List all sessions
|
|
337
|
+
await callTool("session_list", { cli: "claude" });
|
|
338
|
+
|
|
339
|
+
// 5. Switch to a different session
|
|
340
|
+
await callTool("session_set_active", {
|
|
341
|
+
cli: "claude",
|
|
342
|
+
sessionId: "some-other-session-id"
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// 6. Delete when done
|
|
346
|
+
await callTool("session_delete", {
|
|
347
|
+
sessionId: "session-id-to-delete"
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Configuration
|
|
352
|
+
|
|
353
|
+
### Environment Variables
|
|
354
|
+
|
|
355
|
+
- `DEBUG`: Enable debug logging (set to any value)
|
|
356
|
+
```bash
|
|
357
|
+
DEBUG=1 node dist/index.js
|
|
358
|
+
```
|
|
359
|
+
- `LLM_GATEWAY_APPROVAL_POLICY`: Default approval policy when request does not pass `approvalPolicy` (`strict`, `balanced`, `permissive`)
|
|
360
|
+
```bash
|
|
361
|
+
LLM_GATEWAY_APPROVAL_POLICY=strict node dist/index.js
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### CLI-Specific Settings
|
|
365
|
+
|
|
366
|
+
Each CLI can be configured through its own configuration files:
|
|
367
|
+
- Claude Code: `~/.claude/config.json`
|
|
368
|
+
- Codex: `~/.codex/config.toml`
|
|
369
|
+
- Gemini: `~/.gemini/config.json`
|
|
370
|
+
|
|
371
|
+
## Development
|
|
372
|
+
|
|
373
|
+
### Project Structure
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
llm-cli-gateway/
|
|
377
|
+
├── src/
|
|
378
|
+
│ ├── index.ts # Main MCP server and tool definitions
|
|
379
|
+
│ ├── executor.ts # CLI execution with timeout support
|
|
380
|
+
│ ├── session-manager.ts # Session management logic
|
|
381
|
+
│ └── __tests__/
|
|
382
|
+
│ ├── executor.test.ts # Unit tests for executor
|
|
383
|
+
│ └── integration.test.ts # Integration tests
|
|
384
|
+
├── dist/ # Compiled JavaScript
|
|
385
|
+
├── package.json
|
|
386
|
+
├── tsconfig.json
|
|
387
|
+
└── vitest.config.ts
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Running Tests
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# Run all tests
|
|
394
|
+
npm test
|
|
395
|
+
|
|
396
|
+
# Run unit tests only
|
|
397
|
+
npm run test:unit
|
|
398
|
+
|
|
399
|
+
# Run integration tests only
|
|
400
|
+
npm run test:integration
|
|
401
|
+
|
|
402
|
+
# Watch mode
|
|
403
|
+
npm run test:watch
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Building
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
npm run build
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Starting the Server
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
npm start
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Error Handling
|
|
419
|
+
|
|
420
|
+
The gateway provides detailed error messages for common issues:
|
|
421
|
+
|
|
422
|
+
### CLI Not Found
|
|
423
|
+
```
|
|
424
|
+
Error executing claude CLI:
|
|
425
|
+
spawn claude ENOENT
|
|
426
|
+
|
|
427
|
+
The 'claude' command was not found. Please ensure claude CLI is installed and in your PATH.
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### External Timeout / Legacy Timeout Option
|
|
431
|
+
```
|
|
432
|
+
Error executing codex CLI: Command timed out
|
|
433
|
+
Process timed out after 120000ms
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Invalid Parameters
|
|
437
|
+
```
|
|
438
|
+
Prompt cannot be empty
|
|
439
|
+
Prompt too long (max 100k chars)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Logging
|
|
443
|
+
|
|
444
|
+
Logs are written to stderr (stdout is reserved for MCP protocol):
|
|
445
|
+
|
|
446
|
+
```
|
|
447
|
+
[INFO] 2026-01-24T05:00:00.000Z - Starting llm-cli-gateway MCP server
|
|
448
|
+
[INFO] 2026-01-24T05:00:01.000Z - claude_request invoked with model=sonnet, prompt length=150
|
|
449
|
+
[INFO] 2026-01-24T05:00:05.000Z - claude_request completed successfully in 4523ms, response length=2048
|
|
450
|
+
[ERROR] 2026-01-24T05:00:10.000Z - codex CLI execution failed: spawn codex ENOENT
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Enable debug logging:
|
|
454
|
+
```bash
|
|
455
|
+
DEBUG=1 node dist/index.js
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Troubleshooting
|
|
459
|
+
|
|
460
|
+
### CLIs Not Found
|
|
461
|
+
|
|
462
|
+
Make sure the CLIs are installed and in your PATH:
|
|
463
|
+
```bash
|
|
464
|
+
which claude
|
|
465
|
+
which codex
|
|
466
|
+
which gemini
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
The gateway extends PATH to include common locations:
|
|
470
|
+
- `~/.local/bin`
|
|
471
|
+
- `/usr/local/bin`
|
|
472
|
+
- `/usr/bin`
|
|
473
|
+
- All `~/.nvm/versions/node/*/bin` directories
|
|
474
|
+
|
|
475
|
+
### Permission Errors
|
|
476
|
+
|
|
477
|
+
If you encounter permission errors, ensure the CLI tools have proper permissions:
|
|
478
|
+
```bash
|
|
479
|
+
chmod +x $(which claude)
|
|
480
|
+
chmod +x $(which codex)
|
|
481
|
+
chmod +x $(which gemini)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Session Storage Issues
|
|
485
|
+
|
|
486
|
+
Sessions are stored in `~/.llm-cli-gateway/sessions.json`. If you encounter issues:
|
|
487
|
+
|
|
488
|
+
1. Check file permissions:
|
|
489
|
+
```bash
|
|
490
|
+
ls -la ~/.llm-cli-gateway/
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
2. Reset sessions:
|
|
494
|
+
```bash
|
|
495
|
+
rm ~/.llm-cli-gateway/sessions.json
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
3. Or manually edit the session file:
|
|
499
|
+
```bash
|
|
500
|
+
cat ~/.llm-cli-gateway/sessions.json
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Performance
|
|
504
|
+
|
|
505
|
+
### Timeouts
|
|
506
|
+
|
|
507
|
+
The gateway does not enforce a default execution timeout for LLM CLI requests.
|
|
508
|
+
|
|
509
|
+
If your MCP client/runtime enforces per-tool-call deadlines, use async tools (`*_request_async` + `llm_job_status`/`llm_job_result`) so long-running jobs can complete outside a single call window.
|
|
510
|
+
|
|
511
|
+
### Concurrent Requests
|
|
512
|
+
|
|
513
|
+
The gateway supports concurrent requests across different CLIs. Each request spawns a separate process.
|
|
514
|
+
|
|
515
|
+
## Security Considerations
|
|
516
|
+
|
|
517
|
+
- **Input Validation**: All prompts are validated (min 1 char, max 100k chars)
|
|
518
|
+
- **Command Execution**: Uses `spawn` with separate arguments (not shell execution)
|
|
519
|
+
- **No Eval**: No dynamic code evaluation
|
|
520
|
+
- **Sandboxing**: Consider running in containers for production use
|
|
521
|
+
|
|
522
|
+
## Contributing
|
|
523
|
+
|
|
524
|
+
1. Fork the repository
|
|
525
|
+
2. Create a feature branch
|
|
526
|
+
3. Make your changes
|
|
527
|
+
4. Run tests: `npm test`
|
|
528
|
+
5. Build: `npm run build`
|
|
529
|
+
6. Submit a pull request
|
|
530
|
+
|
|
531
|
+
## License
|
|
532
|
+
|
|
533
|
+
MIT. See [LICENSE](LICENSE) for details.
|
|
534
|
+
|
|
535
|
+
## Support
|
|
536
|
+
|
|
537
|
+
For issues and questions:
|
|
538
|
+
- Open an issue on GitHub
|
|
539
|
+
- Check existing issues and documentation
|
|
540
|
+
- Review CLI-specific documentation for CLI-related problems
|
|
541
|
+
|
|
542
|
+
## Changelog
|
|
543
|
+
|
|
544
|
+
See [CHANGELOG.md](CHANGELOG.md) for detailed release history.
|
|
545
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Logger } from "./logger.js";
|
|
2
|
+
import type { ReviewIntegrityResult } from "./review-integrity.js";
|
|
3
|
+
export type ApprovalPolicy = "strict" | "balanced" | "permissive";
|
|
4
|
+
export type ApprovalStrategy = "legacy" | "mcp_managed";
|
|
5
|
+
export type ApprovalCli = "claude" | "codex" | "gemini";
|
|
6
|
+
export type ApprovalStatus = "approved" | "denied";
|
|
7
|
+
export interface ApprovalRequest {
|
|
8
|
+
cli: ApprovalCli;
|
|
9
|
+
operation: string;
|
|
10
|
+
prompt: string;
|
|
11
|
+
bypassRequested: boolean;
|
|
12
|
+
fullAuto: boolean;
|
|
13
|
+
requestedMcpServers: string[];
|
|
14
|
+
allowedTools?: string[];
|
|
15
|
+
disallowedTools?: string[];
|
|
16
|
+
policy?: ApprovalPolicy;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
reviewIntegrity?: ReviewIntegrityResult;
|
|
19
|
+
}
|
|
20
|
+
export interface ApprovalRecord {
|
|
21
|
+
id: string;
|
|
22
|
+
ts: string;
|
|
23
|
+
status: ApprovalStatus;
|
|
24
|
+
policy: ApprovalPolicy;
|
|
25
|
+
cli: ApprovalCli;
|
|
26
|
+
operation: string;
|
|
27
|
+
score: number;
|
|
28
|
+
reasons: string[];
|
|
29
|
+
promptPreview: string;
|
|
30
|
+
promptSha256: string;
|
|
31
|
+
requestedMcpServers: string[];
|
|
32
|
+
bypassRequested: boolean;
|
|
33
|
+
fullAuto: boolean;
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
reviewIntegrity?: ReviewIntegrityResult;
|
|
36
|
+
}
|
|
37
|
+
export declare class ApprovalManager {
|
|
38
|
+
private logger;
|
|
39
|
+
private readonly logPath;
|
|
40
|
+
constructor(customPath?: string, logger?: Logger);
|
|
41
|
+
decide(request: ApprovalRequest): ApprovalRecord;
|
|
42
|
+
list(limit?: number, cli?: ApprovalCli): ApprovalRecord[];
|
|
43
|
+
}
|