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/README.md +2 -1
- package/dist/cli.cjs +3714 -3687
- package/dist/index.cjs +1087 -183
- package/dist/index.mjs +1017 -168
- package/dist/types/cli/bool.d.ts +8 -0
- package/dist/types/cli/parsers.d.ts +40 -0
- package/dist/types/configProfilesStore.d.ts +61 -0
- package/dist/types/credential.d.ts +136 -0
- package/dist/types/credentialCache.d.ts +46 -0
- package/dist/types/directoryRulesStore.d.ts +43 -0
- package/dist/types/frontdoor/connect.d.ts +7 -6
- package/dist/types/frontdoor/route.d.ts +112 -0
- package/dist/types/frontdoor/tap.d.ts +2 -1
- package/dist/types/headroom/headers.d.ts +11 -0
- package/dist/types/identityStore.d.ts +85 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/launcher/flags.d.ts +96 -0
- package/dist/types/launcher/identity.d.ts +95 -0
- package/dist/types/poolStore.d.ts +18 -0
- package/dist/types/providersStore.d.ts +137 -0
- package/package.json +1 -1
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
|
|
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) =>
|
|
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
|
|
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
|
|
1329
|
-
var
|
|
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
|
|
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,
|
|
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(
|
|
1427
|
-
const raw =
|
|
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 =
|
|
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(
|
|
1486
|
-
const absolute =
|
|
1487
|
-
const stat =
|
|
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 = [...
|
|
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 =
|
|
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(
|
|
1664
|
+
function reconciliationScope(fs7, farmRoot, manifest) {
|
|
1513
1665
|
if (manifest !== void 0) {
|
|
1514
1666
|
return topLevelScopeRoots(manifest.materialised);
|
|
1515
1667
|
}
|
|
1516
|
-
return [...
|
|
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(
|
|
1519
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1670
|
+
function collectFarmListing(fs7, farmRoot, scopeRoots) {
|
|
1671
|
+
return scopeRoots.flatMap((root) => listSubtree(fs7, farmRoot, root));
|
|
1520
1672
|
}
|
|
1521
|
-
function collectCanonicalCounterparts(
|
|
1673
|
+
function collectCanonicalCounterparts(fs7, claudeHome, farmListing) {
|
|
1522
1674
|
const out = [];
|
|
1523
1675
|
for (const entry of farmListing) {
|
|
1524
|
-
const absolute =
|
|
1525
|
-
const stat =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
1728
|
+
const absolute = import_node_path5.default.join(params.scratchRoot, entry.rel);
|
|
1577
1729
|
if (entry.kind === "link") {
|
|
1578
|
-
params.fs.mkdirp(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1718
|
-
const canonical =
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
1831
|
-
const previousRoot =
|
|
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
|
|
1889
|
-
var
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
2140
|
-
const caPrivateKey = (0,
|
|
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
|
|
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(
|
|
2421
|
+
function readFrontDoorState(fs7, stateFile) {
|
|
2264
2422
|
let raw;
|
|
2265
2423
|
try {
|
|
2266
|
-
raw =
|
|
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(
|
|
2281
|
-
|
|
2282
|
-
|
|
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(
|
|
2290
|
-
|
|
2291
|
-
|
|
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(
|
|
2452
|
+
function listFrontDoorSessions(fs7, sessionsDir) {
|
|
2295
2453
|
const sessions = [];
|
|
2296
|
-
for (const name of
|
|
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(
|
|
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(
|
|
2312
|
-
|
|
2469
|
+
function removeFrontDoorSession(fs7, sessionsDir, pid) {
|
|
2470
|
+
fs7.removeRecursive(import_node_path6.default.join(sessionsDir, `${String(pid)}.json`));
|
|
2313
2471
|
}
|
|
2314
|
-
function pruneDeadFrontDoorSessions(
|
|
2472
|
+
function pruneDeadFrontDoorSessions(fs7, sessionsDir, isRunning) {
|
|
2315
2473
|
const removed = [];
|
|
2316
|
-
for (const session of listFrontDoorSessions(
|
|
2474
|
+
for (const session of listFrontDoorSessions(fs7, sessionsDir)) {
|
|
2317
2475
|
if (!isRunning(session.pid)) {
|
|
2318
|
-
removeFrontDoorSession(
|
|
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:
|
|
2331
|
-
const previous = readFrontDoorState(
|
|
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(
|
|
2342
|
-
|
|
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(
|
|
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(
|
|
2384
|
-
|
|
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(
|
|
2394
|
-
if (listFrontDoorSessions(
|
|
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(
|
|
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(
|
|
2457
|
-
return
|
|
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
|
|
2517
|
-
(0,
|
|
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
|
|
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
|
|
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
|
|
2919
|
+
return fs2.readFileSync(file, "utf8");
|
|
2744
2920
|
} catch (error) {
|
|
2745
|
-
if (
|
|
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
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
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
|
|
3106
|
+
var import_node_crypto6 = require("node:crypto");
|
|
2930
3107
|
|
|
2931
3108
|
// src/headroom/state.ts
|
|
2932
|
-
var
|
|
2933
|
-
var
|
|
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(
|
|
3144
|
+
function readHeadroomState(fs7, stateFile) {
|
|
2968
3145
|
try {
|
|
2969
|
-
return parseJson(
|
|
3146
|
+
return parseJson(fs7.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
2970
3147
|
} catch {
|
|
2971
3148
|
return void 0;
|
|
2972
3149
|
}
|
|
2973
3150
|
}
|
|
2974
|
-
function writeHeadroomState(
|
|
2975
|
-
|
|
2976
|
-
|
|
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,
|
|
3171
|
+
return (0, import_node_crypto5.createHash)("sha256").update(allowlist.join("\n")).digest("hex");
|
|
2995
3172
|
}
|
|
2996
|
-
function readAllProviders(
|
|
3173
|
+
function readAllProviders(fs7, providersDir) {
|
|
2997
3174
|
const result = [];
|
|
2998
|
-
for (const name of
|
|
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(
|
|
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(
|
|
3014
|
-
|
|
3015
|
-
|
|
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(
|
|
3019
|
-
|
|
3195
|
+
function removeSession(fs7, sessionsDir, pid) {
|
|
3196
|
+
fs7.removeRecursive(import_node_path7.default.join(sessionsDir, `${String(pid)}.json`));
|
|
3020
3197
|
}
|
|
3021
|
-
function listSessions(
|
|
3198
|
+
function listSessions(fs7, sessionsDir) {
|
|
3022
3199
|
const sessions = [];
|
|
3023
|
-
for (const name of
|
|
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(
|
|
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(
|
|
3039
|
-
return listSessions(
|
|
3215
|
+
function sessionsForSupervisor(fs7, sessionsDir, supervisorPid) {
|
|
3216
|
+
return listSessions(fs7, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
3040
3217
|
}
|
|
3041
|
-
function pruneDeadSessions(
|
|
3218
|
+
function pruneDeadSessions(fs7, sessionsDir, isRunning) {
|
|
3042
3219
|
const removed = [];
|
|
3043
|
-
for (const session of listSessions(
|
|
3220
|
+
for (const session of listSessions(fs7, sessionsDir)) {
|
|
3044
3221
|
if (!isRunning(session.pid)) {
|
|
3045
|
-
removeSession(
|
|
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(
|
|
3228
|
+
function readStartLock(fs7, lockFile) {
|
|
3052
3229
|
try {
|
|
3053
|
-
return parseJson(
|
|
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,
|
|
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
|
|
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
|
|
3477
|
+
return import_node_path8.default.join(snapshotsDir, `${identity}.json`);
|
|
3301
3478
|
}
|
|
3302
|
-
function readUsageSnapshot(
|
|
3479
|
+
function readUsageSnapshot(fs7, snapshotsDir, identity) {
|
|
3303
3480
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3304
|
-
const raw =
|
|
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(
|
|
3321
|
-
return
|
|
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(
|
|
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
|
|
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,
|
|
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:
|
|
3673
|
+
const { fs: fs7, paths } = ports;
|
|
3497
3674
|
let config = initialConfig;
|
|
3498
|
-
const previousState = readHeadroomState(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
3667
|
-
const providers = readAllProviders(
|
|
3668
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
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
|
|
3911
|
+
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
3735
3912
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
3736
|
-
var
|
|
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 =
|
|
3743
|
-
const legacy =
|
|
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
|
|
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
|
|
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:
|
|
3760
|
-
configProfilesDir:
|
|
3761
|
-
providersDir:
|
|
3762
|
-
directoryRulesFile:
|
|
3763
|
-
activeIdentityFile:
|
|
3764
|
-
globalConfigFile:
|
|
3765
|
-
categoriesLocalFile:
|
|
3766
|
-
claudeShimFile:
|
|
3767
|
-
headroomDir:
|
|
3768
|
-
headroomStateFile:
|
|
3769
|
-
headroomLockFile:
|
|
3770
|
-
headroomSessionsDir:
|
|
3771
|
-
logsDir:
|
|
3772
|
-
headroomLogPath:
|
|
3773
|
-
frontdoorDir:
|
|
3774
|
-
frontdoorCaDir:
|
|
3775
|
-
frontdoorCaCertFile:
|
|
3776
|
-
frontdoorCaKeyFile:
|
|
3777
|
-
frontdoorCaBundlesDir:
|
|
3778
|
-
frontdoorStateFile:
|
|
3779
|
-
frontdoorLockFile:
|
|
3780
|
-
frontdoorSessionsDir:
|
|
3781
|
-
frontdoorLogPath:
|
|
3782
|
-
usageDir:
|
|
3783
|
-
usageLogDir:
|
|
3784
|
-
usageSnapshotsDir:
|
|
3785
|
-
usagePicksFile:
|
|
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
|
});
|