@demicodes/provider-claude-code 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -11,8 +11,17 @@ type ModelCatalogFetch = (input: string | URL | Request, init?: RequestInit) =>
11
11
  declare function listClaudeCodeModels(options?: ClaudeCodeModelCatalogOptions): Promise<ProviderModelList>;
12
12
  //#endregion
13
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';
14
22
  interface ClaudeCodeOAuthAccess {
15
23
  accessToken: string;
24
+ source: ClaudeCodeOAuthSource;
16
25
  subscriptionType?: string | null;
17
26
  rateLimitTier?: string | null;
18
27
  }
@@ -30,11 +39,15 @@ interface ClaudeCodeAuthStore {
30
39
  forceRefresh?: boolean;
31
40
  }): Promise<ClaudeCodeOAuthAccess>;
32
41
  }
42
+ /** Renews a pool oauth secret (wired to `refreshClaudeCodeSecret`; injectable in tests). */
43
+ type ClaudeCodeSecretRefresh = (secret: Record<string, unknown>) => Promise<Record<string, unknown>>;
33
44
  interface FileClaudeCodeAuthStoreOptions {
34
45
  /** Optional path to oauth.json (pool entry). */
35
46
  oauthFile?: string;
36
47
  /** Prefer this token over env/keychain when set (tests / static). */
37
48
  accessToken?: string | null;
49
+ /** Renews the oauth file when its access token nears expiry. */
50
+ refresh?: ClaudeCodeSecretRefresh;
38
51
  }
39
52
  /**
40
53
  * Resolves Claude OAuth: explicit token → oauth file → CLAUDE_CODE_OAUTH_TOKEN → keychain.
@@ -42,6 +55,7 @@ interface FileClaudeCodeAuthStoreOptions {
42
55
  declare class FileClaudeCodeAuthStore implements ClaudeCodeAuthStore {
43
56
  private readonly oauthFile;
44
57
  private readonly accessToken;
58
+ private readonly refresh;
45
59
  constructor(options?: FileClaudeCodeAuthStoreOptions);
46
60
  status(): Promise<ProviderAuthState>;
47
61
  resolveAccess(): Promise<ClaudeCodeOAuthAccess>;
@@ -106,10 +120,44 @@ declare function openClaudeCodeCredentialPool(options?: {
106
120
  stateDir?: string;
107
121
  }): FileCredentialPool;
108
122
  declare function createClaudeCodeCredentials(pool: FileCredentialPool, authStore: ClaudeCodeAuthStore, options?: {
109
- loginCommand?: string;
110
- loginArgs?: string[];
111
123
  quota?: ProviderQuota | null;
112
124
  onActiveChange?: () => void;
125
+ /** Injectable fetch for the OAuth login flow (tests). */
126
+ loginFetch?: typeof fetch;
113
127
  }): ProviderCredentials;
114
128
  //#endregion
115
- export { type ClaudeCodeAuthStore, type ClaudeCodeModelCatalogOptions, type ClaudeCodeOAuthAccess, type ClaudeCodeProviderOptions, type ClaudeCodeQuotaOptions, FileClaudeCodeAuthStore, PoolAwareClaudeCodeAuthStore, StaticClaudeCodeAuthStore, createClaudeCodeCredentials, createClaudeCodeProvider, createClaudeCodeQuota, listClaudeCodeModels, mapClaudeUsagePayload, observeClaudeRateLimitHeaders, observeClaudeStreamBody, openClaudeCodeCredentialPool, resolveClaudeCodeOAuthAccess, resolveWireLogDir };
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 };
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import { errorMessage, isRecord, nonEmptyString, numberOrNull, stringOrNull } from "@demicodes/utils";
2
- import { createHash, randomUUID } from "node:crypto";
2
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
3
3
  import { applyModelPolicy, clampUsedPercent, createProviderQuota, defineProvider, numberHeader, severityFromUsedPercent, toolResultContentToText, unixSecondsToIso } from "@demicodes/provider";
4
4
  import { execFile, spawn } from "node:child_process";
5
- import { readFile } from "node:fs/promises";
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, runVendorLoginCommand } from "@demicodes/provider/credentials-pool";
8
+ import { FileCredentialPool, credentialIdFromIdentity } from "@demicodes/provider/credentials-pool";
9
9
  import { Buffer as Buffer$1 } from "node:buffer";
10
10
  import { appendFileSync, mkdirSync, statSync } from "node:fs";
11
11
  import { createInterface } from "node:readline";
