@demicodes/provider-claude-code 0.10.2 → 0.10.3
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 +0 -30
- package/dist/index.d.mts +15 -117
- package/dist/index.mjs +84 -602
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -5,38 +5,8 @@ and `listClaudeCodeModels()`.
|
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
7
|
import { createClaudeCodeProvider, listClaudeCodeModels } from '@demicodes/provider-claude-code'
|
|
8
|
-
|
|
9
|
-
const provider = createClaudeCodeProvider()
|
|
10
|
-
// provider.auth, provider.quota, provider.credentials (multi-account pool by default)
|
|
11
8
|
```
|
|
12
9
|
|
|
13
|
-
## Auth and credentials
|
|
14
|
-
|
|
15
|
-
- Default material: `CLAUDE_CODE_OAUTH_TOKEN` or macOS Keychain (`Claude Code-credentials`).
|
|
16
|
-
- Multi-credential pool under `$DEMI_HOME/credentials/claude-code/`; active token is
|
|
17
|
-
injected into the CLI child as `CLAUDE_CODE_OAUTH_TOKEN`.
|
|
18
|
-
- Lifecycle: `beginLogin` → `claude auth login` → `importDefault` / `add` → `setActive`.
|
|
19
|
-
- Changing active credential forces a cold restart of a long-lived CLI process.
|
|
20
|
-
|
|
21
|
-
See [docs/provider-global-credentials.md](../../docs/provider-global-credentials.md).
|
|
22
|
-
|
|
23
|
-
## Quota
|
|
24
|
-
|
|
25
|
-
- **probe** (cost: `free`): `GET /api/oauth/usage` with the active OAuth token.
|
|
26
|
-
- **observe**: stream-json `rate_limits` bodies (and unified rate-limit headers when present).
|
|
27
|
-
|
|
28
|
-
See [docs/provider-quota.md](../../docs/provider-quota.md).
|
|
29
|
-
|
|
30
|
-
## Tool-call batches
|
|
31
|
-
|
|
32
|
-
Claude Code's SDK-MCP control channel can hold later `tools/call` callbacks until
|
|
33
|
-
the preceding callback receives a result, even when the model emitted several
|
|
34
|
-
`tool_use` blocks in one response. The provider preserves the model's original
|
|
35
|
-
batch and tool-use IDs for the host scheduler, then matches the completed
|
|
36
|
-
results to SDK-MCP callbacks as the CLI releases them.
|
|
37
|
-
|
|
38
|
-
See [docs/tool-call-concurrency.md](../../docs/tool-call-concurrency.md).
|
|
39
|
-
|
|
40
10
|
> Diagnostics: the transport writes a raw request/response wire log (including
|
|
41
11
|
> prompts) to `$TMPDIR/demi-claude-wire` by default. Disable with
|
|
42
12
|
> `DEMI_CLAUDE_WIRE_LOG=0`. See [SECURITY](../../SECURITY.md).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ModelPolicy, Provider,
|
|
2
|
-
|
|
1
|
+
import { ModelPolicy, Provider, ProviderModelList, ProviderQuota, ProviderQuotaProbeResult } from "@demicodes/provider";
|
|
2
|
+
|
|
3
3
|
//#region src/models.d.ts
|
|
4
4
|
interface ClaudeCodeModelCatalogOptions {
|
|
5
5
|
fetch?: ModelCatalogFetch;
|
|
@@ -10,74 +10,12 @@ interface ClaudeCodeModelCatalogOptions {
|
|
|
10
10
|
type ModelCatalogFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
11
11
|
declare function listClaudeCodeModels(options?: ClaudeCodeModelCatalogOptions): Promise<ProviderModelList>;
|
|
12
12
|
//#endregion
|
|
13
|
-
//#region src/oauth.d.ts
|
|
14
|
-
/**
|
|
15
|
-
* Where a resolved token came from. Callers that inject the token into a
|
|
16
|
-
* spawned CLI must skip `keychain`: that token is the CLI's own short-lived
|
|
17
|
-
* credential, and injecting it via CLAUDE_CODE_OAUTH_TOKEN disables the CLI's
|
|
18
|
-
* refresh flow — the run starts 401ing as soon as the token expires. Owned
|
|
19
|
-
* sources (static/file/env) are the caller's responsibility and inject as-is.
|
|
20
|
-
*/
|
|
21
|
-
type ClaudeCodeOAuthSource = 'static' | 'file' | 'env' | 'keychain';
|
|
22
|
-
interface ClaudeCodeOAuthAccess {
|
|
23
|
-
accessToken: string;
|
|
24
|
-
source: ClaudeCodeOAuthSource;
|
|
25
|
-
subscriptionType?: string | null;
|
|
26
|
-
rateLimitTier?: string | null;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Resolve Claude Code consumer OAuth access for quota APIs.
|
|
30
|
-
* Order: options env CLAUDE_CODE_OAUTH_TOKEN, then macOS Keychain "Claude Code-credentials".
|
|
31
|
-
* Prefer injecting {@link ClaudeCodeAuthStore} when multi-credential is enabled.
|
|
32
|
-
*/
|
|
33
|
-
declare function resolveClaudeCodeOAuthAccess(): Promise<ClaudeCodeOAuthAccess | null>;
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/auth.d.ts
|
|
36
|
-
interface ClaudeCodeAuthStore {
|
|
37
|
-
status(): Promise<ProviderAuthState>;
|
|
38
|
-
resolveAccess(options?: {
|
|
39
|
-
forceRefresh?: boolean;
|
|
40
|
-
}): Promise<ClaudeCodeOAuthAccess>;
|
|
41
|
-
}
|
|
42
|
-
/** Renews a pool oauth secret (wired to `refreshClaudeCodeSecret`; injectable in tests). */
|
|
43
|
-
type ClaudeCodeSecretRefresh = (secret: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
44
|
-
interface FileClaudeCodeAuthStoreOptions {
|
|
45
|
-
/** Optional path to oauth.json (pool entry). */
|
|
46
|
-
oauthFile?: string;
|
|
47
|
-
/** Prefer this token over env/keychain when set (tests / static). */
|
|
48
|
-
accessToken?: string | null;
|
|
49
|
-
/** Renews the oauth file when its access token nears expiry. */
|
|
50
|
-
refresh?: ClaudeCodeSecretRefresh;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Resolves Claude OAuth: explicit token → oauth file → CLAUDE_CODE_OAUTH_TOKEN → keychain.
|
|
54
|
-
*/
|
|
55
|
-
declare class FileClaudeCodeAuthStore implements ClaudeCodeAuthStore {
|
|
56
|
-
private readonly oauthFile;
|
|
57
|
-
private readonly accessToken;
|
|
58
|
-
private readonly refresh;
|
|
59
|
-
constructor(options?: FileClaudeCodeAuthStoreOptions);
|
|
60
|
-
status(): Promise<ProviderAuthState>;
|
|
61
|
-
resolveAccess(): Promise<ClaudeCodeOAuthAccess>;
|
|
62
|
-
}
|
|
63
|
-
declare class StaticClaudeCodeAuthStore implements ClaudeCodeAuthStore {
|
|
64
|
-
private readonly access;
|
|
65
|
-
constructor(access: ClaudeCodeOAuthAccess);
|
|
66
|
-
status(): Promise<ProviderAuthState>;
|
|
67
|
-
resolveAccess(): Promise<ClaudeCodeOAuthAccess>;
|
|
68
|
-
}
|
|
69
|
-
//#endregion
|
|
70
13
|
//#region src/provider.d.ts
|
|
71
14
|
interface ClaudeCodeProviderOptions {
|
|
72
15
|
id?: string;
|
|
73
16
|
displayName?: string;
|
|
74
17
|
claudePath?: string;
|
|
75
18
|
models?: ModelPolicy;
|
|
76
|
-
/** Demi state root for credential pool (`$DEMI_HOME` / `~/.demi`). */
|
|
77
|
-
stateDir?: string;
|
|
78
|
-
/** When true (default), attach multi-credential pool + global switch. */
|
|
79
|
-
credentials?: boolean;
|
|
80
|
-
authStore?: ClaudeCodeAuthStore;
|
|
81
19
|
}
|
|
82
20
|
declare function createClaudeCodeProvider(options?: ClaudeCodeProviderOptions): Provider;
|
|
83
21
|
//#endregion
|
|
@@ -89,6 +27,18 @@ declare function createClaudeCodeProvider(options?: ClaudeCodeProviderOptions):
|
|
|
89
27
|
*/
|
|
90
28
|
declare function resolveWireLogDir(): string | null;
|
|
91
29
|
//#endregion
|
|
30
|
+
//#region src/oauth.d.ts
|
|
31
|
+
interface ClaudeCodeOAuthAccess {
|
|
32
|
+
accessToken: string;
|
|
33
|
+
subscriptionType?: string | null;
|
|
34
|
+
rateLimitTier?: string | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve Claude Code consumer OAuth access for quota APIs.
|
|
38
|
+
* Order: options env CLAUDE_CODE_OAUTH_TOKEN, then macOS Keychain "Claude Code-credentials".
|
|
39
|
+
*/
|
|
40
|
+
declare function resolveClaudeCodeOAuthAccess(): Promise<ClaudeCodeOAuthAccess | null>;
|
|
41
|
+
//#endregion
|
|
92
42
|
//#region src/quota.d.ts
|
|
93
43
|
interface ClaudeCodeQuotaOptions {
|
|
94
44
|
providerId?: string;
|
|
@@ -108,56 +58,4 @@ declare function mapClaudeUsagePayload(payload: unknown, access?: ClaudeCodeOAut
|
|
|
108
58
|
/** Map anthropic-ratelimit-unified-* headers into a coarse snapshot. */
|
|
109
59
|
declare function observeClaudeRateLimitHeaders(headers: Headers | undefined): ProviderQuotaProbeResult | null;
|
|
110
60
|
//#endregion
|
|
111
|
-
|
|
112
|
-
declare class PoolAwareClaudeCodeAuthStore implements ClaudeCodeAuthStore {
|
|
113
|
-
private readonly pool;
|
|
114
|
-
constructor(pool: FileCredentialPool);
|
|
115
|
-
status(): Promise<import("@demicodes/provider").ProviderAuthState>;
|
|
116
|
-
resolveAccess(): Promise<ClaudeCodeOAuthAccess>;
|
|
117
|
-
private currentStore;
|
|
118
|
-
}
|
|
119
|
-
declare function openClaudeCodeCredentialPool(options?: {
|
|
120
|
-
stateDir?: string;
|
|
121
|
-
}): FileCredentialPool;
|
|
122
|
-
declare function createClaudeCodeCredentials(pool: FileCredentialPool, authStore: ClaudeCodeAuthStore, options?: {
|
|
123
|
-
quota?: ProviderQuota | null;
|
|
124
|
-
onActiveChange?: () => void;
|
|
125
|
-
/** Injectable fetch for the OAuth login flow (tests). */
|
|
126
|
-
loginFetch?: typeof fetch;
|
|
127
|
-
}): ProviderCredentials;
|
|
128
|
-
//#endregion
|
|
129
|
-
//#region src/login.d.ts
|
|
130
|
-
/** Pool-entry oauth.json shape (demi-owned; refreshable when refreshToken present). */
|
|
131
|
-
interface ClaudeCodeOAuthSecret {
|
|
132
|
-
accessToken: string;
|
|
133
|
-
refreshToken?: string | null;
|
|
134
|
-
/** ISO-8601 access token expiry. */
|
|
135
|
-
expiresAt?: string | null;
|
|
136
|
-
scopes?: string[] | null;
|
|
137
|
-
subscriptionType?: string | null;
|
|
138
|
-
rateLimitTier?: string | null;
|
|
139
|
-
emailAddress?: string | null;
|
|
140
|
-
[key: string]: unknown;
|
|
141
|
-
}
|
|
142
|
-
interface ClaudeCodeLoginOptions {
|
|
143
|
-
signal?: AbortSignal;
|
|
144
|
-
/** Fires once with the authorize URL; the flow then waits on promptForCode. */
|
|
145
|
-
onPending?: (pending: {
|
|
146
|
-
verificationUrl: string;
|
|
147
|
-
requiresCodeInput: true;
|
|
148
|
-
}) => void;
|
|
149
|
-
/** Collects the "code#state" string the vendor page shows after approval. */
|
|
150
|
-
promptForCode: () => Promise<string>;
|
|
151
|
-
fetch?: typeof fetch;
|
|
152
|
-
consoleBase?: string;
|
|
153
|
-
}
|
|
154
|
-
/** Runs the copy-back OAuth flow and returns a refreshable pool secret. */
|
|
155
|
-
declare function runClaudeCodeLogin(options: ClaudeCodeLoginOptions): Promise<ClaudeCodeOAuthSecret>;
|
|
156
|
-
/** Refreshes a pool secret in place; returns the renewed secret. */
|
|
157
|
-
declare function refreshClaudeCodeSecret(secret: ClaudeCodeOAuthSecret, options?: {
|
|
158
|
-
fetch?: typeof fetch;
|
|
159
|
-
consoleBase?: string;
|
|
160
|
-
signal?: AbortSignal;
|
|
161
|
-
}): Promise<ClaudeCodeOAuthSecret>;
|
|
162
|
-
//#endregion
|
|
163
|
-
export { type ClaudeCodeAuthStore, type ClaudeCodeLoginOptions, type ClaudeCodeModelCatalogOptions, type ClaudeCodeOAuthAccess, type ClaudeCodeOAuthSecret, type ClaudeCodeProviderOptions, type ClaudeCodeQuotaOptions, FileClaudeCodeAuthStore, PoolAwareClaudeCodeAuthStore, StaticClaudeCodeAuthStore, createClaudeCodeCredentials, createClaudeCodeProvider, createClaudeCodeQuota, listClaudeCodeModels, mapClaudeUsagePayload, observeClaudeRateLimitHeaders, observeClaudeStreamBody, openClaudeCodeCredentialPool, refreshClaudeCodeSecret, resolveClaudeCodeOAuthAccess, resolveWireLogDir, runClaudeCodeLogin };
|
|
61
|
+
export { type ClaudeCodeModelCatalogOptions, type ClaudeCodeOAuthAccess, type ClaudeCodeProviderOptions, type ClaudeCodeQuotaOptions, createClaudeCodeProvider, createClaudeCodeQuota, listClaudeCodeModels, mapClaudeUsagePayload, observeClaudeRateLimitHeaders, observeClaudeStreamBody, resolveClaudeCodeOAuthAccess, resolveWireLogDir };
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { applyModelPolicy, clampUsedPercent, createProviderQuota, defineProvider,
|
|
1
|
+
import { errorMessage, isRecord, nonEmptyString, numberOrNull, stringOrNull } from "@demicodes/utils";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { applyModelPolicy, clampUsedPercent, createProviderQuota, defineProvider, severityFromUsedPercent, unixSecondsToIso } from "@demicodes/provider";
|
|
4
|
+
import { Buffer as Buffer$1 } from "node:buffer";
|
|
4
5
|
import { execFile, spawn } from "node:child_process";
|
|
5
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import process$1 from "node:process";
|
|
8
|
-
import { FileCredentialPool, credentialIdFromIdentity } from "@demicodes/provider/credentials-pool";
|
|
9
|
-
import { Buffer as Buffer$1 } from "node:buffer";
|
|
10
8
|
import { appendFileSync, mkdirSync, statSync } from "node:fs";
|
|
11
9
|
import { createInterface } from "node:readline";
|
|
12
10
|
import { tmpdir } from "node:os";
|
|
@@ -97,8 +95,7 @@ const CLAUDE_FAMILY_RANK = {
|
|
|
97
95
|
haiku: 2
|
|
98
96
|
};
|
|
99
97
|
function claudeFamilyRank(id) {
|
|
100
|
-
|
|
101
|
-
return CLAUDE_FAMILY_RANK[family] ?? 3;
|
|
98
|
+
return CLAUDE_FAMILY_RANK[id.slice(7).split("-")[0] ?? ""] ?? 3;
|
|
102
99
|
}
|
|
103
100
|
/** Canonical catalog order: flagship family first (Opus > Sonnet > Haiku > others), newest version first. */
|
|
104
101
|
function compareClaudeModels(a, b) {
|
|
@@ -200,377 +197,6 @@ function reasoningEfforts(value) {
|
|
|
200
197
|
return efforts.length > 0 ? efforts : [];
|
|
201
198
|
}
|
|
202
199
|
//#endregion
|
|
203
|
-
//#region src/auth.ts
|
|
204
|
-
const execFileAsync = promisify(execFile);
|
|
205
|
-
const OAUTH_EXPIRY_SKEW_MS = 300 * 1e3;
|
|
206
|
-
/**
|
|
207
|
-
* Resolves Claude OAuth: explicit token → oauth file → CLAUDE_CODE_OAUTH_TOKEN → keychain.
|
|
208
|
-
*/
|
|
209
|
-
var FileClaudeCodeAuthStore = class {
|
|
210
|
-
oauthFile;
|
|
211
|
-
accessToken;
|
|
212
|
-
refresh;
|
|
213
|
-
constructor(options = {}) {
|
|
214
|
-
this.oauthFile = options.oauthFile ?? null;
|
|
215
|
-
this.accessToken = nonEmptyString(options.accessToken) ?? null;
|
|
216
|
-
this.refresh = options.refresh ?? null;
|
|
217
|
-
}
|
|
218
|
-
async status() {
|
|
219
|
-
try {
|
|
220
|
-
return {
|
|
221
|
-
status: "authenticated",
|
|
222
|
-
accountLabel: nonEmptyString((await this.resolveAccess()).subscriptionType) ?? "Claude Code"
|
|
223
|
-
};
|
|
224
|
-
} catch (error) {
|
|
225
|
-
if (error instanceof ClaudeCodeAuthError && error.code === "auth_missing") return {
|
|
226
|
-
status: "unauthenticated",
|
|
227
|
-
message: error.message
|
|
228
|
-
};
|
|
229
|
-
return {
|
|
230
|
-
status: "error",
|
|
231
|
-
message: error instanceof Error ? error.message : String(error)
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async resolveAccess() {
|
|
236
|
-
if (this.accessToken) return {
|
|
237
|
-
accessToken: this.accessToken,
|
|
238
|
-
source: "static"
|
|
239
|
-
};
|
|
240
|
-
if (this.oauthFile) try {
|
|
241
|
-
let raw = JSON.parse(await readFile(this.oauthFile, "utf8"));
|
|
242
|
-
if (!isRecord(raw)) throw new ClaudeCodeAuthError("auth_invalid", `Invalid OAuth file: ${this.oauthFile}`);
|
|
243
|
-
const expiresAt = nonEmptyString(raw.expiresAt);
|
|
244
|
-
if (expiresAt !== void 0 && Date.parse(expiresAt) - Date.now() < OAUTH_EXPIRY_SKEW_MS && nonEmptyString(raw.refreshToken) && this.refresh) {
|
|
245
|
-
raw = await this.refresh(raw);
|
|
246
|
-
await writeFile(this.oauthFile, `${JSON.stringify(raw, null, 2)}\n`);
|
|
247
|
-
}
|
|
248
|
-
if (!isRecord(raw)) throw new ClaudeCodeAuthError("auth_invalid", `Invalid OAuth file: ${this.oauthFile}`);
|
|
249
|
-
const accessToken = nonEmptyString(raw.accessToken) ?? nonEmptyString(raw.access_token);
|
|
250
|
-
if (!accessToken) throw new ClaudeCodeAuthError("auth_missing", `No accessToken in ${this.oauthFile}`);
|
|
251
|
-
return {
|
|
252
|
-
accessToken,
|
|
253
|
-
source: "file",
|
|
254
|
-
subscriptionType: nonEmptyString(raw.subscriptionType) ?? null,
|
|
255
|
-
rateLimitTier: nonEmptyString(raw.rateLimitTier) ?? null
|
|
256
|
-
};
|
|
257
|
-
} catch (error) {
|
|
258
|
-
if (error instanceof ClaudeCodeAuthError) throw error;
|
|
259
|
-
throw new ClaudeCodeAuthError("auth_missing", `Failed to read Claude OAuth file ${this.oauthFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
260
|
-
}
|
|
261
|
-
const fromEnv = nonEmptyString(process$1.env.CLAUDE_CODE_OAUTH_TOKEN);
|
|
262
|
-
if (fromEnv) return {
|
|
263
|
-
accessToken: fromEnv,
|
|
264
|
-
source: "env"
|
|
265
|
-
};
|
|
266
|
-
if (process$1.platform === "darwin") try {
|
|
267
|
-
const { stdout } = await execFileAsync("security", [
|
|
268
|
-
"find-generic-password",
|
|
269
|
-
"-s",
|
|
270
|
-
"Claude Code-credentials",
|
|
271
|
-
"-w"
|
|
272
|
-
], {
|
|
273
|
-
encoding: "utf8",
|
|
274
|
-
timeout: 5e3
|
|
275
|
-
});
|
|
276
|
-
const parsed = JSON.parse(stdout.trim());
|
|
277
|
-
if (!isRecord(parsed)) throw new ClaudeCodeAuthError("auth_missing", "Claude Code keychain item is not a JSON object");
|
|
278
|
-
const oauth = isRecord(parsed.claudeAiOauth) ? parsed.claudeAiOauth : null;
|
|
279
|
-
if (!oauth) throw new ClaudeCodeAuthError("auth_missing", "Claude Code keychain missing claudeAiOauth");
|
|
280
|
-
const accessToken = nonEmptyString(oauth.accessToken);
|
|
281
|
-
if (!accessToken) throw new ClaudeCodeAuthError("auth_missing", "Claude Code keychain missing accessToken");
|
|
282
|
-
return {
|
|
283
|
-
accessToken,
|
|
284
|
-
source: "keychain",
|
|
285
|
-
subscriptionType: nonEmptyString(oauth.subscriptionType) ?? null,
|
|
286
|
-
rateLimitTier: nonEmptyString(oauth.rateLimitTier) ?? null
|
|
287
|
-
};
|
|
288
|
-
} catch (error) {
|
|
289
|
-
if (error instanceof ClaudeCodeAuthError) throw error;
|
|
290
|
-
}
|
|
291
|
-
throw new ClaudeCodeAuthError("auth_missing", "Claude Code OAuth access token not found (set CLAUDE_CODE_OAUTH_TOKEN or log in with Claude Code)");
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
var StaticClaudeCodeAuthStore = class {
|
|
295
|
-
access;
|
|
296
|
-
constructor(access) {
|
|
297
|
-
this.access = access;
|
|
298
|
-
}
|
|
299
|
-
async status() {
|
|
300
|
-
return {
|
|
301
|
-
status: "authenticated",
|
|
302
|
-
accountLabel: nonEmptyString(this.access.subscriptionType) ?? "Claude Code"
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
async resolveAccess() {
|
|
306
|
-
return this.access;
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
var ClaudeCodeAuthError = class extends Error {
|
|
310
|
-
code;
|
|
311
|
-
constructor(code, message) {
|
|
312
|
-
super(message);
|
|
313
|
-
this.code = code;
|
|
314
|
-
this.name = "ClaudeCodeAuthError";
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
//#endregion
|
|
318
|
-
//#region src/login.ts
|
|
319
|
-
const CLAUDE_AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
320
|
-
const CLAUDE_CONSOLE_BASE = "https://console.anthropic.com";
|
|
321
|
-
const CLAUDE_CODE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
322
|
-
const CLAUDE_LOGIN_SCOPE = "org:create_api_key user:profile user:inference";
|
|
323
|
-
function tokenEndpoint(consoleBase) {
|
|
324
|
-
return `${consoleBase.replace(/\/+$/, "")}/v1/oauth/token`;
|
|
325
|
-
}
|
|
326
|
-
async function requestTokens(fetchImpl, consoleBase, body, signal) {
|
|
327
|
-
const response = await fetchImpl(tokenEndpoint(consoleBase), {
|
|
328
|
-
method: "POST",
|
|
329
|
-
headers: { "content-type": "application/json" },
|
|
330
|
-
body: JSON.stringify(body),
|
|
331
|
-
signal
|
|
332
|
-
});
|
|
333
|
-
if (!response.ok) throw new ClaudeCodeAuthError("auth_invalid", `Claude OAuth token request failed with HTTP ${response.status}`);
|
|
334
|
-
const parsed = await response.json().catch(() => null);
|
|
335
|
-
if (!isRecord(parsed)) throw new ClaudeCodeAuthError("auth_invalid", "Claude OAuth token response is not a JSON object");
|
|
336
|
-
const accessToken = nonEmptyString(parsed.access_token);
|
|
337
|
-
if (!accessToken) throw new ClaudeCodeAuthError("auth_invalid", "Claude OAuth token response is missing access_token");
|
|
338
|
-
const expiresIn = Number(parsed.expires_in);
|
|
339
|
-
const account = isRecord(parsed.account) ? parsed.account : {};
|
|
340
|
-
return {
|
|
341
|
-
accessToken,
|
|
342
|
-
refreshToken: nonEmptyString(parsed.refresh_token) ?? null,
|
|
343
|
-
expiresAt: Number.isFinite(expiresIn) && expiresIn > 0 ? new Date(Date.now() + expiresIn * 1e3).toISOString() : null,
|
|
344
|
-
scopes: typeof parsed.scope === "string" ? parsed.scope.split(" ").filter(Boolean) : null,
|
|
345
|
-
subscriptionType: nonEmptyString(parsed.subscription_type) ?? nonEmptyString(account.subscription_type) ?? null,
|
|
346
|
-
...nonEmptyString(account.email_address) ? { emailAddress: nonEmptyString(account.email_address) } : {}
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
/** Runs the copy-back OAuth flow and returns a refreshable pool secret. */
|
|
350
|
-
async function runClaudeCodeLogin(options) {
|
|
351
|
-
const fetchImpl = options.fetch ?? fetch;
|
|
352
|
-
const consoleBase = options.consoleBase ?? CLAUDE_CONSOLE_BASE;
|
|
353
|
-
const verifier = randomBytes(32).toString("base64url");
|
|
354
|
-
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
355
|
-
const state = randomBytes(32).toString("base64url");
|
|
356
|
-
const redirectUri = `${consoleBase.replace(/\/+$/, "")}/oauth/code/callback`;
|
|
357
|
-
const params = new URLSearchParams({
|
|
358
|
-
code: "true",
|
|
359
|
-
client_id: CLAUDE_CODE_CLIENT_ID,
|
|
360
|
-
response_type: "code",
|
|
361
|
-
redirect_uri: redirectUri,
|
|
362
|
-
scope: CLAUDE_LOGIN_SCOPE,
|
|
363
|
-
code_challenge: challenge,
|
|
364
|
-
code_challenge_method: "S256",
|
|
365
|
-
state
|
|
366
|
-
});
|
|
367
|
-
options.onPending?.({
|
|
368
|
-
verificationUrl: `${CLAUDE_AUTHORIZE_URL}?${params.toString()}`,
|
|
369
|
-
requiresCodeInput: true
|
|
370
|
-
});
|
|
371
|
-
const pasted = (await options.promptForCode()).trim();
|
|
372
|
-
if (!pasted) throw new ClaudeCodeAuthError("auth_invalid", "Empty authorization code");
|
|
373
|
-
const [code, returnedState] = pasted.split("#");
|
|
374
|
-
if (!nonEmptyString(code)) throw new ClaudeCodeAuthError("auth_invalid", "Authorization code is missing the code part");
|
|
375
|
-
if (returnedState && returnedState !== state) throw new ClaudeCodeAuthError("auth_invalid", "Authorization code state mismatch — copy the full string from the callback page");
|
|
376
|
-
return requestTokens(fetchImpl, consoleBase, {
|
|
377
|
-
grant_type: "authorization_code",
|
|
378
|
-
code,
|
|
379
|
-
state: returnedState ?? state,
|
|
380
|
-
client_id: CLAUDE_CODE_CLIENT_ID,
|
|
381
|
-
redirect_uri: redirectUri,
|
|
382
|
-
code_verifier: verifier
|
|
383
|
-
}, options.signal);
|
|
384
|
-
}
|
|
385
|
-
/** Refreshes a pool secret in place; returns the renewed secret. */
|
|
386
|
-
async function refreshClaudeCodeSecret(secret, options = {}) {
|
|
387
|
-
const refreshToken = nonEmptyString(secret.refreshToken);
|
|
388
|
-
if (!refreshToken) throw new ClaudeCodeAuthError("auth_missing", "Claude OAuth secret has no refreshToken to renew with");
|
|
389
|
-
const renewed = await requestTokens(options.fetch ?? fetch, options.consoleBase ?? CLAUDE_CONSOLE_BASE, {
|
|
390
|
-
grant_type: "refresh_token",
|
|
391
|
-
refresh_token: refreshToken,
|
|
392
|
-
client_id: CLAUDE_CODE_CLIENT_ID
|
|
393
|
-
}, options.signal);
|
|
394
|
-
return {
|
|
395
|
-
...secret,
|
|
396
|
-
accessToken: renewed.accessToken,
|
|
397
|
-
refreshToken: renewed.refreshToken ?? refreshToken,
|
|
398
|
-
expiresAt: renewed.expiresAt ?? secret.expiresAt ?? null,
|
|
399
|
-
scopes: renewed.scopes ?? secret.scopes ?? null,
|
|
400
|
-
subscriptionType: renewed.subscriptionType ?? secret.subscriptionType ?? null,
|
|
401
|
-
...nonEmptyString(renewed.emailAddress) ? { emailAddress: renewed.emailAddress } : {}
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
//#endregion
|
|
405
|
-
//#region src/credentials.ts
|
|
406
|
-
var PoolAwareClaudeCodeAuthStore = class {
|
|
407
|
-
pool;
|
|
408
|
-
constructor(pool) {
|
|
409
|
-
this.pool = pool;
|
|
410
|
-
}
|
|
411
|
-
async status() {
|
|
412
|
-
return this.currentStore().then((s) => s.status());
|
|
413
|
-
}
|
|
414
|
-
async resolveAccess() {
|
|
415
|
-
return this.currentStore().then((s) => s.resolveAccess());
|
|
416
|
-
}
|
|
417
|
-
async currentStore() {
|
|
418
|
-
await this.pool.ensureActivePointer();
|
|
419
|
-
const activeId = await this.pool.getActiveId();
|
|
420
|
-
if (activeId) return new FileClaudeCodeAuthStore({
|
|
421
|
-
oauthFile: this.pool.secretPath(activeId),
|
|
422
|
-
refresh: (secret) => refreshClaudeCodeSecret(secret)
|
|
423
|
-
});
|
|
424
|
-
return new FileClaudeCodeAuthStore();
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
function openClaudeCodeCredentialPool(options = {}) {
|
|
428
|
-
return new FileCredentialPool({
|
|
429
|
-
stateDir: options.stateDir,
|
|
430
|
-
providerKey: "claude-code",
|
|
431
|
-
secretFileName: "oauth.json"
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
function createClaudeCodeCredentials(pool, authStore, options = {}) {
|
|
435
|
-
const capability = () => ({
|
|
436
|
-
mode: "supported",
|
|
437
|
-
canBeginLogin: true,
|
|
438
|
-
canImportDefault: true,
|
|
439
|
-
canAdd: true,
|
|
440
|
-
multi: true
|
|
441
|
-
});
|
|
442
|
-
const getActive = async () => {
|
|
443
|
-
await pool.ensureActivePointer();
|
|
444
|
-
return {
|
|
445
|
-
credentialId: await pool.getActiveId(),
|
|
446
|
-
status: await authStore.status()
|
|
447
|
-
};
|
|
448
|
-
};
|
|
449
|
-
const setActive = async (credentialId) => {
|
|
450
|
-
await pool.setActiveId(credentialId);
|
|
451
|
-
options.quota?.clearLatest?.();
|
|
452
|
-
options.onActiveChange?.();
|
|
453
|
-
return getActive();
|
|
454
|
-
};
|
|
455
|
-
const importAccess = async (access, source) => {
|
|
456
|
-
const token = nonEmptyString(access.accessToken);
|
|
457
|
-
if (!token) throw new ClaudeCodeAuthError("auth_missing", "No Claude access token to import");
|
|
458
|
-
const identityKey = nonEmptyString(access.subscriptionType) != null ? `token:${createHash("sha256").update(token).digest("hex").slice(0, 16)}:${access.subscriptionType}` : `token:${createHash("sha256").update(token).digest("hex").slice(0, 16)}`;
|
|
459
|
-
const label = nonEmptyString(access.subscriptionType) ?? `claude-${identityKey.slice(-8)}`;
|
|
460
|
-
const id = (await pool.findByIdentityKey(identityKey))?.id ?? credentialIdFromIdentity(identityKey, label);
|
|
461
|
-
const meta = {
|
|
462
|
-
id,
|
|
463
|
-
label,
|
|
464
|
-
detail: nonEmptyString(access.rateLimitTier) ?? null,
|
|
465
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
466
|
-
source,
|
|
467
|
-
identityKey
|
|
468
|
-
};
|
|
469
|
-
const secret = {
|
|
470
|
-
accessToken: token,
|
|
471
|
-
subscriptionType: access.subscriptionType ?? null,
|
|
472
|
-
rateLimitTier: access.rateLimitTier ?? null
|
|
473
|
-
};
|
|
474
|
-
await pool.writeEntry(meta, `${JSON.stringify(secret, null, 2)}\n`);
|
|
475
|
-
if (!await pool.getActiveId()) await pool.setActiveId(id);
|
|
476
|
-
options.quota?.clearLatest?.();
|
|
477
|
-
options.onActiveChange?.();
|
|
478
|
-
return {
|
|
479
|
-
id: meta.id,
|
|
480
|
-
label: meta.label,
|
|
481
|
-
detail: meta.detail,
|
|
482
|
-
updatedAt: meta.updatedAt
|
|
483
|
-
};
|
|
484
|
-
};
|
|
485
|
-
const importSecret = async (secret, source) => {
|
|
486
|
-
const email = nonEmptyString(secret.emailAddress);
|
|
487
|
-
const identityKey = email ? `email:${email}` : `token:${createHash("sha256").update(secret.accessToken).digest("hex").slice(0, 16)}`;
|
|
488
|
-
const label = email ?? nonEmptyString(secret.subscriptionType) ?? `claude-${identityKey.slice(-8)}`;
|
|
489
|
-
const id = (await pool.findByIdentityKey(identityKey))?.id ?? credentialIdFromIdentity(identityKey, label);
|
|
490
|
-
const meta = {
|
|
491
|
-
id,
|
|
492
|
-
label,
|
|
493
|
-
detail: nonEmptyString(secret.subscriptionType) ?? nonEmptyString(secret.rateLimitTier) ?? null,
|
|
494
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
495
|
-
source,
|
|
496
|
-
identityKey
|
|
497
|
-
};
|
|
498
|
-
await pool.writeEntry(meta, `${JSON.stringify(secret, null, 2)}\n`);
|
|
499
|
-
if (!await pool.getActiveId()) await pool.setActiveId(id);
|
|
500
|
-
options.quota?.clearLatest?.();
|
|
501
|
-
options.onActiveChange?.();
|
|
502
|
-
return {
|
|
503
|
-
id: meta.id,
|
|
504
|
-
label: meta.label,
|
|
505
|
-
detail: meta.detail,
|
|
506
|
-
updatedAt: meta.updatedAt
|
|
507
|
-
};
|
|
508
|
-
};
|
|
509
|
-
return {
|
|
510
|
-
capability,
|
|
511
|
-
list: () => pool.list(),
|
|
512
|
-
getActive,
|
|
513
|
-
setActive,
|
|
514
|
-
beginLogin: async (loginOptions) => {
|
|
515
|
-
if (!loginOptions?.promptForCode) return {
|
|
516
|
-
status: "unavailable",
|
|
517
|
-
message: "Claude login requires promptForCode to collect the pasted authorization code"
|
|
518
|
-
};
|
|
519
|
-
try {
|
|
520
|
-
const secret = await runClaudeCodeLogin({
|
|
521
|
-
signal: loginOptions.signal,
|
|
522
|
-
onPending: loginOptions.onPending,
|
|
523
|
-
promptForCode: loginOptions.promptForCode,
|
|
524
|
-
fetch: options.loginFetch
|
|
525
|
-
});
|
|
526
|
-
return {
|
|
527
|
-
status: "completed",
|
|
528
|
-
credentialId: (await importSecret(secret, "login:oauth")).id
|
|
529
|
-
};
|
|
530
|
-
} catch (error) {
|
|
531
|
-
if (loginOptions.signal?.aborted) return { status: "cancelled" };
|
|
532
|
-
return {
|
|
533
|
-
status: "failed",
|
|
534
|
-
message: errorMessage(error)
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
},
|
|
538
|
-
importDefault: async () => {
|
|
539
|
-
const vendor = new FileClaudeCodeAuthStore();
|
|
540
|
-
let access;
|
|
541
|
-
try {
|
|
542
|
-
access = await vendor.resolveAccess();
|
|
543
|
-
} catch {
|
|
544
|
-
throw new ClaudeCodeAuthError("auth_missing", "No Claude Code OAuth to import. Run claude auth login or beginLogin first.");
|
|
545
|
-
}
|
|
546
|
-
return importAccess(access, "vendor:default");
|
|
547
|
-
},
|
|
548
|
-
add: async (input) => {
|
|
549
|
-
if (typeof input.accessToken === "string") return importAccess({
|
|
550
|
-
accessToken: input.accessToken,
|
|
551
|
-
source: "static",
|
|
552
|
-
subscriptionType: typeof input.subscriptionType === "string" ? input.subscriptionType : null,
|
|
553
|
-
rateLimitTier: typeof input.rateLimitTier === "string" ? input.rateLimitTier : null
|
|
554
|
-
}, "add:accessToken");
|
|
555
|
-
if (isRecord(input.oauth) && typeof input.oauth.accessToken === "string") {
|
|
556
|
-
const oauth = input.oauth;
|
|
557
|
-
return importAccess({
|
|
558
|
-
accessToken: oauth.accessToken,
|
|
559
|
-
source: "static",
|
|
560
|
-
subscriptionType: typeof oauth.subscriptionType === "string" ? oauth.subscriptionType : null,
|
|
561
|
-
rateLimitTier: typeof oauth.rateLimitTier === "string" ? oauth.rateLimitTier : null
|
|
562
|
-
}, "add:oauth");
|
|
563
|
-
}
|
|
564
|
-
throw new Error("Claude credentials.add expects accessToken or oauth.accessToken");
|
|
565
|
-
},
|
|
566
|
-
remove: async (credentialId) => {
|
|
567
|
-
await pool.remove(credentialId);
|
|
568
|
-
options.quota?.clearLatest?.();
|
|
569
|
-
options.onActiveChange?.();
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
//#endregion
|
|
574
200
|
//#region src/jsonl.ts
|
|
575
201
|
/**
|
|
576
202
|
* Builds the input messages used to prime a *fresh* Claude CLI process with prior
|
|
@@ -648,7 +274,7 @@ function renderToolUseText(toolName, input) {
|
|
|
648
274
|
return `[Earlier in this conversation I called the tool ${toolName} with input: ${safeJson(input)}.`;
|
|
649
275
|
}
|
|
650
276
|
function renderToolResultText(toolName, item) {
|
|
651
|
-
const body =
|
|
277
|
+
const body = toolResultToText(item.output);
|
|
652
278
|
const suffix = toolName ? ` from ${toolName}` : "";
|
|
653
279
|
return item.isError ? `It returned an error${suffix}: ${body}]` : `It returned${suffix}: ${body}]`;
|
|
654
280
|
}
|
|
@@ -713,10 +339,6 @@ function userContentToClaude(content) {
|
|
|
713
339
|
type: "image",
|
|
714
340
|
source: imageSourceToClaude(block.source)
|
|
715
341
|
};
|
|
716
|
-
if (block.type === "video") return {
|
|
717
|
-
type: "text",
|
|
718
|
-
text: "[video]"
|
|
719
|
-
};
|
|
720
342
|
if (block.type === "document") return documentSourceToClaude(block.source);
|
|
721
343
|
return {
|
|
722
344
|
type: "text",
|
|
@@ -749,30 +371,11 @@ function assistantItemToClaudeContent(item) {
|
|
|
749
371
|
};
|
|
750
372
|
}
|
|
751
373
|
function toolResultToClaudeContent(item) {
|
|
752
|
-
const hasImage = item.output.some((block) => block.type !== "text");
|
|
753
374
|
return {
|
|
754
375
|
type: "tool_result",
|
|
755
376
|
tool_use_id: item.toolUseId,
|
|
756
377
|
is_error: item.isError,
|
|
757
|
-
content:
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
function toolResultBlockToClaude(block) {
|
|
761
|
-
if (block.type === "text") return {
|
|
762
|
-
type: "text",
|
|
763
|
-
text: block.text
|
|
764
|
-
};
|
|
765
|
-
if (block.type === "video") return {
|
|
766
|
-
type: "text",
|
|
767
|
-
text: `[video:${block.source.mediaType}]`
|
|
768
|
-
};
|
|
769
|
-
return {
|
|
770
|
-
type: "image",
|
|
771
|
-
source: {
|
|
772
|
-
type: "base64",
|
|
773
|
-
media_type: block.source.mediaType,
|
|
774
|
-
data: block.source.data
|
|
775
|
-
}
|
|
378
|
+
content: toolResultToText(item.output)
|
|
776
379
|
};
|
|
777
380
|
}
|
|
778
381
|
function imageSourceToClaude(source) {
|
|
@@ -797,6 +400,9 @@ function documentSourceToClaude(source) {
|
|
|
797
400
|
title: source.fileName
|
|
798
401
|
};
|
|
799
402
|
}
|
|
403
|
+
function toolResultToText(output) {
|
|
404
|
+
return output.map((block) => block.type === "text" ? block.text : `[image:${block.source.mediaType}]`).join("\n");
|
|
405
|
+
}
|
|
800
406
|
function bytesToBase64(data) {
|
|
801
407
|
return Buffer$1.from(data.buffer, data.byteOffset, data.byteLength).toString("base64");
|
|
802
408
|
}
|
|
@@ -805,28 +411,6 @@ function toolNameToClaude(name) {
|
|
|
805
411
|
return `mcp__main__${name}`;
|
|
806
412
|
}
|
|
807
413
|
//#endregion
|
|
808
|
-
//#region src/oauth.ts
|
|
809
|
-
/**
|
|
810
|
-
* The token to inject into a spawned CLI as CLAUDE_CODE_OAUTH_TOKEN, or null
|
|
811
|
-
* to let the CLI authenticate itself. Keychain-sourced tokens are never
|
|
812
|
-
* injected — see {@link ClaudeCodeOAuthSource}.
|
|
813
|
-
*/
|
|
814
|
-
function injectableCliToken(access) {
|
|
815
|
-
return access.source === "keychain" ? null : access.accessToken;
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Resolve Claude Code consumer OAuth access for quota APIs.
|
|
819
|
-
* Order: options env CLAUDE_CODE_OAUTH_TOKEN, then macOS Keychain "Claude Code-credentials".
|
|
820
|
-
* Prefer injecting {@link ClaudeCodeAuthStore} when multi-credential is enabled.
|
|
821
|
-
*/
|
|
822
|
-
async function resolveClaudeCodeOAuthAccess() {
|
|
823
|
-
try {
|
|
824
|
-
return await new FileClaudeCodeAuthStore().resolveAccess();
|
|
825
|
-
} catch {
|
|
826
|
-
return null;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
//#endregion
|
|
830
414
|
//#region src/output.ts
|
|
831
415
|
function mapClaudeStdoutMessage(message, options = {}) {
|
|
832
416
|
const events = [];
|
|
@@ -834,7 +418,7 @@ function mapClaudeStdoutMessage(message, options = {}) {
|
|
|
834
418
|
events,
|
|
835
419
|
terminal: false
|
|
836
420
|
};
|
|
837
|
-
if (message.type === "assistant" && isRecord(message.message)) events.push(...mapContentArray(message.message.content, options));
|
|
421
|
+
if (message.type === "assistant" && isRecord(message.message) && !options.ignoreAssistantContent) events.push(...mapContentArray(message.message.content, options));
|
|
838
422
|
if (message.type === "stream_event" && isRecord(message.event)) events.push(...mapStreamEvent(message.event));
|
|
839
423
|
if (message.type === "control_request") {
|
|
840
424
|
const request = parseControlRequest(message);
|
|
@@ -855,7 +439,7 @@ function mapClaudeStdoutMessage(message, options = {}) {
|
|
|
855
439
|
}
|
|
856
440
|
events.push({
|
|
857
441
|
type: "response",
|
|
858
|
-
usage:
|
|
442
|
+
usage: mapUsage(message.usage)
|
|
859
443
|
});
|
|
860
444
|
return {
|
|
861
445
|
events,
|
|
@@ -892,11 +476,11 @@ function mapContentArray(content, options) {
|
|
|
892
476
|
const events = [];
|
|
893
477
|
for (const block of content) {
|
|
894
478
|
if (!isRecord(block)) continue;
|
|
895
|
-
if (block.type === "text"
|
|
479
|
+
if (block.type === "text") events.push({
|
|
896
480
|
type: "text_delta",
|
|
897
481
|
text: String(block.text ?? "")
|
|
898
482
|
});
|
|
899
|
-
else if (block.type === "thinking"
|
|
483
|
+
else if (block.type === "thinking") {
|
|
900
484
|
events.push({ type: "thinking_start" });
|
|
901
485
|
events.push({
|
|
902
486
|
type: "thinking_delta",
|
|
@@ -906,7 +490,7 @@ function mapContentArray(content, options) {
|
|
|
906
490
|
type: "thinking_signature",
|
|
907
491
|
signature: block.signature
|
|
908
492
|
});
|
|
909
|
-
} else if (block.type === "redacted_thinking"
|
|
493
|
+
} else if (block.type === "redacted_thinking") events.push({
|
|
910
494
|
type: "redacted_thinking",
|
|
911
495
|
data: String(block.data ?? "")
|
|
912
496
|
});
|
|
@@ -970,7 +554,6 @@ function parseControlRequest(message) {
|
|
|
970
554
|
outerRequestId: message.request_id,
|
|
971
555
|
serverName: request.server_name,
|
|
972
556
|
id,
|
|
973
|
-
toolUseId: sdkMcpToolUseId(inner.params),
|
|
974
557
|
method,
|
|
975
558
|
params: inner.params
|
|
976
559
|
};
|
|
@@ -985,26 +568,6 @@ function parseControlRequest(message) {
|
|
|
985
568
|
params: message.params
|
|
986
569
|
};
|
|
987
570
|
}
|
|
988
|
-
function sdkMcpToolUseId(params) {
|
|
989
|
-
if (!isRecord(params) || !isRecord(params._meta)) return void 0;
|
|
990
|
-
const value = params._meta["claudecode/toolUseId"];
|
|
991
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* Usage for the response event from a `result` message. The top-level
|
|
995
|
-
* `result.usage` sums every API call the CLI made within the turn (initial call
|
|
996
|
-
* plus one per tool result), so its input/cache counts can exceed the context
|
|
997
|
-
* window itself and must not be reported as request usage. `usage.iterations`
|
|
998
|
-
* lists the per-call usage; the last entry is the final request — the one the
|
|
999
|
-
* response contract requires.
|
|
1000
|
-
*/
|
|
1001
|
-
function mapResultUsage(usage) {
|
|
1002
|
-
if (isRecord(usage) && Array.isArray(usage.iterations)) {
|
|
1003
|
-
const last = usage.iterations[usage.iterations.length - 1];
|
|
1004
|
-
if (isRecord(last)) return mapUsage(last);
|
|
1005
|
-
}
|
|
1006
|
-
return mapUsage(usage);
|
|
1007
|
-
}
|
|
1008
571
|
function mapUsage(usage) {
|
|
1009
572
|
if (!isRecord(usage)) return {
|
|
1010
573
|
inputTokens: 0,
|
|
@@ -1042,6 +605,42 @@ function stripMcpToolPrefix(name) {
|
|
|
1042
605
|
return /^mcp__[^_]+__(.+)$/.exec(name)?.[1] ?? name;
|
|
1043
606
|
}
|
|
1044
607
|
//#endregion
|
|
608
|
+
//#region src/oauth.ts
|
|
609
|
+
const execFileAsync = promisify(execFile);
|
|
610
|
+
/**
|
|
611
|
+
* Resolve Claude Code consumer OAuth access for quota APIs.
|
|
612
|
+
* Order: options env CLAUDE_CODE_OAUTH_TOKEN, then macOS Keychain "Claude Code-credentials".
|
|
613
|
+
*/
|
|
614
|
+
async function resolveClaudeCodeOAuthAccess() {
|
|
615
|
+
const fromEnv = nonEmptyString(process$1.env.CLAUDE_CODE_OAUTH_TOKEN);
|
|
616
|
+
if (fromEnv) return { accessToken: fromEnv };
|
|
617
|
+
if (process$1.platform === "darwin") try {
|
|
618
|
+
const { stdout } = await execFileAsync("security", [
|
|
619
|
+
"find-generic-password",
|
|
620
|
+
"-s",
|
|
621
|
+
"Claude Code-credentials",
|
|
622
|
+
"-w"
|
|
623
|
+
], {
|
|
624
|
+
encoding: "utf8",
|
|
625
|
+
timeout: 5e3
|
|
626
|
+
});
|
|
627
|
+
const parsed = JSON.parse(stdout.trim());
|
|
628
|
+
if (!isRecord(parsed)) return null;
|
|
629
|
+
const oauth = isRecord(parsed.claudeAiOauth) ? parsed.claudeAiOauth : null;
|
|
630
|
+
if (!oauth) return null;
|
|
631
|
+
const accessToken = nonEmptyString(oauth.accessToken);
|
|
632
|
+
if (!accessToken) return null;
|
|
633
|
+
return {
|
|
634
|
+
accessToken,
|
|
635
|
+
subscriptionType: nonEmptyString(oauth.subscriptionType) ?? null,
|
|
636
|
+
rateLimitTier: nonEmptyString(oauth.rateLimitTier) ?? null
|
|
637
|
+
};
|
|
638
|
+
} catch {
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
//#endregion
|
|
1045
644
|
//#region src/quota.ts
|
|
1046
645
|
const DEFAULT_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
1047
646
|
const DEFAULT_OAUTH_BETA = "oauth-2025-04-20";
|
|
@@ -1063,15 +662,14 @@ function createClaudeCodeQuota(options = {}) {
|
|
|
1063
662
|
probe: async ({ signal } = {}) => {
|
|
1064
663
|
const access = await resolveAccess();
|
|
1065
664
|
if (!access?.accessToken) throw new Error("Claude Code OAuth access token not found (set CLAUDE_CODE_OAUTH_TOKEN or log in with Claude Code)");
|
|
1066
|
-
const headers = new Headers({
|
|
1067
|
-
authorization: `Bearer ${access.accessToken}`,
|
|
1068
|
-
"anthropic-beta": DEFAULT_OAUTH_BETA,
|
|
1069
|
-
accept: "application/json",
|
|
1070
|
-
"user-agent": "demi-provider-claude-code"
|
|
1071
|
-
});
|
|
1072
665
|
const response = await fetchImpl(usageUrl, {
|
|
1073
666
|
method: "GET",
|
|
1074
|
-
headers
|
|
667
|
+
headers: new Headers({
|
|
668
|
+
authorization: `Bearer ${access.accessToken}`,
|
|
669
|
+
"anthropic-beta": DEFAULT_OAUTH_BETA,
|
|
670
|
+
accept: "application/json",
|
|
671
|
+
"user-agent": "demi-provider-claude-code"
|
|
672
|
+
}),
|
|
1075
673
|
signal
|
|
1076
674
|
});
|
|
1077
675
|
if (!response.ok) {
|
|
@@ -1148,7 +746,7 @@ function observeClaudeRateLimitHeaders(headers) {
|
|
|
1148
746
|
const claim = headers.get("anthropic-ratelimit-unified-representative-claim");
|
|
1149
747
|
const overageUtil = headers.get("anthropic-ratelimit-unified-overage-period-channel-utilization");
|
|
1150
748
|
if (!status && !reset && !claim && !overageUtil) return null;
|
|
1151
|
-
const usedPercent = clampUsedPercent(
|
|
749
|
+
const usedPercent = clampUsedPercent(overageUtil != null ? Number(overageUtil) : null);
|
|
1152
750
|
return {
|
|
1153
751
|
windows: [{
|
|
1154
752
|
id: "unified",
|
|
@@ -1205,15 +803,13 @@ function buildClaudeArgs(params) {
|
|
|
1205
803
|
if (params.thinkingEffort) args.push("--effort", params.thinkingEffort);
|
|
1206
804
|
return args;
|
|
1207
805
|
}
|
|
1208
|
-
function buildClaudeEnv(base = process.env
|
|
806
|
+
function buildClaudeEnv(base = process.env) {
|
|
1209
807
|
const env = {
|
|
1210
808
|
...base,
|
|
1211
809
|
DISABLE_AUTO_COMPACT: "1",
|
|
1212
810
|
MAX_MCP_OUTPUT_TOKENS: "1000000"
|
|
1213
811
|
};
|
|
1214
812
|
delete env.CLAUDECODE;
|
|
1215
|
-
const token = options.oauthAccessToken?.trim();
|
|
1216
|
-
if (token) env.CLAUDE_CODE_OAUTH_TOKEN = token;
|
|
1217
813
|
return env;
|
|
1218
814
|
}
|
|
1219
815
|
//#endregion
|
|
@@ -1260,19 +856,13 @@ function resolveSpawnCwd(cwd) {
|
|
|
1260
856
|
try {
|
|
1261
857
|
if (statSync(cwd).isDirectory()) return cwd;
|
|
1262
858
|
} catch {}
|
|
1263
|
-
return process
|
|
859
|
+
return process.cwd();
|
|
1264
860
|
}
|
|
1265
861
|
var ClaudeCliTransportFactory = class {
|
|
1266
862
|
claudePath;
|
|
1267
|
-
resolveOAuthAccessToken;
|
|
1268
863
|
constructor(options = {}) {
|
|
1269
|
-
if (typeof options === "string")
|
|
1270
|
-
|
|
1271
|
-
this.resolveOAuthAccessToken = null;
|
|
1272
|
-
} else {
|
|
1273
|
-
this.claudePath = options.claudePath ?? "claude";
|
|
1274
|
-
this.resolveOAuthAccessToken = options.resolveOAuthAccessToken ?? null;
|
|
1275
|
-
}
|
|
864
|
+
if (typeof options === "string") this.claudePath = options;
|
|
865
|
+
else this.claudePath = options.claudePath ?? "claude";
|
|
1276
866
|
}
|
|
1277
867
|
async start(request) {
|
|
1278
868
|
const args = buildClaudeArgsForRequest(request);
|
|
@@ -1284,10 +874,9 @@ var ClaudeCliTransportFactory = class {
|
|
|
1284
874
|
cwd: request.cwd,
|
|
1285
875
|
args
|
|
1286
876
|
});
|
|
1287
|
-
const oauthAccessToken = this.resolveOAuthAccessToken ? await this.resolveOAuthAccessToken() : null;
|
|
1288
877
|
return new ChildProcessClaudeTransport(spawn(this.claudePath, args, {
|
|
1289
878
|
cwd: resolveSpawnCwd(request.cwd),
|
|
1290
|
-
env: buildClaudeEnv(
|
|
879
|
+
env: buildClaudeEnv(),
|
|
1291
880
|
stdio: [
|
|
1292
881
|
"pipe",
|
|
1293
882
|
"pipe",
|
|
@@ -1376,21 +965,10 @@ function thinkingEffort(thinking) {
|
|
|
1376
965
|
var ClaudeCodeProvider = class {
|
|
1377
966
|
transportFactory;
|
|
1378
967
|
quota;
|
|
1379
|
-
getActiveCredentialId;
|
|
1380
968
|
active = null;
|
|
1381
969
|
constructor(options = {}) {
|
|
1382
|
-
this.transportFactory = options.transportFactory ?? new ClaudeCliTransportFactory({
|
|
1383
|
-
claudePath: options.claudePath,
|
|
1384
|
-
resolveOAuthAccessToken: options.authStore ? async () => {
|
|
1385
|
-
try {
|
|
1386
|
-
return injectableCliToken(await options.authStore.resolveAccess());
|
|
1387
|
-
} catch {
|
|
1388
|
-
return null;
|
|
1389
|
-
}
|
|
1390
|
-
} : void 0
|
|
1391
|
-
});
|
|
970
|
+
this.transportFactory = options.transportFactory ?? new ClaudeCliTransportFactory({ claudePath: options.claudePath });
|
|
1392
971
|
this.quota = options.quota ?? null;
|
|
1393
|
-
this.getActiveCredentialId = options.getActiveCredentialId ?? null;
|
|
1394
972
|
}
|
|
1395
973
|
observeQuotaFromMessage(message) {
|
|
1396
974
|
try {
|
|
@@ -1440,50 +1018,21 @@ var ClaudeCodeProvider = class {
|
|
|
1440
1018
|
}
|
|
1441
1019
|
const raw = next.value;
|
|
1442
1020
|
this.observeQuotaFromMessage(raw);
|
|
1443
|
-
const mapped = mapClaudeStdoutMessage(raw, {
|
|
1021
|
+
const mapped = mapClaudeStdoutMessage(raw, {
|
|
1022
|
+
ignoreAssistantContent: active.hasStreamed && isMessageType(raw, "assistant"),
|
|
1023
|
+
ignoreAssistantToolUse: active.sdkMcpEnabled
|
|
1024
|
+
});
|
|
1444
1025
|
if (isMessageType(raw, "stream_event")) active.hasStreamed = true;
|
|
1445
1026
|
if (mapped.controlRequest) {
|
|
1446
|
-
|
|
1447
|
-
if (handled === "tool-call") {
|
|
1027
|
+
if (await this.handleControlRequest(active, mapped.controlRequest, request) === "tool-call") {
|
|
1448
1028
|
const event = active.pendingControlRequest ? controlRequestToToolCall(active.pendingControlRequest) : null;
|
|
1449
1029
|
keepActiveForContinuation = true;
|
|
1450
1030
|
if (event) yield event;
|
|
1451
1031
|
return;
|
|
1452
1032
|
}
|
|
1453
|
-
if (handled === "sdk-tool-call") {
|
|
1454
|
-
const pending = active.pendingSdkControlRequests.get(mapped.controlRequest.toolUseId ?? "");
|
|
1455
|
-
const event = pending ? controlRequestToToolCall(pending) : null;
|
|
1456
|
-
if (event?.type === "tool_call_requested") {
|
|
1457
|
-
active.pendingSdkToolCalls = [event];
|
|
1458
|
-
keepActiveForContinuation = true;
|
|
1459
|
-
yield event;
|
|
1460
|
-
}
|
|
1461
|
-
return;
|
|
1462
|
-
}
|
|
1463
1033
|
continue;
|
|
1464
1034
|
}
|
|
1465
1035
|
const toolUseIds = mapped.events.filter(isToolCallRequested).map((event) => event.toolUseId);
|
|
1466
|
-
if (active.sdkMcpEnabled) {
|
|
1467
|
-
for (const event of mapped.events) {
|
|
1468
|
-
if (event.type === "tool_call_requested") {
|
|
1469
|
-
active.collectingSdkToolCalls.set(event.toolUseId, event);
|
|
1470
|
-
continue;
|
|
1471
|
-
}
|
|
1472
|
-
yield event;
|
|
1473
|
-
}
|
|
1474
|
-
if (isStreamMessageStop(raw) && active.collectingSdkToolCalls.size > 0) {
|
|
1475
|
-
active.pendingSdkToolCalls = [...active.collectingSdkToolCalls.values()];
|
|
1476
|
-
active.collectingSdkToolCalls.clear();
|
|
1477
|
-
keepActiveForContinuation = true;
|
|
1478
|
-
for (const event of active.pendingSdkToolCalls) yield event;
|
|
1479
|
-
return;
|
|
1480
|
-
}
|
|
1481
|
-
if (mapped.terminal) {
|
|
1482
|
-
keepActiveForContinuation = true;
|
|
1483
|
-
return;
|
|
1484
|
-
}
|
|
1485
|
-
continue;
|
|
1486
|
-
}
|
|
1487
1036
|
if (toolUseIds.length > 0) active.pendingToolUseIds = toolUseIds;
|
|
1488
1037
|
for (const event of mapped.events) {
|
|
1489
1038
|
if (event.type === "tool_call_requested") keepActiveForContinuation = true;
|
|
@@ -1526,26 +1075,22 @@ var ClaudeCodeProvider = class {
|
|
|
1526
1075
|
* the session changed, or the transcript was rewritten underneath us (compaction).
|
|
1527
1076
|
*/
|
|
1528
1077
|
async ensureActiveForRequest(request) {
|
|
1529
|
-
const credentialId = this.getActiveCredentialId ? await this.getActiveCredentialId() : null;
|
|
1530
1078
|
const existing = this.active;
|
|
1531
|
-
if (existing && existing.sessionId === request.sessionId && existing.modelId === request.modelId && existing.thinkingSig === thinkingSignature(request)
|
|
1532
|
-
if (existing.pendingControlRequest !== null || existing.
|
|
1079
|
+
if (existing && existing.sessionId === request.sessionId && existing.modelId === request.modelId && existing.thinkingSig === thinkingSignature(request)) {
|
|
1080
|
+
if (existing.pendingControlRequest !== null || existing.pendingToolUseIds.length > 0 || !itemsDiverged(existing, request.items)) {
|
|
1533
1081
|
await this.sendContinuation(existing, request);
|
|
1534
1082
|
return existing;
|
|
1535
1083
|
}
|
|
1536
1084
|
}
|
|
1537
1085
|
if (existing) await this.disposeActive(existing);
|
|
1538
|
-
return this.coldStart(request
|
|
1086
|
+
return this.coldStart(request);
|
|
1539
1087
|
}
|
|
1540
|
-
async coldStart(request
|
|
1088
|
+
async coldStart(request) {
|
|
1541
1089
|
const transport = await this.transportFactory.start(request);
|
|
1542
1090
|
const active = {
|
|
1543
1091
|
transport,
|
|
1544
1092
|
iterator: transport.messages()[Symbol.asyncIterator](),
|
|
1545
1093
|
pendingControlRequest: null,
|
|
1546
|
-
pendingSdkControlRequests: /* @__PURE__ */ new Map(),
|
|
1547
|
-
pendingSdkToolCalls: [],
|
|
1548
|
-
collectingSdkToolCalls: /* @__PURE__ */ new Map(),
|
|
1549
1094
|
pendingToolUseIds: [],
|
|
1550
1095
|
bufferedMessages: [],
|
|
1551
1096
|
sdkMcpEnabled: request.tools.length > 0,
|
|
@@ -1553,7 +1098,6 @@ var ClaudeCodeProvider = class {
|
|
|
1553
1098
|
sessionId: request.sessionId,
|
|
1554
1099
|
modelId: request.modelId,
|
|
1555
1100
|
thinkingSig: thinkingSignature(request),
|
|
1556
|
-
credentialId,
|
|
1557
1101
|
sentUserMessageCount: 0,
|
|
1558
1102
|
firstUserSig: null
|
|
1559
1103
|
};
|
|
@@ -1570,10 +1114,6 @@ var ClaudeCodeProvider = class {
|
|
|
1570
1114
|
await this.writeToolResults(request, active.pendingControlRequest);
|
|
1571
1115
|
active.pendingControlRequest = null;
|
|
1572
1116
|
}
|
|
1573
|
-
if (active.pendingSdkToolCalls.length > 0) {
|
|
1574
|
-
await this.writeSdkMcpToolResults(active, request);
|
|
1575
|
-
active.pendingSdkToolCalls = [];
|
|
1576
|
-
}
|
|
1577
1117
|
if (active.pendingToolUseIds.length > 0) {
|
|
1578
1118
|
await this.writeToolResultMessages(request, active.pendingToolUseIds);
|
|
1579
1119
|
active.pendingToolUseIds = [];
|
|
@@ -1627,15 +1167,6 @@ var ClaudeCodeProvider = class {
|
|
|
1627
1167
|
await this.writeControlError(active, request, "Invalid tools/call request");
|
|
1628
1168
|
return "handled";
|
|
1629
1169
|
}
|
|
1630
|
-
if (request.protocol === "sdk-mcp") {
|
|
1631
|
-
request.toolUseId ??= `mcp-control-${randomUUID()}`;
|
|
1632
|
-
const normalized = {
|
|
1633
|
-
...request,
|
|
1634
|
-
toolUseId: request.toolUseId
|
|
1635
|
-
};
|
|
1636
|
-
active.pendingSdkControlRequests.set(normalized.toolUseId, normalized);
|
|
1637
|
-
return active.hasStreamed ? "handled" : "sdk-tool-call";
|
|
1638
|
-
}
|
|
1639
1170
|
active.pendingControlRequest = {
|
|
1640
1171
|
...request,
|
|
1641
1172
|
toolUseId: `mcp-control-${randomUUID()}`
|
|
@@ -1645,38 +1176,6 @@ var ClaudeCodeProvider = class {
|
|
|
1645
1176
|
await this.writeControlError(active, request, `Unsupported method: ${request.method}`);
|
|
1646
1177
|
return "handled";
|
|
1647
1178
|
}
|
|
1648
|
-
async writeSdkMcpToolResults(active, request) {
|
|
1649
|
-
const results = new Map(request.items.filter((item) => item.type === "tool_result").map((item) => [item.toolUseId, item]));
|
|
1650
|
-
const expected = active.pendingSdkToolCalls.map((event) => event.toolUseId);
|
|
1651
|
-
const missing = expected.filter((toolUseId) => !results.has(toolUseId));
|
|
1652
|
-
if (missing.length > 0) throw new Error(`Claude Code provider missing tool_result for SDK MCP tool_use ${missing.join(", ")}`);
|
|
1653
|
-
const remaining = new Set(expected);
|
|
1654
|
-
while (remaining.size > 0) {
|
|
1655
|
-
let responded = false;
|
|
1656
|
-
for (const toolUseId of remaining) {
|
|
1657
|
-
const controlRequest = active.pendingSdkControlRequests.get(toolUseId);
|
|
1658
|
-
if (!controlRequest) continue;
|
|
1659
|
-
await this.writeToolResults(request, controlRequest);
|
|
1660
|
-
active.pendingSdkControlRequests.delete(toolUseId);
|
|
1661
|
-
remaining.delete(toolUseId);
|
|
1662
|
-
responded = true;
|
|
1663
|
-
}
|
|
1664
|
-
if (remaining.size === 0) return;
|
|
1665
|
-
if (responded) continue;
|
|
1666
|
-
const next = await abortable(active.iterator.next(), request.cancel);
|
|
1667
|
-
if (next.done) throw new Error(`Claude Code exited before requesting SDK MCP tool result for ${[...remaining].join(", ")}`);
|
|
1668
|
-
this.observeQuotaFromMessage(next.value);
|
|
1669
|
-
const mapped = mapClaudeStdoutMessage(next.value, {
|
|
1670
|
-
ignoreAssistantContent: true,
|
|
1671
|
-
ignoreAssistantToolUse: true
|
|
1672
|
-
});
|
|
1673
|
-
if (mapped.controlRequest) {
|
|
1674
|
-
if (await this.handleControlRequest(active, mapped.controlRequest, request) === "tool-call") throw new Error("Claude Code emitted a legacy tool call while awaiting SDK MCP results");
|
|
1675
|
-
continue;
|
|
1676
|
-
}
|
|
1677
|
-
active.bufferedMessages.push(next.value);
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
1179
|
async writeToolResults(request, controlRequest) {
|
|
1681
1180
|
if (!this.active) throw new Error("No active Claude transport");
|
|
1682
1181
|
const expectedToolUseId = controlRequest.toolUseId ?? String(controlRequest.id);
|
|
@@ -1777,32 +1276,19 @@ var ClaudeCodeProvider = class {
|
|
|
1777
1276
|
function createClaudeCodeProvider(options = {}) {
|
|
1778
1277
|
const id = options.id ?? "claude-code";
|
|
1779
1278
|
const displayName = options.displayName ?? "Claude Code";
|
|
1780
|
-
const
|
|
1781
|
-
const pool = !options.authStore && enableCredentials ? openClaudeCodeCredentialPool({ stateDir: options.stateDir }) : null;
|
|
1782
|
-
const authStore = options.authStore ?? (pool ? new PoolAwareClaudeCodeAuthStore(pool) : new FileClaudeCodeAuthStore());
|
|
1783
|
-
const quota = createClaudeCodeQuota({
|
|
1784
|
-
providerId: id,
|
|
1785
|
-
resolveAccess: async () => {
|
|
1786
|
-
try {
|
|
1787
|
-
return await authStore.resolveAccess();
|
|
1788
|
-
} catch {
|
|
1789
|
-
return null;
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
});
|
|
1793
|
-
const credentialsApi = pool ? createClaudeCodeCredentials(pool, authStore, { quota }) : void 0;
|
|
1279
|
+
const quota = createClaudeCodeQuota({ providerId: id });
|
|
1794
1280
|
const runtimeOptions = {
|
|
1795
1281
|
claudePath: options.claudePath,
|
|
1796
|
-
quota
|
|
1797
|
-
authStore,
|
|
1798
|
-
getActiveCredentialId: pool ? () => pool.getActiveId() : void 0
|
|
1282
|
+
quota
|
|
1799
1283
|
};
|
|
1800
1284
|
return defineProvider({
|
|
1801
1285
|
id,
|
|
1802
1286
|
displayName,
|
|
1803
|
-
auth: { status: () =>
|
|
1287
|
+
auth: { status: () => ({
|
|
1288
|
+
status: "unknown",
|
|
1289
|
+
message: "Auth is checked when a Claude Code request runs"
|
|
1290
|
+
}) },
|
|
1804
1291
|
quota,
|
|
1805
|
-
...credentialsApi ? { credentials: credentialsApi } : {},
|
|
1806
1292
|
state: () => ({
|
|
1807
1293
|
status: "unknown",
|
|
1808
1294
|
message: "Runtime is checked when a Claude Code request runs"
|
|
@@ -1820,19 +1306,18 @@ function isControlResponseFor(value, requestId) {
|
|
|
1820
1306
|
if (!isRecord(value) || value.type !== "control_response" || !isRecord(value.response)) return false;
|
|
1821
1307
|
return value.response.request_id === requestId && value.response.subtype === "success";
|
|
1822
1308
|
}
|
|
1309
|
+
function toolResultContentToText(output) {
|
|
1310
|
+
return output.map((block) => block.type === "text" ? block.text : `[image:${block.source.mediaType}]`).join("\n");
|
|
1311
|
+
}
|
|
1823
1312
|
function toolResultContentToMcp(output) {
|
|
1824
1313
|
return output.map((block) => {
|
|
1825
1314
|
if (block.type === "text") return {
|
|
1826
1315
|
type: "text",
|
|
1827
1316
|
text: block.text
|
|
1828
1317
|
};
|
|
1829
|
-
if (block.type === "video") return {
|
|
1830
|
-
type: "text",
|
|
1831
|
-
text: `[video:${block.source.mediaType}]`
|
|
1832
|
-
};
|
|
1833
1318
|
return {
|
|
1834
1319
|
type: "image",
|
|
1835
|
-
data: block.source.data,
|
|
1320
|
+
data: Buffer.from(block.source.data).toString("base64"),
|
|
1836
1321
|
mimeType: block.source.mediaType
|
|
1837
1322
|
};
|
|
1838
1323
|
});
|
|
@@ -1840,9 +1325,6 @@ function toolResultContentToMcp(output) {
|
|
|
1840
1325
|
function isMessageType(value, type) {
|
|
1841
1326
|
return isRecord(value) && value.type === type;
|
|
1842
1327
|
}
|
|
1843
|
-
function isStreamMessageStop(value) {
|
|
1844
|
-
return isRecord(value) && value.type === "stream_event" && isRecord(value.event) && value.event.type === "message_stop";
|
|
1845
|
-
}
|
|
1846
1328
|
function thinkingSignature(request) {
|
|
1847
1329
|
return JSON.stringify(request.thinking ?? null);
|
|
1848
1330
|
}
|
|
@@ -1867,4 +1349,4 @@ function itemsDiverged(active, items) {
|
|
|
1867
1349
|
return false;
|
|
1868
1350
|
}
|
|
1869
1351
|
//#endregion
|
|
1870
|
-
export {
|
|
1352
|
+
export { createClaudeCodeProvider, createClaudeCodeQuota, listClaudeCodeModels, mapClaudeUsagePayload, observeClaudeRateLimitHeaders, observeClaudeStreamBody, resolveClaudeCodeOAuthAccess, resolveWireLogDir };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@demicodes/provider-claude-code",
|
|
3
3
|
"description": "Claude Code provider adapter for Demi.",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.3",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@demicodes/core": "^0.10.
|
|
15
|
-
"@demicodes/provider": "^0.10.
|
|
16
|
-
"@demicodes/utils": "^0.10.
|
|
14
|
+
"@demicodes/core": "^0.10.3",
|
|
15
|
+
"@demicodes/provider": "^0.10.3",
|
|
16
|
+
"@demicodes/utils": "^0.10.3"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@demicodes/agent": "^0.10.
|
|
20
|
-
"@demicodes/shell": "^0.10.
|
|
19
|
+
"@demicodes/agent": "^0.10.3",
|
|
20
|
+
"@demicodes/shell": "^0.10.3"
|
|
21
21
|
},
|
|
22
22
|
"license": "Apache-2.0",
|
|
23
23
|
"main": "./dist/index.mjs",
|