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.cjs CHANGED
@@ -42,6 +42,9 @@ __export(index_exports, {
42
42
  CredentialCacheSchema: () => CredentialCacheSchema,
43
43
  CredentialSchema: () => CredentialSchema,
44
44
  CredentialSourceSchema: () => CredentialSourceSchema,
45
+ DirectoryRuleAlreadyExistsError: () => DirectoryRuleAlreadyExistsError,
46
+ DirectoryRuleMissingTargetError: () => DirectoryRuleMissingTargetError,
47
+ DirectoryRuleNotFoundError: () => DirectoryRuleNotFoundError,
45
48
  DirectoryRuleSchema: () => DirectoryRuleSchema,
46
49
  DirectoryRulesSchema: () => DirectoryRulesSchema,
47
50
  EXIT_FAILURE: () => EXIT_FAILURE,
@@ -51,20 +54,38 @@ __export(index_exports, {
51
54
  FrontDoorStartError: () => FrontDoorStartError,
52
55
  GlobalConfigSchema: () => GlobalConfigSchema,
53
56
  HeadroomStartError: () => HeadroomStartError,
57
+ IdentityAlreadyExistsError: () => IdentityAlreadyExistsError,
58
+ IdentityNotFoundError: () => IdentityNotFoundError,
54
59
  IdentitySchema: () => IdentitySchema,
60
+ InvalidCategoryNameError: () => InvalidCategoryNameError,
61
+ InvalidIdentityNameError: () => InvalidIdentityNameError,
62
+ InvalidProviderNameError: () => InvalidProviderNameError,
63
+ LegacyProviderFileError: () => LegacyProviderFileError,
55
64
  PoolNameSchema: () => PoolNameSchema,
65
+ PoolNotFoundError: () => PoolNotFoundError,
56
66
  PoolSchema: () => PoolSchema,
57
67
  PortableConfigSchema: () => PortableConfigSchema,
68
+ ProfileAlreadyExistsError: () => ProfileAlreadyExistsError,
69
+ ProfileNotFoundError: () => ProfileNotFoundError,
70
+ ProviderAlreadyExistsError: () => ProviderAlreadyExistsError,
71
+ ProviderKindMismatchError: () => ProviderKindMismatchError,
72
+ ProviderNotFoundError: () => ProviderNotFoundError,
58
73
  ProviderSchema: () => ProviderSchema,
59
74
  ROUTED_PATH_PREFIX: () => ROUTED_PATH_PREFIX,
60
75
  UsageError: () => UsageError,
61
76
  UsageSnapshotError: () => UsageSnapshotError,
62
77
  UsageSnapshotSchema: () => UsageSnapshotSchema,
63
78
  WhenSchema: () => WhenSchema,
79
+ addDirectoryRule: () => addDirectoryRule,
80
+ addIdentity: () => addIdentity,
81
+ addPool: () => addPool,
82
+ addProvider: () => addProvider,
64
83
  buildEntryFacts: () => buildEntryFacts,
65
84
  buildLayoutPaths: () => buildLayoutPaths,
66
85
  carryOver: () => carryOver,
67
86
  createLeafCache: () => createLeafCache,
87
+ createProfile: () => createProfile,
88
+ describeProviderEndpoint: () => describeProviderEndpoint,
68
89
  detectAmbientCredential: () => detectAmbientCredential,
69
90
  ensureCa: () => ensureCa,
70
91
  ensureFrontDoor: () => ensureFrontDoor,
@@ -73,16 +94,37 @@ __export(index_exports, {
73
94
  formatAmbientCredentialGuardMessage: () => formatAmbientCredentialGuardMessage,
74
95
  forwardableHeaders: () => forwardableHeaders,
75
96
  generateCa: () => generateCa,
97
+ identityExists: () => identityExists,
98
+ isIdentityDirectoryName: () => isIdentityDirectoryName,
76
99
  isInterceptedHost: () => isInterceptedHost,
100
+ listDirectoryRules: () => listDirectoryRules,
101
+ listIdentities: () => listIdentities,
102
+ listProfiles: () => listProfiles,
103
+ listProviders: () => listProviders,
77
104
  listUsageSnapshots: () => listUsageSnapshots,
78
105
  mintLeaf: () => mintLeaf,
79
106
  parseConnectTarget: () => parseConnectTarget,
107
+ profileExists: () => profileExists,
108
+ providerExists: () => providerExists,
109
+ readActiveIdentity: () => readActiveIdentity,
110
+ readDirectoryRules: () => readDirectoryRules,
80
111
  readFarmManifest: () => readFarmManifest,
112
+ readGlobalConfig: () => readGlobalConfig,
113
+ readIdentity: () => readIdentity,
114
+ readPools: () => readPools,
115
+ readProfile: () => readProfile,
116
+ readProvider: () => readProvider,
81
117
  readUsageSnapshot: () => readUsageSnapshot,
82
118
  realConnectCertStore: () => realConnectCertStore,
83
119
  realConnectEffects: () => realConnectEffects,
84
120
  recoverFarm: () => recoverFarm,
85
121
  recoveryDiagnostics: () => recoveryDiagnostics,
122
+ removeDirectoryRule: () => removeDirectoryRule,
123
+ removeIdentity: () => removeIdentity,
124
+ removePool: () => removePool,
125
+ removeProfile: () => removeProfile,
126
+ removeProvider: () => removeProvider,
127
+ requirePool: () => requirePool,
86
128
  resolveAgentShimHome: () => resolveAgentShimHome,
87
129
  resolveClaudeHome: () => resolveClaudeHome,
88
130
  resolveDecisions: () => resolveDecisions,
@@ -92,9 +134,22 @@ __export(index_exports, {
92
134
  runFrontDoorSupervisor: () => runFrontDoorSupervisor,
93
135
  runSupervisor: () => runSupervisor,
94
136
  servedByPipeline: () => servedByPipeline,
137
+ setAllowAmbientCredential: () => setAllowAmbientCredential,
138
+ setDefaultConfigProfile: () => setDefaultConfigProfile,
139
+ setGlobalDefaultProfile: () => setGlobalDefaultProfile,
140
+ setIdentityCredential: () => setIdentityCredential,
141
+ setPool: () => setPool,
142
+ setProfileCategories: () => setProfileCategories,
143
+ setProfileEntries: () => setProfileEntries,
144
+ setProfileLaunchFlags: () => setProfileLaunchFlags,
145
+ setProfileMetadata: () => setProfileMetadata,
95
146
  snapshotPath: () => snapshotPath,
96
147
  startConnectServer: () => startConnectServer,
97
- topLevelNames: () => topLevelNames
148
+ topLevelNames: () => topLevelNames,
149
+ updateDirectoryRule: () => updateDirectoryRule,
150
+ updateProvider: () => updateProvider,
151
+ useIdentity: () => useIdentity,
152
+ writeDirectoryRules: () => writeDirectoryRules
98
153
  });
99
154
  module.exports = __toCommonJS(index_exports);
100
155
 
@@ -343,6 +398,11 @@ var SHIPPED_CATEGORY_DEFAULTS = Object.freeze({
343
398
  settings: true
344
399
  });
345
400
 
401
+ // src/config/store.ts
402
+ var import_node_fs = __toESM(require("node:fs"), 1);
403
+ var import_node_path2 = __toESM(require("node:path"), 1);
404
+ var import_node_crypto = require("node:crypto");
405
+
346
406
  // src/config/load.ts
347
407
  var import_cosmiconfig = require("cosmiconfig");
348
408
 
@@ -361,6 +421,98 @@ var UsageError = class extends CliError {
361
421
  }
362
422
  };
363
423
 
424
+ // src/config/load.ts
425
+ var ConfigValidationError = class extends CliError {
426
+ constructor(filepath, issues) {
427
+ const detail = issues.map((issue) => ` ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n");
428
+ super(`Invalid configuration in ${filepath}:
429
+ ${detail}`);
430
+ this.filepath = filepath;
431
+ this.issues = issues;
432
+ this.name = "ConfigValidationError";
433
+ }
434
+ filepath;
435
+ issues;
436
+ };
437
+
438
+ // src/config/store.ts
439
+ function isEnoent(error) {
440
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
441
+ }
442
+ var nodeStoreFs = {
443
+ readFileUtf8(filePath) {
444
+ try {
445
+ return import_node_fs.default.readFileSync(filePath, "utf8");
446
+ } catch (error) {
447
+ if (isEnoent(error)) {
448
+ return void 0;
449
+ }
450
+ throw error;
451
+ }
452
+ },
453
+ writeFileUtf8(filePath, contents) {
454
+ import_node_fs.default.writeFileSync(filePath, contents, "utf8");
455
+ },
456
+ renameSync(from, to) {
457
+ import_node_fs.default.renameSync(from, to);
458
+ },
459
+ mkdirSync(dirPath) {
460
+ import_node_fs.default.mkdirSync(dirPath, { recursive: true });
461
+ },
462
+ unlinkSync(filePath) {
463
+ try {
464
+ import_node_fs.default.unlinkSync(filePath);
465
+ } catch (error) {
466
+ if (!isEnoent(error)) {
467
+ throw error;
468
+ }
469
+ }
470
+ }
471
+ };
472
+ function readJson(filePath, schema, storeFs = nodeStoreFs) {
473
+ const raw = storeFs.readFileUtf8(filePath);
474
+ if (raw === void 0) {
475
+ return void 0;
476
+ }
477
+ const parsed = JSON.parse(raw);
478
+ const result = schema.safeParse(parsed);
479
+ if (!result.success) {
480
+ throw new ConfigValidationError(filePath, result.error.issues);
481
+ }
482
+ return result.data;
483
+ }
484
+ function writeTextAtomic(filePath, contents, storeFs = nodeStoreFs) {
485
+ const dir = import_node_path2.default.dirname(filePath);
486
+ storeFs.mkdirSync(dir);
487
+ const tempPath = import_node_path2.default.join(dir, `.${import_node_path2.default.basename(filePath)}.${String(process.pid)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
488
+ storeFs.writeFileUtf8(tempPath, contents);
489
+ try {
490
+ storeFs.renameSync(tempPath, filePath);
491
+ } catch (error) {
492
+ storeFs.unlinkSync(tempPath);
493
+ throw error;
494
+ }
495
+ }
496
+ function writeJsonAtomic(filePath, data, storeFs = nodeStoreFs) {
497
+ writeTextAtomic(filePath, `${JSON.stringify(data, null, 2)}
498
+ `, storeFs);
499
+ }
500
+ function applyPatch(filePath, schema, patch, options = {}) {
501
+ const storeFs = options.storeFs ?? nodeStoreFs;
502
+ const existing = readJson(filePath, schema, storeFs) ?? options.defaults;
503
+ if (existing === void 0) {
504
+ throw new Error(`Cannot patch ${filePath}: it does not exist yet and no defaults were provided.`);
505
+ }
506
+ const merged = { ...existing, ...patch };
507
+ const result = schema.safeParse(merged);
508
+ if (!result.success) {
509
+ throw new ConfigValidationError(filePath, result.error.issues);
510
+ }
511
+ const validated = result.data;
512
+ writeJsonAtomic(filePath, validated, storeFs);
513
+ return validated;
514
+ }
515
+
364
516
  // src/config/classify.ts
365
517
  var GLOB_METACHARACTERS = /[*?[\]{}!()]/;
366
518
  function isExactPattern(pattern) {
@@ -952,7 +1104,7 @@ function dedupeDiagnostics(diagnostics) {
952
1104
  }
953
1105
 
954
1106
  // src/resolve/plan.ts
955
- var import_node_path2 = __toESM(require("node:path"), 1);
1107
+ var import_node_path3 = __toESM(require("node:path"), 1);
956
1108
  function parentOf(rel) {
957
1109
  const index = rel.lastIndexOf("/");
958
1110
  return index === -1 ? "" : rel.slice(0, index);
@@ -1002,7 +1154,7 @@ function planFarm(params) {
1002
1154
  }
1003
1155
  return value;
1004
1156
  };
1005
- const linkTarget = (rel) => import_node_path2.default.join(params.facts.claudeHome, rel);
1157
+ const linkTarget = (rel) => import_node_path3.default.join(params.facts.claudeHome, rel);
1006
1158
  const visit = (rel) => {
1007
1159
  const fact = params.facts.entries.get(rel);
1008
1160
  const decision = params.decisions.get(rel);
@@ -1226,7 +1378,7 @@ function resolveDecisions(input) {
1226
1378
  }
1227
1379
 
1228
1380
  // src/launcher/farm.ts
1229
- var import_node_path4 = __toESM(require("node:path"), 1);
1381
+ var import_node_path5 = __toESM(require("node:path"), 1);
1230
1382
  var import_zod2 = require("zod");
1231
1383
 
1232
1384
  // src/resolve/reconcile.ts
@@ -1325,8 +1477,8 @@ var LEGACY_HOME_DIRNAME = ".claude-use";
1325
1477
  var LEGACY_FARM_MANIFEST_FILENAME = ".claude-use-farm.json";
1326
1478
 
1327
1479
  // src/launcher/lock.ts
1328
- var import_node_crypto = require("node:crypto");
1329
- var import_node_path3 = __toESM(require("node:path"), 1);
1480
+ var import_node_crypto2 = require("node:crypto");
1481
+ var import_node_path4 = __toESM(require("node:path"), 1);
1330
1482
  var DEFAULT_STALE_AFTER_MS = 12e4;
1331
1483
  var DEFAULT_RETRY_DELAY_MS = 50;
1332
1484
  var DEFAULT_MAX_ATTEMPTS = 200;
@@ -1345,7 +1497,7 @@ var IdentityLockBusyError = class extends CliError {
1345
1497
  holderPid;
1346
1498
  };
1347
1499
  function identityLockPath(dir, identity) {
1348
- return import_node_path3.default.join(dir, `.${identity}.lock`);
1500
+ return import_node_path4.default.join(dir, `.${identity}.lock`);
1349
1501
  }
1350
1502
  function parseLockRecord(raw) {
1351
1503
  if (raw === void 0) {
@@ -1374,7 +1526,7 @@ function acquireIdentityLock(params) {
1374
1526
  const staleAfterMs = params.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
1375
1527
  const retryDelayMs = params.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
1376
1528
  const maxAttempts = params.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
1377
- const token = (0, import_node_crypto.randomUUID)();
1529
+ const token = (0, import_node_crypto2.randomUUID)();
1378
1530
  if (params.fs.lstat(params.dir) === void 0) {
1379
1531
  params.fs.mkdirp(params.dir);
1380
1532
  }
@@ -1423,8 +1575,8 @@ var FarmManifestSchema = import_zod2.z.strictObject({
1423
1575
  materialised: import_zod2.z.array(import_zod2.z.string()),
1424
1576
  links: import_zod2.z.array(import_zod2.z.strictObject({ rel: import_zod2.z.string(), target: import_zod2.z.string() }))
1425
1577
  });
1426
- function readFarmManifest(fs3, farmRoot) {
1427
- const raw = fs3.readFileUtf8(import_node_path4.default.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs3.readFileUtf8(import_node_path4.default.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
1578
+ function readFarmManifest(fs7, farmRoot) {
1579
+ const raw = fs7.readFileUtf8(import_node_path5.default.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs7.readFileUtf8(import_node_path5.default.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
1428
1580
  if (raw === void 0) {
1429
1581
  return void 0;
1430
1582
  }
@@ -1440,7 +1592,7 @@ function readFarmManifest(fs3, farmRoot) {
1440
1592
  function buildEntryFacts(params) {
1441
1593
  const entries = /* @__PURE__ */ new Map();
1442
1594
  const visit = (rel) => {
1443
- const absolute = import_node_path4.default.join(params.claudeHome, rel);
1595
+ const absolute = import_node_path5.default.join(params.claudeHome, rel);
1444
1596
  const stat = params.fs.lstat(absolute);
1445
1597
  if (stat === void 0) {
1446
1598
  return void 0;
@@ -1482,9 +1634,9 @@ function buildEntryFacts(params) {
1482
1634
  entries
1483
1635
  };
1484
1636
  }
1485
- function listSubtree(fs3, root, rel) {
1486
- const absolute = import_node_path4.default.join(root, rel);
1487
- const stat = fs3.lstat(absolute);
1637
+ function listSubtree(fs7, root, rel) {
1638
+ const absolute = import_node_path5.default.join(root, rel);
1639
+ const stat = fs7.lstat(absolute);
1488
1640
  if (stat === void 0) {
1489
1641
  return [];
1490
1642
  }
@@ -1492,10 +1644,10 @@ function listSubtree(fs3, root, rel) {
1492
1644
  return [{ rel, kind: "symlink" }];
1493
1645
  }
1494
1646
  if (stat.kind === "dir") {
1495
- const children = [...fs3.readdir(absolute)].sort().flatMap((name) => listSubtree(fs3, root, `${rel}/${name}`));
1647
+ const children = [...fs7.readdir(absolute)].sort().flatMap((name) => listSubtree(fs7, root, `${rel}/${name}`));
1496
1648
  return [{ rel, kind: "dir" }, ...children];
1497
1649
  }
1498
- const contentHash = fs3.hashFile(absolute);
1650
+ const contentHash = fs7.hashFile(absolute);
1499
1651
  return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
1500
1652
  }
1501
1653
  function topLevelScopeRoots(roots) {
@@ -1509,25 +1661,25 @@ function topLevelScopeRoots(roots) {
1509
1661
  }
1510
1662
  return kept;
1511
1663
  }
1512
- function reconciliationScope(fs3, farmRoot, manifest) {
1664
+ function reconciliationScope(fs7, farmRoot, manifest) {
1513
1665
  if (manifest !== void 0) {
1514
1666
  return topLevelScopeRoots(manifest.materialised);
1515
1667
  }
1516
- return [...fs3.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs3.lstat(import_node_path4.default.join(farmRoot, name))?.kind === "dir").sort();
1668
+ return [...fs7.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs7.lstat(import_node_path5.default.join(farmRoot, name))?.kind === "dir").sort();
1517
1669
  }
1518
- function collectFarmListing(fs3, farmRoot, scopeRoots) {
1519
- return scopeRoots.flatMap((root) => listSubtree(fs3, farmRoot, root));
1670
+ function collectFarmListing(fs7, farmRoot, scopeRoots) {
1671
+ return scopeRoots.flatMap((root) => listSubtree(fs7, farmRoot, root));
1520
1672
  }
1521
- function collectCanonicalCounterparts(fs3, claudeHome, farmListing) {
1673
+ function collectCanonicalCounterparts(fs7, claudeHome, farmListing) {
1522
1674
  const out = [];
1523
1675
  for (const entry of farmListing) {
1524
- const absolute = import_node_path4.default.join(claudeHome, entry.rel);
1525
- const stat = fs3.lstat(absolute);
1676
+ const absolute = import_node_path5.default.join(claudeHome, entry.rel);
1677
+ const stat = fs7.lstat(absolute);
1526
1678
  if (stat === void 0) {
1527
1679
  continue;
1528
1680
  }
1529
1681
  if (stat.kind === "file") {
1530
- const contentHash = fs3.hashFile(absolute);
1682
+ const contentHash = fs7.hashFile(absolute);
1531
1683
  out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
1532
1684
  continue;
1533
1685
  }
@@ -1551,21 +1703,21 @@ function carryOver(params) {
1551
1703
  if (accounted.has(name)) {
1552
1704
  continue;
1553
1705
  }
1554
- const stat = params.fs.lstat(import_node_path4.default.join(params.previousRoot, name));
1706
+ const stat = params.fs.lstat(import_node_path5.default.join(params.previousRoot, name));
1555
1707
  if (stat === void 0 || stat.kind === "symlink") {
1556
1708
  continue;
1557
1709
  }
1558
- if (params.fs.lstat(import_node_path4.default.join(params.farmRoot, name)) !== void 0) {
1710
+ if (params.fs.lstat(import_node_path5.default.join(params.farmRoot, name)) !== void 0) {
1559
1711
  const category = params.classification === void 0 ? void 0 : classifyEntries([name], params.classification).classification.get(name);
1560
1712
  if (category === "runtime") {
1561
- params.fs.removeRecursive(import_node_path4.default.join(params.previousRoot, name));
1713
+ params.fs.removeRecursive(import_node_path5.default.join(params.previousRoot, name));
1562
1714
  autoResolved.push(name);
1563
1715
  continue;
1564
1716
  }
1565
1717
  collided.push(name);
1566
1718
  continue;
1567
1719
  }
1568
- params.fs.rename(import_node_path4.default.join(params.previousRoot, name), import_node_path4.default.join(params.farmRoot, name));
1720
+ params.fs.rename(import_node_path5.default.join(params.previousRoot, name), import_node_path5.default.join(params.farmRoot, name));
1569
1721
  carried.push(name);
1570
1722
  }
1571
1723
  return { carried, autoResolved, collided };
@@ -1573,9 +1725,9 @@ function carryOver(params) {
1573
1725
  function buildScratchTree(params) {
1574
1726
  params.fs.mkdirp(params.scratchRoot);
1575
1727
  for (const entry of params.plan.entries) {
1576
- const absolute = import_node_path4.default.join(params.scratchRoot, entry.rel);
1728
+ const absolute = import_node_path5.default.join(params.scratchRoot, entry.rel);
1577
1729
  if (entry.kind === "link") {
1578
- params.fs.mkdirp(import_node_path4.default.dirname(absolute));
1730
+ params.fs.mkdirp(import_node_path5.default.dirname(absolute));
1579
1731
  params.fs.symlink(entry.target, absolute);
1580
1732
  continue;
1581
1733
  }
@@ -1584,14 +1736,14 @@ function buildScratchTree(params) {
1584
1736
  }
1585
1737
  }
1586
1738
  params.fs.writeFileUtf8(
1587
- import_node_path4.default.join(params.scratchRoot, FARM_MANIFEST_FILENAME),
1739
+ import_node_path5.default.join(params.scratchRoot, FARM_MANIFEST_FILENAME),
1588
1740
  `${JSON.stringify(params.manifest, null, 2)}
1589
1741
  `
1590
1742
  );
1591
1743
  }
1592
1744
  function swapIn(params) {
1593
1745
  if (params.fs.lstat(params.farmRoot) === void 0) {
1594
- params.fs.mkdirp(import_node_path4.default.dirname(params.farmRoot));
1746
+ params.fs.mkdirp(import_node_path5.default.dirname(params.farmRoot));
1595
1747
  params.fs.rename(params.scratchRoot, params.farmRoot);
1596
1748
  return { carried: [], autoResolved: [], collided: [] };
1597
1749
  }
@@ -1615,14 +1767,14 @@ function recoverInterruptedSwap(params) {
1615
1767
  const names = [...params.fs.readdir(params.identitiesDir)].sort();
1616
1768
  const removedScratch = [];
1617
1769
  for (const name of names.filter((candidate) => candidate.startsWith(scratchPrefix))) {
1618
- params.fs.removeRecursive(import_node_path4.default.join(params.identitiesDir, name));
1770
+ params.fs.removeRecursive(import_node_path5.default.join(params.identitiesDir, name));
1619
1771
  removedScratch.push(name);
1620
1772
  }
1621
1773
  let previous = names.filter((candidate) => candidate.startsWith(previousPrefix));
1622
1774
  let restoredFrom;
1623
1775
  const first = previous[0];
1624
1776
  if (first !== void 0 && params.fs.lstat(params.farmRoot) === void 0) {
1625
- params.fs.rename(import_node_path4.default.join(params.identitiesDir, first), params.farmRoot);
1777
+ params.fs.rename(import_node_path5.default.join(params.identitiesDir, first), params.farmRoot);
1626
1778
  restoredFrom = first;
1627
1779
  previous = previous.slice(1);
1628
1780
  }
@@ -1630,7 +1782,7 @@ function recoverInterruptedSwap(params) {
1630
1782
  const autoResolved = [];
1631
1783
  const retained = [];
1632
1784
  for (const name of previous) {
1633
- const previousRoot = import_node_path4.default.join(params.identitiesDir, name);
1785
+ const previousRoot = import_node_path5.default.join(params.identitiesDir, name);
1634
1786
  const result = carryOver({
1635
1787
  fs: params.fs,
1636
1788
  previousRoot,
@@ -1655,7 +1807,7 @@ function recoverInterruptedSwap(params) {
1655
1807
  };
1656
1808
  }
1657
1809
  function recoverFarm(params) {
1658
- const farmRoot = import_node_path4.default.join(params.identitiesDir, params.identity);
1810
+ const farmRoot = import_node_path5.default.join(params.identitiesDir, params.identity);
1659
1811
  const lock = acquireIdentityLock({
1660
1812
  identity: params.identity,
1661
1813
  dir: params.identitiesDir,
@@ -1714,16 +1866,16 @@ function executeReconciliation(params) {
1714
1866
  });
1715
1867
  continue;
1716
1868
  }
1717
- const from = import_node_path4.default.join(params.farmRoot, action.rel);
1718
- const canonical = import_node_path4.default.join(params.claudeHome, action.rel);
1869
+ const from = import_node_path5.default.join(params.farmRoot, action.rel);
1870
+ const canonical = import_node_path5.default.join(params.claudeHome, action.rel);
1719
1871
  if (action.kind === "adopt") {
1720
- params.fs.mkdirp(import_node_path4.default.dirname(canonical));
1872
+ params.fs.mkdirp(import_node_path5.default.dirname(canonical));
1721
1873
  params.fs.copyRecursive(from, canonical);
1722
1874
  adopted.push(action.rel);
1723
1875
  continue;
1724
1876
  }
1725
1877
  const preserved = `${canonical}.farm-conflict-${String(params.nowMs)}`;
1726
- params.fs.mkdirp(import_node_path4.default.dirname(canonical));
1878
+ params.fs.mkdirp(import_node_path5.default.dirname(canonical));
1727
1879
  params.fs.copyRecursive(from, preserved);
1728
1880
  conflicts.push(action.rel);
1729
1881
  }
@@ -1748,7 +1900,7 @@ function farmAlreadyMatches(existing, next) {
1748
1900
  return existing.identity === next.identity && existing.claudeHome === next.claudeHome && sameStrings(existing.materialised, next.materialised) && sameLinks(existing.links, next.links);
1749
1901
  }
1750
1902
  function resyncFarm(params) {
1751
- const farmRoot = import_node_path4.default.join(params.identitiesDir, params.identity);
1903
+ const farmRoot = import_node_path5.default.join(params.identitiesDir, params.identity);
1752
1904
  const lock = acquireIdentityLock({
1753
1905
  identity: params.identity,
1754
1906
  dir: params.identitiesDir,
@@ -1827,8 +1979,8 @@ function resyncFarm(params) {
1827
1979
  diagnostics
1828
1980
  };
1829
1981
  }
1830
- const scratchRoot = import_node_path4.default.join(params.identitiesDir, `.${params.identity}.scratch.${params.uniqueSuffix}`);
1831
- const previousRoot = import_node_path4.default.join(params.identitiesDir, `.${params.identity}.previous.${params.uniqueSuffix}`);
1982
+ const scratchRoot = import_node_path5.default.join(params.identitiesDir, `.${params.identity}.scratch.${params.uniqueSuffix}`);
1983
+ const previousRoot = import_node_path5.default.join(params.identitiesDir, `.${params.identity}.previous.${params.uniqueSuffix}`);
1832
1984
  buildScratchTree({ fs: params.fs, scratchRoot, plan: resolved.farm, manifest });
1833
1985
  const swap = swapIn({ fs: params.fs, farmRoot, scratchRoot, previousRoot, classification: params.classification });
1834
1986
  if (swap.retainedPrevious !== void 0) {
@@ -1885,11 +2037,11 @@ function recoveryDiagnostics(recovery, identity) {
1885
2037
  }
1886
2038
 
1887
2039
  // src/frontdoor/connect.ts
1888
- var import_node_crypto3 = require("node:crypto");
1889
- var fs = __toESM(require("node:fs"), 1);
2040
+ var import_node_crypto4 = require("node:crypto");
2041
+ var fs2 = __toESM(require("node:fs"), 1);
1890
2042
 
1891
2043
  // src/frontdoor/x509.ts
1892
- var import_node_crypto2 = require("node:crypto");
2044
+ var import_node_crypto3 = require("node:crypto");
1893
2045
  var import_node_net = __toESM(require("node:net"), 1);
1894
2046
  var TAG_BOOLEAN = 1;
1895
2047
  var TAG_INTEGER = 2;
@@ -2041,7 +2193,7 @@ function ipBytes(address) {
2041
2193
  return bytes;
2042
2194
  }
2043
2195
  function keyIdentifier(publicKey) {
2044
- return (0, import_node_crypto2.createHash)("sha1").update(publicKey.export({ type: "pkcs1", format: "der" })).digest();
2196
+ return (0, import_node_crypto3.createHash)("sha1").update(publicKey.export({ type: "pkcs1", format: "der" })).digest();
2045
2197
  }
2046
2198
  function readElement(buffer, offset) {
2047
2199
  const tag = buffer[offset];
@@ -2087,7 +2239,7 @@ ${body.join("\n")}
2087
2239
  `;
2088
2240
  }
2089
2241
  function randomSerial() {
2090
- const bytes = (0, import_node_crypto2.randomBytes)(SERIAL_NUMBER_BYTES);
2242
+ const bytes = (0, import_node_crypto3.randomBytes)(SERIAL_NUMBER_BYTES);
2091
2243
  bytes[0] = (bytes[0] ?? 0) & BASE_128_PAYLOAD_MASK | HIGH_BIT >> 1;
2092
2244
  return bytes;
2093
2245
  }
@@ -2102,7 +2254,7 @@ function assemble(spec) {
2102
2254
  spec.subjectPublicKey.export({ type: "spki", format: "der" }),
2103
2255
  tlv(TAG_EXTENSIONS, sequence(...spec.extensions))
2104
2256
  );
2105
- const signature = (0, import_node_crypto2.sign)("sha256", tbs, spec.signingKey);
2257
+ const signature = (0, import_node_crypto3.sign)("sha256", tbs, spec.signingKey);
2106
2258
  return sequence(tbs, SIGNATURE_ALGORITHM, wholeBitString(signature));
2107
2259
  }
2108
2260
  function subjectKeyIdentifierExtension(publicKey) {
@@ -2112,7 +2264,7 @@ function basicConstraintsExtension(isCa) {
2112
2264
  return extension(OID_BASIC_CONSTRAINTS, true, sequence(...isCa ? [boolean(true)] : []));
2113
2265
  }
2114
2266
  function rsaKeyPair() {
2115
- return (0, import_node_crypto2.generateKeyPairSync)("rsa", { modulusLength: RSA_MODULUS_BITS });
2267
+ return (0, import_node_crypto3.generateKeyPairSync)("rsa", { modulusLength: RSA_MODULUS_BITS });
2116
2268
  }
2117
2269
  function keyPem(privateKey) {
2118
2270
  return privateKey.export({ type: "pkcs8", format: "pem" });
@@ -2136,8 +2288,8 @@ function issueLeafCertificate(ca, names, notBefore, notAfter) {
2136
2288
  if (commonName === void 0) {
2137
2289
  throw new Error("a leaf certificate needs at least one name");
2138
2290
  }
2139
- const caCertificate = new import_node_crypto2.X509Certificate(ca.certPem);
2140
- const caPrivateKey = (0, import_node_crypto2.createPrivateKey)(ca.keyPem);
2291
+ const caCertificate = new import_node_crypto3.X509Certificate(ca.certPem);
2292
+ const caPrivateKey = (0, import_node_crypto3.createPrivateKey)(ca.keyPem);
2141
2293
  const { publicKey, privateKey } = rsaKeyPair();
2142
2294
  const generalNames = names.map((name) => import_node_net.default.isIP(name) === 0 ? tlv(TAG_GENERAL_NAME_DNS, Buffer.from(name, "ascii")) : tlv(TAG_GENERAL_NAME_IP, ipBytes(name)));
2143
2295
  const der = assemble({
@@ -2232,7 +2384,7 @@ function capabilityFromProxyAuthorization(value) {
2232
2384
  var AUTH_HEADER = "x-agent-shim-auth";
2233
2385
 
2234
2386
  // src/frontdoor/state.ts
2235
- var import_node_path5 = __toESM(require("node:path"), 1);
2387
+ var import_node_path6 = __toESM(require("node:path"), 1);
2236
2388
  var import_zod3 = require("zod");
2237
2389
  var FRONT_DOOR_PROTOCOL = 1;
2238
2390
  var FrontDoorSessionSchema = import_zod3.z.strictObject({ pid: import_zod3.z.number().int().positive(), startedAt: import_zod3.z.number(), token: import_zod3.z.string().min(1) });
@@ -2266,10 +2418,10 @@ var FrontDoorStateSchema = import_zod3.z.strictObject({
2266
2418
  /** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
2267
2419
  lastError: import_zod3.z.string().optional()
2268
2420
  });
2269
- function readFrontDoorState(fs3, stateFile) {
2421
+ function readFrontDoorState(fs7, stateFile) {
2270
2422
  let raw;
2271
2423
  try {
2272
- raw = fs3.readFileUtf8(stateFile);
2424
+ raw = fs7.readFileUtf8(stateFile);
2273
2425
  } catch {
2274
2426
  return void 0;
2275
2427
  }
@@ -2283,28 +2435,28 @@ function readFrontDoorState(fs3, stateFile) {
2283
2435
  return void 0;
2284
2436
  }
2285
2437
  }
2286
- function writeFrontDoorState(fs3, stateFile, state) {
2287
- fs3.mkdirp(import_node_path5.default.dirname(stateFile));
2288
- fs3.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
2438
+ function writeFrontDoorState(fs7, stateFile, state) {
2439
+ fs7.mkdirp(import_node_path6.default.dirname(stateFile));
2440
+ fs7.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
2289
2441
  `);
2290
2442
  }
2291
2443
  function frontDoorOrigin(state) {
2292
2444
  const port = state?.directPort ?? state?.lastDirectPort;
2293
2445
  return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
2294
2446
  }
2295
- function writeFrontDoorSession(fs3, sessionsDir, session) {
2296
- fs3.mkdirPrivate(sessionsDir);
2297
- fs3.writeFilePrivate(import_node_path5.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
2447
+ function writeFrontDoorSession(fs7, sessionsDir, session) {
2448
+ fs7.mkdirPrivate(sessionsDir);
2449
+ fs7.writeFilePrivate(import_node_path6.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
2298
2450
  `);
2299
2451
  }
2300
- function listFrontDoorSessions(fs3, sessionsDir) {
2452
+ function listFrontDoorSessions(fs7, sessionsDir) {
2301
2453
  const sessions = [];
2302
- for (const name of fs3.readdir(sessionsDir)) {
2454
+ for (const name of fs7.readdir(sessionsDir)) {
2303
2455
  if (!name.endsWith(".json")) {
2304
2456
  continue;
2305
2457
  }
2306
2458
  try {
2307
- const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs3.readFileUtf8(import_node_path5.default.join(sessionsDir, name)) ?? ""));
2459
+ const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs7.readFileUtf8(import_node_path6.default.join(sessionsDir, name)) ?? ""));
2308
2460
  if (parsed.success) {
2309
2461
  sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
2310
2462
  }
@@ -2314,14 +2466,14 @@ function listFrontDoorSessions(fs3, sessionsDir) {
2314
2466
  }
2315
2467
  return sessions.sort((a, b) => a.pid - b.pid);
2316
2468
  }
2317
- function removeFrontDoorSession(fs3, sessionsDir, pid) {
2318
- fs3.removeRecursive(import_node_path5.default.join(sessionsDir, `${String(pid)}.json`));
2469
+ function removeFrontDoorSession(fs7, sessionsDir, pid) {
2470
+ fs7.removeRecursive(import_node_path6.default.join(sessionsDir, `${String(pid)}.json`));
2319
2471
  }
2320
- function pruneDeadFrontDoorSessions(fs3, sessionsDir, isRunning) {
2472
+ function pruneDeadFrontDoorSessions(fs7, sessionsDir, isRunning) {
2321
2473
  const removed = [];
2322
- for (const session of listFrontDoorSessions(fs3, sessionsDir)) {
2474
+ for (const session of listFrontDoorSessions(fs7, sessionsDir)) {
2323
2475
  if (!isRunning(session.pid)) {
2324
- removeFrontDoorSession(fs3, sessionsDir, session.pid);
2476
+ removeFrontDoorSession(fs7, sessionsDir, session.pid);
2325
2477
  removed.push(session.pid);
2326
2478
  }
2327
2479
  }
@@ -2333,8 +2485,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
2333
2485
  var FRONTDOOR_POLL_MS = 1e3;
2334
2486
  var MS_PER_MINUTE = 6e4;
2335
2487
  async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
2336
- const { fs: fs3, paths } = ports;
2337
- const previous = readFrontDoorState(fs3, paths.frontdoorStateFile);
2488
+ const { fs: fs7, paths } = ports;
2489
+ const previous = readFrontDoorState(fs7, paths.frontdoorStateFile);
2338
2490
  let lastPort = previous?.lastPort;
2339
2491
  let lastConnectPort = previous?.lastConnectPort;
2340
2492
  let lastDirectPort = previous?.lastDirectPort;
@@ -2344,12 +2496,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2344
2496
  ...lastDirectPort === void 0 ? {} : { lastDirectPort }
2345
2497
  });
2346
2498
  const fail = (message) => {
2347
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { lastError: message, ...sticky() });
2348
- fs3.removeRecursive(paths.frontdoorLockFile);
2499
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { lastError: message, ...sticky() });
2500
+ fs7.removeRecursive(paths.frontdoorLockFile);
2349
2501
  ports.log(`agent-shim frontdoor supervisor: ${message}`);
2350
2502
  return 1;
2351
2503
  };
2352
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
2504
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
2353
2505
  const previousSticky = lastPort;
2354
2506
  let http2;
2355
2507
  try {
@@ -2386,8 +2538,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2386
2538
  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)`);
2387
2539
  }
2388
2540
  lastDirectPort = direct.port;
2389
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
2390
- fs3.removeRecursive(paths.frontdoorLockFile);
2541
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
2542
+ fs7.removeRecursive(paths.frontdoorLockFile);
2391
2543
  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)}`);
2392
2544
  let idleSince;
2393
2545
  let ticks = 0;
@@ -2396,15 +2548,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2396
2548
  return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
2397
2549
  }
2398
2550
  ticks += 1;
2399
- pruneDeadFrontDoorSessions(fs3, paths.frontdoorSessionsDir, ports.isRunning);
2400
- if (listFrontDoorSessions(fs3, paths.frontdoorSessionsDir).length === 0) {
2551
+ pruneDeadFrontDoorSessions(fs7, paths.frontdoorSessionsDir, ports.isRunning);
2552
+ if (listFrontDoorSessions(fs7, paths.frontdoorSessionsDir).length === 0) {
2401
2553
  idleSince ??= ports.now();
2402
2554
  if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
2403
2555
  ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
2404
2556
  await direct.close();
2405
2557
  await connect3.close();
2406
2558
  await http2.close();
2407
- writeFrontDoorState(fs3, paths.frontdoorStateFile, sticky());
2559
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, sticky());
2408
2560
  return 0;
2409
2561
  }
2410
2562
  } else {
@@ -2459,8 +2611,8 @@ function parseConnectTarget(authority) {
2459
2611
  function isInterceptedHost(host, interceptHosts) {
2460
2612
  return interceptHosts.includes(host);
2461
2613
  }
2462
- function servedByPipeline(path9) {
2463
- return path9?.startsWith(ROUTED_PATH_PREFIX) === true;
2614
+ function servedByPipeline(path13) {
2615
+ return path13?.startsWith(ROUTED_PATH_PREFIX) === true;
2464
2616
  }
2465
2617
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
2466
2618
  "connection",
@@ -2519,8 +2671,8 @@ function ensureCa(store, generate) {
2519
2671
  }
2520
2672
  function caParses(ca) {
2521
2673
  try {
2522
- new import_node_crypto3.X509Certificate(ca.certPem);
2523
- (0, import_node_crypto3.createPrivateKey)(ca.keyPem);
2674
+ new import_node_crypto4.X509Certificate(ca.certPem);
2675
+ (0, import_node_crypto4.createPrivateKey)(ca.keyPem);
2524
2676
  return true;
2525
2677
  } catch {
2526
2678
  return false;
@@ -2758,15 +2910,15 @@ async function blindTunnel(socket, target, effects) {
2758
2910
  socket.destroy();
2759
2911
  });
2760
2912
  }
2761
- function isEnoent(error) {
2913
+ function isEnoent2(error) {
2762
2914
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2763
2915
  }
2764
2916
  function realConnectCertStore(paths) {
2765
2917
  const read = (file) => {
2766
2918
  try {
2767
- return fs.readFileSync(file, "utf8");
2919
+ return fs2.readFileSync(file, "utf8");
2768
2920
  } catch (error) {
2769
- if (isEnoent(error)) {
2921
+ if (isEnoent2(error)) {
2770
2922
  return void 0;
2771
2923
  }
2772
2924
  throw error;
@@ -2779,10 +2931,10 @@ function realConnectCertStore(paths) {
2779
2931
  return certPem === void 0 || keyPem2 === void 0 ? void 0 : { certPem, keyPem: keyPem2 };
2780
2932
  },
2781
2933
  writeCa: (ca) => {
2782
- fs.mkdirSync(paths.frontdoorCaDir, { recursive: true });
2783
- fs.writeFileSync(paths.frontdoorCaCertFile, ca.certPem, "utf8");
2784
- fs.writeFileSync(paths.frontdoorCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
2785
- fs.chmodSync(paths.frontdoorCaKeyFile, CA_KEY_FILE_MODE);
2934
+ fs2.mkdirSync(paths.frontdoorCaDir, { recursive: true });
2935
+ fs2.writeFileSync(paths.frontdoorCaCertFile, ca.certPem, "utf8");
2936
+ fs2.writeFileSync(paths.frontdoorCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
2937
+ fs2.chmodSync(paths.frontdoorCaKeyFile, CA_KEY_FILE_MODE);
2786
2938
  }
2787
2939
  };
2788
2940
  }
@@ -2951,11 +3103,11 @@ function realConnectEffects() {
2951
3103
  }
2952
3104
 
2953
3105
  // src/frontdoor/ensure.ts
2954
- var import_node_crypto5 = require("node:crypto");
3106
+ var import_node_crypto6 = require("node:crypto");
2955
3107
 
2956
3108
  // src/headroom/state.ts
2957
- var import_node_crypto4 = require("node:crypto");
2958
- var import_node_path6 = __toESM(require("node:path"), 1);
3109
+ var import_node_crypto5 = require("node:crypto");
3110
+ var import_node_path7 = __toESM(require("node:path"), 1);
2959
3111
  var import_zod4 = require("zod");
2960
3112
  var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
2961
3113
  var HeadroomStateSchema = import_zod4.z.strictObject({
@@ -2989,16 +3141,16 @@ function parseJson(raw, schema) {
2989
3141
  const parsed = schema.safeParse(JSON.parse(raw));
2990
3142
  return parsed.success ? parsed.data : void 0;
2991
3143
  }
2992
- function readHeadroomState(fs3, stateFile) {
3144
+ function readHeadroomState(fs7, stateFile) {
2993
3145
  try {
2994
- return parseJson(fs3.readFileUtf8(stateFile), HeadroomStateSchema);
3146
+ return parseJson(fs7.readFileUtf8(stateFile), HeadroomStateSchema);
2995
3147
  } catch {
2996
3148
  return void 0;
2997
3149
  }
2998
3150
  }
2999
- function writeHeadroomState(fs3, stateFile, state) {
3000
- fs3.mkdirp(import_node_path6.default.dirname(stateFile));
3001
- fs3.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
3151
+ function writeHeadroomState(fs7, stateFile, state) {
3152
+ fs7.mkdirp(import_node_path7.default.dirname(stateFile));
3153
+ fs7.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
3002
3154
  `);
3003
3155
  }
3004
3156
  function headroomAllowlist(providers) {
@@ -3016,16 +3168,16 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
3016
3168
  return upstreams;
3017
3169
  }
3018
3170
  function hashAllowlist(allowlist) {
3019
- return (0, import_node_crypto4.createHash)("sha256").update(allowlist.join("\n")).digest("hex");
3171
+ return (0, import_node_crypto5.createHash)("sha256").update(allowlist.join("\n")).digest("hex");
3020
3172
  }
3021
- function readAllProviders(fs3, providersDir) {
3173
+ function readAllProviders(fs7, providersDir) {
3022
3174
  const result = [];
3023
- for (const name of fs3.readdir(providersDir)) {
3175
+ for (const name of fs7.readdir(providersDir)) {
3024
3176
  if (!name.endsWith(".json")) {
3025
3177
  continue;
3026
3178
  }
3027
3179
  try {
3028
- const parsed = parseJson(fs3.readFileUtf8(import_node_path6.default.join(providersDir, name)), ProviderSchema);
3180
+ const parsed = parseJson(fs7.readFileUtf8(import_node_path7.default.join(providersDir, name)), ProviderSchema);
3029
3181
  if (parsed !== void 0) {
3030
3182
  result.push({ name: name.slice(0, -".json".length), provider: parsed });
3031
3183
  }
@@ -3035,22 +3187,22 @@ function readAllProviders(fs3, providersDir) {
3035
3187
  }
3036
3188
  return result;
3037
3189
  }
3038
- function writeSession(fs3, sessionsDir, session) {
3039
- fs3.mkdirp(sessionsDir);
3040
- fs3.writeFileUtf8(import_node_path6.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
3190
+ function writeSession(fs7, sessionsDir, session) {
3191
+ fs7.mkdirp(sessionsDir);
3192
+ fs7.writeFileUtf8(import_node_path7.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
3041
3193
  `);
3042
3194
  }
3043
- function removeSession(fs3, sessionsDir, pid) {
3044
- fs3.removeRecursive(import_node_path6.default.join(sessionsDir, `${String(pid)}.json`));
3195
+ function removeSession(fs7, sessionsDir, pid) {
3196
+ fs7.removeRecursive(import_node_path7.default.join(sessionsDir, `${String(pid)}.json`));
3045
3197
  }
3046
- function listSessions(fs3, sessionsDir) {
3198
+ function listSessions(fs7, sessionsDir) {
3047
3199
  const sessions = [];
3048
- for (const name of fs3.readdir(sessionsDir)) {
3200
+ for (const name of fs7.readdir(sessionsDir)) {
3049
3201
  if (!name.endsWith(".json")) {
3050
3202
  continue;
3051
3203
  }
3052
3204
  try {
3053
- const parsed = parseJson(fs3.readFileUtf8(import_node_path6.default.join(sessionsDir, name)), HeadroomSessionSchema);
3205
+ const parsed = parseJson(fs7.readFileUtf8(import_node_path7.default.join(sessionsDir, name)), HeadroomSessionSchema);
3054
3206
  if (parsed !== void 0) {
3055
3207
  sessions.push(parsed);
3056
3208
  }
@@ -3060,22 +3212,22 @@ function listSessions(fs3, sessionsDir) {
3060
3212
  }
3061
3213
  return sessions.sort((a, b) => a.pid - b.pid);
3062
3214
  }
3063
- function sessionsForSupervisor(fs3, sessionsDir, supervisorPid) {
3064
- return listSessions(fs3, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
3215
+ function sessionsForSupervisor(fs7, sessionsDir, supervisorPid) {
3216
+ return listSessions(fs7, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
3065
3217
  }
3066
- function pruneDeadSessions(fs3, sessionsDir, isRunning) {
3218
+ function pruneDeadSessions(fs7, sessionsDir, isRunning) {
3067
3219
  const removed = [];
3068
- for (const session of listSessions(fs3, sessionsDir)) {
3220
+ for (const session of listSessions(fs7, sessionsDir)) {
3069
3221
  if (!isRunning(session.pid)) {
3070
- removeSession(fs3, sessionsDir, session.pid);
3222
+ removeSession(fs7, sessionsDir, session.pid);
3071
3223
  removed.push(session.pid);
3072
3224
  }
3073
3225
  }
3074
3226
  return removed;
3075
3227
  }
3076
- function readStartLock(fs3, lockFile) {
3228
+ function readStartLock(fs7, lockFile) {
3077
3229
  try {
3078
- return parseJson(fs3.readFileUtf8(lockFile), HeadroomLockSchema);
3230
+ return parseJson(fs7.readFileUtf8(lockFile), HeadroomLockSchema);
3079
3231
  } catch {
3080
3232
  return void 0;
3081
3233
  }
@@ -3115,7 +3267,7 @@ function ensureFrontDoor(params) {
3115
3267
  continue;
3116
3268
  }
3117
3269
  if (verdict.ok) {
3118
- const token = (0, import_node_crypto5.randomUUID)();
3270
+ const token = (0, import_node_crypto6.randomUUID)();
3119
3271
  writeFrontDoorSession(ports.fs, paths.frontdoorSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), token });
3120
3272
  return { port: state.port, connectPort: state.connectPort, token };
3121
3273
  }
@@ -3155,7 +3307,7 @@ function ensureFrontDoor(params) {
3155
3307
  }
3156
3308
 
3157
3309
  // src/usage/read.ts
3158
- var import_node_path7 = __toESM(require("node:path"), 1);
3310
+ var import_node_path8 = __toESM(require("node:path"), 1);
3159
3311
 
3160
3312
  // src/usage/schema.ts
3161
3313
  var import_zod5 = require("zod");
@@ -3322,11 +3474,11 @@ function snapshotPath(snapshotsDir, identity) {
3322
3474
  if (!isIdentityName(identity)) {
3323
3475
  throw new Error(`"${identity}" is not a valid identity name.`);
3324
3476
  }
3325
- return import_node_path7.default.join(snapshotsDir, `${identity}.json`);
3477
+ return import_node_path8.default.join(snapshotsDir, `${identity}.json`);
3326
3478
  }
3327
- function readUsageSnapshot(fs3, snapshotsDir, identity) {
3479
+ function readUsageSnapshot(fs7, snapshotsDir, identity) {
3328
3480
  const file = snapshotPath(snapshotsDir, identity);
3329
- const raw = fs3.readFileUtf8(file);
3481
+ const raw = fs7.readFileUtf8(file);
3330
3482
  if (raw === void 0) {
3331
3483
  return void 0;
3332
3484
  }
@@ -3342,8 +3494,8 @@ function readUsageSnapshot(fs3, snapshotsDir, identity) {
3342
3494
  }
3343
3495
  return snapshot.data;
3344
3496
  }
3345
- function listUsageSnapshots(fs3, snapshotsDir) {
3346
- return fs3.readdir(snapshotsDir).flatMap((name) => {
3497
+ function listUsageSnapshots(fs7, snapshotsDir) {
3498
+ return fs7.readdir(snapshotsDir).flatMap((name) => {
3347
3499
  if (!name.endsWith(".json")) {
3348
3500
  return [];
3349
3501
  }
@@ -3351,7 +3503,7 @@ function listUsageSnapshots(fs3, snapshotsDir) {
3351
3503
  if (!isIdentityName(identity)) {
3352
3504
  return [];
3353
3505
  }
3354
- const snapshot = readUsageSnapshot(fs3, snapshotsDir, identity);
3506
+ const snapshot = readUsageSnapshot(fs7, snapshotsDir, identity);
3355
3507
  return snapshot === void 0 ? [] : [snapshot];
3356
3508
  }).sort((left, right) => left.identity.localeCompare(right.identity));
3357
3509
  }
@@ -3411,7 +3563,7 @@ function ensureHeadroom(params) {
3411
3563
  }
3412
3564
 
3413
3565
  // src/headroom/settings.ts
3414
- var import_node_crypto6 = require("node:crypto");
3566
+ var import_node_crypto7 = require("node:crypto");
3415
3567
  function settingsOf(config) {
3416
3568
  return {
3417
3569
  ...config.mode === void 0 ? {} : { mode: config.mode },
@@ -3436,7 +3588,7 @@ function settingsEnv(settings) {
3436
3588
  return settings.rolloutChannel === void 0 ? {} : { HEADROOM_ROLLOUT_CHANNEL: settings.rolloutChannel };
3437
3589
  }
3438
3590
  function hashSettings(settings) {
3439
- return (0, import_node_crypto6.createHash)("sha256").update(JSON.stringify([...settingsArgs(settings), JSON.stringify(settingsEnv(settings))])).digest("hex");
3591
+ return (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify([...settingsArgs(settings), JSON.stringify(settingsEnv(settings))])).digest("hex");
3440
3592
  }
3441
3593
 
3442
3594
  // src/headroom/source.ts
@@ -3518,9 +3670,9 @@ function compareVersions(a, b) {
3518
3670
  }
3519
3671
  var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
3520
3672
  async function runSupervisor(initialConfig, ports, options = {}) {
3521
- const { fs: fs3, paths } = ports;
3673
+ const { fs: fs7, paths } = ports;
3522
3674
  let config = initialConfig;
3523
- const previousState = readHeadroomState(fs3, paths.headroomStateFile);
3675
+ const previousState = readHeadroomState(fs7, paths.headroomStateFile);
3524
3676
  if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
3525
3677
  ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
3526
3678
  return 0;
@@ -3529,19 +3681,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3529
3681
  let lastPort = previousState?.lastPort;
3530
3682
  const sticky = () => lastPort === void 0 ? {} : { lastPort };
3531
3683
  const ownsState = () => {
3532
- const owner = readHeadroomState(fs3, paths.headroomStateFile)?.supervisorPid;
3684
+ const owner = readHeadroomState(fs7, paths.headroomStateFile)?.supervisorPid;
3533
3685
  return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
3534
3686
  };
3535
3687
  const writeOwnState = (state) => {
3536
3688
  if (!ownsState()) {
3537
3689
  return false;
3538
3690
  }
3539
- writeHeadroomState(fs3, paths.headroomStateFile, state);
3691
+ writeHeadroomState(fs7, paths.headroomStateFile, state);
3540
3692
  return true;
3541
3693
  };
3542
3694
  const fail = (message) => {
3543
3695
  if (writeOwnState({ lastError: message, ...sticky() })) {
3544
- fs3.removeRecursive(paths.headroomLockFile);
3696
+ fs7.removeRecursive(paths.headroomLockFile);
3545
3697
  }
3546
3698
  ports.log(`agent-shim headroom supervisor: ${message}`);
3547
3699
  return 1;
@@ -3590,7 +3742,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3590
3742
  const settingsHash = hashSettings(config.settings);
3591
3743
  const allowlist = allowlistOf(ports);
3592
3744
  const allowlistHash = hashAllowlist(allowlist);
3593
- pruneDeadSessions(fs3, paths.headroomSessionsDir, ports.isRunning);
3745
+ pruneDeadSessions(fs7, paths.headroomSessionsDir, ports.isRunning);
3594
3746
  const ownsNow = ownsState();
3595
3747
  if (!ownsNow && !superseded) {
3596
3748
  ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
@@ -3642,7 +3794,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3642
3794
  ...sticky()
3643
3795
  });
3644
3796
  if (recorded) {
3645
- fs3.removeRecursive(paths.headroomLockFile);
3797
+ fs7.removeRecursive(paths.headroomLockFile);
3646
3798
  }
3647
3799
  idleSince = void 0;
3648
3800
  ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
@@ -3657,7 +3809,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3657
3809
  }
3658
3810
  } else {
3659
3811
  if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
3660
- if (sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3812
+ if (sessionsForSupervisor(fs7, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3661
3813
  ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
3662
3814
  ports.stopProcess(headroomPid);
3663
3815
  headroomPid = void 0;
@@ -3666,7 +3818,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3666
3818
  continue;
3667
3819
  }
3668
3820
  }
3669
- const sessions = sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid);
3821
+ const sessions = sessionsForSupervisor(fs7, paths.headroomSessionsDir, ports.ownPid);
3670
3822
  if (sessions.length === 0) {
3671
3823
  idleSince ??= ports.now();
3672
3824
  const idleMs = ports.now() - idleSince;
@@ -3688,9 +3840,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3688
3840
  await ports.sleep(HEADROOM_POLL_MS);
3689
3841
  }
3690
3842
  }
3691
- function currentHeadroomAllowlist(fs3, paths) {
3692
- const providers = readAllProviders(fs3, paths.providersDir).map((entry) => entry.provider);
3693
- return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs3, paths.frontdoorStateFile))));
3843
+ function currentHeadroomAllowlist(fs7, paths) {
3844
+ const providers = readAllProviders(fs7, paths.providersDir).map((entry) => entry.provider);
3845
+ return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs7, paths.frontdoorStateFile))));
3694
3846
  }
3695
3847
  function allowlistOf(ports) {
3696
3848
  return currentHeadroomAllowlist(ports.fs, ports.paths);
@@ -3756,63 +3908,735 @@ function evaluateAmbientCredentialGuard(params) {
3756
3908
  }
3757
3909
 
3758
3910
  // src/paths.ts
3759
- var import_node_fs = __toESM(require("node:fs"), 1);
3911
+ var import_node_fs2 = __toESM(require("node:fs"), 1);
3760
3912
  var import_node_os = __toESM(require("node:os"), 1);
3761
- var import_node_path8 = __toESM(require("node:path"), 1);
3913
+ var import_node_path9 = __toESM(require("node:path"), 1);
3762
3914
  function resolveAgentShimHome(env = process.env, home = import_node_os.default.homedir(), isDirectory = isExistingDirectory) {
3763
3915
  const fromEnv = env.AGENT_SHIM_HOME;
3764
3916
  if (fromEnv !== void 0 && fromEnv !== "") {
3765
3917
  return fromEnv;
3766
3918
  }
3767
- const current = import_node_path8.default.join(home, HOME_DIRNAME);
3768
- const legacy = import_node_path8.default.join(home, LEGACY_HOME_DIRNAME);
3919
+ const current = import_node_path9.default.join(home, HOME_DIRNAME);
3920
+ const legacy = import_node_path9.default.join(home, LEGACY_HOME_DIRNAME);
3769
3921
  return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
3770
3922
  }
3771
3923
  function isExistingDirectory(candidate) {
3772
- return import_node_fs.default.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
3924
+ return import_node_fs2.default.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
3773
3925
  }
3774
3926
  function resolveClaudeHome() {
3775
3927
  const fromEnv = process.env.AGENT_SHIM_CLAUDE_HOME;
3776
3928
  if (fromEnv !== void 0 && fromEnv !== "") {
3777
3929
  return fromEnv;
3778
3930
  }
3779
- return import_node_path8.default.join(import_node_os.default.homedir(), ".claude");
3931
+ return import_node_path9.default.join(import_node_os.default.homedir(), ".claude");
3780
3932
  }
3781
3933
  function buildLayoutPaths(root) {
3782
3934
  return {
3783
3935
  root,
3784
- identitiesDir: import_node_path8.default.join(root, "identities"),
3785
- configProfilesDir: import_node_path8.default.join(root, "config-profiles"),
3786
- providersDir: import_node_path8.default.join(root, "providers"),
3787
- directoryRulesFile: import_node_path8.default.join(root, "directory-rules.json"),
3788
- activeIdentityFile: import_node_path8.default.join(root, "active-identity"),
3789
- globalConfigFile: import_node_path8.default.join(root, "config.json"),
3790
- categoriesLocalFile: import_node_path8.default.join(root, "categories.local.json"),
3791
- claudeShimFile: import_node_path8.default.join(root, "claude-shim.json"),
3792
- headroomDir: import_node_path8.default.join(root, "headroom"),
3793
- headroomStateFile: import_node_path8.default.join(root, "headroom", "state.json"),
3794
- headroomLockFile: import_node_path8.default.join(root, "headroom", "start.lock"),
3795
- headroomSessionsDir: import_node_path8.default.join(root, "headroom", "sessions"),
3796
- logsDir: import_node_path8.default.join(root, "logs"),
3797
- headroomLogPath: import_node_path8.default.join(root, "logs", "headroom.log"),
3798
- frontdoorDir: import_node_path8.default.join(root, "frontdoor"),
3799
- frontdoorCaDir: import_node_path8.default.join(root, "frontdoor", "ca"),
3800
- frontdoorCaCertFile: import_node_path8.default.join(root, "frontdoor", "ca", "ca.pem"),
3801
- frontdoorCaKeyFile: import_node_path8.default.join(root, "frontdoor", "ca", "ca.key"),
3802
- frontdoorCaBundlesDir: import_node_path8.default.join(root, "frontdoor", "ca", "bundles"),
3803
- frontdoorStateFile: import_node_path8.default.join(root, "frontdoor", "state.json"),
3804
- frontdoorLockFile: import_node_path8.default.join(root, "frontdoor", "start.lock"),
3805
- frontdoorSessionsDir: import_node_path8.default.join(root, "frontdoor", "sessions"),
3806
- frontdoorLogPath: import_node_path8.default.join(root, "logs", "frontdoor.log"),
3807
- usageDir: import_node_path8.default.join(root, "usage"),
3808
- usageLogDir: import_node_path8.default.join(root, "usage", "log"),
3809
- usageSnapshotsDir: import_node_path8.default.join(root, "usage", "snapshots"),
3810
- usagePicksFile: import_node_path8.default.join(root, "usage", "picks.json")
3936
+ identitiesDir: import_node_path9.default.join(root, "identities"),
3937
+ configProfilesDir: import_node_path9.default.join(root, "config-profiles"),
3938
+ providersDir: import_node_path9.default.join(root, "providers"),
3939
+ directoryRulesFile: import_node_path9.default.join(root, "directory-rules.json"),
3940
+ activeIdentityFile: import_node_path9.default.join(root, "active-identity"),
3941
+ globalConfigFile: import_node_path9.default.join(root, "config.json"),
3942
+ categoriesLocalFile: import_node_path9.default.join(root, "categories.local.json"),
3943
+ claudeShimFile: import_node_path9.default.join(root, "claude-shim.json"),
3944
+ headroomDir: import_node_path9.default.join(root, "headroom"),
3945
+ headroomStateFile: import_node_path9.default.join(root, "headroom", "state.json"),
3946
+ headroomLockFile: import_node_path9.default.join(root, "headroom", "start.lock"),
3947
+ headroomSessionsDir: import_node_path9.default.join(root, "headroom", "sessions"),
3948
+ logsDir: import_node_path9.default.join(root, "logs"),
3949
+ headroomLogPath: import_node_path9.default.join(root, "logs", "headroom.log"),
3950
+ frontdoorDir: import_node_path9.default.join(root, "frontdoor"),
3951
+ frontdoorCaDir: import_node_path9.default.join(root, "frontdoor", "ca"),
3952
+ frontdoorCaCertFile: import_node_path9.default.join(root, "frontdoor", "ca", "ca.pem"),
3953
+ frontdoorCaKeyFile: import_node_path9.default.join(root, "frontdoor", "ca", "ca.key"),
3954
+ frontdoorCaBundlesDir: import_node_path9.default.join(root, "frontdoor", "ca", "bundles"),
3955
+ frontdoorStateFile: import_node_path9.default.join(root, "frontdoor", "state.json"),
3956
+ frontdoorLockFile: import_node_path9.default.join(root, "frontdoor", "start.lock"),
3957
+ frontdoorSessionsDir: import_node_path9.default.join(root, "frontdoor", "sessions"),
3958
+ frontdoorLogPath: import_node_path9.default.join(root, "logs", "frontdoor.log"),
3959
+ usageDir: import_node_path9.default.join(root, "usage"),
3960
+ usageLogDir: import_node_path9.default.join(root, "usage", "log"),
3961
+ usageSnapshotsDir: import_node_path9.default.join(root, "usage", "snapshots"),
3962
+ usagePicksFile: import_node_path9.default.join(root, "usage", "picks.json")
3811
3963
  };
3812
3964
  }
3813
3965
  function resolveLayoutPaths() {
3814
3966
  return buildLayoutPaths(resolveAgentShimHome());
3815
3967
  }
3968
+
3969
+ // src/identityStore.ts
3970
+ var import_node_fs4 = __toESM(require("node:fs"), 1);
3971
+ var import_node_path11 = __toESM(require("node:path"), 1);
3972
+
3973
+ // src/launcher/identity.ts
3974
+ function poolNameOf(selector) {
3975
+ return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
3976
+ }
3977
+
3978
+ // src/configProfilesStore.ts
3979
+ var import_node_fs3 = __toESM(require("node:fs"), 1);
3980
+ var import_node_path10 = __toESM(require("node:path"), 1);
3981
+ var ProfileNotFoundError = class extends CliError {
3982
+ constructor(profileName) {
3983
+ super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
3984
+ this.profileName = profileName;
3985
+ this.name = "ProfileNotFoundError";
3986
+ }
3987
+ profileName;
3988
+ };
3989
+ var ProfileAlreadyExistsError = class extends CliError {
3990
+ constructor(profileName) {
3991
+ super(`A configuration profile named "${profileName}" already exists.`);
3992
+ this.profileName = profileName;
3993
+ this.name = "ProfileAlreadyExistsError";
3994
+ }
3995
+ profileName;
3996
+ };
3997
+ var InvalidCategoryNameError = class extends CliError {
3998
+ constructor(categoryName) {
3999
+ super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
4000
+ this.categoryName = categoryName;
4001
+ this.name = "InvalidCategoryNameError";
4002
+ }
4003
+ categoryName;
4004
+ };
4005
+ function profileJsonPath(paths, name) {
4006
+ return import_node_path10.default.join(paths.configProfilesDir, `${name}.json`);
4007
+ }
4008
+ function profileExists(paths, name) {
4009
+ return import_node_fs3.default.existsSync(profileJsonPath(paths, name));
4010
+ }
4011
+ function requireProfileExists(paths, name) {
4012
+ if (!profileExists(paths, name)) {
4013
+ throw new ProfileNotFoundError(name);
4014
+ }
4015
+ }
4016
+ function readProfile(paths, name) {
4017
+ return readJson(profileJsonPath(paths, name), ConfigProfileSchema);
4018
+ }
4019
+ function createProfile(paths, name, extendsList, description) {
4020
+ if (profileExists(paths, name)) {
4021
+ throw new ProfileAlreadyExistsError(name);
4022
+ }
4023
+ const filePath = profileJsonPath(paths, name);
4024
+ const parsed = ConfigProfileSchema.safeParse({
4025
+ ...description === void 0 ? {} : { description },
4026
+ ...extendsList !== void 0 && extendsList.length > 0 ? { extends: [...extendsList] } : {}
4027
+ });
4028
+ if (!parsed.success) {
4029
+ throw new ConfigValidationError(filePath, parsed.error.issues);
4030
+ }
4031
+ writeJsonAtomic(filePath, parsed.data);
4032
+ return parsed.data;
4033
+ }
4034
+ function listProfiles(paths) {
4035
+ if (!import_node_fs3.default.existsSync(paths.configProfilesDir)) {
4036
+ return [];
4037
+ }
4038
+ const names = import_node_fs3.default.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort();
4039
+ const result = [];
4040
+ for (const name of names) {
4041
+ const profile = readProfile(paths, name);
4042
+ if (profile !== void 0) {
4043
+ result.push({ name, profile });
4044
+ }
4045
+ }
4046
+ return result;
4047
+ }
4048
+ function globalConfigPath(paths) {
4049
+ return paths.globalConfigFile;
4050
+ }
4051
+ function readGlobalConfig(paths) {
4052
+ return readJson(globalConfigPath(paths), GlobalConfigSchema);
4053
+ }
4054
+ function setGlobalDefaultProfile(paths, name) {
4055
+ return applyPatch(
4056
+ globalConfigPath(paths),
4057
+ GlobalConfigSchema,
4058
+ { defaultConfigProfile: name },
4059
+ { defaults: {} }
4060
+ );
4061
+ }
4062
+ function validateCategoryNames(patch) {
4063
+ for (const key of Object.keys(patch)) {
4064
+ if (!isOverridableCategory(key)) {
4065
+ throw new InvalidCategoryNameError(key);
4066
+ }
4067
+ }
4068
+ }
4069
+ function setProfileCategories(paths, name, patch) {
4070
+ requireProfileExists(paths, name);
4071
+ const expandedPatch = expandAllCategoryKey(patch);
4072
+ validateCategoryNames(expandedPatch);
4073
+ const existing = readProfile(paths, name) ?? {};
4074
+ const mergedCategories = { ...existing.categories, ...expandedPatch };
4075
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { categories: mergedCategories });
4076
+ }
4077
+ function setProfileEntries(paths, name, patch) {
4078
+ requireProfileExists(paths, name);
4079
+ const existing = readProfile(paths, name) ?? {};
4080
+ const mergedEntries = { ...existing.entries, ...patch };
4081
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { entries: mergedEntries });
4082
+ }
4083
+ function setProfileLaunchFlags(paths, name, patch) {
4084
+ requireProfileExists(paths, name);
4085
+ const existing = readProfile(paths, name) ?? {};
4086
+ const mergedLaunch = { ...existing.launch, ...patch };
4087
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
4088
+ // Read as `unknown` so the check sees the `undefined` a cleared key holds, which LaunchFlags' optional properties hide from the value type.
4089
+ launch: Object.values(mergedLaunch).every((value) => value === void 0) ? void 0 : mergedLaunch
4090
+ });
4091
+ }
4092
+ function setProfileMetadata(paths, name, patch) {
4093
+ requireProfileExists(paths, name);
4094
+ return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
4095
+ ...patch.extends === void 0 ? {} : { extends: patch.extends.length === 0 ? void 0 : [...patch.extends] },
4096
+ ...patch.description === void 0 ? {} : { description: patch.description === false ? void 0 : patch.description }
4097
+ });
4098
+ }
4099
+ function removeProfile(paths, name) {
4100
+ requireProfileExists(paths, name);
4101
+ import_node_fs3.default.rmSync(profileJsonPath(paths, name));
4102
+ }
4103
+
4104
+ // src/poolStore.ts
4105
+ var PoolNotFoundError = class extends CliError {
4106
+ constructor(poolName) {
4107
+ super(`No pool named "${poolName}". Run \`agent-shim pool add ${poolName} --identity <name>...\` first.`);
4108
+ this.poolName = poolName;
4109
+ this.name = "PoolNotFoundError";
4110
+ }
4111
+ poolName;
4112
+ };
4113
+ var PoolAlreadyExistsError = class extends CliError {
4114
+ constructor(poolName) {
4115
+ super(`A pool named "${poolName}" already exists. Use \`agent-shim pool set ${poolName}\` to change its members.`);
4116
+ this.poolName = poolName;
4117
+ this.name = "PoolAlreadyExistsError";
4118
+ }
4119
+ poolName;
4120
+ };
4121
+ var InvalidPoolNameError = class extends CliError {
4122
+ constructor(attemptedName) {
4123
+ 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.`);
4124
+ this.attemptedName = attemptedName;
4125
+ this.name = "InvalidPoolNameError";
4126
+ }
4127
+ attemptedName;
4128
+ };
4129
+ function readPools(paths) {
4130
+ return readGlobalConfig(paths)?.pools ?? {};
4131
+ }
4132
+ function requirePool(paths, name) {
4133
+ const pool = readPools(paths)[name];
4134
+ if (pool === void 0) {
4135
+ throw new PoolNotFoundError(name);
4136
+ }
4137
+ return pool;
4138
+ }
4139
+ function writePools(paths, pools) {
4140
+ applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
4141
+ }
4142
+ function addPool(paths, name, identities) {
4143
+ if (!PoolNameSchema.safeParse(name).success) {
4144
+ throw new InvalidPoolNameError(name);
4145
+ }
4146
+ const pools = readPools(paths);
4147
+ if (pools[name] !== void 0) {
4148
+ throw new PoolAlreadyExistsError(name);
4149
+ }
4150
+ const pool = { identities: [...identities] };
4151
+ writePools(paths, { ...pools, [name]: pool });
4152
+ return pool;
4153
+ }
4154
+ function setPool(paths, name, identities) {
4155
+ requirePool(paths, name);
4156
+ const pool = { identities: [...identities] };
4157
+ writePools(paths, { ...readPools(paths), [name]: pool });
4158
+ return pool;
4159
+ }
4160
+ function removePool(paths, name) {
4161
+ requirePool(paths, name);
4162
+ const remaining = Object.fromEntries(Object.entries(readPools(paths)).filter(([existing]) => existing !== name));
4163
+ writePools(paths, remaining);
4164
+ }
4165
+
4166
+ // src/identityStore.ts
4167
+ var IdentityNotFoundError = class extends CliError {
4168
+ constructor(name) {
4169
+ super(`No identity named "${name}". Run \`agent-shim identity add ${name}\` first.`);
4170
+ this.name = name;
4171
+ this.name = "IdentityNotFoundError";
4172
+ }
4173
+ name;
4174
+ };
4175
+ var IdentityAlreadyExistsError = class extends CliError {
4176
+ constructor(identityName) {
4177
+ super(`An identity named "${identityName}" already exists.`);
4178
+ this.identityName = identityName;
4179
+ this.name = "IdentityAlreadyExistsError";
4180
+ }
4181
+ identityName;
4182
+ };
4183
+ var InvalidIdentityNameError = class extends CliError {
4184
+ constructor(attemptedName) {
4185
+ super(
4186
+ `"${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.`
4187
+ );
4188
+ this.attemptedName = attemptedName;
4189
+ this.name = "InvalidIdentityNameError";
4190
+ }
4191
+ attemptedName;
4192
+ };
4193
+ function identityJsonPath(paths, name) {
4194
+ return import_node_path11.default.join(paths.identitiesDir, name, "identity.json");
4195
+ }
4196
+ function identityExists(paths, name) {
4197
+ return import_node_fs4.default.existsSync(identityJsonPath(paths, name));
4198
+ }
4199
+ function readIdentity(paths, name) {
4200
+ return readJson(identityJsonPath(paths, name), IdentitySchema);
4201
+ }
4202
+ function addIdentity(paths, name) {
4203
+ if (identityExists(paths, name)) {
4204
+ throw new IdentityAlreadyExistsError(name);
4205
+ }
4206
+ const parsed = IdentitySchema.safeParse({ name, allowAmbientCredential: false });
4207
+ if (!parsed.success) {
4208
+ throw new InvalidIdentityNameError(name);
4209
+ }
4210
+ writeJsonAtomic(identityJsonPath(paths, name), parsed.data);
4211
+ return parsed.data;
4212
+ }
4213
+ function useIdentity(paths, name) {
4214
+ const pool = poolNameOf(name);
4215
+ if (pool !== void 0) {
4216
+ requirePool(paths, pool);
4217
+ } else if (!identityExists(paths, name)) {
4218
+ throw new IdentityNotFoundError(name);
4219
+ }
4220
+ writeTextAtomic(paths.activeIdentityFile, `${name}
4221
+ `);
4222
+ }
4223
+ function readActiveIdentity(paths) {
4224
+ if (!import_node_fs4.default.existsSync(paths.activeIdentityFile)) {
4225
+ return void 0;
4226
+ }
4227
+ const raw = import_node_fs4.default.readFileSync(paths.activeIdentityFile, "utf8").trim();
4228
+ return raw === "" ? void 0 : raw;
4229
+ }
4230
+ function isIdentityDirectoryName(name) {
4231
+ return !name.startsWith(".");
4232
+ }
4233
+ function readIdentityForListing(paths, name) {
4234
+ try {
4235
+ return readIdentity(paths, name);
4236
+ } catch (error) {
4237
+ if (error instanceof ConfigValidationError || error instanceof SyntaxError) {
4238
+ return { problem: error.message.replace(/\s*\n\s*/g, " ") };
4239
+ }
4240
+ throw error;
4241
+ }
4242
+ }
4243
+ function listIdentities(paths) {
4244
+ if (!import_node_fs4.default.existsSync(paths.identitiesDir)) {
4245
+ return [];
4246
+ }
4247
+ const active = readActiveIdentity(paths);
4248
+ const names = import_node_fs4.default.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort();
4249
+ const result = [];
4250
+ for (const name of names) {
4251
+ const read = readIdentityForListing(paths, name);
4252
+ if (read === void 0) {
4253
+ continue;
4254
+ }
4255
+ const isActive = name === active;
4256
+ result.push("problem" in read ? { name, isActive, problem: read.problem } : { name, identity: read, isActive });
4257
+ }
4258
+ return result;
4259
+ }
4260
+ function setDefaultConfigProfile(paths, identityName, profileName) {
4261
+ if (!identityExists(paths, identityName)) {
4262
+ throw new IdentityNotFoundError(identityName);
4263
+ }
4264
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
4265
+ defaultConfigProfile: profileName
4266
+ });
4267
+ }
4268
+ function setAllowAmbientCredential(paths, identityName, allow) {
4269
+ if (!identityExists(paths, identityName)) {
4270
+ throw new IdentityNotFoundError(identityName);
4271
+ }
4272
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
4273
+ allowAmbientCredential: allow
4274
+ });
4275
+ }
4276
+ function setIdentityCredential(paths, identityName, change) {
4277
+ const existing = readIdentity(paths, identityName);
4278
+ if (existing === void 0) {
4279
+ throw new IdentityNotFoundError(identityName);
4280
+ }
4281
+ if (change === false) {
4282
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, { credential: void 0 });
4283
+ }
4284
+ const sources = change.sources ?? existing.credential?.sources;
4285
+ if (sources === void 0) {
4286
+ throw new UsageError(`Identity "${identityName}" has no credential yet: pass --credential to give it one before choosing its target.`);
4287
+ }
4288
+ const target = change.target ?? existing.credential?.target;
4289
+ const cache = change.cache === void 0 ? existing.credential?.cache : change.cache === false ? void 0 : change.cache;
4290
+ return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
4291
+ credential: { sources: [...sources], ...target === void 0 ? {} : { target }, ...cache === void 0 ? {} : { cache } }
4292
+ });
4293
+ }
4294
+ function belongsToIdentity(entry, name) {
4295
+ return entry === name || entry === import_node_path11.default.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
4296
+ }
4297
+ function removeIdentity(paths, name) {
4298
+ if (!identityExists(paths, name)) {
4299
+ throw new IdentityNotFoundError(name);
4300
+ }
4301
+ for (const entry of import_node_fs4.default.readdirSync(paths.identitiesDir)) {
4302
+ if (belongsToIdentity(entry, name)) {
4303
+ import_node_fs4.default.rmSync(import_node_path11.default.join(paths.identitiesDir, entry), { recursive: true, force: true });
4304
+ }
4305
+ }
4306
+ if (readActiveIdentity(paths) === name) {
4307
+ import_node_fs4.default.rmSync(paths.activeIdentityFile);
4308
+ }
4309
+ }
4310
+
4311
+ // src/providersStore.ts
4312
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
4313
+ var import_node_path12 = __toESM(require("node:path"), 1);
4314
+
4315
+ // src/credentialCache.ts
4316
+ var import_zod7 = require("zod");
4317
+ var CachedCredentialSchema = import_zod7.z.strictObject({
4318
+ token: import_zod7.z.string().min(1),
4319
+ fetchedAt: import_zod7.z.number().int().nonnegative(),
4320
+ source: CredentialSourceSchema
4321
+ });
4322
+
4323
+ // src/providersStore.ts
4324
+ var ProviderNotFoundError = class extends CliError {
4325
+ constructor(name) {
4326
+ super(`No provider named "${name}". Run \`agent-shim provider add ${name}\` first.`);
4327
+ this.name = name;
4328
+ this.name = "ProviderNotFoundError";
4329
+ }
4330
+ name;
4331
+ };
4332
+ var ProviderAlreadyExistsError = class extends CliError {
4333
+ constructor(providerName) {
4334
+ super(`A provider named "${providerName}" already exists.`);
4335
+ this.providerName = providerName;
4336
+ this.name = "ProviderAlreadyExistsError";
4337
+ }
4338
+ providerName;
4339
+ };
4340
+ var InvalidProviderNameError = class extends CliError {
4341
+ constructor(attemptedName) {
4342
+ super(
4343
+ `"${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.`
4344
+ );
4345
+ this.attemptedName = attemptedName;
4346
+ this.name = "InvalidProviderNameError";
4347
+ }
4348
+ attemptedName;
4349
+ };
4350
+ var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
4351
+ function providerJsonPath(paths, name) {
4352
+ return import_node_path12.default.join(paths.providersDir, `${name}.json`);
4353
+ }
4354
+ function providerExists(paths, name) {
4355
+ return import_node_fs5.default.existsSync(providerJsonPath(paths, name));
4356
+ }
4357
+ var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
4358
+ var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
4359
+ var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
4360
+ function isRecord(value) {
4361
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4362
+ }
4363
+ function legacyProviderConversion(raw) {
4364
+ if (!isRecord(raw) || "credential" in raw) {
4365
+ return void 0;
4366
+ }
4367
+ const env = isRecord(raw.env) ? raw.env : void 0;
4368
+ const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
4369
+ const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
4370
+ if (fields.length === 0) {
4371
+ return void 0;
4372
+ }
4373
+ const sources = [];
4374
+ if (typeof raw.tokenEnv === "string") {
4375
+ sources.push({ env: raw.tokenEnv });
4376
+ }
4377
+ if (Array.isArray(raw.tokenCommand)) {
4378
+ sources.push({ command: raw.tokenCommand });
4379
+ }
4380
+ if (typeof env?.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN !== "") {
4381
+ sources.push({ literal: LEGACY_LITERAL_PLACEHOLDER });
4382
+ }
4383
+ const remainingEnv = env === void 0 ? void 0 : Object.fromEntries(Object.entries(env).filter(([key]) => !LEGACY_ENV_CREDENTIAL_KEYS.includes(key)));
4384
+ const replacedKeys = [...LEGACY_PROVIDER_FIELDS, "env"];
4385
+ const rest = Object.fromEntries(Object.entries(raw).filter(([key]) => !replacedKeys.includes(key)));
4386
+ return {
4387
+ fields,
4388
+ replacement: {
4389
+ ...rest,
4390
+ credential: { sources, ...raw.authScheme === "apiKey" ? { target: "apiKey" } : {} },
4391
+ ...remainingEnv === void 0 || Object.keys(remainingEnv).length === 0 ? {} : { env: remainingEnv }
4392
+ }
4393
+ };
4394
+ }
4395
+ var LegacyProviderFileError = class extends CliError {
4396
+ constructor(filePath, conversion) {
4397
+ super(
4398
+ `${filePath} uses ${conversion.fields.join(", ")}, which a credential block replaced. Rewrite it as:
4399
+ ` + JSON.stringify(conversion.replacement, null, 2)
4400
+ );
4401
+ this.filePath = filePath;
4402
+ this.conversion = conversion;
4403
+ this.name = "LegacyProviderFileError";
4404
+ }
4405
+ filePath;
4406
+ conversion;
4407
+ };
4408
+ function parseProviderFile(filePath, raw) {
4409
+ const conversion = legacyProviderConversion(raw);
4410
+ if (conversion !== void 0) {
4411
+ throw new LegacyProviderFileError(filePath, conversion);
4412
+ }
4413
+ const parsed = ProviderSchema.safeParse(raw);
4414
+ if (!parsed.success) {
4415
+ throw new ConfigValidationError(filePath, parsed.error.issues);
4416
+ }
4417
+ return parsed.data;
4418
+ }
4419
+ function readProvider(paths, name) {
4420
+ const filePath = providerJsonPath(paths, name);
4421
+ if (!import_node_fs5.default.existsSync(filePath)) {
4422
+ return void 0;
4423
+ }
4424
+ const raw = JSON.parse(import_node_fs5.default.readFileSync(filePath, "utf8"));
4425
+ return parseProviderFile(filePath, raw);
4426
+ }
4427
+ function listProviders(paths) {
4428
+ const names = listProviderNames(
4429
+ (dir) => import_node_fs5.default.readdirSync(dir),
4430
+ paths.providersDir
4431
+ );
4432
+ const result = [];
4433
+ for (const name of names) {
4434
+ let provider;
4435
+ try {
4436
+ provider = readProvider(paths, name);
4437
+ } catch (error) {
4438
+ if (!(error instanceof ConfigValidationError || error instanceof LegacyProviderFileError)) {
4439
+ throw error;
4440
+ }
4441
+ console.error(`agent-shim: providers/${name}.json is invalid and was skipped: ${error.message}`);
4442
+ continue;
4443
+ }
4444
+ if (provider !== void 0) {
4445
+ result.push({ name, provider });
4446
+ }
4447
+ }
4448
+ return result;
4449
+ }
4450
+ function listProviderNames(readdir, providersDir) {
4451
+ let entries;
4452
+ try {
4453
+ entries = readdir(providersDir);
4454
+ } catch {
4455
+ return [];
4456
+ }
4457
+ return entries.filter((entry) => entry.endsWith(".json")).map((entry) => entry.slice(0, -".json".length)).sort();
4458
+ }
4459
+ function describeProviderEndpoint(provider) {
4460
+ return isCodexProvider(provider) ? "codex translation daemon" : provider.baseUrl;
4461
+ }
4462
+ var ProviderKindMismatchError = class extends UsageError {
4463
+ constructor(message) {
4464
+ super(message);
4465
+ this.name = "ProviderKindMismatchError";
4466
+ }
4467
+ };
4468
+ function isEmptyCodexConfig(config) {
4469
+ return config === void 0 || config.defaultModel === void 0 && config.effort === void 0 && (config.models === void 0 || Object.keys(config.models).length === 0);
4470
+ }
4471
+ function addProvider(paths, name, input) {
4472
+ if (providerExists(paths, name)) {
4473
+ throw new ProviderAlreadyExistsError(name);
4474
+ }
4475
+ if (!PROVIDER_NAME_RE.test(name)) {
4476
+ throw new InvalidProviderNameError(name);
4477
+ }
4478
+ const kind = input.kind ?? "http";
4479
+ if (kind === "codex" && input.baseUrl !== void 0) {
4480
+ throw new ProviderKindMismatchError("A codex provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
4481
+ }
4482
+ if (kind === "http" && input.baseUrl === void 0) {
4483
+ throw new ProviderKindMismatchError("An http provider needs --base-url.");
4484
+ }
4485
+ if (kind === "http" && !isEmptyCodexConfig(input.codex)) {
4486
+ throw new ProviderKindMismatchError("Codex settings apply only to a codex provider (--kind codex).");
4487
+ }
4488
+ const shared = {
4489
+ displayName: input.displayName,
4490
+ credential: { sources: input.sources, ...input.target === void 0 ? {} : { target: input.target } },
4491
+ ...input.env === void 0 || Object.keys(input.env).length === 0 ? {} : { env: input.env }
4492
+ };
4493
+ const parsed = ProviderSchema.safeParse(
4494
+ kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : { ...shared, baseUrl: input.baseUrl }
4495
+ );
4496
+ if (!parsed.success) {
4497
+ throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
4498
+ }
4499
+ writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
4500
+ return parsed.data;
4501
+ }
4502
+ function updateProvider(paths, name, input) {
4503
+ const existing = readProvider(paths, name);
4504
+ if (existing === void 0) {
4505
+ throw new ProviderNotFoundError(name);
4506
+ }
4507
+ const unset = new Set(input.unsetEnv);
4508
+ const env = Object.fromEntries(Object.entries({ ...existing.env, ...input.env }).filter(([key]) => !unset.has(key)));
4509
+ const target = input.target ?? existing.credential.target;
4510
+ const cache = input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
4511
+ if (isCodexProvider(existing) && input.baseUrl !== void 0) {
4512
+ throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
4513
+ }
4514
+ if (!isCodexProvider(existing) && !isEmptyCodexConfig(input.codex)) {
4515
+ throw new ProviderKindMismatchError(`Provider "${name}" is an http provider; codex settings apply only to a codex provider.`);
4516
+ }
4517
+ const shared = {
4518
+ ...input.displayName === void 0 ? {} : { displayName: input.displayName },
4519
+ credential: {
4520
+ sources: input.sources ?? existing.credential.sources,
4521
+ ...target === void 0 ? {} : { target },
4522
+ ...cache === void 0 ? {} : { cache }
4523
+ },
4524
+ env: Object.keys(env).length === 0 ? void 0 : env
4525
+ };
4526
+ let candidate;
4527
+ if (isCodexProvider(existing)) {
4528
+ const codex = {
4529
+ ...existing.codex,
4530
+ ...input.codex,
4531
+ ...existing.codex?.models === void 0 && input.codex?.models === void 0 ? {} : { models: { ...existing.codex?.models, ...input.codex?.models } }
4532
+ };
4533
+ candidate = { ...existing, ...shared, codex: isEmptyCodexConfig(codex) ? void 0 : codex };
4534
+ } else {
4535
+ candidate = { ...existing, ...shared, ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl } };
4536
+ }
4537
+ const parsed = ProviderSchema.safeParse(candidate);
4538
+ if (!parsed.success) {
4539
+ throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
4540
+ }
4541
+ writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
4542
+ return parsed.data;
4543
+ }
4544
+ function removeProvider(paths, name) {
4545
+ if (!providerExists(paths, name)) {
4546
+ throw new ProviderNotFoundError(name);
4547
+ }
4548
+ import_node_fs5.default.rmSync(providerJsonPath(paths, name));
4549
+ }
4550
+
4551
+ // src/directoryRulesStore.ts
4552
+ var DirectoryRuleNotFoundError = class extends CliError {
4553
+ constructor(rulePath) {
4554
+ super(`No directory rule found for path "${rulePath}".`);
4555
+ this.rulePath = rulePath;
4556
+ this.name = "DirectoryRuleNotFoundError";
4557
+ }
4558
+ rulePath;
4559
+ };
4560
+ var DirectoryRuleMissingTargetError = class extends CliError {
4561
+ constructor() {
4562
+ super("A directory rule must set at least one of --config-profile or --identity.");
4563
+ this.name = "DirectoryRuleMissingTargetError";
4564
+ }
4565
+ };
4566
+ var DirectoryRuleAlreadyExistsError = class extends CliError {
4567
+ constructor(rulePath) {
4568
+ super(`A directory rule for path "${rulePath}" already exists. Use \`agent-shim rule set\` to change it.`);
4569
+ this.rulePath = rulePath;
4570
+ this.name = "DirectoryRuleAlreadyExistsError";
4571
+ }
4572
+ rulePath;
4573
+ };
4574
+ function readDirectoryRules(paths) {
4575
+ return readJson(paths.directoryRulesFile, DirectoryRulesSchema) ?? { rules: [] };
4576
+ }
4577
+ function writeDirectoryRules(paths, rules) {
4578
+ const parsed = DirectoryRulesSchema.safeParse(rules);
4579
+ if (!parsed.success) {
4580
+ throw new ConfigValidationError(paths.directoryRulesFile, parsed.error.issues);
4581
+ }
4582
+ writeJsonAtomic(paths.directoryRulesFile, parsed.data);
4583
+ }
4584
+ function listDirectoryRules(paths) {
4585
+ return readDirectoryRules(paths).rules;
4586
+ }
4587
+ function addDirectoryRule(paths, rulePath, options) {
4588
+ if (options.configProfile === void 0 && options.identity === void 0) {
4589
+ throw new DirectoryRuleMissingTargetError();
4590
+ }
4591
+ const current = readDirectoryRules(paths);
4592
+ if (current.rules.some((rule) => rule.path === rulePath)) {
4593
+ throw new DirectoryRuleAlreadyExistsError(rulePath);
4594
+ }
4595
+ const created = buildNewRule(rulePath, options);
4596
+ writeDirectoryRules(paths, { ...current, rules: [...current.rules, created] });
4597
+ return created;
4598
+ }
4599
+ function ruleHasEffect(rule) {
4600
+ return rule.configProfile !== void 0 || rule.identity !== void 0 || rule.categories !== void 0 || rule.entries !== void 0 || rule.launch !== void 0;
4601
+ }
4602
+ function updateDirectoryRule(paths, rulePath, options) {
4603
+ const current = readDirectoryRules(paths);
4604
+ const index = current.rules.findIndex((rule) => rule.path === rulePath);
4605
+ const existing = index === -1 ? void 0 : current.rules[index];
4606
+ if (existing === void 0) {
4607
+ throw new DirectoryRuleNotFoundError(rulePath);
4608
+ }
4609
+ const { configProfile, identity, ...rest } = existing;
4610
+ const nextConfigProfile = options.configProfile === void 0 ? configProfile : options.configProfile === false ? void 0 : options.configProfile;
4611
+ const nextIdentity = options.identity === void 0 ? identity : options.identity === false ? void 0 : options.identity;
4612
+ const updated = {
4613
+ ...rest,
4614
+ ...nextConfigProfile === void 0 ? {} : { configProfile: nextConfigProfile },
4615
+ ...nextIdentity === void 0 ? {} : { identity: nextIdentity }
4616
+ };
4617
+ if (!ruleHasEffect(updated)) {
4618
+ throw new DirectoryRuleMissingTargetError();
4619
+ }
4620
+ const nextRules = [...current.rules];
4621
+ nextRules[index] = updated;
4622
+ writeDirectoryRules(paths, { ...current, rules: nextRules });
4623
+ return updated;
4624
+ }
4625
+ function buildNewRule(rulePath, options) {
4626
+ return {
4627
+ path: rulePath,
4628
+ ...options.configProfile !== void 0 ? { configProfile: options.configProfile } : {},
4629
+ ...options.identity !== void 0 ? { identity: options.identity } : {}
4630
+ };
4631
+ }
4632
+ function removeDirectoryRule(paths, rulePath) {
4633
+ const current = readDirectoryRules(paths);
4634
+ const nextRules = current.rules.filter((rule) => rule.path !== rulePath);
4635
+ if (nextRules.length === current.rules.length) {
4636
+ throw new DirectoryRuleNotFoundError(rulePath);
4637
+ }
4638
+ writeDirectoryRules(paths, { ...current, rules: nextRules });
4639
+ }
3816
4640
  // Annotate the CommonJS export names for ESM import in node:
3817
4641
  0 && (module.exports = {
3818
4642
  AMBIENT_CREDENTIAL_VARS,
@@ -3827,6 +4651,9 @@ function resolveLayoutPaths() {
3827
4651
  CredentialCacheSchema,
3828
4652
  CredentialSchema,
3829
4653
  CredentialSourceSchema,
4654
+ DirectoryRuleAlreadyExistsError,
4655
+ DirectoryRuleMissingTargetError,
4656
+ DirectoryRuleNotFoundError,
3830
4657
  DirectoryRuleSchema,
3831
4658
  DirectoryRulesSchema,
3832
4659
  EXIT_FAILURE,
@@ -3836,20 +4663,38 @@ function resolveLayoutPaths() {
3836
4663
  FrontDoorStartError,
3837
4664
  GlobalConfigSchema,
3838
4665
  HeadroomStartError,
4666
+ IdentityAlreadyExistsError,
4667
+ IdentityNotFoundError,
3839
4668
  IdentitySchema,
4669
+ InvalidCategoryNameError,
4670
+ InvalidIdentityNameError,
4671
+ InvalidProviderNameError,
4672
+ LegacyProviderFileError,
3840
4673
  PoolNameSchema,
4674
+ PoolNotFoundError,
3841
4675
  PoolSchema,
3842
4676
  PortableConfigSchema,
4677
+ ProfileAlreadyExistsError,
4678
+ ProfileNotFoundError,
4679
+ ProviderAlreadyExistsError,
4680
+ ProviderKindMismatchError,
4681
+ ProviderNotFoundError,
3843
4682
  ProviderSchema,
3844
4683
  ROUTED_PATH_PREFIX,
3845
4684
  UsageError,
3846
4685
  UsageSnapshotError,
3847
4686
  UsageSnapshotSchema,
3848
4687
  WhenSchema,
4688
+ addDirectoryRule,
4689
+ addIdentity,
4690
+ addPool,
4691
+ addProvider,
3849
4692
  buildEntryFacts,
3850
4693
  buildLayoutPaths,
3851
4694
  carryOver,
3852
4695
  createLeafCache,
4696
+ createProfile,
4697
+ describeProviderEndpoint,
3853
4698
  detectAmbientCredential,
3854
4699
  ensureCa,
3855
4700
  ensureFrontDoor,
@@ -3858,16 +4703,37 @@ function resolveLayoutPaths() {
3858
4703
  formatAmbientCredentialGuardMessage,
3859
4704
  forwardableHeaders,
3860
4705
  generateCa,
4706
+ identityExists,
4707
+ isIdentityDirectoryName,
3861
4708
  isInterceptedHost,
4709
+ listDirectoryRules,
4710
+ listIdentities,
4711
+ listProfiles,
4712
+ listProviders,
3862
4713
  listUsageSnapshots,
3863
4714
  mintLeaf,
3864
4715
  parseConnectTarget,
4716
+ profileExists,
4717
+ providerExists,
4718
+ readActiveIdentity,
4719
+ readDirectoryRules,
3865
4720
  readFarmManifest,
4721
+ readGlobalConfig,
4722
+ readIdentity,
4723
+ readPools,
4724
+ readProfile,
4725
+ readProvider,
3866
4726
  readUsageSnapshot,
3867
4727
  realConnectCertStore,
3868
4728
  realConnectEffects,
3869
4729
  recoverFarm,
3870
4730
  recoveryDiagnostics,
4731
+ removeDirectoryRule,
4732
+ removeIdentity,
4733
+ removePool,
4734
+ removeProfile,
4735
+ removeProvider,
4736
+ requirePool,
3871
4737
  resolveAgentShimHome,
3872
4738
  resolveClaudeHome,
3873
4739
  resolveDecisions,
@@ -3877,7 +4743,20 @@ function resolveLayoutPaths() {
3877
4743
  runFrontDoorSupervisor,
3878
4744
  runSupervisor,
3879
4745
  servedByPipeline,
4746
+ setAllowAmbientCredential,
4747
+ setDefaultConfigProfile,
4748
+ setGlobalDefaultProfile,
4749
+ setIdentityCredential,
4750
+ setPool,
4751
+ setProfileCategories,
4752
+ setProfileEntries,
4753
+ setProfileLaunchFlags,
4754
+ setProfileMetadata,
3880
4755
  snapshotPath,
3881
4756
  startConnectServer,
3882
- topLevelNames
4757
+ topLevelNames,
4758
+ updateDirectoryRule,
4759
+ updateProvider,
4760
+ useIdentity,
4761
+ writeDirectoryRules
3883
4762
  });