agent-shim 8.9.0 → 8.11.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 +3 -1
- package/dist/cli.cjs +498 -282
- package/dist/index.cjs +530 -317
- package/dist/index.mjs +531 -319
- package/dist/types/index.d.ts +81 -4
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -54,8 +54,113 @@ var categories_default_default = {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
// src/config/schema.ts
|
|
57
|
-
import
|
|
57
|
+
import path2 from "node:path";
|
|
58
58
|
import { z } from "zod";
|
|
59
|
+
|
|
60
|
+
// src/versionDiscovery.ts
|
|
61
|
+
import os from "node:os";
|
|
62
|
+
import path from "node:path";
|
|
63
|
+
|
|
64
|
+
// src/cliError.ts
|
|
65
|
+
var EXIT_FAILURE = 1;
|
|
66
|
+
var EXIT_USAGE = 2;
|
|
67
|
+
var CliError = class extends Error {
|
|
68
|
+
/** The process exit status this failure maps to. `EXIT_FAILURE` unless a subclass says otherwise. */
|
|
69
|
+
exitCode = EXIT_FAILURE;
|
|
70
|
+
};
|
|
71
|
+
var UsageError = class extends CliError {
|
|
72
|
+
exitCode = EXIT_USAGE;
|
|
73
|
+
constructor(message) {
|
|
74
|
+
super(message);
|
|
75
|
+
this.name = "UsageError";
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// src/versionDiscovery.ts
|
|
80
|
+
function defaultVersionsDir() {
|
|
81
|
+
return path.join(os.homedir(), ".local", "share", "claude", "versions");
|
|
82
|
+
}
|
|
83
|
+
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
84
|
+
function isNumericDottedVersion(name) {
|
|
85
|
+
return NUMERIC_DOTTED_VERSION_RE.test(name);
|
|
86
|
+
}
|
|
87
|
+
function compareVersions(a, b) {
|
|
88
|
+
if (!isNumericDottedVersion(a)) {
|
|
89
|
+
throw new Error(`compareVersions: "${a}" is not a valid plain dotted-numeric version`);
|
|
90
|
+
}
|
|
91
|
+
if (!isNumericDottedVersion(b)) {
|
|
92
|
+
throw new Error(`compareVersions: "${b}" is not a valid plain dotted-numeric version`);
|
|
93
|
+
}
|
|
94
|
+
const segmentsA = a.split(".").map(Number);
|
|
95
|
+
const segmentsB = b.split(".").map(Number);
|
|
96
|
+
const length = Math.max(segmentsA.length, segmentsB.length);
|
|
97
|
+
for (let i = 0; i < length; i += 1) {
|
|
98
|
+
const segmentA = segmentsA[i] ?? 0;
|
|
99
|
+
const segmentB = segmentsB[i] ?? 0;
|
|
100
|
+
if (segmentA !== segmentB) {
|
|
101
|
+
return segmentA - segmentB;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
var ClaudeVersionNotInstalledError = class extends CliError {
|
|
107
|
+
constructor(version, versionsDir, installed) {
|
|
108
|
+
super(
|
|
109
|
+
`Claude Code ${version} is pinned for this launch but is not installed under "${versionsDir}"` + (installed.length === 0 ? " (no versions are installed there)." : `. Installed: ${installed.join(", ")}.`) + " Install it, or change or remove the pin (--claude-version, AGENT_SHIM_CLAUDE_VERSION, or launch.claudeVersion)."
|
|
110
|
+
);
|
|
111
|
+
this.version = version;
|
|
112
|
+
this.versionsDir = versionsDir;
|
|
113
|
+
this.installed = installed;
|
|
114
|
+
this.name = "ClaudeVersionNotInstalledError";
|
|
115
|
+
}
|
|
116
|
+
version;
|
|
117
|
+
versionsDir;
|
|
118
|
+
installed;
|
|
119
|
+
};
|
|
120
|
+
function installedVersions(entries) {
|
|
121
|
+
return entries.filter((entry) => entry.isFile && entry.isExecutable && entry.sizeBytes > 0 && isNumericDottedVersion(entry.name)).map((entry) => entry.name).sort(compareVersions);
|
|
122
|
+
}
|
|
123
|
+
function pickHighestVersion(entries) {
|
|
124
|
+
const candidates = entries.filter(
|
|
125
|
+
(entry) => entry.isFile && entry.isExecutable && entry.sizeBytes > 0 && isNumericDottedVersion(entry.name)
|
|
126
|
+
);
|
|
127
|
+
if (candidates.length === 0) {
|
|
128
|
+
return void 0;
|
|
129
|
+
}
|
|
130
|
+
return candidates.reduce(
|
|
131
|
+
(highest, candidate) => compareVersions(candidate.name, highest.name) > 0 ? candidate : highest
|
|
132
|
+
).name;
|
|
133
|
+
}
|
|
134
|
+
function discoverClaudeBinary(options) {
|
|
135
|
+
const versionsDir = options.versionsDir ?? defaultVersionsDir();
|
|
136
|
+
const entries = options.listVersionsDir(versionsDir);
|
|
137
|
+
const highestVersion = pickHighestVersion(entries);
|
|
138
|
+
if (options.version !== void 0) {
|
|
139
|
+
const installed = installedVersions(entries);
|
|
140
|
+
if (!installed.includes(options.version)) {
|
|
141
|
+
throw new ClaudeVersionNotInstalledError(options.version, versionsDir, installed);
|
|
142
|
+
}
|
|
143
|
+
return { path: path.join(versionsDir, options.version), source: "versions-dir", version: options.version };
|
|
144
|
+
}
|
|
145
|
+
if (highestVersion !== void 0) {
|
|
146
|
+
return {
|
|
147
|
+
path: path.join(versionsDir, highestVersion),
|
|
148
|
+
source: "versions-dir",
|
|
149
|
+
version: highestVersion
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
for (const dir of options.pathDirs) {
|
|
153
|
+
const found = options.findExecutableInDir(dir, "claude");
|
|
154
|
+
if (found !== void 0 && !options.isOwnBinary(found)) {
|
|
155
|
+
return { path: found, source: "path-fallback" };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
throw new Error(
|
|
159
|
+
`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 (never this tool's own binary). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/config/schema.ts
|
|
59
164
|
var CATEGORY_NAMES = ["secret", "runtime", "history", "knowledge", "settings"];
|
|
60
165
|
var OVERRIDABLE_CATEGORIES = ["runtime", "history", "knowledge", "settings"];
|
|
61
166
|
function isOverridableCategory(name) {
|
|
@@ -99,7 +204,9 @@ var LaunchSchema = z.strictObject({
|
|
|
99
204
|
remoteControl: z.boolean().optional(),
|
|
100
205
|
provider: z.string().min(1).optional(),
|
|
101
206
|
headroom: z.boolean().optional(),
|
|
102
|
-
trackUsage: z.boolean().optional()
|
|
207
|
+
trackUsage: z.boolean().optional(),
|
|
208
|
+
/** The exact Claude Code version to run, as it is named in Claude Code's versions directory (`2.1.220`). A pin that is not installed fails the launch; it never falls back to the highest version. */
|
|
209
|
+
claudeVersion: z.string().regex(NUMERIC_DOTTED_VERSION_RE, "must be an exact dotted-numeric Claude Code version such as 2.1.220").optional()
|
|
103
210
|
});
|
|
104
211
|
var CREDENTIAL_TARGETS = ["bearer", "apiKey", "oauthToken"];
|
|
105
212
|
var PROVIDER_CREDENTIAL_TARGETS = ["bearer", "apiKey"];
|
|
@@ -118,7 +225,7 @@ var CommandSourceOptions = {
|
|
|
118
225
|
var CredentialSourceSchema = z.union([
|
|
119
226
|
z.strictObject({ env: z.string().min(1) }),
|
|
120
227
|
z.strictObject({
|
|
121
|
-
file: z.string().min(1).refine((filePath) => filePath.startsWith("~/") ||
|
|
228
|
+
file: z.string().min(1).refine((filePath) => filePath.startsWith("~/") || path2.isAbsolute(filePath), {
|
|
122
229
|
message: "a credential file must be an absolute or ~-rooted path, since a launch can start in any directory"
|
|
123
230
|
})
|
|
124
231
|
}),
|
|
@@ -297,28 +404,11 @@ var SHIPPED_CATEGORY_DEFAULTS = Object.freeze({
|
|
|
297
404
|
|
|
298
405
|
// src/config/store.ts
|
|
299
406
|
import fs from "node:fs";
|
|
300
|
-
import
|
|
407
|
+
import path3 from "node:path";
|
|
301
408
|
import { randomUUID } from "node:crypto";
|
|
302
409
|
|
|
303
410
|
// src/config/load.ts
|
|
304
411
|
import { cosmiconfigSync } from "cosmiconfig";
|
|
305
|
-
|
|
306
|
-
// src/cliError.ts
|
|
307
|
-
var EXIT_FAILURE = 1;
|
|
308
|
-
var EXIT_USAGE = 2;
|
|
309
|
-
var CliError = class extends Error {
|
|
310
|
-
/** The process exit status this failure maps to. `EXIT_FAILURE` unless a subclass says otherwise. */
|
|
311
|
-
exitCode = EXIT_FAILURE;
|
|
312
|
-
};
|
|
313
|
-
var UsageError = class extends CliError {
|
|
314
|
-
exitCode = EXIT_USAGE;
|
|
315
|
-
constructor(message) {
|
|
316
|
-
super(message);
|
|
317
|
-
this.name = "UsageError";
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// src/config/load.ts
|
|
322
412
|
function createExplorer(moduleName = "agent-shim") {
|
|
323
413
|
return cosmiconfigSync(moduleName, { searchPlaces: [] });
|
|
324
414
|
}
|
|
@@ -439,9 +529,9 @@ function readJson(filePath, schema, storeFs = nodeStoreFs) {
|
|
|
439
529
|
return result.data;
|
|
440
530
|
}
|
|
441
531
|
function writeTextAtomic(filePath, contents, storeFs = nodeStoreFs) {
|
|
442
|
-
const dir =
|
|
532
|
+
const dir = path3.dirname(filePath);
|
|
443
533
|
storeFs.mkdirSync(dir);
|
|
444
|
-
const tempPath =
|
|
534
|
+
const tempPath = path3.join(dir, `.${path3.basename(filePath)}.${String(process.pid)}.${randomUUID()}.tmp`);
|
|
445
535
|
storeFs.writeFileUtf8(tempPath, contents);
|
|
446
536
|
try {
|
|
447
537
|
storeFs.renameSync(tempPath, filePath);
|
|
@@ -465,9 +555,9 @@ function applyPatch(filePath, schema, patch, options = {}) {
|
|
|
465
555
|
if (!result.success) {
|
|
466
556
|
throw new ConfigValidationError(filePath, result.error.issues);
|
|
467
557
|
}
|
|
468
|
-
const
|
|
469
|
-
writeJsonAtomic(filePath,
|
|
470
|
-
return
|
|
558
|
+
const validated2 = result.data;
|
|
559
|
+
writeJsonAtomic(filePath, validated2, storeFs);
|
|
560
|
+
return validated2;
|
|
471
561
|
}
|
|
472
562
|
|
|
473
563
|
// src/config/classify.ts
|
|
@@ -890,6 +980,9 @@ function flattenLayers(layers, options) {
|
|
|
890
980
|
if (layer.launch.trackUsage !== void 0) {
|
|
891
981
|
launch.trackUsage = layer.launch.trackUsage;
|
|
892
982
|
}
|
|
983
|
+
if (layer.launch.claudeVersion !== void 0) {
|
|
984
|
+
launch.claudeVersion = layer.launch.claudeVersion;
|
|
985
|
+
}
|
|
893
986
|
}
|
|
894
987
|
if (layer.entries === void 0) {
|
|
895
988
|
continue;
|
|
@@ -1127,7 +1220,7 @@ function dedupeDiagnostics(diagnostics) {
|
|
|
1127
1220
|
}
|
|
1128
1221
|
|
|
1129
1222
|
// src/resolve/plan.ts
|
|
1130
|
-
import
|
|
1223
|
+
import path4 from "node:path";
|
|
1131
1224
|
function parentOf(rel) {
|
|
1132
1225
|
const index = rel.lastIndexOf("/");
|
|
1133
1226
|
return index === -1 ? "" : rel.slice(0, index);
|
|
@@ -1177,7 +1270,7 @@ function planFarm(params) {
|
|
|
1177
1270
|
}
|
|
1178
1271
|
return value;
|
|
1179
1272
|
};
|
|
1180
|
-
const linkTarget = (rel) =>
|
|
1273
|
+
const linkTarget = (rel) => path4.join(params.facts.claudeHome, rel);
|
|
1181
1274
|
const visit = (rel) => {
|
|
1182
1275
|
const fact = params.facts.entries.get(rel);
|
|
1183
1276
|
const decision = params.decisions.get(rel);
|
|
@@ -1255,7 +1348,7 @@ function descendPolicy(rules) {
|
|
|
1255
1348
|
}
|
|
1256
1349
|
|
|
1257
1350
|
// src/resolve/walk.ts
|
|
1258
|
-
import
|
|
1351
|
+
import path5 from "node:path";
|
|
1259
1352
|
|
|
1260
1353
|
// src/resolve/extends.ts
|
|
1261
1354
|
function lineariseProfile(rootName, load) {
|
|
@@ -1323,10 +1416,10 @@ function profileLayers(rootName, load, startId) {
|
|
|
1323
1416
|
|
|
1324
1417
|
// src/resolve/walk.ts
|
|
1325
1418
|
function walkDirectoryAncestors(cwd, options) {
|
|
1326
|
-
const limit =
|
|
1419
|
+
const limit = path5.resolve(options.limit ?? options.home);
|
|
1327
1420
|
const isReadable = options.isReadable ?? (() => true);
|
|
1328
1421
|
const collected = [];
|
|
1329
|
-
let current =
|
|
1422
|
+
let current = path5.resolve(cwd);
|
|
1330
1423
|
for (; ; ) {
|
|
1331
1424
|
if (!isReadable(current)) {
|
|
1332
1425
|
break;
|
|
@@ -1335,7 +1428,7 @@ function walkDirectoryAncestors(cwd, options) {
|
|
|
1335
1428
|
if (current === limit) {
|
|
1336
1429
|
break;
|
|
1337
1430
|
}
|
|
1338
|
-
const parent =
|
|
1431
|
+
const parent = path5.dirname(current);
|
|
1339
1432
|
if (parent === current) {
|
|
1340
1433
|
break;
|
|
1341
1434
|
}
|
|
@@ -1459,7 +1552,7 @@ function resolveDecisions(input) {
|
|
|
1459
1552
|
}
|
|
1460
1553
|
|
|
1461
1554
|
// src/launcher/farm.ts
|
|
1462
|
-
import
|
|
1555
|
+
import path7 from "node:path";
|
|
1463
1556
|
import { z as z2 } from "zod";
|
|
1464
1557
|
|
|
1465
1558
|
// src/resolve/reconcile.ts
|
|
@@ -1563,7 +1656,7 @@ var LEGACY_FARM_MANIFEST_FILENAME = ".claude-use-farm.json";
|
|
|
1563
1656
|
|
|
1564
1657
|
// src/launcher/lock.ts
|
|
1565
1658
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1566
|
-
import
|
|
1659
|
+
import path6 from "node:path";
|
|
1567
1660
|
var DEFAULT_STALE_AFTER_MS = 12e4;
|
|
1568
1661
|
var DEFAULT_RETRY_DELAY_MS = 50;
|
|
1569
1662
|
var DEFAULT_MAX_ATTEMPTS = 200;
|
|
@@ -1582,7 +1675,7 @@ var IdentityLockBusyError = class extends CliError {
|
|
|
1582
1675
|
holderPid;
|
|
1583
1676
|
};
|
|
1584
1677
|
function identityLockPath(dir, identity) {
|
|
1585
|
-
return
|
|
1678
|
+
return path6.join(dir, `.${identity}.lock`);
|
|
1586
1679
|
}
|
|
1587
1680
|
function parseLockRecord(raw) {
|
|
1588
1681
|
if (raw === void 0) {
|
|
@@ -1661,7 +1754,7 @@ var FarmManifestSchema = z2.strictObject({
|
|
|
1661
1754
|
links: z2.array(z2.strictObject({ rel: z2.string(), target: z2.string() }))
|
|
1662
1755
|
});
|
|
1663
1756
|
function readFarmManifest(fs12, farmRoot) {
|
|
1664
|
-
const raw = fs12.readFileUtf8(
|
|
1757
|
+
const raw = fs12.readFileUtf8(path7.join(farmRoot, FARM_MANIFEST_FILENAME)) ?? fs12.readFileUtf8(path7.join(farmRoot, LEGACY_FARM_MANIFEST_FILENAME));
|
|
1665
1758
|
if (raw === void 0) {
|
|
1666
1759
|
return void 0;
|
|
1667
1760
|
}
|
|
@@ -1677,7 +1770,7 @@ function readFarmManifest(fs12, farmRoot) {
|
|
|
1677
1770
|
function buildEntryFacts(params) {
|
|
1678
1771
|
const entries = /* @__PURE__ */ new Map();
|
|
1679
1772
|
const visit = (rel) => {
|
|
1680
|
-
const absolute =
|
|
1773
|
+
const absolute = path7.join(params.claudeHome, rel);
|
|
1681
1774
|
const stat = params.fs.lstat(absolute);
|
|
1682
1775
|
if (stat === void 0) {
|
|
1683
1776
|
return void 0;
|
|
@@ -1720,7 +1813,7 @@ function buildEntryFacts(params) {
|
|
|
1720
1813
|
};
|
|
1721
1814
|
}
|
|
1722
1815
|
function listSubtree(fs12, root, rel) {
|
|
1723
|
-
const absolute =
|
|
1816
|
+
const absolute = path7.join(root, rel);
|
|
1724
1817
|
const stat = fs12.lstat(absolute);
|
|
1725
1818
|
if (stat === void 0) {
|
|
1726
1819
|
return [];
|
|
@@ -1750,7 +1843,7 @@ function reconciliationScope(fs12, farmRoot, manifest) {
|
|
|
1750
1843
|
if (manifest !== void 0) {
|
|
1751
1844
|
return topLevelScopeRoots(manifest.materialised);
|
|
1752
1845
|
}
|
|
1753
|
-
return [...fs12.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs12.lstat(
|
|
1846
|
+
return [...fs12.readdir(farmRoot)].filter((name) => name !== FARM_MANIFEST_FILENAME && name !== LEGACY_FARM_MANIFEST_FILENAME && fs12.lstat(path7.join(farmRoot, name))?.kind === "dir").sort();
|
|
1754
1847
|
}
|
|
1755
1848
|
function collectFarmListing(fs12, farmRoot, scopeRoots) {
|
|
1756
1849
|
return scopeRoots.flatMap((root) => listSubtree(fs12, farmRoot, root));
|
|
@@ -1758,7 +1851,7 @@ function collectFarmListing(fs12, farmRoot, scopeRoots) {
|
|
|
1758
1851
|
function collectCanonicalCounterparts(fs12, claudeHome, farmListing) {
|
|
1759
1852
|
const out = [];
|
|
1760
1853
|
for (const entry of farmListing) {
|
|
1761
|
-
const absolute =
|
|
1854
|
+
const absolute = path7.join(claudeHome, entry.rel);
|
|
1762
1855
|
const stat = fs12.lstat(absolute);
|
|
1763
1856
|
if (stat === void 0) {
|
|
1764
1857
|
continue;
|
|
@@ -1788,21 +1881,21 @@ function carryOver(params) {
|
|
|
1788
1881
|
if (accounted.has(name)) {
|
|
1789
1882
|
continue;
|
|
1790
1883
|
}
|
|
1791
|
-
const stat = params.fs.lstat(
|
|
1884
|
+
const stat = params.fs.lstat(path7.join(params.previousRoot, name));
|
|
1792
1885
|
if (stat === void 0 || stat.kind === "symlink") {
|
|
1793
1886
|
continue;
|
|
1794
1887
|
}
|
|
1795
|
-
if (params.fs.lstat(
|
|
1888
|
+
if (params.fs.lstat(path7.join(params.farmRoot, name)) !== void 0) {
|
|
1796
1889
|
const category = params.classification === void 0 ? void 0 : classifyEntries([name], params.classification).classification.get(name);
|
|
1797
1890
|
if (category === "runtime") {
|
|
1798
|
-
params.fs.removeRecursive(
|
|
1891
|
+
params.fs.removeRecursive(path7.join(params.previousRoot, name));
|
|
1799
1892
|
autoResolved.push(name);
|
|
1800
1893
|
continue;
|
|
1801
1894
|
}
|
|
1802
1895
|
collided.push(name);
|
|
1803
1896
|
continue;
|
|
1804
1897
|
}
|
|
1805
|
-
params.fs.rename(
|
|
1898
|
+
params.fs.rename(path7.join(params.previousRoot, name), path7.join(params.farmRoot, name));
|
|
1806
1899
|
carried.push(name);
|
|
1807
1900
|
}
|
|
1808
1901
|
return { carried, autoResolved, collided };
|
|
@@ -1810,9 +1903,9 @@ function carryOver(params) {
|
|
|
1810
1903
|
function buildScratchTree(params) {
|
|
1811
1904
|
params.fs.mkdirp(params.scratchRoot);
|
|
1812
1905
|
for (const entry of params.plan.entries) {
|
|
1813
|
-
const absolute =
|
|
1906
|
+
const absolute = path7.join(params.scratchRoot, entry.rel);
|
|
1814
1907
|
if (entry.kind === "link") {
|
|
1815
|
-
params.fs.mkdirp(
|
|
1908
|
+
params.fs.mkdirp(path7.dirname(absolute));
|
|
1816
1909
|
params.fs.symlink(entry.target, absolute);
|
|
1817
1910
|
continue;
|
|
1818
1911
|
}
|
|
@@ -1821,14 +1914,14 @@ function buildScratchTree(params) {
|
|
|
1821
1914
|
}
|
|
1822
1915
|
}
|
|
1823
1916
|
params.fs.writeFileUtf8(
|
|
1824
|
-
|
|
1917
|
+
path7.join(params.scratchRoot, FARM_MANIFEST_FILENAME),
|
|
1825
1918
|
`${JSON.stringify(params.manifest, null, 2)}
|
|
1826
1919
|
`
|
|
1827
1920
|
);
|
|
1828
1921
|
}
|
|
1829
1922
|
function swapIn(params) {
|
|
1830
1923
|
if (params.fs.lstat(params.farmRoot) === void 0) {
|
|
1831
|
-
params.fs.mkdirp(
|
|
1924
|
+
params.fs.mkdirp(path7.dirname(params.farmRoot));
|
|
1832
1925
|
params.fs.rename(params.scratchRoot, params.farmRoot);
|
|
1833
1926
|
return { carried: [], autoResolved: [], collided: [] };
|
|
1834
1927
|
}
|
|
@@ -1852,14 +1945,14 @@ function recoverInterruptedSwap(params) {
|
|
|
1852
1945
|
const names = [...params.fs.readdir(params.identitiesDir)].sort();
|
|
1853
1946
|
const removedScratch = [];
|
|
1854
1947
|
for (const name of names.filter((candidate) => candidate.startsWith(scratchPrefix))) {
|
|
1855
|
-
params.fs.removeRecursive(
|
|
1948
|
+
params.fs.removeRecursive(path7.join(params.identitiesDir, name));
|
|
1856
1949
|
removedScratch.push(name);
|
|
1857
1950
|
}
|
|
1858
1951
|
let previous = names.filter((candidate) => candidate.startsWith(previousPrefix));
|
|
1859
1952
|
let restoredFrom;
|
|
1860
1953
|
const first = previous[0];
|
|
1861
1954
|
if (first !== void 0 && params.fs.lstat(params.farmRoot) === void 0) {
|
|
1862
|
-
params.fs.rename(
|
|
1955
|
+
params.fs.rename(path7.join(params.identitiesDir, first), params.farmRoot);
|
|
1863
1956
|
restoredFrom = first;
|
|
1864
1957
|
previous = previous.slice(1);
|
|
1865
1958
|
}
|
|
@@ -1867,7 +1960,7 @@ function recoverInterruptedSwap(params) {
|
|
|
1867
1960
|
const autoResolved = [];
|
|
1868
1961
|
const retained = [];
|
|
1869
1962
|
for (const name of previous) {
|
|
1870
|
-
const previousRoot =
|
|
1963
|
+
const previousRoot = path7.join(params.identitiesDir, name);
|
|
1871
1964
|
const result = carryOver({
|
|
1872
1965
|
fs: params.fs,
|
|
1873
1966
|
previousRoot,
|
|
@@ -1892,7 +1985,7 @@ function recoverInterruptedSwap(params) {
|
|
|
1892
1985
|
};
|
|
1893
1986
|
}
|
|
1894
1987
|
function recoverFarm(params) {
|
|
1895
|
-
const farmRoot =
|
|
1988
|
+
const farmRoot = path7.join(params.identitiesDir, params.identity);
|
|
1896
1989
|
const lock = acquireIdentityLock({
|
|
1897
1990
|
identity: params.identity,
|
|
1898
1991
|
dir: params.identitiesDir,
|
|
@@ -1951,16 +2044,16 @@ function executeReconciliation(params) {
|
|
|
1951
2044
|
});
|
|
1952
2045
|
continue;
|
|
1953
2046
|
}
|
|
1954
|
-
const from =
|
|
1955
|
-
const canonical =
|
|
2047
|
+
const from = path7.join(params.farmRoot, action.rel);
|
|
2048
|
+
const canonical = path7.join(params.claudeHome, action.rel);
|
|
1956
2049
|
if (action.kind === "adopt") {
|
|
1957
|
-
params.fs.mkdirp(
|
|
2050
|
+
params.fs.mkdirp(path7.dirname(canonical));
|
|
1958
2051
|
params.fs.copyRecursive(from, canonical);
|
|
1959
2052
|
adopted.push(action.rel);
|
|
1960
2053
|
continue;
|
|
1961
2054
|
}
|
|
1962
2055
|
const preserved = `${canonical}.farm-conflict-${String(params.nowMs)}`;
|
|
1963
|
-
params.fs.mkdirp(
|
|
2056
|
+
params.fs.mkdirp(path7.dirname(canonical));
|
|
1964
2057
|
params.fs.copyRecursive(from, preserved);
|
|
1965
2058
|
conflicts.push(action.rel);
|
|
1966
2059
|
}
|
|
@@ -1985,7 +2078,7 @@ function farmAlreadyMatches(existing, next) {
|
|
|
1985
2078
|
return existing.identity === next.identity && existing.claudeHome === next.claudeHome && sameStrings(existing.materialised, next.materialised) && sameLinks(existing.links, next.links);
|
|
1986
2079
|
}
|
|
1987
2080
|
function resyncFarm(params) {
|
|
1988
|
-
const farmRoot =
|
|
2081
|
+
const farmRoot = path7.join(params.identitiesDir, params.identity);
|
|
1989
2082
|
const lock = acquireIdentityLock({
|
|
1990
2083
|
identity: params.identity,
|
|
1991
2084
|
dir: params.identitiesDir,
|
|
@@ -2065,8 +2158,8 @@ function resyncFarm(params) {
|
|
|
2065
2158
|
diagnostics
|
|
2066
2159
|
};
|
|
2067
2160
|
}
|
|
2068
|
-
const scratchRoot =
|
|
2069
|
-
const previousRoot =
|
|
2161
|
+
const scratchRoot = path7.join(params.identitiesDir, `.${params.identity}.scratch.${params.uniqueSuffix}`);
|
|
2162
|
+
const previousRoot = path7.join(params.identitiesDir, `.${params.identity}.previous.${params.uniqueSuffix}`);
|
|
2070
2163
|
buildScratchTree({ fs: params.fs, scratchRoot, plan: resolved.farm, manifest });
|
|
2071
2164
|
const swap = swapIn({ fs: params.fs, farmRoot, scratchRoot, previousRoot, classification: params.classification });
|
|
2072
2165
|
if (swap.retainedPrevious !== void 0) {
|
|
@@ -2495,7 +2588,7 @@ function providerBaseUrl(port, provider) {
|
|
|
2495
2588
|
}
|
|
2496
2589
|
|
|
2497
2590
|
// src/frontdoor/state.ts
|
|
2498
|
-
import
|
|
2591
|
+
import path8 from "node:path";
|
|
2499
2592
|
import { z as z3 } from "zod";
|
|
2500
2593
|
var FRONT_DOOR_PROTOCOL = 2;
|
|
2501
2594
|
var FrontDoorSessionSchema = z3.strictObject({ pid: z3.number().int().positive(), startedAt: z3.number(), token: z3.string().min(1) });
|
|
@@ -2547,7 +2640,7 @@ function readFrontDoorState(fs12, stateFile) {
|
|
|
2547
2640
|
}
|
|
2548
2641
|
}
|
|
2549
2642
|
function writeFrontDoorState(fs12, stateFile, state) {
|
|
2550
|
-
fs12.mkdirp(
|
|
2643
|
+
fs12.mkdirp(path8.dirname(stateFile));
|
|
2551
2644
|
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
2552
2645
|
`);
|
|
2553
2646
|
}
|
|
@@ -2557,7 +2650,7 @@ function frontDoorOrigin(state) {
|
|
|
2557
2650
|
}
|
|
2558
2651
|
function writeFrontDoorSession(fs12, sessionsDir, session) {
|
|
2559
2652
|
fs12.mkdirPrivate(sessionsDir);
|
|
2560
|
-
fs12.writeFilePrivate(
|
|
2653
|
+
fs12.writeFilePrivate(path8.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
2561
2654
|
`);
|
|
2562
2655
|
}
|
|
2563
2656
|
function listFrontDoorSessions(fs12, sessionsDir) {
|
|
@@ -2567,7 +2660,7 @@ function listFrontDoorSessions(fs12, sessionsDir) {
|
|
|
2567
2660
|
continue;
|
|
2568
2661
|
}
|
|
2569
2662
|
try {
|
|
2570
|
-
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs12.readFileUtf8(
|
|
2663
|
+
const parsed = FrontDoorSessionSchema.safeParse(JSON.parse(fs12.readFileUtf8(path8.join(sessionsDir, name)) ?? ""));
|
|
2571
2664
|
if (parsed.success) {
|
|
2572
2665
|
sessions.push({ pid: parsed.data.pid, startedAt: parsed.data.startedAt });
|
|
2573
2666
|
}
|
|
@@ -2578,7 +2671,7 @@ function listFrontDoorSessions(fs12, sessionsDir) {
|
|
|
2578
2671
|
return sessions.sort((a, b) => a.pid - b.pid);
|
|
2579
2672
|
}
|
|
2580
2673
|
function removeFrontDoorSession(fs12, sessionsDir, pid) {
|
|
2581
|
-
fs12.removeRecursive(
|
|
2674
|
+
fs12.removeRecursive(path8.join(sessionsDir, `${String(pid)}.json`));
|
|
2582
2675
|
}
|
|
2583
2676
|
function pruneDeadFrontDoorSessions(fs12, sessionsDir, isRunning) {
|
|
2584
2677
|
const removed = [];
|
|
@@ -2722,8 +2815,8 @@ function parseConnectTarget(authority) {
|
|
|
2722
2815
|
function isInterceptedHost(host, interceptHosts) {
|
|
2723
2816
|
return interceptHosts.includes(host);
|
|
2724
2817
|
}
|
|
2725
|
-
function servedByPipeline(
|
|
2726
|
-
return
|
|
2818
|
+
function servedByPipeline(path30) {
|
|
2819
|
+
return path30?.startsWith(ROUTED_PATH_PREFIX) === true;
|
|
2727
2820
|
}
|
|
2728
2821
|
var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
2729
2822
|
"connection",
|
|
@@ -3247,13 +3340,18 @@ function createRcSessionTracker(deps) {
|
|
|
3247
3340
|
entries.set(id, record);
|
|
3248
3341
|
return record;
|
|
3249
3342
|
};
|
|
3343
|
+
const persistCredential = (entry) => {
|
|
3344
|
+
if (entry.oauthAuthorization !== void 0) {
|
|
3345
|
+
deps.credentialStore?.write(entry.id, { authorization: entry.oauthAuthorization, anthropicVersion: entry.anthropicVersion, anthropicClientPlatform: entry.anthropicClientPlatform });
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
3250
3348
|
const observeExchange = (request4) => {
|
|
3251
3349
|
const method = (request4.method ?? "").toUpperCase();
|
|
3252
|
-
const
|
|
3253
|
-
if (!
|
|
3350
|
+
const path30 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
|
|
3351
|
+
if (!path30.startsWith(RC_SESSIONS_PATH_PREFIX)) {
|
|
3254
3352
|
return void 0;
|
|
3255
3353
|
}
|
|
3256
|
-
const tail =
|
|
3354
|
+
const tail = path30.slice(RC_SESSIONS_PATH_PREFIX.length);
|
|
3257
3355
|
const facts = {
|
|
3258
3356
|
oauthAuthorization: oauthAuthorizationOf(request4.headers),
|
|
3259
3357
|
anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
|
|
@@ -3286,7 +3384,7 @@ function createRcSessionTracker(deps) {
|
|
|
3286
3384
|
if (id2 === void 0) {
|
|
3287
3385
|
return;
|
|
3288
3386
|
}
|
|
3289
|
-
birth(id2, facts);
|
|
3387
|
+
persistCredential(birth(id2, facts));
|
|
3290
3388
|
}
|
|
3291
3389
|
};
|
|
3292
3390
|
}
|
|
@@ -3300,6 +3398,7 @@ function createRcSessionTracker(deps) {
|
|
|
3300
3398
|
entry.oauthAuthorization = facts.oauthAuthorization ?? entry.oauthAuthorization;
|
|
3301
3399
|
entry.anthropicVersion = facts.anthropicVersion ?? entry.anthropicVersion;
|
|
3302
3400
|
entry.anthropicClientPlatform = facts.anthropicClientPlatform ?? entry.anthropicClientPlatform;
|
|
3401
|
+
persistCredential(entry);
|
|
3303
3402
|
const bodyKind = method === "POST" && rest === `${id}/worker/events` ? "worker-events" : method === "POST" && rest === `${id}/events` ? "client-events" : method === "POST" && rest === `${id}/worker/heartbeat` ? "heartbeat" : method === "PUT" && rest === `${id}/worker` ? "worker-registration" : void 0;
|
|
3304
3403
|
let chunks = [];
|
|
3305
3404
|
let buffered = 0;
|
|
@@ -3353,6 +3452,7 @@ function createRcSessionTracker(deps) {
|
|
|
3353
3452
|
},
|
|
3354
3453
|
onResponse: (status, headers) => {
|
|
3355
3454
|
if (singleHeader(headers, CONFLICT_REASON_HEADER) !== void 0 || archiving && isSuccessful(status)) {
|
|
3455
|
+
deps.credentialStore?.remove(id);
|
|
3356
3456
|
expire(id);
|
|
3357
3457
|
return;
|
|
3358
3458
|
}
|
|
@@ -3385,7 +3485,18 @@ function createRcSessionTracker(deps) {
|
|
|
3385
3485
|
},
|
|
3386
3486
|
credentialOf: (sessionId) => {
|
|
3387
3487
|
const entry = entries.get(sessionId);
|
|
3388
|
-
|
|
3488
|
+
if (entry === void 0) {
|
|
3489
|
+
return void 0;
|
|
3490
|
+
}
|
|
3491
|
+
if (entry.oauthAuthorization === void 0 && deps.credentialStore !== void 0) {
|
|
3492
|
+
const persisted = deps.credentialStore.read(sessionId);
|
|
3493
|
+
if (persisted?.authorization !== void 0) {
|
|
3494
|
+
entry.oauthAuthorization = persisted.authorization;
|
|
3495
|
+
entry.anthropicVersion = entry.anthropicVersion ?? persisted.anthropicVersion;
|
|
3496
|
+
entry.anthropicClientPlatform = entry.anthropicClientPlatform ?? persisted.anthropicClientPlatform;
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
return { authorization: entry.oauthAuthorization, anthropicVersion: entry.anthropicVersion, anthropicClientPlatform: entry.anthropicClientPlatform };
|
|
3389
3500
|
},
|
|
3390
3501
|
noteSequenceNums: (sessionId, sequenceNums) => {
|
|
3391
3502
|
const entry = entries.get(sessionId);
|
|
@@ -4001,9 +4112,9 @@ function sequenceNumsOfBody(body) {
|
|
|
4001
4112
|
}
|
|
4002
4113
|
function frontDoorRcControl(transport, token) {
|
|
4003
4114
|
const headers = { authorization: `Bearer ${token}` };
|
|
4004
|
-
const listAnswerOf = async (
|
|
4115
|
+
const listAnswerOf = async (path30, what) => {
|
|
4005
4116
|
try {
|
|
4006
|
-
return await transport.request({ method: "GET", path:
|
|
4117
|
+
return await transport.request({ method: "GET", path: path30, headers });
|
|
4007
4118
|
} catch (error) {
|
|
4008
4119
|
throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
4009
4120
|
}
|
|
@@ -4075,10 +4186,10 @@ function frontDoorRcControl(transport, token) {
|
|
|
4075
4186
|
}
|
|
4076
4187
|
return pending;
|
|
4077
4188
|
};
|
|
4078
|
-
const writeOf = async (
|
|
4189
|
+
const writeOf = async (path30, body, describeFailure) => {
|
|
4079
4190
|
let answer;
|
|
4080
4191
|
try {
|
|
4081
|
-
answer = await transport.request({ method: "POST", path:
|
|
4192
|
+
answer = await transport.request({ method: "POST", path: path30, headers: { ...headers, "content-type": "application/json" }, body });
|
|
4082
4193
|
} catch (error) {
|
|
4083
4194
|
return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
|
|
4084
4195
|
}
|
|
@@ -4126,15 +4237,27 @@ var SUCCESS_STATUS_MAX_EXCLUSIVE2 = 300;
|
|
|
4126
4237
|
function isSuccessful2(status) {
|
|
4127
4238
|
return status >= HTTP_STATUS.ok && status < SUCCESS_STATUS_MAX_EXCLUSIVE2;
|
|
4128
4239
|
}
|
|
4129
|
-
var
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4240
|
+
var SourcePortRange = class {
|
|
4241
|
+
constructor(start, end) {
|
|
4242
|
+
this.start = start;
|
|
4243
|
+
this.end = end;
|
|
4244
|
+
this.count = end - start + 1;
|
|
4245
|
+
this.next = start;
|
|
4246
|
+
}
|
|
4247
|
+
start;
|
|
4248
|
+
end;
|
|
4249
|
+
count;
|
|
4250
|
+
next;
|
|
4251
|
+
/** The next port in rotation, wrapping from the end back to the start. */
|
|
4252
|
+
take() {
|
|
4253
|
+
const port = this.next;
|
|
4254
|
+
this.next = this.next === this.end ? this.start : this.next + 1;
|
|
4255
|
+
return port;
|
|
4256
|
+
}
|
|
4257
|
+
};
|
|
4258
|
+
var DOOR_SOURCE_PORT_START = 47900;
|
|
4259
|
+
var DOOR_SOURCE_PORT_END = 47919;
|
|
4260
|
+
var DOOR_SOURCE_PORTS = new SourcePortRange(DOOR_SOURCE_PORT_START, DOOR_SOURCE_PORT_END);
|
|
4138
4261
|
var realAddressLookup = (host, _options, callback) => {
|
|
4139
4262
|
if (net2.isIP(host) !== 0) {
|
|
4140
4263
|
callback(null, [{ address: host, family: net2.isIP(host) }]);
|
|
@@ -4172,14 +4295,14 @@ function dialTarget(options, defaultPort) {
|
|
|
4172
4295
|
function literalDial(target) {
|
|
4173
4296
|
return net2.connect({ host: target.host, port: target.port });
|
|
4174
4297
|
}
|
|
4175
|
-
async function reservedDial(target) {
|
|
4298
|
+
async function reservedDial(target, ports) {
|
|
4176
4299
|
for (let attempt = 0; ; attempt += 1) {
|
|
4177
|
-
const outcome = await dialOnce({ host: target.host, port: target.port, localPort:
|
|
4300
|
+
const outcome = await dialOnce({ host: target.host, port: target.port, localPort: ports.take(), lookup: realAddressLookup });
|
|
4178
4301
|
if (outcome.error === null) {
|
|
4179
4302
|
return outcome;
|
|
4180
4303
|
}
|
|
4181
4304
|
const held = outcome.error.code !== void 0 && HELD_PORT_ERRNOS.has(outcome.error.code);
|
|
4182
|
-
if (!held || attempt ===
|
|
4305
|
+
if (!held || attempt === ports.count - 1) {
|
|
4183
4306
|
return outcome;
|
|
4184
4307
|
}
|
|
4185
4308
|
}
|
|
@@ -4188,26 +4311,36 @@ function requestServername(options, fallback) {
|
|
|
4188
4311
|
return "servername" in options && typeof options.servername === "string" && options.servername !== "" ? options.servername : fallback;
|
|
4189
4312
|
}
|
|
4190
4313
|
var ExemptHttpAgent = class extends http.Agent {
|
|
4314
|
+
constructor(ports = DOOR_SOURCE_PORTS) {
|
|
4315
|
+
super();
|
|
4316
|
+
this.ports = ports;
|
|
4317
|
+
}
|
|
4318
|
+
ports;
|
|
4191
4319
|
// A reserved-port dial that has to retry cannot produce its socket synchronously, so it settles first and arrives through the callback (node's documented asynchronous createConnection form); a literal dial has nothing to retry and keeps node's own synchronous shape.
|
|
4192
4320
|
createConnection(options, oncreate) {
|
|
4193
4321
|
const target = dialTarget(options, HTTP_PORT);
|
|
4194
4322
|
if (net2.isIP(target.host) !== 0) {
|
|
4195
4323
|
return literalDial(target);
|
|
4196
4324
|
}
|
|
4197
|
-
void reservedDial(target).then((outcome) => {
|
|
4325
|
+
void reservedDial(target, this.ports).then((outcome) => {
|
|
4198
4326
|
oncreate?.(outcome.error, outcome.socket);
|
|
4199
4327
|
});
|
|
4200
4328
|
return void 0;
|
|
4201
4329
|
}
|
|
4202
4330
|
};
|
|
4203
4331
|
var ExemptTlsAgent = class extends https2.Agent {
|
|
4332
|
+
constructor(ports = DOOR_SOURCE_PORTS) {
|
|
4333
|
+
super();
|
|
4334
|
+
this.ports = ports;
|
|
4335
|
+
}
|
|
4336
|
+
ports;
|
|
4204
4337
|
// The same split as the plain sibling: a literal dial wraps TLS around the socket node's own agent would have returned, a reserved dial settles (retries included) first and arrives through the callback, which is exactly where node's own agent wraps its own dials.
|
|
4205
4338
|
createConnection(options, oncreate) {
|
|
4206
4339
|
const target = dialTarget(options, HTTPS_PORT);
|
|
4207
4340
|
if (net2.isIP(target.host) !== 0) {
|
|
4208
4341
|
return tls.connect({ socket: literalDial(target), servername: requestServername(options, target.host), ...tlsFacts(options) });
|
|
4209
4342
|
}
|
|
4210
|
-
void reservedDial(target).then((outcome) => {
|
|
4343
|
+
void reservedDial(target, this.ports).then((outcome) => {
|
|
4211
4344
|
if (outcome.error !== null) {
|
|
4212
4345
|
oncreate?.(outcome.error, outcome.socket);
|
|
4213
4346
|
return;
|
|
@@ -4268,12 +4401,12 @@ var NO_CHUNKS = {
|
|
|
4268
4401
|
};
|
|
4269
4402
|
function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
|
|
4270
4403
|
const agent = new ExemptTlsAgent();
|
|
4271
|
-
const request4 = async (method,
|
|
4404
|
+
const request4 = async (method, path30, headers, body, signal) => await new Promise((resolve, reject) => {
|
|
4272
4405
|
const options = {
|
|
4273
4406
|
host: target.host,
|
|
4274
4407
|
port: target.port,
|
|
4275
4408
|
method,
|
|
4276
|
-
path:
|
|
4409
|
+
path: path30,
|
|
4277
4410
|
// The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
|
|
4278
4411
|
headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
|
|
4279
4412
|
agent,
|
|
@@ -4310,9 +4443,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
|
|
|
4310
4443
|
return { status, body: await readAll(response) };
|
|
4311
4444
|
},
|
|
4312
4445
|
openStream: async (sessionId, headers, resume, signal) => {
|
|
4313
|
-
const
|
|
4446
|
+
const path30 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
|
|
4314
4447
|
const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
|
|
4315
|
-
const { status, response } = await request4("GET",
|
|
4448
|
+
const { status, response } = await request4("GET", path30, streamHeaders, void 0, signal);
|
|
4316
4449
|
if (!isSuccessful2(status)) {
|
|
4317
4450
|
await readAll(response);
|
|
4318
4451
|
return { status, chunks: NO_CHUNKS };
|
|
@@ -4321,9 +4454,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
|
|
|
4321
4454
|
}
|
|
4322
4455
|
};
|
|
4323
4456
|
}
|
|
4324
|
-
function realConnectEffects() {
|
|
4325
|
-
const plainAgent = new ExemptHttpAgent();
|
|
4326
|
-
const tlsAgent = new ExemptTlsAgent();
|
|
4457
|
+
function realConnectEffects(ports = DOOR_SOURCE_PORTS) {
|
|
4458
|
+
const plainAgent = new ExemptHttpAgent(ports);
|
|
4459
|
+
const tlsAgent = new ExemptTlsAgent(ports);
|
|
4327
4460
|
const listenOnce = async (port, onSocket) => await new Promise((resolve, reject) => {
|
|
4328
4461
|
const accepted = /* @__PURE__ */ new Set();
|
|
4329
4462
|
const server = net2.createServer((socket) => {
|
|
@@ -4441,7 +4574,7 @@ function realConnectEffects() {
|
|
|
4441
4574
|
};
|
|
4442
4575
|
},
|
|
4443
4576
|
connectTlsUpstream: async (host, port, clientAlpn) => {
|
|
4444
|
-
const dialled = await reservedDial({ host, port });
|
|
4577
|
+
const dialled = await reservedDial({ host, port }, ports);
|
|
4445
4578
|
if (dialled.error !== null) {
|
|
4446
4579
|
throw dialled.error;
|
|
4447
4580
|
}
|
|
@@ -4523,10 +4656,68 @@ function realConnectEffects() {
|
|
|
4523
4656
|
};
|
|
4524
4657
|
}
|
|
4525
4658
|
|
|
4659
|
+
// src/frontdoor/rcCredentialStore.ts
|
|
4660
|
+
import path9 from "node:path";
|
|
4661
|
+
function isSafeSessionId(sessionId) {
|
|
4662
|
+
return /^[A-Za-z0-9_-]+$/.test(sessionId);
|
|
4663
|
+
}
|
|
4664
|
+
function isRecord3(value) {
|
|
4665
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4666
|
+
}
|
|
4667
|
+
function credentialOfJson(parsed) {
|
|
4668
|
+
if (!isRecord3(parsed)) {
|
|
4669
|
+
return void 0;
|
|
4670
|
+
}
|
|
4671
|
+
const { authorization, anthropicVersion, anthropicClientPlatform } = parsed;
|
|
4672
|
+
if (typeof authorization !== "string" || authorization === "") {
|
|
4673
|
+
return void 0;
|
|
4674
|
+
}
|
|
4675
|
+
return {
|
|
4676
|
+
authorization,
|
|
4677
|
+
anthropicVersion: typeof anthropicVersion === "string" ? anthropicVersion : void 0,
|
|
4678
|
+
anthropicClientPlatform: typeof anthropicClientPlatform === "string" ? anthropicClientPlatform : void 0
|
|
4679
|
+
};
|
|
4680
|
+
}
|
|
4681
|
+
function createRcCredentialStore(fs12, dir) {
|
|
4682
|
+
fs12.mkdirPrivate(dir);
|
|
4683
|
+
const fileFor = (sessionId) => path9.join(dir, `${sessionId}.json`);
|
|
4684
|
+
return {
|
|
4685
|
+
read: (sessionId) => {
|
|
4686
|
+
if (!isSafeSessionId(sessionId)) {
|
|
4687
|
+
return void 0;
|
|
4688
|
+
}
|
|
4689
|
+
const raw = fs12.readFileUtf8(fileFor(sessionId));
|
|
4690
|
+
if (raw === void 0) {
|
|
4691
|
+
return void 0;
|
|
4692
|
+
}
|
|
4693
|
+
let parsed;
|
|
4694
|
+
try {
|
|
4695
|
+
parsed = JSON.parse(raw);
|
|
4696
|
+
} catch {
|
|
4697
|
+
return void 0;
|
|
4698
|
+
}
|
|
4699
|
+
return credentialOfJson(parsed);
|
|
4700
|
+
},
|
|
4701
|
+
write: (sessionId, credential) => {
|
|
4702
|
+
if (!isSafeSessionId(sessionId)) {
|
|
4703
|
+
return;
|
|
4704
|
+
}
|
|
4705
|
+
fs12.writeFilePrivate(fileFor(sessionId), `${JSON.stringify(credential, null, 2)}
|
|
4706
|
+
`);
|
|
4707
|
+
},
|
|
4708
|
+
remove: (sessionId) => {
|
|
4709
|
+
if (!isSafeSessionId(sessionId)) {
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
fs12.removeRecursive(fileFor(sessionId));
|
|
4713
|
+
}
|
|
4714
|
+
};
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4526
4717
|
// src/frontdoor/rcApi.ts
|
|
4527
4718
|
import { createORPCClient, ORPCError } from "@orpc/client";
|
|
4528
4719
|
import { RPCLink } from "@orpc/client/fetch";
|
|
4529
|
-
import { eventIterator, os, withEventMeta } from "@orpc/server";
|
|
4720
|
+
import { eventIterator, os as os2, withEventMeta } from "@orpc/server";
|
|
4530
4721
|
import { BodyLimitPlugin, RPCHandler } from "@orpc/server/node";
|
|
4531
4722
|
import { Agent as Agent3, fetch as undiciFetch } from "undici";
|
|
4532
4723
|
|
|
@@ -4613,7 +4804,7 @@ var RcAnswerOutputSchema = z4.strictObject({
|
|
|
4613
4804
|
var RC_ORPC_PATH_PREFIX = "/__agent-shim/orpc";
|
|
4614
4805
|
var RC_SUBSCRIBER_BUFFER_EVENTS = 100;
|
|
4615
4806
|
function createRcApiRouter(deps) {
|
|
4616
|
-
const authed =
|
|
4807
|
+
const authed = os2.$context().use(({ context, next }) => {
|
|
4617
4808
|
const presented = context.headers.authorization;
|
|
4618
4809
|
const bearerPrefix = "bearer ".length;
|
|
4619
4810
|
const token = typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
|
|
@@ -4780,7 +4971,7 @@ function createSseParser() {
|
|
|
4780
4971
|
}
|
|
4781
4972
|
};
|
|
4782
4973
|
}
|
|
4783
|
-
function
|
|
4974
|
+
function isRecord4(value) {
|
|
4784
4975
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4785
4976
|
}
|
|
4786
4977
|
function sequenceNumOf(value) {
|
|
@@ -4802,7 +4993,7 @@ function parseRcStreamEnvelope(parsed) {
|
|
|
4802
4993
|
} catch {
|
|
4803
4994
|
return void 0;
|
|
4804
4995
|
}
|
|
4805
|
-
if (!
|
|
4996
|
+
if (!isRecord4(data)) {
|
|
4806
4997
|
return void 0;
|
|
4807
4998
|
}
|
|
4808
4999
|
const eventType = data.event_type;
|
|
@@ -4891,6 +5082,7 @@ function createRcStreamHub(deps) {
|
|
|
4891
5082
|
if (!isSuccessful3(answer.status)) {
|
|
4892
5083
|
throw new Error(`the client read stream answered HTTP ${String(answer.status)}`);
|
|
4893
5084
|
}
|
|
5085
|
+
deps.log?.(`rc stream ${attachment.sessionId}: attached as client ${attachment.clientId}${resume === void 0 ? ", reading from the stream's own head" : `, resuming after sequence number ${String(resume.fromSequenceNum)}`}`);
|
|
4894
5086
|
const parser = createSseParser();
|
|
4895
5087
|
for await (const chunk of answer.chunks) {
|
|
4896
5088
|
attachment.controller.signal.throwIfAborted();
|
|
@@ -4914,6 +5106,7 @@ function createRcStreamHub(deps) {
|
|
|
4914
5106
|
const live = deps.trackedSessions().includes(attachment.sessionId);
|
|
4915
5107
|
const credential = deps.credentialOf(attachment.sessionId);
|
|
4916
5108
|
if (!live || credential?.authorization === void 0) {
|
|
5109
|
+
deps.log?.(`rc stream ${attachment.sessionId}: the attachment ended (${live ? "the session's client credential is no longer known" : "the tracker gave up on the session"})`);
|
|
4917
5110
|
if (attachments.get(attachment.sessionId) === attachment) {
|
|
4918
5111
|
attachments.delete(attachment.sessionId);
|
|
4919
5112
|
}
|
|
@@ -4935,6 +5128,7 @@ function createRcStreamHub(deps) {
|
|
|
4935
5128
|
return;
|
|
4936
5129
|
}
|
|
4937
5130
|
if (outcome === "dropped") {
|
|
5131
|
+
deps.log?.(`rc stream ${attachment.sessionId}: the stream ended of its own accord, reconnecting`);
|
|
4938
5132
|
retriedUnauthorized = false;
|
|
4939
5133
|
continue;
|
|
4940
5134
|
}
|
|
@@ -4984,7 +5178,7 @@ import { randomUUID as randomUUID3 } from "node:crypto";
|
|
|
4984
5178
|
|
|
4985
5179
|
// src/headroom/state.ts
|
|
4986
5180
|
import { createHash as createHash3 } from "node:crypto";
|
|
4987
|
-
import
|
|
5181
|
+
import path10 from "node:path";
|
|
4988
5182
|
import { z as z5 } from "zod";
|
|
4989
5183
|
var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
|
|
4990
5184
|
var HEADROOM_STATE_SCHEMA_VERSION = 2;
|
|
@@ -5025,7 +5219,7 @@ function readHeadroomState(fs12, stateFile) {
|
|
|
5025
5219
|
}
|
|
5026
5220
|
}
|
|
5027
5221
|
function writeHeadroomState(fs12, stateFile, state) {
|
|
5028
|
-
fs12.mkdirp(
|
|
5222
|
+
fs12.mkdirp(path10.dirname(stateFile));
|
|
5029
5223
|
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
5030
5224
|
`);
|
|
5031
5225
|
}
|
|
@@ -5053,7 +5247,7 @@ function readAllProviders(fs12, providersDir) {
|
|
|
5053
5247
|
continue;
|
|
5054
5248
|
}
|
|
5055
5249
|
try {
|
|
5056
|
-
const parsed = parseJson(fs12.readFileUtf8(
|
|
5250
|
+
const parsed = parseJson(fs12.readFileUtf8(path10.join(providersDir, name)), ProviderSchema);
|
|
5057
5251
|
if (parsed !== void 0) {
|
|
5058
5252
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
5059
5253
|
}
|
|
@@ -5065,11 +5259,11 @@ function readAllProviders(fs12, providersDir) {
|
|
|
5065
5259
|
}
|
|
5066
5260
|
function writeSession(fs12, sessionsDir, session) {
|
|
5067
5261
|
fs12.mkdirp(sessionsDir);
|
|
5068
|
-
fs12.writeFileUtf8(
|
|
5262
|
+
fs12.writeFileUtf8(path10.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
5069
5263
|
`);
|
|
5070
5264
|
}
|
|
5071
5265
|
function removeSession(fs12, sessionsDir, pid) {
|
|
5072
|
-
fs12.removeRecursive(
|
|
5266
|
+
fs12.removeRecursive(path10.join(sessionsDir, `${String(pid)}.json`));
|
|
5073
5267
|
}
|
|
5074
5268
|
function listSessions(fs12, sessionsDir) {
|
|
5075
5269
|
const sessions = [];
|
|
@@ -5078,7 +5272,7 @@ function listSessions(fs12, sessionsDir) {
|
|
|
5078
5272
|
continue;
|
|
5079
5273
|
}
|
|
5080
5274
|
try {
|
|
5081
|
-
const parsed = parseJson(fs12.readFileUtf8(
|
|
5275
|
+
const parsed = parseJson(fs12.readFileUtf8(path10.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
5082
5276
|
if (parsed !== void 0) {
|
|
5083
5277
|
sessions.push(parsed);
|
|
5084
5278
|
}
|
|
@@ -5183,10 +5377,10 @@ function ensureFrontDoor(params) {
|
|
|
5183
5377
|
}
|
|
5184
5378
|
|
|
5185
5379
|
// src/usage/read.ts
|
|
5186
|
-
import
|
|
5380
|
+
import path12 from "node:path";
|
|
5187
5381
|
|
|
5188
5382
|
// src/usage/account.ts
|
|
5189
|
-
import
|
|
5383
|
+
import path11 from "node:path";
|
|
5190
5384
|
|
|
5191
5385
|
// src/usage/schema.ts
|
|
5192
5386
|
import { z as z6 } from "zod";
|
|
@@ -5352,7 +5546,7 @@ function isIdentityName(name) {
|
|
|
5352
5546
|
return IdentitySchema.shape.name.safeParse(name).success;
|
|
5353
5547
|
}
|
|
5354
5548
|
function claudeJsonPath(identitiesDir, identity) {
|
|
5355
|
-
return
|
|
5549
|
+
return path11.join(identitiesDir, identity, CLAUDE_JSON);
|
|
5356
5550
|
}
|
|
5357
5551
|
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
5358
5552
|
if (!isIdentityName(identity)) {
|
|
@@ -5411,7 +5605,7 @@ function readUsageLog(fs12, logDir, options = {}) {
|
|
|
5411
5605
|
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
5412
5606
|
continue;
|
|
5413
5607
|
}
|
|
5414
|
-
const contents = fs12.readFileUtf8(
|
|
5608
|
+
const contents = fs12.readFileUtf8(path12.join(logDir, segment.name));
|
|
5415
5609
|
if (contents === void 0) {
|
|
5416
5610
|
continue;
|
|
5417
5611
|
}
|
|
@@ -5453,7 +5647,7 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
5453
5647
|
if (!isIdentityName(identity)) {
|
|
5454
5648
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
5455
5649
|
}
|
|
5456
|
-
return
|
|
5650
|
+
return path12.join(snapshotsDir, `${identity}.json`);
|
|
5457
5651
|
}
|
|
5458
5652
|
function readUsageSnapshot(fs12, snapshotsDir, identity) {
|
|
5459
5653
|
const file = snapshotPath(snapshotsDir, identity);
|
|
@@ -5787,7 +5981,7 @@ function rankPool(input) {
|
|
|
5787
5981
|
}
|
|
5788
5982
|
|
|
5789
5983
|
// src/usage/poolPick.ts
|
|
5790
|
-
import
|
|
5984
|
+
import path13 from "node:path";
|
|
5791
5985
|
import { z as z9 } from "zod";
|
|
5792
5986
|
|
|
5793
5987
|
// src/usage/store.ts
|
|
@@ -5854,13 +6048,13 @@ function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
|
5854
6048
|
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5855
6049
|
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5856
6050
|
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5857
|
-
fs12.mkdirPrivate(
|
|
6051
|
+
fs12.mkdirPrivate(path13.dirname(picksFile));
|
|
5858
6052
|
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5859
6053
|
`);
|
|
5860
6054
|
}
|
|
5861
6055
|
|
|
5862
6056
|
// src/launcher/identity.ts
|
|
5863
|
-
import
|
|
6057
|
+
import path14 from "node:path";
|
|
5864
6058
|
function poolNameOf(selector) {
|
|
5865
6059
|
return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
|
|
5866
6060
|
}
|
|
@@ -5909,7 +6103,7 @@ function decideConfigProfile(params) {
|
|
|
5909
6103
|
return { source: "none" };
|
|
5910
6104
|
}
|
|
5911
6105
|
function loadIdentity(identitiesDir, name, fs12) {
|
|
5912
|
-
return loadConfigFile(
|
|
6106
|
+
return loadConfigFile(path14.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
5913
6107
|
}
|
|
5914
6108
|
|
|
5915
6109
|
// src/launcher/pool.ts
|
|
@@ -6124,7 +6318,7 @@ function hashSettings(settings) {
|
|
|
6124
6318
|
}
|
|
6125
6319
|
|
|
6126
6320
|
// src/headroom/socket.ts
|
|
6127
|
-
import
|
|
6321
|
+
import path15 from "node:path";
|
|
6128
6322
|
var GROUP_OR_OTHER_BITS = 63;
|
|
6129
6323
|
var OCTAL_RADIX = 8;
|
|
6130
6324
|
var MODE_DIGITS = 4;
|
|
@@ -6133,7 +6327,7 @@ var HEADROOM_SOCKET_NAME = /^(\d+)\.sock$/;
|
|
|
6133
6327
|
var UNIX_SOCKET_PATH_MAX_BYTES = 103;
|
|
6134
6328
|
var PLATFORMS_WITHOUT_UNIX_SOCKETS = ["win32"];
|
|
6135
6329
|
function headroomSocketPath(paths, supervisorPid) {
|
|
6136
|
-
return
|
|
6330
|
+
return path15.join(paths.headroomSocketDir, `${String(supervisorPid)}${HEADROOM_SOCKET_SUFFIX}`);
|
|
6137
6331
|
}
|
|
6138
6332
|
function platformRefusal(platform) {
|
|
6139
6333
|
return PLATFORMS_WITHOUT_UNIX_SOCKETS.includes(platform) ? `headroom routing needs a unix domain socket, which headroom cannot serve on ${platform}; agent-shim never falls back to a TCP port for this hop` : void 0;
|
|
@@ -6182,7 +6376,7 @@ function pruneStaleHeadroomSockets(fs12, paths, isRunning) {
|
|
|
6182
6376
|
if (pid === void 0 || isRunning(Number.parseInt(pid, 10))) {
|
|
6183
6377
|
continue;
|
|
6184
6378
|
}
|
|
6185
|
-
fs12.removeRecursive(
|
|
6379
|
+
fs12.removeRecursive(path15.join(paths.headroomSocketDir, name));
|
|
6186
6380
|
removed.push(name);
|
|
6187
6381
|
}
|
|
6188
6382
|
return removed;
|
|
@@ -6236,7 +6430,7 @@ function versionSatisfies(version, source) {
|
|
|
6236
6430
|
if (installed === void 0 || wanted === void 0) {
|
|
6237
6431
|
return true;
|
|
6238
6432
|
}
|
|
6239
|
-
const cmp =
|
|
6433
|
+
const cmp = compareVersions2(installed, wanted);
|
|
6240
6434
|
switch (specifier[0]) {
|
|
6241
6435
|
case "==":
|
|
6242
6436
|
return cmp === 0;
|
|
@@ -6251,7 +6445,7 @@ function versionSatisfies(version, source) {
|
|
|
6251
6445
|
case "<":
|
|
6252
6446
|
return cmp < 0;
|
|
6253
6447
|
case "~=":
|
|
6254
|
-
return
|
|
6448
|
+
return compareVersions2(installed.slice(0, Math.max(0, wanted.length - 1)), wanted.slice(0, Math.max(0, wanted.length - 1))) === 0 && cmp >= 0;
|
|
6255
6449
|
default:
|
|
6256
6450
|
return true;
|
|
6257
6451
|
}
|
|
@@ -6263,7 +6457,7 @@ function parseVersion(input) {
|
|
|
6263
6457
|
}
|
|
6264
6458
|
return match[0].split(".").map((part) => Number.parseInt(part, 10));
|
|
6265
6459
|
}
|
|
6266
|
-
function
|
|
6460
|
+
function compareVersions2(a, b) {
|
|
6267
6461
|
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
6268
6462
|
const left = a[index] ?? 0;
|
|
6269
6463
|
const right = b[index] ?? 0;
|
|
@@ -6505,15 +6699,15 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
6505
6699
|
|
|
6506
6700
|
// src/paths.ts
|
|
6507
6701
|
import fs3 from "node:fs";
|
|
6508
|
-
import
|
|
6509
|
-
import
|
|
6510
|
-
function resolveAgentShimHome(env = process.env, home =
|
|
6702
|
+
import os3 from "node:os";
|
|
6703
|
+
import path16 from "node:path";
|
|
6704
|
+
function resolveAgentShimHome(env = process.env, home = os3.homedir(), isDirectory = isExistingDirectory) {
|
|
6511
6705
|
const fromEnv = env.AGENT_SHIM_HOME;
|
|
6512
6706
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6513
6707
|
return fromEnv;
|
|
6514
6708
|
}
|
|
6515
|
-
const current =
|
|
6516
|
-
const legacy =
|
|
6709
|
+
const current = path16.join(home, HOME_DIRNAME);
|
|
6710
|
+
const legacy = path16.join(home, LEGACY_HOME_DIRNAME);
|
|
6517
6711
|
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
6518
6712
|
}
|
|
6519
6713
|
function isExistingDirectory(candidate) {
|
|
@@ -6524,40 +6718,41 @@ function resolveClaudeHome() {
|
|
|
6524
6718
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6525
6719
|
return fromEnv;
|
|
6526
6720
|
}
|
|
6527
|
-
return
|
|
6721
|
+
return path16.join(os3.homedir(), ".claude");
|
|
6528
6722
|
}
|
|
6529
6723
|
function buildLayoutPaths(root) {
|
|
6530
6724
|
return {
|
|
6531
6725
|
root,
|
|
6532
|
-
identitiesDir:
|
|
6533
|
-
configProfilesDir:
|
|
6534
|
-
providersDir:
|
|
6535
|
-
directoryRulesFile:
|
|
6536
|
-
activeIdentityFile:
|
|
6537
|
-
globalConfigFile:
|
|
6538
|
-
categoriesLocalFile:
|
|
6539
|
-
claudeShimFile:
|
|
6540
|
-
headroomDir:
|
|
6541
|
-
headroomStateFile:
|
|
6542
|
-
headroomSocketDir:
|
|
6543
|
-
headroomLockFile:
|
|
6544
|
-
headroomSessionsDir:
|
|
6545
|
-
logsDir:
|
|
6546
|
-
headroomLogPath:
|
|
6547
|
-
frontdoorDir:
|
|
6548
|
-
frontdoorCaDir:
|
|
6549
|
-
frontdoorCaCertFile:
|
|
6550
|
-
frontdoorCaKeyFile:
|
|
6551
|
-
frontdoorCaBundlesDir:
|
|
6552
|
-
frontdoorStateFile:
|
|
6553
|
-
frontdoorLockFile:
|
|
6554
|
-
frontdoorControlTokenFile:
|
|
6555
|
-
frontdoorSessionsDir:
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6726
|
+
identitiesDir: path16.join(root, "identities"),
|
|
6727
|
+
configProfilesDir: path16.join(root, "config-profiles"),
|
|
6728
|
+
providersDir: path16.join(root, "providers"),
|
|
6729
|
+
directoryRulesFile: path16.join(root, "directory-rules.json"),
|
|
6730
|
+
activeIdentityFile: path16.join(root, "active-identity"),
|
|
6731
|
+
globalConfigFile: path16.join(root, "config.json"),
|
|
6732
|
+
categoriesLocalFile: path16.join(root, "categories.local.json"),
|
|
6733
|
+
claudeShimFile: path16.join(root, "claude-shim.json"),
|
|
6734
|
+
headroomDir: path16.join(root, "headroom"),
|
|
6735
|
+
headroomStateFile: path16.join(root, "headroom", `state.v${String(HEADROOM_STATE_SCHEMA_VERSION)}.json`),
|
|
6736
|
+
headroomSocketDir: path16.join(root, "headroom", "run"),
|
|
6737
|
+
headroomLockFile: path16.join(root, "headroom", "start.lock"),
|
|
6738
|
+
headroomSessionsDir: path16.join(root, "headroom", "sessions"),
|
|
6739
|
+
logsDir: path16.join(root, "logs"),
|
|
6740
|
+
headroomLogPath: path16.join(root, "logs", "headroom.log"),
|
|
6741
|
+
frontdoorDir: path16.join(root, "frontdoor"),
|
|
6742
|
+
frontdoorCaDir: path16.join(root, "frontdoor", "ca"),
|
|
6743
|
+
frontdoorCaCertFile: path16.join(root, "frontdoor", "ca", "ca.pem"),
|
|
6744
|
+
frontdoorCaKeyFile: path16.join(root, "frontdoor", "ca", "ca.key"),
|
|
6745
|
+
frontdoorCaBundlesDir: path16.join(root, "frontdoor", "ca", "bundles"),
|
|
6746
|
+
frontdoorStateFile: path16.join(root, "frontdoor", "state.json"),
|
|
6747
|
+
frontdoorLockFile: path16.join(root, "frontdoor", "start.lock"),
|
|
6748
|
+
frontdoorControlTokenFile: path16.join(root, "frontdoor", "control-token"),
|
|
6749
|
+
frontdoorSessionsDir: path16.join(root, "frontdoor", "sessions"),
|
|
6750
|
+
frontdoorRcCredentialsDir: path16.join(root, "frontdoor", "rc-credentials"),
|
|
6751
|
+
frontdoorLogPath: path16.join(root, "logs", "frontdoor.log"),
|
|
6752
|
+
usageDir: path16.join(root, "usage"),
|
|
6753
|
+
usageLogDir: path16.join(root, "usage", "log"),
|
|
6754
|
+
usageSnapshotsDir: path16.join(root, "usage", "snapshots"),
|
|
6755
|
+
usagePicksFile: path16.join(root, "usage", "picks.json")
|
|
6561
6756
|
};
|
|
6562
6757
|
}
|
|
6563
6758
|
function resolveLayoutPaths() {
|
|
@@ -6566,11 +6761,11 @@ function resolveLayoutPaths() {
|
|
|
6566
6761
|
|
|
6567
6762
|
// src/identityStore.ts
|
|
6568
6763
|
import fs5 from "node:fs";
|
|
6569
|
-
import
|
|
6764
|
+
import path18 from "node:path";
|
|
6570
6765
|
|
|
6571
6766
|
// src/configProfilesStore.ts
|
|
6572
6767
|
import fs4 from "node:fs";
|
|
6573
|
-
import
|
|
6768
|
+
import path17 from "node:path";
|
|
6574
6769
|
var ProfileNotFoundError = class extends CliError {
|
|
6575
6770
|
constructor(profileName) {
|
|
6576
6771
|
super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
|
|
@@ -6596,7 +6791,7 @@ var InvalidCategoryNameError = class extends CliError {
|
|
|
6596
6791
|
categoryName;
|
|
6597
6792
|
};
|
|
6598
6793
|
function profileJsonPath(paths, name) {
|
|
6599
|
-
return
|
|
6794
|
+
return path17.join(paths.configProfilesDir, `${name}.json`);
|
|
6600
6795
|
}
|
|
6601
6796
|
function profileExists(paths, name) {
|
|
6602
6797
|
return fs4.existsSync(profileJsonPath(paths, name));
|
|
@@ -6784,7 +6979,7 @@ var InvalidIdentityNameError = class extends CliError {
|
|
|
6784
6979
|
attemptedName;
|
|
6785
6980
|
};
|
|
6786
6981
|
function identityJsonPath(paths, name) {
|
|
6787
|
-
return
|
|
6982
|
+
return path18.join(paths.identitiesDir, name, "identity.json");
|
|
6788
6983
|
}
|
|
6789
6984
|
function identityExists(paths, name) {
|
|
6790
6985
|
return fs5.existsSync(identityJsonPath(paths, name));
|
|
@@ -6885,7 +7080,7 @@ function setIdentityCredential(paths, identityName, change) {
|
|
|
6885
7080
|
});
|
|
6886
7081
|
}
|
|
6887
7082
|
function belongsToIdentity(entry, name) {
|
|
6888
|
-
return entry === name || entry ===
|
|
7083
|
+
return entry === name || entry === path18.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
6889
7084
|
}
|
|
6890
7085
|
function removeIdentity(paths, name) {
|
|
6891
7086
|
if (!identityExists(paths, name)) {
|
|
@@ -6893,7 +7088,7 @@ function removeIdentity(paths, name) {
|
|
|
6893
7088
|
}
|
|
6894
7089
|
for (const entry of fs5.readdirSync(paths.identitiesDir)) {
|
|
6895
7090
|
if (belongsToIdentity(entry, name)) {
|
|
6896
|
-
fs5.rmSync(
|
|
7091
|
+
fs5.rmSync(path18.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
6897
7092
|
}
|
|
6898
7093
|
}
|
|
6899
7094
|
if (readActiveIdentity(paths) === name) {
|
|
@@ -6903,7 +7098,7 @@ function removeIdentity(paths, name) {
|
|
|
6903
7098
|
|
|
6904
7099
|
// src/providersStore.ts
|
|
6905
7100
|
import fs6 from "node:fs";
|
|
6906
|
-
import
|
|
7101
|
+
import path19 from "node:path";
|
|
6907
7102
|
|
|
6908
7103
|
// src/credentialCache.ts
|
|
6909
7104
|
import { z as z12 } from "zod";
|
|
@@ -7168,7 +7363,7 @@ var InvalidProviderNameError = class extends CliError {
|
|
|
7168
7363
|
};
|
|
7169
7364
|
var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
|
|
7170
7365
|
function providerJsonPath(paths, name) {
|
|
7171
|
-
return
|
|
7366
|
+
return path19.join(paths.providersDir, `${name}.json`);
|
|
7172
7367
|
}
|
|
7173
7368
|
function providerExists(paths, name) {
|
|
7174
7369
|
return fs6.existsSync(providerJsonPath(paths, name));
|
|
@@ -7176,14 +7371,14 @@ function providerExists(paths, name) {
|
|
|
7176
7371
|
var LEGACY_PROVIDER_FIELDS = ["tokenEnv", "tokenCommand", "authScheme"];
|
|
7177
7372
|
var LEGACY_ENV_CREDENTIAL_KEYS = Object.values(CREDENTIAL_TARGET_VARS);
|
|
7178
7373
|
var LEGACY_LITERAL_PLACEHOLDER = "<the value of env.ANTHROPIC_AUTH_TOKEN>";
|
|
7179
|
-
function
|
|
7374
|
+
function isRecord5(value) {
|
|
7180
7375
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7181
7376
|
}
|
|
7182
7377
|
function legacyProviderConversion(raw) {
|
|
7183
|
-
if (!
|
|
7378
|
+
if (!isRecord5(raw) || "credential" in raw) {
|
|
7184
7379
|
return void 0;
|
|
7185
7380
|
}
|
|
7186
|
-
const env =
|
|
7381
|
+
const env = isRecord5(raw.env) ? raw.env : void 0;
|
|
7187
7382
|
const legacyEnvKeys = env === void 0 ? [] : Object.keys(env).filter((key) => LEGACY_ENV_CREDENTIAL_KEYS.includes(key));
|
|
7188
7383
|
const fields = [...LEGACY_PROVIDER_FIELDS.filter((field) => field in raw), ...legacyEnvKeys.map((key) => `env.${key}`)];
|
|
7189
7384
|
if (fields.length === 0) {
|
|
@@ -7244,7 +7439,7 @@ function readProvider(paths, name) {
|
|
|
7244
7439
|
return parseProviderFile(filePath, raw);
|
|
7245
7440
|
}
|
|
7246
7441
|
function loadProvider(providersDir, name, port) {
|
|
7247
|
-
const filePath =
|
|
7442
|
+
const filePath = path19.join(providersDir, `${name}.json`);
|
|
7248
7443
|
const raw = port.readConfigFile(filePath);
|
|
7249
7444
|
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
7250
7445
|
}
|
|
@@ -7499,21 +7694,21 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
7499
7694
|
import os5 from "node:os";
|
|
7500
7695
|
|
|
7501
7696
|
// src/launcher/cascade.ts
|
|
7502
|
-
import
|
|
7697
|
+
import path21 from "node:path";
|
|
7503
7698
|
|
|
7504
7699
|
// src/pathNorm.ts
|
|
7505
|
-
import
|
|
7700
|
+
import path20 from "node:path";
|
|
7506
7701
|
function expandTilde(value, home) {
|
|
7507
7702
|
if (value === "~") {
|
|
7508
7703
|
return home;
|
|
7509
7704
|
}
|
|
7510
7705
|
if (value.startsWith("~/")) {
|
|
7511
|
-
return
|
|
7706
|
+
return path20.join(home, value.slice(2));
|
|
7512
7707
|
}
|
|
7513
7708
|
return value;
|
|
7514
7709
|
}
|
|
7515
7710
|
function normaliseRulePath(rulePath, home) {
|
|
7516
|
-
return
|
|
7711
|
+
return path20.resolve(expandTilde(rulePath, home));
|
|
7517
7712
|
}
|
|
7518
7713
|
|
|
7519
7714
|
// src/launcher/cascade.ts
|
|
@@ -7523,7 +7718,7 @@ function loadCascadeInput(params) {
|
|
|
7523
7718
|
const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
|
|
7524
7719
|
const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
|
|
7525
7720
|
const loadProfile = (name) => {
|
|
7526
|
-
const filepath =
|
|
7721
|
+
const filepath = path21.join(params.paths.configProfilesDir, `${name}.json`);
|
|
7527
7722
|
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
7528
7723
|
if (loaded === void 0) {
|
|
7529
7724
|
return void 0;
|
|
@@ -7538,8 +7733,8 @@ function loadCascadeInput(params) {
|
|
|
7538
7733
|
});
|
|
7539
7734
|
const levels = [];
|
|
7540
7735
|
for (const dir of dirs) {
|
|
7541
|
-
const portable = loadConfigFile(
|
|
7542
|
-
const portableLocal = loadConfigFile(
|
|
7736
|
+
const portable = loadConfigFile(path21.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path21.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
7737
|
+
const portableLocal = loadConfigFile(path21.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path21.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
7543
7738
|
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
7544
7739
|
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
7545
7740
|
return {
|
|
@@ -7590,7 +7785,7 @@ function readDirectorySelections(loaded) {
|
|
|
7590
7785
|
// src/realCredentialCache.ts
|
|
7591
7786
|
import { spawnSync } from "node:child_process";
|
|
7592
7787
|
import fs7 from "node:fs";
|
|
7593
|
-
import
|
|
7788
|
+
import path22 from "node:path";
|
|
7594
7789
|
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
7595
7790
|
var CACHE_DIR_MODE = 448;
|
|
7596
7791
|
var CACHE_FILE_MODE = 384;
|
|
@@ -7617,8 +7812,8 @@ function security(args, stdin) {
|
|
|
7617
7812
|
return { status: result.status, stdout: result.stdout };
|
|
7618
7813
|
}
|
|
7619
7814
|
function createRealCredentialCache(root) {
|
|
7620
|
-
const dir =
|
|
7621
|
-
const filePath = (owner) =>
|
|
7815
|
+
const dir = path22.join(root, "credential-cache");
|
|
7816
|
+
const filePath = (owner) => path22.join(dir, cacheFileNameFor(owner));
|
|
7622
7817
|
return {
|
|
7623
7818
|
read(store, owner) {
|
|
7624
7819
|
if (store === "file") {
|
|
@@ -7670,72 +7865,8 @@ import { spawn, spawnSync as spawnSync2 } from "node:child_process";
|
|
|
7670
7865
|
import { createHash as createHash5 } from "node:crypto";
|
|
7671
7866
|
import fs8 from "node:fs";
|
|
7672
7867
|
import os4 from "node:os";
|
|
7673
|
-
import
|
|
7868
|
+
import path23 from "node:path";
|
|
7674
7869
|
import { isSea } from "node:sea";
|
|
7675
|
-
|
|
7676
|
-
// src/versionDiscovery.ts
|
|
7677
|
-
import os3 from "node:os";
|
|
7678
|
-
import path21 from "node:path";
|
|
7679
|
-
function defaultVersionsDir() {
|
|
7680
|
-
return path21.join(os3.homedir(), ".local", "share", "claude", "versions");
|
|
7681
|
-
}
|
|
7682
|
-
var NUMERIC_DOTTED_VERSION_RE = /^\d+(?:\.\d+)*$/;
|
|
7683
|
-
function isNumericDottedVersion(name) {
|
|
7684
|
-
return NUMERIC_DOTTED_VERSION_RE.test(name);
|
|
7685
|
-
}
|
|
7686
|
-
function compareVersions2(a, b) {
|
|
7687
|
-
if (!isNumericDottedVersion(a)) {
|
|
7688
|
-
throw new Error(`compareVersions: "${a}" is not a valid plain dotted-numeric version`);
|
|
7689
|
-
}
|
|
7690
|
-
if (!isNumericDottedVersion(b)) {
|
|
7691
|
-
throw new Error(`compareVersions: "${b}" is not a valid plain dotted-numeric version`);
|
|
7692
|
-
}
|
|
7693
|
-
const segmentsA = a.split(".").map(Number);
|
|
7694
|
-
const segmentsB = b.split(".").map(Number);
|
|
7695
|
-
const length = Math.max(segmentsA.length, segmentsB.length);
|
|
7696
|
-
for (let i = 0; i < length; i += 1) {
|
|
7697
|
-
const segmentA = segmentsA[i] ?? 0;
|
|
7698
|
-
const segmentB = segmentsB[i] ?? 0;
|
|
7699
|
-
if (segmentA !== segmentB) {
|
|
7700
|
-
return segmentA - segmentB;
|
|
7701
|
-
}
|
|
7702
|
-
}
|
|
7703
|
-
return 0;
|
|
7704
|
-
}
|
|
7705
|
-
function pickHighestVersion(entries) {
|
|
7706
|
-
const candidates = entries.filter(
|
|
7707
|
-
(entry) => entry.isFile && entry.isExecutable && entry.sizeBytes > 0 && isNumericDottedVersion(entry.name)
|
|
7708
|
-
);
|
|
7709
|
-
if (candidates.length === 0) {
|
|
7710
|
-
return void 0;
|
|
7711
|
-
}
|
|
7712
|
-
return candidates.reduce(
|
|
7713
|
-
(highest, candidate) => compareVersions2(candidate.name, highest.name) > 0 ? candidate : highest
|
|
7714
|
-
).name;
|
|
7715
|
-
}
|
|
7716
|
-
function discoverClaudeBinary(options) {
|
|
7717
|
-
const versionsDir = options.versionsDir ?? defaultVersionsDir();
|
|
7718
|
-
const entries = options.listVersionsDir(versionsDir);
|
|
7719
|
-
const highestVersion = pickHighestVersion(entries);
|
|
7720
|
-
if (highestVersion !== void 0) {
|
|
7721
|
-
return {
|
|
7722
|
-
path: path21.join(versionsDir, highestVersion),
|
|
7723
|
-
source: "versions-dir",
|
|
7724
|
-
version: highestVersion
|
|
7725
|
-
};
|
|
7726
|
-
}
|
|
7727
|
-
for (const dir of options.pathDirs) {
|
|
7728
|
-
const found = options.findExecutableInDir(dir, "claude");
|
|
7729
|
-
if (found !== void 0 && !options.isOwnBinary(found)) {
|
|
7730
|
-
return { path: found, source: "path-fallback" };
|
|
7731
|
-
}
|
|
7732
|
-
}
|
|
7733
|
-
throw new Error(
|
|
7734
|
-
`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 (never this tool's own binary). Install Claude Code, or set the versions directory / PATH correctly, then try again.`
|
|
7735
|
-
);
|
|
7736
|
-
}
|
|
7737
|
-
|
|
7738
|
-
// src/realPorts.ts
|
|
7739
7870
|
function isEnoent3(error) {
|
|
7740
7871
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
7741
7872
|
}
|
|
@@ -7893,7 +8024,7 @@ var realRunPort = {
|
|
|
7893
8024
|
}
|
|
7894
8025
|
};
|
|
7895
8026
|
function expandHome2(filePath) {
|
|
7896
|
-
return filePath.startsWith("~/") ?
|
|
8027
|
+
return filePath.startsWith("~/") ? path23.join(os4.homedir(), filePath.slice(1)) : filePath;
|
|
7897
8028
|
}
|
|
7898
8029
|
var MACOS_GUI_SESSION_MANAGER = "Aqua";
|
|
7899
8030
|
function hasDesktopSession() {
|
|
@@ -7966,7 +8097,7 @@ function spawnDaemonThrough(executable) {
|
|
|
7966
8097
|
return (paths, subcommand, logPath) => spawnDetachedDaemon({ command: executable, args: [subcommand] }, paths, subcommand, logPath);
|
|
7967
8098
|
}
|
|
7968
8099
|
function spawnDetachedDaemon(invocation, paths, subcommand, logPath) {
|
|
7969
|
-
fs8.mkdirSync(
|
|
8100
|
+
fs8.mkdirSync(path23.dirname(logPath), { recursive: true });
|
|
7970
8101
|
const logFd = fs8.openSync(logPath, "a");
|
|
7971
8102
|
try {
|
|
7972
8103
|
const child = spawn(invocation.command, invocation.args, detachedDaemonSpawnOptions(logFd, { ...process.env, AGENT_SHIM_HOME: paths.root }));
|
|
@@ -8048,7 +8179,7 @@ function listVersionsDir(dir) {
|
|
|
8048
8179
|
throw error;
|
|
8049
8180
|
}
|
|
8050
8181
|
return entries.filter((entry) => entry.isFile()).map((entry) => {
|
|
8051
|
-
const fullPath =
|
|
8182
|
+
const fullPath = path23.join(dir, entry.name);
|
|
8052
8183
|
const stat = fs8.statSync(fullPath);
|
|
8053
8184
|
return {
|
|
8054
8185
|
name: entry.name,
|
|
@@ -8063,7 +8194,7 @@ function windowsPathExtensions(pathext) {
|
|
|
8063
8194
|
return (pathext ?? WINDOWS_DEFAULT_PATHEXT).split(";").map((ext) => ext.trim()).filter((ext) => ext.length > 0);
|
|
8064
8195
|
}
|
|
8065
8196
|
function resolveExecutableCandidate(dir, name, env) {
|
|
8066
|
-
const candidate =
|
|
8197
|
+
const candidate = path23.join(dir, name);
|
|
8067
8198
|
if (env.platform === "win32") {
|
|
8068
8199
|
const extensions = windowsPathExtensions(env.pathext);
|
|
8069
8200
|
const alreadyHasRecognisedExtension = extensions.some((ext) => candidate.toLowerCase().endsWith(ext.toLowerCase()));
|
|
@@ -8112,13 +8243,13 @@ function resolveOwnExecutablePath(env) {
|
|
|
8112
8243
|
if (env.argv1 !== void 0 && !env.argv1.includes("/") && !env.argv1.includes("\\")) {
|
|
8113
8244
|
return searchPathForExecutable(env.pathDirs, env.argv1, env.findExecutableInDir) ?? env.execPath;
|
|
8114
8245
|
}
|
|
8115
|
-
const candidate = env.argv1 === void 0 ? env.execPath :
|
|
8116
|
-
const candidateDir =
|
|
8117
|
-
const candidateDirOnPath = env.pathDirs.some((dir) =>
|
|
8246
|
+
const candidate = env.argv1 === void 0 ? env.execPath : path23.resolve(env.cwd, env.argv1);
|
|
8247
|
+
const candidateDir = path23.dirname(candidate);
|
|
8248
|
+
const candidateDirOnPath = env.pathDirs.some((dir) => path23.resolve(dir) === candidateDir);
|
|
8118
8249
|
if (candidateDirOnPath) {
|
|
8119
8250
|
return candidate;
|
|
8120
8251
|
}
|
|
8121
|
-
const viaPath = searchPathForExecutable(env.pathDirs,
|
|
8252
|
+
const viaPath = searchPathForExecutable(env.pathDirs, path23.basename(candidate), env.findExecutableInDir);
|
|
8122
8253
|
return viaPath ?? candidate;
|
|
8123
8254
|
}
|
|
8124
8255
|
function realOwnExecutablePath() {
|
|
@@ -8126,7 +8257,7 @@ function realOwnExecutablePath() {
|
|
|
8126
8257
|
argv1: process.argv[1],
|
|
8127
8258
|
execPath: process.execPath,
|
|
8128
8259
|
cwd: process.cwd(),
|
|
8129
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
8260
|
+
pathDirs: (process.env.PATH ?? "").split(path23.delimiter).filter((dir) => dir !== ""),
|
|
8130
8261
|
findExecutableInDir
|
|
8131
8262
|
});
|
|
8132
8263
|
}
|
|
@@ -8137,16 +8268,20 @@ function realContentSourcePath() {
|
|
|
8137
8268
|
return resolveContentSourcePath({ isSea: isSea(), execPath: process.execPath, argv1: process.argv[1] });
|
|
8138
8269
|
}
|
|
8139
8270
|
function realResolveClaudeBinary(isOwnBinary) {
|
|
8140
|
-
return () => discoverClaudeBinary({
|
|
8271
|
+
return (request4) => discoverClaudeBinary({
|
|
8141
8272
|
listVersionsDir,
|
|
8142
|
-
pathDirs: (process.env.PATH ?? "").split(
|
|
8273
|
+
pathDirs: (process.env.PATH ?? "").split(path23.delimiter).filter((dir) => dir !== ""),
|
|
8143
8274
|
findExecutableInDir,
|
|
8144
|
-
isOwnBinary
|
|
8275
|
+
isOwnBinary,
|
|
8276
|
+
...request4?.version === void 0 ? {} : { version: request4.version }
|
|
8145
8277
|
});
|
|
8146
8278
|
}
|
|
8279
|
+
function realInstalledClaudeVersions() {
|
|
8280
|
+
return installedVersions(listVersionsDir(defaultVersionsDir()));
|
|
8281
|
+
}
|
|
8147
8282
|
|
|
8148
8283
|
// src/checkReport.ts
|
|
8149
|
-
import
|
|
8284
|
+
import path24 from "node:path";
|
|
8150
8285
|
import { z as z13 } from "zod";
|
|
8151
8286
|
|
|
8152
8287
|
// src/cli/bool.ts
|
|
@@ -8185,6 +8320,34 @@ function parseEnvBool(variable, value) {
|
|
|
8185
8320
|
return parsed;
|
|
8186
8321
|
}
|
|
8187
8322
|
|
|
8323
|
+
// src/launcher/claudeVersion.ts
|
|
8324
|
+
var InvalidClaudeVersionError = class extends UsageError {
|
|
8325
|
+
constructor(value, origin) {
|
|
8326
|
+
super(`${origin} names Claude Code version "${value}", which is not an exact dotted-numeric version such as 2.1.220.`);
|
|
8327
|
+
this.value = value;
|
|
8328
|
+
this.origin = origin;
|
|
8329
|
+
this.name = "InvalidClaudeVersionError";
|
|
8330
|
+
}
|
|
8331
|
+
value;
|
|
8332
|
+
origin;
|
|
8333
|
+
};
|
|
8334
|
+
function validated(value, origin) {
|
|
8335
|
+
if (!NUMERIC_DOTTED_VERSION_RE.test(value)) {
|
|
8336
|
+
throw new InvalidClaudeVersionError(value, origin);
|
|
8337
|
+
}
|
|
8338
|
+
return value;
|
|
8339
|
+
}
|
|
8340
|
+
function resolveClaudeVersion(params) {
|
|
8341
|
+
if (params.flag !== void 0) {
|
|
8342
|
+
return { version: validated(params.flag, "--claude-version"), source: "flag" };
|
|
8343
|
+
}
|
|
8344
|
+
const fromEnv = params.env.AGENT_SHIM_CLAUDE_VERSION;
|
|
8345
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
8346
|
+
return { version: validated(fromEnv, "AGENT_SHIM_CLAUDE_VERSION"), source: "environment" };
|
|
8347
|
+
}
|
|
8348
|
+
return params.cascade === void 0 ? void 0 : { version: params.cascade, source: "cascade" };
|
|
8349
|
+
}
|
|
8350
|
+
|
|
8188
8351
|
// src/checkReport.ts
|
|
8189
8352
|
var HISTORY_PROJECTS_KEY_PREFIX = "history/projects/";
|
|
8190
8353
|
function projectFragmentOf(rawKey) {
|
|
@@ -8335,6 +8498,19 @@ function buildCredentialReport(provider, identity, identityName, cacheEnv) {
|
|
|
8335
8498
|
...identityCached === void 0 ? {} : { identityCached }
|
|
8336
8499
|
};
|
|
8337
8500
|
}
|
|
8501
|
+
function buildClaudeVersionReport(params, cascadePin) {
|
|
8502
|
+
const installed = params.installedClaudeVersions;
|
|
8503
|
+
if (installed === void 0) {
|
|
8504
|
+
return void 0;
|
|
8505
|
+
}
|
|
8506
|
+
const pin = resolveClaudeVersion({ env: params.env, ...cascadePin === void 0 ? {} : { cascade: cascadePin } });
|
|
8507
|
+
const highestInstalled = installed.at(-1);
|
|
8508
|
+
return {
|
|
8509
|
+
...pin === void 0 ? {} : { pinned: { ...pin, installed: installed.includes(pin.version) } },
|
|
8510
|
+
...highestInstalled === void 0 ? {} : { highestInstalled },
|
|
8511
|
+
installed
|
|
8512
|
+
};
|
|
8513
|
+
}
|
|
8338
8514
|
function runCheck(params) {
|
|
8339
8515
|
const facts = buildEntryFacts({
|
|
8340
8516
|
fs: params.farmFs,
|
|
@@ -8359,6 +8535,7 @@ function runCheck(params) {
|
|
|
8359
8535
|
const settingsShared = resolved.flattened.categories.get("settings") ?? SHIPPED_CATEGORY_DEFAULTS.settings;
|
|
8360
8536
|
const settingsExposure = inspectSettingsExposure({ settingsShared, files: params.settingsFiles });
|
|
8361
8537
|
const keychain = params.platform === "darwin" && params.run !== void 0 && params.farmRoot !== void 0 ? lookupKeychainService(params.run, params.farmRoot) : void 0;
|
|
8538
|
+
const claudeVersion = buildClaudeVersionReport(params, resolved.flattened.launch.claudeVersion);
|
|
8362
8539
|
return {
|
|
8363
8540
|
...params.identityName === void 0 ? {} : { identityName: params.identityName },
|
|
8364
8541
|
identitySource: params.identitySource,
|
|
@@ -8371,7 +8548,8 @@ function runCheck(params) {
|
|
|
8371
8548
|
ambientCredential,
|
|
8372
8549
|
...keychain === void 0 ? {} : { keychain },
|
|
8373
8550
|
settingsExposure,
|
|
8374
|
-
credential: buildCredentialReport(params.provider, params.identity, params.identityName, params.credentialCache)
|
|
8551
|
+
credential: buildCredentialReport(params.provider, params.identity, params.identityName, params.credentialCache),
|
|
8552
|
+
...claudeVersion === void 0 ? {} : { claudeVersion }
|
|
8375
8553
|
};
|
|
8376
8554
|
}
|
|
8377
8555
|
function formatCheckReport(report) {
|
|
@@ -8420,6 +8598,17 @@ function formatCheckReport(report) {
|
|
|
8420
8598
|
if (credential.applies === "stored-login") {
|
|
8421
8599
|
lines.push(" The identity's stored login (no credential block applies).");
|
|
8422
8600
|
}
|
|
8601
|
+
if (report.claudeVersion !== void 0) {
|
|
8602
|
+
lines.push("", "Claude Code version:");
|
|
8603
|
+
const { pinned, highestInstalled, installed } = report.claudeVersion;
|
|
8604
|
+
if (pinned !== void 0) {
|
|
8605
|
+
lines.push(
|
|
8606
|
+
pinned.installed ? ` ${pinned.version}, pinned by the ${pinned.source} (installed)` : ` ${pinned.version}, pinned by the ${pinned.source}, but NOT installed; a launch here fails. Installed: ${installed.length === 0 ? "none" : installed.join(", ")}`
|
|
8607
|
+
);
|
|
8608
|
+
} else {
|
|
8609
|
+
lines.push(highestInstalled === void 0 ? " none installed in the versions directory (a launch falls back to a claude on PATH)" : ` ${highestInstalled} (the highest installed; nothing pins a version)`);
|
|
8610
|
+
}
|
|
8611
|
+
}
|
|
8423
8612
|
lines.push("", "Ambient-credential exposure:");
|
|
8424
8613
|
lines.push(
|
|
8425
8614
|
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")
|
|
@@ -8458,11 +8647,12 @@ function checkReportToJson(report) {
|
|
|
8458
8647
|
ambientCredential: report.ambientCredential.ok ? { ok: true } : { ok: false, variable: report.ambientCredential.variable, message: report.ambientCredential.message },
|
|
8459
8648
|
...report.keychain === void 0 ? {} : { keychain: report.keychain },
|
|
8460
8649
|
settingsExposure: report.settingsExposure,
|
|
8461
|
-
credential: report.credential
|
|
8650
|
+
credential: report.credential,
|
|
8651
|
+
...report.claudeVersion === void 0 ? {} : { claudeVersion: report.claudeVersion }
|
|
8462
8652
|
};
|
|
8463
8653
|
}
|
|
8464
8654
|
function checkReportHasWarnings(report) {
|
|
8465
|
-
return report.resolved.diagnostics.some((diagnostic) => diagnostic.severity !== "info") || report.projectEncodingAmbiguities.length > 0 || !report.ambientCredential.ok || report.credential.provider?.problem !== void 0;
|
|
8655
|
+
return report.resolved.diagnostics.some((diagnostic) => diagnostic.severity !== "info") || report.projectEncodingAmbiguities.length > 0 || !report.ambientCredential.ok || report.credential.provider?.problem !== void 0 || report.claudeVersion?.pinned?.installed === false;
|
|
8466
8656
|
}
|
|
8467
8657
|
function loadCheckProvider(paths, name) {
|
|
8468
8658
|
let definition;
|
|
@@ -8523,10 +8713,10 @@ function collectCheckReport(params) {
|
|
|
8523
8713
|
}).input;
|
|
8524
8714
|
const providerName = cascadeProviderName(cascade);
|
|
8525
8715
|
const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
|
|
8526
|
-
const farmRoot = identityDecision.name === void 0 ? void 0 :
|
|
8716
|
+
const farmRoot = identityDecision.name === void 0 ? void 0 : path24.join(paths.identitiesDir, identityDecision.name);
|
|
8527
8717
|
const settingsFiles = {
|
|
8528
|
-
"settings.json": realFsPort.readFileUtf8(
|
|
8529
|
-
"settings.local.json": realFsPort.readFileUtf8(
|
|
8718
|
+
"settings.json": realFsPort.readFileUtf8(path24.join(claudeHome, "settings.json")),
|
|
8719
|
+
"settings.local.json": realFsPort.readFileUtf8(path24.join(claudeHome, "settings.local.json"))
|
|
8530
8720
|
};
|
|
8531
8721
|
const report = runCheck({
|
|
8532
8722
|
cwd,
|
|
@@ -8548,6 +8738,7 @@ function collectCheckReport(params) {
|
|
|
8548
8738
|
settingsFiles,
|
|
8549
8739
|
run: realRunPort,
|
|
8550
8740
|
credentialCache: realCredentialCacheEnv(paths),
|
|
8741
|
+
installedClaudeVersions: realInstalledClaudeVersions(),
|
|
8551
8742
|
...farmRoot === void 0 ? {} : { farmRoot },
|
|
8552
8743
|
platform: process.platform,
|
|
8553
8744
|
...provider === void 0 ? {} : { provider }
|
|
@@ -8557,7 +8748,7 @@ function collectCheckReport(params) {
|
|
|
8557
8748
|
|
|
8558
8749
|
// src/claudeShim.ts
|
|
8559
8750
|
import fs9 from "node:fs";
|
|
8560
|
-
import
|
|
8751
|
+
import path25 from "node:path";
|
|
8561
8752
|
import { z as z14 } from "zod";
|
|
8562
8753
|
var ClaudeShimStateSchema = z14.strictObject({
|
|
8563
8754
|
targetPath: z14.string().min(1),
|
|
@@ -8569,10 +8760,10 @@ function commandFilename(ownExecutablePath, commandName) {
|
|
|
8569
8760
|
}
|
|
8570
8761
|
function resolveOwnBinaryCheck(paths, contentSourcePath) {
|
|
8571
8762
|
const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
8572
|
-
const recordedTarget = state === void 0 ? void 0 :
|
|
8763
|
+
const recordedTarget = state === void 0 ? void 0 : path25.resolve(state.targetPath);
|
|
8573
8764
|
let ownStat;
|
|
8574
8765
|
return (candidate) => {
|
|
8575
|
-
if (recordedTarget !== void 0 &&
|
|
8766
|
+
if (recordedTarget !== void 0 && path25.resolve(candidate) === recordedTarget) {
|
|
8576
8767
|
return true;
|
|
8577
8768
|
}
|
|
8578
8769
|
ownStat ??= statOrUndefined(contentSourcePath);
|
|
@@ -8596,20 +8787,20 @@ function statOrUndefined(target) {
|
|
|
8596
8787
|
}
|
|
8597
8788
|
var nodeLinkFs = { link: fs9.linkSync, copyFile: fs9.copyFileSync, chmod: fs9.chmodSync };
|
|
8598
8789
|
function findPathShadow(params) {
|
|
8599
|
-
const resolvedTargetDir =
|
|
8790
|
+
const resolvedTargetDir = path25.resolve(params.targetDir);
|
|
8600
8791
|
for (const dir of params.pathDirs) {
|
|
8601
8792
|
const found = params.findExecutableInDir(dir, params.targetFilename);
|
|
8602
8793
|
if (found === void 0) {
|
|
8603
8794
|
continue;
|
|
8604
8795
|
}
|
|
8605
|
-
return
|
|
8796
|
+
return path25.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
|
|
8606
8797
|
}
|
|
8607
|
-
return params.pathDirs.some((dir) =>
|
|
8798
|
+
return params.pathDirs.some((dir) => path25.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
|
|
8608
8799
|
}
|
|
8609
8800
|
|
|
8610
8801
|
// src/doctorReport.ts
|
|
8611
8802
|
import fs10 from "node:fs";
|
|
8612
|
-
import
|
|
8803
|
+
import path26 from "node:path";
|
|
8613
8804
|
function refinePathShadow(status, ownExecutablePath, realpath) {
|
|
8614
8805
|
if (status.status !== "shadowed") {
|
|
8615
8806
|
return status;
|
|
@@ -8633,7 +8824,7 @@ function validateJson(schema, input) {
|
|
|
8633
8824
|
return { ok: true, data: result.data };
|
|
8634
8825
|
}
|
|
8635
8826
|
function pushPathResolution(push, resolution) {
|
|
8636
|
-
const ownDir =
|
|
8827
|
+
const ownDir = path26.dirname(resolution.ownExecutablePath);
|
|
8637
8828
|
switch (resolution.agentShim.status) {
|
|
8638
8829
|
case "ok":
|
|
8639
8830
|
push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
|
|
@@ -8699,24 +8890,24 @@ function pushProvider(push, entry, cacheEnv) {
|
|
|
8699
8890
|
push("provider", "fail", new LegacyProviderFileError(entry.path, conversion).message, entry.name);
|
|
8700
8891
|
return;
|
|
8701
8892
|
}
|
|
8702
|
-
const
|
|
8703
|
-
if (!
|
|
8704
|
-
push("provider", "fail", new ConfigValidationError(entry.path,
|
|
8893
|
+
const validated2 = ProviderSchema.safeParse(parsed);
|
|
8894
|
+
if (!validated2.success) {
|
|
8895
|
+
push("provider", "fail", new ConfigValidationError(entry.path, validated2.error.issues).message, entry.name);
|
|
8705
8896
|
return;
|
|
8706
8897
|
}
|
|
8707
|
-
const cache = cacheClause(cacheEnv, `provider ${entry.name}`,
|
|
8708
|
-
push("provider", cache.unreadable ? "fail" : "pass", `${entry.name} is valid (${describeProviderEndpoint(
|
|
8898
|
+
const cache = cacheClause(cacheEnv, `provider ${entry.name}`, validated2.data.credential);
|
|
8899
|
+
push("provider", cache.unreadable ? "fail" : "pass", `${entry.name} is valid (${describeProviderEndpoint(validated2.data)}, credential ${describeCredential(validated2.data.credential)})${cache.text}.`, entry.name);
|
|
8709
8900
|
}
|
|
8710
8901
|
function pushLegacyName(push, params) {
|
|
8711
8902
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
8712
|
-
if (legacyVariables.length === 0 &&
|
|
8903
|
+
if (legacyVariables.length === 0 && path26.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
|
|
8713
8904
|
push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
|
|
8714
8905
|
return;
|
|
8715
8906
|
}
|
|
8716
8907
|
for (const name of legacyVariables) {
|
|
8717
8908
|
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);
|
|
8718
8909
|
}
|
|
8719
|
-
if (
|
|
8910
|
+
if (path26.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
|
|
8720
8911
|
push(
|
|
8721
8912
|
"legacy-name",
|
|
8722
8913
|
"warn",
|
|
@@ -8729,6 +8920,13 @@ function runDoctor(params) {
|
|
|
8729
8920
|
const push = (section, severity, message, subject) => {
|
|
8730
8921
|
findings.push({ section, severity, message, ...subject === void 0 ? {} : { subject } });
|
|
8731
8922
|
};
|
|
8923
|
+
const checkPin = (section, subject, where, version) => {
|
|
8924
|
+
const installed = params.installedClaudeVersions;
|
|
8925
|
+
if (version === void 0 || installed === void 0 || installed.includes(version)) {
|
|
8926
|
+
return;
|
|
8927
|
+
}
|
|
8928
|
+
push(section, "warn", `${where} pins Claude Code ${version}, which is not installed (installed: ${installed.length === 0 ? "none" : installed.join(", ")}); a launch that resolves to it fails.`, subject);
|
|
8929
|
+
};
|
|
8732
8930
|
const ambient = detectAmbientCredential(params.env);
|
|
8733
8931
|
if (ambient === void 0) {
|
|
8734
8932
|
push("ambient-credential", "pass", "No ambient-credential environment variable is set.");
|
|
@@ -8761,12 +8959,13 @@ function runDoctor(params) {
|
|
|
8761
8959
|
pushLegacyName(push, params);
|
|
8762
8960
|
const profileSources = /* @__PURE__ */ new Map();
|
|
8763
8961
|
for (const entry of params.configProfiles) {
|
|
8764
|
-
const
|
|
8765
|
-
if (!
|
|
8766
|
-
push("config-profile", "fail",
|
|
8962
|
+
const validated2 = validateJson(ConfigProfileSchema, entry);
|
|
8963
|
+
if (!validated2.ok) {
|
|
8964
|
+
push("config-profile", "fail", validated2.message, entry.name);
|
|
8767
8965
|
continue;
|
|
8768
8966
|
}
|
|
8769
|
-
profileSources.set(entry.name, { name: entry.name, profile:
|
|
8967
|
+
profileSources.set(entry.name, { name: entry.name, profile: validated2.data });
|
|
8968
|
+
checkPin("config-profile", entry.name, `Configuration profile "${entry.name}"`, validated2.data.launch?.claudeVersion);
|
|
8770
8969
|
}
|
|
8771
8970
|
const loadProfile = (name) => profileSources.get(name);
|
|
8772
8971
|
for (const name of profileSources.keys()) {
|
|
@@ -8782,13 +8981,13 @@ function runDoctor(params) {
|
|
|
8782
8981
|
const validProfileNames = new Set(profileSources.keys());
|
|
8783
8982
|
const validIdentityNames = /* @__PURE__ */ new Set();
|
|
8784
8983
|
for (const entry of params.identities) {
|
|
8785
|
-
const
|
|
8786
|
-
if (!
|
|
8787
|
-
push("identity", "fail",
|
|
8984
|
+
const validated2 = validateJson(IdentitySchema, entry);
|
|
8985
|
+
if (!validated2.ok) {
|
|
8986
|
+
push("identity", "fail", validated2.message, entry.name);
|
|
8788
8987
|
continue;
|
|
8789
8988
|
}
|
|
8790
8989
|
validIdentityNames.add(entry.name);
|
|
8791
|
-
const defaultProfile =
|
|
8990
|
+
const defaultProfile = validated2.data.defaultConfigProfile;
|
|
8792
8991
|
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
8793
8992
|
push(
|
|
8794
8993
|
"identity",
|
|
@@ -8797,7 +8996,7 @@ function runDoctor(params) {
|
|
|
8797
8996
|
entry.name
|
|
8798
8997
|
);
|
|
8799
8998
|
} else {
|
|
8800
|
-
const credential =
|
|
8999
|
+
const credential = validated2.data.credential;
|
|
8801
9000
|
const cache = credential === void 0 ? { text: "", unreadable: false } : cacheClause(params.credentialCache, `identity ${entry.name}`, credential);
|
|
8802
9001
|
push(
|
|
8803
9002
|
"identity",
|
|
@@ -8835,12 +9034,12 @@ function runDoctor(params) {
|
|
|
8835
9034
|
if (params.directoryRules.raw === void 0) {
|
|
8836
9035
|
push("directory-rules", "pass", "No directory-rules.json configured.");
|
|
8837
9036
|
} else {
|
|
8838
|
-
const
|
|
8839
|
-
if (!
|
|
8840
|
-
push("directory-rules", "fail",
|
|
9037
|
+
const validated2 = validateJson(DirectoryRulesSchema, params.directoryRules);
|
|
9038
|
+
if (!validated2.ok) {
|
|
9039
|
+
push("directory-rules", "fail", validated2.message);
|
|
8841
9040
|
} else {
|
|
8842
9041
|
push("directory-rules", "pass", `${params.directoryRules.path} is valid.`);
|
|
8843
|
-
for (const rule of
|
|
9042
|
+
for (const rule of validated2.data.rules) {
|
|
8844
9043
|
const badRefs = [];
|
|
8845
9044
|
if (rule.identity !== void 0 && !selectionExists(rule.identity)) {
|
|
8846
9045
|
badRefs.push(poolNameOf(rule.identity) === void 0 ? `identity "${rule.identity}"` : `pool "${poolNameOf(rule.identity) ?? ""}"`);
|
|
@@ -8848,6 +9047,7 @@ function runDoctor(params) {
|
|
|
8848
9047
|
if (rule.configProfile !== void 0 && !validProfileNames.has(rule.configProfile)) {
|
|
8849
9048
|
badRefs.push(`configuration profile "${rule.configProfile}"`);
|
|
8850
9049
|
}
|
|
9050
|
+
checkPin("directory-rules", rule.path, `Rule for "${rule.path}"`, rule.launch?.claudeVersion);
|
|
8851
9051
|
if (badRefs.length > 0) {
|
|
8852
9052
|
push("directory-rules", "fail", `Rule for "${rule.path}" names ${badRefs.join(" and ")}, which do not exist.`, rule.path);
|
|
8853
9053
|
} else {
|
|
@@ -8859,11 +9059,11 @@ function runDoctor(params) {
|
|
|
8859
9059
|
if (params.globalConfig.raw === void 0) {
|
|
8860
9060
|
push("global-config", "pass", "No config.json configured.");
|
|
8861
9061
|
} else {
|
|
8862
|
-
const
|
|
8863
|
-
if (!
|
|
8864
|
-
push("global-config", "fail",
|
|
9062
|
+
const validated2 = validateJson(GlobalConfigSchema, params.globalConfig);
|
|
9063
|
+
if (!validated2.ok) {
|
|
9064
|
+
push("global-config", "fail", validated2.message);
|
|
8865
9065
|
} else {
|
|
8866
|
-
const defaultProfile =
|
|
9066
|
+
const defaultProfile = validated2.data.defaultConfigProfile;
|
|
8867
9067
|
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
8868
9068
|
push(
|
|
8869
9069
|
"global-config",
|
|
@@ -8872,27 +9072,28 @@ function runDoctor(params) {
|
|
|
8872
9072
|
);
|
|
8873
9073
|
} else {
|
|
8874
9074
|
push("global-config", "pass", `${params.globalConfig.path} is valid.`);
|
|
9075
|
+
checkPin("global-config", void 0, "config.json", validated2.data.launch?.claudeVersion);
|
|
8875
9076
|
}
|
|
8876
9077
|
}
|
|
8877
9078
|
}
|
|
8878
9079
|
if (params.categoriesLocal.raw === void 0) {
|
|
8879
9080
|
push("categories-local", "pass", "No categories.local.json configured.");
|
|
8880
9081
|
} else {
|
|
8881
|
-
const
|
|
9082
|
+
const validated2 = validateJson(CategoryClassificationOverlaySchema, params.categoriesLocal);
|
|
8882
9083
|
push(
|
|
8883
9084
|
"categories-local",
|
|
8884
|
-
|
|
8885
|
-
|
|
9085
|
+
validated2.ok ? "pass" : "fail",
|
|
9086
|
+
validated2.ok ? `${params.categoriesLocal.path} is valid.` : validated2.message
|
|
8886
9087
|
);
|
|
8887
9088
|
}
|
|
8888
9089
|
if (params.headroom.state.raw === void 0) {
|
|
8889
9090
|
push("headroom", "pass", "No headroom daemon has ever run; nothing to check.");
|
|
8890
9091
|
} else {
|
|
8891
|
-
const
|
|
8892
|
-
if (!
|
|
8893
|
-
push("headroom", "fail",
|
|
9092
|
+
const validated2 = validateJson(HeadroomStateSchema, params.headroom.state);
|
|
9093
|
+
if (!validated2.ok) {
|
|
9094
|
+
push("headroom", "fail", validated2.message);
|
|
8894
9095
|
} else {
|
|
8895
|
-
const state =
|
|
9096
|
+
const state = validated2.data;
|
|
8896
9097
|
const supervisorAlive = state.supervisorPid !== void 0 && params.headroom.isRunning(state.supervisorPid);
|
|
8897
9098
|
const headroomAlive = state.headroomPid !== void 0 && params.headroom.isRunning(state.headroomPid);
|
|
8898
9099
|
if (state.supervisorPid === void 0) {
|
|
@@ -9025,18 +9226,18 @@ function collectDoctorReport(params) {
|
|
|
9025
9226
|
const { paths } = params;
|
|
9026
9227
|
const identityNames = fs10.existsSync(paths.identitiesDir) ? fs10.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
|
|
9027
9228
|
const identities = identityNames.map((name) => {
|
|
9028
|
-
const farmRoot =
|
|
9029
|
-
const identityPath =
|
|
9229
|
+
const farmRoot = path26.join(paths.identitiesDir, name);
|
|
9230
|
+
const identityPath = path26.join(farmRoot, "identity.json");
|
|
9030
9231
|
return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
|
|
9031
9232
|
});
|
|
9032
9233
|
const profileNames = fs10.existsSync(paths.configProfilesDir) ? fs10.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
9033
9234
|
const configProfiles = profileNames.map((name) => {
|
|
9034
|
-
const profilePath =
|
|
9235
|
+
const profilePath = path26.join(paths.configProfilesDir, `${name}.json`);
|
|
9035
9236
|
return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
|
|
9036
9237
|
});
|
|
9037
9238
|
const providerNames = fs10.existsSync(paths.providersDir) ? fs10.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
|
|
9038
9239
|
const providers = providerNames.map((name) => {
|
|
9039
|
-
const providerPath =
|
|
9240
|
+
const providerPath = path26.join(paths.providersDir, `${name}.json`);
|
|
9040
9241
|
return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
|
|
9041
9242
|
});
|
|
9042
9243
|
const ownExecutablePath = realOwnExecutablePath();
|
|
@@ -9048,12 +9249,12 @@ function collectDoctorReport(params) {
|
|
|
9048
9249
|
binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
9049
9250
|
}
|
|
9050
9251
|
const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
|
|
9051
|
-
const pathDirs = (params.env.PATH ?? "").split(
|
|
9252
|
+
const pathDirs = (params.env.PATH ?? "").split(path26.delimiter).filter((dir) => dir !== "");
|
|
9052
9253
|
const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
|
|
9053
9254
|
findPathShadow({
|
|
9054
9255
|
pathDirs,
|
|
9055
|
-
targetDir:
|
|
9056
|
-
targetFilename:
|
|
9256
|
+
targetDir: path26.dirname(shimState.targetPath),
|
|
9257
|
+
targetFilename: path26.basename(shimState.targetPath),
|
|
9057
9258
|
findExecutableInDir
|
|
9058
9259
|
}),
|
|
9059
9260
|
shimState.targetPath,
|
|
@@ -9062,6 +9263,7 @@ function collectDoctorReport(params) {
|
|
|
9062
9263
|
const report = runDoctor({
|
|
9063
9264
|
env: params.env,
|
|
9064
9265
|
credentialCache: realCredentialCacheEnv(paths),
|
|
9266
|
+
installedClaudeVersions: realInstalledClaudeVersions(),
|
|
9065
9267
|
identities,
|
|
9066
9268
|
configProfiles,
|
|
9067
9269
|
providers,
|
|
@@ -9077,7 +9279,7 @@ function collectDoctorReport(params) {
|
|
|
9077
9279
|
agentShim: refinePathShadow(
|
|
9078
9280
|
findPathShadow({
|
|
9079
9281
|
pathDirs,
|
|
9080
|
-
targetDir:
|
|
9282
|
+
targetDir: path26.dirname(ownExecutablePath),
|
|
9081
9283
|
targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
|
|
9082
9284
|
findExecutableInDir
|
|
9083
9285
|
}),
|
|
@@ -9098,7 +9300,7 @@ function collectDoctorReport(params) {
|
|
|
9098
9300
|
|
|
9099
9301
|
// src/launcher.ts
|
|
9100
9302
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
9101
|
-
import
|
|
9303
|
+
import path28 from "node:path";
|
|
9102
9304
|
|
|
9103
9305
|
// src/launcher/argv.ts
|
|
9104
9306
|
var ConflictingIdentityError = class extends UsageError {
|
|
@@ -9111,7 +9313,7 @@ var ConflictingIdentityError = class extends UsageError {
|
|
|
9111
9313
|
positional;
|
|
9112
9314
|
flag;
|
|
9113
9315
|
};
|
|
9114
|
-
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--category", "--share", "--hide"];
|
|
9316
|
+
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--claude-version", "--category", "--share", "--hide"];
|
|
9115
9317
|
var BOOLEAN_FLAGS = [
|
|
9116
9318
|
{ flag: "--headroom", key: "headroom" },
|
|
9117
9319
|
{ flag: "--track-usage", key: "trackUsage" },
|
|
@@ -9157,6 +9359,7 @@ function parseLauncherArgv(argv) {
|
|
|
9157
9359
|
let flagIdentity;
|
|
9158
9360
|
let configProfile;
|
|
9159
9361
|
let provider;
|
|
9362
|
+
let claudeVersion;
|
|
9160
9363
|
const booleans = {};
|
|
9161
9364
|
const categoryFlags = [];
|
|
9162
9365
|
const shareFlags = [];
|
|
@@ -9211,6 +9414,9 @@ function parseLauncherArgv(argv) {
|
|
|
9211
9414
|
case "--provider":
|
|
9212
9415
|
provider = value;
|
|
9213
9416
|
break;
|
|
9417
|
+
case "--claude-version":
|
|
9418
|
+
claudeVersion = value;
|
|
9419
|
+
break;
|
|
9214
9420
|
case "--category":
|
|
9215
9421
|
categoryFlags.push(value);
|
|
9216
9422
|
break;
|
|
@@ -9232,6 +9438,7 @@ function parseLauncherArgv(argv) {
|
|
|
9232
9438
|
...identity === void 0 ? {} : { identity },
|
|
9233
9439
|
...configProfile === void 0 ? {} : { configProfile },
|
|
9234
9440
|
...provider === void 0 ? {} : { provider },
|
|
9441
|
+
...claudeVersion === void 0 ? {} : { claudeVersion },
|
|
9235
9442
|
...native ? { native: true } : {},
|
|
9236
9443
|
...booleans,
|
|
9237
9444
|
categoryFlags,
|
|
@@ -9358,7 +9565,7 @@ function buildCliOverride(params) {
|
|
|
9358
9565
|
}
|
|
9359
9566
|
|
|
9360
9567
|
// src/launcher/flags.ts
|
|
9361
|
-
import
|
|
9568
|
+
import path27 from "node:path";
|
|
9362
9569
|
|
|
9363
9570
|
// src/headroom/headers.ts
|
|
9364
9571
|
function mergeAnthropicCustomHeaders(existing, additions) {
|
|
@@ -9428,7 +9635,7 @@ function buildEnv(params) {
|
|
|
9428
9635
|
const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
|
|
9429
9636
|
let providerCustomHeaders;
|
|
9430
9637
|
if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
|
|
9431
|
-
env.CLAUDE_CONFIG_DIR =
|
|
9638
|
+
env.CLAUDE_CONFIG_DIR = path27.join(params.identitiesDir, params.resolvedIdentityName);
|
|
9432
9639
|
}
|
|
9433
9640
|
if (params.provider !== void 0) {
|
|
9434
9641
|
const providerEnv = params.provider.definition.env ?? {};
|
|
@@ -9589,7 +9796,7 @@ function prepareLaunch(params) {
|
|
|
9589
9796
|
identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
|
|
9590
9797
|
globalDefaultConfigProfile: params.globalDefaultConfigProfile
|
|
9591
9798
|
});
|
|
9592
|
-
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(
|
|
9799
|
+
if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path28.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
|
|
9593
9800
|
log.error(
|
|
9594
9801
|
`agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
|
|
9595
9802
|
);
|
|
@@ -9693,7 +9900,11 @@ function prepareLaunch(params) {
|
|
|
9693
9900
|
} else if (params.farm !== void 0) {
|
|
9694
9901
|
cascadeLaunch = flattenLayers(assembleCascade(params.farm.loadCascade(configProfileDecision.name, cliOverride)).layers, { home: params.farm.home }).launch;
|
|
9695
9902
|
}
|
|
9696
|
-
const
|
|
9903
|
+
const pinnedVersion = resolveClaudeVersion({ env, ...parsedArgv.claudeVersion === void 0 ? {} : { flag: parsedArgv.claudeVersion }, ...cascadeLaunch?.claudeVersion === void 0 ? {} : { cascade: cascadeLaunch.claudeVersion } });
|
|
9904
|
+
const discovered = params.resolveClaudeBinary(pinnedVersion === void 0 ? void 0 : { version: pinnedVersion.version });
|
|
9905
|
+
if (pinnedVersion !== void 0) {
|
|
9906
|
+
log.info(`agent-shim: Claude Code ${pinnedVersion.version} (pinned by ${pinnedVersion.source})`);
|
|
9907
|
+
}
|
|
9697
9908
|
const resolvedFlags = resolveLaunchFlags({
|
|
9698
9909
|
env,
|
|
9699
9910
|
// A pool pick reads the usage the front door records, so a pool launch records its own unless something says otherwise.
|
|
@@ -9831,11 +10042,11 @@ function probeFrontDoorSync(port, caPem) {
|
|
|
9831
10042
|
|
|
9832
10043
|
// src/frontdoor/trust.ts
|
|
9833
10044
|
import { createHash as createHash6 } from "node:crypto";
|
|
9834
|
-
import
|
|
10045
|
+
import path29 from "node:path";
|
|
9835
10046
|
var BUNDLE_NAME_HEX_DIGITS = 16;
|
|
9836
10047
|
function resolveTrustBundle(params) {
|
|
9837
10048
|
const { caCertFile, bundlesDir, variable, inherited, fs: fs12 } = params;
|
|
9838
|
-
if (inherited === void 0 || inherited === "" ||
|
|
10049
|
+
if (inherited === void 0 || inherited === "" || path29.resolve(inherited) === path29.resolve(caCertFile)) {
|
|
9839
10050
|
return { path: caCertFile };
|
|
9840
10051
|
}
|
|
9841
10052
|
const caPem = fs12.readFileUtf8(caCertFile);
|
|
@@ -9853,7 +10064,7 @@ function resolveTrustBundle(params) {
|
|
|
9853
10064
|
}
|
|
9854
10065
|
const combined = `${inheritedPem.trimEnd()}
|
|
9855
10066
|
${caPem}`;
|
|
9856
|
-
const bundle =
|
|
10067
|
+
const bundle = path29.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
|
|
9857
10068
|
if (!fs12.exists(bundle)) {
|
|
9858
10069
|
fs12.mkdirp(bundlesDir);
|
|
9859
10070
|
fs12.writeFileAtomic(bundle, combined);
|
|
@@ -10080,6 +10291,7 @@ export {
|
|
|
10080
10291
|
createRcApiNodeHandler,
|
|
10081
10292
|
createRcApiRouter,
|
|
10082
10293
|
createRcControlHandler,
|
|
10294
|
+
createRcCredentialStore,
|
|
10083
10295
|
createRcEventFanout,
|
|
10084
10296
|
createRcSessionTracker,
|
|
10085
10297
|
createRcStreamHub,
|