@@ -202,15 +202,18 @@ function reasoningEfforts(value) {
202
202
  //#endregion
203
203
  //#region src/auth.ts
204
204
  const execFileAsync = promisify(execFile);
205
+ const OAUTH_EXPIRY_SKEW_MS = 300 * 1e3;
205
206
  /**
206
207
  * Resolves Claude OAuth: explicit token → oauth file → CLAUDE_CODE_OAUTH_TOKEN → keychain.
207
208
  */
208
209
  var FileClaudeCodeAuthStore = class {
209
210
  oauthFile;
210
211
  accessToken;
212
+ refresh;
211
213
  constructor(options = {}) {
212
214
  this.oauthFile = options.oauthFile ?? null;
213
215
  this.accessToken = nonEmptyString(options.accessToken) ?? null;
216
+ this.refresh = options.refresh ?? null;
214
217
  }
215
218
  async status() {
216
219
  try {
@@ -230,14 +233,24 @@ var FileClaudeCodeAuthStore = class {
230
233
  }
231
234
  }
232
235
  async resolveAccess() {
233
- if (this.accessToken) return { accessToken: this.accessToken };
236
+ if (this.accessToken) return {
237
+ accessToken: this.accessToken,
238
+ source: "static"
239
+ };
234
240
  if (this.oauthFile) try {
235
- const raw = JSON.parse(await readFile(this.oauthFile, "utf8"));
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
+ }
236
248
  if (!isRecord(raw)) throw new ClaudeCodeAuthError("auth_invalid", `Invalid OAuth file: ${this.oauthFile}`);
237
249
  const accessToken = nonEmptyString(raw.accessToken) ?? nonEmptyString(raw.access_token);
238
250
  if (!accessToken) throw new ClaudeCodeAuthError("auth_missing", `No accessToken in ${this.oauthFile}`);
239
251
  return {
240
252
  accessToken,
253
+ source: "file",
241
254
  subscriptionType: nonEmptyString(raw.subscriptionType) ?? null,
242
255
  rateLimitTier: nonEmptyString(raw.rateLimitTier) ?? null
243
256
  };
@@ -246,7 +259,10 @@ var FileClaudeCodeAuthStore = class {
246
259
  throw new ClaudeCodeAuthError("auth_missing", `Failed to read Claude OAuth file ${this.oauthFile}: ${error instanceof Error ? error.message : String(error)}`);
247
260
  }
248
261
  const fromEnv = nonEmptyString(process$1.env.CLAUDE_CODE_OAUTH_TOKEN);
249
- if (fromEnv) return { accessToken: fromEnv };
262
+ if (fromEnv) return {
263
+ accessToken: fromEnv,
264
+ source: "env"
265
+ };
250
266
  if (process$1.platform === "darwin") try {
251
267
  const { stdout } = await execFileAsync("security", [
252
268
  "find-generic-password",
@@ -265,6 +281,7 @@ var FileClaudeCodeAuthStore = class {
265
281
  if (!accessToken) throw new ClaudeCodeAuthError("auth_missing", "Claude Code keychain missing accessToken");
266
282
  return {
267
283
  accessToken,
284
+ source: "keychain",
268
285
  subscriptionType: nonEmptyString(oauth.subscriptionType) ?? null,
269
286
  rateLimitTier: nonEmptyString(oauth.rateLimitTier) ?? null
270
287
  };
@@ -298,6 +315,93 @@ var ClaudeCodeAuthError = class extends Error {
298
315
  }
299
316
  };
300
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
301
405
  //#region src/credentials.ts
302
406
  var PoolAwareClaudeCodeAuthStore = class {
303
407
  pool;
@@ -313,7 +417,10 @@ var PoolAwareClaudeCodeAuthStore = class {
313
417
  async currentStore() {
314
418
  await this.pool.ensureActivePointer();
315
419
  const activeId = await this.pool.getActiveId();
316
- if (activeId) return new FileClaudeCodeAuthStore({ oauthFile: this.pool.secretPath(activeId) });
420
+ if (activeId) return new FileClaudeCodeAuthStore({
421
+ oauthFile: this.pool.secretPath(activeId),
422
+ refresh: (secret) => refreshClaudeCodeSecret(secret)
423
+ });
317
424
  return new FileClaudeCodeAuthStore();
318
425
  }
319
426
  };
@@ -325,8 +432,6 @@ function openClaudeCodeCredentialPool(options = {}) {
325
432
  });
326
433
  }
327
434
  function createClaudeCodeCredentials(pool, authStore, options = {}) {
328
- const loginCommand = options.loginCommand ?? "claude";
329
- const loginArgs = options.loginArgs ?? ["auth", "login"];
330
435
  const capability = () => ({
331
436
  mode: "supported",
332
437
  canBeginLogin: true,
@@ -377,23 +482,58 @@ function createClaudeCodeCredentials(pool, authStore, options = {}) {
377
482
  updatedAt: meta.updatedAt
378
483
  };
379
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
+ };
380
509
  return {
381
510
  capability,
382
511
  list: () => pool.list(),
383
512
  getActive,
384
513
  setActive,
385
514
  beginLogin: async (loginOptions) => {
386
- const result = await runVendorLoginCommand(loginCommand, loginArgs, { signal: loginOptions?.signal });
387
- if (result.status === "completed") return { status: "completed" };
388
- if (result.status === "cancelled") return { status: "cancelled" };
389
- if (result.status === "unavailable") return {
515
+ if (!loginOptions?.promptForCode) return {
390
516
  status: "unavailable",
391
- message: result.message ?? "Login unavailable"
392
- };
393
- return {
394
- status: "failed",
395
- message: result.message ?? "Login failed"
517
+ message: "Claude login requires promptForCode to collect the pasted authorization code"
396
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
+ }
397
537
  },
398
538
  importDefault: async () => {
399
539
  const vendor = new FileClaudeCodeAuthStore();
@@ -408,6 +548,7 @@ function createClaudeCodeCredentials(pool, authStore, options = {}) {
408
548
  add: async (input) => {
409
549
  if (typeof input.accessToken === "string") return importAccess({
410
550
  accessToken: input.accessToken,
551
+ source: "static",
411
552
  subscriptionType: typeof input.subscriptionType === "string" ? input.subscriptionType : null,
412
553
  rateLimitTier: typeof input.rateLimitTier === "string" ? input.rateLimitTier : null
413
554
  }, "add:accessToken");
@@ -415,6 +556,7 @@ function createClaudeCodeCredentials(pool, authStore, options = {}) {
415
556
  const oauth = input.oauth;
416
557
  return importAccess({
417
558
  accessToken: oauth.accessToken,
559
+ source: "static",
418
560
  subscriptionType: typeof oauth.subscriptionType === "string" ? oauth.subscriptionType : null,
419
561
  rateLimitTier: typeof oauth.rateLimitTier === "string" ? oauth.rateLimitTier : null
420
562
  }, "add:oauth");
@@ -663,6 +805,28 @@ function toolNameToClaude(name) {
663
805
  return `mcp__main__${name}`;
664
806
  }
665
807
  //#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
666
830
  //#region src/output.ts
667
831
  function mapClaudeStdoutMessage(message, options = {}) {
668
832
  const events = [];
@@ -872,20 +1036,6 @@ function stripMcpToolPrefix(name) {
872
1036
  return /^mcp__[^_]+__(.+)$/.exec(name)?.[1] ?? name;
873
1037
  }
874
1038
  //#endregion
875
- //#region src/oauth.ts
876
- /**
877
- * Resolve Claude Code consumer OAuth access for quota APIs.
878
- * Order: options env CLAUDE_CODE_OAUTH_TOKEN, then macOS Keychain "Claude Code-credentials".
879
- * Prefer injecting {@link ClaudeCodeAuthStore} when multi-credential is enabled.
880
- */
881
- async function resolveClaudeCodeOAuthAccess() {
882
- try {
883
- return await new FileClaudeCodeAuthStore().resolveAccess();
884
- } catch {
885
- return null;
886
- }
887
- }
888
- //#endregion
889
1039
  //#region src/quota.ts
890
1040
  const DEFAULT_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
891
1041
  const DEFAULT_OAUTH_BETA = "oauth-2025-04-20";
@@ -1227,7 +1377,7 @@ var ClaudeCodeProvider = class {
1227
1377
  claudePath: options.claudePath,
1228
1378
  resolveOAuthAccessToken: options.authStore ? async () => {
1229
1379
  try {
1230
- return (await options.authStore.resolveAccess()).accessToken;
1380
+ return injectableCliToken(await options.authStore.resolveAccess());
1231
1381
  } catch {
1232
1382
  return null;
1233
1383
  }
@@ -1631,4 +1781,4 @@ function itemsDiverged(active, items) {
1631
1781
  return false;
1632
1782
  }
1633
1783
  //#endregion
1634
- export { FileClaudeCodeAuthStore, PoolAwareClaudeCodeAuthStore, StaticClaudeCodeAuthStore, createClaudeCodeCredentials, createClaudeCodeProvider, createClaudeCodeQuota, listClaudeCodeModels, mapClaudeUsagePayload, observeClaudeRateLimitHeaders, observeClaudeStreamBody, openClaudeCodeCredentialPool, resolveClaudeCodeOAuthAccess, resolveWireLogDir };
1784
+ export { FileClaudeCodeAuthStore, PoolAwareClaudeCodeAuthStore, StaticClaudeCodeAuthStore, createClaudeCodeCredentials, createClaudeCodeProvider, createClaudeCodeQuota, listClaudeCodeModels, mapClaudeUsagePayload, observeClaudeRateLimitHeaders, observeClaudeStreamBody, openClaudeCodeCredentialPool, refreshClaudeCodeSecret, resolveClaudeCodeOAuthAccess, resolveWireLogDir, runClaudeCodeLogin };
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.3.2",
4
+ "version": "0.4.0",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "exports": {
@@ -12,11 +12,11 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@demicodes/core": "^0.3.2",
15
- "@demicodes/provider": "^0.3.2",
15
+ "@demicodes/provider": "^0.4.0",
16
16
  "@demicodes/utils": "^0.3.2"
17
17
  },
18
18
  "devDependencies": {
19
- "@demicodes/agent": "^0.3.2",
19
+ "@demicodes/agent": "^0.3.3",
20
20
  "@demicodes/shell": "^0.3.2"
21
21
  },
22
22
  "license": "Apache-2.0",