agent-shim 7.7.0 → 7.9.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 +4 -2
- package/dist/cli.cjs +3983 -3955
- package/dist/index.cjs +3214 -173
- package/dist/index.mjs +3136 -158
- package/dist/types/checkReport.d.ts +154 -0
- package/dist/types/claudeShim.d.ts +114 -0
- package/dist/types/cli/bool.d.ts +8 -0
- package/dist/types/cli/commandDeps.d.ts +28 -0
- package/dist/types/cli/envBool.d.ts +11 -0
- package/dist/types/codex/http.d.ts +15 -0
- package/dist/types/configProfilesStore.d.ts +61 -0
- package/dist/types/configure.d.ts +170 -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/doctorReport.d.ts +117 -0
- package/dist/types/frontdoor/custody.d.ts +32 -0
- package/dist/types/frontdoor/headroomHop.d.ts +22 -0
- package/dist/types/frontdoor/pipeline.d.ts +109 -0
- package/dist/types/frontdoor/server.d.ts +44 -0
- package/dist/types/headroom/headers.d.ts +11 -0
- package/dist/types/identityStore.d.ts +85 -0
- package/dist/types/index.d.ts +12 -1
- package/dist/types/launcher/cascade.d.ts +42 -0
- package/dist/types/launcher/flags.d.ts +96 -0
- package/dist/types/launcher/identity.d.ts +95 -0
- package/dist/types/launcher/pool.d.ts +44 -0
- package/dist/types/pathNorm.d.ts +14 -0
- package/dist/types/poolPickReport.d.ts +44 -0
- package/dist/types/poolStore.d.ts +18 -0
- package/dist/types/providersStore.d.ts +137 -0
- package/dist/types/realPorts.d.ts +102 -0
- package/dist/types/usage/middleware.d.ts +17 -0
- package/dist/types/usage/pick.d.ts +62 -0
- package/dist/types/usage/plan.d.ts +18 -0
- package/dist/types/usage/poolPick.d.ts +31 -0
- package/dist/types/usage/preflight.d.ts +20 -0
- package/dist/types/usage/rateLimit.d.ts +25 -0
- package/dist/types/usage/scan.d.ts +24 -0
- package/dist/types/usage/store.d.ts +43 -0
- package/dist/types/versionDiscovery.d.ts +47 -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,56 +54,168 @@ __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,
|
|
86
|
+
checkReportHasWarnings: () => checkReportHasWarnings,
|
|
87
|
+
checkReportToJson: () => checkReportToJson,
|
|
88
|
+
collectCheckReport: () => collectCheckReport,
|
|
89
|
+
collectDoctorReport: () => collectDoctorReport,
|
|
67
90
|
createLeafCache: () => createLeafCache,
|
|
91
|
+
createProfile: () => createProfile,
|
|
92
|
+
describeProviderEndpoint: () => describeProviderEndpoint,
|
|
68
93
|
detectAmbientCredential: () => detectAmbientCredential,
|
|
69
94
|
ensureCa: () => ensureCa,
|
|
70
95
|
ensureFrontDoor: () => ensureFrontDoor,
|
|
71
96
|
ensureHeadroom: () => ensureHeadroom,
|
|
72
97
|
evaluateAmbientCredentialGuard: () => evaluateAmbientCredentialGuard,
|
|
73
98
|
formatAmbientCredentialGuardMessage: () => formatAmbientCredentialGuardMessage,
|
|
99
|
+
formatCheckReport: () => formatCheckReport,
|
|
100
|
+
formatDoctorReport: () => formatDoctorReport,
|
|
74
101
|
forwardableHeaders: () => forwardableHeaders,
|
|
75
102
|
generateCa: () => generateCa,
|
|
103
|
+
identityExists: () => identityExists,
|
|
104
|
+
isIdentityDirectoryName: () => isIdentityDirectoryName,
|
|
76
105
|
isInterceptedHost: () => isInterceptedHost,
|
|
106
|
+
listDirectoryRules: () => listDirectoryRules,
|
|
107
|
+
listIdentities: () => listIdentities,
|
|
108
|
+
listProfiles: () => listProfiles,
|
|
109
|
+
listProviders: () => listProviders,
|
|
77
110
|
listUsageSnapshots: () => listUsageSnapshots,
|
|
78
111
|
mintLeaf: () => mintLeaf,
|
|
79
112
|
parseConnectTarget: () => parseConnectTarget,
|
|
113
|
+
profileExists: () => profileExists,
|
|
114
|
+
providerExists: () => providerExists,
|
|
115
|
+
readActiveIdentity: () => readActiveIdentity,
|
|
116
|
+
readDirectoryRules: () => readDirectoryRules,
|
|
80
117
|
readFarmManifest: () => readFarmManifest,
|
|
118
|
+
readGlobalConfig: () => readGlobalConfig,
|
|
119
|
+
readIdentity: () => readIdentity,
|
|
120
|
+
readPools: () => readPools,
|
|
121
|
+
readProfile: () => readProfile,
|
|
122
|
+
readProvider: () => readProvider,
|
|
81
123
|
readUsageSnapshot: () => readUsageSnapshot,
|
|
82
124
|
realConnectCertStore: () => realConnectCertStore,
|
|
83
125
|
realConnectEffects: () => realConnectEffects,
|
|
84
126
|
recoverFarm: () => recoverFarm,
|
|
85
127
|
recoveryDiagnostics: () => recoveryDiagnostics,
|
|
128
|
+
removeDirectoryRule: () => removeDirectoryRule,
|
|
129
|
+
removeIdentity: () => removeIdentity,
|
|
130
|
+
removePool: () => removePool,
|
|
131
|
+
removeProfile: () => removeProfile,
|
|
132
|
+
removeProvider: () => removeProvider,
|
|
133
|
+
requirePool: () => requirePool,
|
|
86
134
|
resolveAgentShimHome: () => resolveAgentShimHome,
|
|
87
135
|
resolveClaudeHome: () => resolveClaudeHome,
|
|
88
136
|
resolveDecisions: () => resolveDecisions,
|
|
89
137
|
resolveLayoutPaths: () => resolveLayoutPaths,
|
|
90
138
|
resolveSupervisorConfig: () => resolveSupervisorConfig,
|
|
91
139
|
resyncFarm: () => resyncFarm,
|
|
140
|
+
runCheck: () => runCheck,
|
|
141
|
+
runDoctor: () => runDoctor,
|
|
92
142
|
runFrontDoorSupervisor: () => runFrontDoorSupervisor,
|
|
93
143
|
runSupervisor: () => runSupervisor,
|
|
94
144
|
servedByPipeline: () => servedByPipeline,
|
|
145
|
+
setAllowAmbientCredential: () => setAllowAmbientCredential,
|
|
146
|
+
setDefaultConfigProfile: () => setDefaultConfigProfile,
|
|
147
|
+
setGlobalDefaultProfile: () => setGlobalDefaultProfile,
|
|
148
|
+
setIdentityCredential: () => setIdentityCredential,
|
|
149
|
+
setPool: () => setPool,
|
|
150
|
+
setProfileCategories: () => setProfileCategories,
|
|
151
|
+
setProfileEntries: () => setProfileEntries,
|
|
152
|
+
setProfileLaunchFlags: () => setProfileLaunchFlags,
|
|
153
|
+
setProfileMetadata: () => setProfileMetadata,
|
|
95
154
|
snapshotPath: () => snapshotPath,
|
|
96
155
|
startConnectServer: () => startConnectServer,
|
|
97
|
-
topLevelNames: () => topLevelNames
|
|
156
|
+
topLevelNames: () => topLevelNames,
|
|
157
|
+
updateDirectoryRule: () => updateDirectoryRule,
|
|
158
|
+
updateProvider: () => updateProvider,
|
|
159
|
+
useIdentity: () => useIdentity,
|
|
160
|
+
writeDirectoryRules: () => writeDirectoryRules
|
|
98
161
|
});
|
|
99
162
|
module.exports = __toCommonJS(index_exports);
|
|
100
163
|
|
|
101
164
|
// src/config/classify.ts
|
|
102
165
|
var import_picomatch = __toESM(require("picomatch"), 1);
|
|
103
166
|
|
|
167
|
+
// src/config/categories.default.json
|
|
168
|
+
var categories_default_default = {
|
|
169
|
+
secret: [".credentials.json", "backups", "settings.json.bak*"],
|
|
170
|
+
runtime: [
|
|
171
|
+
"daemon*",
|
|
172
|
+
".git*",
|
|
173
|
+
".DS_Store",
|
|
174
|
+
"mcp-needs-auth-cache.json",
|
|
175
|
+
"codex-quota.json",
|
|
176
|
+
"file-transfers",
|
|
177
|
+
"shell-snapshots",
|
|
178
|
+
"statsig",
|
|
179
|
+
"telemetry",
|
|
180
|
+
"stats-cache.json",
|
|
181
|
+
"usage-data",
|
|
182
|
+
"ide",
|
|
183
|
+
"cache",
|
|
184
|
+
"scheduled_tasks.lock"
|
|
185
|
+
],
|
|
186
|
+
history: [
|
|
187
|
+
"projects",
|
|
188
|
+
"sessions",
|
|
189
|
+
"session-env",
|
|
190
|
+
"teams",
|
|
191
|
+
"tasks",
|
|
192
|
+
"todos",
|
|
193
|
+
"history.jsonl",
|
|
194
|
+
"transcripts",
|
|
195
|
+
"paste-cache",
|
|
196
|
+
"file-history",
|
|
197
|
+
"plans",
|
|
198
|
+
"workflows",
|
|
199
|
+
"jobs",
|
|
200
|
+
"debug",
|
|
201
|
+
"downloads",
|
|
202
|
+
"chrome"
|
|
203
|
+
],
|
|
204
|
+
knowledge: [
|
|
205
|
+
"skills",
|
|
206
|
+
"agents",
|
|
207
|
+
"rules",
|
|
208
|
+
"memory",
|
|
209
|
+
"commands",
|
|
210
|
+
"plugins",
|
|
211
|
+
"hooks",
|
|
212
|
+
"AGENTS.md",
|
|
213
|
+
"CLAUDE.md",
|
|
214
|
+
"README.md"
|
|
215
|
+
],
|
|
216
|
+
settings: ["settings.json", "settings.local.json"]
|
|
217
|
+
};
|
|
218
|
+
|
|
104
219
|
// src/config/schema.ts
|
|
105
220
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
106
221
|
var import_zod = require("zod");
|
|
@@ -343,6 +458,11 @@ var SHIPPED_CATEGORY_DEFAULTS = Object.freeze({
|
|
|
343
458
|
settings: true
|
|
344
459
|
});
|
|
345
460
|
|
|
461
|
+
// src/config/store.ts
|
|
462
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
463
|
+
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
464
|
+
var import_node_crypto = require("node:crypto");
|
|
465
|
+
|
|
346
466
|
// src/config/load.ts
|
|
347
467
|
var import_cosmiconfig = require("cosmiconfig");
|
|
348
468
|
|
|
@@ -361,6 +481,158 @@ var UsageError = class extends CliError {
|
|
|
361
481
|
}
|
|
362
482
|
};
|
|
363
483
|
|
|
484
|
+
// src/config/load.ts
|
|
485
|
+
function createExplorer(moduleName = "agent-shim") {
|
|
486
|
+
return (0, import_cosmiconfig.cosmiconfigSync)(moduleName, { searchPlaces: [] });
|
|
487
|
+
}
|
|
488
|
+
function isRecord(value) {
|
|
489
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
490
|
+
}
|
|
491
|
+
function captureEntryOrder(value) {
|
|
492
|
+
if (!isRecord(value)) {
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
const entries = value.entries;
|
|
496
|
+
if (!isRecord(entries)) {
|
|
497
|
+
return [];
|
|
498
|
+
}
|
|
499
|
+
return Object.keys(entries);
|
|
500
|
+
}
|
|
501
|
+
function captureRuleEntryOrders(value) {
|
|
502
|
+
if (!isRecord(value)) {
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
const rules = value.rules;
|
|
506
|
+
if (!Array.isArray(rules)) {
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
return rules.map((rule) => captureEntryOrder(rule));
|
|
510
|
+
}
|
|
511
|
+
var ConfigValidationError = class extends CliError {
|
|
512
|
+
constructor(filepath, issues) {
|
|
513
|
+
const detail = issues.map((issue) => ` ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n");
|
|
514
|
+
super(`Invalid configuration in ${filepath}:
|
|
515
|
+
${detail}`);
|
|
516
|
+
this.filepath = filepath;
|
|
517
|
+
this.issues = issues;
|
|
518
|
+
this.name = "ConfigValidationError";
|
|
519
|
+
}
|
|
520
|
+
filepath;
|
|
521
|
+
issues;
|
|
522
|
+
};
|
|
523
|
+
function cosmiconfigReader(explorer = createExplorer()) {
|
|
524
|
+
return (filepath) => {
|
|
525
|
+
let result;
|
|
526
|
+
try {
|
|
527
|
+
result = explorer.load(filepath);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
const code = isRecord(error) ? error.code : void 0;
|
|
530
|
+
if (code === "ENOENT") {
|
|
531
|
+
return void 0;
|
|
532
|
+
}
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
if (result === null || result.isEmpty === true) {
|
|
536
|
+
return void 0;
|
|
537
|
+
}
|
|
538
|
+
return result.config;
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function loadConfigFile(filepath, schema, read = cosmiconfigReader()) {
|
|
542
|
+
const raw = read(filepath);
|
|
543
|
+
if (raw === void 0) {
|
|
544
|
+
return void 0;
|
|
545
|
+
}
|
|
546
|
+
const parsed = schema.safeParse(raw);
|
|
547
|
+
if (!parsed.success) {
|
|
548
|
+
throw new ConfigValidationError(filepath, parsed.error.issues);
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
filepath,
|
|
552
|
+
config: parsed.data,
|
|
553
|
+
entryOrder: captureEntryOrder(raw),
|
|
554
|
+
ruleEntryOrders: captureRuleEntryOrders(raw)
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// src/config/store.ts
|
|
559
|
+
function isEnoent(error) {
|
|
560
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
561
|
+
}
|
|
562
|
+
var nodeStoreFs = {
|
|
563
|
+
readFileUtf8(filePath) {
|
|
564
|
+
try {
|
|
565
|
+
return import_node_fs.default.readFileSync(filePath, "utf8");
|
|
566
|
+
} catch (error) {
|
|
567
|
+
if (isEnoent(error)) {
|
|
568
|
+
return void 0;
|
|
569
|
+
}
|
|
570
|
+
throw error;
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
writeFileUtf8(filePath, contents) {
|
|
574
|
+
import_node_fs.default.writeFileSync(filePath, contents, "utf8");
|
|
575
|
+
},
|
|
576
|
+
renameSync(from, to) {
|
|
577
|
+
import_node_fs.default.renameSync(from, to);
|
|
578
|
+
},
|
|
579
|
+
mkdirSync(dirPath) {
|
|
580
|
+
import_node_fs.default.mkdirSync(dirPath, { recursive: true });
|
|
581
|
+
},
|
|
582
|
+
unlinkSync(filePath) {
|
|
583
|
+
try {
|
|
584
|
+
import_node_fs.default.unlinkSync(filePath);
|
|
585
|
+
} catch (error) {
|
|
586
|
+
if (!isEnoent(error)) {
|
|
587
|
+
throw error;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
function readJson(filePath, schema, storeFs = nodeStoreFs) {
|
|
593
|
+
const raw = storeFs.readFileUtf8(filePath);
|
|
594
|
+
if (raw === void 0) {
|
|
595
|
+
return void 0;
|
|
596
|
+
}
|
|
597
|
+
const parsed = JSON.parse(raw);
|
|
598
|
+
const result = schema.safeParse(parsed);
|
|
599
|
+
if (!result.success) {
|
|
600
|
+
throw new ConfigValidationError(filePath, result.error.issues);
|
|
601
|
+
}
|
|
602
|
+
return result.data;
|
|
603
|
+
}
|
|
604
|
+
function writeTextAtomic(filePath, contents, storeFs = nodeStoreFs) {
|
|
605
|
+
const dir = import_node_path2.default.dirname(filePath);
|
|
606
|
+
storeFs.mkdirSync(dir);
|
|
607
|
+
const tempPath = import_node_path2.default.join(dir, `.${import_node_path2.default.basename(filePath)}.${String(process.pid)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
|
|
608
|
+
storeFs.writeFileUtf8(tempPath, contents);
|
|
609
|
+
try {
|
|
610
|
+
storeFs.renameSync(tempPath, filePath);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
storeFs.unlinkSync(tempPath);
|
|
613
|
+
throw error;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
function writeJsonAtomic(filePath, data, storeFs = nodeStoreFs) {
|
|
617
|
+
writeTextAtomic(filePath, `${JSON.stringify(data, null, 2)}
|
|
618
|
+
`, storeFs);
|
|
619
|
+
}
|
|
620
|
+
function applyPatch(filePath, schema, patch, options = {}) {
|
|
621
|
+
const storeFs = options.storeFs ?? nodeStoreFs;
|
|
622
|
+
const existing = readJson(filePath, schema, storeFs) ?? options.defaults;
|
|
623
|
+
if (existing === void 0) {
|
|
624
|
+
throw new Error(`Cannot patch ${filePath}: it does not exist yet and no defaults were provided.`);
|
|
625
|
+
}
|
|
626
|
+
const merged = { ...existing, ...patch };
|
|
627
|
+
const result = schema.safeParse(merged);
|
|
628
|
+
if (!result.success) {
|
|
629
|
+
throw new ConfigValidationError(filePath, result.error.issues);
|
|
630
|
+
}
|
|
631
|
+
const validated = result.data;
|
|
632
|
+
writeJsonAtomic(filePath, validated, storeFs);
|
|
633
|
+
return validated;
|
|
634
|
+
}
|
|
635
|
+
|
|
364
636
|
// src/config/classify.ts
|
|
365
637
|
var GLOB_METACHARACTERS = /[*?[\]{}!()]/;
|
|
366
638
|
function isExactPattern(pattern) {
|
|
@@ -409,6 +681,13 @@ function compareClassificationPatterns(a, b) {
|
|
|
409
681
|
}
|
|
410
682
|
return a.ordinal - b.ordinal;
|
|
411
683
|
}
|
|
684
|
+
function loadClassification(paths) {
|
|
685
|
+
const overlay = readJson(paths.categoriesLocalFile, CategoryClassificationOverlaySchema);
|
|
686
|
+
return {
|
|
687
|
+
defaults: CategoryClassificationSchema.parse(categories_default_default),
|
|
688
|
+
...overlay === void 0 ? {} : { overlay }
|
|
689
|
+
};
|
|
690
|
+
}
|
|
412
691
|
function classifyEntries(names, options) {
|
|
413
692
|
const patterns = compileClassificationPatterns(options.defaults, options.overlay);
|
|
414
693
|
const classification = /* @__PURE__ */ new Map();
|
|
@@ -520,6 +799,7 @@ var import_picomatch3 = __toESM(require("picomatch"), 1);
|
|
|
520
799
|
|
|
521
800
|
// src/resolve/projects.ts
|
|
522
801
|
var NON_ALPHANUMERIC = /[^A-Za-z0-9]/g;
|
|
802
|
+
var NON_ALPHANUMERIC_CHARACTER = /[^A-Za-z0-9]/;
|
|
523
803
|
var WILDCARD_TOKENS = ["**", "*", "?"];
|
|
524
804
|
function encodeProjectPath(realPath) {
|
|
525
805
|
return realPath.replace(NON_ALPHANUMERIC, "-");
|
|
@@ -573,6 +853,64 @@ function encodeProjectPattern(fragment, options) {
|
|
|
573
853
|
}
|
|
574
854
|
return splitOnWildcards(expanded).map((piece) => piece.kind === "literal" ? encodeProjectPath(piece.text) : piece.text).join("");
|
|
575
855
|
}
|
|
856
|
+
function lossyCharacters(literal) {
|
|
857
|
+
const found = /* @__PURE__ */ new Set();
|
|
858
|
+
for (const character of literal) {
|
|
859
|
+
if (character !== "/" && NON_ALPHANUMERIC_CHARACTER.test(character)) {
|
|
860
|
+
found.add(character);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return [...found];
|
|
864
|
+
}
|
|
865
|
+
function detectEncodingAmbiguity(fragments, options) {
|
|
866
|
+
const ambiguities = [];
|
|
867
|
+
const encodedByFragment = /* @__PURE__ */ new Map();
|
|
868
|
+
for (const fragment of fragments) {
|
|
869
|
+
let encoded;
|
|
870
|
+
try {
|
|
871
|
+
encoded = encodeProjectPattern(fragment, { home: options.home });
|
|
872
|
+
} catch {
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
encodedByFragment.set(fragment, encoded);
|
|
876
|
+
const expanded = expandHome(fragment, options.home);
|
|
877
|
+
const literalText = splitOnWildcards(expanded).filter((piece) => piece.kind === "literal").map((piece) => piece.text).join("");
|
|
878
|
+
const lossy = lossyCharacters(literalText);
|
|
879
|
+
if (lossy.length > 0) {
|
|
880
|
+
const matched = options.existingNames?.filter((name) => name === encoded).length;
|
|
881
|
+
const suffix = matched === void 0 ? "" : ` It currently matches ${String(matched)} existing project directory name(s).`;
|
|
882
|
+
ambiguities.push({
|
|
883
|
+
fragment,
|
|
884
|
+
encoded,
|
|
885
|
+
reason: "lossy-characters",
|
|
886
|
+
detail: `The literal portion contains ${lossy.map((character) => JSON.stringify(character)).join(", ")}, which the encoding collapses to "-" exactly as it collapses "/". A different real path differing only in those characters would encode to the same name.${suffix}`
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const byEncoded = /* @__PURE__ */ new Map();
|
|
891
|
+
for (const [fragment, encoded] of encodedByFragment) {
|
|
892
|
+
const group = byEncoded.get(encoded);
|
|
893
|
+
if (group === void 0) {
|
|
894
|
+
byEncoded.set(encoded, [fragment]);
|
|
895
|
+
} else {
|
|
896
|
+
group.push(fragment);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
for (const [encoded, group] of byEncoded) {
|
|
900
|
+
if (group.length < 2) {
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
for (const fragment of group) {
|
|
904
|
+
ambiguities.push({
|
|
905
|
+
fragment,
|
|
906
|
+
encoded,
|
|
907
|
+
reason: "collides-with-sibling-pattern",
|
|
908
|
+
detail: `Encodes identically to ${group.filter((other) => other !== fragment).map((other) => JSON.stringify(other)).join(", ")}.`
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return ambiguities;
|
|
913
|
+
}
|
|
576
914
|
|
|
577
915
|
// src/resolve/match.ts
|
|
578
916
|
var GLOB_METACHARACTERS2 = /[*?[\]{}!()]/;
|
|
@@ -952,7 +1290,7 @@ function dedupeDiagnostics(diagnostics) {
|
|
|
952
1290
|
}
|
|
953
1291
|
|
|
954
1292
|
// src/resolve/plan.ts
|
|
955
|
-
var
|
|
1293
|
+
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
956
1294
|
function parentOf(rel) {
|
|
957
1295
|
const index = rel.lastIndexOf("/");
|
|
958
1296
|
return index === -1 ? "" : rel.slice(0, index);
|
|
@@ -1002,7 +1340,7 @@ function planFarm(params) {
|
|
|
1002
1340
|
}
|
|
1003
1341
|
return value;
|
|
1004
1342
|
};
|
|
1005
|
-
const linkTarget = (rel) =>
|
|
1343
|
+
const linkTarget = (rel) => import_node_path3.default.join(params.facts.claudeHome, rel);
|
|
1006
1344
|
const visit = (rel) => {
|
|
1007
1345
|
const fact = params.facts.entries.get(rel);
|
|
1008
1346
|
const decision = params.decisions.get(rel);
|
|
@@ -1048,6 +1386,9 @@ function planFarm(params) {
|
|
|
1048
1386
|
return { entries, materialised, links };
|
|
1049
1387
|
}
|
|
1050
1388
|
|
|
1389
|
+
// src/resolve/walk.ts
|
|
1390
|
+
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
1391
|
+
|
|
1051
1392
|
// src/resolve/extends.ts
|
|
1052
1393
|
function lineariseProfile(rootName, load) {
|
|
1053
1394
|
const stack = [];
|
|
@@ -1113,6 +1454,27 @@ function profileLayers(rootName, load, startId) {
|
|
|
1113
1454
|
}
|
|
1114
1455
|
|
|
1115
1456
|
// src/resolve/walk.ts
|
|
1457
|
+
function walkDirectoryAncestors(cwd, options) {
|
|
1458
|
+
const limit = import_node_path4.default.resolve(options.limit ?? options.home);
|
|
1459
|
+
const isReadable = options.isReadable ?? (() => true);
|
|
1460
|
+
const collected = [];
|
|
1461
|
+
let current = import_node_path4.default.resolve(cwd);
|
|
1462
|
+
for (; ; ) {
|
|
1463
|
+
if (!isReadable(current)) {
|
|
1464
|
+
break;
|
|
1465
|
+
}
|
|
1466
|
+
collected.push(current);
|
|
1467
|
+
if (current === limit) {
|
|
1468
|
+
break;
|
|
1469
|
+
}
|
|
1470
|
+
const parent = import_node_path4.default.dirname(current);
|
|
1471
|
+
if (parent === current) {
|
|
1472
|
+
break;
|
|
1473
|
+
}
|
|
1474
|
+
current = parent;
|
|
1475
|
+
}
|
|
1476
|
+
return collected.reverse();
|
|
1477
|
+
}
|
|
1116
1478
|
function assembleCascade(input) {
|
|
1117
1479
|
const layers = [];
|
|
1118
1480
|
const diagnostics = [];
|
|
@@ -1226,7 +1588,7 @@ function resolveDecisions(input) {
|
|
|
1226
1588
|
}
|
|
1227
1589
|
|
|
1228
1590
|
// src/launcher/farm.ts
|
|
1229
|
-
var
|
|
1591
|
+
var import_node_path6 = __toESM(require("node:path"), 1);
|
|
1230
1592
|
var import_zod2 = require("zod");
|
|
1231
1593
|
|
|
1232
1594
|
// src/resolve/reconcile.ts
|
|
@@ -1320,13 +1682,17 @@ function planReconciliation(params) {
|
|
|
1320
1682
|
}
|
|
1321
1683
|
|
|
1322
1684
|
// src/legacy.ts
|
|
1685
|
+
var ENV_PREFIX = "AGENT_SHIM_";
|
|
1686
|
+
var LEGACY_ENV_PREFIX = "CLAUDE_USE_";
|
|
1323
1687
|
var HOME_DIRNAME = ".agent-shim";
|
|
1324
1688
|
var LEGACY_HOME_DIRNAME = ".claude-use";
|
|
1689
|
+
var LEGACY_PORTABLE_CONFIG_FILENAME = ".claude-use.json";
|
|
1690
|
+
var LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME = ".claude-use.local.json";
|
|
1325
1691
|
var LEGACY_FARM_MANIFEST_FILENAME = ".claude-use-farm.json";
|
|
1326
1692
|
|
|
1327
1693
|
// src/launcher/lock.ts
|
|
1328
|
-
var
|
|
1329
|
-
var
|
|
1694
|
+
var import_node_crypto2 = require("node:crypto");
|
|
1695
|
+
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
1330
1696
|
var DEFAULT_STALE_AFTER_MS = 12e4;
|
|
1331
1697
|
var DEFAULT_RETRY_DELAY_MS = 50;
|
|
1332
1698
|
var DEFAULT_MAX_ATTEMPTS = 200;
|
|
@@ -1345,7 +1711,7 @@ var IdentityLockBusyError = class extends CliError {
|
|
|
1345
1711
|
holderPid;
|
|
1346
1712
|
};
|
|
1347
1713
|
function identityLockPath(dir, identity) {
|
|
1348
|
-
return
|
|
1714
|
+
return import_node_path5.default.join(dir, `.${identity}.lock`);
|
|
1349
1715
|
}
|
|
1350
1716
|
function parseLockRecord(raw) {
|
|
1351
1717
|
if (raw === void 0) {
|
|
@@ -1374,7 +1740,7 @@ function acquireIdentityLock(params) {
|
|
|
1374
1740
|
const staleAfterMs = params.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
|
|
1375
1741
|
const retryDelayMs = params.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
|
|
1376
1742
|
const maxAttempts = params.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
1377
|
-
const token = (0,
|
|
1743
|
+
const token = (0, import_node_crypto2.randomUUID)();
|
|
1378
1744
|
if (params.fs.lstat(params.dir) === void 0) {
|
|
1379
1745
|
params.fs.mkdirp(params.dir);
|
|
1380
1746
|
}
|
|
@@ -1423,8 +1789,8 @@ var FarmManifestSchema = import_zod2.z.strictObject({
|
|
|
1423
1789
|
materialised: import_zod2.z.array(import_zod2.z.string()),
|
|
1424
1790
|
links: import_zod2.z.array(import_zod2.z.strictObject({ rel: import_zod2.z.string(), target: import_zod2.z.string() }))
|
|
1425
1791
|
});
|
|
1426
|
-
function readFarmManifest(
|
|
1427
|
-
const raw =
|
|
1792
|
+
function readFarmManifest(fs10, farmRoot) {
|
|
1793
|
+
const raw = fs10.readFileUtf8(import_node_path6.default.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs10.readFileUtf8(import_node_path6.default.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1428
1794
|
if (raw === void 0) {
|
|
1429
1795
|
return void 0;
|
|
1430
1796
|
}
|
|
@@ -1440,7 +1806,7 @@ function readFarmManifest(fs3, farmRoot) {
|
|
|
1440
1806
|
function buildEntryFacts(params) {
|
|
1441
1807
|
const entries = /* @__PURE__ */ new Map();
|
|
1442
1808
|
const visit = (rel) => {
|
|
1443
|
-
const absolute =
|
|
1809
|
+
const absolute = import_node_path6.default.join(params.claudeHome, rel);
|
|
1444
1810
|
const stat = params.fs.lstat(absolute);
|
|
1445
1811
|
if (stat === void 0) {
|
|
1446
1812
|
return void 0;
|
|
@@ -1482,9 +1848,9 @@ function buildEntryFacts(params) {
|
|
|
1482
1848
|
entries
|
|
1483
1849
|
};
|
|
1484
1850
|
}
|
|
1485
|
-
function listSubtree(
|
|
1486
|
-
const absolute =
|
|
1487
|
-
const stat =
|
|
1851
|
+
function listSubtree(fs10, root, rel) {
|
|
1852
|
+
const absolute = import_node_path6.default.join(root, rel);
|
|
1853
|
+
const stat = fs10.lstat(absolute);
|
|
1488
1854
|
if (stat === void 0) {
|
|
1489
1855
|
return [];
|
|
1490
1856
|
}
|
|
@@ -1492,10 +1858,10 @@ function listSubtree(fs3, root, rel) {
|
|
|
1492
1858
|
return [{ rel, kind: "symlink" }];
|
|
1493
1859
|
}
|
|
1494
1860
|
if (stat.kind === "dir") {
|
|
1495
|
-
const children = [...
|
|
1861
|
+
const children = [...fs10.readdir(absolute)].sort().flatMap((name) => listSubtree(fs10, root, `${rel}/${name}`));
|
|
1496
1862
|
return [{ rel, kind: "dir" }, ...children];
|
|
1497
1863
|
}
|
|
1498
|
-
const contentHash =
|
|
1864
|
+
const contentHash = fs10.hashFile(absolute);
|
|
1499
1865
|
return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
|
|
1500
1866
|
}
|
|
1501
1867
|
function topLevelScopeRoots(roots) {
|
|
@@ -1509,25 +1875,25 @@ function topLevelScopeRoots(roots) {
|
|
|
1509
1875
|
}
|
|
1510
1876
|
return kept;
|
|
1511
1877
|
}
|
|
1512
|
-
function reconciliationScope(
|
|
1878
|
+
function reconciliationScope(fs10, farmRoot, manifest) {
|
|
1513
1879
|
if (manifest !== void 0) {
|
|
1514
1880
|
return topLevelScopeRoots(manifest.materialised);
|
|
1515
1881
|
}
|
|
1516
|
-
return [...
|
|
1882
|
+
return [...fs10.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs10.lstat(import_node_path6.default.join(farmRoot, name))?.kind === "dir").sort();
|
|
1517
1883
|
}
|
|
1518
|
-
function collectFarmListing(
|
|
1519
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1884
|
+
function collectFarmListing(fs10, farmRoot, scopeRoots) {
|
|
1885
|
+
return scopeRoots.flatMap((root) => listSubtree(fs10, farmRoot, root));
|
|
1520
1886
|
}
|
|
1521
|
-
function collectCanonicalCounterparts(
|
|
1887
|
+
function collectCanonicalCounterparts(fs10, claudeHome, farmListing) {
|
|
1522
1888
|
const out = [];
|
|
1523
1889
|
for (const entry of farmListing) {
|
|
1524
|
-
const absolute =
|
|
1525
|
-
const stat =
|
|
1890
|
+
const absolute = import_node_path6.default.join(claudeHome, entry.rel);
|
|
1891
|
+
const stat = fs10.lstat(absolute);
|
|
1526
1892
|
if (stat === void 0) {
|
|
1527
1893
|
continue;
|
|
1528
1894
|
}
|
|
1529
1895
|
if (stat.kind === "file") {
|
|
1530
|
-
const contentHash =
|
|
1896
|
+
const contentHash = fs10.hashFile(absolute);
|
|
1531
1897
|
out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
|
|
1532
1898
|
continue;
|
|
1533
1899
|
}
|
|
@@ -1551,21 +1917,21 @@ function carryOver(params) {
|
|
|
1551
1917
|
if (accounted.has(name)) {
|
|
1552
1918
|
continue;
|
|
1553
1919
|
}
|
|
1554
|
-
const stat = params.fs.lstat(
|
|
1920
|
+
const stat = params.fs.lstat(import_node_path6.default.join(params.previousRoot, name));
|
|
1555
1921
|
if (stat === void 0 || stat.kind === "symlink") {
|
|
1556
1922
|
continue;
|
|
1557
1923
|
}
|
|
1558
|
-
if (params.fs.lstat(
|
|
1924
|
+
if (params.fs.lstat(import_node_path6.default.join(params.farmRoot, name)) !== void 0) {
|
|
1559
1925
|
const category = params.classification === void 0 ? void 0 : classifyEntries([name], params.classification).classification.get(name);
|
|
1560
1926
|
if (category === "runtime") {
|
|
1561
|
-
params.fs.removeRecursive(
|
|
1927
|
+
params.fs.removeRecursive(import_node_path6.default.join(params.previousRoot, name));
|
|
1562
1928
|
autoResolved.push(name);
|
|
1563
1929
|
continue;
|
|
1564
1930
|
}
|
|
1565
1931
|
collided.push(name);
|
|
1566
1932
|
continue;
|
|
1567
1933
|
}
|
|
1568
|
-
params.fs.rename(
|
|
1934
|
+
params.fs.rename(import_node_path6.default.join(params.previousRoot, name), import_node_path6.default.join(params.farmRoot, name));
|
|
1569
1935
|
carried.push(name);
|
|
1570
1936
|
}
|
|
1571
1937
|
return { carried, autoResolved, collided };
|
|
@@ -1573,9 +1939,9 @@ function carryOver(params) {
|
|
|
1573
1939
|
function buildScratchTree(params) {
|
|
1574
1940
|
params.fs.mkdirp(params.scratchRoot);
|
|
1575
1941
|
for (const entry of params.plan.entries) {
|
|
1576
|
-
const absolute =
|
|
1942
|
+
const absolute = import_node_path6.default.join(params.scratchRoot, entry.rel);
|
|
1577
1943
|
if (entry.kind === "link") {
|
|
1578
|
-
params.fs.mkdirp(
|
|
1944
|
+
params.fs.mkdirp(import_node_path6.default.dirname(absolute));
|
|
1579
1945
|
params.fs.symlink(entry.target, absolute);
|
|
1580
1946
|
continue;
|
|
1581
1947
|
}
|
|
@@ -1584,14 +1950,14 @@ function buildScratchTree(params) {
|
|
|
1584
1950
|
}
|
|
1585
1951
|
}
|
|
1586
1952
|
params.fs.writeFileUtf8(
|
|
1587
|
-
|
|
1953
|
+
import_node_path6.default.join(params.scratchRoot, FARM_MANIFEST_FILENAME),
|
|
1588
1954
|
`${JSON.stringify(params.manifest, null, 2)}
|
|
1589
1955
|
`
|
|
1590
1956
|
);
|
|
1591
1957
|
}
|
|
1592
1958
|
function swapIn(params) {
|
|
1593
1959
|
if (params.fs.lstat(params.farmRoot) === void 0) {
|
|
1594
|
-
params.fs.mkdirp(
|
|
1960
|
+
params.fs.mkdirp(import_node_path6.default.dirname(params.farmRoot));
|
|
1595
1961
|
params.fs.rename(params.scratchRoot, params.farmRoot);
|
|
1596
1962
|
return { carried: [], autoResolved: [], collided: [] };
|
|
1597
1963
|
}
|
|
@@ -1615,14 +1981,14 @@ function recoverInterruptedSwap(params) {
|
|
|
1615
1981
|
const names = [...params.fs.readdir(params.identitiesDir)].sort();
|
|
1616
1982
|
const removedScratch = [];
|
|
1617
1983
|
for (const name of names.filter((candidate) => candidate.startsWith(scratchPrefix))) {
|
|
1618
|
-
params.fs.removeRecursive(
|
|
1984
|
+
params.fs.removeRecursive(import_node_path6.default.join(params.identitiesDir, name));
|
|
1619
1985
|
removedScratch.push(name);
|
|
1620
1986
|
}
|
|
1621
1987
|
let previous = names.filter((candidate) => candidate.startsWith(previousPrefix));
|
|
1622
1988
|
let restoredFrom;
|
|
1623
1989
|
const first = previous[0];
|
|
1624
1990
|
if (first !== void 0 && params.fs.lstat(params.farmRoot) === void 0) {
|
|
1625
|
-
params.fs.rename(
|
|
1991
|
+
params.fs.rename(import_node_path6.default.join(params.identitiesDir, first), params.farmRoot);
|
|
1626
1992
|
restoredFrom = first;
|
|
1627
1993
|
previous = previous.slice(1);
|
|
1628
1994
|
}
|
|
@@ -1630,7 +1996,7 @@ function recoverInterruptedSwap(params) {
|
|
|
1630
1996
|
const autoResolved = [];
|
|
1631
1997
|
const retained = [];
|
|
1632
1998
|
for (const name of previous) {
|
|
1633
|
-
const previousRoot =
|
|
1999
|
+
const previousRoot = import_node_path6.default.join(params.identitiesDir, name);
|
|
1634
2000
|
const result = carryOver({
|
|
1635
2001
|
fs: params.fs,
|
|
1636
2002
|
previousRoot,
|
|
@@ -1655,7 +2021,7 @@ function recoverInterruptedSwap(params) {
|
|
|
1655
2021
|
};
|
|
1656
2022
|
}
|
|
1657
2023
|
function recoverFarm(params) {
|
|
1658
|
-
const farmRoot =
|
|
2024
|
+
const farmRoot = import_node_path6.default.join(params.identitiesDir, params.identity);
|
|
1659
2025
|
const lock = acquireIdentityLock({
|
|
1660
2026
|
identity: params.identity,
|
|
1661
2027
|
dir: params.identitiesDir,
|
|
@@ -1714,16 +2080,16 @@ function executeReconciliation(params) {
|
|
|
1714
2080
|
});
|
|
1715
2081
|
continue;
|
|
1716
2082
|
}
|
|
1717
|
-
const from =
|
|
1718
|
-
const canonical =
|
|
2083
|
+
const from = import_node_path6.default.join(params.farmRoot, action.rel);
|
|
2084
|
+
const canonical = import_node_path6.default.join(params.claudeHome, action.rel);
|
|
1719
2085
|
if (action.kind === "adopt") {
|
|
1720
|
-
params.fs.mkdirp(
|
|
2086
|
+
params.fs.mkdirp(import_node_path6.default.dirname(canonical));
|
|
1721
2087
|
params.fs.copyRecursive(from, canonical);
|
|
1722
2088
|
adopted.push(action.rel);
|
|
1723
2089
|
continue;
|
|
1724
2090
|
}
|
|
1725
2091
|
const preserved = `${canonical}.farm-conflict-${String(params.nowMs)}`;
|
|
1726
|
-
params.fs.mkdirp(
|
|
2092
|
+
params.fs.mkdirp(import_node_path6.default.dirname(canonical));
|
|
1727
2093
|
params.fs.copyRecursive(from, preserved);
|
|
1728
2094
|
conflicts.push(action.rel);
|
|
1729
2095
|
}
|
|
@@ -1748,7 +2114,7 @@ function farmAlreadyMatches(existing, next) {
|
|
|
1748
2114
|
return existing.identity === next.identity && existing.claudeHome === next.claudeHome && sameStrings(existing.materialised, next.materialised) && sameLinks(existing.links, next.links);
|
|
1749
2115
|
}
|
|
1750
2116
|
function resyncFarm(params) {
|
|
1751
|
-
const farmRoot =
|
|
2117
|
+
const farmRoot = import_node_path6.default.join(params.identitiesDir, params.identity);
|
|
1752
2118
|
const lock = acquireIdentityLock({
|
|
1753
2119
|
identity: params.identity,
|
|
1754
2120
|
dir: params.identitiesDir,
|
|
@@ -1827,8 +2193,8 @@ function resyncFarm(params) {
|
|
|
1827
2193
|
diagnostics
|
|
1828
2194
|
};
|
|
1829
2195
|
}
|
|
1830
|
-
const scratchRoot =
|
|
1831
|
-
const previousRoot =
|
|
2196
|
+
const scratchRoot = import_node_path6.default.join(params.identitiesDir, `.${params.identity}.scratch.${params.uniqueSuffix}`);
|
|
2197
|
+
const previousRoot = import_node_path6.default.join(params.identitiesDir, `.${params.identity}.previous.${params.uniqueSuffix}`);
|
|
1832
2198
|
buildScratchTree({ fs: params.fs, scratchRoot, plan: resolved.farm, manifest });
|
|
1833
2199
|
const swap = swapIn({ fs: params.fs, farmRoot, scratchRoot, previousRoot, classification: params.classification });
|
|
1834
2200
|
if (swap.retainedPrevious !== void 0) {
|
|
@@ -1885,11 +2251,11 @@ function recoveryDiagnostics(recovery, identity) {
|
|
|
1885
2251
|
}
|
|
1886
2252
|
|
|
1887
2253
|
// src/frontdoor/connect.ts
|
|
1888
|
-
var
|
|
1889
|
-
var
|
|
2254
|
+
var import_node_crypto4 = require("node:crypto");
|
|
2255
|
+
var fs2 = __toESM(require("node:fs"), 1);
|
|
1890
2256
|
|
|
1891
2257
|
// src/frontdoor/x509.ts
|
|
1892
|
-
var
|
|
2258
|
+
var import_node_crypto3 = require("node:crypto");
|
|
1893
2259
|
var import_node_net = __toESM(require("node:net"), 1);
|
|
1894
2260
|
var TAG_BOOLEAN = 1;
|
|
1895
2261
|
var TAG_INTEGER = 2;
|
|
@@ -2041,7 +2407,7 @@ function ipBytes(address) {
|
|
|
2041
2407
|
return bytes;
|
|
2042
2408
|
}
|
|
2043
2409
|
function keyIdentifier(publicKey) {
|
|
2044
|
-
return (0,
|
|
2410
|
+
return (0, import_node_crypto3.createHash)("sha1").update(publicKey.export({ type: "pkcs1", format: "der" })).digest();
|
|
2045
2411
|
}
|
|
2046
2412
|
function readElement(buffer, offset) {
|
|
2047
2413
|
const tag = buffer[offset];
|
|
@@ -2087,7 +2453,7 @@ ${body.join("\n")}
|
|
|
2087
2453
|
`;
|
|
2088
2454
|
}
|
|
2089
2455
|
function randomSerial() {
|
|
2090
|
-
const bytes = (0,
|
|
2456
|
+
const bytes = (0, import_node_crypto3.randomBytes)(SERIAL_NUMBER_BYTES);
|
|
2091
2457
|
bytes[0] = (bytes[0] ?? 0) & BASE_128_PAYLOAD_MASK | HIGH_BIT >> 1;
|
|
2092
2458
|
return bytes;
|
|
2093
2459
|
}
|
|
@@ -2102,7 +2468,7 @@ function assemble(spec) {
|
|
|
2102
2468
|
spec.subjectPublicKey.export({ type: "spki", format: "der" }),
|
|
2103
2469
|
tlv(TAG_EXTENSIONS, sequence(...spec.extensions))
|
|
2104
2470
|
);
|
|
2105
|
-
const signature = (0,
|
|
2471
|
+
const signature = (0, import_node_crypto3.sign)("sha256", tbs, spec.signingKey);
|
|
2106
2472
|
return sequence(tbs, SIGNATURE_ALGORITHM, wholeBitString(signature));
|
|
2107
2473
|
}
|
|
2108
2474
|
function subjectKeyIdentifierExtension(publicKey) {
|
|
@@ -2112,7 +2478,7 @@ function basicConstraintsExtension(isCa) {
|
|
|
2112
2478
|
return extension(OID_BASIC_CONSTRAINTS, true, sequence(...isCa ? [boolean(true)] : []));
|
|
2113
2479
|
}
|
|
2114
2480
|
function rsaKeyPair() {
|
|
2115
|
-
return (0,
|
|
2481
|
+
return (0, import_node_crypto3.generateKeyPairSync)("rsa", { modulusLength: RSA_MODULUS_BITS });
|
|
2116
2482
|
}
|
|
2117
2483
|
function keyPem(privateKey) {
|
|
2118
2484
|
return privateKey.export({ type: "pkcs8", format: "pem" });
|
|
@@ -2136,8 +2502,8 @@ function issueLeafCertificate(ca, names, notBefore, notAfter) {
|
|
|
2136
2502
|
if (commonName === void 0) {
|
|
2137
2503
|
throw new Error("a leaf certificate needs at least one name");
|
|
2138
2504
|
}
|
|
2139
|
-
const caCertificate = new
|
|
2140
|
-
const caPrivateKey = (0,
|
|
2505
|
+
const caCertificate = new import_node_crypto3.X509Certificate(ca.certPem);
|
|
2506
|
+
const caPrivateKey = (0, import_node_crypto3.createPrivateKey)(ca.keyPem);
|
|
2141
2507
|
const { publicKey, privateKey } = rsaKeyPair();
|
|
2142
2508
|
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
2509
|
const der = assemble({
|
|
@@ -2232,7 +2598,7 @@ function capabilityFromProxyAuthorization(value) {
|
|
|
2232
2598
|
var AUTH_HEADER = "x-agent-shim-auth";
|
|
2233
2599
|
|
|
2234
2600
|
// src/frontdoor/state.ts
|
|
2235
|
-
var
|
|
2601
|
+
var import_node_path7 = __toESM(require("node:path"), 1);
|
|
2236
2602
|
var import_zod3 = require("zod");
|
|
2237
2603
|
var FRONT_DOOR_PROTOCOL = 1;
|
|
2238
2604
|
var FrontDoorSessionSchema = import_zod3.z.strictObject({ pid: import_zod3.z.number().int().positive(), startedAt: import_zod3.z.number(), token: import_zod3.z.string().min(1) });
|
|
@@ -2266,10 +2632,10 @@ var FrontDoorStateSchema = import_zod3.z.strictObject({
|
|
|
2266
2632
|
/** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
|
|
2267
2633
|
lastError: import_zod3.z.string().optional()
|
|
2268
2634
|
});
|
|
2269
|
-
function readFrontDoorState(
|
|
2635
|
+
function readFrontDoorState(fs10, stateFile) {
|
|
2270
2636
|
let raw;
|
|
2271
2637
|
try {
|
|
2272
|
-
raw =
|
|
2638
|
+
raw = fs10.readFileUtf8(stateFile);
|
|
2273
2639
|
} catch {
|
|
2274
2640
|
return void 0;
|
|
2275
2641
|
}
|
|
@@ -2283,28 +2649,28 @@ function readFrontDoorState(fs3, stateFile) {
|
|
|
2283
2649
|
return void 0;
|
|
2284
2650
|
}
|
|
2285
2651
|
}
|
|
2286
|
-
function writeFrontDoorState(
|
|
2287
|
-
|
|
2288
|
-
|
|
2652
|
+
function writeFrontDoorState(fs10, stateFile, state) {
|
|
2653
|
+
fs10.mkdirp(import_node_path7.default.dirname(stateFile));
|
|
2654
|
+
fs10.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2289
2655
|
`);
|
|
2290
2656
|
}
|
|
2291
2657
|
function frontDoorOrigin(state) {
|
|
2292
2658
|
const port = state?.directPort ?? state?.lastDirectPort;
|
|
2293
2659
|
return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
|
|
2294
2660
|
}
|
|
2295
|
-
function writeFrontDoorSession(
|
|
2296
|
-
|
|
2297
|
-
|
|
2661
|
+
function writeFrontDoorSession(fs10, sessionsDir, session) {
|
|
2662
|
+
fs10.mkdirPrivate(sessionsDir);
|
|
2663
|
+
fs10.writeFilePrivate(import_node_path7.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2298
2664
|
`);
|
|
2299
2665
|
}
|
|
2300
|
-
function listFrontDoorSessions(
|
|
2666
|
+
function listFrontDoorSessions(fs10, sessionsDir) {
|
|
2301
2667
|
const sessions = [];
|
|
2302
|
-
for (const name of
|
|
2668
|
+
for (const name of fs10.readdir(sessionsDir)) {
|
|
2303
2669
|
if (!name.endsWith(".json")) {
|
|
2304
2670
|
continue;
|
|
2305
2671
|
}
|
|
2306
2672
|
try {
|
|
2307
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(
|
|
2673
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs10.readFileUtf8(import_node_path7.default.join(sessionsDir, name)) ?? ""));
|
|
2308
2674
|
if (parsed.success) {
|
|
2309
2675
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2310
2676
|
}
|
|
@@ -2314,14 +2680,14 @@ function listFrontDoorSessions(fs3, sessionsDir) {
|
|
|
2314
2680
|
}
|
|
2315
2681
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2316
2682
|
}
|
|
2317
|
-
function removeFrontDoorSession(
|
|
2318
|
-
|
|
2683
|
+
function removeFrontDoorSession(fs10, sessionsDir, pid) {
|
|
2684
|
+
fs10.removeRecursive(import_node_path7.default.join(sessionsDir, `${String(pid)}.json`));
|
|
2319
2685
|
}
|
|
2320
|
-
function pruneDeadFrontDoorSessions(
|
|
2686
|
+
function pruneDeadFrontDoorSessions(fs10, sessionsDir, isRunning) {
|
|
2321
2687
|
const removed = [];
|
|
2322
|
-
for (const session of listFrontDoorSessions(
|
|
2688
|
+
for (const session of listFrontDoorSessions(fs10, sessionsDir)) {
|
|
2323
2689
|
if (!isRunning(session.pid)) {
|
|
2324
|
-
removeFrontDoorSession(
|
|
2690
|
+
removeFrontDoorSession(fs10, sessionsDir, session.pid);
|
|
2325
2691
|
removed.push(session.pid);
|
|
2326
2692
|
}
|
|
2327
2693
|
}
|
|
@@ -2333,8 +2699,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
|
|
|
2333
2699
|
var FRONTDOOR_POLL_MS = 1e3;
|
|
2334
2700
|
var MS_PER_MINUTE = 6e4;
|
|
2335
2701
|
async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
|
|
2336
|
-
const { fs:
|
|
2337
|
-
const previous = readFrontDoorState(
|
|
2702
|
+
const { fs: fs10, paths } = ports;
|
|
2703
|
+
const previous = readFrontDoorState(fs10, paths.frontdoorStateFile);
|
|
2338
2704
|
let lastPort = previous?.lastPort;
|
|
2339
2705
|
let lastConnectPort = previous?.lastConnectPort;
|
|
2340
2706
|
let lastDirectPort = previous?.lastDirectPort;
|
|
@@ -2344,12 +2710,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2344
2710
|
...lastDirectPort === void 0 ? {} : { lastDirectPort }
|
|
2345
2711
|
});
|
|
2346
2712
|
const fail = (message) => {
|
|
2347
|
-
writeFrontDoorState(
|
|
2348
|
-
|
|
2713
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2714
|
+
fs10.removeRecursive(paths.frontdoorLockFile);
|
|
2349
2715
|
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2350
2716
|
return 1;
|
|
2351
2717
|
};
|
|
2352
|
-
writeFrontDoorState(
|
|
2718
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
2353
2719
|
const previousSticky = lastPort;
|
|
2354
2720
|
let http2;
|
|
2355
2721
|
try {
|
|
@@ -2386,8 +2752,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2386
2752
|
ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom must be restarted to admit the new address)`);
|
|
2387
2753
|
}
|
|
2388
2754
|
lastDirectPort = direct.port;
|
|
2389
|
-
writeFrontDoorState(
|
|
2390
|
-
|
|
2755
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
|
|
2756
|
+
fs10.removeRecursive(paths.frontdoorLockFile);
|
|
2391
2757
|
ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http2.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
|
|
2392
2758
|
let idleSince;
|
|
2393
2759
|
let ticks = 0;
|
|
@@ -2396,15 +2762,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2396
2762
|
return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
|
|
2397
2763
|
}
|
|
2398
2764
|
ticks += 1;
|
|
2399
|
-
pruneDeadFrontDoorSessions(
|
|
2400
|
-
if (listFrontDoorSessions(
|
|
2765
|
+
pruneDeadFrontDoorSessions(fs10, paths.frontdoorSessionsDir, ports.isRunning);
|
|
2766
|
+
if (listFrontDoorSessions(fs10, paths.frontdoorSessionsDir).length === 0) {
|
|
2401
2767
|
idleSince ??= ports.now();
|
|
2402
2768
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2403
2769
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2404
2770
|
await direct.close();
|
|
2405
2771
|
await connect3.close();
|
|
2406
2772
|
await http2.close();
|
|
2407
|
-
writeFrontDoorState(
|
|
2773
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, sticky());
|
|
2408
2774
|
return 0;
|
|
2409
2775
|
}
|
|
2410
2776
|
} else {
|
|
@@ -2459,8 +2825,8 @@ function parseConnectTarget(authority) {
|
|
|
2459
2825
|
function isInterceptedHost(host, interceptHosts) {
|
|
2460
2826
|
return interceptHosts.includes(host);
|
|
2461
2827
|
}
|
|
2462
|
-
function servedByPipeline(
|
|
2463
|
-
return
|
|
2828
|
+
function servedByPipeline(path23) {
|
|
2829
|
+
return path23?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2464
2830
|
}
|
|
2465
2831
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2466
2832
|
"connection",
|
|
@@ -2519,8 +2885,8 @@ function ensureCa(store, generate) {
|
|
|
2519
2885
|
}
|
|
2520
2886
|
function caParses(ca) {
|
|
2521
2887
|
try {
|
|
2522
|
-
new
|
|
2523
|
-
(0,
|
|
2888
|
+
new import_node_crypto4.X509Certificate(ca.certPem);
|
|
2889
|
+
(0, import_node_crypto4.createPrivateKey)(ca.keyPem);
|
|
2524
2890
|
return true;
|
|
2525
2891
|
} catch {
|
|
2526
2892
|
return false;
|
|
@@ -2758,15 +3124,15 @@ async function blindTunnel(socket, target, effects) {
|
|
|
2758
3124
|
socket.destroy();
|
|
2759
3125
|
});
|
|
2760
3126
|
}
|
|
2761
|
-
function
|
|
3127
|
+
function isEnoent2(error) {
|
|
2762
3128
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2763
3129
|
}
|
|
2764
3130
|
function realConnectCertStore(paths) {
|
|
2765
3131
|
const read = (file) => {
|
|
2766
3132
|
try {
|
|
2767
|
-
return
|
|
3133
|
+
return fs2.readFileSync(file, "utf8");
|
|
2768
3134
|
} catch (error) {
|
|
2769
|
-
if (
|
|
3135
|
+
if (isEnoent2(error)) {
|
|
2770
3136
|
return void 0;
|
|
2771
3137
|
}
|
|
2772
3138
|
throw error;
|
|
@@ -2779,10 +3145,10 @@ function realConnectCertStore(paths) {
|
|
|
2779
3145
|
return certPem === void 0 || keyPem2 === void 0 ? void 0 : { certPem, keyPem: keyPem2 };
|
|
2780
3146
|
},
|
|
2781
3147
|
writeCa: (ca) => {
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
3148
|
+
fs2.mkdirSync(paths.frontdoorCaDir, { recursive: true });
|
|
3149
|
+
fs2.writeFileSync(paths.frontdoorCaCertFile, ca.certPem, "utf8");
|
|
3150
|
+
fs2.writeFileSync(paths.frontdoorCaKeyFile, ca.keyPem, { encoding: "utf8", mode: CA_KEY_FILE_MODE });
|
|
3151
|
+
fs2.chmodSync(paths.frontdoorCaKeyFile, CA_KEY_FILE_MODE);
|
|
2786
3152
|
}
|
|
2787
3153
|
};
|
|
2788
3154
|
}
|
|
@@ -2951,11 +3317,11 @@ function realConnectEffects() {
|
|
|
2951
3317
|
}
|
|
2952
3318
|
|
|
2953
3319
|
// src/frontdoor/ensure.ts
|
|
2954
|
-
var
|
|
3320
|
+
var import_node_crypto6 = require("node:crypto");
|
|
2955
3321
|
|
|
2956
3322
|
// src/headroom/state.ts
|
|
2957
|
-
var
|
|
2958
|
-
var
|
|
3323
|
+
var import_node_crypto5 = require("node:crypto");
|
|
3324
|
+
var import_node_path8 = __toESM(require("node:path"), 1);
|
|
2959
3325
|
var import_zod4 = require("zod");
|
|
2960
3326
|
var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
|
|
2961
3327
|
var HeadroomStateSchema = import_zod4.z.strictObject({
|
|
@@ -2989,16 +3355,16 @@ function parseJson(raw, schema) {
|
|
|
2989
3355
|
const parsed = schema.safeParse(JSON.parse(raw));
|
|
2990
3356
|
return parsed.success ? parsed.data : void 0;
|
|
2991
3357
|
}
|
|
2992
|
-
function readHeadroomState(
|
|
3358
|
+
function readHeadroomState(fs10, stateFile) {
|
|
2993
3359
|
try {
|
|
2994
|
-
return parseJson(
|
|
3360
|
+
return parseJson(fs10.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
2995
3361
|
} catch {
|
|
2996
3362
|
return void 0;
|
|
2997
3363
|
}
|
|
2998
3364
|
}
|
|
2999
|
-
function writeHeadroomState(
|
|
3000
|
-
|
|
3001
|
-
|
|
3365
|
+
function writeHeadroomState(fs10, stateFile, state) {
|
|
3366
|
+
fs10.mkdirp(import_node_path8.default.dirname(stateFile));
|
|
3367
|
+
fs10.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
3002
3368
|
`);
|
|
3003
3369
|
}
|
|
3004
3370
|
function headroomAllowlist(providers) {
|
|
@@ -3016,16 +3382,16 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
3016
3382
|
return upstreams;
|
|
3017
3383
|
}
|
|
3018
3384
|
function hashAllowlist(allowlist) {
|
|
3019
|
-
return (0,
|
|
3385
|
+
return (0, import_node_crypto5.createHash)("sha256").update(allowlist.join("\n")).digest("hex");
|
|
3020
3386
|
}
|
|
3021
|
-
function readAllProviders(
|
|
3387
|
+
function readAllProviders(fs10, providersDir) {
|
|
3022
3388
|
const result = [];
|
|
3023
|
-
for (const name of
|
|
3389
|
+
for (const name of fs10.readdir(providersDir)) {
|
|
3024
3390
|
if (!name.endsWith(".json")) {
|
|
3025
3391
|
continue;
|
|
3026
3392
|
}
|
|
3027
3393
|
try {
|
|
3028
|
-
const parsed = parseJson(
|
|
3394
|
+
const parsed = parseJson(fs10.readFileUtf8(import_node_path8.default.join(providersDir, name)), ProviderSchema);
|
|
3029
3395
|
if (parsed !== void 0) {
|
|
3030
3396
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
3031
3397
|
}
|
|
@@ -3035,22 +3401,22 @@ function readAllProviders(fs3, providersDir) {
|
|
|
3035
3401
|
}
|
|
3036
3402
|
return result;
|
|
3037
3403
|
}
|
|
3038
|
-
function writeSession(
|
|
3039
|
-
|
|
3040
|
-
|
|
3404
|
+
function writeSession(fs10, sessionsDir, session) {
|
|
3405
|
+
fs10.mkdirp(sessionsDir);
|
|
3406
|
+
fs10.writeFileUtf8(import_node_path8.default.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
3041
3407
|
`);
|
|
3042
3408
|
}
|
|
3043
|
-
function removeSession(
|
|
3044
|
-
|
|
3409
|
+
function removeSession(fs10, sessionsDir, pid) {
|
|
3410
|
+
fs10.removeRecursive(import_node_path8.default.join(sessionsDir, `${String(pid)}.json`));
|
|
3045
3411
|
}
|
|
3046
|
-
function listSessions(
|
|
3412
|
+
function listSessions(fs10, sessionsDir) {
|
|
3047
3413
|
const sessions = [];
|
|
3048
|
-
for (const name of
|
|
3414
|
+
for (const name of fs10.readdir(sessionsDir)) {
|
|
3049
3415
|
if (!name.endsWith(".json")) {
|
|
3050
3416
|
continue;
|
|
3051
3417
|
}
|
|
3052
3418
|
try {
|
|
3053
|
-
const parsed = parseJson(
|
|
3419
|
+
const parsed = parseJson(fs10.readFileUtf8(import_node_path8.default.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
3054
3420
|
if (parsed !== void 0) {
|
|
3055
3421
|
sessions.push(parsed);
|
|
3056
3422
|
}
|
|
@@ -3060,22 +3426,22 @@ function listSessions(fs3, sessionsDir) {
|
|
|
3060
3426
|
}
|
|
3061
3427
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
3062
3428
|
}
|
|
3063
|
-
function sessionsForSupervisor(
|
|
3064
|
-
return listSessions(
|
|
3429
|
+
function sessionsForSupervisor(fs10, sessionsDir, supervisorPid) {
|
|
3430
|
+
return listSessions(fs10, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
3065
3431
|
}
|
|
3066
|
-
function pruneDeadSessions(
|
|
3432
|
+
function pruneDeadSessions(fs10, sessionsDir, isRunning) {
|
|
3067
3433
|
const removed = [];
|
|
3068
|
-
for (const session of listSessions(
|
|
3434
|
+
for (const session of listSessions(fs10, sessionsDir)) {
|
|
3069
3435
|
if (!isRunning(session.pid)) {
|
|
3070
|
-
removeSession(
|
|
3436
|
+
removeSession(fs10, sessionsDir, session.pid);
|
|
3071
3437
|
removed.push(session.pid);
|
|
3072
3438
|
}
|
|
3073
3439
|
}
|
|
3074
3440
|
return removed;
|
|
3075
3441
|
}
|
|
3076
|
-
function readStartLock(
|
|
3442
|
+
function readStartLock(fs10, lockFile) {
|
|
3077
3443
|
try {
|
|
3078
|
-
return parseJson(
|
|
3444
|
+
return parseJson(fs10.readFileUtf8(lockFile), HeadroomLockSchema);
|
|
3079
3445
|
} catch {
|
|
3080
3446
|
return void 0;
|
|
3081
3447
|
}
|
|
@@ -3115,7 +3481,7 @@ function ensureFrontDoor(params) {
|
|
|
3115
3481
|
continue;
|
|
3116
3482
|
}
|
|
3117
3483
|
if (verdict.ok) {
|
|
3118
|
-
const token = (0,
|
|
3484
|
+
const token = (0, import_node_crypto6.randomUUID)();
|
|
3119
3485
|
writeFrontDoorSession(ports.fs, paths.frontdoorSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), token });
|
|
3120
3486
|
return { port: state.port, connectPort: state.connectPort, token };
|
|
3121
3487
|
}
|
|
@@ -3155,7 +3521,10 @@ function ensureFrontDoor(params) {
|
|
|
3155
3521
|
}
|
|
3156
3522
|
|
|
3157
3523
|
// src/usage/read.ts
|
|
3158
|
-
var
|
|
3524
|
+
var import_node_path10 = __toESM(require("node:path"), 1);
|
|
3525
|
+
|
|
3526
|
+
// src/usage/account.ts
|
|
3527
|
+
var import_node_path9 = __toESM(require("node:path"), 1);
|
|
3159
3528
|
|
|
3160
3529
|
// src/usage/schema.ts
|
|
3161
3530
|
var import_zod5 = require("zod");
|
|
@@ -3305,11 +3674,111 @@ var UsageSnapshotSchema = import_zod5.z.strictObject({
|
|
|
3305
3674
|
});
|
|
3306
3675
|
|
|
3307
3676
|
// src/usage/account.ts
|
|
3677
|
+
var CLAUDE_JSON = ".claude.json";
|
|
3678
|
+
var AccountMetadataError = class extends CliError {
|
|
3679
|
+
constructor(file, detail) {
|
|
3680
|
+
super(`${file}: ${detail}, so its account metadata cannot be read.`);
|
|
3681
|
+
this.file = file;
|
|
3682
|
+
this.name = "AccountMetadataError";
|
|
3683
|
+
}
|
|
3684
|
+
file;
|
|
3685
|
+
};
|
|
3686
|
+
function isObject(value) {
|
|
3687
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3688
|
+
}
|
|
3308
3689
|
function isIdentityName(name) {
|
|
3309
3690
|
return IdentitySchema.shape.name.safeParse(name).success;
|
|
3310
3691
|
}
|
|
3692
|
+
function claudeJsonPath(identitiesDir, identity) {
|
|
3693
|
+
return import_node_path9.default.join(identitiesDir, identity, CLAUDE_JSON);
|
|
3694
|
+
}
|
|
3695
|
+
function readAccountMetadata(fs10, identitiesDir, identity) {
|
|
3696
|
+
if (!isIdentityName(identity)) {
|
|
3697
|
+
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3698
|
+
}
|
|
3699
|
+
const file = claudeJsonPath(identitiesDir, identity);
|
|
3700
|
+
const raw = fs10.readFileUtf8(file);
|
|
3701
|
+
if (raw === void 0) {
|
|
3702
|
+
return void 0;
|
|
3703
|
+
}
|
|
3704
|
+
let parsed;
|
|
3705
|
+
try {
|
|
3706
|
+
parsed = JSON.parse(raw);
|
|
3707
|
+
} catch {
|
|
3708
|
+
throw new AccountMetadataError(file, "not valid JSON");
|
|
3709
|
+
}
|
|
3710
|
+
if (!isObject(parsed)) {
|
|
3711
|
+
throw new AccountMetadataError(file, "not a JSON object");
|
|
3712
|
+
}
|
|
3713
|
+
const oauthAccount = parsed.oauthAccount;
|
|
3714
|
+
if (!isObject(oauthAccount)) {
|
|
3715
|
+
return void 0;
|
|
3716
|
+
}
|
|
3717
|
+
const picked = {};
|
|
3718
|
+
for (const key of Object.keys(AccountMetadataSchema.shape)) {
|
|
3719
|
+
const value = oauthAccount[key];
|
|
3720
|
+
if (value !== null && value !== void 0) {
|
|
3721
|
+
picked[key] = value;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
const account = AccountMetadataSchema.safeParse(picked);
|
|
3725
|
+
if (!account.success) {
|
|
3726
|
+
throw new AccountMetadataError(file, account.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
|
|
3727
|
+
}
|
|
3728
|
+
return account.data;
|
|
3729
|
+
}
|
|
3311
3730
|
|
|
3312
3731
|
// src/usage/read.ts
|
|
3732
|
+
var SEGMENT_RE = /^(\d{4}-\d{2}-\d{2})\.(\d+)\.jsonl$/;
|
|
3733
|
+
var DAY_MS = 864e5;
|
|
3734
|
+
function listLogSegments(fs10, logDir) {
|
|
3735
|
+
return fs10.readdir(logDir).flatMap((name) => {
|
|
3736
|
+
const match = SEGMENT_RE.exec(name);
|
|
3737
|
+
const day = match?.[1];
|
|
3738
|
+
if (day === void 0) {
|
|
3739
|
+
return [];
|
|
3740
|
+
}
|
|
3741
|
+
return [{ name, day, endsAt: Date.parse(`${day}T00:00:00.000Z`) + DAY_MS }];
|
|
3742
|
+
}).sort((left, right) => left.day.localeCompare(right.day) || left.name.localeCompare(right.name));
|
|
3743
|
+
}
|
|
3744
|
+
function readUsageLog(fs10, logDir, options = {}) {
|
|
3745
|
+
const { sinceMs } = options;
|
|
3746
|
+
const records = [];
|
|
3747
|
+
let invalidLines = 0;
|
|
3748
|
+
for (const segment of listLogSegments(fs10, logDir)) {
|
|
3749
|
+
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
3750
|
+
continue;
|
|
3751
|
+
}
|
|
3752
|
+
const contents = fs10.readFileUtf8(import_node_path10.default.join(logDir, segment.name));
|
|
3753
|
+
if (contents === void 0) {
|
|
3754
|
+
continue;
|
|
3755
|
+
}
|
|
3756
|
+
const lines = contents.split("\n");
|
|
3757
|
+
lines.pop();
|
|
3758
|
+
for (const line of lines) {
|
|
3759
|
+
if (line === "") {
|
|
3760
|
+
continue;
|
|
3761
|
+
}
|
|
3762
|
+
let raw;
|
|
3763
|
+
try {
|
|
3764
|
+
raw = JSON.parse(line);
|
|
3765
|
+
} catch {
|
|
3766
|
+
invalidLines += 1;
|
|
3767
|
+
continue;
|
|
3768
|
+
}
|
|
3769
|
+
const parsed = UsageRecordSchema.safeParse(raw);
|
|
3770
|
+
if (!parsed.success) {
|
|
3771
|
+
invalidLines += 1;
|
|
3772
|
+
continue;
|
|
3773
|
+
}
|
|
3774
|
+
if (sinceMs === void 0 || Date.parse(parsed.data.at) >= sinceMs) {
|
|
3775
|
+
records.push(parsed.data);
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
records.sort((left, right) => left.at.localeCompare(right.at));
|
|
3780
|
+
return { records, invalidLines };
|
|
3781
|
+
}
|
|
3313
3782
|
var UsageSnapshotError = class extends Error {
|
|
3314
3783
|
constructor(file, detail) {
|
|
3315
3784
|
super(`${file} is not a usage snapshot this agent-shim can read (schema version ${String(USAGE_SCHEMA_VERSION)}): ${detail}`);
|
|
@@ -3322,11 +3791,11 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
3322
3791
|
if (!isIdentityName(identity)) {
|
|
3323
3792
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3324
3793
|
}
|
|
3325
|
-
return
|
|
3794
|
+
return import_node_path10.default.join(snapshotsDir, `${identity}.json`);
|
|
3326
3795
|
}
|
|
3327
|
-
function readUsageSnapshot(
|
|
3796
|
+
function readUsageSnapshot(fs10, snapshotsDir, identity) {
|
|
3328
3797
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3329
|
-
const raw =
|
|
3798
|
+
const raw = fs10.readFileUtf8(file);
|
|
3330
3799
|
if (raw === void 0) {
|
|
3331
3800
|
return void 0;
|
|
3332
3801
|
}
|
|
@@ -3342,8 +3811,8 @@ function readUsageSnapshot(fs3, snapshotsDir, identity) {
|
|
|
3342
3811
|
}
|
|
3343
3812
|
return snapshot.data;
|
|
3344
3813
|
}
|
|
3345
|
-
function listUsageSnapshots(
|
|
3346
|
-
return
|
|
3814
|
+
function listUsageSnapshots(fs10, snapshotsDir) {
|
|
3815
|
+
return fs10.readdir(snapshotsDir).flatMap((name) => {
|
|
3347
3816
|
if (!name.endsWith(".json")) {
|
|
3348
3817
|
return [];
|
|
3349
3818
|
}
|
|
@@ -3351,7 +3820,7 @@ function listUsageSnapshots(fs3, snapshotsDir) {
|
|
|
3351
3820
|
if (!isIdentityName(identity)) {
|
|
3352
3821
|
return [];
|
|
3353
3822
|
}
|
|
3354
|
-
const snapshot = readUsageSnapshot(
|
|
3823
|
+
const snapshot = readUsageSnapshot(fs10, snapshotsDir, identity);
|
|
3355
3824
|
return snapshot === void 0 ? [] : [snapshot];
|
|
3356
3825
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3357
3826
|
}
|
|
@@ -3411,7 +3880,7 @@ function ensureHeadroom(params) {
|
|
|
3411
3880
|
}
|
|
3412
3881
|
|
|
3413
3882
|
// src/headroom/settings.ts
|
|
3414
|
-
var
|
|
3883
|
+
var import_node_crypto7 = require("node:crypto");
|
|
3415
3884
|
function settingsOf(config) {
|
|
3416
3885
|
return {
|
|
3417
3886
|
...config.mode === void 0 ? {} : { mode: config.mode },
|
|
@@ -3436,7 +3905,7 @@ function settingsEnv(settings) {
|
|
|
3436
3905
|
return settings.rolloutChannel === void 0 ? {} : { HEADROOM_ROLLOUT_CHANNEL: settings.rolloutChannel };
|
|
3437
3906
|
}
|
|
3438
3907
|
function hashSettings(settings) {
|
|
3439
|
-
return (0,
|
|
3908
|
+
return (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify([...settingsArgs(settings), JSON.stringify(settingsEnv(settings))])).digest("hex");
|
|
3440
3909
|
}
|
|
3441
3910
|
|
|
3442
3911
|
// src/headroom/source.ts
|
|
@@ -3444,6 +3913,14 @@ var import_zod6 = require("zod");
|
|
|
3444
3913
|
var GIT_SPEC_RE = /git\+[^@\s]+(?:@([^\s#]+))?/;
|
|
3445
3914
|
var COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
3446
3915
|
var DirectUrlSchema = import_zod6.z.object({ vcs_info: import_zod6.z.object({ commit_id: import_zod6.z.string() }) });
|
|
3916
|
+
function isMovingGitSource(spec) {
|
|
3917
|
+
const match = GIT_SPEC_RE.exec(spec);
|
|
3918
|
+
if (match === null) {
|
|
3919
|
+
return false;
|
|
3920
|
+
}
|
|
3921
|
+
const ref = match[1];
|
|
3922
|
+
return ref === void 0 || !COMMIT_SHA_RE.test(ref);
|
|
3923
|
+
}
|
|
3447
3924
|
function pinnedGitCommit(spec) {
|
|
3448
3925
|
const ref = GIT_SPEC_RE.exec(spec)?.[1];
|
|
3449
3926
|
return ref !== void 0 && COMMIT_SHA_RE.test(ref) ? ref.toLowerCase() : void 0;
|
|
@@ -3518,9 +3995,9 @@ function compareVersions(a, b) {
|
|
|
3518
3995
|
}
|
|
3519
3996
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
3520
3997
|
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
3521
|
-
const { fs:
|
|
3998
|
+
const { fs: fs10, paths } = ports;
|
|
3522
3999
|
let config = initialConfig;
|
|
3523
|
-
const previousState = readHeadroomState(
|
|
4000
|
+
const previousState = readHeadroomState(fs10, paths.headroomStateFile);
|
|
3524
4001
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3525
4002
|
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3526
4003
|
return 0;
|
|
@@ -3529,19 +4006,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3529
4006
|
let lastPort = previousState?.lastPort;
|
|
3530
4007
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
3531
4008
|
const ownsState = () => {
|
|
3532
|
-
const owner = readHeadroomState(
|
|
4009
|
+
const owner = readHeadroomState(fs10, paths.headroomStateFile)?.supervisorPid;
|
|
3533
4010
|
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
3534
4011
|
};
|
|
3535
4012
|
const writeOwnState = (state) => {
|
|
3536
4013
|
if (!ownsState()) {
|
|
3537
4014
|
return false;
|
|
3538
4015
|
}
|
|
3539
|
-
writeHeadroomState(
|
|
4016
|
+
writeHeadroomState(fs10, paths.headroomStateFile, state);
|
|
3540
4017
|
return true;
|
|
3541
4018
|
};
|
|
3542
4019
|
const fail = (message) => {
|
|
3543
4020
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3544
|
-
|
|
4021
|
+
fs10.removeRecursive(paths.headroomLockFile);
|
|
3545
4022
|
}
|
|
3546
4023
|
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
3547
4024
|
return 1;
|
|
@@ -3590,7 +4067,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3590
4067
|
const settingsHash = hashSettings(config.settings);
|
|
3591
4068
|
const allowlist = allowlistOf(ports);
|
|
3592
4069
|
const allowlistHash = hashAllowlist(allowlist);
|
|
3593
|
-
pruneDeadSessions(
|
|
4070
|
+
pruneDeadSessions(fs10, paths.headroomSessionsDir, ports.isRunning);
|
|
3594
4071
|
const ownsNow = ownsState();
|
|
3595
4072
|
if (!ownsNow && !superseded) {
|
|
3596
4073
|
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
@@ -3642,7 +4119,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3642
4119
|
...sticky()
|
|
3643
4120
|
});
|
|
3644
4121
|
if (recorded) {
|
|
3645
|
-
|
|
4122
|
+
fs10.removeRecursive(paths.headroomLockFile);
|
|
3646
4123
|
}
|
|
3647
4124
|
idleSince = void 0;
|
|
3648
4125
|
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
@@ -3657,7 +4134,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3657
4134
|
}
|
|
3658
4135
|
} else {
|
|
3659
4136
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3660
|
-
if (sessionsForSupervisor(
|
|
4137
|
+
if (sessionsForSupervisor(fs10, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3661
4138
|
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3662
4139
|
ports.stopProcess(headroomPid);
|
|
3663
4140
|
headroomPid = void 0;
|
|
@@ -3666,7 +4143,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3666
4143
|
continue;
|
|
3667
4144
|
}
|
|
3668
4145
|
}
|
|
3669
|
-
const sessions = sessionsForSupervisor(
|
|
4146
|
+
const sessions = sessionsForSupervisor(fs10, paths.headroomSessionsDir, ports.ownPid);
|
|
3670
4147
|
if (sessions.length === 0) {
|
|
3671
4148
|
idleSince ??= ports.now();
|
|
3672
4149
|
const idleMs = ports.now() - idleSince;
|
|
@@ -3688,9 +4165,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3688
4165
|
await ports.sleep(HEADROOM_POLL_MS);
|
|
3689
4166
|
}
|
|
3690
4167
|
}
|
|
3691
|
-
function currentHeadroomAllowlist(
|
|
3692
|
-
const providers = readAllProviders(
|
|
3693
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
4168
|
+
function currentHeadroomAllowlist(fs10, paths) {
|
|
4169
|
+
const providers = readAllProviders(fs10, paths.providersDir).map((entry) => entry.provider);
|
|
4170
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs10, paths.frontdoorStateFile))));
|
|
3694
4171
|
}
|
|
3695
4172
|
function allowlistOf(ports) {
|
|
3696
4173
|
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
@@ -3756,63 +4233,2564 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
3756
4233
|
}
|
|
3757
4234
|
|
|
3758
4235
|
// src/paths.ts
|
|
3759
|
-
var
|
|
4236
|
+
var import_node_fs2 = __toESM(require("node:fs"), 1);
|
|
3760
4237
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
3761
|
-
var
|
|
4238
|
+
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
3762
4239
|
function resolveAgentShimHome(env = process.env, home = import_node_os.default.homedir(), isDirectory = isExistingDirectory) {
|
|
3763
4240
|
const fromEnv = env.AGENT_SHIM_HOME;
|
|
3764
4241
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
3765
4242
|
return fromEnv;
|
|
3766
4243
|
}
|
|
3767
|
-
const current =
|
|
3768
|
-
const legacy =
|
|
4244
|
+
const current = import_node_path11.default.join(home, HOME_DIRNAME);
|
|
4245
|
+
const legacy = import_node_path11.default.join(home, LEGACY_HOME_DIRNAME);
|
|
3769
4246
|
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
3770
4247
|
}
|
|
3771
4248
|
function isExistingDirectory(candidate) {
|
|
3772
|
-
return
|
|
4249
|
+
return import_node_fs2.default.statSync(candidate, { throwIfNoEntry: false })?.isDirectory() ?? false;
|
|
3773
4250
|
}
|
|
3774
4251
|
function resolveClaudeHome() {
|
|
3775
4252
|
const fromEnv = process.env.AGENT_SHIM_CLAUDE_HOME;
|
|
3776
4253
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
3777
4254
|
return fromEnv;
|
|
3778
4255
|
}
|
|
3779
|
-
return
|
|
4256
|
+
return import_node_path11.default.join(import_node_os.default.homedir(), ".claude");
|
|
3780
4257
|
}
|
|
3781
4258
|
function buildLayoutPaths(root) {
|
|
3782
4259
|
return {
|
|
3783
4260
|
root,
|
|
3784
|
-
identitiesDir:
|
|
3785
|
-
configProfilesDir:
|
|
3786
|
-
providersDir:
|
|
3787
|
-
directoryRulesFile:
|
|
3788
|
-
activeIdentityFile:
|
|
3789
|
-
globalConfigFile:
|
|
3790
|
-
categoriesLocalFile:
|
|
3791
|
-
claudeShimFile:
|
|
3792
|
-
headroomDir:
|
|
3793
|
-
headroomStateFile:
|
|
3794
|
-
headroomLockFile:
|
|
3795
|
-
headroomSessionsDir:
|
|
3796
|
-
logsDir:
|
|
3797
|
-
headroomLogPath:
|
|
3798
|
-
frontdoorDir:
|
|
3799
|
-
frontdoorCaDir:
|
|
3800
|
-
frontdoorCaCertFile:
|
|
3801
|
-
frontdoorCaKeyFile:
|
|
3802
|
-
frontdoorCaBundlesDir:
|
|
3803
|
-
frontdoorStateFile:
|
|
3804
|
-
frontdoorLockFile:
|
|
3805
|
-
frontdoorSessionsDir:
|
|
3806
|
-
frontdoorLogPath:
|
|
3807
|
-
usageDir:
|
|
3808
|
-
usageLogDir:
|
|
3809
|
-
usageSnapshotsDir:
|
|
3810
|
-
usagePicksFile:
|
|
4261
|
+
identitiesDir: import_node_path11.default.join(root, "identities"),
|
|
4262
|
+
configProfilesDir: import_node_path11.default.join(root, "config-profiles"),
|
|
4263
|
+
providersDir: import_node_path11.default.join(root, "providers"),
|
|
4264
|
+
directoryRulesFile: import_node_path11.default.join(root, "directory-rules.json"),
|
|
4265
|
+
activeIdentityFile: import_node_path11.default.join(root, "active-identity"),
|
|
4266
|
+
globalConfigFile: import_node_path11.default.join(root, "config.json"),
|
|
4267
|
+
categoriesLocalFile: import_node_path11.default.join(root, "categories.local.json"),
|
|
4268
|
+
claudeShimFile: import_node_path11.default.join(root, "claude-shim.json"),
|
|
4269
|
+
headroomDir: import_node_path11.default.join(root, "headroom"),
|
|
4270
|
+
headroomStateFile: import_node_path11.default.join(root, "headroom", "state.json"),
|
|
4271
|
+
headroomLockFile: import_node_path11.default.join(root, "headroom", "start.lock"),
|
|
4272
|
+
headroomSessionsDir: import_node_path11.default.join(root, "headroom", "sessions"),
|
|
4273
|
+
logsDir: import_node_path11.default.join(root, "logs"),
|
|
4274
|
+
headroomLogPath: import_node_path11.default.join(root, "logs", "headroom.log"),
|
|
4275
|
+
frontdoorDir: import_node_path11.default.join(root, "frontdoor"),
|
|
4276
|
+
frontdoorCaDir: import_node_path11.default.join(root, "frontdoor", "ca"),
|
|
4277
|
+
frontdoorCaCertFile: import_node_path11.default.join(root, "frontdoor", "ca", "ca.pem"),
|
|
4278
|
+
frontdoorCaKeyFile: import_node_path11.default.join(root, "frontdoor", "ca", "ca.key"),
|
|
4279
|
+
frontdoorCaBundlesDir: import_node_path11.default.join(root, "frontdoor", "ca", "bundles"),
|
|
4280
|
+
frontdoorStateFile: import_node_path11.default.join(root, "frontdoor", "state.json"),
|
|
4281
|
+
frontdoorLockFile: import_node_path11.default.join(root, "frontdoor", "start.lock"),
|
|
4282
|
+
frontdoorSessionsDir: import_node_path11.default.join(root, "frontdoor", "sessions"),
|
|
4283
|
+
frontdoorLogPath: import_node_path11.default.join(root, "logs", "frontdoor.log"),
|
|
4284
|
+
usageDir: import_node_path11.default.join(root, "usage"),
|
|
4285
|
+
usageLogDir: import_node_path11.default.join(root, "usage", "log"),
|
|
4286
|
+
usageSnapshotsDir: import_node_path11.default.join(root, "usage", "snapshots"),
|
|
4287
|
+
usagePicksFile: import_node_path11.default.join(root, "usage", "picks.json")
|
|
3811
4288
|
};
|
|
3812
4289
|
}
|
|
3813
4290
|
function resolveLayoutPaths() {
|
|
3814
4291
|
return buildLayoutPaths(resolveAgentShimHome());
|
|
3815
4292
|
}
|
|
4293
|
+
|
|
4294
|
+
// src/identityStore.ts
|
|
4295
|
+
var import_node_fs4 = __toESM(require("node:fs"), 1);
|
|
4296
|
+
var import_node_path14 = __toESM(require("node:path"), 1);
|
|
4297
|
+
|
|
4298
|
+
// src/launcher/identity.ts
|
|
4299
|
+
var import_node_path12 = __toESM(require("node:path"), 1);
|
|
4300
|
+
function poolNameOf(selector) {
|
|
4301
|
+
return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
|
|
4302
|
+
}
|
|
4303
|
+
function isNonEmpty(value) {
|
|
4304
|
+
return value !== void 0 && value !== "";
|
|
4305
|
+
}
|
|
4306
|
+
function selected(selector, source) {
|
|
4307
|
+
const pool = poolNameOf(selector);
|
|
4308
|
+
return pool === void 0 ? { name: selector, source, configDirEscapeHatch: false } : { pool, source, configDirEscapeHatch: false };
|
|
4309
|
+
}
|
|
4310
|
+
function decideIdentity(params) {
|
|
4311
|
+
if (isNonEmpty(params.env.CLAUDE_CONFIG_DIR)) {
|
|
4312
|
+
return { source: "config-dir-escape-hatch", configDirEscapeHatch: true };
|
|
4313
|
+
}
|
|
4314
|
+
if (isNonEmpty(params.argv0Identity)) {
|
|
4315
|
+
return selected(params.argv0Identity, "argv");
|
|
4316
|
+
}
|
|
4317
|
+
if (isNonEmpty(params.env.AGENT_SHIM_IDENTITY)) {
|
|
4318
|
+
return selected(params.env.AGENT_SHIM_IDENTITY, "env");
|
|
4319
|
+
}
|
|
4320
|
+
if (isNonEmpty(params.directoryPinnedIdentity)) {
|
|
4321
|
+
return selected(params.directoryPinnedIdentity, "directory-pin");
|
|
4322
|
+
}
|
|
4323
|
+
const persisted = params.readActiveIdentityFile();
|
|
4324
|
+
if (isNonEmpty(persisted)) {
|
|
4325
|
+
return selected(persisted, "active-identity-file");
|
|
4326
|
+
}
|
|
4327
|
+
return { source: "none", configDirEscapeHatch: false };
|
|
4328
|
+
}
|
|
4329
|
+
function decideConfigProfile(params) {
|
|
4330
|
+
if (isNonEmpty(params.cliFlagConfigProfile)) {
|
|
4331
|
+
return { name: params.cliFlagConfigProfile, source: "cli-flag" };
|
|
4332
|
+
}
|
|
4333
|
+
if (isNonEmpty(params.env.AGENT_SHIM_CONFIG_PROFILE)) {
|
|
4334
|
+
return { name: params.env.AGENT_SHIM_CONFIG_PROFILE, source: "env" };
|
|
4335
|
+
}
|
|
4336
|
+
if (isNonEmpty(params.directoryRuleConfigProfile)) {
|
|
4337
|
+
return { name: params.directoryRuleConfigProfile, source: "directory-rule" };
|
|
4338
|
+
}
|
|
4339
|
+
if (isNonEmpty(params.identityDefaultConfigProfile)) {
|
|
4340
|
+
return { name: params.identityDefaultConfigProfile, source: "identity-default" };
|
|
4341
|
+
}
|
|
4342
|
+
if (isNonEmpty(params.globalDefaultConfigProfile)) {
|
|
4343
|
+
return { name: params.globalDefaultConfigProfile, source: "global-default" };
|
|
4344
|
+
}
|
|
4345
|
+
return { source: "none" };
|
|
4346
|
+
}
|
|
4347
|
+
function loadIdentity(identitiesDir, name, fs10) {
|
|
4348
|
+
return loadConfigFile(import_node_path12.default.join(identitiesDir, name, "identity.json"), IdentitySchema, fs10.readConfigFile);
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4351
|
+
// src/configProfilesStore.ts
|
|
4352
|
+
var import_node_fs3 = __toESM(require("node:fs"), 1);
|
|
4353
|
+
var import_node_path13 = __toESM(require("node:path"), 1);
|
|
4354
|
+
var ProfileNotFoundError = class extends CliError {
|
|
4355
|
+
constructor(profileName) {
|
|
4356
|
+
super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
|
|
4357
|
+
this.profileName = profileName;
|
|
4358
|
+
this.name = "ProfileNotFoundError";
|
|
4359
|
+
}
|
|
4360
|
+
profileName;
|
|
4361
|
+
};
|
|
4362
|
+
var ProfileAlreadyExistsError = class extends CliError {
|
|
4363
|
+
constructor(profileName) {
|
|
4364
|
+
super(`A configuration profile named "${profileName}" already exists.`);
|
|
4365
|
+
this.profileName = profileName;
|
|
4366
|
+
this.name = "ProfileAlreadyExistsError";
|
|
4367
|
+
}
|
|
4368
|
+
profileName;
|
|
4369
|
+
};
|
|
4370
|
+
var InvalidCategoryNameError = class extends CliError {
|
|
4371
|
+
constructor(categoryName) {
|
|
4372
|
+
super(`"${categoryName}" is not a category a configuration profile may toggle (runtime, history, knowledge, settings).`);
|
|
4373
|
+
this.categoryName = categoryName;
|
|
4374
|
+
this.name = "InvalidCategoryNameError";
|
|
4375
|
+
}
|
|
4376
|
+
categoryName;
|
|
4377
|
+
};
|
|
4378
|
+
function profileJsonPath(paths, name) {
|
|
4379
|
+
return import_node_path13.default.join(paths.configProfilesDir, `${name}.json`);
|
|
4380
|
+
}
|
|
4381
|
+
function profileExists(paths, name) {
|
|
4382
|
+
return import_node_fs3.default.existsSync(profileJsonPath(paths, name));
|
|
4383
|
+
}
|
|
4384
|
+
function requireProfileExists(paths, name) {
|
|
4385
|
+
if (!profileExists(paths, name)) {
|
|
4386
|
+
throw new ProfileNotFoundError(name);
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
function readProfile(paths, name) {
|
|
4390
|
+
return readJson(profileJsonPath(paths, name), ConfigProfileSchema);
|
|
4391
|
+
}
|
|
4392
|
+
function createProfile(paths, name, extendsList, description) {
|
|
4393
|
+
if (profileExists(paths, name)) {
|
|
4394
|
+
throw new ProfileAlreadyExistsError(name);
|
|
4395
|
+
}
|
|
4396
|
+
const filePath = profileJsonPath(paths, name);
|
|
4397
|
+
const parsed = ConfigProfileSchema.safeParse({
|
|
4398
|
+
...description === void 0 ? {} : { description },
|
|
4399
|
+
...extendsList !== void 0 && extendsList.length > 0 ? { extends: [...extendsList] } : {}
|
|
4400
|
+
});
|
|
4401
|
+
if (!parsed.success) {
|
|
4402
|
+
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
4403
|
+
}
|
|
4404
|
+
writeJsonAtomic(filePath, parsed.data);
|
|
4405
|
+
return parsed.data;
|
|
4406
|
+
}
|
|
4407
|
+
function listProfiles(paths) {
|
|
4408
|
+
if (!import_node_fs3.default.existsSync(paths.configProfilesDir)) {
|
|
4409
|
+
return [];
|
|
4410
|
+
}
|
|
4411
|
+
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();
|
|
4412
|
+
const result = [];
|
|
4413
|
+
for (const name of names) {
|
|
4414
|
+
const profile = readProfile(paths, name);
|
|
4415
|
+
if (profile !== void 0) {
|
|
4416
|
+
result.push({ name, profile });
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
return result;
|
|
4420
|
+
}
|
|
4421
|
+
function globalConfigPath(paths) {
|
|
4422
|
+
return paths.globalConfigFile;
|
|
4423
|
+
}
|
|
4424
|
+
function readGlobalConfig(paths) {
|
|
4425
|
+
return readJson(globalConfigPath(paths), GlobalConfigSchema);
|
|
4426
|
+
}
|
|
4427
|
+
function setGlobalDefaultProfile(paths, name) {
|
|
4428
|
+
return applyPatch(
|
|
4429
|
+
globalConfigPath(paths),
|
|
4430
|
+
GlobalConfigSchema,
|
|
4431
|
+
{ defaultConfigProfile: name },
|
|
4432
|
+
{ defaults: {} }
|
|
4433
|
+
);
|
|
4434
|
+
}
|
|
4435
|
+
function validateCategoryNames(patch) {
|
|
4436
|
+
for (const key of Object.keys(patch)) {
|
|
4437
|
+
if (!isOverridableCategory(key)) {
|
|
4438
|
+
throw new InvalidCategoryNameError(key);
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
function setProfileCategories(paths, name, patch) {
|
|
4443
|
+
requireProfileExists(paths, name);
|
|
4444
|
+
const expandedPatch = expandAllCategoryKey(patch);
|
|
4445
|
+
validateCategoryNames(expandedPatch);
|
|
4446
|
+
const existing = readProfile(paths, name) ?? {};
|
|
4447
|
+
const mergedCategories = { ...existing.categories, ...expandedPatch };
|
|
4448
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { categories: mergedCategories });
|
|
4449
|
+
}
|
|
4450
|
+
function setProfileEntries(paths, name, patch) {
|
|
4451
|
+
requireProfileExists(paths, name);
|
|
4452
|
+
const existing = readProfile(paths, name) ?? {};
|
|
4453
|
+
const mergedEntries = { ...existing.entries, ...patch };
|
|
4454
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, { entries: mergedEntries });
|
|
4455
|
+
}
|
|
4456
|
+
function setProfileLaunchFlags(paths, name, patch) {
|
|
4457
|
+
requireProfileExists(paths, name);
|
|
4458
|
+
const existing = readProfile(paths, name) ?? {};
|
|
4459
|
+
const mergedLaunch = { ...existing.launch, ...patch };
|
|
4460
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
4461
|
+
// Read as `unknown` so the check sees the `undefined` a cleared key holds, which LaunchFlags' optional properties hide from the value type.
|
|
4462
|
+
launch: Object.values(mergedLaunch).every((value) => value === void 0) ? void 0 : mergedLaunch
|
|
4463
|
+
});
|
|
4464
|
+
}
|
|
4465
|
+
function setProfileMetadata(paths, name, patch) {
|
|
4466
|
+
requireProfileExists(paths, name);
|
|
4467
|
+
return applyPatch(profileJsonPath(paths, name), ConfigProfileSchema, {
|
|
4468
|
+
...patch.extends === void 0 ? {} : { extends: patch.extends.length === 0 ? void 0 : [...patch.extends] },
|
|
4469
|
+
...patch.description === void 0 ? {} : { description: patch.description === false ? void 0 : patch.description }
|
|
4470
|
+
});
|
|
4471
|
+
}
|
|
4472
|
+
function removeProfile(paths, name) {
|
|
4473
|
+
requireProfileExists(paths, name);
|
|
4474
|
+
import_node_fs3.default.rmSync(profileJsonPath(paths, name));
|
|
4475
|
+
}
|
|
4476
|
+
|
|
4477
|
+
// src/poolStore.ts
|
|
4478
|
+
var PoolNotFoundError = class extends CliError {
|
|
4479
|
+
constructor(poolName) {
|
|
4480
|
+
super(`No pool named "${poolName}". Run \`agent-shim pool add ${poolName} --identity <name>...\` first.`);
|
|
4481
|
+
this.poolName = poolName;
|
|
4482
|
+
this.name = "PoolNotFoundError";
|
|
4483
|
+
}
|
|
4484
|
+
poolName;
|
|
4485
|
+
};
|
|
4486
|
+
var PoolAlreadyExistsError = class extends CliError {
|
|
4487
|
+
constructor(poolName) {
|
|
4488
|
+
super(`A pool named "${poolName}" already exists. Use \`agent-shim pool set ${poolName}\` to change its members.`);
|
|
4489
|
+
this.poolName = poolName;
|
|
4490
|
+
this.name = "PoolAlreadyExistsError";
|
|
4491
|
+
}
|
|
4492
|
+
poolName;
|
|
4493
|
+
};
|
|
4494
|
+
var InvalidPoolNameError = class extends CliError {
|
|
4495
|
+
constructor(attemptedName) {
|
|
4496
|
+
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.`);
|
|
4497
|
+
this.attemptedName = attemptedName;
|
|
4498
|
+
this.name = "InvalidPoolNameError";
|
|
4499
|
+
}
|
|
4500
|
+
attemptedName;
|
|
4501
|
+
};
|
|
4502
|
+
function readPools(paths) {
|
|
4503
|
+
return readGlobalConfig(paths)?.pools ?? {};
|
|
4504
|
+
}
|
|
4505
|
+
function requirePool(paths, name) {
|
|
4506
|
+
const pool = readPools(paths)[name];
|
|
4507
|
+
if (pool === void 0) {
|
|
4508
|
+
throw new PoolNotFoundError(name);
|
|
4509
|
+
}
|
|
4510
|
+
return pool;
|
|
4511
|
+
}
|
|
4512
|
+
function writePools(paths, pools) {
|
|
4513
|
+
applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
|
|
4514
|
+
}
|
|
4515
|
+
function addPool(paths, name, identities) {
|
|
4516
|
+
if (!PoolNameSchema.safeParse(name).success) {
|
|
4517
|
+
throw new InvalidPoolNameError(name);
|
|
4518
|
+
}
|
|
4519
|
+
const pools = readPools(paths);
|
|
4520
|
+
if (pools[name] !== void 0) {
|
|
4521
|
+
throw new PoolAlreadyExistsError(name);
|
|
4522
|
+
}
|
|
4523
|
+
const pool = { identities: [...identities] };
|
|
4524
|
+
writePools(paths, { ...pools, [name]: pool });
|
|
4525
|
+
return pool;
|
|
4526
|
+
}
|
|
4527
|
+
function setPool(paths, name, identities) {
|
|
4528
|
+
requirePool(paths, name);
|
|
4529
|
+
const pool = { identities: [...identities] };
|
|
4530
|
+
writePools(paths, { ...readPools(paths), [name]: pool });
|
|
4531
|
+
return pool;
|
|
4532
|
+
}
|
|
4533
|
+
function removePool(paths, name) {
|
|
4534
|
+
requirePool(paths, name);
|
|
4535
|
+
const remaining = Object.fromEntries(Object.entries(readPools(paths)).filter(([existing]) => existing !== name));
|
|
4536
|
+
writePools(paths, remaining);
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
// src/identityStore.ts
|
|
4540
|
+
var IdentityNotFoundError = class extends CliError {
|
|
4541
|
+
constructor(name) {
|
|
4542
|
+
super(`No identity named "${name}". Run \`agent-shim identity add ${name}\` first.`);
|
|
4543
|
+
this.name = name;
|
|
4544
|
+
this.name = "IdentityNotFoundError";
|
|
4545
|
+
}
|
|
4546
|
+
name;
|
|
4547
|
+
};
|
|
4548
|
+
var IdentityAlreadyExistsError = class extends CliError {
|
|
4549
|
+
constructor(identityName) {
|
|
4550
|
+
super(`An identity named "${identityName}" already exists.`);
|
|
4551
|
+
this.identityName = identityName;
|
|
4552
|
+
this.name = "IdentityAlreadyExistsError";
|
|
4553
|
+
}
|
|
4554
|
+
identityName;
|
|
4555
|
+
};
|
|
4556
|
+
var InvalidIdentityNameError = class extends CliError {
|
|
4557
|
+
constructor(attemptedName) {
|
|
4558
|
+
super(
|
|
4559
|
+
`"${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.`
|
|
4560
|
+
);
|
|
4561
|
+
this.attemptedName = attemptedName;
|
|
4562
|
+
this.name = "InvalidIdentityNameError";
|
|
4563
|
+
}
|
|
4564
|
+
attemptedName;
|
|
4565
|
+
};
|
|
4566
|
+
function identityJsonPath(paths, name) {
|
|
4567
|
+
return import_node_path14.default.join(paths.identitiesDir, name, "identity.json");
|
|
4568
|
+
}
|
|
4569
|
+
function identityExists(paths, name) {
|
|
4570
|
+
return import_node_fs4.default.existsSync(identityJsonPath(paths, name));
|
|
4571
|
+
}
|
|
4572
|
+
function readIdentity(paths, name) {
|
|
4573
|
+
return readJson(identityJsonPath(paths, name), IdentitySchema);
|
|
4574
|
+
}
|
|
4575
|
+
function addIdentity(paths, name) {
|
|
4576
|
+
if (identityExists(paths, name)) {
|
|
4577
|
+
throw new IdentityAlreadyExistsError(name);
|
|
4578
|
+
}
|
|
4579
|
+
const parsed = IdentitySchema.safeParse({ name, allowAmbientCredential: false });
|
|
4580
|
+
if (!parsed.success) {
|
|
4581
|
+
throw new InvalidIdentityNameError(name);
|
|
4582
|
+
}
|
|
4583
|
+
writeJsonAtomic(identityJsonPath(paths, name), parsed.data);
|
|
4584
|
+
return parsed.data;
|
|
4585
|
+
}
|
|
4586
|
+
function useIdentity(paths, name) {
|
|
4587
|
+
const pool = poolNameOf(name);
|
|
4588
|
+
if (pool !== void 0) {
|
|
4589
|
+
requirePool(paths, pool);
|
|
4590
|
+
} else if (!identityExists(paths, name)) {
|
|
4591
|
+
throw new IdentityNotFoundError(name);
|
|
4592
|
+
}
|
|
4593
|
+
writeTextAtomic(paths.activeIdentityFile, `${name}
|
|
4594
|
+
`);
|
|
4595
|
+
}
|
|
4596
|
+
function readActiveIdentity(paths) {
|
|
4597
|
+
if (!import_node_fs4.default.existsSync(paths.activeIdentityFile)) {
|
|
4598
|
+
return void 0;
|
|
4599
|
+
}
|
|
4600
|
+
const raw = import_node_fs4.default.readFileSync(paths.activeIdentityFile, "utf8").trim();
|
|
4601
|
+
return raw === "" ? void 0 : raw;
|
|
4602
|
+
}
|
|
4603
|
+
function isIdentityDirectoryName(name) {
|
|
4604
|
+
return !name.startsWith(".");
|
|
4605
|
+
}
|
|
4606
|
+
function readIdentityForListing(paths, name) {
|
|
4607
|
+
try {
|
|
4608
|
+
return readIdentity(paths, name);
|
|
4609
|
+
} catch (error) {
|
|
4610
|
+
if (error instanceof ConfigValidationError || error instanceof SyntaxError) {
|
|
4611
|
+
return { problem: error.message.replace(/\s*\n\s*/g, " ") };
|
|
4612
|
+
}
|
|
4613
|
+
throw error;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
function listIdentities(paths) {
|
|
4617
|
+
if (!import_node_fs4.default.existsSync(paths.identitiesDir)) {
|
|
4618
|
+
return [];
|
|
4619
|
+
}
|
|
4620
|
+
const active = readActiveIdentity(paths);
|
|
4621
|
+
const names = import_node_fs4.default.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort();
|
|
4622
|
+
const result = [];
|
|
4623
|
+
for (const name of names) {
|
|
4624
|
+
const read = readIdentityForListing(paths, name);
|
|
4625
|
+
if (read === void 0) {
|
|
4626
|
+
continue;
|
|
4627
|
+
}
|
|
4628
|
+
const isActive = name === active;
|
|
4629
|
+
result.push("problem" in read ? { name, isActive, problem: read.problem } : { name, identity: read, isActive });
|
|
4630
|
+
}
|
|
4631
|
+
return result;
|
|
4632
|
+
}
|
|
4633
|
+
function setDefaultConfigProfile(paths, identityName, profileName) {
|
|
4634
|
+
if (!identityExists(paths, identityName)) {
|
|
4635
|
+
throw new IdentityNotFoundError(identityName);
|
|
4636
|
+
}
|
|
4637
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
4638
|
+
defaultConfigProfile: profileName
|
|
4639
|
+
});
|
|
4640
|
+
}
|
|
4641
|
+
function setAllowAmbientCredential(paths, identityName, allow) {
|
|
4642
|
+
if (!identityExists(paths, identityName)) {
|
|
4643
|
+
throw new IdentityNotFoundError(identityName);
|
|
4644
|
+
}
|
|
4645
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
4646
|
+
allowAmbientCredential: allow
|
|
4647
|
+
});
|
|
4648
|
+
}
|
|
4649
|
+
function setIdentityCredential(paths, identityName, change) {
|
|
4650
|
+
const existing = readIdentity(paths, identityName);
|
|
4651
|
+
if (existing === void 0) {
|
|
4652
|
+
throw new IdentityNotFoundError(identityName);
|
|
4653
|
+
}
|
|
4654
|
+
if (change === false) {
|
|
4655
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, { credential: void 0 });
|
|
4656
|
+
}
|
|
4657
|
+
const sources = change.sources ?? existing.credential?.sources;
|
|
4658
|
+
if (sources === void 0) {
|
|
4659
|
+
throw new UsageError(`Identity "${identityName}" has no credential yet: pass --credential to give it one before choosing its target.`);
|
|
4660
|
+
}
|
|
4661
|
+
const target = change.target ?? existing.credential?.target;
|
|
4662
|
+
const cache = change.cache === void 0 ? existing.credential?.cache : change.cache === false ? void 0 : change.cache;
|
|
4663
|
+
return applyPatch(identityJsonPath(paths, identityName), IdentitySchema, {
|
|
4664
|
+
credential: { sources: [...sources], ...target === void 0 ? {} : { target }, ...cache === void 0 ? {} : { cache } }
|
|
4665
|
+
});
|
|
4666
|
+
}
|
|
4667
|
+
function belongsToIdentity(entry, name) {
|
|
4668
|
+
return entry === name || entry === import_node_path14.default.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
4669
|
+
}
|
|
4670
|
+
function removeIdentity(paths, name) {
|
|
4671
|
+
if (!identityExists(paths, name)) {
|
|
4672
|
+
throw new IdentityNotFoundError(name);
|
|
4673
|
+
}
|
|
4674
|
+
for (const entry of import_node_fs4.default.readdirSync(paths.identitiesDir)) {
|
|
4675
|
+
if (belongsToIdentity(entry, name)) {
|
|
4676
|
+
import_node_fs4.default.rmSync(import_node_path14.default.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
if (readActiveIdentity(paths) === name) {
|
|
4680
|
+
import_node_fs4.default.rmSync(paths.activeIdentityFile);
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
|
|
4684
|
+
// src/providersStore.ts
|
|
4685
|
+
var import_node_fs5 = __toESM(require("node:fs"), 1);
|
|
4686
|
+
var import_node_path15 = __toESM(require("node:path"), 1);
|
|
4687
|
+
|
|
4688
|
+
// src/credentialCache.ts
|
|
4689
|
+
var import_zod7 = require("zod");
|
|
4690
|
+
var CachedCredentialSchema = import_zod7.z.strictObject({
|
|
4691
|
+
token: import_zod7.z.string().min(1),
|
|
4692
|
+
fetchedAt: import_zod7.z.number().int().nonnegative(),
|
|
4693
|
+
source: CredentialSourceSchema
|
|
4694
|
+
});
|
|
4695
|
+
|
|
4696
|
+
// src/credential.ts
|
|
4697
|
+
function summariseSource(source) {
|
|
4698
|
+
if ("env" in source) {
|
|
4699
|
+
return { kind: "env", variable: source.env };
|
|
4700
|
+
}
|
|
4701
|
+
if ("file" in source) {
|
|
4702
|
+
return { kind: "file", path: source.file };
|
|
4703
|
+
}
|
|
4704
|
+
if ("command" in source) {
|
|
4705
|
+
return { kind: "command", program: source.command[0] };
|
|
4706
|
+
}
|
|
4707
|
+
if ("op" in source) {
|
|
4708
|
+
return { kind: "op", reference: source.op };
|
|
4709
|
+
}
|
|
4710
|
+
if ("keychain" in source) {
|
|
4711
|
+
return {
|
|
4712
|
+
kind: "keychain",
|
|
4713
|
+
service: source.keychain.service,
|
|
4714
|
+
...source.keychain.account === void 0 ? {} : { account: source.keychain.account }
|
|
4715
|
+
};
|
|
4716
|
+
}
|
|
4717
|
+
return { kind: "literal" };
|
|
4718
|
+
}
|
|
4719
|
+
function summariseCredential(credential) {
|
|
4720
|
+
return {
|
|
4721
|
+
target: credential.target ?? "bearer",
|
|
4722
|
+
sources: credential.sources.map(summariseSource),
|
|
4723
|
+
...credential.cache === void 0 ? {} : { cache: credential.cache }
|
|
4724
|
+
};
|
|
4725
|
+
}
|
|
4726
|
+
function formatSourceSummary(summary) {
|
|
4727
|
+
switch (summary.kind) {
|
|
4728
|
+
case "env":
|
|
4729
|
+
return `env ${summary.variable}`;
|
|
4730
|
+
case "file":
|
|
4731
|
+
return `file ${summary.path}`;
|
|
4732
|
+
case "command":
|
|
4733
|
+
return `command ${summary.program}`;
|
|
4734
|
+
case "op":
|
|
4735
|
+
return `op ${summary.reference}`;
|
|
4736
|
+
case "keychain":
|
|
4737
|
+
return `keychain ${summary.service}${summary.account === void 0 ? "" : ` (account ${summary.account})`}`;
|
|
4738
|
+
case "literal":
|
|
4739
|
+
return "literal (non-secret placeholder)";
|
|
4740
|
+
default:
|
|
4741
|
+
return summary;
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
function formatCredentialSummary(summary) {
|
|
4745
|
+
const cache = summary.cache === void 0 ? "" : `, cached ${summary.cache.ttl === void 0 ? "with no expiry" : `for ${summary.cache.ttl}`}${summary.cache.store === void 0 ? "" : ` in the ${summary.cache.store} store`}`;
|
|
4746
|
+
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
4747
|
+
}
|
|
4748
|
+
function describeCredential(credential) {
|
|
4749
|
+
return formatCredentialSummary(summariseCredential(credential));
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
// src/providersStore.ts
|
|
4753
|
+
var ProviderNotFoundError = class extends CliError {
|
|
4754
|
+
constructor(name) {
|
|
4755
|
+
super(`No provider named "${name}". Run \`agent-shim provider add ${name}\` first.`);
|
|
4756
|
+
this.name = name;
|
|
4757
|
+
this.name = "ProviderNotFoundError";
|
|
4758
|
+
}
|
|
4759
|
+
name;
|
|
4760
|
+
};
|
|
4761
|
+
var ProviderAlreadyExistsError = class extends CliError {
|
|
4762
|
+
constructor(providerName) {
|
|
4763
|
+
super(`A provider named "${providerName}" already exists.`);
|
|
4764
|
+
this.providerName = providerName;
|
|
4765
|
+
this.name = "ProviderAlreadyExistsError";
|
|
4766
|
+
}
|
|
4767
|
+
providerName;
|
|
4768
|
+
};
|
|
4769
|
+
var InvalidProviderNameError = class extends CliError {
|
|
4770
|
+
constructor(attemptedName) {
|
|
4771
|
+
super(
|
|
4772
|
+
`"${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.`
|
|
4773
|
+
);
|
|
4774
|
+
this.attemptedName = attemptedName;
|
|
4775
|
+
this.name = "InvalidProviderNameError";
|
|
4776
|
+
}
|
|
4777
|
+
attemptedName;
|
|
4778
|
+
};
|
|
4779
|
+
var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
|
|
4780
|
+
function providerJsonPath(paths, name) {
|
|
4781
|
+
return import_node_path15.default.join(paths.providersDir, `${name}.json`);
|
|
4782
|
+
}
|
|
4783
|
+
function providerExists(paths, name) {
|
|
4784
|
+
return import_node_fs5.default.existsSync(providerJsonPath(paths, name));
|
|
4785
|
+
}
|
|
4786
|
+
var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
|
|
4787
|
+
var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
|
|
4788
|
+
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
4789
|
+
function isRecord2(value) {
|
|
4790
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4791
|
+
}
|
|
4792
|
+
function legacyProviderConversion(raw) {
|
|
4793
|
+
if (!isRecord2(raw) || "credential" in raw) {
|
|
4794
|
+
return void 0;
|
|
4795
|
+
}
|
|
4796
|
+
const env = isRecord2(raw.env) ? raw.env : void 0;
|
|
4797
|
+
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
4798
|
+
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
4799
|
+
if (fields.length === 0) {
|
|
4800
|
+
return void 0;
|
|
4801
|
+
}
|
|
4802
|
+
const sources = [];
|
|
4803
|
+
if (typeof raw.tokenEnv === "string") {
|
|
4804
|
+
sources.push({ env: raw.tokenEnv });
|
|
4805
|
+
}
|
|
4806
|
+
if (Array.isArray(raw.tokenCommand)) {
|
|
4807
|
+
sources.push({ command: raw.tokenCommand });
|
|
4808
|
+
}
|
|
4809
|
+
if (typeof env?.ANTHROPIC_AUTH_TOKEN === "string" && env.ANTHROPIC_AUTH_TOKEN !== "") {
|
|
4810
|
+
sources.push({ literal: LEGACY_LITERAL_PLACEHOLDER });
|
|
4811
|
+
}
|
|
4812
|
+
const remainingEnv = env === void 0 ? void 0 : Object.fromEntries(Object.entries(env).filter(([key]) => !LEGACY_ENV_CREDENTIAL_KEYS.includes(key)));
|
|
4813
|
+
const replacedKeys = [...LEGACY_PROVIDER_FIELDS, "env"];
|
|
4814
|
+
const rest = Object.fromEntries(Object.entries(raw).filter(([key]) => !replacedKeys.includes(key)));
|
|
4815
|
+
return {
|
|
4816
|
+
fields,
|
|
4817
|
+
replacement: {
|
|
4818
|
+
...rest,
|
|
4819
|
+
credential: { sources, ...raw.authScheme === "apiKey" ? { target: "apiKey" } : {} },
|
|
4820
|
+
...remainingEnv === void 0 || Object.keys(remainingEnv).length === 0 ? {} : { env: remainingEnv }
|
|
4821
|
+
}
|
|
4822
|
+
};
|
|
4823
|
+
}
|
|
4824
|
+
var LegacyProviderFileError = class extends CliError {
|
|
4825
|
+
constructor(filePath, conversion) {
|
|
4826
|
+
super(
|
|
4827
|
+
`${filePath} uses ${conversion.fields.join(", ")}, which a credential block replaced. Rewrite it as:
|
|
4828
|
+
` + JSON.stringify(conversion.replacement, null, 2)
|
|
4829
|
+
);
|
|
4830
|
+
this.filePath = filePath;
|
|
4831
|
+
this.conversion = conversion;
|
|
4832
|
+
this.name = "LegacyProviderFileError";
|
|
4833
|
+
}
|
|
4834
|
+
filePath;
|
|
4835
|
+
conversion;
|
|
4836
|
+
};
|
|
4837
|
+
function parseProviderFile(filePath, raw) {
|
|
4838
|
+
const conversion = legacyProviderConversion(raw);
|
|
4839
|
+
if (conversion !== void 0) {
|
|
4840
|
+
throw new LegacyProviderFileError(filePath, conversion);
|
|
4841
|
+
}
|
|
4842
|
+
const parsed = ProviderSchema.safeParse(raw);
|
|
4843
|
+
if (!parsed.success) {
|
|
4844
|
+
throw new ConfigValidationError(filePath, parsed.error.issues);
|
|
4845
|
+
}
|
|
4846
|
+
return parsed.data;
|
|
4847
|
+
}
|
|
4848
|
+
function readProvider(paths, name) {
|
|
4849
|
+
const filePath = providerJsonPath(paths, name);
|
|
4850
|
+
if (!import_node_fs5.default.existsSync(filePath)) {
|
|
4851
|
+
return void 0;
|
|
4852
|
+
}
|
|
4853
|
+
const raw = JSON.parse(import_node_fs5.default.readFileSync(filePath, "utf8"));
|
|
4854
|
+
return parseProviderFile(filePath, raw);
|
|
4855
|
+
}
|
|
4856
|
+
function listProviders(paths) {
|
|
4857
|
+
const names = listProviderNames(
|
|
4858
|
+
(dir) => import_node_fs5.default.readdirSync(dir),
|
|
4859
|
+
paths.providersDir
|
|
4860
|
+
);
|
|
4861
|
+
const result = [];
|
|
4862
|
+
for (const name of names) {
|
|
4863
|
+
let provider;
|
|
4864
|
+
try {
|
|
4865
|
+
provider = readProvider(paths, name);
|
|
4866
|
+
} catch (error) {
|
|
4867
|
+
if (!(error instanceof ConfigValidationError || error instanceof LegacyProviderFileError)) {
|
|
4868
|
+
throw error;
|
|
4869
|
+
}
|
|
4870
|
+
console.error(`agent-shim: providers/${name}.json is invalid and was skipped: ${error.message}`);
|
|
4871
|
+
continue;
|
|
4872
|
+
}
|
|
4873
|
+
if (provider !== void 0) {
|
|
4874
|
+
result.push({ name, provider });
|
|
4875
|
+
}
|
|
4876
|
+
}
|
|
4877
|
+
return result;
|
|
4878
|
+
}
|
|
4879
|
+
function listProviderNames(readdir, providersDir) {
|
|
4880
|
+
let entries;
|
|
4881
|
+
try {
|
|
4882
|
+
entries = readdir(providersDir);
|
|
4883
|
+
} catch {
|
|
4884
|
+
return [];
|
|
4885
|
+
}
|
|
4886
|
+
return entries.filter((entry) => entry.endsWith(".json")).map((entry) => entry.slice(0, -".json".length)).sort();
|
|
4887
|
+
}
|
|
4888
|
+
function describeProviderEndpoint(provider) {
|
|
4889
|
+
return isCodexProvider(provider) ? "codex translation daemon" : provider.baseUrl;
|
|
4890
|
+
}
|
|
4891
|
+
var ProviderKindMismatchError = class extends UsageError {
|
|
4892
|
+
constructor(message) {
|
|
4893
|
+
super(message);
|
|
4894
|
+
this.name = "ProviderKindMismatchError";
|
|
4895
|
+
}
|
|
4896
|
+
};
|
|
4897
|
+
function isEmptyCodexConfig(config) {
|
|
4898
|
+
return config === void 0 || config.defaultModel === void 0 && config.effort === void 0 && (config.models === void 0 || Object.keys(config.models).length === 0);
|
|
4899
|
+
}
|
|
4900
|
+
function addProvider(paths, name, input) {
|
|
4901
|
+
if (providerExists(paths, name)) {
|
|
4902
|
+
throw new ProviderAlreadyExistsError(name);
|
|
4903
|
+
}
|
|
4904
|
+
if (!PROVIDER_NAME_RE.test(name)) {
|
|
4905
|
+
throw new InvalidProviderNameError(name);
|
|
4906
|
+
}
|
|
4907
|
+
const kind = input.kind ?? "http";
|
|
4908
|
+
if (kind === "codex" && input.baseUrl !== void 0) {
|
|
4909
|
+
throw new ProviderKindMismatchError("A codex provider has no base URL: the launcher points its sessions at the front-door daemon. Drop --base-url.");
|
|
4910
|
+
}
|
|
4911
|
+
if (kind === "http" && input.baseUrl === void 0) {
|
|
4912
|
+
throw new ProviderKindMismatchError("An http provider needs --base-url.");
|
|
4913
|
+
}
|
|
4914
|
+
if (kind === "http" && !isEmptyCodexConfig(input.codex)) {
|
|
4915
|
+
throw new ProviderKindMismatchError("Codex settings apply only to a codex provider (--kind codex).");
|
|
4916
|
+
}
|
|
4917
|
+
const shared = {
|
|
4918
|
+
displayName: input.displayName,
|
|
4919
|
+
credential: { sources: input.sources, ...input.target === void 0 ? {} : { target: input.target } },
|
|
4920
|
+
...input.env === void 0 || Object.keys(input.env).length === 0 ? {} : { env: input.env }
|
|
4921
|
+
};
|
|
4922
|
+
const parsed = ProviderSchema.safeParse(
|
|
4923
|
+
kind === "codex" ? { kind, ...shared, ...isEmptyCodexConfig(input.codex) ? {} : { codex: input.codex } } : { ...shared, baseUrl: input.baseUrl }
|
|
4924
|
+
);
|
|
4925
|
+
if (!parsed.success) {
|
|
4926
|
+
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
4927
|
+
}
|
|
4928
|
+
writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
|
|
4929
|
+
return parsed.data;
|
|
4930
|
+
}
|
|
4931
|
+
function updateProvider(paths, name, input) {
|
|
4932
|
+
const existing = readProvider(paths, name);
|
|
4933
|
+
if (existing === void 0) {
|
|
4934
|
+
throw new ProviderNotFoundError(name);
|
|
4935
|
+
}
|
|
4936
|
+
const unset = new Set(input.unsetEnv);
|
|
4937
|
+
const env = Object.fromEntries(Object.entries({ ...existing.env, ...input.env }).filter(([key]) => !unset.has(key)));
|
|
4938
|
+
const target = input.target ?? existing.credential.target;
|
|
4939
|
+
const cache = input.cache === void 0 ? existing.credential.cache : input.cache === false ? void 0 : input.cache;
|
|
4940
|
+
if (isCodexProvider(existing) && input.baseUrl !== void 0) {
|
|
4941
|
+
throw new ProviderKindMismatchError(`Provider "${name}" is a codex provider, which has no base URL.`);
|
|
4942
|
+
}
|
|
4943
|
+
if (!isCodexProvider(existing) && !isEmptyCodexConfig(input.codex)) {
|
|
4944
|
+
throw new ProviderKindMismatchError(`Provider "${name}" is an http provider; codex settings apply only to a codex provider.`);
|
|
4945
|
+
}
|
|
4946
|
+
const shared = {
|
|
4947
|
+
...input.displayName === void 0 ? {} : { displayName: input.displayName },
|
|
4948
|
+
credential: {
|
|
4949
|
+
sources: input.sources ?? existing.credential.sources,
|
|
4950
|
+
...target === void 0 ? {} : { target },
|
|
4951
|
+
...cache === void 0 ? {} : { cache }
|
|
4952
|
+
},
|
|
4953
|
+
env: Object.keys(env).length === 0 ? void 0 : env
|
|
4954
|
+
};
|
|
4955
|
+
let candidate;
|
|
4956
|
+
if (isCodexProvider(existing)) {
|
|
4957
|
+
const codex = {
|
|
4958
|
+
...existing.codex,
|
|
4959
|
+
...input.codex,
|
|
4960
|
+
...existing.codex?.models === void 0 && input.codex?.models === void 0 ? {} : { models: { ...existing.codex?.models, ...input.codex?.models } }
|
|
4961
|
+
};
|
|
4962
|
+
candidate = { ...existing, ...shared, codex: isEmptyCodexConfig(codex) ? void 0 : codex };
|
|
4963
|
+
} else {
|
|
4964
|
+
candidate = { ...existing, ...shared, ...input.baseUrl === void 0 ? {} : { baseUrl: input.baseUrl } };
|
|
4965
|
+
}
|
|
4966
|
+
const parsed = ProviderSchema.safeParse(candidate);
|
|
4967
|
+
if (!parsed.success) {
|
|
4968
|
+
throw new ConfigValidationError(providerJsonPath(paths, name), parsed.error.issues);
|
|
4969
|
+
}
|
|
4970
|
+
writeJsonAtomic(providerJsonPath(paths, name), parsed.data);
|
|
4971
|
+
return parsed.data;
|
|
4972
|
+
}
|
|
4973
|
+
function removeProvider(paths, name) {
|
|
4974
|
+
if (!providerExists(paths, name)) {
|
|
4975
|
+
throw new ProviderNotFoundError(name);
|
|
4976
|
+
}
|
|
4977
|
+
import_node_fs5.default.rmSync(providerJsonPath(paths, name));
|
|
4978
|
+
}
|
|
4979
|
+
function cascadeProviderName(cascade) {
|
|
4980
|
+
let name;
|
|
4981
|
+
for (const layer of assembleCascade(cascade).layers) {
|
|
4982
|
+
if (layer.launch?.provider !== void 0) {
|
|
4983
|
+
name = layer.launch.provider;
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4986
|
+
return name;
|
|
4987
|
+
}
|
|
4988
|
+
|
|
4989
|
+
// src/directoryRulesStore.ts
|
|
4990
|
+
var DirectoryRuleNotFoundError = class extends CliError {
|
|
4991
|
+
constructor(rulePath) {
|
|
4992
|
+
super(`No directory rule found for path "${rulePath}".`);
|
|
4993
|
+
this.rulePath = rulePath;
|
|
4994
|
+
this.name = "DirectoryRuleNotFoundError";
|
|
4995
|
+
}
|
|
4996
|
+
rulePath;
|
|
4997
|
+
};
|
|
4998
|
+
var DirectoryRuleMissingTargetError = class extends CliError {
|
|
4999
|
+
constructor() {
|
|
5000
|
+
super("A directory rule must set at least one of --config-profile or --identity.");
|
|
5001
|
+
this.name = "DirectoryRuleMissingTargetError";
|
|
5002
|
+
}
|
|
5003
|
+
};
|
|
5004
|
+
var DirectoryRuleAlreadyExistsError = class extends CliError {
|
|
5005
|
+
constructor(rulePath) {
|
|
5006
|
+
super(`A directory rule for path "${rulePath}" already exists. Use \`agent-shim rule set\` to change it.`);
|
|
5007
|
+
this.rulePath = rulePath;
|
|
5008
|
+
this.name = "DirectoryRuleAlreadyExistsError";
|
|
5009
|
+
}
|
|
5010
|
+
rulePath;
|
|
5011
|
+
};
|
|
5012
|
+
function readDirectoryRules(paths) {
|
|
5013
|
+
return readJson(paths.directoryRulesFile, DirectoryRulesSchema) ?? { rules: [] };
|
|
5014
|
+
}
|
|
5015
|
+
function writeDirectoryRules(paths, rules) {
|
|
5016
|
+
const parsed = DirectoryRulesSchema.safeParse(rules);
|
|
5017
|
+
if (!parsed.success) {
|
|
5018
|
+
throw new ConfigValidationError(paths.directoryRulesFile, parsed.error.issues);
|
|
5019
|
+
}
|
|
5020
|
+
writeJsonAtomic(paths.directoryRulesFile, parsed.data);
|
|
5021
|
+
}
|
|
5022
|
+
function listDirectoryRules(paths) {
|
|
5023
|
+
return readDirectoryRules(paths).rules;
|
|
5024
|
+
}
|
|
5025
|
+
function addDirectoryRule(paths, rulePath, options) {
|
|
5026
|
+
if (options.configProfile === void 0 && options.identity === void 0) {
|
|
5027
|
+
throw new DirectoryRuleMissingTargetError();
|
|
5028
|
+
}
|
|
5029
|
+
const current = readDirectoryRules(paths);
|
|
5030
|
+
if (current.rules.some((rule) => rule.path === rulePath)) {
|
|
5031
|
+
throw new DirectoryRuleAlreadyExistsError(rulePath);
|
|
5032
|
+
}
|
|
5033
|
+
const created = buildNewRule(rulePath, options);
|
|
5034
|
+
writeDirectoryRules(paths, { ...current, rules: [...current.rules, created] });
|
|
5035
|
+
return created;
|
|
5036
|
+
}
|
|
5037
|
+
function ruleHasEffect(rule) {
|
|
5038
|
+
return rule.configProfile !== void 0 || rule.identity !== void 0 || rule.categories !== void 0 || rule.entries !== void 0 || rule.launch !== void 0;
|
|
5039
|
+
}
|
|
5040
|
+
function updateDirectoryRule(paths, rulePath, options) {
|
|
5041
|
+
const current = readDirectoryRules(paths);
|
|
5042
|
+
const index = current.rules.findIndex((rule) => rule.path === rulePath);
|
|
5043
|
+
const existing = index === -1 ? void 0 : current.rules[index];
|
|
5044
|
+
if (existing === void 0) {
|
|
5045
|
+
throw new DirectoryRuleNotFoundError(rulePath);
|
|
5046
|
+
}
|
|
5047
|
+
const { configProfile, identity, ...rest } = existing;
|
|
5048
|
+
const nextConfigProfile = options.configProfile === void 0 ? configProfile : options.configProfile === false ? void 0 : options.configProfile;
|
|
5049
|
+
const nextIdentity = options.identity === void 0 ? identity : options.identity === false ? void 0 : options.identity;
|
|
5050
|
+
const updated = {
|
|
5051
|
+
...rest,
|
|
5052
|
+
...nextConfigProfile === void 0 ? {} : { configProfile: nextConfigProfile },
|
|
5053
|
+
...nextIdentity === void 0 ? {} : { identity: nextIdentity }
|
|
5054
|
+
};
|
|
5055
|
+
if (!ruleHasEffect(updated)) {
|
|
5056
|
+
throw new DirectoryRuleMissingTargetError();
|
|
5057
|
+
}
|
|
5058
|
+
const nextRules = [...current.rules];
|
|
5059
|
+
nextRules[index] = updated;
|
|
5060
|
+
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
5061
|
+
return updated;
|
|
5062
|
+
}
|
|
5063
|
+
function buildNewRule(rulePath, options) {
|
|
5064
|
+
return {
|
|
5065
|
+
path: rulePath,
|
|
5066
|
+
...options.configProfile !== void 0 ? { configProfile: options.configProfile } : {},
|
|
5067
|
+
...options.identity !== void 0 ? { identity: options.identity } : {}
|
|
5068
|
+
};
|
|
5069
|
+
}
|
|
5070
|
+
function removeDirectoryRule(paths, rulePath) {
|
|
5071
|
+
const current = readDirectoryRules(paths);
|
|
5072
|
+
const nextRules = current.rules.filter((rule) => rule.path !== rulePath);
|
|
5073
|
+
if (nextRules.length === current.rules.length) {
|
|
5074
|
+
throw new DirectoryRuleNotFoundError(rulePath);
|
|
5075
|
+
}
|
|
5076
|
+
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
5077
|
+
}
|
|
5078
|
+
|
|
5079
|
+
// src/checkReport.ts
|
|
5080
|
+
var import_node_os3 = __toESM(require("node:os"), 1);
|
|
5081
|
+
|
|
5082
|
+
// src/launcher/cascade.ts
|
|
5083
|
+
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
5084
|
+
|
|
5085
|
+
// src/pathNorm.ts
|
|
5086
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
5087
|
+
function expandTilde(value, home) {
|
|
5088
|
+
if (value === "~") {
|
|
5089
|
+
return home;
|
|
5090
|
+
}
|
|
5091
|
+
if (value.startsWith("~/")) {
|
|
5092
|
+
return import_node_path16.default.join(home, value.slice(2));
|
|
5093
|
+
}
|
|
5094
|
+
return value;
|
|
5095
|
+
}
|
|
5096
|
+
function normaliseRulePath(rulePath, home) {
|
|
5097
|
+
return import_node_path16.default.resolve(expandTilde(rulePath, home));
|
|
5098
|
+
}
|
|
5099
|
+
|
|
5100
|
+
// src/launcher/cascade.ts
|
|
5101
|
+
var PORTABLE_CONFIG_FILENAME = ".agent-shim.json";
|
|
5102
|
+
var PORTABLE_LOCAL_CONFIG_FILENAME = ".agent-shim.local.json";
|
|
5103
|
+
function loadCascadeInput(params) {
|
|
5104
|
+
const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
|
|
5105
|
+
const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
|
|
5106
|
+
const loadProfile = (name) => {
|
|
5107
|
+
const filepath = import_node_path17.default.join(params.paths.configProfilesDir, `${name}.json`);
|
|
5108
|
+
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
5109
|
+
if (loaded === void 0) {
|
|
5110
|
+
return void 0;
|
|
5111
|
+
}
|
|
5112
|
+
return { name, profile: loaded.config, entryOrder: loaded.entryOrder, filepath: loaded.filepath };
|
|
5113
|
+
};
|
|
5114
|
+
const limit = globalConfig?.config.walkUpLimit;
|
|
5115
|
+
const dirs = walkDirectoryAncestors(params.cwd, {
|
|
5116
|
+
home: params.home,
|
|
5117
|
+
...limit === void 0 ? {} : { limit: expandTilde(limit, params.home) },
|
|
5118
|
+
...params.isReadable === void 0 ? {} : { isReadable: params.isReadable }
|
|
5119
|
+
});
|
|
5120
|
+
const levels = [];
|
|
5121
|
+
for (const dir of dirs) {
|
|
5122
|
+
const portable = loadConfigFile(import_node_path17.default.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path17.default.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
5123
|
+
const portableLocal = loadConfigFile(import_node_path17.default.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(import_node_path17.default.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
5124
|
+
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
5125
|
+
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
5126
|
+
return {
|
|
5127
|
+
rule,
|
|
5128
|
+
...entryOrder === void 0 ? {} : { entryOrder },
|
|
5129
|
+
filepath: params.paths.directoryRulesFile
|
|
5130
|
+
};
|
|
5131
|
+
});
|
|
5132
|
+
if (portable === void 0 && portableLocal === void 0 && rules.length === 0) {
|
|
5133
|
+
continue;
|
|
5134
|
+
}
|
|
5135
|
+
levels.push({
|
|
5136
|
+
dir,
|
|
5137
|
+
...portable === void 0 ? {} : { portable: { config: portable.config, entryOrder: portable.entryOrder, filepath: portable.filepath } },
|
|
5138
|
+
...rules.length === 0 ? {} : { rules },
|
|
5139
|
+
...portableLocal === void 0 ? {} : { portableLocal: { config: portableLocal.config, entryOrder: portableLocal.entryOrder, filepath: portableLocal.filepath } }
|
|
5140
|
+
});
|
|
5141
|
+
}
|
|
5142
|
+
const input = {
|
|
5143
|
+
home: params.home,
|
|
5144
|
+
...globalConfig === void 0 ? {} : { globalConfig: { config: globalConfig.config, entryOrder: globalConfig.entryOrder, filepath: globalConfig.filepath } },
|
|
5145
|
+
...params.baseConfigProfile === void 0 ? {} : { baseConfigProfile: params.baseConfigProfile },
|
|
5146
|
+
loadProfile,
|
|
5147
|
+
levels,
|
|
5148
|
+
...params.cliOverride === void 0 ? {} : { cliOverride: params.cliOverride }
|
|
5149
|
+
};
|
|
5150
|
+
return { input, ...globalConfig === void 0 ? {} : { globalConfig: globalConfig.config } };
|
|
5151
|
+
}
|
|
5152
|
+
function readDirectorySelections(loaded) {
|
|
5153
|
+
let identity;
|
|
5154
|
+
let configProfile;
|
|
5155
|
+
for (const level of loaded.input.levels ?? []) {
|
|
5156
|
+
for (const source of [level.portable?.config, ...(level.rules ?? []).map((entry) => entry.rule), level.portableLocal?.config]) {
|
|
5157
|
+
if (source === void 0) {
|
|
5158
|
+
continue;
|
|
5159
|
+
}
|
|
5160
|
+
if (source.identity !== void 0) {
|
|
5161
|
+
identity = source.identity;
|
|
5162
|
+
}
|
|
5163
|
+
if (source.configProfile !== void 0) {
|
|
5164
|
+
configProfile = source.configProfile;
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
return { ...identity === void 0 ? {} : { identity }, ...configProfile === void 0 ? {} : { configProfile } };
|
|
5169
|
+
}
|
|
5170
|
+
|
|
5171
|
+
// src/usage/preflight.ts
|
|
5172
|
+
var MS_PER_SECOND2 = 1e3;
|
|
5173
|
+
var SECONDS_PER_MINUTE = 60;
|
|
5174
|
+
var MINUTES_PER_HOUR = 60;
|
|
5175
|
+
var HOURS_PER_DAY = 24;
|
|
5176
|
+
function formatAge(ms) {
|
|
5177
|
+
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
5178
|
+
if (minutes < MINUTES_PER_HOUR) {
|
|
5179
|
+
return `${String(Math.max(minutes, 0))}m`;
|
|
5180
|
+
}
|
|
5181
|
+
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
5182
|
+
return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
|
|
5183
|
+
}
|
|
5184
|
+
function effectiveWindow(window, nowMs) {
|
|
5185
|
+
const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
|
|
5186
|
+
if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
|
|
5187
|
+
return { reset: true, utilization: 0 };
|
|
5188
|
+
}
|
|
5189
|
+
return {
|
|
5190
|
+
reset: false,
|
|
5191
|
+
...window.utilization === void 0 ? {} : { utilization: window.utilization },
|
|
5192
|
+
...resetsAtMs === void 0 ? {} : { resetsAtMs },
|
|
5193
|
+
...window.status === void 0 ? {} : { status: window.status }
|
|
5194
|
+
};
|
|
5195
|
+
}
|
|
5196
|
+
|
|
5197
|
+
// src/usage/poolPick.ts
|
|
5198
|
+
var import_zod9 = require("zod");
|
|
5199
|
+
|
|
5200
|
+
// src/usage/plan.ts
|
|
5201
|
+
var TIER_MULTIPLIER = /(?:^|_)(\d+)x$/;
|
|
5202
|
+
var ZERO_TIER = /(?:^|_)zero$/;
|
|
5203
|
+
var BASE_TIER = /(?:^|_)(?:pro|free)$/;
|
|
5204
|
+
function planOf(account) {
|
|
5205
|
+
const tier = account?.organizationRateLimitTier ?? account?.userRateLimitTier;
|
|
5206
|
+
const tierField = tier === void 0 ? {} : { tier };
|
|
5207
|
+
if (tier === void 0) {
|
|
5208
|
+
return { kind: "subscription", capacity: 1, recognised: false };
|
|
5209
|
+
}
|
|
5210
|
+
if (ZERO_TIER.test(tier)) {
|
|
5211
|
+
return { kind: "pay-per-use", ...tierField };
|
|
5212
|
+
}
|
|
5213
|
+
const multiplier = TIER_MULTIPLIER.exec(tier)?.[1];
|
|
5214
|
+
if (multiplier !== void 0) {
|
|
5215
|
+
return { kind: "subscription", capacity: Number(multiplier), recognised: true, ...tierField };
|
|
5216
|
+
}
|
|
5217
|
+
return { kind: "subscription", capacity: 1, recognised: BASE_TIER.test(tier), ...tierField };
|
|
5218
|
+
}
|
|
5219
|
+
|
|
5220
|
+
// src/usage/rateLimit.ts
|
|
5221
|
+
var MS_PER_SECOND3 = 1e3;
|
|
5222
|
+
function epochSecondsToIso(value) {
|
|
5223
|
+
if (value === void 0) {
|
|
5224
|
+
return void 0;
|
|
5225
|
+
}
|
|
5226
|
+
const seconds = Number(value);
|
|
5227
|
+
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * MS_PER_SECOND3).toISOString() : void 0;
|
|
5228
|
+
}
|
|
5229
|
+
function nonNegativeNumber(value) {
|
|
5230
|
+
if (value === void 0 || value.trim() === "") {
|
|
5231
|
+
return void 0;
|
|
5232
|
+
}
|
|
5233
|
+
const parsed = Number(value);
|
|
5234
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
5235
|
+
}
|
|
5236
|
+
var UNIFIED_PREFIX = "anthropic-ratelimit-unified-";
|
|
5237
|
+
function unifiedWindow(headers, window) {
|
|
5238
|
+
const utilization = nonNegativeNumber(headers[`${UNIFIED_PREFIX}${window}-utilization`]);
|
|
5239
|
+
const resetsAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}${window}-reset`]);
|
|
5240
|
+
const status = headers[`${UNIFIED_PREFIX}${window}-status`];
|
|
5241
|
+
if (utilization === void 0 && resetsAt === void 0 && status === void 0) {
|
|
5242
|
+
return void 0;
|
|
5243
|
+
}
|
|
5244
|
+
return { ...utilization === void 0 ? {} : { utilization }, ...resetsAt === void 0 ? {} : { resetsAt }, ...status === void 0 ? {} : { status } };
|
|
5245
|
+
}
|
|
5246
|
+
function parseUnifiedRateLimit(headers) {
|
|
5247
|
+
if (!Object.keys(headers).some((name) => name.startsWith(UNIFIED_PREFIX))) {
|
|
5248
|
+
return void 0;
|
|
5249
|
+
}
|
|
5250
|
+
const status = headers[`${UNIFIED_PREFIX}status`];
|
|
5251
|
+
const fiveHour = unifiedWindow(headers, "5h");
|
|
5252
|
+
const sevenDay = unifiedWindow(headers, "7d");
|
|
5253
|
+
const representativeClaim = headers[`${UNIFIED_PREFIX}representative-claim`];
|
|
5254
|
+
const resetAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}reset`]);
|
|
5255
|
+
const overageStatus = headers[`${UNIFIED_PREFIX}overage-status`];
|
|
5256
|
+
return {
|
|
5257
|
+
...status === void 0 ? {} : { status },
|
|
5258
|
+
...fiveHour === void 0 ? {} : { fiveHour },
|
|
5259
|
+
...sevenDay === void 0 ? {} : { sevenDay },
|
|
5260
|
+
...representativeClaim === void 0 ? {} : { representativeClaim },
|
|
5261
|
+
...resetAt === void 0 ? {} : { resetAt },
|
|
5262
|
+
...overageStatus === void 0 ? {} : { overageStatus }
|
|
5263
|
+
};
|
|
5264
|
+
}
|
|
5265
|
+
|
|
5266
|
+
// src/usage/scan.ts
|
|
5267
|
+
var import_zod8 = require("zod");
|
|
5268
|
+
var AnthropicUsageSchema = import_zod8.z.looseObject({
|
|
5269
|
+
input_tokens: import_zod8.z.number().int().nonnegative().nullish(),
|
|
5270
|
+
output_tokens: import_zod8.z.number().int().nonnegative().nullish(),
|
|
5271
|
+
cache_creation_input_tokens: import_zod8.z.number().int().nonnegative().nullish(),
|
|
5272
|
+
cache_read_input_tokens: import_zod8.z.number().int().nonnegative().nullish()
|
|
5273
|
+
});
|
|
5274
|
+
var ErrorObjectSchema = import_zod8.z.looseObject({
|
|
5275
|
+
type: import_zod8.z.string().optional(),
|
|
5276
|
+
code: import_zod8.z.union([import_zod8.z.string(), import_zod8.z.number()]).optional(),
|
|
5277
|
+
message: import_zod8.z.string().optional()
|
|
5278
|
+
});
|
|
5279
|
+
|
|
5280
|
+
// src/usage/middleware.ts
|
|
5281
|
+
var ANTHROPIC_PROVIDER = "anthropic";
|
|
5282
|
+
|
|
5283
|
+
// src/usage/pick.ts
|
|
5284
|
+
var MS_PER_SECOND4 = 1e3;
|
|
5285
|
+
var MS_PER_HOUR = 36e5;
|
|
5286
|
+
var HOURS_PER_DAY2 = 24;
|
|
5287
|
+
var FIVE_HOURS = 5;
|
|
5288
|
+
var PROMPT_CACHE_TTL_HOURS = 1;
|
|
5289
|
+
var PERCENT = 100;
|
|
5290
|
+
var SEVEN_DAYS = 7;
|
|
5291
|
+
var FIVE_HOUR_WINDOW_MS = FIVE_HOURS * MS_PER_HOUR;
|
|
5292
|
+
var SEVEN_DAY_WINDOW_MS = SEVEN_DAYS * HOURS_PER_DAY2 * MS_PER_HOUR;
|
|
5293
|
+
var PROMPT_CACHE_TTL_MS = PROMPT_CACHE_TTL_HOURS * MS_PER_HOUR;
|
|
5294
|
+
var CLASS_ORDER = { scored: 0, unknown: 1, "pay-per-use": 2, ineligible: 3 };
|
|
5295
|
+
function unifiedOf(state) {
|
|
5296
|
+
return state?.rateLimit?.unified;
|
|
5297
|
+
}
|
|
5298
|
+
function limitBlockedUntil(state, nowMs) {
|
|
5299
|
+
const limit = state?.lastLimit;
|
|
5300
|
+
if (state === void 0 || limit === void 0) {
|
|
5301
|
+
return void 0;
|
|
5302
|
+
}
|
|
5303
|
+
if (Date.parse(state.lastRequestAt) > Date.parse(limit.observedAt)) {
|
|
5304
|
+
return void 0;
|
|
5305
|
+
}
|
|
5306
|
+
const until = limit.resetAt === void 0 ? limit.retryAfterSeconds === void 0 ? void 0 : Date.parse(limit.observedAt) + limit.retryAfterSeconds * MS_PER_SECOND4 : Date.parse(limit.resetAt);
|
|
5307
|
+
return until !== void 0 && until > nowMs ? until : void 0;
|
|
5308
|
+
}
|
|
5309
|
+
function windowBlockedUntil(windows) {
|
|
5310
|
+
const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
|
|
5311
|
+
return resets.length === 0 ? void 0 : Math.max(...resets);
|
|
5312
|
+
}
|
|
5313
|
+
function fiveHourBurn(records) {
|
|
5314
|
+
const points = [];
|
|
5315
|
+
for (const record of records) {
|
|
5316
|
+
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.fiveHour;
|
|
5317
|
+
if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
|
|
5318
|
+
points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record.at), utilization: window.utilization } });
|
|
5319
|
+
}
|
|
5320
|
+
}
|
|
5321
|
+
const latest = points.at(-1)?.resetsAt;
|
|
5322
|
+
const current = points.filter((entry) => entry.resetsAt === latest).map((entry) => entry.point);
|
|
5323
|
+
const first = current.at(0);
|
|
5324
|
+
const last = current.at(-1);
|
|
5325
|
+
if (first === void 0 || last === void 0 || last.atMs <= first.atMs || last.utilization <= first.utilization) {
|
|
5326
|
+
return void 0;
|
|
5327
|
+
}
|
|
5328
|
+
return (last.utilization - first.utilization) / (last.atMs - first.atMs);
|
|
5329
|
+
}
|
|
5330
|
+
function percent(fraction) {
|
|
5331
|
+
return `${String(Math.round(fraction * PERCENT))}%`;
|
|
5332
|
+
}
|
|
5333
|
+
function describeWindow(label, window, effective, nowMs) {
|
|
5334
|
+
if (effective.reset) {
|
|
5335
|
+
return `${label} window has reset`;
|
|
5336
|
+
}
|
|
5337
|
+
const used = effective.utilization === void 0 ? "usage unknown" : `${percent(effective.utilization)} used`;
|
|
5338
|
+
const resets = effective.resetsAtMs === void 0 ? "" : `, resets in ${formatAge(effective.resetsAtMs - nowMs)}`;
|
|
5339
|
+
return `${label} ${used}${resets}${window.status === "allowed_warning" ? " (nearly used)" : ""}`;
|
|
5340
|
+
}
|
|
5341
|
+
function capacityOf(plan) {
|
|
5342
|
+
return plan.kind === "subscription" ? plan.capacity : 1;
|
|
5343
|
+
}
|
|
5344
|
+
function assess(member, nowMs) {
|
|
5345
|
+
const plan = planOf(member.account);
|
|
5346
|
+
const planReasons = plan.kind === "pay-per-use" ? ["billed by use (no plan allowance)"] : plan.recognised ? [`${String(plan.capacity)}x plan`] : [`plan tier ${plan.tier === void 0 ? "not known" : `"${plan.tier}" not recognised`}, counted as 1x`];
|
|
5347
|
+
const base = { identity: member.identity, plan };
|
|
5348
|
+
if (member.readError !== void 0) {
|
|
5349
|
+
return { candidate: { ...base, class: "unknown", reasons: [`usage state unreadable: ${member.readError}`, ...planReasons] } };
|
|
5350
|
+
}
|
|
5351
|
+
const state = member.snapshot?.providers[ANTHROPIC_PROVIDER];
|
|
5352
|
+
const unified = unifiedOf(state);
|
|
5353
|
+
if (member.snapshot === void 0 || state === void 0 || unified === void 0) {
|
|
5354
|
+
return { candidate: { ...base, class: "unknown", reasons: ["no usage recorded yet, so its quota is unknown (turn on trackUsage for it)", ...planReasons] } };
|
|
5355
|
+
}
|
|
5356
|
+
const observedAgo = state.rateLimit === void 0 ? "" : ` (last seen ${formatAge(nowMs - Date.parse(state.rateLimit.observedAt))} ago)`;
|
|
5357
|
+
const fiveHourWindow = unified.fiveHour;
|
|
5358
|
+
const sevenDayWindow = unified.sevenDay;
|
|
5359
|
+
const five = fiveHourWindow === void 0 ? void 0 : effectiveWindow(fiveHourWindow, nowMs);
|
|
5360
|
+
const seven = sevenDayWindow === void 0 ? void 0 : effectiveWindow(sevenDayWindow, nowMs);
|
|
5361
|
+
const windowReasons = [
|
|
5362
|
+
...fiveHourWindow === void 0 || five === void 0 ? [] : [describeWindow("5h", fiveHourWindow, five, nowMs)],
|
|
5363
|
+
...sevenDayWindow === void 0 || seven === void 0 ? [] : [describeWindow("7d", sevenDayWindow, seven, nowMs)]
|
|
5364
|
+
];
|
|
5365
|
+
const blockedUntil = Math.max(
|
|
5366
|
+
windowBlockedUntil([five, seven].flatMap((window) => window === void 0 ? [] : [window])) ?? 0,
|
|
5367
|
+
limitBlockedUntil(state, nowMs) ?? 0
|
|
5368
|
+
);
|
|
5369
|
+
if (blockedUntil > nowMs) {
|
|
5370
|
+
if (unified.overageStatus === "allowed") {
|
|
5371
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [`plan exhausted until ${new Date(blockedUntil).toISOString()}, continues as extra usage`, ...windowReasons, ...planReasons] } };
|
|
5372
|
+
}
|
|
5373
|
+
return { candidate: { ...base, class: "ineligible", blockedUntilMs: blockedUntil, reasons: [`refused until ${new Date(blockedUntil).toISOString()} (in ${formatAge(blockedUntil - nowMs)})`, ...windowReasons, ...planReasons] } };
|
|
5374
|
+
}
|
|
5375
|
+
if (plan.kind === "pay-per-use" || fiveHourWindow === void 0 && sevenDayWindow === void 0) {
|
|
5376
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [...plan.kind === "pay-per-use" ? planReasons : ["no plan windows reported, so usage bills as extra usage"]] } };
|
|
5377
|
+
}
|
|
5378
|
+
const basis = seven?.utilization !== void 0 ? { window: seven, windowMs: SEVEN_DAY_WINDOW_MS } : five?.utilization !== void 0 ? { window: five, windowMs: FIVE_HOUR_WINDOW_MS } : void 0;
|
|
5379
|
+
if (basis?.window.utilization === void 0) {
|
|
5380
|
+
return { candidate: { ...base, class: "unknown", reasons: ["quota windows reported no utilisation", ...windowReasons, ...planReasons] } };
|
|
5381
|
+
}
|
|
5382
|
+
const remaining = Math.max(0, 1 - basis.window.utilization);
|
|
5383
|
+
const untilResetMs = basis.window.resetsAtMs === void 0 ? basis.windowMs : Math.max(basis.window.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5384
|
+
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
5385
|
+
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
5386
|
+
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5387
|
+
const burn = fiveHourBurn(member.records);
|
|
5388
|
+
return {
|
|
5389
|
+
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
5390
|
+
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
5391
|
+
...burn === void 0 ? {} : { burn }
|
|
5392
|
+
};
|
|
5393
|
+
}
|
|
5394
|
+
function rankPool(input) {
|
|
5395
|
+
const { members, nowMs } = input;
|
|
5396
|
+
const assessments = members.map((member) => assess(member, nowMs));
|
|
5397
|
+
const demands = assessments.flatMap(({ candidate, burn }) => burn === void 0 ? [] : [burn * capacityOf(candidate.plan)]);
|
|
5398
|
+
const sharedDemand = demands.length === 0 ? void 0 : Math.max(...demands);
|
|
5399
|
+
const candidates = assessments.map((assessment) => {
|
|
5400
|
+
const { candidate, fiveHour, burn } = assessment;
|
|
5401
|
+
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
5402
|
+
if (candidate.class !== "scored" || fiveHour === void 0 || pace === void 0) {
|
|
5403
|
+
return { ...candidate, feasible: true };
|
|
5404
|
+
}
|
|
5405
|
+
const runsDryInMs = fiveHour.remaining / pace;
|
|
5406
|
+
if (runsDryInMs >= fiveHour.untilResetMs) {
|
|
5407
|
+
return { ...candidate, feasible: true };
|
|
5408
|
+
}
|
|
5409
|
+
return { ...candidate, feasible: false, reasons: [`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`, ...candidate.reasons] };
|
|
5410
|
+
});
|
|
5411
|
+
const ordered = [...candidates].sort(
|
|
5412
|
+
(left, right) => CLASS_ORDER[left.class] - CLASS_ORDER[right.class] || Number(right.feasible) - Number(left.feasible) || (right.score ?? 0) - (left.score ?? 0) || left.identity.localeCompare(right.identity)
|
|
5413
|
+
);
|
|
5414
|
+
const sticky = input.sticky;
|
|
5415
|
+
const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
|
|
5416
|
+
const stickyCandidate = ordered[stickyIndex];
|
|
5417
|
+
if (sticky !== void 0 && stickyCandidate?.class === "scored" && stickyCandidate.feasible) {
|
|
5418
|
+
const ageMs = nowMs - Date.parse(sticky.at);
|
|
5419
|
+
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
5420
|
+
if (input.resuming || warm) {
|
|
5421
|
+
const why = warm ? `used here ${formatAge(ageMs)} ago, prompt cache still warm` : "resuming a conversation started on this account";
|
|
5422
|
+
ordered.splice(stickyIndex, 1);
|
|
5423
|
+
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
const pick = ordered.find((candidate) => candidate.class !== "ineligible");
|
|
5427
|
+
const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
|
|
5428
|
+
const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
|
|
5429
|
+
return {
|
|
5430
|
+
candidates: ordered,
|
|
5431
|
+
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick }
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5435
|
+
// src/usage/store.ts
|
|
5436
|
+
var DAY_MS2 = 864e5;
|
|
5437
|
+
var DAYS_IN_WEEK = 7;
|
|
5438
|
+
var USAGE_RETENTION_MS = DAYS_IN_WEEK * DAY_MS2;
|
|
5439
|
+
|
|
5440
|
+
// src/usage/poolPick.ts
|
|
5441
|
+
var SCHEMA_VERSION = 1;
|
|
5442
|
+
var PickEntrySchema = import_zod9.z.strictObject({ identity: import_zod9.z.string().min(1), at: import_zod9.z.iso.datetime() });
|
|
5443
|
+
var PicksFileSchema = import_zod9.z.strictObject({
|
|
5444
|
+
schemaVersion: import_zod9.z.literal(SCHEMA_VERSION),
|
|
5445
|
+
picks: import_zod9.z.record(import_zod9.z.string().min(1), PickEntrySchema)
|
|
5446
|
+
});
|
|
5447
|
+
function errorMessage(error) {
|
|
5448
|
+
return error instanceof Error ? error.message : String(error);
|
|
5449
|
+
}
|
|
5450
|
+
function loadPoolMembers(fs10, paths, identities, nowMs) {
|
|
5451
|
+
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5452
|
+
const log = readUsageLog(fs10, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5453
|
+
return identities.map((identity) => {
|
|
5454
|
+
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5455
|
+
try {
|
|
5456
|
+
const snapshot = readUsageSnapshot(fs10, paths.usageSnapshotsDir, identity);
|
|
5457
|
+
const account = readAccountMetadata(fs10, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5458
|
+
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5459
|
+
} catch (error) {
|
|
5460
|
+
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
5461
|
+
return { identity, records, readError: errorMessage(error) };
|
|
5462
|
+
}
|
|
5463
|
+
throw error;
|
|
5464
|
+
}
|
|
5465
|
+
});
|
|
5466
|
+
}
|
|
5467
|
+
function rankPoolFromStore(params) {
|
|
5468
|
+
return rankPool({
|
|
5469
|
+
members: loadPoolMembers(params.fs, params.paths, params.identities, params.nowMs),
|
|
5470
|
+
nowMs: params.nowMs,
|
|
5471
|
+
resuming: params.resuming,
|
|
5472
|
+
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5473
|
+
});
|
|
5474
|
+
}
|
|
5475
|
+
function readPicksFile(fs10, file) {
|
|
5476
|
+
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5477
|
+
const raw = fs10.readFileUtf8(file);
|
|
5478
|
+
if (raw === void 0) {
|
|
5479
|
+
return { file: empty };
|
|
5480
|
+
}
|
|
5481
|
+
let parsed;
|
|
5482
|
+
try {
|
|
5483
|
+
parsed = JSON.parse(raw);
|
|
5484
|
+
} catch {
|
|
5485
|
+
return { file: empty, problem: `${file} is not valid JSON` };
|
|
5486
|
+
}
|
|
5487
|
+
const result = PicksFileSchema.safeParse(parsed);
|
|
5488
|
+
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5489
|
+
}
|
|
5490
|
+
function readStickyPick(fs10, picksFile, directory) {
|
|
5491
|
+
const read = readPicksFile(fs10, picksFile);
|
|
5492
|
+
const entry = read.file.picks[directory];
|
|
5493
|
+
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5494
|
+
}
|
|
5495
|
+
|
|
5496
|
+
// src/launcher/pool.ts
|
|
5497
|
+
function splitMembers(pool, paths, fs10) {
|
|
5498
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs10) !== void 0;
|
|
5499
|
+
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5500
|
+
}
|
|
5501
|
+
|
|
5502
|
+
// src/poolPickReport.ts
|
|
5503
|
+
function candidateView(candidate) {
|
|
5504
|
+
return {
|
|
5505
|
+
identity: candidate.identity,
|
|
5506
|
+
class: candidate.class,
|
|
5507
|
+
...candidate.score === void 0 ? {} : { score: candidate.score },
|
|
5508
|
+
feasible: candidate.feasible,
|
|
5509
|
+
...candidate.blockedUntilMs === void 0 ? {} : { blockedUntil: new Date(candidate.blockedUntilMs).toISOString() },
|
|
5510
|
+
plan: candidate.plan,
|
|
5511
|
+
reasons: candidate.reasons
|
|
5512
|
+
};
|
|
5513
|
+
}
|
|
5514
|
+
function collectPoolPick(params) {
|
|
5515
|
+
const { present, missing } = splitMembers(params.pool, params.paths, params.fs);
|
|
5516
|
+
const sticky = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.directory);
|
|
5517
|
+
const ranking = rankPoolFromStore({
|
|
5518
|
+
fs: params.usageFs,
|
|
5519
|
+
paths: params.paths,
|
|
5520
|
+
identities: present,
|
|
5521
|
+
nowMs: params.nowMs,
|
|
5522
|
+
resuming: false,
|
|
5523
|
+
...sticky.sticky === void 0 ? {} : { sticky: sticky.sticky }
|
|
5524
|
+
});
|
|
5525
|
+
return {
|
|
5526
|
+
pool: params.poolName,
|
|
5527
|
+
directory: params.directory,
|
|
5528
|
+
...ranking.pick === void 0 ? {} : { pick: ranking.pick.identity },
|
|
5529
|
+
candidates: ranking.candidates.map(candidateView),
|
|
5530
|
+
missing,
|
|
5531
|
+
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
5532
|
+
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
5533
|
+
};
|
|
5534
|
+
}
|
|
5535
|
+
|
|
5536
|
+
// src/realPorts.ts
|
|
5537
|
+
var import_node_child_process = require("node:child_process");
|
|
5538
|
+
var import_node_crypto8 = require("node:crypto");
|
|
5539
|
+
var import_node_fs6 = __toESM(require("node:fs"), 1);
|
|
5540
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
5541
|
+
var import_node_sea = require("node:sea");
|
|
5542
|
+
|
|
5543
|
+
// src/versionDiscovery.ts
|
|
5544
|
+
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
5545
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
5546
|
+
function defaultVersionsDir() {
|
|
5547
|
+
return import_node_path18.default.join(import_node_os2.default.homedir(), ".local", "share", "claude", "versions");
|
|
5548
|
+
}
|
|
5549
|
+
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
5550
|
+
function isNumericDottedVersion(name) {
|
|
5551
|
+
return NUMERIC_DOTTED_VERSION_RE.test(name);
|
|
5552
|
+
}
|
|
5553
|
+
function compareVersions2(a, b) {
|
|
5554
|
+
if (!isNumericDottedVersion(a)) {
|
|
5555
|
+
throw new Error(`compareVersions: "${a}" is not a valid plain dotted-numeric version`);
|
|
5556
|
+
}
|
|
5557
|
+
if (!isNumericDottedVersion(b)) {
|
|
5558
|
+
throw new Error(`compareVersions: "${b}" is not a valid plain dotted-numeric version`);
|
|
5559
|
+
}
|
|
5560
|
+
const segmentsA = a.split(".").map(Number);
|
|
5561
|
+
const segmentsB = b.split(".").map(Number);
|
|
5562
|
+
const length = Math.max(segmentsA.length, segmentsB.length);
|
|
5563
|
+
for (let i = 0; i < length; i += 1) {
|
|
5564
|
+
const segmentA = segmentsA[i] ?? 0;
|
|
5565
|
+
const segmentB = segmentsB[i] ?? 0;
|
|
5566
|
+
if (segmentA !== segmentB) {
|
|
5567
|
+
return segmentA - segmentB;
|
|
5568
|
+
}
|
|
5569
|
+
}
|
|
5570
|
+
return 0;
|
|
5571
|
+
}
|
|
5572
|
+
function pickHighestVersion(entries) {
|
|
5573
|
+
const candidates = entries.filter(
|
|
5574
|
+
(entry) => entry.isFile && entry.isExecutable && entry.sizeBytes > 0 && isNumericDottedVersion(entry.name)
|
|
5575
|
+
);
|
|
5576
|
+
if (candidates.length === 0) {
|
|
5577
|
+
return void 0;
|
|
5578
|
+
}
|
|
5579
|
+
return candidates.reduce(
|
|
5580
|
+
(highest, candidate) => compareVersions2(candidate.name, highest.name) > 0 ? candidate : highest
|
|
5581
|
+
).name;
|
|
5582
|
+
}
|
|
5583
|
+
function discoverClaudeBinary(options) {
|
|
5584
|
+
const versionsDir = options.versionsDir ?? defaultVersionsDir();
|
|
5585
|
+
const entries = options.listVersionsDir(versionsDir);
|
|
5586
|
+
const highestVersion = pickHighestVersion(entries);
|
|
5587
|
+
if (highestVersion !== void 0) {
|
|
5588
|
+
return {
|
|
5589
|
+
path: import_node_path18.default.join(versionsDir, highestVersion),
|
|
5590
|
+
source: "versions-dir",
|
|
5591
|
+
version: highestVersion
|
|
5592
|
+
};
|
|
5593
|
+
}
|
|
5594
|
+
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => import_node_path18.default.resolve(dir)));
|
|
5595
|
+
for (const dir of options.pathDirs) {
|
|
5596
|
+
if (ownInstallDirs.has(import_node_path18.default.resolve(dir))) {
|
|
5597
|
+
continue;
|
|
5598
|
+
}
|
|
5599
|
+
const found = options.findExecutableInDir(dir, "claude");
|
|
5600
|
+
if (found !== void 0) {
|
|
5601
|
+
return { path: found, source: "path-fallback" };
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
throw new Error(
|
|
5605
|
+
`Could not find a claude binary. Looked for genuinely-executable versioned binaries under "${versionsDir}" (missing, empty, or containing nothing executable) and for a "claude" executable on PATH (excluding this tool's own install directory). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
|
|
5606
|
+
);
|
|
5607
|
+
}
|
|
5608
|
+
|
|
5609
|
+
// src/realPorts.ts
|
|
5610
|
+
function isEnoent3(error) {
|
|
5611
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
5612
|
+
}
|
|
5613
|
+
var realFsPort = {
|
|
5614
|
+
readFileUtf8(filePath) {
|
|
5615
|
+
try {
|
|
5616
|
+
return import_node_fs6.default.readFileSync(filePath, "utf8");
|
|
5617
|
+
} catch (error) {
|
|
5618
|
+
if (isEnoent3(error)) {
|
|
5619
|
+
return void 0;
|
|
5620
|
+
}
|
|
5621
|
+
throw error;
|
|
5622
|
+
}
|
|
5623
|
+
},
|
|
5624
|
+
readConfigFile: cosmiconfigReader(),
|
|
5625
|
+
readdir(dirPath) {
|
|
5626
|
+
try {
|
|
5627
|
+
return import_node_fs6.default.readdirSync(dirPath);
|
|
5628
|
+
} catch (error) {
|
|
5629
|
+
if (isEnoent3(error)) {
|
|
5630
|
+
return [];
|
|
5631
|
+
}
|
|
5632
|
+
throw error;
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
};
|
|
5636
|
+
function isErrorWithCode(error, code) {
|
|
5637
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
5638
|
+
}
|
|
5639
|
+
var OWNER_ONLY_DIR_MODE = 448;
|
|
5640
|
+
var OWNER_ONLY_FILE_MODE = 384;
|
|
5641
|
+
var PERMISSION_BITS = 511;
|
|
5642
|
+
var realFarmFs = {
|
|
5643
|
+
lstat(filePath) {
|
|
5644
|
+
let stat;
|
|
5645
|
+
try {
|
|
5646
|
+
stat = import_node_fs6.default.lstatSync(filePath);
|
|
5647
|
+
} catch (error) {
|
|
5648
|
+
if (isEnoent3(error)) {
|
|
5649
|
+
return void 0;
|
|
5650
|
+
}
|
|
5651
|
+
throw error;
|
|
5652
|
+
}
|
|
5653
|
+
const kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "dir" : "file";
|
|
5654
|
+
return { kind, mtimeMs: stat.mtimeMs, sizeBytes: stat.size };
|
|
5655
|
+
},
|
|
5656
|
+
readdir(dirPath) {
|
|
5657
|
+
try {
|
|
5658
|
+
return import_node_fs6.default.readdirSync(dirPath);
|
|
5659
|
+
} catch (error) {
|
|
5660
|
+
if (isEnoent3(error) || isErrorWithCode(error, "ENOTDIR")) {
|
|
5661
|
+
return [];
|
|
5662
|
+
}
|
|
5663
|
+
throw error;
|
|
5664
|
+
}
|
|
5665
|
+
},
|
|
5666
|
+
mkdirp(dirPath) {
|
|
5667
|
+
import_node_fs6.default.mkdirSync(dirPath, { recursive: true });
|
|
5668
|
+
},
|
|
5669
|
+
symlink(target, linkPath) {
|
|
5670
|
+
import_node_fs6.default.symlinkSync(target, linkPath);
|
|
5671
|
+
},
|
|
5672
|
+
rename(from, to) {
|
|
5673
|
+
import_node_fs6.default.renameSync(from, to);
|
|
5674
|
+
},
|
|
5675
|
+
removeRecursive(targetPath) {
|
|
5676
|
+
import_node_fs6.default.rmSync(targetPath, { recursive: true, force: true });
|
|
5677
|
+
},
|
|
5678
|
+
copyRecursive(from, to) {
|
|
5679
|
+
import_node_fs6.default.cpSync(from, to, { recursive: true, force: true, verbatimSymlinks: true });
|
|
5680
|
+
},
|
|
5681
|
+
readFileUtf8(filePath) {
|
|
5682
|
+
try {
|
|
5683
|
+
return import_node_fs6.default.readFileSync(filePath, "utf8");
|
|
5684
|
+
} catch (error) {
|
|
5685
|
+
if (isEnoent3(error)) {
|
|
5686
|
+
return void 0;
|
|
5687
|
+
}
|
|
5688
|
+
throw error;
|
|
5689
|
+
}
|
|
5690
|
+
},
|
|
5691
|
+
writeFileUtf8(filePath, contents) {
|
|
5692
|
+
import_node_fs6.default.writeFileSync(filePath, contents, "utf8");
|
|
5693
|
+
},
|
|
5694
|
+
mkdirPrivate(dirPath) {
|
|
5695
|
+
import_node_fs6.default.mkdirSync(dirPath, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
|
|
5696
|
+
import_node_fs6.default.chmodSync(dirPath, OWNER_ONLY_DIR_MODE);
|
|
5697
|
+
},
|
|
5698
|
+
writeFilePrivate(filePath, contents) {
|
|
5699
|
+
const temp = `${filePath}.${String(process.pid)}.tmp`;
|
|
5700
|
+
import_node_fs6.default.writeFileSync(temp, contents, { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
|
|
5701
|
+
import_node_fs6.default.chmodSync(temp, OWNER_ONLY_FILE_MODE);
|
|
5702
|
+
import_node_fs6.default.renameSync(temp, filePath);
|
|
5703
|
+
},
|
|
5704
|
+
appendFilePrivate(filePath, contents) {
|
|
5705
|
+
const descriptor = import_node_fs6.default.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
|
|
5706
|
+
try {
|
|
5707
|
+
if ((import_node_fs6.default.fstatSync(descriptor).mode & PERMISSION_BITS) !== OWNER_ONLY_FILE_MODE) {
|
|
5708
|
+
import_node_fs6.default.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
|
|
5709
|
+
}
|
|
5710
|
+
const bytes = Buffer.from(contents, "utf8");
|
|
5711
|
+
let written = 0;
|
|
5712
|
+
while (written < bytes.length) {
|
|
5713
|
+
written += import_node_fs6.default.writeSync(descriptor, bytes, written, bytes.length - written);
|
|
5714
|
+
}
|
|
5715
|
+
} finally {
|
|
5716
|
+
import_node_fs6.default.closeSync(descriptor);
|
|
5717
|
+
}
|
|
5718
|
+
},
|
|
5719
|
+
writeFileExclusive(filePath, contents) {
|
|
5720
|
+
try {
|
|
5721
|
+
import_node_fs6.default.writeFileSync(filePath, contents, { encoding: "utf8", flag: "wx" });
|
|
5722
|
+
return true;
|
|
5723
|
+
} catch (error) {
|
|
5724
|
+
if (isErrorWithCode(error, "EEXIST")) {
|
|
5725
|
+
return false;
|
|
5726
|
+
}
|
|
5727
|
+
throw error;
|
|
5728
|
+
}
|
|
5729
|
+
},
|
|
5730
|
+
hashFile(filePath) {
|
|
5731
|
+
try {
|
|
5732
|
+
return (0, import_node_crypto8.createHash)("sha256").update(import_node_fs6.default.readFileSync(filePath)).digest("hex");
|
|
5733
|
+
} catch (error) {
|
|
5734
|
+
if (isEnoent3(error) || isErrorWithCode(error, "EISDIR")) {
|
|
5735
|
+
return void 0;
|
|
5736
|
+
}
|
|
5737
|
+
throw error;
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
};
|
|
5741
|
+
function realIsProcessAlive(pid) {
|
|
5742
|
+
try {
|
|
5743
|
+
process.kill(pid, 0);
|
|
5744
|
+
return true;
|
|
5745
|
+
} catch (error) {
|
|
5746
|
+
return isErrorWithCode(error, "EPERM");
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
function realIsProcessZombie(pid) {
|
|
5750
|
+
const result = (0, import_node_child_process.spawnSync)("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
|
|
5751
|
+
return result.status === 0 && result.stdout.trim().startsWith("Z");
|
|
5752
|
+
}
|
|
5753
|
+
function realIsProcessRunning(pid) {
|
|
5754
|
+
return realIsProcessAlive(pid) && !realIsProcessZombie(pid);
|
|
5755
|
+
}
|
|
5756
|
+
var realRunPort = {
|
|
5757
|
+
run(command, args) {
|
|
5758
|
+
const result = (0, import_node_child_process.spawnSync)(command, [...args], { encoding: "utf8" });
|
|
5759
|
+
return { status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
5760
|
+
}
|
|
5761
|
+
};
|
|
5762
|
+
function resolveGitBranch(run, cwd) {
|
|
5763
|
+
const result = run.run("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
5764
|
+
if (result.status !== 0) {
|
|
5765
|
+
return {};
|
|
5766
|
+
}
|
|
5767
|
+
const name = result.stdout.trim();
|
|
5768
|
+
if (name === "") {
|
|
5769
|
+
return {};
|
|
5770
|
+
}
|
|
5771
|
+
if (name === "HEAD") {
|
|
5772
|
+
return { branchDetached: true };
|
|
5773
|
+
}
|
|
5774
|
+
return { branch: name, branchDetached: false };
|
|
5775
|
+
}
|
|
5776
|
+
var realProcPort = {
|
|
5777
|
+
env: process.env,
|
|
5778
|
+
argv: process.argv.slice(2),
|
|
5779
|
+
exit: (code) => process.exit(code)
|
|
5780
|
+
};
|
|
5781
|
+
var EXECUTE_BITS_MASK = 73;
|
|
5782
|
+
function listVersionsDir(dir) {
|
|
5783
|
+
let entries;
|
|
5784
|
+
try {
|
|
5785
|
+
entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
|
|
5786
|
+
} catch (error) {
|
|
5787
|
+
if (isEnoent3(error)) {
|
|
5788
|
+
return [];
|
|
5789
|
+
}
|
|
5790
|
+
throw error;
|
|
5791
|
+
}
|
|
5792
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
5793
|
+
const fullPath = import_node_path19.default.join(dir, entry.name);
|
|
5794
|
+
const stat = import_node_fs6.default.statSync(fullPath);
|
|
5795
|
+
return {
|
|
5796
|
+
name: entry.name,
|
|
5797
|
+
isFile: true,
|
|
5798
|
+
isExecutable: (stat.mode & EXECUTE_BITS_MASK) !== 0,
|
|
5799
|
+
sizeBytes: stat.size
|
|
5800
|
+
};
|
|
5801
|
+
});
|
|
5802
|
+
}
|
|
5803
|
+
var WINDOWS_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PS1";
|
|
5804
|
+
function windowsPathExtensions(pathext) {
|
|
5805
|
+
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
5806
|
+
}
|
|
5807
|
+
function resolveExecutableCandidate(dir, name, env) {
|
|
5808
|
+
const candidate = import_node_path19.default.join(dir, name);
|
|
5809
|
+
if (env.platform === "win32") {
|
|
5810
|
+
const extensions = windowsPathExtensions(env.pathext);
|
|
5811
|
+
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
5812
|
+
if (alreadyHasRecognisedExtension) {
|
|
5813
|
+
return env.statFileMode(candidate) !== void 0 ? candidate : void 0;
|
|
5814
|
+
}
|
|
5815
|
+
for (const ext of extensions) {
|
|
5816
|
+
const withExt = candidate + ext;
|
|
5817
|
+
if (env.statFileMode(withExt) !== void 0) {
|
|
5818
|
+
return withExt;
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5821
|
+
return void 0;
|
|
5822
|
+
}
|
|
5823
|
+
const mode = env.statFileMode(candidate);
|
|
5824
|
+
return mode !== void 0 && (mode & EXECUTE_BITS_MASK) !== 0 ? candidate : void 0;
|
|
5825
|
+
}
|
|
5826
|
+
function realStatFileMode(candidate) {
|
|
5827
|
+
try {
|
|
5828
|
+
const stat = import_node_fs6.default.statSync(candidate);
|
|
5829
|
+
return stat.isFile() ? stat.mode : void 0;
|
|
5830
|
+
} catch (error) {
|
|
5831
|
+
if (isEnoent3(error)) {
|
|
5832
|
+
return void 0;
|
|
5833
|
+
}
|
|
5834
|
+
throw error;
|
|
5835
|
+
}
|
|
5836
|
+
}
|
|
5837
|
+
function findExecutableInDir(dir, name) {
|
|
5838
|
+
return resolveExecutableCandidate(dir, name, {
|
|
5839
|
+
platform: process.platform,
|
|
5840
|
+
pathext: process.env.PATHEXT,
|
|
5841
|
+
statFileMode: realStatFileMode
|
|
5842
|
+
});
|
|
5843
|
+
}
|
|
5844
|
+
function searchPathForExecutable(pathDirs, name, findExecutable) {
|
|
5845
|
+
for (const dir of pathDirs) {
|
|
5846
|
+
const found = findExecutable(dir, name);
|
|
5847
|
+
if (found !== void 0) {
|
|
5848
|
+
return found;
|
|
5849
|
+
}
|
|
5850
|
+
}
|
|
5851
|
+
return void 0;
|
|
5852
|
+
}
|
|
5853
|
+
function resolveOwnExecutablePath(env) {
|
|
5854
|
+
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
5855
|
+
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
5856
|
+
}
|
|
5857
|
+
const candidate = env.argv1 === void 0 ? env.execPath : import_node_path19.default.resolve(env.cwd, env.argv1);
|
|
5858
|
+
const candidateDir = import_node_path19.default.dirname(candidate);
|
|
5859
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => import_node_path19.default.resolve(dir) === candidateDir);
|
|
5860
|
+
if (candidateDirOnPath) {
|
|
5861
|
+
return candidate;
|
|
5862
|
+
}
|
|
5863
|
+
const viaPath = searchPathForExecutable(env.pathDirs, import_node_path19.default.basename(candidate), env.findExecutableInDir);
|
|
5864
|
+
return viaPath ?? candidate;
|
|
5865
|
+
}
|
|
5866
|
+
function realOwnExecutablePath() {
|
|
5867
|
+
return resolveOwnExecutablePath({
|
|
5868
|
+
argv1: process.argv[1],
|
|
5869
|
+
execPath: process.execPath,
|
|
5870
|
+
cwd: process.cwd(),
|
|
5871
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path19.default.delimiter).filter((dir) => dir !== ""),
|
|
5872
|
+
findExecutableInDir
|
|
5873
|
+
});
|
|
5874
|
+
}
|
|
5875
|
+
function realResolveClaudeBinary(ownInstallDirs) {
|
|
5876
|
+
return () => discoverClaudeBinary({
|
|
5877
|
+
listVersionsDir,
|
|
5878
|
+
pathDirs: (process.env.PATH ?? "").split(import_node_path19.default.delimiter).filter((dir) => dir !== ""),
|
|
5879
|
+
findExecutableInDir,
|
|
5880
|
+
ownInstallDirs: [...ownInstallDirs]
|
|
5881
|
+
});
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
// src/checkReport.ts
|
|
5885
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
5886
|
+
var import_zod10 = require("zod");
|
|
5887
|
+
|
|
5888
|
+
// src/cli/bool.ts
|
|
5889
|
+
function parseBoolWord(input) {
|
|
5890
|
+
switch (input) {
|
|
5891
|
+
case "true":
|
|
5892
|
+
case "1":
|
|
5893
|
+
return true;
|
|
5894
|
+
case "false":
|
|
5895
|
+
case "0":
|
|
5896
|
+
return false;
|
|
5897
|
+
default:
|
|
5898
|
+
return void 0;
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5901
|
+
|
|
5902
|
+
// src/cli/envBool.ts
|
|
5903
|
+
var InvalidEnvBoolError = class extends UsageError {
|
|
5904
|
+
constructor(variable, value) {
|
|
5905
|
+
super(`${variable} must be one of true, false, 1 or 0, got "${value}".`);
|
|
5906
|
+
this.variable = variable;
|
|
5907
|
+
this.value = value;
|
|
5908
|
+
this.name = "InvalidEnvBoolError";
|
|
5909
|
+
}
|
|
5910
|
+
variable;
|
|
5911
|
+
value;
|
|
5912
|
+
};
|
|
5913
|
+
function parseEnvBool(variable, value) {
|
|
5914
|
+
if (value === void 0 || value === "") {
|
|
5915
|
+
return void 0;
|
|
5916
|
+
}
|
|
5917
|
+
const parsed = parseBoolWord(value);
|
|
5918
|
+
if (parsed === void 0) {
|
|
5919
|
+
throw new InvalidEnvBoolError(variable, value);
|
|
5920
|
+
}
|
|
5921
|
+
return parsed;
|
|
5922
|
+
}
|
|
5923
|
+
|
|
5924
|
+
// src/checkReport.ts
|
|
5925
|
+
var HISTORY_PROJECTS_KEY_PREFIX = "history/projects/";
|
|
5926
|
+
function projectFragmentOf(rawKey) {
|
|
5927
|
+
return rawKey.startsWith(HISTORY_PROJECTS_KEY_PREFIX) ? rawKey.slice(HISTORY_PROJECTS_KEY_PREFIX.length) : void 0;
|
|
5928
|
+
}
|
|
5929
|
+
function existingProjectNames(facts) {
|
|
5930
|
+
const names = /* @__PURE__ */ new Set();
|
|
5931
|
+
const prefix = "projects/";
|
|
5932
|
+
for (const rel of facts.entries.keys()) {
|
|
5933
|
+
if (!rel.startsWith(prefix)) {
|
|
5934
|
+
continue;
|
|
5935
|
+
}
|
|
5936
|
+
const rest = rel.slice(prefix.length);
|
|
5937
|
+
const head = rest.split("/")[0];
|
|
5938
|
+
if (head !== void 0 && head !== "") {
|
|
5939
|
+
names.add(head);
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
return [...names];
|
|
5943
|
+
}
|
|
5944
|
+
function flagAmbiguousEncodings(flattened, facts) {
|
|
5945
|
+
const fragments = [];
|
|
5946
|
+
for (const rule of flattened.rules.values()) {
|
|
5947
|
+
const fragment = projectFragmentOf(rule.rawKey);
|
|
5948
|
+
if (fragment !== void 0) {
|
|
5949
|
+
fragments.push(fragment);
|
|
5950
|
+
}
|
|
5951
|
+
}
|
|
5952
|
+
if (fragments.length === 0) {
|
|
5953
|
+
return [];
|
|
5954
|
+
}
|
|
5955
|
+
return detectEncodingAmbiguity(fragments, { home: facts.home, existingNames: existingProjectNames(facts) });
|
|
5956
|
+
}
|
|
5957
|
+
function formatDecision(decision) {
|
|
5958
|
+
const status = decision.shared ? "shared" : "hidden";
|
|
5959
|
+
let reason;
|
|
5960
|
+
switch (decision.via) {
|
|
5961
|
+
case "secret-floor":
|
|
5962
|
+
reason = "secret (never shared, cannot be overridden by any layer)";
|
|
5963
|
+
break;
|
|
5964
|
+
case "unclassified":
|
|
5965
|
+
reason = "unclassified entry (no category recognises it)";
|
|
5966
|
+
break;
|
|
5967
|
+
case "entry-rule":
|
|
5968
|
+
reason = decision.rule === void 0 ? "an entries rule" : `entries rule "${decision.rule.rawKey}" from layer ${String(decision.rule.layer)}`;
|
|
5969
|
+
break;
|
|
5970
|
+
case "category-override":
|
|
5971
|
+
reason = `category "${decision.category ?? "?"}" overridden by a layer`;
|
|
5972
|
+
break;
|
|
5973
|
+
case "category-default":
|
|
5974
|
+
reason = `category "${decision.category ?? "?"}" shipped default`;
|
|
5975
|
+
break;
|
|
5976
|
+
}
|
|
5977
|
+
const eliminatedNote = decision.eliminated !== void 0 && decision.eliminated.length > 0 ? ` [${String(decision.eliminated.length)} more specific rule(s) eliminated by a failing when-condition]` : "";
|
|
5978
|
+
return `${decision.relPath}: ${status} \u2014 ${reason}${eliminatedNote}`;
|
|
5979
|
+
}
|
|
5980
|
+
function parseKeychainServiceName(stderr) {
|
|
5981
|
+
const match = /"svce"<blob>="([^"]*)"/.exec(stderr);
|
|
5982
|
+
return match?.[1];
|
|
5983
|
+
}
|
|
5984
|
+
function lookupKeychainService(run, farmRoot) {
|
|
5985
|
+
const result = run.run("security", ["find-generic-password", "-a", farmRoot, "-g"]);
|
|
5986
|
+
if (result.status !== 0) {
|
|
5987
|
+
return {
|
|
5988
|
+
checked: true,
|
|
5989
|
+
found: false,
|
|
5990
|
+
note: `No Keychain entry found for account "${farmRoot}" (security exited ${result.status === null ? "with no status" : String(result.status)}).`
|
|
5991
|
+
};
|
|
5992
|
+
}
|
|
5993
|
+
const serviceName = parseKeychainServiceName(result.stderr);
|
|
5994
|
+
return {
|
|
5995
|
+
checked: true,
|
|
5996
|
+
found: true,
|
|
5997
|
+
...serviceName === void 0 ? {} : { serviceName },
|
|
5998
|
+
note: serviceName === void 0 ? `A Keychain entry was found for account "${farmRoot}" but its service name could not be parsed from security's output.` : `Keychain service name for this identity: "${serviceName}".`
|
|
5999
|
+
};
|
|
6000
|
+
}
|
|
6001
|
+
var SettingsSecretsShapeSchema = import_zod10.z.looseObject({
|
|
6002
|
+
env: import_zod10.z.record(import_zod10.z.string(), import_zod10.z.unknown()).optional(),
|
|
6003
|
+
hooks: import_zod10.z.record(import_zod10.z.string(), import_zod10.z.array(import_zod10.z.unknown())).optional()
|
|
6004
|
+
});
|
|
6005
|
+
function hookCommandCountOf(group) {
|
|
6006
|
+
if (typeof group !== "object" || group === null) {
|
|
6007
|
+
return 0;
|
|
6008
|
+
}
|
|
6009
|
+
if (!("hooks" in group)) {
|
|
6010
|
+
return 0;
|
|
6011
|
+
}
|
|
6012
|
+
return Array.isArray(group.hooks) ? group.hooks.length : 0;
|
|
6013
|
+
}
|
|
6014
|
+
function countHookCommands(hooks) {
|
|
6015
|
+
if (hooks === void 0) {
|
|
6016
|
+
return 0;
|
|
6017
|
+
}
|
|
6018
|
+
let total = 0;
|
|
6019
|
+
for (const groups of Object.values(hooks)) {
|
|
6020
|
+
for (const group of groups) {
|
|
6021
|
+
total += hookCommandCountOf(group);
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
6024
|
+
return total;
|
|
6025
|
+
}
|
|
6026
|
+
function inspectSettingsExposure(params) {
|
|
6027
|
+
if (!params.settingsShared) {
|
|
6028
|
+
return [];
|
|
6029
|
+
}
|
|
6030
|
+
const reports = [];
|
|
6031
|
+
for (const [file, raw] of Object.entries(params.files)) {
|
|
6032
|
+
if (raw === void 0) {
|
|
6033
|
+
continue;
|
|
6034
|
+
}
|
|
6035
|
+
let parsed;
|
|
6036
|
+
try {
|
|
6037
|
+
parsed = JSON.parse(raw);
|
|
6038
|
+
} catch {
|
|
6039
|
+
continue;
|
|
6040
|
+
}
|
|
6041
|
+
const result = SettingsSecretsShapeSchema.safeParse(parsed);
|
|
6042
|
+
if (!result.success) {
|
|
6043
|
+
continue;
|
|
6044
|
+
}
|
|
6045
|
+
const envKeyNames = Object.keys(result.data.env ?? {});
|
|
6046
|
+
const hookEventNames = Object.keys(result.data.hooks ?? {});
|
|
6047
|
+
const hookCommandCount = countHookCommands(result.data.hooks);
|
|
6048
|
+
if (envKeyNames.length === 0 && hookCommandCount === 0) {
|
|
6049
|
+
continue;
|
|
6050
|
+
}
|
|
6051
|
+
reports.push({ file, envKeyNames, hookEventNames, hookCommandCount });
|
|
6052
|
+
}
|
|
6053
|
+
return reports;
|
|
6054
|
+
}
|
|
6055
|
+
function buildCredentialReport(provider, identity) {
|
|
6056
|
+
const identityCredential = identity?.credential === void 0 ? void 0 : summariseCredential(identity.credential);
|
|
6057
|
+
return {
|
|
6058
|
+
applies: provider !== void 0 ? "provider" : identityCredential !== void 0 ? "identity" : "stored-login",
|
|
6059
|
+
...provider === void 0 ? {} : {
|
|
6060
|
+
provider: provider.problem === void 0 ? { name: provider.name, credential: summariseCredential(provider.definition.credential) } : { name: provider.name, problem: provider.problem }
|
|
6061
|
+
},
|
|
6062
|
+
...identityCredential === void 0 ? {} : { identity: identityCredential }
|
|
6063
|
+
};
|
|
6064
|
+
}
|
|
6065
|
+
function runCheck(params) {
|
|
6066
|
+
const facts = buildEntryFacts({
|
|
6067
|
+
fs: params.farmFs,
|
|
6068
|
+
claudeHome: params.claudeHome,
|
|
6069
|
+
home: params.home,
|
|
6070
|
+
cwd: params.cwd,
|
|
6071
|
+
nowMs: params.nowMs,
|
|
6072
|
+
env: params.env,
|
|
6073
|
+
...params.branch === void 0 ? {} : { branch: params.branch },
|
|
6074
|
+
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached }
|
|
6075
|
+
});
|
|
6076
|
+
const resolved = resolveDecisions({ facts, cascade: params.cascade, classification: params.classification });
|
|
6077
|
+
const decisionLines = [...resolved.decisions.values()].slice().sort((a, b) => a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0).map(formatDecision);
|
|
6078
|
+
const projectEncodingAmbiguities = flagAmbiguousEncodings(resolved.flattened, facts);
|
|
6079
|
+
const ambientCredential = evaluateAmbientCredentialGuard({
|
|
6080
|
+
env: params.env,
|
|
6081
|
+
allowAmbientCredential: params.identity?.allowAmbientCredential ?? false,
|
|
6082
|
+
allowAmbientCredentialOverride: parseEnvBool("AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL", params.env.AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL) === true,
|
|
6083
|
+
...params.identityName === void 0 ? {} : { identityName: params.identityName }
|
|
6084
|
+
});
|
|
6085
|
+
const settingsShared = resolved.flattened.categories.get("settings") ?? SHIPPED_CATEGORY_DEFAULTS.settings;
|
|
6086
|
+
const settingsExposure = inspectSettingsExposure({ settingsShared, files: params.settingsFiles });
|
|
6087
|
+
const keychain = params.platform === "darwin" && params.run !== void 0 && params.farmRoot !== void 0 ? lookupKeychainService(params.run, params.farmRoot) : void 0;
|
|
6088
|
+
return {
|
|
6089
|
+
...params.identityName === void 0 ? {} : { identityName: params.identityName },
|
|
6090
|
+
identitySource: params.identitySource,
|
|
6091
|
+
...params.poolPick === void 0 ? {} : { poolPick: params.poolPick },
|
|
6092
|
+
...params.configProfileName === void 0 ? {} : { configProfileName: params.configProfileName },
|
|
6093
|
+
configProfileSource: params.configProfileSource,
|
|
6094
|
+
resolved,
|
|
6095
|
+
decisionLines,
|
|
6096
|
+
projectEncodingAmbiguities,
|
|
6097
|
+
ambientCredential,
|
|
6098
|
+
...keychain === void 0 ? {} : { keychain },
|
|
6099
|
+
settingsExposure,
|
|
6100
|
+
credential: buildCredentialReport(params.provider, params.identity)
|
|
6101
|
+
};
|
|
6102
|
+
}
|
|
6103
|
+
function formatCheckReport(report) {
|
|
6104
|
+
const lines = [];
|
|
6105
|
+
lines.push(`Identity: ${report.identityName ?? "(none)"} (${report.identitySource})`);
|
|
6106
|
+
if (report.poolPick !== void 0) {
|
|
6107
|
+
const top = report.poolPick.candidates.find((candidate) => candidate.identity === report.poolPick?.pick);
|
|
6108
|
+
lines.push(`Pool: ${report.poolPick.pool}${top === void 0 ? ", every member is refused right now" : `, would pick ${top.identity} (${top.reasons.join("; ")})`}`);
|
|
6109
|
+
}
|
|
6110
|
+
lines.push(`Configuration profile: ${report.configProfileName ?? "(none)"} (${report.configProfileSource})`);
|
|
6111
|
+
lines.push("", "Layers (shallowest/earliest first):");
|
|
6112
|
+
for (const layer of report.resolved.assembled.layers) {
|
|
6113
|
+
lines.push(` [${String(layer.id)}] ${layer.kind}: ${layer.source}`);
|
|
6114
|
+
}
|
|
6115
|
+
lines.push("", "Resolved entries:");
|
|
6116
|
+
if (report.decisionLines.length === 0) {
|
|
6117
|
+
lines.push(" (nothing under ~/.claude to report)");
|
|
6118
|
+
}
|
|
6119
|
+
for (const line of report.decisionLines) {
|
|
6120
|
+
lines.push(` ${line}`);
|
|
6121
|
+
}
|
|
6122
|
+
if (report.projectEncodingAmbiguities.length > 0) {
|
|
6123
|
+
lines.push("", "Ambiguous history/projects/ encodings:");
|
|
6124
|
+
for (const ambiguity of report.projectEncodingAmbiguities) {
|
|
6125
|
+
lines.push(` "${ambiguity.fragment}" -> "${ambiguity.encoded}" (${ambiguity.reason}): ${ambiguity.detail}`);
|
|
6126
|
+
}
|
|
6127
|
+
}
|
|
6128
|
+
if (report.resolved.diagnostics.length > 0) {
|
|
6129
|
+
lines.push("", "Diagnostics:");
|
|
6130
|
+
for (const diagnostic of report.resolved.diagnostics) {
|
|
6131
|
+
lines.push(` [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
lines.push("", "Credential (source kinds and targets only, never values):");
|
|
6135
|
+
const { credential } = report;
|
|
6136
|
+
if (credential.provider !== void 0) {
|
|
6137
|
+
lines.push(
|
|
6138
|
+
` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem ?? ""}` : formatCredentialSummary(credential.provider.credential)}`
|
|
6139
|
+
);
|
|
6140
|
+
}
|
|
6141
|
+
if (credential.identity !== void 0) {
|
|
6142
|
+
lines.push(
|
|
6143
|
+
` Identity ${report.identityName ?? "(none)"}: ${formatCredentialSummary(credential.identity)}${credential.applies === "provider" ? " (not used: the provider's credential applies)" : ""}`
|
|
6144
|
+
);
|
|
6145
|
+
}
|
|
6146
|
+
if (credential.applies === "stored-login") {
|
|
6147
|
+
lines.push(" The identity's stored login (no credential block applies).");
|
|
6148
|
+
}
|
|
6149
|
+
lines.push("", "Ambient-credential exposure:");
|
|
6150
|
+
lines.push(
|
|
6151
|
+
report.ambientCredential.ok ? ` OK \u2014 none of ${AMBIENT_CREDENTIAL_VARS.join(", ")} is set to a non-empty value.` : report.ambientCredential.message.split("\n").map((line) => ` ${line}`).join("\n")
|
|
6152
|
+
);
|
|
6153
|
+
if (report.keychain !== void 0) {
|
|
6154
|
+
lines.push("", "macOS Keychain:");
|
|
6155
|
+
lines.push(` ${report.keychain.note}`);
|
|
6156
|
+
}
|
|
6157
|
+
if (report.settingsExposure.length > 0) {
|
|
6158
|
+
lines.push("", "Settings exposure (names and counts only, never values):");
|
|
6159
|
+
for (const exposure of report.settingsExposure) {
|
|
6160
|
+
lines.push(
|
|
6161
|
+
` ${exposure.file}: ${String(exposure.envKeyNames.length)} env key(s) [${exposure.envKeyNames.join(", ")}], ${String(exposure.hookEventNames.length)} hook event(s) [${exposure.hookEventNames.join(", ")}], ${String(exposure.hookCommandCount)} hook command(s)`
|
|
6162
|
+
);
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
6165
|
+
return lines;
|
|
6166
|
+
}
|
|
6167
|
+
function checkReportToJson(report) {
|
|
6168
|
+
const decisions = [...report.resolved.decisions.values()].sort((a, b) => a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0);
|
|
6169
|
+
return {
|
|
6170
|
+
identity: { name: report.identityName ?? null, source: report.identitySource },
|
|
6171
|
+
...report.poolPick === void 0 ? {} : { pool: report.poolPick },
|
|
6172
|
+
configProfile: { name: report.configProfileName ?? null, source: report.configProfileSource },
|
|
6173
|
+
layers: report.resolved.assembled.layers.map((layer) => ({ id: layer.id, kind: layer.kind, source: layer.source })),
|
|
6174
|
+
entries: decisions.map((decision) => ({
|
|
6175
|
+
path: decision.relPath,
|
|
6176
|
+
shared: decision.shared,
|
|
6177
|
+
via: decision.via,
|
|
6178
|
+
category: decision.category,
|
|
6179
|
+
...decision.rule === void 0 ? {} : { rule: { key: decision.rule.rawKey, layer: decision.rule.layer } },
|
|
6180
|
+
...decision.eliminated === void 0 || decision.eliminated.length === 0 ? {} : { eliminated: decision.eliminated.map((eliminated) => ({ key: eliminated.rule.rawKey, failed: eliminated.failed })) }
|
|
6181
|
+
})),
|
|
6182
|
+
projectEncodingAmbiguities: report.projectEncodingAmbiguities,
|
|
6183
|
+
diagnostics: report.resolved.diagnostics,
|
|
6184
|
+
ambientCredential: report.ambientCredential.ok ? { ok: true } : { ok: false, variable: report.ambientCredential.variable, message: report.ambientCredential.message },
|
|
6185
|
+
...report.keychain === void 0 ? {} : { keychain: report.keychain },
|
|
6186
|
+
settingsExposure: report.settingsExposure,
|
|
6187
|
+
credential: report.credential
|
|
6188
|
+
};
|
|
6189
|
+
}
|
|
6190
|
+
function checkReportHasWarnings(report) {
|
|
6191
|
+
return report.resolved.diagnostics.some((diagnostic) => diagnostic.severity !== "info") || report.projectEncodingAmbiguities.length > 0 || !report.ambientCredential.ok || report.credential.provider?.problem !== void 0;
|
|
6192
|
+
}
|
|
6193
|
+
function loadCheckProvider(paths, name) {
|
|
6194
|
+
let definition;
|
|
6195
|
+
try {
|
|
6196
|
+
definition = readProvider(paths, name);
|
|
6197
|
+
} catch (error) {
|
|
6198
|
+
if (error instanceof ConfigValidationError || error instanceof LegacyProviderFileError) {
|
|
6199
|
+
return { name, problem: error.message };
|
|
6200
|
+
}
|
|
6201
|
+
throw error;
|
|
6202
|
+
}
|
|
6203
|
+
return definition === void 0 ? { name, problem: `no provider named "${name}"` } : { name, definition };
|
|
6204
|
+
}
|
|
6205
|
+
function collectCheckReport(params) {
|
|
6206
|
+
const { paths, cwd } = params;
|
|
6207
|
+
const home = import_node_os3.default.homedir();
|
|
6208
|
+
const claudeHome = resolveClaudeHome();
|
|
6209
|
+
const read = cosmiconfigReader();
|
|
6210
|
+
const classification = loadClassification(paths);
|
|
6211
|
+
const loaded = loadCascadeInput({ paths, home, cwd, read });
|
|
6212
|
+
const selections = readDirectorySelections(loaded);
|
|
6213
|
+
const git = resolveGitBranch(realRunPort, cwd);
|
|
6214
|
+
const decidedIdentity = decideIdentity({
|
|
6215
|
+
env: params.env,
|
|
6216
|
+
argv0Identity: params.identity,
|
|
6217
|
+
directoryPinnedIdentity: selections.identity,
|
|
6218
|
+
readActiveIdentityFile: () => {
|
|
6219
|
+
const raw = realFsPort.readFileUtf8(paths.activeIdentityFile);
|
|
6220
|
+
if (raw === void 0) {
|
|
6221
|
+
return void 0;
|
|
6222
|
+
}
|
|
6223
|
+
const trimmed = raw.trim();
|
|
6224
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
6225
|
+
}
|
|
6226
|
+
});
|
|
6227
|
+
let poolPick;
|
|
6228
|
+
if (decidedIdentity.pool !== void 0) {
|
|
6229
|
+
const pool = loaded.globalConfig?.pools?.[decidedIdentity.pool];
|
|
6230
|
+
if (pool === void 0) {
|
|
6231
|
+
throw new PoolNotFoundError(decidedIdentity.pool);
|
|
6232
|
+
}
|
|
6233
|
+
poolPick = collectPoolPick({ paths, fs: realFsPort, usageFs: realFarmFs, poolName: decidedIdentity.pool, pool, directory: cwd, nowMs: Date.now() });
|
|
6234
|
+
}
|
|
6235
|
+
const identityDecision = poolPick?.pick === void 0 ? decidedIdentity : { ...decidedIdentity, name: poolPick.pick };
|
|
6236
|
+
const loadedIdentity = identityDecision.name === void 0 ? void 0 : loadIdentity(paths.identitiesDir, identityDecision.name, realFsPort);
|
|
6237
|
+
const configProfileDecision = decideConfigProfile({
|
|
6238
|
+
env: params.env,
|
|
6239
|
+
directoryRuleConfigProfile: selections.configProfile,
|
|
6240
|
+
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
6241
|
+
globalDefaultConfigProfile: loaded.globalConfig?.defaultConfigProfile
|
|
6242
|
+
});
|
|
6243
|
+
const cascade = loadCascadeInput({
|
|
6244
|
+
paths,
|
|
6245
|
+
home,
|
|
6246
|
+
cwd,
|
|
6247
|
+
read,
|
|
6248
|
+
...configProfileDecision.name === void 0 ? {} : { baseConfigProfile: configProfileDecision.name }
|
|
6249
|
+
}).input;
|
|
6250
|
+
const providerName = cascadeProviderName(cascade);
|
|
6251
|
+
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
6252
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : import_node_path20.default.join(paths.identitiesDir, identityDecision.name);
|
|
6253
|
+
const settingsFiles = {
|
|
6254
|
+
"settings.json": realFsPort.readFileUtf8(import_node_path20.default.join(claudeHome, "settings.json")),
|
|
6255
|
+
"settings.local.json": realFsPort.readFileUtf8(import_node_path20.default.join(claudeHome, "settings.local.json"))
|
|
6256
|
+
};
|
|
6257
|
+
const report = runCheck({
|
|
6258
|
+
cwd,
|
|
6259
|
+
home,
|
|
6260
|
+
claudeHome,
|
|
6261
|
+
env: params.env,
|
|
6262
|
+
...git.branch === void 0 ? {} : { branch: git.branch },
|
|
6263
|
+
...git.branchDetached === void 0 ? {} : { branchDetached: git.branchDetached },
|
|
6264
|
+
nowMs: Date.now(),
|
|
6265
|
+
farmFs: realFarmFs,
|
|
6266
|
+
cascade,
|
|
6267
|
+
classification,
|
|
6268
|
+
...identityDecision.name === void 0 ? {} : { identityName: identityDecision.name },
|
|
6269
|
+
identitySource: identityDecision.source,
|
|
6270
|
+
...poolPick === void 0 ? {} : { poolPick },
|
|
6271
|
+
...configProfileDecision.name === void 0 ? {} : { configProfileName: configProfileDecision.name },
|
|
6272
|
+
configProfileSource: configProfileDecision.source,
|
|
6273
|
+
...loadedIdentity === void 0 ? {} : { identity: loadedIdentity.config },
|
|
6274
|
+
settingsFiles,
|
|
6275
|
+
run: realRunPort,
|
|
6276
|
+
...farmRoot === void 0 ? {} : { farmRoot },
|
|
6277
|
+
platform: process.platform,
|
|
6278
|
+
...provider === void 0 ? {} : { provider }
|
|
6279
|
+
});
|
|
6280
|
+
return report;
|
|
6281
|
+
}
|
|
6282
|
+
|
|
6283
|
+
// src/claudeShim.ts
|
|
6284
|
+
var import_node_fs7 = __toESM(require("node:fs"), 1);
|
|
6285
|
+
var import_node_path21 = __toESM(require("node:path"), 1);
|
|
6286
|
+
var import_zod11 = require("zod");
|
|
6287
|
+
var ClaudeShimStateSchema = import_zod11.z.strictObject({
|
|
6288
|
+
targetPath: import_zod11.z.string().min(1),
|
|
6289
|
+
method: import_zod11.z.enum(["hardlink", "copy"]),
|
|
6290
|
+
installedAtMs: import_zod11.z.number()
|
|
6291
|
+
});
|
|
6292
|
+
function commandFilename(ownExecutablePath, commandName) {
|
|
6293
|
+
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
6294
|
+
}
|
|
6295
|
+
function resolveOwnInstallDirs(paths, ownExecutablePath) {
|
|
6296
|
+
const dirs = [import_node_path21.default.dirname(ownExecutablePath)];
|
|
6297
|
+
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6298
|
+
if (state !== void 0) {
|
|
6299
|
+
dirs.push(import_node_path21.default.dirname(state.targetPath));
|
|
6300
|
+
}
|
|
6301
|
+
return dirs;
|
|
6302
|
+
}
|
|
6303
|
+
var nodeLinkFs = { link: import_node_fs7.default.linkSync, copyFile: import_node_fs7.default.copyFileSync, chmod: import_node_fs7.default.chmodSync };
|
|
6304
|
+
function findPathShadow(params) {
|
|
6305
|
+
const resolvedTargetDir = import_node_path21.default.resolve(params.targetDir);
|
|
6306
|
+
for (const dir of params.pathDirs) {
|
|
6307
|
+
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
6308
|
+
if (found === void 0) {
|
|
6309
|
+
continue;
|
|
6310
|
+
}
|
|
6311
|
+
return import_node_path21.default.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
6312
|
+
}
|
|
6313
|
+
return params.pathDirs.some((dir) => import_node_path21.default.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
6314
|
+
}
|
|
6315
|
+
|
|
6316
|
+
// src/doctorReport.ts
|
|
6317
|
+
var import_node_fs8 = __toESM(require("node:fs"), 1);
|
|
6318
|
+
var import_node_path22 = __toESM(require("node:path"), 1);
|
|
6319
|
+
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
6320
|
+
if (status.status !== "shadowed") {
|
|
6321
|
+
return status;
|
|
6322
|
+
}
|
|
6323
|
+
return realpath(status.by) === realpath(ownExecutablePath) ? { status: "ok" } : status;
|
|
6324
|
+
}
|
|
6325
|
+
function validateJson(schema, input) {
|
|
6326
|
+
if (input.raw === void 0) {
|
|
6327
|
+
return { ok: false, message: `${input.path} is missing.` };
|
|
6328
|
+
}
|
|
6329
|
+
let parsed;
|
|
6330
|
+
try {
|
|
6331
|
+
parsed = JSON.parse(input.raw);
|
|
6332
|
+
} catch (error) {
|
|
6333
|
+
return { ok: false, message: `${input.path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}` };
|
|
6334
|
+
}
|
|
6335
|
+
const result = schema.safeParse(parsed);
|
|
6336
|
+
if (!result.success) {
|
|
6337
|
+
return { ok: false, message: new ConfigValidationError(input.path, result.error.issues).message };
|
|
6338
|
+
}
|
|
6339
|
+
return { ok: true, data: result.data };
|
|
6340
|
+
}
|
|
6341
|
+
function pushPathResolution(push, resolution) {
|
|
6342
|
+
const ownDir = import_node_path22.default.dirname(resolution.ownExecutablePath);
|
|
6343
|
+
switch (resolution.agentShim.status) {
|
|
6344
|
+
case "ok":
|
|
6345
|
+
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
6346
|
+
break;
|
|
6347
|
+
case "not-on-path":
|
|
6348
|
+
push(
|
|
6349
|
+
"path-resolution",
|
|
6350
|
+
"warn",
|
|
6351
|
+
`${ownDir} is not on PATH, so a bare \`agent-shim\` does not reach ${resolution.ownExecutablePath}. Add it to PATH, or keep invoking this executable by its full path.`,
|
|
6352
|
+
"agent-shim"
|
|
6353
|
+
);
|
|
6354
|
+
break;
|
|
6355
|
+
case "shadowed":
|
|
6356
|
+
push(
|
|
6357
|
+
"path-resolution",
|
|
6358
|
+
"fail",
|
|
6359
|
+
`\`agent-shim\` on PATH resolves to ${resolution.agentShim.by}, not this running executable, ${resolution.ownExecutablePath}. Every command you type runs that one instead, at whatever version it happens to be \u2014 including the checks in this report, which describe this executable. Remove ${resolution.agentShim.by}, repoint it at ${resolution.ownExecutablePath}, or put ${ownDir} ahead of it on PATH.`,
|
|
6360
|
+
"agent-shim"
|
|
6361
|
+
);
|
|
6362
|
+
break;
|
|
6363
|
+
}
|
|
6364
|
+
if (resolution.claude === void 0) {
|
|
6365
|
+
return;
|
|
6366
|
+
}
|
|
6367
|
+
switch (resolution.claude.status) {
|
|
6368
|
+
case "ok":
|
|
6369
|
+
push("path-resolution", "pass", "`claude` on PATH resolves to the enabled shim.", "claude");
|
|
6370
|
+
break;
|
|
6371
|
+
case "not-on-path":
|
|
6372
|
+
push("path-resolution", "warn", "The enabled `claude` shim's directory is not on PATH \u2014 add it, or use `agent-shim run` instead.", "claude");
|
|
6373
|
+
break;
|
|
6374
|
+
case "shadowed":
|
|
6375
|
+
push(
|
|
6376
|
+
"path-resolution",
|
|
6377
|
+
"warn",
|
|
6378
|
+
`\`claude\` on PATH resolves to ${resolution.claude.by}, not the enabled shim. Put the shim's directory ahead of it on PATH, or run \`agent-shim shim disable\` if you meant to launch that one directly.`,
|
|
6379
|
+
"claude"
|
|
6380
|
+
);
|
|
6381
|
+
break;
|
|
6382
|
+
}
|
|
6383
|
+
}
|
|
6384
|
+
function pushProvider(push, entry) {
|
|
6385
|
+
if (entry.raw === void 0) {
|
|
6386
|
+
push("provider", "fail", `${entry.path} is missing.`, entry.name);
|
|
6387
|
+
return;
|
|
6388
|
+
}
|
|
6389
|
+
let parsed;
|
|
6390
|
+
try {
|
|
6391
|
+
parsed = JSON.parse(entry.raw);
|
|
6392
|
+
} catch (error) {
|
|
6393
|
+
push("provider", "fail", `${entry.path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, entry.name);
|
|
6394
|
+
return;
|
|
6395
|
+
}
|
|
6396
|
+
const conversion = legacyProviderConversion(parsed);
|
|
6397
|
+
if (conversion !== void 0) {
|
|
6398
|
+
push("provider", "fail", new LegacyProviderFileError(entry.path, conversion).message, entry.name);
|
|
6399
|
+
return;
|
|
6400
|
+
}
|
|
6401
|
+
const validated = ProviderSchema.safeParse(parsed);
|
|
6402
|
+
if (!validated.success) {
|
|
6403
|
+
push("provider", "fail", new ConfigValidationError(entry.path, validated.error.issues).message, entry.name);
|
|
6404
|
+
return;
|
|
6405
|
+
}
|
|
6406
|
+
push("provider", "pass", `${entry.name} is valid (${describeProviderEndpoint(validated.data)}, credential ${describeCredential(validated.data.credential)}).`, entry.name);
|
|
6407
|
+
}
|
|
6408
|
+
function pushLegacyName(push, params) {
|
|
6409
|
+
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
6410
|
+
if (legacyVariables.length === 0 && import_node_path22.default.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
6411
|
+
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
6412
|
+
return;
|
|
6413
|
+
}
|
|
6414
|
+
for (const name of legacyVariables) {
|
|
6415
|
+
push("legacy-name", "warn", `${name} is the former name of ${name.replace(LEGACY_ENV_PREFIX, ENV_PREFIX)}. Rename it; the old name still works but is only a fallback.`, name);
|
|
6416
|
+
}
|
|
6417
|
+
if (import_node_path22.default.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
6418
|
+
push(
|
|
6419
|
+
"legacy-name",
|
|
6420
|
+
"warn",
|
|
6421
|
+
`The state root is the former ${params.rootPath}, used in place. Leave it where it is: macOS Claude Code keys each identity's Keychain login on its exact configuration directory path, so moving it signs every identity out.`
|
|
6422
|
+
);
|
|
6423
|
+
}
|
|
6424
|
+
}
|
|
6425
|
+
function runDoctor(params) {
|
|
6426
|
+
const findings = [];
|
|
6427
|
+
const push = (section, severity, message, subject) => {
|
|
6428
|
+
findings.push({ section, severity, message, ...subject === void 0 ? {} : { subject } });
|
|
6429
|
+
};
|
|
6430
|
+
const ambient = detectAmbientCredential(params.env);
|
|
6431
|
+
if (ambient === void 0) {
|
|
6432
|
+
push("ambient-credential", "pass", "No ambient-credential environment variable is set.");
|
|
6433
|
+
} else {
|
|
6434
|
+
push("ambient-credential", "warn", formatAmbientCredentialGuardMessage(ambient.variable));
|
|
6435
|
+
}
|
|
6436
|
+
if (params.binaryDiscovery.ok) {
|
|
6437
|
+
const { binary } = params.binaryDiscovery;
|
|
6438
|
+
const versionNote = binary.version === void 0 ? "" : `, version ${binary.version}`;
|
|
6439
|
+
push("binary-discovery", "pass", `Found ${binary.path} (${binary.source}${versionNote}).`);
|
|
6440
|
+
} else {
|
|
6441
|
+
push("binary-discovery", "fail", params.binaryDiscovery.message);
|
|
6442
|
+
}
|
|
6443
|
+
if (params.claudeShim.state === void 0) {
|
|
6444
|
+
push("claude-shim", "pass", "No `claude` command shim enabled (the default). Run `agent-shim shim enable` to add one.");
|
|
6445
|
+
} else if (!params.claudeShim.targetExists) {
|
|
6446
|
+
push(
|
|
6447
|
+
"claude-shim",
|
|
6448
|
+
"warn",
|
|
6449
|
+
`claude-shim.json records a \`claude\` shim at ${params.claudeShim.state.targetPath}, but nothing is there. Run \`agent-shim shim enable\` again, or \`agent-shim shim disable\` to clear the stale record.`
|
|
6450
|
+
);
|
|
6451
|
+
} else {
|
|
6452
|
+
push(
|
|
6453
|
+
"claude-shim",
|
|
6454
|
+
"pass",
|
|
6455
|
+
`\`claude\` is enabled at ${params.claudeShim.state.targetPath} (${params.claudeShim.state.method}). If you've upgraded agent-shim since, re-run \`agent-shim shim enable\` to refresh it.`
|
|
6456
|
+
);
|
|
6457
|
+
}
|
|
6458
|
+
pushPathResolution(push, params.pathResolution);
|
|
6459
|
+
pushLegacyName(push, params);
|
|
6460
|
+
const profileSources = /* @__PURE__ */ new Map();
|
|
6461
|
+
for (const entry of params.configProfiles) {
|
|
6462
|
+
const validated = validateJson(ConfigProfileSchema, entry);
|
|
6463
|
+
if (!validated.ok) {
|
|
6464
|
+
push("config-profile", "fail", validated.message, entry.name);
|
|
6465
|
+
continue;
|
|
6466
|
+
}
|
|
6467
|
+
profileSources.set(entry.name, { name: entry.name, profile: validated.data });
|
|
6468
|
+
}
|
|
6469
|
+
const loadProfile = (name) => profileSources.get(name);
|
|
6470
|
+
for (const name of profileSources.keys()) {
|
|
6471
|
+
const linearised = lineariseProfile(name, loadProfile);
|
|
6472
|
+
if (linearised.diagnostics.length === 0) {
|
|
6473
|
+
push("config-profile", "pass", `${name} is valid and its extends chain resolves cleanly.`, name);
|
|
6474
|
+
} else {
|
|
6475
|
+
for (const diagnostic of linearised.diagnostics) {
|
|
6476
|
+
push("config-profile", "fail", diagnostic.message, name);
|
|
6477
|
+
}
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6480
|
+
const validProfileNames = new Set(profileSources.keys());
|
|
6481
|
+
const validIdentityNames = /* @__PURE__ */ new Set();
|
|
6482
|
+
for (const entry of params.identities) {
|
|
6483
|
+
const validated = validateJson(IdentitySchema, entry);
|
|
6484
|
+
if (!validated.ok) {
|
|
6485
|
+
push("identity", "fail", validated.message, entry.name);
|
|
6486
|
+
continue;
|
|
6487
|
+
}
|
|
6488
|
+
validIdentityNames.add(entry.name);
|
|
6489
|
+
const defaultProfile = validated.data.defaultConfigProfile;
|
|
6490
|
+
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
6491
|
+
push(
|
|
6492
|
+
"identity",
|
|
6493
|
+
"fail",
|
|
6494
|
+
`Identity "${entry.name}" names configuration profile "${defaultProfile}" as its default, but no such profile exists.`,
|
|
6495
|
+
entry.name
|
|
6496
|
+
);
|
|
6497
|
+
} else {
|
|
6498
|
+
const credential = validated.data.credential;
|
|
6499
|
+
push(
|
|
6500
|
+
"identity",
|
|
6501
|
+
"pass",
|
|
6502
|
+
`${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}.`,
|
|
6503
|
+
entry.name
|
|
6504
|
+
);
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6507
|
+
const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
|
|
6508
|
+
const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
|
|
6509
|
+
for (const [poolName, pool] of Object.entries(pools)) {
|
|
6510
|
+
const missing = pool.identities.filter((member) => !validIdentityNames.has(member));
|
|
6511
|
+
if (missing.length === 0) {
|
|
6512
|
+
push("pool", "pass", `${poolName} is valid (${pool.identities.join(", ")}).`, poolName);
|
|
6513
|
+
} else {
|
|
6514
|
+
push("pool", "fail", `Pool "${poolName}" names ${missing.map((member) => `identity "${member}"`).join(" and ")}, which ${missing.length === 1 ? "does" : "do"} not exist.`, poolName);
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
const selectionExists = (selector) => {
|
|
6518
|
+
const poolName = poolNameOf(selector);
|
|
6519
|
+
return poolName === void 0 ? validIdentityNames.has(selector) : poolName in pools;
|
|
6520
|
+
};
|
|
6521
|
+
for (const entry of params.providers) {
|
|
6522
|
+
pushProvider(push, entry);
|
|
6523
|
+
}
|
|
6524
|
+
if (params.platform !== "darwin" || params.run === void 0) {
|
|
6525
|
+
push("keychain", "pass", "Skipped (not macOS).");
|
|
6526
|
+
} else {
|
|
6527
|
+
for (const entry of params.identities) {
|
|
6528
|
+
const result = lookupKeychainService(params.run, entry.farmRoot);
|
|
6529
|
+
push("keychain", result.found ? "pass" : "warn", result.note, entry.name);
|
|
6530
|
+
}
|
|
6531
|
+
}
|
|
6532
|
+
if (params.directoryRules.raw === void 0) {
|
|
6533
|
+
push("directory-rules", "pass", "No directory-rules.json configured.");
|
|
6534
|
+
} else {
|
|
6535
|
+
const validated = validateJson(DirectoryRulesSchema, params.directoryRules);
|
|
6536
|
+
if (!validated.ok) {
|
|
6537
|
+
push("directory-rules", "fail", validated.message);
|
|
6538
|
+
} else {
|
|
6539
|
+
push("directory-rules", "pass", `${params.directoryRules.path} is valid.`);
|
|
6540
|
+
for (const rule of validated.data.rules) {
|
|
6541
|
+
const badRefs = [];
|
|
6542
|
+
if (rule.identity !== void 0 && !selectionExists(rule.identity)) {
|
|
6543
|
+
badRefs.push(poolNameOf(rule.identity) === void 0 ? `identity "${rule.identity}"` : `pool "${poolNameOf(rule.identity) ?? ""}"`);
|
|
6544
|
+
}
|
|
6545
|
+
if (rule.configProfile !== void 0 && !validProfileNames.has(rule.configProfile)) {
|
|
6546
|
+
badRefs.push(`configuration profile "${rule.configProfile}"`);
|
|
6547
|
+
}
|
|
6548
|
+
if (badRefs.length > 0) {
|
|
6549
|
+
push("directory-rules", "fail", `Rule for "${rule.path}" names ${badRefs.join(" and ")}, which do not exist.`, rule.path);
|
|
6550
|
+
} else {
|
|
6551
|
+
push("directory-rules", "pass", `Rule for "${rule.path}" is valid.`, rule.path);
|
|
6552
|
+
}
|
|
6553
|
+
}
|
|
6554
|
+
}
|
|
6555
|
+
}
|
|
6556
|
+
if (params.globalConfig.raw === void 0) {
|
|
6557
|
+
push("global-config", "pass", "No config.json configured.");
|
|
6558
|
+
} else {
|
|
6559
|
+
const validated = validateJson(GlobalConfigSchema, params.globalConfig);
|
|
6560
|
+
if (!validated.ok) {
|
|
6561
|
+
push("global-config", "fail", validated.message);
|
|
6562
|
+
} else {
|
|
6563
|
+
const defaultProfile = validated.data.defaultConfigProfile;
|
|
6564
|
+
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
6565
|
+
push(
|
|
6566
|
+
"global-config",
|
|
6567
|
+
"fail",
|
|
6568
|
+
`config.json names configuration profile "${defaultProfile}" as its default, but no such profile exists.`
|
|
6569
|
+
);
|
|
6570
|
+
} else {
|
|
6571
|
+
push("global-config", "pass", `${params.globalConfig.path} is valid.`);
|
|
6572
|
+
}
|
|
6573
|
+
}
|
|
6574
|
+
}
|
|
6575
|
+
if (params.categoriesLocal.raw === void 0) {
|
|
6576
|
+
push("categories-local", "pass", "No categories.local.json configured.");
|
|
6577
|
+
} else {
|
|
6578
|
+
const validated = validateJson(CategoryClassificationOverlaySchema, params.categoriesLocal);
|
|
6579
|
+
push(
|
|
6580
|
+
"categories-local",
|
|
6581
|
+
validated.ok ? "pass" : "fail",
|
|
6582
|
+
validated.ok ? `${params.categoriesLocal.path} is valid.` : validated.message
|
|
6583
|
+
);
|
|
6584
|
+
}
|
|
6585
|
+
if (params.headroom.state.raw === void 0) {
|
|
6586
|
+
push("headroom", "pass", "No headroom daemon has ever run; nothing to check.");
|
|
6587
|
+
} else {
|
|
6588
|
+
const validated = validateJson(HeadroomStateSchema, params.headroom.state);
|
|
6589
|
+
if (!validated.ok) {
|
|
6590
|
+
push("headroom", "fail", validated.message);
|
|
6591
|
+
} else {
|
|
6592
|
+
const state = validated.data;
|
|
6593
|
+
const supervisorAlive = state.supervisorPid !== void 0 && params.headroom.isRunning(state.supervisorPid);
|
|
6594
|
+
const headroomAlive = state.headroomPid !== void 0 && params.headroom.isRunning(state.headroomPid);
|
|
6595
|
+
if (state.supervisorPid === void 0) {
|
|
6596
|
+
if (state.lastError === void 0) {
|
|
6597
|
+
push("headroom", "pass", "Headroom daemon is stopped (idle shutdown) with no recorded error.");
|
|
6598
|
+
} else {
|
|
6599
|
+
push("headroom", "warn", `Headroom supervisor is not running; last error: ${state.lastError}`);
|
|
6600
|
+
}
|
|
6601
|
+
} else if (!supervisorAlive) {
|
|
6602
|
+
push(
|
|
6603
|
+
"headroom",
|
|
6604
|
+
"warn",
|
|
6605
|
+
`Headroom state names supervisor pid ${String(state.supervisorPid)}, which is not running; the next launch through headroom will start a replacement.`
|
|
6606
|
+
);
|
|
6607
|
+
} else if (state.headroomPid === void 0 || state.port === void 0) {
|
|
6608
|
+
push(
|
|
6609
|
+
"headroom",
|
|
6610
|
+
"warn",
|
|
6611
|
+
state.lastError === void 0 ? "Headroom supervisor is running but the daemon has no healthy process; it should be restarting it." : `Headroom supervisor is running but the daemon is down; last error: ${state.lastError}`
|
|
6612
|
+
);
|
|
6613
|
+
} else if (!headroomAlive) {
|
|
6614
|
+
push(
|
|
6615
|
+
"headroom",
|
|
6616
|
+
"warn",
|
|
6617
|
+
`Headroom state names daemon pid ${String(state.headroomPid)}, which is not running; its supervisor should be restarting it.`
|
|
6618
|
+
);
|
|
6619
|
+
} else {
|
|
6620
|
+
push(
|
|
6621
|
+
"headroom",
|
|
6622
|
+
"pass",
|
|
6623
|
+
`Headroom daemon is up on 127.0.0.1:${String(state.port)} (supervisor ${String(state.supervisorPid)}, daemon ${String(state.headroomPid)}).`
|
|
6624
|
+
);
|
|
6625
|
+
}
|
|
6626
|
+
if (state.lastError !== void 0 && supervisorAlive) {
|
|
6627
|
+
push("headroom", "warn", `Headroom recorded a previous error: ${state.lastError}`);
|
|
6628
|
+
}
|
|
6629
|
+
if (state.installedSource !== void 0 && isMovingGitSource(state.installedSource)) {
|
|
6630
|
+
push(
|
|
6631
|
+
"headroom",
|
|
6632
|
+
"warn",
|
|
6633
|
+
`Headroom is installed from a git ref that can move (${state.installedSource}); pin headroom.source to a full commit SHA so a rebased or deleted branch cannot change or break the install.`
|
|
6634
|
+
);
|
|
6635
|
+
}
|
|
6636
|
+
}
|
|
6637
|
+
}
|
|
6638
|
+
if (params.activeIdentity.raw === void 0) {
|
|
6639
|
+
push("active-identity", "pass", "No active identity set.");
|
|
6640
|
+
} else {
|
|
6641
|
+
const trimmed = params.activeIdentity.raw.trim();
|
|
6642
|
+
if (trimmed === "") {
|
|
6643
|
+
push("active-identity", "warn", "active-identity is present but empty \u2014 treated the same as unset.");
|
|
6644
|
+
} else if (!selectionExists(trimmed)) {
|
|
6645
|
+
push("active-identity", "fail", `active-identity names "${trimmed}", which does not exist.`);
|
|
6646
|
+
} else {
|
|
6647
|
+
push("active-identity", "pass", `Active identity "${trimmed}" is valid.`);
|
|
6648
|
+
}
|
|
6649
|
+
}
|
|
6650
|
+
return { findings, ok: !findings.some((finding) => finding.severity === "fail") };
|
|
6651
|
+
}
|
|
6652
|
+
var SECTION_TITLES = {
|
|
6653
|
+
"ambient-credential": "Ambient-credential exposure",
|
|
6654
|
+
"binary-discovery": "Claude Code binary discovery",
|
|
6655
|
+
"claude-shim": "`claude` command shim",
|
|
6656
|
+
"path-resolution": "PATH resolution",
|
|
6657
|
+
"legacy-name": "Former `claude-use` name",
|
|
6658
|
+
"config-profile": "Configuration profiles",
|
|
6659
|
+
identity: "Identities",
|
|
6660
|
+
pool: "Pools",
|
|
6661
|
+
provider: "Providers",
|
|
6662
|
+
keychain: "macOS Keychain",
|
|
6663
|
+
"directory-rules": "Directory rules",
|
|
6664
|
+
"global-config": "Global config",
|
|
6665
|
+
headroom: "Headroom daemon",
|
|
6666
|
+
"categories-local": "categories.local.json",
|
|
6667
|
+
"active-identity": "Active identity"
|
|
6668
|
+
};
|
|
6669
|
+
var SECTION_ORDER = [
|
|
6670
|
+
"ambient-credential",
|
|
6671
|
+
"binary-discovery",
|
|
6672
|
+
"claude-shim",
|
|
6673
|
+
"path-resolution",
|
|
6674
|
+
"config-profile",
|
|
6675
|
+
"identity",
|
|
6676
|
+
"pool",
|
|
6677
|
+
"provider",
|
|
6678
|
+
"keychain",
|
|
6679
|
+
"directory-rules",
|
|
6680
|
+
"global-config",
|
|
6681
|
+
"headroom",
|
|
6682
|
+
"categories-local",
|
|
6683
|
+
"active-identity"
|
|
6684
|
+
];
|
|
6685
|
+
function severityPrefix(severity) {
|
|
6686
|
+
switch (severity) {
|
|
6687
|
+
case "pass":
|
|
6688
|
+
return "[PASS]";
|
|
6689
|
+
case "warn":
|
|
6690
|
+
return "[WARN]";
|
|
6691
|
+
case "fail":
|
|
6692
|
+
return "[FAIL]";
|
|
6693
|
+
default:
|
|
6694
|
+
return severity;
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6697
|
+
function formatDoctorReport(report) {
|
|
6698
|
+
const lines = [];
|
|
6699
|
+
for (const section of SECTION_ORDER) {
|
|
6700
|
+
const sectionFindings = report.findings.filter((finding) => finding.section === section);
|
|
6701
|
+
if (sectionFindings.length === 0) {
|
|
6702
|
+
continue;
|
|
6703
|
+
}
|
|
6704
|
+
lines.push("", `${SECTION_TITLES[section]}:`);
|
|
6705
|
+
for (const finding of sectionFindings) {
|
|
6706
|
+
const subject = finding.subject === void 0 ? "" : `${finding.subject}: `;
|
|
6707
|
+
lines.push(` ${severityPrefix(finding.severity)} ${subject}${finding.message}`);
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6710
|
+
const failCount = report.findings.filter((finding) => finding.severity === "fail").length;
|
|
6711
|
+
lines.push("", report.ok ? "All checks passed." : `${String(failCount)} check(s) failed.`);
|
|
6712
|
+
return lines;
|
|
6713
|
+
}
|
|
6714
|
+
function realpathOrSelf(target) {
|
|
6715
|
+
try {
|
|
6716
|
+
return import_node_fs8.default.realpathSync(target);
|
|
6717
|
+
} catch {
|
|
6718
|
+
return target;
|
|
6719
|
+
}
|
|
6720
|
+
}
|
|
6721
|
+
function collectDoctorReport(params) {
|
|
6722
|
+
const { paths } = params;
|
|
6723
|
+
const identityNames = import_node_fs8.default.existsSync(paths.identitiesDir) ? import_node_fs8.default.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
|
|
6724
|
+
const identities = identityNames.map((name) => {
|
|
6725
|
+
const farmRoot = import_node_path22.default.join(paths.identitiesDir, name);
|
|
6726
|
+
const identityPath = import_node_path22.default.join(farmRoot, "identity.json");
|
|
6727
|
+
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
6728
|
+
});
|
|
6729
|
+
const profileNames = import_node_fs8.default.existsSync(paths.configProfilesDir) ? import_node_fs8.default.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6730
|
+
const configProfiles = profileNames.map((name) => {
|
|
6731
|
+
const profilePath = import_node_path22.default.join(paths.configProfilesDir, `${name}.json`);
|
|
6732
|
+
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
6733
|
+
});
|
|
6734
|
+
const providerNames = import_node_fs8.default.existsSync(paths.providersDir) ? import_node_fs8.default.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6735
|
+
const providers = providerNames.map((name) => {
|
|
6736
|
+
const providerPath = import_node_path22.default.join(paths.providersDir, `${name}.json`);
|
|
6737
|
+
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
6738
|
+
});
|
|
6739
|
+
const ownExecutablePath = realOwnExecutablePath();
|
|
6740
|
+
let binaryDiscovery;
|
|
6741
|
+
try {
|
|
6742
|
+
const binary = realResolveClaudeBinary(resolveOwnInstallDirs(paths, ownExecutablePath))();
|
|
6743
|
+
binaryDiscovery = { ok: true, binary };
|
|
6744
|
+
} catch (error) {
|
|
6745
|
+
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
6746
|
+
}
|
|
6747
|
+
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6748
|
+
const pathDirs = (params.env.PATH ?? "").split(import_node_path22.default.delimiter).filter((dir) => dir !== "");
|
|
6749
|
+
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
6750
|
+
findPathShadow({
|
|
6751
|
+
pathDirs,
|
|
6752
|
+
targetDir: import_node_path22.default.dirname(shimState.targetPath),
|
|
6753
|
+
targetFilename: import_node_path22.default.basename(shimState.targetPath),
|
|
6754
|
+
findExecutableInDir
|
|
6755
|
+
}),
|
|
6756
|
+
shimState.targetPath,
|
|
6757
|
+
realpathOrSelf
|
|
6758
|
+
);
|
|
6759
|
+
const report = runDoctor({
|
|
6760
|
+
env: params.env,
|
|
6761
|
+
identities,
|
|
6762
|
+
configProfiles,
|
|
6763
|
+
providers,
|
|
6764
|
+
directoryRules: { path: paths.directoryRulesFile, raw: realFsPort.readFileUtf8(paths.directoryRulesFile) },
|
|
6765
|
+
globalConfig: { path: paths.globalConfigFile, raw: realFsPort.readFileUtf8(paths.globalConfigFile) },
|
|
6766
|
+
categoriesLocal: { path: paths.categoriesLocalFile, raw: realFsPort.readFileUtf8(paths.categoriesLocalFile) },
|
|
6767
|
+
activeIdentity: { path: paths.activeIdentityFile, raw: realFsPort.readFileUtf8(paths.activeIdentityFile) },
|
|
6768
|
+
binaryDiscovery,
|
|
6769
|
+
claudeShim: { state: shimState, targetExists: shimState !== void 0 && import_node_fs8.default.existsSync(shimState.targetPath) },
|
|
6770
|
+
rootPath: paths.root,
|
|
6771
|
+
pathResolution: {
|
|
6772
|
+
ownExecutablePath,
|
|
6773
|
+
agentShim: refinePathShadow(
|
|
6774
|
+
findPathShadow({
|
|
6775
|
+
pathDirs,
|
|
6776
|
+
targetDir: import_node_path22.default.dirname(ownExecutablePath),
|
|
6777
|
+
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
6778
|
+
findExecutableInDir
|
|
6779
|
+
}),
|
|
6780
|
+
ownExecutablePath,
|
|
6781
|
+
realpathOrSelf
|
|
6782
|
+
),
|
|
6783
|
+
...claudeShimShadow === void 0 ? {} : { claude: claudeShimShadow }
|
|
6784
|
+
},
|
|
6785
|
+
run: realRunPort,
|
|
6786
|
+
platform: process.platform,
|
|
6787
|
+
headroom: {
|
|
6788
|
+
state: { path: paths.headroomStateFile, raw: realFsPort.readFileUtf8(paths.headroomStateFile) },
|
|
6789
|
+
isRunning: realIsProcessRunning
|
|
6790
|
+
}
|
|
6791
|
+
});
|
|
6792
|
+
return report;
|
|
6793
|
+
}
|
|
3816
6794
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3817
6795
|
0 && (module.exports = {
|
|
3818
6796
|
AMBIENT_CREDENTIAL_VARS,
|
|
@@ -3827,6 +6805,9 @@ function resolveLayoutPaths() {
|
|
|
3827
6805
|
CredentialCacheSchema,
|
|
3828
6806
|
CredentialSchema,
|
|
3829
6807
|
CredentialSourceSchema,
|
|
6808
|
+
DirectoryRuleAlreadyExistsError,
|
|
6809
|
+
DirectoryRuleMissingTargetError,
|
|
6810
|
+
DirectoryRuleNotFoundError,
|
|
3830
6811
|
DirectoryRuleSchema,
|
|
3831
6812
|
DirectoryRulesSchema,
|
|
3832
6813
|
EXIT_FAILURE,
|
|
@@ -3836,48 +6817,108 @@ function resolveLayoutPaths() {
|
|
|
3836
6817
|
FrontDoorStartError,
|
|
3837
6818
|
GlobalConfigSchema,
|
|
3838
6819
|
HeadroomStartError,
|
|
6820
|
+
IdentityAlreadyExistsError,
|
|
6821
|
+
IdentityNotFoundError,
|
|
3839
6822
|
IdentitySchema,
|
|
6823
|
+
InvalidCategoryNameError,
|
|
6824
|
+
InvalidIdentityNameError,
|
|
6825
|
+
InvalidProviderNameError,
|
|
6826
|
+
LegacyProviderFileError,
|
|
3840
6827
|
PoolNameSchema,
|
|
6828
|
+
PoolNotFoundError,
|
|
3841
6829
|
PoolSchema,
|
|
3842
6830
|
PortableConfigSchema,
|
|
6831
|
+
ProfileAlreadyExistsError,
|
|
6832
|
+
ProfileNotFoundError,
|
|
6833
|
+
ProviderAlreadyExistsError,
|
|
6834
|
+
ProviderKindMismatchError,
|
|
6835
|
+
ProviderNotFoundError,
|
|
3843
6836
|
ProviderSchema,
|
|
3844
6837
|
ROUTED_PATH_PREFIX,
|
|
3845
6838
|
UsageError,
|
|
3846
6839
|
UsageSnapshotError,
|
|
3847
6840
|
UsageSnapshotSchema,
|
|
3848
6841
|
WhenSchema,
|
|
6842
|
+
addDirectoryRule,
|
|
6843
|
+
addIdentity,
|
|
6844
|
+
addPool,
|
|
6845
|
+
addProvider,
|
|
3849
6846
|
buildEntryFacts,
|
|
3850
6847
|
buildLayoutPaths,
|
|
3851
6848
|
carryOver,
|
|
6849
|
+
checkReportHasWarnings,
|
|
6850
|
+
checkReportToJson,
|
|
6851
|
+
collectCheckReport,
|
|
6852
|
+
collectDoctorReport,
|
|
3852
6853
|
createLeafCache,
|
|
6854
|
+
createProfile,
|
|
6855
|
+
describeProviderEndpoint,
|
|
3853
6856
|
detectAmbientCredential,
|
|
3854
6857
|
ensureCa,
|
|
3855
6858
|
ensureFrontDoor,
|
|
3856
6859
|
ensureHeadroom,
|
|
3857
6860
|
evaluateAmbientCredentialGuard,
|
|
3858
6861
|
formatAmbientCredentialGuardMessage,
|
|
6862
|
+
formatCheckReport,
|
|
6863
|
+
formatDoctorReport,
|
|
3859
6864
|
forwardableHeaders,
|
|
3860
6865
|
generateCa,
|
|
6866
|
+
identityExists,
|
|
6867
|
+
isIdentityDirectoryName,
|
|
3861
6868
|
isInterceptedHost,
|
|
6869
|
+
listDirectoryRules,
|
|
6870
|
+
listIdentities,
|
|
6871
|
+
listProfiles,
|
|
6872
|
+
listProviders,
|
|
3862
6873
|
listUsageSnapshots,
|
|
3863
6874
|
mintLeaf,
|
|
3864
6875
|
parseConnectTarget,
|
|
6876
|
+
profileExists,
|
|
6877
|
+
providerExists,
|
|
6878
|
+
readActiveIdentity,
|
|
6879
|
+
readDirectoryRules,
|
|
3865
6880
|
readFarmManifest,
|
|
6881
|
+
readGlobalConfig,
|
|
6882
|
+
readIdentity,
|
|
6883
|
+
readPools,
|
|
6884
|
+
readProfile,
|
|
6885
|
+
readProvider,
|
|
3866
6886
|
readUsageSnapshot,
|
|
3867
6887
|
realConnectCertStore,
|
|
3868
6888
|
realConnectEffects,
|
|
3869
6889
|
recoverFarm,
|
|
3870
6890
|
recoveryDiagnostics,
|
|
6891
|
+
removeDirectoryRule,
|
|
6892
|
+
removeIdentity,
|
|
6893
|
+
removePool,
|
|
6894
|
+
removeProfile,
|
|
6895
|
+
removeProvider,
|
|
6896
|
+
requirePool,
|
|
3871
6897
|
resolveAgentShimHome,
|
|
3872
6898
|
resolveClaudeHome,
|
|
3873
6899
|
resolveDecisions,
|
|
3874
6900
|
resolveLayoutPaths,
|
|
3875
6901
|
resolveSupervisorConfig,
|
|
3876
6902
|
resyncFarm,
|
|
6903
|
+
runCheck,
|
|
6904
|
+
runDoctor,
|
|
3877
6905
|
runFrontDoorSupervisor,
|
|
3878
6906
|
runSupervisor,
|
|
3879
6907
|
servedByPipeline,
|
|
6908
|
+
setAllowAmbientCredential,
|
|
6909
|
+
setDefaultConfigProfile,
|
|
6910
|
+
setGlobalDefaultProfile,
|
|
6911
|
+
setIdentityCredential,
|
|
6912
|
+
setPool,
|
|
6913
|
+
setProfileCategories,
|
|
6914
|
+
setProfileEntries,
|
|
6915
|
+
setProfileLaunchFlags,
|
|
6916
|
+
setProfileMetadata,
|
|
3880
6917
|
snapshotPath,
|
|
3881
6918
|
startConnectServer,
|
|
3882
|
-
topLevelNames
|
|
6919
|
+
topLevelNames,
|
|
6920
|
+
updateDirectoryRule,
|
|
6921
|
+
updateProvider,
|
|
6922
|
+
useIdentity,
|
|
6923
|
+
writeDirectoryRules
|
|
3883
6924
|
});
|