agent-shim 7.8.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 +2 -1
- package/dist/cli.cjs +533 -510
- package/dist/index.cjs +2314 -152
- package/dist/index.mjs +2306 -152
- package/dist/types/checkReport.d.ts +154 -0
- package/dist/types/claudeShim.d.ts +114 -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/configure.d.ts +170 -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/index.d.ts +5 -1
- package/dist/types/launcher/cascade.d.ts +42 -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/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/types/cli/parsers.d.ts +0 -40
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
// src/config/classify.ts
|
|
2
2
|
import picomatch from "picomatch";
|
|
3
3
|
|
|
4
|
+
// src/config/categories.default.json
|
|
5
|
+
var categories_default_default = {
|
|
6
|
+
secret: [".credentials.json", "backups", "settings.json.bak*"],
|
|
7
|
+
runtime: [
|
|
8
|
+
"daemon*",
|
|
9
|
+
".git*",
|
|
10
|
+
".DS_Store",
|
|
11
|
+
"mcp-needs-auth-cache.json",
|
|
12
|
+
"codex-quota.json",
|
|
13
|
+
"file-transfers",
|
|
14
|
+
"shell-snapshots",
|
|
15
|
+
"statsig",
|
|
16
|
+
"telemetry",
|
|
17
|
+
"stats-cache.json",
|
|
18
|
+
"usage-data",
|
|
19
|
+
"ide",
|
|
20
|
+
"cache",
|
|
21
|
+
"scheduled_tasks.lock"
|
|
22
|
+
],
|
|
23
|
+
history: [
|
|
24
|
+
"projects",
|
|
25
|
+
"sessions",
|
|
26
|
+
"session-env",
|
|
27
|
+
"teams",
|
|
28
|
+
"tasks",
|
|
29
|
+
"todos",
|
|
30
|
+
"history.jsonl",
|
|
31
|
+
"transcripts",
|
|
32
|
+
"paste-cache",
|
|
33
|
+
"file-history",
|
|
34
|
+
"plans",
|
|
35
|
+
"workflows",
|
|
36
|
+
"jobs",
|
|
37
|
+
"debug",
|
|
38
|
+
"downloads",
|
|
39
|
+
"chrome"
|
|
40
|
+
],
|
|
41
|
+
knowledge: [
|
|
42
|
+
"skills",
|
|
43
|
+
"agents",
|
|
44
|
+
"rules",
|
|
45
|
+
"memory",
|
|
46
|
+
"commands",
|
|
47
|
+
"plugins",
|
|
48
|
+
"hooks",
|
|
49
|
+
"AGENTS.md",
|
|
50
|
+
"CLAUDE.md",
|
|
51
|
+
"README.md"
|
|
52
|
+
],
|
|
53
|
+
settings: ["settings.json", "settings.local.json"]
|
|
54
|
+
};
|
|
55
|
+
|
|
4
56
|
// src/config/schema.ts
|
|
5
57
|
import path from "node:path";
|
|
6
58
|
import { z } from "zod";
|
|
@@ -267,6 +319,32 @@ var UsageError = class extends CliError {
|
|
|
267
319
|
};
|
|
268
320
|
|
|
269
321
|
// src/config/load.ts
|
|
322
|
+
function createExplorer(moduleName = "agent-shim") {
|
|
323
|
+
return cosmiconfigSync(moduleName, { searchPlaces: [] });
|
|
324
|
+
}
|
|
325
|
+
function isRecord(value) {
|
|
326
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
327
|
+
}
|
|
328
|
+
function captureEntryOrder(value) {
|
|
329
|
+
if (!isRecord(value)) {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
const entries = value.entries;
|
|
333
|
+
if (!isRecord(entries)) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
return Object.keys(entries);
|
|
337
|
+
}
|
|
338
|
+
function captureRuleEntryOrders(value) {
|
|
339
|
+
if (!isRecord(value)) {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
const rules = value.rules;
|
|
343
|
+
if (!Array.isArray(rules)) {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
return rules.map((rule) => captureEntryOrder(rule));
|
|
347
|
+
}
|
|
270
348
|
var ConfigValidationError = class extends CliError {
|
|
271
349
|
constructor(filepath, issues) {
|
|
272
350
|
const detail = issues.map((issue) => ` ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n");
|
|
@@ -279,6 +357,40 @@ ${detail}`);
|
|
|
279
357
|
filepath;
|
|
280
358
|
issues;
|
|
281
359
|
};
|
|
360
|
+
function cosmiconfigReader(explorer = createExplorer()) {
|
|
361
|
+
return (filepath) => {
|
|
362
|
+
let result;
|
|
363
|
+
try {
|
|
364
|
+
result = explorer.load(filepath);
|
|
365
|
+
} catch (error) {
|
|
366
|
+
const code = isRecord(error) ? error.code : void 0;
|
|
367
|
+
if (code === "ENOENT") {
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
throw error;
|
|
371
|
+
}
|
|
372
|
+
if (result === null || result.isEmpty === true) {
|
|
373
|
+
return void 0;
|
|
374
|
+
}
|
|
375
|
+
return result.config;
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function loadConfigFile(filepath, schema, read = cosmiconfigReader()) {
|
|
379
|
+
const raw = read(filepath);
|
|
380
|
+
if (raw === void 0) {
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
const parsed = schema.safeParse(raw);
|
|
384
|
+
if (!parsed.success) {
|
|
385
|
+
throw new ConfigValidationError(filepath, parsed.error.issues);
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
filepath,
|
|
389
|
+
config: parsed.data,
|
|
390
|
+
entryOrder: captureEntryOrder(raw),
|
|
391
|
+
ruleEntryOrders: captureRuleEntryOrders(raw)
|
|
392
|
+
};
|
|
393
|
+
}
|
|
282
394
|
|
|
283
395
|
// src/config/store.ts
|
|
284
396
|
function isEnoent(error) {
|
|
@@ -406,6 +518,13 @@ function compareClassificationPatterns(a, b) {
|
|
|
406
518
|
}
|
|
407
519
|
return a.ordinal - b.ordinal;
|
|
408
520
|
}
|
|
521
|
+
function loadClassification(paths) {
|
|
522
|
+
const overlay = readJson(paths.categoriesLocalFile, CategoryClassificationOverlaySchema);
|
|
523
|
+
return {
|
|
524
|
+
defaults: CategoryClassificationSchema.parse(categories_default_default),
|
|
525
|
+
...overlay === void 0 ? {} : { overlay }
|
|
526
|
+
};
|
|
527
|
+
}
|
|
409
528
|
function classifyEntries(names, options) {
|
|
410
529
|
const patterns = compileClassificationPatterns(options.defaults, options.overlay);
|
|
411
530
|
const classification = /* @__PURE__ */ new Map();
|
|
@@ -517,6 +636,7 @@ import picomatch3 from "picomatch";
|
|
|
517
636
|
|
|
518
637
|
// src/resolve/projects.ts
|
|
519
638
|
var NON_ALPHANUMERIC = /[^A-Za-z0-9]/g;
|
|
639
|
+
var NON_ALPHANUMERIC_CHARACTER = /[^A-Za-z0-9]/;
|
|
520
640
|
var WILDCARD_TOKENS = ["**", "*", "?"];
|
|
521
641
|
function encodeProjectPath(realPath) {
|
|
522
642
|
return realPath.replace(NON_ALPHANUMERIC, "-");
|
|
@@ -570,6 +690,64 @@ function encodeProjectPattern(fragment, options) {
|
|
|
570
690
|
}
|
|
571
691
|
return splitOnWildcards(expanded).map((piece) => piece.kind === "literal" ? encodeProjectPath(piece.text) : piece.text).join("");
|
|
572
692
|
}
|
|
693
|
+
function lossyCharacters(literal) {
|
|
694
|
+
const found = /* @__PURE__ */ new Set();
|
|
695
|
+
for (const character of literal) {
|
|
696
|
+
if (character !== "/" && NON_ALPHANUMERIC_CHARACTER.test(character)) {
|
|
697
|
+
found.add(character);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return [...found];
|
|
701
|
+
}
|
|
702
|
+
function detectEncodingAmbiguity(fragments, options) {
|
|
703
|
+
const ambiguities = [];
|
|
704
|
+
const encodedByFragment = /* @__PURE__ */ new Map();
|
|
705
|
+
for (const fragment of fragments) {
|
|
706
|
+
let encoded;
|
|
707
|
+
try {
|
|
708
|
+
encoded = encodeProjectPattern(fragment, { home: options.home });
|
|
709
|
+
} catch {
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
encodedByFragment.set(fragment, encoded);
|
|
713
|
+
const expanded = expandHome(fragment, options.home);
|
|
714
|
+
const literalText = splitOnWildcards(expanded).filter((piece) => piece.kind === "literal").map((piece) => piece.text).join("");
|
|
715
|
+
const lossy = lossyCharacters(literalText);
|
|
716
|
+
if (lossy.length > 0) {
|
|
717
|
+
const matched = options.existingNames?.filter((name) => name === encoded).length;
|
|
718
|
+
const suffix = matched === void 0 ? "" : ` It currently matches ${String(matched)} existing project directory name(s).`;
|
|
719
|
+
ambiguities.push({
|
|
720
|
+
fragment,
|
|
721
|
+
encoded,
|
|
722
|
+
reason: "lossy-characters",
|
|
723
|
+
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}`
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const byEncoded = /* @__PURE__ */ new Map();
|
|
728
|
+
for (const [fragment, encoded] of encodedByFragment) {
|
|
729
|
+
const group = byEncoded.get(encoded);
|
|
730
|
+
if (group === void 0) {
|
|
731
|
+
byEncoded.set(encoded, [fragment]);
|
|
732
|
+
} else {
|
|
733
|
+
group.push(fragment);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
for (const [encoded, group] of byEncoded) {
|
|
737
|
+
if (group.length < 2) {
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
for (const fragment of group) {
|
|
741
|
+
ambiguities.push({
|
|
742
|
+
fragment,
|
|
743
|
+
encoded,
|
|
744
|
+
reason: "collides-with-sibling-pattern",
|
|
745
|
+
detail: `Encodes identically to ${group.filter((other) => other !== fragment).map((other) => JSON.stringify(other)).join(", ")}.`
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return ambiguities;
|
|
750
|
+
}
|
|
573
751
|
|
|
574
752
|
// src/resolve/match.ts
|
|
575
753
|
var GLOB_METACHARACTERS2 = /[*?[\]{}!()]/;
|
|
@@ -1045,6 +1223,9 @@ function planFarm(params) {
|
|
|
1045
1223
|
return { entries, materialised, links };
|
|
1046
1224
|
}
|
|
1047
1225
|
|
|
1226
|
+
// src/resolve/walk.ts
|
|
1227
|
+
import path4 from "node:path";
|
|
1228
|
+
|
|
1048
1229
|
// src/resolve/extends.ts
|
|
1049
1230
|
function lineariseProfile(rootName, load) {
|
|
1050
1231
|
const stack = [];
|
|
@@ -1110,6 +1291,27 @@ function profileLayers(rootName, load, startId) {
|
|
|
1110
1291
|
}
|
|
1111
1292
|
|
|
1112
1293
|
// src/resolve/walk.ts
|
|
1294
|
+
function walkDirectoryAncestors(cwd, options) {
|
|
1295
|
+
const limit = path4.resolve(options.limit ?? options.home);
|
|
1296
|
+
const isReadable = options.isReadable ?? (() => true);
|
|
1297
|
+
const collected = [];
|
|
1298
|
+
let current = path4.resolve(cwd);
|
|
1299
|
+
for (; ; ) {
|
|
1300
|
+
if (!isReadable(current)) {
|
|
1301
|
+
break;
|
|
1302
|
+
}
|
|
1303
|
+
collected.push(current);
|
|
1304
|
+
if (current === limit) {
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
const parent = path4.dirname(current);
|
|
1308
|
+
if (parent === current) {
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
current = parent;
|
|
1312
|
+
}
|
|
1313
|
+
return collected.reverse();
|
|
1314
|
+
}
|
|
1113
1315
|
function assembleCascade(input) {
|
|
1114
1316
|
const layers = [];
|
|
1115
1317
|
const diagnostics = [];
|
|
@@ -1223,7 +1425,7 @@ function resolveDecisions(input) {
|
|
|
1223
1425
|
}
|
|
1224
1426
|
|
|
1225
1427
|
// src/launcher/farm.ts
|
|
1226
|
-
import
|
|
1428
|
+
import path6 from "node:path";
|
|
1227
1429
|
import { z as z2 } from "zod";
|
|
1228
1430
|
|
|
1229
1431
|
// src/resolve/reconcile.ts
|
|
@@ -1317,13 +1519,17 @@ function planReconciliation(params) {
|
|
|
1317
1519
|
}
|
|
1318
1520
|
|
|
1319
1521
|
// src/legacy.ts
|
|
1522
|
+
var ENV_PREFIX = "AGENT_SHIM_";
|
|
1523
|
+
var LEGACY_ENV_PREFIX = "CLAUDE_USE_";
|
|
1320
1524
|
var HOME_DIRNAME = ".agent-shim";
|
|
1321
1525
|
var LEGACY_HOME_DIRNAME = ".claude-use";
|
|
1526
|
+
var LEGACY_PORTABLE_CONFIG_FILENAME = ".claude-use.json";
|
|
1527
|
+
var LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME = ".claude-use.local.json";
|
|
1322
1528
|
var LEGACY_FARM_MANIFEST_FILENAME = ".claude-use-farm.json";
|
|
1323
1529
|
|
|
1324
1530
|
// src/launcher/lock.ts
|
|
1325
1531
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1326
|
-
import
|
|
1532
|
+
import path5 from "node:path";
|
|
1327
1533
|
var DEFAULT_STALE_AFTER_MS = 12e4;
|
|
1328
1534
|
var DEFAULT_RETRY_DELAY_MS = 50;
|
|
1329
1535
|
var DEFAULT_MAX_ATTEMPTS = 200;
|
|
@@ -1342,7 +1548,7 @@ var IdentityLockBusyError = class extends CliError {
|
|
|
1342
1548
|
holderPid;
|
|
1343
1549
|
};
|
|
1344
1550
|
function identityLockPath(dir, identity) {
|
|
1345
|
-
return
|
|
1551
|
+
return path5.join(dir, `.${identity}.lock`);
|
|
1346
1552
|
}
|
|
1347
1553
|
function parseLockRecord(raw) {
|
|
1348
1554
|
if (raw === void 0) {
|
|
@@ -1420,8 +1626,8 @@ var FarmManifestSchema = z2.strictObject({
|
|
|
1420
1626
|
materialised: z2.array(z2.string()),
|
|
1421
1627
|
links: z2.array(z2.strictObject({ rel: z2.string(), target: z2.string() }))
|
|
1422
1628
|
});
|
|
1423
|
-
function readFarmManifest(
|
|
1424
|
-
const raw =
|
|
1629
|
+
function readFarmManifest(fs10, farmRoot) {
|
|
1630
|
+
const raw = fs10.readFileUtf8(path6.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs10.readFileUtf8(path6.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1425
1631
|
if (raw === void 0) {
|
|
1426
1632
|
return void 0;
|
|
1427
1633
|
}
|
|
@@ -1437,7 +1643,7 @@ function readFarmManifest(fs7, farmRoot) {
|
|
|
1437
1643
|
function buildEntryFacts(params) {
|
|
1438
1644
|
const entries = /* @__PURE__ */ new Map();
|
|
1439
1645
|
const visit = (rel) => {
|
|
1440
|
-
const absolute =
|
|
1646
|
+
const absolute = path6.join(params.claudeHome, rel);
|
|
1441
1647
|
const stat = params.fs.lstat(absolute);
|
|
1442
1648
|
if (stat === void 0) {
|
|
1443
1649
|
return void 0;
|
|
@@ -1479,9 +1685,9 @@ function buildEntryFacts(params) {
|
|
|
1479
1685
|
entries
|
|
1480
1686
|
};
|
|
1481
1687
|
}
|
|
1482
|
-
function listSubtree(
|
|
1483
|
-
const absolute =
|
|
1484
|
-
const stat =
|
|
1688
|
+
function listSubtree(fs10, root, rel) {
|
|
1689
|
+
const absolute = path6.join(root, rel);
|
|
1690
|
+
const stat = fs10.lstat(absolute);
|
|
1485
1691
|
if (stat === void 0) {
|
|
1486
1692
|
return [];
|
|
1487
1693
|
}
|
|
@@ -1489,10 +1695,10 @@ function listSubtree(fs7, root, rel) {
|
|
|
1489
1695
|
return [{ rel, kind: "symlink" }];
|
|
1490
1696
|
}
|
|
1491
1697
|
if (stat.kind === "dir") {
|
|
1492
|
-
const children = [...
|
|
1698
|
+
const children = [...fs10.readdir(absolute)].sort().flatMap((name) => listSubtree(fs10, root, `${rel}/${name}`));
|
|
1493
1699
|
return [{ rel, kind: "dir" }, ...children];
|
|
1494
1700
|
}
|
|
1495
|
-
const contentHash =
|
|
1701
|
+
const contentHash = fs10.hashFile(absolute);
|
|
1496
1702
|
return [{ rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } }];
|
|
1497
1703
|
}
|
|
1498
1704
|
function topLevelScopeRoots(roots) {
|
|
@@ -1506,25 +1712,25 @@ function topLevelScopeRoots(roots) {
|
|
|
1506
1712
|
}
|
|
1507
1713
|
return kept;
|
|
1508
1714
|
}
|
|
1509
|
-
function reconciliationScope(
|
|
1715
|
+
function reconciliationScope(fs10, farmRoot, manifest) {
|
|
1510
1716
|
if (manifest !== void 0) {
|
|
1511
1717
|
return topLevelScopeRoots(manifest.materialised);
|
|
1512
1718
|
}
|
|
1513
|
-
return [...
|
|
1719
|
+
return [...fs10.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs10.lstat(path6.join(farmRoot, name))?.kind === "dir").sort();
|
|
1514
1720
|
}
|
|
1515
|
-
function collectFarmListing(
|
|
1516
|
-
return scopeRoots.flatMap((root) => listSubtree(
|
|
1721
|
+
function collectFarmListing(fs10, farmRoot, scopeRoots) {
|
|
1722
|
+
return scopeRoots.flatMap((root) => listSubtree(fs10, farmRoot, root));
|
|
1517
1723
|
}
|
|
1518
|
-
function collectCanonicalCounterparts(
|
|
1724
|
+
function collectCanonicalCounterparts(fs10, claudeHome, farmListing) {
|
|
1519
1725
|
const out = [];
|
|
1520
1726
|
for (const entry of farmListing) {
|
|
1521
|
-
const absolute =
|
|
1522
|
-
const stat =
|
|
1727
|
+
const absolute = path6.join(claudeHome, entry.rel);
|
|
1728
|
+
const stat = fs10.lstat(absolute);
|
|
1523
1729
|
if (stat === void 0) {
|
|
1524
1730
|
continue;
|
|
1525
1731
|
}
|
|
1526
1732
|
if (stat.kind === "file") {
|
|
1527
|
-
const contentHash =
|
|
1733
|
+
const contentHash = fs10.hashFile(absolute);
|
|
1528
1734
|
out.push({ rel: entry.rel, kind: "file", ...contentHash === void 0 ? {} : { contentHash } });
|
|
1529
1735
|
continue;
|
|
1530
1736
|
}
|
|
@@ -1548,21 +1754,21 @@ function carryOver(params) {
|
|
|
1548
1754
|
if (accounted.has(name)) {
|
|
1549
1755
|
continue;
|
|
1550
1756
|
}
|
|
1551
|
-
const stat = params.fs.lstat(
|
|
1757
|
+
const stat = params.fs.lstat(path6.join(params.previousRoot, name));
|
|
1552
1758
|
if (stat === void 0 || stat.kind === "symlink") {
|
|
1553
1759
|
continue;
|
|
1554
1760
|
}
|
|
1555
|
-
if (params.fs.lstat(
|
|
1761
|
+
if (params.fs.lstat(path6.join(params.farmRoot, name)) !== void 0) {
|
|
1556
1762
|
const category = params.classification === void 0 ? void 0 : classifyEntries([name], params.classification).classification.get(name);
|
|
1557
1763
|
if (category === "runtime") {
|
|
1558
|
-
params.fs.removeRecursive(
|
|
1764
|
+
params.fs.removeRecursive(path6.join(params.previousRoot, name));
|
|
1559
1765
|
autoResolved.push(name);
|
|
1560
1766
|
continue;
|
|
1561
1767
|
}
|
|
1562
1768
|
collided.push(name);
|
|
1563
1769
|
continue;
|
|
1564
1770
|
}
|
|
1565
|
-
params.fs.rename(
|
|
1771
|
+
params.fs.rename(path6.join(params.previousRoot, name), path6.join(params.farmRoot, name));
|
|
1566
1772
|
carried.push(name);
|
|
1567
1773
|
}
|
|
1568
1774
|
return { carried, autoResolved, collided };
|
|
@@ -1570,9 +1776,9 @@ function carryOver(params) {
|
|
|
1570
1776
|
function buildScratchTree(params) {
|
|
1571
1777
|
params.fs.mkdirp(params.scratchRoot);
|
|
1572
1778
|
for (const entry of params.plan.entries) {
|
|
1573
|
-
const absolute =
|
|
1779
|
+
const absolute = path6.join(params.scratchRoot, entry.rel);
|
|
1574
1780
|
if (entry.kind === "link") {
|
|
1575
|
-
params.fs.mkdirp(
|
|
1781
|
+
params.fs.mkdirp(path6.dirname(absolute));
|
|
1576
1782
|
params.fs.symlink(entry.target, absolute);
|
|
1577
1783
|
continue;
|
|
1578
1784
|
}
|
|
@@ -1581,14 +1787,14 @@ function buildScratchTree(params) {
|
|
|
1581
1787
|
}
|
|
1582
1788
|
}
|
|
1583
1789
|
params.fs.writeFileUtf8(
|
|
1584
|
-
|
|
1790
|
+
path6.join(params.scratchRoot, FARM_MANIFEST_FILENAME),
|
|
1585
1791
|
`${JSON.stringify(params.manifest, null, 2)}
|
|
1586
1792
|
`
|
|
1587
1793
|
);
|
|
1588
1794
|
}
|
|
1589
1795
|
function swapIn(params) {
|
|
1590
1796
|
if (params.fs.lstat(params.farmRoot) === void 0) {
|
|
1591
|
-
params.fs.mkdirp(
|
|
1797
|
+
params.fs.mkdirp(path6.dirname(params.farmRoot));
|
|
1592
1798
|
params.fs.rename(params.scratchRoot, params.farmRoot);
|
|
1593
1799
|
return { carried: [], autoResolved: [], collided: [] };
|
|
1594
1800
|
}
|
|
@@ -1612,14 +1818,14 @@ function recoverInterruptedSwap(params) {
|
|
|
1612
1818
|
const names = [...params.fs.readdir(params.identitiesDir)].sort();
|
|
1613
1819
|
const removedScratch = [];
|
|
1614
1820
|
for (const name of names.filter((candidate) => candidate.startsWith(scratchPrefix))) {
|
|
1615
|
-
params.fs.removeRecursive(
|
|
1821
|
+
params.fs.removeRecursive(path6.join(params.identitiesDir, name));
|
|
1616
1822
|
removedScratch.push(name);
|
|
1617
1823
|
}
|
|
1618
1824
|
let previous = names.filter((candidate) => candidate.startsWith(previousPrefix));
|
|
1619
1825
|
let restoredFrom;
|
|
1620
1826
|
const first = previous[0];
|
|
1621
1827
|
if (first !== void 0 && params.fs.lstat(params.farmRoot) === void 0) {
|
|
1622
|
-
params.fs.rename(
|
|
1828
|
+
params.fs.rename(path6.join(params.identitiesDir, first), params.farmRoot);
|
|
1623
1829
|
restoredFrom = first;
|
|
1624
1830
|
previous = previous.slice(1);
|
|
1625
1831
|
}
|
|
@@ -1627,7 +1833,7 @@ function recoverInterruptedSwap(params) {
|
|
|
1627
1833
|
const autoResolved = [];
|
|
1628
1834
|
const retained = [];
|
|
1629
1835
|
for (const name of previous) {
|
|
1630
|
-
const previousRoot =
|
|
1836
|
+
const previousRoot = path6.join(params.identitiesDir, name);
|
|
1631
1837
|
const result = carryOver({
|
|
1632
1838
|
fs: params.fs,
|
|
1633
1839
|
previousRoot,
|
|
@@ -1652,7 +1858,7 @@ function recoverInterruptedSwap(params) {
|
|
|
1652
1858
|
};
|
|
1653
1859
|
}
|
|
1654
1860
|
function recoverFarm(params) {
|
|
1655
|
-
const farmRoot =
|
|
1861
|
+
const farmRoot = path6.join(params.identitiesDir, params.identity);
|
|
1656
1862
|
const lock = acquireIdentityLock({
|
|
1657
1863
|
identity: params.identity,
|
|
1658
1864
|
dir: params.identitiesDir,
|
|
@@ -1711,16 +1917,16 @@ function executeReconciliation(params) {
|
|
|
1711
1917
|
});
|
|
1712
1918
|
continue;
|
|
1713
1919
|
}
|
|
1714
|
-
const from =
|
|
1715
|
-
const canonical =
|
|
1920
|
+
const from = path6.join(params.farmRoot, action.rel);
|
|
1921
|
+
const canonical = path6.join(params.claudeHome, action.rel);
|
|
1716
1922
|
if (action.kind === "adopt") {
|
|
1717
|
-
params.fs.mkdirp(
|
|
1923
|
+
params.fs.mkdirp(path6.dirname(canonical));
|
|
1718
1924
|
params.fs.copyRecursive(from, canonical);
|
|
1719
1925
|
adopted.push(action.rel);
|
|
1720
1926
|
continue;
|
|
1721
1927
|
}
|
|
1722
1928
|
const preserved = `${canonical}.farm-conflict-${String(params.nowMs)}`;
|
|
1723
|
-
params.fs.mkdirp(
|
|
1929
|
+
params.fs.mkdirp(path6.dirname(canonical));
|
|
1724
1930
|
params.fs.copyRecursive(from, preserved);
|
|
1725
1931
|
conflicts.push(action.rel);
|
|
1726
1932
|
}
|
|
@@ -1745,7 +1951,7 @@ function farmAlreadyMatches(existing, next) {
|
|
|
1745
1951
|
return existing.identity === next.identity && existing.claudeHome === next.claudeHome && sameStrings(existing.materialised, next.materialised) && sameLinks(existing.links, next.links);
|
|
1746
1952
|
}
|
|
1747
1953
|
function resyncFarm(params) {
|
|
1748
|
-
const farmRoot =
|
|
1954
|
+
const farmRoot = path6.join(params.identitiesDir, params.identity);
|
|
1749
1955
|
const lock = acquireIdentityLock({
|
|
1750
1956
|
identity: params.identity,
|
|
1751
1957
|
dir: params.identitiesDir,
|
|
@@ -1824,8 +2030,8 @@ function resyncFarm(params) {
|
|
|
1824
2030
|
diagnostics
|
|
1825
2031
|
};
|
|
1826
2032
|
}
|
|
1827
|
-
const scratchRoot =
|
|
1828
|
-
const previousRoot =
|
|
2033
|
+
const scratchRoot = path6.join(params.identitiesDir, `.${params.identity}.scratch.${params.uniqueSuffix}`);
|
|
2034
|
+
const previousRoot = path6.join(params.identitiesDir, `.${params.identity}.previous.${params.uniqueSuffix}`);
|
|
1829
2035
|
buildScratchTree({ fs: params.fs, scratchRoot, plan: resolved.farm, manifest });
|
|
1830
2036
|
const swap = swapIn({ fs: params.fs, farmRoot, scratchRoot, previousRoot, classification: params.classification });
|
|
1831
2037
|
if (swap.retainedPrevious !== void 0) {
|
|
@@ -2229,7 +2435,7 @@ function capabilityFromProxyAuthorization(value) {
|
|
|
2229
2435
|
var AUTH_HEADER = "x-agent-shim-auth";
|
|
2230
2436
|
|
|
2231
2437
|
// src/frontdoor/state.ts
|
|
2232
|
-
import
|
|
2438
|
+
import path7 from "node:path";
|
|
2233
2439
|
import { z as z3 } from "zod";
|
|
2234
2440
|
var FRONT_DOOR_PROTOCOL = 1;
|
|
2235
2441
|
var FrontDoorSessionSchema = z3.strictObject({ pid: z3.number().int().positive(), startedAt: z3.number(), token: z3.string().min(1) });
|
|
@@ -2263,10 +2469,10 @@ var FrontDoorStateSchema = z3.strictObject({
|
|
|
2263
2469
|
/** The last fatal error, kept across shutdowns so `frontdoor status` can explain a front door that is not running. */
|
|
2264
2470
|
lastError: z3.string().optional()
|
|
2265
2471
|
});
|
|
2266
|
-
function readFrontDoorState(
|
|
2472
|
+
function readFrontDoorState(fs10, stateFile) {
|
|
2267
2473
|
let raw;
|
|
2268
2474
|
try {
|
|
2269
|
-
raw =
|
|
2475
|
+
raw = fs10.readFileUtf8(stateFile);
|
|
2270
2476
|
} catch {
|
|
2271
2477
|
return void 0;
|
|
2272
2478
|
}
|
|
@@ -2280,28 +2486,28 @@ function readFrontDoorState(fs7, stateFile) {
|
|
|
2280
2486
|
return void 0;
|
|
2281
2487
|
}
|
|
2282
2488
|
}
|
|
2283
|
-
function writeFrontDoorState(
|
|
2284
|
-
|
|
2285
|
-
|
|
2489
|
+
function writeFrontDoorState(fs10, stateFile, state) {
|
|
2490
|
+
fs10.mkdirp(path7.dirname(stateFile));
|
|
2491
|
+
fs10.writeFileUtf8(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2286
2492
|
`);
|
|
2287
2493
|
}
|
|
2288
2494
|
function frontDoorOrigin(state) {
|
|
2289
2495
|
const port = state?.directPort ?? state?.lastDirectPort;
|
|
2290
2496
|
return port === void 0 ? void 0 : `http://127.0.0.1:${String(port)}`;
|
|
2291
2497
|
}
|
|
2292
|
-
function writeFrontDoorSession(
|
|
2293
|
-
|
|
2294
|
-
|
|
2498
|
+
function writeFrontDoorSession(fs10, sessionsDir, session) {
|
|
2499
|
+
fs10.mkdirPrivate(sessionsDir);
|
|
2500
|
+
fs10.writeFilePrivate(path7.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2295
2501
|
`);
|
|
2296
2502
|
}
|
|
2297
|
-
function listFrontDoorSessions(
|
|
2503
|
+
function listFrontDoorSessions(fs10, sessionsDir) {
|
|
2298
2504
|
const sessions = [];
|
|
2299
|
-
for (const name of
|
|
2505
|
+
for (const name of fs10.readdir(sessionsDir)) {
|
|
2300
2506
|
if (!name.endsWith(".json")) {
|
|
2301
2507
|
continue;
|
|
2302
2508
|
}
|
|
2303
2509
|
try {
|
|
2304
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(
|
|
2510
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs10.readFileUtf8(path7.join(sessionsDir, name)) ?? ""));
|
|
2305
2511
|
if (parsed.success) {
|
|
2306
2512
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2307
2513
|
}
|
|
@@ -2311,14 +2517,14 @@ function listFrontDoorSessions(fs7, sessionsDir) {
|
|
|
2311
2517
|
}
|
|
2312
2518
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2313
2519
|
}
|
|
2314
|
-
function removeFrontDoorSession(
|
|
2315
|
-
|
|
2520
|
+
function removeFrontDoorSession(fs10, sessionsDir, pid) {
|
|
2521
|
+
fs10.removeRecursive(path7.join(sessionsDir, `${String(pid)}.json`));
|
|
2316
2522
|
}
|
|
2317
|
-
function pruneDeadFrontDoorSessions(
|
|
2523
|
+
function pruneDeadFrontDoorSessions(fs10, sessionsDir, isRunning) {
|
|
2318
2524
|
const removed = [];
|
|
2319
|
-
for (const session of listFrontDoorSessions(
|
|
2525
|
+
for (const session of listFrontDoorSessions(fs10, sessionsDir)) {
|
|
2320
2526
|
if (!isRunning(session.pid)) {
|
|
2321
|
-
removeFrontDoorSession(
|
|
2527
|
+
removeFrontDoorSession(fs10, sessionsDir, session.pid);
|
|
2322
2528
|
removed.push(session.pid);
|
|
2323
2529
|
}
|
|
2324
2530
|
}
|
|
@@ -2330,8 +2536,8 @@ var FRONTDOOR_SUPERVISOR_STILL_RUNNING = -1;
|
|
|
2330
2536
|
var FRONTDOOR_POLL_MS = 1e3;
|
|
2331
2537
|
var MS_PER_MINUTE = 6e4;
|
|
2332
2538
|
async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {}) {
|
|
2333
|
-
const { fs:
|
|
2334
|
-
const previous = readFrontDoorState(
|
|
2539
|
+
const { fs: fs10, paths } = ports;
|
|
2540
|
+
const previous = readFrontDoorState(fs10, paths.frontdoorStateFile);
|
|
2335
2541
|
let lastPort = previous?.lastPort;
|
|
2336
2542
|
let lastConnectPort = previous?.lastConnectPort;
|
|
2337
2543
|
let lastDirectPort = previous?.lastDirectPort;
|
|
@@ -2341,12 +2547,12 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2341
2547
|
...lastDirectPort === void 0 ? {} : { lastDirectPort }
|
|
2342
2548
|
});
|
|
2343
2549
|
const fail = (message) => {
|
|
2344
|
-
writeFrontDoorState(
|
|
2345
|
-
|
|
2550
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, { lastError: message, ...sticky() });
|
|
2551
|
+
fs10.removeRecursive(paths.frontdoorLockFile);
|
|
2346
2552
|
ports.log(`agent-shim frontdoor supervisor: ${message}`);
|
|
2347
2553
|
return 1;
|
|
2348
2554
|
};
|
|
2349
|
-
writeFrontDoorState(
|
|
2555
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
|
|
2350
2556
|
const previousSticky = lastPort;
|
|
2351
2557
|
let http2;
|
|
2352
2558
|
try {
|
|
@@ -2383,8 +2589,8 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2383
2589
|
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)`);
|
|
2384
2590
|
}
|
|
2385
2591
|
lastDirectPort = direct.port;
|
|
2386
|
-
writeFrontDoorState(
|
|
2387
|
-
|
|
2592
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http2.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
|
|
2593
|
+
fs10.removeRecursive(paths.frontdoorLockFile);
|
|
2388
2594
|
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)}`);
|
|
2389
2595
|
let idleSince;
|
|
2390
2596
|
let ticks = 0;
|
|
@@ -2393,15 +2599,15 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
|
|
|
2393
2599
|
return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
|
|
2394
2600
|
}
|
|
2395
2601
|
ticks += 1;
|
|
2396
|
-
pruneDeadFrontDoorSessions(
|
|
2397
|
-
if (listFrontDoorSessions(
|
|
2602
|
+
pruneDeadFrontDoorSessions(fs10, paths.frontdoorSessionsDir, ports.isRunning);
|
|
2603
|
+
if (listFrontDoorSessions(fs10, paths.frontdoorSessionsDir).length === 0) {
|
|
2398
2604
|
idleSince ??= ports.now();
|
|
2399
2605
|
if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE) {
|
|
2400
2606
|
ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
|
|
2401
2607
|
await direct.close();
|
|
2402
2608
|
await connect3.close();
|
|
2403
2609
|
await http2.close();
|
|
2404
|
-
writeFrontDoorState(
|
|
2610
|
+
writeFrontDoorState(fs10, paths.frontdoorStateFile, sticky());
|
|
2405
2611
|
return 0;
|
|
2406
2612
|
}
|
|
2407
2613
|
} else {
|
|
@@ -2456,8 +2662,8 @@ function parseConnectTarget(authority) {
|
|
|
2456
2662
|
function isInterceptedHost(host, interceptHosts) {
|
|
2457
2663
|
return interceptHosts.includes(host);
|
|
2458
2664
|
}
|
|
2459
|
-
function servedByPipeline(
|
|
2460
|
-
return
|
|
2665
|
+
function servedByPipeline(path23) {
|
|
2666
|
+
return path23?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2461
2667
|
}
|
|
2462
2668
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2463
2669
|
"connection",
|
|
@@ -2952,7 +3158,7 @@ import { randomUUID as randomUUID3 } from "node:crypto";
|
|
|
2952
3158
|
|
|
2953
3159
|
// src/headroom/state.ts
|
|
2954
3160
|
import { createHash as createHash2 } from "node:crypto";
|
|
2955
|
-
import
|
|
3161
|
+
import path8 from "node:path";
|
|
2956
3162
|
import { z as z4 } from "zod";
|
|
2957
3163
|
var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
|
|
2958
3164
|
var HeadroomStateSchema = z4.strictObject({
|
|
@@ -2986,16 +3192,16 @@ function parseJson(raw, schema) {
|
|
|
2986
3192
|
const parsed = schema.safeParse(JSON.parse(raw));
|
|
2987
3193
|
return parsed.success ? parsed.data : void 0;
|
|
2988
3194
|
}
|
|
2989
|
-
function readHeadroomState(
|
|
3195
|
+
function readHeadroomState(fs10, stateFile) {
|
|
2990
3196
|
try {
|
|
2991
|
-
return parseJson(
|
|
3197
|
+
return parseJson(fs10.readFileUtf8(stateFile), HeadroomStateSchema);
|
|
2992
3198
|
} catch {
|
|
2993
3199
|
return void 0;
|
|
2994
3200
|
}
|
|
2995
3201
|
}
|
|
2996
|
-
function writeHeadroomState(
|
|
2997
|
-
|
|
2998
|
-
|
|
3202
|
+
function writeHeadroomState(fs10, stateFile, state) {
|
|
3203
|
+
fs10.mkdirp(path8.dirname(stateFile));
|
|
3204
|
+
fs10.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2999
3205
|
`);
|
|
3000
3206
|
}
|
|
3001
3207
|
function headroomAllowlist(providers) {
|
|
@@ -3015,14 +3221,14 @@ function headroomUpstreams(providers, frontDoorOrigin2) {
|
|
|
3015
3221
|
function hashAllowlist(allowlist) {
|
|
3016
3222
|
return createHash2("sha256").update(allowlist.join("\n")).digest("hex");
|
|
3017
3223
|
}
|
|
3018
|
-
function readAllProviders(
|
|
3224
|
+
function readAllProviders(fs10, providersDir) {
|
|
3019
3225
|
const result = [];
|
|
3020
|
-
for (const name of
|
|
3226
|
+
for (const name of fs10.readdir(providersDir)) {
|
|
3021
3227
|
if (!name.endsWith(".json")) {
|
|
3022
3228
|
continue;
|
|
3023
3229
|
}
|
|
3024
3230
|
try {
|
|
3025
|
-
const parsed = parseJson(
|
|
3231
|
+
const parsed = parseJson(fs10.readFileUtf8(path8.join(providersDir, name)), ProviderSchema);
|
|
3026
3232
|
if (parsed !== void 0) {
|
|
3027
3233
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
3028
3234
|
}
|
|
@@ -3032,22 +3238,22 @@ function readAllProviders(fs7, providersDir) {
|
|
|
3032
3238
|
}
|
|
3033
3239
|
return result;
|
|
3034
3240
|
}
|
|
3035
|
-
function writeSession(
|
|
3036
|
-
|
|
3037
|
-
|
|
3241
|
+
function writeSession(fs10, sessionsDir, session) {
|
|
3242
|
+
fs10.mkdirp(sessionsDir);
|
|
3243
|
+
fs10.writeFileUtf8(path8.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
3038
3244
|
`);
|
|
3039
3245
|
}
|
|
3040
|
-
function removeSession(
|
|
3041
|
-
|
|
3246
|
+
function removeSession(fs10, sessionsDir, pid) {
|
|
3247
|
+
fs10.removeRecursive(path8.join(sessionsDir, `${String(pid)}.json`));
|
|
3042
3248
|
}
|
|
3043
|
-
function listSessions(
|
|
3249
|
+
function listSessions(fs10, sessionsDir) {
|
|
3044
3250
|
const sessions = [];
|
|
3045
|
-
for (const name of
|
|
3251
|
+
for (const name of fs10.readdir(sessionsDir)) {
|
|
3046
3252
|
if (!name.endsWith(".json")) {
|
|
3047
3253
|
continue;
|
|
3048
3254
|
}
|
|
3049
3255
|
try {
|
|
3050
|
-
const parsed = parseJson(
|
|
3256
|
+
const parsed = parseJson(fs10.readFileUtf8(path8.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
3051
3257
|
if (parsed !== void 0) {
|
|
3052
3258
|
sessions.push(parsed);
|
|
3053
3259
|
}
|
|
@@ -3057,22 +3263,22 @@ function listSessions(fs7, sessionsDir) {
|
|
|
3057
3263
|
}
|
|
3058
3264
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
3059
3265
|
}
|
|
3060
|
-
function sessionsForSupervisor(
|
|
3061
|
-
return listSessions(
|
|
3266
|
+
function sessionsForSupervisor(fs10, sessionsDir, supervisorPid) {
|
|
3267
|
+
return listSessions(fs10, sessionsDir).filter((session) => session.supervisorPid === supervisorPid);
|
|
3062
3268
|
}
|
|
3063
|
-
function pruneDeadSessions(
|
|
3269
|
+
function pruneDeadSessions(fs10, sessionsDir, isRunning) {
|
|
3064
3270
|
const removed = [];
|
|
3065
|
-
for (const session of listSessions(
|
|
3271
|
+
for (const session of listSessions(fs10, sessionsDir)) {
|
|
3066
3272
|
if (!isRunning(session.pid)) {
|
|
3067
|
-
removeSession(
|
|
3273
|
+
removeSession(fs10, sessionsDir, session.pid);
|
|
3068
3274
|
removed.push(session.pid);
|
|
3069
3275
|
}
|
|
3070
3276
|
}
|
|
3071
3277
|
return removed;
|
|
3072
3278
|
}
|
|
3073
|
-
function readStartLock(
|
|
3279
|
+
function readStartLock(fs10, lockFile) {
|
|
3074
3280
|
try {
|
|
3075
|
-
return parseJson(
|
|
3281
|
+
return parseJson(fs10.readFileUtf8(lockFile), HeadroomLockSchema);
|
|
3076
3282
|
} catch {
|
|
3077
3283
|
return void 0;
|
|
3078
3284
|
}
|
|
@@ -3152,7 +3358,10 @@ function ensureFrontDoor(params) {
|
|
|
3152
3358
|
}
|
|
3153
3359
|
|
|
3154
3360
|
// src/usage/read.ts
|
|
3155
|
-
import
|
|
3361
|
+
import path10 from "node:path";
|
|
3362
|
+
|
|
3363
|
+
// src/usage/account.ts
|
|
3364
|
+
import path9 from "node:path";
|
|
3156
3365
|
|
|
3157
3366
|
// src/usage/schema.ts
|
|
3158
3367
|
import { z as z5 } from "zod";
|
|
@@ -3302,11 +3511,111 @@ var UsageSnapshotSchema = z5.strictObject({
|
|
|
3302
3511
|
});
|
|
3303
3512
|
|
|
3304
3513
|
// src/usage/account.ts
|
|
3514
|
+
var CLAUDE_JSON = ".claude.json";
|
|
3515
|
+
var AccountMetadataError = class extends CliError {
|
|
3516
|
+
constructor(file, detail) {
|
|
3517
|
+
super(`${file}: ${detail}, so its account metadata cannot be read.`);
|
|
3518
|
+
this.file = file;
|
|
3519
|
+
this.name = "AccountMetadataError";
|
|
3520
|
+
}
|
|
3521
|
+
file;
|
|
3522
|
+
};
|
|
3523
|
+
function isObject(value) {
|
|
3524
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3525
|
+
}
|
|
3305
3526
|
function isIdentityName(name) {
|
|
3306
3527
|
return IdentitySchema.shape.name.safeParse(name).success;
|
|
3307
3528
|
}
|
|
3529
|
+
function claudeJsonPath(identitiesDir, identity) {
|
|
3530
|
+
return path9.join(identitiesDir, identity, CLAUDE_JSON);
|
|
3531
|
+
}
|
|
3532
|
+
function readAccountMetadata(fs10, identitiesDir, identity) {
|
|
3533
|
+
if (!isIdentityName(identity)) {
|
|
3534
|
+
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3535
|
+
}
|
|
3536
|
+
const file = claudeJsonPath(identitiesDir, identity);
|
|
3537
|
+
const raw = fs10.readFileUtf8(file);
|
|
3538
|
+
if (raw === void 0) {
|
|
3539
|
+
return void 0;
|
|
3540
|
+
}
|
|
3541
|
+
let parsed;
|
|
3542
|
+
try {
|
|
3543
|
+
parsed = JSON.parse(raw);
|
|
3544
|
+
} catch {
|
|
3545
|
+
throw new AccountMetadataError(file, "not valid JSON");
|
|
3546
|
+
}
|
|
3547
|
+
if (!isObject(parsed)) {
|
|
3548
|
+
throw new AccountMetadataError(file, "not a JSON object");
|
|
3549
|
+
}
|
|
3550
|
+
const oauthAccount = parsed.oauthAccount;
|
|
3551
|
+
if (!isObject(oauthAccount)) {
|
|
3552
|
+
return void 0;
|
|
3553
|
+
}
|
|
3554
|
+
const picked = {};
|
|
3555
|
+
for (const key of Object.keys(AccountMetadataSchema.shape)) {
|
|
3556
|
+
const value = oauthAccount[key];
|
|
3557
|
+
if (value !== null && value !== void 0) {
|
|
3558
|
+
picked[key] = value;
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
const account = AccountMetadataSchema.safeParse(picked);
|
|
3562
|
+
if (!account.success) {
|
|
3563
|
+
throw new AccountMetadataError(file, account.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; "));
|
|
3564
|
+
}
|
|
3565
|
+
return account.data;
|
|
3566
|
+
}
|
|
3308
3567
|
|
|
3309
3568
|
// src/usage/read.ts
|
|
3569
|
+
var SEGMENT_RE = /^(\d{4}-\d{2}-\d{2})\.(\d+)\.jsonl$/;
|
|
3570
|
+
var DAY_MS = 864e5;
|
|
3571
|
+
function listLogSegments(fs10, logDir) {
|
|
3572
|
+
return fs10.readdir(logDir).flatMap((name) => {
|
|
3573
|
+
const match = SEGMENT_RE.exec(name);
|
|
3574
|
+
const day = match?.[1];
|
|
3575
|
+
if (day === void 0) {
|
|
3576
|
+
return [];
|
|
3577
|
+
}
|
|
3578
|
+
return [{ name, day, endsAt: Date.parse(`${day}T00:00:00.000Z`) + DAY_MS }];
|
|
3579
|
+
}).sort((left, right) => left.day.localeCompare(right.day) || left.name.localeCompare(right.name));
|
|
3580
|
+
}
|
|
3581
|
+
function readUsageLog(fs10, logDir, options = {}) {
|
|
3582
|
+
const { sinceMs } = options;
|
|
3583
|
+
const records = [];
|
|
3584
|
+
let invalidLines = 0;
|
|
3585
|
+
for (const segment of listLogSegments(fs10, logDir)) {
|
|
3586
|
+
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
3587
|
+
continue;
|
|
3588
|
+
}
|
|
3589
|
+
const contents = fs10.readFileUtf8(path10.join(logDir, segment.name));
|
|
3590
|
+
if (contents === void 0) {
|
|
3591
|
+
continue;
|
|
3592
|
+
}
|
|
3593
|
+
const lines = contents.split("\n");
|
|
3594
|
+
lines.pop();
|
|
3595
|
+
for (const line of lines) {
|
|
3596
|
+
if (line === "") {
|
|
3597
|
+
continue;
|
|
3598
|
+
}
|
|
3599
|
+
let raw;
|
|
3600
|
+
try {
|
|
3601
|
+
raw = JSON.parse(line);
|
|
3602
|
+
} catch {
|
|
3603
|
+
invalidLines += 1;
|
|
3604
|
+
continue;
|
|
3605
|
+
}
|
|
3606
|
+
const parsed = UsageRecordSchema.safeParse(raw);
|
|
3607
|
+
if (!parsed.success) {
|
|
3608
|
+
invalidLines += 1;
|
|
3609
|
+
continue;
|
|
3610
|
+
}
|
|
3611
|
+
if (sinceMs === void 0 || Date.parse(parsed.data.at) >= sinceMs) {
|
|
3612
|
+
records.push(parsed.data);
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
records.sort((left, right) => left.at.localeCompare(right.at));
|
|
3617
|
+
return { records, invalidLines };
|
|
3618
|
+
}
|
|
3310
3619
|
var UsageSnapshotError = class extends Error {
|
|
3311
3620
|
constructor(file, detail) {
|
|
3312
3621
|
super(`${file} is not a usage snapshot this agent-shim can read (schema version ${String(USAGE_SCHEMA_VERSION)}): ${detail}`);
|
|
@@ -3319,11 +3628,11 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
3319
3628
|
if (!isIdentityName(identity)) {
|
|
3320
3629
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
3321
3630
|
}
|
|
3322
|
-
return
|
|
3631
|
+
return path10.join(snapshotsDir, `${identity}.json`);
|
|
3323
3632
|
}
|
|
3324
|
-
function readUsageSnapshot(
|
|
3633
|
+
function readUsageSnapshot(fs10, snapshotsDir, identity) {
|
|
3325
3634
|
const file = snapshotPath(snapshotsDir, identity);
|
|
3326
|
-
const raw =
|
|
3635
|
+
const raw = fs10.readFileUtf8(file);
|
|
3327
3636
|
if (raw === void 0) {
|
|
3328
3637
|
return void 0;
|
|
3329
3638
|
}
|
|
@@ -3339,8 +3648,8 @@ function readUsageSnapshot(fs7, snapshotsDir, identity) {
|
|
|
3339
3648
|
}
|
|
3340
3649
|
return snapshot.data;
|
|
3341
3650
|
}
|
|
3342
|
-
function listUsageSnapshots(
|
|
3343
|
-
return
|
|
3651
|
+
function listUsageSnapshots(fs10, snapshotsDir) {
|
|
3652
|
+
return fs10.readdir(snapshotsDir).flatMap((name) => {
|
|
3344
3653
|
if (!name.endsWith(".json")) {
|
|
3345
3654
|
return [];
|
|
3346
3655
|
}
|
|
@@ -3348,7 +3657,7 @@ function listUsageSnapshots(fs7, snapshotsDir) {
|
|
|
3348
3657
|
if (!isIdentityName(identity)) {
|
|
3349
3658
|
return [];
|
|
3350
3659
|
}
|
|
3351
|
-
const snapshot = readUsageSnapshot(
|
|
3660
|
+
const snapshot = readUsageSnapshot(fs10, snapshotsDir, identity);
|
|
3352
3661
|
return snapshot === void 0 ? [] : [snapshot];
|
|
3353
3662
|
}).sort((left, right) => left.identity.localeCompare(right.identity));
|
|
3354
3663
|
}
|
|
@@ -3441,6 +3750,14 @@ import { z as z6 } from "zod";
|
|
|
3441
3750
|
var GIT_SPEC_RE = /git\+[^@\s]+(?:@([^\s#]+))?/;
|
|
3442
3751
|
var COMMIT_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
3443
3752
|
var DirectUrlSchema = z6.object({ vcs_info: z6.object({ commit_id: z6.string() }) });
|
|
3753
|
+
function isMovingGitSource(spec) {
|
|
3754
|
+
const match = GIT_SPEC_RE.exec(spec);
|
|
3755
|
+
if (match === null) {
|
|
3756
|
+
return false;
|
|
3757
|
+
}
|
|
3758
|
+
const ref = match[1];
|
|
3759
|
+
return ref === void 0 || !COMMIT_SHA_RE.test(ref);
|
|
3760
|
+
}
|
|
3444
3761
|
function pinnedGitCommit(spec) {
|
|
3445
3762
|
const ref = GIT_SPEC_RE.exec(spec)?.[1];
|
|
3446
3763
|
return ref !== void 0 && COMMIT_SHA_RE.test(ref) ? ref.toLowerCase() : void 0;
|
|
@@ -3515,9 +3832,9 @@ function compareVersions(a, b) {
|
|
|
3515
3832
|
}
|
|
3516
3833
|
var HEADROOM_SUPERVISOR_STILL_RUNNING = -1;
|
|
3517
3834
|
async function runSupervisor(initialConfig, ports, options = {}) {
|
|
3518
|
-
const { fs:
|
|
3835
|
+
const { fs: fs10, paths } = ports;
|
|
3519
3836
|
let config = initialConfig;
|
|
3520
|
-
const previousState = readHeadroomState(
|
|
3837
|
+
const previousState = readHeadroomState(fs10, paths.headroomStateFile);
|
|
3521
3838
|
if (previousState?.supervisorPid !== void 0 && previousState.supervisorPid !== ports.ownPid && ports.isRunning(previousState.supervisorPid)) {
|
|
3522
3839
|
ports.log(`agent-shim headroom supervisor ${String(ports.ownPid)}: supervisor ${String(previousState.supervisorPid)} already owns the daemon; exiting`);
|
|
3523
3840
|
return 0;
|
|
@@ -3526,19 +3843,19 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3526
3843
|
let lastPort = previousState?.lastPort;
|
|
3527
3844
|
const sticky = () => lastPort === void 0 ? {} : { lastPort };
|
|
3528
3845
|
const ownsState = () => {
|
|
3529
|
-
const owner = readHeadroomState(
|
|
3846
|
+
const owner = readHeadroomState(fs10, paths.headroomStateFile)?.supervisorPid;
|
|
3530
3847
|
return owner === void 0 || owner === ports.ownPid || !ports.isRunning(owner);
|
|
3531
3848
|
};
|
|
3532
3849
|
const writeOwnState = (state) => {
|
|
3533
3850
|
if (!ownsState()) {
|
|
3534
3851
|
return false;
|
|
3535
3852
|
}
|
|
3536
|
-
writeHeadroomState(
|
|
3853
|
+
writeHeadroomState(fs10, paths.headroomStateFile, state);
|
|
3537
3854
|
return true;
|
|
3538
3855
|
};
|
|
3539
3856
|
const fail = (message) => {
|
|
3540
3857
|
if (writeOwnState({ lastError: message, ...sticky() })) {
|
|
3541
|
-
|
|
3858
|
+
fs10.removeRecursive(paths.headroomLockFile);
|
|
3542
3859
|
}
|
|
3543
3860
|
ports.log(`agent-shim headroom supervisor: ${message}`);
|
|
3544
3861
|
return 1;
|
|
@@ -3587,7 +3904,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3587
3904
|
const settingsHash = hashSettings(config.settings);
|
|
3588
3905
|
const allowlist = allowlistOf(ports);
|
|
3589
3906
|
const allowlistHash = hashAllowlist(allowlist);
|
|
3590
|
-
pruneDeadSessions(
|
|
3907
|
+
pruneDeadSessions(fs10, paths.headroomSessionsDir, ports.isRunning);
|
|
3591
3908
|
const ownsNow = ownsState();
|
|
3592
3909
|
if (!ownsNow && !superseded) {
|
|
3593
3910
|
ports.log("agent-shim headroom supervisor: another supervisor now owns state.json; serving this daemon's own sessions until they end");
|
|
@@ -3639,7 +3956,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3639
3956
|
...sticky()
|
|
3640
3957
|
});
|
|
3641
3958
|
if (recorded) {
|
|
3642
|
-
|
|
3959
|
+
fs10.removeRecursive(paths.headroomLockFile);
|
|
3643
3960
|
}
|
|
3644
3961
|
idleSince = void 0;
|
|
3645
3962
|
ports.log(`agent-shim headroom supervisor: headroom pid ${String(pid)} ready on 127.0.0.1:${String(port)}`);
|
|
@@ -3654,7 +3971,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3654
3971
|
}
|
|
3655
3972
|
} else {
|
|
3656
3973
|
if (runningHash !== allowlistHash || runningSettingsHash !== settingsHash || installedSource !== config.source) {
|
|
3657
|
-
if (sessionsForSupervisor(
|
|
3974
|
+
if (sessionsForSupervisor(fs10, paths.headroomSessionsDir, ports.ownPid).length === 0) {
|
|
3658
3975
|
ports.log("agent-shim headroom supervisor: configuration drifted and no sessions are live; restarting headroom");
|
|
3659
3976
|
ports.stopProcess(headroomPid);
|
|
3660
3977
|
headroomPid = void 0;
|
|
@@ -3663,7 +3980,7 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3663
3980
|
continue;
|
|
3664
3981
|
}
|
|
3665
3982
|
}
|
|
3666
|
-
const sessions = sessionsForSupervisor(
|
|
3983
|
+
const sessions = sessionsForSupervisor(fs10, paths.headroomSessionsDir, ports.ownPid);
|
|
3667
3984
|
if (sessions.length === 0) {
|
|
3668
3985
|
idleSince ??= ports.now();
|
|
3669
3986
|
const idleMs = ports.now() - idleSince;
|
|
@@ -3685,9 +4002,9 @@ async function runSupervisor(initialConfig, ports, options = {}) {
|
|
|
3685
4002
|
await ports.sleep(HEADROOM_POLL_MS);
|
|
3686
4003
|
}
|
|
3687
4004
|
}
|
|
3688
|
-
function currentHeadroomAllowlist(
|
|
3689
|
-
const providers = readAllProviders(
|
|
3690
|
-
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(
|
|
4005
|
+
function currentHeadroomAllowlist(fs10, paths) {
|
|
4006
|
+
const providers = readAllProviders(fs10, paths.providersDir).map((entry) => entry.provider);
|
|
4007
|
+
return headroomAllowlist(headroomUpstreams(providers, frontDoorOrigin(readFrontDoorState(fs10, paths.frontdoorStateFile))));
|
|
3691
4008
|
}
|
|
3692
4009
|
function allowlistOf(ports) {
|
|
3693
4010
|
return currentHeadroomAllowlist(ports.fs, ports.paths);
|
|
@@ -3755,14 +4072,14 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
3755
4072
|
// src/paths.ts
|
|
3756
4073
|
import fs3 from "node:fs";
|
|
3757
4074
|
import os from "node:os";
|
|
3758
|
-
import
|
|
4075
|
+
import path11 from "node:path";
|
|
3759
4076
|
function resolveAgentShimHome(env = process.env, home = os.homedir(), isDirectory = isExistingDirectory) {
|
|
3760
4077
|
const fromEnv = env.AGENT_SHIM_HOME;
|
|
3761
4078
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
3762
4079
|
return fromEnv;
|
|
3763
4080
|
}
|
|
3764
|
-
const current =
|
|
3765
|
-
const legacy =
|
|
4081
|
+
const current = path11.join(home, HOME_DIRNAME);
|
|
4082
|
+
const legacy = path11.join(home, LEGACY_HOME_DIRNAME);
|
|
3766
4083
|
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
3767
4084
|
}
|
|
3768
4085
|
function isExistingDirectory(candidate) {
|
|
@@ -3773,38 +4090,38 @@ function resolveClaudeHome() {
|
|
|
3773
4090
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
3774
4091
|
return fromEnv;
|
|
3775
4092
|
}
|
|
3776
|
-
return
|
|
4093
|
+
return path11.join(os.homedir(), ".claude");
|
|
3777
4094
|
}
|
|
3778
4095
|
function buildLayoutPaths(root) {
|
|
3779
4096
|
return {
|
|
3780
4097
|
root,
|
|
3781
|
-
identitiesDir:
|
|
3782
|
-
configProfilesDir:
|
|
3783
|
-
providersDir:
|
|
3784
|
-
directoryRulesFile:
|
|
3785
|
-
activeIdentityFile:
|
|
3786
|
-
globalConfigFile:
|
|
3787
|
-
categoriesLocalFile:
|
|
3788
|
-
claudeShimFile:
|
|
3789
|
-
headroomDir:
|
|
3790
|
-
headroomStateFile:
|
|
3791
|
-
headroomLockFile:
|
|
3792
|
-
headroomSessionsDir:
|
|
3793
|
-
logsDir:
|
|
3794
|
-
headroomLogPath:
|
|
3795
|
-
frontdoorDir:
|
|
3796
|
-
frontdoorCaDir:
|
|
3797
|
-
frontdoorCaCertFile:
|
|
3798
|
-
frontdoorCaKeyFile:
|
|
3799
|
-
frontdoorCaBundlesDir:
|
|
3800
|
-
frontdoorStateFile:
|
|
3801
|
-
frontdoorLockFile:
|
|
3802
|
-
frontdoorSessionsDir:
|
|
3803
|
-
frontdoorLogPath:
|
|
3804
|
-
usageDir:
|
|
3805
|
-
usageLogDir:
|
|
3806
|
-
usageSnapshotsDir:
|
|
3807
|
-
usagePicksFile:
|
|
4098
|
+
identitiesDir: path11.join(root, "identities"),
|
|
4099
|
+
configProfilesDir: path11.join(root, "config-profiles"),
|
|
4100
|
+
providersDir: path11.join(root, "providers"),
|
|
4101
|
+
directoryRulesFile: path11.join(root, "directory-rules.json"),
|
|
4102
|
+
activeIdentityFile: path11.join(root, "active-identity"),
|
|
4103
|
+
globalConfigFile: path11.join(root, "config.json"),
|
|
4104
|
+
categoriesLocalFile: path11.join(root, "categories.local.json"),
|
|
4105
|
+
claudeShimFile: path11.join(root, "claude-shim.json"),
|
|
4106
|
+
headroomDir: path11.join(root, "headroom"),
|
|
4107
|
+
headroomStateFile: path11.join(root, "headroom", "state.json"),
|
|
4108
|
+
headroomLockFile: path11.join(root, "headroom", "start.lock"),
|
|
4109
|
+
headroomSessionsDir: path11.join(root, "headroom", "sessions"),
|
|
4110
|
+
logsDir: path11.join(root, "logs"),
|
|
4111
|
+
headroomLogPath: path11.join(root, "logs", "headroom.log"),
|
|
4112
|
+
frontdoorDir: path11.join(root, "frontdoor"),
|
|
4113
|
+
frontdoorCaDir: path11.join(root, "frontdoor", "ca"),
|
|
4114
|
+
frontdoorCaCertFile: path11.join(root, "frontdoor", "ca", "ca.pem"),
|
|
4115
|
+
frontdoorCaKeyFile: path11.join(root, "frontdoor", "ca", "ca.key"),
|
|
4116
|
+
frontdoorCaBundlesDir: path11.join(root, "frontdoor", "ca", "bundles"),
|
|
4117
|
+
frontdoorStateFile: path11.join(root, "frontdoor", "state.json"),
|
|
4118
|
+
frontdoorLockFile: path11.join(root, "frontdoor", "start.lock"),
|
|
4119
|
+
frontdoorSessionsDir: path11.join(root, "frontdoor", "sessions"),
|
|
4120
|
+
frontdoorLogPath: path11.join(root, "logs", "frontdoor.log"),
|
|
4121
|
+
usageDir: path11.join(root, "usage"),
|
|
4122
|
+
usageLogDir: path11.join(root, "usage", "log"),
|
|
4123
|
+
usageSnapshotsDir: path11.join(root, "usage", "snapshots"),
|
|
4124
|
+
usagePicksFile: path11.join(root, "usage", "picks.json")
|
|
3808
4125
|
};
|
|
3809
4126
|
}
|
|
3810
4127
|
function resolveLayoutPaths() {
|
|
@@ -3813,16 +4130,64 @@ function resolveLayoutPaths() {
|
|
|
3813
4130
|
|
|
3814
4131
|
// src/identityStore.ts
|
|
3815
4132
|
import fs5 from "node:fs";
|
|
3816
|
-
import
|
|
4133
|
+
import path14 from "node:path";
|
|
3817
4134
|
|
|
3818
4135
|
// src/launcher/identity.ts
|
|
4136
|
+
import path12 from "node:path";
|
|
3819
4137
|
function poolNameOf(selector) {
|
|
3820
4138
|
return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
|
|
3821
4139
|
}
|
|
4140
|
+
function isNonEmpty(value) {
|
|
4141
|
+
return value !== void 0 && value !== "";
|
|
4142
|
+
}
|
|
4143
|
+
function selected(selector, source) {
|
|
4144
|
+
const pool = poolNameOf(selector);
|
|
4145
|
+
return pool === void 0 ? { name: selector, source, configDirEscapeHatch: false } : { pool, source, configDirEscapeHatch: false };
|
|
4146
|
+
}
|
|
4147
|
+
function decideIdentity(params) {
|
|
4148
|
+
if (isNonEmpty(params.env.CLAUDE_CONFIG_DIR)) {
|
|
4149
|
+
return { source: "config-dir-escape-hatch", configDirEscapeHatch: true };
|
|
4150
|
+
}
|
|
4151
|
+
if (isNonEmpty(params.argv0Identity)) {
|
|
4152
|
+
return selected(params.argv0Identity, "argv");
|
|
4153
|
+
}
|
|
4154
|
+
if (isNonEmpty(params.env.AGENT_SHIM_IDENTITY)) {
|
|
4155
|
+
return selected(params.env.AGENT_SHIM_IDENTITY, "env");
|
|
4156
|
+
}
|
|
4157
|
+
if (isNonEmpty(params.directoryPinnedIdentity)) {
|
|
4158
|
+
return selected(params.directoryPinnedIdentity, "directory-pin");
|
|
4159
|
+
}
|
|
4160
|
+
const persisted = params.readActiveIdentityFile();
|
|
4161
|
+
if (isNonEmpty(persisted)) {
|
|
4162
|
+
return selected(persisted, "active-identity-file");
|
|
4163
|
+
}
|
|
4164
|
+
return { source: "none", configDirEscapeHatch: false };
|
|
4165
|
+
}
|
|
4166
|
+
function decideConfigProfile(params) {
|
|
4167
|
+
if (isNonEmpty(params.cliFlagConfigProfile)) {
|
|
4168
|
+
return { name: params.cliFlagConfigProfile, source: "cli-flag" };
|
|
4169
|
+
}
|
|
4170
|
+
if (isNonEmpty(params.env.AGENT_SHIM_CONFIG_PROFILE)) {
|
|
4171
|
+
return { name: params.env.AGENT_SHIM_CONFIG_PROFILE, source: "env" };
|
|
4172
|
+
}
|
|
4173
|
+
if (isNonEmpty(params.directoryRuleConfigProfile)) {
|
|
4174
|
+
return { name: params.directoryRuleConfigProfile, source: "directory-rule" };
|
|
4175
|
+
}
|
|
4176
|
+
if (isNonEmpty(params.identityDefaultConfigProfile)) {
|
|
4177
|
+
return { name: params.identityDefaultConfigProfile, source: "identity-default" };
|
|
4178
|
+
}
|
|
4179
|
+
if (isNonEmpty(params.globalDefaultConfigProfile)) {
|
|
4180
|
+
return { name: params.globalDefaultConfigProfile, source: "global-default" };
|
|
4181
|
+
}
|
|
4182
|
+
return { source: "none" };
|
|
4183
|
+
}
|
|
4184
|
+
function loadIdentity(identitiesDir, name, fs10) {
|
|
4185
|
+
return loadConfigFile(path12.join(identitiesDir, name, "identity.json"), IdentitySchema, fs10.readConfigFile);
|
|
4186
|
+
}
|
|
3822
4187
|
|
|
3823
4188
|
// src/configProfilesStore.ts
|
|
3824
4189
|
import fs4 from "node:fs";
|
|
3825
|
-
import
|
|
4190
|
+
import path13 from "node:path";
|
|
3826
4191
|
var ProfileNotFoundError = class extends CliError {
|
|
3827
4192
|
constructor(profileName) {
|
|
3828
4193
|
super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
|
|
@@ -3848,7 +4213,7 @@ var InvalidCategoryNameError = class extends CliError {
|
|
|
3848
4213
|
categoryName;
|
|
3849
4214
|
};
|
|
3850
4215
|
function profileJsonPath(paths, name) {
|
|
3851
|
-
return
|
|
4216
|
+
return path13.join(paths.configProfilesDir, `${name}.json`);
|
|
3852
4217
|
}
|
|
3853
4218
|
function profileExists(paths, name) {
|
|
3854
4219
|
return fs4.existsSync(profileJsonPath(paths, name));
|
|
@@ -4036,7 +4401,7 @@ var InvalidIdentityNameError = class extends CliError {
|
|
|
4036
4401
|
attemptedName;
|
|
4037
4402
|
};
|
|
4038
4403
|
function identityJsonPath(paths, name) {
|
|
4039
|
-
return
|
|
4404
|
+
return path14.join(paths.identitiesDir, name, "identity.json");
|
|
4040
4405
|
}
|
|
4041
4406
|
function identityExists(paths, name) {
|
|
4042
4407
|
return fs5.existsSync(identityJsonPath(paths, name));
|
|
@@ -4137,7 +4502,7 @@ function setIdentityCredential(paths, identityName, change) {
|
|
|
4137
4502
|
});
|
|
4138
4503
|
}
|
|
4139
4504
|
function belongsToIdentity(entry, name) {
|
|
4140
|
-
return entry === name || entry ===
|
|
4505
|
+
return entry === name || entry === path14.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
4141
4506
|
}
|
|
4142
4507
|
function removeIdentity(paths, name) {
|
|
4143
4508
|
if (!identityExists(paths, name)) {
|
|
@@ -4145,7 +4510,7 @@ function removeIdentity(paths, name) {
|
|
|
4145
4510
|
}
|
|
4146
4511
|
for (const entry of fs5.readdirSync(paths.identitiesDir)) {
|
|
4147
4512
|
if (belongsToIdentity(entry, name)) {
|
|
4148
|
-
fs5.rmSync(
|
|
4513
|
+
fs5.rmSync(path14.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
4149
4514
|
}
|
|
4150
4515
|
}
|
|
4151
4516
|
if (readActiveIdentity(paths) === name) {
|
|
@@ -4155,7 +4520,7 @@ function removeIdentity(paths, name) {
|
|
|
4155
4520
|
|
|
4156
4521
|
// src/providersStore.ts
|
|
4157
4522
|
import fs6 from "node:fs";
|
|
4158
|
-
import
|
|
4523
|
+
import path15 from "node:path";
|
|
4159
4524
|
|
|
4160
4525
|
// src/credentialCache.ts
|
|
4161
4526
|
import { z as z7 } from "zod";
|
|
@@ -4165,6 +4530,62 @@ var CachedCredentialSchema = z7.strictObject({
|
|
|
4165
4530
|
source: CredentialSourceSchema
|
|
4166
4531
|
});
|
|
4167
4532
|
|
|
4533
|
+
// src/credential.ts
|
|
4534
|
+
function summariseSource(source) {
|
|
4535
|
+
if ("env" in source) {
|
|
4536
|
+
return { kind: "env", variable: source.env };
|
|
4537
|
+
}
|
|
4538
|
+
if ("file" in source) {
|
|
4539
|
+
return { kind: "file", path: source.file };
|
|
4540
|
+
}
|
|
4541
|
+
if ("command" in source) {
|
|
4542
|
+
return { kind: "command", program: source.command[0] };
|
|
4543
|
+
}
|
|
4544
|
+
if ("op" in source) {
|
|
4545
|
+
return { kind: "op", reference: source.op };
|
|
4546
|
+
}
|
|
4547
|
+
if ("keychain" in source) {
|
|
4548
|
+
return {
|
|
4549
|
+
kind: "keychain",
|
|
4550
|
+
service: source.keychain.service,
|
|
4551
|
+
...source.keychain.account === void 0 ? {} : { account: source.keychain.account }
|
|
4552
|
+
};
|
|
4553
|
+
}
|
|
4554
|
+
return { kind: "literal" };
|
|
4555
|
+
}
|
|
4556
|
+
function summariseCredential(credential) {
|
|
4557
|
+
return {
|
|
4558
|
+
target: credential.target ?? "bearer",
|
|
4559
|
+
sources: credential.sources.map(summariseSource),
|
|
4560
|
+
...credential.cache === void 0 ? {} : { cache: credential.cache }
|
|
4561
|
+
};
|
|
4562
|
+
}
|
|
4563
|
+
function formatSourceSummary(summary) {
|
|
4564
|
+
switch (summary.kind) {
|
|
4565
|
+
case "env":
|
|
4566
|
+
return `env ${summary.variable}`;
|
|
4567
|
+
case "file":
|
|
4568
|
+
return `file ${summary.path}`;
|
|
4569
|
+
case "command":
|
|
4570
|
+
return `command ${summary.program}`;
|
|
4571
|
+
case "op":
|
|
4572
|
+
return `op ${summary.reference}`;
|
|
4573
|
+
case "keychain":
|
|
4574
|
+
return `keychain ${summary.service}${summary.account === void 0 ? "" : ` (account ${summary.account})`}`;
|
|
4575
|
+
case "literal":
|
|
4576
|
+
return "literal (non-secret placeholder)";
|
|
4577
|
+
default:
|
|
4578
|
+
return summary;
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
function formatCredentialSummary(summary) {
|
|
4582
|
+
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`}`;
|
|
4583
|
+
return `${summary.target} from ${summary.sources.map(formatSourceSummary).join(", then ")}${cache}`;
|
|
4584
|
+
}
|
|
4585
|
+
function describeCredential(credential) {
|
|
4586
|
+
return formatCredentialSummary(summariseCredential(credential));
|
|
4587
|
+
}
|
|
4588
|
+
|
|
4168
4589
|
// src/providersStore.ts
|
|
4169
4590
|
var ProviderNotFoundError = class extends CliError {
|
|
4170
4591
|
constructor(name) {
|
|
@@ -4194,7 +4615,7 @@ var InvalidProviderNameError = class extends CliError {
|
|
|
4194
4615
|
};
|
|
4195
4616
|
var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
|
|
4196
4617
|
function providerJsonPath(paths, name) {
|
|
4197
|
-
return
|
|
4618
|
+
return path15.join(paths.providersDir, `${name}.json`);
|
|
4198
4619
|
}
|
|
4199
4620
|
function providerExists(paths, name) {
|
|
4200
4621
|
return fs6.existsSync(providerJsonPath(paths, name));
|
|
@@ -4202,14 +4623,14 @@ function providerExists(paths, name) {
|
|
|
4202
4623
|
var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
|
|
4203
4624
|
var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
|
|
4204
4625
|
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
4205
|
-
function
|
|
4626
|
+
function isRecord2(value) {
|
|
4206
4627
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4207
4628
|
}
|
|
4208
4629
|
function legacyProviderConversion(raw) {
|
|
4209
|
-
if (!
|
|
4630
|
+
if (!isRecord2(raw) || "credential" in raw) {
|
|
4210
4631
|
return void 0;
|
|
4211
4632
|
}
|
|
4212
|
-
const env =
|
|
4633
|
+
const env = isRecord2(raw.env) ? raw.env : void 0;
|
|
4213
4634
|
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
4214
4635
|
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
4215
4636
|
if (fields.length === 0) {
|
|
@@ -4392,6 +4813,15 @@ function removeProvider(paths, name) {
|
|
|
4392
4813
|
}
|
|
4393
4814
|
fs6.rmSync(providerJsonPath(paths, name));
|
|
4394
4815
|
}
|
|
4816
|
+
function cascadeProviderName(cascade) {
|
|
4817
|
+
let name;
|
|
4818
|
+
for (const layer of assembleCascade(cascade).layers) {
|
|
4819
|
+
if (layer.launch?.provider !== void 0) {
|
|
4820
|
+
name = layer.launch.provider;
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
return name;
|
|
4824
|
+
}
|
|
4395
4825
|
|
|
4396
4826
|
// src/directoryRulesStore.ts
|
|
4397
4827
|
var DirectoryRuleNotFoundError = class extends CliError {
|
|
@@ -4482,6 +4912,1722 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
4482
4912
|
}
|
|
4483
4913
|
writeDirectoryRules(paths, { ...current, rules: nextRules });
|
|
4484
4914
|
}
|
|
4915
|
+
|
|
4916
|
+
// src/checkReport.ts
|
|
4917
|
+
import os3 from "node:os";
|
|
4918
|
+
|
|
4919
|
+
// src/launcher/cascade.ts
|
|
4920
|
+
import path17 from "node:path";
|
|
4921
|
+
|
|
4922
|
+
// src/pathNorm.ts
|
|
4923
|
+
import path16 from "node:path";
|
|
4924
|
+
function expandTilde(value, home) {
|
|
4925
|
+
if (value === "~") {
|
|
4926
|
+
return home;
|
|
4927
|
+
}
|
|
4928
|
+
if (value.startsWith("~/")) {
|
|
4929
|
+
return path16.join(home, value.slice(2));
|
|
4930
|
+
}
|
|
4931
|
+
return value;
|
|
4932
|
+
}
|
|
4933
|
+
function normaliseRulePath(rulePath, home) {
|
|
4934
|
+
return path16.resolve(expandTilde(rulePath, home));
|
|
4935
|
+
}
|
|
4936
|
+
|
|
4937
|
+
// src/launcher/cascade.ts
|
|
4938
|
+
var PORTABLE_CONFIG_FILENAME = ".agent-shim.json";
|
|
4939
|
+
var PORTABLE_LOCAL_CONFIG_FILENAME = ".agent-shim.local.json";
|
|
4940
|
+
function loadCascadeInput(params) {
|
|
4941
|
+
const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
|
|
4942
|
+
const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
|
|
4943
|
+
const loadProfile = (name) => {
|
|
4944
|
+
const filepath = path17.join(params.paths.configProfilesDir, `${name}.json`);
|
|
4945
|
+
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
4946
|
+
if (loaded === void 0) {
|
|
4947
|
+
return void 0;
|
|
4948
|
+
}
|
|
4949
|
+
return { name, profile: loaded.config, entryOrder: loaded.entryOrder, filepath: loaded.filepath };
|
|
4950
|
+
};
|
|
4951
|
+
const limit = globalConfig?.config.walkUpLimit;
|
|
4952
|
+
const dirs = walkDirectoryAncestors(params.cwd, {
|
|
4953
|
+
home: params.home,
|
|
4954
|
+
...limit === void 0 ? {} : { limit: expandTilde(limit, params.home) },
|
|
4955
|
+
...params.isReadable === void 0 ? {} : { isReadable: params.isReadable }
|
|
4956
|
+
});
|
|
4957
|
+
const levels = [];
|
|
4958
|
+
for (const dir of dirs) {
|
|
4959
|
+
const portable = loadConfigFile(path17.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path17.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
4960
|
+
const portableLocal = loadConfigFile(path17.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path17.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
4961
|
+
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
4962
|
+
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
4963
|
+
return {
|
|
4964
|
+
rule,
|
|
4965
|
+
...entryOrder === void 0 ? {} : { entryOrder },
|
|
4966
|
+
filepath: params.paths.directoryRulesFile
|
|
4967
|
+
};
|
|
4968
|
+
});
|
|
4969
|
+
if (portable === void 0 && portableLocal === void 0 && rules.length === 0) {
|
|
4970
|
+
continue;
|
|
4971
|
+
}
|
|
4972
|
+
levels.push({
|
|
4973
|
+
dir,
|
|
4974
|
+
...portable === void 0 ? {} : { portable: { config: portable.config, entryOrder: portable.entryOrder, filepath: portable.filepath } },
|
|
4975
|
+
...rules.length === 0 ? {} : { rules },
|
|
4976
|
+
...portableLocal === void 0 ? {} : { portableLocal: { config: portableLocal.config, entryOrder: portableLocal.entryOrder, filepath: portableLocal.filepath } }
|
|
4977
|
+
});
|
|
4978
|
+
}
|
|
4979
|
+
const input = {
|
|
4980
|
+
home: params.home,
|
|
4981
|
+
...globalConfig === void 0 ? {} : { globalConfig: { config: globalConfig.config, entryOrder: globalConfig.entryOrder, filepath: globalConfig.filepath } },
|
|
4982
|
+
...params.baseConfigProfile === void 0 ? {} : { baseConfigProfile: params.baseConfigProfile },
|
|
4983
|
+
loadProfile,
|
|
4984
|
+
levels,
|
|
4985
|
+
...params.cliOverride === void 0 ? {} : { cliOverride: params.cliOverride }
|
|
4986
|
+
};
|
|
4987
|
+
return { input, ...globalConfig === void 0 ? {} : { globalConfig: globalConfig.config } };
|
|
4988
|
+
}
|
|
4989
|
+
function readDirectorySelections(loaded) {
|
|
4990
|
+
let identity;
|
|
4991
|
+
let configProfile;
|
|
4992
|
+
for (const level of loaded.input.levels ?? []) {
|
|
4993
|
+
for (const source of [level.portable?.config, ...(level.rules ?? []).map((entry) => entry.rule), level.portableLocal?.config]) {
|
|
4994
|
+
if (source === void 0) {
|
|
4995
|
+
continue;
|
|
4996
|
+
}
|
|
4997
|
+
if (source.identity !== void 0) {
|
|
4998
|
+
identity = source.identity;
|
|
4999
|
+
}
|
|
5000
|
+
if (source.configProfile !== void 0) {
|
|
5001
|
+
configProfile = source.configProfile;
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
return { ...identity === void 0 ? {} : { identity }, ...configProfile === void 0 ? {} : { configProfile } };
|
|
5006
|
+
}
|
|
5007
|
+
|
|
5008
|
+
// src/usage/preflight.ts
|
|
5009
|
+
var MS_PER_SECOND2 = 1e3;
|
|
5010
|
+
var SECONDS_PER_MINUTE = 60;
|
|
5011
|
+
var MINUTES_PER_HOUR = 60;
|
|
5012
|
+
var HOURS_PER_DAY = 24;
|
|
5013
|
+
function formatAge(ms) {
|
|
5014
|
+
const minutes = Math.floor(ms / (MS_PER_SECOND2 * SECONDS_PER_MINUTE));
|
|
5015
|
+
if (minutes < MINUTES_PER_HOUR) {
|
|
5016
|
+
return `${String(Math.max(minutes, 0))}m`;
|
|
5017
|
+
}
|
|
5018
|
+
const hours = Math.floor(minutes / MINUTES_PER_HOUR);
|
|
5019
|
+
return hours < HOURS_PER_DAY ? `${String(hours)}h` : `${String(Math.floor(hours / HOURS_PER_DAY))}d`;
|
|
5020
|
+
}
|
|
5021
|
+
function effectiveWindow(window, nowMs) {
|
|
5022
|
+
const resetsAtMs = window.resetsAt === void 0 ? void 0 : Date.parse(window.resetsAt);
|
|
5023
|
+
if (resetsAtMs !== void 0 && resetsAtMs <= nowMs) {
|
|
5024
|
+
return { reset: true, utilization: 0 };
|
|
5025
|
+
}
|
|
5026
|
+
return {
|
|
5027
|
+
reset: false,
|
|
5028
|
+
...window.utilization === void 0 ? {} : { utilization: window.utilization },
|
|
5029
|
+
...resetsAtMs === void 0 ? {} : { resetsAtMs },
|
|
5030
|
+
...window.status === void 0 ? {} : { status: window.status }
|
|
5031
|
+
};
|
|
5032
|
+
}
|
|
5033
|
+
|
|
5034
|
+
// src/usage/poolPick.ts
|
|
5035
|
+
import { z as z9 } from "zod";
|
|
5036
|
+
|
|
5037
|
+
// src/usage/plan.ts
|
|
5038
|
+
var TIER_MULTIPLIER = /(?:^|_)(\d+)x$/;
|
|
5039
|
+
var ZERO_TIER = /(?:^|_)zero$/;
|
|
5040
|
+
var BASE_TIER = /(?:^|_)(?:pro|free)$/;
|
|
5041
|
+
function planOf(account) {
|
|
5042
|
+
const tier = account?.organizationRateLimitTier ?? account?.userRateLimitTier;
|
|
5043
|
+
const tierField = tier === void 0 ? {} : { tier };
|
|
5044
|
+
if (tier === void 0) {
|
|
5045
|
+
return { kind: "subscription", capacity: 1, recognised: false };
|
|
5046
|
+
}
|
|
5047
|
+
if (ZERO_TIER.test(tier)) {
|
|
5048
|
+
return { kind: "pay-per-use", ...tierField };
|
|
5049
|
+
}
|
|
5050
|
+
const multiplier = TIER_MULTIPLIER.exec(tier)?.[1];
|
|
5051
|
+
if (multiplier !== void 0) {
|
|
5052
|
+
return { kind: "subscription", capacity: Number(multiplier), recognised: true, ...tierField };
|
|
5053
|
+
}
|
|
5054
|
+
return { kind: "subscription", capacity: 1, recognised: BASE_TIER.test(tier), ...tierField };
|
|
5055
|
+
}
|
|
5056
|
+
|
|
5057
|
+
// src/usage/rateLimit.ts
|
|
5058
|
+
var MS_PER_SECOND3 = 1e3;
|
|
5059
|
+
function epochSecondsToIso(value) {
|
|
5060
|
+
if (value === void 0) {
|
|
5061
|
+
return void 0;
|
|
5062
|
+
}
|
|
5063
|
+
const seconds = Number(value);
|
|
5064
|
+
return Number.isFinite(seconds) && seconds > 0 ? new Date(seconds * MS_PER_SECOND3).toISOString() : void 0;
|
|
5065
|
+
}
|
|
5066
|
+
function nonNegativeNumber(value) {
|
|
5067
|
+
if (value === void 0 || value.trim() === "") {
|
|
5068
|
+
return void 0;
|
|
5069
|
+
}
|
|
5070
|
+
const parsed = Number(value);
|
|
5071
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : void 0;
|
|
5072
|
+
}
|
|
5073
|
+
var UNIFIED_PREFIX = "anthropic-ratelimit-unified-";
|
|
5074
|
+
function unifiedWindow(headers, window) {
|
|
5075
|
+
const utilization = nonNegativeNumber(headers[`${UNIFIED_PREFIX}${window}-utilization`]);
|
|
5076
|
+
const resetsAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}${window}-reset`]);
|
|
5077
|
+
const status = headers[`${UNIFIED_PREFIX}${window}-status`];
|
|
5078
|
+
if (utilization === void 0 && resetsAt === void 0 && status === void 0) {
|
|
5079
|
+
return void 0;
|
|
5080
|
+
}
|
|
5081
|
+
return { ...utilization === void 0 ? {} : { utilization }, ...resetsAt === void 0 ? {} : { resetsAt }, ...status === void 0 ? {} : { status } };
|
|
5082
|
+
}
|
|
5083
|
+
function parseUnifiedRateLimit(headers) {
|
|
5084
|
+
if (!Object.keys(headers).some((name) => name.startsWith(UNIFIED_PREFIX))) {
|
|
5085
|
+
return void 0;
|
|
5086
|
+
}
|
|
5087
|
+
const status = headers[`${UNIFIED_PREFIX}status`];
|
|
5088
|
+
const fiveHour = unifiedWindow(headers, "5h");
|
|
5089
|
+
const sevenDay = unifiedWindow(headers, "7d");
|
|
5090
|
+
const representativeClaim = headers[`${UNIFIED_PREFIX}representative-claim`];
|
|
5091
|
+
const resetAt = epochSecondsToIso(headers[`${UNIFIED_PREFIX}reset`]);
|
|
5092
|
+
const overageStatus = headers[`${UNIFIED_PREFIX}overage-status`];
|
|
5093
|
+
return {
|
|
5094
|
+
...status === void 0 ? {} : { status },
|
|
5095
|
+
...fiveHour === void 0 ? {} : { fiveHour },
|
|
5096
|
+
...sevenDay === void 0 ? {} : { sevenDay },
|
|
5097
|
+
...representativeClaim === void 0 ? {} : { representativeClaim },
|
|
5098
|
+
...resetAt === void 0 ? {} : { resetAt },
|
|
5099
|
+
...overageStatus === void 0 ? {} : { overageStatus }
|
|
5100
|
+
};
|
|
5101
|
+
}
|
|
5102
|
+
|
|
5103
|
+
// src/usage/scan.ts
|
|
5104
|
+
import { z as z8 } from "zod";
|
|
5105
|
+
var AnthropicUsageSchema = z8.looseObject({
|
|
5106
|
+
input_tokens: z8.number().int().nonnegative().nullish(),
|
|
5107
|
+
output_tokens: z8.number().int().nonnegative().nullish(),
|
|
5108
|
+
cache_creation_input_tokens: z8.number().int().nonnegative().nullish(),
|
|
5109
|
+
cache_read_input_tokens: z8.number().int().nonnegative().nullish()
|
|
5110
|
+
});
|
|
5111
|
+
var ErrorObjectSchema = z8.looseObject({
|
|
5112
|
+
type: z8.string().optional(),
|
|
5113
|
+
code: z8.union([z8.string(), z8.number()]).optional(),
|
|
5114
|
+
message: z8.string().optional()
|
|
5115
|
+
});
|
|
5116
|
+
|
|
5117
|
+
// src/usage/middleware.ts
|
|
5118
|
+
var ANTHROPIC_PROVIDER = "anthropic";
|
|
5119
|
+
|
|
5120
|
+
// src/usage/pick.ts
|
|
5121
|
+
var MS_PER_SECOND4 = 1e3;
|
|
5122
|
+
var MS_PER_HOUR = 36e5;
|
|
5123
|
+
var HOURS_PER_DAY2 = 24;
|
|
5124
|
+
var FIVE_HOURS = 5;
|
|
5125
|
+
var PROMPT_CACHE_TTL_HOURS = 1;
|
|
5126
|
+
var PERCENT = 100;
|
|
5127
|
+
var SEVEN_DAYS = 7;
|
|
5128
|
+
var FIVE_HOUR_WINDOW_MS = FIVE_HOURS * MS_PER_HOUR;
|
|
5129
|
+
var SEVEN_DAY_WINDOW_MS = SEVEN_DAYS * HOURS_PER_DAY2 * MS_PER_HOUR;
|
|
5130
|
+
var PROMPT_CACHE_TTL_MS = PROMPT_CACHE_TTL_HOURS * MS_PER_HOUR;
|
|
5131
|
+
var CLASS_ORDER = { scored: 0, unknown: 1, "pay-per-use": 2, ineligible: 3 };
|
|
5132
|
+
function unifiedOf(state) {
|
|
5133
|
+
return state?.rateLimit?.unified;
|
|
5134
|
+
}
|
|
5135
|
+
function limitBlockedUntil(state, nowMs) {
|
|
5136
|
+
const limit = state?.lastLimit;
|
|
5137
|
+
if (state === void 0 || limit === void 0) {
|
|
5138
|
+
return void 0;
|
|
5139
|
+
}
|
|
5140
|
+
if (Date.parse(state.lastRequestAt) > Date.parse(limit.observedAt)) {
|
|
5141
|
+
return void 0;
|
|
5142
|
+
}
|
|
5143
|
+
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);
|
|
5144
|
+
return until !== void 0 && until > nowMs ? until : void 0;
|
|
5145
|
+
}
|
|
5146
|
+
function windowBlockedUntil(windows) {
|
|
5147
|
+
const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
|
|
5148
|
+
return resets.length === 0 ? void 0 : Math.max(...resets);
|
|
5149
|
+
}
|
|
5150
|
+
function fiveHourBurn(records) {
|
|
5151
|
+
const points = [];
|
|
5152
|
+
for (const record of records) {
|
|
5153
|
+
const window = record.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record.rateLimitHeaders)?.fiveHour;
|
|
5154
|
+
if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
|
|
5155
|
+
points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record.at), utilization: window.utilization } });
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
const latest = points.at(-1)?.resetsAt;
|
|
5159
|
+
const current = points.filter((entry) => entry.resetsAt === latest).map((entry) => entry.point);
|
|
5160
|
+
const first = current.at(0);
|
|
5161
|
+
const last = current.at(-1);
|
|
5162
|
+
if (first === void 0 || last === void 0 || last.atMs <= first.atMs || last.utilization <= first.utilization) {
|
|
5163
|
+
return void 0;
|
|
5164
|
+
}
|
|
5165
|
+
return (last.utilization - first.utilization) / (last.atMs - first.atMs);
|
|
5166
|
+
}
|
|
5167
|
+
function percent(fraction) {
|
|
5168
|
+
return `${String(Math.round(fraction * PERCENT))}%`;
|
|
5169
|
+
}
|
|
5170
|
+
function describeWindow(label, window, effective, nowMs) {
|
|
5171
|
+
if (effective.reset) {
|
|
5172
|
+
return `${label} window has reset`;
|
|
5173
|
+
}
|
|
5174
|
+
const used = effective.utilization === void 0 ? "usage unknown" : `${percent(effective.utilization)} used`;
|
|
5175
|
+
const resets = effective.resetsAtMs === void 0 ? "" : `, resets in ${formatAge(effective.resetsAtMs - nowMs)}`;
|
|
5176
|
+
return `${label} ${used}${resets}${window.status === "allowed_warning" ? " (nearly used)" : ""}`;
|
|
5177
|
+
}
|
|
5178
|
+
function capacityOf(plan) {
|
|
5179
|
+
return plan.kind === "subscription" ? plan.capacity : 1;
|
|
5180
|
+
}
|
|
5181
|
+
function assess(member, nowMs) {
|
|
5182
|
+
const plan = planOf(member.account);
|
|
5183
|
+
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`];
|
|
5184
|
+
const base = { identity: member.identity, plan };
|
|
5185
|
+
if (member.readError !== void 0) {
|
|
5186
|
+
return { candidate: { ...base, class: "unknown", reasons: [`usage state unreadable: ${member.readError}`, ...planReasons] } };
|
|
5187
|
+
}
|
|
5188
|
+
const state = member.snapshot?.providers[ANTHROPIC_PROVIDER];
|
|
5189
|
+
const unified = unifiedOf(state);
|
|
5190
|
+
if (member.snapshot === void 0 || state === void 0 || unified === void 0) {
|
|
5191
|
+
return { candidate: { ...base, class: "unknown", reasons: ["no usage recorded yet, so its quota is unknown (turn on trackUsage for it)", ...planReasons] } };
|
|
5192
|
+
}
|
|
5193
|
+
const observedAgo = state.rateLimit === void 0 ? "" : ` (last seen ${formatAge(nowMs - Date.parse(state.rateLimit.observedAt))} ago)`;
|
|
5194
|
+
const fiveHourWindow = unified.fiveHour;
|
|
5195
|
+
const sevenDayWindow = unified.sevenDay;
|
|
5196
|
+
const five = fiveHourWindow === void 0 ? void 0 : effectiveWindow(fiveHourWindow, nowMs);
|
|
5197
|
+
const seven = sevenDayWindow === void 0 ? void 0 : effectiveWindow(sevenDayWindow, nowMs);
|
|
5198
|
+
const windowReasons = [
|
|
5199
|
+
...fiveHourWindow === void 0 || five === void 0 ? [] : [describeWindow("5h", fiveHourWindow, five, nowMs)],
|
|
5200
|
+
...sevenDayWindow === void 0 || seven === void 0 ? [] : [describeWindow("7d", sevenDayWindow, seven, nowMs)]
|
|
5201
|
+
];
|
|
5202
|
+
const blockedUntil = Math.max(
|
|
5203
|
+
windowBlockedUntil([five, seven].flatMap((window) => window === void 0 ? [] : [window])) ?? 0,
|
|
5204
|
+
limitBlockedUntil(state, nowMs) ?? 0
|
|
5205
|
+
);
|
|
5206
|
+
if (blockedUntil > nowMs) {
|
|
5207
|
+
if (unified.overageStatus === "allowed") {
|
|
5208
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [`plan exhausted until ${new Date(blockedUntil).toISOString()}, continues as extra usage`, ...windowReasons, ...planReasons] } };
|
|
5209
|
+
}
|
|
5210
|
+
return { candidate: { ...base, class: "ineligible", blockedUntilMs: blockedUntil, reasons: [`refused until ${new Date(blockedUntil).toISOString()} (in ${formatAge(blockedUntil - nowMs)})`, ...windowReasons, ...planReasons] } };
|
|
5211
|
+
}
|
|
5212
|
+
if (plan.kind === "pay-per-use" || fiveHourWindow === void 0 && sevenDayWindow === void 0) {
|
|
5213
|
+
return { candidate: { ...base, class: "pay-per-use", reasons: [...plan.kind === "pay-per-use" ? planReasons : ["no plan windows reported, so usage bills as extra usage"]] } };
|
|
5214
|
+
}
|
|
5215
|
+
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;
|
|
5216
|
+
if (basis?.window.utilization === void 0) {
|
|
5217
|
+
return { candidate: { ...base, class: "unknown", reasons: ["quota windows reported no utilisation", ...windowReasons, ...planReasons] } };
|
|
5218
|
+
}
|
|
5219
|
+
const remaining = Math.max(0, 1 - basis.window.utilization);
|
|
5220
|
+
const untilResetMs = basis.window.resetsAtMs === void 0 ? basis.windowMs : Math.max(basis.window.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5221
|
+
const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
|
|
5222
|
+
const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
|
|
5223
|
+
const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND4);
|
|
5224
|
+
const burn = fiveHourBurn(member.records);
|
|
5225
|
+
return {
|
|
5226
|
+
candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
|
|
5227
|
+
...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
|
|
5228
|
+
...burn === void 0 ? {} : { burn }
|
|
5229
|
+
};
|
|
5230
|
+
}
|
|
5231
|
+
function rankPool(input) {
|
|
5232
|
+
const { members, nowMs } = input;
|
|
5233
|
+
const assessments = members.map((member) => assess(member, nowMs));
|
|
5234
|
+
const demands = assessments.flatMap(({ candidate, burn }) => burn === void 0 ? [] : [burn * capacityOf(candidate.plan)]);
|
|
5235
|
+
const sharedDemand = demands.length === 0 ? void 0 : Math.max(...demands);
|
|
5236
|
+
const candidates = assessments.map((assessment) => {
|
|
5237
|
+
const { candidate, fiveHour, burn } = assessment;
|
|
5238
|
+
const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
|
|
5239
|
+
if (candidate.class !== "scored" || fiveHour === void 0 || pace === void 0) {
|
|
5240
|
+
return { ...candidate, feasible: true };
|
|
5241
|
+
}
|
|
5242
|
+
const runsDryInMs = fiveHour.remaining / pace;
|
|
5243
|
+
if (runsDryInMs >= fiveHour.untilResetMs) {
|
|
5244
|
+
return { ...candidate, feasible: true };
|
|
5245
|
+
}
|
|
5246
|
+
return { ...candidate, feasible: false, reasons: [`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`, ...candidate.reasons] };
|
|
5247
|
+
});
|
|
5248
|
+
const ordered = [...candidates].sort(
|
|
5249
|
+
(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)
|
|
5250
|
+
);
|
|
5251
|
+
const sticky = input.sticky;
|
|
5252
|
+
const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
|
|
5253
|
+
const stickyCandidate = ordered[stickyIndex];
|
|
5254
|
+
if (sticky !== void 0 && stickyCandidate?.class === "scored" && stickyCandidate.feasible) {
|
|
5255
|
+
const ageMs = nowMs - Date.parse(sticky.at);
|
|
5256
|
+
const warm = ageMs <= PROMPT_CACHE_TTL_MS;
|
|
5257
|
+
if (input.resuming || warm) {
|
|
5258
|
+
const why = warm ? `used here ${formatAge(ageMs)} ago, prompt cache still warm` : "resuming a conversation started on this account";
|
|
5259
|
+
ordered.splice(stickyIndex, 1);
|
|
5260
|
+
ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
const pick = ordered.find((candidate) => candidate.class !== "ineligible");
|
|
5264
|
+
const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
|
|
5265
|
+
const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
|
|
5266
|
+
return {
|
|
5267
|
+
candidates: ordered,
|
|
5268
|
+
...pick === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick }
|
|
5269
|
+
};
|
|
5270
|
+
}
|
|
5271
|
+
|
|
5272
|
+
// src/usage/store.ts
|
|
5273
|
+
var DAY_MS2 = 864e5;
|
|
5274
|
+
var DAYS_IN_WEEK = 7;
|
|
5275
|
+
var USAGE_RETENTION_MS = DAYS_IN_WEEK * DAY_MS2;
|
|
5276
|
+
|
|
5277
|
+
// src/usage/poolPick.ts
|
|
5278
|
+
var SCHEMA_VERSION = 1;
|
|
5279
|
+
var PickEntrySchema = z9.strictObject({ identity: z9.string().min(1), at: z9.iso.datetime() });
|
|
5280
|
+
var PicksFileSchema = z9.strictObject({
|
|
5281
|
+
schemaVersion: z9.literal(SCHEMA_VERSION),
|
|
5282
|
+
picks: z9.record(z9.string().min(1), PickEntrySchema)
|
|
5283
|
+
});
|
|
5284
|
+
function errorMessage(error) {
|
|
5285
|
+
return error instanceof Error ? error.message : String(error);
|
|
5286
|
+
}
|
|
5287
|
+
function loadPoolMembers(fs10, paths, identities, nowMs) {
|
|
5288
|
+
const windowStartMs = nowMs - FIVE_HOUR_WINDOW_MS;
|
|
5289
|
+
const log = readUsageLog(fs10, paths.usageLogDir, { sinceMs: windowStartMs });
|
|
5290
|
+
return identities.map((identity) => {
|
|
5291
|
+
const records = log.records.filter((record) => record.identity === identity && record.provider === ANTHROPIC_PROVIDER && Date.parse(record.at) >= windowStartMs);
|
|
5292
|
+
try {
|
|
5293
|
+
const snapshot = readUsageSnapshot(fs10, paths.usageSnapshotsDir, identity);
|
|
5294
|
+
const account = readAccountMetadata(fs10, paths.identitiesDir, identity) ?? snapshot?.account;
|
|
5295
|
+
return { identity, records, ...snapshot === void 0 ? {} : { snapshot }, ...account === void 0 ? {} : { account } };
|
|
5296
|
+
} catch (error) {
|
|
5297
|
+
if (error instanceof UsageSnapshotError || error instanceof AccountMetadataError) {
|
|
5298
|
+
return { identity, records, readError: errorMessage(error) };
|
|
5299
|
+
}
|
|
5300
|
+
throw error;
|
|
5301
|
+
}
|
|
5302
|
+
});
|
|
5303
|
+
}
|
|
5304
|
+
function rankPoolFromStore(params) {
|
|
5305
|
+
return rankPool({
|
|
5306
|
+
members: loadPoolMembers(params.fs, params.paths, params.identities, params.nowMs),
|
|
5307
|
+
nowMs: params.nowMs,
|
|
5308
|
+
resuming: params.resuming,
|
|
5309
|
+
...params.sticky === void 0 ? {} : { sticky: params.sticky }
|
|
5310
|
+
});
|
|
5311
|
+
}
|
|
5312
|
+
function readPicksFile(fs10, file) {
|
|
5313
|
+
const empty = { schemaVersion: SCHEMA_VERSION, picks: {} };
|
|
5314
|
+
const raw = fs10.readFileUtf8(file);
|
|
5315
|
+
if (raw === void 0) {
|
|
5316
|
+
return { file: empty };
|
|
5317
|
+
}
|
|
5318
|
+
let parsed;
|
|
5319
|
+
try {
|
|
5320
|
+
parsed = JSON.parse(raw);
|
|
5321
|
+
} catch {
|
|
5322
|
+
return { file: empty, problem: `${file} is not valid JSON` };
|
|
5323
|
+
}
|
|
5324
|
+
const result = PicksFileSchema.safeParse(parsed);
|
|
5325
|
+
return result.success ? { file: result.data } : { file: empty, problem: `${file} is not a picks file this agent-shim can read` };
|
|
5326
|
+
}
|
|
5327
|
+
function readStickyPick(fs10, picksFile, directory) {
|
|
5328
|
+
const read = readPicksFile(fs10, picksFile);
|
|
5329
|
+
const entry = read.file.picks[directory];
|
|
5330
|
+
return { ...entry === void 0 ? {} : { sticky: entry }, ...read.problem === void 0 ? {} : { problem: read.problem } };
|
|
5331
|
+
}
|
|
5332
|
+
|
|
5333
|
+
// src/launcher/pool.ts
|
|
5334
|
+
function splitMembers(pool, paths, fs10) {
|
|
5335
|
+
const exists = (name) => loadIdentity(paths.identitiesDir, name, fs10) !== void 0;
|
|
5336
|
+
return { present: pool.identities.filter(exists), missing: pool.identities.filter((name) => !exists(name)) };
|
|
5337
|
+
}
|
|
5338
|
+
|
|
5339
|
+
// src/poolPickReport.ts
|
|
5340
|
+
function candidateView(candidate) {
|
|
5341
|
+
return {
|
|
5342
|
+
identity: candidate.identity,
|
|
5343
|
+
class: candidate.class,
|
|
5344
|
+
...candidate.score === void 0 ? {} : { score: candidate.score },
|
|
5345
|
+
feasible: candidate.feasible,
|
|
5346
|
+
...candidate.blockedUntilMs === void 0 ? {} : { blockedUntil: new Date(candidate.blockedUntilMs).toISOString() },
|
|
5347
|
+
plan: candidate.plan,
|
|
5348
|
+
reasons: candidate.reasons
|
|
5349
|
+
};
|
|
5350
|
+
}
|
|
5351
|
+
function collectPoolPick(params) {
|
|
5352
|
+
const { present, missing } = splitMembers(params.pool, params.paths, params.fs);
|
|
5353
|
+
const sticky = readStickyPick(params.usageFs, params.paths.usagePicksFile, params.directory);
|
|
5354
|
+
const ranking = rankPoolFromStore({
|
|
5355
|
+
fs: params.usageFs,
|
|
5356
|
+
paths: params.paths,
|
|
5357
|
+
identities: present,
|
|
5358
|
+
nowMs: params.nowMs,
|
|
5359
|
+
resuming: false,
|
|
5360
|
+
...sticky.sticky === void 0 ? {} : { sticky: sticky.sticky }
|
|
5361
|
+
});
|
|
5362
|
+
return {
|
|
5363
|
+
pool: params.poolName,
|
|
5364
|
+
directory: params.directory,
|
|
5365
|
+
...ranking.pick === void 0 ? {} : { pick: ranking.pick.identity },
|
|
5366
|
+
candidates: ranking.candidates.map(candidateView),
|
|
5367
|
+
missing,
|
|
5368
|
+
...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
|
|
5369
|
+
...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
|
|
5370
|
+
};
|
|
5371
|
+
}
|
|
5372
|
+
|
|
5373
|
+
// src/realPorts.ts
|
|
5374
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
5375
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
5376
|
+
import fs7 from "node:fs";
|
|
5377
|
+
import path19 from "node:path";
|
|
5378
|
+
import { isSea } from "node:sea";
|
|
5379
|
+
|
|
5380
|
+
// src/versionDiscovery.ts
|
|
5381
|
+
import os2 from "node:os";
|
|
5382
|
+
import path18 from "node:path";
|
|
5383
|
+
function defaultVersionsDir() {
|
|
5384
|
+
return path18.join(os2.homedir(), ".local", "share", "claude", "versions");
|
|
5385
|
+
}
|
|
5386
|
+
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
5387
|
+
function isNumericDottedVersion(name) {
|
|
5388
|
+
return NUMERIC_DOTTED_VERSION_RE.test(name);
|
|
5389
|
+
}
|
|
5390
|
+
function compareVersions2(a, b) {
|
|
5391
|
+
if (!isNumericDottedVersion(a)) {
|
|
5392
|
+
throw new Error(`compareVersions: "${a}" is not a valid plain dotted-numeric version`);
|
|
5393
|
+
}
|
|
5394
|
+
if (!isNumericDottedVersion(b)) {
|
|
5395
|
+
throw new Error(`compareVersions: "${b}" is not a valid plain dotted-numeric version`);
|
|
5396
|
+
}
|
|
5397
|
+
const segmentsA = a.split(".").map(Number);
|
|
5398
|
+
const segmentsB = b.split(".").map(Number);
|
|
5399
|
+
const length = Math.max(segmentsA.length, segmentsB.length);
|
|
5400
|
+
for (let i = 0; i < length; i += 1) {
|
|
5401
|
+
const segmentA = segmentsA[i] ?? 0;
|
|
5402
|
+
const segmentB = segmentsB[i] ?? 0;
|
|
5403
|
+
if (segmentA !== segmentB) {
|
|
5404
|
+
return segmentA - segmentB;
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5407
|
+
return 0;
|
|
5408
|
+
}
|
|
5409
|
+
function pickHighestVersion(entries) {
|
|
5410
|
+
const candidates = entries.filter(
|
|
5411
|
+
(entry) => entry.isFile && entry.isExecutable && entry.sizeBytes > 0 && isNumericDottedVersion(entry.name)
|
|
5412
|
+
);
|
|
5413
|
+
if (candidates.length === 0) {
|
|
5414
|
+
return void 0;
|
|
5415
|
+
}
|
|
5416
|
+
return candidates.reduce(
|
|
5417
|
+
(highest, candidate) => compareVersions2(candidate.name, highest.name) > 0 ? candidate : highest
|
|
5418
|
+
).name;
|
|
5419
|
+
}
|
|
5420
|
+
function discoverClaudeBinary(options) {
|
|
5421
|
+
const versionsDir = options.versionsDir ?? defaultVersionsDir();
|
|
5422
|
+
const entries = options.listVersionsDir(versionsDir);
|
|
5423
|
+
const highestVersion = pickHighestVersion(entries);
|
|
5424
|
+
if (highestVersion !== void 0) {
|
|
5425
|
+
return {
|
|
5426
|
+
path: path18.join(versionsDir, highestVersion),
|
|
5427
|
+
source: "versions-dir",
|
|
5428
|
+
version: highestVersion
|
|
5429
|
+
};
|
|
5430
|
+
}
|
|
5431
|
+
const ownInstallDirs = new Set(options.ownInstallDirs.map((dir) => path18.resolve(dir)));
|
|
5432
|
+
for (const dir of options.pathDirs) {
|
|
5433
|
+
if (ownInstallDirs.has(path18.resolve(dir))) {
|
|
5434
|
+
continue;
|
|
5435
|
+
}
|
|
5436
|
+
const found = options.findExecutableInDir(dir, "claude");
|
|
5437
|
+
if (found !== void 0) {
|
|
5438
|
+
return { path: found, source: "path-fallback" };
|
|
5439
|
+
}
|
|
5440
|
+
}
|
|
5441
|
+
throw new Error(
|
|
5442
|
+
`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.`
|
|
5443
|
+
);
|
|
5444
|
+
}
|
|
5445
|
+
|
|
5446
|
+
// src/realPorts.ts
|
|
5447
|
+
function isEnoent3(error) {
|
|
5448
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
5449
|
+
}
|
|
5450
|
+
var realFsPort = {
|
|
5451
|
+
readFileUtf8(filePath) {
|
|
5452
|
+
try {
|
|
5453
|
+
return fs7.readFileSync(filePath, "utf8");
|
|
5454
|
+
} catch (error) {
|
|
5455
|
+
if (isEnoent3(error)) {
|
|
5456
|
+
return void 0;
|
|
5457
|
+
}
|
|
5458
|
+
throw error;
|
|
5459
|
+
}
|
|
5460
|
+
},
|
|
5461
|
+
readConfigFile: cosmiconfigReader(),
|
|
5462
|
+
readdir(dirPath) {
|
|
5463
|
+
try {
|
|
5464
|
+
return fs7.readdirSync(dirPath);
|
|
5465
|
+
} catch (error) {
|
|
5466
|
+
if (isEnoent3(error)) {
|
|
5467
|
+
return [];
|
|
5468
|
+
}
|
|
5469
|
+
throw error;
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5472
|
+
};
|
|
5473
|
+
function isErrorWithCode(error, code) {
|
|
5474
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
5475
|
+
}
|
|
5476
|
+
var OWNER_ONLY_DIR_MODE = 448;
|
|
5477
|
+
var OWNER_ONLY_FILE_MODE = 384;
|
|
5478
|
+
var PERMISSION_BITS = 511;
|
|
5479
|
+
var realFarmFs = {
|
|
5480
|
+
lstat(filePath) {
|
|
5481
|
+
let stat;
|
|
5482
|
+
try {
|
|
5483
|
+
stat = fs7.lstatSync(filePath);
|
|
5484
|
+
} catch (error) {
|
|
5485
|
+
if (isEnoent3(error)) {
|
|
5486
|
+
return void 0;
|
|
5487
|
+
}
|
|
5488
|
+
throw error;
|
|
5489
|
+
}
|
|
5490
|
+
const kind = stat.isSymbolicLink() ? "symlink" : stat.isDirectory() ? "dir" : "file";
|
|
5491
|
+
return { kind, mtimeMs: stat.mtimeMs, sizeBytes: stat.size };
|
|
5492
|
+
},
|
|
5493
|
+
readdir(dirPath) {
|
|
5494
|
+
try {
|
|
5495
|
+
return fs7.readdirSync(dirPath);
|
|
5496
|
+
} catch (error) {
|
|
5497
|
+
if (isEnoent3(error) || isErrorWithCode(error, "ENOTDIR")) {
|
|
5498
|
+
return [];
|
|
5499
|
+
}
|
|
5500
|
+
throw error;
|
|
5501
|
+
}
|
|
5502
|
+
},
|
|
5503
|
+
mkdirp(dirPath) {
|
|
5504
|
+
fs7.mkdirSync(dirPath, { recursive: true });
|
|
5505
|
+
},
|
|
5506
|
+
symlink(target, linkPath) {
|
|
5507
|
+
fs7.symlinkSync(target, linkPath);
|
|
5508
|
+
},
|
|
5509
|
+
rename(from, to) {
|
|
5510
|
+
fs7.renameSync(from, to);
|
|
5511
|
+
},
|
|
5512
|
+
removeRecursive(targetPath) {
|
|
5513
|
+
fs7.rmSync(targetPath, { recursive: true, force: true });
|
|
5514
|
+
},
|
|
5515
|
+
copyRecursive(from, to) {
|
|
5516
|
+
fs7.cpSync(from, to, { recursive: true, force: true, verbatimSymlinks: true });
|
|
5517
|
+
},
|
|
5518
|
+
readFileUtf8(filePath) {
|
|
5519
|
+
try {
|
|
5520
|
+
return fs7.readFileSync(filePath, "utf8");
|
|
5521
|
+
} catch (error) {
|
|
5522
|
+
if (isEnoent3(error)) {
|
|
5523
|
+
return void 0;
|
|
5524
|
+
}
|
|
5525
|
+
throw error;
|
|
5526
|
+
}
|
|
5527
|
+
},
|
|
5528
|
+
writeFileUtf8(filePath, contents) {
|
|
5529
|
+
fs7.writeFileSync(filePath, contents, "utf8");
|
|
5530
|
+
},
|
|
5531
|
+
mkdirPrivate(dirPath) {
|
|
5532
|
+
fs7.mkdirSync(dirPath, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
|
|
5533
|
+
fs7.chmodSync(dirPath, OWNER_ONLY_DIR_MODE);
|
|
5534
|
+
},
|
|
5535
|
+
writeFilePrivate(filePath, contents) {
|
|
5536
|
+
const temp = `${filePath}.${String(process.pid)}.tmp`;
|
|
5537
|
+
fs7.writeFileSync(temp, contents, { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
|
|
5538
|
+
fs7.chmodSync(temp, OWNER_ONLY_FILE_MODE);
|
|
5539
|
+
fs7.renameSync(temp, filePath);
|
|
5540
|
+
},
|
|
5541
|
+
appendFilePrivate(filePath, contents) {
|
|
5542
|
+
const descriptor = fs7.openSync(filePath, "a", OWNER_ONLY_FILE_MODE);
|
|
5543
|
+
try {
|
|
5544
|
+
if ((fs7.fstatSync(descriptor).mode & PERMISSION_BITS) !== OWNER_ONLY_FILE_MODE) {
|
|
5545
|
+
fs7.fchmodSync(descriptor, OWNER_ONLY_FILE_MODE);
|
|
5546
|
+
}
|
|
5547
|
+
const bytes = Buffer.from(contents, "utf8");
|
|
5548
|
+
let written = 0;
|
|
5549
|
+
while (written < bytes.length) {
|
|
5550
|
+
written += fs7.writeSync(descriptor, bytes, written, bytes.length - written);
|
|
5551
|
+
}
|
|
5552
|
+
} finally {
|
|
5553
|
+
fs7.closeSync(descriptor);
|
|
5554
|
+
}
|
|
5555
|
+
},
|
|
5556
|
+
writeFileExclusive(filePath, contents) {
|
|
5557
|
+
try {
|
|
5558
|
+
fs7.writeFileSync(filePath, contents, { encoding: "utf8", flag: "wx" });
|
|
5559
|
+
return true;
|
|
5560
|
+
} catch (error) {
|
|
5561
|
+
if (isErrorWithCode(error, "EEXIST")) {
|
|
5562
|
+
return false;
|
|
5563
|
+
}
|
|
5564
|
+
throw error;
|
|
5565
|
+
}
|
|
5566
|
+
},
|
|
5567
|
+
hashFile(filePath) {
|
|
5568
|
+
try {
|
|
5569
|
+
return createHash4("sha256").update(fs7.readFileSync(filePath)).digest("hex");
|
|
5570
|
+
} catch (error) {
|
|
5571
|
+
if (isEnoent3(error) || isErrorWithCode(error, "EISDIR")) {
|
|
5572
|
+
return void 0;
|
|
5573
|
+
}
|
|
5574
|
+
throw error;
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
};
|
|
5578
|
+
function realIsProcessAlive(pid) {
|
|
5579
|
+
try {
|
|
5580
|
+
process.kill(pid, 0);
|
|
5581
|
+
return true;
|
|
5582
|
+
} catch (error) {
|
|
5583
|
+
return isErrorWithCode(error, "EPERM");
|
|
5584
|
+
}
|
|
5585
|
+
}
|
|
5586
|
+
function realIsProcessZombie(pid) {
|
|
5587
|
+
const result = spawnSync("ps", ["-o", "stat=", "-p", String(pid)], { encoding: "utf8" });
|
|
5588
|
+
return result.status === 0 && result.stdout.trim().startsWith("Z");
|
|
5589
|
+
}
|
|
5590
|
+
function realIsProcessRunning(pid) {
|
|
5591
|
+
return realIsProcessAlive(pid) && !realIsProcessZombie(pid);
|
|
5592
|
+
}
|
|
5593
|
+
var realRunPort = {
|
|
5594
|
+
run(command, args) {
|
|
5595
|
+
const result = spawnSync(command, [...args], { encoding: "utf8" });
|
|
5596
|
+
return { status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
5597
|
+
}
|
|
5598
|
+
};
|
|
5599
|
+
function resolveGitBranch(run, cwd) {
|
|
5600
|
+
const result = run.run("git", ["-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
5601
|
+
if (result.status !== 0) {
|
|
5602
|
+
return {};
|
|
5603
|
+
}
|
|
5604
|
+
const name = result.stdout.trim();
|
|
5605
|
+
if (name === "") {
|
|
5606
|
+
return {};
|
|
5607
|
+
}
|
|
5608
|
+
if (name === "HEAD") {
|
|
5609
|
+
return { branchDetached: true };
|
|
5610
|
+
}
|
|
5611
|
+
return { branch: name, branchDetached: false };
|
|
5612
|
+
}
|
|
5613
|
+
var realProcPort = {
|
|
5614
|
+
env: process.env,
|
|
5615
|
+
argv: process.argv.slice(2),
|
|
5616
|
+
exit: (code) => process.exit(code)
|
|
5617
|
+
};
|
|
5618
|
+
var EXECUTE_BITS_MASK = 73;
|
|
5619
|
+
function listVersionsDir(dir) {
|
|
5620
|
+
let entries;
|
|
5621
|
+
try {
|
|
5622
|
+
entries = fs7.readdirSync(dir, { withFileTypes: true });
|
|
5623
|
+
} catch (error) {
|
|
5624
|
+
if (isEnoent3(error)) {
|
|
5625
|
+
return [];
|
|
5626
|
+
}
|
|
5627
|
+
throw error;
|
|
5628
|
+
}
|
|
5629
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
5630
|
+
const fullPath = path19.join(dir, entry.name);
|
|
5631
|
+
const stat = fs7.statSync(fullPath);
|
|
5632
|
+
return {
|
|
5633
|
+
name: entry.name,
|
|
5634
|
+
isFile: true,
|
|
5635
|
+
isExecutable: (stat.mode & EXECUTE_BITS_MASK) !== 0,
|
|
5636
|
+
sizeBytes: stat.size
|
|
5637
|
+
};
|
|
5638
|
+
});
|
|
5639
|
+
}
|
|
5640
|
+
var WINDOWS_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PS1";
|
|
5641
|
+
function windowsPathExtensions(pathext) {
|
|
5642
|
+
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
5643
|
+
}
|
|
5644
|
+
function resolveExecutableCandidate(dir, name, env) {
|
|
5645
|
+
const candidate = path19.join(dir, name);
|
|
5646
|
+
if (env.platform === "win32") {
|
|
5647
|
+
const extensions = windowsPathExtensions(env.pathext);
|
|
5648
|
+
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
5649
|
+
if (alreadyHasRecognisedExtension) {
|
|
5650
|
+
return env.statFileMode(candidate) !== void 0 ? candidate : void 0;
|
|
5651
|
+
}
|
|
5652
|
+
for (const ext of extensions) {
|
|
5653
|
+
const withExt = candidate + ext;
|
|
5654
|
+
if (env.statFileMode(withExt) !== void 0) {
|
|
5655
|
+
return withExt;
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5658
|
+
return void 0;
|
|
5659
|
+
}
|
|
5660
|
+
const mode = env.statFileMode(candidate);
|
|
5661
|
+
return mode !== void 0 && (mode & EXECUTE_BITS_MASK) !== 0 ? candidate : void 0;
|
|
5662
|
+
}
|
|
5663
|
+
function realStatFileMode(candidate) {
|
|
5664
|
+
try {
|
|
5665
|
+
const stat = fs7.statSync(candidate);
|
|
5666
|
+
return stat.isFile() ? stat.mode : void 0;
|
|
5667
|
+
} catch (error) {
|
|
5668
|
+
if (isEnoent3(error)) {
|
|
5669
|
+
return void 0;
|
|
5670
|
+
}
|
|
5671
|
+
throw error;
|
|
5672
|
+
}
|
|
5673
|
+
}
|
|
5674
|
+
function findExecutableInDir(dir, name) {
|
|
5675
|
+
return resolveExecutableCandidate(dir, name, {
|
|
5676
|
+
platform: process.platform,
|
|
5677
|
+
pathext: process.env.PATHEXT,
|
|
5678
|
+
statFileMode: realStatFileMode
|
|
5679
|
+
});
|
|
5680
|
+
}
|
|
5681
|
+
function searchPathForExecutable(pathDirs, name, findExecutable) {
|
|
5682
|
+
for (const dir of pathDirs) {
|
|
5683
|
+
const found = findExecutable(dir, name);
|
|
5684
|
+
if (found !== void 0) {
|
|
5685
|
+
return found;
|
|
5686
|
+
}
|
|
5687
|
+
}
|
|
5688
|
+
return void 0;
|
|
5689
|
+
}
|
|
5690
|
+
function resolveOwnExecutablePath(env) {
|
|
5691
|
+
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
5692
|
+
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
5693
|
+
}
|
|
5694
|
+
const candidate = env.argv1 === void 0 ? env.execPath : path19.resolve(env.cwd, env.argv1);
|
|
5695
|
+
const candidateDir = path19.dirname(candidate);
|
|
5696
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => path19.resolve(dir) === candidateDir);
|
|
5697
|
+
if (candidateDirOnPath) {
|
|
5698
|
+
return candidate;
|
|
5699
|
+
}
|
|
5700
|
+
const viaPath = searchPathForExecutable(env.pathDirs, path19.basename(candidate), env.findExecutableInDir);
|
|
5701
|
+
return viaPath ?? candidate;
|
|
5702
|
+
}
|
|
5703
|
+
function realOwnExecutablePath() {
|
|
5704
|
+
return resolveOwnExecutablePath({
|
|
5705
|
+
argv1: process.argv[1],
|
|
5706
|
+
execPath: process.execPath,
|
|
5707
|
+
cwd: process.cwd(),
|
|
5708
|
+
pathDirs: (process.env.PATH ?? "").split(path19.delimiter).filter((dir) => dir !== ""),
|
|
5709
|
+
findExecutableInDir
|
|
5710
|
+
});
|
|
5711
|
+
}
|
|
5712
|
+
function realResolveClaudeBinary(ownInstallDirs) {
|
|
5713
|
+
return () => discoverClaudeBinary({
|
|
5714
|
+
listVersionsDir,
|
|
5715
|
+
pathDirs: (process.env.PATH ?? "").split(path19.delimiter).filter((dir) => dir !== ""),
|
|
5716
|
+
findExecutableInDir,
|
|
5717
|
+
ownInstallDirs: [...ownInstallDirs]
|
|
5718
|
+
});
|
|
5719
|
+
}
|
|
5720
|
+
|
|
5721
|
+
// src/checkReport.ts
|
|
5722
|
+
import path20 from "node:path";
|
|
5723
|
+
import { z as z10 } from "zod";
|
|
5724
|
+
|
|
5725
|
+
// src/cli/bool.ts
|
|
5726
|
+
function parseBoolWord(input) {
|
|
5727
|
+
switch (input) {
|
|
5728
|
+
case "true":
|
|
5729
|
+
case "1":
|
|
5730
|
+
return true;
|
|
5731
|
+
case "false":
|
|
5732
|
+
case "0":
|
|
5733
|
+
return false;
|
|
5734
|
+
default:
|
|
5735
|
+
return void 0;
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
|
+
|
|
5739
|
+
// src/cli/envBool.ts
|
|
5740
|
+
var InvalidEnvBoolError = class extends UsageError {
|
|
5741
|
+
constructor(variable, value) {
|
|
5742
|
+
super(`${variable} must be one of true, false, 1 or 0, got "${value}".`);
|
|
5743
|
+
this.variable = variable;
|
|
5744
|
+
this.value = value;
|
|
5745
|
+
this.name = "InvalidEnvBoolError";
|
|
5746
|
+
}
|
|
5747
|
+
variable;
|
|
5748
|
+
value;
|
|
5749
|
+
};
|
|
5750
|
+
function parseEnvBool(variable, value) {
|
|
5751
|
+
if (value === void 0 || value === "") {
|
|
5752
|
+
return void 0;
|
|
5753
|
+
}
|
|
5754
|
+
const parsed = parseBoolWord(value);
|
|
5755
|
+
if (parsed === void 0) {
|
|
5756
|
+
throw new InvalidEnvBoolError(variable, value);
|
|
5757
|
+
}
|
|
5758
|
+
return parsed;
|
|
5759
|
+
}
|
|
5760
|
+
|
|
5761
|
+
// src/checkReport.ts
|
|
5762
|
+
var HISTORY_PROJECTS_KEY_PREFIX = "history/projects/";
|
|
5763
|
+
function projectFragmentOf(rawKey) {
|
|
5764
|
+
return rawKey.startsWith(HISTORY_PROJECTS_KEY_PREFIX) ? rawKey.slice(HISTORY_PROJECTS_KEY_PREFIX.length) : void 0;
|
|
5765
|
+
}
|
|
5766
|
+
function existingProjectNames(facts) {
|
|
5767
|
+
const names = /* @__PURE__ */ new Set();
|
|
5768
|
+
const prefix = "projects/";
|
|
5769
|
+
for (const rel of facts.entries.keys()) {
|
|
5770
|
+
if (!rel.startsWith(prefix)) {
|
|
5771
|
+
continue;
|
|
5772
|
+
}
|
|
5773
|
+
const rest = rel.slice(prefix.length);
|
|
5774
|
+
const head = rest.split("/")[0];
|
|
5775
|
+
if (head !== void 0 && head !== "") {
|
|
5776
|
+
names.add(head);
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
return [...names];
|
|
5780
|
+
}
|
|
5781
|
+
function flagAmbiguousEncodings(flattened, facts) {
|
|
5782
|
+
const fragments = [];
|
|
5783
|
+
for (const rule of flattened.rules.values()) {
|
|
5784
|
+
const fragment = projectFragmentOf(rule.rawKey);
|
|
5785
|
+
if (fragment !== void 0) {
|
|
5786
|
+
fragments.push(fragment);
|
|
5787
|
+
}
|
|
5788
|
+
}
|
|
5789
|
+
if (fragments.length === 0) {
|
|
5790
|
+
return [];
|
|
5791
|
+
}
|
|
5792
|
+
return detectEncodingAmbiguity(fragments, { home: facts.home, existingNames: existingProjectNames(facts) });
|
|
5793
|
+
}
|
|
5794
|
+
function formatDecision(decision) {
|
|
5795
|
+
const status = decision.shared ? "shared" : "hidden";
|
|
5796
|
+
let reason;
|
|
5797
|
+
switch (decision.via) {
|
|
5798
|
+
case "secret-floor":
|
|
5799
|
+
reason = "secret (never shared, cannot be overridden by any layer)";
|
|
5800
|
+
break;
|
|
5801
|
+
case "unclassified":
|
|
5802
|
+
reason = "unclassified entry (no category recognises it)";
|
|
5803
|
+
break;
|
|
5804
|
+
case "entry-rule":
|
|
5805
|
+
reason = decision.rule === void 0 ? "an entries rule" : `entries rule "${decision.rule.rawKey}" from layer ${String(decision.rule.layer)}`;
|
|
5806
|
+
break;
|
|
5807
|
+
case "category-override":
|
|
5808
|
+
reason = `category "${decision.category ?? "?"}" overridden by a layer`;
|
|
5809
|
+
break;
|
|
5810
|
+
case "category-default":
|
|
5811
|
+
reason = `category "${decision.category ?? "?"}" shipped default`;
|
|
5812
|
+
break;
|
|
5813
|
+
}
|
|
5814
|
+
const eliminatedNote = decision.eliminated !== void 0 && decision.eliminated.length > 0 ? ` [${String(decision.eliminated.length)} more specific rule(s) eliminated by a failing when-condition]` : "";
|
|
5815
|
+
return `${decision.relPath}: ${status} \u2014 ${reason}${eliminatedNote}`;
|
|
5816
|
+
}
|
|
5817
|
+
function parseKeychainServiceName(stderr) {
|
|
5818
|
+
const match = /"svce"<blob>="([^"]*)"/.exec(stderr);
|
|
5819
|
+
return match?.[1];
|
|
5820
|
+
}
|
|
5821
|
+
function lookupKeychainService(run, farmRoot) {
|
|
5822
|
+
const result = run.run("security", ["find-generic-password", "-a", farmRoot, "-g"]);
|
|
5823
|
+
if (result.status !== 0) {
|
|
5824
|
+
return {
|
|
5825
|
+
checked: true,
|
|
5826
|
+
found: false,
|
|
5827
|
+
note: `No Keychain entry found for account "${farmRoot}" (security exited ${result.status === null ? "with no status" : String(result.status)}).`
|
|
5828
|
+
};
|
|
5829
|
+
}
|
|
5830
|
+
const serviceName = parseKeychainServiceName(result.stderr);
|
|
5831
|
+
return {
|
|
5832
|
+
checked: true,
|
|
5833
|
+
found: true,
|
|
5834
|
+
...serviceName === void 0 ? {} : { serviceName },
|
|
5835
|
+
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}".`
|
|
5836
|
+
};
|
|
5837
|
+
}
|
|
5838
|
+
var SettingsSecretsShapeSchema = z10.looseObject({
|
|
5839
|
+
env: z10.record(z10.string(), z10.unknown()).optional(),
|
|
5840
|
+
hooks: z10.record(z10.string(), z10.array(z10.unknown())).optional()
|
|
5841
|
+
});
|
|
5842
|
+
function hookCommandCountOf(group) {
|
|
5843
|
+
if (typeof group !== "object" || group === null) {
|
|
5844
|
+
return 0;
|
|
5845
|
+
}
|
|
5846
|
+
if (!("hooks" in group)) {
|
|
5847
|
+
return 0;
|
|
5848
|
+
}
|
|
5849
|
+
return Array.isArray(group.hooks) ? group.hooks.length : 0;
|
|
5850
|
+
}
|
|
5851
|
+
function countHookCommands(hooks) {
|
|
5852
|
+
if (hooks === void 0) {
|
|
5853
|
+
return 0;
|
|
5854
|
+
}
|
|
5855
|
+
let total = 0;
|
|
5856
|
+
for (const groups of Object.values(hooks)) {
|
|
5857
|
+
for (const group of groups) {
|
|
5858
|
+
total += hookCommandCountOf(group);
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
return total;
|
|
5862
|
+
}
|
|
5863
|
+
function inspectSettingsExposure(params) {
|
|
5864
|
+
if (!params.settingsShared) {
|
|
5865
|
+
return [];
|
|
5866
|
+
}
|
|
5867
|
+
const reports = [];
|
|
5868
|
+
for (const [file, raw] of Object.entries(params.files)) {
|
|
5869
|
+
if (raw === void 0) {
|
|
5870
|
+
continue;
|
|
5871
|
+
}
|
|
5872
|
+
let parsed;
|
|
5873
|
+
try {
|
|
5874
|
+
parsed = JSON.parse(raw);
|
|
5875
|
+
} catch {
|
|
5876
|
+
continue;
|
|
5877
|
+
}
|
|
5878
|
+
const result = SettingsSecretsShapeSchema.safeParse(parsed);
|
|
5879
|
+
if (!result.success) {
|
|
5880
|
+
continue;
|
|
5881
|
+
}
|
|
5882
|
+
const envKeyNames = Object.keys(result.data.env ?? {});
|
|
5883
|
+
const hookEventNames = Object.keys(result.data.hooks ?? {});
|
|
5884
|
+
const hookCommandCount = countHookCommands(result.data.hooks);
|
|
5885
|
+
if (envKeyNames.length === 0 && hookCommandCount === 0) {
|
|
5886
|
+
continue;
|
|
5887
|
+
}
|
|
5888
|
+
reports.push({ file, envKeyNames, hookEventNames, hookCommandCount });
|
|
5889
|
+
}
|
|
5890
|
+
return reports;
|
|
5891
|
+
}
|
|
5892
|
+
function buildCredentialReport(provider, identity) {
|
|
5893
|
+
const identityCredential = identity?.credential === void 0 ? void 0 : summariseCredential(identity.credential);
|
|
5894
|
+
return {
|
|
5895
|
+
applies: provider !== void 0 ? "provider" : identityCredential !== void 0 ? "identity" : "stored-login",
|
|
5896
|
+
...provider === void 0 ? {} : {
|
|
5897
|
+
provider: provider.problem === void 0 ? { name: provider.name, credential: summariseCredential(provider.definition.credential) } : { name: provider.name, problem: provider.problem }
|
|
5898
|
+
},
|
|
5899
|
+
...identityCredential === void 0 ? {} : { identity: identityCredential }
|
|
5900
|
+
};
|
|
5901
|
+
}
|
|
5902
|
+
function runCheck(params) {
|
|
5903
|
+
const facts = buildEntryFacts({
|
|
5904
|
+
fs: params.farmFs,
|
|
5905
|
+
claudeHome: params.claudeHome,
|
|
5906
|
+
home: params.home,
|
|
5907
|
+
cwd: params.cwd,
|
|
5908
|
+
nowMs: params.nowMs,
|
|
5909
|
+
env: params.env,
|
|
5910
|
+
...params.branch === void 0 ? {} : { branch: params.branch },
|
|
5911
|
+
...params.branchDetached === void 0 ? {} : { branchDetached: params.branchDetached }
|
|
5912
|
+
});
|
|
5913
|
+
const resolved = resolveDecisions({ facts, cascade: params.cascade, classification: params.classification });
|
|
5914
|
+
const decisionLines = [...resolved.decisions.values()].slice().sort((a, b) => a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0).map(formatDecision);
|
|
5915
|
+
const projectEncodingAmbiguities = flagAmbiguousEncodings(resolved.flattened, facts);
|
|
5916
|
+
const ambientCredential = evaluateAmbientCredentialGuard({
|
|
5917
|
+
env: params.env,
|
|
5918
|
+
allowAmbientCredential: params.identity?.allowAmbientCredential ?? false,
|
|
5919
|
+
allowAmbientCredentialOverride: parseEnvBool("AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL", params.env.AGENT_SHIM_ALLOW_AMBIENT_CREDENTIAL) === true,
|
|
5920
|
+
...params.identityName === void 0 ? {} : { identityName: params.identityName }
|
|
5921
|
+
});
|
|
5922
|
+
const settingsShared = resolved.flattened.categories.get("settings") ?? SHIPPED_CATEGORY_DEFAULTS.settings;
|
|
5923
|
+
const settingsExposure = inspectSettingsExposure({ settingsShared, files: params.settingsFiles });
|
|
5924
|
+
const keychain = params.platform === "darwin" && params.run !== void 0 && params.farmRoot !== void 0 ? lookupKeychainService(params.run, params.farmRoot) : void 0;
|
|
5925
|
+
return {
|
|
5926
|
+
...params.identityName === void 0 ? {} : { identityName: params.identityName },
|
|
5927
|
+
identitySource: params.identitySource,
|
|
5928
|
+
...params.poolPick === void 0 ? {} : { poolPick: params.poolPick },
|
|
5929
|
+
...params.configProfileName === void 0 ? {} : { configProfileName: params.configProfileName },
|
|
5930
|
+
configProfileSource: params.configProfileSource,
|
|
5931
|
+
resolved,
|
|
5932
|
+
decisionLines,
|
|
5933
|
+
projectEncodingAmbiguities,
|
|
5934
|
+
ambientCredential,
|
|
5935
|
+
...keychain === void 0 ? {} : { keychain },
|
|
5936
|
+
settingsExposure,
|
|
5937
|
+
credential: buildCredentialReport(params.provider, params.identity)
|
|
5938
|
+
};
|
|
5939
|
+
}
|
|
5940
|
+
function formatCheckReport(report) {
|
|
5941
|
+
const lines = [];
|
|
5942
|
+
lines.push(`Identity: ${report.identityName ?? "(none)"} (${report.identitySource})`);
|
|
5943
|
+
if (report.poolPick !== void 0) {
|
|
5944
|
+
const top = report.poolPick.candidates.find((candidate) => candidate.identity === report.poolPick?.pick);
|
|
5945
|
+
lines.push(`Pool: ${report.poolPick.pool}${top === void 0 ? ", every member is refused right now" : `, would pick ${top.identity} (${top.reasons.join("; ")})`}`);
|
|
5946
|
+
}
|
|
5947
|
+
lines.push(`Configuration profile: ${report.configProfileName ?? "(none)"} (${report.configProfileSource})`);
|
|
5948
|
+
lines.push("", "Layers (shallowest/earliest first):");
|
|
5949
|
+
for (const layer of report.resolved.assembled.layers) {
|
|
5950
|
+
lines.push(` [${String(layer.id)}] ${layer.kind}: ${layer.source}`);
|
|
5951
|
+
}
|
|
5952
|
+
lines.push("", "Resolved entries:");
|
|
5953
|
+
if (report.decisionLines.length === 0) {
|
|
5954
|
+
lines.push(" (nothing under ~/.claude to report)");
|
|
5955
|
+
}
|
|
5956
|
+
for (const line of report.decisionLines) {
|
|
5957
|
+
lines.push(` ${line}`);
|
|
5958
|
+
}
|
|
5959
|
+
if (report.projectEncodingAmbiguities.length > 0) {
|
|
5960
|
+
lines.push("", "Ambiguous history/projects/ encodings:");
|
|
5961
|
+
for (const ambiguity of report.projectEncodingAmbiguities) {
|
|
5962
|
+
lines.push(` "${ambiguity.fragment}" -> "${ambiguity.encoded}" (${ambiguity.reason}): ${ambiguity.detail}`);
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
if (report.resolved.diagnostics.length > 0) {
|
|
5966
|
+
lines.push("", "Diagnostics:");
|
|
5967
|
+
for (const diagnostic of report.resolved.diagnostics) {
|
|
5968
|
+
lines.push(` [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5971
|
+
lines.push("", "Credential (source kinds and targets only, never values):");
|
|
5972
|
+
const { credential } = report;
|
|
5973
|
+
if (credential.provider !== void 0) {
|
|
5974
|
+
lines.push(
|
|
5975
|
+
` Provider ${credential.provider.name}: ${credential.provider.credential === void 0 ? `unusable: ${credential.provider.problem ?? ""}` : formatCredentialSummary(credential.provider.credential)}`
|
|
5976
|
+
);
|
|
5977
|
+
}
|
|
5978
|
+
if (credential.identity !== void 0) {
|
|
5979
|
+
lines.push(
|
|
5980
|
+
` Identity ${report.identityName ?? "(none)"}: ${formatCredentialSummary(credential.identity)}${credential.applies === "provider" ? " (not used: the provider's credential applies)" : ""}`
|
|
5981
|
+
);
|
|
5982
|
+
}
|
|
5983
|
+
if (credential.applies === "stored-login") {
|
|
5984
|
+
lines.push(" The identity's stored login (no credential block applies).");
|
|
5985
|
+
}
|
|
5986
|
+
lines.push("", "Ambient-credential exposure:");
|
|
5987
|
+
lines.push(
|
|
5988
|
+
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")
|
|
5989
|
+
);
|
|
5990
|
+
if (report.keychain !== void 0) {
|
|
5991
|
+
lines.push("", "macOS Keychain:");
|
|
5992
|
+
lines.push(` ${report.keychain.note}`);
|
|
5993
|
+
}
|
|
5994
|
+
if (report.settingsExposure.length > 0) {
|
|
5995
|
+
lines.push("", "Settings exposure (names and counts only, never values):");
|
|
5996
|
+
for (const exposure of report.settingsExposure) {
|
|
5997
|
+
lines.push(
|
|
5998
|
+
` ${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)`
|
|
5999
|
+
);
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
return lines;
|
|
6003
|
+
}
|
|
6004
|
+
function checkReportToJson(report) {
|
|
6005
|
+
const decisions = [...report.resolved.decisions.values()].sort((a, b) => a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0);
|
|
6006
|
+
return {
|
|
6007
|
+
identity: { name: report.identityName ?? null, source: report.identitySource },
|
|
6008
|
+
...report.poolPick === void 0 ? {} : { pool: report.poolPick },
|
|
6009
|
+
configProfile: { name: report.configProfileName ?? null, source: report.configProfileSource },
|
|
6010
|
+
layers: report.resolved.assembled.layers.map((layer) => ({ id: layer.id, kind: layer.kind, source: layer.source })),
|
|
6011
|
+
entries: decisions.map((decision) => ({
|
|
6012
|
+
path: decision.relPath,
|
|
6013
|
+
shared: decision.shared,
|
|
6014
|
+
via: decision.via,
|
|
6015
|
+
category: decision.category,
|
|
6016
|
+
...decision.rule === void 0 ? {} : { rule: { key: decision.rule.rawKey, layer: decision.rule.layer } },
|
|
6017
|
+
...decision.eliminated === void 0 || decision.eliminated.length === 0 ? {} : { eliminated: decision.eliminated.map((eliminated) => ({ key: eliminated.rule.rawKey, failed: eliminated.failed })) }
|
|
6018
|
+
})),
|
|
6019
|
+
projectEncodingAmbiguities: report.projectEncodingAmbiguities,
|
|
6020
|
+
diagnostics: report.resolved.diagnostics,
|
|
6021
|
+
ambientCredential: report.ambientCredential.ok ? { ok: true } : { ok: false, variable: report.ambientCredential.variable, message: report.ambientCredential.message },
|
|
6022
|
+
...report.keychain === void 0 ? {} : { keychain: report.keychain },
|
|
6023
|
+
settingsExposure: report.settingsExposure,
|
|
6024
|
+
credential: report.credential
|
|
6025
|
+
};
|
|
6026
|
+
}
|
|
6027
|
+
function checkReportHasWarnings(report) {
|
|
6028
|
+
return report.resolved.diagnostics.some((diagnostic) => diagnostic.severity !== "info") || report.projectEncodingAmbiguities.length > 0 || !report.ambientCredential.ok || report.credential.provider?.problem !== void 0;
|
|
6029
|
+
}
|
|
6030
|
+
function loadCheckProvider(paths, name) {
|
|
6031
|
+
let definition;
|
|
6032
|
+
try {
|
|
6033
|
+
definition = readProvider(paths, name);
|
|
6034
|
+
} catch (error) {
|
|
6035
|
+
if (error instanceof ConfigValidationError || error instanceof LegacyProviderFileError) {
|
|
6036
|
+
return { name, problem: error.message };
|
|
6037
|
+
}
|
|
6038
|
+
throw error;
|
|
6039
|
+
}
|
|
6040
|
+
return definition === void 0 ? { name, problem: `no provider named "${name}"` } : { name, definition };
|
|
6041
|
+
}
|
|
6042
|
+
function collectCheckReport(params) {
|
|
6043
|
+
const { paths, cwd } = params;
|
|
6044
|
+
const home = os3.homedir();
|
|
6045
|
+
const claudeHome = resolveClaudeHome();
|
|
6046
|
+
const read = cosmiconfigReader();
|
|
6047
|
+
const classification = loadClassification(paths);
|
|
6048
|
+
const loaded = loadCascadeInput({ paths, home, cwd, read });
|
|
6049
|
+
const selections = readDirectorySelections(loaded);
|
|
6050
|
+
const git = resolveGitBranch(realRunPort, cwd);
|
|
6051
|
+
const decidedIdentity = decideIdentity({
|
|
6052
|
+
env: params.env,
|
|
6053
|
+
argv0Identity: params.identity,
|
|
6054
|
+
directoryPinnedIdentity: selections.identity,
|
|
6055
|
+
readActiveIdentityFile: () => {
|
|
6056
|
+
const raw = realFsPort.readFileUtf8(paths.activeIdentityFile);
|
|
6057
|
+
if (raw === void 0) {
|
|
6058
|
+
return void 0;
|
|
6059
|
+
}
|
|
6060
|
+
const trimmed = raw.trim();
|
|
6061
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
6062
|
+
}
|
|
6063
|
+
});
|
|
6064
|
+
let poolPick;
|
|
6065
|
+
if (decidedIdentity.pool !== void 0) {
|
|
6066
|
+
const pool = loaded.globalConfig?.pools?.[decidedIdentity.pool];
|
|
6067
|
+
if (pool === void 0) {
|
|
6068
|
+
throw new PoolNotFoundError(decidedIdentity.pool);
|
|
6069
|
+
}
|
|
6070
|
+
poolPick = collectPoolPick({ paths, fs: realFsPort, usageFs: realFarmFs, poolName: decidedIdentity.pool, pool, directory: cwd, nowMs: Date.now() });
|
|
6071
|
+
}
|
|
6072
|
+
const identityDecision = poolPick?.pick === void 0 ? decidedIdentity : { ...decidedIdentity, name: poolPick.pick };
|
|
6073
|
+
const loadedIdentity = identityDecision.name === void 0 ? void 0 : loadIdentity(paths.identitiesDir, identityDecision.name, realFsPort);
|
|
6074
|
+
const configProfileDecision = decideConfigProfile({
|
|
6075
|
+
env: params.env,
|
|
6076
|
+
directoryRuleConfigProfile: selections.configProfile,
|
|
6077
|
+
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
6078
|
+
globalDefaultConfigProfile: loaded.globalConfig?.defaultConfigProfile
|
|
6079
|
+
});
|
|
6080
|
+
const cascade = loadCascadeInput({
|
|
6081
|
+
paths,
|
|
6082
|
+
home,
|
|
6083
|
+
cwd,
|
|
6084
|
+
read,
|
|
6085
|
+
...configProfileDecision.name === void 0 ? {} : { baseConfigProfile: configProfileDecision.name }
|
|
6086
|
+
}).input;
|
|
6087
|
+
const providerName = cascadeProviderName(cascade);
|
|
6088
|
+
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
6089
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : path20.join(paths.identitiesDir, identityDecision.name);
|
|
6090
|
+
const settingsFiles = {
|
|
6091
|
+
"settings.json": realFsPort.readFileUtf8(path20.join(claudeHome, "settings.json")),
|
|
6092
|
+
"settings.local.json": realFsPort.readFileUtf8(path20.join(claudeHome, "settings.local.json"))
|
|
6093
|
+
};
|
|
6094
|
+
const report = runCheck({
|
|
6095
|
+
cwd,
|
|
6096
|
+
home,
|
|
6097
|
+
claudeHome,
|
|
6098
|
+
env: params.env,
|
|
6099
|
+
...git.branch === void 0 ? {} : { branch: git.branch },
|
|
6100
|
+
...git.branchDetached === void 0 ? {} : { branchDetached: git.branchDetached },
|
|
6101
|
+
nowMs: Date.now(),
|
|
6102
|
+
farmFs: realFarmFs,
|
|
6103
|
+
cascade,
|
|
6104
|
+
classification,
|
|
6105
|
+
...identityDecision.name === void 0 ? {} : { identityName: identityDecision.name },
|
|
6106
|
+
identitySource: identityDecision.source,
|
|
6107
|
+
...poolPick === void 0 ? {} : { poolPick },
|
|
6108
|
+
...configProfileDecision.name === void 0 ? {} : { configProfileName: configProfileDecision.name },
|
|
6109
|
+
configProfileSource: configProfileDecision.source,
|
|
6110
|
+
...loadedIdentity === void 0 ? {} : { identity: loadedIdentity.config },
|
|
6111
|
+
settingsFiles,
|
|
6112
|
+
run: realRunPort,
|
|
6113
|
+
...farmRoot === void 0 ? {} : { farmRoot },
|
|
6114
|
+
platform: process.platform,
|
|
6115
|
+
...provider === void 0 ? {} : { provider }
|
|
6116
|
+
});
|
|
6117
|
+
return report;
|
|
6118
|
+
}
|
|
6119
|
+
|
|
6120
|
+
// src/claudeShim.ts
|
|
6121
|
+
import fs8 from "node:fs";
|
|
6122
|
+
import path21 from "node:path";
|
|
6123
|
+
import { z as z11 } from "zod";
|
|
6124
|
+
var ClaudeShimStateSchema = z11.strictObject({
|
|
6125
|
+
targetPath: z11.string().min(1),
|
|
6126
|
+
method: z11.enum(["hardlink", "copy"]),
|
|
6127
|
+
installedAtMs: z11.number()
|
|
6128
|
+
});
|
|
6129
|
+
function commandFilename(ownExecutablePath, commandName) {
|
|
6130
|
+
return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
|
|
6131
|
+
}
|
|
6132
|
+
function resolveOwnInstallDirs(paths, ownExecutablePath) {
|
|
6133
|
+
const dirs = [path21.dirname(ownExecutablePath)];
|
|
6134
|
+
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6135
|
+
if (state !== void 0) {
|
|
6136
|
+
dirs.push(path21.dirname(state.targetPath));
|
|
6137
|
+
}
|
|
6138
|
+
return dirs;
|
|
6139
|
+
}
|
|
6140
|
+
var nodeLinkFs = { link: fs8.linkSync, copyFile: fs8.copyFileSync, chmod: fs8.chmodSync };
|
|
6141
|
+
function findPathShadow(params) {
|
|
6142
|
+
const resolvedTargetDir = path21.resolve(params.targetDir);
|
|
6143
|
+
for (const dir of params.pathDirs) {
|
|
6144
|
+
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
6145
|
+
if (found === void 0) {
|
|
6146
|
+
continue;
|
|
6147
|
+
}
|
|
6148
|
+
return path21.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
6149
|
+
}
|
|
6150
|
+
return params.pathDirs.some((dir) => path21.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
6151
|
+
}
|
|
6152
|
+
|
|
6153
|
+
// src/doctorReport.ts
|
|
6154
|
+
import fs9 from "node:fs";
|
|
6155
|
+
import path22 from "node:path";
|
|
6156
|
+
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
6157
|
+
if (status.status !== "shadowed") {
|
|
6158
|
+
return status;
|
|
6159
|
+
}
|
|
6160
|
+
return realpath(status.by) === realpath(ownExecutablePath) ? { status: "ok" } : status;
|
|
6161
|
+
}
|
|
6162
|
+
function validateJson(schema, input) {
|
|
6163
|
+
if (input.raw === void 0) {
|
|
6164
|
+
return { ok: false, message: `${input.path} is missing.` };
|
|
6165
|
+
}
|
|
6166
|
+
let parsed;
|
|
6167
|
+
try {
|
|
6168
|
+
parsed = JSON.parse(input.raw);
|
|
6169
|
+
} catch (error) {
|
|
6170
|
+
return { ok: false, message: `${input.path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}` };
|
|
6171
|
+
}
|
|
6172
|
+
const result = schema.safeParse(parsed);
|
|
6173
|
+
if (!result.success) {
|
|
6174
|
+
return { ok: false, message: new ConfigValidationError(input.path, result.error.issues).message };
|
|
6175
|
+
}
|
|
6176
|
+
return { ok: true, data: result.data };
|
|
6177
|
+
}
|
|
6178
|
+
function pushPathResolution(push, resolution) {
|
|
6179
|
+
const ownDir = path22.dirname(resolution.ownExecutablePath);
|
|
6180
|
+
switch (resolution.agentShim.status) {
|
|
6181
|
+
case "ok":
|
|
6182
|
+
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
6183
|
+
break;
|
|
6184
|
+
case "not-on-path":
|
|
6185
|
+
push(
|
|
6186
|
+
"path-resolution",
|
|
6187
|
+
"warn",
|
|
6188
|
+
`${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.`,
|
|
6189
|
+
"agent-shim"
|
|
6190
|
+
);
|
|
6191
|
+
break;
|
|
6192
|
+
case "shadowed":
|
|
6193
|
+
push(
|
|
6194
|
+
"path-resolution",
|
|
6195
|
+
"fail",
|
|
6196
|
+
`\`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.`,
|
|
6197
|
+
"agent-shim"
|
|
6198
|
+
);
|
|
6199
|
+
break;
|
|
6200
|
+
}
|
|
6201
|
+
if (resolution.claude === void 0) {
|
|
6202
|
+
return;
|
|
6203
|
+
}
|
|
6204
|
+
switch (resolution.claude.status) {
|
|
6205
|
+
case "ok":
|
|
6206
|
+
push("path-resolution", "pass", "`claude` on PATH resolves to the enabled shim.", "claude");
|
|
6207
|
+
break;
|
|
6208
|
+
case "not-on-path":
|
|
6209
|
+
push("path-resolution", "warn", "The enabled `claude` shim's directory is not on PATH \u2014 add it, or use `agent-shim run` instead.", "claude");
|
|
6210
|
+
break;
|
|
6211
|
+
case "shadowed":
|
|
6212
|
+
push(
|
|
6213
|
+
"path-resolution",
|
|
6214
|
+
"warn",
|
|
6215
|
+
`\`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.`,
|
|
6216
|
+
"claude"
|
|
6217
|
+
);
|
|
6218
|
+
break;
|
|
6219
|
+
}
|
|
6220
|
+
}
|
|
6221
|
+
function pushProvider(push, entry) {
|
|
6222
|
+
if (entry.raw === void 0) {
|
|
6223
|
+
push("provider", "fail", `${entry.path} is missing.`, entry.name);
|
|
6224
|
+
return;
|
|
6225
|
+
}
|
|
6226
|
+
let parsed;
|
|
6227
|
+
try {
|
|
6228
|
+
parsed = JSON.parse(entry.raw);
|
|
6229
|
+
} catch (error) {
|
|
6230
|
+
push("provider", "fail", `${entry.path} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, entry.name);
|
|
6231
|
+
return;
|
|
6232
|
+
}
|
|
6233
|
+
const conversion = legacyProviderConversion(parsed);
|
|
6234
|
+
if (conversion !== void 0) {
|
|
6235
|
+
push("provider", "fail", new LegacyProviderFileError(entry.path, conversion).message, entry.name);
|
|
6236
|
+
return;
|
|
6237
|
+
}
|
|
6238
|
+
const validated = ProviderSchema.safeParse(parsed);
|
|
6239
|
+
if (!validated.success) {
|
|
6240
|
+
push("provider", "fail", new ConfigValidationError(entry.path, validated.error.issues).message, entry.name);
|
|
6241
|
+
return;
|
|
6242
|
+
}
|
|
6243
|
+
push("provider", "pass", `${entry.name} is valid (${describeProviderEndpoint(validated.data)}, credential ${describeCredential(validated.data.credential)}).`, entry.name);
|
|
6244
|
+
}
|
|
6245
|
+
function pushLegacyName(push, params) {
|
|
6246
|
+
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
6247
|
+
if (legacyVariables.length === 0 && path22.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
6248
|
+
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
6249
|
+
return;
|
|
6250
|
+
}
|
|
6251
|
+
for (const name of legacyVariables) {
|
|
6252
|
+
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);
|
|
6253
|
+
}
|
|
6254
|
+
if (path22.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
6255
|
+
push(
|
|
6256
|
+
"legacy-name",
|
|
6257
|
+
"warn",
|
|
6258
|
+
`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.`
|
|
6259
|
+
);
|
|
6260
|
+
}
|
|
6261
|
+
}
|
|
6262
|
+
function runDoctor(params) {
|
|
6263
|
+
const findings = [];
|
|
6264
|
+
const push = (section, severity, message, subject) => {
|
|
6265
|
+
findings.push({ section, severity, message, ...subject === void 0 ? {} : { subject } });
|
|
6266
|
+
};
|
|
6267
|
+
const ambient = detectAmbientCredential(params.env);
|
|
6268
|
+
if (ambient === void 0) {
|
|
6269
|
+
push("ambient-credential", "pass", "No ambient-credential environment variable is set.");
|
|
6270
|
+
} else {
|
|
6271
|
+
push("ambient-credential", "warn", formatAmbientCredentialGuardMessage(ambient.variable));
|
|
6272
|
+
}
|
|
6273
|
+
if (params.binaryDiscovery.ok) {
|
|
6274
|
+
const { binary } = params.binaryDiscovery;
|
|
6275
|
+
const versionNote = binary.version === void 0 ? "" : `, version ${binary.version}`;
|
|
6276
|
+
push("binary-discovery", "pass", `Found ${binary.path} (${binary.source}${versionNote}).`);
|
|
6277
|
+
} else {
|
|
6278
|
+
push("binary-discovery", "fail", params.binaryDiscovery.message);
|
|
6279
|
+
}
|
|
6280
|
+
if (params.claudeShim.state === void 0) {
|
|
6281
|
+
push("claude-shim", "pass", "No `claude` command shim enabled (the default). Run `agent-shim shim enable` to add one.");
|
|
6282
|
+
} else if (!params.claudeShim.targetExists) {
|
|
6283
|
+
push(
|
|
6284
|
+
"claude-shim",
|
|
6285
|
+
"warn",
|
|
6286
|
+
`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.`
|
|
6287
|
+
);
|
|
6288
|
+
} else {
|
|
6289
|
+
push(
|
|
6290
|
+
"claude-shim",
|
|
6291
|
+
"pass",
|
|
6292
|
+
`\`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.`
|
|
6293
|
+
);
|
|
6294
|
+
}
|
|
6295
|
+
pushPathResolution(push, params.pathResolution);
|
|
6296
|
+
pushLegacyName(push, params);
|
|
6297
|
+
const profileSources = /* @__PURE__ */ new Map();
|
|
6298
|
+
for (const entry of params.configProfiles) {
|
|
6299
|
+
const validated = validateJson(ConfigProfileSchema, entry);
|
|
6300
|
+
if (!validated.ok) {
|
|
6301
|
+
push("config-profile", "fail", validated.message, entry.name);
|
|
6302
|
+
continue;
|
|
6303
|
+
}
|
|
6304
|
+
profileSources.set(entry.name, { name: entry.name, profile: validated.data });
|
|
6305
|
+
}
|
|
6306
|
+
const loadProfile = (name) => profileSources.get(name);
|
|
6307
|
+
for (const name of profileSources.keys()) {
|
|
6308
|
+
const linearised = lineariseProfile(name, loadProfile);
|
|
6309
|
+
if (linearised.diagnostics.length === 0) {
|
|
6310
|
+
push("config-profile", "pass", `${name} is valid and its extends chain resolves cleanly.`, name);
|
|
6311
|
+
} else {
|
|
6312
|
+
for (const diagnostic of linearised.diagnostics) {
|
|
6313
|
+
push("config-profile", "fail", diagnostic.message, name);
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
const validProfileNames = new Set(profileSources.keys());
|
|
6318
|
+
const validIdentityNames = /* @__PURE__ */ new Set();
|
|
6319
|
+
for (const entry of params.identities) {
|
|
6320
|
+
const validated = validateJson(IdentitySchema, entry);
|
|
6321
|
+
if (!validated.ok) {
|
|
6322
|
+
push("identity", "fail", validated.message, entry.name);
|
|
6323
|
+
continue;
|
|
6324
|
+
}
|
|
6325
|
+
validIdentityNames.add(entry.name);
|
|
6326
|
+
const defaultProfile = validated.data.defaultConfigProfile;
|
|
6327
|
+
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
6328
|
+
push(
|
|
6329
|
+
"identity",
|
|
6330
|
+
"fail",
|
|
6331
|
+
`Identity "${entry.name}" names configuration profile "${defaultProfile}" as its default, but no such profile exists.`,
|
|
6332
|
+
entry.name
|
|
6333
|
+
);
|
|
6334
|
+
} else {
|
|
6335
|
+
const credential = validated.data.credential;
|
|
6336
|
+
push(
|
|
6337
|
+
"identity",
|
|
6338
|
+
"pass",
|
|
6339
|
+
`${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}.`,
|
|
6340
|
+
entry.name
|
|
6341
|
+
);
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
|
|
6345
|
+
const pools = validatedGlobalConfig?.ok === true ? validatedGlobalConfig.data.pools ?? {} : {};
|
|
6346
|
+
for (const [poolName, pool] of Object.entries(pools)) {
|
|
6347
|
+
const missing = pool.identities.filter((member) => !validIdentityNames.has(member));
|
|
6348
|
+
if (missing.length === 0) {
|
|
6349
|
+
push("pool", "pass", `${poolName} is valid (${pool.identities.join(", ")}).`, poolName);
|
|
6350
|
+
} else {
|
|
6351
|
+
push("pool", "fail", `Pool "${poolName}" names ${missing.map((member) => `identity "${member}"`).join(" and ")}, which ${missing.length === 1 ? "does" : "do"} not exist.`, poolName);
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
const selectionExists = (selector) => {
|
|
6355
|
+
const poolName = poolNameOf(selector);
|
|
6356
|
+
return poolName === void 0 ? validIdentityNames.has(selector) : poolName in pools;
|
|
6357
|
+
};
|
|
6358
|
+
for (const entry of params.providers) {
|
|
6359
|
+
pushProvider(push, entry);
|
|
6360
|
+
}
|
|
6361
|
+
if (params.platform !== "darwin" || params.run === void 0) {
|
|
6362
|
+
push("keychain", "pass", "Skipped (not macOS).");
|
|
6363
|
+
} else {
|
|
6364
|
+
for (const entry of params.identities) {
|
|
6365
|
+
const result = lookupKeychainService(params.run, entry.farmRoot);
|
|
6366
|
+
push("keychain", result.found ? "pass" : "warn", result.note, entry.name);
|
|
6367
|
+
}
|
|
6368
|
+
}
|
|
6369
|
+
if (params.directoryRules.raw === void 0) {
|
|
6370
|
+
push("directory-rules", "pass", "No directory-rules.json configured.");
|
|
6371
|
+
} else {
|
|
6372
|
+
const validated = validateJson(DirectoryRulesSchema, params.directoryRules);
|
|
6373
|
+
if (!validated.ok) {
|
|
6374
|
+
push("directory-rules", "fail", validated.message);
|
|
6375
|
+
} else {
|
|
6376
|
+
push("directory-rules", "pass", `${params.directoryRules.path} is valid.`);
|
|
6377
|
+
for (const rule of validated.data.rules) {
|
|
6378
|
+
const badRefs = [];
|
|
6379
|
+
if (rule.identity !== void 0 && !selectionExists(rule.identity)) {
|
|
6380
|
+
badRefs.push(poolNameOf(rule.identity) === void 0 ? `identity "${rule.identity}"` : `pool "${poolNameOf(rule.identity) ?? ""}"`);
|
|
6381
|
+
}
|
|
6382
|
+
if (rule.configProfile !== void 0 && !validProfileNames.has(rule.configProfile)) {
|
|
6383
|
+
badRefs.push(`configuration profile "${rule.configProfile}"`);
|
|
6384
|
+
}
|
|
6385
|
+
if (badRefs.length > 0) {
|
|
6386
|
+
push("directory-rules", "fail", `Rule for "${rule.path}" names ${badRefs.join(" and ")}, which do not exist.`, rule.path);
|
|
6387
|
+
} else {
|
|
6388
|
+
push("directory-rules", "pass", `Rule for "${rule.path}" is valid.`, rule.path);
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6391
|
+
}
|
|
6392
|
+
}
|
|
6393
|
+
if (params.globalConfig.raw === void 0) {
|
|
6394
|
+
push("global-config", "pass", "No config.json configured.");
|
|
6395
|
+
} else {
|
|
6396
|
+
const validated = validateJson(GlobalConfigSchema, params.globalConfig);
|
|
6397
|
+
if (!validated.ok) {
|
|
6398
|
+
push("global-config", "fail", validated.message);
|
|
6399
|
+
} else {
|
|
6400
|
+
const defaultProfile = validated.data.defaultConfigProfile;
|
|
6401
|
+
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
6402
|
+
push(
|
|
6403
|
+
"global-config",
|
|
6404
|
+
"fail",
|
|
6405
|
+
`config.json names configuration profile "${defaultProfile}" as its default, but no such profile exists.`
|
|
6406
|
+
);
|
|
6407
|
+
} else {
|
|
6408
|
+
push("global-config", "pass", `${params.globalConfig.path} is valid.`);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
}
|
|
6412
|
+
if (params.categoriesLocal.raw === void 0) {
|
|
6413
|
+
push("categories-local", "pass", "No categories.local.json configured.");
|
|
6414
|
+
} else {
|
|
6415
|
+
const validated = validateJson(CategoryClassificationOverlaySchema, params.categoriesLocal);
|
|
6416
|
+
push(
|
|
6417
|
+
"categories-local",
|
|
6418
|
+
validated.ok ? "pass" : "fail",
|
|
6419
|
+
validated.ok ? `${params.categoriesLocal.path} is valid.` : validated.message
|
|
6420
|
+
);
|
|
6421
|
+
}
|
|
6422
|
+
if (params.headroom.state.raw === void 0) {
|
|
6423
|
+
push("headroom", "pass", "No headroom daemon has ever run; nothing to check.");
|
|
6424
|
+
} else {
|
|
6425
|
+
const validated = validateJson(HeadroomStateSchema, params.headroom.state);
|
|
6426
|
+
if (!validated.ok) {
|
|
6427
|
+
push("headroom", "fail", validated.message);
|
|
6428
|
+
} else {
|
|
6429
|
+
const state = validated.data;
|
|
6430
|
+
const supervisorAlive = state.supervisorPid !== void 0 && params.headroom.isRunning(state.supervisorPid);
|
|
6431
|
+
const headroomAlive = state.headroomPid !== void 0 && params.headroom.isRunning(state.headroomPid);
|
|
6432
|
+
if (state.supervisorPid === void 0) {
|
|
6433
|
+
if (state.lastError === void 0) {
|
|
6434
|
+
push("headroom", "pass", "Headroom daemon is stopped (idle shutdown) with no recorded error.");
|
|
6435
|
+
} else {
|
|
6436
|
+
push("headroom", "warn", `Headroom supervisor is not running; last error: ${state.lastError}`);
|
|
6437
|
+
}
|
|
6438
|
+
} else if (!supervisorAlive) {
|
|
6439
|
+
push(
|
|
6440
|
+
"headroom",
|
|
6441
|
+
"warn",
|
|
6442
|
+
`Headroom state names supervisor pid ${String(state.supervisorPid)}, which is not running; the next launch through headroom will start a replacement.`
|
|
6443
|
+
);
|
|
6444
|
+
} else if (state.headroomPid === void 0 || state.port === void 0) {
|
|
6445
|
+
push(
|
|
6446
|
+
"headroom",
|
|
6447
|
+
"warn",
|
|
6448
|
+
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}`
|
|
6449
|
+
);
|
|
6450
|
+
} else if (!headroomAlive) {
|
|
6451
|
+
push(
|
|
6452
|
+
"headroom",
|
|
6453
|
+
"warn",
|
|
6454
|
+
`Headroom state names daemon pid ${String(state.headroomPid)}, which is not running; its supervisor should be restarting it.`
|
|
6455
|
+
);
|
|
6456
|
+
} else {
|
|
6457
|
+
push(
|
|
6458
|
+
"headroom",
|
|
6459
|
+
"pass",
|
|
6460
|
+
`Headroom daemon is up on 127.0.0.1:${String(state.port)} (supervisor ${String(state.supervisorPid)}, daemon ${String(state.headroomPid)}).`
|
|
6461
|
+
);
|
|
6462
|
+
}
|
|
6463
|
+
if (state.lastError !== void 0 && supervisorAlive) {
|
|
6464
|
+
push("headroom", "warn", `Headroom recorded a previous error: ${state.lastError}`);
|
|
6465
|
+
}
|
|
6466
|
+
if (state.installedSource !== void 0 && isMovingGitSource(state.installedSource)) {
|
|
6467
|
+
push(
|
|
6468
|
+
"headroom",
|
|
6469
|
+
"warn",
|
|
6470
|
+
`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.`
|
|
6471
|
+
);
|
|
6472
|
+
}
|
|
6473
|
+
}
|
|
6474
|
+
}
|
|
6475
|
+
if (params.activeIdentity.raw === void 0) {
|
|
6476
|
+
push("active-identity", "pass", "No active identity set.");
|
|
6477
|
+
} else {
|
|
6478
|
+
const trimmed = params.activeIdentity.raw.trim();
|
|
6479
|
+
if (trimmed === "") {
|
|
6480
|
+
push("active-identity", "warn", "active-identity is present but empty \u2014 treated the same as unset.");
|
|
6481
|
+
} else if (!selectionExists(trimmed)) {
|
|
6482
|
+
push("active-identity", "fail", `active-identity names "${trimmed}", which does not exist.`);
|
|
6483
|
+
} else {
|
|
6484
|
+
push("active-identity", "pass", `Active identity "${trimmed}" is valid.`);
|
|
6485
|
+
}
|
|
6486
|
+
}
|
|
6487
|
+
return { findings, ok: !findings.some((finding) => finding.severity === "fail") };
|
|
6488
|
+
}
|
|
6489
|
+
var SECTION_TITLES = {
|
|
6490
|
+
"ambient-credential": "Ambient-credential exposure",
|
|
6491
|
+
"binary-discovery": "Claude Code binary discovery",
|
|
6492
|
+
"claude-shim": "`claude` command shim",
|
|
6493
|
+
"path-resolution": "PATH resolution",
|
|
6494
|
+
"legacy-name": "Former `claude-use` name",
|
|
6495
|
+
"config-profile": "Configuration profiles",
|
|
6496
|
+
identity: "Identities",
|
|
6497
|
+
pool: "Pools",
|
|
6498
|
+
provider: "Providers",
|
|
6499
|
+
keychain: "macOS Keychain",
|
|
6500
|
+
"directory-rules": "Directory rules",
|
|
6501
|
+
"global-config": "Global config",
|
|
6502
|
+
headroom: "Headroom daemon",
|
|
6503
|
+
"categories-local": "categories.local.json",
|
|
6504
|
+
"active-identity": "Active identity"
|
|
6505
|
+
};
|
|
6506
|
+
var SECTION_ORDER = [
|
|
6507
|
+
"ambient-credential",
|
|
6508
|
+
"binary-discovery",
|
|
6509
|
+
"claude-shim",
|
|
6510
|
+
"path-resolution",
|
|
6511
|
+
"config-profile",
|
|
6512
|
+
"identity",
|
|
6513
|
+
"pool",
|
|
6514
|
+
"provider",
|
|
6515
|
+
"keychain",
|
|
6516
|
+
"directory-rules",
|
|
6517
|
+
"global-config",
|
|
6518
|
+
"headroom",
|
|
6519
|
+
"categories-local",
|
|
6520
|
+
"active-identity"
|
|
6521
|
+
];
|
|
6522
|
+
function severityPrefix(severity) {
|
|
6523
|
+
switch (severity) {
|
|
6524
|
+
case "pass":
|
|
6525
|
+
return "[PASS]";
|
|
6526
|
+
case "warn":
|
|
6527
|
+
return "[WARN]";
|
|
6528
|
+
case "fail":
|
|
6529
|
+
return "[FAIL]";
|
|
6530
|
+
default:
|
|
6531
|
+
return severity;
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
function formatDoctorReport(report) {
|
|
6535
|
+
const lines = [];
|
|
6536
|
+
for (const section of SECTION_ORDER) {
|
|
6537
|
+
const sectionFindings = report.findings.filter((finding) => finding.section === section);
|
|
6538
|
+
if (sectionFindings.length === 0) {
|
|
6539
|
+
continue;
|
|
6540
|
+
}
|
|
6541
|
+
lines.push("", `${SECTION_TITLES[section]}:`);
|
|
6542
|
+
for (const finding of sectionFindings) {
|
|
6543
|
+
const subject = finding.subject === void 0 ? "" : `${finding.subject}: `;
|
|
6544
|
+
lines.push(` ${severityPrefix(finding.severity)} ${subject}${finding.message}`);
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
const failCount = report.findings.filter((finding) => finding.severity === "fail").length;
|
|
6548
|
+
lines.push("", report.ok ? "All checks passed." : `${String(failCount)} check(s) failed.`);
|
|
6549
|
+
return lines;
|
|
6550
|
+
}
|
|
6551
|
+
function realpathOrSelf(target) {
|
|
6552
|
+
try {
|
|
6553
|
+
return fs9.realpathSync(target);
|
|
6554
|
+
} catch {
|
|
6555
|
+
return target;
|
|
6556
|
+
}
|
|
6557
|
+
}
|
|
6558
|
+
function collectDoctorReport(params) {
|
|
6559
|
+
const { paths } = params;
|
|
6560
|
+
const identityNames = fs9.existsSync(paths.identitiesDir) ? fs9.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
|
|
6561
|
+
const identities = identityNames.map((name) => {
|
|
6562
|
+
const farmRoot = path22.join(paths.identitiesDir, name);
|
|
6563
|
+
const identityPath = path22.join(farmRoot, "identity.json");
|
|
6564
|
+
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
6565
|
+
});
|
|
6566
|
+
const profileNames = fs9.existsSync(paths.configProfilesDir) ? fs9.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6567
|
+
const configProfiles = profileNames.map((name) => {
|
|
6568
|
+
const profilePath = path22.join(paths.configProfilesDir, `${name}.json`);
|
|
6569
|
+
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
6570
|
+
});
|
|
6571
|
+
const providerNames = fs9.existsSync(paths.providersDir) ? fs9.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
6572
|
+
const providers = providerNames.map((name) => {
|
|
6573
|
+
const providerPath = path22.join(paths.providersDir, `${name}.json`);
|
|
6574
|
+
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
6575
|
+
});
|
|
6576
|
+
const ownExecutablePath = realOwnExecutablePath();
|
|
6577
|
+
let binaryDiscovery;
|
|
6578
|
+
try {
|
|
6579
|
+
const binary = realResolveClaudeBinary(resolveOwnInstallDirs(paths, ownExecutablePath))();
|
|
6580
|
+
binaryDiscovery = { ok: true, binary };
|
|
6581
|
+
} catch (error) {
|
|
6582
|
+
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
6583
|
+
}
|
|
6584
|
+
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
6585
|
+
const pathDirs = (params.env.PATH ?? "").split(path22.delimiter).filter((dir) => dir !== "");
|
|
6586
|
+
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
6587
|
+
findPathShadow({
|
|
6588
|
+
pathDirs,
|
|
6589
|
+
targetDir: path22.dirname(shimState.targetPath),
|
|
6590
|
+
targetFilename: path22.basename(shimState.targetPath),
|
|
6591
|
+
findExecutableInDir
|
|
6592
|
+
}),
|
|
6593
|
+
shimState.targetPath,
|
|
6594
|
+
realpathOrSelf
|
|
6595
|
+
);
|
|
6596
|
+
const report = runDoctor({
|
|
6597
|
+
env: params.env,
|
|
6598
|
+
identities,
|
|
6599
|
+
configProfiles,
|
|
6600
|
+
providers,
|
|
6601
|
+
directoryRules: { path: paths.directoryRulesFile, raw: realFsPort.readFileUtf8(paths.directoryRulesFile) },
|
|
6602
|
+
globalConfig: { path: paths.globalConfigFile, raw: realFsPort.readFileUtf8(paths.globalConfigFile) },
|
|
6603
|
+
categoriesLocal: { path: paths.categoriesLocalFile, raw: realFsPort.readFileUtf8(paths.categoriesLocalFile) },
|
|
6604
|
+
activeIdentity: { path: paths.activeIdentityFile, raw: realFsPort.readFileUtf8(paths.activeIdentityFile) },
|
|
6605
|
+
binaryDiscovery,
|
|
6606
|
+
claudeShim: { state: shimState, targetExists: shimState !== void 0 && fs9.existsSync(shimState.targetPath) },
|
|
6607
|
+
rootPath: paths.root,
|
|
6608
|
+
pathResolution: {
|
|
6609
|
+
ownExecutablePath,
|
|
6610
|
+
agentShim: refinePathShadow(
|
|
6611
|
+
findPathShadow({
|
|
6612
|
+
pathDirs,
|
|
6613
|
+
targetDir: path22.dirname(ownExecutablePath),
|
|
6614
|
+
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
6615
|
+
findExecutableInDir
|
|
6616
|
+
}),
|
|
6617
|
+
ownExecutablePath,
|
|
6618
|
+
realpathOrSelf
|
|
6619
|
+
),
|
|
6620
|
+
...claudeShimShadow === void 0 ? {} : { claude: claudeShimShadow }
|
|
6621
|
+
},
|
|
6622
|
+
run: realRunPort,
|
|
6623
|
+
platform: process.platform,
|
|
6624
|
+
headroom: {
|
|
6625
|
+
state: { path: paths.headroomStateFile, raw: realFsPort.readFileUtf8(paths.headroomStateFile) },
|
|
6626
|
+
isRunning: realIsProcessRunning
|
|
6627
|
+
}
|
|
6628
|
+
});
|
|
6629
|
+
return report;
|
|
6630
|
+
}
|
|
4485
6631
|
export {
|
|
4486
6632
|
AMBIENT_CREDENTIAL_VARS,
|
|
4487
6633
|
CONNECT_INTERCEPT_HOST,
|
|
@@ -4536,6 +6682,10 @@ export {
|
|
|
4536
6682
|
buildEntryFacts,
|
|
4537
6683
|
buildLayoutPaths,
|
|
4538
6684
|
carryOver,
|
|
6685
|
+
checkReportHasWarnings,
|
|
6686
|
+
checkReportToJson,
|
|
6687
|
+
collectCheckReport,
|
|
6688
|
+
collectDoctorReport,
|
|
4539
6689
|
createLeafCache,
|
|
4540
6690
|
createProfile,
|
|
4541
6691
|
describeProviderEndpoint,
|
|
@@ -4545,6 +6695,8 @@ export {
|
|
|
4545
6695
|
ensureHeadroom,
|
|
4546
6696
|
evaluateAmbientCredentialGuard,
|
|
4547
6697
|
formatAmbientCredentialGuardMessage,
|
|
6698
|
+
formatCheckReport,
|
|
6699
|
+
formatDoctorReport,
|
|
4548
6700
|
forwardableHeaders,
|
|
4549
6701
|
generateCa,
|
|
4550
6702
|
identityExists,
|
|
@@ -4584,6 +6736,8 @@ export {
|
|
|
4584
6736
|
resolveLayoutPaths,
|
|
4585
6737
|
resolveSupervisorConfig,
|
|
4586
6738
|
resyncFarm,
|
|
6739
|
+
runCheck,
|
|
6740
|
+
runDoctor,
|
|
4587
6741
|
runFrontDoorSupervisor,
|
|
4588
6742
|
runSupervisor,
|
|
4589
6743
|
servedByPipeline,
|