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