agent-shim 8.9.0 → 8.10.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 +159 -45
- package/dist/index.cjs +348 -239
- package/dist/index.mjs +350 -241
- package/dist/types/index.d.ts +35 -1
- 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 = [];
|
|
@@ -4526,7 +4619,7 @@ function realConnectEffects() {
|
|
|
4526
4619
|
// src/frontdoor/rcApi.ts
|
|
4527
4620
|
import { createORPCClient, ORPCError } from "@orpc/client";
|
|
4528
4621
|
import { RPCLink } from "@orpc/client/fetch";
|
|
4529
|
-
import { eventIterator, os, withEventMeta } from "@orpc/server";
|
|
4622
|
+
import { eventIterator, os as os2, withEventMeta } from "@orpc/server";
|
|
4530
4623
|
import { BodyLimitPlugin, RPCHandler } from "@orpc/server/node";
|
|
4531
4624
|
import { Agent as Agent3, fetch as undiciFetch } from "undici";
|
|
4532
4625
|
|
|
@@ -4613,7 +4706,7 @@ var RcAnswerOutputSchema = z4.strictObject({
|
|
|
4613
4706
|
var RC_ORPC_PATH_PREFIX = "/__agent-shim/orpc";
|
|
4614
4707
|
var RC_SUBSCRIBER_BUFFER_EVENTS = 100;
|
|
4615
4708
|
function createRcApiRouter(deps) {
|
|
4616
|
-
const authed =
|
|
4709
|
+
const authed = os2.$context().use(({ context, next }) => {
|
|
4617
4710
|
const presented = context.headers.authorization;
|
|
4618
4711
|
const bearerPrefix = "bearer ".length;
|
|
4619
4712
|
const token = typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
|
|
@@ -4984,7 +5077,7 @@ import { randomUUID as randomUUID3 } from "node:crypto";
|
|
|
4984
5077
|
|
|
4985
5078
|
// src/headroom/state.ts
|
|
4986
5079
|
import { createHash as createHash3 } from "node:crypto";
|
|
4987
|
-
import
|
|
5080
|
+
import path9 from "node:path";
|
|
4988
5081
|
import { z as z5 } from "zod";
|
|
4989
5082
|
var HEADROOM_ANTHROPIC_UPSTREAM = "https://api.anthropic.com";
|
|
4990
5083
|
var HEADROOM_STATE_SCHEMA_VERSION = 2;
|
|
@@ -5025,7 +5118,7 @@ function readHeadroomState(fs12, stateFile) {
|
|
|
5025
5118
|
}
|
|
5026
5119
|
}
|
|
5027
5120
|
function writeHeadroomState(fs12, stateFile, state) {
|
|
5028
|
-
fs12.mkdirp(
|
|
5121
|
+
fs12.mkdirp(path9.dirname(stateFile));
|
|
5029
5122
|
fs12.writeFilePrivate(stateFile, `${JSON.stringify(state, null, 2)}
|
|
5030
5123
|
`);
|
|
5031
5124
|
}
|
|
@@ -5053,7 +5146,7 @@ function readAllProviders(fs12, providersDir) {
|
|
|
5053
5146
|
continue;
|
|
5054
5147
|
}
|
|
5055
5148
|
try {
|
|
5056
|
-
const parsed = parseJson(fs12.readFileUtf8(
|
|
5149
|
+
const parsed = parseJson(fs12.readFileUtf8(path9.join(providersDir, name)), ProviderSchema);
|
|
5057
5150
|
if (parsed !== void 0) {
|
|
5058
5151
|
result.push({ name: name.slice(0, -".json".length), provider: parsed });
|
|
5059
5152
|
}
|
|
@@ -5065,11 +5158,11 @@ function readAllProviders(fs12, providersDir) {
|
|
|
5065
5158
|
}
|
|
5066
5159
|
function writeSession(fs12, sessionsDir, session) {
|
|
5067
5160
|
fs12.mkdirp(sessionsDir);
|
|
5068
|
-
fs12.writeFileUtf8(
|
|
5161
|
+
fs12.writeFileUtf8(path9.join(sessionsDir, `${String(session.pid)}.json`), `${JSON.stringify(session, null, 2)}
|
|
5069
5162
|
`);
|
|
5070
5163
|
}
|
|
5071
5164
|
function removeSession(fs12, sessionsDir, pid) {
|
|
5072
|
-
fs12.removeRecursive(
|
|
5165
|
+
fs12.removeRecursive(path9.join(sessionsDir, `${String(pid)}.json`));
|
|
5073
5166
|
}
|
|
5074
5167
|
function listSessions(fs12, sessionsDir) {
|
|
5075
5168
|
const sessions = [];
|
|
@@ -5078,7 +5171,7 @@ function listSessions(fs12, sessionsDir) {
|
|
|
5078
5171
|
continue;
|
|
5079
5172
|
}
|
|
5080
5173
|
try {
|
|
5081
|
-
const parsed = parseJson(fs12.readFileUtf8(
|
|
5174
|
+
const parsed = parseJson(fs12.readFileUtf8(path9.join(sessionsDir, name)), HeadroomSessionSchema);
|
|
5082
5175
|
if (parsed !== void 0) {
|
|
5083
5176
|
sessions.push(parsed);
|
|
5084
5177
|
}
|
|
@@ -5183,10 +5276,10 @@ function ensureFrontDoor(params) {
|
|
|
5183
5276
|
}
|
|
5184
5277
|
|
|
5185
5278
|
// src/usage/read.ts
|
|
5186
|
-
import
|
|
5279
|
+
import path11 from "node:path";
|
|
5187
5280
|
|
|
5188
5281
|
// src/usage/account.ts
|
|
5189
|
-
import
|
|
5282
|
+
import path10 from "node:path";
|
|
5190
5283
|
|
|
5191
5284
|
// src/usage/schema.ts
|
|
5192
5285
|
import { z as z6 } from "zod";
|
|
@@ -5352,7 +5445,7 @@ function isIdentityName(name) {
|
|
|
5352
5445
|
return IdentitySchema.shape.name.safeParse(name).success;
|
|
5353
5446
|
}
|
|
5354
5447
|
function claudeJsonPath(identitiesDir, identity) {
|
|
5355
|
-
return
|
|
5448
|
+
return path10.join(identitiesDir, identity, CLAUDE_JSON);
|
|
5356
5449
|
}
|
|
5357
5450
|
function readAccountMetadata(fs12, identitiesDir, identity) {
|
|
5358
5451
|
if (!isIdentityName(identity)) {
|
|
@@ -5411,7 +5504,7 @@ function readUsageLog(fs12, logDir, options = {}) {
|
|
|
5411
5504
|
if (sinceMs !== void 0 && segment.endsAt <= sinceMs) {
|
|
5412
5505
|
continue;
|
|
5413
5506
|
}
|
|
5414
|
-
const contents = fs12.readFileUtf8(
|
|
5507
|
+
const contents = fs12.readFileUtf8(path11.join(logDir, segment.name));
|
|
5415
5508
|
if (contents === void 0) {
|
|
5416
5509
|
continue;
|
|
5417
5510
|
}
|
|
@@ -5453,7 +5546,7 @@ function snapshotPath(snapshotsDir, identity) {
|
|
|
5453
5546
|
if (!isIdentityName(identity)) {
|
|
5454
5547
|
throw new Error(`"${identity}" is not a valid identity name.`);
|
|
5455
5548
|
}
|
|
5456
|
-
return
|
|
5549
|
+
return path11.join(snapshotsDir, `${identity}.json`);
|
|
5457
5550
|
}
|
|
5458
5551
|
function readUsageSnapshot(fs12, snapshotsDir, identity) {
|
|
5459
5552
|
const file = snapshotPath(snapshotsDir, identity);
|
|
@@ -5787,7 +5880,7 @@ function rankPool(input) {
|
|
|
5787
5880
|
}
|
|
5788
5881
|
|
|
5789
5882
|
// src/usage/poolPick.ts
|
|
5790
|
-
import
|
|
5883
|
+
import path12 from "node:path";
|
|
5791
5884
|
import { z as z9 } from "zod";
|
|
5792
5885
|
|
|
5793
5886
|
// src/usage/store.ts
|
|
@@ -5854,13 +5947,13 @@ function recordStickyPick(fs12, picksFile, directory, identity, nowMs) {
|
|
|
5854
5947
|
const current = readPicksFile(fs12, picksFile).file.picks;
|
|
5855
5948
|
const kept = Object.entries(current).filter(([, entry]) => nowMs - Date.parse(entry.at) <= USAGE_RETENTION_MS);
|
|
5856
5949
|
const next = { schemaVersion: SCHEMA_VERSION, picks: { ...Object.fromEntries(kept), [directory]: { identity, at: new Date(nowMs).toISOString() } } };
|
|
5857
|
-
fs12.mkdirPrivate(
|
|
5950
|
+
fs12.mkdirPrivate(path12.dirname(picksFile));
|
|
5858
5951
|
fs12.writeFilePrivate(picksFile, `${JSON.stringify(next, null, 2)}
|
|
5859
5952
|
`);
|
|
5860
5953
|
}
|
|
5861
5954
|
|
|
5862
5955
|
// src/launcher/identity.ts
|
|
5863
|
-
import
|
|
5956
|
+
import path13 from "node:path";
|
|
5864
5957
|
function poolNameOf(selector) {
|
|
5865
5958
|
return selector.startsWith(POOL_SELECTOR_PREFIX) ? selector.slice(POOL_SELECTOR_PREFIX.length) : void 0;
|
|
5866
5959
|
}
|
|
@@ -5909,7 +6002,7 @@ function decideConfigProfile(params) {
|
|
|
5909
6002
|
return { source: "none" };
|
|
5910
6003
|
}
|
|
5911
6004
|
function loadIdentity(identitiesDir, name, fs12) {
|
|
5912
|
-
return loadConfigFile(
|
|
6005
|
+
return loadConfigFile(path13.join(identitiesDir, name, "identity.json"), IdentitySchema, fs12.readConfigFile);
|
|
5913
6006
|
}
|
|
5914
6007
|
|
|
5915
6008
|
// src/launcher/pool.ts
|
|
@@ -6124,7 +6217,7 @@ function hashSettings(settings) {
|
|
|
6124
6217
|
}
|
|
6125
6218
|
|
|
6126
6219
|
// src/headroom/socket.ts
|
|
6127
|
-
import
|
|
6220
|
+
import path14 from "node:path";
|
|
6128
6221
|
var GROUP_OR_OTHER_BITS = 63;
|
|
6129
6222
|
var OCTAL_RADIX = 8;
|
|
6130
6223
|
var MODE_DIGITS = 4;
|
|
@@ -6133,7 +6226,7 @@ var HEADROOM_SOCKET_NAME = /^(\d+)\.sock$/;
|
|
|
6133
6226
|
var UNIX_SOCKET_PATH_MAX_BYTES = 103;
|
|
6134
6227
|
var PLATFORMS_WITHOUT_UNIX_SOCKETS = ["win32"];
|
|
6135
6228
|
function headroomSocketPath(paths, supervisorPid) {
|
|
6136
|
-
return
|
|
6229
|
+
return path14.join(paths.headroomSocketDir, `${String(supervisorPid)}${HEADROOM_SOCKET_SUFFIX}`);
|
|
6137
6230
|
}
|
|
6138
6231
|
function platformRefusal(platform) {
|
|
6139
6232
|
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 +6275,7 @@ function pruneStaleHeadroomSockets(fs12, paths, isRunning) {
|
|
|
6182
6275
|
if (pid === void 0 || isRunning(Number.parseInt(pid, 10))) {
|
|
6183
6276
|
continue;
|
|
6184
6277
|
}
|
|
6185
|
-
fs12.removeRecursive(
|
|
6278
|
+
fs12.removeRecursive(path14.join(paths.headroomSocketDir, name));
|
|
6186
6279
|
removed.push(name);
|
|
6187
6280
|
}
|
|
6188
6281
|
return removed;
|
|
@@ -6236,7 +6329,7 @@ function versionSatisfies(version, source) {
|
|
|
6236
6329
|
if (installed === void 0 || wanted === void 0) {
|
|
6237
6330
|
return true;
|
|
6238
6331
|
}
|
|
6239
|
-
const cmp =
|
|
6332
|
+
const cmp = compareVersions2(installed, wanted);
|
|
6240
6333
|
switch (specifier[0]) {
|
|
6241
6334
|
case "==":
|
|
6242
6335
|
return cmp === 0;
|
|
@@ -6251,7 +6344,7 @@ function versionSatisfies(version, source) {
|
|
|
6251
6344
|
case "<":
|
|
6252
6345
|
return cmp < 0;
|
|
6253
6346
|
case "~=":
|
|
6254
|
-
return
|
|
6347
|
+
return compareVersions2(installed.slice(0, Math.max(0, wanted.length - 1)), wanted.slice(0, Math.max(0, wanted.length - 1))) === 0 && cmp >= 0;
|
|
6255
6348
|
default:
|
|
6256
6349
|
return true;
|
|
6257
6350
|
}
|
|
@@ -6263,7 +6356,7 @@ function parseVersion(input) {
|
|
|
6263
6356
|
}
|
|
6264
6357
|
return match[0].split(".").map((part) => Number.parseInt(part, 10));
|
|
6265
6358
|
}
|
|
6266
|
-
function
|
|
6359
|
+
function compareVersions2(a, b) {
|
|
6267
6360
|
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
6268
6361
|
const left = a[index] ?? 0;
|
|
6269
6362
|
const right = b[index] ?? 0;
|
|
@@ -6505,15 +6598,15 @@ function evaluateAmbientCredentialGuard(params) {
|
|
|
6505
6598
|
|
|
6506
6599
|
// src/paths.ts
|
|
6507
6600
|
import fs3 from "node:fs";
|
|
6508
|
-
import
|
|
6509
|
-
import
|
|
6510
|
-
function resolveAgentShimHome(env = process.env, home =
|
|
6601
|
+
import os3 from "node:os";
|
|
6602
|
+
import path15 from "node:path";
|
|
6603
|
+
function resolveAgentShimHome(env = process.env, home = os3.homedir(), isDirectory = isExistingDirectory) {
|
|
6511
6604
|
const fromEnv = env.AGENT_SHIM_HOME;
|
|
6512
6605
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6513
6606
|
return fromEnv;
|
|
6514
6607
|
}
|
|
6515
|
-
const current =
|
|
6516
|
-
const legacy =
|
|
6608
|
+
const current = path15.join(home, HOME_DIRNAME);
|
|
6609
|
+
const legacy = path15.join(home, LEGACY_HOME_DIRNAME);
|
|
6517
6610
|
return !isDirectory(current) && isDirectory(legacy) ? legacy : current;
|
|
6518
6611
|
}
|
|
6519
6612
|
function isExistingDirectory(candidate) {
|
|
@@ -6524,40 +6617,40 @@ function resolveClaudeHome() {
|
|
|
6524
6617
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6525
6618
|
return fromEnv;
|
|
6526
6619
|
}
|
|
6527
|
-
return
|
|
6620
|
+
return path15.join(os3.homedir(), ".claude");
|
|
6528
6621
|
}
|
|
6529
6622
|
function buildLayoutPaths(root) {
|
|
6530
6623
|
return {
|
|
6531
6624
|
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
|
-
frontdoorLogPath:
|
|
6557
|
-
usageDir:
|
|
6558
|
-
usageLogDir:
|
|
6559
|
-
usageSnapshotsDir:
|
|
6560
|
-
usagePicksFile:
|
|
6625
|
+
identitiesDir: path15.join(root, "identities"),
|
|
6626
|
+
configProfilesDir: path15.join(root, "config-profiles"),
|
|
6627
|
+
providersDir: path15.join(root, "providers"),
|
|
6628
|
+
directoryRulesFile: path15.join(root, "directory-rules.json"),
|
|
6629
|
+
activeIdentityFile: path15.join(root, "active-identity"),
|
|
6630
|
+
globalConfigFile: path15.join(root, "config.json"),
|
|
6631
|
+
categoriesLocalFile: path15.join(root, "categories.local.json"),
|
|
6632
|
+
claudeShimFile: path15.join(root, "claude-shim.json"),
|
|
6633
|
+
headroomDir: path15.join(root, "headroom"),
|
|
6634
|
+
headroomStateFile: path15.join(root, "headroom", `state.v${String(HEADROOM_STATE_SCHEMA_VERSION)}.json`),
|
|
6635
|
+
headroomSocketDir: path15.join(root, "headroom", "run"),
|
|
6636
|
+
headroomLockFile: path15.join(root, "headroom", "start.lock"),
|
|
6637
|
+
headroomSessionsDir: path15.join(root, "headroom", "sessions"),
|
|
6638
|
+
logsDir: path15.join(root, "logs"),
|
|
6639
|
+
headroomLogPath: path15.join(root, "logs", "headroom.log"),
|
|
6640
|
+
frontdoorDir: path15.join(root, "frontdoor"),
|
|
6641
|
+
frontdoorCaDir: path15.join(root, "frontdoor", "ca"),
|
|
6642
|
+
frontdoorCaCertFile: path15.join(root, "frontdoor", "ca", "ca.pem"),
|
|
6643
|
+
frontdoorCaKeyFile: path15.join(root, "frontdoor", "ca", "ca.key"),
|
|
6644
|
+
frontdoorCaBundlesDir: path15.join(root, "frontdoor", "ca", "bundles"),
|
|
6645
|
+
frontdoorStateFile: path15.join(root, "frontdoor", "state.json"),
|
|
6646
|
+
frontdoorLockFile: path15.join(root, "frontdoor", "start.lock"),
|
|
6647
|
+
frontdoorControlTokenFile: path15.join(root, "frontdoor", "control-token"),
|
|
6648
|
+
frontdoorSessionsDir: path15.join(root, "frontdoor", "sessions"),
|
|
6649
|
+
frontdoorLogPath: path15.join(root, "logs", "frontdoor.log"),
|
|
6650
|
+
usageDir: path15.join(root, "usage"),
|
|
6651
|
+
usageLogDir: path15.join(root, "usage", "log"),
|
|
6652
|
+
usageSnapshotsDir: path15.join(root, "usage", "snapshots"),
|
|
6653
|
+
usagePicksFile: path15.join(root, "usage", "picks.json")
|
|
6561
6654
|
};
|
|
6562
6655
|
}
|
|
6563
6656
|
function resolveLayoutPaths() {
|
|
@@ -6566,11 +6659,11 @@ function resolveLayoutPaths() {
|
|
|
6566
6659
|
|
|
6567
6660
|
// src/identityStore.ts
|
|
6568
6661
|
import fs5 from "node:fs";
|
|
6569
|
-
import
|
|
6662
|
+
import path17 from "node:path";
|
|
6570
6663
|
|
|
6571
6664
|
// src/configProfilesStore.ts
|
|
6572
6665
|
import fs4 from "node:fs";
|
|
6573
|
-
import
|
|
6666
|
+
import path16 from "node:path";
|
|
6574
6667
|
var ProfileNotFoundError = class extends CliError {
|
|
6575
6668
|
constructor(profileName) {
|
|
6576
6669
|
super(`No configuration profile named "${profileName}". Run \`agent-shim profile add ${profileName}\` first.`);
|
|
@@ -6596,7 +6689,7 @@ var InvalidCategoryNameError = class extends CliError {
|
|
|
6596
6689
|
categoryName;
|
|
6597
6690
|
};
|
|
6598
6691
|
function profileJsonPath(paths, name) {
|
|
6599
|
-
return
|
|
6692
|
+
return path16.join(paths.configProfilesDir, `${name}.json`);
|
|
6600
6693
|
}
|
|
6601
6694
|
function profileExists(paths, name) {
|
|
6602
6695
|
return fs4.existsSync(profileJsonPath(paths, name));
|
|
@@ -6784,7 +6877,7 @@ var InvalidIdentityNameError = class extends CliError {
|
|
|
6784
6877
|
attemptedName;
|
|
6785
6878
|
};
|
|
6786
6879
|
function identityJsonPath(paths, name) {
|
|
6787
|
-
return
|
|
6880
|
+
return path17.join(paths.identitiesDir, name, "identity.json");
|
|
6788
6881
|
}
|
|
6789
6882
|
function identityExists(paths, name) {
|
|
6790
6883
|
return fs5.existsSync(identityJsonPath(paths, name));
|
|
@@ -6885,7 +6978,7 @@ function setIdentityCredential(paths, identityName, change) {
|
|
|
6885
6978
|
});
|
|
6886
6979
|
}
|
|
6887
6980
|
function belongsToIdentity(entry, name) {
|
|
6888
|
-
return entry === name || entry ===
|
|
6981
|
+
return entry === name || entry === path17.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
|
|
6889
6982
|
}
|
|
6890
6983
|
function removeIdentity(paths, name) {
|
|
6891
6984
|
if (!identityExists(paths, name)) {
|
|
@@ -6893,7 +6986,7 @@ function removeIdentity(paths, name) {
|
|
|
6893
6986
|
}
|
|
6894
6987
|
for (const entry of fs5.readdirSync(paths.identitiesDir)) {
|
|
6895
6988
|
if (belongsToIdentity(entry, name)) {
|
|
6896
|
-
fs5.rmSync(
|
|
6989
|
+
fs5.rmSync(path17.join(paths.identitiesDir, entry), { recursive: true, force: true });
|
|
6897
6990
|
}
|
|
6898
6991
|
}
|
|
6899
6992
|
if (readActiveIdentity(paths) === name) {
|
|
@@ -6903,7 +6996,7 @@ function removeIdentity(paths, name) {
|
|
|
6903
6996
|
|
|
6904
6997
|
// src/providersStore.ts
|
|
6905
6998
|
import fs6 from "node:fs";
|
|
6906
|
-
import
|
|
6999
|
+
import path18 from "node:path";
|
|
6907
7000
|
|
|
6908
7001
|
// src/credentialCache.ts
|
|
6909
7002
|
import { z as z12 } from "zod";
|
|
@@ -7168,7 +7261,7 @@ var InvalidProviderNameError = class extends CliError {
|
|
|
7168
7261
|
};
|
|
7169
7262
|
var PROVIDER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._@-]*$/;
|
|
7170
7263
|
function providerJsonPath(paths, name) {
|
|
7171
|
-
return
|
|
7264
|
+
return path18.join(paths.providersDir, `${name}.json`);
|
|
7172
7265
|
}
|
|
7173
7266
|
function providerExists(paths, name) {
|
|
7174
7267
|
return fs6.existsSync(providerJsonPath(paths, name));
|
|
@@ -7244,7 +7337,7 @@ function readProvider(paths, name) {
|
|
|
7244
7337
|
return parseProviderFile(filePath, raw);
|
|
7245
7338
|
}
|
|
7246
7339
|
function loadProvider(providersDir, name, port) {
|
|
7247
|
-
const filePath =
|
|
7340
|
+
const filePath = path18.join(providersDir, `${name}.json`);
|
|
7248
7341
|
const raw = port.readConfigFile(filePath);
|
|
7249
7342
|
return raw === void 0 ? void 0 : parseProviderFile(filePath, raw);
|
|
7250
7343
|
}
|
|
@@ -7499,21 +7592,21 @@ function removeDirectoryRule(paths, rulePath) {
|
|
|
7499
7592
|
import os5 from "node:os";
|
|
7500
7593
|
|
|
7501
7594
|
// src/launcher/cascade.ts
|
|
7502
|
-
import
|
|
7595
|
+
import path20 from "node:path";
|
|
7503
7596
|
|
|
7504
7597
|
// src/pathNorm.ts
|
|
7505
|
-
import
|
|
7598
|
+
import path19 from "node:path";
|
|
7506
7599
|
function expandTilde(value, home) {
|
|
7507
7600
|
if (value === "~") {
|
|
7508
7601
|
return home;
|
|
7509
7602
|
}
|
|
7510
7603
|
if (value.startsWith("~/")) {
|
|
7511
|
-
return
|
|
7604
|
+
return path19.join(home, value.slice(2));
|
|
7512
7605
|
}
|
|
7513
7606
|
return value;
|
|
7514
7607
|
}
|
|
7515
7608
|
function normaliseRulePath(rulePath, home) {
|
|
7516
|
-
return
|
|
7609
|
+
return path19.resolve(expandTilde(rulePath, home));
|
|
7517
7610
|
}
|
|
7518
7611
|
|
|
7519
7612
|
// src/launcher/cascade.ts
|
|
@@ -7523,7 +7616,7 @@ function loadCascadeInput(params) {
|
|
|
7523
7616
|
const globalConfig = loadConfigFile(params.paths.globalConfigFile, GlobalConfigSchema, params.read);
|
|
7524
7617
|
const directoryRules = loadConfigFile(params.paths.directoryRulesFile, DirectoryRulesSchema, params.read);
|
|
7525
7618
|
const loadProfile = (name) => {
|
|
7526
|
-
const filepath =
|
|
7619
|
+
const filepath = path20.join(params.paths.configProfilesDir, `${name}.json`);
|
|
7527
7620
|
const loaded = loadConfigFile(filepath, ConfigProfileSchema, params.read);
|
|
7528
7621
|
if (loaded === void 0) {
|
|
7529
7622
|
return void 0;
|
|
@@ -7538,8 +7631,8 @@ function loadCascadeInput(params) {
|
|
|
7538
7631
|
});
|
|
7539
7632
|
const levels = [];
|
|
7540
7633
|
for (const dir of dirs) {
|
|
7541
|
-
const portable = loadConfigFile(
|
|
7542
|
-
const portableLocal = loadConfigFile(
|
|
7634
|
+
const portable = loadConfigFile(path20.join(dir, PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path20.join(dir, LEGACY_PORTABLE_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
7635
|
+
const portableLocal = loadConfigFile(path20.join(dir, PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read) ?? loadConfigFile(path20.join(dir, LEGACY_PORTABLE_LOCAL_CONFIG_FILENAME), PortableConfigSchema, params.read);
|
|
7543
7636
|
const rules = (directoryRules?.config.rules ?? []).map((rule, index) => ({ rule, index })).filter(({ rule }) => normaliseRulePath(rule.path, params.home) === dir).map(({ rule, index }) => {
|
|
7544
7637
|
const entryOrder = directoryRules?.ruleEntryOrders[index];
|
|
7545
7638
|
return {
|
|
@@ -7590,7 +7683,7 @@ function readDirectorySelections(loaded) {
|
|
|
7590
7683
|
// src/realCredentialCache.ts
|
|
7591
7684
|
import { spawnSync } from "node:child_process";
|
|
7592
7685
|
import fs7 from "node:fs";
|
|
7593
|
-
import
|
|
7686
|
+
import path21 from "node:path";
|
|
7594
7687
|
var KEYCHAIN_CACHE_SERVICE = "agent-shim-credential-cache";
|
|
7595
7688
|
var CACHE_DIR_MODE = 448;
|
|
7596
7689
|
var CACHE_FILE_MODE = 384;
|
|
@@ -7617,8 +7710,8 @@ function security(args, stdin) {
|
|
|
7617
7710
|
return { status: result.status, stdout: result.stdout };
|
|
7618
7711
|
}
|
|
7619
7712
|
function createRealCredentialCache(root) {
|
|
7620
|
-
const dir =
|
|
7621
|
-
const filePath = (owner) =>
|
|
7713
|
+
const dir = path21.join(root, "credential-cache");
|
|
7714
|
+
const filePath = (owner) => path21.join(dir, cacheFileNameFor(owner));
|
|
7622
7715
|
return {
|
|
7623
7716
|
read(store, owner) {
|
|
7624
7717
|
if (store === "file") {
|
|
@@ -7672,70 +7765,6 @@ import fs8 from "node:fs";
|
|
|
7672
7765
|
import os4 from "node:os";
|
|
7673
7766
|
import path22 from "node:path";
|
|
7674
7767
|
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
7768
|
function isEnoent3(error) {
|
|
7740
7769
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
7741
7770
|
}
|
|
@@ -8137,13 +8166,17 @@ function realContentSourcePath() {
|
|
|
8137
8166
|
return resolveContentSourcePath({ isSea: isSea(), execPath: process.execPath, argv1: process.argv[1] });
|
|
8138
8167
|
}
|
|
8139
8168
|
function realResolveClaudeBinary(isOwnBinary) {
|
|
8140
|
-
return () => discoverClaudeBinary({
|
|
8169
|
+
return (request4) => discoverClaudeBinary({
|
|
8141
8170
|
listVersionsDir,
|
|
8142
8171
|
pathDirs: (process.env.PATH ?? "").split(path22.delimiter).filter((dir) => dir !== ""),
|
|
8143
8172
|
findExecutableInDir,
|
|
8144
|
-
isOwnBinary
|
|
8173
|
+
isOwnBinary,
|
|
8174
|
+
...request4?.version === void 0 ? {} : { version: request4.version }
|
|
8145
8175
|
});
|
|
8146
8176
|
}
|
|
8177
|
+
function realInstalledClaudeVersions() {
|
|
8178
|
+
return installedVersions(listVersionsDir(defaultVersionsDir()));
|
|
8179
|
+
}
|
|
8147
8180
|
|
|
8148
8181
|
// src/checkReport.ts
|
|
8149
8182
|
import path23 from "node:path";
|
|
@@ -8185,6 +8218,34 @@ function parseEnvBool(variable, value) {
|
|
|
8185
8218
|
return parsed;
|
|
8186
8219
|
}
|
|
8187
8220
|
|
|
8221
|
+
// src/launcher/claudeVersion.ts
|
|
8222
|
+
var InvalidClaudeVersionError = class extends UsageError {
|
|
8223
|
+
constructor(value, origin) {
|
|
8224
|
+
super(`${origin} names Claude Code version "${value}", which is not an exact dotted-numeric version such as 2.1.220.`);
|
|
8225
|
+
this.value = value;
|
|
8226
|
+
this.origin = origin;
|
|
8227
|
+
this.name = "InvalidClaudeVersionError";
|
|
8228
|
+
}
|
|
8229
|
+
value;
|
|
8230
|
+
origin;
|
|
8231
|
+
};
|
|
8232
|
+
function validated(value, origin) {
|
|
8233
|
+
if (!NUMERIC_DOTTED_VERSION_RE.test(value)) {
|
|
8234
|
+
throw new InvalidClaudeVersionError(value, origin);
|
|
8235
|
+
}
|
|
8236
|
+
return value;
|
|
8237
|
+
}
|
|
8238
|
+
function resolveClaudeVersion(params) {
|
|
8239
|
+
if (params.flag !== void 0) {
|
|
8240
|
+
return { version: validated(params.flag, "--claude-version"), source: "flag" };
|
|
8241
|
+
}
|
|
8242
|
+
const fromEnv = params.env.AGENT_SHIM_CLAUDE_VERSION;
|
|
8243
|
+
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
8244
|
+
return { version: validated(fromEnv, "AGENT_SHIM_CLAUDE_VERSION"), source: "environment" };
|
|
8245
|
+
}
|
|
8246
|
+
return params.cascade === void 0 ? void 0 : { version: params.cascade, source: "cascade" };
|
|
8247
|
+
}
|
|
8248
|
+
|
|
8188
8249
|
// src/checkReport.ts
|
|
8189
8250
|
var HISTORY_PROJECTS_KEY_PREFIX = "history/projects/";
|
|
8190
8251
|
function projectFragmentOf(rawKey) {
|
|
@@ -8335,6 +8396,19 @@ function buildCredentialReport(provider, identity, identityName, cacheEnv) {
|
|
|
8335
8396
|
...identityCached === void 0 ? {} : { identityCached }
|
|
8336
8397
|
};
|
|
8337
8398
|
}
|
|
8399
|
+
function buildClaudeVersionReport(params, cascadePin) {
|
|
8400
|
+
const installed = params.installedClaudeVersions;
|
|
8401
|
+
if (installed === void 0) {
|
|
8402
|
+
return void 0;
|
|
8403
|
+
}
|
|
8404
|
+
const pin = resolveClaudeVersion({ env: params.env, ...cascadePin === void 0 ? {} : { cascade: cascadePin } });
|
|
8405
|
+
const highestInstalled = installed.at(-1);
|
|
8406
|
+
return {
|
|
8407
|
+
...pin === void 0 ? {} : { pinned: { ...pin, installed: installed.includes(pin.version) } },
|
|
8408
|
+
...highestInstalled === void 0 ? {} : { highestInstalled },
|
|
8409
|
+
installed
|
|
8410
|
+
};
|
|
8411
|
+
}
|
|
8338
8412
|
function runCheck(params) {
|
|
8339
8413
|
const facts = buildEntryFacts({
|
|
8340
8414
|
fs: params.farmFs,
|
|
@@ -8359,6 +8433,7 @@ function runCheck(params) {
|
|
|
8359
8433
|
const settingsShared = resolved.flattened.categories.get("settings") ?? SHIPPED_CATEGORY_DEFAULTS.settings;
|
|
8360
8434
|
const settingsExposure = inspectSettingsExposure({ settingsShared, files: params.settingsFiles });
|
|
8361
8435
|
const keychain = params.platform === "darwin" && params.run !== void 0 && params.farmRoot !== void 0 ? lookupKeychainService(params.run, params.farmRoot) : void 0;
|
|
8436
|
+
const claudeVersion = buildClaudeVersionReport(params, resolved.flattened.launch.claudeVersion);
|
|
8362
8437
|
return {
|
|
8363
8438
|
...params.identityName === void 0 ? {} : { identityName: params.identityName },
|
|
8364
8439
|
identitySource: params.identitySource,
|
|
@@ -8371,7 +8446,8 @@ function runCheck(params) {
|
|
|
8371
8446
|
ambientCredential,
|
|
8372
8447
|
...keychain === void 0 ? {} : { keychain },
|
|
8373
8448
|
settingsExposure,
|
|
8374
|
-
credential: buildCredentialReport(params.provider, params.identity, params.identityName, params.credentialCache)
|
|
8449
|
+
credential: buildCredentialReport(params.provider, params.identity, params.identityName, params.credentialCache),
|
|
8450
|
+
...claudeVersion === void 0 ? {} : { claudeVersion }
|
|
8375
8451
|
};
|
|
8376
8452
|
}
|
|
8377
8453
|
function formatCheckReport(report) {
|
|
@@ -8420,6 +8496,17 @@ function formatCheckReport(report) {
|
|
|
8420
8496
|
if (credential.applies === "stored-login") {
|
|
8421
8497
|
lines.push(" The identity's stored login (no credential block applies).");
|
|
8422
8498
|
}
|
|
8499
|
+
if (report.claudeVersion !== void 0) {
|
|
8500
|
+
lines.push("", "Claude Code version:");
|
|
8501
|
+
const { pinned, highestInstalled, installed } = report.claudeVersion;
|
|
8502
|
+
if (pinned !== void 0) {
|
|
8503
|
+
lines.push(
|
|
8504
|
+
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(", ")}`
|
|
8505
|
+
);
|
|
8506
|
+
} else {
|
|
8507
|
+
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)`);
|
|
8508
|
+
}
|
|
8509
|
+
}
|
|
8423
8510
|
lines.push("", "Ambient-credential exposure:");
|
|
8424
8511
|
lines.push(
|
|
8425
8512
|
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 +8545,12 @@ function checkReportToJson(report) {
|
|
|
8458
8545
|
ambientCredential: report.ambientCredential.ok ? { ok: true } : { ok: false, variable: report.ambientCredential.variable, message: report.ambientCredential.message },
|
|
8459
8546
|
...report.keychain === void 0 ? {} : { keychain: report.keychain },
|
|
8460
8547
|
settingsExposure: report.settingsExposure,
|
|
8461
|
-
credential: report.credential
|
|
8548
|
+
credential: report.credential,
|
|
8549
|
+
...report.claudeVersion === void 0 ? {} : { claudeVersion: report.claudeVersion }
|
|
8462
8550
|
};
|
|
8463
8551
|
}
|
|
8464
8552
|
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;
|
|
8553
|
+
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
8554
|
}
|
|
8467
8555
|
function loadCheckProvider(paths, name) {
|
|
8468
8556
|
let definition;
|
|
@@ -8548,6 +8636,7 @@ function collectCheckReport(params) {
|
|
|
8548
8636
|
settingsFiles,
|
|
8549
8637
|
run: realRunPort,
|
|
8550
8638
|
credentialCache: realCredentialCacheEnv(paths),
|
|
8639
|
+
installedClaudeVersions: realInstalledClaudeVersions(),
|
|
8551
8640
|
...farmRoot === void 0 ? {} : { farmRoot },
|
|
8552
8641
|
platform: process.platform,
|
|
8553
8642
|
...provider === void 0 ? {} : { provider }
|
|
@@ -8699,13 +8788,13 @@ function pushProvider(push, entry, cacheEnv) {
|
|
|
8699
8788
|
push("provider", "fail", new LegacyProviderFileError(entry.path, conversion).message, entry.name);
|
|
8700
8789
|
return;
|
|
8701
8790
|
}
|
|
8702
|
-
const
|
|
8703
|
-
if (!
|
|
8704
|
-
push("provider", "fail", new ConfigValidationError(entry.path,
|
|
8791
|
+
const validated2 = ProviderSchema.safeParse(parsed);
|
|
8792
|
+
if (!validated2.success) {
|
|
8793
|
+
push("provider", "fail", new ConfigValidationError(entry.path, validated2.error.issues).message, entry.name);
|
|
8705
8794
|
return;
|
|
8706
8795
|
}
|
|
8707
|
-
const cache = cacheClause(cacheEnv, `provider ${entry.name}`,
|
|
8708
|
-
push("provider", cache.unreadable ? "fail" : "pass", `${entry.name} is valid (${describeProviderEndpoint(
|
|
8796
|
+
const cache = cacheClause(cacheEnv, `provider ${entry.name}`, validated2.data.credential);
|
|
8797
|
+
push("provider", cache.unreadable ? "fail" : "pass", `${entry.name} is valid (${describeProviderEndpoint(validated2.data)}, credential ${describeCredential(validated2.data.credential)})${cache.text}.`, entry.name);
|
|
8709
8798
|
}
|
|
8710
8799
|
function pushLegacyName(push, params) {
|
|
8711
8800
|
const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
|
|
@@ -8729,6 +8818,13 @@ function runDoctor(params) {
|
|
|
8729
8818
|
const push = (section, severity, message, subject) => {
|
|
8730
8819
|
findings.push({ section, severity, message, ...subject === void 0 ? {} : { subject } });
|
|
8731
8820
|
};
|
|
8821
|
+
const checkPin = (section, subject, where, version) => {
|
|
8822
|
+
const installed = params.installedClaudeVersions;
|
|
8823
|
+
if (version === void 0 || installed === void 0 || installed.includes(version)) {
|
|
8824
|
+
return;
|
|
8825
|
+
}
|
|
8826
|
+
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);
|
|
8827
|
+
};
|
|
8732
8828
|
const ambient = detectAmbientCredential(params.env);
|
|
8733
8829
|
if (ambient === void 0) {
|
|
8734
8830
|
push("ambient-credential", "pass", "No ambient-credential environment variable is set.");
|
|
@@ -8761,12 +8857,13 @@ function runDoctor(params) {
|
|
|
8761
8857
|
pushLegacyName(push, params);
|
|
8762
8858
|
const profileSources = /* @__PURE__ */ new Map();
|
|
8763
8859
|
for (const entry of params.configProfiles) {
|
|
8764
|
-
const
|
|
8765
|
-
if (!
|
|
8766
|
-
push("config-profile", "fail",
|
|
8860
|
+
const validated2 = validateJson(ConfigProfileSchema, entry);
|
|
8861
|
+
if (!validated2.ok) {
|
|
8862
|
+
push("config-profile", "fail", validated2.message, entry.name);
|
|
8767
8863
|
continue;
|
|
8768
8864
|
}
|
|
8769
|
-
profileSources.set(entry.name, { name: entry.name, profile:
|
|
8865
|
+
profileSources.set(entry.name, { name: entry.name, profile: validated2.data });
|
|
8866
|
+
checkPin("config-profile", entry.name, `Configuration profile "${entry.name}"`, validated2.data.launch?.claudeVersion);
|
|
8770
8867
|
}
|
|
8771
8868
|
const loadProfile = (name) => profileSources.get(name);
|
|
8772
8869
|
for (const name of profileSources.keys()) {
|
|
@@ -8782,13 +8879,13 @@ function runDoctor(params) {
|
|
|
8782
8879
|
const validProfileNames = new Set(profileSources.keys());
|
|
8783
8880
|
const validIdentityNames = /* @__PURE__ */ new Set();
|
|
8784
8881
|
for (const entry of params.identities) {
|
|
8785
|
-
const
|
|
8786
|
-
if (!
|
|
8787
|
-
push("identity", "fail",
|
|
8882
|
+
const validated2 = validateJson(IdentitySchema, entry);
|
|
8883
|
+
if (!validated2.ok) {
|
|
8884
|
+
push("identity", "fail", validated2.message, entry.name);
|
|
8788
8885
|
continue;
|
|
8789
8886
|
}
|
|
8790
8887
|
validIdentityNames.add(entry.name);
|
|
8791
|
-
const defaultProfile =
|
|
8888
|
+
const defaultProfile = validated2.data.defaultConfigProfile;
|
|
8792
8889
|
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
8793
8890
|
push(
|
|
8794
8891
|
"identity",
|
|
@@ -8797,7 +8894,7 @@ function runDoctor(params) {
|
|
|
8797
8894
|
entry.name
|
|
8798
8895
|
);
|
|
8799
8896
|
} else {
|
|
8800
|
-
const credential =
|
|
8897
|
+
const credential = validated2.data.credential;
|
|
8801
8898
|
const cache = credential === void 0 ? { text: "", unreadable: false } : cacheClause(params.credentialCache, `identity ${entry.name}`, credential);
|
|
8802
8899
|
push(
|
|
8803
8900
|
"identity",
|
|
@@ -8835,12 +8932,12 @@ function runDoctor(params) {
|
|
|
8835
8932
|
if (params.directoryRules.raw === void 0) {
|
|
8836
8933
|
push("directory-rules", "pass", "No directory-rules.json configured.");
|
|
8837
8934
|
} else {
|
|
8838
|
-
const
|
|
8839
|
-
if (!
|
|
8840
|
-
push("directory-rules", "fail",
|
|
8935
|
+
const validated2 = validateJson(DirectoryRulesSchema, params.directoryRules);
|
|
8936
|
+
if (!validated2.ok) {
|
|
8937
|
+
push("directory-rules", "fail", validated2.message);
|
|
8841
8938
|
} else {
|
|
8842
8939
|
push("directory-rules", "pass", `${params.directoryRules.path} is valid.`);
|
|
8843
|
-
for (const rule of
|
|
8940
|
+
for (const rule of validated2.data.rules) {
|
|
8844
8941
|
const badRefs = [];
|
|
8845
8942
|
if (rule.identity !== void 0 && !selectionExists(rule.identity)) {
|
|
8846
8943
|
badRefs.push(poolNameOf(rule.identity) === void 0 ? `identity "${rule.identity}"` : `pool "${poolNameOf(rule.identity) ?? ""}"`);
|
|
@@ -8848,6 +8945,7 @@ function runDoctor(params) {
|
|
|
8848
8945
|
if (rule.configProfile !== void 0 && !validProfileNames.has(rule.configProfile)) {
|
|
8849
8946
|
badRefs.push(`configuration profile "${rule.configProfile}"`);
|
|
8850
8947
|
}
|
|
8948
|
+
checkPin("directory-rules", rule.path, `Rule for "${rule.path}"`, rule.launch?.claudeVersion);
|
|
8851
8949
|
if (badRefs.length > 0) {
|
|
8852
8950
|
push("directory-rules", "fail", `Rule for "${rule.path}" names ${badRefs.join(" and ")}, which do not exist.`, rule.path);
|
|
8853
8951
|
} else {
|
|
@@ -8859,11 +8957,11 @@ function runDoctor(params) {
|
|
|
8859
8957
|
if (params.globalConfig.raw === void 0) {
|
|
8860
8958
|
push("global-config", "pass", "No config.json configured.");
|
|
8861
8959
|
} else {
|
|
8862
|
-
const
|
|
8863
|
-
if (!
|
|
8864
|
-
push("global-config", "fail",
|
|
8960
|
+
const validated2 = validateJson(GlobalConfigSchema, params.globalConfig);
|
|
8961
|
+
if (!validated2.ok) {
|
|
8962
|
+
push("global-config", "fail", validated2.message);
|
|
8865
8963
|
} else {
|
|
8866
|
-
const defaultProfile =
|
|
8964
|
+
const defaultProfile = validated2.data.defaultConfigProfile;
|
|
8867
8965
|
if (defaultProfile !== void 0 && !validProfileNames.has(defaultProfile)) {
|
|
8868
8966
|
push(
|
|
8869
8967
|
"global-config",
|
|
@@ -8872,27 +8970,28 @@ function runDoctor(params) {
|
|
|
8872
8970
|
);
|
|
8873
8971
|
} else {
|
|
8874
8972
|
push("global-config", "pass", `${params.globalConfig.path} is valid.`);
|
|
8973
|
+
checkPin("global-config", void 0, "config.json", validated2.data.launch?.claudeVersion);
|
|
8875
8974
|
}
|
|
8876
8975
|
}
|
|
8877
8976
|
}
|
|
8878
8977
|
if (params.categoriesLocal.raw === void 0) {
|
|
8879
8978
|
push("categories-local", "pass", "No categories.local.json configured.");
|
|
8880
8979
|
} else {
|
|
8881
|
-
const
|
|
8980
|
+
const validated2 = validateJson(CategoryClassificationOverlaySchema, params.categoriesLocal);
|
|
8882
8981
|
push(
|
|
8883
8982
|
"categories-local",
|
|
8884
|
-
|
|
8885
|
-
|
|
8983
|
+
validated2.ok ? "pass" : "fail",
|
|
8984
|
+
validated2.ok ? `${params.categoriesLocal.path} is valid.` : validated2.message
|
|
8886
8985
|
);
|
|
8887
8986
|
}
|
|
8888
8987
|
if (params.headroom.state.raw === void 0) {
|
|
8889
8988
|
push("headroom", "pass", "No headroom daemon has ever run; nothing to check.");
|
|
8890
8989
|
} else {
|
|
8891
|
-
const
|
|
8892
|
-
if (!
|
|
8893
|
-
push("headroom", "fail",
|
|
8990
|
+
const validated2 = validateJson(HeadroomStateSchema, params.headroom.state);
|
|
8991
|
+
if (!validated2.ok) {
|
|
8992
|
+
push("headroom", "fail", validated2.message);
|
|
8894
8993
|
} else {
|
|
8895
|
-
const state =
|
|
8994
|
+
const state = validated2.data;
|
|
8896
8995
|
const supervisorAlive = state.supervisorPid !== void 0 && params.headroom.isRunning(state.supervisorPid);
|
|
8897
8996
|
const headroomAlive = state.headroomPid !== void 0 && params.headroom.isRunning(state.headroomPid);
|
|
8898
8997
|
if (state.supervisorPid === void 0) {
|
|
@@ -9062,6 +9161,7 @@ function collectDoctorReport(params) {
|
|
|
9062
9161
|
const report = runDoctor({
|
|
9063
9162
|
env: params.env,
|
|
9064
9163
|
credentialCache: realCredentialCacheEnv(paths),
|
|
9164
|
+
installedClaudeVersions: realInstalledClaudeVersions(),
|
|
9065
9165
|
identities,
|
|
9066
9166
|
configProfiles,
|
|
9067
9167
|
providers,
|
|
@@ -9111,7 +9211,7 @@ var ConflictingIdentityError = class extends UsageError {
|
|
|
9111
9211
|
positional;
|
|
9112
9212
|
flag;
|
|
9113
9213
|
};
|
|
9114
|
-
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--category", "--share", "--hide"];
|
|
9214
|
+
var VALUED_FLAGS = ["--identity", "--config-profile", "--provider", "--claude-version", "--category", "--share", "--hide"];
|
|
9115
9215
|
var BOOLEAN_FLAGS = [
|
|
9116
9216
|
{ flag: "--headroom", key: "headroom" },
|
|
9117
9217
|
{ flag: "--track-usage", key: "trackUsage" },
|
|
@@ -9157,6 +9257,7 @@ function parseLauncherArgv(argv) {
|
|
|
9157
9257
|
let flagIdentity;
|
|
9158
9258
|
let configProfile;
|
|
9159
9259
|
let provider;
|
|
9260
|
+
let claudeVersion;
|
|
9160
9261
|
const booleans = {};
|
|
9161
9262
|
const categoryFlags = [];
|
|
9162
9263
|
const shareFlags = [];
|
|
@@ -9211,6 +9312,9 @@ function parseLauncherArgv(argv) {
|
|
|
9211
9312
|
case "--provider":
|
|
9212
9313
|
provider = value;
|
|
9213
9314
|
break;
|
|
9315
|
+
case "--claude-version":
|
|
9316
|
+
claudeVersion = value;
|
|
9317
|
+
break;
|
|
9214
9318
|
case "--category":
|
|
9215
9319
|
categoryFlags.push(value);
|
|
9216
9320
|
break;
|
|
@@ -9232,6 +9336,7 @@ function parseLauncherArgv(argv) {
|
|
|
9232
9336
|
...identity === void 0 ? {} : { identity },
|
|
9233
9337
|
...configProfile === void 0 ? {} : { configProfile },
|
|
9234
9338
|
...provider === void 0 ? {} : { provider },
|
|
9339
|
+
...claudeVersion === void 0 ? {} : { claudeVersion },
|
|
9235
9340
|
...native ? { native: true } : {},
|
|
9236
9341
|
...booleans,
|
|
9237
9342
|
categoryFlags,
|
|
@@ -9693,7 +9798,11 @@ function prepareLaunch(params) {
|
|
|
9693
9798
|
} else if (params.farm !== void 0) {
|
|
9694
9799
|
cascadeLaunch = flattenLayers(assembleCascade(params.farm.loadCascade(configProfileDecision.name, cliOverride)).layers, { home: params.farm.home }).launch;
|
|
9695
9800
|
}
|
|
9696
|
-
const
|
|
9801
|
+
const pinnedVersion = resolveClaudeVersion({ env, ...parsedArgv.claudeVersion === void 0 ? {} : { flag: parsedArgv.claudeVersion }, ...cascadeLaunch?.claudeVersion === void 0 ? {} : { cascade: cascadeLaunch.claudeVersion } });
|
|
9802
|
+
const discovered = params.resolveClaudeBinary(pinnedVersion === void 0 ? void 0 : { version: pinnedVersion.version });
|
|
9803
|
+
if (pinnedVersion !== void 0) {
|
|
9804
|
+
log.info(`agent-shim: Claude Code ${pinnedVersion.version} (pinned by ${pinnedVersion.source})`);
|
|
9805
|
+
}
|
|
9697
9806
|
const resolvedFlags = resolveLaunchFlags({
|
|
9698
9807
|
env,
|
|
9699
9808
|
// A pool pick reads the usage the front door records, so a pool launch records its own unless something says otherwise.
|