@vacbo/opencode-anthropic-fix 0.1.7 → 0.1.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.
Files changed (107) hide show
  1. package/README.md +88 -88
  2. package/dist/opencode-anthropic-auth-cli.mjs +804 -507
  3. package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
  4. package/package.json +67 -59
  5. package/src/__tests__/billing-edge-cases.test.ts +59 -59
  6. package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
  7. package/src/__tests__/cc-comparison.test.ts +87 -87
  8. package/src/__tests__/cc-credentials.test.ts +254 -250
  9. package/src/__tests__/cch-drift-checker.test.ts +51 -51
  10. package/src/__tests__/cch-native-style.test.ts +56 -56
  11. package/src/__tests__/debug-gating.test.ts +42 -42
  12. package/src/__tests__/decomposition-smoke.test.ts +68 -68
  13. package/src/__tests__/fingerprint-regression.test.ts +575 -566
  14. package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
  15. package/src/__tests__/helpers/conversation-history.ts +119 -119
  16. package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
  17. package/src/__tests__/helpers/deferred.ts +69 -69
  18. package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
  19. package/src/__tests__/helpers/in-memory-storage.ts +88 -88
  20. package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
  21. package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
  22. package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
  23. package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
  24. package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
  25. package/src/__tests__/helpers/sse.ts +209 -209
  26. package/src/__tests__/index.parallel.test.ts +605 -595
  27. package/src/__tests__/sanitization-regex.test.ts +112 -112
  28. package/src/__tests__/state-bounds.test.ts +90 -90
  29. package/src/account-identity.test.ts +197 -192
  30. package/src/account-identity.ts +69 -67
  31. package/src/account-state.test.ts +86 -86
  32. package/src/account-state.ts +25 -25
  33. package/src/accounts/matching.test.ts +335 -0
  34. package/src/accounts/matching.ts +167 -0
  35. package/src/accounts/persistence.test.ts +345 -0
  36. package/src/accounts/persistence.ts +432 -0
  37. package/src/accounts/repair.test.ts +276 -0
  38. package/src/accounts/repair.ts +407 -0
  39. package/src/accounts.dedup.test.ts +621 -621
  40. package/src/accounts.test.ts +933 -929
  41. package/src/accounts.ts +633 -989
  42. package/src/backoff.test.ts +345 -345
  43. package/src/backoff.ts +219 -219
  44. package/src/betas.ts +124 -124
  45. package/src/bun-fetch.test.ts +345 -342
  46. package/src/bun-fetch.ts +424 -424
  47. package/src/bun-proxy.test.ts +25 -25
  48. package/src/bun-proxy.ts +209 -209
  49. package/src/cc-credentials.ts +111 -111
  50. package/src/circuit-breaker.test.ts +184 -184
  51. package/src/circuit-breaker.ts +169 -169
  52. package/src/cli/commands/auth.ts +963 -0
  53. package/src/cli/commands/config.ts +547 -0
  54. package/src/cli/formatting.test.ts +406 -0
  55. package/src/cli/formatting.ts +219 -0
  56. package/src/cli.ts +255 -2022
  57. package/src/commands/handlers/betas.ts +100 -0
  58. package/src/commands/handlers/config.ts +99 -0
  59. package/src/commands/handlers/files.ts +375 -0
  60. package/src/commands/oauth-flow.ts +181 -166
  61. package/src/commands/prompts.ts +61 -61
  62. package/src/commands/router.test.ts +421 -0
  63. package/src/commands/router.ts +143 -635
  64. package/src/config.test.ts +482 -482
  65. package/src/config.ts +412 -404
  66. package/src/constants.ts +48 -48
  67. package/src/drift/cch-constants.ts +95 -95
  68. package/src/env.ts +111 -105
  69. package/src/headers/billing.ts +33 -33
  70. package/src/headers/builder.ts +130 -130
  71. package/src/headers/cch.ts +75 -75
  72. package/src/headers/stainless.ts +25 -25
  73. package/src/headers/user-agent.ts +23 -23
  74. package/src/index.ts +436 -828
  75. package/src/models.ts +27 -27
  76. package/src/oauth.test.ts +102 -102
  77. package/src/oauth.ts +178 -178
  78. package/src/parent-pid-watcher.test.ts +148 -148
  79. package/src/parent-pid-watcher.ts +69 -69
  80. package/src/plugin-helpers.ts +82 -82
  81. package/src/refresh-helpers.ts +145 -139
  82. package/src/refresh-lock.test.ts +94 -94
  83. package/src/refresh-lock.ts +93 -93
  84. package/src/request/body.history.test.ts +579 -571
  85. package/src/request/body.ts +255 -255
  86. package/src/request/metadata.ts +65 -65
  87. package/src/request/retry.test.ts +156 -156
  88. package/src/request/retry.ts +67 -67
  89. package/src/request/url.ts +21 -21
  90. package/src/request-orchestration-helpers.ts +648 -0
  91. package/src/response/index.ts +5 -5
  92. package/src/response/mcp.ts +58 -58
  93. package/src/response/streaming.test.ts +313 -311
  94. package/src/response/streaming.ts +412 -410
  95. package/src/rotation.test.ts +304 -301
  96. package/src/rotation.ts +205 -205
  97. package/src/storage.test.ts +547 -547
  98. package/src/storage.ts +315 -291
  99. package/src/system-prompt/builder.ts +38 -38
  100. package/src/system-prompt/index.ts +5 -5
  101. package/src/system-prompt/normalize.ts +60 -60
  102. package/src/system-prompt/sanitize.ts +30 -30
  103. package/src/thinking.ts +21 -20
  104. package/src/token-refresh.test.ts +265 -265
  105. package/src/token-refresh.ts +219 -214
  106. package/src/types.ts +30 -30
  107. package/dist/bun-proxy.mjs +0 -291
