@cortexkit/anthropic-auth-core 1.19.1 → 1.21.0
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/README.md +1 -1
- package/dist/accounts.d.ts +81 -5
- package/dist/accounts.js +531 -81
- package/dist/auth.d.ts +2 -0
- package/dist/auth.js +3 -2
- package/dist/cache1h.d.ts +1 -1
- package/dist/cache1h.js +1 -1
- package/dist/cachekeep.d.ts +20 -2
- package/dist/cachekeep.js +134 -28
- package/dist/cch.d.ts +1 -0
- package/dist/cch.js +6 -1
- package/dist/claude-code.d.ts +5 -2
- package/dist/claude-code.js +127 -24
- package/dist/commands/account.js +1 -1
- package/dist/constants.d.ts +3 -3
- package/dist/dump.d.ts +13 -1
- package/dist/dump.js +171 -22
- package/dist/index.d.ts +3 -0
- package/dist/index.js +27 -24
- package/dist/killswitch.js +1 -1
- package/dist/models.d.ts +22 -6
- package/dist/models.js +16 -0
- package/dist/oauth-profile.d.ts +2 -1
- package/dist/oauth-profile.js +10 -4
- package/dist/prime.d.ts +219 -0
- package/dist/prime.js +823 -0
- package/dist/quota-header-feed.d.ts +40 -0
- package/dist/quota-header-feed.js +248 -0
- package/dist/quota-headers.js +6 -4
- package/dist/quota-manager.d.ts +41 -25
- package/dist/quota-manager.js +391 -151
- package/dist/quotas.d.ts +4 -4
- package/dist/quotas.js +2 -2
- package/dist/relay.d.ts +2 -0
- package/dist/relay.js +10 -4
- package/dist/routing.d.ts +1 -1
- package/dist/routing.js +1 -1
- package/dist/start.d.ts +13 -0
- package/dist/start.js +29 -0
- package/dist/sticky-routing.d.ts +15 -2
- package/dist/sticky-routing.js +44 -17
- package/package.json +1 -1
package/dist/quotas.d.ts
CHANGED
|
@@ -11,13 +11,13 @@ export type QuotaAccountSummary = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare function formatQuotaMoney(money: QuotaMoney): string;
|
|
13
13
|
export declare function buildFallbackQuotaSummaries(storage: AccountStorage | null, errors?: ReadonlyMap<string, string>): {
|
|
14
|
-
error?: string | undefined;
|
|
15
|
-
lastRefreshedAt: number | undefined;
|
|
16
|
-
tierLabel?: string | undefined;
|
|
17
14
|
name: string;
|
|
18
|
-
role:
|
|
15
|
+
role: 'fallback';
|
|
19
16
|
enabled: boolean;
|
|
20
17
|
quota: OAuthQuotaSnapshot | undefined;
|
|
18
|
+
tierLabel?: string | undefined;
|
|
19
|
+
lastRefreshedAt: number | undefined;
|
|
20
|
+
error?: string | undefined;
|
|
21
21
|
}[];
|
|
22
22
|
export declare function buildClaudeQuotaSummary(input: {
|
|
23
23
|
accounts: QuotaAccountSummary[];
|
package/dist/quotas.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isOAuthAccount } from
|
|
2
|
-
import { formatOAuthAccountTier } from
|
|
1
|
+
import { isOAuthAccount } from './accounts.js';
|
|
2
|
+
import { formatOAuthAccountTier } from './oauth-profile.js';
|
|
3
3
|
export const CLAUDE_QUOTAS_COMMAND_NAME = 'claude-quota';
|
|
4
4
|
const WINDOW_LABELS = {
|
|
5
5
|
five_hour: '5h',
|
package/dist/relay.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export declare function sendViaRelay(options: {
|
|
|
31
31
|
* within an attempt are ignored.
|
|
32
32
|
*/
|
|
33
33
|
onResponseHeaders?: (headers: Headers) => void;
|
|
34
|
+
dumpTag?: import('./dump.ts').DumpTag;
|
|
35
|
+
onDumpCreated?: (handle: import('./dump.ts').DumpHandle) => void;
|
|
34
36
|
setTimeoutImpl?: typeof globalThis.setTimeout;
|
|
35
37
|
clearTimeoutImpl?: typeof globalThis.clearTimeout;
|
|
36
38
|
}): Promise<Response>;
|
package/dist/relay.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { createHash, randomBytes } from 'node:crypto';
|
|
3
|
-
import { dumpRelayRequest } from
|
|
4
|
-
import { relayLog } from
|
|
3
|
+
import { dumpRelayRequest } from './dump.js';
|
|
4
|
+
import { relayLog } from './logger.js';
|
|
5
5
|
class RelayStateMismatchError extends Error {
|
|
6
6
|
}
|
|
7
7
|
class RelayWebSocketConnectionResetError extends Error {
|
|
@@ -752,7 +752,7 @@ function getPersistentRelaySession(config, affinity, timers) {
|
|
|
752
752
|
return session;
|
|
753
753
|
}
|
|
754
754
|
export async function sendViaRelay(options) {
|
|
755
|
-
const { config, input, init, headers, body, fallback, affinity: explicitAffinity, optimisticResponse, onResponseHeaders, setTimeoutImpl = globalThis.setTimeout, clearTimeoutImpl = globalThis.clearTimeout, } = options;
|
|
755
|
+
const { config, input, init, headers, body, fallback, affinity: explicitAffinity, optimisticResponse, onResponseHeaders, dumpTag, onDumpCreated, setTimeoutImpl = globalThis.setTimeout, clearTimeoutImpl = globalThis.clearTimeout, } = options;
|
|
756
756
|
if (!config || !isRelayableAnthropicRequest(input, body))
|
|
757
757
|
return fallback();
|
|
758
758
|
const affinity = explicitAffinity ||
|
|
@@ -848,7 +848,7 @@ export async function sendViaRelay(options) {
|
|
|
848
848
|
});
|
|
849
849
|
relayLog(`used relay transport=${result.transport} protocol=${result.protocol} mode=${result.payload.mode} status=${result.response.status} session=${shortAffinity(affinity)} bodyBytes=${bodyText.length} relayBytes=${actualPayloadBytes}`);
|
|
850
850
|
const dumpStart = perfNowMs();
|
|
851
|
-
await dumpRelayRequest({
|
|
851
|
+
const dumpHandle = await dumpRelayRequest({
|
|
852
852
|
affinity,
|
|
853
853
|
transport: result.transport,
|
|
854
854
|
protocol: result.protocol,
|
|
@@ -858,7 +858,13 @@ export async function sendViaRelay(options) {
|
|
|
858
858
|
previousBodyText: previous?.body,
|
|
859
859
|
payload: result.payload,
|
|
860
860
|
relayBytes: actualPayloadBytes,
|
|
861
|
+
tag: dumpTag,
|
|
861
862
|
});
|
|
863
|
+
try {
|
|
864
|
+
if (dumpHandle)
|
|
865
|
+
onDumpCreated?.(dumpHandle);
|
|
866
|
+
}
|
|
867
|
+
catch { }
|
|
862
868
|
relayPerfLog('dump', {
|
|
863
869
|
session: shortAffinity(affinity),
|
|
864
870
|
ms: formatMs(perfNowMs() - dumpStart),
|
package/dist/routing.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AccountStorage, type RoutingMode } from './accounts.ts';
|
|
2
2
|
export declare const CLAUDE_ROUTING_COMMAND_NAME = "claude-routing";
|
|
3
|
-
export declare const ROUTING_MODES: readonly [
|
|
3
|
+
export declare const ROUTING_MODES: readonly ['main-first', 'fallback-first', 'sticky-balanced'];
|
|
4
4
|
export declare const DEFAULT_ROUTING_MODE: RoutingMode;
|
|
5
5
|
type RoutingCommandAction = {
|
|
6
6
|
type: 'status';
|
package/dist/routing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createEmptyStorage, getAccountStoragePath, loadAccounts, saveAccounts, } from
|
|
1
|
+
import { createEmptyStorage, getAccountStoragePath, loadAccounts, saveAccounts, } from './accounts.js';
|
|
2
2
|
export const CLAUDE_ROUTING_COMMAND_NAME = 'claude-routing';
|
|
3
3
|
export const ROUTING_MODES = [
|
|
4
4
|
'main-first',
|
package/dist/start.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const CLAUDE_START_COMMAND_NAME = "claude-start";
|
|
2
|
+
export type LaneStartCommandAction = {
|
|
3
|
+
type: 'fire';
|
|
4
|
+
} | {
|
|
5
|
+
type: 'usage';
|
|
6
|
+
};
|
|
7
|
+
export declare function parseLaneStartCommandAction(input: string): LaneStartCommandAction;
|
|
8
|
+
export declare function executeLaneStartCommand(input: {
|
|
9
|
+
argumentsText: string;
|
|
10
|
+
}): {
|
|
11
|
+
action: LaneStartCommandAction;
|
|
12
|
+
text: string;
|
|
13
|
+
};
|
package/dist/start.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const CLAUDE_START_COMMAND_NAME = 'claude-start';
|
|
2
|
+
const START_STATUS_TITLE = '## Claude Lane Start Status';
|
|
3
|
+
const START_QUEUED_TITLE = '## Claude Lane Start Queued';
|
|
4
|
+
const START_USAGE_TITLE = '## Claude Lane Start Usage';
|
|
5
|
+
const START_USAGE = 'Usage: `/claude-start`.';
|
|
6
|
+
export function parseLaneStartCommandAction(input) {
|
|
7
|
+
const normalized = input.trim().split(/\s+/).filter(Boolean);
|
|
8
|
+
if (normalized.length === 0)
|
|
9
|
+
return { type: 'fire' };
|
|
10
|
+
return { type: 'usage' };
|
|
11
|
+
}
|
|
12
|
+
export function executeLaneStartCommand(input) {
|
|
13
|
+
const action = parseLaneStartCommandAction(input.argumentsText);
|
|
14
|
+
if (action.type === 'fire') {
|
|
15
|
+
return {
|
|
16
|
+
action,
|
|
17
|
+
text: [
|
|
18
|
+
START_QUEUED_TITLE,
|
|
19
|
+
'',
|
|
20
|
+
'- Queued an explicit lane-start request.',
|
|
21
|
+
'- This is a billed request: the one-token limit does not remove prompt-cache read or write charges.',
|
|
22
|
+
].join('\n'),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
action,
|
|
27
|
+
text: [START_USAGE_TITLE, '', START_USAGE, '', START_STATUS_TITLE].join('\n'),
|
|
28
|
+
};
|
|
29
|
+
}
|
package/dist/sticky-routing.d.ts
CHANGED
|
@@ -3,9 +3,21 @@ export declare const STICKY_ROUTING_MAIN_ACCOUNT_ID = "main";
|
|
|
3
3
|
export declare const STICKY_ROUTING_SHORT_RESET_GRACE_MS: number;
|
|
4
4
|
export declare const STICKY_ROUTING_ASSIGNMENT_TTL_MS: number;
|
|
5
5
|
export type StickyRouteFamily = 'fable' | 'opus' | 'general';
|
|
6
|
-
export type
|
|
6
|
+
export type IdentityState = {
|
|
7
|
+
kind: 'known';
|
|
7
8
|
accountId: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'unknown';
|
|
11
|
+
};
|
|
12
|
+
export type QuotaState = {
|
|
13
|
+
kind: 'known';
|
|
8
14
|
quota: OAuthQuotaSnapshot;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'unknown';
|
|
17
|
+
};
|
|
18
|
+
export type StickyRouteCandidate = {
|
|
19
|
+
accountId: string;
|
|
20
|
+
quota: OAuthQuotaSnapshot | QuotaState;
|
|
9
21
|
order: number;
|
|
10
22
|
};
|
|
11
23
|
export type StickyRouteAssignment = {
|
|
@@ -40,6 +52,7 @@ export type StickyQuotaFailureDecision = {
|
|
|
40
52
|
*/
|
|
41
53
|
export declare function createStickyNoRouteResponse(input: {
|
|
42
54
|
mainRefreshError?: AccountOperationError;
|
|
55
|
+
fallbackReauthLabels?: readonly string[];
|
|
43
56
|
routeQuotas: readonly OAuthQuotaSnapshot[];
|
|
44
57
|
modelId?: string;
|
|
45
58
|
now?: number;
|
|
@@ -55,7 +68,7 @@ export declare function stickyRouteCandidateWeight(input: {
|
|
|
55
68
|
}): number;
|
|
56
69
|
export declare function stickyRouteKnownFableExhausted(candidate: StickyRouteCandidate, storage: AccountStorage | null, now?: number): boolean;
|
|
57
70
|
export declare function decideStickyQuotaFailure(input: {
|
|
58
|
-
quota: OAuthQuotaSnapshot | undefined;
|
|
71
|
+
quota: OAuthQuotaSnapshot | QuotaState | undefined;
|
|
59
72
|
modelId?: string;
|
|
60
73
|
now?: number;
|
|
61
74
|
}): StickyQuotaFailureDecision;
|
package/dist/sticky-routing.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { basename, dirname, join } from 'node:path';
|
|
4
|
-
import { acquireRefreshFileLock, getKillswitchThresholdsForAccount, getQuotaCheckIntervalMs, getQuotaMinimumRemainingThresholds, getScopedQuotaWindowForModel, isKillswitchEnabled, isPermanentRefreshError, killswitchRetryAfterSeconds, quotaSnapshotPassesModelScope, } from
|
|
5
|
-
import { isClaudeFableOrMythos5Model } from
|
|
4
|
+
import { acquireRefreshFileLock, getKillswitchThresholdsForAccount, getQuotaCheckIntervalMs, getQuotaMinimumRemainingThresholds, getScopedQuotaWindowForModel, isKillswitchEnabled, isPermanentRefreshError, killswitchRetryAfterSeconds, quotaSnapshotPassesModelScope, } from './accounts.js';
|
|
5
|
+
import { isClaudeFableOrMythos5Model } from './models.js';
|
|
6
6
|
export const STICKY_ROUTING_MAIN_ACCOUNT_ID = 'main';
|
|
7
7
|
export const STICKY_ROUTING_SHORT_RESET_GRACE_MS = 15 * 60_000;
|
|
8
8
|
export const STICKY_ROUTING_ASSIGNMENT_TTL_MS = 7 * 24 * 60 * 60_000;
|
|
@@ -24,13 +24,23 @@ export function createStickyNoRouteResponse(input) {
|
|
|
24
24
|
.map((quota) => getScopedQuotaWindowForModel(quota, modelId))
|
|
25
25
|
.find(Boolean)?.modelName
|
|
26
26
|
: undefined;
|
|
27
|
+
let authenticationMessage;
|
|
27
28
|
if (isPermanentRefreshError(input.mainRefreshError)) {
|
|
28
|
-
|
|
29
|
+
authenticationMessage = modelName
|
|
29
30
|
? `Main Claude OAuth account requires re-login, and no fallback OAuth account is currently routable for ${modelName}.`
|
|
30
31
|
: 'Main Claude OAuth account requires re-login, and no fallback OAuth account is currently routable.';
|
|
32
|
+
}
|
|
33
|
+
else if (input.fallbackReauthLabels?.length) {
|
|
34
|
+
const scope = modelName ? ` for ${modelName}` : '';
|
|
35
|
+
authenticationMessage = `No OAuth account is currently routable${scope}. Fallback Claude OAuth accounts require re-login: ${input.fallbackReauthLabels.join(', ')}.`;
|
|
36
|
+
}
|
|
37
|
+
if (authenticationMessage) {
|
|
31
38
|
return new Response(JSON.stringify({
|
|
32
39
|
type: 'error',
|
|
33
|
-
error: {
|
|
40
|
+
error: {
|
|
41
|
+
type: 'authentication_error',
|
|
42
|
+
message: authenticationMessage,
|
|
43
|
+
},
|
|
34
44
|
}), {
|
|
35
45
|
status: 401,
|
|
36
46
|
headers: { 'content-type': 'application/json' },
|
|
@@ -126,6 +136,13 @@ export function stickyQuotaSnapshotIsFresh(quota, storage, now = Date.now(), mod
|
|
|
126
136
|
function snapshotCheckedAt(quota) {
|
|
127
137
|
return Math.max(quota.checkedAt ?? 0, quota.five_hour?.checkedAt ?? 0, quota.seven_day?.checkedAt ?? 0, ...(quota.scoped ?? []).map((window) => window.checkedAt ?? 0));
|
|
128
138
|
}
|
|
139
|
+
function knownQuota(quota) {
|
|
140
|
+
if (!quota)
|
|
141
|
+
return undefined;
|
|
142
|
+
if ('kind' in quota)
|
|
143
|
+
return quota.kind === 'known' ? quota.quota : undefined;
|
|
144
|
+
return quota;
|
|
145
|
+
}
|
|
129
146
|
function remainingThreshold(storage, accountId, key) {
|
|
130
147
|
const quotaMinimum = getQuotaMinimumRemainingThresholds(storage);
|
|
131
148
|
const quotaThreshold = key === 'five_hour'
|
|
@@ -160,18 +177,21 @@ export function stickyRouteFamilyForModel(model) {
|
|
|
160
177
|
export function stickyRouteCandidateWeight(input) {
|
|
161
178
|
const now = input.now ?? Date.now();
|
|
162
179
|
const { candidate, storage } = input;
|
|
163
|
-
|
|
180
|
+
const quota = knownQuota(candidate.quota);
|
|
181
|
+
if (!quota)
|
|
182
|
+
return 0;
|
|
183
|
+
if (!stickyQuotaSnapshotIsFresh(quota, storage, now, input.modelId)) {
|
|
164
184
|
return 0;
|
|
165
185
|
}
|
|
166
186
|
const weights = [];
|
|
167
187
|
for (const key of ['five_hour', 'seven_day']) {
|
|
168
|
-
const window =
|
|
188
|
+
const window = quota[key];
|
|
169
189
|
if (!window || !Number.isFinite(window.remainingPercent))
|
|
170
190
|
return 0;
|
|
171
191
|
weights.push(sustainableWindowWeight(window.remainingPercent, remainingThreshold(storage, candidate.accountId, key), window.resetsAt, now));
|
|
172
192
|
}
|
|
173
193
|
if (input.family === 'fable') {
|
|
174
|
-
const window = getScopedQuotaWindowForModel(
|
|
194
|
+
const window = getScopedQuotaWindowForModel(quota, input.modelId);
|
|
175
195
|
if (window && Number.isFinite(window.remainingPercent)) {
|
|
176
196
|
weights.push(sustainableWindowWeight(window.remainingPercent, remainingThreshold(storage, candidate.accountId, 'scoped'), window.resetsAt, now));
|
|
177
197
|
}
|
|
@@ -179,29 +199,31 @@ export function stickyRouteCandidateWeight(input) {
|
|
|
179
199
|
return Math.min(...weights);
|
|
180
200
|
}
|
|
181
201
|
export function stickyRouteKnownFableExhausted(candidate, storage, now = Date.now()) {
|
|
182
|
-
const
|
|
202
|
+
const quota = knownQuota(candidate.quota);
|
|
203
|
+
const window = getScopedQuotaWindowForModel(quota, 'claude-fable-5');
|
|
183
204
|
return Boolean(window &&
|
|
184
|
-
stickyQuotaSnapshotIsFresh(
|
|
205
|
+
stickyQuotaSnapshotIsFresh(quota, storage, now, 'claude-fable-5') &&
|
|
185
206
|
Number.isFinite(window.remainingPercent) &&
|
|
186
207
|
window.remainingPercent <= 0);
|
|
187
208
|
}
|
|
188
209
|
export function decideStickyQuotaFailure(input) {
|
|
189
210
|
const now = input.now ?? Date.now();
|
|
190
|
-
|
|
211
|
+
const quota = knownQuota(input.quota);
|
|
212
|
+
if (!quota)
|
|
191
213
|
return { action: 'retain', reason: 'unknown' };
|
|
192
|
-
const scoped = getScopedQuotaWindowForModel(
|
|
214
|
+
const scoped = getScopedQuotaWindowForModel(quota, input.modelId);
|
|
193
215
|
if (scoped &&
|
|
194
216
|
Number.isFinite(scoped.remainingPercent) &&
|
|
195
217
|
scoped.remainingPercent <= 0) {
|
|
196
218
|
return { action: 'migrate', reason: 'model-scoped' };
|
|
197
219
|
}
|
|
198
|
-
const sevenDay =
|
|
220
|
+
const sevenDay = quota.seven_day;
|
|
199
221
|
if (sevenDay &&
|
|
200
222
|
Number.isFinite(sevenDay.remainingPercent) &&
|
|
201
223
|
sevenDay.remainingPercent <= 0) {
|
|
202
224
|
return { action: 'migrate', reason: 'seven-day' };
|
|
203
225
|
}
|
|
204
|
-
const fiveHour =
|
|
226
|
+
const fiveHour = quota.five_hour;
|
|
205
227
|
if (fiveHour &&
|
|
206
228
|
Number.isFinite(fiveHour.remainingPercent) &&
|
|
207
229
|
fiveHour.remainingPercent <= 0) {
|
|
@@ -376,14 +398,18 @@ export class StickySessionRouter {
|
|
|
376
398
|
});
|
|
377
399
|
return weight > 0 ? [{ candidate, weight }] : [];
|
|
378
400
|
});
|
|
379
|
-
if (weighted.length === 0
|
|
380
|
-
|
|
401
|
+
if (weighted.length === 0 &&
|
|
402
|
+
candidates.every((candidate) => !knownQuota(candidate.quota))) {
|
|
403
|
+
return [...candidates].sort((left, right) => left.order - right.order ||
|
|
404
|
+
left.accountId.localeCompare(right.accountId))[0];
|
|
405
|
+
}
|
|
381
406
|
const pendingBytes = new Map();
|
|
382
407
|
for (const assignment of Object.values(input.state.assignments)) {
|
|
383
408
|
const candidate = weighted.find((entry) => entry.candidate.accountId === assignment.accountId)?.candidate;
|
|
384
409
|
if (!candidate)
|
|
385
410
|
continue;
|
|
386
|
-
|
|
411
|
+
const quota = knownQuota(candidate.quota);
|
|
412
|
+
if (quota && assignment.quotaCheckedAt !== snapshotCheckedAt(quota))
|
|
387
413
|
continue;
|
|
388
414
|
pendingBytes.set(assignment.accountId, (pendingBytes.get(assignment.accountId) ?? 0) +
|
|
389
415
|
assignment.initialInputBytes);
|
|
@@ -456,13 +482,14 @@ export class StickySessionRouter {
|
|
|
456
482
|
return null;
|
|
457
483
|
}
|
|
458
484
|
const now = this.now();
|
|
485
|
+
const selectedQuota = knownQuota(selected.quota);
|
|
459
486
|
const assignment = {
|
|
460
487
|
accountId: selected.accountId,
|
|
461
488
|
family: input.family,
|
|
462
489
|
assignedAt: now,
|
|
463
490
|
lastSeenAt: now,
|
|
464
491
|
initialInputBytes: Math.max(1, input.inputBytes),
|
|
465
|
-
quotaCheckedAt: snapshotCheckedAt(
|
|
492
|
+
quotaCheckedAt: selectedQuota ? snapshotCheckedAt(selectedQuota) : 0,
|
|
466
493
|
};
|
|
467
494
|
state.assignments[key] = assignment;
|
|
468
495
|
await this.writeState(state);
|