opencode-anthropic-multi-account 0.2.6 → 0.2.7

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.
@@ -1,9 +1,6 @@
1
1
  /** Anthropic OAuth adapter config */
2
2
  export declare const ANTHROPIC_OAUTH_ADAPTER: import("opencode-multi-account-core").OAuthAdapter;
3
3
  export declare const ANTHROPIC_CLIENT_ID: string;
4
- export declare const ANTHROPIC_AUTHORIZE_ENDPOINT: string;
5
- export declare const ANTHROPIC_REDIRECT_URI: string;
6
- export declare const ANTHROPIC_SCOPES: string;
7
4
  /** Token exchange / refresh endpoint */
8
5
  export declare const ANTHROPIC_TOKEN_ENDPOINT: string;
9
6
  /** OAuth usage stats endpoint */
package/dist/index.js CHANGED
@@ -1820,33 +1820,6 @@ async function confirm(message, defaultYes = false) {
1820
1820
  }
1821
1821
 
1822
1822
  // ../multi-account-core/src/adapters/anthropic.ts
1823
- var ANTHROPIC_DEFAULT_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
1824
- var ANTHROPIC_DEFAULT_CLI_VERSION = "2.1.80";
1825
- var ANTHROPIC_DEFAULT_USER_AGENT = "claude-cli/2.1.2 (external, cli)";
1826
- var ANTHROPIC_DEFAULT_AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
1827
- var ANTHROPIC_DEFAULT_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
1828
- var ANTHROPIC_DEFAULT_REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
1829
- var ANTHROPIC_DEFAULT_SCOPES = "org:create_api_key user:profile user:inference";
1830
- var ANTHROPIC_DEFAULT_BETA_FLAGS = "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,prompt-caching-scope-2026-01-05";
1831
- function buildCliUserAgent(cliVersion) {
1832
- return `claude-cli/${cliVersion} (external, cli)`;
1833
- }
1834
- function resolveAnthropicOAuthEnv(env = process.env) {
1835
- const cliVersion = env.ANTHROPIC_CLI_VERSION || ANTHROPIC_DEFAULT_CLI_VERSION;
1836
- const composedUserAgent = buildCliUserAgent(cliVersion);
1837
- const userAgent = env.ANTHROPIC_USER_AGENT || (env.ANTHROPIC_CLI_VERSION ? composedUserAgent : "") || ANTHROPIC_DEFAULT_USER_AGENT;
1838
- return {
1839
- clientId: env.ANTHROPIC_CLIENT_ID || ANTHROPIC_DEFAULT_CLIENT_ID,
1840
- cliVersion,
1841
- userAgent,
1842
- authorizeUrl: env.ANTHROPIC_AUTHORIZE_URL || ANTHROPIC_DEFAULT_AUTHORIZE_URL,
1843
- tokenUrl: env.ANTHROPIC_TOKEN_URL || ANTHROPIC_DEFAULT_TOKEN_URL,
1844
- redirectUri: env.ANTHROPIC_REDIRECT_URI || ANTHROPIC_DEFAULT_REDIRECT_URI,
1845
- scopes: env.ANTHROPIC_SCOPES || ANTHROPIC_DEFAULT_SCOPES,
1846
- betaFlags: env.ANTHROPIC_BETA_FLAGS || ANTHROPIC_DEFAULT_BETA_FLAGS
1847
- };
1848
- }
1849
- var anthropicEnv = resolveAnthropicOAuthEnv();
1850
1823
  var anthropicOAuthAdapter = {
1851
1824
  id: "anthropic",
1852
1825
  authProviderId: "anthropic",
@@ -1854,14 +1827,14 @@ var anthropicOAuthAdapter = {
1854
1827
  statusToolName: "claude_multiauth_status",
1855
1828
  authMethodLabel: "Claude Pro/Max (Multi-Auth)",
1856
1829
  serviceLogName: "claude-multiauth",
1857
- oauthClientId: anthropicEnv.clientId,
1858
- tokenEndpoint: anthropicEnv.tokenUrl,
1830
+ oauthClientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
1831
+ tokenEndpoint: "https://console.anthropic.com/v1/oauth/token",
1859
1832
  usageEndpoint: "https://api.anthropic.com/api/oauth/usage",
1860
1833
  profileEndpoint: "https://api.anthropic.com/api/oauth/profile",
1861
1834
  oauthBetaHeader: "oauth-2025-04-20",
1862
- requestBetaHeader: anthropicEnv.betaFlags,
1863
- cliUserAgent: anthropicEnv.userAgent,
1864
- cliVersion: anthropicEnv.cliVersion,
1835
+ requestBetaHeader: "oauth-2025-04-20,interleaved-thinking-2025-05-14",
1836
+ cliUserAgent: "claude-cli/2.1.2 (external, cli)",
1837
+ cliVersion: "2.1.80",
1865
1838
  billingSalt: "59cf53e54c78",
1866
1839
  toolPrefix: "mcp_",
1867
1840
  accountStorageFilename: "anthropic-multi-account-accounts.json",
@@ -2210,12 +2183,8 @@ var CascadeStateManager = class {
2210
2183
  };
2211
2184
 
2212
2185
  // src/constants.ts
2213
- var resolvedAnthropicOAuthEnv = resolveAnthropicOAuthEnv();
2214
2186
  var ANTHROPIC_OAUTH_ADAPTER = anthropicOAuthAdapter;
2215
2187
  var ANTHROPIC_CLIENT_ID = ANTHROPIC_OAUTH_ADAPTER.oauthClientId;
2216
- var ANTHROPIC_AUTHORIZE_ENDPOINT = resolvedAnthropicOAuthEnv.authorizeUrl;
2217
- var ANTHROPIC_REDIRECT_URI = resolvedAnthropicOAuthEnv.redirectUri;
2218
- var ANTHROPIC_SCOPES = resolvedAnthropicOAuthEnv.scopes;
2219
2188
  var ANTHROPIC_TOKEN_ENDPOINT = ANTHROPIC_OAUTH_ADAPTER.tokenEndpoint;
2220
2189
  var ANTHROPIC_USAGE_ENDPOINT = ANTHROPIC_OAUTH_ADAPTER.usageEndpoint;
2221
2190
  var ANTHROPIC_PROFILE_ENDPOINT = ANTHROPIC_OAUTH_ADAPTER.profileEndpoint;
@@ -2500,14 +2469,12 @@ function fromPiAiCredentials(creds) {
2500
2469
  expiresAt: creds.expires
2501
2470
  };
2502
2471
  }
2503
- var ANTHROPIC_REFRESH_ENDPOINT = ANTHROPIC_TOKEN_ENDPOINT;
2504
- var LEGACY_ANTHROPIC_TOKEN_ENDPOINT = "https://platform.claude.com/v1/oauth/token";
2472
+ var ANTHROPIC_REFRESH_ENDPOINT = "https://platform.claude.com/v1/oauth/token";
2505
2473
  var REFRESH_NODE_EXECUTABLE = process.env.OPENCODE_REFRESH_NODE_EXECUTABLE || "node";
2506
2474
  var tokenProxyContext = new AsyncLocalStorage();
2507
2475
  var tokenProxyInstalled = false;
2508
2476
  var tokenProxyOriginalFetch = null;
2509
2477
  var refreshEndpointUrl = new URL(ANTHROPIC_REFRESH_ENDPOINT);
2510
- var legacyRefreshEndpointUrl = new URL(LEGACY_ANTHROPIC_TOKEN_ENDPOINT);
2511
2478
  function buildRefreshRequestError(details) {
2512
2479
  return new Error(`Anthropic token refresh request failed. url=${ANTHROPIC_REFRESH_ENDPOINT}; details=${details}`);
2513
2480
  }
@@ -2523,65 +2490,9 @@ function isAnthropicTokenEndpoint(input) {
2523
2490
  const rawUrl = getRequestUrlString(input);
2524
2491
  try {
2525
2492
  const url = new URL(rawUrl);
2526
- const isConfiguredEndpoint = url.origin === refreshEndpointUrl.origin && url.pathname === refreshEndpointUrl.pathname;
2527
- const isLegacyEndpoint = url.origin === legacyRefreshEndpointUrl.origin && url.pathname === legacyRefreshEndpointUrl.pathname;
2528
- return isConfiguredEndpoint || isLegacyEndpoint;
2529
- } catch {
2530
- return rawUrl === ANTHROPIC_REFRESH_ENDPOINT || rawUrl === LEGACY_ANTHROPIC_TOKEN_ENDPOINT;
2531
- }
2532
- }
2533
- function applyOverridesToTokenParams(params) {
2534
- params.set("client_id", ANTHROPIC_CLIENT_ID);
2535
- if (params.get("grant_type") === "authorization_code") {
2536
- params.set("redirect_uri", ANTHROPIC_REDIRECT_URI);
2537
- params.set("scope", ANTHROPIC_SCOPES);
2538
- }
2539
- }
2540
- function isRecord(value) {
2541
- return typeof value === "object" && value !== null;
2542
- }
2543
- function applyAnthropicTokenRequestOverrides(rawBody) {
2544
- const trimmed = rawBody.trim();
2545
- if (trimmed.length === 0) {
2546
- return rawBody;
2547
- }
2548
- if (trimmed.startsWith("{")) {
2549
- try {
2550
- const parsed = JSON.parse(trimmed);
2551
- if (!isRecord(parsed)) {
2552
- return rawBody;
2553
- }
2554
- const grantType = typeof parsed.grant_type === "string" ? parsed.grant_type : "";
2555
- parsed.client_id = ANTHROPIC_CLIENT_ID;
2556
- if (grantType === "authorization_code") {
2557
- parsed.redirect_uri = ANTHROPIC_REDIRECT_URI;
2558
- parsed.scope = ANTHROPIC_SCOPES;
2559
- }
2560
- return JSON.stringify(parsed);
2561
- } catch {
2562
- return rawBody;
2563
- }
2564
- }
2565
- const params = new URLSearchParams(rawBody);
2566
- applyOverridesToTokenParams(params);
2567
- return params.toString();
2568
- }
2569
- function rewriteAnthropicAuthUrl(rawUrl) {
2570
- try {
2571
- const configuredAuthorizeUrl = new URL(ANTHROPIC_AUTHORIZE_ENDPOINT);
2572
- const rewritten = new URL(rawUrl);
2573
- rewritten.protocol = configuredAuthorizeUrl.protocol;
2574
- rewritten.host = configuredAuthorizeUrl.host;
2575
- rewritten.pathname = configuredAuthorizeUrl.pathname;
2576
- for (const [key, value] of configuredAuthorizeUrl.searchParams.entries()) {
2577
- rewritten.searchParams.set(key, value);
2578
- }
2579
- rewritten.searchParams.set("client_id", ANTHROPIC_CLIENT_ID);
2580
- rewritten.searchParams.set("redirect_uri", ANTHROPIC_REDIRECT_URI);
2581
- rewritten.searchParams.set("scope", ANTHROPIC_SCOPES);
2582
- return rewritten.toString();
2493
+ return url.origin === refreshEndpointUrl.origin && url.pathname === refreshEndpointUrl.pathname;
2583
2494
  } catch {
2584
- return rawUrl;
2495
+ return rawUrl === ANTHROPIC_REFRESH_ENDPOINT;
2585
2496
  }
2586
2497
  }
2587
2498
  function getRequestBodySource(input, init) {
@@ -2619,11 +2530,10 @@ function shouldProxyTokenRequest(input) {
2619
2530
  return tokenProxyContext.getStore() === true && isAnthropicTokenEndpoint(input);
2620
2531
  }
2621
2532
  async function postAnthropicTokenViaNode(body) {
2622
- const overriddenBody = applyAnthropicTokenRequestOverrides(body);
2623
2533
  let output;
2624
2534
  try {
2625
2535
  output = await runNodeTokenRequest({
2626
- body: overriddenBody,
2536
+ body,
2627
2537
  endpoint: ANTHROPIC_REFRESH_ENDPOINT,
2628
2538
  executable: REFRESH_NODE_EXECUTABLE,
2629
2539
  timeoutMs: TOKEN_REFRESH_TIMEOUT_MS
@@ -2686,12 +2596,7 @@ async function fetchProfileWithSingleRetry(accessToken) {
2686
2596
  }
2687
2597
  async function loginWithPiAi(callbacks) {
2688
2598
  const piCreds = await withAnthropicTokenProxyFetch(() => piAiOauth.loginAnthropic({
2689
- onAuth: (info) => {
2690
- callbacks.onAuth({
2691
- ...info,
2692
- url: info.url ? rewriteAnthropicAuthUrl(info.url) : info.url
2693
- });
2694
- },
2599
+ onAuth: callbacks.onAuth,
2695
2600
  onPrompt: callbacks.onPrompt,
2696
2601
  onProgress: callbacks.onProgress,
2697
2602
  onManualCodeInput: callbacks.onManualCodeInput
@@ -3630,30 +3535,11 @@ function buildBillingHeader(firstUserMessage) {
3630
3535
  if (!version || !salt) return "";
3631
3536
  const sampled = sampleCodeUnits(firstUserMessage, [4, 7, 20]);
3632
3537
  const hash = createHash("sha256").update(`${salt}${sampled}${version}`).digest("hex").slice(0, 3);
3633
- return `cc_version=${version}.${hash}; cc_entrypoint=cli; cch=00000;`;
3538
+ return `x-anthropic-billing-header: cc_version=${version}.${hash}; cc_entrypoint=cli; cch=00000;`;
3634
3539
  }
3635
3540
  var OPENCODE_CAMEL_RE = /OpenCode/g;
3636
3541
  var OPENCODE_LOWER_RE = /(?<!\/)opencode/gi;
3637
3542
  var TOOL_PREFIX_RESPONSE_RE = /"name"\s*:\s*"mcp_([^"]+)"/g;
3638
- function extractFirstUserTextFromBody(body) {
3639
- if (!body) return "";
3640
- try {
3641
- const parsed = JSON.parse(body);
3642
- if (!Array.isArray(parsed.messages)) return "";
3643
- for (const message of parsed.messages) {
3644
- if (message.role !== "user") continue;
3645
- if (typeof message.content === "string") return message.content;
3646
- if (!Array.isArray(message.content)) continue;
3647
- for (const block of message.content) {
3648
- if (block.type === "text" && typeof block.text === "string") {
3649
- return block.text;
3650
- }
3651
- }
3652
- }
3653
- } catch {
3654
- }
3655
- return "";
3656
- }
3657
3543
  function addToolPrefix(name) {
3658
3544
  if (!ANTHROPIC_OAUTH_ADAPTER.transform.addToolPrefix) {
3659
3545
  return name;
@@ -3679,7 +3565,7 @@ function processCompleteLines(buffer) {
3679
3565
  `;
3680
3566
  return { output, remaining };
3681
3567
  }
3682
- function buildRequestHeaders(input, init, accessToken, bodyString) {
3568
+ function buildRequestHeaders(input, init, accessToken) {
3683
3569
  const headers = new Headers();
3684
3570
  if (input instanceof Request) {
3685
3571
  input.headers.forEach((value, key) => {
@@ -3711,13 +3597,6 @@ function buildRequestHeaders(input, init, accessToken, bodyString) {
3711
3597
  headers.set("user-agent", CLAUDE_CLI_USER_AGENT);
3712
3598
  headers.set("anthropic-dangerous-direct-browser-access", "true");
3713
3599
  headers.set("x-app", "cli");
3714
- const resolvedBody = bodyString ?? (typeof init?.body === "string" ? init.body : void 0);
3715
- const billingHeader = buildBillingHeader(
3716
- extractFirstUserTextFromBody(resolvedBody)
3717
- );
3718
- if (billingHeader) {
3719
- headers.set("x-anthropic-billing-header", billingHeader);
3720
- }
3721
3600
  headers.delete("x-api-key");
3722
3601
  return headers;
3723
3602
  }
@@ -3896,9 +3775,8 @@ var AccountRuntimeFactory = class {
3896
3775
  }
3897
3776
  async executeTransformedFetch(input, init, accessToken) {
3898
3777
  const transformedInput = transformRequestUrl(input);
3899
- const rawBody = typeof init?.body === "string" ? init.body : void 0;
3900
- const headers = buildRequestHeaders(transformedInput, init, accessToken, rawBody);
3901
- const transformedBody = rawBody !== void 0 ? transformRequestBody(rawBody) : init?.body;
3778
+ const headers = buildRequestHeaders(transformedInput, init, accessToken);
3779
+ const transformedBody = typeof init?.body === "string" ? transformRequestBody(init.body) : init?.body;
3902
3780
  const response = await fetch(transformedInput, {
3903
3781
  ...init,
3904
3782
  headers,
@@ -3929,6 +3807,24 @@ var AccountRuntimeFactory = class {
3929
3807
  };
3930
3808
 
3931
3809
  // src/index.ts
3810
+ function extractFirstUserText(input) {
3811
+ try {
3812
+ const raw = input;
3813
+ const messages = raw.messages ?? raw.request?.messages;
3814
+ if (!Array.isArray(messages)) return "";
3815
+ for (const msg of messages) {
3816
+ if (msg.role !== "user") continue;
3817
+ if (typeof msg.content === "string") return msg.content;
3818
+ if (Array.isArray(msg.content)) {
3819
+ for (const block of msg.content) {
3820
+ if (block.type === "text" && block.text) return block.text;
3821
+ }
3822
+ }
3823
+ }
3824
+ } catch {
3825
+ }
3826
+ return "";
3827
+ }
3932
3828
  function injectSystemPrompt(output) {
3933
3829
  const systemPrompt = getSystemPrompt();
3934
3830
  if (!Array.isArray(output.system)) {
@@ -3950,8 +3846,12 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
3950
3846
  let cascadeStateManager = null;
3951
3847
  let poolChainConfig = { pools: [], chains: [] };
3952
3848
  return {
3953
- "experimental.chat.system.transform": (_input, output) => {
3849
+ "experimental.chat.system.transform": (input, output) => {
3954
3850
  injectSystemPrompt(output);
3851
+ const billingHeader = buildBillingHeader(extractFirstUserText(input));
3852
+ if (billingHeader && !output.system?.includes(billingHeader)) {
3853
+ output.system?.unshift(billingHeader);
3854
+ }
3955
3855
  },
3956
3856
  tool: {
3957
3857
  [ANTHROPIC_OAUTH_ADAPTER.statusToolName]: tool({
@@ -11,8 +11,6 @@ export interface LoginWithPiAiCallbacks {
11
11
  onProgress?: (message: string) => void;
12
12
  onManualCodeInput?: () => Promise<string>;
13
13
  }
14
- export declare function applyAnthropicTokenRequestOverrides(rawBody: string): string;
15
- export declare function rewriteAnthropicAuthUrl(rawUrl: string): string;
16
14
  export declare function withAnthropicTokenProxyFetch<T>(operation: () => Promise<T>): Promise<T>;
17
15
  export declare function resetAnthropicTokenProxyStateForTest(): void;
18
16
  export declare function loginWithPiAi(callbacks: LoginWithPiAiCallbacks): Promise<Partial<StoredAccount>>;
@@ -1,6 +1,6 @@
1
1
  export declare function getSystemPrompt(): string;
2
2
  export declare function buildBillingHeader(firstUserMessage: string): string;
3
- export declare function buildRequestHeaders(input: RequestInfo | URL, init: RequestInit | undefined, accessToken: string, bodyString?: string): Headers;
3
+ export declare function buildRequestHeaders(input: RequestInfo | URL, init: RequestInit | undefined, accessToken: string): Headers;
4
4
  export declare function transformRequestBody(body: string | undefined): string | undefined;
5
5
  export declare function transformRequestUrl(input: RequestInfo | URL): RequestInfo | URL;
6
6
  export declare function createResponseStreamTransform(response: Response): Response;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-anthropic-multi-account",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "OpenCode plugin for Anthropic multi-account management with automatic rate limit switching",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -39,7 +39,7 @@
39
39
  "directory": "packages/anthropic-multi-account"
40
40
  },
41
41
  "dependencies": {
42
- "opencode-multi-account-core": "^0.2.6",
42
+ "opencode-multi-account-core": "^0.2.7",
43
43
  "@mariozechner/pi-ai": "^0.61.0",
44
44
  "valibot": "^1.2.0"
45
45
  },