package/src/backoff.ts CHANGED
@@ -7,22 +7,22 @@ const MIN_BACKOFF_MS = 2_000;
7
7
  const RETRIABLE_NETWORK_ERROR_CODES = new Set(["ECONNRESET", "ECONNREFUSED", "EPIPE", "ETIMEDOUT", "UND_ERR_SOCKET"]);
8
8
  const NON_RETRIABLE_ERROR_NAMES = new Set(["AbortError", "TimeoutError", "APIUserAbortError"]);
9
9
  const RETRIABLE_NETWORK_ERROR_MESSAGES = [
10
- "bun proxy upstream error",
11
- "connection reset by peer",
12
- "connection reset by server",
13
- "econnreset",
14
- "econnrefused",
15
- "epipe",
16
- "etimedout",
17
- "fetch failed",
18
- "network connection lost",
19
- "socket hang up",
20
- "und_err_socket",
10
+ "bun proxy upstream error",
11
+ "connection reset by peer",
12
+ "connection reset by server",
13
+ "econnreset",
14
+ "econnrefused",
15
+ "epipe",
16
+ "etimedout",
17
+ "fetch failed",
18
+ "network connection lost",
19
+ "socket hang up",
20
+ "und_err_socket",
21
21
  ];
22
22
 
23
23
  interface ErrorWithCode extends Error {
24
- code?: string;
25
- cause?: unknown;
24
+ code?: string;
25
+ cause?: unknown;
26
26
  }
27
27
 
28
28
  /**
@@ -30,23 +30,23 @@ interface ErrorWithCode extends Error {
30
30
  * Supports both seconds (integer) and HTTP-date formats.
31
31
  */
32
32
  export function parseRetryAfterHeader(response: Response): number | null {
33
- const header = response.headers.get("retry-after");
34
- if (!header) return null;
35
-
36
- // Try as integer (seconds)
37
- const seconds = parseInt(header, 10);
38
- if (!isNaN(seconds) && seconds > 0) {
39
- return seconds * 1000;
40
- }
41
-
42
- // Try as HTTP-date
43
- const date = new Date(header);
44
- if (!isNaN(date.getTime())) {
45
- const ms = date.getTime() - Date.now();
46
- return ms > 0 ? ms : null;
47
- }
48
-
49
- return null;
33
+ const header = response.headers.get("retry-after");
34
+ if (!header) return null;
35
+
36
+ // Try as integer (seconds)
37
+ const seconds = parseInt(header, 10);
38
+ if (!isNaN(seconds) && seconds > 0) {
39
+ return seconds * 1000;
40
+ }
41
+
42
+ // Try as HTTP-date
43
+ const date = new Date(header);
44
+ if (!isNaN(date.getTime())) {
45
+ const ms = date.getTime() - Date.now();
46
+ return ms > 0 ? ms : null;
47
+ }
48
+
49
+ return null;
50
50
  }
