@xtruder/opencode-claude-max-plugin 0.1.10 → 0.1.12
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/README.md +27 -126
- package/build/credentials.d.ts +24 -1
- package/build/credentials.d.ts.map +1 -1
- package/build/index.d.ts +22 -32
- package/build/index.d.ts.map +1 -1
- package/build/index.js +150 -42
- package/build/model.d.ts.map +1 -1
- package/build/stream.d.ts.map +1 -1
- package/build/tool-names.d.ts.map +1 -1
- package/build/tools.d.ts.map +1 -1
- package/build/usage-cache.d.ts +15 -0
- package/build/usage-cache.d.ts.map +1 -0
- package/build/usage.d.ts.map +1 -1
- package/package.json +31 -25
- package/build/agent-index.d.ts +0 -24
- package/build/agent-index.d.ts.map +0 -1
- package/build/agent-index.js +0 -302
- package/build/agent-model.d.ts +0 -34
- package/build/agent-model.d.ts.map +0 -1
- package/build/agent-test.d.ts +0 -2
- package/build/agent-test.d.ts.map +0 -1
- package/build/usage-cli.d.ts +0 -3
- package/build/usage-cli.d.ts.map +0 -1
- package/build/usage-cli.js +0 -148
package/README.md
CHANGED
|
@@ -7,144 +7,45 @@ An [OpenCode](https://opencode.ai/) plugin that enables Claude Pro/Max subscript
|
|
|
7
7
|
- **Use your Claude subscription** — Automatically reads OAuth credentials from Claude Code, no separate API key needed
|
|
8
8
|
- **Matches Claude Code exactly** — Same headers, billing, tool names, and request format as Claude Code CLI
|
|
9
9
|
- **Prompt caching** — 98% of input tokens served from cache (system prompt + tools cached globally)
|
|
10
|
-
- **All Claude models** — Opus 4.6, Sonnet 4.6,
|
|
10
|
+
- **All Claude models** — Opus 4.6, Sonnet 4.6, Haiku 4.5
|
|
11
11
|
- **Extended thinking** — Full reasoning support with thinking variants (high/max)
|
|
12
12
|
- **Usage tracking** — Built-in `/usage` command shows subscription utilization
|
|
13
|
+
- **Self-registering** — Models are registered automatically, no manual provider config needed
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
Add the plugin to your `opencode.json` (project-level or `~/.config/opencode/opencode.json` globally):
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"$schema": "https://opencode.ai/config.json",
|
|
22
|
+
"plugin": ["@xtruder/opencode-claude-max-plugin"]
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That's it. The plugin self-registers the `anthropic-sdk` provider and its models (Haiku 4.5, Sonnet 4.6, Opus 4.6) at startup via the OpenCode config hook. No separate `provider` block is needed.
|
|
27
|
+
|
|
28
|
+
Then open OpenCode and run `/connect` → Other → `anthropic-sdk`. If Claude Code is installed and you're logged in, credentials are read automatically from `~/.claude/.credentials.json` — no API key needed.
|
|
29
|
+
|
|
30
|
+
### Custom model options
|
|
31
|
+
|
|
32
|
+
If you want to override model settings (e.g. thinking budgets, variants), you can add a `provider` block alongside the plugin:
|
|
19
33
|
|
|
20
34
|
```json
|
|
21
35
|
{
|
|
22
36
|
"$schema": "https://opencode.ai/config.json",
|
|
37
|
+
"plugin": ["@xtruder/opencode-claude-max-plugin"],
|
|
23
38
|
"provider": {
|
|
24
39
|
"anthropic-sdk": {
|
|
25
|
-
"npm": "@xtruder/opencode-claude-max-plugin",
|
|
26
|
-
"name": "Anthropic SDK",
|
|
27
40
|
"models": {
|
|
28
41
|
"claude-sonnet-4-6": {
|
|
29
|
-
"name": "Claude Sonnet 4.6",
|
|
30
|
-
"attachment": false,
|
|
31
|
-
"reasoning": true,
|
|
32
|
-
"tool_call": true,
|
|
33
|
-
"temperature": true,
|
|
34
|
-
"limit": { "context": 200000, "output": 64000 },
|
|
35
|
-
"options": {
|
|
36
|
-
"thinking": { "type": "enabled", "budgetTokens": 1024 }
|
|
37
|
-
},
|
|
38
|
-
"variants": {
|
|
39
|
-
"high": { "thinking": { "type": "enabled", "budgetTokens": 10000 } },
|
|
40
|
-
"max": { "thinking": { "type": "enabled", "budgetTokens": 32000 } }
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
"claude-opus-4-6": {
|
|
44
|
-
"name": "Claude Opus 4.6",
|
|
45
|
-
"attachment": false,
|
|
46
|
-
"reasoning": true,
|
|
47
|
-
"tool_call": true,
|
|
48
|
-
"temperature": true,
|
|
49
|
-
"limit": { "context": 1000000, "output": 64000 },
|
|
50
|
-
"options": {
|
|
51
|
-
"thinking": { "type": "adaptive" }
|
|
52
|
-
},
|
|
53
|
-
"variants": {
|
|
54
|
-
"high": { "thinking": { "type": "enabled", "budgetTokens": 10000 } },
|
|
55
|
-
"max": { "thinking": { "type": "enabled", "budgetTokens": 32000 } }
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"claude-opus-4-5-20251101": {
|
|
59
|
-
"name": "Claude Opus 4.5",
|
|
60
|
-
"attachment": false,
|
|
61
|
-
"reasoning": true,
|
|
62
|
-
"tool_call": true,
|
|
63
|
-
"temperature": true,
|
|
64
|
-
"limit": { "context": 200000, "output": 32000 },
|
|
65
|
-
"options": {
|
|
66
|
-
"thinking": { "type": "enabled", "budgetTokens": 1024 }
|
|
67
|
-
},
|
|
68
|
-
"variants": {
|
|
69
|
-
"high": { "thinking": { "type": "enabled", "budgetTokens": 10000 } },
|
|
70
|
-
"max": { "thinking": { "type": "enabled", "budgetTokens": 32000 } }
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
"claude-sonnet-4-5-20250929": {
|
|
74
|
-
"name": "Claude Sonnet 4.5",
|
|
75
|
-
"attachment": false,
|
|
76
|
-
"reasoning": true,
|
|
77
|
-
"tool_call": true,
|
|
78
|
-
"temperature": true,
|
|
79
|
-
"limit": { "context": 200000, "output": 64000 },
|
|
80
|
-
"options": {
|
|
81
|
-
"thinking": { "type": "enabled", "budgetTokens": 1024 }
|
|
82
|
-
},
|
|
83
|
-
"variants": {
|
|
84
|
-
"high": { "thinking": { "type": "enabled", "budgetTokens": 10000 } },
|
|
85
|
-
"max": { "thinking": { "type": "enabled", "budgetTokens": 32000 } }
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
"claude-haiku-4-5-20251001": {
|
|
89
|
-
"name": "Claude Haiku 4.5",
|
|
90
|
-
"attachment": false,
|
|
91
|
-
"reasoning": false,
|
|
92
|
-
"tool_call": true,
|
|
93
|
-
"temperature": true,
|
|
94
|
-
"limit": { "context": 200000, "output": 8192 }
|
|
95
|
-
},
|
|
96
|
-
"claude-opus-4-1-20250805": {
|
|
97
|
-
"name": "Claude Opus 4.1",
|
|
98
|
-
"attachment": false,
|
|
99
|
-
"reasoning": true,
|
|
100
|
-
"tool_call": true,
|
|
101
|
-
"temperature": true,
|
|
102
|
-
"limit": { "context": 200000, "output": 32000 },
|
|
103
42
|
"options": {
|
|
104
43
|
"thinking": { "type": "enabled", "budgetTokens": 1024 }
|
|
105
44
|
},
|
|
106
45
|
"variants": {
|
|
107
46
|
"high": { "thinking": { "type": "enabled", "budgetTokens": 10000 } },
|
|
108
|
-
"max":
|
|
47
|
+
"max": { "thinking": { "type": "enabled", "budgetTokens": 32000 } }
|
|
109
48
|
}
|
|
110
|
-
},
|
|
111
|
-
"claude-opus-4-20250514": {
|
|
112
|
-
"name": "Claude Opus 4",
|
|
113
|
-
"attachment": false,
|
|
114
|
-
"reasoning": true,
|
|
115
|
-
"tool_call": true,
|
|
116
|
-
"temperature": true,
|
|
117
|
-
"limit": { "context": 200000, "output": 32000 },
|
|
118
|
-
"options": {
|
|
119
|
-
"thinking": { "type": "enabled", "budgetTokens": 1024 }
|
|
120
|
-
},
|
|
121
|
-
"variants": {
|
|
122
|
-
"high": { "thinking": { "type": "enabled", "budgetTokens": 10000 } },
|
|
123
|
-
"max": { "thinking": { "type": "enabled", "budgetTokens": 32000 } }
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
"claude-sonnet-4-20250514": {
|
|
127
|
-
"name": "Claude Sonnet 4",
|
|
128
|
-
"attachment": false,
|
|
129
|
-
"reasoning": true,
|
|
130
|
-
"tool_call": true,
|
|
131
|
-
"temperature": true,
|
|
132
|
-
"limit": { "context": 200000, "output": 64000 },
|
|
133
|
-
"options": {
|
|
134
|
-
"thinking": { "type": "enabled", "budgetTokens": 1024 }
|
|
135
|
-
},
|
|
136
|
-
"variants": {
|
|
137
|
-
"high": { "thinking": { "type": "enabled", "budgetTokens": 10000 } },
|
|
138
|
-
"max": { "thinking": { "type": "enabled", "budgetTokens": 32000 } }
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
"claude-3-haiku-20240307": {
|
|
142
|
-
"name": "Claude 3 Haiku",
|
|
143
|
-
"attachment": false,
|
|
144
|
-
"reasoning": false,
|
|
145
|
-
"tool_call": true,
|
|
146
|
-
"temperature": true,
|
|
147
|
-
"limit": { "context": 200000, "output": 4096 }
|
|
148
49
|
}
|
|
149
50
|
}
|
|
150
51
|
}
|
|
@@ -152,7 +53,7 @@ Just add the following to `.opencode/opencode.json` in your project (or `~/.conf
|
|
|
152
53
|
}
|
|
153
54
|
```
|
|
154
55
|
|
|
155
|
-
|
|
56
|
+
Config-level settings are merged with plugin defaults — you only need to specify what you want to override.
|
|
156
57
|
|
|
157
58
|
## Authentication
|
|
158
59
|
|
|
@@ -195,16 +96,16 @@ After adding the config, run `/usage` inside OpenCode to see your subscription u
|
|
|
195
96
|
The plugin also works as a standalone Vercel AI SDK provider:
|
|
196
97
|
|
|
197
98
|
```typescript
|
|
198
|
-
import { createAnthropicSDK } from "@xtruder/opencode-claude-max-plugin"
|
|
199
|
-
import { streamText } from "ai"
|
|
99
|
+
import { createAnthropicSDK } from "@xtruder/opencode-claude-max-plugin"
|
|
100
|
+
import { streamText } from "ai"
|
|
200
101
|
|
|
201
102
|
// Uses ~/.claude/.credentials.json automatically
|
|
202
|
-
const provider = createAnthropicSDK()
|
|
203
|
-
const model = provider.languageModel("claude-sonnet-4-6")
|
|
103
|
+
const provider = createAnthropicSDK()
|
|
104
|
+
const model = provider.languageModel("claude-sonnet-4-6")
|
|
204
105
|
|
|
205
|
-
const result = streamText({ model, prompt: "Hello!" })
|
|
106
|
+
const result = streamText({ model, prompt: "Hello!" })
|
|
206
107
|
for await (const chunk of result.textStream) {
|
|
207
|
-
process.stdout.write(chunk)
|
|
108
|
+
process.stdout.write(chunk)
|
|
208
109
|
}
|
|
209
110
|
```
|
|
210
111
|
|
|
@@ -213,7 +114,7 @@ for await (const chunk of result.textStream) {
|
|
|
213
114
|
```bash
|
|
214
115
|
bun install
|
|
215
116
|
bun run build
|
|
216
|
-
bun run test #
|
|
117
|
+
bun run test # integration tests (requires ANTHROPIC_API_KEY or Claude Code credentials)
|
|
217
118
|
```
|
|
218
119
|
|
|
219
120
|
See [RESEARCH.md](RESEARCH.md) for detailed reverse-engineering findings on how we matched Claude Code's request format.
|
package/build/credentials.d.ts
CHANGED
|
@@ -15,7 +15,30 @@ export declare function getCredentialsPath(): string;
|
|
|
15
15
|
*/
|
|
16
16
|
export declare function readClaudeCredentials(path?: string): ClaudeOAuthCredentials | null;
|
|
17
17
|
/**
|
|
18
|
-
* Check if credentials are expired (with
|
|
18
|
+
* Check if credentials are expired (with 60s buffer).
|
|
19
19
|
*/
|
|
20
20
|
export declare function isExpired(creds: ClaudeOAuthCredentials): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Run the Claude CLI to trigger an OAuth token refresh.
|
|
23
|
+
* The CLI writes refreshed credentials to ~/.claude/.credentials.json.
|
|
24
|
+
* Retries once on failure (matching opencode-claude-auth behavior).
|
|
25
|
+
*/
|
|
26
|
+
export declare function refreshViaCli(): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Read credentials, refreshing via Claude CLI if the token is near expiry.
|
|
29
|
+
* Returns null only if credentials are completely unavailable.
|
|
30
|
+
*/
|
|
31
|
+
export declare function refreshIfNeeded(path?: string): ClaudeOAuthCredentials | null;
|
|
32
|
+
/**
|
|
33
|
+
* Get cached credentials with automatic refresh.
|
|
34
|
+
* Uses in-memory caching with a 30-second TTL to avoid
|
|
35
|
+
* hitting the filesystem on every request.
|
|
36
|
+
* When the cached token is near expiry, triggers a CLI refresh.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getCachedCredentials(path?: string): ClaudeOAuthCredentials | null;
|
|
39
|
+
/**
|
|
40
|
+
* Clear the in-memory credential cache.
|
|
41
|
+
* Forces the next getCachedCredentials() call to re-read from disk.
|
|
42
|
+
*/
|
|
43
|
+
export declare function clearCredentialCache(): void;
|
|
21
44
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC;AAqBD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAalF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAgBvC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAyB5E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAoBjF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAI3C"}
|
package/build/index.d.ts
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
import type { LanguageModelV2 } from "@ai-sdk/provider";
|
|
2
|
-
|
|
3
|
-
* Beta flags that Claude Code sends on every OAuth request.
|
|
4
|
-
* Order and exact values must match what Claude Code sends.
|
|
5
|
-
*/
|
|
6
|
-
export declare const OAUTH_BETAS: string[];
|
|
7
|
-
/**
|
|
8
|
-
* Beta flag that enables 1M context window. Only sent when model ID
|
|
9
|
-
* includes [1m] suffix — e.g. "claude-sonnet-4-6[1m]".
|
|
10
|
-
* Claude Code checks: /\[1m\]/i.test(modelId) before including this.
|
|
11
|
-
*/
|
|
12
|
-
export declare const CONTEXT_1M_BETA = "context-1m-2025-08-07";
|
|
13
|
-
/**
|
|
14
|
-
* Beta flags for regular API key auth (no OAuth).
|
|
15
|
-
*/
|
|
16
|
-
export declare const API_KEY_BETAS: string[];
|
|
2
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
17
3
|
export interface AnthropicSDKProviderOptions {
|
|
18
4
|
/**
|
|
19
5
|
* Anthropic API key. Defaults to ANTHROPIC_API_KEY env var.
|
|
@@ -51,30 +37,34 @@ export interface AnthropicSDKProvider {
|
|
|
51
37
|
*/
|
|
52
38
|
languageModel(modelId: string): LanguageModelV2;
|
|
53
39
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Cached ratelimit usage from the most recent API response headers.
|
|
56
|
-
* Updated on every successful inference call — same data Claude Code
|
|
57
|
-
* reads for its /usage display. No extra API call needed.
|
|
58
|
-
*/
|
|
59
|
-
export interface CachedUsage {
|
|
60
|
-
fiveHourUtil?: number;
|
|
61
|
-
sevenDayUtil?: number;
|
|
62
|
-
fiveHourReset?: number;
|
|
63
|
-
sevenDayReset?: number;
|
|
64
|
-
overageStatus?: string;
|
|
65
|
-
timestamp?: number;
|
|
66
|
-
}
|
|
67
|
-
export declare const cachedUsage: CachedUsage;
|
|
68
40
|
/**
|
|
69
41
|
* Create an Anthropic SDK provider for use with OpenCode / Vercel AI SDK.
|
|
70
42
|
*
|
|
71
43
|
* When using OAuth credentials from Claude Code, requests are made to
|
|
72
44
|
* look identical to Claude Code CLI requests — same headers, user-agent,
|
|
73
45
|
* beta flags, billing system block, and body structure.
|
|
46
|
+
*
|
|
47
|
+
* OpenCode discovers this function by looking for an export whose name
|
|
48
|
+
* starts with "create" when loading the npm package as a provider.
|
|
74
49
|
*/
|
|
75
50
|
export declare function createAnthropicSDK(options?: AnthropicSDKProviderOptions): AnthropicSDKProvider;
|
|
51
|
+
/**
|
|
52
|
+
* OpenCode plugin that self-registers the anthropic-sdk provider and its
|
|
53
|
+
* supported models into the running config on startup.
|
|
54
|
+
*
|
|
55
|
+
* Add this package to the `plugin` array in your opencode.json:
|
|
56
|
+
*
|
|
57
|
+
* { "plugin": ["@xtruder/opencode-claude-max-plugin"] }
|
|
58
|
+
*
|
|
59
|
+
* The plugin uses the `config` hook which is called after config is loaded
|
|
60
|
+
* but before providers are initialized. We mutate the Config object in-place
|
|
61
|
+
* to inject the provider definition and model list.
|
|
62
|
+
*
|
|
63
|
+
* OpenCode's plugin loader iterates all module exports and calls each one
|
|
64
|
+
* as a Plugin function. `createAnthropicSDK` is also called — it returns
|
|
65
|
+
* { languageModel } which is harmlessly pushed to the hooks list with all
|
|
66
|
+
* hook slots undefined (no-ops).
|
|
67
|
+
*/
|
|
68
|
+
export declare const anthropicSDKPlugin: Plugin;
|
|
76
69
|
export default createAnthropicSDK;
|
|
77
|
-
export { AnthropicSDKModel } from "./model.js";
|
|
78
|
-
export type { ConvertedPrompt } from "./prompt.js";
|
|
79
|
-
export { readClaudeCredentials, isExpired, getCredentialsPath, type ClaudeOAuthCredentials, } from "./credentials.js";
|
|
80
70
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAoFjD,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAEhC;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAE/B;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAA;CAChD;AA4BD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,2BAAgC,GACxC,oBAAoB,CAiHtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAoBhC,CAAA;AAID,eAAe,kBAAkB,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -28,7 +28,6 @@ var BUILTIN_OPENCODE_TO_CLAUDE = {
|
|
|
28
28
|
codesearch: "CodeSearch",
|
|
29
29
|
lsp: "LSP"
|
|
30
30
|
};
|
|
31
|
-
var BUILTIN_OPENCODE_NAMES = new Set(Object.keys(BUILTIN_OPENCODE_TO_CLAUDE));
|
|
32
31
|
var BUILTIN_CLAUDE_TO_OPENCODE = {};
|
|
33
32
|
for (const [oc, cc] of Object.entries(BUILTIN_OPENCODE_TO_CLAUDE)) {
|
|
34
33
|
BUILTIN_CLAUDE_TO_OPENCODE[cc] = oc;
|
|
@@ -55,7 +54,7 @@ function detectMcpServers() {
|
|
|
55
54
|
}
|
|
56
55
|
} catch {}
|
|
57
56
|
}
|
|
58
|
-
return [...servers].
|
|
57
|
+
return [...servers].toSorted((a, b) => b.length - a.length);
|
|
59
58
|
}
|
|
60
59
|
function getMcpServers() {
|
|
61
60
|
if (mcpServerNames === null) {
|
|
@@ -265,10 +264,19 @@ function formatToolResultContent(output) {
|
|
|
265
264
|
}
|
|
266
265
|
}
|
|
267
266
|
|
|
267
|
+
// src/usage-cache.ts
|
|
268
|
+
var cachedUsage = {};
|
|
269
|
+
|
|
268
270
|
// src/credentials.ts
|
|
269
271
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
272
|
+
import { execSync } from "node:child_process";
|
|
270
273
|
import { join as join2 } from "node:path";
|
|
271
274
|
import { homedir as homedir2 } from "node:os";
|
|
275
|
+
var CREDENTIAL_CACHE_TTL_MS = 30000;
|
|
276
|
+
var EXPIRY_BUFFER_MS = 60000;
|
|
277
|
+
var cachedCreds = null;
|
|
278
|
+
var cachedAt = 0;
|
|
279
|
+
var cachedPath;
|
|
272
280
|
function getCredentialsPath() {
|
|
273
281
|
return join2(homedir2(), ".claude", ".credentials.json");
|
|
274
282
|
}
|
|
@@ -287,8 +295,57 @@ function readClaudeCredentials(path) {
|
|
|
287
295
|
}
|
|
288
296
|
}
|
|
289
297
|
function isExpired(creds) {
|
|
290
|
-
|
|
291
|
-
|
|
298
|
+
return Date.now() > creds.expiresAt - EXPIRY_BUFFER_MS;
|
|
299
|
+
}
|
|
300
|
+
function refreshViaCli() {
|
|
301
|
+
const maxAttempts = 2;
|
|
302
|
+
for (let i = 0;i < maxAttempts; i++) {
|
|
303
|
+
try {
|
|
304
|
+
execSync("claude -p . --model haiku", {
|
|
305
|
+
timeout: 60000,
|
|
306
|
+
encoding: "utf-8",
|
|
307
|
+
env: { ...process.env, TERM: "dumb" },
|
|
308
|
+
stdio: "ignore"
|
|
309
|
+
});
|
|
310
|
+
return true;
|
|
311
|
+
} catch {}
|
|
312
|
+
}
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
function refreshIfNeeded(path) {
|
|
316
|
+
const creds = readClaudeCredentials(path);
|
|
317
|
+
if (!creds)
|
|
318
|
+
return null;
|
|
319
|
+
if (!isExpired(creds))
|
|
320
|
+
return creds;
|
|
321
|
+
console.warn("[anthropic-sdk-provider] OAuth token near expiry, refreshing via Claude CLI...");
|
|
322
|
+
const refreshed = refreshViaCli();
|
|
323
|
+
if (!refreshed) {
|
|
324
|
+
console.warn("[anthropic-sdk-provider] CLI refresh failed. Token may be expired.");
|
|
325
|
+
return creds;
|
|
326
|
+
}
|
|
327
|
+
const fresh = readClaudeCredentials(path);
|
|
328
|
+
if (fresh && !isExpired(fresh)) {
|
|
329
|
+
console.warn("[anthropic-sdk-provider] Token refreshed successfully.");
|
|
330
|
+
return fresh;
|
|
331
|
+
}
|
|
332
|
+
return fresh ?? creds;
|
|
333
|
+
}
|
|
334
|
+
function getCachedCredentials(path) {
|
|
335
|
+
const now = Date.now();
|
|
336
|
+
const resolvedPath = path ?? cachedPath;
|
|
337
|
+
if (cachedCreds && now - cachedAt < CREDENTIAL_CACHE_TTL_MS && !isExpired(cachedCreds)) {
|
|
338
|
+
return cachedCreds;
|
|
339
|
+
}
|
|
340
|
+
const fresh = refreshIfNeeded(resolvedPath);
|
|
341
|
+
if (fresh) {
|
|
342
|
+
cachedCreds = fresh;
|
|
343
|
+
cachedAt = now;
|
|
344
|
+
cachedPath = resolvedPath;
|
|
345
|
+
} else {
|
|
346
|
+
cachedCreds = null;
|
|
347
|
+
}
|
|
348
|
+
return cachedCreds;
|
|
292
349
|
}
|
|
293
350
|
|
|
294
351
|
// src/usage.ts
|
|
@@ -682,8 +739,8 @@ function formatReset2(epoch) {
|
|
|
682
739
|
const dateStr = reset.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
683
740
|
return diffMs < 86400000 ? `Resets ${timeStr}` : `Resets ${dateStr}, ${timeStr}`;
|
|
684
741
|
}
|
|
742
|
+
var bar = (pct, width = 50) => "█".repeat(Math.round(pct / 100 * width)) + " ".repeat(width - Math.round(pct / 100 * width));
|
|
685
743
|
function formatCachedUsage(usage) {
|
|
686
|
-
const bar = (pct, width = 50) => "█".repeat(Math.round(pct / 100 * width)) + " ".repeat(width - Math.round(pct / 100 * width));
|
|
687
744
|
const lines = [];
|
|
688
745
|
const h5 = Math.round((usage.fiveHourUtil ?? 0) * 100);
|
|
689
746
|
lines.push(" Current session");
|
|
@@ -763,7 +820,11 @@ class AnthropicSDKModel {
|
|
|
763
820
|
const rewrite = (s) => rewriteToolNamesInText(s);
|
|
764
821
|
const SYSTEM_CACHE = { type: "ephemeral", ttl: "1h", scope: "global" };
|
|
765
822
|
if (system) {
|
|
766
|
-
const contentBlocks = typeof system === "string" ? [{ type: "text", text: rewrite(system), cache_control: SYSTEM_CACHE }] : Array.isArray(system) ? system.map((b, i) => b.type === "text" ? {
|
|
823
|
+
const contentBlocks = typeof system === "string" ? [{ type: "text", text: rewrite(system), cache_control: SYSTEM_CACHE }] : Array.isArray(system) ? system.map((b, i) => b.type === "text" ? {
|
|
824
|
+
...b,
|
|
825
|
+
text: rewrite(b.text),
|
|
826
|
+
...i === system.length - 1 ? { cache_control: SYSTEM_CACHE } : {}
|
|
827
|
+
} : b) : [{ ...system, cache_control: SYSTEM_CACHE }];
|
|
767
828
|
params.system = [BILLING_SYSTEM_BLOCK, IDENTITY_SYSTEM_BLOCK, ...contentBlocks];
|
|
768
829
|
} else {
|
|
769
830
|
params.system = [BILLING_SYSTEM_BLOCK, IDENTITY_SYSTEM_BLOCK];
|
|
@@ -787,7 +848,10 @@ class AnthropicSDKModel {
|
|
|
787
848
|
const blocks = system.map((b, i) => b.type === "text" && i === system.length - 1 ? { ...b, cache_control: CACHE } : b);
|
|
788
849
|
params.system = [{ ...IDENTITY_SYSTEM_BLOCK, cache_control: CACHE }, ...blocks];
|
|
789
850
|
} else {
|
|
790
|
-
params.system = [
|
|
851
|
+
params.system = [
|
|
852
|
+
{ ...IDENTITY_SYSTEM_BLOCK, cache_control: CACHE },
|
|
853
|
+
{ ...system, cache_control: CACHE }
|
|
854
|
+
];
|
|
791
855
|
}
|
|
792
856
|
}
|
|
793
857
|
if (tools && tools.length > 0)
|
|
@@ -973,11 +1037,50 @@ var OAUTH_BETAS = [
|
|
|
973
1037
|
"effort-2025-11-24"
|
|
974
1038
|
];
|
|
975
1039
|
var CONTEXT_1M_BETA = "context-1m-2025-08-07";
|
|
976
|
-
var API_KEY_BETAS = [
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1040
|
+
var API_KEY_BETAS = ["interleaved-thinking-2025-05-14", "fine-grained-tool-streaming-2025-05-14"];
|
|
1041
|
+
var PACKAGE_NAME = "@xtruder/opencode-claude-max-plugin";
|
|
1042
|
+
var PROVIDER_ID = "anthropic-sdk";
|
|
1043
|
+
var PLUGIN_MODELS = {
|
|
1044
|
+
"claude-haiku-4-5": {
|
|
1045
|
+
name: "Claude Haiku 4.5",
|
|
1046
|
+
reasoning: false,
|
|
1047
|
+
tool_call: true,
|
|
1048
|
+
attachment: true,
|
|
1049
|
+
temperature: true,
|
|
1050
|
+
limit: { context: 200000, output: 8192 },
|
|
1051
|
+
cost: { input: 0.8, output: 4, cache_read: 0.08, cache_write: 1 },
|
|
1052
|
+
modalities: {
|
|
1053
|
+
input: ["text", "image"],
|
|
1054
|
+
output: ["text"]
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
"claude-sonnet-4-6": {
|
|
1058
|
+
name: "Claude Sonnet 4.6",
|
|
1059
|
+
reasoning: true,
|
|
1060
|
+
tool_call: true,
|
|
1061
|
+
attachment: true,
|
|
1062
|
+
temperature: true,
|
|
1063
|
+
limit: { context: 200000, output: 16000 },
|
|
1064
|
+
cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 },
|
|
1065
|
+
modalities: {
|
|
1066
|
+
input: ["text", "image", "pdf"],
|
|
1067
|
+
output: ["text"]
|
|
1068
|
+
}
|
|
1069
|
+
},
|
|
1070
|
+
"claude-opus-4-6": {
|
|
1071
|
+
name: "Claude Opus 4.6",
|
|
1072
|
+
reasoning: true,
|
|
1073
|
+
tool_call: true,
|
|
1074
|
+
attachment: true,
|
|
1075
|
+
temperature: true,
|
|
1076
|
+
limit: { context: 200000, output: 32000 },
|
|
1077
|
+
cost: { input: 15, output: 75, cache_read: 1.5, cache_write: 18.75 },
|
|
1078
|
+
modalities: {
|
|
1079
|
+
input: ["text", "image", "pdf"],
|
|
1080
|
+
output: ["text"]
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
981
1084
|
function resolveAuth(options) {
|
|
982
1085
|
if (options.apiKey) {
|
|
983
1086
|
return { apiKey: options.apiKey, isOAuth: false };
|
|
@@ -985,25 +1088,16 @@ function resolveAuth(options) {
|
|
|
985
1088
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
986
1089
|
return { apiKey: process.env.ANTHROPIC_API_KEY, isOAuth: false };
|
|
987
1090
|
}
|
|
988
|
-
const
|
|
1091
|
+
const credentialsPath = typeof options.credentialsPath === "string" ? options.credentialsPath : undefined;
|
|
1092
|
+
const creds = getCachedCredentials(credentialsPath);
|
|
989
1093
|
if (creds) {
|
|
990
|
-
if (isExpired(creds)) {
|
|
991
|
-
console.warn("[anthropic-sdk-provider] Claude Code OAuth token is expired. " + "Run 'claude' to re-authenticate, then restart.");
|
|
992
|
-
}
|
|
993
1094
|
return { apiKey: null, authToken: creds.accessToken, isOAuth: true };
|
|
994
1095
|
}
|
|
995
1096
|
return { isOAuth: false };
|
|
996
1097
|
}
|
|
997
1098
|
function createAnthropicSDK(options = {}) {
|
|
998
|
-
const {
|
|
999
|
-
|
|
1000
|
-
baseURL,
|
|
1001
|
-
headers,
|
|
1002
|
-
fetch: customFetch,
|
|
1003
|
-
name = "anthropic-sdk",
|
|
1004
|
-
credentialsPath,
|
|
1005
|
-
...rest
|
|
1006
|
-
} = options;
|
|
1099
|
+
const { baseURL, headers, fetch: customFetch, name = "anthropic-sdk" } = options;
|
|
1100
|
+
const credentialsPath = typeof options.credentialsPath === "string" ? options.credentialsPath : undefined;
|
|
1007
1101
|
const auth = resolveAuth(options);
|
|
1008
1102
|
const defaultHeaders = {
|
|
1009
1103
|
...headers,
|
|
@@ -1018,12 +1112,20 @@ function createAnthropicSDK(options = {}) {
|
|
|
1018
1112
|
const CONTEXT_1M_BODY_THRESHOLD = 600000;
|
|
1019
1113
|
const baseFetch = customFetch ?? globalThis.fetch;
|
|
1020
1114
|
const wrappedFetch = async (url, init) => {
|
|
1115
|
+
if (auth.isOAuth && init) {
|
|
1116
|
+
const freshCreds = getCachedCredentials(credentialsPath);
|
|
1117
|
+
if (freshCreds) {
|
|
1118
|
+
const reqHeaders = new Headers(init.headers);
|
|
1119
|
+
reqHeaders.set("authorization", `Bearer ${freshCreds.accessToken}`);
|
|
1120
|
+
init = { ...init, headers: reqHeaders };
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1021
1123
|
if (init?.body && typeof init.body === "string" && init.body.length > CONTEXT_1M_BODY_THRESHOLD) {
|
|
1022
|
-
const
|
|
1023
|
-
const existingBetas =
|
|
1124
|
+
const betaHeaders = new Headers(init.headers);
|
|
1125
|
+
const existingBetas = betaHeaders.get("anthropic-beta") ?? "";
|
|
1024
1126
|
if (!existingBetas.includes(CONTEXT_1M_BETA)) {
|
|
1025
|
-
|
|
1026
|
-
init = { ...init, headers:
|
|
1127
|
+
betaHeaders.set("anthropic-beta", existingBetas + "," + CONTEXT_1M_BETA);
|
|
1128
|
+
init = { ...init, headers: betaHeaders };
|
|
1027
1129
|
}
|
|
1028
1130
|
}
|
|
1029
1131
|
const resp = await baseFetch(url, init);
|
|
@@ -1043,13 +1145,12 @@ function createAnthropicSDK(options = {}) {
|
|
|
1043
1145
|
const isLongContextLimit = bodyText.includes("Extra usage is required for long context");
|
|
1044
1146
|
const isSubscriptionLimit = isLongContextLimit || unifiedStatus === "over_limit" || retryAfter > 120;
|
|
1045
1147
|
if (isSubscriptionLimit) {
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
return new Response(body, {
|
|
1148
|
+
const retryHeaders = new Headers(resp.headers);
|
|
1149
|
+
retryHeaders.set("x-should-retry", "false");
|
|
1150
|
+
return new Response(bodyText, {
|
|
1050
1151
|
status: resp.status,
|
|
1051
1152
|
statusText: resp.statusText,
|
|
1052
|
-
headers:
|
|
1153
|
+
headers: retryHeaders
|
|
1053
1154
|
});
|
|
1054
1155
|
}
|
|
1055
1156
|
}
|
|
@@ -1068,16 +1169,23 @@ function createAnthropicSDK(options = {}) {
|
|
|
1068
1169
|
}
|
|
1069
1170
|
};
|
|
1070
1171
|
}
|
|
1172
|
+
var anthropicSDKPlugin = async () => {
|
|
1173
|
+
return {
|
|
1174
|
+
config: async (cfg) => {
|
|
1175
|
+
if (!cfg.provider) {
|
|
1176
|
+
cfg.provider = {};
|
|
1177
|
+
}
|
|
1178
|
+
cfg.provider[PROVIDER_ID] = {
|
|
1179
|
+
npm: PACKAGE_NAME,
|
|
1180
|
+
models: PLUGIN_MODELS,
|
|
1181
|
+
...cfg.provider[PROVIDER_ID]
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
};
|
|
1071
1186
|
var src_default = createAnthropicSDK;
|
|
1072
1187
|
export {
|
|
1073
|
-
readClaudeCredentials,
|
|
1074
|
-
isExpired,
|
|
1075
|
-
getCredentialsPath,
|
|
1076
1188
|
src_default as default,
|
|
1077
1189
|
createAnthropicSDK,
|
|
1078
|
-
|
|
1079
|
-
OAUTH_BETAS,
|
|
1080
|
-
CONTEXT_1M_BETA,
|
|
1081
|
-
AnthropicSDKModel,
|
|
1082
|
-
API_KEY_BETAS
|
|
1190
|
+
anthropicSDKPlugin
|
|
1083
1191
|
};
|
package/build/model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAK3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAU9C,KAAK,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;AAC1E,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAK3B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAU9C,KAAK,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;AAC1E,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;AAsLtE,qBAAa,iBAAkB,YAAW,eAAe;IAQrD,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,OAAO;IATjB,QAAQ,CAAC,oBAAoB,EAAG,IAAI,CAAS;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAK;gBAGnD,OAAO,EAAE,MAAM,EACP,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,OAAe;IAMlC,OAAO,CAAC,WAAW;IAqLb,UAAU,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoF1E,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,cAAc,CAAC;CA6C7E"}
|
package/build/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAA+B,MAAM,kBAAkB,CAAA;AAC9F,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,kBAAkB,GAAG,SAAS,CAAC,kBAAkB,CAAA;AAiCtD,wBAAgB,aAAa,CAC3B,eAAe,EAAE,aAAa,CAAC,kBAAkB,CAAC,EAClD,OAAO,EAAE,MAAM,GACd,cAAc,CAAC,yBAAyB,CAAC,CAqD3C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../src/tool-names.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../src/tool-names.ts"],"names":[],"mappings":"AAiDA;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAE1D;AAgED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAY7D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAiB7D;AAqCD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM3D"}
|
package/build/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,kCAAkC,EAClC,yBAAyB,EAC1B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AACnC,KAAK,mBAAmB,GAAG,SAAS,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAA;AAwBvE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,CAAC,2BAA2B,GAAG,kCAAkC,CAAC,GAAG,SAAS,GACzF,aAAa,EAAE,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,kCAAkC,EAClC,yBAAyB,EAC1B,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAA;AAG9C,KAAK,aAAa,GAAG,SAAS,CAAC,IAAI,CAAA;AACnC,KAAK,mBAAmB,GAAG,SAAS,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAA;AAwBvE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,CAAC,2BAA2B,GAAG,kCAAkC,CAAC,GAAG,SAAS,GACzF,aAAa,EAAE,GAAG,SAAS,CAY7B;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,yBAAyB,GAAG,SAAS,GAChD,mBAAmB,GAAG,SAAS,CAcjC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cached ratelimit usage from the most recent API response headers.
|
|
3
|
+
* Updated on every successful inference call — same data Claude Code
|
|
4
|
+
* reads for its /usage display. No extra API call needed.
|
|
5
|
+
*/
|
|
6
|
+
export interface CachedUsage {
|
|
7
|
+
fiveHourUtil?: number;
|
|
8
|
+
sevenDayUtil?: number;
|
|
9
|
+
fiveHourReset?: number;
|
|
10
|
+
sevenDayReset?: number;
|
|
11
|
+
overageStatus?: string;
|
|
12
|
+
timestamp?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const cachedUsage: CachedUsage;
|
|
15
|
+
//# sourceMappingURL=usage-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-cache.d.ts","sourceRoot":"","sources":["../src/usage-cache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,WAAW,EAAE,WAAgB,CAAA"}
|
package/build/usage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,oBAAoB,EAAE,WAAW,GAAG,IAAI,CAAA;IACxC,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,WAAW,EAAE;QACX,UAAU,EAAE,OAAO,CAAA;QACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B,GAAG,IAAI,CAAA;CACT;AAID;;;GAGG;AACH,wBAAsB,UAAU,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAuBpF;AA0CD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,SAAS,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,cAAc,EAAE,WAAW,GAAG,IAAI,CAAA;IAClC,oBAAoB,EAAE,WAAW,GAAG,IAAI,CAAA;IACxC,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAA;IACpC,WAAW,EAAE;QACX,UAAU,EAAE,OAAO,CAAA;QACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;QAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B,GAAG,IAAI,CAAA;CACT;AAID;;;GAGG;AACH,wBAAsB,UAAU,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAuBpF;AA0CD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAwDnD"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xtruder/opencode-claude-max-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "OpenCode plugin for Claude Max/Pro subscription via @anthropic-ai/sdk - uses Claude Code OAuth credentials",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai-sdk",
|
|
7
|
+
"anthropic",
|
|
8
|
+
"claude",
|
|
9
|
+
"opencode",
|
|
10
|
+
"provider"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"files": [
|
|
14
|
+
"build"
|
|
15
|
+
],
|
|
5
16
|
"type": "module",
|
|
6
17
|
"main": "build/index.js",
|
|
7
18
|
"types": "build/index.d.ts",
|
|
@@ -9,42 +20,37 @@
|
|
|
9
20
|
".": {
|
|
10
21
|
"import": "./build/index.js",
|
|
11
22
|
"types": "./build/index.d.ts"
|
|
12
|
-
},
|
|
13
|
-
"./agent": {
|
|
14
|
-
"import": "./build/agent-index.js",
|
|
15
|
-
"types": "./build/agent-index.d.ts"
|
|
16
23
|
}
|
|
17
24
|
},
|
|
18
|
-
"bin": {
|
|
19
|
-
"claude-usage": "build/usage-cli.js"
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
"build"
|
|
23
|
-
],
|
|
24
25
|
"scripts": {
|
|
25
|
-
"build": "bun build ./src/index.ts
|
|
26
|
-
"
|
|
26
|
+
"build": "bun build ./src/index.ts --outdir ./build --target node --format esm --packages external && bun x tsc --emitDeclarationOnly --declaration --outDir build",
|
|
27
|
+
"dev": "bun run dev:config && bun build ./src/index.ts --outdir ./build --target node --format esm --packages external --watch",
|
|
28
|
+
"dev:config": "bun scripts/dev-config.ts",
|
|
29
|
+
"test": "bun test --concurrency 1 src/*.test.ts",
|
|
30
|
+
"lint": "oxlint src",
|
|
31
|
+
"lint:fix": "oxlint --fix src",
|
|
32
|
+
"format": "oxfmt --write .",
|
|
33
|
+
"format:check": "oxfmt --check .",
|
|
34
|
+
"prepare": "simple-git-hooks"
|
|
27
35
|
},
|
|
28
36
|
"dependencies": {
|
|
29
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.81",
|
|
30
37
|
"@anthropic-ai/sdk": "0.74.0"
|
|
31
38
|
},
|
|
32
|
-
"peerDependencies": {
|
|
33
|
-
"@ai-sdk/provider": ">=2.0.0"
|
|
34
|
-
},
|
|
35
39
|
"devDependencies": {
|
|
36
40
|
"@ai-sdk/provider": "2.0.1",
|
|
37
41
|
"@ai-sdk/provider-utils": "3.0.2",
|
|
42
|
+
"@opencode-ai/plugin": "^1.3.0",
|
|
38
43
|
"ai": "5.0.124",
|
|
39
44
|
"bun-types": "^1.3.11",
|
|
45
|
+
"oxfmt": "^0.41.0",
|
|
46
|
+
"oxlint": "^1.56.0",
|
|
47
|
+
"simple-git-hooks": "^2.13.1",
|
|
40
48
|
"typescript": "^5.7.0"
|
|
41
49
|
},
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
],
|
|
49
|
-
"license": "MIT"
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@ai-sdk/provider": ">=2.0.0"
|
|
52
|
+
},
|
|
53
|
+
"simple-git-hooks": {
|
|
54
|
+
"pre-commit": "bun run lint && bun run format:check"
|
|
55
|
+
}
|
|
50
56
|
}
|
package/build/agent-index.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { LanguageModelV2 } from "@ai-sdk/provider";
|
|
2
|
-
export interface AgentSDKProviderOptions {
|
|
3
|
-
/**
|
|
4
|
-
* Provider name for logging. Defaults to "anthropic-agent-sdk".
|
|
5
|
-
*/
|
|
6
|
-
name?: string;
|
|
7
|
-
/**
|
|
8
|
-
* Any additional options (passed through by OpenCode).
|
|
9
|
-
*/
|
|
10
|
-
[key: string]: unknown;
|
|
11
|
-
}
|
|
12
|
-
export interface AgentSDKProvider {
|
|
13
|
-
languageModel(modelId: string): LanguageModelV2;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Create an Anthropic Agent SDK provider for use with OpenCode / Vercel AI SDK.
|
|
17
|
-
*
|
|
18
|
-
* Uses @anthropic-ai/claude-agent-sdk which handles authentication via
|
|
19
|
-
* Claude Code's built-in OAuth flow — no API keys or billing headers needed.
|
|
20
|
-
*/
|
|
21
|
-
export declare function createAgentSDK(options?: AgentSDKProviderOptions): AgentSDKProvider;
|
|
22
|
-
export default createAgentSDK;
|
|
23
|
-
export { AgentSDKModel } from "./agent-model.js";
|
|
24
|
-
//# sourceMappingURL=agent-index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-index.d.ts","sourceRoot":"","sources":["../src/agent-index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAEvD,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAA;CAChD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE,uBAA4B,GACpC,gBAAgB,CAQlB;AAED,eAAe,cAAc,CAAA;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
package/build/agent-index.js
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
// src/agent-model.ts
|
|
2
|
-
import {
|
|
3
|
-
unstable_v2_createSession
|
|
4
|
-
} from "@anthropic-ai/claude-agent-sdk";
|
|
5
|
-
function mapFinishReason(reason) {
|
|
6
|
-
switch (reason) {
|
|
7
|
-
case "end_turn":
|
|
8
|
-
return "stop";
|
|
9
|
-
case "stop_sequence":
|
|
10
|
-
return "stop";
|
|
11
|
-
case "max_tokens":
|
|
12
|
-
return "length";
|
|
13
|
-
case "tool_use":
|
|
14
|
-
return "tool-calls";
|
|
15
|
-
default:
|
|
16
|
-
return "unknown";
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function extractSystem(prompt) {
|
|
20
|
-
const parts = [];
|
|
21
|
-
for (const msg of prompt) {
|
|
22
|
-
if (msg.role === "system")
|
|
23
|
-
parts.push(msg.content);
|
|
24
|
-
}
|
|
25
|
-
return parts.length > 0 ? parts.join(`
|
|
26
|
-
|
|
27
|
-
`) : undefined;
|
|
28
|
-
}
|
|
29
|
-
function extractMessages(prompt) {
|
|
30
|
-
return prompt.filter((m) => m.role !== "system");
|
|
31
|
-
}
|
|
32
|
-
function formatLastMessage(msg) {
|
|
33
|
-
if (msg.role === "user") {
|
|
34
|
-
return msg.content.map((p) => {
|
|
35
|
-
if (p.type === "text")
|
|
36
|
-
return p.text;
|
|
37
|
-
return "";
|
|
38
|
-
}).filter(Boolean).join(`
|
|
39
|
-
`);
|
|
40
|
-
}
|
|
41
|
-
if (msg.role === "tool") {
|
|
42
|
-
return msg.content.map((p) => {
|
|
43
|
-
const output = p.output;
|
|
44
|
-
if (output.type === "text" || output.type === "error-text")
|
|
45
|
-
return output.value;
|
|
46
|
-
if (output.type === "json" || output.type === "error-json")
|
|
47
|
-
return JSON.stringify(output.value);
|
|
48
|
-
return String(output.value);
|
|
49
|
-
}).join(`
|
|
50
|
-
`);
|
|
51
|
-
}
|
|
52
|
-
return "";
|
|
53
|
-
}
|
|
54
|
-
function convertAgentStream(stream, modelId) {
|
|
55
|
-
let idCounter = 0;
|
|
56
|
-
return new ReadableStream({
|
|
57
|
-
async start(controller) {
|
|
58
|
-
try {
|
|
59
|
-
let emittedMetadata = false;
|
|
60
|
-
let firstAssistantTurnDone = false;
|
|
61
|
-
for await (const msg of stream) {
|
|
62
|
-
if (firstAssistantTurnDone) {
|
|
63
|
-
if (msg.type === "result")
|
|
64
|
-
break;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (msg.type === "assistant") {
|
|
68
|
-
const message = msg.message;
|
|
69
|
-
const content = message.content ?? [];
|
|
70
|
-
if (!emittedMetadata && message.id) {
|
|
71
|
-
emittedMetadata = true;
|
|
72
|
-
controller.enqueue({
|
|
73
|
-
type: "response-metadata",
|
|
74
|
-
id: message.id,
|
|
75
|
-
modelId: message.model ?? modelId,
|
|
76
|
-
timestamp: new Date
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
for (const block of content) {
|
|
80
|
-
if (block.type === "text") {
|
|
81
|
-
const id = `block-${idCounter++}`;
|
|
82
|
-
controller.enqueue({ type: "text-start", id });
|
|
83
|
-
controller.enqueue({ type: "text-delta", id, delta: block.text });
|
|
84
|
-
controller.enqueue({ type: "text-end", id });
|
|
85
|
-
} else if (block.type === "thinking") {
|
|
86
|
-
const id = `block-${idCounter++}`;
|
|
87
|
-
controller.enqueue({ type: "reasoning-start", id });
|
|
88
|
-
controller.enqueue({ type: "reasoning-delta", id, delta: block.thinking });
|
|
89
|
-
controller.enqueue({ type: "reasoning-end", id });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (msg.type === "result") {
|
|
94
|
-
const result = msg;
|
|
95
|
-
const cacheRead = result.usage?.cache_read_input_tokens ?? 0;
|
|
96
|
-
const cacheCreate = result.usage?.cache_creation_input_tokens ?? 0;
|
|
97
|
-
controller.enqueue({
|
|
98
|
-
type: "finish",
|
|
99
|
-
finishReason: mapFinishReason(result.stop_reason),
|
|
100
|
-
usage: {
|
|
101
|
-
inputTokens: result.usage?.input_tokens ?? 0,
|
|
102
|
-
outputTokens: result.usage?.output_tokens ?? 0,
|
|
103
|
-
totalTokens: (result.usage?.input_tokens ?? 0) + (result.usage?.output_tokens ?? 0) + cacheRead + cacheCreate,
|
|
104
|
-
cachedInputTokens: cacheRead
|
|
105
|
-
},
|
|
106
|
-
providerMetadata: {
|
|
107
|
-
anthropic: {
|
|
108
|
-
cacheCreationInputTokens: cacheCreate
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
firstAssistantTurnDone = true;
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
} catch (error) {
|
|
117
|
-
controller.enqueue({ type: "error", error });
|
|
118
|
-
} finally {
|
|
119
|
-
controller.close();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
class AgentSDKModel {
|
|
126
|
-
providerName;
|
|
127
|
-
specificationVersion = "v2";
|
|
128
|
-
provider;
|
|
129
|
-
modelId;
|
|
130
|
-
supportedUrls = {};
|
|
131
|
-
session = null;
|
|
132
|
-
processedMessageCount = 0;
|
|
133
|
-
lastSystemPrompt = undefined;
|
|
134
|
-
constructor(modelId, providerName) {
|
|
135
|
-
this.providerName = providerName;
|
|
136
|
-
this.modelId = modelId;
|
|
137
|
-
this.provider = providerName;
|
|
138
|
-
}
|
|
139
|
-
needsNewSession(system, messages) {
|
|
140
|
-
if (!this.session)
|
|
141
|
-
return true;
|
|
142
|
-
if (system !== this.lastSystemPrompt)
|
|
143
|
-
return true;
|
|
144
|
-
if (messages.length < this.processedMessageCount)
|
|
145
|
-
return true;
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
createSession(system, tools) {
|
|
149
|
-
const opts = {
|
|
150
|
-
model: this.modelId,
|
|
151
|
-
maxTurns: 100,
|
|
152
|
-
includePartialMessages: true,
|
|
153
|
-
permissionMode: "bypassPermissions",
|
|
154
|
-
allowDangerouslySkipPermissions: true,
|
|
155
|
-
persistSession: false,
|
|
156
|
-
systemPrompt: system ?? "You are a helpful assistant.",
|
|
157
|
-
tools: []
|
|
158
|
-
};
|
|
159
|
-
return unstable_v2_createSession(opts);
|
|
160
|
-
}
|
|
161
|
-
async doStream(options) {
|
|
162
|
-
const system = extractSystem(options.prompt);
|
|
163
|
-
const messages = extractMessages(options.prompt);
|
|
164
|
-
const warnings = [];
|
|
165
|
-
if (this.needsNewSession(system, messages)) {
|
|
166
|
-
if (this.session) {
|
|
167
|
-
try {
|
|
168
|
-
this.session.close();
|
|
169
|
-
} catch {}
|
|
170
|
-
}
|
|
171
|
-
this.session = this.createSession(system, options.tools);
|
|
172
|
-
this.lastSystemPrompt = system;
|
|
173
|
-
this.processedMessageCount = 0;
|
|
174
|
-
}
|
|
175
|
-
let messageToSend;
|
|
176
|
-
if (this.processedMessageCount === 0) {
|
|
177
|
-
const firstUser = messages.find((m) => m.role === "user");
|
|
178
|
-
if (firstUser) {
|
|
179
|
-
messageToSend = formatLastMessage(firstUser);
|
|
180
|
-
this.processedMessageCount = messages.indexOf(firstUser) + 1;
|
|
181
|
-
}
|
|
182
|
-
} else {
|
|
183
|
-
const newMessages = messages.slice(this.processedMessageCount);
|
|
184
|
-
const toolMsg = newMessages.find((m) => m.role === "tool");
|
|
185
|
-
if (toolMsg) {
|
|
186
|
-
messageToSend = formatLastMessage(toolMsg);
|
|
187
|
-
} else {
|
|
188
|
-
const userMsg = [...newMessages].reverse().find((m) => m.role === "user");
|
|
189
|
-
if (userMsg) {
|
|
190
|
-
messageToSend = formatLastMessage(userMsg);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
this.processedMessageCount = messages.length;
|
|
194
|
-
}
|
|
195
|
-
if (!messageToSend) {
|
|
196
|
-
messageToSend = "Continue.";
|
|
197
|
-
}
|
|
198
|
-
await this.session.send(messageToSend);
|
|
199
|
-
const stream = convertAgentStream(this.session.stream(), this.modelId);
|
|
200
|
-
const wrappedStream = new ReadableStream({
|
|
201
|
-
async start(controller) {
|
|
202
|
-
controller.enqueue({ type: "stream-start", warnings });
|
|
203
|
-
const reader = stream.getReader();
|
|
204
|
-
try {
|
|
205
|
-
while (true) {
|
|
206
|
-
const { done, value } = await reader.read();
|
|
207
|
-
if (done)
|
|
208
|
-
break;
|
|
209
|
-
controller.enqueue(value);
|
|
210
|
-
}
|
|
211
|
-
} finally {
|
|
212
|
-
reader.releaseLock();
|
|
213
|
-
controller.close();
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
return {
|
|
218
|
-
stream: wrappedStream,
|
|
219
|
-
request: { body: messageToSend }
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
async doGenerate(options) {
|
|
223
|
-
const result = await this.doStream(options);
|
|
224
|
-
const reader = result.stream.getReader();
|
|
225
|
-
const content = [];
|
|
226
|
-
let finishReason = "unknown";
|
|
227
|
-
let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
228
|
-
let responseId = "";
|
|
229
|
-
let responseModel = this.modelId;
|
|
230
|
-
let currentText = "";
|
|
231
|
-
while (true) {
|
|
232
|
-
const { done, value } = await reader.read();
|
|
233
|
-
if (done)
|
|
234
|
-
break;
|
|
235
|
-
switch (value.type) {
|
|
236
|
-
case "text-delta":
|
|
237
|
-
currentText += value.delta ?? "";
|
|
238
|
-
break;
|
|
239
|
-
case "text-end":
|
|
240
|
-
if (currentText) {
|
|
241
|
-
content.push({ type: "text", text: currentText });
|
|
242
|
-
currentText = "";
|
|
243
|
-
}
|
|
244
|
-
break;
|
|
245
|
-
case "tool-call":
|
|
246
|
-
content.push({
|
|
247
|
-
type: "tool-call",
|
|
248
|
-
toolCallId: value.toolCallId,
|
|
249
|
-
toolName: value.toolName,
|
|
250
|
-
input: value.input
|
|
251
|
-
});
|
|
252
|
-
break;
|
|
253
|
-
case "finish":
|
|
254
|
-
finishReason = value.finishReason;
|
|
255
|
-
usage = value.usage ?? usage;
|
|
256
|
-
break;
|
|
257
|
-
case "response-metadata":
|
|
258
|
-
responseId = value.id ?? "";
|
|
259
|
-
responseModel = value.modelId ?? this.modelId;
|
|
260
|
-
break;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
if (currentText) {
|
|
264
|
-
content.push({ type: "text", text: currentText });
|
|
265
|
-
}
|
|
266
|
-
return {
|
|
267
|
-
content,
|
|
268
|
-
finishReason,
|
|
269
|
-
usage,
|
|
270
|
-
response: {
|
|
271
|
-
id: responseId,
|
|
272
|
-
modelId: responseModel,
|
|
273
|
-
timestamp: new Date
|
|
274
|
-
},
|
|
275
|
-
warnings: []
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
close() {
|
|
279
|
-
if (this.session) {
|
|
280
|
-
try {
|
|
281
|
-
this.session.close();
|
|
282
|
-
} catch {}
|
|
283
|
-
this.session = null;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// src/agent-index.ts
|
|
289
|
-
function createAgentSDK(options = {}) {
|
|
290
|
-
const { name = "anthropic-agent-sdk", ...rest } = options;
|
|
291
|
-
return {
|
|
292
|
-
languageModel(modelId) {
|
|
293
|
-
return new AgentSDKModel(modelId, name);
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
var agent_index_default = createAgentSDK;
|
|
298
|
-
export {
|
|
299
|
-
agent_index_default as default,
|
|
300
|
-
createAgentSDK,
|
|
301
|
-
AgentSDKModel
|
|
302
|
-
};
|
package/build/agent-model.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { LanguageModelV2, LanguageModelV2CallOptions } from "@ai-sdk/provider";
|
|
2
|
-
type DoStreamResult = Awaited<ReturnType<LanguageModelV2["doStream"]>>;
|
|
3
|
-
type DoGenerateResult = Awaited<ReturnType<LanguageModelV2["doGenerate"]>>;
|
|
4
|
-
export declare class AgentSDKModel implements LanguageModelV2 {
|
|
5
|
-
private providerName;
|
|
6
|
-
readonly specificationVersion: "v2";
|
|
7
|
-
readonly provider: string;
|
|
8
|
-
readonly modelId: string;
|
|
9
|
-
readonly supportedUrls: Record<string, RegExp[]>;
|
|
10
|
-
private session;
|
|
11
|
-
private processedMessageCount;
|
|
12
|
-
private lastSystemPrompt;
|
|
13
|
-
constructor(modelId: string, providerName: string);
|
|
14
|
-
/**
|
|
15
|
-
* Determine if we need a new session based on the prompt.
|
|
16
|
-
* A new session is needed when:
|
|
17
|
-
* - No session exists
|
|
18
|
-
* - System prompt changed
|
|
19
|
-
* - Message count decreased (new conversation)
|
|
20
|
-
*
|
|
21
|
-
* The session is REUSED across doStream() calls for the same conversation
|
|
22
|
-
* (tool result continuations and multi-turn).
|
|
23
|
-
*/
|
|
24
|
-
private needsNewSession;
|
|
25
|
-
private createSession;
|
|
26
|
-
doStream(options: LanguageModelV2CallOptions): Promise<DoStreamResult>;
|
|
27
|
-
doGenerate(options: LanguageModelV2CallOptions): Promise<DoGenerateResult>;
|
|
28
|
-
/**
|
|
29
|
-
* Close the session and clean up resources.
|
|
30
|
-
*/
|
|
31
|
-
close(): void;
|
|
32
|
-
}
|
|
33
|
-
export {};
|
|
34
|
-
//# sourceMappingURL=agent-model.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-model.d.ts","sourceRoot":"","sources":["../src/agent-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,0BAA0B,EAO3B,MAAM,kBAAkB,CAAA;AAMzB,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;AACtE,KAAK,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;AA6J1E,qBAAa,aAAc,YAAW,eAAe;IAajD,OAAO,CAAC,YAAY;IAZtB,QAAQ,CAAC,oBAAoB,EAAG,IAAI,CAAS;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAK;IAErD,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,qBAAqB,CAAI;IACjC,OAAO,CAAC,gBAAgB,CAAgC;gBAItD,OAAO,EAAE,MAAM,EACP,YAAY,EAAE,MAAM;IAM9B;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,aAAa;IAoBf,QAAQ,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,cAAc,CAAC;IA2EtE,UAAU,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+DhF;;OAEG;IACH,KAAK;CAMN"}
|
package/build/agent-test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"agent-test.d.ts","sourceRoot":"","sources":["../src/agent-test.ts"],"names":[],"mappings":""}
|
package/build/usage-cli.d.ts
DELETED
package/build/usage-cli.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"usage-cli.d.ts","sourceRoot":"","sources":["../src/usage-cli.ts"],"names":[],"mappings":""}
|
package/build/usage-cli.js
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
// src/credentials.ts
|
|
4
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
import { homedir } from "node:os";
|
|
7
|
-
function getCredentialsPath() {
|
|
8
|
-
return join(homedir(), ".claude", ".credentials.json");
|
|
9
|
-
}
|
|
10
|
-
function readClaudeCredentials(path) {
|
|
11
|
-
const credPath = path ?? getCredentialsPath();
|
|
12
|
-
if (!existsSync(credPath))
|
|
13
|
-
return null;
|
|
14
|
-
try {
|
|
15
|
-
const raw = readFileSync(credPath, "utf-8");
|
|
16
|
-
const parsed = JSON.parse(raw);
|
|
17
|
-
if (!parsed.claudeAiOauth?.accessToken)
|
|
18
|
-
return null;
|
|
19
|
-
return parsed.claudeAiOauth;
|
|
20
|
-
} catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function isExpired(creds) {
|
|
25
|
-
const buffer = 5 * 60 * 1000;
|
|
26
|
-
return Date.now() > creds.expiresAt - buffer;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// src/usage.ts
|
|
30
|
-
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
31
|
-
async function fetchUsage(credentialsPath) {
|
|
32
|
-
const creds = readClaudeCredentials(credentialsPath);
|
|
33
|
-
if (!creds)
|
|
34
|
-
return null;
|
|
35
|
-
if (isExpired(creds))
|
|
36
|
-
return null;
|
|
37
|
-
const resp = await fetch(USAGE_URL, {
|
|
38
|
-
headers: {
|
|
39
|
-
authorization: `Bearer ${creds.accessToken}`,
|
|
40
|
-
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
41
|
-
"content-type": "application/json",
|
|
42
|
-
"user-agent": "claude-cli/2.1.81 (external, sdk-cli)",
|
|
43
|
-
"x-app": "cli"
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
if (!resp.ok) {
|
|
47
|
-
const body = await resp.text().catch(() => "");
|
|
48
|
-
const msg = body.includes("message") ? JSON.parse(body)?.error?.message ?? body : `HTTP ${resp.status}`;
|
|
49
|
-
throw new Error(`Usage API error: ${msg}`);
|
|
50
|
-
}
|
|
51
|
-
return resp.json();
|
|
52
|
-
}
|
|
53
|
-
function progressBar(utilization, width = 50) {
|
|
54
|
-
const filled = Math.round(utilization / 100 * width);
|
|
55
|
-
const bar = "█".repeat(filled) + " ".repeat(width - filled);
|
|
56
|
-
return bar;
|
|
57
|
-
}
|
|
58
|
-
function formatReset(resetsAt) {
|
|
59
|
-
if (!resetsAt)
|
|
60
|
-
return "";
|
|
61
|
-
const reset = new Date(resetsAt);
|
|
62
|
-
const now = new Date;
|
|
63
|
-
const diffMs = reset.getTime() - now.getTime();
|
|
64
|
-
if (diffMs <= 0)
|
|
65
|
-
return "Reset now";
|
|
66
|
-
const diffMin = Math.floor(diffMs / 60000);
|
|
67
|
-
const diffHour = Math.floor(diffMin / 60);
|
|
68
|
-
const timeStr = reset.toLocaleTimeString("en-US", {
|
|
69
|
-
hour: "numeric",
|
|
70
|
-
minute: "2-digit",
|
|
71
|
-
timeZoneName: "short"
|
|
72
|
-
});
|
|
73
|
-
const dateStr = reset.toLocaleDateString("en-US", {
|
|
74
|
-
month: "short",
|
|
75
|
-
day: "numeric"
|
|
76
|
-
});
|
|
77
|
-
if (diffHour < 24) {
|
|
78
|
-
return `Resets ${timeStr}`;
|
|
79
|
-
}
|
|
80
|
-
return `Resets ${dateStr}, ${timeStr}`;
|
|
81
|
-
}
|
|
82
|
-
function formatUsage(data) {
|
|
83
|
-
const lines = [];
|
|
84
|
-
if (data.five_hour) {
|
|
85
|
-
const pct = Math.round(data.five_hour.utilization);
|
|
86
|
-
lines.push(" Current session");
|
|
87
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
88
|
-
lines.push(` ${formatReset(data.five_hour.resets_at)}`);
|
|
89
|
-
lines.push("");
|
|
90
|
-
}
|
|
91
|
-
if (data.seven_day) {
|
|
92
|
-
const pct = Math.round(data.seven_day.utilization);
|
|
93
|
-
lines.push(" Current week (all models)");
|
|
94
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
95
|
-
lines.push(` ${formatReset(data.seven_day.resets_at)}`);
|
|
96
|
-
lines.push("");
|
|
97
|
-
}
|
|
98
|
-
if (data.seven_day_sonnet) {
|
|
99
|
-
const pct = Math.round(data.seven_day_sonnet.utilization);
|
|
100
|
-
lines.push(" Current week (Sonnet only)");
|
|
101
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
102
|
-
if (data.seven_day_sonnet.resets_at) {
|
|
103
|
-
lines.push(` ${formatReset(data.seven_day_sonnet.resets_at)}`);
|
|
104
|
-
}
|
|
105
|
-
lines.push("");
|
|
106
|
-
}
|
|
107
|
-
if (data.seven_day_opus) {
|
|
108
|
-
const pct = Math.round(data.seven_day_opus.utilization);
|
|
109
|
-
lines.push(" Current week (Opus only)");
|
|
110
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
111
|
-
if (data.seven_day_opus.resets_at) {
|
|
112
|
-
lines.push(` ${formatReset(data.seven_day_opus.resets_at)}`);
|
|
113
|
-
}
|
|
114
|
-
lines.push("");
|
|
115
|
-
}
|
|
116
|
-
if (data.extra_usage) {
|
|
117
|
-
if (data.extra_usage.is_enabled) {
|
|
118
|
-
const pct = Math.round(data.extra_usage.utilization ?? 0);
|
|
119
|
-
lines.push(" Extra usage");
|
|
120
|
-
lines.push(` ${progressBar(pct)} ${pct}% used`);
|
|
121
|
-
if (data.extra_usage.monthly_limit != null) {
|
|
122
|
-
lines.push(` $${data.extra_usage.used_credits?.toFixed(2) ?? "0.00"} / $${data.extra_usage.monthly_limit.toFixed(2)}`);
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
lines.push(" Extra usage: disabled");
|
|
126
|
-
}
|
|
127
|
-
lines.push("");
|
|
128
|
-
}
|
|
129
|
-
return lines.join(`
|
|
130
|
-
`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// src/usage-cli.ts
|
|
134
|
-
var data;
|
|
135
|
-
try {
|
|
136
|
-
data = await fetchUsage();
|
|
137
|
-
} catch (e) {
|
|
138
|
-
console.error(e.message ?? "Could not fetch usage.");
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
if (!data) {
|
|
142
|
-
console.error("Could not fetch usage. Make sure you're logged into Claude Code (~/.claude/.credentials.json).");
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
console.log("");
|
|
146
|
-
console.log(" Claude Subscription Usage");
|
|
147
|
-
console.log(" " + "─".repeat(52));
|
|
148
|
-
console.log(formatUsage(data));
|