@cortexkit/anthropic-auth-core 1.23.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.
Files changed (134) hide show
  1. package/README.md +2 -0
  2. package/dist/accounts.d.ts +3 -16
  3. package/dist/cachekeep.d.ts +9 -0
  4. package/dist/claude-code.d.ts +16 -0
  5. package/dist/claustrum-enrollment.d.ts +2 -0
  6. package/dist/claustrum-scoped-roster.d.ts +4 -0
  7. package/dist/claustrum-scoped-runtime.d.ts +3 -0
  8. package/dist/claustrum-scoped.d.ts +14 -0
  9. package/dist/claustrum.d.ts +0 -209
  10. package/dist/commands/account.d.ts +2 -3
  11. package/dist/index.d.ts +44 -1
  12. package/dist/index.js +29700 -38
  13. package/dist/internal-types/LICENSE +21 -0
  14. package/dist/internal-types/commands/builtins.d.ts +71 -0
  15. package/dist/internal-types/commands/index.d.ts +10 -0
  16. package/dist/internal-types/commands/menu.d.ts +61 -0
  17. package/dist/internal-types/commands/model.d.ts +238 -0
  18. package/dist/internal-types/commands/pi.d.ts +23 -0
  19. package/dist/internal-types/commands/seam.d.ts +72 -0
  20. package/dist/internal-types/fs/refresh-file-lock.d.ts +33 -0
  21. package/dist/internal-types/logger/capture-sink.d.ts +14 -0
  22. package/dist/internal-types/logger/engine.d.ts +100 -0
  23. package/dist/internal-types/logger/index.d.ts +6 -0
  24. package/dist/internal-types/logger/redact.d.ts +11 -0
  25. package/dist/internal-types/pi-slot/index.d.ts +3 -0
  26. package/dist/internal-types/pi-slot/slot.d.ts +49 -0
  27. package/dist/internal-types/quota/codec.d.ts +11 -0
  28. package/dist/internal-types/quota/format.d.ts +46 -0
  29. package/dist/internal-types/quota/index.d.ts +10 -0
  30. package/dist/internal-types/quota/map.d.ts +63 -0
  31. package/dist/internal-types/quota/merge.d.ts +48 -0
  32. package/dist/internal-types/quota/projection.d.ts +64 -0
  33. package/dist/internal-types/store/attribution.d.ts +28 -0
  34. package/dist/internal-types/store/errors.d.ts +55 -0
  35. package/dist/internal-types/store/hooks.d.ts +12 -0
  36. package/dist/internal-types/store/identity.d.ts +39 -0
  37. package/dist/internal-types/store/index.d.ts +21 -0
  38. package/dist/internal-types/store/mutate.d.ts +189 -0
  39. package/dist/internal-types/store/pool.d.ts +196 -0
  40. package/dist/internal-types/store/provider-state.d.ts +131 -0
  41. package/dist/internal-types/store/publication.d.ts +47 -0
  42. package/dist/internal-types/store/pull.d.ts +36 -0
  43. package/dist/internal-types/store/refresh-lock.d.ts +63 -0
  44. package/dist/internal-types/store/refresh.d.ts +72 -0
  45. package/dist/internal-types/store/rows.d.ts +313 -0
  46. package/dist/internal-types/store/runtime.d.ts +36 -0
  47. package/dist/internal-types/store/schema.d.ts +443 -0
  48. package/dist/internal-types/store/settings.d.ts +63 -0
  49. package/dist/internal-types/store/torn.d.ts +78 -0
  50. package/dist/migration-source.d.ts +33 -0
  51. package/dist/models.d.ts +41 -0
  52. package/dist/native-account-runtime.d.ts +121 -0
  53. package/dist/native-account-view.d.ts +126 -0
  54. package/dist/native-command-effects.d.ts +59 -0
  55. package/dist/native-credential-validation.d.ts +42 -0
  56. package/dist/native-custody-activation.d.ts +45 -0
  57. package/dist/native-custody.d.ts +114 -0
  58. package/dist/native-enrollment-authority.d.ts +2 -0
  59. package/dist/native-host-auth-read.d.ts +3 -0
  60. package/dist/native-host-auth-write.d.ts +51 -0
  61. package/dist/native-host-auth.d.ts +12 -0
  62. package/dist/native-local-credential-service.d.ts +76 -0
  63. package/dist/native-local-runtime-readers.d.ts +22 -0
  64. package/dist/native-lock-observer.d.ts +12 -0
  65. package/dist/native-menu-executor.d.ts +243 -0
  66. package/dist/native-menu-model.d.ts +142 -0
  67. package/dist/native-migration-proof.d.ts +28 -0
  68. package/dist/native-migration-source.d.ts +47 -0
  69. package/dist/native-migration.d.ts +66 -0
  70. package/dist/native-quota-codec.d.ts +32 -0
  71. package/dist/native-refresh-coordinator.d.ts +240 -0
  72. package/dist/native-roster-seed.d.ts +115 -0
  73. package/dist/native-runtime-registry.d.ts +19 -0
  74. package/dist/native-runtime.d.ts +204 -0
  75. package/dist/native-transient-account.d.ts +38 -0
  76. package/dist/native-ui.d.ts +45 -0
  77. package/dist/native-usage-publication.d.ts +11 -0
  78. package/dist/native-vault-admission.d.ts +8 -0
  79. package/dist/native-vault-connection.d.ts +33 -0
  80. package/dist/native-vault-paths.d.ts +13 -0
  81. package/dist/native-vault-runtime.d.ts +102 -0
  82. package/dist/native-vault-seed.d.ts +16 -0
  83. package/dist/pool-authority.d.ts +159 -0
  84. package/dist/pool-binding.d.ts +32 -0
  85. package/dist/pool-paths.d.ts +21 -0
  86. package/dist/pool-store.d.ts +32 -0
  87. package/dist/provider-error.d.ts +139 -0
  88. package/dist/quota-manager.d.ts +18 -7
  89. package/dist/relay.d.ts +14 -1
  90. package/dist/request-history.d.ts +34 -0
  91. package/dist/sse.d.ts +81 -0
  92. package/dist/sticky-routing.d.ts +27 -0
  93. package/package.json +9 -4
  94. package/dist/accounts.js +0 -3881
  95. package/dist/auth.js +0 -185
  96. package/dist/cache1h.js +0 -97
  97. package/dist/cachekeep-registry.js +0 -124
  98. package/dist/cachekeep.js +0 -604
  99. package/dist/cch.js +0 -172
  100. package/dist/claude-code.js +0 -403
  101. package/dist/claustrum-enrollment-registry.d.ts +0 -17
  102. package/dist/claustrum-enrollment-registry.js +0 -194
  103. package/dist/claustrum-enrollment.js +0 -485
  104. package/dist/claustrum-scoped-roster.js +0 -194
  105. package/dist/claustrum-scoped-runtime.js +0 -230
  106. package/dist/claustrum-scoped.js +0 -193
  107. package/dist/claustrum.js +0 -1471
  108. package/dist/commands/account.js +0 -368
  109. package/dist/constants.js +0 -83
  110. package/dist/custom-headers.js +0 -102
  111. package/dist/dump.js +0 -616
  112. package/dist/fast.js +0 -66
  113. package/dist/json.js +0 -10
  114. package/dist/killswitch.js +0 -132
  115. package/dist/logger.js +0 -288
  116. package/dist/logging.js +0 -59
  117. package/dist/mid-conversation-output-config.js +0 -89
  118. package/dist/model-remap.js +0 -72
  119. package/dist/models.js +0 -177
  120. package/dist/network-errors.js +0 -27
  121. package/dist/oauth-profile.js +0 -60
  122. package/dist/pkce.js +0 -17
  123. package/dist/prime.js +0 -841
  124. package/dist/provider.js +0 -0
  125. package/dist/quota-header-feed.js +0 -331
  126. package/dist/quota-headers.js +0 -121
  127. package/dist/quota-manager.js +0 -856
  128. package/dist/quotas.js +0 -141
  129. package/dist/relay.js +0 -1303
  130. package/dist/routing.js +0 -94
  131. package/dist/start.js +0 -29
  132. package/dist/sticky-routing.js +0 -541
  133. package/dist/thinking-binding.js +0 -54
  134. package/dist/token-fingerprint.js +0 -8
