opencode-copilot-account-switcher 0.10.7 → 0.10.9

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.
@@ -3,7 +3,6 @@ const RETRYABLE_MESSAGES = [
3
3
  "load failed",
4
4
  "failed to fetch",
5
5
  "network request failed",
6
- "sse read timed out",
7
6
  "unable to connect",
8
7
  "econnreset",
9
8
  "etimedout",
@@ -490,8 +489,9 @@ function stripOpenAIItemId(part) {
490
489
  };
491
490
  }
492
491
  function getInternalPatchClient(client) {
493
- const patch = client?._client?.patch;
494
- return typeof patch === "function" ? patch : undefined;
492
+ const internalClient = client?._client;
493
+ const patch = internalClient?.patch;
494
+ return typeof patch === "function" ? patch.bind(internalClient) : undefined;
495
495
  }
496
496
  function collectSessionRepairMatches(messages, predicate) {
497
497
  return (messages?.data ?? []).flatMap((message) => {
@@ -1077,21 +1077,25 @@ function withStreamDebugLogs(response, request) {
1077
1077
  }
1078
1078
  catch (error) {
1079
1079
  const message = getErrorMessage(error);
1080
+ const isSseReadTimeout = message.includes("sse read timed out");
1080
1081
  const retryable = RETRYABLE_MESSAGES.some((part) => message.includes(part));
1081
1082
  if (isDebugEnabled()) {
1082
1083
  debugLog("sse stream read error", {
1083
1084
  url: rawUrl,
1084
1085
  message,
1085
1086
  retryableByMessage: retryable,
1087
+ bypassedTimeoutWrap: isSseReadTimeout,
1086
1088
  });
1087
1089
  }
1088
- controller.error(retryable
1089
- ? toRetryableApiCallError(error, request, {
1090
- group: "stream",
1091
- statusCode: response.status,
1092
- responseHeaders: response.headers,
1093
- })
1094
- : error);
1090
+ controller.error(isSseReadTimeout
1091
+ ? error
1092
+ : retryable
1093
+ ? toRetryableApiCallError(error, request, {
1094
+ group: "stream",
1095
+ statusCode: response.status,
1096
+ responseHeaders: response.headers,
1097
+ })
1098
+ : error);
1095
1099
  }
1096
1100
  };
1097
1101
  void pump();
@@ -55,7 +55,18 @@ export declare function buildPluginHooks(input: {
55
55
  loadOfficialConfig?: (input: {
56
56
  getAuth: () => Promise<CopilotAuthState | undefined>;
57
57
  provider?: CopilotProviderConfig;
58
+ baseFetch?: typeof fetch;
59
+ version?: string;
58
60
  }) => Promise<OfficialCopilotConfig | undefined>;
61
+ finalizeRequestForSelection?: (input: {
62
+ request: Request | URL | string;
63
+ init?: RequestInit;
64
+ getAuth: () => Promise<CopilotAuthState | undefined>;
65
+ provider?: CopilotProviderConfig;
66
+ }) => Promise<{
67
+ request: Request | URL | string;
68
+ init?: RequestInit;
69
+ } | undefined>;
59
70
  loadOfficialChatHeaders?: (input: {
60
71
  client?: object;
61
72
  directory?: string;
@@ -418,11 +418,12 @@ export function buildPluginHooks(input) {
418
418
  reason: toReasonByInitiator(initiator),
419
419
  };
420
420
  }
421
- const sessionLookup = input.client?.session?.get;
422
- const messageLookup = input.client?.session?.message;
421
+ const sessionClient = input.client?.session;
422
+ const sessionLookup = sessionClient?.get;
423
+ const messageLookup = sessionClient?.message;
423
424
  const messageIDHeader = getMergedRequestHeader(requestInput.request, requestInput.init, INTERNAL_DEBUG_LINK_HEADER);
424
425
  if (typeof messageIDHeader === "string" && messageIDHeader.length > 0) {
425
- const currentMessage = await messageLookup?.({
426
+ const currentMessage = await messageLookup?.call(sessionClient, {
426
427
  path: {
427
428
  id: requestInput.sessionID,
428
429
  messageID: messageIDHeader,
@@ -439,7 +440,7 @@ export function buildPluginHooks(input) {
439
440
  };
440
441
  }
441
442
  }
442
- const session = await sessionLookup?.({
443
+ const session = await sessionLookup?.call(sessionClient, {
443
444
  path: {
444
445
  id: requestInput.sessionID,
445
446
  },
@@ -479,12 +480,42 @@ export function buildPluginHooks(input) {
479
480
  const loader = async (getAuth, provider) => {
480
481
  const authOverride = new AsyncLocalStorage();
481
482
  const getScopedAuth = async () => authOverride.getStore() ?? getAuth();
483
+ const providerConfig = provider;
482
484
  const config = await loadOfficialConfig({
483
485
  getAuth: getScopedAuth,
484
- provider: provider,
486
+ provider: providerConfig,
485
487
  });
486
488
  if (!config)
487
489
  return {};
490
+ const finalizeRequestForSelection = input.finalizeRequestForSelection
491
+ ?? (input.loadOfficialConfig
492
+ ? undefined
493
+ : async (selectionInput) => {
494
+ let captured;
495
+ const captureConfig = await loadOfficialConfig({
496
+ getAuth: getScopedAuth,
497
+ provider: providerConfig,
498
+ baseFetch: async (nextRequest, nextInit) => {
499
+ captured = {
500
+ request: nextRequest,
501
+ init: nextInit,
502
+ };
503
+ return new Response("{}", {
504
+ status: 200,
505
+ headers: {
506
+ "content-type": "application/json",
507
+ },
508
+ });
509
+ },
510
+ });
511
+ if (!captureConfig)
512
+ return undefined;
513
+ const inspectionRequest = selectionInput.request instanceof Request
514
+ ? selectionInput.request.clone()
515
+ : selectionInput.request;
516
+ await captureConfig.fetch(inspectionRequest, selectionInput.init).catch(() => undefined);
517
+ return captured;
518
+ });
488
519
  const store = await loadStore().catch(() => undefined);
489
520
  const retryStore = readRetryStoreContext(store);
490
521
  const fetchWithModelAccount = async (request, init) => {
@@ -500,7 +531,15 @@ export function buildPluginHooks(input) {
500
531
  maxEntries: touchWriteCacheMaxEntries,
501
532
  });
502
533
  const sessionID = getInternalSessionID(request, init);
503
- const initiator = getMergedRequestHeader(request, init, "x-initiator");
534
+ const finalized = await finalizeRequestForSelection?.({
535
+ request,
536
+ init,
537
+ getAuth: getScopedAuth,
538
+ provider: providerConfig,
539
+ }).catch(() => undefined);
540
+ const selectionRequest = finalized?.request ?? request;
541
+ const selectionInit = finalized?.init ?? init;
542
+ const initiator = getMergedRequestHeader(selectionRequest, selectionInit, "x-initiator");
504
543
  const allowReselect = initiator === "user";
505
544
  const candidates = latestStore ? resolveCopilotModelAccounts(latestStore, modelID) : [];
506
545
  const hasExplicitModelGroup = Boolean(latestStore
@@ -536,7 +575,7 @@ export function buildPluginHooks(input) {
536
575
  }
537
576
  const candidateNames = candidates.map((item) => item.name);
538
577
  const classification = sessionID.length > 0
539
- ? await classifyRequestReason({ sessionID, request, init })
578
+ ? await classifyRequestReason({ sessionID, request: selectionRequest, init: selectionInit })
540
579
  : {
541
580
  reason: toReasonByInitiator(initiator),
542
581
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-copilot-account-switcher",
3
- "version": "0.10.7",
3
+ "version": "0.10.9",
4
4
  "description": "GitHub Copilot account switcher plugin for OpenCode",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",