@cochon123/cli2api 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/dist/adapters/claude.d.ts +27 -0
  4. package/dist/adapters/claude.js +253 -0
  5. package/dist/adapters/claude.js.map +1 -0
  6. package/dist/adapters/codex.d.ts +51 -0
  7. package/dist/adapters/codex.js +491 -0
  8. package/dist/adapters/codex.js.map +1 -0
  9. package/dist/adapters/cursor.d.ts +31 -0
  10. package/dist/adapters/cursor.js +231 -0
  11. package/dist/adapters/cursor.js.map +1 -0
  12. package/dist/adapters/mock.d.ts +2 -0
  13. package/dist/adapters/mock.js +101 -0
  14. package/dist/adapters/mock.js.map +1 -0
  15. package/dist/adapters/opencode.d.ts +27 -0
  16. package/dist/adapters/opencode.js +208 -0
  17. package/dist/adapters/opencode.js.map +1 -0
  18. package/dist/adapters/registry.d.ts +33 -0
  19. package/dist/adapters/registry.js +99 -0
  20. package/dist/adapters/registry.js.map +1 -0
  21. package/dist/adapters/types.d.ts +18 -0
  22. package/dist/adapters/types.js +35 -0
  23. package/dist/adapters/types.js.map +1 -0
  24. package/dist/config.d.ts +126 -0
  25. package/dist/config.js +121 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +253 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/openrouter/catalog.d.ts +28 -0
  31. package/dist/openrouter/catalog.js +162 -0
  32. package/dist/openrouter/catalog.js.map +1 -0
  33. package/dist/protocol/openai.d.ts +48 -0
  34. package/dist/protocol/openai.js +236 -0
  35. package/dist/protocol/openai.js.map +1 -0
  36. package/dist/protocol/responses.d.ts +57 -0
  37. package/dist/protocol/responses.js +142 -0
  38. package/dist/protocol/responses.js.map +1 -0
  39. package/dist/protocol/tools.d.ts +7 -0
  40. package/dist/protocol/tools.js +143 -0
  41. package/dist/protocol/tools.js.map +1 -0
  42. package/dist/server/app.d.ts +25 -0
  43. package/dist/server/app.js +425 -0
  44. package/dist/server/app.js.map +1 -0
  45. package/dist/server/listen.d.ts +18 -0
  46. package/dist/server/listen.js +30 -0
  47. package/dist/server/listen.js.map +1 -0
  48. package/dist/session.d.ts +9 -0
  49. package/dist/session.js +39 -0
  50. package/dist/session.js.map +1 -0
  51. package/dist/types.d.ts +192 -0
  52. package/dist/types.js +3 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/util/process.d.ts +38 -0
  55. package/dist/util/process.js +292 -0
  56. package/dist/util/process.js.map +1 -0
  57. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cli2api contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # cli2api