package/dist/dump.js DELETED
@@ -1,616 +0,0 @@
1
- import { createHash, randomBytes } from 'node:crypto';
2
- import { lstat, mkdir, readdir, readFile, rename, unlink, writeFile, } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import { join, resolve } from 'node:path';
5
- import { extractBillingHeaderCCH } from './cch.js';
6
- import { parseJsonRedacted } from './json.js';
7
- import { isSecretKey, logger, relayLog } from './logger.js';
8
- export const CLAUDE_DUMP_COMMAND_NAME = 'claude-dump';
9
- const DUMP_STATUS_TITLE = '## Claude Dump Status';
10
- const DUMP_ENABLED_TITLE = '## Claude Dump Enabled';
11
- const DUMP_DISABLED_TITLE = '## Claude Dump Disabled';
12
- const DUMP_USAGE_TITLE = '## Claude Dump Usage';
13
- const DUMP_USAGE = 'Usage: `/claude-dump`, `/claude-dump on`, or `/claude-dump off`.';
14
- const DUMP_DIR_ENV = 'OPENCODE_ANTHROPIC_AUTH_DUMP_DIR';
15
- const DUMP_MAX_BYTES_ENV = 'OPENCODE_ANTHROPIC_AUTH_DUMP_MAX_BYTES';
16
- const DEFAULT_DUMP_DIR = join(tmpdir(), 'opencode-anthropic-auth-dumps');
17
- const DEFAULT_DUMP_MAX_BYTES = 512 * 1024 * 1024;
18
- const DUMP_SWEEP_INTERVAL_MS = 5 * 60 * 1000;
19
- const DUMP_SWEEP_NEWNESS_FLOOR_MS = 60 * 1000;
20
- const DUMP_PARTIAL_STALE_MS = 10 * 60 * 1000;
21
- const DUMP_ARTIFACT_SUFFIX_PATTERN = /\.(body|meta|relay|request|response)\.json$/;
22
- // Earlier builds emitted five-digit counters; current counters grow without truncation.
23
- const DUMP_ARTIFACT_ID_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z-\d{5,}-(.+)$/;
24
- const DUMP_SEGMENT_PATTERN = '[a-zA-Z0-9._-]';
25
- const DIRECT_DUMP_PATTERN = new RegExp(`^${DUMP_SEGMENT_PATTERN}{1,80}-direct(?:-${DUMP_SEGMENT_PATTERN}{1,80})?$`);
26
- const RELAY_DUMP_PATTERN = new RegExp(`^${DUMP_SEGMENT_PATTERN}{1,80}-(?:http|websocket)-p[12]-(?:full_sync|patch)$`);
27
- const DIRECT_DUMP_SUFFIX_PATTERN = new RegExp(`^-direct(?:-${DUMP_SEGMENT_PATTERN}{1,80})?$`);
28
- const RELAY_DUMP_SUFFIX_PATTERN = /^-(?:http|websocket)-p[12]-(?:full_sync|patch)$/;
29
- let dumpEnabled = false;
30
- let nextDumpId = 0;
31
- let lastDumpSweepAt = 0;
32
- const DIRECT_DUMP_PREVIOUS_BODY_LIMIT = 100;
33
- const directDumpPreviousBodies = new Map();
34
- export function isDumpEnabled() {
35
- return dumpEnabled;
36
- }
37
- export function setDumpEnabled(enabled) {
38
- dumpEnabled = enabled;
39
- }
40
- export function resetDumpState() {
41
- dumpEnabled = false;
42
- nextDumpId = 0;
43
- lastDumpSweepAt = 0;
44
- directDumpPreviousBodies.clear();
45
- }
46
- export function getDumpDirectory() {
47
- return process.env[DUMP_DIR_ENV] || DEFAULT_DUMP_DIR;
48
- }
49
- function getDumpMaxBytes() {
50
- const configured = Number(process.env[DUMP_MAX_BYTES_ENV]);
51
- return Number.isFinite(configured) && configured >= 0
52
- ? Math.floor(configured)
53
- : DEFAULT_DUMP_MAX_BYTES;
54
- }
55
- function isConfiguredDumpDirectory(path) {
56
- return resolve(path) === resolve(getDumpDirectory());
57
- }
58
- function isDumpArtifactFileName(name) {
59
- const stem = name.replace(DUMP_ARTIFACT_SUFFIX_PATTERN, '');
60
- if (stem === name)
61
- return false;
62
- const match = DUMP_ARTIFACT_ID_PATTERN.exec(stem);
63
- const requestPath = match?.[1];
64
- if (!requestPath)
65
- return false;
66
- return (DIRECT_DUMP_PATTERN.test(requestPath) ||
67
- RELAY_DUMP_PATTERN.test(requestPath));
68
- }
69
- function isDumpPartialFileName(name) {
70
- if (!name.endsWith('.partial'))
71
- return false;
72
- const partialStem = name.slice(0, -8);
73
- if (isDumpArtifactFileName(partialStem))
74
- return true;
75
- const separator = partialStem.lastIndexOf('.');
76
- if (separator < 0)
77
- return false;
78
- const nonce = partialStem.slice(separator + 1);
79
- return (/^[a-f0-9]{24}$/.test(nonce) &&
80
- isDumpArtifactFileName(partialStem.slice(0, separator)));
81
- }
82
- export async function writeDumpFile(path, contents) {
83
- for (let attempt = 0; attempt < 2; attempt += 1) {
84
- const nonce = randomBytes(12).toString('hex');
85
- const partialPath = `${path}.${nonce}.partial`;
86
- let created = false;
87
- try {
88
- await writeFile(partialPath, contents, { encoding: 'utf8', flag: 'wx' });
89
- created = true;
90
- await rename(partialPath, path);
91
- return true;
92
- }
93
- catch (error) {
94
- if (error.code === 'EEXIST')
95
- continue;
96
- if (created)
97
- await unlink(partialPath).catch(() => { });
98
- throw error;
99
- }
100
- }
101
- return false;
102
- }
103
- /**
104
- * Caps completed dump artifacts in the configured directory. A zero-byte cap
105
- * disables sweeping. Custom directories may contain unrelated files: only
106
- * recognized final artifact names and stale partials are eligible. Symlinks,
107
- * fresh partial writes, and files younger than the newness floor remain
108
- * protected.
109
- */
110
- export async function sweepDumpDirectory(options) {
111
- const dumpDir = options.dumpDir ?? getDumpDirectory();
112
- const maxBytes = options.maxBytes ?? getDumpMaxBytes();
113
- const now = options.now ?? Date.now();
114
- const minAgeMs = options.minAgeMs ?? DUMP_SWEEP_NEWNESS_FLOOR_MS;
115
- const partialStaleMs = options.partialStaleMs ?? DUMP_PARTIAL_STALE_MS;
116
- const emptyResult = { removed: 0, freedBytes: 0 };
117
- if (!isConfiguredDumpDirectory(dumpDir) || maxBytes <= 0)
118
- return emptyResult;
119
- try {
120
- const dumpDirStats = await lstat(dumpDir);
121
- if (dumpDirStats.isSymbolicLink())
122
- return emptyResult;
123
- const protectedPaths = new Set((options.protectedPaths ?? []).map((path) => resolve(path)));
124
- const entries = await readdir(dumpDir, { withFileTypes: true });
125
- const files = [];
126
- await Promise.all(entries.map(async (entry) => {
127
- if (!entry.isFile())
128
- return;
129
- const partial = isDumpPartialFileName(entry.name);
130
- if (!partial && !isDumpArtifactFileName(entry.name))
131
- return;
132
- const path = join(dumpDir, entry.name);
133
- try {
134
- const stats = await lstat(path);
135
- if (!stats.isFile() || stats.isSymbolicLink())
136
- return;
137
- files.push({
138
- path,
139
- size: stats.size,
140
- mtimeMs: stats.mtimeMs,
141
- partial,
142
- ...(!partial && {
143
- artifactGroup: entry.name.replace(DUMP_ARTIFACT_SUFFIX_PATTERN, ''),
144
- }),
145
- });
146
- }
147
- catch {
148
- // Files can disappear while concurrent dump requests finish.
149
- }
150
- }));
151
- let totalBytes = files.reduce((total, file) => total + file.size, 0);
152
- let removed = 0;
153
- let freedBytes = 0;
154
- const removeFile = async (file) => {
155
- try {
156
- await unlink(file.path);
157
- totalBytes -= file.size;
158
- freedBytes += file.size;
159
- removed += 1;
160
- }
161
- catch {
162
- // Dump cleanup is best-effort and must not affect request handling.
163
- }
164
- };
165
- // Partials are not usable dump records. Reclaim stale ones independently,
166
- // including while the completed-artifact set is already below the cap.
167
- const partials = files
168
- .filter((file) => file.partial)
169
- .sort((left, right) => left.mtimeMs - right.mtimeMs || left.path.localeCompare(right.path));
170
- for (const file of partials) {
171
- if (protectedPaths.has(resolve(file.path)))
172
- continue;
173
- if (now - file.mtimeMs < partialStaleMs)
174
- continue;
175
- await removeFile(file);
176
- }
177
- const artifactGroups = new Map();
178
- for (const file of files) {
179
- if (file.partial || !file.artifactGroup)
180
- continue;
181
- const group = artifactGroups.get(file.artifactGroup) ?? {
182
- files: [],
183
- newestMtimeMs: 0,
184
- };
185
- group.files.push(file);
186
- group.newestMtimeMs = Math.max(group.newestMtimeMs, file.mtimeMs);
187
- artifactGroups.set(file.artifactGroup, group);
188
- }
189
- const completedGroups = [...artifactGroups.entries()].sort(([leftName, left], [rightName, right]) => left.newestMtimeMs - right.newestMtimeMs ||
190
- leftName.localeCompare(rightName));
191
- for (const [, group] of completedGroups) {
192
- if (totalBytes <= maxBytes)
193
- break;
194
- if (now - group.newestMtimeMs < minAgeMs)
195
- continue;
196
- if (group.files.some((file) => protectedPaths.has(resolve(file.path)))) {
197
- continue;
198
- }
199
- for (const file of group.files)
200
- await removeFile(file);
201
- }
202
- if (removed > 0) {
203
- logger.debug('dump', 'removed old dump files', { removed, freedBytes });
204
- }
205
- return { removed, freedBytes };
206
- }
207
- catch {
208
- return emptyResult;
209
- }
210
- }
211
- function scheduleDumpSweep(dumpDir, protectedPaths) {
212
- const now = Date.now();
213
- if (now - lastDumpSweepAt < DUMP_SWEEP_INTERVAL_MS)
214
- return;
215
- lastDumpSweepAt = now;
216
- void sweepDumpDirectory({ dumpDir, protectedPaths }).catch(() => { });
217
- }
218
- export function parseDumpCommandAction(argumentsText) {
219
- const normalized = argumentsText.trim().split(/\s+/).filter(Boolean);
220
- if (normalized.length === 0)
221
- return { type: 'status' };
222
- if (normalized.length === 1 && normalized[0] === 'on')
223
- return { type: 'enable' };
224
- if (normalized.length === 1 && normalized[0] === 'off')
225
- return { type: 'disable' };
226
- return { type: 'usage' };
227
- }
228
- export function buildDumpStatusSummary(input) {
229
- const enabled = input?.enabled ?? dumpEnabled;
230
- return [
231
- DUMP_STATUS_TITLE,
232
- '',
233
- `- Enabled: ${enabled ? 'enabled' : 'disabled'}`,
234
- `- Directory: ${getDumpDirectory()}`,
235
- '- Persisted: ~/.config/opencode/anthropic-auth.json',
236
- '- Captures: final rewritten Anthropic body in direct and relay modes; request/relay metadata is redacted',
237
- '- Warning: body dumps may contain prompt/session content; turn this off after debugging',
238
- ].join('\n');
239
- }
240
- export function executeDumpCommand(input) {
241
- const action = parseDumpCommandAction(input.argumentsText);
242
- const enabled = input.enabled ?? dumpEnabled;
243
- if (action.type === 'status')
244
- return buildDumpStatusSummary({ enabled });
245
- if (action.type === 'enable') {
246
- return [
247
- DUMP_ENABLED_TITLE,
248
- '',
249
- buildDumpStatusSummary({ enabled: true }),
250
- ].join('\n');
251
- }
252
- if (action.type === 'disable') {
253
- return [
254
- DUMP_DISABLED_TITLE,
255
- '',
256
- buildDumpStatusSummary({ enabled: false }),
257
- ].join('\n');
258
- }
259
- return [
260
- DUMP_USAGE_TITLE,
261
- '',
262
- DUMP_USAGE,
263
- '',
264
- buildDumpStatusSummary({ enabled }),
265
- ].join('\n');
266
- }
267
- function shortAffinity(affinity) {
268
- return affinity.length <= 16 ? affinity : `${affinity.slice(0, 12)}…`;
269
- }
270
- function dumpFileSessionSegment(affinity, maxLength = 80) {
271
- const normalized = affinity
272
- .trim()
273
- .replace(/[^a-zA-Z0-9._-]+/g, '-')
274
- .replace(/^-+|-+$/g, '');
275
- if (!normalized)
276
- return 'session-unknown';
277
- return normalized.length <= maxLength
278
- ? normalized
279
- : normalized.slice(0, maxLength);
280
- }
281
- function dumpRequestSegment(input) {
282
- if (input.transport !== 'direct') {
283
- return `${input.transport}-p${input.protocol}-${input.mode}`;
284
- }
285
- const route = input.route ? `-${dumpFileSessionSegment(input.route)}` : '';
286
- return `direct${route}`;
287
- }
288
- function dumpTagSegment(tag) {
289
- if (tag === 'cachekeep')
290
- return '-prewarm-cachekeep';
291
- if (tag === 'start')
292
- return '-start';
293
- return '';
294
- }
295
- function directDumpPreviousKey(input) {
296
- const affinity = input.affinity?.trim();
297
- if (affinity)
298
- return `session:${affinity}`;
299
- return `request:${input.route ?? 'direct'}:${input.url ?? ''}`;
300
- }
301
- function dumpBodyTagForAffinity(name, affinity) {
302
- if (!name.endsWith('.body.json'))
303
- return undefined;
304
- const stem = name.replace(DUMP_ARTIFACT_SUFFIX_PATTERN, '');
305
- const requestPath = DUMP_ARTIFACT_ID_PATTERN.exec(stem)?.[1];
306
- if (!requestPath ||
307
- (!DIRECT_DUMP_PATTERN.test(requestPath) &&
308
- !RELAY_DUMP_PATTERN.test(requestPath))) {
309
- return undefined;
310
- }
311
- const cachekeepTag = dumpTagSegment('cachekeep');
312
- const candidates = [
313
- { prefix: dumpFileSessionSegment(affinity), tag: null },
314
- {
315
- prefix: `${dumpFileSessionSegment(affinity, 80 - cachekeepTag.length)}${cachekeepTag}`,
316
- tag: 'cachekeep',
317
- },
318
- ];
319
- for (const candidate of candidates) {
320
- if (!requestPath.startsWith(`${candidate.prefix}-`))
321
- continue;
322
- const suffix = requestPath.slice(candidate.prefix.length);
323
- if (DIRECT_DUMP_SUFFIX_PATTERN.test(suffix) ||
324
- RELAY_DUMP_SUFFIX_PATTERN.test(suffix)) {
325
- return candidate.tag;
326
- }
327
- }
328
- return undefined;
329
- }
330
- async function loadLatestDumpBody(input) {
331
- const affinity = input.affinity?.trim();
332
- if (!affinity)
333
- return undefined;
334
- const dumpDir = getDumpDirectory();
335
- let names;
336
- try {
337
- names = await readdir(dumpDir);
338
- }
339
- catch {
340
- return undefined;
341
- }
342
- const candidates = names
343
- .map((name) => ({ name, tag: dumpBodyTagForAffinity(name, affinity) }))
344
- .filter((candidate) => candidate.tag !== undefined)
345
- .sort((a, b) => b.name.localeCompare(a.name));
346
- for (const candidate of candidates) {
347
- try {
348
- const path = join(dumpDir, candidate.name);
349
- const metadataPath = path.replace(/\.body\.json$/, '.meta.json');
350
- if (!(await lstat(path)).isFile())
351
- continue;
352
- const metadata = JSON.parse(await readFile(metadataPath, 'utf8'));
353
- if (metadata.session !== shortAffinity(affinity) ||
354
- (candidate.tag === null
355
- ? metadata.tag != null
356
- : metadata.tag !== candidate.tag)) {
357
- continue;
358
- }
359
- return await readFile(path, 'utf8');
360
- }
361
- catch {
362
- // A concurrent sweep can remove the newest artifact; try its predecessor.
363
- }
364
- }
365
- return undefined;
366
- }
367
- function rememberDirectDumpBody(key, bodyText) {
368
- if (!directDumpPreviousBodies.has(key)) {
369
- while (directDumpPreviousBodies.size >= DIRECT_DUMP_PREVIOUS_BODY_LIMIT) {
370
- const oldest = directDumpPreviousBodies.keys().next().value;
371
- if (oldest === undefined)
372
- break;
373
- directDumpPreviousBodies.delete(oldest);
374
- }
375
- }
376
- directDumpPreviousBodies.set(key, bodyText);
377
- }
378
- function headersToRecord(headers) {
379
- if (headers == null)
380
- return undefined;
381
- return Object.fromEntries(new Headers(headers).entries());
382
- }
383
- function hashText(value) {
384
- return createHash('sha256').update(value).digest('hex');
385
- }
386
- function cchToken(bodyText) {
387
- return extractBillingHeaderCCH(bodyText);
388
- }
389
- function diffSummary(previousBodyText, bodyText) {
390
- if (previousBodyText == null)
391
- return null;
392
- if (previousBodyText === bodyText) {
393
- return {
394
- changed: false,
395
- firstByte: -1,
396
- lastPreviousByte: -1,
397
- lastCurrentByte: -1,
398
- previousBytes: previousBodyText.length,
399
- currentBytes: bodyText.length,
400
- };
401
- }
402
- let firstByte = 0;
403
- while (firstByte < previousBodyText.length &&
404
- firstByte < bodyText.length &&
405
- previousBodyText[firstByte] === bodyText[firstByte]) {
406
- firstByte += 1;
407
- }
408
- let previousTail = previousBodyText.length - 1;
409
- let currentTail = bodyText.length - 1;
410
- while (previousTail >= firstByte &&
411
- currentTail >= firstByte &&
412
- previousBodyText[previousTail] === bodyText[currentTail]) {
413
- previousTail -= 1;
414
- currentTail -= 1;
415
- }
416
- return {
417
- changed: true,
418
- firstByte,
419
- lastPreviousByte: previousTail,
420
- lastCurrentByte: currentTail,
421
- changedPreviousBytes: previousTail - firstByte + 1,
422
- changedCurrentBytes: currentTail - firstByte + 1,
423
- previousBytes: previousBodyText.length,
424
- currentBytes: bodyText.length,
425
- };
426
- }
427
- function parseBody(bodyText) {
428
- try {
429
- const parsed = parseJsonRedacted(bodyText);
430
- return parsed != null &&
431
- typeof parsed === 'object' &&
432
- !Array.isArray(parsed)
433
- ? parsed
434
- : null;
435
- }
436
- catch (error) {
437
- relayLog(`dump body parse failed: ${error instanceof Error ? error.message : String(error)}`);
438
- return null;
439
- }
440
- }
441
- function hashJson(value) {
442
- return hashText(JSON.stringify(value));
443
- }
444
- function bodyStructureSummary(bodyText) {
445
- const parsed = parseBody(bodyText);
446
- if (!parsed)
447
- return { parseable: false };
448
- const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
449
- const system = Array.isArray(parsed.system) ? parsed.system : [];
450
- const message0 = messages[0];
451
- const messagesAfter0 = messages.slice(1);
452
- return {
453
- parseable: true,
454
- model: typeof parsed.model === 'string' ? parsed.model : undefined,
455
- stream: parsed.stream,
456
- systemCount: system.length,
457
- messagesCount: messages.length,
458
- systemHash: hashJson(system),
459
- message0Hash: message0 === undefined ? null : hashJson(message0),
460
- message0Bytes: message0 === undefined ? 0 : JSON.stringify(message0).length,
461
- messagesAfter0Hash: hashJson(messagesAfter0),
462
- messagesAfter0Bytes: JSON.stringify(messagesAfter0).length,
463
- cch: cchToken(bodyText),
464
- };
465
- }
466
- function redactForDump(value) {
467
- if (Array.isArray(value))
468
- return value.map(redactForDump);
469
- if (value == null || typeof value !== 'object')
470
- return value;
471
- const redacted = {};
472
- for (const [key, entry] of Object.entries(value)) {
473
- const lower = key.toLowerCase();
474
- if (lower === 'authorization' ||
475
- lower === 'x-api-key' ||
476
- lower === 'cookie' ||
477
- lower === 'set-cookie' ||
478
- isSecretKey(key)) {
479
- redacted[key] = '[redacted]';
480
- continue;
481
- }
482
- redacted[key] = redactForDump(entry);
483
- }
484
- return redacted;
485
- }
486
- async function dumpRequest(input) {
487
- if (!dumpEnabled)
488
- return null;
489
- nextDumpId += 1;
490
- const affinity = input.affinity?.trim() || 'session-unknown';
491
- const tagSegment = dumpTagSegment(input.tag);
492
- const id = `${new Date().toISOString().replace(/[:.]/g, '-')}-${String(nextDumpId).padStart(6, '0')}-${dumpFileSessionSegment(affinity, 80 - tagSegment.length)}${tagSegment}-${dumpRequestSegment(input)}`;
493
- const dumpDir = getDumpDirectory();
494
- const prefix = join(dumpDir, id);
495
- const files = {
496
- body: `${prefix}.body.json`,
497
- metadata: `${prefix}.meta.json`,
498
- response: `${prefix}.response.json`,
499
- };
500
- if (input.payload !== undefined)
501
- files.relay = `${prefix}.relay.json`;
502
- if (input.request !== undefined)
503
- files.request = `${prefix}.request.json`;
504
- try {
505
- await mkdir(dumpDir, { recursive: true });
506
- const previousBodyText = input.previousBodyText ?? (await loadLatestDumpBody(input));
507
- const metadata = {
508
- id,
509
- createdAt: new Date().toISOString(),
510
- session: shortAffinity(affinity),
511
- transport: input.transport,
512
- protocol: input.protocol,
513
- mode: input.mode,
514
- route: input.route,
515
- status: input.status,
516
- error: input.error,
517
- tag: input.tag,
518
- bodyBytes: input.bodyText.length,
519
- relayBytes: input.relayBytes,
520
- bodyHash: hashText(input.bodyText),
521
- diff: diffSummary(previousBodyText, input.bodyText),
522
- body: bodyStructureSummary(input.bodyText),
523
- files,
524
- };
525
- const writes = [
526
- writeDumpFile(files.body, input.bodyText),
527
- writeDumpFile(files.metadata, `${JSON.stringify(metadata, null, 2)}\n`),
528
- ];
529
- if (input.payload !== undefined && files.relay) {
530
- writes.push(writeDumpFile(files.relay, `${JSON.stringify(redactForDump(input.payload), null, 2)}\n`));
531
- }
532
- if (input.request !== undefined && files.request) {
533
- writes.push(writeDumpFile(files.request, `${JSON.stringify(redactForDump({
534
- ...input.request,
535
- headers: headersToRecord(input.request.headers),
536
- }), null, 2)}\n`));
537
- }
538
- await Promise.all(writes);
539
- scheduleDumpSweep(dumpDir, Object.values(files));
540
- relayLog(`dumped request id=${id} session=${shortAffinity(affinity)} body=${files.body} meta=${files.metadata}`);
541
- }
542
- catch (error) {
543
- relayLog(`dump failed: ${error instanceof Error ? error.message : String(error)}`);
544
- return null;
545
- }
546
- return {
547
- responsePath: files.response,
548
- ...(input.tag ? { tag: input.tag } : {}),
549
- };
550
- }
551
- export async function dumpDirectRequest(input) {
552
- if (!dumpEnabled)
553
- return null;
554
- const previousKey = directDumpPreviousKey(input);
555
- const previousBodyText = directDumpPreviousBodies.get(previousKey);
556
- const handle = await dumpRequest({
557
- affinity: input.affinity,
558
- transport: 'direct',
559
- route: input.route,
560
- status: input.status,
561
- error: input.error,
562
- bodyText: input.bodyText,
563
- previousBodyText,
564
- request: {
565
- url: input.url,
566
- method: input.method,
567
- headers: input.headers,
568
- },
569
- tag: input.tag,
570
- });
571
- rememberDirectDumpBody(previousKey, input.bodyText);
572
- return handle;
573
- }
574
- export async function dumpRelayRequest(input) {
575
- return dumpRequest({
576
- affinity: input.affinity,
577
- transport: input.transport,
578
- protocol: input.protocol,
579
- mode: input.mode,
580
- status: input.status,
581
- bodyText: input.bodyText,
582
- previousBodyText: input.previousBodyText,
583
- payload: input.payload,
584
- relayBytes: input.relayBytes,
585
- tag: input.tag,
586
- });
587
- }
588
- export async function dumpResponseArtifact(handle, input) {
589
- if (!handle)
590
- return;
591
- const message = input.message != null &&
592
- typeof input.message === 'object' &&
593
- !Array.isArray(input.message)
594
- ? input.message
595
- : {};
596
- const artifact = {
597
- status: input.status,
598
- stream_complete: input.complete ?? true,
599
- };
600
- if (typeof message.id === 'string' && message.id.length > 0)
601
- artifact.message_id = message.id;
602
- if (typeof message.model === 'string' && message.model.length > 0)
603
- artifact.model = message.model;
604
- if (Object.hasOwn(message, 'usage'))
605
- artifact.usage = message.usage;
606
- if (Object.hasOwn(message, 'diagnostics'))
607
- artifact.diagnostics = message.diagnostics;
608
- if (typeof message.stop_reason === 'string' && message.stop_reason.length > 0)
609
- artifact.stop_reason = message.stop_reason;
610
- try {
611
- await writeDumpFile(handle.responsePath, `${JSON.stringify(artifact, null, 2)}\n`);
612
- }
613
- catch (error) {
614
- relayLog(`dump response failed: ${error instanceof Error ? error.message : String(error)}`);
615
- }
616
- }
package/dist/fast.js DELETED
@@ -1,66 +0,0 @@
1
- export const CLAUDE_FAST_COMMAND_NAME = 'claude-fast';
2
- const FAST_STATUS_TITLE = '## Claude Fast Mode Status';
3
- const FAST_ENABLED_TITLE = '## Claude Fast Mode Enabled';
4
- const FAST_DISABLED_TITLE = '## Claude Fast Mode Disabled';
5
- const FAST_USAGE_TITLE = '## Claude Fast Mode Usage';
6
- const FAST_USAGE = 'Usage: `/claude-fast`, `/claude-fast on`, or `/claude-fast off`.';
7
- let fastModeEnabled = false;
8
- export function isFastModeEnabled() {
9
- return fastModeEnabled;
10
- }
11
- export function setFastModeEnabled(enabled) {
12
- fastModeEnabled = enabled;
13
- }
14
- export function resetFastModeState() {
15
- fastModeEnabled = false;
16
- }
17
- export function parseFastModeCommandAction(argumentsText) {
18
- const normalized = argumentsText.trim().split(/\s+/).filter(Boolean);
19
- if (normalized.length === 0)
20
- return { type: 'status' };
21
- if (normalized.length === 1 && normalized[0] === 'on')
22
- return { type: 'enable' };
23
- if (normalized.length === 1 && normalized[0] === 'off')
24
- return { type: 'disable' };
25
- return { type: 'usage' };
26
- }
27
- export function buildFastModeStatusSummary(input) {
28
- const enabled = input?.enabled ?? fastModeEnabled;
29
- return [
30
- FAST_STATUS_TITLE,
31
- '',
32
- `- Enabled: ${enabled ? 'enabled' : 'disabled'}`,
33
- '- Persisted: ~/.config/opencode/anthropic-auth.json',
34
- '- Scope: adds Anthropic fast mode to supported Opus requests',
35
- '- Supported models: claude-opus-4-6, claude-opus-4-7, claude-opus-4-8, claude-opus-5, and claude-opus-5-5',
36
- '- Request changes: adds `speed: "fast"` and the `fast-mode-2026-02-01` beta header',
37
- '- Note: fast and standard speeds do not share prompt-cache prefixes',
38
- ].join('\n');
39
- }
40
- export function executeFastModeCommand(input) {
41
- const action = parseFastModeCommandAction(input.argumentsText);
42
- const enabled = input.enabled ?? fastModeEnabled;
43
- if (action.type === 'status')
44
- return buildFastModeStatusSummary({ enabled });
45
- if (action.type === 'enable') {
46
- return [
47
- FAST_ENABLED_TITLE,
48
- '',
49
- buildFastModeStatusSummary({ enabled: true }),
50
- ].join('\n');
51
- }
52
- if (action.type === 'disable') {
53
- return [
54
- FAST_DISABLED_TITLE,
55
- '',
56
- buildFastModeStatusSummary({ enabled: false }),
57
- ].join('\n');
58
- }
59
- return [
60
- FAST_USAGE_TITLE,
61
- '',
62
- FAST_USAGE,
63
- '',
64
- buildFastModeStatusSummary({ enabled }),
65
- ].join('\n');
66
- }
package/dist/json.js DELETED
@@ -1,10 +0,0 @@
1
- export function parseJsonRedacted(text) {
2
- try {
3
- return JSON.parse(text);
4
- }
5
- catch (error) {
6
- if (error instanceof SyntaxError)
7
- throw new SyntaxError('invalid JSON');
8
- throw error;
9
- }
10
- }