@cortexkit/anthropic-auth-core 1.22.0 → 2.0.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 +2 -0
- package/dist/accounts.d.ts +52 -8
- package/dist/cachekeep.d.ts +27 -1
- package/dist/cch.d.ts +11 -1
- package/dist/claude-code.d.ts +23 -1
- package/dist/claustrum-enrollment.d.ts +72 -0
- package/dist/claustrum-scoped-roster.d.ts +27 -0
- package/dist/claustrum-scoped-runtime.d.ts +31 -0
- package/dist/claustrum-scoped.d.ts +53 -0
- package/dist/claustrum.d.ts +16 -82
- package/dist/commands/account.d.ts +38 -11
- package/dist/constants.d.ts +3 -2
- package/dist/custom-headers.d.ts +3 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +29700 -31
- package/dist/internal-types/LICENSE +21 -0
- package/dist/internal-types/commands/builtins.d.ts +71 -0
- package/dist/internal-types/commands/index.d.ts +10 -0
- package/dist/internal-types/commands/menu.d.ts +61 -0
- package/dist/internal-types/commands/model.d.ts +238 -0
- package/dist/internal-types/commands/pi.d.ts +23 -0
- package/dist/internal-types/commands/seam.d.ts +72 -0
- package/dist/internal-types/fs/refresh-file-lock.d.ts +33 -0
- package/dist/internal-types/logger/capture-sink.d.ts +14 -0
- package/dist/internal-types/logger/engine.d.ts +100 -0
- package/dist/internal-types/logger/index.d.ts +6 -0
- package/dist/internal-types/logger/redact.d.ts +11 -0
- package/dist/internal-types/pi-slot/index.d.ts +3 -0
- package/dist/internal-types/pi-slot/slot.d.ts +49 -0
- package/dist/internal-types/quota/codec.d.ts +11 -0
- package/dist/internal-types/quota/format.d.ts +46 -0
- package/dist/internal-types/quota/index.d.ts +10 -0
- package/dist/internal-types/quota/map.d.ts +63 -0
- package/dist/internal-types/quota/merge.d.ts +48 -0
- package/dist/internal-types/quota/projection.d.ts +64 -0
- package/dist/internal-types/store/attribution.d.ts +28 -0
- package/dist/internal-types/store/errors.d.ts +55 -0
- package/dist/internal-types/store/hooks.d.ts +12 -0
- package/dist/internal-types/store/identity.d.ts +39 -0
- package/dist/internal-types/store/index.d.ts +21 -0
- package/dist/internal-types/store/mutate.d.ts +189 -0
- package/dist/internal-types/store/pool.d.ts +196 -0
- package/dist/internal-types/store/provider-state.d.ts +131 -0
- package/dist/internal-types/store/publication.d.ts +47 -0
- package/dist/internal-types/store/pull.d.ts +36 -0
- package/dist/internal-types/store/refresh-lock.d.ts +63 -0
- package/dist/internal-types/store/refresh.d.ts +72 -0
- package/dist/internal-types/store/rows.d.ts +313 -0
- package/dist/internal-types/store/runtime.d.ts +36 -0
- package/dist/internal-types/store/schema.d.ts +443 -0
- package/dist/internal-types/store/settings.d.ts +63 -0
- package/dist/internal-types/store/torn.d.ts +78 -0
- package/dist/migration-source.d.ts +33 -0
- package/dist/model-remap.d.ts +27 -0
- package/dist/models.d.ts +60 -0
- package/dist/native-account-runtime.d.ts +121 -0
- package/dist/native-account-view.d.ts +126 -0
- package/dist/native-command-effects.d.ts +59 -0
- package/dist/native-credential-validation.d.ts +42 -0
- package/dist/native-custody-activation.d.ts +45 -0
- package/dist/native-custody.d.ts +114 -0
- package/dist/native-enrollment-authority.d.ts +2 -0
- package/dist/native-host-auth-read.d.ts +3 -0
- package/dist/native-host-auth-write.d.ts +51 -0
- package/dist/native-host-auth.d.ts +12 -0
- package/dist/native-local-credential-service.d.ts +76 -0
- package/dist/native-local-runtime-readers.d.ts +22 -0
- package/dist/native-lock-observer.d.ts +12 -0
- package/dist/native-menu-executor.d.ts +243 -0
- package/dist/native-menu-model.d.ts +142 -0
- package/dist/native-migration-proof.d.ts +28 -0
- package/dist/native-migration-source.d.ts +47 -0
- package/dist/native-migration.d.ts +66 -0
- package/dist/native-quota-codec.d.ts +32 -0
- package/dist/native-refresh-coordinator.d.ts +240 -0
- package/dist/native-roster-seed.d.ts +115 -0
- package/dist/native-runtime-registry.d.ts +19 -0
- package/dist/native-runtime.d.ts +204 -0
- package/dist/native-transient-account.d.ts +38 -0
- package/dist/native-ui.d.ts +45 -0
- package/dist/native-usage-publication.d.ts +11 -0
- package/dist/native-vault-admission.d.ts +8 -0
- package/dist/native-vault-connection.d.ts +33 -0
- package/dist/native-vault-paths.d.ts +13 -0
- package/dist/native-vault-runtime.d.ts +102 -0
- package/dist/native-vault-seed.d.ts +16 -0
- package/dist/oauth-profile.d.ts +2 -1
- package/dist/pool-authority.d.ts +159 -0
- package/dist/pool-binding.d.ts +32 -0
- package/dist/pool-paths.d.ts +21 -0
- package/dist/pool-store.d.ts +32 -0
- package/dist/prime.d.ts +11 -3
- package/dist/provider-error.d.ts +139 -0
- package/dist/quota-header-feed.d.ts +13 -0
- package/dist/quota-manager.d.ts +30 -5
- package/dist/relay.d.ts +22 -1
- package/dist/request-history.d.ts +34 -0
- package/dist/sse.d.ts +81 -0
- package/dist/sticky-routing.d.ts +29 -0
- package/package.json +9 -3
- package/dist/accounts.js +0 -3330
- package/dist/auth.js +0 -182
- package/dist/cache1h.js +0 -97
- package/dist/cachekeep-registry.js +0 -124
- package/dist/cachekeep.js +0 -578
- package/dist/cch.js +0 -127
- package/dist/claude-code.js +0 -388
- package/dist/claustrum.js +0 -529
- package/dist/commands/account.js +0 -239
- package/dist/constants.js +0 -82
- package/dist/dump.js +0 -616
- package/dist/fast.js +0 -66
- package/dist/json.js +0 -10
- package/dist/killswitch.js +0 -132
- package/dist/logger.js +0 -286
- package/dist/logging.js +0 -59
- package/dist/mid-conversation-output-config.js +0 -89
- package/dist/models.js +0 -141
- package/dist/network-errors.js +0 -27
- package/dist/oauth-profile.js +0 -54
- package/dist/pkce.js +0 -17
- package/dist/prime.js +0 -823
- package/dist/provider.js +0 -0
- package/dist/quota-header-feed.js +0 -279
- package/dist/quota-headers.js +0 -121
- package/dist/quota-manager.js +0 -805
- package/dist/quotas.js +0 -141
- package/dist/relay.js +0 -1243
- package/dist/routing.js +0 -94
- package/dist/start.js +0 -29
- package/dist/sticky-routing.js +0 -523
- package/dist/thinking-binding.js +0 -54
- package/dist/token-fingerprint.js +0 -8
package/dist/cachekeep.js
DELETED
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
import { signRequestBody } from './cch.js';
|
|
2
|
-
import { orderClaudeCodeBody } from './claude-code.js';
|
|
3
|
-
import { dumpDirectRequest, dumpResponseArtifact } from './dump.js';
|
|
4
|
-
import { logger } from './logger.js';
|
|
5
|
-
export const CLAUDE_CACHE_KEEP_COMMAND_NAME = 'claude-cachekeep';
|
|
6
|
-
export const CACHE_KEEP_TTL_MS = 60 * 60_000;
|
|
7
|
-
export const CACHE_KEEP_PREWARM_LEAD_MS = 5 * 60_000;
|
|
8
|
-
export const CACHE_KEEP_TICK_MS = 60_000;
|
|
9
|
-
export const CACHE_KEEP_EXTENDED_TTL_BETA = 'extended-cache-ttl-2025-04-11';
|
|
10
|
-
export const CACHE_KEEP_MAX_TARGETS = 32;
|
|
11
|
-
export const CACHE_KEEP_MAX_BODY_BYTES = 16 * 1024 * 1024;
|
|
12
|
-
export const CACHE_KEEP_PREWARM_TIMEOUT_MS = 30_000;
|
|
13
|
-
export const CACHE_KEEP_RETRY_BASE_MS = 60_000;
|
|
14
|
-
export const CACHE_KEEP_RETRY_MAX_MS = 15 * 60_000;
|
|
15
|
-
export const CACHE_KEEP_RETRY_JITTER_MAX_MS = 30_000;
|
|
16
|
-
const STATUS_TITLE = '## Claude Cache Keep Status';
|
|
17
|
-
const ENABLED_TITLE = '## Claude Cache Keep Enabled';
|
|
18
|
-
const DISABLED_TITLE = '## Claude Cache Keep Disabled';
|
|
19
|
-
const USAGE_TITLE = '## Claude Cache Keep Usage';
|
|
20
|
-
const USAGE = 'Usage: `/claude-cachekeep`, `/claude-cachekeep always`, `/claude-cachekeep off`, or `/claude-cachekeep HH-HH`.';
|
|
21
|
-
function padHour(hour) {
|
|
22
|
-
return String(hour).padStart(2, '0');
|
|
23
|
-
}
|
|
24
|
-
export function parseCacheKeepCommandAction(input) {
|
|
25
|
-
const trimmed = input.trim();
|
|
26
|
-
if (!trimmed)
|
|
27
|
-
return { type: 'status' };
|
|
28
|
-
if (trimmed === 'off')
|
|
29
|
-
return { type: 'disable' };
|
|
30
|
-
if (trimmed === 'always')
|
|
31
|
-
return { type: 'always' };
|
|
32
|
-
if (trimmed === 'subagents on')
|
|
33
|
-
return { type: 'subagents', enabled: true };
|
|
34
|
-
if (trimmed === 'subagents off')
|
|
35
|
-
return { type: 'subagents', enabled: false };
|
|
36
|
-
const match = /^(\d{1,2})-(\d{1,2})$/.exec(trimmed);
|
|
37
|
-
if (!match)
|
|
38
|
-
return { type: 'usage' };
|
|
39
|
-
const startHour = Number(match[1]);
|
|
40
|
-
const endHour = Number(match[2]);
|
|
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 { type: 'usage' };
|
|
49
|
-
}
|
|
50
|
-
return { type: 'window', startHour, endHour };
|
|
51
|
-
}
|
|
52
|
-
export function getCacheKeepWindow(storage) {
|
|
53
|
-
const startHour = Number(storage?.cacheKeep?.startHour);
|
|
54
|
-
const endHour = Number(storage?.cacheKeep?.endHour);
|
|
55
|
-
if (!Number.isInteger(startHour) ||
|
|
56
|
-
!Number.isInteger(endHour) ||
|
|
57
|
-
startHour < 0 ||
|
|
58
|
-
startHour > 23 ||
|
|
59
|
-
endHour < 0 ||
|
|
60
|
-
endHour > 23 ||
|
|
61
|
-
startHour === endHour) {
|
|
62
|
-
return undefined;
|
|
63
|
-
}
|
|
64
|
-
return { startHour, endHour };
|
|
65
|
-
}
|
|
66
|
-
export function isCacheKeepAlways(storage) {
|
|
67
|
-
return (storage?.cacheKeep?.enabled === true && storage.cacheKeep.always === true);
|
|
68
|
-
}
|
|
69
|
-
export function isCacheKeepPersistentlyEnabled(storage) {
|
|
70
|
-
return (storage?.cacheKeep?.enabled === true &&
|
|
71
|
-
(isCacheKeepAlways(storage) || !!getCacheKeepWindow(storage)));
|
|
72
|
-
}
|
|
73
|
-
export function isCacheKeepHybridActive(storage) {
|
|
74
|
-
return (isCacheKeepPersistentlyEnabled(storage) &&
|
|
75
|
-
storage?.claudeCache?.enabled === true &&
|
|
76
|
-
storage.claudeCache.mode === 'hybrid');
|
|
77
|
-
}
|
|
78
|
-
export function isWithinCacheKeepWindow(window, now = new Date()) {
|
|
79
|
-
if (!window)
|
|
80
|
-
return false;
|
|
81
|
-
const hour = now.getHours();
|
|
82
|
-
if (window.startHour < window.endHour) {
|
|
83
|
-
return hour >= window.startHour && hour < window.endHour;
|
|
84
|
-
}
|
|
85
|
-
return hour >= window.startHour || hour < window.endHour;
|
|
86
|
-
}
|
|
87
|
-
export function isCacheKeepActiveNow(storage, now = new Date()) {
|
|
88
|
-
if (storage?.cacheKeep?.enabled !== true)
|
|
89
|
-
return false;
|
|
90
|
-
return (isCacheKeepAlways(storage) ||
|
|
91
|
-
isWithinCacheKeepWindow(getCacheKeepWindow(storage), now));
|
|
92
|
-
}
|
|
93
|
-
export function localDayKey(now = new Date()) {
|
|
94
|
-
return `${now.getFullYear()}-${padHour(now.getMonth() + 1)}-${padHour(now.getDate())}`;
|
|
95
|
-
}
|
|
96
|
-
function localWindowKey(window, now = new Date()) {
|
|
97
|
-
if (window &&
|
|
98
|
-
window.startHour > window.endHour &&
|
|
99
|
-
now.getHours() < window.endHour) {
|
|
100
|
-
const previous = new Date(now);
|
|
101
|
-
previous.setDate(previous.getDate() - 1);
|
|
102
|
-
return localDayKey(previous);
|
|
103
|
-
}
|
|
104
|
-
return localDayKey(now);
|
|
105
|
-
}
|
|
106
|
-
function cacheKeepPeriodKey(storage, window, now = new Date()) {
|
|
107
|
-
return isCacheKeepAlways(storage) ? 'always' : localWindowKey(window, now);
|
|
108
|
-
}
|
|
109
|
-
export function buildCacheKeepStatusSummary(status) {
|
|
110
|
-
const schedule = status.always
|
|
111
|
-
? 'always (while this process is running)'
|
|
112
|
-
: status.window
|
|
113
|
-
? `${padHour(status.window.startHour)}-${padHour(status.window.endHour)}`
|
|
114
|
-
: 'not configured';
|
|
115
|
-
const lines = [
|
|
116
|
-
`Cache keep: ${status.enabled ? 'enabled' : 'disabled'}`,
|
|
117
|
-
`Schedule: ${schedule}`,
|
|
118
|
-
'Mode requirement: `/claude-cache mode hybrid` must be active.',
|
|
119
|
-
];
|
|
120
|
-
if (typeof status.hybridActive === 'boolean') {
|
|
121
|
-
lines.push(`Hybrid active: ${status.hybridActive ? 'yes' : 'no'}`);
|
|
122
|
-
}
|
|
123
|
-
if (typeof status.trackedSessions === 'number') {
|
|
124
|
-
lines.push(`Tracked sessions: ${status.trackedSessions}`);
|
|
125
|
-
}
|
|
126
|
-
if (status.trackedSessionDetails?.length) {
|
|
127
|
-
lines.push('Sessions:');
|
|
128
|
-
for (const session of status.trackedSessionDetails) {
|
|
129
|
-
lines.push(`- ${session.id}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (status.nextPrewarmAt) {
|
|
133
|
-
lines.push(`Next prewarm: ${new Date(status.nextPrewarmAt).toLocaleString()}`);
|
|
134
|
-
}
|
|
135
|
-
return lines.join('\n');
|
|
136
|
-
}
|
|
137
|
-
export function executeCacheKeepCommand(input) {
|
|
138
|
-
const action = parseCacheKeepCommandAction(input.argumentsText);
|
|
139
|
-
const status = {
|
|
140
|
-
enabled: input.enabled ?? false,
|
|
141
|
-
always: input.always,
|
|
142
|
-
window: input.window,
|
|
143
|
-
trackedSessions: input.trackedSessions,
|
|
144
|
-
trackedSessionDetails: input.trackedSessionDetails,
|
|
145
|
-
nextPrewarmAt: input.nextPrewarmAt,
|
|
146
|
-
hybridActive: input.hybridActive,
|
|
147
|
-
};
|
|
148
|
-
if (action.type === 'status') {
|
|
149
|
-
return [STATUS_TITLE, '', buildCacheKeepStatusSummary(status)].join('\n');
|
|
150
|
-
}
|
|
151
|
-
if (action.type === 'disable') {
|
|
152
|
-
return [
|
|
153
|
-
DISABLED_TITLE,
|
|
154
|
-
'',
|
|
155
|
-
buildCacheKeepStatusSummary({ ...status, enabled: false }),
|
|
156
|
-
].join('\n');
|
|
157
|
-
}
|
|
158
|
-
if (action.type === 'always') {
|
|
159
|
-
return [
|
|
160
|
-
ENABLED_TITLE,
|
|
161
|
-
'',
|
|
162
|
-
buildCacheKeepStatusSummary({
|
|
163
|
-
...status,
|
|
164
|
-
enabled: true,
|
|
165
|
-
always: true,
|
|
166
|
-
window: undefined,
|
|
167
|
-
}),
|
|
168
|
-
].join('\n');
|
|
169
|
-
}
|
|
170
|
-
if (action.type === 'window') {
|
|
171
|
-
return [
|
|
172
|
-
ENABLED_TITLE,
|
|
173
|
-
'',
|
|
174
|
-
buildCacheKeepStatusSummary({
|
|
175
|
-
...status,
|
|
176
|
-
enabled: true,
|
|
177
|
-
always: false,
|
|
178
|
-
window: { startHour: action.startHour, endHour: action.endHour },
|
|
179
|
-
}),
|
|
180
|
-
].join('\n');
|
|
181
|
-
}
|
|
182
|
-
if (action.type === 'subagents') {
|
|
183
|
-
return [
|
|
184
|
-
STATUS_TITLE,
|
|
185
|
-
'',
|
|
186
|
-
buildCacheKeepStatusSummary(status),
|
|
187
|
-
'',
|
|
188
|
-
`Subagent tracking: ${action.enabled ? 'enabled' : 'disabled'}`,
|
|
189
|
-
].join('\n');
|
|
190
|
-
}
|
|
191
|
-
return [USAGE_TITLE, '', USAGE, '', buildCacheKeepStatusSummary(status)].join('\n');
|
|
192
|
-
}
|
|
193
|
-
function hasExplicitCacheControl(value) {
|
|
194
|
-
if (!value || typeof value !== 'object')
|
|
195
|
-
return false;
|
|
196
|
-
if (Array.isArray(value))
|
|
197
|
-
return value.some(hasExplicitCacheControl);
|
|
198
|
-
const record = value;
|
|
199
|
-
const cacheControl = record.cache_control;
|
|
200
|
-
if (cacheControl &&
|
|
201
|
-
typeof cacheControl === 'object' &&
|
|
202
|
-
cacheControl.type === 'ephemeral') {
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
return Object.values(record).some(hasExplicitCacheControl);
|
|
206
|
-
}
|
|
207
|
-
export async function buildCacheKeepPrewarmBody(bodyText) {
|
|
208
|
-
let body;
|
|
209
|
-
try {
|
|
210
|
-
body = JSON.parse(bodyText);
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
return { ok: false, reason: 'body is not valid JSON' };
|
|
214
|
-
}
|
|
215
|
-
if (!hasExplicitCacheControl(body)) {
|
|
216
|
-
return { ok: false, reason: 'body has no explicit cache breakpoints' };
|
|
217
|
-
}
|
|
218
|
-
const warm = structuredClone(body);
|
|
219
|
-
warm.max_tokens = 0;
|
|
220
|
-
delete warm.stream;
|
|
221
|
-
const thinking = warm.thinking;
|
|
222
|
-
if (thinking?.type === 'enabled')
|
|
223
|
-
delete warm.thinking;
|
|
224
|
-
const outputConfig = warm.output_config;
|
|
225
|
-
if (outputConfig?.format)
|
|
226
|
-
delete warm.output_config;
|
|
227
|
-
const toolChoice = warm.tool_choice;
|
|
228
|
-
if (toolChoice?.type === 'tool' || toolChoice?.type === 'any') {
|
|
229
|
-
delete warm.tool_choice;
|
|
230
|
-
}
|
|
231
|
-
const signedBodyText = await signRequestBody(JSON.stringify(orderClaudeCodeBody(warm)));
|
|
232
|
-
return { ok: true, bodyText: signedBodyText };
|
|
233
|
-
}
|
|
234
|
-
function cacheKeepRetryDelayMs(targetId, failureCount) {
|
|
235
|
-
const exponential = Math.min(CACHE_KEEP_RETRY_MAX_MS, CACHE_KEEP_RETRY_BASE_MS * 2 ** Math.min(Math.max(failureCount - 1, 0), 8));
|
|
236
|
-
let hash = 2_166_136_261;
|
|
237
|
-
for (const character of `${targetId}:${failureCount}`) {
|
|
238
|
-
hash ^= character.charCodeAt(0);
|
|
239
|
-
hash = Math.imul(hash, 16_777_619);
|
|
240
|
-
}
|
|
241
|
-
const jitter = (hash >>> 0) %
|
|
242
|
-
(Math.min(CACHE_KEEP_RETRY_JITTER_MAX_MS, exponential / 4) + 1);
|
|
243
|
-
return exponential + jitter;
|
|
244
|
-
}
|
|
245
|
-
export class CacheKeepManager {
|
|
246
|
-
options;
|
|
247
|
-
targets = new Map();
|
|
248
|
-
timer = null;
|
|
249
|
-
tickPromise = null;
|
|
250
|
-
constructor(options) {
|
|
251
|
-
this.options = options;
|
|
252
|
-
}
|
|
253
|
-
start() {
|
|
254
|
-
if (this.timer)
|
|
255
|
-
return;
|
|
256
|
-
logger.debug('cachekeep', 'started');
|
|
257
|
-
this.timer = (this.options.setIntervalImpl ?? globalThis.setInterval)(() => {
|
|
258
|
-
void this.tick().catch((error) => {
|
|
259
|
-
logger.warn('cachekeep', 'tick failed', {
|
|
260
|
-
error: error instanceof Error ? error.message : String(error),
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
}, CACHE_KEEP_TICK_MS);
|
|
264
|
-
if ('unref' in this.timer)
|
|
265
|
-
this.timer.unref();
|
|
266
|
-
}
|
|
267
|
-
stop() {
|
|
268
|
-
if (!this.timer)
|
|
269
|
-
return;
|
|
270
|
-
logger.debug('cachekeep', 'stopped');
|
|
271
|
-
const clearIntervalImpl = this.options.clearIntervalImpl ?? globalThis.clearInterval;
|
|
272
|
-
clearIntervalImpl(this.timer);
|
|
273
|
-
this.timer = null;
|
|
274
|
-
}
|
|
275
|
-
stats(window, now = this.options.now?.() ?? Date.now(), always = false) {
|
|
276
|
-
const today = always ? 'always' : localWindowKey(window, new Date(now));
|
|
277
|
-
const targets = [...this.targets.values()].filter((target) => target.dayKey === today);
|
|
278
|
-
const nextPrewarmAt = targets.length
|
|
279
|
-
? Math.min(...targets.map((target) => target.nextPrewarmAt))
|
|
280
|
-
: undefined;
|
|
281
|
-
return { trackedSessions: targets.length, nextPrewarmAt };
|
|
282
|
-
}
|
|
283
|
-
trackedSessions() {
|
|
284
|
-
return [...this.targets.values()]
|
|
285
|
-
.map((target) => ({
|
|
286
|
-
id: target.id,
|
|
287
|
-
cacheExpiresAt: target.cacheExpiresAt,
|
|
288
|
-
nextPrewarmAt: target.nextPrewarmAt,
|
|
289
|
-
}))
|
|
290
|
-
.sort((left, right) => left.id.localeCompare(right.id));
|
|
291
|
-
}
|
|
292
|
-
trackedOAuthRoute(sessionId) {
|
|
293
|
-
const target = this.targets.get(sessionId);
|
|
294
|
-
return target ? { oauthAccountId: target.oauthAccountId } : undefined;
|
|
295
|
-
}
|
|
296
|
-
trackedCount() {
|
|
297
|
-
return this.targets.size;
|
|
298
|
-
}
|
|
299
|
-
publishTrackedSessions() {
|
|
300
|
-
const callback = this.options.onTrackedSessionsChanged;
|
|
301
|
-
if (!callback)
|
|
302
|
-
return;
|
|
303
|
-
try {
|
|
304
|
-
void Promise.resolve(callback(this.trackedSessions())).catch((error) => {
|
|
305
|
-
logger.warn('cachekeep', 'failed to publish tracked sessions', {
|
|
306
|
-
error: error instanceof Error ? error.message : String(error),
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
catch (error) {
|
|
311
|
-
logger.warn('cachekeep', 'failed to publish tracked sessions', {
|
|
312
|
-
error: error instanceof Error ? error.message : String(error),
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
totalBodyBytes() {
|
|
317
|
-
return [...this.targets.values()].reduce((sum, target) => sum + target.bodyText.length, 0);
|
|
318
|
-
}
|
|
319
|
-
evictOldestTarget() {
|
|
320
|
-
const oldest = this.targets.keys().next().value;
|
|
321
|
-
if (oldest !== undefined)
|
|
322
|
-
this.targets.delete(oldest);
|
|
323
|
-
}
|
|
324
|
-
pruneTargets(now, today) {
|
|
325
|
-
for (const [id, target] of this.targets) {
|
|
326
|
-
if (today && target.dayKey !== today) {
|
|
327
|
-
this.targets.delete(id);
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
if (now >= target.cacheExpiresAt)
|
|
331
|
-
this.targets.delete(id);
|
|
332
|
-
}
|
|
333
|
-
while (this.targets.size > CACHE_KEEP_MAX_TARGETS)
|
|
334
|
-
this.evictOldestTarget();
|
|
335
|
-
while (this.totalBodyBytes() > CACHE_KEEP_MAX_BODY_BYTES) {
|
|
336
|
-
const before = this.targets.size;
|
|
337
|
-
this.evictOldestTarget();
|
|
338
|
-
if (this.targets.size === before)
|
|
339
|
-
break;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
track(input) {
|
|
343
|
-
if (!input.sessionId)
|
|
344
|
-
return { tracked: false, reason: 'missing session id' };
|
|
345
|
-
if (!isCacheKeepPersistentlyEnabled(input.storage)) {
|
|
346
|
-
return { tracked: false, reason: 'cachekeep disabled' };
|
|
347
|
-
}
|
|
348
|
-
if (input.cacheMode !== 'hybrid') {
|
|
349
|
-
return { tracked: false, reason: 'cache mode is not hybrid' };
|
|
350
|
-
}
|
|
351
|
-
if (input.bodyText.length > CACHE_KEEP_MAX_BODY_BYTES) {
|
|
352
|
-
return { tracked: false, reason: 'body exceeds cachekeep memory budget' };
|
|
353
|
-
}
|
|
354
|
-
const now = this.options.now?.() ?? Date.now();
|
|
355
|
-
const window = getCacheKeepWindow(input.storage);
|
|
356
|
-
if (!isCacheKeepActiveNow(input.storage, new Date(now))) {
|
|
357
|
-
return { tracked: false, reason: 'outside configured schedule' };
|
|
358
|
-
}
|
|
359
|
-
const today = cacheKeepPeriodKey(input.storage, window, new Date(now));
|
|
360
|
-
this.pruneTargets(now, today);
|
|
361
|
-
if (this.targets.has(input.sessionId))
|
|
362
|
-
this.targets.delete(input.sessionId);
|
|
363
|
-
const headers = {};
|
|
364
|
-
input.headers.forEach((value, key) => {
|
|
365
|
-
headers[key] = value;
|
|
366
|
-
});
|
|
367
|
-
this.targets.set(input.sessionId, {
|
|
368
|
-
id: input.sessionId,
|
|
369
|
-
url: input.url,
|
|
370
|
-
headers,
|
|
371
|
-
bodyText: input.bodyText,
|
|
372
|
-
cacheExpiresAt: now + CACHE_KEEP_TTL_MS,
|
|
373
|
-
nextPrewarmAt: now + CACHE_KEEP_TTL_MS - CACHE_KEEP_PREWARM_LEAD_MS,
|
|
374
|
-
consecutiveFailures: 0,
|
|
375
|
-
dayKey: today,
|
|
376
|
-
oauthAccountId: input.oauthAccountId,
|
|
377
|
-
isSubagent: input.isSubagent ?? false,
|
|
378
|
-
});
|
|
379
|
-
this.pruneTargets(now, today);
|
|
380
|
-
this.publishTrackedSessions();
|
|
381
|
-
this.start();
|
|
382
|
-
return { tracked: true };
|
|
383
|
-
}
|
|
384
|
-
async prewarmNow(input) {
|
|
385
|
-
const headers = {};
|
|
386
|
-
input.headers.forEach((value, key) => {
|
|
387
|
-
headers[key] = value;
|
|
388
|
-
});
|
|
389
|
-
const target = {
|
|
390
|
-
id: input.sessionId,
|
|
391
|
-
url: input.url,
|
|
392
|
-
headers,
|
|
393
|
-
bodyText: input.bodyText,
|
|
394
|
-
cacheExpiresAt: this.options.now?.() ?? Date.now(),
|
|
395
|
-
nextPrewarmAt: this.options.now?.() ?? Date.now(),
|
|
396
|
-
consecutiveFailures: 0,
|
|
397
|
-
dayKey: '',
|
|
398
|
-
oauthAccountId: input.oauthAccountId,
|
|
399
|
-
isSubagent: input.isSubagent ?? false,
|
|
400
|
-
};
|
|
401
|
-
return this.sendPrewarm(target);
|
|
402
|
-
}
|
|
403
|
-
tick() {
|
|
404
|
-
if (this.tickPromise)
|
|
405
|
-
return this.tickPromise;
|
|
406
|
-
let run;
|
|
407
|
-
run = this.runTick().finally(() => {
|
|
408
|
-
if (this.tickPromise === run)
|
|
409
|
-
this.tickPromise = null;
|
|
410
|
-
});
|
|
411
|
-
this.tickPromise = run;
|
|
412
|
-
return run;
|
|
413
|
-
}
|
|
414
|
-
async runTick() {
|
|
415
|
-
const storage = await this.options.loadStorage();
|
|
416
|
-
const window = getCacheKeepWindow(storage);
|
|
417
|
-
const now = this.options.now?.() ?? Date.now();
|
|
418
|
-
const today = cacheKeepPeriodKey(storage, window, new Date(now));
|
|
419
|
-
if (isCacheKeepAlways(storage)) {
|
|
420
|
-
for (const target of this.targets.values())
|
|
421
|
-
target.dayKey = 'always';
|
|
422
|
-
}
|
|
423
|
-
this.pruneTargets(now, today);
|
|
424
|
-
if (!this.targets.size) {
|
|
425
|
-
this.publishTrackedSessions();
|
|
426
|
-
this.stop();
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
if (!isCacheKeepHybridActive(storage)) {
|
|
430
|
-
this.publishTrackedSessions();
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
if (!isCacheKeepActiveNow(storage, new Date(now))) {
|
|
434
|
-
this.publishTrackedSessions();
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
logger.debug('cachekeep', 'fired', { targets: this.targets.size });
|
|
438
|
-
for (const target of this.targets.values()) {
|
|
439
|
-
if (target.nextPrewarmAt > now)
|
|
440
|
-
continue;
|
|
441
|
-
try {
|
|
442
|
-
await this.prewarm(target, now);
|
|
443
|
-
}
|
|
444
|
-
catch (error) {
|
|
445
|
-
logger.warn('cachekeep', 'prewarm failed', {
|
|
446
|
-
session: target.id,
|
|
447
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
448
|
-
});
|
|
449
|
-
target.consecutiveFailures += 1;
|
|
450
|
-
target.nextPrewarmAt =
|
|
451
|
-
now + cacheKeepRetryDelayMs(target.id, target.consecutiveFailures);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
this.publishTrackedSessions();
|
|
455
|
-
}
|
|
456
|
-
async sendPrewarm(target) {
|
|
457
|
-
let bodyText = target.bodyText;
|
|
458
|
-
if (this.options.prepareBody) {
|
|
459
|
-
try {
|
|
460
|
-
bodyText = await this.options.prepareBody(bodyText, target);
|
|
461
|
-
}
|
|
462
|
-
catch (error) {
|
|
463
|
-
logger.warn('cachekeep', 'prepare body failed', {
|
|
464
|
-
session: target.id,
|
|
465
|
-
error: error instanceof Error ? error.message : String(error),
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
const preparedTarget = { ...target, bodyText };
|
|
470
|
-
const prewarm = await buildCacheKeepPrewarmBody(bodyText);
|
|
471
|
-
if (!prewarm.ok)
|
|
472
|
-
return prewarm;
|
|
473
|
-
const fetchImpl = this.options.fetchImpl ?? fetch;
|
|
474
|
-
const prewarmTarget = { ...preparedTarget, bodyText: prewarm.bodyText };
|
|
475
|
-
const headers = this.options.prepareHeaders
|
|
476
|
-
? await this.options.prepareHeaders(new Headers(target.headers), prewarmTarget)
|
|
477
|
-
: new Headers(target.headers);
|
|
478
|
-
headers.delete('content-length');
|
|
479
|
-
headers.delete('transfer-encoding');
|
|
480
|
-
let response;
|
|
481
|
-
try {
|
|
482
|
-
response = await fetchImpl(target.url, {
|
|
483
|
-
method: 'POST',
|
|
484
|
-
headers,
|
|
485
|
-
body: prewarm.bodyText,
|
|
486
|
-
signal: AbortSignal.timeout(this.options.prewarmTimeoutMs ?? CACHE_KEEP_PREWARM_TIMEOUT_MS),
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
catch (error) {
|
|
490
|
-
return {
|
|
491
|
-
ok: false,
|
|
492
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
493
|
-
transient: true,
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
const receivedAt = this.options.now?.() ?? Date.now();
|
|
497
|
-
const raw = await response.text().catch(() => '');
|
|
498
|
-
let data = null;
|
|
499
|
-
try {
|
|
500
|
-
data = raw ? JSON.parse(raw) : null;
|
|
501
|
-
}
|
|
502
|
-
catch { }
|
|
503
|
-
try {
|
|
504
|
-
await this.options.onResponse?.({
|
|
505
|
-
target,
|
|
506
|
-
bodyText: prewarm.bodyText,
|
|
507
|
-
status: response.status,
|
|
508
|
-
data,
|
|
509
|
-
receivedAt,
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
catch { }
|
|
513
|
-
try {
|
|
514
|
-
const dumpHandle = await dumpDirectRequest({
|
|
515
|
-
affinity: target.id,
|
|
516
|
-
route: 'cachekeep',
|
|
517
|
-
status: response.status,
|
|
518
|
-
bodyText: prewarm.bodyText,
|
|
519
|
-
url: target.url,
|
|
520
|
-
method: 'POST',
|
|
521
|
-
headers,
|
|
522
|
-
tag: 'cachekeep',
|
|
523
|
-
});
|
|
524
|
-
await dumpResponseArtifact(dumpHandle, {
|
|
525
|
-
status: response.status,
|
|
526
|
-
message: data,
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
catch (error) {
|
|
530
|
-
logger.debug('cachekeep', 'dump failed', {
|
|
531
|
-
session: target.id,
|
|
532
|
-
error: error instanceof Error ? error.message : String(error),
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
if (!response.ok) {
|
|
536
|
-
return {
|
|
537
|
-
ok: false,
|
|
538
|
-
reason: raw || `HTTP ${response.status}`,
|
|
539
|
-
status: response.status,
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
const objectData = data && typeof data === 'object' && !Array.isArray(data)
|
|
543
|
-
? data
|
|
544
|
-
: null;
|
|
545
|
-
const usage = objectData?.usage;
|
|
546
|
-
return { ok: true, ...(usage && { usage }) };
|
|
547
|
-
}
|
|
548
|
-
async prewarm(target, now) {
|
|
549
|
-
const result = await this.sendPrewarm(target);
|
|
550
|
-
if (!result.ok) {
|
|
551
|
-
if (result.status == null && !result.transient) {
|
|
552
|
-
logger.debug('cachekeep', 'prewarm skipped', {
|
|
553
|
-
session: target.id,
|
|
554
|
-
reason: result.reason,
|
|
555
|
-
});
|
|
556
|
-
this.targets.delete(target.id);
|
|
557
|
-
}
|
|
558
|
-
else {
|
|
559
|
-
logger.warn('cachekeep', 'prewarm failed', {
|
|
560
|
-
session: target.id,
|
|
561
|
-
status: result.status,
|
|
562
|
-
reason: result.reason,
|
|
563
|
-
});
|
|
564
|
-
target.consecutiveFailures += 1;
|
|
565
|
-
target.nextPrewarmAt =
|
|
566
|
-
now + cacheKeepRetryDelayMs(target.id, target.consecutiveFailures);
|
|
567
|
-
}
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
target.cacheExpiresAt = now + CACHE_KEEP_TTL_MS;
|
|
571
|
-
target.nextPrewarmAt = target.cacheExpiresAt - CACHE_KEEP_PREWARM_LEAD_MS;
|
|
572
|
-
target.consecutiveFailures = 0;
|
|
573
|
-
logger.debug('cachekeep', 'prewarm succeeded', {
|
|
574
|
-
session: target.id,
|
|
575
|
-
...(result.usage && { usage: result.usage }),
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
}
|
package/dist/cch.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import xxhashInit from 'xxhash-wasm';
|
|
3
|
-
import { CCH_POSITIONS, CCH_SALT, CLAUDE_CODE_VERSION } from './constants.js';
|
|
4
|
-
const CCH_SEED = 0x4d659218e32a3268n;
|
|
5
|
-
const CCH_PLACEHOLDER = 'cch=00000;';
|
|
6
|
-
export const CCH_PATTERN = /\bcch=([0-9a-f]{5});/;
|
|
7
|
-
const BILLING_HEADER_CCH_PATTERN = /("system":\[\{"type":"text","text":"x-anthropic-billing-header: cc_version=[^;"]+; cc_entrypoint=[^;"]+; )cch=([0-9a-f]{5});/;
|
|
8
|
-
const BILLING_HEADER_CCH_PLACEHOLDER_PATTERN = /("system":\[\{"type":"text","text":"x-anthropic-billing-header: cc_version=[^;"]+; cc_entrypoint=[^;"]+; )cch=00000;/;
|
|
9
|
-
let xxhashPromise = null;
|
|
10
|
-
let xxhash64Raw = null;
|
|
11
|
-
async function ensureXxhash() {
|
|
12
|
-
if (xxhash64Raw)
|
|
13
|
-
return;
|
|
14
|
-
xxhashPromise ??= (async () => {
|
|
15
|
-
const hasher = await xxhashInit();
|
|
16
|
-
xxhash64Raw = hasher.h64Raw;
|
|
17
|
-
})();
|
|
18
|
-
await xxhashPromise;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Extract the visible prompt from the first user message. Claude Code prepends
|
|
22
|
-
* reminder blocks and derives its version suffix from the final text block.
|
|
23
|
-
*/
|
|
24
|
-
export function extractFirstUserMessageText(messages) {
|
|
25
|
-
const userMsg = messages.find((message) => message.role === 'user');
|
|
26
|
-
if (!userMsg)
|
|
27
|
-
return '';
|
|
28
|
-
const { content } = userMsg;
|
|
29
|
-
if (typeof content === 'string')
|
|
30
|
-
return content;
|
|
31
|
-
if (Array.isArray(content)) {
|
|
32
|
-
const textBlocks = content.filter((block) => block.type === 'text');
|
|
33
|
-
const commandBlock = textBlocks.find((block) => block.text?.includes('<command-name>'));
|
|
34
|
-
if (commandBlock?.text) {
|
|
35
|
-
return commandBlock.text.slice(commandBlock.text.indexOf('<command-name>'));
|
|
36
|
-
}
|
|
37
|
-
const visibleBlock = textBlocks.findLast((block) => typeof block.text === 'string' && block.text.length > 0);
|
|
38
|
-
if (visibleBlock?.text)
|
|
39
|
-
return visibleBlock.text;
|
|
40
|
-
}
|
|
41
|
-
return '';
|
|
42
|
-
}
|
|
43
|
-
export class ClaudeCodeFirstUserTextTracker {
|
|
44
|
-
limit;
|
|
45
|
-
#values = new Map();
|
|
46
|
-
constructor(limit = 1_000) {
|
|
47
|
-
this.limit = limit;
|
|
48
|
-
}
|
|
49
|
-
resolve(sessionId, messages, pin = true) {
|
|
50
|
-
const existing = this.#values.get(sessionId);
|
|
51
|
-
if (existing !== undefined) {
|
|
52
|
-
this.#values.delete(sessionId);
|
|
53
|
-
this.#values.set(sessionId, existing);
|
|
54
|
-
return existing;
|
|
55
|
-
}
|
|
56
|
-
const value = extractFirstUserMessageText(messages);
|
|
57
|
-
if (pin) {
|
|
58
|
-
if (!this.#values.has(sessionId) && this.#values.size >= this.limit) {
|
|
59
|
-
const oldest = this.#values.keys().next().value;
|
|
60
|
-
if (oldest !== undefined)
|
|
61
|
-
this.#values.delete(oldest);
|
|
62
|
-
}
|
|
63
|
-
this.#values.set(sessionId, value);
|
|
64
|
-
}
|
|
65
|
-
return value;
|
|
66
|
-
}
|
|
67
|
-
has(sessionId) {
|
|
68
|
-
return this.#values.has(sessionId);
|
|
69
|
-
}
|
|
70
|
-
clear() {
|
|
71
|
-
this.#values.clear();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Compute Claude Code's cch token over the final serialized request body.
|
|
76
|
-
*
|
|
77
|
-
* Real Claude Code signs the full body bytes with xxHash64 using a fixed seed,
|
|
78
|
-
* masks to 20 bits, and writes that value into the billing-header placeholder.
|
|
79
|
-
*/
|
|
80
|
-
export async function computeCCH(bodyBytes) {
|
|
81
|
-
await ensureXxhash();
|
|
82
|
-
const hash = xxhash64Raw?.(bodyBytes, CCH_SEED) ?? 0n;
|
|
83
|
-
return (hash & 0xfffffn).toString(16).padStart(5, '0');
|
|
84
|
-
}
|
|
85
|
-
export async function computeXxhash64Hex(value) {
|
|
86
|
-
await ensureXxhash();
|
|
87
|
-
const hash = xxhash64Raw?.(new TextEncoder().encode(value), 0n) ?? 0n;
|
|
88
|
-
return hash.toString(16).padStart(16, '0').slice(0, 16);
|
|
89
|
-
}
|
|
90
|
-
export function resetBillingHeaderCCH(bodyString) {
|
|
91
|
-
return bodyString.replace(BILLING_HEADER_CCH_PATTERN, `$1${CCH_PLACEHOLDER}`);
|
|
92
|
-
}
|
|
93
|
-
export function extractBillingHeaderCCH(bodyString) {
|
|
94
|
-
return BILLING_HEADER_CCH_PATTERN.exec(bodyString)?.[2] ?? null;
|
|
95
|
-
}
|
|
96
|
-
export async function signRequestBody(bodyString) {
|
|
97
|
-
if (!BILLING_HEADER_CCH_PATTERN.test(bodyString))
|
|
98
|
-
return bodyString;
|
|
99
|
-
const unsignedBodyString = resetBillingHeaderCCH(bodyString);
|
|
100
|
-
const canonicalBody = JSON.parse(unsignedBodyString);
|
|
101
|
-
if ('model' in canonicalBody)
|
|
102
|
-
canonicalBody.model = '';
|
|
103
|
-
delete canonicalBody.max_tokens;
|
|
104
|
-
const token = await computeCCH(new TextEncoder().encode(JSON.stringify(canonicalBody)));
|
|
105
|
-
return unsignedBodyString.replace(BILLING_HEADER_CCH_PLACEHOLDER_PATTERN, `$1cch=${token};`);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Compute Claude Code's 3-character suffix for cc_version.
|
|
109
|
-
*/
|
|
110
|
-
export function computeCcVersionSuffix(firstUserText, version = CLAUDE_CODE_VERSION) {
|
|
111
|
-
const sampledText = CCH_POSITIONS.map((position) => firstUserText[position] ?? '0').join('');
|
|
112
|
-
return createHash('sha256')
|
|
113
|
-
.update(`${CCH_SALT}${sampledText}${version}`)
|
|
114
|
-
.digest('hex')
|
|
115
|
-
.slice(0, 3);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Build the billing header with a cch placeholder.
|
|
119
|
-
* signRequestBody() must run after final request serialization to replace it.
|
|
120
|
-
*/
|
|
121
|
-
export function buildBillingHeaderValue(messages, version = CLAUDE_CODE_VERSION, entrypoint, pinnedFirstUserText) {
|
|
122
|
-
const suffix = computeCcVersionSuffix(pinnedFirstUserText ?? extractFirstUserMessageText(messages), version);
|
|
123
|
-
return ('x-anthropic-billing-header: ' +
|
|
124
|
-
`cc_version=${version}.${suffix}; ` +
|
|
125
|
-
`cc_entrypoint=${entrypoint}; ` +
|
|
126
|
-
'cch=00000;');
|
|
127
|
-
}
|