opencode-anthropic-multi-account 0.2.20 → 0.2.22

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.js CHANGED
@@ -25,8 +25,8 @@ import {
25
25
  showToast,
26
26
  sleep,
27
27
  updateConfigField
28
- } from "./chunk-RVXWLAVK.js";
29
- import "./chunk-IETVH43F.js";
28
+ } from "./chunk-TOCJ3XJM.js";
29
+ import "./chunk-RAX4SFCO.js";
30
30
 
31
31
  // src/index.ts
32
32
  import { tool } from "@opencode-ai/plugin";
@@ -637,7 +637,6 @@ async function refreshWithOAuth(currentRefreshToken) {
637
637
  }
638
638
 
639
639
  // src/token.ts
640
- var PERMANENT_FAILURE_HTTP_STATUSES = /* @__PURE__ */ new Set([400, 401, 403]);
641
640
  var PERMANENT_FAILURE_MESSAGE_PATTERNS = [
642
641
  /\binvalid_grant\b/i,
643
642
  /\binvalid_scope\b/i,
@@ -660,10 +659,8 @@ async function refreshToken(currentRefreshToken, accountId, client) {
660
659
  return { ok: true, patch };
661
660
  } catch (error) {
662
661
  const message = error instanceof Error ? error.message : String(error);
663
- const statusMatch = message.match(/\b(400|401|403)\b/);
664
- const hasPermanentStatus = statusMatch !== null && PERMANENT_FAILURE_HTTP_STATUSES.has(Number(statusMatch[1]));
665
662
  const hasPermanentMessage = PERMANENT_FAILURE_MESSAGE_PATTERNS.some((pattern) => pattern.test(message));
666
- const isPermanent = hasPermanentStatus || hasPermanentMessage;
663
+ const isPermanent = hasPermanentMessage;
667
664
  await client.app.log({
668
665
  body: {
669
666
  service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName,
@@ -722,7 +719,7 @@ function buildCascadePrompt(input, init) {
722
719
  }
723
720
  function createQueueAwareManager(manager, queue, cascadeStateManager) {
724
721
  return Object.create(manager, {
725
- selectAccount: { value: async function selectAccount() {
722
+ selectAccount: { value: async function selectAccount(stickyKey) {
726
723
  await manager.refresh();
727
724
  while (queue.length > 0) {
728
725
  const next = queue.shift();
@@ -737,7 +734,7 @@ function createQueueAwareManager(manager, queue, cascadeStateManager) {
737
734
  }
738
735
  return account;
739
736
  }
740
- return manager.selectAccount();
737
+ return manager.selectAccount(stickyKey);
741
738
  } }
742
739
  });
743
740
  }
@@ -1761,12 +1758,7 @@ var ADAPTIVE_THINKING_MODEL_MATCHERS = [
1761
1758
  (modelId) => modelId.includes("claude-opus-4-6") || modelId.includes("claude-opus-4.6"),
1762
1759
  (modelId) => /claude-opus-4[-._]([7-9]|\d{2,})/.test(modelId)
1763
1760
  ];
1764
- var LARGE_OUTPUT_MODEL_MATCHERS = [
1765
- (modelId) => modelId.includes("claude-opus-4-6") || modelId.includes("claude-opus-4.6"),
1766
- (modelId) => /claude-opus-4[-._]([7-9]|\d{2,})/.test(modelId)
1767
- ];
1768
1761
  var DEFAULT_MAX_OUTPUT_TOKENS = 64e3;
1769
- var LARGE_MODEL_MAX_OUTPUT_TOKENS = 128e3;
1770
1762
  function normalizeModelId2(modelId) {
1771
1763
  return modelId.trim().toLowerCase();
1772
1764
  }
@@ -1781,16 +1773,11 @@ function supportsAdaptiveThinking(modelId) {
1781
1773
  }
1782
1774
  return ADAPTIVE_THINKING_MODEL_MATCHERS.some((matches) => matches(normalized));
1783
1775
  }
1784
- function getModelMaxOutputTokens(modelId) {
1785
- const runtimeCapability = getRuntimeModelCapability(modelId);
1786
- if (typeof runtimeCapability?.maxOutputTokens === "number") {
1787
- return runtimeCapability.maxOutputTokens;
1788
- }
1789
- const normalized = normalizeModelId2(modelId);
1790
- return LARGE_OUTPUT_MODEL_MATCHERS.some((matches) => matches(normalized)) ? LARGE_MODEL_MAX_OUTPUT_TOKENS : DEFAULT_MAX_OUTPUT_TOKENS;
1776
+ function getModelMaxOutputTokens() {
1777
+ return DEFAULT_MAX_OUTPUT_TOKENS;
1791
1778
  }
1792
- function resolveMaxTokens(modelId, requestedMaxTokens) {
1793
- const modelCap = getModelMaxOutputTokens(modelId);
1779
+ function resolveMaxTokens(requestedMaxTokens) {
1780
+ const modelCap = getModelMaxOutputTokens();
1794
1781
  if (typeof requestedMaxTokens !== "number" || !Number.isFinite(requestedMaxTokens)) {
1795
1782
  return modelCap;
1796
1783
  }
@@ -2039,7 +2026,7 @@ function buildUpstreamRequest(inputBody, identity, template, options) {
2039
2026
  body.context_management = DEFAULT_CONTEXT_MANAGEMENT;
2040
2027
  body.output_config = DEFAULT_OUTPUT_CONFIG;
2041
2028
  }
2042
- body.max_tokens = resolveMaxTokens(modelId, body.max_tokens);
2029
+ body.max_tokens = resolveMaxTokens(body.max_tokens);
2043
2030
  return orderBodyForOutbound(body, template.body_field_order);
2044
2031
  }
2045
2032
  function orderBodyForOutbound(body, overrideOrder) {
@@ -2942,6 +2929,9 @@ var EMPTY_OAUTH_CREDENTIALS = {
2942
2929
  access: "",
2943
2930
  expires: 0
2944
2931
  };
2932
+ if (process.env.CLAUDE_MULTI_ACCOUNT_TRACE_PLUGIN === "1") {
2933
+ console.error("[anthropic-multi-account] module loaded");
2934
+ }
2945
2935
  function extractFirstUserText2(input) {
2946
2936
  try {
2947
2937
  const raw = input;
@@ -2977,6 +2967,9 @@ function applyOrderedHeaders(output, headers) {
2977
2967
  output.headers = Array.isArray(orderedHeaders) ? Object.fromEntries(orderedHeaders) : orderedHeaders;
2978
2968
  }
2979
2969
  var ClaudeMultiAuthPlugin = async (ctx) => {
2970
+ if (process.env.CLAUDE_MULTI_ACCOUNT_TRACE_PLUGIN === "1") {
2971
+ console.error("[anthropic-multi-account] plugin function called");
2972
+ }
2980
2973
  const { client } = ctx;
2981
2974
  await loadConfig();
2982
2975
  const requestProfile = loadCCDerivedRequestProfile();
@@ -3073,7 +3066,20 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
3073
3066
  });
3074
3067
  });
3075
3068
  const store = new AccountStore();
3076
- await syncBootstrapAuth(client, store).catch(() => {
3069
+ await syncBootstrapAuth(client, store).then((synced) => {
3070
+ debugLog(client, "Bootstrap auth sync completed", { synced });
3071
+ }).catch((error) => {
3072
+ client.app.log({
3073
+ body: {
3074
+ service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName,
3075
+ level: "debug",
3076
+ message: "bootstrap auth sync failed",
3077
+ extra: {
3078
+ error: sanitizeError(error)
3079
+ }
3080
+ }
3081
+ }).catch(() => {
3082
+ });
3077
3083
  });
3078
3084
  let manager = null;
3079
3085
  let runtimeFactory = null;
@@ -3210,13 +3216,90 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
3210
3216
  { type: "api", label: "Manually enter API Key" }
3211
3217
  ],
3212
3218
  async loader(getAuth, provider) {
3213
- ingestProviderModelsCapabilities(provider.models ?? {});
3219
+ const providerModels = provider.models ?? {};
3220
+ ingestProviderModelsCapabilities(providerModels);
3221
+ debugLog(client, "Auth loader received provider metadata", {
3222
+ providerId: typeof provider.id === "string" ? provider.id : void 0,
3223
+ providerName: typeof provider.name === "string" ? provider.name : void 0,
3224
+ modelCount: Object.keys(providerModels).length,
3225
+ modelIds: Object.keys(providerModels)
3226
+ });
3214
3227
  const auth = await getAuth();
3228
+ debugLog(client, "Auth loader resolved auth payload", {
3229
+ authType: typeof auth.type === "string" ? auth.type : void 0,
3230
+ authKeys: Object.keys(auth)
3231
+ });
3215
3232
  if (auth.type !== "oauth") {
3216
- stopHeartbeat();
3217
- return { apiKey: "", fetch };
3233
+ await syncBootstrapAuth(client, store).then((synced) => {
3234
+ debugLog(client, "Auth loader requested bootstrap auth sync", { synced });
3235
+ }).catch((error) => {
3236
+ client.app.log({
3237
+ body: {
3238
+ service: ANTHROPIC_OAUTH_ADAPTER.serviceLogName,
3239
+ level: "debug",
3240
+ message: "auth loader bootstrap sync failed",
3241
+ extra: {
3242
+ error: sanitizeError(error)
3243
+ }
3244
+ }
3245
+ }).catch(() => {
3246
+ });
3247
+ });
3248
+ const recoveredFromStore = await initializeManagerFromStore();
3249
+ debugLog(client, "Auth loader attempted store recovery", {
3250
+ recoveredFromStore
3251
+ });
3252
+ if (!recoveredFromStore || !manager || !runtimeFactory) {
3253
+ stopHeartbeat();
3254
+ return { apiKey: "", fetch };
3255
+ }
3256
+ const authProfile2 = await loadCCDerivedAuthProfile();
3257
+ return {
3258
+ apiKey: "",
3259
+ baseURL: authProfile2.apiV1BaseUrl,
3260
+ "chat.headers": async (input, output) => {
3261
+ if (input.provider?.info?.id !== ANTHROPIC_OAUTH_ADAPTER.authProviderId) return;
3262
+ const sessionId2 = getUpstreamSessionId();
3263
+ applyOrderedHeaders(output, {
3264
+ ...output.headers,
3265
+ ...getStaticHeaders(),
3266
+ ...getPerRequestHeaders(sessionId2),
3267
+ "anthropic-beta": getBetaHeader()
3268
+ });
3269
+ },
3270
+ async fetch(input, init) {
3271
+ if (!manager || !runtimeFactory) {
3272
+ stopHeartbeat();
3273
+ return fetch(input, init);
3274
+ }
3275
+ if (manager.getAccountCount() === 0) {
3276
+ stopHeartbeat();
3277
+ throw new Error(
3278
+ "No Anthropic accounts configured. Run `opencode auth login` to add an account."
3279
+ );
3280
+ }
3281
+ ensureHeartbeat(manager.getActiveAccount()?.accessToken);
3282
+ if (!poolManager || !cascadeStateManager) {
3283
+ poolManager = new PoolManager();
3284
+ poolManager.loadPools(poolChainConfig.pools);
3285
+ cascadeStateManager = new CascadeStateManager();
3286
+ }
3287
+ return executeWithAccountRotation(
3288
+ manager,
3289
+ runtimeFactory,
3290
+ client,
3291
+ input,
3292
+ init,
3293
+ {
3294
+ poolManager,
3295
+ cascadeStateManager,
3296
+ poolChainConfig
3297
+ }
3298
+ );
3299
+ }
3300
+ };
3218
3301
  }
3219
- for (const model of Object.values(provider.models ?? {})) {
3302
+ for (const model of Object.values(providerModels)) {
3220
3303
  if (model) {
3221
3304
  model.cost = { input: 0, output: 0, cache: { read: 0, write: 0 } };
3222
3305
  }
@@ -3229,6 +3312,10 @@ var ClaudeMultiAuthPlugin = async (ctx) => {
3229
3312
  if (!initializedManager) {
3230
3313
  return { apiKey: "", fetch };
3231
3314
  }
3315
+ debugLog(client, "Auth loader initialized manager state", {
3316
+ accountCount: initializedManager.getAccountCount(),
3317
+ activeAccountUuid: initializedManager.getActiveAccount()?.uuid
3318
+ });
3232
3319
  if (initializedManager.getAccountCount() > 0) {
3233
3320
  const activeAccount = initializedManager.getActiveAccount();
3234
3321
  const activeLabel = activeAccount ? getAccountLabel(activeAccount) : "none";