@cortexkit/anthropic-auth-core 1.1.1 → 1.1.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @cortexkit/anthropic-auth-core
2
2
 
3
- Shared Anthropic OAuth, quota, cache, fast mode, relay, dump, and request-signing helpers used by CortexKit's OpenCode and Pi integrations.
3
+ Shared Anthropic OAuth, quota, cache, cache-keepalive, fast mode, relay, dump, and request-signing helpers used by CortexKit's OpenCode and Pi integrations.
4
4
 
5
5
  User-facing packages:
6
6
 
@@ -62,6 +62,11 @@ export type AccountStorage = {
62
62
  claudeFast?: {
63
63
  enabled?: boolean;
64
64
  };
65
+ cacheKeep?: {
66
+ enabled?: boolean;
67
+ startHour?: number;
68
+ endHour?: number;
69
+ };
65
70
  relay?: {
66
71
  enabled?: boolean;
67
72
  url?: string;
@@ -92,6 +97,8 @@ export declare function isDumpPersistentlyEnabled(storage: AccountStorage | null
92
97
  export declare function setDumpPersistentEnabled(enabled: boolean, path?: string): Promise<AccountStorage>;
93
98
  export declare function isFastModePersistentlyEnabled(storage: AccountStorage | null): boolean;
94
99
  export declare function setFastModePersistentEnabled(enabled: boolean, path?: string): Promise<AccountStorage>;
100
+ export declare function setCacheKeepPersistentWindow(startHour: number, endHour: number, path?: string): Promise<AccountStorage>;
101
+ export declare function setCacheKeepPersistentEnabled(enabled: boolean, path?: string): Promise<AccountStorage>;
95
102
  export declare function shouldFallbackStatus(status: number, storage: AccountStorage | null): boolean;
96
103
  export declare function getRefreshIntervalMs(storage: AccountStorage | null): number;
97
104
  export declare function hashRefreshToken(refreshToken: string): string;
package/dist/accounts.js CHANGED
@@ -4,6 +4,7 @@ import { homedir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { ClaudeOAuthRefreshError, refreshClaudeOAuthToken } from "./auth.js";
6
6
  import { CACHE_1H_MODES, CLAUDE_CODE_VERSION, DEFAULT_CACHE_1H_MODE, } from "./constants.js";
7
+ import { log } from "./logger.js";
7
8
  export const ACCOUNT_FILE_NAME = 'anthropic-auth.json';
8
9
  export const QUOTA_URL = 'https://api.anthropic.com/api/oauth/usage';
9
10
  const DEFAULT_FALLBACK_ON = [401, 403, 429];
@@ -116,6 +117,7 @@ function normalizeStorage(value) {
116
117
  claudeCache: isRecord(value.claudeCache) ? value.claudeCache : undefined,
117
118
  dump: isRecord(value.dump) ? value.dump : undefined,
118
119
  claudeFast: isRecord(value.claudeFast) ? value.claudeFast : undefined,
120
+ cacheKeep: isRecord(value.cacheKeep) ? value.cacheKeep : undefined,
119
121
  relay: isRecord(value.relay) ? value.relay : undefined,
120
122
  accounts: value.accounts
121
123
  .map(normalizeAccount)
@@ -229,6 +231,33 @@ export async function setFastModePersistentEnabled(enabled, path = getAccountSto
229
231
  await saveAccounts(storage, path);
230
232
  return storage;
231
233
  }
234
+ export async function setCacheKeepPersistentWindow(startHour, endHour, path = getAccountStoragePath()) {
235
+ const storage = (await loadAccounts(path)) ?? {
236
+ version: 1,
237
+ main: { type: 'opencode', provider: 'anthropic' },
238
+ accounts: [],
239
+ };
240
+ storage.cacheKeep = {
241
+ enabled: true,
242
+ startHour,
243
+ endHour,
244
+ };
245
+ await saveAccounts(storage, path);
246
+ return storage;
247
+ }
248
+ export async function setCacheKeepPersistentEnabled(enabled, path = getAccountStoragePath()) {
249
+ const storage = (await loadAccounts(path)) ?? {
250
+ version: 1,
251
+ main: { type: 'opencode', provider: 'anthropic' },
252
+ accounts: [],
253
+ };
254
+ storage.cacheKeep = {
255
+ ...(storage.cacheKeep ?? {}),
256
+ enabled,
257
+ };
258
+ await saveAccounts(storage, path);
259
+ return storage;
260
+ }
232
261
  function getFallbackStatuses(storage) {
233
262
  return storage?.fallbackOn?.length ? storage.fallbackOn : DEFAULT_FALLBACK_ON;
234
263
  }
@@ -278,7 +307,11 @@ function isTransientRefreshError(error) {
278
307
  error.code === 'UND_ERR_CONNECT_TIMEOUT')));
279
308
  }
280
309
  export function buildRefreshOperationError(input) {
281
- const retryCount = (input.previous?.retryCount ?? 0) + 1;
310
+ const tokenHash = hashRefreshToken(input.refreshToken);
311
+ const previousRetryCount = input.previous?.tokenHash === tokenHash
312
+ ? (input.previous.retryCount ?? 0)
313
+ : 0;
314
+ const retryCount = previousRetryCount + 1;
282
315
  const delay = isTransientRefreshError(input.error)
283
316
  ? Math.min(MAX_REFRESH_RETRY_DELAY_MS, MIN_REFRESH_RETRY_DELAY_MS * 2 ** Math.min(retryCount - 1, 6))
284
317
  : NON_TRANSIENT_REFRESH_RETRY_DELAY_MS;
@@ -287,7 +320,7 @@ export function buildRefreshOperationError(input) {
287
320
  checkedAt: input.now,
288
321
  nextRetryAt: input.now + delay,
289
322
  retryCount,
290
- tokenHash: hashRefreshToken(input.refreshToken),
323
+ tokenHash,
291
324
  };
292
325
  }
293
326
  export function refreshBackoffActive(error, refreshToken, now) {
@@ -525,13 +558,28 @@ export class FallbackAccountManager {
525
558
  if (!tokenNeedsRefresh(account, storage, this.now()))
526
559
  continue;
527
560
  if (refreshBackoffActive(account.lastRefreshError, account.refresh, this.now())) {
561
+ log('[refresh] fallback oauth skipped backoff', {
562
+ accountId: account.id,
563
+ nextRetryAt: account.lastRefreshError?.nextRetryAt,
564
+ retryCount: account.lastRefreshError?.retryCount,
565
+ });
528
566
  continue;
529
567
  }
530
568
  try {
569
+ log('[refresh] fallback oauth background due', {
570
+ accountId: account.id,
571
+ expiresInMs: account.expires
572
+ ? account.expires - this.now()
573
+ : undefined,
574
+ });
531
575
  await this.refreshAccount(account, storage);
532
576
  changed = true;
533
577
  }
534
578
  catch (error) {
579
+ log('[refresh] fallback oauth background failed', {
580
+ accountId: account.id,
581
+ error: error instanceof Error ? error.message : String(error),
582
+ });
535
583
  recordRefreshError(account, error, this.now());
536
584
  updateStoredAccount(storage, account);
537
585
  changed = true;
@@ -636,6 +684,13 @@ export class FallbackAccountManager {
636
684
  }
637
685
  const sourceAccount = latestAccount ?? account;
638
686
  const refreshToken = sourceAccount.refresh;
687
+ log('[refresh] fallback oauth refresh request start', {
688
+ accountId: sourceAccount.id,
689
+ force: options.force === true,
690
+ expiresInMs: sourceAccount.expires
691
+ ? sourceAccount.expires - this.now()
692
+ : undefined,
693
+ });
639
694
  const refreshed = await refreshClaudeOAuthToken({
640
695
  refreshToken,
641
696
  fetchImpl: this.fetchImpl,
@@ -648,6 +703,12 @@ export class FallbackAccountManager {
648
703
  refreshed.expires - refreshed.expiresIn * 1000;
649
704
  sourceAccount.lastRefreshError = undefined;
650
705
  updateStoredAccount(storage, sourceAccount);
706
+ log('[refresh] fallback oauth refresh succeeded', {
707
+ accountId: sourceAccount.id,
708
+ expiresInMs: sourceAccount.expires
709
+ ? sourceAccount.expires - this.now()
710
+ : undefined,
711
+ });
651
712
  return sourceAccount;
652
713
  }
653
714
  async refreshAccountQuota(account, storage) {
@@ -0,0 +1,92 @@
1
+ import type { AccountStorage } from './accounts.ts';
2
+ export declare const CLAUDE_CACHE_KEEP_COMMAND_NAME = "claude-cachekeep";
3
+ export declare const CACHE_KEEP_TTL_MS: number;
4
+ export declare const CACHE_KEEP_PREWARM_LEAD_MS: number;
5
+ export declare const CACHE_KEEP_TICK_MS = 60000;
6
+ export declare const CACHE_KEEP_EXTENDED_TTL_BETA = "extended-cache-ttl-2025-04-11";
7
+ export type CacheKeepWindow = {
8
+ startHour: number;
9
+ endHour: number;
10
+ };
11
+ export type CacheKeepCommandAction = {
12
+ type: 'status';
13
+ } | {
14
+ type: 'disable';
15
+ } | {
16
+ type: 'window';
17
+ startHour: number;
18
+ endHour: number;
19
+ } | {
20
+ type: 'usage';
21
+ };
22
+ export type CacheKeepStatus = {
23
+ enabled: boolean;
24
+ window?: CacheKeepWindow;
25
+ trackedSessions?: number;
26
+ nextPrewarmAt?: number;
27
+ hybridActive?: boolean;
28
+ };
29
+ export declare function parseCacheKeepCommandAction(input: string): CacheKeepCommandAction;
30
+ export declare function getCacheKeepWindow(storage: AccountStorage | null): CacheKeepWindow | undefined;
31
+ export declare function isCacheKeepPersistentlyEnabled(storage: AccountStorage | null): boolean;
32
+ export declare function isCacheKeepHybridActive(storage: AccountStorage | null): boolean;
33
+ export declare function isWithinCacheKeepWindow(window: CacheKeepWindow | undefined, now?: Date): boolean;
34
+ export declare function localDayKey(now?: Date): string;
35
+ export declare function buildCacheKeepStatusSummary(status: CacheKeepStatus): string;
36
+ export declare function executeCacheKeepCommand(input: {
37
+ argumentsText: string;
38
+ enabled?: boolean;
39
+ window?: CacheKeepWindow;
40
+ trackedSessions?: number;
41
+ nextPrewarmAt?: number;
42
+ hybridActive?: boolean;
43
+ }): string;
44
+ export declare function buildCacheKeepPrewarmBody(bodyText: string): Promise<{
45
+ ok: true;
46
+ bodyText: string;
47
+ } | {
48
+ ok: false;
49
+ reason: string;
50
+ }>;
51
+ export type CacheKeepTarget = {
52
+ id: string;
53
+ url: string;
54
+ headers: Record<string, string>;
55
+ bodyText: string;
56
+ cacheExpiresAt: number;
57
+ dayKey: string;
58
+ };
59
+ export declare class CacheKeepManager {
60
+ private readonly options;
61
+ private readonly targets;
62
+ private timer;
63
+ constructor(options: {
64
+ loadStorage: () => Promise<AccountStorage | null>;
65
+ fetchImpl?: typeof fetch;
66
+ now?: () => number;
67
+ prepareHeaders?: (headers: Headers, target: CacheKeepTarget) => Promise<Headers> | Headers;
68
+ log?: (message: string, data?: Record<string, unknown>) => void;
69
+ });
70
+ start(): void;
71
+ stop(): void;
72
+ stats(window?: CacheKeepWindow, now?: number): {
73
+ trackedSessions: number;
74
+ nextPrewarmAt: number | undefined;
75
+ };
76
+ track(input: {
77
+ sessionId?: string | null;
78
+ url: string;
79
+ headers: Headers;
80
+ bodyText: string;
81
+ storage: AccountStorage | null;
82
+ cacheMode: string;
83
+ }): {
84
+ tracked: boolean;
85
+ reason: string;
86
+ } | {
87
+ tracked: boolean;
88
+ reason?: undefined;
89
+ };
90
+ tick(): Promise<void>;
91
+ private prewarm;
92
+ }
@@ -0,0 +1,297 @@
1
+ import { CCH_PATTERN, signRequestBody } from "./cch.js";
2
+ import { orderClaudeCodeBody } from "./claude-code.js";
3
+ export const CLAUDE_CACHE_KEEP_COMMAND_NAME = 'claude-cachekeep';
4
+ export const CACHE_KEEP_TTL_MS = 60 * 60_000;
5
+ export const CACHE_KEEP_PREWARM_LEAD_MS = 5 * 60_000;
6
+ export const CACHE_KEEP_TICK_MS = 60_000;
7
+ export const CACHE_KEEP_EXTENDED_TTL_BETA = 'extended-cache-ttl-2025-04-11';
8
+ const STATUS_TITLE = '## Claude Cache Keep Status';
9
+ const ENABLED_TITLE = '## Claude Cache Keep Enabled';
10
+ const DISABLED_TITLE = '## Claude Cache Keep Disabled';
11
+ const USAGE_TITLE = '## Claude Cache Keep Usage';
12
+ const USAGE = 'Usage: `/claude-cachekeep`, `/claude-cachekeep off`, or `/claude-cachekeep HH-HH`.';
13
+ function padHour(hour) {
14
+ return String(hour).padStart(2, '0');
15
+ }
16
+ export function parseCacheKeepCommandAction(input) {
17
+ const trimmed = input.trim();
18
+ if (!trimmed)
19
+ return { type: 'status' };
20
+ if (trimmed === 'off')
21
+ return { type: 'disable' };
22
+ const match = /^(\d{1,2})-(\d{1,2})$/.exec(trimmed);
23
+ if (!match)
24
+ return { type: 'usage' };
25
+ const startHour = Number(match[1]);
26
+ const endHour = Number(match[2]);
27
+ if (!Number.isInteger(startHour) ||
28
+ !Number.isInteger(endHour) ||
29
+ startHour < 0 ||
30
+ startHour > 23 ||
31
+ endHour < 0 ||
32
+ endHour > 23 ||
33
+ startHour === endHour) {
34
+ return { type: 'usage' };
35
+ }
36
+ return { type: 'window', startHour, endHour };
37
+ }
38
+ export function getCacheKeepWindow(storage) {
39
+ const startHour = Number(storage?.cacheKeep?.startHour);
40
+ const endHour = Number(storage?.cacheKeep?.endHour);
41
+ if (!Number.isInteger(startHour) ||
42
+ !Number.isInteger(endHour) ||
43
+ startHour < 0 ||
44
+ startHour > 23 ||
45
+ endHour < 0 ||
46
+ endHour > 23 ||
47
+ startHour === endHour) {
48
+ return undefined;
49
+ }
50
+ return { startHour, endHour };
51
+ }
52
+ export function isCacheKeepPersistentlyEnabled(storage) {
53
+ return storage?.cacheKeep?.enabled === true && !!getCacheKeepWindow(storage);
54
+ }
55
+ export function isCacheKeepHybridActive(storage) {
56
+ return (isCacheKeepPersistentlyEnabled(storage) &&
57
+ storage?.claudeCache?.enabled === true &&
58
+ storage.claudeCache.mode === 'hybrid');
59
+ }
60
+ export function isWithinCacheKeepWindow(window, now = new Date()) {
61
+ if (!window)
62
+ return false;
63
+ const hour = now.getHours();
64
+ if (window.startHour < window.endHour) {
65
+ return hour >= window.startHour && hour < window.endHour;
66
+ }
67
+ return hour >= window.startHour || hour < window.endHour;
68
+ }
69
+ export function localDayKey(now = new Date()) {
70
+ return `${now.getFullYear()}-${padHour(now.getMonth() + 1)}-${padHour(now.getDate())}`;
71
+ }
72
+ function localWindowKey(window, now = new Date()) {
73
+ if (window &&
74
+ window.startHour > window.endHour &&
75
+ now.getHours() < window.endHour) {
76
+ const previous = new Date(now);
77
+ previous.setDate(previous.getDate() - 1);
78
+ return localDayKey(previous);
79
+ }
80
+ return localDayKey(now);
81
+ }
82
+ export function buildCacheKeepStatusSummary(status) {
83
+ const window = status.window
84
+ ? `${padHour(status.window.startHour)}-${padHour(status.window.endHour)}`
85
+ : 'not configured';
86
+ const lines = [
87
+ `Cache keep: ${status.enabled ? 'enabled' : 'disabled'}`,
88
+ `Window: ${window}`,
89
+ 'Mode requirement: `/claude-cache mode hybrid` must be active.',
90
+ ];
91
+ if (typeof status.hybridActive === 'boolean') {
92
+ lines.push(`Hybrid active: ${status.hybridActive ? 'yes' : 'no'}`);
93
+ }
94
+ if (typeof status.trackedSessions === 'number') {
95
+ lines.push(`Tracked sessions: ${status.trackedSessions}`);
96
+ }
97
+ if (status.nextPrewarmAt) {
98
+ lines.push(`Next prewarm: ${new Date(status.nextPrewarmAt).toLocaleString()}`);
99
+ }
100
+ return lines.join('\n');
101
+ }
102
+ export function executeCacheKeepCommand(input) {
103
+ const action = parseCacheKeepCommandAction(input.argumentsText);
104
+ const status = {
105
+ enabled: input.enabled ?? false,
106
+ window: input.window,
107
+ trackedSessions: input.trackedSessions,
108
+ nextPrewarmAt: input.nextPrewarmAt,
109
+ hybridActive: input.hybridActive,
110
+ };
111
+ if (action.type === 'status') {
112
+ return [STATUS_TITLE, '', buildCacheKeepStatusSummary(status)].join('\n');
113
+ }
114
+ if (action.type === 'disable') {
115
+ return [
116
+ DISABLED_TITLE,
117
+ '',
118
+ buildCacheKeepStatusSummary({ ...status, enabled: false }),
119
+ ].join('\n');
120
+ }
121
+ if (action.type === 'window') {
122
+ return [
123
+ ENABLED_TITLE,
124
+ '',
125
+ buildCacheKeepStatusSummary({
126
+ ...status,
127
+ enabled: true,
128
+ window: { startHour: action.startHour, endHour: action.endHour },
129
+ }),
130
+ ].join('\n');
131
+ }
132
+ return [USAGE_TITLE, '', USAGE, '', buildCacheKeepStatusSummary(status)].join('\n');
133
+ }
134
+ function hasExplicitCacheControl(value) {
135
+ if (!value || typeof value !== 'object')
136
+ return false;
137
+ if (Array.isArray(value))
138
+ return value.some(hasExplicitCacheControl);
139
+ const record = value;
140
+ const cacheControl = record.cache_control;
141
+ if (cacheControl &&
142
+ typeof cacheControl === 'object' &&
143
+ cacheControl.type === 'ephemeral') {
144
+ return true;
145
+ }
146
+ return Object.values(record).some(hasExplicitCacheControl);
147
+ }
148
+ export async function buildCacheKeepPrewarmBody(bodyText) {
149
+ let body;
150
+ try {
151
+ body = JSON.parse(bodyText);
152
+ }
153
+ catch {
154
+ return { ok: false, reason: 'body is not valid JSON' };
155
+ }
156
+ if (!hasExplicitCacheControl(body)) {
157
+ return { ok: false, reason: 'body has no explicit cache breakpoints' };
158
+ }
159
+ const warm = structuredClone(body);
160
+ warm.max_tokens = 0;
161
+ delete warm.stream;
162
+ const thinking = warm.thinking;
163
+ if (thinking?.type === 'enabled')
164
+ delete warm.thinking;
165
+ const outputConfig = warm.output_config;
166
+ if (outputConfig?.format)
167
+ delete warm.output_config;
168
+ const toolChoice = warm.tool_choice;
169
+ if (toolChoice?.type === 'tool' || toolChoice?.type === 'any') {
170
+ delete warm.tool_choice;
171
+ }
172
+ const unsignedBodyText = JSON.stringify(orderClaudeCodeBody(warm)).replace(CCH_PATTERN, 'cch=00000;');
173
+ const signedBodyText = await signRequestBody(unsignedBodyText);
174
+ return { ok: true, bodyText: signedBodyText };
175
+ }
176
+ export class CacheKeepManager {
177
+ options;
178
+ targets = new Map();
179
+ timer = null;
180
+ constructor(options) {
181
+ this.options = options;
182
+ }
183
+ start() {
184
+ if (this.timer)
185
+ return;
186
+ this.timer = setInterval(() => {
187
+ void this.tick().catch((error) => {
188
+ this.options.log?.('[cachekeep] tick failed', {
189
+ error: error instanceof Error ? error.message : String(error),
190
+ });
191
+ });
192
+ }, CACHE_KEEP_TICK_MS);
193
+ if ('unref' in this.timer)
194
+ this.timer.unref();
195
+ }
196
+ stop() {
197
+ if (this.timer)
198
+ clearInterval(this.timer);
199
+ this.timer = null;
200
+ }
201
+ stats(window, now = this.options.now?.() ?? Date.now()) {
202
+ const today = localWindowKey(window, new Date(now));
203
+ const targets = [...this.targets.values()].filter((target) => target.dayKey === today);
204
+ const nextPrewarmAt = targets.length
205
+ ? Math.min(...targets.map((target) => target.cacheExpiresAt - CACHE_KEEP_PREWARM_LEAD_MS))
206
+ : undefined;
207
+ return { trackedSessions: targets.length, nextPrewarmAt };
208
+ }
209
+ track(input) {
210
+ if (!input.sessionId)
211
+ return { tracked: false, reason: 'missing session id' };
212
+ if (!isCacheKeepPersistentlyEnabled(input.storage)) {
213
+ return { tracked: false, reason: 'cachekeep disabled' };
214
+ }
215
+ if (input.cacheMode !== 'hybrid') {
216
+ return { tracked: false, reason: 'cache mode is not hybrid' };
217
+ }
218
+ const now = this.options.now?.() ?? Date.now();
219
+ const window = getCacheKeepWindow(input.storage);
220
+ if (!isWithinCacheKeepWindow(window, new Date(now))) {
221
+ return { tracked: false, reason: 'outside configured window' };
222
+ }
223
+ const headers = {};
224
+ input.headers.forEach((value, key) => {
225
+ headers[key] = value;
226
+ });
227
+ this.targets.set(input.sessionId, {
228
+ id: input.sessionId,
229
+ url: input.url,
230
+ headers,
231
+ bodyText: input.bodyText,
232
+ cacheExpiresAt: now + CACHE_KEEP_TTL_MS,
233
+ dayKey: localWindowKey(window, new Date(now)),
234
+ });
235
+ this.start();
236
+ return { tracked: true };
237
+ }
238
+ async tick() {
239
+ const storage = await this.options.loadStorage();
240
+ const window = getCacheKeepWindow(storage);
241
+ const now = this.options.now?.() ?? Date.now();
242
+ const today = localWindowKey(window, new Date(now));
243
+ for (const [id, target] of this.targets) {
244
+ if (target.dayKey !== today)
245
+ this.targets.delete(id);
246
+ }
247
+ if (!this.targets.size) {
248
+ this.stop();
249
+ return;
250
+ }
251
+ if (!isCacheKeepHybridActive(storage))
252
+ return;
253
+ if (!isWithinCacheKeepWindow(window, new Date(now)))
254
+ return;
255
+ const dueAt = now + CACHE_KEEP_PREWARM_LEAD_MS;
256
+ for (const target of this.targets.values()) {
257
+ if (target.cacheExpiresAt > dueAt)
258
+ continue;
259
+ await this.prewarm(target, now);
260
+ }
261
+ }
262
+ async prewarm(target, now) {
263
+ const prewarm = await buildCacheKeepPrewarmBody(target.bodyText);
264
+ if (!prewarm.ok) {
265
+ this.options.log?.('[cachekeep] prewarm skipped', {
266
+ session: target.id,
267
+ reason: prewarm.reason,
268
+ });
269
+ this.targets.delete(target.id);
270
+ return;
271
+ }
272
+ const fetchImpl = this.options.fetchImpl ?? fetch;
273
+ const prewarmTarget = { ...target, bodyText: prewarm.bodyText };
274
+ const headers = this.options.prepareHeaders
275
+ ? await this.options.prepareHeaders(new Headers(target.headers), prewarmTarget)
276
+ : new Headers(target.headers);
277
+ headers.delete('content-length');
278
+ headers.delete('transfer-encoding');
279
+ const response = await fetchImpl(target.url, {
280
+ method: 'POST',
281
+ headers,
282
+ body: prewarm.bodyText,
283
+ });
284
+ if (!response.ok) {
285
+ this.options.log?.('[cachekeep] prewarm failed', {
286
+ session: target.id,
287
+ status: response.status,
288
+ body: await response.text().catch(() => ''),
289
+ });
290
+ target.cacheExpiresAt = now + CACHE_KEEP_PREWARM_LEAD_MS + 5 * 60_000;
291
+ return;
292
+ }
293
+ await response.body?.cancel().catch(() => { });
294
+ target.cacheExpiresAt = now + CACHE_KEEP_TTL_MS;
295
+ this.options.log?.('[cachekeep] prewarm succeeded', { session: target.id });
296
+ }
297
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './accounts.ts';
2
2
  export * from './auth.ts';
3
3
  export * from './cache1h.ts';
4
+ export * from './cachekeep.ts';
4
5
  export * from './cch.ts';
5
6
  export * from './claude-code.ts';
6
7
  export * from './constants.ts';
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./accounts.js";
2
2
  export * from "./auth.js";
3
3
  export * from "./cache1h.js";
4
+ export * from "./cachekeep.js";
4
5
  export * from "./cch.js";
5
6
  export * from "./claude-code.js";
6
7
  export * from "./constants.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",