@cortexkit/anthropic-auth-core 2.4.2 → 2.4.3

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.
@@ -72,6 +72,7 @@ export type CacheKeepTarget = {
72
72
  url: string;
73
73
  headers: Record<string, string>;
74
74
  bodyText: string;
75
+ bodyBytes: number;
75
76
  cacheExpiresAt: number;
76
77
  nextPrewarmAt: number;
77
78
  consecutiveFailures: number;
@@ -157,6 +158,7 @@ export declare class CacheKeepManager {
157
158
  dropExpiredTargets(now?: number): number | undefined;
158
159
  private publishTrackedSessions;
159
160
  private totalBodyBytes;
161
+ private removeTarget;
160
162
  private evictOldestTarget;
161
163
  private pruneTargets;
162
164
  track(input: {
@@ -171,11 +173,10 @@ export declare class CacheKeepManager {
171
173
  accountStoragePath?: string;
172
174
  isSubagent?: boolean;
173
175
  }): {
174
- tracked: boolean;
175
- reason: string;
176
+ tracked: true;
176
177
  } | {
177
- reason?: undefined;
178
- tracked: boolean;
178
+ tracked: false;
179
+ reason: string;
179
180
  };
180
181
  prewarmNow(input: {
181
182
  sessionId: string;
package/dist/index.js CHANGED
@@ -687,7 +687,7 @@ var WRITER_LOCK_CONSTANTS = Object.freeze({
687
687
  });
688
688
  // ../../node_modules/@cortexkit/common-auth/dist/fs/refresh-file-lock.js
689
689
  import { randomUUID as randomUUID3 } from "node:crypto";
690
- import { mkdir as mkdir2, open as open2, readFile as readFile2, rename as rename3, rm as rm2, stat } from "node:fs/promises";
690
+ import { lstat as lstat3, mkdir as mkdir2, open as open2, readFile as readFile2, rename as rename3, rm as rm2, stat } from "node:fs/promises";
691
691
  import { dirname as dirname3, join as join2 } from "node:path";
692
692
 
693
693
  // ../../node_modules/@cortexkit/common-auth/dist/fs/with-lock.js
@@ -747,8 +747,24 @@ async function withLock(target, options, fn) {
747
747
  // ../../node_modules/@cortexkit/common-auth/dist/fs/refresh-file-lock.js
748
748
  async function createExclusiveText(path, text) {
749
749
  const handle = await open2(path, "wx", 384);
750
+ let createdStat;
751
+ try {
752
+ createdStat = await handle.stat();
753
+ } catch {
754
+ createdStat = undefined;
755
+ }
750
756
  try {
751
757
  await handle.writeFile(text, "utf8");
758
+ } catch (error) {
759
+ try {
760
+ const current = await lstat3(path).catch(() => {
761
+ return;
762
+ });
763
+ if (createdStat && current && current.dev === createdStat.dev && current.ino === createdStat.ino) {
764
+ await rm2(path, { force: true }).catch(() => {});
765
+ }
766
+ } catch {}
767
+ throw error;
752
768
  } finally {
753
769
  await handle.close();
754
770
  }
@@ -759,6 +775,15 @@ function isLostMarkerRaceError(error) {
759
775
  const code = error?.code;
760
776
  return code === "ENOENT" || !windowsPrivateFiles && (code === "EINVAL" || code === "ENOTDIR");
761
777
  }
778
+ var evictionMarkerTestHooks;
779
+ function sameMarkerDirectory(created, current) {
780
+ if (created.dev !== current.dev || created.ino !== current.ino)
781
+ return false;
782
+ if (created.ino === 0 && created.birthtimeMs !== current.birthtimeMs)
783
+ return false;
784
+ return true;
785
+ }
786
+ var OWNERLESS_MARKER_GRACE_MS = 1000;
762
787
  function acquireRefreshFileLock(options) {
763
788
  return acquireRefreshFileLockWithClock({ ...options, now: Date.now });
764
789
  }
@@ -1005,12 +1030,47 @@ async function acquireRefreshFileLockWithClock(options) {
1005
1030
  await removeLeaseObject(evictPath);
1006
1031
  }
1007
1032
  }
1033
+ async function removeOwnMarkerAfterFailedOwnerWrite(created) {
1034
+ if (!created)
1035
+ return false;
1036
+ let current;
1037
+ try {
1038
+ const st = await lstat3(evictPath);
1039
+ current = { dev: st.dev, ino: st.ino, birthtimeMs: st.birthtimeMs };
1040
+ } catch {
1041
+ return false;
1042
+ }
1043
+ if (!sameMarkerDirectory(created, current))
1044
+ return false;
1045
+ try {
1046
+ await stat(evictOwnerPath);
1047
+ return true;
1048
+ } catch (error) {
1049
+ if (error.code !== "ENOENT")
1050
+ return true;
1051
+ }
1052
+ try {
1053
+ await removeLeaseObject(evictPath);
1054
+ } catch {}
1055
+ return true;
1056
+ }
1008
1057
  async function tryAcquireEvictionMarker() {
1009
1058
  if (windowsPrivateFiles)
1010
1059
  await createPrivateDirectoryExclusive(evictPath);
1011
1060
  else
1012
1061
  await mkdir2(evictPath);
1062
+ let created;
1063
+ try {
1064
+ const st = await lstat3(evictPath);
1065
+ created = { dev: st.dev, ino: st.ino, birthtimeMs: st.birthtimeMs };
1066
+ } catch {
1067
+ created = undefined;
1068
+ }
1013
1069
  try {
1070
+ await evictionMarkerTestHooks?.beforeOwnerWrite?.(evictPath, evictOwnerPath);
1071
+ const injected = await evictionMarkerTestHooks?.failOwnerWrite?.(evictPath, evictOwnerPath);
1072
+ if (injected !== undefined)
1073
+ throw injected;
1014
1074
  const bytes = `${JSON.stringify({ ownerId: evictOwnerId, createdAt: now() })}
1015
1075
  `;
1016
1076
  if (windowsPrivateFiles)
@@ -1018,9 +1078,12 @@ async function acquireRefreshFileLockWithClock(options) {
1018
1078
  else
1019
1079
  await createExclusiveText(evictOwnerPath, bytes);
1020
1080
  } catch (error) {
1021
- if (isLostMarkerRaceError(error))
1081
+ const stillOurs = await removeOwnMarkerAfterFailedOwnerWrite(created);
1082
+ if (isLostMarkerRaceError(error) && !stillOurs)
1022
1083
  return false;
1023
- await releaseEvictionMarker();
1084
+ try {
1085
+ await releaseEvictionMarker();
1086
+ } catch {}
1024
1087
  throw error;
1025
1088
  }
1026
1089
  if (options.onStep)
@@ -1045,8 +1108,21 @@ async function acquireRefreshFileLockWithClock(options) {
1045
1108
  throw error;
1046
1109
  }
1047
1110
  }
1048
- if (evictStat.mtimeMs + EVICT_TTL > now())
1049
- return "fresh";
1111
+ if (evictStat.mtimeMs + EVICT_TTL > now()) {
1112
+ let hasOwner = true;
1113
+ try {
1114
+ await stat(evictOwnerPath);
1115
+ } catch (error) {
1116
+ if (error.code === "ENOENT")
1117
+ hasOwner = false;
1118
+ else
1119
+ return "fresh";
1120
+ }
1121
+ if (hasOwner)
1122
+ return "fresh";
1123
+ if (evictStat.mtimeMs + OWNERLESS_MARKER_GRACE_MS > now())
1124
+ return "fresh";
1125
+ }
1050
1126
  if (options.onStep)
1051
1127
  await options.onStep("stale-marker-stat");
1052
1128
  const claimedPath = `${evictPath}.${randomUUID3()}`;
@@ -1317,7 +1393,9 @@ async function acquireRefreshFileLockWithClock(options) {
1317
1393
  released = true;
1318
1394
  cancelRenewalTimer();
1319
1395
  await renewalInFlight;
1320
- for (let attempt = 0;attempt < MAX_STEAL_ATTEMPTS; attempt++) {
1396
+ const deadline = performance.now() + EVICT_TTL + 1000;
1397
+ let delayMs = 5;
1398
+ for (;; ) {
1321
1399
  try {
1322
1400
  let removalConfirmed = false;
1323
1401
  const markerAcquired = await withEvictionMarker(async () => {
@@ -1356,7 +1434,11 @@ async function acquireRefreshFileLockWithClock(options) {
1356
1434
  throw error;
1357
1435
  return;
1358
1436
  }
1359
- await backoff();
1437
+ const remainingMs = deadline - performance.now();
1438
+ if (remainingMs <= 0)
1439
+ break;
1440
+ await new Promise((resolve3) => setTimeout(resolve3, Math.min(delayMs, remainingMs)));
1441
+ delayMs = Math.min(delayMs * 2, 50);
1360
1442
  }
1361
1443
  if (windowsPrivateFiles)
1362
1444
  throw new PrivateFileError(lockPath, "release-not-confirmed", {
@@ -5873,7 +5955,7 @@ function claudeCodeEntrypoint() {
5873
5955
  // src/dump.ts
5874
5956
  import { createHash as createHash4, randomBytes as randomBytes3 } from "node:crypto";
5875
5957
  import {
5876
- lstat as lstat3,
5958
+ lstat as lstat4,
5877
5959
  mkdir as mkdir6,
5878
5960
  readdir,
5879
5961
  readFile as readFile6,
@@ -6244,7 +6326,7 @@ async function sweepDumpDirectory(options) {
6244
6326
  if (!isConfiguredDumpDirectory(dumpDir) || maxBytes <= 0)
6245
6327
  return emptyResult;
6246
6328
  try {
6247
- const dumpDirStats = await lstat3(dumpDir);
6329
+ const dumpDirStats = await lstat4(dumpDir);
6248
6330
  if (dumpDirStats.isSymbolicLink())
6249
6331
  return emptyResult;
6250
6332
  const protectedPaths = new Set((options.protectedPaths ?? []).map((path2) => resolve4(path2)));
@@ -6258,7 +6340,7 @@ async function sweepDumpDirectory(options) {
6258
6340
  return;
6259
6341
  const path2 = join9(dumpDir, entry.name);
6260
6342
  try {
6261
- const stats = await lstat3(path2);
6343
+ const stats = await lstat4(path2);
6262
6344
  if (!stats.isFile() || stats.isSymbolicLink())
6263
6345
  return;
6264
6346
  files.push({
@@ -6463,7 +6545,7 @@ async function loadLatestDumpBody(input) {
6463
6545
  try {
6464
6546
  const path2 = join9(dumpDir, candidate.name);
6465
6547
  const metadataPath = path2.replace(/\.body\.json$/, ".meta.json");
6466
- if (!(await lstat3(path2)).isFile())
6548
+ if (!(await lstat4(path2)).isFile())
6467
6549
  continue;
6468
6550
  const metadata = JSON.parse(await readFile6(metadataPath, "utf8"));
6469
6551
  if (metadata.session !== shortAffinity(affinity) || (candidate.tag === null ? metadata.tag != null : metadata.tag !== candidate.tag)) {
@@ -8187,7 +8269,7 @@ var CACHE_KEEP_PREWARM_LEAD_MS = 5 * 60000;
8187
8269
  var CACHE_KEEP_TICK_MS = 60000;
8188
8270
  var CACHE_KEEP_EXTENDED_TTL_BETA = "extended-cache-ttl-2025-04-11";
8189
8271
  var CACHE_KEEP_MAX_TARGETS = 32;
8190
- var CACHE_KEEP_MAX_BODY_BYTES = 16 * 1024 * 1024;
8272
+ var CACHE_KEEP_MAX_BODY_BYTES = 64 * 1024 * 1024;
8191
8273
  var CACHE_KEEP_PREWARM_TIMEOUT_MS = 30000;
8192
8274
  var CACHE_KEEP_RETRY_BASE_MS = 60000;
8193
8275
  var CACHE_KEEP_RETRY_MAX_MS = 15 * 60000;
@@ -8487,47 +8569,92 @@ class CacheKeepManager {
8487
8569
  }
8488
8570
  }
8489
8571
  totalBodyBytes() {
8490
- return [...this.targets.values()].reduce((sum, target) => sum + target.bodyText.length, 0);
8572
+ return [...this.targets.values()].reduce((sum, target) => sum + target.bodyBytes, 0);
8491
8573
  }
8492
- evictOldestTarget() {
8493
- const oldest = this.targets.keys().next().value;
8494
- if (oldest !== undefined)
8495
- this.targets.delete(oldest);
8574
+ removeTarget(target, reason, now, details = {}) {
8575
+ if (this.targets.get(target.id) !== target)
8576
+ return;
8577
+ if (now < target.cacheExpiresAt)
8578
+ logger.warn("cachekeep", "stopped warming session", {
8579
+ session: target.id,
8580
+ reason,
8581
+ bodyBytes: target.bodyBytes,
8582
+ totalBodyBytes: this.totalBodyBytes(),
8583
+ budgetBytes: CACHE_KEEP_MAX_BODY_BYTES,
8584
+ targets: this.targets.size,
8585
+ maxTargets: CACHE_KEEP_MAX_TARGETS,
8586
+ cacheExpiresAt: target.cacheExpiresAt,
8587
+ nextPrewarmAt: target.nextPrewarmAt,
8588
+ ...details
8589
+ });
8590
+ this.targets.delete(target.id);
8591
+ }
8592
+ evictOldestTarget(now, reason) {
8593
+ const oldest = this.targets.values().next().value;
8594
+ if (!oldest)
8595
+ return;
8596
+ const target = [...this.targets.values()].find((candidate) => candidate.nextPrewarmAt > now + 60000) ?? oldest;
8597
+ this.removeTarget(target, reason, now, {
8598
+ detail: "evicted by cachekeep limits",
8599
+ ...target !== oldest && { selection: "imminent-protected" }
8600
+ });
8496
8601
  }
8497
8602
  pruneTargets(now, today) {
8498
8603
  for (const [id, target] of this.targets) {
8499
8604
  if (today && target.dayKey !== today) {
8500
- this.targets.delete(id);
8605
+ this.removeTarget(target, "schedule-period-ended", now);
8501
8606
  continue;
8502
8607
  }
8503
8608
  if (now >= target.cacheExpiresAt)
8504
8609
  this.targets.delete(id);
8505
8610
  }
8506
8611
  while (this.targets.size > CACHE_KEEP_MAX_TARGETS)
8507
- this.evictOldestTarget();
8612
+ this.evictOldestTarget(now, "count");
8508
8613
  while (this.totalBodyBytes() > CACHE_KEEP_MAX_BODY_BYTES) {
8509
8614
  const before = this.targets.size;
8510
- this.evictOldestTarget();
8615
+ this.evictOldestTarget(now, "budget");
8511
8616
  if (this.targets.size === before)
8512
8617
  break;
8513
8618
  }
8514
8619
  }
8515
8620
  track(input) {
8621
+ const now = this.options.now?.() ?? Date.now();
8622
+ let bodyBytes;
8623
+ const refuse = (reason, reasonCode) => {
8624
+ if (reasonCode === "body-budget")
8625
+ logger.warn("cachekeep", "tracking skipped", {
8626
+ session: input.sessionId ?? null,
8627
+ reason: reasonCode,
8628
+ detail: reason,
8629
+ bodyBytes,
8630
+ totalBodyBytes: this.totalBodyBytes(),
8631
+ budgetBytes: CACHE_KEEP_MAX_BODY_BYTES,
8632
+ targets: this.targets.size,
8633
+ maxTargets: CACHE_KEEP_MAX_TARGETS,
8634
+ cacheExpiresAt: input.sessionId ? this.targets.get(input.sessionId)?.cacheExpiresAt : undefined
8635
+ });
8636
+ else
8637
+ logger.debug("cachekeep", "tracking skipped", {
8638
+ session: input.sessionId ?? null,
8639
+ reason: reasonCode
8640
+ });
8641
+ return { tracked: false, reason };
8642
+ };
8516
8643
  if (!input.sessionId)
8517
- return { tracked: false, reason: "missing session id" };
8644
+ return refuse("missing session id", "missing-session");
8518
8645
  if (!isCacheKeepPersistentlyEnabled(input.storage)) {
8519
- return { tracked: false, reason: "cachekeep disabled" };
8646
+ return refuse("cachekeep disabled", "disabled");
8520
8647
  }
8521
8648
  if (input.cacheMode !== "hybrid") {
8522
- return { tracked: false, reason: "cache mode is not hybrid" };
8649
+ return refuse("cache mode is not hybrid", "not-hybrid");
8523
8650
  }
8524
- if (input.bodyText.length > CACHE_KEEP_MAX_BODY_BYTES) {
8525
- return { tracked: false, reason: "body exceeds cachekeep memory budget" };
8651
+ bodyBytes = Buffer.byteLength(input.bodyText, "utf8");
8652
+ if (bodyBytes > CACHE_KEEP_MAX_BODY_BYTES) {
8653
+ return refuse("body exceeds cachekeep memory budget", "body-budget");
8526
8654
  }
8527
- const now = this.options.now?.() ?? Date.now();
8528
8655
  const window = getCacheKeepWindow(input.storage);
8529
8656
  if (!isCacheKeepActiveNow(input.storage, new Date(now))) {
8530
- return { tracked: false, reason: "outside configured schedule" };
8657
+ return refuse("outside configured schedule", "outside-schedule");
8531
8658
  }
8532
8659
  const today = cacheKeepPeriodKey(input.storage, window, new Date(now));
8533
8660
  this.pruneTargets(now, today);
@@ -8542,6 +8669,7 @@ class CacheKeepManager {
8542
8669
  url: input.url,
8543
8670
  headers,
8544
8671
  bodyText: input.bodyText,
8672
+ bodyBytes,
8545
8673
  cacheExpiresAt: now + CACHE_KEEP_TTL_MS,
8546
8674
  nextPrewarmAt: now + CACHE_KEEP_TTL_MS - CACHE_KEEP_PREWARM_LEAD_MS,
8547
8675
  consecutiveFailures: 0,
@@ -8553,6 +8681,8 @@ class CacheKeepManager {
8553
8681
  });
8554
8682
  this.pruneTargets(now, today);
8555
8683
  this.publishTrackedSessions();
8684
+ if (!this.targets.has(input.sessionId))
8685
+ return { tracked: false, reason: "evicted by cachekeep limits" };
8556
8686
  this.start();
8557
8687
  return { tracked: true };
8558
8688
  }
@@ -8566,6 +8696,7 @@ class CacheKeepManager {
8566
8696
  url: input.url,
8567
8697
  headers,
8568
8698
  bodyText: input.bodyText,
8699
+ bodyBytes: Buffer.byteLength(input.bodyText, "utf8"),
8569
8700
  cacheExpiresAt: this.options.now?.() ?? Date.now(),
8570
8701
  nextPrewarmAt: this.options.now?.() ?? Date.now(),
8571
8702
  consecutiveFailures: 0,
@@ -8766,11 +8897,7 @@ class CacheKeepManager {
8766
8897
  const result = await this.sendPrewarm(target);
8767
8898
  if (!result.ok) {
8768
8899
  if (result.status == null && !result.transient) {
8769
- logger.debug("cachekeep", "prewarm skipped", {
8770
- session: target.id,
8771
- reason: result.reason
8772
- });
8773
- this.targets.delete(target.id);
8900
+ this.removeTarget(target, result.reason === "body is not valid JSON" ? "invalid-json" : "no-breakpoints", now, { detail: result.reason });
8774
8901
  } else {
8775
8902
  logger.warn("cachekeep", "prewarm failed", {
8776
8903
  session: target.id,
@@ -8812,7 +8939,7 @@ import { join as join19 } from "node:path";
8812
8939
  import { execFile } from "node:child_process";
8813
8940
  import { randomUUID as randomUUID13 } from "node:crypto";
8814
8941
  import { readFileSync as readFileSync2 } from "node:fs";
8815
- import { lstat as lstat11, mkdir as mkdir9, readFile as readFile11 } from "node:fs/promises";
8942
+ import { lstat as lstat12, mkdir as mkdir9, readFile as readFile11 } from "node:fs/promises";
8816
8943
  import { basename as basename8, dirname as dirname16, join as join18 } from "node:path";
8817
8944
 
8818
8945
  // src/native-lock-observer.ts
@@ -13558,7 +13685,7 @@ function isOpenAIReasoningSignature(value) {
13558
13685
  }
13559
13686
 
13560
13687
  // src/native-vault-seed.ts
13561
- import { lstat as lstat6, readFile as readFile7 } from "node:fs/promises";
13688
+ import { lstat as lstat7, readFile as readFile7 } from "node:fs/promises";
13562
13689
 
13563
13690
  // src/native-migration-source.ts
13564
13691
  import { createHash as createHash13 } from "node:crypto";
@@ -17476,7 +17603,7 @@ var nativeQuotaCodec = {
17476
17603
  // src/native-runtime.ts
17477
17604
  import { randomUUID as randomUUID10 } from "node:crypto";
17478
17605
  import { constants as constants2 } from "node:fs";
17479
- import { lstat as lstat5, mkdir as mkdir7, open as open7, rename as rename9, rm as rm5 } from "node:fs/promises";
17606
+ import { lstat as lstat6, mkdir as mkdir7, open as open7, rename as rename9, rm as rm5 } from "node:fs/promises";
17480
17607
  import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute3, join as join12 } from "node:path";
17481
17608
  import { types as utilTypes5 } from "node:util";
17482
17609
 
@@ -17564,7 +17691,7 @@ function nativeLocalCredentialValidationMatches(validation, binding, material) {
17564
17691
  }
17565
17692
 
17566
17693
  // src/owner-only.ts
17567
- import { lstat as lstat4 } from "node:fs/promises";
17694
+ import { lstat as lstat5 } from "node:fs/promises";
17568
17695
  import { dirname as dirname10 } from "node:path";
17569
17696
  import { types as utilTypes3 } from "node:util";
17570
17697
  async function windowsBackend(options) {
@@ -17631,7 +17758,7 @@ async function ensureWindowsOwnerOnlyDirectory(path2, options = {}) {
17631
17758
  };
17632
17759
  async function exists(directory) {
17633
17760
  try {
17634
- const metadata = await lstat4(directory);
17761
+ const metadata = await lstat5(directory);
17635
17762
  if (metadata.isSymbolicLink())
17636
17763
  throw refused("verify", "reparse-point");
17637
17764
  if (!metadata.isDirectory())
@@ -18035,7 +18162,7 @@ async function readNativeRuntime(path2, storageId, ownership = {}) {
18035
18162
  const noFollow = constants2.O_NOFOLLOW ?? 0;
18036
18163
  try {
18037
18164
  for (let attempt = 0;attempt < 2; attempt++) {
18038
- const info = await lstat5(path2);
18165
+ const info = await lstat6(path2);
18039
18166
  if (!info.isFile() || !await isOwnerOnly(path2, () => (info.mode & 511) === 384 && (typeof process.geteuid !== "function" || info.uid === process.geteuid()), ownership))
18040
18167
  throw new NativeRuntimeError("unsafe-runtime");
18041
18168
  const file = await open7(path2, constants2.O_RDONLY | noFollow);
@@ -19101,7 +19228,7 @@ async function readNativeVaultLegacyJson(path2) {
19101
19228
  }
19102
19229
  async function nativeVaultFileAbsent(path2) {
19103
19230
  try {
19104
- await lstat6(path2);
19231
+ await lstat7(path2);
19105
19232
  return false;
19106
19233
  } catch (error) {
19107
19234
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
@@ -20032,10 +20159,10 @@ function nativeVaultPaths(local, layout = stateLayoutOfPaths(local)) {
20032
20159
  }
20033
20160
 
20034
20161
  // src/relocation-guard.ts
20035
- import { lstat as lstat10 } from "node:fs/promises";
20162
+ import { lstat as lstat11 } from "node:fs/promises";
20036
20163
 
20037
20164
  // src/state-home-serving-guard.ts
20038
- import { lstat as lstat9 } from "node:fs/promises";
20165
+ import { lstat as lstat10 } from "node:fs/promises";
20039
20166
  import { dirname as dirname15, join as join17 } from "node:path";
20040
20167
 
20041
20168
  // src/state-layout-errors.ts
@@ -20086,9 +20213,8 @@ function stateLayoutMovedElsewhere(configDir, relocatedTo, stateRoot) {
20086
20213
 
20087
20214
  // src/state-layout-io.ts
20088
20215
  import { createHash as createHash17 } from "node:crypto";
20089
- import { constants as constants3 } from "node:fs";
20090
20216
  import {
20091
- lstat as lstat7,
20217
+ lstat as lstat8,
20092
20218
  open as open8,
20093
20219
  readFile as readFile9,
20094
20220
  rename as renameFile,
@@ -20130,7 +20256,7 @@ function sha256Hex(bytes) {
20130
20256
  }
20131
20257
  async function digestFile(path2) {
20132
20258
  try {
20133
- const info = await lstat7(path2);
20259
+ const info = await lstat8(path2);
20134
20260
  if (!info.isFile())
20135
20261
  throw new Error(`anthropic-auth state file ${path2} is not a regular file`);
20136
20262
  return { status: "file", sha256: sha256Hex(await readFile9(path2)) };
@@ -20144,9 +20270,15 @@ async function fileSha256(path2) {
20144
20270
  const digest2 = await digestFile(path2);
20145
20271
  return digest2.status === "file" ? digest2.sha256 : null;
20146
20272
  }
20147
- async function writeOwnerOnlyExclusive(path2, bytes, platform) {
20148
- const nativeCreated = platform === "win32" ? await createWindowsOwnerOnly(path2, "") : false;
20149
- const handle = await open8(path2, nativeCreated ? constants3.O_RDWR | constants3.O_TRUNC : "wx", 384);
20273
+ async function writeOwnerOnlyExclusive(path2, bytes, platform, options = {}) {
20274
+ if (platform === "win32") {
20275
+ await createWindowsOwnerOnly(path2, bytes, {
20276
+ ...options.ownership,
20277
+ platform
20278
+ });
20279
+ return;
20280
+ }
20281
+ const handle = await (options.open ?? open8)(path2, "wx", 384);
20150
20282
  try {
20151
20283
  await handle.writeFile(bytes);
20152
20284
  await handle.sync();
@@ -20258,7 +20390,7 @@ class StateLayoutLockStack {
20258
20390
 
20259
20391
  // src/state-layout-record.ts
20260
20392
  import { randomUUID as randomUUID12 } from "node:crypto";
20261
- import { lstat as lstat8, readFile as readFile10, unlink as unlink5 } from "node:fs/promises";
20393
+ import { lstat as lstat9, readFile as readFile10, unlink as unlink5 } from "node:fs/promises";
20262
20394
  import { join as join16 } from "node:path";
20263
20395
  var RELOCATION_ROLES = [
20264
20396
  "localPoolState",
@@ -20447,7 +20579,7 @@ async function readStateLayoutRecord(sub) {
20447
20579
  const path2 = layoutRecordPath(sub);
20448
20580
  let bytes;
20449
20581
  try {
20450
- const info = await lstat8(path2);
20582
+ const info = await lstat9(path2);
20451
20583
  if (!info.isFile() || info.size > 4 * 1024 * 1024)
20452
20584
  throw new StateLayoutFileError("invalid", path2);
20453
20585
  bytes = await readFile10(path2, "utf8");
@@ -20543,7 +20675,7 @@ async function warnReappearedMetadata(record8) {
20543
20675
  }
20544
20676
  async function identityOf(path2) {
20545
20677
  try {
20546
- const info = await lstat9(path2, { bigint: true });
20678
+ const info = await lstat10(path2, { bigint: true });
20547
20679
  return `${info.dev}:${info.ino}:${info.size}:${info.mtimeNs}:${info.ctimeNs}`;
20548
20680
  } catch (error) {
20549
20681
  if (error?.code === "ENOENT")
@@ -20715,7 +20847,7 @@ var reported2 = new Set;
20715
20847
  async function pointerAt(path2) {
20716
20848
  let info;
20717
20849
  try {
20718
- info = await lstat10(path2, { bigint: true });
20850
+ info = await lstat11(path2, { bigint: true });
20719
20851
  } catch {
20720
20852
  observations2.delete(path2);
20721
20853
  return;
@@ -20945,7 +21077,7 @@ function sameRegistration(value, entry2) {
20945
21077
  async function readHolderFile(path2) {
20946
21078
  let text5;
20947
21079
  try {
20948
- const info = await lstat11(path2);
21080
+ const info = await lstat12(path2);
20949
21081
  if (!info.isFile() || info.size > HOLDER_FILE_MAX_BYTES)
20950
21082
  throw new HolderRegistrationError("malformed", dirname16(path2));
20951
21083
  text5 = await readFile11(path2, "utf8");
@@ -20966,7 +21098,7 @@ async function readHolderFile(path2) {
20966
21098
  }
20967
21099
  async function exists2(path2) {
20968
21100
  try {
20969
- await lstat11(path2);
21101
+ await lstat12(path2);
20970
21102
  return true;
20971
21103
  } catch {
20972
21104
  return false;
@@ -21015,7 +21147,7 @@ async function ensureHolderDirectory(dir, ownerOnly = {}) {
21015
21147
  return;
21016
21148
  }
21017
21149
  try {
21018
- await lstat11(dir);
21150
+ await lstat12(dir);
21019
21151
  return;
21020
21152
  } catch (error) {
21021
21153
  if (error?.code !== "ENOENT")
@@ -24734,8 +24866,8 @@ import { isAbsolute as isAbsolute5, normalize, parse as parse3, sep } from "node
24734
24866
 
24735
24867
  // src/migration-source.ts
24736
24868
  import { createHash as createHash20 } from "node:crypto";
24737
- import { constants as constants4 } from "node:fs";
24738
- import { lstat as lstat12, open as open10 } from "node:fs/promises";
24869
+ import { constants as constants3 } from "node:fs";
24870
+ import { lstat as lstat13, open as open10 } from "node:fs/promises";
24739
24871
  class NativeMigrationSourceError extends Error {
24740
24872
  role;
24741
24873
  code;
@@ -24778,7 +24910,7 @@ async function readNativeMigrationSource(role, path2, hooks = {}) {
24778
24910
  });
24779
24911
  let initial;
24780
24912
  try {
24781
- initial = await lstat12(path2, { bigint: true });
24913
+ initial = await lstat13(path2, { bigint: true });
24782
24914
  } catch (error) {
24783
24915
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
24784
24916
  return missing2;
@@ -24788,7 +24920,7 @@ async function readNativeMigrationSource(role, path2, hooks = {}) {
24788
24920
  throw new NativeMigrationSourceError(role, "unsafe-source");
24789
24921
  let handle;
24790
24922
  try {
24791
- handle = await open10(path2, constants4.O_RDONLY | (constants4.O_NOFOLLOW ?? 0) | (constants4.O_NONBLOCK ?? 0));
24923
+ handle = await open10(path2, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0) | (constants3.O_NONBLOCK ?? 0));
24792
24924
  const before = await handle.stat({ bigint: true });
24793
24925
  if (!before.isFile() || before.dev !== initial.dev || before.ino !== initial.ino) {
24794
24926
  throw new NativeMigrationSourceError(role, "source-changed");
@@ -24816,7 +24948,7 @@ async function readNativeMigrationSource(role, path2, hooks = {}) {
24816
24948
  chunks.push(chunk.subarray(0, read.bytesRead));
24817
24949
  }
24818
24950
  const after = await handle.stat({ bigint: true });
24819
- const current = await lstat12(path2, { bigint: true });
24951
+ const current = await lstat13(path2, { bigint: true });
24820
24952
  if (before.uid !== after.uid || before.gid !== after.gid || before.mode !== after.mode || current.uid !== before.uid || current.gid !== before.gid || current.mode !== before.mode || before.dev !== after.dev || before.ino !== after.ino || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || before.size !== after.size || current.dev !== before.dev || current.ino !== before.ino || current.isSymbolicLink() || current.mtimeNs !== before.mtimeNs || current.ctimeNs !== before.ctimeNs || current.size !== before.size || BigInt(bytes) !== before.size) {
24821
24953
  throw new NativeMigrationSourceError(role, "source-changed");
24822
24954
  }
@@ -30828,7 +30960,7 @@ function relayGeneration(input) {
30828
30960
  }
30829
30961
  // src/native-host-auth-write.ts
30830
30962
  import { randomUUID as randomUUID18 } from "node:crypto";
30831
- import { lstat as lstat13, open as open11, rename as rename13, unlink as unlink7 } from "node:fs/promises";
30963
+ import { lstat as lstat14, open as open11, rename as rename13, unlink as unlink7 } from "node:fs/promises";
30832
30964
  import { dirname as dirname18, isAbsolute as isAbsolute6, join as join21 } from "node:path";
30833
30965
  class NativeHostAuthWriteError extends Error {
30834
30966
  code;
@@ -30940,7 +31072,7 @@ async function writeNativeHostAuth(input, hooks = {}) {
30940
31072
  } finally {
30941
31073
  if (stage && ownedStage) {
30942
31074
  try {
30943
- const current = await lstat13(stage);
31075
+ const current = await lstat14(stage);
30944
31076
  if (current.dev === ownedStage.dev && current.ino === ownedStage.ino)
30945
31077
  await unlink7(stage);
30946
31078
  } catch {}
@@ -30950,7 +31082,7 @@ async function writeNativeHostAuth(input, hooks = {}) {
30950
31082
 
30951
31083
  // src/native-migration.ts
30952
31084
  import { createHash as createHash22 } from "node:crypto";
30953
- import { lstat as lstat14, mkdir as mkdir12, unlink as unlink8 } from "node:fs/promises";
31085
+ import { lstat as lstat15, mkdir as mkdir12, unlink as unlink8 } from "node:fs/promises";
30954
31086
  import { dirname as dirname19, isAbsolute as isAbsolute7 } from "node:path";
30955
31087
  class NativeMigrationError extends Error {
30956
31088
  code;
@@ -31000,7 +31132,7 @@ function owned(snapshot) {
31000
31132
  }
31001
31133
  async function absent(path2) {
31002
31134
  try {
31003
- await lstat14(path2);
31135
+ await lstat15(path2);
31004
31136
  return false;
31005
31137
  } catch (error) {
31006
31138
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
@@ -34174,8 +34306,8 @@ function placeholderSecret(integrationID) {
34174
34306
  var MAX_RETRY_HOLD_MS = 20 * 60 * 1000;
34175
34307
  // ../../node_modules/@cortexkit/common-auth/dist/pi-slot/slot.js
34176
34308
  import { createHash as createHash23 } from "node:crypto";
34177
- import { constants as constants5 } from "node:fs";
34178
- import { lstat as lstat15, mkdir as mkdir14, open as open12, unlink as unlink9 } from "node:fs/promises";
34309
+ import { constants as constants4 } from "node:fs";
34310
+ import { lstat as lstat16, mkdir as mkdir14, open as open12, unlink as unlink9 } from "node:fs/promises";
34179
34311
  import { dirname as dirname20, resolve as resolve13 } from "node:path";
34180
34312
  import { setTimeout as sleep } from "node:timers/promises";
34181
34313
 
@@ -34236,6 +34368,13 @@ async function acquire(path2, stale) {
34236
34368
  await remove(path2);
34237
34369
  return acquire(path2, 0);
34238
34370
  }
34371
+ let created;
34372
+ try {
34373
+ const st = await stat10(path2);
34374
+ created = { dev: st.dev, ino: st.ino };
34375
+ } catch {
34376
+ created = undefined;
34377
+ }
34239
34378
  try {
34240
34379
  if (!precision) {
34241
34380
  const probe2 = new Date(Math.ceil(Date.now() / 1000) * 1000 + 5);
@@ -34246,7 +34385,15 @@ async function acquire(path2, stale) {
34246
34385
  precision ??= mtime % 1000 === 0 ? "s" : "ms";
34247
34386
  return { dev: observed.dev, ino: observed.ino, mtime };
34248
34387
  } catch (error) {
34249
- await remove(path2).catch(() => {});
34388
+ try {
34389
+ if (created) {
34390
+ const current = await stat10(path2).catch(() => {
34391
+ return;
34392
+ });
34393
+ if (current && current.dev === created.dev && current.ino === created.ino)
34394
+ await remove(path2).catch(() => {});
34395
+ }
34396
+ } catch {}
34250
34397
  throw error;
34251
34398
  }
34252
34399
  }
@@ -34459,13 +34606,13 @@ async function safeRead(path2, privateFile = false, afterReadCheck) {
34459
34606
  }
34460
34607
  let file;
34461
34608
  try {
34462
- file = await open12(path2, constants5.O_RDONLY | constants5.O_NOFOLLOW);
34609
+ file = await open12(path2, constants4.O_RDONLY | constants4.O_NOFOLLOW);
34463
34610
  const stat11 = await file.stat();
34464
34611
  if (!stat11.isFile() || privateFile && (stat11.mode & 511) !== 384) {
34465
34612
  throw new PiSlotError("unsafe-path");
34466
34613
  }
34467
34614
  await afterReadCheck?.(path2);
34468
- const current = await lstat15(path2);
34615
+ const current = await lstat16(path2);
34469
34616
  if (!current.isFile() || current.dev !== stat11.dev || current.ino !== stat11.ino) {
34470
34617
  throw new PiSlotError("unsafe-path");
34471
34618
  }
@@ -34663,7 +34810,7 @@ function createPiSlot(options, onStep, hooks) {
34663
34810
  await ensurePrivateDirectory(dirname20(stashPath));
34664
34811
  else {
34665
34812
  await mkdir14(dirname20(stashPath), { recursive: true, mode: 448 });
34666
- const dir = await lstat15(dirname20(stashPath));
34813
+ const dir = await lstat16(dirname20(stashPath));
34667
34814
  if (!dir.isDirectory() || (dir.mode & 511) !== 448)
34668
34815
  throw new PiSlotError("unsafe-path");
34669
34816
  }
@@ -35447,7 +35594,7 @@ class PrimeManager {
35447
35594
  import { randomUUID as randomUUID19 } from "node:crypto";
35448
35595
  import {
35449
35596
  chmod as chmod3,
35450
- lstat as lstat16,
35597
+ lstat as lstat17,
35451
35598
  mkdir as mkdir16,
35452
35599
  readdir as readdir5,
35453
35600
  readFile as readFile13,
@@ -35689,7 +35836,7 @@ class QuotaHeaderFeedRegistry {
35689
35836
  if (file.mtimeMs > now || now - file.mtimeMs < leaseMs)
35690
35837
  return;
35691
35838
  await this.options.beforeRemoveFile?.(path2);
35692
- const current = await lstat16(path2);
35839
+ const current = await lstat17(path2);
35693
35840
  if (current.ino !== file.ino || current.mtimeMs !== file.mtimeMs)
35694
35841
  return;
35695
35842
  await (this.options.removeFile ?? ((target2) => rm9(target2)))(path2);
@@ -36546,7 +36693,7 @@ function describeFenceBlocker(blocker) {
36546
36693
 
36547
36694
  // src/state-layout-move.ts
36548
36695
  import { randomUUID as randomUUID20 } from "node:crypto";
36549
- import { lstat as lstat17, readdir as readdir7, readFile as readFile16, stat as stat13, unlink as unlink10 } from "node:fs/promises";
36696
+ import { lstat as lstat18, readdir as readdir7, readFile as readFile16, stat as stat13, unlink as unlink10 } from "node:fs/promises";
36550
36697
  import { basename as basename12, dirname as dirname23, join as join27, sep as sep2 } from "node:path";
36551
36698
  var ACCOUNT_FILE = "anthropic-auth.json";
36552
36699
  var ACCOUNT_STATE_FILE = "anthropic-auth-state.json";
@@ -36779,6 +36926,15 @@ async function acquireDataLocks(dataset, ctx, options = {}) {
36779
36926
  local: poolRefreshLocks(localPool(dataset), await poolRows(localPool(dataset))),
36780
36927
  vault: poolRefreshLocks(vaultPool(dataset), await poolRows(vaultPool(dataset)))
36781
36928
  } : undefined;
36929
+ if (ctx.fs.platform === "win32" && refresh) {
36930
+ for (const [pool, locks] of [
36931
+ ["local", refresh.local],
36932
+ ["vault", refresh.vault]
36933
+ ]) {
36934
+ if (locks.rows.some((name) => name.includes("*")))
36935
+ throw new Error(`the ${pool} pool has a row id or identity whose refresh lock name contains '*', which Windows filenames do not allow`);
36936
+ }
36937
+ }
36782
36938
  for (const spec of stateLayoutDataLockOrder(dataset, {
36783
36939
  timeoutMs,
36784
36940
  refresh
@@ -36828,7 +36984,7 @@ function restoringPath(path2, attempt) {
36828
36984
  }
36829
36985
  async function exists3(path2) {
36830
36986
  try {
36831
- await lstat17(path2);
36987
+ await lstat18(path2);
36832
36988
  return true;
36833
36989
  } catch (error) {
36834
36990
  if (errorCode6(error) === "ENOENT")
@@ -36838,7 +36994,7 @@ async function exists3(path2) {
36838
36994
  }
36839
36995
  async function removeOwnStaging(path2) {
36840
36996
  try {
36841
- const info = await lstat17(path2);
36997
+ const info = await lstat18(path2);
36842
36998
  if (info.isFile())
36843
36999
  await unlink10(path2);
36844
37000
  } catch (error) {
@@ -36881,7 +37037,7 @@ async function movePreconditionFailure(dataset, platform) {
36881
37037
  for (const path2 of new Set([slot.configured, slot.from])) {
36882
37038
  let info;
36883
37039
  try {
36884
- info = await lstat17(path2);
37040
+ info = await lstat18(path2);
36885
37041
  } catch (error) {
36886
37042
  if (errorCode6(error) === "ENOENT")
36887
37043
  continue;
@@ -36898,7 +37054,7 @@ async function movePreconditionFailure(dataset, platform) {
36898
37054
  anyOld ||= fromExists;
36899
37055
  if (await exists3(slot.to)) {
36900
37056
  if (fromExists) {
36901
- const [a, b] = await Promise.all([lstat17(slot.from), lstat17(slot.to)]);
37057
+ const [a, b] = await Promise.all([lstat18(slot.from), lstat18(slot.to)]);
36902
37058
  if (a.dev === b.dev && a.ino === b.ino)
36903
37059
  return `${slot.from} and ${slot.to} are the same file`;
36904
37060
  }
@@ -1,4 +1,17 @@
1
1
  export declare function isLostMarkerRaceError(error: unknown): boolean;
2
+ /** Test-only fault injection between marker mkdir and owner.json write. */
3
+ export interface EvictionMarkerTestHooks {
4
+ /** Runs after the exclusive mkdir (and identity capture) before owner write. */
5
+ beforeOwnerWrite?: (evictPath: string, ownerPath: string) => Promise<void> | void;
6
+ /** Throw to fail the owner write; return an error to throw it. */
7
+ failOwnerWrite?: (evictPath: string, ownerPath: string) => Promise<unknown> | unknown;
8
+ }
9
+ /**
10
+ * Tests only: inject a failure or a directory replacement between the
11
+ * eviction-marker mkdir and its owner.json write. Production never sets this.
12
+ * Not exported from the fs entry point, so it is not part of the public API.
13
+ */
14
+ export declare function setEvictionMarkerTestHooksForTest(hooks?: EvictionMarkerTestHooks): void;
2
15
  export interface LockLoss {
3
16
  readonly reason: 'taken-over' | 'expired' | 'unreadable' | 'renewal-failed' | 'marker-lost';
4
17
  readonly expectedOwnerId: string;
@@ -11,7 +11,7 @@ export declare function readWindowsOwnerOnly(path: string, options?: OwnerOnlyOp
11
11
  /** Ancestors protect directory entries; they need not themselves be private. */
12
12
  export declare function isSafeWindowsParent(path: string, options?: OwnerOnlyOptions): Promise<boolean | undefined>;
13
13
  /** Native creation establishes a protected DACL before any bytes are written. */
14
- export declare function createWindowsOwnerOnly(path: string, bytes: string, options?: OwnerOnlyOptions): Promise<boolean>;
14
+ export declare function createWindowsOwnerOnly(path: string, bytes: string | Uint8Array, options?: OwnerOnlyOptions): Promise<boolean>;
15
15
  /**
16
16
  * Create only missing Windows directories; never tighten an existing shared directory.
17
17
  * Returns false off Windows so callers retain their POSIX directory policy.
@@ -1,4 +1,6 @@
1
+ import { open } from 'node:fs/promises';
1
2
  import { type NativeLockEvent } from './native-lock-observer.ts';
3
+ import { type OwnerOnlyOptions } from './owner-only.ts';
2
4
  /**
3
5
  * The filesystem calls a layout transition makes, so tests can fail one, drop
4
6
  * one, or record their order. Production uses `node:fs`.
@@ -51,7 +53,10 @@ export declare function fileSha256(path: string): Promise<string | null>;
51
53
  * protected owner-only DACL before any byte is written. The file never exists
52
54
  * with wider permissions, so no later `chmod` is needed.
53
55
  */
54
- export declare function writeOwnerOnlyExclusive(path: string, bytes: Uint8Array, platform: NodeJS.Platform): Promise<void>;
56
+ export declare function writeOwnerOnlyExclusive(path: string, bytes: Uint8Array, platform: NodeJS.Platform, options?: {
57
+ ownership?: OwnerOnlyOptions;
58
+ open?: typeof open;
59
+ }): Promise<void>;
55
60
  /** A target changed between the check before a destructive step and the step. */
56
61
  export declare class StateLayoutOwnershipError extends Error {
57
62
  readonly path: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "2.4.2",
3
+ "version": "2.4.3",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,11 +30,11 @@
30
30
  "prepublishOnly": "bun run build"
31
31
  },
32
32
  "devDependencies": {
33
- "@cortexkit/common-auth": "0.28.0"
33
+ "@cortexkit/common-auth": "0.29.0"
34
34
  },
35
35
  "dependencies": {
36
36
  "@cortexkit/claustrum-client": "0.13.0",
37
- "@cortexkit/private-files": "0.2.1",
37
+ "@cortexkit/private-files": "0.2.2",
38
38
  "@cortexkit/subc-client": "^0.22.10",
39
39
  "jsonc-parser": "^3.3.1",
40
40
  "xxhash-wasm": "^1.1.0"