51
51
 
52
52
  /**
@@ -54,11 +54,11 @@ export function parseRetryAfterHeader(response: Response): number | null {
54
54
  * Returns the value in milliseconds, rounded to nearest integer.
55
55
  */
56
56
  export function parseRetryAfterMsHeader(response: Response): number | null {
57
- const header = response.headers.get("retry-after-ms");
58
- if (!header) return null;
57
+ const header = response.headers.get("retry-after-ms");
58
+ if (!header) return null;
59
59
 
60
- const ms = parseFloat(header);
61
- return !isNaN(ms) && ms > 0 ? Math.round(ms) : null;
60
+ const ms = parseFloat(header);
61
+ return !isNaN(ms) && ms > 0 ? Math.round(ms) : null;
62
62
  }
63
63
 
64
64
  /**
@@ -66,153 +66,153 @@ export function parseRetryAfterMsHeader(response: Response): number | null {
66
66
  * Returns true for "true", false for "false", null for absent or unrecognized.
67
67
  */
68
68
  export function parseShouldRetryHeader(response: Response): boolean | null {
69
- const header = response.headers.get("x-should-retry");
70
- if (header === "true") return true;
71
- if (header === "false") return false;
72
- return null; // not present or unrecognized
69
+ const header = response.headers.get("x-should-retry");
70
+ if (header === "true") return true;
71
+ if (header === "false") return false;
72
+ return null; // not present or unrecognized
73
73
  }
74
74
 
75
75
  interface ErrorSignals {
76
- errorType: string;
77
- message: string;
78
- text: string;
76
+ errorType: string;
77
+ message: string;
78
+ text: string;
79
79
  }
80
80
 
