agent-shim 7.6.1 → 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({
@@ -2160,7 +2312,7 @@ function issueLeafCertificate(ca, names, notBefore, notAfter) {
2160
2312
  }
2161
2313
 
2162
2314
  // src/frontdoor/tap.ts
2163
- function pumpTapSession(secure, openUpstream, tap) {
2315
+ function pumpTapSession(secure, openUpstream, tap, prefix = Buffer.alloc(0)) {
2164
2316
  let upstream;
2165
2317
  let ended = false;
2166
2318
  const end = () => {
@@ -2192,6 +2344,9 @@ function pumpTapSession(secure, openUpstream, tap) {
2192
2344
  upstream.destroy();
2193
2345
  return;
2194
2346
  }
2347
+ if (prefix.length > 0) {
2348
+ upstream.write(prefix);
2349
+ }
2195
2350
  if (tap !== void 0) {
2196
2351
  secure.on("data", (chunk) => {
2197
2352
  tap.onChunk("client-to-server", chunk);
@@ -2225,8 +2380,11 @@ function capabilityFromProxyAuthorization(value) {
2225
2380
  return password === "" ? void 0 : password;
2226
2381
  }
2227
2382
 
2383
+ // src/frontdoor/route.ts
2384
+ var AUTH_HEADER = "x-agent-shim-auth";
2385
+
2228
2386
  // src/frontdoor/state.ts
2229
- var import_node_path5 = __toESM(require("node:path"), 1);
2387
+ var import_node_path6 = __toESM(require("node:path"), 1);
2230
2388
  var import_zod3 = require("zod");
2231
2389
  var FRONT_DOOR_PROTOCOL = 1;
2232
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) });
@@ -2260,10 +2418,10 @@ var FrontDoorStateSchema = import_zod3.z.strictObject({
2260
2418
  /** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
2261
2419
  lastError: import_zod3.z.string().optional()
2262
2420
  });
2263
- function readFrontDoorState(fs3, stateFile) {
2421
+ function readFrontDoorState(fs7, stateFile) {
2264
2422
  let raw;
2265
2423
  try {
2266
- raw = fs3.readFileUtf8(stateFile);
2424
+ raw = fs7.readFileUtf8(stateFile);
2267
2425
  } catch {
2268
2426
  return void 0;
2269
2427
  }
@@ -2277,28 +2435,28 @@ function readFrontDoorState(fs3, stateFile) {
2277
2435
  return void 0;
2278
2436
  }
2279
2437
  }
2280
- function writeFrontDoorState(fs3, stateFile, state) {
2281
- fs3.mkdirp(import_node_path5.default.dirname(stateFile));
2282
- 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)}
2283
2441
  `);
2284
2442
  }
2285
2443
  function frontDoorOrigin(state) {
2286
2444
  const port = state?.directPort ?? state?.lastDirectPort;
2287
2445
  return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
2288
2446
  }
2289
- function writeFrontDoorSession(fs3, sessionsDir, session) {
2290
- fs3.mkdirPrivate(sessionsDir);
2291
- 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)}
2292
2450
  `);
2293
2451
  }
2294
- function listFrontDoorSessions(fs3, sessionsDir) {
2452
+ function listFrontDoorSessions(fs7, sessionsDir) {
2295
2453
  const sessions = [];
2296
- for (const name of fs3.readdir(sessionsDir)) {
2454
+ for (const name of fs7.readdir(sessionsDir)) {
2297
2455
  if (!name.endsWith(".json")) {
2298
2456
  continue;
2299
2457
  }
2300
2458
  try {
2301
- 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)) ?? ""));
2302
2460
  if (parsed.success) {
2303
2461
  sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
2304
2462
  }
@@ -2308,14 +2466,14 @@ function listFrontDoorSessions(fs3, sessionsDir) {
2308
2466
  }
2309
2467
  return sessions.sort((a, b) => a.pid - b.pid);
2310
2468
  }
2311
- function removeFrontDoorSession(fs3, sessionsDir, pid) {
2312
- 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`));
2313
2471
  }
2314
- function pruneDeadFrontDoorSessions(fs3, sessionsDir, isRunning) {
2472
+ function pruneDeadFrontDoorSessions(fs7, sessionsDir, isRunning) {
2315
2473
  const removed = [];
2316
- for (const session of listFrontDoorSessions(fs3, sessionsDir)) {
2474
+ for (const session of listFrontDoorSessions(fs7, sessionsDir)) {
2317
2475
  if (!isRunning(session.pid)) {
2318
- removeFrontDoorSession(fs3, sessionsDir, session.pid);
2476
+ removeFrontDoorSession(fs7, sessionsDir, session.pid);
2319
2477
  removed.push(session.pid);
2320
2478
  }
2321
2479
  }
@@ -2327,8 +2485,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
2327
2485
  var FRONTDOOR_POLL_MS = 1e3;
2328
2486
  var MS_PER_MINUTE = 6e4;
2329
2487
  async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
2330
- const { fs: fs3, paths } = ports;
2331
- const previous = readFrontDoorState(fs3, paths.frontdoorStateFile);
2488
+ const { fs: fs7, paths } = ports;
2489
+ const previous = readFrontDoorState(fs7, paths.frontdoorStateFile);
2332
2490
  let lastPort = previous?.lastPort;
2333
2491
  let lastConnectPort = previous?.lastConnectPort;
2334
2492
  let lastDirectPort = previous?.lastDirectPort;
@@ -2338,12 +2496,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2338
2496
  ...lastDirectPort === void 0 ? {} : { lastDirectPort }
2339
2497
  });
2340
2498
  const fail = (message) => {
2341
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { lastError: message, ...sticky() });
2342
- fs3.removeRecursive(paths.frontdoorLockFile);
2499
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { lastError: message, ...sticky() });
2500
+ fs7.removeRecursive(paths.frontdoorLockFile);
2343
2501
  ports.log(`agent-shim frontdoor supervisor: ${message}`);
2344
2502
  return 1;
2345
2503
  };
2346
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
2504
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
2347
2505
  const previousSticky = lastPort;
2348
2506
  let http2;
2349
2507
  try {
@@ -2380,8 +2538,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2380
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)`);
2381
2539
  }
2382
2540
  lastDirectPort = direct.port;
2383
- writeFrontDoorState(fs3, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
2384
- 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);
2385
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)}`);
2386
2544
  let idleSince;
2387
2545
  let ticks = 0;
@@ -2390,15 +2548,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
2390
2548
  return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
2391
2549
  }
2392
2550
  ticks += 1;
2393
- pruneDeadFrontDoorSessions(fs3, paths.frontdoorSessionsDir, ports.isRunning);
2394
- if (listFrontDoorSessions(fs3, paths.frontdoorSessionsDir).length === 0) {
2551
+ pruneDeadFrontDoorSessions(fs7, paths.frontdoorSessionsDir, ports.isRunning);
2552
+ if (listFrontDoorSessions(fs7, paths.frontdoorSessionsDir).length === 0) {
2395
2553
  idleSince ??= ports.now();
2396
2554
  if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
2397
2555
  ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
2398
2556
  await direct.close();
2399
2557
  await connect3.close();
2400
2558
  await http2.close();
2401
- writeFrontDoorState(fs3, paths.frontdoorStateFile, sticky());
2559
+ writeFrontDoorState(fs7, paths.frontdoorStateFile, sticky());
2402
2560
  return 0;
2403
2561
  }
2404
2562
  } else {
@@ -2453,8 +2611,8 @@ function parseConnectTarget(authority) {
2453
2611
  function isInterceptedHost(host, interceptHosts) {
2454
2612
  return interceptHosts.includes(host);
2455
2613
  }
2456
- function servedByPipeline(path9) {
2457
- return path9?.startsWith(ROUTED_PATH_PREFIX) === true;
2614
+ function servedByPipeline(path13) {
2615
+ return path13?.startsWith(ROUTED_PATH_PREFIX) === true;
2458
2616
  }
2459
2617
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
2460
2618
  "connection",
@@ -2513,8 +2671,8 @@ function ensureCa(store, generate) {
2513
2671
  }
2514
2672
  function caParses(ca) {
2515
2673
  try {
2516
- new import_node_crypto3.X509Certificate(ca.certPem);
2517
- (0, import_node_crypto3.createPrivateKey)(ca.keyPem);
2674
+ new import_node_crypto4.X509Certificate(ca.certPem);
2675
+ (0, import_node_crypto4.createPrivateKey)(ca.keyPem);
2518
2676
  return true;
2519
2677
  } catch {
2520
2678
  return false;
@@ -2522,7 +2680,19 @@ function caParses(ca) {
2522
2680
  }
2523
2681
  var TAP_ALPN_PROTOCOLS = ["h2", "http/1.1"];
2524
2682
  async function startConnectServer(config, effects, preferredPort) {
2525
- const onUpgrade = config.capture === void 0 ? void 0 : (request3) => config.capture?.upgrade(request3);
2683
+ const tunnelTokens = /* @__PURE__ */ new WeakMap();
2684
+ const onUpgrade = (host) => (request3, socket, head) => {
2685
+ config.capture?.upgrade(request3);
2686
+ if (head.length > 0) {
2687
+ socket.pause();
2688
+ socket.unshift(head);
2689
+ }
2690
+ const headText = `${request3.method ?? "GET"} ${request3.url ?? "/"} HTTP/1.1\r
2691
+ ${request3.rawHeaders.reduce((accumulated, value, index) => index % 2 === 0 ? `${accumulated}${value}: ` : `${accumulated}${value}\r
2692
+ `, "")}\r
2693
+ `;
2694
+ pumpTapSession(socket, async (clientAlpn) => await effects.connectTlsUpstream(host, HTTPS_PORT, clientAlpn), void 0, Buffer.from(headText, "utf8"));
2695
+ };
2526
2696
  const sessions = config.interceptHosts.map((host) => {
2527
2697
  if (config.tapHosts.includes(host)) {
2528
2698
  const tlsAcceptor2 = effects.createTlsAcceptor(
@@ -2536,13 +2706,18 @@ async function startConnectServer(config, effects, preferredPort) {
2536
2706
  }
2537
2707
  const handler = (request3, response) => {
2538
2708
  if (host === config.routedHost && servedByPipeline(request3.url)) {
2709
+ const tunnelToken = tunnelTokens.get(request3.socket);
2710
+ if (tunnelToken !== void 0 && request3.headers[AUTH_HEADER] === void 0) {
2711
+ request3.headers[AUTH_HEADER] = tunnelToken;
2712
+ }
2539
2713
  config.serveRouted(request3, response);
2540
2714
  return;
2541
2715
  }
2542
2716
  effects.forwardHttp(config.upstreamFor(host), request3, response, config.capture?.observePassthrough(request3));
2543
2717
  };
2544
- const httpSession = effects.createHttpSession(handler, onUpgrade);
2545
- const tlsAcceptor = effects.createTlsAcceptor(config.leafFor(host), (secure) => {
2718
+ const httpSession = effects.createHttpSession(handler, onUpgrade(host));
2719
+ const tlsAcceptor = effects.createTlsAcceptor(config.leafFor(host), (secure, context) => {
2720
+ tunnelTokens.set(secure, context);
2546
2721
  httpSession.serve(secure);
2547
2722
  });
2548
2723
  return { host, httpSession, tlsAcceptor };
@@ -2683,6 +2858,7 @@ function routeConnect(socket, head, context) {
2683
2858
  const credential = proxyAuthorizationOf(head);
2684
2859
  const capability = credential === void 0 ? void 0 : capabilityFromProxyAuthorization(credential);
2685
2860
  if (capability === void 0 || !config.isLiveCapability(capability)) {
2861
+ console.error(`[upg-dbg] refused CONNECT to ${head.split("\r\n", 1)[0]?.split(" ")[1] ?? "?"} (credential ${credential === void 0 ? "absent" : "present but dead"})`);
2686
2862
  refuse(socket, REFUSAL.proxyAuthenticationRequired, [`Proxy-Authenticate: Basic realm="${CONNECT_PROXY_REALM}"`]);
2687
2863
  return;
2688
2864
  }
@@ -2702,7 +2878,7 @@ function routeConnect(socket, head, context) {
2702
2878
  config.capture?.connect(target, session !== void 0);
2703
2879
  socket.write(CONNECT_ESTABLISHED);
2704
2880
  if (session !== void 0) {
2705
- session.tlsAcceptor.accept(socket);
2881
+ session.tlsAcceptor.accept(socket, capability);
2706
2882
  return;
2707
2883
  }
2708
2884
  void blindTunnel(socket, target, effects);
@@ -2734,15 +2910,15 @@ async function blindTunnel(socket, target, effects) {
2734
2910
  socket.destroy();
2735
2911
  });
2736
2912
  }
2737
- function isEnoent(error) {
2913
+ function isEnoent2(error) {
2738
2914
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2739
2915
  }
2740
2916
  function realConnectCertStore(paths) {
2741
2917
  const read = (file) => {
2742
2918
  try {
2743
- return fs.readFileSync(file, "utf8");
2919
+ return fs2.readFileSync(file, "utf8");
2744
2920
  } catch (error) {
2745
- if (isEnoent(error)) {
2921
+ if (isEnoent2(error)) {
2746
2922
  return void 0;
2747
2923
  }
2748
2924
  throw error;
@@ -2755,10 +2931,10 @@ function realConnectCertStore(paths) {
2755
2931
  return certPem === void 0 || keyPem2 === void 0 ? void 0 : { certPem, keyPem: keyPem2 };
2756
2932
  },
2757
2933
  writeCa: (ca) => {
2758
- fs.mkdirSync(paths.frontdoorCaDir, { recursive: true });
2759
- fs.writeFileSync(paths.frontdoorCaCertFile, ca.certPem, "utf8");
2760
- fs.writeFileSync(paths.frontdoorCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
2761
- 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);
2762
2938
  }
2763
2939
  };
2764
2940
  }
@@ -2814,10 +2990,11 @@ function realConnectEffects() {
2814
2990
  return await listenOnce(0, onSocket);
2815
2991
  },
2816
2992
  createTlsAcceptor: (leaf, onSecure, alpnProtocols) => {
2993
+ let pendingContext = "";
2817
2994
  const server = tls.createServer(
2818
2995
  { key: leaf.keyPem, cert: leaf.certPem, ...alpnProtocols === void 0 ? {} : { ALPNProtocols: [...alpnProtocols] } },
2819
2996
  (secure) => {
2820
- onSecure(secure);
2997
+ onSecure(secure, pendingContext);
2821
2998
  }
2822
2999
  );
2823
3000
  server.on("clientError", (_error, socket) => {
@@ -2825,7 +3002,8 @@ function realConnectEffects() {
2825
3002
  });
2826
3003
  return {
2827
3004
  // tls.Server is a net.Server whose connection listener wraps the raw socket in the TLS handshake, and emitting the event by hand is what runs that listener on a socket this process already owns (the CONNECT half of the connection) rather than one the server accepted itself.
2828
- accept: (socket) => {
3005
+ accept: (socket, context) => {
3006
+ pendingContext = context;
2829
3007
  server.emit("connection", socket);
2830
3008
  },
2831
3009
  close: () => {
@@ -2839,9 +3017,8 @@ function realConnectEffects() {
2839
3017
  socket.destroy();
2840
3018
  });
2841
3019
  if (onUpgrade !== void 0) {
2842
- server.on("upgrade", (request3, socket) => {
2843
- onUpgrade(request3);
2844
- socket.destroy();
3020
+ server.on("upgrade", (request3, socket, head) => {
3021
+ onUpgrade(request3, socket, head);
2845
3022
  });
2846
3023
  }
2847
3024
  return {
@@ -2926,11 +3103,11 @@ function realConnectEffects() {
2926
3103
  }
2927
3104
 
2928
3105
  // src/frontdoor/ensure.ts
2929
- var import_node_crypto5 = require("node:crypto");
3106
+ var import_node_crypto6 = require("node:crypto");
2930
3107
 
2931
3108
  // src/headroom/state.ts
2932
- var import_node_crypto4 = require("node:crypto");
2933
- 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);
2934
3111
  var import_zod4 = require("zod");
2935
3112
  var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
2936
3113
  var HeadroomStateSchema = import_zod4.z.strictObject({
@@ -2964,16 +3141,16 @@ function parseJson(raw, schema) {
2964
3141
  const parsed = schema.safeParse(JSON.parse(raw));
2965
3142
  return parsed.success ? parsed.data : void 0;
2966
3143
  }
2967
- function readHeadroomState(fs3, stateFile) {
3144
+ function readHeadroomState(fs7, stateFile) {
2968
3145
  try {
2969
- return parseJson(fs3.readFileUtf8(stateFile), HeadroomStateSchema);
3146
+ return parseJson(fs7.readFileUtf8(stateFile), HeadroomStateSchema);
2970
3147
  } catch {
2971
3148
  return void 0;
2972
3149
  }
2973
3150
  }
2974
- function writeHeadroomState(fs3, stateFile, state) {
2975
- fs3.mkdirp(import_node_path6.default.dirname(stateFile));
2976
- 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)}
2977
3154
  `);
2978
3155
  }
2979
3156
  function headroomAllowlist(providers) {
@@ -2991,16 +3168,16 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
2991
3168
  return upstreams;
2992
3169
  }
2993
3170
  function hashAllowlist(allowlist) {
2994
- 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");
2995
3172
  }
2996
- function readAllProviders(fs3, providersDir) {
3173
+ function readAllProviders(fs7, providersDir) {
2997
3174
  const result = [];
2998
- for (const name of fs3.readdir(providersDir)) {
3175
+ for (const name of fs7.readdir(providersDir)) {
2999
3176
  if (!name.endsWith(".json")) {
3000
3177
  continue;
3001
3178
  }
3002
3179
  try {
3003
- 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);
3004
3181
  if (parsed !== void 0) {
3005
3182
  result.push({ name: name.slice(0, -".json".length), provider: parsed });
3006
3183
  }
@@ -3010,22 +3187,22 @@ function readAllProviders(fs3, providersDir) {
3010
3187
  }
3011
3188
  return result;
3012
3189
  }
3013
- function writeSession(fs3, sessionsDir, session) {
3014
- fs3.mkdirp(sessionsDir);
3015
- 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)}
3016
3193
  `);
3017
3194
  }
3018
- function removeSession(fs3, sessionsDir, pid) {
3019
- 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`));
3020
3197
  }
3021
- function listSessions(fs3, sessionsDir) {
3198
+ function listSessions(fs7, sessionsDir) {
3022
3199
  const sessions = [];
3023
- for (const name of fs3.readdir(sessionsDir)) {
3200
+ for (const name of fs7.readdir(sessionsDir)) {
3024
3201
  if (!name.endsWith(".json")) {
3025
3202
  continue;
3026
3203
  }
3027
3204
  try {
3028
- 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);
3029
3206
  if (parsed !== void 0) {
3030
3207
  sessions.push(parsed);
3031
3208
  }
@@ -3035,22 +3212,22 @@ function listSessions(fs3, sessionsDir) {
3035
3212
  }
3036
3213
  return sessions.sort((a, b) => a.pid - b.pid);
3037
3214
  }
3038
- function sessionsForSupervisor(fs3, sessionsDir, supervisorPid) {
3039
- 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);
3040
3217
  }
3041
- function pruneDeadSessions(fs3, sessionsDir, isRunning) {
3218
+ function pruneDeadSessions(fs7, sessionsDir, isRunning) {
3042
3219
  const removed = [];
3043
- for (const session of listSessions(fs3, sessionsDir)) {
3220
+ for (const session of listSessions(fs7, sessionsDir)) {
3044
3221
  if (!isRunning(session.pid)) {
3045
- removeSession(fs3, sessionsDir, session.pid);
3222
+ removeSession(fs7, sessionsDir, session.pid);
3046
3223
  removed.push(session.pid);
3047
3224
  }
3048
3225
  }
3049
3226
  return removed;
3050
3227
  }
3051
- function readStartLock(fs3, lockFile) {
3228
+ function readStartLock(fs7, lockFile) {
3052
3229
  try {
3053
- return parseJson(fs3.readFileUtf8(lockFile), HeadroomLockSchema);
3230
+ return parseJson(fs7.readFileUtf8(lockFile), HeadroomLockSchema);
3054
3231
  } catch {
3055
3232
  return void 0;
3056
3233
  }
@@ -3090,7 +3267,7 @@ function ensureFrontDoor(params) {
3090
3267
  continue;
3091
3268
  }
3092
3269
  if (verdict.ok) {
3093
- const token = (0, import_node_crypto5.randomUUID)();
3270
+ const token = (0, import_node_crypto6.randomUUID)();
3094
3271
  writeFrontDoorSession(ports.fs, paths.frontdoorSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), token });
3095
3272
  return { port: state.port, connectPort: state.connectPort, token };
3096
3273
  }
@@ -3130,7 +3307,7 @@ function ensureFrontDoor(params) {
3130
3307
  }
3131
3308
 
3132
3309
  // src/usage/read.ts
3133
- var import_node_path7 = __toESM(require("node:path"), 1);
3310
+ var import_node_path8 = __toESM(require("node:path"), 1);
3134
3311
 
3135
3312
  // src/usage/schema.ts
3136
3313
  var import_zod5 = require("zod");
@@ -3297,11 +3474,11 @@ function snapshotPath(snapshotsDir, identity) {
3297
3474
  if (!isIdentityName(identity)) {
3298
3475
  throw new Error(`"${identity}" is not a valid identity name.`);
3299
3476
  }
3300
- return import_node_path7.default.join(snapshotsDir, `${identity}.json`);
3477
+ return import_node_path8.default.join(snapshotsDir, `${identity}.json`);
3301
3478
  }
3302
- function readUsageSnapshot(fs3, snapshotsDir, identity) {
3479
+ function readUsageSnapshot(fs7, snapshotsDir, identity) {
3303
3480
  const file = snapshotPath(snapshotsDir, identity);
3304
- const raw = fs3.readFileUtf8(file);
3481
+ const raw = fs7.readFileUtf8(file);
3305
3482
  if (raw === void 0) {
3306
3483
  return void 0;
3307
3484
  }
@@ -3317,8 +3494,8 @@ function readUsageSnapshot(fs3, snapshotsDir, identity) {
3317
3494
  }
3318
3495
  return snapshot.data;
3319
3496
  }
3320
- function listUsageSnapshots(fs3, snapshotsDir) {
3321
- return fs3.readdir(snapshotsDir).flatMap((name) => {
3497
+ function listUsageSnapshots(fs7, snapshotsDir) {
3498
+ return fs7.readdir(snapshotsDir).flatMap((name) => {
3322
3499
  if (!name.endsWith(".json")) {
3323
3500
  return [];
3324
3501
  }
@@ -3326,7 +3503,7 @@ function listUsageSnapshots(fs3, snapshotsDir) {
3326
3503
  if (!isIdentityName(identity)) {
3327
3504
  return [];
3328
3505
  }
3329
- const snapshot = readUsageSnapshot(fs3, snapshotsDir, identity);
3506
+ const snapshot = readUsageSnapshot(fs7, snapshotsDir, identity);
3330
3507
  return snapshot === void 0 ? [] : [snapshot];
3331
3508
  }).sort((left, right) => left.identity.localeCompare(right.identity));
3332
3509
  }
@@ -3386,7 +3563,7 @@ function ensureHeadroom(params) {
3386
3563
  }
3387
3564
 
3388
3565
  // src/headroom/settings.ts
3389
- var import_node_crypto6 = require("node:crypto");
3566
+ var import_node_crypto7 = require("node:crypto");
3390
3567
  function settingsOf(config) {
3391
3568
  return {
3392
3569
  ...config.mode === void 0 ? {} : { mode: config.mode },
@@ -3411,7 +3588,7 @@ function settingsEnv(settings) {
3411
3588
  return settings.rolloutChannel === void 0 ? {} : { HEADROOM_ROLLOUT_CHANNEL: settings.rolloutChannel };
3412
3589
  }
3413
3590
  function hashSettings(settings) {
3414
- 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");
3415
3592
  }
3416
3593
 
3417
3594
  // src/headroom/source.ts
@@ -3493,9 +3670,9 @@ function compareVersions(a, b) {
3493
3670
  }
3494
3671
  var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
3495
3672
  async function runSupervisor(initialConfig, ports, options = {}) {
3496
- const { fs: fs3, paths } = ports;
3673
+ const { fs: fs7, paths } = ports;
3497
3674
  let config = initialConfig;
3498
- const previousState = readHeadroomState(fs3, paths.headroomStateFile);
3675
+ const previousState = readHeadroomState(fs7, paths.headroomStateFile);
3499
3676
  if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
3500
3677
  ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
3501
3678
  return 0;
@@ -3504,19 +3681,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3504
3681
  let lastPort = previousState?.lastPort;
3505
3682
  const sticky = () => lastPort === void 0 ? {} : { lastPort };
3506
3683
  const ownsState = () => {
3507
- const owner = readHeadroomState(fs3, paths.headroomStateFile)?.supervisorPid;
3684
+ const owner = readHeadroomState(fs7, paths.headroomStateFile)?.supervisorPid;
3508
3685
  return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
3509
3686
  };
3510
3687
  const writeOwnState = (state) => {
3511
3688
  if (!ownsState()) {
3512
3689
  return false;
3513
3690
  }
3514
- writeHeadroomState(fs3, paths.headroomStateFile, state);
3691
+ writeHeadroomState(fs7, paths.headroomStateFile, state);
3515
3692
  return true;
3516
3693
  };
3517
3694
  const fail = (message) => {
3518
3695
  if (writeOwnState({ lastError: message, ...sticky() })) {
3519
- fs3.removeRecursive(paths.headroomLockFile);
3696
+ fs7.removeRecursive(paths.headroomLockFile);
3520
3697
  }
3521
3698
  ports.log(`agent-shim headroom supervisor: ${message}`);
3522
3699
  return 1;
@@ -3565,7 +3742,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3565
3742
  const settingsHash = hashSettings(config.settings);
3566
3743
  const allowlist = allowlistOf(ports);
3567
3744
  const allowlistHash = hashAllowlist(allowlist);
3568
- pruneDeadSessions(fs3, paths.headroomSessionsDir, ports.isRunning);
3745
+ pruneDeadSessions(fs7, paths.headroomSessionsDir, ports.isRunning);
3569
3746
  const ownsNow = ownsState();
3570
3747
  if (!ownsNow && !superseded) {
3571
3748
  ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
@@ -3617,7 +3794,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3617
3794
  ...sticky()
3618
3795
  });
3619
3796
  if (recorded) {
3620
- fs3.removeRecursive(paths.headroomLockFile);
3797
+ fs7.removeRecursive(paths.headroomLockFile);
3621
3798
  }
3622
3799
  idleSince = void 0;
3623
3800
  ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
@@ -3632,7 +3809,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3632
3809
  }
3633
3810
  } else {
3634
3811
  if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
3635
- if (sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3812
+ if (sessionsForSupervisor(fs7, paths.headroomSessionsDir, ports.ownPid).length === 0) {
3636
3813
  ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
3637
3814
  ports.stopProcess(headroomPid);
3638
3815
  headroomPid = void 0;
@@ -3641,7 +3818,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3641
3818
  continue;
3642
3819
  }
3643
3820
  }
3644
- const sessions = sessionsForSupervisor(fs3, paths.headroomSessionsDir, ports.ownPid);
3821
+ const sessions = sessionsForSupervisor(fs7, paths.headroomSessionsDir, ports.ownPid);
3645
3822
  if (sessions.length === 0) {
3646
3823
  idleSince ??= ports.now();
3647
3824
  const idleMs = ports.now() - idleSince;
@@ -3663,9 +3840,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
3663
3840
  await ports.sleep(HEADROOM_POLL_MS);
3664
3841
  }
3665
3842
  }
3666
- function currentHeadroomAllowlist(fs3, paths) {
3667
- const providers = readAllProviders(fs3, paths.providersDir).map((entry) => entry.provider);
3668
- 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))));
3669
3846
  }
3670
3847
  function allowlistOf(ports) {
3671
3848
  return currentHeadroomAllowlist(ports.fs, ports.paths);
@@ -3731,63 +3908,735 @@ function evaluateAmbientCredentialGuard(params) {
3731
3908
  }
3732
3909
 
3733
3910
  // src/paths.ts
3734
- var import_node_fs = __toESM(require("node:fs"), 1);
3911
+ var import_node_fs2 = __toESM(require("node:fs"), 1);
3735
3912
  var import_node_os = __toESM(require("node:os"), 1);
3736
- var import_node_path8 = __toESM(require("node:path"), 1);
3913
+ var import_node_path9 = __toESM(require("node:path"), 1);
3737
3914
  function resolveAgentShimHome(env = process.env, home = import_node_os.default.homedir(), isDirectory = isExistingDirectory) {
3738
3915
  const fromEnv = env.AGENT_SHIM_HOME;
3739
3916
  if (fromEnv !== void 0 && fromEnv !== "") {
3740
3917
  return fromEnv;
3741
3918
  }
3742
- const current = import_node_path8.default.join(home, HOME_DIRNAME);
3743
- 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);
3744
3921
  return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
3745
3922
  }
3746
3923
  function isExistingDirectory(candidate) {
3747
- return import_node_fs.default.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
3924
+ return import_node_fs2.default.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
3748
3925
  }
3749
3926
  function resolveClaudeHome() {
3750
3927
  const fromEnv = process.env.AGENT_SHIM_CLAUDE_HOME;
3751
3928
  if (fromEnv !== void 0 && fromEnv !== "") {
3752
3929
  return fromEnv;
3753
3930
  }
3754
- 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");
3755
3932
  }
3756
3933
  function buildLayoutPaths(root) {
3757
3934
  return {
3758
3935
  root,
3759
- identitiesDir: import_node_path8.default.join(root, "identities"),
3760
- configProfilesDir: import_node_path8.default.join(root, "config-profiles"),
3761
- providersDir: import_node_path8.default.join(root, "providers"),
3762
- directoryRulesFile: import_node_path8.default.join(root, "directory-rules.json"),
3763
- activeIdentityFile: import_node_path8.default.join(root, "active-identity"),
3764
- globalConfigFile: import_node_path8.default.join(root, "config.json"),
3765
- categoriesLocalFile: import_node_path8.default.join(root, "categories.local.json"),
3766
- claudeShimFile: import_node_path8.default.join(root, "claude-shim.json"),
3767
- headroomDir: import_node_path8.default.join(root, "headroom"),
3768
- headroomStateFile: import_node_path8.default.join(root, "headroom", "state.json"),
3769
- headroomLockFile: import_node_path8.default.join(root, "headroom", "start.lock"),
3770
- headroomSessionsDir: import_node_path8.default.join(root, "headroom", "sessions"),
3771
- logsDir: import_node_path8.default.join(root, "logs"),
3772
- headroomLogPath: import_node_path8.default.join(root, "logs", "headroom.log"),
3773
- frontdoorDir: import_node_path8.default.join(root, "frontdoor"),
3774
- frontdoorCaDir: import_node_path8.default.join(root, "frontdoor", "ca"),
3775
- frontdoorCaCertFile: import_node_path8.default.join(root, "frontdoor", "ca", "ca.pem"),
3776
- frontdoorCaKeyFile: import_node_path8.default.join(root, "frontdoor", "ca", "ca.key"),
3777
- frontdoorCaBundlesDir: import_node_path8.default.join(root, "frontdoor", "ca", "bundles"),
3778
- frontdoorStateFile: import_node_path8.default.join(root, "frontdoor", "state.json"),
3779
- frontdoorLockFile: import_node_path8.default.join(root, "frontdoor", "start.lock"),
3780
- frontdoorSessionsDir: import_node_path8.default.join(root, "frontdoor", "sessions"),
3781
- frontdoorLogPath: import_node_path8.default.join(root, "logs", "frontdoor.log"),
3782
- usageDir: import_node_path8.default.join(root, "usage"),
3783
- usageLogDir: import_node_path8.default.join(root, "usage", "log"),
3784
- usageSnapshotsDir: import_node_path8.default.join(root, "usage", "snapshots"),
3785
- 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")
3786
3963
  };
3787
3964
  }
3788
3965
  function resolveLayoutPaths() {
3789
3966
  return buildLayoutPaths(resolveAgentShimHome());
3790
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
+ }
3791
4640
  // Annotate the CommonJS export names for ESM import in node:
3792
4641
  0 && (module.exports = {
3793
4642
  AMBIENT_CREDENTIAL_VARS,
@@ -3802,6 +4651,9 @@ function resolveLayoutPaths() {
3802
4651
  CredentialCacheSchema,
3803
4652
  CredentialSchema,
3804
4653
  CredentialSourceSchema,
4654
+ DirectoryRuleAlreadyExistsError,
4655
+ DirectoryRuleMissingTargetError,
4656
+ DirectoryRuleNotFoundError,
3805
4657
  DirectoryRuleSchema,
3806
4658
  DirectoryRulesSchema,
3807
4659
  EXIT_FAILURE,
@@ -3811,20 +4663,38 @@ function resolveLayoutPaths() {
3811
4663
  FrontDoorStartError,
3812
4664
  GlobalConfigSchema,
3813
4665
  HeadroomStartError,
4666
+ IdentityAlreadyExistsError,
4667
+ IdentityNotFoundError,
3814
4668
  IdentitySchema,
4669
+ InvalidCategoryNameError,
4670
+ InvalidIdentityNameError,
4671
+ InvalidProviderNameError,
4672
+ LegacyProviderFileError,
3815
4673
  PoolNameSchema,
4674
+ PoolNotFoundError,
3816
4675
  PoolSchema,
3817
4676
  PortableConfigSchema,
4677
+ ProfileAlreadyExistsError,
4678
+ ProfileNotFoundError,
4679
+ ProviderAlreadyExistsError,
4680
+ ProviderKindMismatchError,
4681
+ ProviderNotFoundError,
3818
4682
  ProviderSchema,
3819
4683
  ROUTED_PATH_PREFIX,
3820
4684
  UsageError,
3821
4685
  UsageSnapshotError,
3822
4686
  UsageSnapshotSchema,
3823
4687
  WhenSchema,
4688
+ addDirectoryRule,
4689
+ addIdentity,
4690
+ addPool,
4691
+ addProvider,
3824
4692
  buildEntryFacts,
3825
4693
  buildLayoutPaths,
3826
4694
  carryOver,
3827
4695
  createLeafCache,
4696
+ createProfile,
4697
+ describeProviderEndpoint,
3828
4698
  detectAmbientCredential,
3829
4699
  ensureCa,
3830
4700
  ensureFrontDoor,
@@ -3833,16 +4703,37 @@ function resolveLayoutPaths() {
3833
4703
  formatAmbientCredentialGuardMessage,
3834
4704
  forwardableHeaders,
3835
4705
  generateCa,
4706
+ identityExists,
4707
+ isIdentityDirectoryName,
3836
4708
  isInterceptedHost,
4709
+ listDirectoryRules,
4710
+ listIdentities,
4711
+ listProfiles,
4712
+ listProviders,
3837
4713
  listUsageSnapshots,
3838
4714
  mintLeaf,
3839
4715
  parseConnectTarget,
4716
+ profileExists,
4717
+ providerExists,
4718
+ readActiveIdentity,
4719
+ readDirectoryRules,
3840
4720
  readFarmManifest,
4721
+ readGlobalConfig,
4722
+ readIdentity,
4723
+ readPools,
4724
+ readProfile,
4725
+ readProvider,
3841
4726
  readUsageSnapshot,
3842
4727
  realConnectCertStore,
3843
4728
  realConnectEffects,
3844
4729
  recoverFarm,
3845
4730
  recoveryDiagnostics,
4731
+ removeDirectoryRule,
4732
+ removeIdentity,
4733
+ removePool,
4734
+ removeProfile,
4735
+ removeProvider,
4736
+ requirePool,
3846
4737
  resolveAgentShimHome,
3847
4738
  resolveClaudeHome,
3848
4739
  resolveDecisions,
@@ -3852,7 +4743,20 @@ function resolveLayoutPaths() {
3852
4743
  runFrontDoorSupervisor,
3853
4744
  runSupervisor,
3854
4745
  servedByPipeline,
4746
+ setAllowAmbientCredential,
4747
+ setDefaultConfigProfile,
4748
+ setGlobalDefaultProfile,
4749
+ setIdentityCredential,
4750
+ setPool,
4751
+ setProfileCategories,
4752
+ setProfileEntries,
4753
+ setProfileLaunchFlags,
4754
+ setProfileMetadata,
3855
4755
  snapshotPath,
3856
4756
  startConnectServer,
3857
- topLevelNames
4757
+ topLevelNames,
4758
+ updateDirectoryRule,
4759
+ updateProvider,
4760
+ useIdentity,
4761
+ writeDirectoryRules
3858
4762
  });