agent-shim 7.7.0 → 7.8.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/dist/index.mjs CHANGED
@@ -243,6 +243,11 @@ var SHIPPED_CATEGORY_DEFAULTS = Object.freeze({
243
243
  settings: true
244
244
  });
245
245
 
246
+ // src/config/store.ts
247
+ import fs from "node:fs";
248
+ import path2 from "node:path";
249
+ import { randomUUID } from "node:crypto";
250
+
246
251
  // src/config/load.ts
247
252
  import { cosmiconfigSync } from "cosmiconfig";
248
253
 
@@ -261,6 +266,98 @@ var UsageError = class extends CliError {
261
266
  }
262
267
  };
263
268
 
269
+ // src/config/load.ts
270
+ var ConfigValidationError = class extends CliError {
271
+ constructor(filepath, issues) {
272
+ const detail = issues.map((issue) => ` ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n");
273
+ super(`Invalid configuration in ${filepath}:
274
+ ${detail}`);
275
+ this.filepath = filepath;
276
+ this.issues = issues;
277
+ this.name = "ConfigValidationError";
278
+ }
279
+ filepath;
280
+ issues;
281
+ };
282
+
283
+ // src/config/store.ts
284
+ function isEnoent(error) {
285
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
286
+ }
287
+ var nodeStoreFs = {
288
+ readFileUtf8(filePath) {
289
+ try {
290
+ return fs.readFileSync(filePath, "utf8");
291
+ } catch (error) {
292
+ if (isEnoent(error)) {
293
+ return void 0;
294
+ }
295
+ throw error;
296
+ }
297
+ },
298
+ writeFileUtf8(filePath, contents) {
299
+ fs.writeFileSync(filePath, contents, "utf8");
300
+ },
301
+ renameSync(from, to) {
302
+ fs.renameSync(from, to);
303
+ },
304
+ mkdirSync(dirPath) {
305
+ fs.mkdirSync(dirPath, { recursive: true });
306
+ },
307
+ unlinkSync(filePath) {
308
+ try {
309
+ fs.unlinkSync(filePath);
310
+ } catch (error) {
311
+ if (!isEnoent(error)) {
312
+ throw error;
313
+ }
314
+ }
315
+ }
316
+ };
317
+ function readJson(filePath, schema, storeFs = nodeStoreFs) {
318
+ const raw = storeFs.readFileUtf8(filePath);
319
+ if (raw === void 0) {
320
+ return void 0;
321
+ }
322
+ const parsed = JSON.parse(raw);
323
+ const result = schema.safeParse(parsed);
324
+ if (!result.success) {
325
+ throw new ConfigValidationError(filePath, result.error.issues);
326
+ }
327
+ return result.data;
328
+ }
329
+ function writeTextAtomic(filePath, contents, storeFs = nodeStoreFs) {
330
+ const dir = path2.dirname(filePath);
331
+ storeFs.mkdirSync(dir);
332
+ const tempPath = path2.join(dir, `.${path2.basename(filePath)}.${String(process.pid)}.${randomUUID()}.tmp`);
333
+ storeFs.writeFileUtf8(tempPath, contents);
334
+ try {
335
+ storeFs.renameSync(tempPath, filePath);
336
+ } catch (error) {
337
+ storeFs.unlinkSync(tempPath);
338
+ throw error;
339
+ }
340
+ }
341
+ function writeJsonAtomic(filePath, data, storeFs = nodeStoreFs) {
342
+ writeTextAtomic(filePath, `${JSON.stringify(data, null, 2)}
343
+ `, storeFs);
344
+ }
345
+ function applyPatch(filePath, schema, patch, options = {}) {
346
+ const storeFs = options.storeFs ?? nodeStoreFs;
347
+ const existing = readJson(filePath, schema, storeFs) ?? options.defaults;
348
+ if (existing === void 0) {
349
+ throw new Error(`Cannot patch ${filePath}: it does not exist yet and no defaults were provided.`);
350
+ }
351
+ const merged = { ...existing, ...patch };
352
+ const result = schema.safeParse(merged);
353
+ if (!result.success) {
354
+ throw new ConfigValidationError(filePath, result.error.issues);
355
+ }
356
+ const validated = result.data;
357
+ writeJsonAtomic(filePath, validated, storeFs);
358
+ return validated;
359
+ }
360
+
264
361
  // src/config/classify.ts
265
362
  var GLOB_METACHARACTERS = /[*?[\]{}!()]/;
266
363
  function isExactPattern(pattern) {
@@ -852,7 +949,7 @@ function dedupeDiagnostics(diagnostics) {
852
949
  }
853
950
 
854
951
  // src/resolve/plan.ts
855
- import path2 from "node:path";
952
+ import path3 from "node:path";
856
953
  function parentOf(rel) {
857
954
  const index = rel.lastIndexOf("/");
858
955
  return index === -1 ? "" : rel.slice(0, index);
@@ -902,7 +999,7 @@ function planFarm(params) {
902
999
  }
903
1000
  return value;
904
1001
  };
905
- const linkTarget = (rel) => path2.join(params.facts.claudeHome, rel);
1002
+ const linkTarget = (rel) => path3.join(params.facts.claudeHome, rel);
906
1003
  const visit = (rel) => {
907
1004
  const fact = params.facts.entries.get(rel);
908
1005
  const decision = params.decisions.get(rel);
@@ -1126,7 +1223,7 @@ function resolveDecisions(input) {
1126
1223
  }
1127
1224
 
1128
1225
  // src/launcher/farm.ts
1129
- import path4 from "node:path";
1226
+ import path5 from "node:path";
1130
1227
  import { z as z2 } from "zod";
1131
1228
 
1132
1229
  // src/resolve/reconcile.ts
@@ -1225,8 +1322,8 @@ var LEGACY_HOME_DIRNAME = ".claude-use";
1225
1322
  var LEGACY_FARM_MANIFEST_FILENAME = ".claude-use-farm.json";
1226
1323
 
1227
1324
  // src/launcher/lock.ts
1228
- import { randomUUID } from "node:crypto";
1229
- import path3 from "node:path";
1325
+ import { randomUUID as randomUUID2 } from "node:crypto";
1326
+ import path4 from "node:path";
1230
1327
  var DEFAULT_STALE_AFTER_MS = 12e4;
1231
1328
  var DEFAULT_RETRY_DELAY_MS = 50;
1232
1329
  var DEFAULT_MAX_ATTEMPTS = 200;
@@ -1245,7 +1342,7 @@ var IdentityLockBusyError = class extends CliError {
1245
1342
  holderPid;
1246
1343
  };
1247
1344
  function identityLockPath(dir, identity) {
1248
- return path3.join(dir, `.${identity}.lock`);
1345
+ return path4.join(dir, `.${identity}.lock`);
1249
1346
  }
1250
1347
  function parseLockRecord(raw) {
1251
1348
  if (raw === void 0) {
@@ -1274,7 +1371,7 @@ function acquireIdentityLock(params) {
1274
1371
  const staleAfterMs = params.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
1275
1372
  const retryDelayMs = params.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
1276
1373
  const maxAttempts = params.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
1277
- const token = randomUUID();
1374
+ const token = randomUUID2();
1278
1375
  if (params.fs.lstat(params.dir) === void 0) {
1279
1376
  params.fs.mkdirp(params.dir);
1280
1377
  }
@@ -1323,8 +1420,8 @@ var FarmManifestSchema = z2.strictObject({
1323
1420
  materialised: z2.array(z2.string()),
1324
1421
  links: z2.array(z2.strictObject({ rel: z2.string(), target: z2.string() }))
1325
1422
  });
1326
- function readFarmManifest(fs3, farmRoot) {
1327
- const raw = fs3.readFileUtf8(path4.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs3.readFileUtf8(path4.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
1423
+ function readFarmManifest(fs7, farmRoot) {
1424
+ const raw = fs7.readFileUtf8(path5.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs7.readFileUtf8(path5.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
1328
1425
  if (raw === void 0) {
1329
1426
  return void 0;
1330
1427
  }
@@ -1340,7 +1437,7 @@ function readFarmManifest(fs3, farmRoot) {
1340
1437
  function buildEntryFacts(params) {
1341
1438
  const entries = /* @__PURE__ */ new Map();
1342
1439
  const visit = (rel) => {
1343
- const absolute = path4.join(params.claudeHome, rel);
1440
+ const absolute = path5.join(params.claudeHome, rel);
1344
1441
  const stat = params.fs.lstat(absolute);
1345
1442
  if (stat === void 0) {
1346
1443
  return void 0;
@@ -1382,9 +1479,9 @@ function buildEntryFacts(params) {
1382
1479
  entries
1383
1480
  };
1384
1481
  }
1385
- function listSubtree(fs3, root, rel) {
1386
- const absolute = path4.join(root, rel);
1387
- const stat = fs3.lstat(absolute);
1482
+ function listSubtree(fs7, root, rel) {
1483
+ const absolute = path5.join(root, rel);
1484
+ const stat = fs7.lstat(absolute);
1388
1485
  if (stat === void 0) {
1389
1486
  return [];
1390
1487
  }
@@ -1392,10 +1489,10 @@ function listSubtree(fs3, root, rel) {
1392
1489
  return [{ rel, kind: "symlink" }];
1393
1490
  }
1394
1491
  if (stat.kind === "dir") {
1395
- const children = [...fs3.readdir(absolute)].sort().flatMap((name) => listSubtree(fs3, root, `${rel}/${name}`));
1492
+ const children = [...fs7.readdir(absolute)].sort().flatMap((name) => listSubtree(fs7, root, `${rel}/${name}`));
1396
1493
  return [{ rel, kind: "dir" }, ...children];
1397
1494
  }
1398
- const contentHash = fs3.hashFile(absolute);
1495
+ const contentHash = fs7.hashFile(absolute);
1399
1496
  return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
1400
1497
  }
1401
1498
  function topLevelScopeRoots(roots) {
@@ -1409,25 +1506,25 @@ function topLevelScopeRoots(roots) {
1409
1506
  }
1410
1507
  return kept;
1411
1508
  }
1412
- function reconciliationScope(fs3, farmRoot, manifest) {
1509
+ function reconciliationScope(fs7, farmRoot, manifest) {
1413
1510
  if (manifest !== void 0) {
1414
1511
  return topLevelScopeRoots(manifest.materialised);
1415
1512
  }
1416
- return [...fs3.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs3.lstat(path4.join(farmRoot, name))?.kind === "dir").sort();
1513
+ return [...fs7.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs7.lstat(path5.join(farmRoot, name))?.kind === "dir").sort();
1417
1514
  }
1418
- function collectFarmListing(fs3, farmRoot, scopeRoots) {
1419
- return scopeRoots.flatMap((root) => listSubtree(fs3, farmRoot, root));
1515
+ function collectFarmListing(fs7, farmRoot, scopeRoots) {
1516
+ return scopeRoots.flatMap((root) => listSubtree(fs7, farmRoot, root));
1420
1517
  }
1421
- function collectCanonicalCounterparts(fs3, claudeHome, farmListing) {
1518
+ function collectCanonicalCounterparts(fs7, claudeHome, farmListing) {
1422
1519
  const out = [];
1423
1520
  for (const entry of farmListing) {
1424
- const absolute = path4.join(claudeHome, entry.rel);
1425
- const stat = fs3.lstat(absolute);
1521
+ const absolute = path5.join(claudeHome, entry.rel);
1522
+ const stat = fs7.lstat(absolute);
1426
1523
  if (stat === void 0) {
1427
1524
  continue;
1428
1525
  }
1429
1526
  if (stat.kind === "file") {
1430
- const contentHash = fs3.hashFile(absolute);
1527
+ const contentHash = fs7.hashFile(absolute);
1431
1528
  out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
1432
1529
  continue;
1433
1530
  }
@@ -1451,21 +1548,21 @@ function carryOver(params) {
1451
1548
  if (accounted.has(name)) {
1452
1549
  continue;
1453
1550
  }
1454
- const stat = params.fs.lstat(path4.join(params.previousRoot, name));
1551
+ const stat = params.fs.lstat(path5.join(params.previousRoot, name));
1455
1552
  if (stat === void 0 || stat.kind === "symlink") {
1456
1553
  continue;
1457
1554
  }
1458
- if (params.fs.lstat(path4.join(params.farmRoot, name)) !== void 0) {
1555
+ if (params.fs.lstat(path5.join(params.farmRoot, name)) !== void 0) {
1459
1556
  const category = params.classification === void 0 ? void 0 : classifyEntries([name], params.classification).classification.get(name);
1460
1557
  if (category === "runtime") {
1461
- params.fs.removeRecursive(path4.join(params.previousRoot, name));
1558
+ params.fs.removeRecursive(path5.join(params.previousRoot, name));
1462
1559
  autoResolved.push(name);
1463
1560
  continue;
1464
1561
  }
1465
1562
  collided.push(name);
1466
1563
  continue;
1467
1564
  }
1468
- params.fs.rename(path4.join(params.previousRoot, name), path4.join(params.farmRoot, name));
1565
+ params.fs.rename(path5.join(params.previousRoot, name), path5.join(params.farmRoot, name));
1469
1566
  carried.push(name);
1470
1567
  }
1471
1568
  return { carried, autoResolved, collided };
@@ -1473,9 +1570,9 @@ function carryOver(params) {
1473
1570
  function buildScratchTree(params) {
1474
1571
  params.fs.mkdirp(params.scratchRoot);
1475
1572
  for (const entry of params.plan.entries) {
1476
- const absolute = path4.join(params.scratchRoot, entry.rel);
1573
+ const absolute = path5.join(params.scratchRoot, entry.rel);
1477
1574
  if (entry.kind === "link") {
1478
- params.fs.mkdirp(path4.dirname(absolute));
1575
+ params.fs.mkdirp(path5.dirname(absolute));
1479
1576
  params.fs.symlink(entry.target, absolute);
1480
1577
  continue;
1481
1578
  }
@@ -1484,14 +1581,14 @@ function buildScratchTree(params) {
1484
1581
  }
1485
1582
  }
1486
1583
  params.fs.writeFileUtf8(
1487
- path4.join(params.scratchRoot, FARM_MANIFEST_FILENAME),
1584
+ path5.join(params.scratchRoot, FARM_MANIFEST_FILENAME),
1488
1585
  `${JSON.stringify(params.manifest, null, 2)}
1489
1586
  `
1490
1587
  );
1491
1588
  }
1492
1589
  function swapIn(params) {
1493
1590
  if (params.fs.lstat(params.farmRoot) === void 0) {
1494
- params.fs.mkdirp(path4.dirname(params.farmRoot));
1591
+ params.fs.mkdirp(path5.dirname(params.farmRoot));
1495
1592
  params.fs.rename(params.scratchRoot, params.farmRoot);
1496
1593
  return { carried: [], autoResolved: [], collided: [] };
1497
1594
  }
@@ -1515,14 +1612,14 @@ function recoverInterruptedSwap(params) {
1515
1612
  const names = [...params.fs.readdir(params.identitiesDir)].sort();
1516
1613
  const removedScratch = [];
1517
1614
  for (const name of names.filter((candidate) => candidate.startsWith(scratchPrefix))) {
1518
- params.fs.removeRecursive(path4.join(params.identitiesDir, name));
1615
+ params.fs.removeRecursive(path5.join(params.identitiesDir, name));
1519
1616
  removedScratch.push(name);
1520
1617
  }
1521
1618
  let previous = names.filter((candidate) => candidate.startsWith(previousPrefix));
1522
1619
  let restoredFrom;
1523
1620
  const first = previous[0];
1524
1621
  if (first !== void 0 && params.fs.lstat(params.farmRoot) === void 0) {
1525
- params.fs.rename(path4.join(params.identitiesDir, first), params.farmRoot);
1622
+ params.fs.rename(path5.join(params.identitiesDir, first), params.farmRoot);
1526
1623
  restoredFrom = first;
1527
1624
  previous = previous.slice(1);
1528
1625
  }
@@ -1530,7 +1627,7 @@ function recoverInterruptedSwap(params) {
1530
1627
  const autoResolved = [];
1531
1628
  const retained = [];
1532
1629
  for (const name of previous) {
1533
- const previousRoot = path4.join(params.identitiesDir, name);
1630
+ const previousRoot = path5.join(params.identitiesDir, name);
1534
1631
  const result = carryOver({
1535
1632
  fs: params.fs,
1536
1633
  previousRoot,
@@ -1555,7 +1652,7 @@ function recoverInterruptedSwap(params) {
1555
1652
  };
1556
1653
  }
1557
1654
  function recoverFarm(params) {
1558
- const farmRoot = path4.join(params.identitiesDir, params.identity);
1655
+ const farmRoot = path5.join(params.identitiesDir, params.identity);
1559
1656
  const lock = acquireIdentityLock({
1560
1657
  identity: params.identity,
1561
1658
  dir: params.identitiesDir,
@@ -1614,16 +1711,16 @@ function executeReconciliation(params) {
1614
1711
  });
1615
1712
  continue;
1616
1713
  }
1617
- const from = path4.join(params.farmRoot, action.rel);
1618
- const canonical = path4.join(params.claudeHome, action.rel);
1714
+ const from = path5.join(params.farmRoot, action.rel);
1715
+ const canonical = path5.join(params.claudeHome, action.rel);
1619
1716
  if (action.kind === "adopt") {
1620
- params.fs.mkdirp(path4.dirname(canonical));
1717
+ params.fs.mkdirp(path5.dirname(canonical));
1621
1718
  params.fs.copyRecursive(from, canonical);
1622
1719
  adopted.push(action.rel);
1623
1720
  continue;
1624
1721
  }
1625
1722
  const preserved = `${canonical}.farm-conflict-${String(params.nowMs)}`;
1626
- params.fs.mkdirp(path4.dirname(canonical));
1723
+ params.fs.mkdirp(path5.dirname(canonical));
1627
1724
  params.fs.copyRecursive(from, preserved);
1628
1725
  conflicts.push(action.rel);
1629
1726
  }
@@ -1648,7 +1745,7 @@ function farmAlreadyMatches(existing, next) {
1648
1745
  return existing.identity === next.identity && existing.claudeHome === next.claudeHome && sameStrings(existing.materialised, next.materialised) && sameLinks(existing.links, next.links);
1649
1746
  }
1650
1747
  function resyncFarm(params) {
1651
- const farmRoot = path4.join(params.identitiesDir, params.identity);
1748
+ const farmRoot = path5.join(params.identitiesDir, params.identity);
1652
1749
  const lock = acquireIdentityLock({
1653
1750
  identity: params.identity,
1654
1751
  dir: params.identitiesDir,
@@ -1727,8 +1824,8 @@ function resyncFarm(params) {
1727
1824
  diagnostics
1728
1825
  };
1729
1826
  }
1730
- const scratchRoot = path4.join(params.identitiesDir, `.${params.identity}.scratch.${params.uniqueSuffix}`);
1731
- const previousRoot = path4.join(params.identitiesDir, `.${params.identity}.previous.${params.uniqueSuffix}`);
1827
+ const scratchRoot = path5.join(params.identitiesDir, `.${params.identity}.scratch.${params.uniqueSuffix}`);
1828
+ const previousRoot = path5.join(params.identitiesDir, `.${params.identity}.previous.${params.uniqueSuffix}`);
1732
1829
  buildScratchTree({ fs: params.fs, scratchRoot, plan: resolved.farm, manifest });
1733
1830
  const swap = swapIn({ fs: params.fs, farmRoot, scratchRoot, previousRoot, classification: params.classification });
1734
1831
  if (swap.retainedPrevious !== void 0) {
@@ -1786,7 +1883,7 @@ function recoveryDiagnostics(recovery, identity) {
1786
1883
 
1787
1884
  // src/frontdoor/connect.ts
1788
1885
  import { X509Certificate as X509Certificate2, createPrivateKey as createPrivateKey2 } from "node:crypto";
1789
- import * as fs from "node:fs";
1886
+ import * as fs2 from "node:fs";
1790
1887
 
1791
1888
  // src/frontdoor/x509.ts
1792
1889
  import { createHash, createPrivateKey, generateKeyPairSync, randomBytes, sign, X509Certificate } from "node:crypto";
@@ -2132,7 +2229,7 @@ function capabilityFromProxyAuthorization(value) {
2132
2229
  var AUTH_HEADER = "x-agent-shim-auth";
2133
2230
 
2134
2231
  // src/frontdoor/state.ts
2135
- import path5 from "node:path";
2232
+ import path6 from "node:path";
2136
2233
  import { z as z3 } from "zod";
2137
2234
  var FRONT_DOOR_PROTOCOL = 1;
2138
2235
  var FrontDoorSessionSchema = z3.strictObject({ pid: z3.number().int().positive(), startedAt: z3.number(), token: z3.string().min(1) });
@@ -2166,10 +2263,10 @@ var FrontDoorStateSchema = z3.strictObject({
2166
2263
  /** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
2167
2264
  lastError: z3.string().optional()
2168
2265
  });
2169
- function readFrontDoorState(fs3, stateFile) {
2266
+ function readFrontDoorState(fs7, stateFile) {
2170
2267
  let raw;
2171
2268
  try {
2172
- raw = fs3.readFileUtf8(stateFile);
2269
+ raw = fs7.readFileUtf8(stateFile);
2173
2270
  } catch {
2174
2271
  return void 0;
2175
2272
  }
@@ -2183,28 +2280,28 @@ function readFrontDoorState(fs3, stateFile) {
2183
2280
  return void 0;
2184
2281
  }
2185
2282
  }
2186
- function writeFrontDoorState(fs3, stateFile, state) {
2187
- fs3.mkdirp(path5.dirname(stateFile));
2188
- fs3.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
2283
+ function writeFrontDoorState(fs7, stateFile, state) {
2284
+ fs7.mkdirp(path6.dirname(stateFile));
2285
+ fs7.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
2189
2286
  `);
2190
2287
  }
2191
2288
  function frontDoorOrigin(state) {
2192
2289
  const port = state?.directPort ?? state?.lastDirectPort;
2193
2290
  return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
2194
2291
  }
2195
- function writeFrontDoorSession(fs3, sessionsDir, session) {
2196
- fs3.mkdirPrivate(sessionsDir);
2197
- fs3.writeFilePrivate(path5.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
2292
+ function writeFrontDoorSession(fs7, sessionsDir, session) {
2293
+ fs7.mkdirPrivate(sessionsDir);
2294
+ fs7.writeFilePrivate(path6.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
2198
2295
  `);
2199
2296
  }
2200
- function listFrontDoorSessions(fs3, sessionsDir) {
2297
+ function listFrontDoorSessions(fs7, sessionsDir) {
2201
2298
  const sessions = [];
2202
- for (const name of fs3.readdir(sessionsDir)) {
2299
+ for (const name of fs7.readdir(sessionsDir)) {
2203
2300
  if (!name.endsWith(".json")) {
2204
2301
  continue;
2205
2302
  }
2206
2303
  try {
2207
- const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs3.readFileUtf8(path5.join(sessionsDir, name)) ?? ""));
2304
+ const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs7.readFileUtf8(path6.join(sessionsDir, name)) ?? ""));
2208
2305
  if (parsed.success) {
2209
2306
  sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
2210
2307
  }
@@ -2214,14 +2311,14 @@ function listFrontDoorSessions(fs3, sessionsDir) {
2214
2311
  }
2215
2312
  return sessions.sort((a, b) => a.pid - b.pid);
2216
2313
  }
2217
- function removeFrontDoorSession(fs3, sessionsDir, pid) {
2218
- fs3.removeRecursive(path5.join(sessionsDir, `${String(pid)}.json`));
2314
+ function removeFrontDoorSession(fs7, sessionsDir, pid) {
2315
+ fs7.removeRecursive(path6.join(sessionsDir, `${String(pid)}.json`));
2219
2316
  }
2220
- function pruneDeadFrontDoorSessions(fs3, sessionsDir, isRunning) {
2317
+ function pruneDeadFrontDoorSessions(fs7, sessionsDir, isRunning) {
2221
2318
  const removed = [];
2222
- for (const session of listFrontDoorSessions(fs3, sessionsDir)) {
2319
+ for (const session of listFrontDoorSessions(fs7, sessionsDir)) {
2223
2320
  if (!isRunning(session.pid)) {
2224
- removeFrontDoorSession(fs3, sessionsDir, session.pid);
2321
+ removeFrontDoorSession(fs7, sessionsDir, session.pid);
2225
2322
  removed.push(session.pid);
2226
2323
  }
2227
2324
  }
@@ -2233,8 +2330,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
2233
2330
  var FRONTDOOR_POLL_MS = 1e3;
2234
2331
  var MS_PER_MINUTE = 6e4;
2235
2332
  async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
2236
- const { fs: fs3, paths } = ports;
2237
- const previous = readFrontDoorState(fs3, paths.frontdoorStateFile);
2333
+ const { fs: fs7, paths } = ports;
2334
+ const previous = readFrontDoorState(fs7, paths.frontdoorStateFile);
2238
2335
  let lastPort = previous?.lastPort;
2239
2336
  let lastConnectPort = previous?.lastConnectPort;
2240
2337
  let lastDirectPort = previous?.lastDirectPort;
@@ -2244,12 +2341,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2244
2341
  ...lastDirectPort === void 0 ? {} : { lastDirectPort }
2245
2342
  });
2246
2343
  const fail = (message) => {
2247
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { lastError: message, ...sticky() });
2248
- fs3.removeRecursive(paths.frontdoorLockFile);
2344
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { lastError: message, ...sticky() });
2345
+ fs7.removeRecursive(paths.frontdoorLockFile);
2249
2346
  ports.log(`agent-shim frontdoor supervisor: ${message}`);
2250
2347
  return 1;
2251
2348
  };
2252
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
2349
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
2253
2350
  const previousSticky = lastPort;
2254
2351
  let http2;
2255
2352
  try {
@@ -2286,8 +2383,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2286
2383
  ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom must be restarted to admit the new address)`);
2287
2384
  }
2288
2385
  lastDirectPort = direct.port;
2289
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
2290
- fs3.removeRecursive(paths.frontdoorLockFile);
2386
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
2387
+ fs7.removeRecursive(paths.frontdoorLockFile);
2291
2388
  ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http2.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
2292
2389
  let idleSince;
2293
2390
  let ticks = 0;
@@ -2296,15 +2393,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2296
2393
  return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
2297
2394
  }
2298
2395
  ticks += 1;
2299
- pruneDeadFrontDoorSessions(fs3, paths.frontdoorSessionsDir, ports.isRunning);
2300
- if (listFrontDoorSessions(fs3, paths.frontdoorSessionsDir).length === 0) {
2396
+ pruneDeadFrontDoorSessions(fs7, paths.frontdoorSessionsDir, ports.isRunning);
2397
+ if (listFrontDoorSessions(fs7, paths.frontdoorSessionsDir).length === 0) {
2301
2398
  idleSince ??= ports.now();
2302
2399
  if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
2303
2400
  ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
2304
2401
  await direct.close();
2305
2402
  await connect3.close();
2306
2403
  await http2.close();
2307
- writeFrontDoorState(fs3, paths.frontdoorStateFile, sticky());
2404
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, sticky());
2308
2405
  return 0;
2309
2406
  }
2310
2407
  } else {
@@ -2359,8 +2456,8 @@ function parseConnectTarget(authority) {
2359
2456
  function isInterceptedHost(host, interceptHosts) {
2360
2457
  return interceptHosts.includes(host);
2361
2458
  }
2362
- function servedByPipeline(path9) {
2363
- return path9?.startsWith(ROUTED_PATH_PREFIX) === true;
2459
+ function servedByPipeline(path13) {
2460
+ return path13?.startsWith(ROUTED_PATH_PREFIX) === true;
2364
2461
  }
2365
2462
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
2366
2463
  "connection",
@@ -2658,15 +2755,15 @@ async function blindTunnel(socket, target, effects) {
2658
2755
  socket.destroy();
2659
2756
  });
2660
2757
  }
2661
- function isEnoent(error) {
2758
+ function isEnoent2(error) {
2662
2759
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2663
2760
  }
2664
2761
  function realConnectCertStore(paths) {
2665
2762
  const read = (file) => {
2666
2763
  try {
2667
- return fs.readFileSync(file, "utf8");
2764
+ return fs2.readFileSync(file, "utf8");
2668
2765
  } catch (error) {
2669
- if (isEnoent(error)) {
2766
+ if (isEnoent2(error)) {
2670
2767
  return void 0;
2671
2768
  }
2672
2769
  throw error;
@@ -2679,10 +2776,10 @@ function realConnectCertStore(paths) {
2679
2776
  return certPem === void 0 || keyPem2 === void 0 ? void 0 : { certPem, keyPem: keyPem2 };
2680
2777
  },
2681
2778
  writeCa: (ca) => {
2682
- fs.mkdirSync(paths.frontdoorCaDir, { recursive: true });
2683
- fs.writeFileSync(paths.frontdoorCaCertFile, ca.certPem, "utf8");
2684
- fs.writeFileSync(paths.frontdoorCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
2685
- fs.chmodSync(paths.frontdoorCaKeyFile, CA_KEY_FILE_MODE);
2779
+ fs2.mkdirSync(paths.frontdoorCaDir, { recursive: true });
2780
+ fs2.writeFileSync(paths.frontdoorCaCertFile, ca.certPem, "utf8");
2781
+ fs2.writeFileSync(paths.frontdoorCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
2782
+ fs2.chmodSync(paths.frontdoorCaKeyFile, CA_KEY_FILE_MODE);
2686
2783
  }
2687
2784
  };
2688
2785
  }
@@ -2851,11 +2948,11 @@ function realConnectEffects() {
2851
2948
  }
2852
2949
 
2853
2950
  // src/frontdoor/ensure.ts
2854
- import { randomUUID as randomUUID2 } from "node:crypto";
2951
+ import { randomUUID as randomUUID3 } from "node:crypto";
2855
2952
 
2856
2953
  // src/headroom/state.ts
2857
2954
  import { createHash as createHash2 } from "node:crypto";
2858
- import path6 from "node:path";
2955
+ import path7 from "node:path";
2859
2956
  import { z as z4 } from "zod";
2860
2957
  var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
2861
2958
  var HeadroomStateSchema = z4.strictObject({
@@ -2889,16 +2986,16 @@ function parseJson(raw, schema) {
2889
2986
  const parsed = schema.safeParse(JSON.parse(raw));
2890
2987
  return parsed.success ? parsed.data : void 0;
2891
2988
  }
2892
- function readHeadroomState(fs3, stateFile) {
2989
+ function readHeadroomState(fs7, stateFile) {
2893
2990
  try {
2894
- return parseJson(fs3.readFileUtf8(stateFile), HeadroomStateSchema);
2991
+ return parseJson(fs7.readFileUtf8(stateFile), HeadroomStateSchema);
2895
2992
  } catch {
2896
2993
  return void 0;
2897
2994
  }
2898
2995
  }
2899
- function writeHeadroomState(fs3, stateFile, state) {
2900
- fs3.mkdirp(path6.dirname(stateFile));
2901
- fs3.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
2996
+ function writeHeadroomState(fs7, stateFile, state) {
2997
+ fs7.mkdirp(path7.dirname(stateFile));
2998
+ fs7.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
2902
2999
  `);
2903
3000
  }
2904
3001
  function headroomAllowlist(providers) {
@@ -2918,14 +3015,14 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
2918
3015
  function hashAllowlist(allowlist) {
2919
3016
  return createHash2("sha256").update(allowlist.join("\n")).digest("hex");
2920
3017
  }
2921
- function readAllProviders(fs3, providersDir) {
3018
+ function readAllProviders(fs7, providersDir) {
2922
3019
  const result = [];
2923
- for (const name of fs3.readdir(providersDir)) {
3020
+ for (const name of fs7.readdir(providersDir)) {
2924
3021
  if (!name.endsWith(".json")) {
2925
3022
  continue;
2926
3023
  }
2927
3024
  try {
2928
- const parsed = parseJson(fs3.readFileUtf8(path6.join(providersDir, name)), ProviderSchema);
3025
+ const parsed = parseJson(fs7.readFileUtf8(path7.join(providersDir, name)), ProviderSchema);
2929
3026
  if (parsed !== void 0) {
2930
3027
  result.push({ name: name.slice(0, -".json".length), provider: parsed });
2931
3028
  }
@@ -2935,22 +3032,22 @@ function readAllProviders(fs3, providersDir) {
2935
3032
  }
2936
3033
  return result;
2937
3034
  }
2938
- function writeSession(fs3, sessionsDir, session) {
2939
- fs3.mkdirp(sessionsDir);
2940
- fs3.writeFileUtf8(path6.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
3035
+ function writeSession(fs7, sessionsDir, session) {
3036
+ fs7.mkdirp(sessionsDir);
3037
+ fs7.writeFileUtf8(path7.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
2941
3038
  `);
2942
3039
  }
2943
- function removeSession(fs3, sessionsDir, pid) {
2944
- fs3.removeRecursive(path6.join(sessionsDir, `${String(pid)}.json`));
3040
+ function removeSession(fs7, sessionsDir, pid) {
3041
+ fs7.removeRecursive(path7.join(sessionsDir, `${String(pid)}.json`));
2945
3042
  }
2946
- function listSessions(fs3, sessionsDir) {
3043
+ function listSessions(fs7, sessionsDir) {
2947
3044
  const sessions = [];
2948
- for (const name of fs3.readdir(sessionsDir)) {
3045
+ for (const name of fs7.readdir(sessionsDir)) {
2949
3046
  if (!name.endsWith(".json")) {
2950
3047
  continue;
2951
3048
  }
2952
3049
  try {
2953
- const parsed = parseJson(fs3.readFileUtf8(path6.join(sessionsDir, name)), HeadroomSessionSchema);
3050
+ const parsed = parseJson(fs7.readFileUtf8(path7.join(sessionsDir, name)), HeadroomSessionSchema);
2954
3051
  if (parsed !== void 0) {
2955
3052
  sessions.push(parsed);
2956
3053
  }
@@ -2960,22 +3057,22 @@ function listSessions(fs3, sessionsDir) {
2960
3057
  }
2961
3058
  return sessions.sort((a, b) => a.pid - b.pid);
2962
3059
  }
2963
- function sessionsForSupervisor(fs3, sessionsDir, supervisorPid) {
2964
- return listSessions(fs3, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
3060
+ function sessionsForSupervisor(fs7, sessionsDir, supervisorPid) {
3061
+ return listSessions(fs7, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
2965
3062
  }
2966
- function pruneDeadSessions(fs3, sessionsDir, isRunning) {
3063
+ function pruneDeadSessions(fs7, sessionsDir, isRunning) {
2967
3064
  const removed = [];
2968
- for (const session of listSessions(fs3, sessionsDir)) {
3065
+ for (const session of listSessions(fs7, sessionsDir)) {
2969
3066
  if (!isRunning(session.pid)) {
2970
- removeSession(fs3, sessionsDir, session.pid);
3067
+ removeSession(fs7, sessionsDir, session.pid);
2971
3068
  removed.push(session.pid);
2972
3069
  }
2973
3070
  }
2974
3071
  return removed;
2975
3072
  }
2976
- function readStartLock(fs3, lockFile) {
3073
+ function readStartLock(fs7, lockFile) {
2977
3074
  try {
2978
- return parseJson(fs3.readFileUtf8(lockFile), HeadroomLockSchema);
3075
+ return parseJson(fs7.readFileUtf8(lockFile), HeadroomLockSchema);
2979
3076
  } catch {
2980
3077
  return void 0;
2981
3078
  }
@@ -3015,7 +3112,7 @@ function ensureFrontDoor(params) {
3015
3112
  continue;
3016
3113
  }
3017
3114
  if (verdict.ok) {
3018
- const token = randomUUID2();
3115
+ const token = randomUUID3();
3019
3116
  writeFrontDoorSession(ports.fs, paths.frontdoorSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), token });
3020
3117
  return { port: state.port, connectPort: state.connectPort, token };
3021
3118
  }
@@ -3055,7 +3152,7 @@ function ensureFrontDoor(params) {
3055
3152
  }
3056
3153
 
3057
3154
  // src/usage/read.ts
3058
- import path7 from "node:path";
3155
+ import path8 from "node:path";
3059
3156
 
3060
3157
  // src/usage/schema.ts
3061
3158
  import { z as z5 } from "zod";
@@ -3222,11 +3319,11 @@ function snapshotPath(snapshotsDir, identity) {
3222
3319
  if (!isIdentityName(identity)) {
3223
3320
  throw new Error(`"${identity}" is not a valid identity name.`);
3224
3321
  }
3225
- return path7.join(snapshotsDir, `${identity}.json`);
3322
+ return path8.join(snapshotsDir, `${identity}.json`);
3226
3323
  }
3227
- function readUsageSnapshot(fs3, snapshotsDir, identity) {
3324
+ function readUsageSnapshot(fs7, snapshotsDir, identity) {
3228
3325
  const file = snapshotPath(snapshotsDir, identity);
3229
- const raw = fs3.readFileUtf8(file);
3326
+ const raw = fs7.readFileUtf8(file);
3230
3327
  if (raw === void 0) {
3231
3328
  return void 0;
3232
3329
  }
@@ -3242,8 +3339,8 @@ function readUsageSnapshot(fs3, snapshotsDir, identity) {
3242
3339
  }
3243
3340
  return snapshot.data;
3244
3341
  }
3245
- function listUsageSnapshots(fs3, snapshotsDir) {
3246
- return fs3.readdir(snapshotsDir).flatMap((name) => {
3342
+ function listUsageSnapshots(fs7, snapshotsDir) {
3343
+ return fs7.readdir(snapshotsDir).flatMap((name) => {
3247
3344
  if (!name.endsWith(".json")) {
3248
3345
  return [];
3249
3346
  }
@@ -3251,7 +3348,7 @@ function listUsageSnapshots(fs3, snapshotsDir) {
3251
3348
  if (!isIdentityName(identity)) {
3252
3349
  return [];
3253
3350
  }
3254
- const snapshot = readUsageSnapshot(fs3, snapshotsDir, identity);
3351
+ const snapshot = readUsageSnapshot(fs7, snapshotsDir, identity);
3255
3352
  return snapshot === void 0 ? [] : [snapshot];
3256
3353
  }).sort((left, right) => left.identity.localeCompare(right.identity));
3257
3354
  }
@@ -3418,9 +3515,9 @@ function compareVersions(a, b) {
3418
3515
  }
3419
3516
  var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
3420
3517
  async function runSupervisor(initialConfig, ports, options = {}) {
3421
- const { fs: fs3, paths } = ports;
3518
+ const { fs: fs7, paths } = ports;
3422
3519
  let config = initialConfig;
3423
- const previousState = readHeadroomState(fs3, paths.headroomStateFile);
3520
+ const previousState = readHeadroomState(fs7, paths.headroomStateFile);
3424
3521
  if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
3425
3522
  ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
3426
3523
  return 0;
@@ -3429,19 +3526,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3429
3526
  let lastPort = previousState?.lastPort;
3430
3527
  const sticky = () => lastPort === void 0 ? {} : { lastPort };
3431
3528
  const ownsState = () => {
3432
- const owner = readHeadroomState(fs3, paths.headroomStateFile)?.supervisorPid;
3529
+ const owner = readHeadroomState(fs7, paths.headroomStateFile)?.supervisorPid;
3433
3530
  return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
3434
3531
  };
3435
3532
  const writeOwnState = (state) => {
3436
3533
  if (!ownsState()) {
3437
3534
  return false;
3438
3535
  }
3439
- writeHeadroomState(fs3, paths.headroomStateFile, state);
3536
+ writeHeadroomState(fs7, paths.headroomStateFile, state);
3440
3537
  return true;
3441
3538
  };
3442
3539
  const fail = (message) => {
3443
3540
  if (writeOwnState({ lastError: message, ...sticky() })) {
3444
- fs3.removeRecursive(paths.headroomLockFile);
3541
+ fs7.removeRecursive(paths.headroomLockFile);
3445
3542
  }
3446
3543
  ports.log(`agent-shim headroom supervisor: ${message}`);
3447
3544
  return 1;
@@ -3490,7 +3587,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3490
3587
  const settingsHash = hashSettings(config.settings);
3491
3588
  const allowlist = allowlistOf(ports);
3492
3589
  const allowlistHash = hashAllowlist(allowlist);
3493
- pruneDeadSessions(fs3, paths.headroomSessionsDir, ports.isRunning);
3590
+ pruneDeadSessions(fs7, paths.headroomSessionsDir, ports.isRunning);
3494
3591
  const ownsNow = ownsState();
3495
3592
  if (!ownsNow && !superseded) {
3496
3593
  ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
@@ -3542,7 +3639,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3542
3639
  ...sticky()
3543
3640
  });
3544
3641
  if (recorded) {
3545
- fs3.removeRecursive(paths.headroomLockFile);
3642
+ fs7.removeRecursive(paths.headroomLockFile);
3546
3643
  }
3547
3644
  idleSince = void 0;
3548
3645
  ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
@@ -3557,7 +3654,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3557
3654
  }
3558
3655
  } else {
3559
3656
  if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
3560
- if (sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3657
+ if (sessionsForSupervisor(fs7, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3561
3658
  ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
3562
3659
  ports.stopProcess(headroomPid);
3563
3660
  headroomPid = void 0;
@@ -3566,7 +3663,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3566
3663
  continue;
3567
3664
  }
3568
3665
  }
3569
- const sessions = sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid);
3666
+ const sessions = sessionsForSupervisor(fs7, paths.headroomSessionsDir, ports.ownPid);
3570
3667
  if (sessions.length === 0) {
3571
3668
  idleSince ??= ports.now();
3572
3669
  const idleMs = ports.now() - idleSince;
@@ -3588,9 +3685,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3588
3685
  await ports.sleep(HEADROOM_POLL_MS);
3589
3686
  }
3590
3687
  }
3591
- function currentHeadroomAllowlist(fs3, paths) {
3592
- const providers = readAllProviders(fs3, paths.providersDir).map((entry) => entry.provider);
3593
- return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs3, paths.frontdoorStateFile))));
3688
+ function currentHeadroomAllowlist(fs7, paths) {
3689
+ const providers = readAllProviders(fs7, paths.providersDir).map((entry) => entry.provider);
3690
+ return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs7, paths.frontdoorStateFile))));
3594
3691
  }
3595
3692
  function allowlistOf(ports) {
3596
3693
  return currentHeadroomAllowlist(ports.fs, ports.paths);
@@ -3656,63 +3753,735 @@ function evaluateAmbientCredentialGuard(params) {
3656
3753
  }
3657
3754
 
3658
3755
  // src/paths.ts
3659
- import fs2 from "node:fs";
3756
+ import fs3 from "node:fs";
3660
3757
  import os from "node:os";
3661
- import path8 from "node:path";
3758
+ import path9 from "node:path";
3662
3759
  function resolveAgentShimHome(env = process.env, home = os.homedir(), isDirectory = isExistingDirectory) {
3663
3760
  const fromEnv = env.AGENT_SHIM_HOME;
3664
3761
  if (fromEnv !== void 0 && fromEnv !== "") {
3665
3762
  return fromEnv;
3666
3763
  }
3667
- const current = path8.join(home, HOME_DIRNAME);
3668
- const legacy = path8.join(home, LEGACY_HOME_DIRNAME);
3764
+ const current = path9.join(home, HOME_DIRNAME);
3765
+ const legacy = path9.join(home, LEGACY_HOME_DIRNAME);
3669
3766
  return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
3670
3767
  }
3671
3768
  function isExistingDirectory(candidate) {
3672
- return fs2.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
3769
+ return fs3.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
3673
3770
  }
3674
3771
  function resolveClaudeHome() {
3675
3772
  const fromEnv = process.env.AGENT_SHIM_CLAUDE_HOME;
3676
3773
  if (fromEnv !== void 0 && fromEnv !== "") {
3677
3774
  return fromEnv;
3678
3775
  }
3679
- return path8.join(os.homedir(), ".claude");
3776
+ return path9.join(os.homedir(), ".claude");
3680
3777
  }
3681
3778
  function buildLayoutPaths(root) {
3682
3779
  return {
3683
3780
  root,
3684
- identitiesDir: path8.join(root, "identities"),
3685
- configProfilesDir: path8.join(root, "config-profiles"),
3686
- providersDir: path8.join(root, "providers"),
3687
- directoryRulesFile: path8.join(root, "directory-rules.json"),
3688
- activeIdentityFile: path8.join(root, "active-identity"),
3689
- globalConfigFile: path8.join(root, "config.json"),
3690
- categoriesLocalFile: path8.join(root, "categories.local.json"),
3691
- claudeShimFile: path8.join(root, "claude-shim.json"),
3692
- headroomDir: path8.join(root, "headroom"),
3693
- headroomStateFile: path8.join(root, "headroom", "state.json"),
3694
- headroomLockFile: path8.join(root, "headroom", "start.lock"),
3695
- headroomSessionsDir: path8.join(root, "headroom", "sessions"),
3696
- logsDir: path8.join(root, "logs"),
3697
- headroomLogPath: path8.join(root, "logs", "headroom.log"),
3698
- frontdoorDir: path8.join(root, "frontdoor"),
3699
- frontdoorCaDir: path8.join(root, "frontdoor", "ca"),
3700
- frontdoorCaCertFile: path8.join(root, "frontdoor", "ca", "ca.pem"),
3701
- frontdoorCaKeyFile: path8.join(root, "frontdoor", "ca", "ca.key"),
3702
- frontdoorCaBundlesDir: path8.join(root, "frontdoor", "ca", "bundles"),
3703
- frontdoorStateFile: path8.join(root, "frontdoor", "state.json"),
3704
- frontdoorLockFile: path8.join(root, "frontdoor", "start.lock"),
3705
- frontdoorSessionsDir: path8.join(root, "frontdoor", "sessions"),
3706
- frontdoorLogPath: path8.join(root, "logs", "frontdoor.log"),
3707
- usageDir: path8.join(root, "usage"),
3708
- usageLogDir: path8.join(root, "usage", "log"),
3709
- usageSnapshotsDir: path8.join(root, "usage", "snapshots"),
3710
- usagePicksFile: path8.join(root, "usage", "picks.json")
3781
+ identitiesDir: path9.join(root, "identities"),
3782
+ configProfilesDir: path9.join(root, "config-profiles"),
3783
+ providersDir: path9.join(root, "providers"),
3784
+ directoryRulesFile: path9.join(root, "directory-rules.json"),
3785
+ activeIdentityFile: path9.join(root, "active-identity"),
3786
+ globalConfigFile: path9.join(root, "config.json"),
3787
+ categoriesLocalFile: path9.join(root, "categories.local.json"),
3788
+ claudeShimFile: path9.join(root, "claude-shim.json"),
3789
+ headroomDir: path9.join(root, "headroom"),
3790
+ headroomStateFile: path9.join(root, "headroom", "state.json"),
3791
+ headroomLockFile: path9.join(root, "headroom", "start.lock"),
3792
+ headroomSessionsDir: path9.join(root, "headroom", "sessions"),
3793
+ logsDir: path9.join(root, "logs"),
3794
+ headroomLogPath: path9.join(root, "logs", "headroom.log"),
3795
+ frontdoorDir: path9.join(root, "frontdoor"),
3796
+ frontdoorCaDir: path9.join(root, "frontdoor", "ca"),
3797
+ frontdoorCaCertFile: path9.join(root, "frontdoor", "ca", "ca.pem"),
3798
+ frontdoorCaKeyFile: path9.join(root, "frontdoor", "ca", "ca.key"),
3799
+ frontdoorCaBundlesDir: path9.join(root, "frontdoor", "ca", "bundles"),
3800
+ frontdoorStateFile: path9.join(root, "frontdoor", "state.json"),
3801
+ frontdoorLockFile: path9.join(root, "frontdoor", "start.lock"),
3802
+ frontdoorSessionsDir: path9.join(root, "frontdoor", "sessions"),
3803
+ frontdoorLogPath: path9.join(root, "logs", "frontdoor.log"),
3804
+ usageDir: path9.join(root, "usage"),
3805
+ usageLogDir: path9.join(root, "usage", "log"),
3806
+ usageSnapshotsDir: path9.join(root, "usage", "snapshots"),
3807
+ usagePicksFile: path9.join(root, "usage", "picks.json")
3711
3808
  };
3712
3809
  }
3713
3810
  function resolveLayoutPaths() {
3714
3811
  return buildLayoutPaths(resolveAgentShimHome());
3715
3812
  }
3813
+
3814
+ // src/identityStore.ts
3815
+ import fs5 from "node:fs";
3816
+ import path11 from "node:path";
3817
+
3818
+ // src/launcher/identity.ts
3819
+ function poolNameOf(selector) {
3820
+ return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
3821
+ }
3822
+
3823
+ // src/configProfilesStore.ts
3824
+ import fs4 from "node:fs";
3825
+ import path10 from "node:path";
3826
+ var ProfileNotFoundError = class extends CliError {
3827
+ constructor(profileName) {
3828
+ super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
3829
+ this.profileName = profileName;
3830
+ this.name = "ProfileNotFoundError";
3831
+ }
3832
+ profileName;
3833
+ };
3834
+ var ProfileAlreadyExistsError = class extends CliError {
3835
+ constructor(profileName) {
3836
+ super(`A configuration profile named "${profileName}" already exists.`);
3837
+ this.profileName = profileName;
3838
+ this.name = "ProfileAlreadyExistsError";
3839
+ }
3840
+ profileName;
3841
+ };
3842
+ var InvalidCategoryNameError = class extends CliError {
3843
+ constructor(categoryName) {
3844
+ super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
3845
+ this.categoryName = categoryName;
3846
+ this.name = "InvalidCategoryNameError";
3847
+ }
3848
+ categoryName;
3849
+ };
3850
+ function profileJsonPath(paths, name) {
3851
+ return path10.join(paths.configProfilesDir, `${name}.json`);
3852
+ }
3853
+ function profileExists(paths, name) {
3854
+ return fs4.existsSync(profileJsonPath(paths, name));
3855
+ }
3856
+ function requireProfileExists(paths, name) {
3857
+ if (!profileExists(paths, name)) {
3858
+ throw new ProfileNotFoundError(name);
3859
+ }
3860
+ }
3861
+ function readProfile(paths, name) {
3862
+ return readJson(profileJsonPath(paths, name), ConfigProfileSchema);
3863
+ }
3864
+ function createProfile(paths, name, extendsList, description) {
3865
+ if (profileExists(paths, name)) {
3866
+ throw new ProfileAlreadyExistsError(name);
3867
+ }
3868
+ const filePath = profileJsonPath(paths, name);
3869
+ const parsed = ConfigProfileSchema.safeParse({
3870
+ ...description === void 0 ? {} : { description },
3871
+ ...extendsList !== void 0 && extendsList.length > 0 ? { extends: [...extendsList] } : {}
3872
+ });
3873
+ if (!parsed.success) {
3874
+ throw new ConfigValidationError(filePath, parsed.error.issues);
3875
+ }
3876
+ writeJsonAtomic(filePath, parsed.data);
3877
+ return parsed.data;
3878
+ }
3879
+ function listProfiles(paths) {
3880
+ if (!fs4.existsSync(paths.configProfilesDir)) {
3881
+ return [];
3882
+ }
3883
+ const names = fs4.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort();
3884
+ const result = [];
3885
+ for (const name of names) {
3886
+ const profile = readProfile(paths, name);
3887
+ if (profile !== void 0) {
3888
+ result.push({ name, profile });
3889
+ }
3890
+ }
3891
+ return result;
3892
+ }
3893
+ function globalConfigPath(paths) {
3894
+ return paths.globalConfigFile;
3895
+ }
3896
+ function readGlobalConfig(paths) {
3897
+ return readJson(globalConfigPath(paths), GlobalConfigSchema);
3898
+ }
3899
+ function setGlobalDefaultProfile(paths, name) {
3900
+ return applyPatch(
3901
+ globalConfigPath(paths),
3902
+ GlobalConfigSchema,
3903
+ { defaultConfigProfile: name },
3904
+ { defaults: {} }
3905
+ );
3906
+ }
3907
+ function validateCategoryNames(patch) {
3908
+ for (const key of Object.keys(patch)) {
3909
+ if (!isOverridableCategory(key)) {
3910
+ throw new InvalidCategoryNameError(key);
3911
+ }
3912
+ }
3913
+ }
3914
+ function setProfileCategories(paths, name, patch) {
3915
+ requireProfileExists(paths, name);
3916
+ const expandedPatch = expandAllCategoryKey(patch);
3917
+ validateCategoryNames(expandedPatch);
3918
+ const existing = readProfile(paths, name) ?? {};
3919
+ const mergedCategories = { ...existing.categories, ...expandedPatch };
3920
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { categories: mergedCategories });
3921
+ }
3922
+ function setProfileEntries(paths, name, patch) {
3923
+ requireProfileExists(paths, name);
3924
+ const existing = readProfile(paths, name) ?? {};
3925
+ const mergedEntries = { ...existing.entries, ...patch };
3926
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { entries: mergedEntries });
3927
+ }
3928
+ function setProfileLaunchFlags(paths, name, patch) {
3929
+ requireProfileExists(paths, name);
3930
+ const existing = readProfile(paths, name) ?? {};
3931
+ const mergedLaunch = { ...existing.launch, ...patch };
3932
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
3933
+ // Read as `unknown` so the check sees the `undefined` a cleared key holds, which LaunchFlags' optional properties hide from the value type.
3934
+ launch: Object.values(mergedLaunch).every((value) => value === void 0) ? void 0 : mergedLaunch
3935
+ });
3936
+ }
3937
+ function setProfileMetadata(paths, name, patch) {
3938
+ requireProfileExists(paths, name);
3939
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
3940
+ ...patch.extends === void 0 ? {} : { extends: patch.extends.length === 0 ? void 0 : [...patch.extends] },
3941
+ ...patch.description === void 0 ? {} : { description: patch.description === false ? void 0 : patch.description }
3942
+ });
3943
+ }
3944
+ function removeProfile(paths, name) {
3945
+ requireProfileExists(paths, name);
3946
+ fs4.rmSync(profileJsonPath(paths, name));
3947
+ }
3948
+
3949
+ // src/poolStore.ts
3950
+ var PoolNotFoundError = class extends CliError {
3951
+ constructor(poolName) {
3952
+ super(`No pool named "${poolName}". Run \`agent-shim pool add ${poolName} --identity <name>...\` first.`);
3953
+ this.poolName = poolName;
3954
+ this.name = "PoolNotFoundError";
3955
+ }
3956
+ poolName;
3957
+ };
3958
+ var PoolAlreadyExistsError = class extends CliError {
3959
+ constructor(poolName) {
3960
+ super(`A pool named "${poolName}" already exists. Use \`agent-shim pool set ${poolName}\` to change its members.`);
3961
+ this.poolName = poolName;
3962
+ this.name = "PoolAlreadyExistsError";
3963
+ }
3964
+ poolName;
3965
+ };
3966
+ var InvalidPoolNameError = class extends CliError {
3967
+ constructor(attemptedName) {
3968
+ super(`"${attemptedName}" is not a valid pool name: it must start with a letter or number and may then contain letters, numbers, dots, hyphens and underscores.`);
3969
+ this.attemptedName = attemptedName;
3970
+ this.name = "InvalidPoolNameError";
3971
+ }
3972
+ attemptedName;
3973
+ };
3974
+ function readPools(paths) {
3975
+ return readGlobalConfig(paths)?.pools ?? {};
3976
+ }
3977
+ function requirePool(paths, name) {
3978
+ const pool = readPools(paths)[name];
3979
+ if (pool === void 0) {
3980
+ throw new PoolNotFoundError(name);
3981
+ }
3982
+ return pool;
3983
+ }
3984
+ function writePools(paths, pools) {
3985
+ applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
3986
+ }
3987
+ function addPool(paths, name, identities) {
3988
+ if (!PoolNameSchema.safeParse(name).success) {
3989
+ throw new InvalidPoolNameError(name);
3990
+ }
3991
+ const pools = readPools(paths);
3992
+ if (pools[name] !== void 0) {
3993
+ throw new PoolAlreadyExistsError(name);
3994
+ }
3995
+ const pool = { identities: [...identities] };
3996
+ writePools(paths, { ...pools, [name]: pool });
3997
+ return pool;
3998
+ }
3999
+ function setPool(paths, name, identities) {
4000
+ requirePool(paths, name);
4001
+ const pool = { identities: [...identities] };
4002
+ writePools(paths, { ...readPools(paths), [name]: pool });
4003
+ return pool;
4004
+ }
4005
+ function removePool(paths, name) {
4006
+ requirePool(paths, name);
4007
+ const remaining = Object.fromEntries(Object.entries(readPools(paths)).filter(([existing]) => existing !== name));
4008
+ writePools(paths, remaining);
4009
+ }
4010
+
4011
+ // src/identityStore.ts
4012
+ var IdentityNotFoundError = class extends CliError {
4013
+ constructor(name) {
4014
+ super(`No identity named "${name}". Run \`agent-shim identity add ${name}\` first.`);
4015
+ this.name = name;
4016
+ this.name = "IdentityNotFoundError";
4017
+ }
4018
+ name;
4019
+ };
4020
+ var IdentityAlreadyExistsError = class extends CliError {
4021
+ constructor(identityName) {
4022
+ super(`An identity named "${identityName}" already exists.`);
4023
+ this.identityName = identityName;
4024
+ this.name = "IdentityAlreadyExistsError";
4025
+ }
4026
+ identityName;
4027
+ };
4028
+ var InvalidIdentityNameError = class extends CliError {
4029
+ constructor(attemptedName) {
4030
+ super(
4031
+ `"${attemptedName}" is not a valid identity name \u2014 identity names must start with a letter or number and may then contain letters, numbers, dots, hyphens, underscores, and at signs.`
4032
+ );
4033
+ this.attemptedName = attemptedName;
4034
+ this.name = "InvalidIdentityNameError";
4035
+ }
4036
+ attemptedName;
4037
+ };
4038
+ function identityJsonPath(paths, name) {
4039
+ return path11.join(paths.identitiesDir, name, "identity.json");
4040
+ }
4041
+ function identityExists(paths, name) {
4042
+ return fs5.existsSync(identityJsonPath(paths, name));
4043
+ }
4044
+ function readIdentity(paths, name) {
4045
+ return readJson(identityJsonPath(paths, name), IdentitySchema);
4046
+ }
4047
+ function addIdentity(paths, name) {
4048
+ if (identityExists(paths, name)) {
4049
+ throw new IdentityAlreadyExistsError(name);
4050
+ }
4051
+ const parsed = IdentitySchema.safeParse({ name, allowAmbientCredential: false });
4052
+ if (!parsed.success) {
4053
+ throw new InvalidIdentityNameError(name);
4054
+ }
4055
+ writeJsonAtomic(identityJsonPath(paths, name), parsed.data);
4056
+ return parsed.data;
4057
+ }
4058
+ function useIdentity(paths, name) {
4059
+ const pool = poolNameOf(name);
4060
+ if (pool !== void 0) {
4061
+ requirePool(paths, pool);
4062
+ } else if (!identityExists(paths, name)) {
4063
+ throw new IdentityNotFoundError(name);
4064
+ }
4065
+ writeTextAtomic(paths.activeIdentityFile, `${name}
4066
+ `);
4067
+ }
4068
+ function readActiveIdentity(paths) {
4069
+ if (!fs5.existsSync(paths.activeIdentityFile)) {
4070
+ return void 0;
4071
+ }
4072
+ const raw = fs5.readFileSync(paths.activeIdentityFile, "utf8").trim();
4073
+ return raw === "" ? void 0 : raw;
4074
+ }
4075
+ function isIdentityDirectoryName(name) {
4076
+ return !name.startsWith(".");
4077
+ }
4078
+ function readIdentityForListing(paths, name) {
4079
+ try {
4080
+ return readIdentity(paths, name);
4081
+ } catch (error) {
4082
+ if (error instanceof ConfigValidationError || error instanceof SyntaxError) {
4083
+ return { problem: error.message.replace(/\s*\n\s*/g, " ") };
4084
+ }
4085
+ throw error;
4086
+ }
4087
+ }
4088
+ function listIdentities(paths) {
4089
+ if (!fs5.existsSync(paths.identitiesDir)) {
4090
+ return [];
4091
+ }
4092
+ const active = readActiveIdentity(paths);
4093
+ const names = fs5.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort();
4094
+ const result = [];
4095
+ for (const name of names) {
4096
+ const read = readIdentityForListing(paths, name);
4097
+ if (read === void 0) {
4098
+ continue;
4099
+ }
4100
+ const isActive = name === active;
4101
+ result.push("problem" in read ? { name, isActive, problem: read.problem } : { name, identity: read, isActive });
4102
+ }
4103
+ return result;
4104
+ }
4105
+ function setDefaultConfigProfile(paths, identityName, profileName) {
4106
+ if (!identityExists(paths, identityName)) {
4107
+ throw new IdentityNotFoundError(identityName);
4108
+ }
4109
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
4110
+ defaultConfigProfile: profileName
4111
+ });
4112
+ }
4113
+ function setAllowAmbientCredential(paths, identityName, allow) {
4114
+ if (!identityExists(paths, identityName)) {
4115
+ throw new IdentityNotFoundError(identityName);
4116
+ }
4117
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
4118
+ allowAmbientCredential: allow
4119
+ });
4120
+ }
4121
+ function setIdentityCredential(paths, identityName, change) {
4122
+ const existing = readIdentity(paths, identityName);
4123
+ if (existing === void 0) {
4124
+ throw new IdentityNotFoundError(identityName);
4125
+ }
4126
+ if (change === false) {
4127
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, { credential: void 0 });
4128
+ }
4129
+ const sources = change.sources ?? existing.credential?.sources;
4130
+ if (sources === void 0) {
4131
+ throw new UsageError(`Identity "${identityName}" has no credential yet: pass --credential to give it one before choosing its target.`);
4132
+ }
4133
+ const target = change.target ?? existing.credential?.target;
4134
+ const cache = change.cache === void 0 ? existing.credential?.cache : change.cache === false ? void 0 : change.cache;
4135
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
4136
+ credential: { sources: [...sources], ...target === void 0 ? {} : { target }, ...cache === void 0 ? {} : { cache } }
4137
+ });
4138
+ }
4139
+ function belongsToIdentity(entry, name) {
4140
+ return entry === name || entry === path11.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
4141
+ }
4142
+ function removeIdentity(paths, name) {
4143
+ if (!identityExists(paths, name)) {
4144
+ throw new IdentityNotFoundError(name);
4145
+ }
4146
+ for (const entry of fs5.readdirSync(paths.identitiesDir)) {
4147
+ if (belongsToIdentity(entry, name)) {
4148
+ fs5.rmSync(path11.join(paths.identitiesDir, entry), { recursive: true, force: true });
4149
+ }
4150
+ }
4151
+ if (readActiveIdentity(paths) === name) {
4152
+ fs5.rmSync(paths.activeIdentityFile);
4153
+ }
4154
+ }
4155
+
4156
+ // src/providersStore.ts
4157
+ import fs6 from "node:fs";
4158
+ import path12 from "node:path";
4159
+
4160
+ // src/credentialCache.ts
4161
+ import { z as z7 } from "zod";
4162
+ var CachedCredentialSchema = z7.strictObject({
4163
+ token: z7.string().min(1),
4164
+ fetchedAt: z7.number().int().nonnegative(),
4165
+ source: CredentialSourceSchema
4166
+ });
4167
+
4168
+ // src/providersStore.ts
4169
+ var ProviderNotFoundError = class extends CliError {
4170
+ constructor(name) {
4171
+ super(`No provider named "${name}". Run \`agent-shim provider add ${name}\` first.`);
4172
+ this.name = name;
4173
+ this.name = "ProviderNotFoundError";
4174
+ }
4175
+ name;
4176
+ };
4177
+ var ProviderAlreadyExistsError = class extends CliError {
4178
+ constructor(providerName) {
4179
+ super(`A provider named "${providerName}" already exists.`);
4180
+ this.providerName = providerName;
4181
+ this.name = "ProviderAlreadyExistsError";
4182
+ }
4183
+ providerName;
4184
+ };
4185
+ var InvalidProviderNameError = class extends CliError {
4186
+ constructor(attemptedName) {
4187
+ super(
4188
+ `"${attemptedName}" is not a valid provider name. Provider names must start with a letter or number and may then contain letters, numbers, dots, hyphens, underscores, and at signs.`
4189
+ );
4190
+ this.attemptedName = attemptedName;
4191
+ this.name = "InvalidProviderNameError";
4192
+ }
4193
+ attemptedName;
4194
+ };
4195
+ var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
4196
+ function providerJsonPath(paths, name) {
4197
+ return path12.join(paths.providersDir, `${name}.json`);
4198
+ }
4199
+ function providerExists(paths, name) {
4200
+ return fs6.existsSync(providerJsonPath(paths, name));
4201
+ }
4202
+ var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
4203
+ var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
4204
+ var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
4205
+ function isRecord(value) {
4206
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4207
+ }
4208
+ function legacyProviderConversion(raw) {
4209
+ if (!isRecord(raw) || "credential" in raw) {
4210
+ return void 0;
4211
+ }
4212
+ const env = isRecord(raw.env) ? raw.env : void 0;
4213
+ const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
4214
+ const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
4215
+ if (fields.length === 0) {
4216
+ return void 0;
4217
+ }
4218
+ const sources = [];
4219
+ if (typeof raw.tokenEnv === "string") {
4220
+ sources.push({ env: raw.tokenEnv });
4221
+ }
4222
+ if (Array.isArray(raw.tokenCommand)) {
4223
+ sources.push({ command: raw.tokenCommand });
4224
+ }
4225
+ if (typeof env?.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN !== "") {
4226
+ sources.push({ literal: LEGACY_LITERAL_PLACEHOLDER });
4227
+ }
4228
+ const remainingEnv = env === void 0 ? void 0 : Object.fromEntries(Object.entries(env).filter(([key]) => !LEGACY_ENV_CREDENTIAL_KEYS.includes(key)));
4229
+ const replacedKeys = [...LEGACY_PROVIDER_FIELDS, "env"];
4230
+ const rest = Object.fromEntries(Object.entries(raw).filter(([key]) => !replacedKeys.includes(key)));
4231
+ return {
4232
+ fields,
4233
+ replacement: {
4234
+ ...rest,
4235
+ credential: { sources, ...raw.authScheme === "apiKey" ? { target: "apiKey" } : {} },
4236
+ ...remainingEnv === void 0 || Object.keys(remainingEnv).length === 0 ? {} : { env: remainingEnv }
4237
+ }
4238
+ };
4239
+ }
4240
+ var LegacyProviderFileError = class extends CliError {
4241
+ constructor(filePath, conversion) {
4242
+ super(
4243
+ `${filePath} uses ${conversion.fields.join(", ")}, which a credential block replaced. Rewrite it as:
4244
+ ` + JSON.stringify(conversion.replacement, null, 2)
4245
+ );
4246
+ this.filePath = filePath;
4247
+ this.conversion = conversion;
4248
+ this.name = "LegacyProviderFileError";
4249
+ }
4250
+ filePath;
4251
+ conversion;
4252
+ };
4253
+ function parseProviderFile(filePath, raw) {
4254
+ const conversion = legacyProviderConversion(raw);
4255
+ if (conversion !== void 0) {
4256
+ throw new LegacyProviderFileError(filePath, conversion);
4257
+ }
4258
+ const parsed = ProviderSchema.safeParse(raw);
4259
+ if (!parsed.success) {
4260
+ throw new ConfigValidationError(filePath, parsed.error.issues);
4261
+ }
4262
+ return parsed.data;
4263
+ }
4264
+ function readProvider(paths, name) {
4265
+ const filePath = providerJsonPath(paths, name);
4266
+ if (!fs6.existsSync(filePath)) {
4267
+ return void 0;
4268
+ }
4269
+ const raw = JSON.parse(fs6.readFileSync(filePath, "utf8"));
4270
+ return parseProviderFile(filePath, raw);
4271
+ }
4272
+ function listProviders(paths) {
4273
+ const names = listProviderNames(
4274
+ (dir) => fs6.readdirSync(dir),
4275
+ paths.providersDir
4276
+ );
4277
+ const result = [];
4278
+ for (const name of names) {
4279
+ let provider;
4280
+ try {
4281
+ provider = readProvider(paths, name);
4282
+ } catch (error) {
4283
+ if (!(error instanceof ConfigValidationError || error instanceof LegacyProviderFileError)) {
4284
+ throw error;
4285
+ }
4286
+ console.error(`agent-shim: providers/${name}.json is invalid and was skipped: ${error.message}`);
4287
+ continue;
4288
+ }
4289
+ if (provider !== void 0) {
4290
+ result.push({ name, provider });
4291
+ }
4292
+ }
4293
+ return result;
4294
+ }
4295
+ function listProviderNames(readdir, providersDir) {
4296
+ let entries;
4297
+ try {
4298
+ entries = readdir(providersDir);
4299
+ } catch {
4300
+ return [];
4301
+ }
4302
+ return entries.filter((entry) => entry.endsWith(".json")).map((entry) => entry.slice(0, -".json".length)).sort();
4303
+ }
4304
+ function describeProviderEndpoint(provider) {
4305
+ return isCodexProvider(provider) ? "codex translation daemon" : provider.baseUrl;
4306
+ }
4307
+ var ProviderKindMismatchError = class extends UsageError {
4308
+ constructor(message) {
4309
+ super(message);
4310
+ this.name = "ProviderKindMismatchError";
4311
+ }
4312
+ };
4313
+ function isEmptyCodexConfig(config) {
4314
+ return config === void 0 || config.defaultModel === void 0 && config.effort === void 0 && (config.models === void 0 || Object.keys(config.models).length === 0);
4315
+ }
4316
+ function addProvider(paths, name, input) {
4317
+ if (providerExists(paths, name)) {
4318
+ throw new ProviderAlreadyExistsError(name);
4319
+ }
4320
+ if (!PROVIDER_NAME_RE.test(name)) {
4321
+ throw new InvalidProviderNameError(name);
4322
+ }
4323
+ const kind = input.kind ?? "http";
4324
+ if (kind === "codex" && input.baseUrl !== void 0) {
4325
+ throw new ProviderKindMismatchError("A codex provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
4326
+ }
4327
+ if (kind === "http" && input.baseUrl === void 0) {
4328
+ throw new ProviderKindMismatchError("An http provider needs --base-url.");
4329
+ }
4330
+ if (kind === "http" && !isEmptyCodexConfig(input.codex)) {
4331
+ throw new ProviderKindMismatchError("Codex settings apply only to a codex provider (--kind codex).");
4332
+ }
4333
+ const shared = {
4334
+ displayName: input.displayName,
4335
+ credential: { sources: input.sources, ...input.target === void 0 ? {} : { target: input.target } },
4336
+ ...input.env === void 0 || Object.keys(input.env).length === 0 ? {} : { env: input.env }
4337
+ };
4338
+ const parsed = ProviderSchema.safeParse(
4339
+ kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : { ...shared, baseUrl: input.baseUrl }
4340
+ );
4341
+ if (!parsed.success) {
4342
+ throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
4343
+ }
4344
+ writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
4345
+ return parsed.data;
4346
+ }
4347
+ function updateProvider(paths, name, input) {
4348
+ const existing = readProvider(paths, name);
4349
+ if (existing === void 0) {
4350
+ throw new ProviderNotFoundError(name);
4351
+ }
4352
+ const unset = new Set(input.unsetEnv);
4353
+ const env = Object.fromEntries(Object.entries({ ...existing.env, ...input.env }).filter(([key]) => !unset.has(key)));
4354
+ const target = input.target ?? existing.credential.target;
4355
+ const cache = input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
4356
+ if (isCodexProvider(existing) && input.baseUrl !== void 0) {
4357
+ throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
4358
+ }
4359
+ if (!isCodexProvider(existing) && !isEmptyCodexConfig(input.codex)) {
4360
+ throw new ProviderKindMismatchError(`Provider "${name}" is an http provider; codex settings apply only to a codex provider.`);
4361
+ }
4362
+ const shared = {
4363
+ ...input.displayName === void 0 ? {} : { displayName: input.displayName },
4364
+ credential: {
4365
+ sources: input.sources ?? existing.credential.sources,
4366
+ ...target === void 0 ? {} : { target },
4367
+ ...cache === void 0 ? {} : { cache }
4368
+ },
4369
+ env: Object.keys(env).length === 0 ? void 0 : env
4370
+ };
4371
+ let candidate;
4372
+ if (isCodexProvider(existing)) {
4373
+ const codex = {
4374
+ ...existing.codex,
4375
+ ...input.codex,
4376
+ ...existing.codex?.models === void 0 && input.codex?.models === void 0 ? {} : { models: { ...existing.codex?.models, ...input.codex?.models } }
4377
+ };
4378
+ candidate = { ...existing, ...shared, codex: isEmptyCodexConfig(codex) ? void 0 : codex };
4379
+ } else {
4380
+ candidate = { ...existing, ...shared, ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl } };
4381
+ }
4382
+ const parsed = ProviderSchema.safeParse(candidate);
4383
+ if (!parsed.success) {
4384
+ throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
4385
+ }
4386
+ writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
4387
+ return parsed.data;
4388
+ }
4389
+ function removeProvider(paths, name) {
4390
+ if (!providerExists(paths, name)) {
4391
+ throw new ProviderNotFoundError(name);
4392
+ }
4393
+ fs6.rmSync(providerJsonPath(paths, name));
4394
+ }
4395
+
4396
+ // src/directoryRulesStore.ts
4397
+ var DirectoryRuleNotFoundError = class extends CliError {
4398
+ constructor(rulePath) {
4399
+ super(`No directory rule found for path "${rulePath}".`);
4400
+ this.rulePath = rulePath;
4401
+ this.name = "DirectoryRuleNotFoundError";
4402
+ }
4403
+ rulePath;
4404
+ };
4405
+ var DirectoryRuleMissingTargetError = class extends CliError {
4406
+ constructor() {
4407
+ super("A directory rule must set at least one of --config-profile or --identity.");
4408
+ this.name = "DirectoryRuleMissingTargetError";
4409
+ }
4410
+ };
4411
+ var DirectoryRuleAlreadyExistsError = class extends CliError {
4412
+ constructor(rulePath) {
4413
+ super(`A directory rule for path "${rulePath}" already exists. Use \`agent-shim rule set\` to change it.`);
4414
+ this.rulePath = rulePath;
4415
+ this.name = "DirectoryRuleAlreadyExistsError";
4416
+ }
4417
+ rulePath;
4418
+ };
4419
+ function readDirectoryRules(paths) {
4420
+ return readJson(paths.directoryRulesFile, DirectoryRulesSchema) ?? { rules: [] };
4421
+ }
4422
+ function writeDirectoryRules(paths, rules) {
4423
+ const parsed = DirectoryRulesSchema.safeParse(rules);
4424
+ if (!parsed.success) {
4425
+ throw new ConfigValidationError(paths.directoryRulesFile, parsed.error.issues);
4426
+ }
4427
+ writeJsonAtomic(paths.directoryRulesFile, parsed.data);
4428
+ }
4429
+ function listDirectoryRules(paths) {
4430
+ return readDirectoryRules(paths).rules;
4431
+ }
4432
+ function addDirectoryRule(paths, rulePath, options) {
4433
+ if (options.configProfile === void 0 && options.identity === void 0) {
4434
+ throw new DirectoryRuleMissingTargetError();
4435
+ }
4436
+ const current = readDirectoryRules(paths);
4437
+ if (current.rules.some((rule) => rule.path === rulePath)) {
4438
+ throw new DirectoryRuleAlreadyExistsError(rulePath);
4439
+ }
4440
+ const created = buildNewRule(rulePath, options);
4441
+ writeDirectoryRules(paths, { ...current, rules: [...current.rules, created] });
4442
+ return created;
4443
+ }
4444
+ function ruleHasEffect(rule) {
4445
+ return rule.configProfile !== void 0 || rule.identity !== void 0 || rule.categories !== void 0 || rule.entries !== void 0 || rule.launch !== void 0;
4446
+ }
4447
+ function updateDirectoryRule(paths, rulePath, options) {
4448
+ const current = readDirectoryRules(paths);
4449
+ const index = current.rules.findIndex((rule) => rule.path === rulePath);
4450
+ const existing = index === -1 ? void 0 : current.rules[index];
4451
+ if (existing === void 0) {
4452
+ throw new DirectoryRuleNotFoundError(rulePath);
4453
+ }
4454
+ const { configProfile, identity, ...rest } = existing;
4455
+ const nextConfigProfile = options.configProfile === void 0 ? configProfile : options.configProfile === false ? void 0 : options.configProfile;
4456
+ const nextIdentity = options.identity === void 0 ? identity : options.identity === false ? void 0 : options.identity;
4457
+ const updated = {
4458
+ ...rest,
4459
+ ...nextConfigProfile === void 0 ? {} : { configProfile: nextConfigProfile },
4460
+ ...nextIdentity === void 0 ? {} : { identity: nextIdentity }
4461
+ };
4462
+ if (!ruleHasEffect(updated)) {
4463
+ throw new DirectoryRuleMissingTargetError();
4464
+ }
4465
+ const nextRules = [...current.rules];
4466
+ nextRules[index] = updated;
4467
+ writeDirectoryRules(paths, { ...current, rules: nextRules });
4468
+ return updated;
4469
+ }
4470
+ function buildNewRule(rulePath, options) {
4471
+ return {
4472
+ path: rulePath,
4473
+ ...options.configProfile !== void 0 ? { configProfile: options.configProfile } : {},
4474
+ ...options.identity !== void 0 ? { identity: options.identity } : {}
4475
+ };
4476
+ }
4477
+ function removeDirectoryRule(paths, rulePath) {
4478
+ const current = readDirectoryRules(paths);
4479
+ const nextRules = current.rules.filter((rule) => rule.path !== rulePath);
4480
+ if (nextRules.length === current.rules.length) {
4481
+ throw new DirectoryRuleNotFoundError(rulePath);
4482
+ }
4483
+ writeDirectoryRules(paths, { ...current, rules: nextRules });
4484
+ }
3716
4485
  export {
3717
4486
  AMBIENT_CREDENTIAL_VARS,
3718
4487
  CONNECT_INTERCEPT_HOST,
@@ -3726,6 +4495,9 @@ export {
3726
4495
  CredentialCacheSchema,
3727
4496
  CredentialSchema,
3728
4497
  CredentialSourceSchema,
4498
+ DirectoryRuleAlreadyExistsError,
4499
+ DirectoryRuleMissingTargetError,
4500
+ DirectoryRuleNotFoundError,
3729
4501
  DirectoryRuleSchema,
3730
4502
  DirectoryRulesSchema,
3731
4503
  EXIT_FAILURE,
@@ -3735,20 +4507,38 @@ export {
3735
4507
  FrontDoorStartError,
3736
4508
  GlobalConfigSchema,
3737
4509
  HeadroomStartError,
4510
+ IdentityAlreadyExistsError,
4511
+ IdentityNotFoundError,
3738
4512
  IdentitySchema,
4513
+ InvalidCategoryNameError,
4514
+ InvalidIdentityNameError,
4515
+ InvalidProviderNameError,
4516
+ LegacyProviderFileError,
3739
4517
  PoolNameSchema,
4518
+ PoolNotFoundError,
3740
4519
  PoolSchema,
3741
4520
  PortableConfigSchema,
4521
+ ProfileAlreadyExistsError,
4522
+ ProfileNotFoundError,
4523
+ ProviderAlreadyExistsError,
4524
+ ProviderKindMismatchError,
4525
+ ProviderNotFoundError,
3742
4526
  ProviderSchema,
3743
4527
  ROUTED_PATH_PREFIX,
3744
4528
  UsageError,
3745
4529
  UsageSnapshotError,
3746
4530
  UsageSnapshotSchema,
3747
4531
  WhenSchema,
4532
+ addDirectoryRule,
4533
+ addIdentity,
4534
+ addPool,
4535
+ addProvider,
3748
4536
  buildEntryFacts,
3749
4537
  buildLayoutPaths,
3750
4538
  carryOver,
3751
4539
  createLeafCache,
4540
+ createProfile,
4541
+ describeProviderEndpoint,
3752
4542
  detectAmbientCredential,
3753
4543
  ensureCa,
3754
4544
  ensureFrontDoor,
@@ -3757,16 +4547,37 @@ export {
3757
4547
  formatAmbientCredentialGuardMessage,
3758
4548
  forwardableHeaders,
3759
4549
  generateCa,
4550
+ identityExists,
4551
+ isIdentityDirectoryName,
3760
4552
  isInterceptedHost,
4553
+ listDirectoryRules,
4554
+ listIdentities,
4555
+ listProfiles,
4556
+ listProviders,
3761
4557
  listUsageSnapshots,
3762
4558
  mintLeaf,
3763
4559
  parseConnectTarget,
4560
+ profileExists,
4561
+ providerExists,
4562
+ readActiveIdentity,
4563
+ readDirectoryRules,
3764
4564
  readFarmManifest,
4565
+ readGlobalConfig,
4566
+ readIdentity,
4567
+ readPools,
4568
+ readProfile,
4569
+ readProvider,
3765
4570
  readUsageSnapshot,
3766
4571
  realConnectCertStore,
3767
4572
  realConnectEffects,
3768
4573
  recoverFarm,
3769
4574
  recoveryDiagnostics,
4575
+ removeDirectoryRule,
4576
+ removeIdentity,
4577
+ removePool,
4578
+ removeProfile,
4579
+ removeProvider,
4580
+ requirePool,
3770
4581
  resolveAgentShimHome,
3771
4582
  resolveClaudeHome,
3772
4583
  resolveDecisions,
@@ -3776,7 +4587,20 @@ export {
3776
4587
  runFrontDoorSupervisor,
3777
4588
  runSupervisor,
3778
4589
  servedByPipeline,
4590
+ setAllowAmbientCredential,
4591
+ setDefaultConfigProfile,
4592
+ setGlobalDefaultProfile,
4593
+ setIdentityCredential,
4594
+ setPool,
4595
+ setProfileCategories,
4596
+ setProfileEntries,
4597
+ setProfileLaunchFlags,
4598
+ setProfileMetadata,
3779
4599
  snapshotPath,
3780
4600
  startConnectServer,
3781
- topLevelNames
4601
+ topLevelNames,
4602
+ updateDirectoryRule,
4603
+ updateProvider,
4604
+ useIdentity,
4605
+ writeDirectoryRules
3782
4606
  };