81
81
  function extractErrorSignals(body: string | object | null | undefined): ErrorSignals {
82
- let errorType = "";
83
- let message = "";
84
- let text = "";
82
+ let errorType = "";
83
+ let message = "";
84
+ let text = "";
85
85
 
86
- if (body == null) {
87
- return { errorType, message, text };
88
- }
89
-
90
- if (typeof body === "string") {
91
- text = body.toLowerCase();
92
- try {
93
- const parsed = JSON.parse(body);
94
- errorType = String(parsed?.error?.type || "").toLowerCase();
95
- message = String(parsed?.error?.message || "").toLowerCase();
96
- } catch {
97
- // Not JSON — use raw text only.
86
+ if (body == null) {
87
+ return { errorType, message, text };
98
88
  }
99
- return { errorType, message, text };
100
- }
101
-
102
- if (typeof body === "object") {
103
- const b = body as Record<string, unknown>;
104
- const err = b.error as Record<string, unknown> | undefined;
105
- errorType = String(err?.type || "").toLowerCase();
106
- message = String(err?.message || "").toLowerCase();
107
- try {
108
- text = JSON.stringify(body).toLowerCase();
109
- } catch {
110
- text = "";
89
+
90
+ if (typeof body === "string") {
91
+ text = body.toLowerCase();
92
+ try {
93
+ const parsed = JSON.parse(body);
94
+ errorType = String(parsed?.error?.type || "").toLowerCase();
95
+ message = String(parsed?.error?.message || "").toLowerCase();
96
+ } catch {
97
+ // Not JSON — use raw text only.
98
+ }
99
+ return { errorType, message, text };
111
100
  }
112
- }
113
101
 
114
- return { errorType, message, text };
102
+ if (typeof body === "object") {
103
+ const b = body as Record<string, unknown>;
104
+ const err = b.error as Record<string, unknown> | undefined;
105
+ errorType = String(err?.type || "").toLowerCase();
106
+ message = String(err?.message || "").toLowerCase();
107
+ try {
108
+ text = JSON.stringify(body).toLowerCase();
109
+ } catch {
110
+ text = "";
111
+ }
112
+ }
113
+
114
+ return { errorType, message, text };
115
115
  }
116
116
 
117
117
  function bodyHasAccountError(body: string | object | null | undefined): boolean {
118
- const { errorType, message, text } = extractErrorSignals(body);
119
-
120
- const typeSignals = [
121
- "rate_limit",
122
- "quota",
123
- "billing",
124
- "permission",
125
- "authentication",
126
- "invalid_api_key",
127
- "insufficient_permissions",
128
- "invalid_grant",
129
- ];
130
-
131
- const messageSignals = [
132
- "rate limit",
133
- "would exceed",
134
- "quota",
135
- "exhausted",
136
- "credit balance",
137
- "billing",
138
- "permission",
139
- "forbidden",
140
- "unauthorized",
141
- "authentication",
142
- "not authorized",
143
- // Anthropic returns "We're unable to verify your membership benefits" on 403 when access token is stale
144
- "membership",
145
- "unable to verify",
146
- ];
147
-
148
- return (
149
- typeSignals.some((signal) => errorType.includes(signal)) ||
150
- messageSignals.some((signal) => message.includes(signal)) ||
151
- messageSignals.some((signal) => text.includes(signal))
152
- );
118
+ const { errorType, message, text } = extractErrorSignals(body);
119
+
120
+ const typeSignals = [
121
+ "rate_limit",
122
+ "quota",
123
+ "billing",
124
+ "permission",
125
+ "authentication",
126
+ "invalid_api_key",
127
+ "insufficient_permissions",
128
+ "invalid_grant",
129
+ ];
130
+
131
+ const messageSignals = [
132
+ "rate limit",
133
+ "would exceed",
134
+ "quota",
135
+ "exhausted",
136
+ "credit balance",
137
+ "billing",
138
+ "permission",
139
+ "forbidden",
140
+ "unauthorized",
141
+ "authentication",
142
+ "not authorized",
143
+ // Anthropic returns "We're unable to verify your membership benefits" on 403 when access token is stale
144
+ "membership",
145
+ "unable to verify",
146
+ ];
147
+
148
+ return (
149
+ typeSignals.some((signal) => errorType.includes(signal)) ||
150
+ messageSignals.some((signal) => message.includes(signal)) ||
151
+ messageSignals.some((signal) => text.includes(signal))
152
+ );
153
153
  }
154
154
 
155
155
  function collectErrorChain(error: unknown): ErrorWithCode[] {
156
- const queue: unknown[] = [error];
157
- const visited = new Set<unknown>();
158
- const chain: ErrorWithCode[] = [];
159
-
160
- while (queue.length > 0) {
161
- const candidate = queue.shift();
162
- if (candidate == null || visited.has(candidate)) {
163
- continue;
164
- }
165
-
166
- visited.add(candidate);
167
-
168
- if (candidate instanceof Error) {
169
- const typedCandidate = candidate as ErrorWithCode;
170
- chain.push(typedCandidate);
171
- if (typedCandidate.cause !== undefined) {
172
- queue.push(typedCandidate.cause);
173
- }
174
- continue;
175
- }
176
-
177
- if (typeof candidate === "object" && "cause" in candidate) {
178
- queue.push((candidate as { cause?: unknown }).cause);
156
+ const queue: unknown[] = [error];
157
+ const visited = new Set<unknown>();
158
+ const chain: ErrorWithCode[] = [];
159
+
160
+ while (queue.length > 0) {
161
+ const candidate = queue.shift();
162
+ if (candidate == null || visited.has(candidate)) {
163
+ continue;
164
+ }
165
+
166
+ visited.add(candidate);
167
+
168
+ if (candidate instanceof Error) {
169
+ const typedCandidate = candidate as ErrorWithCode;
170
+ chain.push(typedCandidate);
171
+ if (typedCandidate.cause !== undefined) {
172
+ queue.push(typedCandidate.cause);
173
+ }
174
+ continue;
175
+ }
176
+
177
+ if (typeof candidate === "object" && "cause" in candidate) {
178
+ queue.push((candidate as { cause?: unknown }).cause);
179
+ }
179
180
  }
180
- }
181
181
 
182
- return chain;
182
+ return chain;
183
183
  }
184
184
 
185
185
  /**
186
186
  * Check whether an error represents a transient transport/network failure.
187
187
  */
188
188
  export function isRetriableNetworkError(error: unknown): boolean {
189
- if (typeof error === "string") {
190
- const text = error.toLowerCase();
191
- return RETRIABLE_NETWORK_ERROR_MESSAGES.some((signal) => text.includes(signal));
192
- }
193
-
194
- const chain = collectErrorChain(error);
195
- if (chain.length === 0) {
196
- return false;
197
- }
198
-
199
- for (const candidate of chain) {
200
- if (NON_RETRIABLE_ERROR_NAMES.has(candidate.name)) {
201
- return false;
189
+ if (typeof error === "string") {
190
+ const text = error.toLowerCase();
191
+ return RETRIABLE_NETWORK_ERROR_MESSAGES.some((signal) => text.includes(signal));
202
192
  }
203
193
 
204
- const code = candidate.code?.toUpperCase();
205
- if (code && RETRIABLE_NETWORK_ERROR_CODES.has(code)) {
206
- return true;
194
+ const chain = collectErrorChain(error);
195
+ if (chain.length === 0) {
196
+ return false;
207
197
  }
208
198
 
209
- const message = candidate.message.toLowerCase();
210
- if (RETRIABLE_NETWORK_ERROR_MESSAGES.some((signal) => message.includes(signal))) {
211
- return true;
199
+ for (const candidate of chain) {
200
+ if (NON_RETRIABLE_ERROR_NAMES.has(candidate.name)) {
201
+ return false;
202
+ }
203
+
204
+ const code = candidate.code?.toUpperCase();
205
+ if (code && RETRIABLE_NETWORK_ERROR_CODES.has(code)) {
206
+ return true;
207
+ }
208
+
209
+ const message = candidate.message.toLowerCase();
210
+ if (RETRIABLE_NETWORK_ERROR_MESSAGES.some((signal) => message.includes(signal))) {
211
+ return true;
212
+ }
212
213
  }
213
- }
214
214
 
215
- return false;
215
+ return false;
216
216
  }
217
217
 
218
218
  /**
@@ -220,89 +220,89 @@ export function isRetriableNetworkError(error: unknown): boolean {
220
220
  * that would benefit from switching to a different account.
221
221
  */
222
222
  export function isAccountSpecificError(status: number, body?: string | object | null): boolean {
223
- // 429 is always account-specific (per-account rate limits)
224
- if (status === 429) return true;
223
+ // 429 is always account-specific (per-account rate limits)
224
+ if (status === 429) return true;
225
225
 
226
- // 401 is always account-specific (per-account auth)
227
- if (status === 401) return true;
226
+ // 401 is always account-specific (per-account auth)
227
+ if (status === 401) return true;
228
228
 
229
- // 400/403 are account-specific only if the body contains relevant language
230
- if ((status === 400 || status === 403) && body) {
231
- return bodyHasAccountError(body);
232
- }
229
+ // 400/403 are account-specific only if the body contains relevant language
230
+ if ((status === 400 || status === 403) && body) {
231
+ return bodyHasAccountError(body);
232
+ }
233
233
 
234
- // Everything else (529, 503, 500, etc.) is service-wide
235
- return false;
234
+ // Everything else (529, 503, 500, etc.) is service-wide
235
+ return false;
236
236
  }
237
237
 
238
238
  /**
239
239
  * Parse the rate limit reason from an HTTP status and response body.
240
240
  */
241
241
  export function parseRateLimitReason(status: number, body?: string | object | null): RateLimitReason {
242
- const { errorType, message, text } = extractErrorSignals(body);
243
-
244
- const authSignals = [
245
- "authentication",
246
- "invalid_api_key",
247
- "invalid api key",
248
- "invalid_grant",
249
- "unauthorized",
250
- "invalid access token",
251
- "expired token",
252
- "membership",
253
- ];
254
-
255
- const isAuthFailure =
256
- status === 401 ||
257
- authSignals.some((signal) => errorType.includes(signal)) ||
258
- authSignals.some((signal) => message.includes(signal)) ||
259
- authSignals.some((signal) => text.includes(signal));
260
-
261
- if (isAuthFailure) {
262
- return "AUTH_FAILED";
263
- }
264
-
265
- if (
266
- errorType.includes("quota") ||
267
- errorType.includes("billing") ||
268
- errorType.includes("permission") ||
269
- errorType.includes("insufficient_permissions") ||
270
- message.includes("quota") ||
271
- message.includes("exhausted") ||
272
- message.includes("credit balance") ||
273
- message.includes("billing") ||
274
- message.includes("permission") ||
275
- message.includes("forbidden") ||
276
- text.includes("permission")
277
- ) {
278
- return "QUOTA_EXHAUSTED";
279
- }
280
-
281
- return "RATE_LIMIT_EXCEEDED";
242
+ const { errorType, message, text } = extractErrorSignals(body);
243
+
244
+ const authSignals = [
245
+ "authentication",
246
+ "invalid_api_key",
247
+ "invalid api key",
248
+ "invalid_grant",
249
+ "unauthorized",
250
+ "invalid access token",
251
+ "expired token",
252
+ "membership",
253
+ ];
254
+
255
+ const isAuthFailure =
256
+ status === 401 ||
257
+ authSignals.some((signal) => errorType.includes(signal)) ||
258
+ authSignals.some((signal) => message.includes(signal)) ||
259
+ authSignals.some((signal) => text.includes(signal));
260
+
261
+ if (isAuthFailure) {
262
+ return "AUTH_FAILED";
263
+ }
264
+
265
+ if (
266
+ errorType.includes("quota") ||
267
+ errorType.includes("billing") ||
268
+ errorType.includes("permission") ||
269
+ errorType.includes("insufficient_permissions") ||
270
+ message.includes("quota") ||
271
+ message.includes("exhausted") ||
272
+ message.includes("credit balance") ||
273
+ message.includes("billing") ||
274
+ message.includes("permission") ||
275
+ message.includes("forbidden") ||
276
+ text.includes("permission")
277
+ ) {
278
+ return "QUOTA_EXHAUSTED";
279
+ }
280
+
281
+ return "RATE_LIMIT_EXCEEDED";
282
282
  }
283
283
 
284
284
  /**
285
285
  * Calculate backoff duration in milliseconds.
286
286
  */
287
287
  export function calculateBackoffMs(
288
- reason: RateLimitReason,
289
- consecutiveFailures: number,
290
- retryAfterMs?: number | null,
288
+ reason: RateLimitReason,
289
+ consecutiveFailures: number,
290
+ retryAfterMs?: number | null,
291
291
  ): number {
292
- // Retry-After header takes precedence
293
- if (retryAfterMs && retryAfterMs > 0) {
294
- return Math.max(retryAfterMs, MIN_BACKOFF_MS);
295
- }
296
-
297
- switch (reason) {
298
- case "AUTH_FAILED":
299
- return AUTH_FAILED_BACKOFF;
300
- case "QUOTA_EXHAUSTED": {
301
- const index = Math.min(consecutiveFailures, QUOTA_EXHAUSTED_BACKOFFS.length - 1);
302
- return QUOTA_EXHAUSTED_BACKOFFS[index]!;
292
+ // Retry-After header takes precedence
293
+ if (retryAfterMs && retryAfterMs > 0) {
294
+ return Math.max(retryAfterMs, MIN_BACKOFF_MS);
295
+ }
296
+
297
+ switch (reason) {
298
+ case "AUTH_FAILED":
299
+ return AUTH_FAILED_BACKOFF;
300
+ case "QUOTA_EXHAUSTED": {
301
+ const index = Math.min(consecutiveFailures, QUOTA_EXHAUSTED_BACKOFFS.length - 1);
302
+ return QUOTA_EXHAUSTED_BACKOFFS[index]!;
303
+ }
304
+ case "RATE_LIMIT_EXCEEDED":
305
+ default:
306
+ return RATE_LIMIT_EXCEEDED_BACKOFF;
303
307
  }
304
- case "RATE_LIMIT_EXCEEDED":
305
- default:
306
- return RATE_LIMIT_EXCEEDED_BACKOFF;
307
- }
308
308
  }