2
+
3
+ Local OpenAI- and OpenRouter-compatible gateway for coding CLIs.
4
+
5
+ Point any OpenAI SDK / benchmark at localhost and run against **Codex, OpenCode, Cursor Agent, or Claude Code** (plus the built-in mock) using the credentials/login already configured in each CLI.
6
+
7
+ > **Local only.** Binds to `127.0.0.1` by default. Not a multi-tenant product. Do not expose your CLI auth to the network.
8
+
9
+ ## 30-second start
10
+
11
+ Install the published package globally:
12
+
13
+ ```bash
14
+ npm install --global @cochon123/cli2api
15
+ cli2api serve --adapter mock --port 3927
16
+ ```
17
+
18
+ The npm package is scoped because the unscoped `cli2api` name is already owned by another publisher. The installed executable is still named `cli2api`.
19
+
20
+ Before the first npm release, or to install the current GitHub version directly:
21
+
22
+ ```bash
23
+ npm install --global github:cochon123/cli2api
24
+ cli2api --version
25
+ ```
26
+
27
+ For development from a checkout:
28
+
29
+ ```bash
30
+ # from the project root
31
+ npm install
32
+ npm run serve -- --adapter mock --port 3927
33
+ ```
34
+
35
+ The server prints a generated bearer token on stderr if you did not set one. Copy it into your client env:
36
+
37
+ ```bash
38
+ OPENAI_BASE_URL=http://127.0.0.1:3927/v1
39
+ OPENAI_API_KEY=<token printed by serve>
40
+ OPENAI_MODEL=mock/echo
41
+ ```
42
+
43
+ Or pin the token yourself:
44
+
45
+ ```bash
46
+ CLI2API_TOKEN=dev-secret npm run serve -- --adapter mock
47
+ # OPENAI_API_KEY=dev-secret
48
+ ```
49
+
50
+ Smoke:
51
+
52
+ ```bash
53
+ npm run test:smoke
54
+ npm run doctor
55
+ npm run dev -- completion -m mock/echo -p "hello"
56
+ ```
57
+
58
+ ## Use a real CLI
59
+
60
+ Requires [`codex`](https://github.com/openai/codex) on your `PATH` and logged in.
61
+
62
+ ```bash
63
+ npm run doctor -- --adapter codex
64
+ npm run serve -- --adapter codex --port 3927
65
+ ```
66
+
67
+ ```bash
68
+ OPENAI_BASE_URL=http://127.0.0.1:3927/v1
69
+ OPENAI_API_KEY=<token printed by serve>
70
+ OPENAI_MODEL=codex/default
71
+ ```
72
+
73
+ One-shot without the server:
74
+
75
+ ```bash
76
+ npm run dev -- completion -m codex/default -p "Reply with exactly: pong"
77
+ ```
78
+
79
+ The other adapters use the same commands and model-prefix routing:
80
+
81
+ ```bash
82
+ # OpenCode: completed JSON reasoning/text blocks, safe `plan` agent, plugins disabled
83
+ npm run completion -- -m opencode/deepseek-v4-flash-free -p "Reply with: pong"
84
+
85
+ # Cursor: native partial reasoning/content deltas, read-only ask mode + sandbox
86
+ npm run completion -- -m cursor/composer-2.5-fast -p "Reply with: pong"
87
+
88
+ # Claude Code: native partial stream-json, plan mode, all built-in/MCP tools disabled
89
+ npm run completion -- -m claude/default -p "Reply with: pong"
90
+ ```
91
+
92
+ Claude Code is implemented from its official headless CLI contract but was not live-tested on the development machine because `claude` was not installed.
93
+
94
+ ## CLI
95
+
96
+ | Command | Purpose |
97
+ |---------|---------|
98
+ | `cli2api serve` | Start HTTP gateway |
99
+ | `cli2api doctor` | Check adapters / binaries (`--json`) |
100
+ | `cli2api models` | List model ids |
101
+ | `cli2api completion` | Smoke a prompt |
102
+ | `cli2api adapters` | List adapters |
103
+
104
+ ```bash
105
+ npx tsx src/index.ts serve --help
106
+ ```
107
+
108
+ After `npm run build`, the bin is `cli2api`.
109
+
110
+ ## Env vars
111
+
112
+ | Var | Meaning |
113
+ |-----|---------|
114
+ | `CLI2API_ADAPTER` | Default adapter (`mock` \| `codex` \| `opencode` \| `cursor` \| `claude`) |
115
+ | `CLI2API_TOKEN` | Bearer token (auto-generated for the session if unset) |
116
+ | `CLI2API_CODEX_BIN` | Path/name of Codex binary |
117
+ | `CLI2API_OPENCODE_BIN` | Path/name of OpenCode binary |
118
+ | `CLI2API_CURSOR_BIN` | Path/name of Cursor Agent binary |
119
+ | `CLI2API_CLAUDE_BIN` | Path/name of Claude Code binary |
120
+ | `CLI2API_CWD` | Working directory for CLI adapters |
121
+ | `CLI2API_CHILD_ENV_ALLOWLIST` | Comma-separated extra parent env names to explicitly pass to child CLIs |
122
+ | `CLI2API_CONFIG` | Explicit JSON config path |
123
+ | `CLI2API_OPENROUTER_CATALOG` | OpenRouter catalog mode (`runnable` or `mirror`) |
124
+ | `OPENROUTER_API_KEY` | Optional key used only to refresh OpenRouter model metadata |
125
+
126
+ ## Config and model aliases
127
+
128
+ JSON config is merged in this order: `$XDG_CONFIG_HOME/cli2api/config.json`, project `.cli2api.json`, then `--config <path>` / `CLI2API_CONFIG`. Later values win; `modelAliases` and `binaries` merge by key.
129
+
130
+ ```json
131
+ {
132
+ "defaultAdapter": "opencode",
133
+ "port": 3927,
134
+ "token": "local-secret",
135
+ "cwd": "/path/to/project",
136
+ "modelAliases": {
137
+ "fast": "opencode/deepseek-v4-flash-free",
138
+ "composer": "cursor/composer-2.5-fast"
139
+ },
140
+ "openRouter": {
141
+ "defaultModel": "anthropic/claude-sonnet-4",
142
+ "catalogMode": "runnable",
143
+ "annotateAvailability": true,
144
+ "modelRoutes": {
145
+ "anthropic/claude-sonnet-4": {
146
+ "adapter": "claude",
147
+ "model": "sonnet"
148
+ },
149
+ "openai/gpt-local": {
150
+ "adapter": "codex",
151
+ "model": "default"
152
+ }
153
+ }
154
+ },
155
+ "binaries": {
156
+ "codex": "codex",
157
+ "opencode": "opencode",
158
+ "cursor": "cursor-agent",
159
+ "claude": "claude"
160
+ }
161
+ }
162
+ ```
163
+
164
+ Aliases appear in `/v1/models` and work anywhere a model id is accepted.
165
+
166
+ ## API (subset)
167
+
168
+ - `GET /health`
169
+ - `GET /v1/models`
170
+ - `POST /v1/chat/completions` — non-stream + `stream: true` (SSE)
171
+ - `POST /v1/responses` — text/function input, non-stream + semantic SSE events
172
+ - `GET /api/v1/models` — OpenRouter-compatible model catalog
173
+ - `POST /api/v1/chat/completions` — OpenRouter-compatible chat, tools, reasoning, and SSE
174
+ - `POST /api/v1/responses` — OpenRouter path alias for the Responses subset
175
+
176
+ All endpoints require `Authorization: Bearer <token>`.
177
+
178
+ Model ids: `adapter/model` — e.g. `mock/echo`, `codex/default`, `opencode/deepseek-v4-flash-free`, `cursor/composer-2.5-fast`, `claude/sonnet`. OpenCode provider-qualified models keep the second slash, for example `opencode/openrouter/deepseek/deepseek-v4-flash`.
179
+
180
+ ## OpenRouter compatibility
181
+
182
+ Point an OpenRouter-oriented client at `http://127.0.0.1:3927/api/v1` and use the same cli2api bearer token. Public OpenRouter model ids are mapped to local CLI models with `openRouter.modelRoutes`; the public id is preserved in responses while routing stays internal.
183
+
184
+ If an application relies on OpenRouter's account-level default and omits `model`, set `openRouter.defaultModel` locally.
185
+
186
+ The OpenRouter catalog has two modes:
187
+
188
+ - `runnable` (default) lists only models cli2api can execute. Configured OpenRouter ids are enriched with metadata from OpenRouter's models API.
189
+ - `mirror` returns the cached OpenRouter catalog so model discovery resembles production. Models without a local route remain visible but return a `model_not_available` error when called.
190
+
191
+ Select the mode in config or with `cli2api serve --openrouter-catalog runnable|mirror`.
192
+
193
+ Metadata is cached for 24 hours at `$XDG_CACHE_HOME/cli2api/openrouter-models.json` (or `~/.cache/cli2api/openrouter-models.json`). Stale data is used if refresh fails. Configure `metadataTtlSeconds`, `metadataCachePath`, or `metadataUrl` under `openRouter` when needed. Pricing fields describe OpenRouter's hosted service, not local CLI execution.
194
+
195
+ For safety, `OPENROUTER_API_KEY` is sent only to the canonical `https://openrouter.ai` origin. Custom `metadataUrl` sources never receive it. Cache entries are tied to their source URL so catalogs cannot be mixed across sources.
196
+
197
+ Set `annotateAvailability` to `true` to add a `cli2api` object to model records with local availability and routing details. Set it to `false` in `mirror` mode for the closest upstream catalog shape.
198
+
199
+ OpenRouter cloud-only routing features such as provider fallback, billing, and plugins are accepted as request metadata but are not performed locally. Adapter capabilities still determine the actual result.
200
+
201
+ Function tools are accepted in both OpenAI API shapes. Because these coding CLIs do not expose a uniform native tool protocol, cli2api supplies the function schemas in the prompt and validates a strict JSON call envelope; it returns standard `tool_calls` / `function_call` output. Tool names and JSON argument objects are always validated, and `strict: true` arguments are validated against the supplied JSON Schema. The mock adapter has native deterministic tool events for tests.
202
+
203
+ For Chat Completions, reuse a client-chosen `session_id` to resume the CLI's native conversation. For Responses, pass the prior returned `id` as `previous_response_id`; unknown, expired, restarted, or adapter-mismatched ids return HTTP 400 instead of silently losing context. Mappings are adapter-bound, in memory, limited to 1,000 entries, and expire after 24 hours; restarting cli2api clears them. On native resume, only messages after the last assistant turn are sent, avoiding duplicate history. Responses `instructions` are forwarded as a developer message.
204
+
205
+ ## Phase 0 scope
206
+
207
+ - [x] OpenAI chat completions + models
208
+ - [x] OpenRouter-compatible paths, model routing, catalog modes, reasoning, and streaming usage
209
+ - [x] Mock adapter (instant env-swap proof)
210
+ - [x] Codex adapter via `codex exec --json`
211
+ - [x] Loopback-only bind + `doctor` / `completion`
212
+ - [x] Required bearer token (auto-generated if unset)
213
+ - [x] No open CORS (SDK/script clients only)
214
+ - [x] OpenCode adapter (`run --format json --thinking`, safe `plan` agent)
215
+ - [x] Cursor Agent adapter (native partial `stream-json`, read-only `ask` mode)
216
+ - [x] Claude Code adapter (headless partial `stream-json`, restrictive flags; not live-tested locally)
217
+ - [x] Real JSONL streaming from Codex (`codex exec --json` line-by-line)
218
+ - Enable reasoning summaries via `-c model_reasoning_summary=detailed` (+ supports / unhide)
219
+ - Reasoning summary items → word-by-word `delta.reasoning` / `reasoning_content`
220
+ - Final `agent_message` → word-by-word `delta.content`
221
+ - Short lifecycle crumbs still go to reasoning without fake-stream delay
222
+ - [x] Config files + model aliases
223
+ - [x] Function tool calling (validated prompt-envelope fallback)
224
+ - [x] Responses API subset + semantic streaming events
225
+ - [x] Native CLI session resume (`session_id` / `previous_response_id`)
226
+ - [x] Env scrubbing for spawned CLI processes with a small runtime allowlist, per-adapter auth vars, and explicit opt-in passthrough
227
+
228
+ ## Project layout
229
+
230
+ ```
231
+ src/
232
+ index.ts # CLI
233
+ adapters/ # mock, codex, registry
234
+ protocol/openai.ts # request/response shaping
235
+ openrouter/catalog.ts # cached metadata and runnable/mirror model views
236
+ server/ # Hono app + listen
237
+ types.ts
238
+ scripts/smoke.ts
239
+ ```
240
+
241
+ ## Safety
242
+
243
+ - Default bind: `127.0.0.1` only (refuse non-loopback hosts).
244
+ - Bearer token required on every request; generated on startup if unset.
245
+ - No CORS middleware — browser tabs on the same machine cannot read responses via `fetch` from arbitrary origins.
246
+ - Codex adapter defaults to `--sandbox read-only` (blocks writes; does **not** block reading files the agent is asked about).
247
+ - Spawn uses argv arrays only (`shell: false`); no prompt concatenation into a shell string.
248
+ - Child processes receive a scrubbed environment. Normal runtime paths/local config homes and narrowly scoped adapter auth variables are retained; unrelated parent secrets are removed. Add exceptional keys explicitly with `CLI2API_CHILD_ENV_ALLOWLIST`.
249
+ - OpenCode runs with its `plan` agent and external plugins disabled.
250
+ - Cursor runs in read-only `ask` mode with its sandbox enabled; `--trust` only acknowledges the configured working directory for headless startup.
251
+ - Claude runs in `plan` mode with built-in tools disabled and configured MCP tools excluded.
252
+ - You are responsible for complying with each CLI vendor’s terms for local use.
@@ -0,0 +1,27 @@
1
+ import type { Adapter } from "./types.js";
2
+ import type { ChatCompletionResponse } from "../types.js";
3
+ export interface ClaudeAdapterOptions {
4
+ binary?: string;
5
+ cwd?: string;
6
+ timeoutMs?: number;
7
+ extraArgs?: string[];
8
+ }
9
+ export type ClaudeParsedLine = {
10
+ kind: "content" | "reasoning";
11
+ text: string;
12
+ partial: boolean;
13
+ } | {
14
+ kind: "session";
15
+ id: string;
16
+ } | {
17
+ kind: "result";
18
+ text?: string;
19
+ usage?: ChatCompletionResponse["usage"];
20
+ } | {
21
+ kind: "error";
22
+ message: string;
23
+ } | {
24
+ kind: "ignore";
25
+ };
26
+ export declare function parseClaudeLine(line: string): ClaudeParsedLine[];
27
+ export declare function createClaudeAdapter(opts?: ClaudeAdapterOptions): Adapter;
@@ -0,0 +1,253 @@
1
+ import { requestToPrompt } from "../protocol/openai.js";
2
+ import { runCommand, runCommandLines, which } from "../util/process.js";
3
+ const DEFAULT_MODELS = ["default", "sonnet", "opus", "haiku"];
4
+ function record(value) {
5
+ return value && typeof value === "object" ? value : null;
6
+ }
7
+ function assistantBlocks(value) {
8
+ if (!Array.isArray(value))
9
+ return [];
10
+ const result = [];
11
+ for (const raw of value) {
12
+ const block = record(raw);
13
+ if (!block)
14
+ continue;
15
+ if (block.type === "text" && typeof block.text === "string" && block.text) {
16
+ result.push({ kind: "content", text: block.text });
17
+ }
18
+ else if (block.type === "thinking" && typeof block.thinking === "string" && block.thinking) {
19
+ result.push({ kind: "reasoning", text: block.thinking });
20
+ }
21
+ }
22
+ return result;
23
+ }
24
+ export function parseClaudeLine(line) {
25
+ const trimmed = line.trim();
26
+ if (!trimmed)
27
+ return [{ kind: "ignore" }];
28
+ let parsed;
29
+ try {
30
+ parsed = JSON.parse(trimmed);
31
+ }
32
+ catch {
33
+ return trimmed.startsWith("{") ? [{ kind: "ignore" }] : [{ kind: "content", text: trimmed, partial: false }];
34
+ }
35
+ const message = record(parsed);
36
+ if (!message)
37
+ return [{ kind: "ignore" }];
38
+ const type = typeof message.type === "string" ? message.type : "";
39
+ if (type === "system" && message.subtype === "init" && typeof message.session_id === "string") {
40
+ return [{ kind: "session", id: message.session_id }];
41
+ }
42
+ if (type === "stream_event") {
43
+ const event = record(message.event);
44
+ const delta = record(event?.delta);
45
+ if (event?.type === "content_block_delta" && delta) {
46
+ if (delta.type === "text_delta" && typeof delta.text === "string") {
47
+ return [{ kind: "content", text: delta.text, partial: true }];
48
+ }
49
+ if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
50
+ return [{ kind: "reasoning", text: delta.thinking, partial: true }];
51
+ }
52
+ }
53
+ return [{ kind: "ignore" }];
54
+ }
55
+ if (type === "assistant") {
56
+ const nested = record(message.message);
57
+ const blocks = assistantBlocks(nested?.content);
58
+ return blocks.length
59
+ ? blocks.map((block) => ({ ...block, partial: false }))
60
+ : [{ kind: "ignore" }];
61
+ }
62
+ if (type === "result") {
63
+ if (message.is_error === true || message.subtype === "error") {
64
+ return [{ kind: "error", message: typeof message.result === "string" ? message.result : "Claude Code failed" }];
65
+ }
66
+ const rawUsage = record(message.usage);
67
+ let usage;
68
+ if (rawUsage) {
69
+ const prompt = typeof rawUsage.input_tokens === "number" ? rawUsage.input_tokens : 0;
70
+ const completion = typeof rawUsage.output_tokens === "number" ? rawUsage.output_tokens : 0;
71
+ usage = { prompt_tokens: prompt, completion_tokens: completion, total_tokens: prompt + completion };
72
+ }
73
+ return [{
74
+ kind: "result",
75
+ text: typeof message.result === "string" ? message.result : undefined,
76
+ usage,
77
+ }];
78
+ }
79
+ if (type === "error") {
80
+ return [{ kind: "error", message: typeof message.message === "string" ? message.message : "Claude Code stream error" }];
81
+ }
82
+ return [{ kind: "ignore" }];
83
+ }
84
+ export function createClaudeAdapter(opts = {}) {
85
+ const binary = opts.binary ?? "claude";
86
+ const timeoutMs = opts.timeoutMs ?? 180_000;
87
+ return {
88
+ id: "claude",
89
+ description: "Claude Code via headless stream-json (plan mode, tools disabled)",
90
+ async listModels() {
91
+ return DEFAULT_MODELS.map((model) => ({
92
+ id: `claude/${model}`,
93
+ object: "model",
94
+ created: 0,
95
+ owned_by: "anthropic",
96
+ description: model === "default" ? "Uses the Claude Code default model" : undefined,
97
+ }));
98
+ },
99
+ async *chat(req, signal) {
100
+ const path = await which(binary);
101
+ if (!path) {
102
+ yield {
103
+ type: "error",
104
+ message: `claude binary not found on PATH (looked for "${binary}"). Install Claude Code or set CLI2API_CLAUDE_BIN.`,
105
+ code: "binary_missing",
106
+ };
107
+ return;
108
+ }
109
+ // Official headless flags. Empty --tools disables built-ins; strict MCP plus
110
+ // the deny rule prevents configured MCP tools from bypassing that restriction.
111
+ const args = [
112
+ "-p",
113
+ "--output-format",
114
+ "stream-json",
115
+ "--verbose",
116
+ "--include-partial-messages",
117
+ "--permission-mode",
118
+ "plan",
119
+ "--tools",
120
+ "",
121
+ "--disallowedTools",
122
+ "mcp__*",
123
+ "--strict-mcp-config",
124
+ ];
125
+ if (req.nativeSessionId)
126
+ args.push("--resume", req.nativeSessionId);
127
+ if (req.modelLocal && req.modelLocal !== "default")
128
+ args.push("--model", req.modelLocal);
129
+ if (opts.extraArgs?.length)
130
+ args.push(...opts.extraArgs);
131
+ args.push(requestToPrompt(req));
132
+ let sawContent = false;
133
+ let sawPartialContent = false;
134
+ let sawPartialReasoning = false;
135
+ let fallbackResult = "";
136
+ let usage;
137
+ let exitCode = null;
138
+ let timedOut = false;
139
+ let stderr = "";
140
+ try {
141
+ for await (const event of runCommandLines(path, args, {
142
+ cwd: opts.cwd,
143
+ timeoutMs,
144
+ signal,
145
+ inheritEnv: [
146
+ "CLAUDE_CONFIG_DIR",
147
+ "CLAUDE_CODE_OAUTH_TOKEN",
148
+ "ANTHROPIC_API_KEY",
149
+ "ANTHROPIC_AUTH_TOKEN",
150
+ "ANTHROPIC_BASE_URL",
151
+ ],
152
+ })) {
153
+ if (event.type === "stdout_line") {
154
+ for (const parsed of parseClaudeLine(event.line)) {
155
+ if (parsed.kind === "content") {
156
+ if (parsed.partial) {
157
+ sawPartialContent = true;
158
+ sawContent = true;
159
+ yield { type: "delta", text: parsed.text, channel: "content" };
160
+ }
161
+ else if (!sawPartialContent) {
162
+ sawContent = true;
163
+ yield { type: "delta", text: parsed.text, channel: "content" };
164
+ }
165
+ }
166
+ else if (parsed.kind === "reasoning") {
167
+ if (parsed.partial) {
168
+ sawPartialReasoning = true;
169
+ yield { type: "delta", text: parsed.text, channel: "reasoning" };
170
+ }
171
+ else if (!sawPartialReasoning) {
172
+ yield { type: "delta", text: parsed.text, channel: "reasoning" };
173
+ }
174
+ }
175
+ else if (parsed.kind === "session") {
176
+ yield { type: "session", id: parsed.id };
177
+ }
178
+ else if (parsed.kind === "result") {
179
+ fallbackResult = parsed.text ?? fallbackResult;
180
+ usage = parsed.usage ?? usage;
181
+ }
182
+ else if (parsed.kind === "error") {
183
+ yield { type: "error", message: parsed.message, code: "cli_error" };
184
+ return;
185
+ }
186
+ }
187
+ }
188
+ else if (event.type === "exit") {
189
+ exitCode = event.code;
190
+ timedOut = event.timedOut;
191
+ stderr = event.stderr;
192
+ }
193
+ }
194
+ }
195
+ catch (error) {
196
+ yield {
197
+ type: "error",
198
+ message: `Failed to spawn claude: ${error instanceof Error ? error.message : String(error)}`,
199
+ code: "spawn_error",
200
+ };
201
+ return;
202
+ }
203
+ if (!sawContent && fallbackResult) {
204
+ sawContent = true;
205
+ yield { type: "delta", text: fallbackResult, channel: "content" };
206
+ }
207
+ if (timedOut) {
208
+ yield { type: "error", message: `claude timed out after ${timeoutMs}ms`, code: "timeout" };
209
+ }
210
+ else if (signal.aborted) {
211
+ yield { type: "error", message: "Aborted", code: "abort" };
212
+ }
213
+ else if (exitCode !== 0) {
214
+ yield {
215
+ type: "error",
216
+ message: `claude exited with code ${exitCode}${stderr.trim() ? `: ${stderr.trim().slice(0, 2000)}` : ""}`,
217
+ code: "cli_error",
218
+ };
219
+ }
220
+ else if (!sawContent) {
221
+ yield { type: "error", message: "claude returned empty output", code: "empty_output" };
222
+ }
223
+ else {
224
+ yield { type: "done", finishReason: "stop", usage };
225
+ }
226
+ },
227
+ async health() {
228
+ const path = await which(binary);
229
+ if (!path)
230
+ return { ok: false, adapter: "claude", details: { binary }, message: "claude binary not found on PATH" };
231
+ const version = await runCommand(path, ["--version"], { timeoutMs: 8_000 });
232
+ return {
233
+ ok: version.code === 0,
234
+ adapter: "claude",
235
+ details: { binary: path, version: (version.stdout || version.stderr).trim(), permissionMode: "plan", tools: "disabled" },
236
+ message: version.code === 0 ? "claude available" : "claude --version failed",
237
+ };
238
+ },
239
+ async doctor() {
240
+ const path = await which(binary);
241
+ const checks = [{ name: "binary-on-path", ok: Boolean(path), detail: path ?? `missing: ${binary}` }];
242
+ let version;
243
+ if (path) {
244
+ const result = await runCommand(path, ["--version"], { timeoutMs: 8_000 });
245
+ version = (result.stdout || result.stderr).trim();
246
+ checks.push({ name: "version", ok: result.code === 0, detail: version || `exit ${result.code}` });
247
+ }
248
+ checks.push({ name: "restrictive-default", ok: true, detail: "permission-mode=plan; built-in and MCP tools disabled" });
249
+ return { adapter: "claude", ok: Boolean(path) && checks.every((check) => check.ok), binary: path ?? binary, version, checks };
250
+ },
251
+ };
252
+ }
253
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/adapters/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { Adapter } from "./types.js";
2
+ import type { ChatEvent } from "../types.js";
3
+ export interface CodexAdapterOptions {
4
+ /** Binary name or path (default: codex) */
5
+ binary?: string;
6
+ /** Working directory for codex exec (default: cwd) */
7
+ cwd?: string;
8
+ /** Extra args appended to `codex exec` */
9
+ extraArgs?: string[];
10
+ /** Request timeout in ms */
11
+ timeoutMs?: number;
12
+ /**
13
+ * Sandbox mode for exec.
14
+ * Default: read-only (safer for API/benchmark prompts that should not mutate disk).
15
+ */
16
+ sandbox?: "read-only" | "workspace-write" | "danger-full-access";
17
+ /** Skip git repo check (needed outside of git repos) */
18
+ skipGitRepoCheck?: boolean;
19
+ /** Word-by-word delay for content fake-stream (ms). 0 = instant chunks. */
20
+ contentWordDelayMs?: number;
21
+ }
22
+ /** Yield text word-by-word so SSE clients see a typing effect. */
23
+ export declare function fakeStreamWords(text: string, delayMs: number, signal?: AbortSignal, channel?: "content" | "reasoning"): AsyncGenerator<ChatEvent>;
24
+ export type CodexLineKind = "content" | "reasoning" | "session" | "done" | "error" | "ignore";
25
+ export interface CodexParsedLine {
26
+ kind: CodexLineKind;
27
+ text?: string;
28
+ /** True for model reasoning summaries (fake-stream); false for short status crumbs. */
29
+ fakeStream?: boolean;
30
+ sessionId?: string;
31
+ error?: ChatEvent & {
32
+ type: "error";
33
+ };
34
+ done?: ChatEvent & {
35
+ type: "done";
36
+ };
37
+ }
38
+ /**
39
+ * Parse one Codex JSONL line into a typed payload.
40
+ * Content (agent_message) is returned whole — caller fake-streams words.
41
+ * Everything else useful goes to reasoning.
42
+ */
43
+ export declare function parseCodexLine(line: string): CodexParsedLine;
44
+ /**
45
+ * Map one Codex JSONL event to ChatEvents (content returned as a single delta;
46
+ * prefer parseCodexLine + fakeStreamWords in the live adapter path).
47
+ */
48
+ export declare function chatEventsFromCodexLine(line: string): ChatEvent[];
49
+ /** Batch helper kept for tests / non-streaming inspection. */
50
+ export declare function parseCodexJsonl(stdout: string): string;
51
+ export declare function createCodexAdapter(opts?: CodexAdapterOptions): Adapter;