@gullabs/claude-cli 0.6.1 → 0.16.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/NOTICE +6 -0
- package/README.md +97 -13
- package/dist/index.cjs +311 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -18
- package/dist/index.d.ts +48 -18
- package/dist/index.js +309 -47
- package/dist/index.js.map +1 -1
- package/package.json +18 -8
package/NOTICE
ADDED
package/README.md
CHANGED
|
@@ -10,34 +10,42 @@
|
|
|
10
10
|
[`@gullabs/core`](../core) that routes LLM calls through the `claude`
|
|
11
11
|
(Claude Code) CLI instead of an API key. Because the CLI owns its own
|
|
12
12
|
OAuth/keychain-backed session, calls made through this adapter cost $0 in API
|
|
13
|
-
spend
|
|
13
|
+
spend (the runner scrubs `ANTHROPIC_API_KEY` and the other credential variables from
|
|
14
|
+
the child's environment so that stays true; see below) — the CLI reports its own cost for observability, but that number is
|
|
14
15
|
never fed into `@gullabs/core`'s cost engine (`Cost.microUsd` naturally
|
|
15
16
|
resolves to `null` because these models are unpriced).
|
|
16
17
|
|
|
17
18
|
## Install
|
|
18
19
|
|
|
19
20
|
```sh
|
|
20
|
-
pnpm add -D @gullabs/claude-cli
|
|
21
|
+
pnpm add -D @gullabs/claude-cli @gullabs/core
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
## Key exports
|
|
24
25
|
|
|
25
26
|
| Export | Kind | Description |
|
|
26
27
|
| --------------------------- | -------- | --------------------------------------------------------------- |
|
|
28
|
+
| `claudeCliProvider` | function | The `ProviderPlugin` for `composeProviders` (adapter + models). |
|
|
27
29
|
| `claudeCliAdapter` | function | Creates the `ProviderAdapter` (`id: 'claude-cli'`). |
|
|
28
|
-
| `ClaudeCliAdapterOptions` | type | `{ runner?, claudePath?, maxConcurrency? }`.
|
|
30
|
+
| `ClaudeCliAdapterOptions` | type | `{ runner?, claudePath?, maxConcurrency?, env? }`. |
|
|
29
31
|
| `buildClaudeCliRunner` | function | The real `node:child_process`-backed `ClaudeCliRunner` factory. |
|
|
30
32
|
| `ClaudeCliRunner` | type | The process-execution seam; inject a fake in tests. |
|
|
33
|
+
| `ClaudeCliRunOptions` | type | `{ cwd, timeoutMs?, signal?, env? }`, the options of `run`. |
|
|
34
|
+
| `ClaudeCliRunResult` | type | `{ stdout, stderr, exitCode }`. |
|
|
35
|
+
| `ClaudeCliEnvelope` | type | The `--output-format json` result envelope the adapter reads. |
|
|
31
36
|
| `claudeCliModelDescriptors` | value | `ModelDescriptor[]` for the 4 supported model ids. |
|
|
32
37
|
| `claudeCliRegistry` | value | `ModelRegistry` built from `claudeCliModelDescriptors`. |
|
|
38
|
+
| `CLAUDE_CLI_MODEL_IDS` | value | The four registered model ids. |
|
|
39
|
+
| `CLAUDE_CLI_EFFORTS` | value | `['low', 'medium', 'high', 'xhigh', 'max']`. |
|
|
40
|
+
| `Claude…ConfigSchema` (x4) | value | The strict zod config schema of each model. |
|
|
33
41
|
|
|
34
42
|
## Quick example
|
|
35
43
|
|
|
36
44
|
```ts
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
45
|
+
import { composeProviders, createClient } from '@gullabs/core'
|
|
46
|
+
import { claudeCliProvider } from '@gullabs/claude-cli'
|
|
39
47
|
|
|
40
|
-
const client = createClient({
|
|
48
|
+
const client = createClient({ ...composeProviders([claudeCliProvider()]) })
|
|
41
49
|
|
|
42
50
|
const result = await client.generate(
|
|
43
51
|
{
|
|
@@ -62,14 +70,85 @@ would break subscription-based Claude Code auth and defeat the entire point
|
|
|
62
70
|
of this package (working with **zero** API-key configuration). The full
|
|
63
71
|
invariant argv (never caller-configurable) is:
|
|
64
72
|
|
|
73
|
+
The quotes below illustrate argument boundaries; the runner passes an argv
|
|
74
|
+
array directly without a shell.
|
|
75
|
+
|
|
65
76
|
```
|
|
66
|
-
-p --output-format json --safe-mode --tools "" --disable-slash-commands --no-session-persistence
|
|
77
|
+
-p --output-format json --safe-mode --tools "" --disable-slash-commands --no-session-persistence --settings '{"switchModelsOnFlag":false}'
|
|
67
78
|
```
|
|
68
79
|
|
|
80
|
+
`--settings` requests that the CLI disable silent model switches on a safety
|
|
81
|
+
flag; the CLI does not provide strict validation for this setting. Enforcement
|
|
82
|
+
comes from the post-run `modelUsage` check: a successful response must report
|
|
83
|
+
the requested id and no other model. Any other model id throws
|
|
84
|
+
`LlmError` kind `server` (not retryable). A successful envelope with
|
|
85
|
+
`stop_reason: "refusal"` returns `finishReason: 'content_filter'` and preserves
|
|
86
|
+
its billed usage; an error envelope throws `content_filter`.
|
|
87
|
+
|
|
88
|
+
The live capture of 2026-09-26 (Claude Code 2.1.282) shows it: the invariant argv
|
|
89
|
+
and `--json-schema` completed for all four registered ids, and each success
|
|
90
|
+
envelope contained only its requested id as a `modelUsage` key. The sanitized
|
|
91
|
+
responses are in `src/__fixtures__/model-refresh-p-a1.json`. The full installed CLI version and
|
|
92
|
+
envelope shape are runtime dependencies; an absent `modelUsage` fails closed.
|
|
93
|
+
|
|
94
|
+
Usage follows Anthropic's accounting, in which `input_tokens` excludes both cache lanes: `inputTokens` is
|
|
95
|
+
`input_tokens + cache_read_input_tokens + cache_creation_input_tokens`, `cachedInputTokens` is the cache-read
|
|
96
|
+
part, `details.cacheWrite` is the cache-creation part, and `thinkingTokens` is
|
|
97
|
+
`output_tokens_details.thinking_tokens` (inside `outputTokens`). The adapter is unpriced, so none of this
|
|
98
|
+
becomes a cost.
|
|
99
|
+
|
|
69
100
|
`--model`, `--effort`, `--system-prompt`, and `--json-schema` are appended
|
|
70
101
|
from the request when applicable; the prompt itself is always sent over
|
|
71
102
|
stdin, never as a positional argv entry.
|
|
72
103
|
|
|
104
|
+
`output.jsonSchema` is passed to `--json-schema` as written. Unlike the Google and
|
|
105
|
+
xAI adapters (ADR-034), this adapter does not check the schema against a keyword
|
|
106
|
+
profile: `nullable`, uppercase type names and keywords the CLI may ignore are not
|
|
107
|
+
rejected here, so validate the result yourself.
|
|
108
|
+
|
|
109
|
+
## Environment: the subscription login, never an API key
|
|
110
|
+
|
|
111
|
+
Claude Code's own documentation says `ANTHROPIC_API_KEY` is "used instead of your
|
|
112
|
+
Claude Pro, Max, Team, or Enterprise subscription even if you are logged in. In
|
|
113
|
+
non-interactive mode (`-p`), the key is always used when present"
|
|
114
|
+
(<https://code.claude.com/docs/en/env-vars>), and the adapter always runs `-p`. A
|
|
115
|
+
host that has the key exported for an Anthropic SDK would therefore have every call
|
|
116
|
+
billed to it while the ledger books the call as unpriced, and nothing in the result
|
|
117
|
+
would say so. So the real runner does not hand the host environment to the child. It
|
|
118
|
+
builds an allowlisted copy of `process.env`: `PATH`, `HOME`, `USER`, `LOGNAME`,
|
|
119
|
+
`LANG`/`LC_*`, `TERM`, `TZ`, `TMPDIR`/`TEMP`/`TMP`, `SHELL`, `XDG_*`, the Windows
|
|
120
|
+
profile variables, the proxy variables (`HTTPS_PROXY`, `HTTP_PROXY`, `ALL_PROXY`,
|
|
121
|
+
`NO_PROXY`, either case), `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`/`SSL_CERT_DIR`, and
|
|
122
|
+
the CLI's own documented settings for a subscription login: `CLAUDE_CONFIG_DIR`,
|
|
123
|
+
`CLAUDE_CODE_OAUTH_TOKEN` (the long-lived subscription token from
|
|
124
|
+
`claude setup-token`) and the mTLS variables `CLAUDE_CODE_CLIENT_CERT`,
|
|
125
|
+
`CLAUDE_CODE_CLIENT_KEY`, `CLAUDE_CODE_CLIENT_KEY_PASSPHRASE`. `ANTHROPIC_API_KEY`,
|
|
126
|
+
`ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, `CLAUDE_CODE_USE_BEDROCK`/`_VERTEX`/
|
|
127
|
+
`_FOUNDRY` and everything else are dropped.
|
|
128
|
+
|
|
129
|
+
The list does keep credentials: the library never interprets an environment credential,
|
|
130
|
+
but `CLAUDE_CODE_OAUTH_TOKEN` and the mTLS variables `CLAUDE_CODE_CLIENT_CERT`,
|
|
131
|
+
`CLAUDE_CODE_CLIENT_KEY` and `CLAUDE_CODE_CLIENT_KEY_PASSPHRASE` are forwarded from the
|
|
132
|
+
host environment to the CLI, so a host that has them exported is using them (and a proxy
|
|
133
|
+
URL in `HTTPS_PROXY` can carry credentials too). `env` adds to the copy and cannot remove
|
|
134
|
+
from it: to keep the CLI off an ambient token, unset the variable before the call.
|
|
135
|
+
|
|
136
|
+
`claudeCliAdapter({ env })` adds variables on top, and they win over the allowlisted
|
|
137
|
+
ones. It is the one way to pass anything else on. Putting an API key there is the
|
|
138
|
+
explicit opt-in: the call is then billed to it. `env` is validated at construction
|
|
139
|
+
(string names without `=`, string values without NUL), else `bad_request`. A custom
|
|
140
|
+
runner receives it as `ClaudeCliRunOptions.env` and decides for itself.
|
|
141
|
+
|
|
142
|
+
## Queued calls and killed processes
|
|
143
|
+
|
|
144
|
+
A call waits for a slot before it makes its scratch directory, and leaves the queue
|
|
145
|
+
(rejecting `aborted`) when its signal fires, so calls the engine already gave up on
|
|
146
|
+
cost nothing while they wait. When a timeout, abort or the stdout cap kills the call,
|
|
147
|
+
the whole process group gets SIGTERM, and SIGKILL after five seconds. When the CLI
|
|
148
|
+
itself exits on the SIGTERM the group gets one more SIGKILL at that moment, so a tool
|
|
149
|
+
process that ignored SIGTERM and holds none of the runner's pipes does not outlive
|
|
150
|
+
the call.
|
|
151
|
+
|
|
73
152
|
## Concurrency
|
|
74
153
|
|
|
75
154
|
The adapter caps concurrent `claude` CLI invocations with an internal
|
|
@@ -78,9 +157,14 @@ semaphore, defaulting to `maxConcurrency: 2`. Override via
|
|
|
78
157
|
|
|
79
158
|
## Supported models
|
|
80
159
|
|
|
81
|
-
`claude-fable-5`, `claude-opus-
|
|
82
|
-
`claude-haiku-4-5-20251001`.
|
|
83
|
-
`{ reasoning: { effort }, timeoutMs }`
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
rejects unknown keys.
|
|
160
|
+
`claude-fable-5-1`, `claude-opus-5-5`, `claude-sonnet-5`,
|
|
161
|
+
`claude-haiku-4-5-20251001`. Fable 5.1, Opus 5.5, and Sonnet 5 accept
|
|
162
|
+
`{ reasoning: { effort }, timeoutMs }` with effort `low | medium | high |
|
|
163
|
+
xhigh | max`. Haiku 4.5 has no `reasoning` key — the CLI drops `--effort`.
|
|
164
|
+
No sampling knobs (`temperature`/`topP`/`topK`/`maxOutputTokens`/`stopSequences`)
|
|
165
|
+
are accepted; the strict config schema rejects unknown keys. `claude-fable-5`
|
|
166
|
+
and `claude-opus-4-8` are deleted with no alias.
|
|
167
|
+
|
|
168
|
+
The descriptors' `limits` are what the CLI reports for its own run (the captured
|
|
169
|
+
`modelUsage.contextWindow` and `maxOutputTokens`), not the API maxima: Fable 5.1
|
|
170
|
+
64 000 and Haiku 4.5 32 000 output tokens, Opus 5.5 and Sonnet 5 128 000.
|