@life-ai-tools/claude-code-sdk 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 LifeAITools
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
+ # opencode-claude-toolkit
2
+
3
+ Use your **Claude Max/Pro subscription** programmatically — no API key needed.
4
+
5
+ This toolkit provides:
6
+ - **`@lifeaitools/claude-code-sdk`** — TypeScript SDK for the Claude Code API (streaming, tool use, conversation management)
7
+ - **`@lifeaitools/opencode-proxy`** — OpenAI-compatible proxy server that lets you use Claude Max/Pro in [opencode](https://github.com/opencode-ai/opencode), Cursor, or any OpenAI-compatible client
8
+
9
+ > **Why this exists?** Read our [Open Letter to Anthropic](OPEN-LETTER.md) about token efficiency, developer freedom, and collaboration.
10
+
11
+ ---
12
+
13
+ ## Prerequisites
14
+
15
+ Before you start, make sure you have:
16
+
17
+ 1. **Bun** (runtime for the proxy server):
18
+ ```bash
19
+ curl -fsSL https://bun.sh/install | bash
20
+ ```
21
+
22
+ 2. **opencode** (or any OpenAI-compatible coding client):
23
+ ```bash
24
+ # Via Go
25
+ go install github.com/opencode-ai/opencode@latest
26
+ # Or via npm
27
+ npm install -g opencode-ai
28
+ ```
29
+
30
+ 3. **Claude Code CLI** (needed once, to authenticate your subscription):
31
+ ```bash
32
+ # Install Claude Code CLI
33
+ npm install -g @anthropic-ai/claude-code
34
+
35
+ # Log in (this creates ~/.claude/.credentials.json)
36
+ claude
37
+ # Follow the OAuth flow in your browser, then exit Claude CLI
38
+ ```
39
+
40
+ 4. **Verify credentials exist:**
41
+ ```bash
42
+ ls ~/.claude/.credentials.json
43
+ # Should show the file — if not, run `claude` again
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Quick Start — One Liner
49
+
50
+ ```bash
51
+ # Start proxy and launch opencode in one go
52
+ bunx @lifeaitools/opencode-proxy &
53
+ LOCAL_ENDPOINT=http://localhost:4040/v1 opencode
54
+ ```
55
+
56
+ Or step by step:
57
+
58
+ ```bash
59
+ # Terminal 1: Start the proxy
60
+ bunx @lifeaitools/opencode-proxy --port 4040
61
+
62
+ # Terminal 2: Launch opencode pointing to the proxy
63
+ LOCAL_ENDPOINT=http://localhost:4040/v1 opencode
64
+ ```
65
+
66
+ That's it. opencode will now use your Claude Max subscription for all requests.
67
+
68
+ ### Supported Models
69
+
70
+ | Model ID | Maps to | Best for |
71
+ |----------|---------|----------|
72
+ | `claude-v4.6-sonnet` | Claude Sonnet 4.6 | Fast coding, daily driver |
73
+ | `claude-v4.6-opus` | Claude Opus 4.6 | Complex reasoning, architecture |
74
+ | `claude-v4.5-haiku` | Claude Haiku 4.5 | Quick tasks, low latency |
75
+
76
+ ---
77
+
78
+ ## opencode Configuration
79
+
80
+ To make opencode always use the proxy, add to your `.opencode.json`:
81
+
82
+ ```json
83
+ {
84
+ "provider": {
85
+ "id": "openai",
86
+ "api_key": "not-needed",
87
+ "model": {
88
+ "id": "claude-v4.6-sonnet",
89
+ "name": "Claude Sonnet 4.6",
90
+ "api_model": "claude-v4.6-sonnet",
91
+ "can_reason": true
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ Set the environment variable permanently:
98
+
99
+ ```bash
100
+ # Add to your .bashrc / .zshrc
101
+ export LOCAL_ENDPOINT=http://localhost:4040/v1
102
+ ```
103
+
104
+ ---
105
+
106
+ ## SDK Usage
107
+
108
+ ```typescript
109
+ import { ClaudeCodeSDK } from '@lifeaitools/claude-code-sdk'
110
+
111
+ const sdk = new ClaudeCodeSDK()
112
+
113
+ // Non-streaming
114
+ const response = await sdk.generate({
115
+ model: 'claude-sonnet-4-6-20250415',
116
+ messages: [{ role: 'user', content: 'Hello!' }],
117
+ maxTokens: 1024,
118
+ })
119
+ console.log(response.content)
120
+
121
+ // Streaming
122
+ for await (const event of sdk.stream({
123
+ model: 'claude-sonnet-4-6-20250415',
124
+ messages: [{ role: 'user', content: 'Write a haiku about coding' }],
125
+ maxTokens: 1024,
126
+ })) {
127
+ if (event.type === 'text_delta') {
128
+ process.stdout.write(event.text)
129
+ }
130
+ }
131
+
132
+ // Multi-turn conversation
133
+ import { Conversation } from '@lifeaitools/claude-code-sdk'
134
+
135
+ const conv = new Conversation(sdk, { model: 'claude-sonnet-4-6-20250415' })
136
+ const reply1 = await conv.send('What is TypeScript?')
137
+ const reply2 = await conv.send('How does it compare to JavaScript?')
138
+ ```
139
+
140
+ See [`examples/`](examples/) for more usage patterns.
141
+
142
+ ---
143
+
144
+ ## Features
145
+
146
+ - **Zero API key** — uses your existing Claude Max/Pro OAuth credentials from `~/.claude`
147
+ - **Streaming** — real SSE streaming with text, thinking, and tool use events
148
+ - **Auto-refresh** — tokens are refreshed automatically when expired
149
+ - **Retry logic** — exponential backoff for 5xx errors, proper 429 handling
150
+ - **Tool use** — full support for function calling
151
+ - **Thinking** — extended thinking / chain-of-thought support
152
+ - **Caching** — prompt caching support for cost efficiency
153
+ - **Conversation** — stateful multi-turn conversation management
154
+ - **OpenAI-compatible** — proxy translates OpenAI format to/from Claude
155
+ - **Client disconnect handling** — clean abort propagation from client to API
156
+
157
+ ---
158
+
159
+ ## Troubleshooting
160
+
161
+ ### "No credentials found"
162
+ ```
163
+ Error: No credentials found. Run `claude` first or provide credentials.
164
+ ```
165
+ **Fix:** Run `claude` in your terminal, complete the OAuth login, then try again. The file `~/.claude/.credentials.json` must exist.
166
+
167
+ ### "Rate limited" / 429 errors
168
+ ```
169
+ [proxy] error: Rate limited: ...
170
+ ```
171
+ **Fix:** You've hit your subscription's usage limit. Wait for the reset window (usually resets daily). The proxy never retries 429 errors — this is by design, as subscription rate limits are window-based.
172
+
173
+ ### "Stream idle timeout"
174
+ If opencode shows a timeout while waiting for a response (especially with Opus):
175
+ - The proxy has a 255-second idle timeout and 600-second request timeout
176
+ - Opus can take 30+ seconds for complex reasoning
177
+ - If timeouts persist, check your network connection to `api.anthropic.com`
178
+
179
+ ### Proxy won't start
180
+ ```bash
181
+ # Check if port 4040 is already in use
182
+ lsof -i :4040
183
+
184
+ # Use a different port
185
+ bunx @lifeaitools/opencode-proxy --port 4041
186
+ ```
187
+
188
+ ### "Token expired" / 401 errors
189
+ The SDK auto-refreshes tokens. If you see persistent 401 errors:
190
+ ```bash
191
+ # Re-authenticate
192
+ claude # log in again
193
+ # Restart the proxy
194
+ ```
195
+
196
+ ---
197
+
198
+ ## How It Works
199
+
200
+ ```
201
+ ┌─────────────┐ OpenAI format ┌──────────────────┐ Claude API ┌──────────────┐
202
+ │ opencode │ ──── SSE stream ──→ │ opencode-proxy │ ──── SSE ──────→ │ Anthropic │
203
+ │ (client) │ ←── SSE stream ──── │ (Bun.serve) │ ←── SSE ──────── │ API │
204
+ └─────────────┘ └──────────────────┘ └──────────────┘
205
+
206
+ claude-code-sdk
207
+ • OAuth auth + refresh
208
+ • Request building
209
+ • SSE parsing
210
+ • Retry logic
211
+ ```
212
+
213
+ The proxy translates between OpenAI's chat completion format and Claude's native API format. Streaming is end-to-end — chunks flow from Anthropic through the proxy to your client with minimal buffering.
214
+
215
+ ---
216
+
217
+ ## Project Structure
218
+
219
+ ```
220
+ opencode-claude-toolkit/
221
+ ├── packages/
222
+ │ └── opencode-proxy/ # OpenAI-compatible proxy (open source)
223
+ │ ├── server.ts # HTTP server (Bun.serve)
224
+ │ ├── translate.ts # OpenAI ↔ Claude format translation
225
+ │ └── launch.ts # Auto-launcher
226
+ ├── dist/ # Compiled SDK (published to npm)
227
+ ├── examples/ # Usage examples
228
+ │ ├── basic-chat.ts
229
+ │ └── conversation.ts
230
+ ├── OPEN-LETTER.md # Our message to Anthropic
231
+ ├── REQUEST-SOURCE.md # How to request SDK source access
232
+ ├── LICENSE # MIT
233
+ └── README.md
234
+ ```
235
+
236
+ ---
237
+
238
+ ## License
239
+
240
+ MIT — see [LICENSE](LICENSE)
241
+
242
+ ## Source Access
243
+
244
+ The SDK is distributed as a compiled bundle. If you need source access for auditing, contributions, or enterprise use, see [REQUEST-SOURCE.md](REQUEST-SOURCE.md).
245
+
246
+ ## Open Letter
247
+
248
+ We built this with respect and appreciation for Anthropic's work. Read our [Open Letter to Anthropic](OPEN-LETTER.md) about why this project exists and our invitation to collaborate.
249
+
250
+ ---
251
+
252
+ Built by [LifeAITools](https://lifeaitools.com) | [GitHub](https://github.com/LifeAITools)
@@ -0,0 +1,81 @@
1
+ import type { ClaudeCodeSDK } from './sdk.js';
2
+ import type { MessageParam, ContentBlockParam, ConversationOptions, TurnOptions, GenerateResponse, StreamEvent, TokenUsage } from './types.js';
3
+ /**
4
+ * Multi-turn conversation wrapper around ClaudeCodeSDK.
5
+ *
6
+ * Accumulates messages across turns, handles prompt caching,
7
+ * supports tool execution loop, rewind, and branching.
8
+ *
9
+ * Pattern mirrors CLI's query loop (query.ts:204-321):
10
+ * messages accumulate in mutable state across iterations.
11
+ */
12
+ export declare class Conversation {
13
+ private sdk;
14
+ private options;
15
+ private _messages;
16
+ private _totalUsage;
17
+ constructor(sdk: ClaudeCodeSDK, options: ConversationOptions);
18
+ /** Read-only access to conversation history */
19
+ get messages(): readonly MessageParam[];
20
+ /** Cumulative token usage across all turns */
21
+ get totalUsage(): TokenUsage;
22
+ /** Number of messages in conversation */
23
+ get length(): number;
24
+ /** Send a user message and get complete response */
25
+ send(content: string | ContentBlockParam[], turnOptions?: TurnOptions): Promise<GenerateResponse>;
26
+ /** Send a user message and stream the response */
27
+ stream(content: string | ContentBlockParam[], turnOptions?: TurnOptions): AsyncGenerator<StreamEvent>;
28
+ /**
29
+ * Add a tool result to the conversation.
30
+ * Call this after executing a tool_use returned by the model.
31
+ * Then call send() or stream() with the next user message (or empty)
32
+ * to continue the conversation.
33
+ */
34
+ addToolResult(toolUseId: string, content: string | ContentBlockParam[], isError?: boolean): void;
35
+ /**
36
+ * Add multiple tool results at once (for parallel tool execution).
37
+ */
38
+ addToolResults(results: Array<{
39
+ toolUseId: string;
40
+ content: string | ContentBlockParam[];
41
+ isError?: boolean;
42
+ }>): void;
43
+ /**
44
+ * Continue conversation after adding tool results.
45
+ * Sends accumulated tool results to the model.
46
+ */
47
+ continue(turnOptions?: TurnOptions): Promise<GenerateResponse>;
48
+ /**
49
+ * Continue conversation with streaming after adding tool results.
50
+ */
51
+ continueStream(turnOptions?: TurnOptions): AsyncGenerator<StreamEvent>;
52
+ /**
53
+ * Rewind conversation to a specific message index.
54
+ * Removes all messages at and after the index.
55
+ * Returns removed messages.
56
+ */
57
+ rewind(toIndex: number): MessageParam[];
58
+ /**
59
+ * Rewind to the last user message (undo last turn).
60
+ * Removes the last assistant + any tool results after it.
61
+ */
62
+ undoLastTurn(): MessageParam[];
63
+ /**
64
+ * Branch conversation — create new Conversation with messages up to current point.
65
+ * Like CLI's /branch command.
66
+ */
67
+ branch(): Conversation;
68
+ /**
69
+ * Get message history with indices for rewind UI.
70
+ */
71
+ getHistory(): Array<{
72
+ index: number;
73
+ role: string;
74
+ preview: string;
75
+ }>;
76
+ private appendUserMessage;
77
+ private appendAssistantFromResponse;
78
+ private buildGenerateOptions;
79
+ private accumulateUsage;
80
+ }
81
+ //# sourceMappingURL=conversation.d.ts.map
@@ -0,0 +1,7 @@
1
+ export { ClaudeCodeSDK, FileCredentialStore, MemoryCredentialStore } from './sdk.js';
2
+ export { Conversation } from './conversation.js';
3
+ export { saveSession, loadSession } from './session.js';
4
+ export type { ClaudeCodeSDKOptions, GenerateOptions, GenerateResponse, StreamEvent, TokenUsage, RateLimitInfo, CredentialsFile, MessageParam, ContentBlockParam, TextBlockParam, ToolDef, ToolChoice, SystemParam, ContentBlock, TextBlock, ThinkingBlock, ToolUseBlock, ConversationOptions, TurnOptions, CredentialStore, StoredCredentials, } from './types.js';
5
+ export { ClaudeCodeSDKError, AuthError, APIError, RateLimitError, } from './types.js';
6
+ export type { SessionEntry } from './session.js';
7
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * claude-code-sdk — TypeScript SDK for Claude Code API
3
+ * (c) 2026 Kiberos. Compiled distribution.
4
+ * Source access: see REQUEST-SOURCE.md in the GitHub repo.
5
+ */
6
+ var t=Object.defineProperty,e=(e,s)=>t(e,"name",{value:s,configurable:!0});import{createHash as s,randomBytes as i,randomUUID as r}from"crypto";import{readFileSync as n,writeFileSync as o,chmodSync as a,mkdirSync as l,statSync as h}from"fs";import{join as u}from"path";import{homedir as c}from"os";var d=class extends Error{constructor(t,e){super(t),this.cause=e,this.name="ClaudeCodeSDKError"}static{e(this,"ClaudeCodeSDKError")}},p=class extends d{static{e(this,"AuthError")}constructor(t,e){super(t,e),this.name="AuthError"}},f=class extends d{constructor(t,e,s,i){super(t,i),this.status=e,this.requestId=s,this.name="APIError"}static{e(this,"APIError")}},y=class extends d{constructor(t,e,s=429,i){super(t,i),this.rateLimitInfo=e,this.status=s,this.name="RateLimitError"}static{e(this,"RateLimitError")}},m=3e5,_=class{static{e(this,"ClaudeCodeSDK")}accessToken=null;refreshToken=null;expiresAt=null;credentialStore;sessionId;deviceId;accountUuid;version;timeout;maxRetries;lastRateLimitInfo={status:null,resetAt:null,claim:null,retryAfter:null};pending401=null;lastFailedToken=null;constructor(t={}){this.sessionId=r(),this.deviceId=t.deviceId??i(32).toString("hex"),this.accountUuid=t.accountUuid??this.readAccountUuid(),this.version=t.version??"0.1.0",this.timeout=t.timeout??6e5,this.maxRetries=t.maxRetries??3,t.credentialStore?this.credentialStore=t.credentialStore:t.accessToken?(this.accessToken=t.accessToken,this.refreshToken=t.refreshToken??null,this.expiresAt=t.expiresAt??null,this.credentialStore=new w({accessToken:t.accessToken,refreshToken:t.refreshToken??"",expiresAt:t.expiresAt??0})):this.credentialStore=new k(t.credentialsPath??u(c(),".claude",".credentials.json"))}async generate(t){let e=[];for await(let s of this.stream(t))e.push(s);return this.assembleResponse(e,t.model)}async*stream(t){await this.ensureAuth();let e,s=this.buildRequestBody(t),i=this.buildHeaders(t);for(let r=1;r<=this.maxRetries+1;r++){if(t.signal?.aborted)throw new d("Aborted");try{return void(yield*this.doStreamRequest(s,i,t.signal))}catch(s){if(e=s,s instanceof f){if(401===s.status&&r<=this.maxRetries){await this.handleAuth401(),i.Authorization=`Bearer ${this.accessToken}`;continue}if(429===s.status)throw s instanceof y?s:new y("Rate limited",this.lastRateLimitInfo,429,s);if(s.status>=500&&r<=this.maxRetries){let e=this.getRetryDelay(r,this.lastRateLimitInfo.retryAfter?.toString()??null);await this.sleep(e,t.signal);continue}}throw s}}throw e}getRateLimitInfo(){return this.lastRateLimitInfo}async*doStreamRequest(t,e,s){let i,r=new AbortController,n=setTimeout(()=>r.abort(),this.timeout);s&&s.addEventListener("abort",()=>r.abort(),{once:!0});try{i=await fetch("https://api.anthropic.com/v1/messages",{method:"POST",headers:e,body:JSON.stringify(t),signal:r.signal})}catch(t){throw clearTimeout(n),new d("Network error",t)}if(clearTimeout(n),this.lastRateLimitInfo=this.parseRateLimitHeaders(i.headers),!i.ok){let t="";try{t=await i.text()}catch{}let e=i.headers.get("request-id");throw 429===i.status?new y(`Rate limited: ${t}`,this.lastRateLimitInfo,429):new f(`API error ${i.status}: ${t}`,i.status,e)}if(!i.body)throw new d("No response body");yield*this.parseSSE(i.body,s)}async*parseSSE(t,e){let s=new TextDecoder,i=t.getReader(),r="",n=new Map,o={inputTokens:0,outputTokens:0},a=null;try{for(;;){if(e?.aborted)return void i.cancel();let{done:t,value:l}=await i.read();if(t)break;r+=s.decode(l,{stream:!0});let h=r.split("\n");r=h.pop()??"";for(let t of h){if(!t.startsWith("data: "))continue;let e,s=t.slice(6);if("[DONE]"===s)continue;try{e=JSON.parse(s)}catch{continue}let i=e.type;if("message_start"===i){let t=e.message?.usage;t&&(o={inputTokens:t.input_tokens??0,outputTokens:t.output_tokens??0,cacheCreationInputTokens:t.cache_creation_input_tokens,cacheReadInputTokens:t.cache_read_input_tokens});continue}if("content_block_start"===i){let t=e.index,s=e.content_block;"tool_use"===s.type?(n.set(t,{type:"tool_use",id:s.id,name:s.name,input:""}),yield{type:"tool_use_start",id:s.id,name:s.name}):"text"===s.type?n.set(t,{type:"text",text:""}):"thinking"===s.type&&n.set(t,{type:"thinking",thinking:""});continue}if("content_block_delta"===i){let t=e.index,s=n.get(t),i=e.delta;"text_delta"===i.type&&void 0!==i.text?(s&&(s.text=(s.text??"")+i.text),i.text&&(yield{type:"text_delta",text:i.text})):"thinking_delta"===i.type&&void 0!==i.thinking?(s&&(s.thinking=(s.thinking??"")+i.thinking),i.thinking&&(yield{type:"thinking_delta",text:i.thinking})):"input_json_delta"===i.type&&void 0!==i.partial_json&&(s&&(s.input=(s.input??"")+i.partial_json),i.partial_json&&(yield{type:"tool_use_delta",partialInput:i.partial_json}));continue}if("content_block_stop"===i){let t=e.index,s=n.get(t);if("tool_use"===s?.type&&s.id&&s.name){let t={};try{t=JSON.parse(s.input??"{}")}catch{}yield{type:"tool_use_end",id:s.id,name:s.name,input:t}}continue}if("message_delta"===i){let t=e.delta;t?.stop_reason&&(a=t.stop_reason);let s=e.usage;s?.output_tokens&&(o={...o,outputTokens:s.output_tokens});continue}"message_stop"===i&&(yield{type:"message_stop",usage:o,stopReason:a})}}}finally{i.releaseLock()}}buildHeaders(t){let e=this.buildBetas(t);return{"Content-Type":"application/json",Authorization:`Bearer ${this.accessToken}`,"anthropic-version":"2023-06-01","anthropic-beta":e.join(","),"x-app":"cli","User-Agent":`claude-code/${this.version} (external, sdk)`,"X-Claude-Code-Session-Id":this.sessionId}}buildRequestBody(t){let e,s=this.computeFingerprint(t.messages),i=`x-anthropic-billing-header: cc_version=${this.version}.${s}; cc_entrypoint=cli;`;e="string"==typeof t.system?i+"\n"+t.system:Array.isArray(t.system)?[{type:"text",text:i},...t.system]:i;let r={model:t.model,messages:t.messages,max_tokens:t.maxTokens??16384,stream:!0,system:e,metadata:{user_id:JSON.stringify({device_id:this.deviceId,account_uuid:this.accountUuid,session_id:this.sessionId})}};t.tools&&t.tools.length>0&&(r.tools=t.tools,t.toolChoice&&(r.tool_choice="string"==typeof t.toolChoice?{type:t.toolChoice}:t.toolChoice)),!1!==t.caching&&this.addCacheMarkers(r);let n=t.model.toLowerCase(),o=n.includes("opus-4-6")||n.includes("sonnet-4-6"),a="disabled"===t.thinking?.type;return!a&&o?r.thinking={type:"adaptive"}:"enabled"===t.thinking?.type&&(r.thinking={type:"enabled",budget_tokens:t.thinking.budgetTokens}),!(!a&&(o||"enabled"===t.thinking?.type))&&void 0!==t.temperature&&(r.temperature=t.temperature),void 0!==t.topP&&(r.top_p=t.topP),t.effort&&o&&(r.output_config={effort:t.effort}),t.stopSequences?.length&&(r.stop_sequences=t.stopSequences),t.fast&&(r.speed="fast"),r}addCacheMarkers(t){let e=t.system;if("string"==typeof e)t.system=[{type:"text",text:e,cache_control:{type:"ephemeral"}}];else if(Array.isArray(e)){let t=e;t.length>0&&(t[t.length-1]={...t[t.length-1],cache_control:{type:"ephemeral"}})}let s=t.messages;if(0===s.length)return;let i=s[s.length-1];if("string"==typeof i.content)i.content=[{type:"text",text:i.content,cache_control:{type:"ephemeral"}}];else if(Array.isArray(i.content)&&i.content.length>0){let t=i.content[i.content.length-1];i.content[i.content.length-1]={...t,cache_control:{type:"ephemeral"}}}}buildBetas(t){let e=[],s=t.model.toLowerCase().includes("haiku");return s||e.push("claude-code-20250219"),e.push("oauth-2025-04-20"),/\[1m\]/i.test(t.model)&&e.push("context-1m-2025-08-07"),!s&&"disabled"!==t.thinking?.type&&e.push("interleaved-thinking-2025-05-14"),t.effort&&e.push("effort-2025-11-24"),t.fast&&e.push("fast-mode-2026-02-01"),e.push("prompt-caching-scope-2026-01-05"),t.extraBetas&&e.push(...t.extraBetas),e}async ensureAuth(){this.accessToken&&!this.isTokenExpired()||this.credentialStore.hasChanged&&await this.credentialStore.hasChanged()&&(await this.loadFromStore(),this.accessToken&&!this.isTokenExpired())||!this.accessToken&&(await this.loadFromStore(),this.accessToken&&!this.isTokenExpired())||this.accessToken&&this.isTokenExpired()&&await this.refreshTokenWithTripleCheck()}async loadFromStore(){let t=await this.credentialStore.read();if(!t?.accessToken)throw new p('No OAuth tokens found. Run "claude login" first or provide credentials.');this.accessToken=t.accessToken,this.refreshToken=t.refreshToken,this.expiresAt=t.expiresAt}isTokenExpired(){return!!this.expiresAt&&Date.now()+m>=this.expiresAt}async refreshTokenWithTripleCheck(){let t=await this.credentialStore.read();if(t&&!(Date.now()+m>=t.expiresAt))return this.accessToken=t.accessToken,this.refreshToken=t.refreshToken,void(this.expiresAt=t.expiresAt);await this.doTokenRefresh()}async handleAuth401(){let t=this.accessToken;this.pending401&&this.lastFailedToken===t||(this.lastFailedToken=t,this.pending401=(async()=>{let e=await this.credentialStore.read();return e&&e.accessToken!==t?(this.accessToken=e.accessToken,this.refreshToken=e.refreshToken,this.expiresAt=e.expiresAt,!0):(await this.doTokenRefresh(),!0)})().finally(()=>{this.pending401=null,this.lastFailedToken=null})),await this.pending401}async doTokenRefresh(){if(!this.refreshToken)throw new p("Token expired and no refresh token available.");let t=await fetch("https://platform.claude.com/v1/oauth/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:"refresh_token",refresh_token:this.refreshToken,client_id:"9d1c250a-e61b-44d9-88ed-5944d1962f5e"}),signal:AbortSignal.timeout(15e3)});if(!t.ok){let e=await this.credentialStore.read();if(e&&!(Date.now()+m>=e.expiresAt))return this.accessToken=e.accessToken,this.refreshToken=e.refreshToken,void(this.expiresAt=e.expiresAt);throw new p(`Token refresh failed: ${t.status} ${t.statusText}`)}let e=await t.json();this.accessToken=e.access_token,this.refreshToken=e.refresh_token??this.refreshToken,this.expiresAt=Date.now()+1e3*e.expires_in,await this.credentialStore.write({accessToken:this.accessToken,refreshToken:this.refreshToken,expiresAt:this.expiresAt})}computeFingerprint(t){let e=t.find(t=>"user"===t.role);if(!e)return this.hashFingerprint("000");let s="";"string"==typeof e.content?s=e.content:Array.isArray(e.content)&&(s=e.content.find(t=>"text"===t.type)?.text??"");let i=[4,7,20].map(t=>s[t]||"0").join("");return this.hashFingerprint(i)}hashFingerprint(t){let e=`59cf53e54c78${t}${this.version}`;return s("sha256").update(e).digest("hex").slice(0,3)}assembleResponse(t,e){let s=[],i=[],r=[],n={inputTokens:0,outputTokens:0},o=null,a="",l="";for(let e of t)switch(e.type){case"text_delta":a+=e.text;break;case"thinking_delta":l+=e.text;break;case"tool_use_end":r.push({type:"tool_use",id:e.id,name:e.name,input:e.input});break;case"message_stop":n=e.usage,o=e.stopReason;break;case"error":throw e.error}return a&&s.push({type:"text",text:a}),l&&i.push({type:"thinking",thinking:l}),s.push(...r),{content:s,thinking:i.length>0?i:void 0,toolCalls:r.length>0?r:void 0,usage:n,stopReason:o,rateLimitInfo:this.lastRateLimitInfo,model:e}}parseRateLimitHeaders(t){let e=t.get("retry-after"),s=t.get("anthropic-ratelimit-unified-reset"),i=s?Number(s):null;return{status:t.get("anthropic-ratelimit-unified-status"),resetAt:Number.isFinite(i)?i:null,claim:t.get("anthropic-ratelimit-unified-representative-claim"),retryAfter:e?parseFloat(e):null}}getRetryDelay(t,e){if(e){let t=parseInt(e,10);if(!isNaN(t))return 1e3*t}let s=Math.min(500*Math.pow(2,t-1),32e3);return s+.25*Math.random()*s}sleep(t,e){return new Promise((s,i)=>{if(e?.aborted)return void i(new d("Aborted"));let r=setTimeout(s,t);e?.addEventListener("abort",()=>{clearTimeout(r),i(new d("Aborted"))},{once:!0})})}readAccountUuid(){try{let t=u(c(),".claude","claude_code_config.json");return JSON.parse(n(t,"utf8")).oauthAccount?.accountUuid??""}catch{return""}}},k=class{constructor(t){this.path=t}static{e(this,"FileCredentialStore")}lastMtimeMs=0;async read(){try{let t=n(this.path,"utf8");return this.lastMtimeMs=this.getMtime(),JSON.parse(t).claudeAiOauth??null}catch{return null}}async write(t){let e={};try{e=JSON.parse(n(this.path,"utf8"))}catch{}e.claudeAiOauth=t;let s=u(this.path,"..");try{l(s,{recursive:!0})}catch{}o(this.path,JSON.stringify(e,null,2),"utf8"),a(this.path,384),this.lastMtimeMs=this.getMtime()}async hasChanged(){let t=this.getMtime();return t!==this.lastMtimeMs&&(this.lastMtimeMs=t,!0)}getMtime(){try{return h(this.path).mtimeMs}catch{return 0}}},w=class{static{e(this,"MemoryCredentialStore")}credentials;constructor(t){this.credentials={...t}}async read(){return this.credentials.accessToken?{...this.credentials}:null}async write(t){this.credentials={...t}}},g=class _Conversation{static{e(this,"Conversation")}sdk;options;t=[];i={inputTokens:0,outputTokens:0};constructor(t,e){this.sdk=t,this.options=e}get messages(){return this.t}get totalUsage(){return{...this.i}}get length(){return this.t.length}async send(t,e){this.appendUserMessage(t);let s=this.buildGenerateOptions(e),i=await this.sdk.generate(s);return this.appendAssistantFromResponse(i),this.accumulateUsage(i.usage),i}async*stream(t,e){this.appendUserMessage(t);let s=this.buildGenerateOptions(e),i=[],r=[],n=[],o={inputTokens:0,outputTokens:0};for await(let t of this.sdk.stream(s))switch(yield t,t.type){case"text_delta":i.push(t.text);break;case"thinking_delta":r.push(t.text);break;case"tool_use_end":n.push({type:"tool_use",id:t.id,name:t.name,input:t.input});break;case"message_stop":o=t.usage}let a=[];i.length>0&&a.push({type:"text",text:i.join("")});for(let t of n)a.push({type:"tool_use",id:t.id,name:t.name,input:t.input});a.length>0&&this.t.push({role:"assistant",content:a}),this.accumulateUsage(o)}addToolResult(t,e,s){let i={type:"tool_result",tool_use_id:t,content:e,...s&&{is_error:!0}};this.t.push({role:"user",content:[i]})}addToolResults(t){let e=t.map(t=>({type:"tool_result",tool_use_id:t.toolUseId,content:t.content,...t.isError&&{is_error:!0}}));this.t.push({role:"user",content:e})}async continue(t){let e=this.buildGenerateOptions(t),s=await this.sdk.generate(e);return this.appendAssistantFromResponse(s),this.accumulateUsage(s.usage),s}async*continueStream(t){let e=this.buildGenerateOptions(t),s=[],i=[],r={inputTokens:0,outputTokens:0};for await(let t of this.sdk.stream(e))switch(yield t,t.type){case"text_delta":s.push(t.text);break;case"tool_use_end":i.push({type:"tool_use",id:t.id,name:t.name,input:t.input});break;case"message_stop":r=t.usage}let n=[];s.length>0&&n.push({type:"text",text:s.join("")});for(let t of i)n.push({type:"tool_use",id:t.id,name:t.name,input:t.input});n.length>0&&this.t.push({role:"assistant",content:n}),this.accumulateUsage(r)}rewind(t){if(t<0||t>=this.t.length)throw new Error(`Invalid rewind index: ${t}`);return this.t.splice(t)}undoLastTurn(){for(let t=this.t.length-1;t>=0;t--){let e=this.t[t];if("user"===e.role){let s=e.content;if(!(Array.isArray(s)&&s.length>0&&"tool_result"===s[0].type))return this.rewind(t)}}return[]}branch(){let t=new _Conversation(this.sdk,{...this.options});return t.t=[...this.t],t.i={...this.i},t}getHistory(){return this.t.map((t,e)=>{let s="";if("string"==typeof t.content)s=t.content.slice(0,100);else if(Array.isArray(t.content)){let e=t.content[0];"text"===e?.type?s=e.text?.slice(0,100)??"":"tool_result"===e?.type?s=`[tool_result: ${e.tool_use_id}]`:"tool_use"===e?.type&&(s=`[tool_use: ${e.name}]`)}return{index:e,role:t.role,preview:s}})}appendUserMessage(t){this.t.push({role:"user",content:t})}appendAssistantFromResponse(t){let e=[];for(let s of t.content)"text"===s.type?e.push({type:"text",text:s.text}):"tool_use"===s.type&&e.push({type:"tool_use",id:s.id,name:s.name,input:s.input});e.length>0&&this.t.push({role:"assistant",content:e})}buildGenerateOptions(t){return{model:this.options.model,messages:[...this.t],system:this.options.system,tools:t?.tools??this.options.tools,toolChoice:t?.toolChoice??this.options.toolChoice,maxTokens:this.options.maxTokens,thinking:this.options.thinking,effort:this.options.effort,fast:this.options.fast,signal:t?.signal??this.options.signal,extraBetas:this.options.extraBetas,caching:this.options.caching}}accumulateUsage(t){this.i.inputTokens+=t.inputTokens,this.i.outputTokens+=t.outputTokens,this.i.cacheCreationInputTokens=(this.i.cacheCreationInputTokens??0)+(t.cacheCreationInputTokens??0),this.i.cacheReadInputTokens=(this.i.cacheReadInputTokens??0)+(t.cacheReadInputTokens??0)}};import{readFileSync as x,writeFileSync as b,mkdirSync as T}from"fs";import{dirname as v}from"path";import{randomUUID as S}from"crypto";function A(t,e){T(v(t),{recursive:!0});let s=null,i=[];for(let t of e){let e=S(),r={type:"user"===t.role?"user":"assistant",uuid:e,parentUuid:s,timestamp:Date.now(),content:t.content};i.push(JSON.stringify(r)),s=e}b(t,i.join("\n")+"\n","utf8")}function C(t){let e=x(t,"utf8"),s=[];for(let t of e.split("\n")){if(!t.trim())continue;let e;try{e=JSON.parse(t)}catch{continue}("user"===e.type||"assistant"===e.type)&&s.push({role:"user"===e.type?"user":"assistant",content:e.content})}return s}e(A,"saveSession"),e(C,"loadSession");export{f as APIError,p as AuthError,_ as ClaudeCodeSDK,d as ClaudeCodeSDKError,g as Conversation,k as FileCredentialStore,w as MemoryCredentialStore,y as RateLimitError,C as loadSession,A as saveSession};
package/dist/sdk.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ import type { ClaudeCodeSDKOptions, CredentialStore, StoredCredentials, GenerateOptions, GenerateResponse, StreamEvent, RateLimitInfo } from './types.js';
2
+ export declare class ClaudeCodeSDK {
3
+ private accessToken;
4
+ private refreshToken;
5
+ private expiresAt;
6
+ private credentialStore;
7
+ private sessionId;
8
+ private deviceId;
9
+ private accountUuid;
10
+ private version;
11
+ private timeout;
12
+ private maxRetries;
13
+ private lastRateLimitInfo;
14
+ private pending401;
15
+ private lastFailedToken;
16
+ constructor(options?: ClaudeCodeSDKOptions);
17
+ /** Non-streaming: send messages, get full response */
18
+ generate(options: GenerateOptions): Promise<GenerateResponse>;
19
+ /** Streaming: yields events as they arrive from SSE */
20
+ stream(options: GenerateOptions): AsyncGenerator<StreamEvent>;
21
+ getRateLimitInfo(): RateLimitInfo;
22
+ private doStreamRequest;
23
+ private parseSSE;
24
+ /** HTTP headers — mimics getAnthropicClient() + getAuthHeaders() */
25
+ private buildHeaders;
26
+ /** Request body — mirrors paramsFromContext() in claude.ts:1699 */
27
+ private buildRequestBody;
28
+ /** Add cache_control markers to system + last message — mirrors addCacheBreakpoints() */
29
+ private addCacheMarkers;
30
+ /** Beta headers — mirrors getAllModelBetas() in betas.ts:234 */
31
+ private buildBetas;
32
+ /**
33
+ * Ensure valid auth token before API call.
34
+ * Mirrors checkAndRefreshOAuthTokenIfNeeded() from auth.ts:1427.
35
+ *
36
+ * Triple-check pattern:
37
+ * 1. Check cached token in memory
38
+ * 2. If expired, check store (another process may have refreshed)
39
+ * 3. If still expired, do the refresh
40
+ */
41
+ private ensureAuth;
42
+ /** Load credentials from the credential store */
43
+ private loadFromStore;
44
+ /** 5-minute buffer before actual expiry — from oauth/client.ts:344-353 */
45
+ private isTokenExpired;
46
+ /**
47
+ * Triple-check refresh — mirrors auth.ts:1472-1556.
48
+ * Check store again (race), then refresh, then check store on error.
49
+ */
50
+ private refreshTokenWithTripleCheck;
51
+ /**
52
+ * Handle 401 error — mirrors handleOAuth401Error() from auth.ts:1338-1392.
53
+ * Deduplicates concurrent 401 handlers for the same failed token.
54
+ */
55
+ handleAuth401(): Promise<void>;
56
+ /** POST to platform.claude.com/v1/oauth/token — from oauth/client.ts:146 */
57
+ private doTokenRefresh;
58
+ computeFingerprint(messages: {
59
+ role: string;
60
+ content: string | unknown[];
61
+ }[]): string;
62
+ private hashFingerprint;
63
+ private assembleResponse;
64
+ private parseRateLimitHeaders;
65
+ private getRetryDelay;
66
+ private sleep;
67
+ private readAccountUuid;
68
+ }
69
+ /**
70
+ * File-based credential store with mtime detection.
71
+ * Mirrors CLI's plainTextStorage.ts + invalidateOAuthCacheIfDiskChanged().
72
+ */
73
+ export declare class FileCredentialStore implements CredentialStore {
74
+ private path;
75
+ private lastMtimeMs;
76
+ constructor(path: string);
77
+ read(): Promise<StoredCredentials | null>;
78
+ write(credentials: StoredCredentials): Promise<void>;
79
+ /** Detect cross-process changes via mtime — from auth.ts:1313-1336 */
80
+ hasChanged(): Promise<boolean>;
81
+ private getMtime;
82
+ }
83
+ /**
84
+ * In-memory credential store for direct token injection.
85
+ * No persistence — tokens live only in SDK instance.
86
+ */
87
+ export declare class MemoryCredentialStore implements CredentialStore {
88
+ private credentials;
89
+ constructor(initial: StoredCredentials);
90
+ read(): Promise<StoredCredentials | null>;
91
+ write(credentials: StoredCredentials): Promise<void>;
92
+ }
93
+ //# sourceMappingURL=sdk.d.ts.map
@@ -0,0 +1,13 @@
1
+ import type { MessageParam } from './types.js';
2
+ export interface SessionEntry {
3
+ type: 'user' | 'assistant';
4
+ uuid: string;
5
+ parentUuid: string | null;
6
+ timestamp: number;
7
+ content: MessageParam['content'];
8
+ }
9
+ /** Save conversation messages to JSONL file (CLI-compatible format) */
10
+ export declare function saveSession(path: string, messages: readonly MessageParam[]): void;
11
+ /** Load conversation messages from JSONL file */
12
+ export declare function loadSession(path: string): MessageParam[];
13
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Pluggable credential storage interface.
3
+ * Implement this to store tokens in a database, Redis, KV store, etc.
4
+ * Mirrors CLI's SecureStorage pattern (plainTextStorage.ts).
5
+ */
6
+ export interface CredentialStore {
7
+ /** Read current credentials. Return null if not found. */
8
+ read(): Promise<StoredCredentials | null>;
9
+ /** Write updated credentials after refresh. */
10
+ write(credentials: StoredCredentials): Promise<void>;
11
+ /**
12
+ * Check if credentials changed externally (another process refreshed).
13
+ * Return true if stale → SDK will re-read.
14
+ * Optional: if not implemented, SDK always re-reads before refresh.
15
+ */
16
+ hasChanged?(): Promise<boolean>;
17
+ }
18
+ /** Credentials as stored/retrieved by CredentialStore */
19
+ export interface StoredCredentials {
20
+ accessToken: string;
21
+ refreshToken: string;
22
+ expiresAt: number;
23
+ scopes?: string[];
24
+ subscriptionType?: string | null;
25
+ rateLimitTier?: string | null;
26
+ }
27
+ /** SDK init options. Tokens can come from file OR be passed directly. */
28
+ export interface ClaudeCodeSDKOptions {
29
+ /** Direct access token. If provided, skips file reading. */
30
+ accessToken?: string;
31
+ /** Direct refresh token for auto-refresh. */
32
+ refreshToken?: string;
33
+ /** Expiry timestamp (ms). Required with accessToken if you want auto-refresh. */
34
+ expiresAt?: number;
35
+ /** Path to .credentials.json. Defaults to ~/.claude/.credentials.json */
36
+ credentialsPath?: string;
37
+ /**
38
+ * Custom credential store (DB, Redis, etc.).
39
+ * If provided, overrides credentialsPath and direct token options.
40
+ */
41
+ credentialStore?: CredentialStore;
42
+ /** device_id for metadata. If omitted, generates random 64-byte hex. */
43
+ deviceId?: string;
44
+ /** account_uuid for metadata. If omitted, reads from ~/.claude/config.json. */
45
+ accountUuid?: string;
46
+ /** SDK version string used in fingerprint + User-Agent. */
47
+ version?: string;
48
+ /** Request timeout in ms. Default: 600_000 */
49
+ timeout?: number;
50
+ /** Max retries for 5xx/529. Default: 3. Note: 429 is NEVER retried for subscribers. */
51
+ maxRetries?: number;
52
+ }
53
+ /** What we read from ~/.claude/.credentials.json */
54
+ export interface CredentialsFile {
55
+ claudeAiOauth?: {
56
+ accessToken: string;
57
+ refreshToken: string;
58
+ expiresAt: number;
59
+ scopes?: string[];
60
+ subscriptionType?: string | null;
61
+ rateLimitTier?: string | null;
62
+ };
63
+ }
64
+ /** Message param — mirrors Anthropic API */
65
+ export interface MessageParam {
66
+ role: 'user' | 'assistant';
67
+ content: string | ContentBlockParam[];
68
+ }
69
+ export type ContentBlockParam = TextBlockParam | {
70
+ type: 'image';
71
+ source: {
72
+ type: 'base64';
73
+ media_type: string;
74
+ data: string;
75
+ };
76
+ } | {
77
+ type: 'tool_use';
78
+ id: string;
79
+ name: string;
80
+ input: unknown;
81
+ } | {
82
+ type: 'tool_result';
83
+ tool_use_id: string;
84
+ content: string | ContentBlockParam[];
85
+ is_error?: boolean;
86
+ };
87
+ export interface TextBlockParam {
88
+ type: 'text';
89
+ text: string;
90
+ cache_control?: {
91
+ type: 'ephemeral';
92
+ ttl?: '1h';
93
+ scope?: 'global';
94
+ };
95
+ }
96
+ /** Tool definition */
97
+ export interface ToolDef {
98
+ name: string;
99
+ description: string;
100
+ input_schema: Record<string, unknown>;
101
+ }
102
+ /** System prompt blocks */
103
+ export type SystemParam = string | {
104
+ type: 'text';
105
+ text: string;
106
+ }[];
107
+ /** Tool choice — how the model selects tools */
108
+ export type ToolChoice = 'auto' | 'any' | {
109
+ type: 'tool';
110
+ name: string;
111
+ };
112
+ /** Options for a single generate/stream call */
113
+ export interface GenerateOptions {
114
+ model: string;
115
+ messages: MessageParam[];
116
+ system?: SystemParam;
117
+ maxTokens?: number;
118
+ thinking?: {
119
+ type: 'enabled';
120
+ budgetTokens: number;
121
+ } | {
122
+ type: 'disabled';
123
+ };
124
+ tools?: ToolDef[];
125
+ toolChoice?: ToolChoice;
126
+ temperature?: number;
127
+ topP?: number;
128
+ effort?: 'low' | 'medium' | 'high';
129
+ signal?: AbortSignal;
130
+ stopSequences?: string[];
131
+ extraBetas?: string[];
132
+ fast?: boolean;
133
+ /** Enable prompt caching. Default: true */
134
+ caching?: boolean;
135
+ }
136
+ /** Options for Conversation class */
137
+ export interface ConversationOptions {
138
+ model: string;
139
+ system?: SystemParam;
140
+ tools?: ToolDef[];
141
+ toolChoice?: ToolChoice;
142
+ maxTokens?: number;
143
+ thinking?: {
144
+ type: 'enabled';
145
+ budgetTokens: number;
146
+ } | {
147
+ type: 'disabled';
148
+ };
149
+ effort?: 'low' | 'medium' | 'high';
150
+ fast?: boolean;
151
+ signal?: AbortSignal;
152
+ extraBetas?: string[];
153
+ /** Enable prompt caching. Default: true */
154
+ caching?: boolean;
155
+ }
156
+ /** Turn options — per-send overrides */
157
+ export interface TurnOptions {
158
+ signal?: AbortSignal;
159
+ /** Override tools for this turn */
160
+ tools?: ToolDef[];
161
+ toolChoice?: ToolChoice;
162
+ }
163
+ /** Normalized stream events */
164
+ export type StreamEvent = {
165
+ type: 'text_delta';
166
+ text: string;
167
+ } | {
168
+ type: 'thinking_delta';
169
+ text: string;
170
+ } | {
171
+ type: 'tool_use_start';
172
+ id: string;
173
+ name: string;
174
+ } | {
175
+ type: 'tool_use_delta';
176
+ partialInput: string;
177
+ } | {
178
+ type: 'tool_use_end';
179
+ id: string;
180
+ name: string;
181
+ input: unknown;
182
+ } | {
183
+ type: 'message_stop';
184
+ usage: TokenUsage;
185
+ stopReason: string | null;
186
+ } | {
187
+ type: 'error';
188
+ error: Error;
189
+ };
190
+ export interface TokenUsage {
191
+ inputTokens: number;
192
+ outputTokens: number;
193
+ cacheCreationInputTokens?: number;
194
+ cacheReadInputTokens?: number;
195
+ }
196
+ export interface RateLimitInfo {
197
+ status: string | null;
198
+ resetAt: number | null;
199
+ claim: string | null;
200
+ retryAfter: number | null;
201
+ }
202
+ export interface GenerateResponse {
203
+ content: ContentBlock[];
204
+ thinking?: ThinkingBlock[];
205
+ toolCalls?: ToolUseBlock[];
206
+ usage: TokenUsage;
207
+ stopReason: string | null;
208
+ rateLimitInfo: RateLimitInfo;
209
+ model: string;
210
+ }
211
+ /** Content blocks from API response */
212
+ export type ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock;
213
+ export interface TextBlock {
214
+ type: 'text';
215
+ text: string;
216
+ }
217
+ export interface ThinkingBlock {
218
+ type: 'thinking';
219
+ thinking: string;
220
+ }
221
+ export interface ToolUseBlock {
222
+ type: 'tool_use';
223
+ id: string;
224
+ name: string;
225
+ input: unknown;
226
+ }
227
+ export declare class ClaudeCodeSDKError extends Error {
228
+ readonly cause?: unknown | undefined;
229
+ constructor(message: string, cause?: unknown | undefined);
230
+ }
231
+ export declare class AuthError extends ClaudeCodeSDKError {
232
+ constructor(message: string, cause?: unknown);
233
+ }
234
+ export declare class APIError extends ClaudeCodeSDKError {
235
+ readonly status: number;
236
+ readonly requestId: string | null;
237
+ constructor(message: string, status: number, requestId: string | null, cause?: unknown);
238
+ }
239
+ export declare class RateLimitError extends ClaudeCodeSDKError {
240
+ readonly rateLimitInfo: RateLimitInfo;
241
+ readonly status: number;
242
+ constructor(message: string, rateLimitInfo: RateLimitInfo, status?: number, cause?: unknown);
243
+ }
244
+ //# sourceMappingURL=types.d.ts.map
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@life-ai-tools/claude-code-sdk",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for Claude Code API — use your Claude Max/Pro subscription programmatically",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "bun run scripts/build-sdk.ts",
16
+ "build:dev": "tsc",
17
+ "test": "bun test",
18
+ "typecheck": "tsc --noEmit",
19
+ "prepublishOnly": "echo 'dist/ already built'"
20
+ },
21
+ "dependencies": {},
22
+ "devDependencies": {
23
+ "@types/bun": "latest",
24
+ "esbuild": "^0.27.4",
25
+ "terser": "^5.46.1",
26
+ "typescript": "^5.8.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=20.0.0",
30
+ "bun": ">=1.0.0"
31
+ },
32
+ "keywords": [
33
+ "claude",
34
+ "anthropic",
35
+ "claude-code",
36
+ "claude-max",
37
+ "sdk",
38
+ "openai-compatible",
39
+ "streaming"
40
+ ],
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/LifeAITools/opencode-claude-toolkit"
44
+ },
45
+ "homepage": "https://github.com/LifeAITools/opencode-claude-toolkit",
46
+ "license": "MIT",
47
+ "files": [
48
+ "dist/index.js",
49
+ "dist/*.d.ts",
50
+ "README.md",
51
+ "LICENSE"
52
+ ]
53
+ }