opencode-multi-account-core 0.3.3 → 0.3.5

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/dist/index.js CHANGED
@@ -1177,7 +1177,7 @@ function readRoutingUsageTiers(usage) {
1177
1177
  // src/account-store.ts
1178
1178
  import { promises as fs5 } from "fs";
1179
1179
  import { createHash, randomBytes as randomBytes3 } from "crypto";
1180
- import { dirname as dirname5, join as join5 } from "path";
1180
+ import { dirname as dirname5, join as join6 } from "path";
1181
1181
  import * as v4 from "valibot";
1182
1182
 
1183
1183
  // src/storage.ts
@@ -1274,24 +1274,41 @@ async function loadAccounts(storagePath) {
1274
1274
 
1275
1275
  // src/file-lock.ts
1276
1276
  import { promises as fs4 } from "fs";
1277
- import { dirname as dirname4 } from "path";
1277
+ import { randomUUID as randomUUID2 } from "crypto";
1278
+ import { dirname as dirname4, join as join5 } from "path";
1278
1279
  var DEFAULT_STALE_MS = 1e4;
1279
1280
  var DEFAULT_RETRY_DELAY_MS = 50;
1280
1281
  var DEFAULT_MAX_RETRIES = 10;
1282
+ var OWNER_PREFIX = "owner-";
1281
1283
  function sleep3(ms) {
1282
1284
  return new Promise((resolve) => setTimeout(resolve, ms));
1283
1285
  }
1286
+ function ownerPath(lockPath, ownerToken) {
1287
+ return join5(lockPath, `${OWNER_PREFIX}${ownerToken}`);
1288
+ }
1284
1289
  async function removeIfStale(lockPath, staleMs) {
1285
1290
  try {
1286
- const stat = await fs4.stat(lockPath);
1291
+ const entries = await fs4.readdir(lockPath);
1292
+ const leasePath = entries.length === 1 && entries[0]?.startsWith(OWNER_PREFIX) ? join5(lockPath, entries[0]) : lockPath;
1293
+ const stat = await fs4.stat(leasePath);
1287
1294
  if (Date.now() - stat.mtimeMs > staleMs) {
1288
- await fs4.rm(lockPath, { recursive: true, force: true });
1295
+ if (leasePath !== lockPath) await fs4.unlink(leasePath);
1296
+ await fs4.rmdir(lockPath);
1289
1297
  }
1290
1298
  } catch {
1291
1299
  }
1292
1300
  }
1301
+ async function releaseIfOwner(lockPath, ownerToken) {
1302
+ try {
1303
+ await fs4.unlink(ownerPath(lockPath, ownerToken));
1304
+ await fs4.rmdir(lockPath);
1305
+ } catch {
1306
+ }
1307
+ }
1293
1308
  async function withDirectoryLock(targetPath, fn, options) {
1294
1309
  const lockPath = `${targetPath}.lock`;
1310
+ const ownerToken = randomUUID2();
1311
+ const leasePath = ownerPath(lockPath, ownerToken);
1295
1312
  const staleMs = options?.staleMs ?? DEFAULT_STALE_MS;
1296
1313
  const retryDelayMs = options?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
1297
1314
  const retries = options?.retries ?? DEFAULT_MAX_RETRIES;
@@ -1299,6 +1316,18 @@ async function withDirectoryLock(targetPath, fn, options) {
1299
1316
  for (let attempt = 0; ; attempt += 1) {
1300
1317
  try {
1301
1318
  await fs4.mkdir(lockPath, { mode: 448 });
1319
+ try {
1320
+ await fs4.writeFile(leasePath, ownerToken, { flag: "wx", mode: 384 });
1321
+ } catch (error) {
1322
+ await fs4.rmdir(lockPath).catch(() => {
1323
+ });
1324
+ if (error?.code === "ENOENT") {
1325
+ if (attempt >= retries) throw new Error(`Failed to acquire lock for ${targetPath}`);
1326
+ await sleep3(retryDelayMs * (attempt + 1));
1327
+ continue;
1328
+ }
1329
+ throw error;
1330
+ }
1302
1331
  break;
1303
1332
  } catch (error) {
1304
1333
  const code = error?.code;
@@ -1312,11 +1341,17 @@ async function withDirectoryLock(targetPath, fn, options) {
1312
1341
  await sleep3(retryDelayMs * (attempt + 1));
1313
1342
  }
1314
1343
  }
1344
+ const renewal = setInterval(() => {
1345
+ const now = /* @__PURE__ */ new Date();
1346
+ void fs4.utimes(leasePath, now, now).catch(() => {
1347
+ });
1348
+ }, Math.max(1, Math.floor(staleMs / 3)));
1349
+ renewal.unref();
1315
1350
  try {
1316
1351
  return await fn();
1317
1352
  } finally {
1318
- await fs4.rm(lockPath, { recursive: true, force: true }).catch(() => {
1319
- });
1353
+ clearInterval(renewal);
1354
+ await releaseIfOwner(lockPath, ownerToken);
1320
1355
  }
1321
1356
  }
1322
1357
 
@@ -1326,7 +1361,7 @@ var REFRESH_LOCK_STALE_MS = 45e3;
1326
1361
  var REFRESH_LOCK_RETRY_DELAY_MS = 100;
1327
1362
  var REFRESH_LOCK_RETRIES = 31;
1328
1363
  function resolveStoragePath(filename) {
1329
- return join5(getConfigDir2(), filename);
1364
+ return join6(getConfigDir2(), filename);
1330
1365
  }
1331
1366
  function createEmptyStorage() {
1332
1367
  return { version: 1, accounts: [] };
@@ -2077,7 +2112,7 @@ function createRateLimitHandlers(dependencies) {
2077
2112
 
2078
2113
  // src/auth-migration.ts
2079
2114
  import { promises as fs6 } from "fs";
2080
- import { join as join6 } from "path";
2115
+ import { join as join7 } from "path";
2081
2116
  var AUTH_JSON_FILENAME = "auth.json";
2082
2117
  function isValidOAuthCredential(value) {
2083
2118
  if (typeof value !== "object" || value === null) return false;
@@ -2085,7 +2120,7 @@ function isValidOAuthCredential(value) {
2085
2120
  return candidate.type === "oauth" && typeof candidate.refresh === "string" && candidate.refresh.length > 0;
2086
2121
  }
2087
2122
  function resolveAuthJsonPath() {
2088
- return join6(getConfigDir2(), AUTH_JSON_FILENAME);
2123
+ return join7(getConfigDir2(), AUTH_JSON_FILENAME);
2089
2124
  }
2090
2125
  async function readAuthJson() {
2091
2126
  const authPath = resolveAuthJsonPath();
@@ -2692,7 +2727,7 @@ var PoolChainConfigSchema = v5.object({
2692
2727
  // src/pool-config-store.ts
2693
2728
  import { promises as fs7 } from "fs";
2694
2729
  import { randomBytes as randomBytes4 } from "crypto";
2695
- import { dirname as dirname6, join as join7 } from "path";
2730
+ import { dirname as dirname6, join as join8 } from "path";
2696
2731
  import * as v6 from "valibot";
2697
2732
  var POOL_CONFIG_FILENAME = "multiauth-pools.json";
2698
2733
  var FILE_MODE2 = 384;
@@ -2700,13 +2735,13 @@ function createEmptyConfig() {
2700
2735
  return { pools: [], chains: [] };
2701
2736
  }
2702
2737
  function getGlobalConfigPath() {
2703
- return join7(getConfigDir2(), POOL_CONFIG_FILENAME);
2738
+ return join8(getConfigDir2(), POOL_CONFIG_FILENAME);
2704
2739
  }
2705
2740
  function buildTempPath2(targetPath) {
2706
2741
  return `${targetPath}.${randomBytes4(8).toString("hex")}.tmp`;
2707
2742
  }
2708
2743
  async function resolveConfigPath() {
2709
- const projectPath = join7(process.cwd(), ".opencode", POOL_CONFIG_FILENAME);
2744
+ const projectPath = join8(process.cwd(), ".opencode", POOL_CONFIG_FILENAME);
2710
2745
  try {
2711
2746
  await fs7.access(projectPath);
2712
2747
  return projectPath;
@@ -3237,7 +3272,7 @@ function createOpenCodeNativeAuthLoader(options) {
3237
3272
 
3238
3273
  // src/native-plugin-bootstrap-auth.ts
3239
3274
  import { promises as fs8 } from "fs";
3240
- import { join as join8 } from "path";
3275
+ import { join as join9 } from "path";
3241
3276
  var AUTH_JSON_FILENAME2 = "auth.json";
3242
3277
  function hasCompleteOAuthCredential(account) {
3243
3278
  return typeof account.refreshToken === "string" && account.refreshToken.length > 0 && typeof account.accessToken === "string" && account.accessToken.length > 0 && typeof account.expiresAt === "number" && Number.isFinite(account.expiresAt);
@@ -3257,7 +3292,7 @@ function selectBootstrapAccount(accounts, activeAccountUuid) {
3257
3292
  return firstUsableAccount ?? completeAccounts[0];
3258
3293
  }
3259
3294
  async function readCurrentAuth(providerId) {
3260
- const authPath = join8(getConfigDir2(), AUTH_JSON_FILENAME2);
3295
+ const authPath = join9(getConfigDir2(), AUTH_JSON_FILENAME2);
3261
3296
  let raw;
3262
3297
  try {
3263
3298
  raw = await fs8.readFile(authPath, "utf-8");