@cortexkit/anthropic-auth-core 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/accounts.js +36 -14
  2. package/package.json +1 -1
package/dist/accounts.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
- import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
2
+ import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { ClaudeOAuthRefreshError, refreshClaudeOAuthToken } from "./auth.js";
@@ -21,6 +21,7 @@ const DEFAULT_MINIMUM_REMAINING = {
21
21
  };
22
22
  const DEFAULT_FAIL_CLOSED_ON_UNKNOWN_QUOTA = true;
23
23
  const BACKGROUND_TICK_MS = 60_000;
24
+ const BACKGROUND_TICK_JITTER_MS = 60_000;
24
25
  function getConfigDir() {
25
26
  if (process.env.OPENCODE_CONFIG_DIR?.trim()) {
26
27
  return process.env.OPENCODE_CONFIG_DIR.trim();
@@ -162,46 +163,64 @@ export async function saveAccounts(storage, path = getAccountStoragePath()) {
162
163
  }
163
164
  export async function acquireRefreshFileLock(options) {
164
165
  const accountPath = options.path ?? getAccountStoragePath();
165
- const lockDir = `${accountPath}.${options.name}.lock`;
166
- const ownerPath = join(lockDir, 'owner.json');
166
+ const lockPath = `${accountPath}.${options.name}.lock`;
167
+ const legacyOwnerPath = join(lockPath, 'owner.json');
167
168
  const ownerId = randomUUID();
168
169
  const now = options.now ?? Date.now;
170
+ async function readOwner() {
171
+ try {
172
+ return JSON.parse(await readFile(lockPath, 'utf8'));
173
+ }
174
+ catch (error) {
175
+ const code = error.code;
176
+ if (code !== 'EISDIR')
177
+ throw error;
178
+ return JSON.parse(await readFile(legacyOwnerPath, 'utf8'));
179
+ }
180
+ }
169
181
  async function tryAcquire() {
170
182
  try {
171
- await mkdir(lockDir, { mode: 0o700 });
172
- await writeFile(ownerPath, `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`, { encoding: 'utf8', mode: 0o600 });
183
+ await writeFile(lockPath, `${JSON.stringify({ ownerId, expiresAt: now() + options.ttlMs })}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
173
184
  return true;
174
185
  }
175
186
  catch (error) {
176
- if (error.code !== 'EEXIST')
177
- throw error;
178
- return false;
187
+ const code = error.code;
188
+ if (code === 'EEXIST' || code === 'EISDIR')
189
+ return false;
190
+ throw error;
179
191
  }
180
192
  }
181
193
  if (!(await tryAcquire())) {
182
194
  try {
183
- const owner = JSON.parse(await readFile(ownerPath, 'utf8'));
195
+ const owner = await readOwner();
184
196
  if (Number(owner?.expiresAt) > now())
185
197
  return null;
186
198
  }
187
199
  catch {
188
- // Broken owner metadata is treated as stale.
200
+ try {
201
+ const current = await stat(lockPath);
202
+ if (current.mtimeMs + options.ttlMs > Date.now())
203
+ return null;
204
+ }
205
+ catch {
206
+ return null;
207
+ }
189
208
  }
190
- await rm(lockDir, { recursive: true, force: true }).catch(() => { });
209
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
191
210
  if (!(await tryAcquire()))
192
211
  return null;
193
212
  }
194
213
  return {
195
214
  release: async () => {
196
215
  try {
197
- const owner = JSON.parse(await readFile(ownerPath, 'utf8'));
216
+ const owner = await readOwner();
198
217
  if (owner?.ownerId !== ownerId)
199
218
  return;
200
219
  }
201
220
  catch {
202
221
  return;
203
222
  }
204
- await rm(lockDir, { recursive: true, force: true }).catch(() => { });
223
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
205
224
  },
206
225
  };
207
226
  }
@@ -327,6 +346,9 @@ function quotaEnabled(storage) {
327
346
  function refreshEnabled(storage) {
328
347
  return storage?.refresh?.enabled !== false;
329
348
  }
349
+ function jitterMs(maxMs) {
350
+ return Math.floor(Math.random() * Math.max(0, maxMs));
351
+ }
330
352
  function refreshBeforeExpiryMs(storage) {
331
353
  const minutes = storage?.refresh?.refreshBeforeExpiryMinutes ??
332
354
  DEFAULT_REFRESH_BEFORE_EXPIRY_MINUTES;
@@ -531,7 +553,7 @@ export class FallbackAccountManager {
531
553
  if (!this.refreshTimer) {
532
554
  this.refreshTimer = setInterval(() => {
533
555
  void run().catch(() => { });
534
- }, BACKGROUND_TICK_MS);
556
+ }, BACKGROUND_TICK_MS + jitterMs(BACKGROUND_TICK_JITTER_MS));
535
557
  if ('unref' in this.refreshTimer)
536
558
  this.refreshTimer.unref();
537
559
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/anthropic-auth-core",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",