@cortexkit/opencode-anthropic-auth 1.1.0 → 1.1.1
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 +101 -15
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { authorize, buildClaudeQuotaSummary, buildFallbackQuotaSummaries, buildRefreshOperationError, CACHE_1H_COMMAND_NAME, CLAUDE_DUMP_COMMAND_NAME, CLAUDE_FAST_COMMAND_NAME, CLAUDE_QUOTAS_COMMAND_NAME, ClaudeOAuthRefreshError, exchange, executeCache1hCommand, executeDumpCommand, executeFastModeCommand, FallbackAccountManager, fetchOAuthQuotaSnapshot, formatRefreshBackoffMessage, getCache1hMode, getCache1hPersistentMode, getQuotaCheckIntervalMs, getQuotaNextRefreshAt, getRelayConfig, hashRefreshToken, isCache1hEnabled, isCache1hPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModeEnabled, isFastModePersistentlyEnabled, isFastModeSupportedModel, loadAccounts, log, parseCache1hCommandAction, parseDumpCommandAction, parseFastModeCommandAction, quotaSnapshotPassesPolicy, refreshBackoffActive, refreshClaudeOAuthToken, resolveClaudeCodeIdentity, saveAccounts, sendViaRelay, setCache1hPersistentEnabled, setCache1hPersistentMode, setCache1hState, setDumpEnabled, setDumpPersistentEnabled, setFastModeEnabled, setFastModePersistentEnabled, shouldFallbackStatus, } from '@cortexkit/anthropic-auth-core';
|
|
2
3
|
import { resolvePromptContext } from "./prompt-context.js";
|
|
3
4
|
import { addFastModeBetaHeader, createStrippedStream, isInsecure, mergeHeaders, rewriteRequestBody, rewriteUrl, setOAuthHeaders, } from "./transform.js";
|
|
4
5
|
const HANDLED_SENTINEL = '__OPENCODE_ANTHROPIC_AUTH_COMMAND_HANDLED__';
|
|
@@ -273,7 +274,20 @@ export const AnthropicAuthPlugin = async (ctx) => {
|
|
|
273
274
|
refreshPromise = (async () => {
|
|
274
275
|
const maxRetries = 2;
|
|
275
276
|
const baseDelayMs = 500;
|
|
277
|
+
let leaseId = null;
|
|
278
|
+
let leaseTokenHash = null;
|
|
279
|
+
async function updateMainRefreshState(update) {
|
|
280
|
+
const storage = (await loadAccounts()) ?? {
|
|
281
|
+
version: 1,
|
|
282
|
+
main: { type: 'opencode', provider: 'anthropic' },
|
|
283
|
+
accounts: [],
|
|
284
|
+
};
|
|
285
|
+
storage.refresh = storage.refresh ?? {};
|
|
286
|
+
update(storage);
|
|
287
|
+
await saveAccounts(storage);
|
|
288
|
+
}
|
|
276
289
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
290
|
+
let freshAuth = null;
|
|
277
291
|
try {
|
|
278
292
|
if (attempt > 0) {
|
|
279
293
|
const delay = baseDelayMs * 2 ** (attempt - 1);
|
|
@@ -282,24 +296,43 @@ export const AnthropicAuthPlugin = async (ctx) => {
|
|
|
282
296
|
// Re-read auth to get the latest refresh token.
|
|
283
297
|
// The outer `auth` snapshot may be stale if tokens
|
|
284
298
|
// were rotated since the fetch() call was made.
|
|
285
|
-
|
|
299
|
+
freshAuth = await getAuth();
|
|
286
300
|
if (!freshAuth.refresh) {
|
|
287
301
|
throw new Error('Token refresh failed: missing refresh token');
|
|
288
302
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
303
|
+
const storage = await loadAccounts();
|
|
304
|
+
const refreshTokenHash = hashRefreshToken(freshAuth.refresh);
|
|
305
|
+
const mainError = storage?.refresh?.mainLastRefreshError;
|
|
306
|
+
if (mainError &&
|
|
307
|
+
refreshBackoffActive(mainError, freshAuth.refresh, Date.now())) {
|
|
308
|
+
throw new Error(formatRefreshBackoffMessage(mainError, Date.now()));
|
|
294
309
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
310
|
+
if (storage?.refresh?.mainRefreshLeaseUntil &&
|
|
311
|
+
storage.refresh.mainRefreshLeaseUntil > Date.now() &&
|
|
312
|
+
storage.refresh.mainRefreshLeaseTokenHash ===
|
|
313
|
+
refreshTokenHash) {
|
|
314
|
+
throw new Error('Claude OAuth refresh is already in progress');
|
|
315
|
+
}
|
|
316
|
+
leaseId = randomUUID();
|
|
317
|
+
leaseTokenHash = refreshTokenHash;
|
|
318
|
+
await updateMainRefreshState((nextStorage) => {
|
|
319
|
+
nextStorage.refresh = nextStorage.refresh ?? {};
|
|
320
|
+
nextStorage.refresh.mainRefreshLeaseId =
|
|
321
|
+
leaseId ?? undefined;
|
|
322
|
+
nextStorage.refresh.mainRefreshLeaseUntil =
|
|
323
|
+
Date.now() + 2 * 60_000;
|
|
324
|
+
nextStorage.refresh.mainRefreshLeaseTokenHash =
|
|
325
|
+
refreshTokenHash;
|
|
326
|
+
});
|
|
327
|
+
const latestLease = await loadAccounts();
|
|
328
|
+
if (latestLease?.refresh?.mainRefreshLeaseId !== leaseId ||
|
|
329
|
+
latestLease.refresh.mainRefreshLeaseTokenHash !==
|
|
330
|
+
refreshTokenHash) {
|
|
331
|
+
throw new Error('Claude OAuth refresh is already in progress');
|
|
302
332
|
}
|
|
333
|
+
const refreshed = await refreshClaudeOAuthToken({
|
|
334
|
+
refreshToken: freshAuth.refresh,
|
|
335
|
+
});
|
|
303
336
|
// biome-ignore lint/suspicious/noExplicitAny: SDK types don't expose auth.set
|
|
304
337
|
await client.auth.set({
|
|
305
338
|
path: {
|
|
@@ -312,6 +345,16 @@ export const AnthropicAuthPlugin = async (ctx) => {
|
|
|
312
345
|
expires: refreshed.expires,
|
|
313
346
|
},
|
|
314
347
|
});
|
|
348
|
+
await updateMainRefreshState((storage) => {
|
|
349
|
+
if (!storage?.refresh)
|
|
350
|
+
return;
|
|
351
|
+
storage.refresh.mainLastRefreshError = undefined;
|
|
352
|
+
if (storage.refresh.mainRefreshLeaseId === leaseId) {
|
|
353
|
+
storage.refresh.mainRefreshLeaseId = undefined;
|
|
354
|
+
storage.refresh.mainRefreshLeaseUntil = undefined;
|
|
355
|
+
storage.refresh.mainRefreshLeaseTokenHash = undefined;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
315
358
|
return refreshed.access;
|
|
316
359
|
}
|
|
317
360
|
catch (error) {
|
|
@@ -322,11 +365,43 @@ export const AnthropicAuthPlugin = async (ctx) => {
|
|
|
322
365
|
error.code === 'ECONNREFUSED' ||
|
|
323
366
|
error.code === 'ETIMEDOUT' ||
|
|
324
367
|
error.code === 'UND_ERR_CONNECT_TIMEOUT')));
|
|
325
|
-
if (attempt < maxRetries &&
|
|
368
|
+
if (attempt < maxRetries &&
|
|
369
|
+
(isNetworkError ||
|
|
370
|
+
(error instanceof ClaudeOAuthRefreshError &&
|
|
371
|
+
error.status >= 500))) {
|
|
326
372
|
continue;
|
|
327
373
|
}
|
|
374
|
+
const failedRefreshToken = freshAuth?.refresh;
|
|
375
|
+
if (failedRefreshToken &&
|
|
376
|
+
error instanceof ClaudeOAuthRefreshError) {
|
|
377
|
+
await updateMainRefreshState((storage) => {
|
|
378
|
+
storage.refresh = storage.refresh ?? {};
|
|
379
|
+
storage.refresh.mainLastRefreshError =
|
|
380
|
+
buildRefreshOperationError({
|
|
381
|
+
error,
|
|
382
|
+
now: Date.now(),
|
|
383
|
+
refreshToken: failedRefreshToken,
|
|
384
|
+
previous: storage.refresh.mainLastRefreshError,
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
}
|
|
328
388
|
throw error;
|
|
329
389
|
}
|
|
390
|
+
finally {
|
|
391
|
+
if (leaseId) {
|
|
392
|
+
await updateMainRefreshState((storage) => {
|
|
393
|
+
if (!storage?.refresh)
|
|
394
|
+
return;
|
|
395
|
+
if (storage.refresh.mainRefreshLeaseId === leaseId &&
|
|
396
|
+
storage.refresh.mainRefreshLeaseTokenHash ===
|
|
397
|
+
leaseTokenHash) {
|
|
398
|
+
storage.refresh.mainRefreshLeaseId = undefined;
|
|
399
|
+
storage.refresh.mainRefreshLeaseUntil = undefined;
|
|
400
|
+
storage.refresh.mainRefreshLeaseTokenHash = undefined;
|
|
401
|
+
}
|
|
402
|
+
}).catch(() => { });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
330
405
|
}
|
|
331
406
|
// Unreachable — each iteration either returns or throws.
|
|
332
407
|
// Kept as a TypeScript exhaustiveness guard.
|
|
@@ -356,6 +431,17 @@ export const AnthropicAuthPlugin = async (ctx) => {
|
|
|
356
431
|
mainRefreshBeforeExpiryMs(storage)) {
|
|
357
432
|
return;
|
|
358
433
|
}
|
|
434
|
+
if (latestAuth.refresh &&
|
|
435
|
+
refreshBackoffActive(storage?.refresh?.mainLastRefreshError, latestAuth.refresh, Date.now())) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
if (latestAuth.refresh &&
|
|
439
|
+
storage?.refresh?.mainRefreshLeaseUntil &&
|
|
440
|
+
storage.refresh.mainRefreshLeaseUntil > Date.now() &&
|
|
441
|
+
storage.refresh.mainRefreshLeaseTokenHash ===
|
|
442
|
+
hashRefreshToken(latestAuth.refresh)) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
359
445
|
await refreshMainAccessToken();
|
|
360
446
|
log('[refresh] opencode main oauth refreshed in background', {
|
|
361
447
|
expires: latestAuth.expires,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-anthropic-auth",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"@opencode-ai/plugin": "*"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@cortexkit/anthropic-auth-core": "1.1.
|
|
43
|
+
"@cortexkit/anthropic-auth-core": "1.1.1"
|
|
44
44
|
}
|
|
45
45
|
}
|