@node9/proxy 2.22.0 → 2.23.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/dist/cli.js +1283 -949
- package/dist/cli.mjs +1254 -920
- package/dist/dashboard.mjs +458 -88
- package/dist/index.js +293 -24
- package/dist/index.mjs +293 -24
- package/dist/scan-ink.mjs +12 -0
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1006,6 +1006,7 @@ function normalizeCommandForPolicyImpl(command) {
|
|
|
1006
1006
|
const source = command.slice(s, e);
|
|
1007
1007
|
if (resolved === source) continue;
|
|
1008
1008
|
if (resolved === "" || /\s/.test(resolved)) continue;
|
|
1009
|
+
if (/^[;&|()<>]+$/.test(resolved)) continue;
|
|
1009
1010
|
rewrites.push([s, e, resolved]);
|
|
1010
1011
|
const quoteOnly = source.replace(/['"]/g, "");
|
|
1011
1012
|
if (quoteOnly !== source) quoteOnlyRewrites.push([s, e, quoteOnly]);
|
|
@@ -1355,23 +1356,147 @@ function extractLiteralArgs(callExpr) {
|
|
|
1355
1356
|
const args = positionedArgs(words);
|
|
1356
1357
|
return { name, flags, paths: args.map((a) => a.value), words, args };
|
|
1357
1358
|
}
|
|
1359
|
+
function payloadKey(payload) {
|
|
1360
|
+
if (!assignmentTable || assignmentTable.size === 0) return payload;
|
|
1361
|
+
const bindings = [];
|
|
1362
|
+
for (const [name, rec] of assignmentTable) {
|
|
1363
|
+
if (rec.value !== null) bindings.push(`${name}=${rec.value}`);
|
|
1364
|
+
}
|
|
1365
|
+
return `${payload}\0${bindings.sort().join("")}`;
|
|
1366
|
+
}
|
|
1367
|
+
function claimPayload(payload) {
|
|
1368
|
+
if (!seenPayloads) return "ok";
|
|
1369
|
+
const key = payloadKey(payload);
|
|
1370
|
+
if (seenPayloads.has(key)) return "seen";
|
|
1371
|
+
if (payloadBudget <= 0) return "exhausted";
|
|
1372
|
+
payloadBudget--;
|
|
1373
|
+
seenPayloads.add(key);
|
|
1374
|
+
return "ok";
|
|
1375
|
+
}
|
|
1376
|
+
function recordTopLevelAssignments(f) {
|
|
1377
|
+
const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
|
|
1378
|
+
for (const stmt of stmts) recordTopLevelStmt(stmt);
|
|
1379
|
+
}
|
|
1380
|
+
function probeBinOp(src) {
|
|
1381
|
+
try {
|
|
1382
|
+
const cmd = syntax.NewParser().Parse(src, "probe")?.Stmts?.[0]?.Cmd;
|
|
1383
|
+
if (cmd && syntax.NodeType(cmd) === "BinaryCmd") return cmd.Op;
|
|
1384
|
+
} catch {
|
|
1385
|
+
}
|
|
1386
|
+
return null;
|
|
1387
|
+
}
|
|
1388
|
+
function recordTopLevelStmt(stmt) {
|
|
1389
|
+
if (!stmt || !stmt.Cmd) return false;
|
|
1390
|
+
const t = syntax.NodeType(stmt.Cmd);
|
|
1391
|
+
if (t === "BinaryCmd") {
|
|
1392
|
+
if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
|
|
1393
|
+
if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
|
|
1394
|
+
if (stmt.Cmd.Op === AND_OP) return recordTopLevelStmt(stmt.Cmd.Y);
|
|
1395
|
+
return true;
|
|
1396
|
+
}
|
|
1397
|
+
if (t !== "CallExpr" && t !== "DeclClause") return false;
|
|
1398
|
+
let at = 0;
|
|
1399
|
+
try {
|
|
1400
|
+
at = stmt.Pos().Offset();
|
|
1401
|
+
} catch {
|
|
1402
|
+
at = 0;
|
|
1403
|
+
}
|
|
1404
|
+
recordAssignments(stmt.Cmd, at);
|
|
1405
|
+
if (stmt.Negated) return false;
|
|
1406
|
+
if (t === "DeclClause") return ASSIGNMENT_HEADS.has(stmt.Cmd.Variant?.Value ?? "");
|
|
1407
|
+
return (stmt.Cmd.Args || []).length === 0 && (stmt.Cmd.Assigns || []).length > 0;
|
|
1408
|
+
}
|
|
1409
|
+
function recordAssignments(n, at) {
|
|
1410
|
+
if (!assignmentTable) return;
|
|
1411
|
+
const t = syntax.NodeType(n);
|
|
1412
|
+
let assigns = [];
|
|
1413
|
+
if (t === "CallExpr") {
|
|
1414
|
+
if ((n.Args || []).length > 0) return;
|
|
1415
|
+
assigns = n.Assigns || [];
|
|
1416
|
+
} else if (t === "DeclClause") {
|
|
1417
|
+
if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
|
|
1418
|
+
assigns = (n.Args || []).filter((a) => syntax.NodeType(a) === "Assign");
|
|
1419
|
+
} else return;
|
|
1420
|
+
for (const a of assigns) {
|
|
1421
|
+
const name = a?.Name?.Value;
|
|
1422
|
+
if (!name || !a.Value || a.Append) continue;
|
|
1423
|
+
assignmentTable.set(name, { value: resolveWordLiteral(a.Value), at });
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
function resolveTrivialSubst(part) {
|
|
1427
|
+
if (syntax.NodeType(part) !== "CmdSubst") return void 0;
|
|
1428
|
+
const stmts = part.Stmts || [];
|
|
1429
|
+
if (stmts.length !== 1) return void 0;
|
|
1430
|
+
const st = stmts[0];
|
|
1431
|
+
if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
|
|
1432
|
+
const cmd = st.Cmd;
|
|
1433
|
+
if (!cmd || syntax.NodeType(cmd) !== "CallExpr") return void 0;
|
|
1434
|
+
if ((cmd.Assigns || []).length > 0) return void 0;
|
|
1435
|
+
const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
|
|
1436
|
+
if (words.length === 0 || words.some((w) => w === null)) return void 0;
|
|
1437
|
+
const head = baseWord(words[0]);
|
|
1438
|
+
const rest = words.slice(1);
|
|
1439
|
+
if (head === "echo") {
|
|
1440
|
+
let i = 0;
|
|
1441
|
+
while (i < rest.length && /^-[neE]+$/.test(rest[i])) i++;
|
|
1442
|
+
return rest.slice(i).join(" ");
|
|
1443
|
+
}
|
|
1444
|
+
if (head === "printf") {
|
|
1445
|
+
if (rest.length !== 2) return void 0;
|
|
1446
|
+
if (!/^%s(\\n)?$/.test(rest[0])) return void 0;
|
|
1447
|
+
return rest[1];
|
|
1448
|
+
}
|
|
1449
|
+
return void 0;
|
|
1450
|
+
}
|
|
1451
|
+
function recordedExpansion(name) {
|
|
1452
|
+
if (!assignmentTable || !name) return void 0;
|
|
1453
|
+
const rec = assignmentTable.get(name);
|
|
1454
|
+
if (rec === void 0 || rec.at >= currentStmtOffset) return void 0;
|
|
1455
|
+
return rec.value;
|
|
1456
|
+
}
|
|
1457
|
+
function isPlainParam(p) {
|
|
1458
|
+
if (syntax.NodeType(p) !== "ParamExp") return false;
|
|
1459
|
+
return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
|
|
1460
|
+
}
|
|
1461
|
+
function expandPlainParam(p) {
|
|
1462
|
+
if (!assignmentTable) return void 0;
|
|
1463
|
+
if (!isPlainParam(p)) return void 0;
|
|
1464
|
+
const recorded = recordedExpansion(p.Param?.Value);
|
|
1465
|
+
if (recorded !== void 0) return recorded;
|
|
1466
|
+
return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
|
|
1467
|
+
}
|
|
1358
1468
|
function resolveWordLiteral(w) {
|
|
1359
1469
|
const parts = w?.Parts || [];
|
|
1360
1470
|
let s = "";
|
|
1361
1471
|
for (const p of parts) {
|
|
1362
|
-
const
|
|
1363
|
-
if (
|
|
1364
|
-
|
|
1365
|
-
else if (t === "DblQuoted") {
|
|
1366
|
-
const inner = p.Parts || [];
|
|
1367
|
-
if (!inner.every((ip) => syntax.NodeType(ip) === "Lit")) return null;
|
|
1368
|
-
s += inner.map((ip) => ip.Value ?? "").join("");
|
|
1369
|
-
} else {
|
|
1370
|
-
return null;
|
|
1371
|
-
}
|
|
1472
|
+
const piece = resolvePart(p, false);
|
|
1473
|
+
if (piece === void 0 || piece === null) return null;
|
|
1474
|
+
s += piece;
|
|
1372
1475
|
}
|
|
1373
1476
|
return s;
|
|
1374
1477
|
}
|
|
1478
|
+
function resolvePart(p, inQuotes) {
|
|
1479
|
+
const t = syntax.NodeType(p);
|
|
1480
|
+
if (t === "Lit") {
|
|
1481
|
+
const raw = p.Value ?? "";
|
|
1482
|
+
if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
|
|
1483
|
+
return assignmentTable ? raw.replace(/\\([$`"\\])/g, "$1") : raw;
|
|
1484
|
+
}
|
|
1485
|
+
if (t === "SglQuoted") return p.Value ?? "";
|
|
1486
|
+
if (t === "ParamExp") return expandPlainParam(p);
|
|
1487
|
+
if (t === "CmdSubst") return assignmentTable ? resolveTrivialSubst(p) : void 0;
|
|
1488
|
+
if (t === "DblQuoted" && !inQuotes) {
|
|
1489
|
+
const inner = p.Parts || [];
|
|
1490
|
+
let out = "";
|
|
1491
|
+
for (const ip of inner) {
|
|
1492
|
+
const piece = resolvePart(ip, true);
|
|
1493
|
+
if (piece === void 0 || piece === null) return piece;
|
|
1494
|
+
out += piece;
|
|
1495
|
+
}
|
|
1496
|
+
return out;
|
|
1497
|
+
}
|
|
1498
|
+
return void 0;
|
|
1499
|
+
}
|
|
1375
1500
|
function parseDestHost(token) {
|
|
1376
1501
|
if (!token) return null;
|
|
1377
1502
|
let t = token.trim();
|
|
@@ -1426,6 +1551,7 @@ function destTokensForBinary(binary, args) {
|
|
|
1426
1551
|
case "ssh":
|
|
1427
1552
|
return positionals.slice(0, 1);
|
|
1428
1553
|
case "scp":
|
|
1554
|
+
case "rsync":
|
|
1429
1555
|
return positionals.filter((p) => p.includes(":") || p.includes("@"));
|
|
1430
1556
|
case "nc":
|
|
1431
1557
|
case "ncat":
|
|
@@ -1615,17 +1741,35 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1615
1741
|
const f = parseShared(command);
|
|
1616
1742
|
if (f === PARSE_FAIL) return null;
|
|
1617
1743
|
let result = null;
|
|
1744
|
+
const outerTable = assignmentTable;
|
|
1745
|
+
const outerOffset = currentStmtOffset;
|
|
1746
|
+
const outerSeen = seenPayloads;
|
|
1747
|
+
const outerBudget = payloadBudget;
|
|
1748
|
+
if (depth === 0) {
|
|
1749
|
+
seenPayloads = /* @__PURE__ */ new Set();
|
|
1750
|
+
payloadBudget = PAYLOAD_BUDGET;
|
|
1751
|
+
}
|
|
1752
|
+
assignmentTable = new Map(
|
|
1753
|
+
[...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
|
|
1754
|
+
);
|
|
1755
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1618
1756
|
try {
|
|
1757
|
+
recordTopLevelAssignments(f);
|
|
1619
1758
|
syntax.Walk(f, (node) => {
|
|
1620
1759
|
if (!node || result?.verdict === "block") return false;
|
|
1621
1760
|
const n = node;
|
|
1622
1761
|
const nodeType = syntax.NodeType(n);
|
|
1623
1762
|
if (nodeType === "Stmt") {
|
|
1763
|
+
try {
|
|
1764
|
+
currentStmtOffset = n.Pos().Offset();
|
|
1765
|
+
} catch {
|
|
1766
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
1767
|
+
}
|
|
1624
1768
|
result = stricter(result, jailedRedirectRead(n));
|
|
1625
1769
|
return result?.verdict !== "block";
|
|
1626
1770
|
}
|
|
1627
1771
|
if (nodeType !== "CallExpr") return true;
|
|
1628
|
-
const { name, flags, paths, words
|
|
1772
|
+
const { name, flags, paths, words } = extractLiteralArgs(n);
|
|
1629
1773
|
if (!name) return true;
|
|
1630
1774
|
if (name === "rm") {
|
|
1631
1775
|
const flagStr = flags.join("").toLowerCase();
|
|
@@ -1654,9 +1798,30 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1654
1798
|
}
|
|
1655
1799
|
}
|
|
1656
1800
|
}
|
|
1657
|
-
if (depth <
|
|
1801
|
+
if (depth < 24 && name === "find") {
|
|
1802
|
+
for (const action of findActions(words, 0)) {
|
|
1803
|
+
const h = unwrapCommandHead(action);
|
|
1804
|
+
const inner = literalShellPayload(action.slice(h), baseWord(action[h]));
|
|
1805
|
+
if (inner === null) continue;
|
|
1806
|
+
const claim = claimPayload(inner);
|
|
1807
|
+
if (claim === "exhausted") {
|
|
1808
|
+
result = stricter(result, UNANALYSABLE_NESTING);
|
|
1809
|
+
continue;
|
|
1810
|
+
}
|
|
1811
|
+
if (claim === "seen") continue;
|
|
1812
|
+
const v = analyzeFsOperationImpl(inner, depth + 1);
|
|
1813
|
+
result = stricter(result, v);
|
|
1814
|
+
if (result?.verdict === "block") return false;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
if (depth < 24) {
|
|
1658
1818
|
const payload = literalShellPayload(words, name);
|
|
1659
|
-
|
|
1819
|
+
const claim = payload === null ? "seen" : claimPayload(payload);
|
|
1820
|
+
if (claim === "exhausted") {
|
|
1821
|
+
result = stricter(result, UNANALYSABLE_NESTING);
|
|
1822
|
+
return true;
|
|
1823
|
+
}
|
|
1824
|
+
if (payload !== null && claim === "ok") {
|
|
1660
1825
|
const inner = analyzeFsOperationImpl(payload, depth + 1);
|
|
1661
1826
|
if (inner) {
|
|
1662
1827
|
result = inner;
|
|
@@ -1665,7 +1830,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1665
1830
|
return true;
|
|
1666
1831
|
}
|
|
1667
1832
|
}
|
|
1668
|
-
const readPaths = FS_READ_TOOLS.has(name) ?
|
|
1833
|
+
const readPaths = FS_READ_TOOLS.has(name) ? readerPaths(words, 0) : wrappedReadPaths(words, name);
|
|
1669
1834
|
if (readPaths) {
|
|
1670
1835
|
for (const p of readPaths) {
|
|
1671
1836
|
result = stricter(result, matchSensitivePath2(p));
|
|
@@ -1680,6 +1845,11 @@ function analyzeFsOperationImpl(command, depth = 0) {
|
|
|
1680
1845
|
return result;
|
|
1681
1846
|
} catch {
|
|
1682
1847
|
return null;
|
|
1848
|
+
} finally {
|
|
1849
|
+
assignmentTable = outerTable;
|
|
1850
|
+
currentStmtOffset = outerOffset;
|
|
1851
|
+
seenPayloads = outerSeen;
|
|
1852
|
+
if (depth === 0) payloadBudget = outerBudget;
|
|
1683
1853
|
}
|
|
1684
1854
|
}
|
|
1685
1855
|
function stricter(a, b) {
|
|
@@ -1736,6 +1906,18 @@ function resolveCopyShape(words, h) {
|
|
|
1736
1906
|
}
|
|
1737
1907
|
return null;
|
|
1738
1908
|
}
|
|
1909
|
+
function findAction(words, k) {
|
|
1910
|
+
const end = words.findIndex((w, i) => i > k && (w === ";" || w === "+"));
|
|
1911
|
+
return words.slice(k + 1, end < 0 ? words.length : end);
|
|
1912
|
+
}
|
|
1913
|
+
function findActions(words, h) {
|
|
1914
|
+
const out = [];
|
|
1915
|
+
for (let i = h + 1; i < words.length; i++) {
|
|
1916
|
+
const w = words[i];
|
|
1917
|
+
if (w !== null && FIND_EXEC_FLAGS.has(w)) out.push(findAction(words, i));
|
|
1918
|
+
}
|
|
1919
|
+
return out;
|
|
1920
|
+
}
|
|
1739
1921
|
function findStartPoints(words, h) {
|
|
1740
1922
|
const k = words.findIndex((w, i) => i > h && w !== null && FIND_EXEC_FLAGS.has(w));
|
|
1741
1923
|
if (k < 0) return { k, starts: [] };
|
|
@@ -1751,8 +1933,13 @@ function copySourcePaths(words) {
|
|
|
1751
1933
|
if (fi >= 0) {
|
|
1752
1934
|
const { k, starts } = findStartPoints(words, fi);
|
|
1753
1935
|
if (k < 0) return [];
|
|
1754
|
-
const
|
|
1755
|
-
|
|
1936
|
+
const out = [];
|
|
1937
|
+
for (const action of findActions(words, fi)) {
|
|
1938
|
+
const h2 = unwrapCommandHead(action);
|
|
1939
|
+
if (!resolveCopyShape(action, h2)) continue;
|
|
1940
|
+
out.push(...starts, ...copySourcePaths(action));
|
|
1941
|
+
}
|
|
1942
|
+
return out;
|
|
1756
1943
|
}
|
|
1757
1944
|
if (!COPY_VERB_HEADS.has(baseWord(words[h]))) return [];
|
|
1758
1945
|
const r = resolveCopyShape(words, h);
|
|
@@ -1929,6 +2116,15 @@ function flagEffect(token, shape, known) {
|
|
|
1929
2116
|
}
|
|
1930
2117
|
return NONE;
|
|
1931
2118
|
}
|
|
2119
|
+
function readerPaths(words, h) {
|
|
2120
|
+
const head = baseWord(words[h]);
|
|
2121
|
+
const from = h + 1;
|
|
2122
|
+
const flags = words.slice(from).filter((w) => w !== null && w.startsWith("-"));
|
|
2123
|
+
return [
|
|
2124
|
+
...readTargets(head, positionedArgs(words, from), flags, words, from),
|
|
2125
|
+
...flagOperandFiles(head, words, from)
|
|
2126
|
+
];
|
|
2127
|
+
}
|
|
1932
2128
|
function readTargets(verb, args, flags, words = [], from = 1) {
|
|
1933
2129
|
const shape = PATTERN_VERBS[verb];
|
|
1934
2130
|
if (!shape) return args.map((a) => a.value);
|
|
@@ -2010,18 +2206,21 @@ function flagOperandFiles(verb, words, from) {
|
|
|
2010
2206
|
function wrappedReadPaths(words, name) {
|
|
2011
2207
|
if (name === "find") {
|
|
2012
2208
|
const { k, starts } = findStartPoints(words, 0);
|
|
2013
|
-
|
|
2209
|
+
if (k < 0) return null;
|
|
2210
|
+
const paths = [];
|
|
2211
|
+
let reads = false;
|
|
2212
|
+
for (const action of findActions(words, 0)) {
|
|
2213
|
+
const h2 = unwrapCommandHead(action);
|
|
2214
|
+
if (!isReaderWord(action[h2] ?? null)) continue;
|
|
2215
|
+
reads = true;
|
|
2216
|
+
paths.push(...readerPaths(action, h2));
|
|
2217
|
+
}
|
|
2218
|
+
return reads ? [...starts, ...paths] : paths;
|
|
2014
2219
|
}
|
|
2015
2220
|
if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
|
|
2016
2221
|
const h = unwrapCommandHead(words);
|
|
2017
2222
|
if (h <= 0 || !isReaderWord(words[h] ?? null)) return null;
|
|
2018
|
-
|
|
2019
|
-
const rest = words.slice(h + 1);
|
|
2020
|
-
const restFlags = rest.filter((w) => w !== null && w.startsWith("-"));
|
|
2021
|
-
return [
|
|
2022
|
-
...readTargets(head, positionedArgs(words, h + 1), restFlags, words, h + 1),
|
|
2023
|
-
...flagOperandFiles(head, words, h + 1)
|
|
2024
|
-
];
|
|
2223
|
+
return readerPaths(words, h);
|
|
2025
2224
|
}
|
|
2026
2225
|
function literalShellPayload(words, name) {
|
|
2027
2226
|
const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
|
|
@@ -3784,7 +3983,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
3784
3983
|
}
|
|
3785
3984
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
3786
3985
|
}
|
|
3787
|
-
var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
|
|
3986
|
+
var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
|
|
3788
3987
|
var init_dist = __esm({
|
|
3789
3988
|
"packages/policy-engine/dist/index.mjs"() {
|
|
3790
3989
|
"use strict";
|
|
@@ -5162,6 +5361,60 @@ var init_dist = __esm({
|
|
|
5162
5361
|
"rsync"
|
|
5163
5362
|
]);
|
|
5164
5363
|
VALUE_FLAGS = {
|
|
5364
|
+
// rsync 3.2.7, its own --help: every flag whose operand could be mistaken for
|
|
5365
|
+
// a host. `-e ssh` is the one that matters most (`ssh` is not the destination).
|
|
5366
|
+
rsync: /* @__PURE__ */ new Set([
|
|
5367
|
+
"-e",
|
|
5368
|
+
"--rsh",
|
|
5369
|
+
"-f",
|
|
5370
|
+
"--filter",
|
|
5371
|
+
"-T",
|
|
5372
|
+
"--temp-dir",
|
|
5373
|
+
"-B",
|
|
5374
|
+
"--block-size",
|
|
5375
|
+
"-M",
|
|
5376
|
+
"--remote-option",
|
|
5377
|
+
"--exclude",
|
|
5378
|
+
"--exclude-from",
|
|
5379
|
+
"--include",
|
|
5380
|
+
"--include-from",
|
|
5381
|
+
"--files-from",
|
|
5382
|
+
"--compare-dest",
|
|
5383
|
+
"--copy-dest",
|
|
5384
|
+
"--link-dest",
|
|
5385
|
+
"--partial-dir",
|
|
5386
|
+
"--log-file",
|
|
5387
|
+
"--password-file",
|
|
5388
|
+
"--bwlimit",
|
|
5389
|
+
"--timeout",
|
|
5390
|
+
"--contimeout",
|
|
5391
|
+
"--port",
|
|
5392
|
+
"--sockopts",
|
|
5393
|
+
"--address",
|
|
5394
|
+
"--chmod",
|
|
5395
|
+
"--chown",
|
|
5396
|
+
"--max-size",
|
|
5397
|
+
"--min-size",
|
|
5398
|
+
"--modify-window",
|
|
5399
|
+
"--out-format",
|
|
5400
|
+
"--log-file-format",
|
|
5401
|
+
"--backup-dir",
|
|
5402
|
+
"--suffix",
|
|
5403
|
+
"--iconv",
|
|
5404
|
+
"--max-delete",
|
|
5405
|
+
"--checksum-choice",
|
|
5406
|
+
"--info",
|
|
5407
|
+
"--debug",
|
|
5408
|
+
"--stderr",
|
|
5409
|
+
"--outbuf",
|
|
5410
|
+
"--skip-compress",
|
|
5411
|
+
"--usermap",
|
|
5412
|
+
"--groupmap",
|
|
5413
|
+
"--mkpath",
|
|
5414
|
+
"--write-batch",
|
|
5415
|
+
"--read-batch",
|
|
5416
|
+
"--only-write-batch"
|
|
5417
|
+
]),
|
|
5165
5418
|
curl: /* @__PURE__ */ new Set([
|
|
5166
5419
|
"-d",
|
|
5167
5420
|
"--data",
|
|
@@ -5245,6 +5498,22 @@ var init_dist = __esm({
|
|
|
5245
5498
|
]),
|
|
5246
5499
|
nc: /* @__PURE__ */ new Set(["-p", "-s", "-w", "-X", "-x", "-e", "-g", "-G", "-i", "-O", "-T", "-q", "-m"])
|
|
5247
5500
|
};
|
|
5501
|
+
HOME_VARIABLES = /* @__PURE__ */ new Set(["HOME", "USERPROFILE"]);
|
|
5502
|
+
assignmentTable = null;
|
|
5503
|
+
currentStmtOffset = Number.MAX_SAFE_INTEGER;
|
|
5504
|
+
PAYLOAD_BUDGET = 256;
|
|
5505
|
+
payloadBudget = 0;
|
|
5506
|
+
seenPayloads = null;
|
|
5507
|
+
UNANALYSABLE_NESTING = {
|
|
5508
|
+
ruleName: "review-unanalysable-nesting",
|
|
5509
|
+
verdict: "review",
|
|
5510
|
+
reason: "This command nests more wrapped shell payloads than the policy engine will unwrap, so some of what it runs was not read.",
|
|
5511
|
+
path: ""
|
|
5512
|
+
};
|
|
5513
|
+
ASSIGNMENT_HEADS = /* @__PURE__ */ new Set(["export", "declare", "local", "readonly", "typeset"]);
|
|
5514
|
+
AND_OP = probeBinOp("a && b");
|
|
5515
|
+
OR_OP = probeBinOp("a || b");
|
|
5516
|
+
AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
|
|
5248
5517
|
FS_OP_CACHE_MAX = 5e3;
|
|
5249
5518
|
fsOpCache = /* @__PURE__ */ new Map();
|
|
5250
5519
|
stripDotSlash = (p) => p.replace(/^\.\//, "");
|
|
@@ -6321,7 +6590,7 @@ var init_dist = __esm({
|
|
|
6321
6590
|
{ view: "separators-stripped", decoder: "separators", stripped: true }
|
|
6322
6591
|
];
|
|
6323
6592
|
LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
|
|
6324
|
-
CANONICAL_EXTRACTOR_VERSION = "canonical-
|
|
6593
|
+
CANONICAL_EXTRACTOR_VERSION = "canonical-v18";
|
|
6325
6594
|
DEDUPE_PREVIEW_LEN = 120;
|
|
6326
6595
|
ENGINE_VERSION = "1.4.0";
|
|
6327
6596
|
}
|
|
@@ -10710,115 +10979,284 @@ var init_core = __esm({
|
|
|
10710
10979
|
}
|
|
10711
10980
|
});
|
|
10712
10981
|
|
|
10713
|
-
// src/
|
|
10714
|
-
function locatorCommand() {
|
|
10715
|
-
return process.platform === "win32" ? "where" : "which";
|
|
10716
|
-
}
|
|
10717
|
-
function shellInvocation(command) {
|
|
10718
|
-
if (process.platform === "win32") {
|
|
10719
|
-
return {
|
|
10720
|
-
file: process.env.ComSpec || "cmd.exe",
|
|
10721
|
-
args: ["/d", "/s", "/c", command]
|
|
10722
|
-
};
|
|
10723
|
-
}
|
|
10724
|
-
return { file: "/bin/bash", args: ["-c", command] };
|
|
10725
|
-
}
|
|
10726
|
-
var init_platform_shell = __esm({
|
|
10727
|
-
"src/utils/platform-shell.ts"() {
|
|
10728
|
-
"use strict";
|
|
10729
|
-
}
|
|
10730
|
-
});
|
|
10731
|
-
|
|
10732
|
-
// src/codex-trust.ts
|
|
10982
|
+
// src/agent-wiring.ts
|
|
10733
10983
|
import fs16 from "fs";
|
|
10734
10984
|
import path17 from "path";
|
|
10735
10985
|
import os13 from "os";
|
|
10736
|
-
import
|
|
10986
|
+
import * as yaml from "yaml";
|
|
10737
10987
|
import { parse as parseToml } from "smol-toml";
|
|
10738
|
-
function
|
|
10988
|
+
function readJson(filePath) {
|
|
10989
|
+
if (!fs16.existsSync(filePath)) return null;
|
|
10739
10990
|
try {
|
|
10740
|
-
return
|
|
10991
|
+
return JSON.parse(fs16.readFileSync(filePath, "utf-8"));
|
|
10741
10992
|
} catch {
|
|
10742
|
-
return
|
|
10993
|
+
return "invalid";
|
|
10743
10994
|
}
|
|
10744
10995
|
}
|
|
10745
|
-
function
|
|
10746
|
-
|
|
10996
|
+
function matchersHaveNode9Hook(matchers) {
|
|
10997
|
+
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
10998
|
+
}
|
|
10999
|
+
function flatHaveNode9Hook(entries) {
|
|
11000
|
+
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
11001
|
+
}
|
|
11002
|
+
function readHookRoot(filePath, format) {
|
|
11003
|
+
if (!fs16.existsSync(filePath)) return "absent";
|
|
11004
|
+
let raw;
|
|
10747
11005
|
try {
|
|
10748
|
-
|
|
11006
|
+
raw = fs16.readFileSync(filePath, "utf-8");
|
|
10749
11007
|
} catch {
|
|
10750
|
-
return
|
|
11008
|
+
return "absent";
|
|
10751
11009
|
}
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
if (row.agent !== "Codex" || typeof row.ts !== "string") continue;
|
|
10758
|
-
if (newest === null || row.ts > newest) newest = row.ts;
|
|
10759
|
-
} catch {
|
|
10760
|
-
}
|
|
11010
|
+
try {
|
|
11011
|
+
const parsed = format === "yaml" ? yaml.parse(raw) : JSON.parse(raw);
|
|
11012
|
+
return parsed?.hooks ?? {};
|
|
11013
|
+
} catch {
|
|
11014
|
+
return "invalid";
|
|
10761
11015
|
}
|
|
10762
|
-
return newest;
|
|
10763
11016
|
}
|
|
10764
|
-
function
|
|
10765
|
-
const
|
|
10766
|
-
|
|
10767
|
-
|
|
10768
|
-
else if (trustEntries === 0) state = "never-trusted";
|
|
10769
|
-
else if (hooksWrittenAt && lastCodexActivityAt && lastCodexActivityAt > hooksWrittenAt) {
|
|
10770
|
-
state = "observed";
|
|
10771
|
-
} else state = "unverified";
|
|
10772
|
-
return { state, hooksWrittenAt, lastCodexActivityAt, trustEntries };
|
|
11017
|
+
function eventWired(root, ev, format) {
|
|
11018
|
+
const arr = root[ev.key];
|
|
11019
|
+
if (format === "matcher") return matchersHaveNode9Hook(arr);
|
|
11020
|
+
return flatHaveNode9Hook(arr);
|
|
10773
11021
|
}
|
|
10774
|
-
function
|
|
10775
|
-
const
|
|
10776
|
-
const
|
|
10777
|
-
|
|
11022
|
+
function detectMcp(servers) {
|
|
11023
|
+
const entries = Object.entries(servers ?? {});
|
|
11024
|
+
const present = entries.some(([, s]) => s?.command === "node9");
|
|
11025
|
+
const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
11026
|
+
return { wrapped, present };
|
|
11027
|
+
}
|
|
11028
|
+
function readMcpServers(filePath, format) {
|
|
11029
|
+
if (!fs16.existsSync(filePath)) return {};
|
|
10778
11030
|
try {
|
|
10779
|
-
|
|
11031
|
+
if (format === "toml") {
|
|
11032
|
+
const parsed2 = parseToml(fs16.readFileSync(filePath, "utf-8"));
|
|
11033
|
+
return parsed2?.mcp_servers ?? {};
|
|
11034
|
+
}
|
|
11035
|
+
const parsed = readJson(filePath);
|
|
11036
|
+
if (parsed === null || parsed === "invalid") return {};
|
|
11037
|
+
return parsed.mcpServers ?? {};
|
|
10780
11038
|
} catch {
|
|
10781
|
-
|
|
11039
|
+
return {};
|
|
10782
11040
|
}
|
|
10783
|
-
const config = readTomlSafe(configPath);
|
|
10784
|
-
const hooksDisabled = config?.features?.hooks === false || config?.codex_hooks === false;
|
|
10785
|
-
const trustEntries = Object.keys(config?.hooks?.state ?? {}).length;
|
|
10786
|
-
return assessCodexTrustFrom({
|
|
10787
|
-
hooksDisabled,
|
|
10788
|
-
trustEntries,
|
|
10789
|
-
hooksWrittenAt,
|
|
10790
|
-
lastCodexActivityAt: lastCodexAuditTs(auditLogPath)
|
|
10791
|
-
});
|
|
10792
11041
|
}
|
|
10793
|
-
function
|
|
11042
|
+
function readMcp(filePath, format) {
|
|
11043
|
+
if (!fs16.existsSync(filePath)) return { wrapped: [], present: false };
|
|
10794
11044
|
try {
|
|
10795
|
-
|
|
10796
|
-
|
|
10797
|
-
|
|
11045
|
+
if (format === "toml") {
|
|
11046
|
+
const parsed2 = parseToml(fs16.readFileSync(filePath, "utf-8"));
|
|
11047
|
+
return detectMcp(parsed2?.mcp_servers);
|
|
11048
|
+
}
|
|
11049
|
+
const parsed = readJson(filePath);
|
|
11050
|
+
if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
|
|
11051
|
+
return detectMcp(parsed.mcpServers);
|
|
10798
11052
|
} catch {
|
|
11053
|
+
return { wrapped: [], present: false };
|
|
10799
11054
|
}
|
|
10800
|
-
|
|
10801
|
-
|
|
10802
|
-
|
|
10803
|
-
|
|
10804
|
-
|
|
10805
|
-
|
|
11055
|
+
}
|
|
11056
|
+
function getAgentWiring(home = os13.homedir()) {
|
|
11057
|
+
const detected = detectAgents(home);
|
|
11058
|
+
return AGENT_SPECS.map((spec) => {
|
|
11059
|
+
const present = spec.present(home);
|
|
11060
|
+
const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
|
|
11061
|
+
let hooks;
|
|
11062
|
+
let wireState;
|
|
11063
|
+
let hookLabel;
|
|
11064
|
+
let settingsPath;
|
|
11065
|
+
if (spec.shimFile) {
|
|
11066
|
+
const shimWired = exists(spec.shimFile(home));
|
|
11067
|
+
hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
|
|
11068
|
+
wireState = shimWired ? "wired" : present ? "unwired" : "absent";
|
|
11069
|
+
hookLabel = "node9 plugin";
|
|
11070
|
+
settingsPath = spec.shimFile(home);
|
|
11071
|
+
} else {
|
|
11072
|
+
const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
|
|
11073
|
+
const primary = spec.hookEvents[0];
|
|
11074
|
+
const rootPresent = root !== "absent" && root !== "invalid";
|
|
11075
|
+
hooks = spec.hookEvents.map((ev) => ({
|
|
11076
|
+
label: hookLabelOf(ev, pad),
|
|
11077
|
+
wired: rootPresent && eventWired(root, ev, spec.hookFormat)
|
|
11078
|
+
}));
|
|
11079
|
+
if (root === "absent") wireState = "absent";
|
|
11080
|
+
else if (root === "invalid") wireState = "invalid";
|
|
11081
|
+
else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
|
|
11082
|
+
hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
|
|
11083
|
+
settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
|
|
11084
|
+
}
|
|
11085
|
+
const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
|
|
11086
|
+
const anyHookWired = hooks.some((h) => h.wired);
|
|
11087
|
+
return {
|
|
11088
|
+
id: spec.id,
|
|
11089
|
+
label: spec.label,
|
|
11090
|
+
setupCommand: spec.setupCommand,
|
|
11091
|
+
installed: detected[spec.id],
|
|
11092
|
+
present,
|
|
11093
|
+
hooks,
|
|
11094
|
+
wireState,
|
|
11095
|
+
hookLabel,
|
|
11096
|
+
settingsPath,
|
|
11097
|
+
configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
|
|
11098
|
+
mcpServers: mcp ? mcp.wrapped : null,
|
|
11099
|
+
mcpProtected: mcp ? mcp.present : false,
|
|
11100
|
+
isProtected: anyHookWired || (mcp?.present ?? false)
|
|
11101
|
+
};
|
|
11102
|
+
});
|
|
11103
|
+
}
|
|
11104
|
+
var exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
|
|
11105
|
+
var init_agent_wiring = __esm({
|
|
11106
|
+
"src/agent-wiring.ts"() {
|
|
11107
|
+
"use strict";
|
|
11108
|
+
init_setup();
|
|
11109
|
+
exists = (p) => {
|
|
11110
|
+
try {
|
|
11111
|
+
return fs16.existsSync(p);
|
|
11112
|
+
} catch {
|
|
11113
|
+
return false;
|
|
10806
11114
|
}
|
|
10807
|
-
}
|
|
11115
|
+
};
|
|
11116
|
+
ck = (key) => ({ key, kind: "check" });
|
|
11117
|
+
lg = (key) => ({ key, kind: "log" });
|
|
11118
|
+
DEFAULT_LABEL_PAD = 11;
|
|
11119
|
+
hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
|
|
11120
|
+
AGENT_SPECS = [
|
|
11121
|
+
{
|
|
11122
|
+
id: "claude",
|
|
11123
|
+
label: "Claude Code",
|
|
11124
|
+
setupCommand: "node9 agents add claude",
|
|
11125
|
+
hookFile: (h) => path17.join(h, ".claude", "settings.json"),
|
|
11126
|
+
hookFormat: "matcher",
|
|
11127
|
+
// UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
|
|
11128
|
+
// that shipped; the spec must name it too, or status/doctor never show the
|
|
11129
|
+
// row and heal (which repairs via setupAgent) has no signal it is missing.
|
|
11130
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
11131
|
+
mcpFile: (h) => path17.join(h, ".claude.json"),
|
|
11132
|
+
present: (h) => exists(path17.join(h, ".claude", "settings.json")) || exists(path17.join(h, ".claude.json"))
|
|
11133
|
+
},
|
|
11134
|
+
{
|
|
11135
|
+
id: "gemini",
|
|
11136
|
+
label: "Gemini CLI",
|
|
11137
|
+
setupCommand: "node9 agents add gemini",
|
|
11138
|
+
hookFile: (h) => path17.join(h, ".gemini", "settings.json"),
|
|
11139
|
+
hookFormat: "matcher",
|
|
11140
|
+
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
11141
|
+
mcpFile: (h) => path17.join(h, ".gemini", "settings.json"),
|
|
11142
|
+
present: (h) => exists(path17.join(h, ".gemini", "settings.json"))
|
|
11143
|
+
},
|
|
11144
|
+
{
|
|
11145
|
+
id: "codex",
|
|
11146
|
+
label: "Codex",
|
|
11147
|
+
setupCommand: "node9 agents add codex",
|
|
11148
|
+
hookFile: (h) => path17.join(h, ".codex", "hooks.json"),
|
|
11149
|
+
hookFormat: "matcher",
|
|
11150
|
+
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
11151
|
+
mcpFile: (h) => path17.join(h, ".codex", "config.toml"),
|
|
11152
|
+
mcpFormat: "toml",
|
|
11153
|
+
present: (h) => exists(path17.join(h, ".codex"))
|
|
11154
|
+
},
|
|
11155
|
+
{
|
|
11156
|
+
id: "antigravity",
|
|
11157
|
+
label: "Antigravity",
|
|
11158
|
+
setupCommand: "node9 agents add antigravity",
|
|
11159
|
+
hookFile: (h) => path17.join(h, ".gemini", "config", "hooks.json"),
|
|
11160
|
+
hookFormat: "matcher",
|
|
11161
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
11162
|
+
mcpFile: (h) => path17.join(h, ".gemini", "config", "mcp_config.json"),
|
|
11163
|
+
present: (h) => exists(path17.join(h, ".gemini", "config", "hooks.json")) || exists(path17.join(h, ".gemini", "antigravity-cli")) || exists(path17.join(h, ".gemini", "antigravity-ide"))
|
|
11164
|
+
},
|
|
11165
|
+
{
|
|
11166
|
+
id: "copilot",
|
|
11167
|
+
label: "GitHub Copilot",
|
|
11168
|
+
setupCommand: "node9 agents add copilot",
|
|
11169
|
+
hookFile: (h) => path17.join(h, ".copilot", "hooks", "node9.json"),
|
|
11170
|
+
hookFormat: "flat",
|
|
11171
|
+
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
11172
|
+
mcpFile: (h) => path17.join(h, ".copilot", "mcp-config.json"),
|
|
11173
|
+
present: (h) => exists(path17.join(h, ".copilot"))
|
|
11174
|
+
},
|
|
11175
|
+
{
|
|
11176
|
+
id: "cursor",
|
|
11177
|
+
label: "Cursor",
|
|
11178
|
+
setupCommand: "node9 agents add cursor",
|
|
11179
|
+
// MCP-only — no hook file (see note above).
|
|
11180
|
+
hookFormat: "flat",
|
|
11181
|
+
hookEvents: [],
|
|
11182
|
+
mcpFile: (h) => path17.join(h, ".cursor", "mcp.json"),
|
|
11183
|
+
present: (h) => exists(path17.join(h, ".cursor", "mcp.json"))
|
|
11184
|
+
},
|
|
11185
|
+
{
|
|
11186
|
+
id: "hermes",
|
|
11187
|
+
label: "Hermes Agent",
|
|
11188
|
+
setupCommand: "node9 agents add hermes",
|
|
11189
|
+
hookFile: (h) => hermesConfigPath(h),
|
|
11190
|
+
hookFormat: "yaml",
|
|
11191
|
+
hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
|
|
11192
|
+
labelPad: 14,
|
|
11193
|
+
// 'post_tool_call' is wider than the default
|
|
11194
|
+
present: (h) => exists(hermesConfigPath(h))
|
|
11195
|
+
},
|
|
11196
|
+
{
|
|
11197
|
+
// Plugin-shim agents — protected by a node9-authored plugin/extension file
|
|
11198
|
+
// (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
|
|
11199
|
+
id: "opencode",
|
|
11200
|
+
label: "OpenCode",
|
|
11201
|
+
setupCommand: "node9 agents add opencode",
|
|
11202
|
+
hookFormat: "flat",
|
|
11203
|
+
hookEvents: [],
|
|
11204
|
+
shimFile: (h) => path17.join(opencodeConfigDir(h), "plugins", "node9.js"),
|
|
11205
|
+
present: (h) => exists(opencodeConfigDir(h)) || exists(path17.join(opencodeConfigDir(h), "plugins", "node9.js"))
|
|
11206
|
+
},
|
|
11207
|
+
{
|
|
11208
|
+
id: "pi",
|
|
11209
|
+
label: "Pi",
|
|
11210
|
+
setupCommand: "node9 agents add pi",
|
|
11211
|
+
hookFormat: "flat",
|
|
11212
|
+
hookEvents: [],
|
|
11213
|
+
shimFile: (h) => path17.join(h, ".pi", "agent", "extensions", "node9.js"),
|
|
11214
|
+
present: (h) => exists(path17.join(h, ".pi", "agent")) || exists(path17.join(h, ".pi", "agent", "extensions", "node9.js"))
|
|
11215
|
+
}
|
|
11216
|
+
];
|
|
11217
|
+
}
|
|
11218
|
+
});
|
|
11219
|
+
|
|
11220
|
+
// src/mcp-cmd.ts
|
|
11221
|
+
function tokenize4(cmd) {
|
|
11222
|
+
const tokens = [];
|
|
11223
|
+
let current = "";
|
|
11224
|
+
let inDouble = false;
|
|
11225
|
+
let quoted = false;
|
|
11226
|
+
let i = 0;
|
|
11227
|
+
while (i < cmd.length) {
|
|
11228
|
+
const ch = cmd[i];
|
|
11229
|
+
if (inDouble) {
|
|
11230
|
+
if (ch === '"') inDouble = false;
|
|
11231
|
+
else if (ch === "\\" && i + 1 < cmd.length) current += cmd[++i];
|
|
11232
|
+
else current += ch;
|
|
11233
|
+
} else if (ch === '"') {
|
|
11234
|
+
inDouble = true;
|
|
11235
|
+
quoted = true;
|
|
11236
|
+
} else if (ch === " " || ch === " ") {
|
|
11237
|
+
if (current || quoted) {
|
|
11238
|
+
tokens.push(current);
|
|
11239
|
+
current = "";
|
|
11240
|
+
quoted = false;
|
|
11241
|
+
}
|
|
11242
|
+
} else if (ch === "\\" && i + 1 < cmd.length) {
|
|
11243
|
+
current += cmd[++i];
|
|
11244
|
+
} else {
|
|
11245
|
+
current += ch;
|
|
10808
11246
|
}
|
|
11247
|
+
i++;
|
|
10809
11248
|
}
|
|
10810
|
-
|
|
11249
|
+
if (current || quoted && !inDouble) tokens.push(current);
|
|
11250
|
+
return tokens;
|
|
10811
11251
|
}
|
|
10812
|
-
function
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
Until then Codex runs unprotected. Every rewrite of hooks.json needs this again.`;
|
|
11252
|
+
function quoteArg(s) {
|
|
11253
|
+
if (s === "") return '""';
|
|
11254
|
+
if (/[\s"\\]/.test(s)) return `"${s.replace(/(["\\])/g, "\\$1")}"`;
|
|
11255
|
+
return s;
|
|
10817
11256
|
}
|
|
10818
|
-
var
|
|
10819
|
-
"src/
|
|
11257
|
+
var init_mcp_cmd = __esm({
|
|
11258
|
+
"src/mcp-cmd.ts"() {
|
|
10820
11259
|
"use strict";
|
|
10821
|
-
init_platform_shell();
|
|
10822
11260
|
}
|
|
10823
11261
|
});
|
|
10824
11262
|
|
|
@@ -10979,13 +11417,253 @@ var init_mcp_pin = __esm({
|
|
|
10979
11417
|
}
|
|
10980
11418
|
});
|
|
10981
11419
|
|
|
10982
|
-
// src/
|
|
11420
|
+
// src/mcp-wrap.ts
|
|
10983
11421
|
import fs18 from "fs";
|
|
10984
|
-
import path19 from "path";
|
|
10985
11422
|
import os15 from "os";
|
|
11423
|
+
import { parse as parseToml2, stringify as stringifyToml } from "smol-toml";
|
|
11424
|
+
function isNode9Command(command) {
|
|
11425
|
+
return /(^|[\\/])node9(\.(exe|cmd|ps1|bat))?$/i.test(command ?? "");
|
|
11426
|
+
}
|
|
11427
|
+
function classifyMcp(s) {
|
|
11428
|
+
if (isNode9Command(s.command)) {
|
|
11429
|
+
return (s.args ?? [])[0] === "mcp-gateway" ? "gatewayed" : "node9-self";
|
|
11430
|
+
}
|
|
11431
|
+
if (typeof s.command !== "string" || s.command.trim() === "") return "remote";
|
|
11432
|
+
return "ungoverned";
|
|
11433
|
+
}
|
|
11434
|
+
function mcpUpstreamString(s) {
|
|
11435
|
+
return [s.command ?? "", ...s.args ?? []].map(quoteArg).join(" ");
|
|
11436
|
+
}
|
|
11437
|
+
function toGateway(s, configName) {
|
|
11438
|
+
const upstream = mcpUpstreamString(s);
|
|
11439
|
+
const nameArgs = configName && !configName.startsWith("-") ? ["--config-name", configName] : [];
|
|
11440
|
+
return {
|
|
11441
|
+
...s,
|
|
11442
|
+
command: "node9",
|
|
11443
|
+
args: ["mcp-gateway", ...nameArgs, "--upstream", upstream]
|
|
11444
|
+
};
|
|
11445
|
+
}
|
|
11446
|
+
function fromGateway(s) {
|
|
11447
|
+
if (!isNode9Command(s.command) || (s.args ?? [])[0] !== "mcp-gateway") return null;
|
|
11448
|
+
const args = s.args ?? [];
|
|
11449
|
+
const i = args.indexOf("--upstream");
|
|
11450
|
+
if (i < 0 || !args[i + 1]) return null;
|
|
11451
|
+
const [command, ...rest] = tokenize4(args[i + 1]);
|
|
11452
|
+
if (!command) return null;
|
|
11453
|
+
return { ...s, command, args: rest };
|
|
11454
|
+
}
|
|
11455
|
+
function inventoryMcp(home = os15.homedir()) {
|
|
11456
|
+
const out = [];
|
|
11457
|
+
for (const spec of AGENT_SPECS) {
|
|
11458
|
+
if (!spec.mcpFile) continue;
|
|
11459
|
+
const mcpFile = spec.mcpFile(home);
|
|
11460
|
+
const format = spec.mcpFormat ?? "json";
|
|
11461
|
+
const servers = readMcpServers(mcpFile, format);
|
|
11462
|
+
for (const [name, s] of Object.entries(servers)) {
|
|
11463
|
+
if (!s || typeof s !== "object") continue;
|
|
11464
|
+
out.push({
|
|
11465
|
+
agent: String(spec.id),
|
|
11466
|
+
agentLabel: spec.label,
|
|
11467
|
+
mcpFile,
|
|
11468
|
+
format,
|
|
11469
|
+
name,
|
|
11470
|
+
command: s.command ?? "",
|
|
11471
|
+
args: Array.isArray(s.args) ? s.args : [],
|
|
11472
|
+
state: classifyMcp(s),
|
|
11473
|
+
raw: s
|
|
11474
|
+
});
|
|
11475
|
+
}
|
|
11476
|
+
}
|
|
11477
|
+
return out;
|
|
11478
|
+
}
|
|
11479
|
+
function isCorruptedUpstream(upstream) {
|
|
11480
|
+
if (!upstream) return false;
|
|
11481
|
+
if (upstream.includes("\\") || upstream.includes("/")) return false;
|
|
11482
|
+
return /(^|\s)[A-Za-z]:[^\s]/.test(upstream);
|
|
11483
|
+
}
|
|
11484
|
+
function findCorruptedMcpWraps(home = os15.homedir()) {
|
|
11485
|
+
const out = [];
|
|
11486
|
+
for (const e of inventoryMcp(home)) {
|
|
11487
|
+
if (e.state !== "gatewayed") continue;
|
|
11488
|
+
const i = e.args.indexOf("--upstream");
|
|
11489
|
+
const upstream = i >= 0 ? e.args[i + 1] ?? "" : "";
|
|
11490
|
+
if (!isCorruptedUpstream(upstream)) continue;
|
|
11491
|
+
out.push({
|
|
11492
|
+
agent: e.agent,
|
|
11493
|
+
agentLabel: e.agentLabel,
|
|
11494
|
+
mcpFile: e.mcpFile,
|
|
11495
|
+
name: e.name,
|
|
11496
|
+
upstream
|
|
11497
|
+
});
|
|
11498
|
+
}
|
|
11499
|
+
return out;
|
|
11500
|
+
}
|
|
11501
|
+
function inventoryServerKeys(inv) {
|
|
11502
|
+
const keys = /* @__PURE__ */ new Set();
|
|
11503
|
+
for (const e of inv) {
|
|
11504
|
+
if (e.state === "gatewayed") {
|
|
11505
|
+
const i = e.args.indexOf("--upstream");
|
|
11506
|
+
if (i >= 0 && e.args[i + 1]) {
|
|
11507
|
+
keys.add(getServerKey(e.args[i + 1]));
|
|
11508
|
+
}
|
|
11509
|
+
} else if (e.state === "ungoverned") {
|
|
11510
|
+
const cmd = [e.command, ...e.args].map(quoteArg).join(" ");
|
|
11511
|
+
keys.add(getServerKey(cmd));
|
|
11512
|
+
}
|
|
11513
|
+
}
|
|
11514
|
+
return keys;
|
|
11515
|
+
}
|
|
11516
|
+
function writeMcpEntry(mcpFile, format, name, entry) {
|
|
11517
|
+
const key = format === "toml" ? "mcp_servers" : "mcpServers";
|
|
11518
|
+
let root = {};
|
|
11519
|
+
if (fs18.existsSync(mcpFile)) {
|
|
11520
|
+
const raw = fs18.readFileSync(mcpFile, "utf-8");
|
|
11521
|
+
root = format === "toml" ? parseToml2(raw) : JSON.parse(raw);
|
|
11522
|
+
const bak = `${mcpFile}.node9-bak`;
|
|
11523
|
+
try {
|
|
11524
|
+
fs18.writeFileSync(bak, raw, { mode: 384, flag: "wx" });
|
|
11525
|
+
} catch (e) {
|
|
11526
|
+
if (e.code !== "EEXIST") throw e;
|
|
11527
|
+
}
|
|
11528
|
+
}
|
|
11529
|
+
const existing = root[key];
|
|
11530
|
+
const servers = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
11531
|
+
servers[name] = entry;
|
|
11532
|
+
root[key] = servers;
|
|
11533
|
+
const serialized = format === "toml" ? stringifyToml(root) : JSON.stringify(root, null, 2);
|
|
11534
|
+
const tmp = `${mcpFile}.${process.pid}.tmp`;
|
|
11535
|
+
fs18.writeFileSync(tmp, serialized, { mode: 384 });
|
|
11536
|
+
fs18.renameSync(tmp, mcpFile);
|
|
11537
|
+
}
|
|
11538
|
+
var init_mcp_wrap = __esm({
|
|
11539
|
+
"src/mcp-wrap.ts"() {
|
|
11540
|
+
"use strict";
|
|
11541
|
+
init_agent_wiring();
|
|
11542
|
+
init_mcp_cmd();
|
|
11543
|
+
init_mcp_pin();
|
|
11544
|
+
init_mcp_cmd();
|
|
11545
|
+
}
|
|
11546
|
+
});
|
|
11547
|
+
|
|
11548
|
+
// src/utils/platform-shell.ts
|
|
11549
|
+
function locatorCommand() {
|
|
11550
|
+
return process.platform === "win32" ? "where" : "which";
|
|
11551
|
+
}
|
|
11552
|
+
function shellInvocation(command) {
|
|
11553
|
+
if (process.platform === "win32") {
|
|
11554
|
+
return {
|
|
11555
|
+
file: process.env.ComSpec || "cmd.exe",
|
|
11556
|
+
args: ["/d", "/s", "/c", command]
|
|
11557
|
+
};
|
|
11558
|
+
}
|
|
11559
|
+
return { file: "/bin/bash", args: ["-c", command] };
|
|
11560
|
+
}
|
|
11561
|
+
var init_platform_shell = __esm({
|
|
11562
|
+
"src/utils/platform-shell.ts"() {
|
|
11563
|
+
"use strict";
|
|
11564
|
+
}
|
|
11565
|
+
});
|
|
11566
|
+
|
|
11567
|
+
// src/codex-trust.ts
|
|
11568
|
+
import fs19 from "fs";
|
|
11569
|
+
import path19 from "path";
|
|
11570
|
+
import os16 from "os";
|
|
11571
|
+
import { spawnSync } from "child_process";
|
|
11572
|
+
import { parse as parseToml3 } from "smol-toml";
|
|
11573
|
+
function readTomlSafe(filePath) {
|
|
11574
|
+
try {
|
|
11575
|
+
return parseToml3(fs19.readFileSync(filePath, "utf-8"));
|
|
11576
|
+
} catch {
|
|
11577
|
+
return null;
|
|
11578
|
+
}
|
|
11579
|
+
}
|
|
11580
|
+
function lastCodexAuditTs(auditLogPath) {
|
|
11581
|
+
let text;
|
|
11582
|
+
try {
|
|
11583
|
+
text = fs19.readFileSync(auditLogPath, "utf-8");
|
|
11584
|
+
} catch {
|
|
11585
|
+
return null;
|
|
11586
|
+
}
|
|
11587
|
+
let newest = null;
|
|
11588
|
+
for (const line of text.split("\n")) {
|
|
11589
|
+
if (!line.includes('"Codex"')) continue;
|
|
11590
|
+
try {
|
|
11591
|
+
const row = JSON.parse(line);
|
|
11592
|
+
if (row.agent !== "Codex" || typeof row.ts !== "string") continue;
|
|
11593
|
+
if (newest === null || row.ts > newest) newest = row.ts;
|
|
11594
|
+
} catch {
|
|
11595
|
+
}
|
|
11596
|
+
}
|
|
11597
|
+
return newest;
|
|
11598
|
+
}
|
|
11599
|
+
function assessCodexTrustFrom(input) {
|
|
11600
|
+
const { hooksDisabled, trustEntries, hooksWrittenAt, lastCodexActivityAt } = input;
|
|
11601
|
+
let state;
|
|
11602
|
+
if (hooksDisabled) state = "disabled";
|
|
11603
|
+
else if (trustEntries === 0) state = "never-trusted";
|
|
11604
|
+
else if (hooksWrittenAt && lastCodexActivityAt && lastCodexActivityAt > hooksWrittenAt) {
|
|
11605
|
+
state = "observed";
|
|
11606
|
+
} else state = "unverified";
|
|
11607
|
+
return { state, hooksWrittenAt, lastCodexActivityAt, trustEntries };
|
|
11608
|
+
}
|
|
11609
|
+
function assessCodexTrust(home = os16.homedir(), auditLogPath = path19.join(home, ".node9", "audit.log")) {
|
|
11610
|
+
const hooksPath = path19.join(home, ".codex", "hooks.json");
|
|
11611
|
+
const configPath = path19.join(home, ".codex", "config.toml");
|
|
11612
|
+
let hooksWrittenAt = null;
|
|
11613
|
+
try {
|
|
11614
|
+
hooksWrittenAt = fs19.statSync(hooksPath).mtime.toISOString();
|
|
11615
|
+
} catch {
|
|
11616
|
+
hooksWrittenAt = null;
|
|
11617
|
+
}
|
|
11618
|
+
const config = readTomlSafe(configPath);
|
|
11619
|
+
const hooksDisabled = config?.features?.hooks === false || config?.codex_hooks === false;
|
|
11620
|
+
const trustEntries = Object.keys(config?.hooks?.state ?? {}).length;
|
|
11621
|
+
return assessCodexTrustFrom({
|
|
11622
|
+
hooksDisabled,
|
|
11623
|
+
trustEntries,
|
|
11624
|
+
hooksWrittenAt,
|
|
11625
|
+
lastCodexActivityAt: lastCodexAuditTs(auditLogPath)
|
|
11626
|
+
});
|
|
11627
|
+
}
|
|
11628
|
+
function findCodexTui(env = process.env) {
|
|
11629
|
+
try {
|
|
11630
|
+
const r = spawnSync(locatorCommand(), ["codex"], { encoding: "utf-8", timeout: 3e3 });
|
|
11631
|
+
const first = (r.stdout ?? "").split(/\r?\n/).find((l) => l.trim());
|
|
11632
|
+
if (r.status === 0 && first) return "codex";
|
|
11633
|
+
} catch {
|
|
11634
|
+
}
|
|
11635
|
+
if (process.platform === "win32" && env.LOCALAPPDATA) {
|
|
11636
|
+
const binDir = path19.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin");
|
|
11637
|
+
try {
|
|
11638
|
+
for (const d of fs19.readdirSync(binDir)) {
|
|
11639
|
+
const exe = path19.join(binDir, d, "codex.exe");
|
|
11640
|
+
if (fs19.existsSync(exe)) return `"${exe}"`;
|
|
11641
|
+
}
|
|
11642
|
+
} catch {
|
|
11643
|
+
}
|
|
11644
|
+
}
|
|
11645
|
+
return null;
|
|
11646
|
+
}
|
|
11647
|
+
function codexTrustInstruction(tui = findCodexTui()) {
|
|
11648
|
+
const where = tui ? `run ${tui} in a terminal` : "run the Codex CLI (`codex`) in a terminal \u2014 the desktop app has no hook review screen";
|
|
11649
|
+
return ` \u279C Codex must trust these hooks once: ${where},
|
|
11650
|
+
then choose "Trust all and continue" when Codex asks to review them.
|
|
11651
|
+
Until then Codex runs unprotected. Every rewrite of hooks.json needs this again.`;
|
|
11652
|
+
}
|
|
11653
|
+
var init_codex_trust = __esm({
|
|
11654
|
+
"src/codex-trust.ts"() {
|
|
11655
|
+
"use strict";
|
|
11656
|
+
init_platform_shell();
|
|
11657
|
+
}
|
|
11658
|
+
});
|
|
11659
|
+
|
|
11660
|
+
// src/daemon/hook-baseline.ts
|
|
11661
|
+
import fs20 from "fs";
|
|
11662
|
+
import path20 from "path";
|
|
11663
|
+
import os17 from "os";
|
|
10986
11664
|
function loadHookBaseline() {
|
|
10987
11665
|
try {
|
|
10988
|
-
const raw = JSON.parse(
|
|
11666
|
+
const raw = JSON.parse(fs20.readFileSync(BASELINE_FILE, "utf-8"));
|
|
10989
11667
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
10990
11668
|
} catch {
|
|
10991
11669
|
return {};
|
|
@@ -10993,9 +11671,9 @@ function loadHookBaseline() {
|
|
|
10993
11671
|
}
|
|
10994
11672
|
function saveHookBaseline(b) {
|
|
10995
11673
|
try {
|
|
10996
|
-
const dir =
|
|
10997
|
-
if (!
|
|
10998
|
-
|
|
11674
|
+
const dir = path20.dirname(BASELINE_FILE);
|
|
11675
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
11676
|
+
fs20.writeFileSync(BASELINE_FILE, JSON.stringify(b, null, 2), { mode: 384 });
|
|
10999
11677
|
} catch {
|
|
11000
11678
|
}
|
|
11001
11679
|
}
|
|
@@ -11017,14 +11695,14 @@ function seedHookBaselineIfEmpty(governedNow, now) {
|
|
|
11017
11695
|
function clearHookBaseline() {
|
|
11018
11696
|
for (const f of [BASELINE_FILE, NOTIFIED_FILE]) {
|
|
11019
11697
|
try {
|
|
11020
|
-
|
|
11698
|
+
fs20.rmSync(f, { force: true });
|
|
11021
11699
|
} catch {
|
|
11022
11700
|
}
|
|
11023
11701
|
}
|
|
11024
11702
|
}
|
|
11025
11703
|
function loadNotified() {
|
|
11026
11704
|
try {
|
|
11027
|
-
const raw = JSON.parse(
|
|
11705
|
+
const raw = JSON.parse(fs20.readFileSync(NOTIFIED_FILE, "utf-8"));
|
|
11028
11706
|
return new Set(Array.isArray(raw) ? raw : []);
|
|
11029
11707
|
} catch {
|
|
11030
11708
|
return /* @__PURE__ */ new Set();
|
|
@@ -11032,9 +11710,9 @@ function loadNotified() {
|
|
|
11032
11710
|
}
|
|
11033
11711
|
function saveNotified(s) {
|
|
11034
11712
|
try {
|
|
11035
|
-
const dir =
|
|
11036
|
-
if (!
|
|
11037
|
-
|
|
11713
|
+
const dir = path20.dirname(NOTIFIED_FILE);
|
|
11714
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
11715
|
+
fs20.writeFileSync(NOTIFIED_FILE, JSON.stringify([...s]), { mode: 384 });
|
|
11038
11716
|
} catch {
|
|
11039
11717
|
}
|
|
11040
11718
|
}
|
|
@@ -11042,8 +11720,8 @@ var BASELINE_FILE, NOTIFIED_FILE;
|
|
|
11042
11720
|
var init_hook_baseline = __esm({
|
|
11043
11721
|
"src/daemon/hook-baseline.ts"() {
|
|
11044
11722
|
"use strict";
|
|
11045
|
-
BASELINE_FILE =
|
|
11046
|
-
NOTIFIED_FILE =
|
|
11723
|
+
BASELINE_FILE = path20.join(os17.homedir(), ".node9", "hooks-baseline.json");
|
|
11724
|
+
NOTIFIED_FILE = path20.join(os17.homedir(), ".node9", "hook-heal-notified.json");
|
|
11047
11725
|
}
|
|
11048
11726
|
});
|
|
11049
11727
|
|
|
@@ -11396,13 +12074,13 @@ var init_setup_pi_shim = __esm({
|
|
|
11396
12074
|
});
|
|
11397
12075
|
|
|
11398
12076
|
// src/setup.ts
|
|
11399
|
-
import
|
|
11400
|
-
import
|
|
11401
|
-
import
|
|
12077
|
+
import fs21 from "fs";
|
|
12078
|
+
import path21 from "path";
|
|
12079
|
+
import os18 from "os";
|
|
11402
12080
|
import chalk from "chalk";
|
|
11403
12081
|
import { confirm as rawConfirm } from "@inquirer/prompts";
|
|
11404
|
-
import { parse as
|
|
11405
|
-
import * as
|
|
12082
|
+
import { parse as parseToml4, stringify as stringifyToml2 } from "smol-toml";
|
|
12083
|
+
import * as yaml2 from "yaml";
|
|
11406
12084
|
function isNonInteractive() {
|
|
11407
12085
|
return process.env.NODE9_NONINTERACTIVE === "1";
|
|
11408
12086
|
}
|
|
@@ -11458,7 +12136,7 @@ function printInlineAskNotice() {
|
|
|
11458
12136
|
)
|
|
11459
12137
|
);
|
|
11460
12138
|
}
|
|
11461
|
-
function fullPathCommand(subcommand, platform = process.platform, home =
|
|
12139
|
+
function fullPathCommand(subcommand, platform = process.platform, home = os18.homedir()) {
|
|
11462
12140
|
if (process.env.NODE9_TESTING === "1") return `node9 ${subcommand}`;
|
|
11463
12141
|
const nodeExec = toForwardSlashes(process.execPath);
|
|
11464
12142
|
const cliScript = toForwardSlashes(process.argv[1]);
|
|
@@ -11470,8 +12148,8 @@ function fullPathCommand(subcommand, platform = process.platform, home = os16.ho
|
|
|
11470
12148
|
ensureHookShim(home, nodeExec, cliScript);
|
|
11471
12149
|
return `"${toForwardSlashes(hookShimPath(home))}" ${subcommand}`;
|
|
11472
12150
|
}
|
|
11473
|
-
function hookShimPath(home =
|
|
11474
|
-
return
|
|
12151
|
+
function hookShimPath(home = os18.homedir()) {
|
|
12152
|
+
return path21.join(home, ".node9", "bin", "hook");
|
|
11475
12153
|
}
|
|
11476
12154
|
function hookShimBody(nodeExec, cliScript) {
|
|
11477
12155
|
return `#!/bin/sh
|
|
@@ -11485,12 +12163,12 @@ function ensureHookShim(home, nodeExec, cliScript) {
|
|
|
11485
12163
|
const shim = hookShimPath(home);
|
|
11486
12164
|
const body = hookShimBody(nodeExec, cliScript);
|
|
11487
12165
|
try {
|
|
11488
|
-
if (
|
|
12166
|
+
if (fs21.readFileSync(shim, "utf-8") === body) return false;
|
|
11489
12167
|
} catch {
|
|
11490
12168
|
}
|
|
11491
|
-
|
|
11492
|
-
|
|
11493
|
-
|
|
12169
|
+
fs21.mkdirSync(path21.dirname(shim), { recursive: true });
|
|
12170
|
+
fs21.writeFileSync(shim, body, { mode: 493 });
|
|
12171
|
+
fs21.chmodSync(shim, 493);
|
|
11494
12172
|
return true;
|
|
11495
12173
|
}
|
|
11496
12174
|
function toForwardSlashes(p) {
|
|
@@ -11509,7 +12187,7 @@ function isStaleHookCommand(command) {
|
|
|
11509
12187
|
while ((m = re.exec(command)) !== null) tokens.push(m[1] ?? m[2] ?? "");
|
|
11510
12188
|
for (const tok of tokens) {
|
|
11511
12189
|
if (!tok.startsWith("/") && !/^[A-Za-z]:\//.test(tok)) continue;
|
|
11512
|
-
if (!
|
|
12190
|
+
if (!fs21.existsSync(tok)) return true;
|
|
11513
12191
|
}
|
|
11514
12192
|
return false;
|
|
11515
12193
|
}
|
|
@@ -11529,19 +12207,19 @@ function isChurnProneHookForm(command, platform = process.platform) {
|
|
|
11529
12207
|
function needsRewrite(command, platform = process.platform) {
|
|
11530
12208
|
return isStaleHookCommand(command) || isLegacyHookFormat(command) || isWindowsQuoteBrokenHook(command, platform) || isChurnProneHookForm(command, platform);
|
|
11531
12209
|
}
|
|
11532
|
-
function
|
|
12210
|
+
function readJson2(filePath) {
|
|
11533
12211
|
try {
|
|
11534
|
-
if (
|
|
11535
|
-
return JSON.parse(
|
|
12212
|
+
if (fs21.existsSync(filePath)) {
|
|
12213
|
+
return JSON.parse(fs21.readFileSync(filePath, "utf-8"));
|
|
11536
12214
|
}
|
|
11537
12215
|
} catch {
|
|
11538
12216
|
}
|
|
11539
12217
|
return null;
|
|
11540
12218
|
}
|
|
11541
12219
|
function writeJson(filePath, data) {
|
|
11542
|
-
const dir =
|
|
11543
|
-
if (!
|
|
11544
|
-
|
|
12220
|
+
const dir = path21.dirname(filePath);
|
|
12221
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
12222
|
+
fs21.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
11545
12223
|
}
|
|
11546
12224
|
function mcpWrapArgs(upstream) {
|
|
11547
12225
|
return [MCP_WRAP_SUBCOMMAND, "--upstream", upstream];
|
|
@@ -11561,6 +12239,14 @@ function repairLegacyMcpWraps(servers) {
|
|
|
11561
12239
|
}
|
|
11562
12240
|
return repaired;
|
|
11563
12241
|
}
|
|
12242
|
+
function isCodexAppManagedServer(name, server) {
|
|
12243
|
+
if (!server) return false;
|
|
12244
|
+
const cmd = (server.command ?? "").replace(/\\/g, "/").toLowerCase();
|
|
12245
|
+
if (cmd.includes("/openai/codex/runtimes/")) return true;
|
|
12246
|
+
const envKeys = Object.keys(server.env ?? {});
|
|
12247
|
+
if (envKeys.some((k) => k.startsWith("NODE_REPL_") || k === "CODEX_CLI_PATH")) return true;
|
|
12248
|
+
return name === "node_repl";
|
|
12249
|
+
}
|
|
11564
12250
|
function isNode9Hook(cmd) {
|
|
11565
12251
|
if (!cmd) return false;
|
|
11566
12252
|
return /(?:^|[\s/\\"])node9"? (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js"? (?:check|log)/.test(cmd) || // The shim form: "<home>/.node9/bin/hook" check. Without this alternative a
|
|
@@ -11570,11 +12256,11 @@ function isNode9Hook(cmd) {
|
|
|
11570
12256
|
/[/\\]\.node9[/\\]bin[/\\]hook"? (?:check|log)/.test(cmd);
|
|
11571
12257
|
}
|
|
11572
12258
|
function teardownClaude() {
|
|
11573
|
-
const homeDir2 =
|
|
11574
|
-
const hooksPath =
|
|
11575
|
-
const mcpPath =
|
|
12259
|
+
const homeDir2 = os18.homedir();
|
|
12260
|
+
const hooksPath = path21.join(homeDir2, ".claude", "settings.json");
|
|
12261
|
+
const mcpPath = path21.join(homeDir2, ".claude", ".mcp.json");
|
|
11576
12262
|
let changed = false;
|
|
11577
|
-
const settings =
|
|
12263
|
+
const settings = readJson2(hooksPath);
|
|
11578
12264
|
if (settings?.hooks) {
|
|
11579
12265
|
for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
11580
12266
|
const before = settings.hooks[event]?.length ?? 0;
|
|
@@ -11593,7 +12279,7 @@ function teardownClaude() {
|
|
|
11593
12279
|
console.log(chalk.blue(" \u2139\uFE0F No Node9 hooks found in ~/.claude/settings.json"));
|
|
11594
12280
|
}
|
|
11595
12281
|
}
|
|
11596
|
-
const claudeConfig =
|
|
12282
|
+
const claudeConfig = readJson2(mcpPath);
|
|
11597
12283
|
if (claudeConfig?.mcpServers) {
|
|
11598
12284
|
let mcpChanged = false;
|
|
11599
12285
|
if (removeNode9McpServer(claudeConfig.mcpServers)) {
|
|
@@ -11620,9 +12306,9 @@ function teardownClaude() {
|
|
|
11620
12306
|
}
|
|
11621
12307
|
}
|
|
11622
12308
|
function teardownGemini() {
|
|
11623
|
-
const homeDir2 =
|
|
11624
|
-
const settingsPath =
|
|
11625
|
-
const settings =
|
|
12309
|
+
const homeDir2 = os18.homedir();
|
|
12310
|
+
const settingsPath = path21.join(homeDir2, ".gemini", "settings.json");
|
|
12311
|
+
const settings = readJson2(settingsPath);
|
|
11626
12312
|
if (!settings) {
|
|
11627
12313
|
console.log(chalk.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
|
|
11628
12314
|
return;
|
|
@@ -11664,9 +12350,9 @@ function teardownGemini() {
|
|
|
11664
12350
|
}
|
|
11665
12351
|
}
|
|
11666
12352
|
function teardownCursor() {
|
|
11667
|
-
const homeDir2 =
|
|
11668
|
-
const mcpPath =
|
|
11669
|
-
const mcpConfig =
|
|
12353
|
+
const homeDir2 = os18.homedir();
|
|
12354
|
+
const mcpPath = path21.join(homeDir2, ".cursor", "mcp.json");
|
|
12355
|
+
const mcpConfig = readJson2(mcpPath);
|
|
11670
12356
|
if (!mcpConfig?.mcpServers) {
|
|
11671
12357
|
console.log(chalk.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
|
|
11672
12358
|
return;
|
|
@@ -11697,11 +12383,11 @@ function teardownCursor() {
|
|
|
11697
12383
|
}
|
|
11698
12384
|
async function setupClaude() {
|
|
11699
12385
|
seedMcpPinsIfMissing();
|
|
11700
|
-
const homeDir2 =
|
|
11701
|
-
const mcpPath =
|
|
11702
|
-
const hooksPath =
|
|
11703
|
-
const claudeConfig =
|
|
11704
|
-
const settings =
|
|
12386
|
+
const homeDir2 = os18.homedir();
|
|
12387
|
+
const mcpPath = path21.join(homeDir2, ".claude", ".mcp.json");
|
|
12388
|
+
const hooksPath = path21.join(homeDir2, ".claude", "settings.json");
|
|
12389
|
+
const claudeConfig = readJson2(mcpPath) ?? {};
|
|
12390
|
+
const settings = readJson2(hooksPath) ?? {};
|
|
11705
12391
|
const servers = claudeConfig.mcpServers ?? {};
|
|
11706
12392
|
let hooksChanged = false;
|
|
11707
12393
|
let anythingChanged = false;
|
|
@@ -11822,7 +12508,7 @@ async function setupClaude() {
|
|
|
11822
12508
|
const serversToWrap = [];
|
|
11823
12509
|
for (const [name, server] of Object.entries(servers)) {
|
|
11824
12510
|
if (!server.command || server.command === "node9") continue;
|
|
11825
|
-
const upstream =
|
|
12511
|
+
const upstream = mcpUpstreamString(server);
|
|
11826
12512
|
serversToWrap.push({ name, upstream });
|
|
11827
12513
|
}
|
|
11828
12514
|
if (serversToWrap.length > 0) {
|
|
@@ -11875,9 +12561,9 @@ async function setupGemini() {
|
|
|
11875
12561
|
);
|
|
11876
12562
|
console.log("");
|
|
11877
12563
|
seedMcpPinsIfMissing();
|
|
11878
|
-
const homeDir2 =
|
|
11879
|
-
const settingsPath =
|
|
11880
|
-
const settings =
|
|
12564
|
+
const homeDir2 = os18.homedir();
|
|
12565
|
+
const settingsPath = path21.join(homeDir2, ".gemini", "settings.json");
|
|
12566
|
+
const settings = readJson2(settingsPath) ?? {};
|
|
11881
12567
|
const servers = settings.mcpServers ?? {};
|
|
11882
12568
|
let hooksChanged = false;
|
|
11883
12569
|
let anythingChanged = false;
|
|
@@ -11944,7 +12630,7 @@ async function setupGemini() {
|
|
|
11944
12630
|
const serversToWrap = [];
|
|
11945
12631
|
for (const [name, server] of Object.entries(servers)) {
|
|
11946
12632
|
if (!server.command || server.command === "node9") continue;
|
|
11947
|
-
const upstream =
|
|
12633
|
+
const upstream = mcpUpstreamString(server);
|
|
11948
12634
|
serversToWrap.push({ name, upstream });
|
|
11949
12635
|
}
|
|
11950
12636
|
if (serversToWrap.length > 0) {
|
|
@@ -11990,11 +12676,11 @@ async function setupGemini() {
|
|
|
11990
12676
|
}
|
|
11991
12677
|
async function setupAntigravity() {
|
|
11992
12678
|
seedMcpPinsIfMissing();
|
|
11993
|
-
const homeDir2 =
|
|
11994
|
-
const hooksPath =
|
|
11995
|
-
const mcpPath =
|
|
11996
|
-
const hooksFile =
|
|
11997
|
-
const mcpConfig =
|
|
12679
|
+
const homeDir2 = os18.homedir();
|
|
12680
|
+
const hooksPath = path21.join(homeDir2, ".gemini", "config", "hooks.json");
|
|
12681
|
+
const mcpPath = path21.join(homeDir2, ".gemini", "config", "mcp_config.json");
|
|
12682
|
+
const hooksFile = readJson2(hooksPath) ?? {};
|
|
12683
|
+
const mcpConfig = readJson2(mcpPath) ?? {};
|
|
11998
12684
|
const servers = mcpConfig.mcpServers ?? {};
|
|
11999
12685
|
let hooksChanged = false;
|
|
12000
12686
|
let anythingChanged = false;
|
|
@@ -12087,7 +12773,7 @@ async function setupAntigravity() {
|
|
|
12087
12773
|
}
|
|
12088
12774
|
anythingChanged = true;
|
|
12089
12775
|
}
|
|
12090
|
-
const legacySettings =
|
|
12776
|
+
const legacySettings = readJson2(path21.join(homeDir2, ".gemini", "settings.json"));
|
|
12091
12777
|
const legacyHasNode9 = ["BeforeTool", "AfterTool"].some(
|
|
12092
12778
|
(ev) => legacySettings?.hooks?.[ev]?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)))
|
|
12093
12779
|
);
|
|
@@ -12158,11 +12844,11 @@ async function setupAntigravity() {
|
|
|
12158
12844
|
}
|
|
12159
12845
|
}
|
|
12160
12846
|
function teardownAntigravity() {
|
|
12161
|
-
const homeDir2 =
|
|
12162
|
-
const hooksPath =
|
|
12163
|
-
const mcpPath =
|
|
12847
|
+
const homeDir2 = os18.homedir();
|
|
12848
|
+
const hooksPath = path21.join(homeDir2, ".gemini", "config", "hooks.json");
|
|
12849
|
+
const mcpPath = path21.join(homeDir2, ".gemini", "config", "mcp_config.json");
|
|
12164
12850
|
let changed = false;
|
|
12165
|
-
const hooksFile =
|
|
12851
|
+
const hooksFile = readJson2(hooksPath);
|
|
12166
12852
|
if (hooksFile?.hooks) {
|
|
12167
12853
|
for (const event of ["PreToolUse", "PostToolUse"]) {
|
|
12168
12854
|
const before = hooksFile.hooks[event]?.length ?? 0;
|
|
@@ -12183,7 +12869,7 @@ function teardownAntigravity() {
|
|
|
12183
12869
|
} else {
|
|
12184
12870
|
console.log(chalk.blue(" \u2139\uFE0F ~/.gemini/config/hooks.json not found \u2014 nothing to remove"));
|
|
12185
12871
|
}
|
|
12186
|
-
const mcpConfig =
|
|
12872
|
+
const mcpConfig = readJson2(mcpPath);
|
|
12187
12873
|
if (mcpConfig?.mcpServers) {
|
|
12188
12874
|
let mcpChanged = false;
|
|
12189
12875
|
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
@@ -12212,13 +12898,13 @@ function teardownAntigravity() {
|
|
|
12212
12898
|
}
|
|
12213
12899
|
async function setupCopilot() {
|
|
12214
12900
|
seedMcpPinsIfMissing();
|
|
12215
|
-
const homeDir2 =
|
|
12216
|
-
const hooksPath =
|
|
12217
|
-
const mcpPath =
|
|
12218
|
-
const hooksFile =
|
|
12901
|
+
const homeDir2 = os18.homedir();
|
|
12902
|
+
const hooksPath = path21.join(homeDir2, ".copilot", "hooks", "node9.json");
|
|
12903
|
+
const mcpPath = path21.join(homeDir2, ".copilot", "mcp-config.json");
|
|
12904
|
+
const hooksFile = readJson2(hooksPath) ?? { version: 1 };
|
|
12219
12905
|
if (!hooksFile.version) hooksFile.version = 1;
|
|
12220
12906
|
if (!hooksFile.hooks) hooksFile.hooks = {};
|
|
12221
|
-
const mcpConfig =
|
|
12907
|
+
const mcpConfig = readJson2(mcpPath) ?? {};
|
|
12222
12908
|
const servers = mcpConfig.mcpServers ?? {};
|
|
12223
12909
|
let hooksChanged = false;
|
|
12224
12910
|
let anythingChanged = false;
|
|
@@ -12315,10 +13001,10 @@ async function setupCopilot() {
|
|
|
12315
13001
|
printInlineAskNotice();
|
|
12316
13002
|
}
|
|
12317
13003
|
function teardownCopilot() {
|
|
12318
|
-
const homeDir2 =
|
|
12319
|
-
const hooksPath =
|
|
12320
|
-
const mcpPath =
|
|
12321
|
-
const hooksFile =
|
|
13004
|
+
const homeDir2 = os18.homedir();
|
|
13005
|
+
const hooksPath = path21.join(homeDir2, ".copilot", "hooks", "node9.json");
|
|
13006
|
+
const mcpPath = path21.join(homeDir2, ".copilot", "mcp-config.json");
|
|
13007
|
+
const hooksFile = readJson2(hooksPath);
|
|
12322
13008
|
let changed = false;
|
|
12323
13009
|
if (hooksFile?.hooks) {
|
|
12324
13010
|
for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
@@ -12330,7 +13016,7 @@ function teardownCopilot() {
|
|
|
12330
13016
|
if (changed) {
|
|
12331
13017
|
if (Object.keys(hooksFile.hooks).length === 0) {
|
|
12332
13018
|
try {
|
|
12333
|
-
|
|
13019
|
+
fs21.unlinkSync(hooksPath);
|
|
12334
13020
|
console.log(chalk.green(" \u2705 Removed ~/.copilot/hooks/node9.json"));
|
|
12335
13021
|
} catch {
|
|
12336
13022
|
writeJson(hooksPath, hooksFile);
|
|
@@ -12345,7 +13031,7 @@ function teardownCopilot() {
|
|
|
12345
13031
|
} else {
|
|
12346
13032
|
console.log(chalk.blue(" \u2139\uFE0F ~/.copilot/hooks/node9.json not found \u2014 nothing to remove"));
|
|
12347
13033
|
}
|
|
12348
|
-
const mcpConfig =
|
|
13034
|
+
const mcpConfig = readJson2(mcpPath);
|
|
12349
13035
|
if (mcpConfig?.mcpServers) {
|
|
12350
13036
|
let mcpChanged = false;
|
|
12351
13037
|
if (removeNode9McpServer(mcpConfig.mcpServers)) {
|
|
@@ -12372,9 +13058,9 @@ function teardownCopilot() {
|
|
|
12372
13058
|
}
|
|
12373
13059
|
}
|
|
12374
13060
|
}
|
|
12375
|
-
function claudeDesktopConfigPath(homeDir2 =
|
|
13061
|
+
function claudeDesktopConfigPath(homeDir2 = os18.homedir()) {
|
|
12376
13062
|
if (process.platform === "darwin") {
|
|
12377
|
-
return
|
|
13063
|
+
return path21.join(
|
|
12378
13064
|
homeDir2,
|
|
12379
13065
|
"Library",
|
|
12380
13066
|
"Application Support",
|
|
@@ -12383,47 +13069,47 @@ function claudeDesktopConfigPath(homeDir2 = os16.homedir()) {
|
|
|
12383
13069
|
);
|
|
12384
13070
|
}
|
|
12385
13071
|
if (process.platform === "linux") {
|
|
12386
|
-
return
|
|
13072
|
+
return path21.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
|
|
12387
13073
|
}
|
|
12388
13074
|
if (process.platform === "win32") {
|
|
12389
|
-
const appData = process.env.APPDATA ||
|
|
12390
|
-
return
|
|
13075
|
+
const appData = process.env.APPDATA || path21.join(homeDir2, "AppData", "Roaming");
|
|
13076
|
+
return path21.join(appData, "Claude", "claude_desktop_config.json");
|
|
12391
13077
|
}
|
|
12392
13078
|
return null;
|
|
12393
13079
|
}
|
|
12394
|
-
function opencodeConfigDir(home =
|
|
13080
|
+
function opencodeConfigDir(home = os18.homedir()) {
|
|
12395
13081
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
12396
|
-
const base = xdg &&
|
|
12397
|
-
return
|
|
13082
|
+
const base = xdg && path21.isAbsolute(xdg) ? xdg : path21.join(home, ".config");
|
|
13083
|
+
return path21.join(base, "opencode");
|
|
12398
13084
|
}
|
|
12399
13085
|
function commonBinDirs(home) {
|
|
12400
13086
|
return [
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
13087
|
+
path21.join(home, ".local", "bin"),
|
|
13088
|
+
path21.join(home, ".bun", "bin"),
|
|
13089
|
+
path21.join(home, ".npm-global", "bin"),
|
|
12404
13090
|
"/usr/local/bin",
|
|
12405
13091
|
"/opt/homebrew/bin"
|
|
12406
13092
|
];
|
|
12407
13093
|
}
|
|
12408
|
-
function binaryInPath(binary, home =
|
|
13094
|
+
function binaryInPath(binary, home = os18.homedir()) {
|
|
12409
13095
|
const pathEnv = process.env.PATH ?? "";
|
|
12410
|
-
const dirs = [...pathEnv.split(
|
|
13096
|
+
const dirs = [...pathEnv.split(path21.delimiter), ...commonBinDirs(home)];
|
|
12411
13097
|
const seen = /* @__PURE__ */ new Set();
|
|
12412
13098
|
for (const dir of dirs) {
|
|
12413
13099
|
if (!dir || seen.has(dir)) continue;
|
|
12414
13100
|
seen.add(dir);
|
|
12415
13101
|
try {
|
|
12416
|
-
|
|
13102
|
+
fs21.accessSync(path21.join(dir, binary), fs21.constants.X_OK);
|
|
12417
13103
|
return true;
|
|
12418
13104
|
} catch {
|
|
12419
13105
|
}
|
|
12420
13106
|
}
|
|
12421
13107
|
return false;
|
|
12422
13108
|
}
|
|
12423
|
-
function detectAgents(homeDir2 =
|
|
13109
|
+
function detectAgents(homeDir2 = os18.homedir()) {
|
|
12424
13110
|
const exists2 = (p) => {
|
|
12425
13111
|
try {
|
|
12426
|
-
return
|
|
13112
|
+
return fs21.existsSync(p);
|
|
12427
13113
|
} catch (err2) {
|
|
12428
13114
|
const code = err2.code;
|
|
12429
13115
|
if (code !== "ENOENT") {
|
|
@@ -12435,7 +13121,7 @@ function detectAgents(homeDir2 = os16.homedir()) {
|
|
|
12435
13121
|
};
|
|
12436
13122
|
const desktopPath = claudeDesktopConfigPath(homeDir2);
|
|
12437
13123
|
return {
|
|
12438
|
-
claude: exists2(
|
|
13124
|
+
claude: exists2(path21.join(homeDir2, ".claude")) || exists2(path21.join(homeDir2, ".claude.json")),
|
|
12439
13125
|
// Antigravity (agy) shares the ~/.gemini root, so a bare
|
|
12440
13126
|
// `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
|
|
12441
13127
|
// installed on every agy machine — and `node9 init` would then
|
|
@@ -12445,19 +13131,19 @@ function detectAgents(homeDir2 = os16.homedir()) {
|
|
|
12445
13131
|
// creates ~/.gemini/settings.json on first run; agy does not touch
|
|
12446
13132
|
// it (it uses antigravity-cli/settings.json) — that file is the
|
|
12447
13133
|
// legacy-CLI discriminator.
|
|
12448
|
-
gemini: exists2(
|
|
13134
|
+
gemini: exists2(path21.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini", homeDir2),
|
|
12449
13135
|
// agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
|
|
12450
13136
|
// creates antigravity-ide/. PATH fallback covers installed-but-
|
|
12451
13137
|
// never-launched (same class as opencode #186).
|
|
12452
|
-
antigravity: exists2(
|
|
13138
|
+
antigravity: exists2(path21.join(homeDir2, ".gemini", "antigravity-cli")) || exists2(path21.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy", homeDir2),
|
|
12453
13139
|
// GitHub Copilot CLI creates ~/.copilot on first launch; PATH
|
|
12454
13140
|
// fallback covers installed-but-never-launched (same as opencode #186).
|
|
12455
|
-
copilot: exists2(
|
|
12456
|
-
cursor: exists2(
|
|
12457
|
-
codex: exists2(
|
|
12458
|
-
windsurf: exists2(
|
|
12459
|
-
vscode: exists2(
|
|
12460
|
-
claudeDesktop: desktopPath !== null && exists2(
|
|
13141
|
+
copilot: exists2(path21.join(homeDir2, ".copilot")) || binaryInPath("copilot", homeDir2),
|
|
13142
|
+
cursor: exists2(path21.join(homeDir2, ".cursor")),
|
|
13143
|
+
codex: exists2(path21.join(homeDir2, ".codex")),
|
|
13144
|
+
windsurf: exists2(path21.join(homeDir2, ".codeium", "windsurf")),
|
|
13145
|
+
vscode: exists2(path21.join(homeDir2, ".vscode")),
|
|
13146
|
+
claudeDesktop: desktopPath !== null && exists2(path21.dirname(desktopPath)),
|
|
12461
13147
|
// Opencode creates its config dir lazily on first launch — fall back to a
|
|
12462
13148
|
// PATH lookup so installed-but-never-launched CLIs are still wired. Config
|
|
12463
13149
|
// dir honors $XDG_CONFIG_HOME via opencodeConfigDir (#186, custom-XDG).
|
|
@@ -12468,7 +13154,7 @@ function detectAgents(homeDir2 = os16.homedir()) {
|
|
|
12468
13154
|
// dir lazily on first launch — same class of bug as opencode's #186
|
|
12469
13155
|
// (design R6) — so fall back to PATH lookup for installed-but-never-
|
|
12470
13156
|
// launched pi.
|
|
12471
|
-
pi: exists2(
|
|
13157
|
+
pi: exists2(path21.join(homeDir2, ".pi", "agent")) || binaryInPath("pi", homeDir2),
|
|
12472
13158
|
// Hermes Agent (https://github.com/NousResearch/hermes-agent): home dir
|
|
12473
13159
|
// is $HERMES_HOME (default ~/.hermes) per hermes_constants.py:30. config.yaml
|
|
12474
13160
|
// appears after `hermes setup` has run; the directory alone exists from
|
|
@@ -12479,9 +13165,9 @@ function detectAgents(homeDir2 = os16.homedir()) {
|
|
|
12479
13165
|
}
|
|
12480
13166
|
async function setupCursor() {
|
|
12481
13167
|
seedMcpPinsIfMissing();
|
|
12482
|
-
const homeDir2 =
|
|
12483
|
-
const mcpPath =
|
|
12484
|
-
const mcpConfig =
|
|
13168
|
+
const homeDir2 = os18.homedir();
|
|
13169
|
+
const mcpPath = path21.join(homeDir2, ".cursor", "mcp.json");
|
|
13170
|
+
const mcpConfig = readJson2(mcpPath) ?? {};
|
|
12485
13171
|
const servers = mcpConfig.mcpServers ?? {};
|
|
12486
13172
|
let anythingChanged = false;
|
|
12487
13173
|
const repairedMcpWraps = repairLegacyMcpWraps(
|
|
@@ -12507,7 +13193,7 @@ async function setupCursor() {
|
|
|
12507
13193
|
const serversToWrap = [];
|
|
12508
13194
|
for (const [name, server] of Object.entries(servers)) {
|
|
12509
13195
|
if (!server.command || server.command === "node9") continue;
|
|
12510
|
-
const upstream =
|
|
13196
|
+
const upstream = mcpUpstreamString(server);
|
|
12511
13197
|
serversToWrap.push({ name, upstream });
|
|
12512
13198
|
}
|
|
12513
13199
|
if (serversToWrap.length > 0) {
|
|
@@ -12563,27 +13249,27 @@ async function setupCursor() {
|
|
|
12563
13249
|
}
|
|
12564
13250
|
function readToml(filePath) {
|
|
12565
13251
|
try {
|
|
12566
|
-
if (
|
|
12567
|
-
return
|
|
13252
|
+
if (fs21.existsSync(filePath)) {
|
|
13253
|
+
return parseToml4(fs21.readFileSync(filePath, "utf-8"));
|
|
12568
13254
|
}
|
|
12569
13255
|
} catch {
|
|
12570
13256
|
}
|
|
12571
13257
|
return null;
|
|
12572
13258
|
}
|
|
12573
13259
|
function writeToml(filePath, data) {
|
|
12574
|
-
const dir =
|
|
12575
|
-
if (!
|
|
12576
|
-
|
|
13260
|
+
const dir = path21.dirname(filePath);
|
|
13261
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
13262
|
+
fs21.writeFileSync(filePath, stringifyToml2(data));
|
|
12577
13263
|
}
|
|
12578
13264
|
async function setupCodex() {
|
|
12579
13265
|
seedMcpPinsIfMissing();
|
|
12580
|
-
const homeDir2 =
|
|
12581
|
-
const configPath =
|
|
12582
|
-
const hooksPath =
|
|
13266
|
+
const homeDir2 = os18.homedir();
|
|
13267
|
+
const configPath = path21.join(homeDir2, ".codex", "config.toml");
|
|
13268
|
+
const hooksPath = path21.join(homeDir2, ".codex", "hooks.json");
|
|
12583
13269
|
const config = readToml(configPath) ?? {};
|
|
12584
13270
|
const servers = config.mcp_servers ?? {};
|
|
12585
13271
|
let anythingChanged = false;
|
|
12586
|
-
const hooksFile =
|
|
13272
|
+
const hooksFile = readJson2(hooksPath) ?? {};
|
|
12587
13273
|
if (!hooksFile.hooks) hooksFile.hooks = {};
|
|
12588
13274
|
let hooksChanged = false;
|
|
12589
13275
|
if (!hooksFile.hooks.PreToolUse) hooksFile.hooks.PreToolUse = [];
|
|
@@ -12676,11 +13362,21 @@ async function setupCodex() {
|
|
|
12676
13362
|
anythingChanged = true;
|
|
12677
13363
|
}
|
|
12678
13364
|
const serversToWrap = [];
|
|
13365
|
+
const appManaged = [];
|
|
12679
13366
|
for (const [name, server] of Object.entries(servers)) {
|
|
12680
13367
|
if (!server.command || server.command === "node9") continue;
|
|
12681
|
-
|
|
13368
|
+
if (isCodexAppManagedServer(name, server)) {
|
|
13369
|
+
appManaged.push(name);
|
|
13370
|
+
continue;
|
|
13371
|
+
}
|
|
13372
|
+
const upstream = mcpUpstreamString(server);
|
|
12682
13373
|
serversToWrap.push({ name, upstream });
|
|
12683
13374
|
}
|
|
13375
|
+
if (appManaged.length > 0) {
|
|
13376
|
+
console.log(
|
|
13377
|
+
chalk.gray(` \u2139\uFE0F Managed by the Codex app \u2014 not wrapped: ${appManaged.join(", ")}`)
|
|
13378
|
+
);
|
|
13379
|
+
}
|
|
12684
13380
|
if (serversToWrap.length > 0) {
|
|
12685
13381
|
console.log(chalk.bold("The following existing entries will be modified:\n"));
|
|
12686
13382
|
console.log(chalk.white(` ${configPath}`));
|
|
@@ -12745,10 +13441,10 @@ async function setupCodex() {
|
|
|
12745
13441
|
}
|
|
12746
13442
|
}
|
|
12747
13443
|
function teardownCodex() {
|
|
12748
|
-
const homeDir2 =
|
|
12749
|
-
const configPath =
|
|
12750
|
-
const hooksPath =
|
|
12751
|
-
const hooksFile =
|
|
13444
|
+
const homeDir2 = os18.homedir();
|
|
13445
|
+
const configPath = path21.join(homeDir2, ".codex", "config.toml");
|
|
13446
|
+
const hooksPath = path21.join(homeDir2, ".codex", "hooks.json");
|
|
13447
|
+
const hooksFile = readJson2(hooksPath);
|
|
12752
13448
|
if (hooksFile?.hooks) {
|
|
12753
13449
|
let hooksChanged = false;
|
|
12754
13450
|
for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
|
|
@@ -12794,9 +13490,9 @@ function teardownCodex() {
|
|
|
12794
13490
|
}
|
|
12795
13491
|
}
|
|
12796
13492
|
function setupHud() {
|
|
12797
|
-
const homeDir2 =
|
|
12798
|
-
const hooksPath =
|
|
12799
|
-
const settings =
|
|
13493
|
+
const homeDir2 = os18.homedir();
|
|
13494
|
+
const hooksPath = path21.join(homeDir2, ".claude", "settings.json");
|
|
13495
|
+
const settings = readJson2(hooksPath) ?? {};
|
|
12800
13496
|
const hudCommand = fullPathCommand("hud");
|
|
12801
13497
|
const statusLineObj = { type: "command", command: hudCommand };
|
|
12802
13498
|
const existing = settings.statusLine;
|
|
@@ -12823,9 +13519,9 @@ function setupHud() {
|
|
|
12823
13519
|
console.log(chalk.gray(" Restart Claude Code to activate."));
|
|
12824
13520
|
}
|
|
12825
13521
|
function teardownHud() {
|
|
12826
|
-
const homeDir2 =
|
|
12827
|
-
const hooksPath =
|
|
12828
|
-
const settings =
|
|
13522
|
+
const homeDir2 = os18.homedir();
|
|
13523
|
+
const hooksPath = path21.join(homeDir2, ".claude", "settings.json");
|
|
13524
|
+
const settings = readJson2(hooksPath);
|
|
12829
13525
|
if (!settings) {
|
|
12830
13526
|
console.log(chalk.blue(" \u2139\uFE0F ~/.claude/settings.json not found \u2014 nothing to remove"));
|
|
12831
13527
|
return;
|
|
@@ -12843,9 +13539,9 @@ function teardownHud() {
|
|
|
12843
13539
|
}
|
|
12844
13540
|
async function setupWindsurf() {
|
|
12845
13541
|
seedMcpPinsIfMissing();
|
|
12846
|
-
const homeDir2 =
|
|
12847
|
-
const mcpPath =
|
|
12848
|
-
const mcpConfig =
|
|
13542
|
+
const homeDir2 = os18.homedir();
|
|
13543
|
+
const mcpPath = path21.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
|
|
13544
|
+
const mcpConfig = readJson2(mcpPath) ?? {};
|
|
12849
13545
|
const servers = mcpConfig.mcpServers ?? {};
|
|
12850
13546
|
let anythingChanged = false;
|
|
12851
13547
|
const repairedMcpWraps = repairLegacyMcpWraps(
|
|
@@ -12921,9 +13617,9 @@ async function setupWindsurf() {
|
|
|
12921
13617
|
}
|
|
12922
13618
|
}
|
|
12923
13619
|
function teardownWindsurf() {
|
|
12924
|
-
const homeDir2 =
|
|
12925
|
-
const mcpPath =
|
|
12926
|
-
const mcpConfig =
|
|
13620
|
+
const homeDir2 = os18.homedir();
|
|
13621
|
+
const mcpPath = path21.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
|
|
13622
|
+
const mcpConfig = readJson2(mcpPath);
|
|
12927
13623
|
if (!mcpConfig?.mcpServers) {
|
|
12928
13624
|
console.log(
|
|
12929
13625
|
chalk.blue(" \u2139\uFE0F ~/.codeium/windsurf/mcp_config.json not found \u2014 nothing to remove")
|
|
@@ -12964,9 +13660,9 @@ function hasNode9McpServerVSCode(servers) {
|
|
|
12964
13660
|
}
|
|
12965
13661
|
async function setupVSCode() {
|
|
12966
13662
|
seedMcpPinsIfMissing();
|
|
12967
|
-
const homeDir2 =
|
|
12968
|
-
const mcpPath =
|
|
12969
|
-
const mcpConfig =
|
|
13663
|
+
const homeDir2 = os18.homedir();
|
|
13664
|
+
const mcpPath = path21.join(homeDir2, ".vscode", "mcp.json");
|
|
13665
|
+
const mcpConfig = readJson2(mcpPath) ?? {};
|
|
12970
13666
|
const servers = mcpConfig.servers ?? {};
|
|
12971
13667
|
let anythingChanged = false;
|
|
12972
13668
|
const repairedMcpWraps = repairLegacyMcpWraps(
|
|
@@ -13045,9 +13741,9 @@ async function setupVSCode() {
|
|
|
13045
13741
|
}
|
|
13046
13742
|
}
|
|
13047
13743
|
function teardownVSCode() {
|
|
13048
|
-
const homeDir2 =
|
|
13049
|
-
const mcpPath =
|
|
13050
|
-
const mcpConfig =
|
|
13744
|
+
const homeDir2 = os18.homedir();
|
|
13745
|
+
const mcpPath = path21.join(homeDir2, ".vscode", "mcp.json");
|
|
13746
|
+
const mcpConfig = readJson2(mcpPath);
|
|
13051
13747
|
if (!mcpConfig?.servers) {
|
|
13052
13748
|
console.log(chalk.blue(" \u2139\uFE0F ~/.vscode/mcp.json not found \u2014 nothing to remove"));
|
|
13053
13749
|
return;
|
|
@@ -13085,7 +13781,7 @@ async function setupClaudeDesktop() {
|
|
|
13085
13781
|
console.log(chalk.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
|
|
13086
13782
|
return;
|
|
13087
13783
|
}
|
|
13088
|
-
const config =
|
|
13784
|
+
const config = readJson2(configPath) ?? {};
|
|
13089
13785
|
const servers = config.mcpServers ?? {};
|
|
13090
13786
|
let anythingChanged = false;
|
|
13091
13787
|
const repairedMcpWraps = repairLegacyMcpWraps(
|
|
@@ -13166,7 +13862,7 @@ function teardownClaudeDesktop() {
|
|
|
13166
13862
|
console.log(chalk.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
|
|
13167
13863
|
return;
|
|
13168
13864
|
}
|
|
13169
|
-
const config =
|
|
13865
|
+
const config = readJson2(configPath);
|
|
13170
13866
|
if (!config?.mcpServers) {
|
|
13171
13867
|
console.log(chalk.blue(" \u2139\uFE0F Claude Desktop config not found \u2014 nothing to remove"));
|
|
13172
13868
|
return;
|
|
@@ -13205,7 +13901,7 @@ function node9ArgvForShim() {
|
|
|
13205
13901
|
function node9Version() {
|
|
13206
13902
|
try {
|
|
13207
13903
|
const pkg = JSON.parse(
|
|
13208
|
-
|
|
13904
|
+
fs21.readFileSync(path21.join(__dirname, "..", "package.json"), "utf-8")
|
|
13209
13905
|
);
|
|
13210
13906
|
return pkg.version ?? "0.0.0";
|
|
13211
13907
|
} catch {
|
|
@@ -13214,13 +13910,13 @@ function node9Version() {
|
|
|
13214
13910
|
}
|
|
13215
13911
|
async function setupOpencode() {
|
|
13216
13912
|
seedMcpPinsIfMissing();
|
|
13217
|
-
const homeDir2 =
|
|
13913
|
+
const homeDir2 = os18.homedir();
|
|
13218
13914
|
const configDir = opencodeConfigDir(homeDir2);
|
|
13219
|
-
const pluginsDir =
|
|
13220
|
-
const configPath =
|
|
13221
|
-
const pluginPath =
|
|
13915
|
+
const pluginsDir = path21.join(configDir, "plugins");
|
|
13916
|
+
const configPath = path21.join(configDir, "opencode.json");
|
|
13917
|
+
const pluginPath = path21.join(pluginsDir, OPENCODE_PLUGIN_NAME);
|
|
13222
13918
|
try {
|
|
13223
|
-
|
|
13919
|
+
fs21.mkdirSync(pluginsDir, { recursive: true });
|
|
13224
13920
|
} catch (err2) {
|
|
13225
13921
|
const code = err2.code;
|
|
13226
13922
|
if (code !== "EEXIST") {
|
|
@@ -13235,13 +13931,13 @@ async function setupOpencode() {
|
|
|
13235
13931
|
let pluginChanged = false;
|
|
13236
13932
|
const existingShim = (() => {
|
|
13237
13933
|
try {
|
|
13238
|
-
return
|
|
13934
|
+
return fs21.readFileSync(pluginPath, "utf-8");
|
|
13239
13935
|
} catch {
|
|
13240
13936
|
return null;
|
|
13241
13937
|
}
|
|
13242
13938
|
})();
|
|
13243
13939
|
if (existingShim !== shimContent) {
|
|
13244
|
-
|
|
13940
|
+
fs21.writeFileSync(pluginPath, shimContent);
|
|
13245
13941
|
pluginChanged = true;
|
|
13246
13942
|
if (existingShim) {
|
|
13247
13943
|
console.log(chalk.yellow(" \u{1F527} Opencode plugin shim updated to current version"));
|
|
@@ -13251,7 +13947,7 @@ async function setupOpencode() {
|
|
|
13251
13947
|
);
|
|
13252
13948
|
}
|
|
13253
13949
|
}
|
|
13254
|
-
const config =
|
|
13950
|
+
const config = readJson2(configPath) ?? {};
|
|
13255
13951
|
const mcp = config.mcp ?? {};
|
|
13256
13952
|
let configChanged = false;
|
|
13257
13953
|
const desiredCommand = [...node9ArgvForShim(), "mcp-server"];
|
|
@@ -13282,20 +13978,20 @@ async function setupOpencode() {
|
|
|
13282
13978
|
}
|
|
13283
13979
|
}
|
|
13284
13980
|
function teardownOpencode() {
|
|
13285
|
-
const homeDir2 =
|
|
13981
|
+
const homeDir2 = os18.homedir();
|
|
13286
13982
|
const configDir = opencodeConfigDir(homeDir2);
|
|
13287
|
-
const pluginsDir =
|
|
13288
|
-
const configPath =
|
|
13289
|
-
const pluginPath =
|
|
13983
|
+
const pluginsDir = path21.join(configDir, "plugins");
|
|
13984
|
+
const configPath = path21.join(configDir, "opencode.json");
|
|
13985
|
+
const pluginPath = path21.join(pluginsDir, OPENCODE_PLUGIN_NAME);
|
|
13290
13986
|
try {
|
|
13291
|
-
if (
|
|
13292
|
-
|
|
13987
|
+
if (fs21.existsSync(pluginPath)) {
|
|
13988
|
+
fs21.unlinkSync(pluginPath);
|
|
13293
13989
|
console.log(chalk.green(" \u2705 Removed node9 plugin from ~/.config/opencode/plugins/"));
|
|
13294
13990
|
}
|
|
13295
13991
|
} catch (err2) {
|
|
13296
13992
|
console.log(chalk.yellow(` \u26A0\uFE0F Could not remove ${pluginPath}: ${String(err2)}`));
|
|
13297
13993
|
}
|
|
13298
|
-
const config =
|
|
13994
|
+
const config = readJson2(configPath);
|
|
13299
13995
|
if (!config) {
|
|
13300
13996
|
console.log(chalk.blue(" \u2139\uFE0F ~/.config/opencode/opencode.json not found \u2014 nothing to remove"));
|
|
13301
13997
|
return;
|
|
@@ -13318,11 +14014,11 @@ function teardownOpencode() {
|
|
|
13318
14014
|
}
|
|
13319
14015
|
async function setupPi() {
|
|
13320
14016
|
seedMcpPinsIfMissing();
|
|
13321
|
-
const homeDir2 =
|
|
13322
|
-
const extensionsDir =
|
|
13323
|
-
const extensionPath =
|
|
14017
|
+
const homeDir2 = os18.homedir();
|
|
14018
|
+
const extensionsDir = path21.join(homeDir2, ".pi", "agent", "extensions");
|
|
14019
|
+
const extensionPath = path21.join(extensionsDir, PI_EXTENSION_NAME);
|
|
13324
14020
|
try {
|
|
13325
|
-
|
|
14021
|
+
fs21.mkdirSync(extensionsDir, { recursive: true });
|
|
13326
14022
|
} catch (err2) {
|
|
13327
14023
|
const code = err2.code;
|
|
13328
14024
|
console.log(chalk.yellow(` \u26A0\uFE0F Could not create ${extensionsDir}: ${code ?? String(err2)}`));
|
|
@@ -13334,7 +14030,7 @@ async function setupPi() {
|
|
|
13334
14030
|
});
|
|
13335
14031
|
const existingShim = (() => {
|
|
13336
14032
|
try {
|
|
13337
|
-
return
|
|
14033
|
+
return fs21.readFileSync(extensionPath, "utf-8");
|
|
13338
14034
|
} catch {
|
|
13339
14035
|
return null;
|
|
13340
14036
|
}
|
|
@@ -13343,7 +14039,7 @@ async function setupPi() {
|
|
|
13343
14039
|
console.log(chalk.blue(" \u2139\uFE0F Node9 is already fully configured for Pi."));
|
|
13344
14040
|
return;
|
|
13345
14041
|
}
|
|
13346
|
-
|
|
14042
|
+
fs21.writeFileSync(extensionPath, shimContent);
|
|
13347
14043
|
if (existingShim) {
|
|
13348
14044
|
console.log(chalk.yellow(" \u{1F527} Pi extension shim updated to current version"));
|
|
13349
14045
|
} else {
|
|
@@ -13356,11 +14052,11 @@ async function setupPi() {
|
|
|
13356
14052
|
printDaemonTip();
|
|
13357
14053
|
}
|
|
13358
14054
|
function teardownPi() {
|
|
13359
|
-
const homeDir2 =
|
|
13360
|
-
const extensionPath =
|
|
14055
|
+
const homeDir2 = os18.homedir();
|
|
14056
|
+
const extensionPath = path21.join(homeDir2, ".pi", "agent", "extensions", PI_EXTENSION_NAME);
|
|
13361
14057
|
try {
|
|
13362
|
-
if (
|
|
13363
|
-
|
|
14058
|
+
if (fs21.existsSync(extensionPath)) {
|
|
14059
|
+
fs21.unlinkSync(extensionPath);
|
|
13364
14060
|
console.log(chalk.green(" \u2705 Removed node9 extension from ~/.pi/agent/extensions/"));
|
|
13365
14061
|
} else {
|
|
13366
14062
|
console.log(chalk.blue(" \u2139\uFE0F No Pi extension installed \u2014 nothing to remove"));
|
|
@@ -13369,29 +14065,29 @@ function teardownPi() {
|
|
|
13369
14065
|
console.log(chalk.yellow(` \u26A0\uFE0F Could not remove ${extensionPath}: ${String(err2)}`));
|
|
13370
14066
|
}
|
|
13371
14067
|
}
|
|
13372
|
-
function hermesHomeDir(homeDir2 =
|
|
14068
|
+
function hermesHomeDir(homeDir2 = os18.homedir()) {
|
|
13373
14069
|
const env = process.env.HERMES_HOME?.trim();
|
|
13374
|
-
if (env &&
|
|
13375
|
-
return
|
|
14070
|
+
if (env && path21.isAbsolute(env)) return env;
|
|
14071
|
+
return path21.join(homeDir2, ".hermes");
|
|
13376
14072
|
}
|
|
13377
|
-
function hermesConfigPath(homeDir2 =
|
|
13378
|
-
return
|
|
14073
|
+
function hermesConfigPath(homeDir2 = os18.homedir()) {
|
|
14074
|
+
return path21.join(hermesHomeDir(homeDir2), HERMES_CONFIG_FILENAME);
|
|
13379
14075
|
}
|
|
13380
|
-
function hermesAllowlistPath(homeDir2 =
|
|
13381
|
-
return
|
|
14076
|
+
function hermesAllowlistPath(homeDir2 = os18.homedir()) {
|
|
14077
|
+
return path21.join(hermesHomeDir(homeDir2), HERMES_ALLOWLIST_FILENAME);
|
|
13382
14078
|
}
|
|
13383
14079
|
function setupHermes() {
|
|
13384
|
-
const homeDir2 =
|
|
14080
|
+
const homeDir2 = os18.homedir();
|
|
13385
14081
|
const configPath = hermesConfigPath(homeDir2);
|
|
13386
14082
|
const allowlistPath = hermesAllowlistPath(homeDir2);
|
|
13387
|
-
if (!
|
|
14083
|
+
if (!fs21.existsSync(configPath)) {
|
|
13388
14084
|
console.log(chalk.yellow(` \u26A0\uFE0F Hermes config not found at ${configPath}`));
|
|
13389
14085
|
console.log(chalk.gray(" Run `hermes setup` first, then re-run node9 agents add hermes."));
|
|
13390
14086
|
return;
|
|
13391
14087
|
}
|
|
13392
14088
|
let anythingChanged = false;
|
|
13393
|
-
const raw =
|
|
13394
|
-
const doc =
|
|
14089
|
+
const raw = fs21.readFileSync(configPath, "utf-8");
|
|
14090
|
+
const doc = yaml2.parseDocument(raw);
|
|
13395
14091
|
if (doc.errors.length > 0) {
|
|
13396
14092
|
console.log(chalk.yellow(` \u26A0\uFE0F Hermes config.yaml has YAML parse errors:`));
|
|
13397
14093
|
for (const err2 of doc.errors.slice(0, 3)) {
|
|
@@ -13433,9 +14129,9 @@ function setupHermes() {
|
|
|
13433
14129
|
atomicWriteSync(configPath, doc.toString());
|
|
13434
14130
|
}
|
|
13435
14131
|
let allowlist = {};
|
|
13436
|
-
if (
|
|
14132
|
+
if (fs21.existsSync(allowlistPath)) {
|
|
13437
14133
|
try {
|
|
13438
|
-
allowlist = JSON.parse(
|
|
14134
|
+
allowlist = JSON.parse(fs21.readFileSync(allowlistPath, "utf-8"));
|
|
13439
14135
|
} catch {
|
|
13440
14136
|
allowlist = {};
|
|
13441
14137
|
}
|
|
@@ -13471,15 +14167,15 @@ function setupHermes() {
|
|
|
13471
14167
|
}
|
|
13472
14168
|
}
|
|
13473
14169
|
function teardownHermes() {
|
|
13474
|
-
const homeDir2 =
|
|
14170
|
+
const homeDir2 = os18.homedir();
|
|
13475
14171
|
const configPath = hermesConfigPath(homeDir2);
|
|
13476
14172
|
const allowlistPath = hermesAllowlistPath(homeDir2);
|
|
13477
|
-
if (!
|
|
14173
|
+
if (!fs21.existsSync(configPath)) {
|
|
13478
14174
|
console.log(chalk.blue(` \u2139\uFE0F ${configPath} not found \u2014 nothing to remove`));
|
|
13479
14175
|
return;
|
|
13480
14176
|
}
|
|
13481
|
-
const raw =
|
|
13482
|
-
const doc =
|
|
14177
|
+
const raw = fs21.readFileSync(configPath, "utf-8");
|
|
14178
|
+
const doc = yaml2.parseDocument(raw);
|
|
13483
14179
|
if (doc.errors.length > 0) {
|
|
13484
14180
|
console.log(
|
|
13485
14181
|
chalk.yellow(` \u26A0\uFE0F Skipping ${configPath} \u2014 file has YAML parse errors, fix it manually.`)
|
|
@@ -13511,16 +14207,16 @@ function teardownHermesConfigDoc(doc, configPath) {
|
|
|
13511
14207
|
anythingChanged = true;
|
|
13512
14208
|
}
|
|
13513
14209
|
if (anythingChanged) {
|
|
13514
|
-
|
|
14210
|
+
fs21.writeFileSync(configPath, doc.toString());
|
|
13515
14211
|
console.log(chalk.green(` \u2705 Removed Node9 hooks from ${configPath}`));
|
|
13516
14212
|
} else {
|
|
13517
14213
|
console.log(chalk.blue(` \u2139\uFE0F No Node9 hooks found in ${configPath}`));
|
|
13518
14214
|
}
|
|
13519
14215
|
}
|
|
13520
14216
|
function teardownHermesAllowlist(allowlistPath) {
|
|
13521
|
-
if (!
|
|
14217
|
+
if (!fs21.existsSync(allowlistPath)) return;
|
|
13522
14218
|
try {
|
|
13523
|
-
const raw =
|
|
14219
|
+
const raw = fs21.readFileSync(allowlistPath, "utf-8");
|
|
13524
14220
|
const allowlist = JSON.parse(raw);
|
|
13525
14221
|
if (!Array.isArray(allowlist.approvals)) return;
|
|
13526
14222
|
const before = allowlist.approvals.length;
|
|
@@ -13533,44 +14229,44 @@ function teardownHermesAllowlist(allowlistPath) {
|
|
|
13533
14229
|
} catch {
|
|
13534
14230
|
}
|
|
13535
14231
|
}
|
|
13536
|
-
function getAgentsStatus(homeDir2 =
|
|
14232
|
+
function getAgentsStatus(homeDir2 = os18.homedir()) {
|
|
13537
14233
|
const detected = detectAgents(homeDir2);
|
|
13538
14234
|
const claudeWired = (() => {
|
|
13539
|
-
const settings =
|
|
14235
|
+
const settings = readJson2(path21.join(homeDir2, ".claude", "settings.json"));
|
|
13540
14236
|
return !!settings?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
13541
14237
|
})();
|
|
13542
14238
|
const geminiWired = (() => {
|
|
13543
|
-
const settings =
|
|
14239
|
+
const settings = readJson2(path21.join(homeDir2, ".gemini", "settings.json"));
|
|
13544
14240
|
return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
13545
14241
|
})();
|
|
13546
14242
|
const antigravityWired = (() => {
|
|
13547
|
-
const hooksFile =
|
|
13548
|
-
|
|
14243
|
+
const hooksFile = readJson2(
|
|
14244
|
+
path21.join(homeDir2, ".gemini", "config", "hooks.json")
|
|
13549
14245
|
);
|
|
13550
14246
|
return !!hooksFile?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
|
|
13551
14247
|
})();
|
|
13552
14248
|
const copilotWired = (() => {
|
|
13553
|
-
const hooksFile =
|
|
13554
|
-
|
|
14249
|
+
const hooksFile = readJson2(
|
|
14250
|
+
path21.join(homeDir2, ".copilot", "hooks", "node9.json")
|
|
13555
14251
|
);
|
|
13556
14252
|
return !!hooksFile?.hooks?.PreToolUse?.some((h) => isNode9Hook(h.command));
|
|
13557
14253
|
})();
|
|
13558
14254
|
const cursorWired = (() => {
|
|
13559
|
-
const cfg =
|
|
14255
|
+
const cfg = readJson2(path21.join(homeDir2, ".cursor", "mcp.json"));
|
|
13560
14256
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
13561
14257
|
})();
|
|
13562
14258
|
const codexWired = (() => {
|
|
13563
|
-
const cfg = readToml(
|
|
14259
|
+
const cfg = readToml(path21.join(homeDir2, ".codex", "config.toml"));
|
|
13564
14260
|
return !!(cfg?.mcp_servers && hasNode9McpServer(cfg.mcp_servers));
|
|
13565
14261
|
})();
|
|
13566
14262
|
const windsurfWired = (() => {
|
|
13567
|
-
const cfg =
|
|
13568
|
-
|
|
14263
|
+
const cfg = readJson2(
|
|
14264
|
+
path21.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
|
|
13569
14265
|
);
|
|
13570
14266
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
13571
14267
|
})();
|
|
13572
14268
|
const vscodeWired = (() => {
|
|
13573
|
-
const cfg =
|
|
14269
|
+
const cfg = readJson2(path21.join(homeDir2, ".vscode", "mcp.json"));
|
|
13574
14270
|
return !!(cfg?.servers && hasNode9McpServerVSCode(cfg.servers));
|
|
13575
14271
|
})();
|
|
13576
14272
|
return [
|
|
@@ -13637,7 +14333,7 @@ function getAgentsStatus(homeDir2 = os16.homedir()) {
|
|
|
13637
14333
|
wired: (() => {
|
|
13638
14334
|
const cfgPath = claudeDesktopConfigPath(homeDir2);
|
|
13639
14335
|
if (!cfgPath) return false;
|
|
13640
|
-
const cfg =
|
|
14336
|
+
const cfg = readJson2(cfgPath);
|
|
13641
14337
|
return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
|
|
13642
14338
|
})(),
|
|
13643
14339
|
mode: detected.claudeDesktop ? "mcp" : null
|
|
@@ -13647,16 +14343,16 @@ function getAgentsStatus(homeDir2 = os16.homedir()) {
|
|
|
13647
14343
|
label: "Opencode",
|
|
13648
14344
|
installed: detected.opencode,
|
|
13649
14345
|
wired: (() => {
|
|
13650
|
-
const pluginPath =
|
|
14346
|
+
const pluginPath = path21.join(
|
|
13651
14347
|
homeDir2,
|
|
13652
14348
|
".config",
|
|
13653
14349
|
"opencode",
|
|
13654
14350
|
"plugins",
|
|
13655
14351
|
OPENCODE_PLUGIN_NAME
|
|
13656
14352
|
);
|
|
13657
|
-
if (
|
|
13658
|
-
const cfg =
|
|
13659
|
-
|
|
14353
|
+
if (fs21.existsSync(pluginPath)) return true;
|
|
14354
|
+
const cfg = readJson2(
|
|
14355
|
+
path21.join(homeDir2, ".config", "opencode", "opencode.json")
|
|
13660
14356
|
);
|
|
13661
14357
|
return !!cfg?.mcp?.["node9"];
|
|
13662
14358
|
})(),
|
|
@@ -13668,7 +14364,7 @@ function getAgentsStatus(homeDir2 = os16.homedir()) {
|
|
|
13668
14364
|
installed: detected.pi,
|
|
13669
14365
|
// Pi has no MCP path — only the extension file. "wired" is a
|
|
13670
14366
|
// simple existence check on the canonical install location.
|
|
13671
|
-
wired:
|
|
14367
|
+
wired: fs21.existsSync(path21.join(homeDir2, ".pi", "agent", "extensions", PI_EXTENSION_NAME)),
|
|
13672
14368
|
mode: detected.pi ? "hooks" : null
|
|
13673
14369
|
},
|
|
13674
14370
|
{
|
|
@@ -13680,8 +14376,8 @@ function getAgentsStatus(homeDir2 = os16.homedir()) {
|
|
|
13680
14376
|
// Document API for a boolean status check.
|
|
13681
14377
|
wired: (() => {
|
|
13682
14378
|
try {
|
|
13683
|
-
const raw =
|
|
13684
|
-
const cfg =
|
|
14379
|
+
const raw = fs21.readFileSync(hermesConfigPath(homeDir2), "utf-8");
|
|
14380
|
+
const cfg = yaml2.parse(raw);
|
|
13685
14381
|
const pre = cfg?.hooks?.["pre_tool_call"] ?? [];
|
|
13686
14382
|
return pre.some((e) => typeof e?.command === "string" && isNode9Hook(e.command));
|
|
13687
14383
|
} catch {
|
|
@@ -13696,6 +14392,7 @@ var NODE9_MCP_SERVER_ENTRY, MCP_WRAP_SUBCOMMAND, LEGACY_MCP_WRAP_SUBCOMMAND, COD
|
|
|
13696
14392
|
var init_setup = __esm({
|
|
13697
14393
|
"src/setup.ts"() {
|
|
13698
14394
|
"use strict";
|
|
14395
|
+
init_mcp_wrap();
|
|
13699
14396
|
init_codex_trust();
|
|
13700
14397
|
init_mcp_pin();
|
|
13701
14398
|
init_hook_baseline();
|
|
@@ -13717,244 +14414,6 @@ var init_setup = __esm({
|
|
|
13717
14414
|
}
|
|
13718
14415
|
});
|
|
13719
14416
|
|
|
13720
|
-
// src/agent-wiring.ts
|
|
13721
|
-
import fs20 from "fs";
|
|
13722
|
-
import path21 from "path";
|
|
13723
|
-
import os17 from "os";
|
|
13724
|
-
import * as yaml2 from "yaml";
|
|
13725
|
-
import { parse as parseToml3 } from "smol-toml";
|
|
13726
|
-
function readJson2(filePath) {
|
|
13727
|
-
if (!fs20.existsSync(filePath)) return null;
|
|
13728
|
-
try {
|
|
13729
|
-
return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
|
|
13730
|
-
} catch {
|
|
13731
|
-
return "invalid";
|
|
13732
|
-
}
|
|
13733
|
-
}
|
|
13734
|
-
function matchersHaveNode9Hook(matchers) {
|
|
13735
|
-
return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
|
|
13736
|
-
}
|
|
13737
|
-
function flatHaveNode9Hook(entries) {
|
|
13738
|
-
return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
|
|
13739
|
-
}
|
|
13740
|
-
function readHookRoot(filePath, format) {
|
|
13741
|
-
if (!fs20.existsSync(filePath)) return "absent";
|
|
13742
|
-
let raw;
|
|
13743
|
-
try {
|
|
13744
|
-
raw = fs20.readFileSync(filePath, "utf-8");
|
|
13745
|
-
} catch {
|
|
13746
|
-
return "absent";
|
|
13747
|
-
}
|
|
13748
|
-
try {
|
|
13749
|
-
const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
|
|
13750
|
-
return parsed?.hooks ?? {};
|
|
13751
|
-
} catch {
|
|
13752
|
-
return "invalid";
|
|
13753
|
-
}
|
|
13754
|
-
}
|
|
13755
|
-
function eventWired(root, ev, format) {
|
|
13756
|
-
const arr = root[ev.key];
|
|
13757
|
-
if (format === "matcher") return matchersHaveNode9Hook(arr);
|
|
13758
|
-
return flatHaveNode9Hook(arr);
|
|
13759
|
-
}
|
|
13760
|
-
function detectMcp(servers) {
|
|
13761
|
-
const entries = Object.entries(servers ?? {});
|
|
13762
|
-
const present = entries.some(([, s]) => s?.command === "node9");
|
|
13763
|
-
const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
|
|
13764
|
-
return { wrapped, present };
|
|
13765
|
-
}
|
|
13766
|
-
function readMcpServers(filePath, format) {
|
|
13767
|
-
if (!fs20.existsSync(filePath)) return {};
|
|
13768
|
-
try {
|
|
13769
|
-
if (format === "toml") {
|
|
13770
|
-
const parsed2 = parseToml3(fs20.readFileSync(filePath, "utf-8"));
|
|
13771
|
-
return parsed2?.mcp_servers ?? {};
|
|
13772
|
-
}
|
|
13773
|
-
const parsed = readJson2(filePath);
|
|
13774
|
-
if (parsed === null || parsed === "invalid") return {};
|
|
13775
|
-
return parsed.mcpServers ?? {};
|
|
13776
|
-
} catch {
|
|
13777
|
-
return {};
|
|
13778
|
-
}
|
|
13779
|
-
}
|
|
13780
|
-
function readMcp(filePath, format) {
|
|
13781
|
-
if (!fs20.existsSync(filePath)) return { wrapped: [], present: false };
|
|
13782
|
-
try {
|
|
13783
|
-
if (format === "toml") {
|
|
13784
|
-
const parsed2 = parseToml3(fs20.readFileSync(filePath, "utf-8"));
|
|
13785
|
-
return detectMcp(parsed2?.mcp_servers);
|
|
13786
|
-
}
|
|
13787
|
-
const parsed = readJson2(filePath);
|
|
13788
|
-
if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
|
|
13789
|
-
return detectMcp(parsed.mcpServers);
|
|
13790
|
-
} catch {
|
|
13791
|
-
return { wrapped: [], present: false };
|
|
13792
|
-
}
|
|
13793
|
-
}
|
|
13794
|
-
function getAgentWiring(home = os17.homedir()) {
|
|
13795
|
-
const detected = detectAgents(home);
|
|
13796
|
-
return AGENT_SPECS.map((spec) => {
|
|
13797
|
-
const present = spec.present(home);
|
|
13798
|
-
const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
|
|
13799
|
-
let hooks;
|
|
13800
|
-
let wireState;
|
|
13801
|
-
let hookLabel;
|
|
13802
|
-
let settingsPath;
|
|
13803
|
-
if (spec.shimFile) {
|
|
13804
|
-
const shimWired = exists(spec.shimFile(home));
|
|
13805
|
-
hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
|
|
13806
|
-
wireState = shimWired ? "wired" : present ? "unwired" : "absent";
|
|
13807
|
-
hookLabel = "node9 plugin";
|
|
13808
|
-
settingsPath = spec.shimFile(home);
|
|
13809
|
-
} else {
|
|
13810
|
-
const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
|
|
13811
|
-
const primary = spec.hookEvents[0];
|
|
13812
|
-
const rootPresent = root !== "absent" && root !== "invalid";
|
|
13813
|
-
hooks = spec.hookEvents.map((ev) => ({
|
|
13814
|
-
label: hookLabelOf(ev, pad),
|
|
13815
|
-
wired: rootPresent && eventWired(root, ev, spec.hookFormat)
|
|
13816
|
-
}));
|
|
13817
|
-
if (root === "absent") wireState = "absent";
|
|
13818
|
-
else if (root === "invalid") wireState = "invalid";
|
|
13819
|
-
else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
|
|
13820
|
-
hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
|
|
13821
|
-
settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
|
|
13822
|
-
}
|
|
13823
|
-
const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
|
|
13824
|
-
const anyHookWired = hooks.some((h) => h.wired);
|
|
13825
|
-
return {
|
|
13826
|
-
id: spec.id,
|
|
13827
|
-
label: spec.label,
|
|
13828
|
-
setupCommand: spec.setupCommand,
|
|
13829
|
-
installed: detected[spec.id],
|
|
13830
|
-
present,
|
|
13831
|
-
hooks,
|
|
13832
|
-
wireState,
|
|
13833
|
-
hookLabel,
|
|
13834
|
-
settingsPath,
|
|
13835
|
-
configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
|
|
13836
|
-
mcpServers: mcp ? mcp.wrapped : null,
|
|
13837
|
-
mcpProtected: mcp ? mcp.present : false,
|
|
13838
|
-
isProtected: anyHookWired || (mcp?.present ?? false)
|
|
13839
|
-
};
|
|
13840
|
-
});
|
|
13841
|
-
}
|
|
13842
|
-
var exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
|
|
13843
|
-
var init_agent_wiring = __esm({
|
|
13844
|
-
"src/agent-wiring.ts"() {
|
|
13845
|
-
"use strict";
|
|
13846
|
-
init_setup();
|
|
13847
|
-
exists = (p) => {
|
|
13848
|
-
try {
|
|
13849
|
-
return fs20.existsSync(p);
|
|
13850
|
-
} catch {
|
|
13851
|
-
return false;
|
|
13852
|
-
}
|
|
13853
|
-
};
|
|
13854
|
-
ck = (key) => ({ key, kind: "check" });
|
|
13855
|
-
lg = (key) => ({ key, kind: "log" });
|
|
13856
|
-
DEFAULT_LABEL_PAD = 11;
|
|
13857
|
-
hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
|
|
13858
|
-
AGENT_SPECS = [
|
|
13859
|
-
{
|
|
13860
|
-
id: "claude",
|
|
13861
|
-
label: "Claude Code",
|
|
13862
|
-
setupCommand: "node9 agents add claude",
|
|
13863
|
-
hookFile: (h) => path21.join(h, ".claude", "settings.json"),
|
|
13864
|
-
hookFormat: "matcher",
|
|
13865
|
-
// UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
|
|
13866
|
-
// that shipped; the spec must name it too, or status/doctor never show the
|
|
13867
|
-
// row and heal (which repairs via setupAgent) has no signal it is missing.
|
|
13868
|
-
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
13869
|
-
mcpFile: (h) => path21.join(h, ".claude.json"),
|
|
13870
|
-
present: (h) => exists(path21.join(h, ".claude", "settings.json")) || exists(path21.join(h, ".claude.json"))
|
|
13871
|
-
},
|
|
13872
|
-
{
|
|
13873
|
-
id: "gemini",
|
|
13874
|
-
label: "Gemini CLI",
|
|
13875
|
-
setupCommand: "node9 agents add gemini",
|
|
13876
|
-
hookFile: (h) => path21.join(h, ".gemini", "settings.json"),
|
|
13877
|
-
hookFormat: "matcher",
|
|
13878
|
-
hookEvents: [ck("BeforeTool"), lg("AfterTool")],
|
|
13879
|
-
mcpFile: (h) => path21.join(h, ".gemini", "settings.json"),
|
|
13880
|
-
present: (h) => exists(path21.join(h, ".gemini", "settings.json"))
|
|
13881
|
-
},
|
|
13882
|
-
{
|
|
13883
|
-
id: "codex",
|
|
13884
|
-
label: "Codex",
|
|
13885
|
-
setupCommand: "node9 agents add codex",
|
|
13886
|
-
hookFile: (h) => path21.join(h, ".codex", "hooks.json"),
|
|
13887
|
-
hookFormat: "matcher",
|
|
13888
|
-
hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
|
|
13889
|
-
mcpFile: (h) => path21.join(h, ".codex", "config.toml"),
|
|
13890
|
-
mcpFormat: "toml",
|
|
13891
|
-
present: (h) => exists(path21.join(h, ".codex"))
|
|
13892
|
-
},
|
|
13893
|
-
{
|
|
13894
|
-
id: "antigravity",
|
|
13895
|
-
label: "Antigravity",
|
|
13896
|
-
setupCommand: "node9 agents add antigravity",
|
|
13897
|
-
hookFile: (h) => path21.join(h, ".gemini", "config", "hooks.json"),
|
|
13898
|
-
hookFormat: "matcher",
|
|
13899
|
-
hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
|
|
13900
|
-
mcpFile: (h) => path21.join(h, ".gemini", "config", "mcp_config.json"),
|
|
13901
|
-
present: (h) => exists(path21.join(h, ".gemini", "config", "hooks.json")) || exists(path21.join(h, ".gemini", "antigravity-cli")) || exists(path21.join(h, ".gemini", "antigravity-ide"))
|
|
13902
|
-
},
|
|
13903
|
-
{
|
|
13904
|
-
id: "copilot",
|
|
13905
|
-
label: "GitHub Copilot",
|
|
13906
|
-
setupCommand: "node9 agents add copilot",
|
|
13907
|
-
hookFile: (h) => path21.join(h, ".copilot", "hooks", "node9.json"),
|
|
13908
|
-
hookFormat: "flat",
|
|
13909
|
-
hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
|
|
13910
|
-
mcpFile: (h) => path21.join(h, ".copilot", "mcp-config.json"),
|
|
13911
|
-
present: (h) => exists(path21.join(h, ".copilot"))
|
|
13912
|
-
},
|
|
13913
|
-
{
|
|
13914
|
-
id: "cursor",
|
|
13915
|
-
label: "Cursor",
|
|
13916
|
-
setupCommand: "node9 agents add cursor",
|
|
13917
|
-
// MCP-only — no hook file (see note above).
|
|
13918
|
-
hookFormat: "flat",
|
|
13919
|
-
hookEvents: [],
|
|
13920
|
-
mcpFile: (h) => path21.join(h, ".cursor", "mcp.json"),
|
|
13921
|
-
present: (h) => exists(path21.join(h, ".cursor", "mcp.json"))
|
|
13922
|
-
},
|
|
13923
|
-
{
|
|
13924
|
-
id: "hermes",
|
|
13925
|
-
label: "Hermes Agent",
|
|
13926
|
-
setupCommand: "node9 agents add hermes",
|
|
13927
|
-
hookFile: (h) => hermesConfigPath(h),
|
|
13928
|
-
hookFormat: "yaml",
|
|
13929
|
-
hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
|
|
13930
|
-
labelPad: 14,
|
|
13931
|
-
// 'post_tool_call' is wider than the default
|
|
13932
|
-
present: (h) => exists(hermesConfigPath(h))
|
|
13933
|
-
},
|
|
13934
|
-
{
|
|
13935
|
-
// Plugin-shim agents — protected by a node9-authored plugin/extension file
|
|
13936
|
-
// (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
|
|
13937
|
-
id: "opencode",
|
|
13938
|
-
label: "OpenCode",
|
|
13939
|
-
setupCommand: "node9 agents add opencode",
|
|
13940
|
-
hookFormat: "flat",
|
|
13941
|
-
hookEvents: [],
|
|
13942
|
-
shimFile: (h) => path21.join(opencodeConfigDir(h), "plugins", "node9.js"),
|
|
13943
|
-
present: (h) => exists(opencodeConfigDir(h)) || exists(path21.join(opencodeConfigDir(h), "plugins", "node9.js"))
|
|
13944
|
-
},
|
|
13945
|
-
{
|
|
13946
|
-
id: "pi",
|
|
13947
|
-
label: "Pi",
|
|
13948
|
-
setupCommand: "node9 agents add pi",
|
|
13949
|
-
hookFormat: "flat",
|
|
13950
|
-
hookEvents: [],
|
|
13951
|
-
shimFile: (h) => path21.join(h, ".pi", "agent", "extensions", "node9.js"),
|
|
13952
|
-
present: (h) => exists(path21.join(h, ".pi", "agent")) || exists(path21.join(h, ".pi", "agent", "extensions", "node9.js"))
|
|
13953
|
-
}
|
|
13954
|
-
];
|
|
13955
|
-
}
|
|
13956
|
-
});
|
|
13957
|
-
|
|
13958
14417
|
// src/config/keyed-guard.ts
|
|
13959
14418
|
import chalk2 from "chalk";
|
|
13960
14419
|
function isKeyedForPolicy() {
|
|
@@ -13987,15 +14446,15 @@ var init_keyed_guard = __esm({
|
|
|
13987
14446
|
});
|
|
13988
14447
|
|
|
13989
14448
|
// src/pricing/litellm.ts
|
|
13990
|
-
import
|
|
14449
|
+
import fs23 from "fs";
|
|
13991
14450
|
import path23 from "path";
|
|
13992
|
-
import
|
|
14451
|
+
import os20 from "os";
|
|
13993
14452
|
function normalizeModel(raw) {
|
|
13994
14453
|
return raw.replace(/-\d{8}$/, "").toLowerCase();
|
|
13995
14454
|
}
|
|
13996
14455
|
function readCache(opts) {
|
|
13997
14456
|
try {
|
|
13998
|
-
const raw = JSON.parse(
|
|
14457
|
+
const raw = JSON.parse(fs23.readFileSync(CACHE_FILE(), "utf-8"));
|
|
13999
14458
|
if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
|
|
14000
14459
|
return null;
|
|
14001
14460
|
}
|
|
@@ -14011,17 +14470,17 @@ function writeCache(prices) {
|
|
|
14011
14470
|
try {
|
|
14012
14471
|
const target = CACHE_FILE();
|
|
14013
14472
|
const dir = path23.dirname(target);
|
|
14014
|
-
if (!
|
|
14473
|
+
if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
|
|
14015
14474
|
const tmp = target + ".tmp";
|
|
14016
14475
|
const body = {
|
|
14017
14476
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14018
14477
|
prices
|
|
14019
14478
|
};
|
|
14020
|
-
|
|
14021
|
-
|
|
14479
|
+
fs23.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
|
|
14480
|
+
fs23.renameSync(tmp, target);
|
|
14022
14481
|
} catch (err2) {
|
|
14023
14482
|
try {
|
|
14024
|
-
|
|
14483
|
+
fs23.appendFileSync(
|
|
14025
14484
|
HOOK_DEBUG_LOG,
|
|
14026
14485
|
`[pricing] cache write failed: ${err2.message}
|
|
14027
14486
|
`
|
|
@@ -14179,7 +14638,7 @@ var init_litellm = __esm({
|
|
|
14179
14638
|
"gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
|
|
14180
14639
|
"gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
|
|
14181
14640
|
};
|
|
14182
|
-
CACHE_FILE = () => path23.join(
|
|
14641
|
+
CACHE_FILE = () => path23.join(os20.homedir(), ".node9", "model-pricing.json");
|
|
14183
14642
|
TTL_MS = 24 * 60 * 60 * 1e3;
|
|
14184
14643
|
memCache = null;
|
|
14185
14644
|
memCacheAt = 0;
|
|
@@ -14189,11 +14648,11 @@ var init_litellm = __esm({
|
|
|
14189
14648
|
});
|
|
14190
14649
|
|
|
14191
14650
|
// src/cost-gemini.ts
|
|
14192
|
-
import
|
|
14193
|
-
import
|
|
14651
|
+
import fs24 from "fs";
|
|
14652
|
+
import os21 from "os";
|
|
14194
14653
|
import path24 from "path";
|
|
14195
14654
|
function geminiTmpDir() {
|
|
14196
|
-
return path24.join(
|
|
14655
|
+
return path24.join(os21.homedir(), ".gemini", "tmp");
|
|
14197
14656
|
}
|
|
14198
14657
|
function geminiPriceFor(model) {
|
|
14199
14658
|
let tuple = pricingFor(model);
|
|
@@ -14208,14 +14667,14 @@ function geminiPriceFor(model) {
|
|
|
14208
14667
|
}
|
|
14209
14668
|
function safeReaddir(dir) {
|
|
14210
14669
|
try {
|
|
14211
|
-
return
|
|
14670
|
+
return fs24.readdirSync(dir);
|
|
14212
14671
|
} catch {
|
|
14213
14672
|
return [];
|
|
14214
14673
|
}
|
|
14215
14674
|
}
|
|
14216
14675
|
function isDir(p) {
|
|
14217
14676
|
try {
|
|
14218
|
-
return
|
|
14677
|
+
return fs24.statSync(p).isDirectory();
|
|
14219
14678
|
} catch {
|
|
14220
14679
|
return false;
|
|
14221
14680
|
}
|
|
@@ -14294,7 +14753,7 @@ var init_cost_gemini = __esm({
|
|
|
14294
14753
|
id: "gemini",
|
|
14295
14754
|
available() {
|
|
14296
14755
|
try {
|
|
14297
|
-
return
|
|
14756
|
+
return fs24.existsSync(geminiTmpDir());
|
|
14298
14757
|
} catch {
|
|
14299
14758
|
return false;
|
|
14300
14759
|
}
|
|
@@ -14303,13 +14762,13 @@ var init_cost_gemini = __esm({
|
|
|
14303
14762
|
const combined = /* @__PURE__ */ new Map();
|
|
14304
14763
|
for (const { file, project } of listGeminiSessionFiles(geminiTmpDir())) {
|
|
14305
14764
|
try {
|
|
14306
|
-
if (sinceMs !== void 0 &&
|
|
14765
|
+
if (sinceMs !== void 0 && fs24.statSync(file).mtimeMs < sinceMs) continue;
|
|
14307
14766
|
} catch {
|
|
14308
14767
|
continue;
|
|
14309
14768
|
}
|
|
14310
14769
|
let content;
|
|
14311
14770
|
try {
|
|
14312
|
-
content =
|
|
14771
|
+
content = fs24.readFileSync(file, "utf8");
|
|
14313
14772
|
} catch {
|
|
14314
14773
|
continue;
|
|
14315
14774
|
}
|
|
@@ -14334,11 +14793,11 @@ var init_cost_gemini = __esm({
|
|
|
14334
14793
|
});
|
|
14335
14794
|
|
|
14336
14795
|
// src/cost-codex.ts
|
|
14337
|
-
import
|
|
14338
|
-
import
|
|
14796
|
+
import fs25 from "fs";
|
|
14797
|
+
import os22 from "os";
|
|
14339
14798
|
import path25 from "path";
|
|
14340
14799
|
function codexSessionsDir() {
|
|
14341
|
-
return path25.join(process.env.CODEX_HOME?.trim() || path25.join(
|
|
14800
|
+
return path25.join(process.env.CODEX_HOME?.trim() || path25.join(os22.homedir(), ".codex"), "sessions");
|
|
14342
14801
|
}
|
|
14343
14802
|
function codexPriceFor(model) {
|
|
14344
14803
|
return pricingFor(codexModel(model), { exact: true }) ?? CODEX_FALLBACK;
|
|
@@ -14370,14 +14829,14 @@ function codexSessionCost(model, tokens, request2) {
|
|
|
14370
14829
|
function statAndFirstLine(file) {
|
|
14371
14830
|
const CAP = 4 * 1024 * 1024;
|
|
14372
14831
|
const CHUNK = 64 * 1024;
|
|
14373
|
-
const fd =
|
|
14832
|
+
const fd = fs25.openSync(file, "r");
|
|
14374
14833
|
try {
|
|
14375
|
-
const stat =
|
|
14834
|
+
const stat = fs25.fstatSync(fd);
|
|
14376
14835
|
const limit = Math.min(stat.size, CAP);
|
|
14377
14836
|
const parts = [];
|
|
14378
14837
|
for (let pos = 0; pos < limit; pos += CHUNK) {
|
|
14379
14838
|
const buf = Buffer.alloc(Math.min(CHUNK, limit - pos));
|
|
14380
|
-
const read2 =
|
|
14839
|
+
const read2 = fs25.readSync(fd, buf, 0, buf.length, pos);
|
|
14381
14840
|
if (read2 <= 0) break;
|
|
14382
14841
|
const slice = buf.subarray(0, read2);
|
|
14383
14842
|
const nl = slice.indexOf(10);
|
|
@@ -14386,14 +14845,14 @@ function statAndFirstLine(file) {
|
|
|
14386
14845
|
}
|
|
14387
14846
|
return { stat, first: Buffer.concat(parts).toString("utf8") };
|
|
14388
14847
|
} finally {
|
|
14389
|
-
|
|
14848
|
+
fs25.closeSync(fd);
|
|
14390
14849
|
}
|
|
14391
14850
|
}
|
|
14392
14851
|
function listCodexSessionFiles(base = codexSessionsDir()) {
|
|
14393
14852
|
const files = [];
|
|
14394
14853
|
const walk = (dir) => {
|
|
14395
14854
|
try {
|
|
14396
|
-
for (const entry of
|
|
14855
|
+
for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
|
|
14397
14856
|
const file = path25.join(dir, entry.name);
|
|
14398
14857
|
if (entry.isDirectory()) walk(file);
|
|
14399
14858
|
else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file);
|
|
@@ -14600,13 +15059,13 @@ var init_cost_codex = __esm({
|
|
|
14600
15059
|
CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
|
|
14601
15060
|
codexSource = {
|
|
14602
15061
|
id: "codex",
|
|
14603
|
-
available: () =>
|
|
15062
|
+
available: () => fs25.existsSync(codexSessionsDir()) || fs25.existsSync(path25.join(path25.dirname(codexSessionsDir()), "archived_sessions")),
|
|
14604
15063
|
collect(sinceMs) {
|
|
14605
15064
|
const entries = [];
|
|
14606
15065
|
for (const file of listCodexSessionFiles()) {
|
|
14607
15066
|
try {
|
|
14608
|
-
if (sinceMs !== void 0 &&
|
|
14609
|
-
entries.push(...parseCodexSession(
|
|
15067
|
+
if (sinceMs !== void 0 && fs25.statSync(file).mtimeMs < sinceMs) continue;
|
|
15068
|
+
entries.push(...parseCodexSession(fs25.readFileSync(file, "utf8").split("\n")));
|
|
14610
15069
|
} catch {
|
|
14611
15070
|
}
|
|
14612
15071
|
}
|
|
@@ -14617,15 +15076,15 @@ var init_cost_codex = __esm({
|
|
|
14617
15076
|
});
|
|
14618
15077
|
|
|
14619
15078
|
// src/cost-copilot.ts
|
|
14620
|
-
import
|
|
14621
|
-
import
|
|
15079
|
+
import fs26 from "fs";
|
|
15080
|
+
import os23 from "os";
|
|
14622
15081
|
import path26 from "path";
|
|
14623
15082
|
function copilotSessionsDir() {
|
|
14624
|
-
return path26.join(
|
|
15083
|
+
return path26.join(os23.homedir(), ".copilot", "session-state");
|
|
14625
15084
|
}
|
|
14626
15085
|
function safeReaddir2(dir) {
|
|
14627
15086
|
try {
|
|
14628
|
-
return
|
|
15087
|
+
return fs26.readdirSync(dir);
|
|
14629
15088
|
} catch {
|
|
14630
15089
|
return [];
|
|
14631
15090
|
}
|
|
@@ -14701,7 +15160,7 @@ var init_cost_copilot = __esm({
|
|
|
14701
15160
|
id: "copilot",
|
|
14702
15161
|
available() {
|
|
14703
15162
|
try {
|
|
14704
|
-
return
|
|
15163
|
+
return fs26.existsSync(copilotSessionsDir());
|
|
14705
15164
|
} catch {
|
|
14706
15165
|
return false;
|
|
14707
15166
|
}
|
|
@@ -14712,13 +15171,13 @@ var init_cost_copilot = __esm({
|
|
|
14712
15171
|
for (const sid of safeReaddir2(base)) {
|
|
14713
15172
|
const file = path26.join(base, sid, "events.jsonl");
|
|
14714
15173
|
try {
|
|
14715
|
-
if (sinceMs !== void 0 &&
|
|
15174
|
+
if (sinceMs !== void 0 && fs26.statSync(file).mtimeMs < sinceMs) continue;
|
|
14716
15175
|
} catch {
|
|
14717
15176
|
continue;
|
|
14718
15177
|
}
|
|
14719
15178
|
let content;
|
|
14720
15179
|
try {
|
|
14721
|
-
content =
|
|
15180
|
+
content = fs26.readFileSync(file, "utf8");
|
|
14722
15181
|
} catch {
|
|
14723
15182
|
continue;
|
|
14724
15183
|
}
|
|
@@ -15072,9 +15531,9 @@ var init_scan_summary = __esm({
|
|
|
15072
15531
|
|
|
15073
15532
|
// src/cli/commands/blast.ts
|
|
15074
15533
|
import chalk3 from "chalk";
|
|
15075
|
-
import
|
|
15534
|
+
import fs27 from "fs";
|
|
15076
15535
|
import path27 from "path";
|
|
15077
|
-
import
|
|
15536
|
+
import os24 from "os";
|
|
15078
15537
|
function buildSensitivePaths(home, cwd) {
|
|
15079
15538
|
return [
|
|
15080
15539
|
{
|
|
@@ -15159,7 +15618,7 @@ function buildSensitivePaths(home, cwd) {
|
|
|
15159
15618
|
}
|
|
15160
15619
|
function isReadable(filePath) {
|
|
15161
15620
|
try {
|
|
15162
|
-
|
|
15621
|
+
fs27.accessSync(filePath, fs27.constants.R_OK);
|
|
15163
15622
|
return true;
|
|
15164
15623
|
} catch {
|
|
15165
15624
|
return false;
|
|
@@ -15172,13 +15631,13 @@ function scoreLabel(score) {
|
|
|
15172
15631
|
return chalk3.red.bold(`${score}/100 Critical`);
|
|
15173
15632
|
}
|
|
15174
15633
|
function runBlast() {
|
|
15175
|
-
const home =
|
|
15634
|
+
const home = os24.homedir();
|
|
15176
15635
|
const cwd = process.cwd();
|
|
15177
15636
|
const paths = buildSensitivePaths(home, cwd);
|
|
15178
15637
|
let scoreDeduction = 0;
|
|
15179
15638
|
const reachable = [];
|
|
15180
15639
|
for (const p of paths) {
|
|
15181
|
-
if (
|
|
15640
|
+
if (fs27.existsSync(p.full) && isReadable(p.full)) {
|
|
15182
15641
|
reachable.push(p);
|
|
15183
15642
|
scoreDeduction += p.score;
|
|
15184
15643
|
}
|
|
@@ -15196,7 +15655,7 @@ function runBlast() {
|
|
|
15196
15655
|
}
|
|
15197
15656
|
function registerBlastCommand(program2) {
|
|
15198
15657
|
program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
|
|
15199
|
-
const home =
|
|
15658
|
+
const home = os24.homedir();
|
|
15200
15659
|
const cwd = process.cwd();
|
|
15201
15660
|
const { reachable, envFindings, score } = runBlast();
|
|
15202
15661
|
console.log("");
|
|
@@ -15385,7 +15844,7 @@ var init_scan_json = __esm({
|
|
|
15385
15844
|
});
|
|
15386
15845
|
|
|
15387
15846
|
// src/session-files.ts
|
|
15388
|
-
import * as
|
|
15847
|
+
import * as fs28 from "fs";
|
|
15389
15848
|
import * as path28 from "path";
|
|
15390
15849
|
function listSessionFiles(dir, maxDepth = 6) {
|
|
15391
15850
|
const out = [];
|
|
@@ -15393,7 +15852,7 @@ function listSessionFiles(dir, maxDepth = 6) {
|
|
|
15393
15852
|
if (depth > maxDepth) return;
|
|
15394
15853
|
let entries;
|
|
15395
15854
|
try {
|
|
15396
|
-
entries =
|
|
15855
|
+
entries = fs28.readdirSync(d, { withFileTypes: true });
|
|
15397
15856
|
} catch {
|
|
15398
15857
|
return;
|
|
15399
15858
|
}
|
|
@@ -15416,17 +15875,17 @@ var init_session_files = __esm({
|
|
|
15416
15875
|
});
|
|
15417
15876
|
|
|
15418
15877
|
// src/cli/render/scan-history.ts
|
|
15419
|
-
import
|
|
15878
|
+
import fs29 from "fs";
|
|
15420
15879
|
import path29 from "path";
|
|
15421
|
-
import
|
|
15880
|
+
import os25 from "os";
|
|
15422
15881
|
function defaultHistoryPath() {
|
|
15423
|
-
return path29.join(
|
|
15882
|
+
return path29.join(os25.homedir(), ".node9", "scan-history.json");
|
|
15424
15883
|
}
|
|
15425
15884
|
function readPreviousScan(opts = {}) {
|
|
15426
15885
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
15427
15886
|
try {
|
|
15428
|
-
if (!
|
|
15429
|
-
const raw =
|
|
15887
|
+
if (!fs29.existsSync(filePath)) return null;
|
|
15888
|
+
const raw = fs29.readFileSync(filePath, "utf8");
|
|
15430
15889
|
const parsed = JSON.parse(raw);
|
|
15431
15890
|
if (!Array.isArray(parsed) || parsed.length === 0) return null;
|
|
15432
15891
|
const last = parsed[parsed.length - 1];
|
|
@@ -15440,11 +15899,11 @@ function appendScanHistory(record2, opts = {}) {
|
|
|
15440
15899
|
const filePath = opts.path ?? defaultHistoryPath();
|
|
15441
15900
|
const cap = opts.cap ?? SCAN_HISTORY_CAP;
|
|
15442
15901
|
try {
|
|
15443
|
-
|
|
15902
|
+
fs29.mkdirSync(path29.dirname(filePath), { recursive: true });
|
|
15444
15903
|
let history = [];
|
|
15445
|
-
if (
|
|
15904
|
+
if (fs29.existsSync(filePath)) {
|
|
15446
15905
|
try {
|
|
15447
|
-
const parsed = JSON.parse(
|
|
15906
|
+
const parsed = JSON.parse(fs29.readFileSync(filePath, "utf8"));
|
|
15448
15907
|
if (Array.isArray(parsed)) {
|
|
15449
15908
|
history = parsed.filter(isValidRecord);
|
|
15450
15909
|
}
|
|
@@ -15487,9 +15946,9 @@ var init_scan_history = __esm({
|
|
|
15487
15946
|
});
|
|
15488
15947
|
|
|
15489
15948
|
// src/costSync.ts
|
|
15490
|
-
import
|
|
15949
|
+
import fs30 from "fs";
|
|
15491
15950
|
import path30 from "path";
|
|
15492
|
-
import
|
|
15951
|
+
import os26 from "os";
|
|
15493
15952
|
function decodeProjectDirName(dirName) {
|
|
15494
15953
|
return dirName.replace(/-/g, "/");
|
|
15495
15954
|
}
|
|
@@ -15497,7 +15956,7 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
|
|
|
15497
15956
|
const runId = path30.basename(filePath, ".jsonl");
|
|
15498
15957
|
let content;
|
|
15499
15958
|
try {
|
|
15500
|
-
content =
|
|
15959
|
+
content = fs30.readFileSync(filePath, "utf8");
|
|
15501
15960
|
} catch {
|
|
15502
15961
|
return /* @__PURE__ */ new Map();
|
|
15503
15962
|
}
|
|
@@ -15597,7 +16056,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
|
|
|
15597
16056
|
signal: AbortSignal.timeout(15e3)
|
|
15598
16057
|
});
|
|
15599
16058
|
if (!res.ok) {
|
|
15600
|
-
|
|
16059
|
+
fs30.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
|
|
15601
16060
|
`);
|
|
15602
16061
|
} else {
|
|
15603
16062
|
let stored;
|
|
@@ -15607,7 +16066,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
|
|
|
15607
16066
|
} catch {
|
|
15608
16067
|
}
|
|
15609
16068
|
if (typeof stored === "number" && stored < batch.length) {
|
|
15610
|
-
|
|
16069
|
+
fs30.appendFileSync(
|
|
15611
16070
|
HOOK_DEBUG_LOG,
|
|
15612
16071
|
`[cost-sync] dropped ${batch.length - stored} of ${batch.length} rows
|
|
15613
16072
|
`
|
|
@@ -15615,7 +16074,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
|
|
|
15615
16074
|
}
|
|
15616
16075
|
}
|
|
15617
16076
|
} catch (err2) {
|
|
15618
|
-
|
|
16077
|
+
fs30.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${safeMessage(err2)}
|
|
15619
16078
|
`);
|
|
15620
16079
|
}
|
|
15621
16080
|
}
|
|
@@ -15628,10 +16087,10 @@ async function syncCost() {
|
|
|
15628
16087
|
if (entries.length === 0) return;
|
|
15629
16088
|
let username = "unknown";
|
|
15630
16089
|
try {
|
|
15631
|
-
username =
|
|
16090
|
+
username = os26.userInfo().username;
|
|
15632
16091
|
} catch {
|
|
15633
16092
|
}
|
|
15634
|
-
const machineId = `${
|
|
16093
|
+
const machineId = `${os26.hostname()}:${username}`;
|
|
15635
16094
|
await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
|
|
15636
16095
|
}
|
|
15637
16096
|
function startCostSync() {
|
|
@@ -15659,22 +16118,22 @@ var init_costSync = __esm({
|
|
|
15659
16118
|
claudeSource = {
|
|
15660
16119
|
id: "claude",
|
|
15661
16120
|
available() {
|
|
15662
|
-
return
|
|
16121
|
+
return fs30.existsSync(path30.join(os26.homedir(), ".claude", "projects"));
|
|
15663
16122
|
},
|
|
15664
16123
|
collect(sinceMs) {
|
|
15665
|
-
const projectsDir = path30.join(
|
|
15666
|
-
if (!
|
|
16124
|
+
const projectsDir = path30.join(os26.homedir(), ".claude", "projects");
|
|
16125
|
+
if (!fs30.existsSync(projectsDir)) return [];
|
|
15667
16126
|
const combined = /* @__PURE__ */ new Map();
|
|
15668
16127
|
let dirs;
|
|
15669
16128
|
try {
|
|
15670
|
-
dirs =
|
|
16129
|
+
dirs = fs30.readdirSync(projectsDir);
|
|
15671
16130
|
} catch {
|
|
15672
16131
|
return [];
|
|
15673
16132
|
}
|
|
15674
16133
|
for (const dir of dirs) {
|
|
15675
16134
|
const dirPath = path30.join(projectsDir, dir);
|
|
15676
16135
|
try {
|
|
15677
|
-
if (!
|
|
16136
|
+
if (!fs30.statSync(dirPath).isDirectory()) continue;
|
|
15678
16137
|
} catch {
|
|
15679
16138
|
continue;
|
|
15680
16139
|
}
|
|
@@ -15689,7 +16148,7 @@ var init_costSync = __esm({
|
|
|
15689
16148
|
const filePath = path30.join(dirPath, file);
|
|
15690
16149
|
if (sinceMs !== void 0) {
|
|
15691
16150
|
try {
|
|
15692
|
-
if (
|
|
16151
|
+
if (fs30.statSync(filePath).mtimeMs < sinceMs) continue;
|
|
15693
16152
|
} catch {
|
|
15694
16153
|
continue;
|
|
15695
16154
|
}
|
|
@@ -15729,8 +16188,8 @@ __export(scan_watermark_exports, {
|
|
|
15729
16188
|
tickForensicBroadcast: () => tickForensicBroadcast,
|
|
15730
16189
|
tickScanWatcher: () => tickScanWatcher
|
|
15731
16190
|
});
|
|
15732
|
-
import
|
|
15733
|
-
import
|
|
16191
|
+
import fs31 from "fs";
|
|
16192
|
+
import os27 from "os";
|
|
15734
16193
|
import path31 from "path";
|
|
15735
16194
|
import readline from "readline";
|
|
15736
16195
|
function freshWatermark() {
|
|
@@ -15744,7 +16203,7 @@ function freshWatermark() {
|
|
|
15744
16203
|
function loadWatermark() {
|
|
15745
16204
|
let raw;
|
|
15746
16205
|
try {
|
|
15747
|
-
raw =
|
|
16206
|
+
raw = fs31.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
15748
16207
|
} catch {
|
|
15749
16208
|
return { status: "fresh", wm: freshWatermark() };
|
|
15750
16209
|
}
|
|
@@ -15797,21 +16256,21 @@ function saveWatermark(wm) {
|
|
|
15797
16256
|
if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
|
|
15798
16257
|
const target = WATERMARK_FILE();
|
|
15799
16258
|
const dir = path31.dirname(target);
|
|
15800
|
-
if (!
|
|
16259
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
15801
16260
|
const tmp = target + ".tmp";
|
|
15802
|
-
|
|
15803
|
-
|
|
16261
|
+
fs31.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
|
|
16262
|
+
fs31.renameSync(tmp, target);
|
|
15804
16263
|
}
|
|
15805
16264
|
function listJsonlFiles() {
|
|
15806
16265
|
const root = PROJECTS_DIR();
|
|
15807
|
-
if (!
|
|
16266
|
+
if (!fs31.existsSync(root)) return [];
|
|
15808
16267
|
const out = [];
|
|
15809
|
-
for (const entry of
|
|
16268
|
+
for (const entry of fs31.readdirSync(root, { withFileTypes: true })) {
|
|
15810
16269
|
if (!entry.isDirectory()) continue;
|
|
15811
16270
|
const projectDir = path31.join(root, entry.name);
|
|
15812
16271
|
let inner;
|
|
15813
16272
|
try {
|
|
15814
|
-
inner =
|
|
16273
|
+
inner = fs31.readdirSync(projectDir, { withFileTypes: true });
|
|
15815
16274
|
} catch {
|
|
15816
16275
|
continue;
|
|
15817
16276
|
}
|
|
@@ -15825,7 +16284,7 @@ function listJsonlFiles() {
|
|
|
15825
16284
|
}
|
|
15826
16285
|
function fileSize(p) {
|
|
15827
16286
|
try {
|
|
15828
|
-
return
|
|
16287
|
+
return fs31.statSync(p).size;
|
|
15829
16288
|
} catch {
|
|
15830
16289
|
return 0;
|
|
15831
16290
|
}
|
|
@@ -15835,7 +16294,7 @@ async function scanDelta(filePath, fromByte, onLine) {
|
|
|
15835
16294
|
if (size <= fromByte) return fromByte;
|
|
15836
16295
|
const lastNl = findLastNewline(filePath, fromByte, size);
|
|
15837
16296
|
const endsWithNewline = lastNl === size - 1;
|
|
15838
|
-
const stream =
|
|
16297
|
+
const stream = fs31.createReadStream(filePath, {
|
|
15839
16298
|
start: fromByte,
|
|
15840
16299
|
end: size - 1,
|
|
15841
16300
|
highWaterMark: 64 * 1024
|
|
@@ -15864,7 +16323,7 @@ function findLastNewline(filePath, from, size) {
|
|
|
15864
16323
|
const CHUNK = 64 * 1024;
|
|
15865
16324
|
let fd;
|
|
15866
16325
|
try {
|
|
15867
|
-
fd =
|
|
16326
|
+
fd = fs31.openSync(filePath, "r");
|
|
15868
16327
|
} catch {
|
|
15869
16328
|
return -1;
|
|
15870
16329
|
}
|
|
@@ -15873,7 +16332,7 @@ function findLastNewline(filePath, from, size) {
|
|
|
15873
16332
|
let end = size;
|
|
15874
16333
|
while (end > from) {
|
|
15875
16334
|
const start = Math.max(from, end - CHUNK);
|
|
15876
|
-
const n =
|
|
16335
|
+
const n = fs31.readSync(fd, buf, 0, end - start, start);
|
|
15877
16336
|
const idx = buf.subarray(0, n).lastIndexOf(10);
|
|
15878
16337
|
if (idx !== -1) return start + idx;
|
|
15879
16338
|
end = start;
|
|
@@ -15882,7 +16341,7 @@ function findLastNewline(filePath, from, size) {
|
|
|
15882
16341
|
} catch {
|
|
15883
16342
|
return -1;
|
|
15884
16343
|
} finally {
|
|
15885
|
-
|
|
16344
|
+
fs31.closeSync(fd);
|
|
15886
16345
|
}
|
|
15887
16346
|
}
|
|
15888
16347
|
function safeCanaryCtxValues() {
|
|
@@ -16047,7 +16506,7 @@ function emptyTick(uploadAs) {
|
|
|
16047
16506
|
function readRawWatermarkPreservingOffsets() {
|
|
16048
16507
|
let raw;
|
|
16049
16508
|
try {
|
|
16050
|
-
raw =
|
|
16509
|
+
raw = fs31.readFileSync(WATERMARK_FILE(), "utf-8");
|
|
16051
16510
|
} catch {
|
|
16052
16511
|
return null;
|
|
16053
16512
|
}
|
|
@@ -16082,7 +16541,7 @@ async function runActualTick(wm) {
|
|
|
16082
16541
|
if (!known) {
|
|
16083
16542
|
let mtimeMs = 0;
|
|
16084
16543
|
try {
|
|
16085
|
-
mtimeMs =
|
|
16544
|
+
mtimeMs = fs31.statSync(filePath).mtime.getTime();
|
|
16086
16545
|
} catch {
|
|
16087
16546
|
continue;
|
|
16088
16547
|
}
|
|
@@ -16141,8 +16600,8 @@ var init_scan_watermark = __esm({
|
|
|
16141
16600
|
init_registry();
|
|
16142
16601
|
init_config();
|
|
16143
16602
|
init_dist();
|
|
16144
|
-
PROJECTS_DIR = () => path31.join(
|
|
16145
|
-
WATERMARK_FILE = () => path31.join(
|
|
16603
|
+
PROJECTS_DIR = () => path31.join(os27.homedir(), ".claude", "projects");
|
|
16604
|
+
WATERMARK_FILE = () => path31.join(os27.homedir(), ".node9", "scan-watermark.json");
|
|
16146
16605
|
MAX_LINE_BYTES = 2 * 1024 * 1024;
|
|
16147
16606
|
WATERMARK_SCHEMA_VERSION = 2;
|
|
16148
16607
|
LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
|
|
@@ -16158,9 +16617,9 @@ __export(scan_upload_history_exports, {
|
|
|
16158
16617
|
parseSinceCutoff: () => parseSinceCutoff,
|
|
16159
16618
|
runUploadHistory: () => runUploadHistory
|
|
16160
16619
|
});
|
|
16161
|
-
import
|
|
16620
|
+
import fs32 from "fs";
|
|
16162
16621
|
import https from "https";
|
|
16163
|
-
import
|
|
16622
|
+
import os28 from "os";
|
|
16164
16623
|
import path32 from "path";
|
|
16165
16624
|
import chalk5 from "chalk";
|
|
16166
16625
|
function emptySignals2() {
|
|
@@ -16196,10 +16655,10 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
|
|
|
16196
16655
|
return now.getTime() - 90 * 864e5;
|
|
16197
16656
|
}
|
|
16198
16657
|
function* iterateJsonlFiles(cutoffMs) {
|
|
16199
|
-
const projectsDir = path32.join(
|
|
16658
|
+
const projectsDir = path32.join(os28.homedir(), ".claude", "projects");
|
|
16200
16659
|
let dirs;
|
|
16201
16660
|
try {
|
|
16202
|
-
dirs =
|
|
16661
|
+
dirs = fs32.readdirSync(projectsDir);
|
|
16203
16662
|
} catch {
|
|
16204
16663
|
return;
|
|
16205
16664
|
}
|
|
@@ -16207,14 +16666,14 @@ function* iterateJsonlFiles(cutoffMs) {
|
|
|
16207
16666
|
const dirPath = path32.join(projectsDir, dir);
|
|
16208
16667
|
let stats;
|
|
16209
16668
|
try {
|
|
16210
|
-
stats =
|
|
16669
|
+
stats = fs32.statSync(dirPath);
|
|
16211
16670
|
} catch {
|
|
16212
16671
|
continue;
|
|
16213
16672
|
}
|
|
16214
16673
|
if (!stats.isDirectory()) continue;
|
|
16215
16674
|
let files;
|
|
16216
16675
|
try {
|
|
16217
|
-
files =
|
|
16676
|
+
files = fs32.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
16218
16677
|
} catch {
|
|
16219
16678
|
continue;
|
|
16220
16679
|
}
|
|
@@ -16222,7 +16681,7 @@ function* iterateJsonlFiles(cutoffMs) {
|
|
|
16222
16681
|
const filePath = path32.join(dirPath, file);
|
|
16223
16682
|
let mtime = 0;
|
|
16224
16683
|
try {
|
|
16225
|
-
mtime =
|
|
16684
|
+
mtime = fs32.statSync(filePath).mtimeMs;
|
|
16226
16685
|
} catch {
|
|
16227
16686
|
continue;
|
|
16228
16687
|
}
|
|
@@ -16297,7 +16756,7 @@ async function runUploadHistory(opts) {
|
|
|
16297
16756
|
filesScanned++;
|
|
16298
16757
|
let content;
|
|
16299
16758
|
try {
|
|
16300
|
-
content =
|
|
16759
|
+
content = fs32.readFileSync(filePath, "utf8");
|
|
16301
16760
|
} catch {
|
|
16302
16761
|
continue;
|
|
16303
16762
|
}
|
|
@@ -16371,10 +16830,10 @@ async function runUploadHistory(opts) {
|
|
|
16371
16830
|
const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
|
|
16372
16831
|
let username = "unknown";
|
|
16373
16832
|
try {
|
|
16374
|
-
username =
|
|
16833
|
+
username = os28.userInfo().username;
|
|
16375
16834
|
} catch {
|
|
16376
16835
|
}
|
|
16377
|
-
const machineId = `${
|
|
16836
|
+
const machineId = `${os28.hostname()}:${username}`;
|
|
16378
16837
|
await postJson(costUrl, creds.apiKey, {
|
|
16379
16838
|
machineId,
|
|
16380
16839
|
entries: dailyEntries
|
|
@@ -16448,9 +16907,9 @@ var init_scan_upload_history = __esm({
|
|
|
16448
16907
|
|
|
16449
16908
|
// src/cli/commands/scan.ts
|
|
16450
16909
|
import chalk6 from "chalk";
|
|
16451
|
-
import
|
|
16910
|
+
import fs33 from "fs";
|
|
16452
16911
|
import path33 from "path";
|
|
16453
|
-
import
|
|
16912
|
+
import os29 from "os";
|
|
16454
16913
|
import stringWidth2 from "string-width";
|
|
16455
16914
|
function claudeModelPrice(model) {
|
|
16456
16915
|
const t = pricingFor(model);
|
|
@@ -16538,7 +16997,7 @@ function findingKey(ruleName, inputPreview, projLabel) {
|
|
|
16538
16997
|
return `${ruleName ?? "<unnamed>"}|${inputPreview}|${projLabel}`;
|
|
16539
16998
|
}
|
|
16540
16999
|
function displayHome(p) {
|
|
16541
|
-
const home =
|
|
17000
|
+
const home = os29.homedir();
|
|
16542
17001
|
return p.startsWith(home) ? "~" + p.slice(home.length) : p;
|
|
16543
17002
|
}
|
|
16544
17003
|
function dlpKey(patternName, redactedSample, projLabel) {
|
|
@@ -16735,14 +17194,14 @@ function buildRuleSources() {
|
|
|
16735
17194
|
}
|
|
16736
17195
|
function countScanFiles() {
|
|
16737
17196
|
let total = 0;
|
|
16738
|
-
const claudeDir = path33.join(
|
|
16739
|
-
if (
|
|
17197
|
+
const claudeDir = path33.join(os29.homedir(), ".claude", "projects");
|
|
17198
|
+
if (fs33.existsSync(claudeDir)) {
|
|
16740
17199
|
try {
|
|
16741
|
-
for (const proj of
|
|
17200
|
+
for (const proj of fs33.readdirSync(claudeDir)) {
|
|
16742
17201
|
const p = path33.join(claudeDir, proj);
|
|
16743
17202
|
try {
|
|
16744
|
-
if (!
|
|
16745
|
-
total +=
|
|
17203
|
+
if (!fs33.statSync(p).isDirectory()) continue;
|
|
17204
|
+
total += fs33.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
|
|
16746
17205
|
} catch {
|
|
16747
17206
|
continue;
|
|
16748
17207
|
}
|
|
@@ -16750,17 +17209,17 @@ function countScanFiles() {
|
|
|
16750
17209
|
} catch {
|
|
16751
17210
|
}
|
|
16752
17211
|
}
|
|
16753
|
-
const geminiDir = path33.join(
|
|
16754
|
-
if (
|
|
17212
|
+
const geminiDir = path33.join(os29.homedir(), ".gemini", "tmp");
|
|
17213
|
+
if (fs33.existsSync(geminiDir)) {
|
|
16755
17214
|
try {
|
|
16756
|
-
for (const slug2 of
|
|
17215
|
+
for (const slug2 of fs33.readdirSync(geminiDir)) {
|
|
16757
17216
|
const p = path33.join(geminiDir, slug2);
|
|
16758
17217
|
try {
|
|
16759
|
-
if (!
|
|
17218
|
+
if (!fs33.statSync(p).isDirectory()) continue;
|
|
16760
17219
|
const chatsDir = path33.join(p, "chats");
|
|
16761
|
-
if (
|
|
17220
|
+
if (fs33.existsSync(chatsDir)) {
|
|
16762
17221
|
try {
|
|
16763
|
-
total +=
|
|
17222
|
+
total += fs33.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
|
|
16764
17223
|
} catch {
|
|
16765
17224
|
}
|
|
16766
17225
|
}
|
|
@@ -16772,15 +17231,15 @@ function countScanFiles() {
|
|
|
16772
17231
|
}
|
|
16773
17232
|
}
|
|
16774
17233
|
for (const surface of ["antigravity-cli", "antigravity-ide"]) {
|
|
16775
|
-
const brainDir = path33.join(
|
|
16776
|
-
if (!
|
|
17234
|
+
const brainDir = path33.join(os29.homedir(), ".gemini", surface, "brain");
|
|
17235
|
+
if (!fs33.existsSync(brainDir)) continue;
|
|
16777
17236
|
try {
|
|
16778
|
-
for (const conv of
|
|
17237
|
+
for (const conv of fs33.readdirSync(brainDir)) {
|
|
16779
17238
|
const convPath = path33.join(brainDir, conv);
|
|
16780
17239
|
try {
|
|
16781
|
-
if (!
|
|
17240
|
+
if (!fs33.statSync(convPath).isDirectory()) continue;
|
|
16782
17241
|
const logsDir = path33.join(convPath, ".system_generated", "logs");
|
|
16783
|
-
if (
|
|
17242
|
+
if (fs33.existsSync(path33.join(logsDir, "transcript_full.jsonl")) || fs33.existsSync(path33.join(logsDir, "transcript.jsonl"))) {
|
|
16784
17243
|
total += 1;
|
|
16785
17244
|
}
|
|
16786
17245
|
} catch {
|
|
@@ -16790,11 +17249,11 @@ function countScanFiles() {
|
|
|
16790
17249
|
} catch {
|
|
16791
17250
|
}
|
|
16792
17251
|
}
|
|
16793
|
-
const copilotDir = path33.join(
|
|
16794
|
-
if (
|
|
17252
|
+
const copilotDir = path33.join(os29.homedir(), ".copilot", "session-state");
|
|
17253
|
+
if (fs33.existsSync(copilotDir)) {
|
|
16795
17254
|
try {
|
|
16796
|
-
for (const sid of
|
|
16797
|
-
if (
|
|
17255
|
+
for (const sid of fs33.readdirSync(copilotDir)) {
|
|
17256
|
+
if (fs33.existsSync(path33.join(copilotDir, sid, "events.jsonl"))) total += 1;
|
|
16798
17257
|
}
|
|
16799
17258
|
} catch {
|
|
16800
17259
|
}
|
|
@@ -16821,7 +17280,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
16821
17280
|
const session = { sessionId, costUSD: 0, toolCalls: 0 };
|
|
16822
17281
|
let raw;
|
|
16823
17282
|
try {
|
|
16824
|
-
raw =
|
|
17283
|
+
raw = fs33.readFileSync(path33.join(projPath, file), "utf-8");
|
|
16825
17284
|
} catch {
|
|
16826
17285
|
return;
|
|
16827
17286
|
}
|
|
@@ -17065,11 +17524,11 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
17065
17524
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, canaryVals, onProgress, onLine) {
|
|
17066
17525
|
const projPath = path33.join(projectsDir, proj);
|
|
17067
17526
|
try {
|
|
17068
|
-
if (!
|
|
17527
|
+
if (!fs33.statSync(projPath).isDirectory()) return;
|
|
17069
17528
|
} catch {
|
|
17070
17529
|
return;
|
|
17071
17530
|
}
|
|
17072
|
-
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(
|
|
17531
|
+
const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os29.homedir(), "~")).slice(
|
|
17073
17532
|
0,
|
|
17074
17533
|
40
|
|
17075
17534
|
);
|
|
@@ -17112,12 +17571,12 @@ function emptyClaudeScan() {
|
|
|
17112
17571
|
};
|
|
17113
17572
|
}
|
|
17114
17573
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
17115
|
-
const projectsDir = path33.join(
|
|
17574
|
+
const projectsDir = path33.join(os29.homedir(), ".claude", "projects");
|
|
17116
17575
|
const result = emptyClaudeScan();
|
|
17117
|
-
if (!
|
|
17576
|
+
if (!fs33.existsSync(projectsDir)) return result;
|
|
17118
17577
|
let projDirs;
|
|
17119
17578
|
try {
|
|
17120
|
-
projDirs =
|
|
17579
|
+
projDirs = fs33.readdirSync(projectsDir);
|
|
17121
17580
|
} catch {
|
|
17122
17581
|
return result;
|
|
17123
17582
|
}
|
|
@@ -17141,7 +17600,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
|
17141
17600
|
}
|
|
17142
17601
|
function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
17143
17602
|
const canaryVals = safeCanaryScanValues();
|
|
17144
|
-
const tmpDir = path33.join(
|
|
17603
|
+
const tmpDir = path33.join(os29.homedir(), ".gemini", "tmp");
|
|
17145
17604
|
const result = {
|
|
17146
17605
|
filesScanned: 0,
|
|
17147
17606
|
sessions: 0,
|
|
@@ -17158,10 +17617,10 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
17158
17617
|
perSession: []
|
|
17159
17618
|
};
|
|
17160
17619
|
const dedup = emptyScanDedup();
|
|
17161
|
-
if (!
|
|
17620
|
+
if (!fs33.existsSync(tmpDir)) return result;
|
|
17162
17621
|
let slugDirs;
|
|
17163
17622
|
try {
|
|
17164
|
-
slugDirs =
|
|
17623
|
+
slugDirs = fs33.readdirSync(tmpDir);
|
|
17165
17624
|
} catch {
|
|
17166
17625
|
return result;
|
|
17167
17626
|
}
|
|
@@ -17169,22 +17628,22 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
17169
17628
|
for (const slug2 of slugDirs) {
|
|
17170
17629
|
const slugPath = path33.join(tmpDir, slug2);
|
|
17171
17630
|
try {
|
|
17172
|
-
if (!
|
|
17631
|
+
if (!fs33.statSync(slugPath).isDirectory()) continue;
|
|
17173
17632
|
} catch {
|
|
17174
17633
|
continue;
|
|
17175
17634
|
}
|
|
17176
17635
|
let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
|
|
17177
17636
|
try {
|
|
17178
17637
|
projLabel = stripTerminalEscapes(
|
|
17179
|
-
|
|
17180
|
-
).replace(
|
|
17638
|
+
fs33.readFileSync(path33.join(slugPath, ".project_root"), "utf-8").trim()
|
|
17639
|
+
).replace(os29.homedir(), "~").slice(0, 40);
|
|
17181
17640
|
} catch {
|
|
17182
17641
|
}
|
|
17183
17642
|
const chatsDir = path33.join(slugPath, "chats");
|
|
17184
|
-
if (!
|
|
17643
|
+
if (!fs33.existsSync(chatsDir)) continue;
|
|
17185
17644
|
let chatFiles;
|
|
17186
17645
|
try {
|
|
17187
|
-
chatFiles =
|
|
17646
|
+
chatFiles = fs33.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
|
|
17188
17647
|
} catch {
|
|
17189
17648
|
continue;
|
|
17190
17649
|
}
|
|
@@ -17197,7 +17656,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
17197
17656
|
onProgress?.(result.filesScanned);
|
|
17198
17657
|
let raw;
|
|
17199
17658
|
try {
|
|
17200
|
-
raw =
|
|
17659
|
+
raw = fs33.readFileSync(path33.join(chatsDir, chatFile), "utf-8");
|
|
17201
17660
|
} catch {
|
|
17202
17661
|
continue;
|
|
17203
17662
|
}
|
|
@@ -17392,13 +17851,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
17392
17851
|
return result;
|
|
17393
17852
|
}
|
|
17394
17853
|
function antigravityBrainDirs() {
|
|
17395
|
-
return ["antigravity-cli", "antigravity-ide"].map((surface) => path33.join(
|
|
17854
|
+
return ["antigravity-cli", "antigravity-ide"].map((surface) => path33.join(os29.homedir(), ".gemini", surface, "brain")).filter((p) => fs33.existsSync(p));
|
|
17396
17855
|
}
|
|
17397
17856
|
function antigravityTranscriptPath(convPath) {
|
|
17398
17857
|
const logsDir = path33.join(convPath, ".system_generated", "logs");
|
|
17399
17858
|
for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
|
|
17400
17859
|
const p = path33.join(logsDir, name);
|
|
17401
|
-
if (
|
|
17860
|
+
if (fs33.existsSync(p)) return p;
|
|
17402
17861
|
}
|
|
17403
17862
|
return null;
|
|
17404
17863
|
}
|
|
@@ -17427,14 +17886,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
17427
17886
|
for (const brainDir of brainDirs) {
|
|
17428
17887
|
let convDirs;
|
|
17429
17888
|
try {
|
|
17430
|
-
convDirs =
|
|
17889
|
+
convDirs = fs33.readdirSync(brainDir);
|
|
17431
17890
|
} catch {
|
|
17432
17891
|
continue;
|
|
17433
17892
|
}
|
|
17434
17893
|
for (const conv of convDirs) {
|
|
17435
17894
|
const convPath = path33.join(brainDir, conv);
|
|
17436
17895
|
try {
|
|
17437
|
-
if (!
|
|
17896
|
+
if (!fs33.statSync(convPath).isDirectory()) continue;
|
|
17438
17897
|
} catch {
|
|
17439
17898
|
continue;
|
|
17440
17899
|
}
|
|
@@ -17444,7 +17903,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
17444
17903
|
onProgress?.(result.filesScanned);
|
|
17445
17904
|
let raw;
|
|
17446
17905
|
try {
|
|
17447
|
-
raw =
|
|
17906
|
+
raw = fs33.readFileSync(transcriptFile, "utf-8");
|
|
17448
17907
|
} catch {
|
|
17449
17908
|
continue;
|
|
17450
17909
|
}
|
|
@@ -17512,7 +17971,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
17512
17971
|
result.bashCalls++;
|
|
17513
17972
|
const cwd = String(input.cwd ?? "");
|
|
17514
17973
|
if (cwd && projLabel === conv.slice(0, 8)) {
|
|
17515
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
17974
|
+
projLabel = stripTerminalEscapes(cwd).replace(os29.homedir(), "~").slice(0, 40);
|
|
17516
17975
|
}
|
|
17517
17976
|
}
|
|
17518
17977
|
const rawCmd = String(input.command ?? "").trimStart();
|
|
@@ -17624,7 +18083,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
17624
18083
|
}
|
|
17625
18084
|
function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
17626
18085
|
const canaryVals = safeCanaryScanValues();
|
|
17627
|
-
const sessionDir = path33.join(
|
|
18086
|
+
const sessionDir = path33.join(os29.homedir(), ".copilot", "session-state");
|
|
17628
18087
|
const result = {
|
|
17629
18088
|
filesScanned: 0,
|
|
17630
18089
|
sessions: 0,
|
|
@@ -17642,22 +18101,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
17642
18101
|
perSession: []
|
|
17643
18102
|
};
|
|
17644
18103
|
const dedup = emptyScanDedup();
|
|
17645
|
-
if (!
|
|
18104
|
+
if (!fs33.existsSync(sessionDir)) return result;
|
|
17646
18105
|
let sessionIds;
|
|
17647
18106
|
try {
|
|
17648
|
-
sessionIds =
|
|
18107
|
+
sessionIds = fs33.readdirSync(sessionDir);
|
|
17649
18108
|
} catch {
|
|
17650
18109
|
return result;
|
|
17651
18110
|
}
|
|
17652
18111
|
const ruleSources = buildRuleSources();
|
|
17653
18112
|
for (const sessionId of sessionIds) {
|
|
17654
18113
|
const eventsPath = path33.join(sessionDir, sessionId, "events.jsonl");
|
|
17655
|
-
if (!
|
|
18114
|
+
if (!fs33.existsSync(eventsPath)) continue;
|
|
17656
18115
|
result.filesScanned++;
|
|
17657
18116
|
onProgress?.(result.filesScanned);
|
|
17658
18117
|
let raw;
|
|
17659
18118
|
try {
|
|
17660
|
-
raw =
|
|
18119
|
+
raw = fs33.readFileSync(eventsPath, "utf-8");
|
|
17661
18120
|
} catch {
|
|
17662
18121
|
continue;
|
|
17663
18122
|
}
|
|
@@ -17681,7 +18140,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
17681
18140
|
if (ev.type === "session.start") {
|
|
17682
18141
|
const cwd = ev.data?.context?.cwd;
|
|
17683
18142
|
if (typeof cwd === "string" && cwd) {
|
|
17684
|
-
projLabel = stripTerminalEscapes(cwd).replace(
|
|
18143
|
+
projLabel = stripTerminalEscapes(cwd).replace(os29.homedir(), "~").slice(0, 40);
|
|
17685
18144
|
}
|
|
17686
18145
|
continue;
|
|
17687
18146
|
}
|
|
@@ -17859,7 +18318,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
17859
18318
|
onProgress?.(result.filesScanned);
|
|
17860
18319
|
let lines;
|
|
17861
18320
|
try {
|
|
17862
|
-
lines =
|
|
18321
|
+
lines = fs33.readFileSync(filePath, "utf-8").split("\n");
|
|
17863
18322
|
} catch {
|
|
17864
18323
|
continue;
|
|
17865
18324
|
}
|
|
@@ -17882,7 +18341,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
17882
18341
|
sessionId = String(payload["id"] ?? filePath);
|
|
17883
18342
|
startTime = String(payload["timestamp"] ?? "");
|
|
17884
18343
|
const cwd = String(payload["cwd"] ?? "");
|
|
17885
|
-
projLabel = stripTerminalEscapes(cwd.replace(
|
|
18344
|
+
projLabel = stripTerminalEscapes(cwd.replace(os29.homedir(), "~")).slice(0, 40);
|
|
17886
18345
|
continue;
|
|
17887
18346
|
}
|
|
17888
18347
|
if (entry.type === "event_msg" && payload["type"] === "user_message") {
|
|
@@ -18049,17 +18508,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
18049
18508
|
return result;
|
|
18050
18509
|
}
|
|
18051
18510
|
function scanShellConfig() {
|
|
18052
|
-
const home =
|
|
18511
|
+
const home = os29.homedir();
|
|
18053
18512
|
const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
|
|
18054
18513
|
(f) => path33.join(home, f)
|
|
18055
18514
|
);
|
|
18056
18515
|
const findings = [];
|
|
18057
18516
|
const seen = /* @__PURE__ */ new Set();
|
|
18058
18517
|
for (const filePath of configFiles) {
|
|
18059
|
-
if (!
|
|
18518
|
+
if (!fs33.existsSync(filePath)) continue;
|
|
18060
18519
|
let lines;
|
|
18061
18520
|
try {
|
|
18062
|
-
lines =
|
|
18521
|
+
lines = fs33.readFileSync(filePath, "utf-8").split("\n");
|
|
18063
18522
|
} catch {
|
|
18064
18523
|
continue;
|
|
18065
18524
|
}
|
|
@@ -19289,12 +19748,12 @@ var init_scan = __esm({
|
|
|
19289
19748
|
});
|
|
19290
19749
|
|
|
19291
19750
|
// src/daemon/build-id.ts
|
|
19292
|
-
import
|
|
19751
|
+
import fs34 from "fs";
|
|
19293
19752
|
import path34 from "path";
|
|
19294
19753
|
function readOwnVersion() {
|
|
19295
19754
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
19296
19755
|
try {
|
|
19297
|
-
const raw =
|
|
19756
|
+
const raw = fs34.readFileSync(path34.join(__dirname, rel), "utf-8");
|
|
19298
19757
|
const v = JSON.parse(raw).version;
|
|
19299
19758
|
if (typeof v === "string" && v.length > 0) return v;
|
|
19300
19759
|
} catch {
|
|
@@ -19305,7 +19764,7 @@ function readOwnVersion() {
|
|
|
19305
19764
|
function computeBuildId(entry = process.argv[1] ?? "") {
|
|
19306
19765
|
let mtimeMs = 0;
|
|
19307
19766
|
try {
|
|
19308
|
-
if (entry) mtimeMs =
|
|
19767
|
+
if (entry) mtimeMs = fs34.statSync(entry).mtimeMs;
|
|
19309
19768
|
} catch {
|
|
19310
19769
|
}
|
|
19311
19770
|
return { version: readOwnVersion(), mtimeMs };
|
|
@@ -19446,7 +19905,7 @@ var init_suggestion_tracker = __esm({
|
|
|
19446
19905
|
});
|
|
19447
19906
|
|
|
19448
19907
|
// src/daemon/taint-store.ts
|
|
19449
|
-
import
|
|
19908
|
+
import fs35 from "fs";
|
|
19450
19909
|
import path35 from "path";
|
|
19451
19910
|
var DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
|
|
19452
19911
|
var init_taint_store = __esm({
|
|
@@ -19517,7 +19976,7 @@ var init_taint_store = __esm({
|
|
|
19517
19976
|
/** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
|
|
19518
19977
|
_resolve(filePath) {
|
|
19519
19978
|
try {
|
|
19520
|
-
return
|
|
19979
|
+
return fs35.realpathSync.native(path35.resolve(filePath));
|
|
19521
19980
|
} catch {
|
|
19522
19981
|
return path35.resolve(filePath);
|
|
19523
19982
|
}
|
|
@@ -19684,14 +20143,14 @@ var init_session_history = __esm({
|
|
|
19684
20143
|
|
|
19685
20144
|
// src/daemon/state.ts
|
|
19686
20145
|
import net2 from "net";
|
|
19687
|
-
import
|
|
20146
|
+
import fs36 from "fs";
|
|
19688
20147
|
import path36 from "path";
|
|
19689
|
-
import
|
|
20148
|
+
import os30 from "os";
|
|
19690
20149
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
19691
20150
|
function loadInsightCounts() {
|
|
19692
20151
|
try {
|
|
19693
|
-
if (!
|
|
19694
|
-
const data = JSON.parse(
|
|
20152
|
+
if (!fs36.existsSync(INSIGHT_COUNTS_FILE)) return;
|
|
20153
|
+
const data = JSON.parse(fs36.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
|
|
19695
20154
|
for (const [tool, count] of Object.entries(data)) {
|
|
19696
20155
|
if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
|
|
19697
20156
|
}
|
|
@@ -19748,15 +20207,15 @@ function appendAuditLog(data) {
|
|
|
19748
20207
|
source: "daemon"
|
|
19749
20208
|
};
|
|
19750
20209
|
const dir = path36.dirname(AUDIT_LOG_FILE);
|
|
19751
|
-
if (!
|
|
19752
|
-
|
|
20210
|
+
if (!fs36.existsSync(dir)) fs36.mkdirSync(dir, { recursive: true });
|
|
20211
|
+
fs36.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
|
|
19753
20212
|
} catch {
|
|
19754
20213
|
}
|
|
19755
20214
|
}
|
|
19756
20215
|
function getAuditHistory(limit = 20) {
|
|
19757
20216
|
try {
|
|
19758
|
-
if (!
|
|
19759
|
-
const lines =
|
|
20217
|
+
if (!fs36.existsSync(AUDIT_LOG_FILE)) return [];
|
|
20218
|
+
const lines = fs36.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
|
|
19760
20219
|
if (lines.length === 1 && lines[0] === "") return [];
|
|
19761
20220
|
return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
|
|
19762
20221
|
} catch {
|
|
@@ -19765,7 +20224,7 @@ function getAuditHistory(limit = 20) {
|
|
|
19765
20224
|
}
|
|
19766
20225
|
function getOrgName() {
|
|
19767
20226
|
try {
|
|
19768
|
-
if (
|
|
20227
|
+
if (fs36.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
|
|
19769
20228
|
} catch {
|
|
19770
20229
|
}
|
|
19771
20230
|
return null;
|
|
@@ -19773,8 +20232,8 @@ function getOrgName() {
|
|
|
19773
20232
|
function writeGlobalSetting(key, value) {
|
|
19774
20233
|
let config = {};
|
|
19775
20234
|
try {
|
|
19776
|
-
if (
|
|
19777
|
-
config = JSON.parse(
|
|
20235
|
+
if (fs36.existsSync(GLOBAL_CONFIG_FILE)) {
|
|
20236
|
+
config = JSON.parse(fs36.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
|
|
19778
20237
|
}
|
|
19779
20238
|
} catch {
|
|
19780
20239
|
}
|
|
@@ -19786,8 +20245,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
19786
20245
|
try {
|
|
19787
20246
|
let trust = { entries: [] };
|
|
19788
20247
|
try {
|
|
19789
|
-
if (
|
|
19790
|
-
trust = JSON.parse(
|
|
20248
|
+
if (fs36.existsSync(TRUST_FILE2))
|
|
20249
|
+
trust = JSON.parse(fs36.readFileSync(TRUST_FILE2, "utf-8"));
|
|
19791
20250
|
} catch {
|
|
19792
20251
|
}
|
|
19793
20252
|
trust.entries = trust.entries.filter(
|
|
@@ -19804,8 +20263,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
|
|
|
19804
20263
|
}
|
|
19805
20264
|
function readPersistentDecisions() {
|
|
19806
20265
|
try {
|
|
19807
|
-
if (
|
|
19808
|
-
return JSON.parse(
|
|
20266
|
+
if (fs36.existsSync(DECISIONS_FILE)) {
|
|
20267
|
+
return JSON.parse(fs36.readFileSync(DECISIONS_FILE, "utf-8"));
|
|
19809
20268
|
}
|
|
19810
20269
|
} catch {
|
|
19811
20270
|
}
|
|
@@ -19833,7 +20292,7 @@ function estimateToolCost(tool, args) {
|
|
|
19833
20292
|
const filePath = a.file_path ?? a.path;
|
|
19834
20293
|
if (filePath) {
|
|
19835
20294
|
try {
|
|
19836
|
-
const bytes =
|
|
20295
|
+
const bytes = fs36.statSync(filePath).size;
|
|
19837
20296
|
return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
|
|
19838
20297
|
} catch {
|
|
19839
20298
|
}
|
|
@@ -19907,7 +20366,7 @@ function abandonPending() {
|
|
|
19907
20366
|
});
|
|
19908
20367
|
if (autoStarted) {
|
|
19909
20368
|
try {
|
|
19910
|
-
|
|
20369
|
+
fs36.unlinkSync(DAEMON_PID_FILE);
|
|
19911
20370
|
} catch {
|
|
19912
20371
|
}
|
|
19913
20372
|
setTimeout(() => {
|
|
@@ -19918,7 +20377,7 @@ function abandonPending() {
|
|
|
19918
20377
|
}
|
|
19919
20378
|
function logActivitySocket(msg) {
|
|
19920
20379
|
try {
|
|
19921
|
-
|
|
20380
|
+
fs36.appendFileSync(
|
|
19922
20381
|
path36.join(homeDir, ".node9", "hook-debug.log"),
|
|
19923
20382
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
|
|
19924
20383
|
`
|
|
@@ -19941,13 +20400,13 @@ function shouldRebind(now = Date.now()) {
|
|
|
19941
20400
|
function startActivitySocket() {
|
|
19942
20401
|
bindActivitySocket();
|
|
19943
20402
|
activityHealthInterval = setInterval(() => {
|
|
19944
|
-
if (!
|
|
20403
|
+
if (!fs36.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
|
|
19945
20404
|
}, ACTIVITY_HEALTH_PROBE_MS);
|
|
19946
20405
|
activityHealthInterval.unref();
|
|
19947
20406
|
process.on("exit", () => {
|
|
19948
20407
|
if (activityHealthInterval) clearInterval(activityHealthInterval);
|
|
19949
20408
|
try {
|
|
19950
|
-
|
|
20409
|
+
fs36.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
19951
20410
|
} catch {
|
|
19952
20411
|
}
|
|
19953
20412
|
});
|
|
@@ -19975,7 +20434,7 @@ function attemptRebind(reason) {
|
|
|
19975
20434
|
}
|
|
19976
20435
|
function bindActivitySocket() {
|
|
19977
20436
|
try {
|
|
19978
|
-
|
|
20437
|
+
fs36.unlinkSync(ACTIVITY_SOCKET_PATH2);
|
|
19979
20438
|
} catch {
|
|
19980
20439
|
}
|
|
19981
20440
|
const ACTIVITY_MAX_BYTES = 1024 * 1024;
|
|
@@ -20079,7 +20538,7 @@ var init_state2 = __esm({
|
|
|
20079
20538
|
init_session_counters();
|
|
20080
20539
|
init_session_history();
|
|
20081
20540
|
init_atomic_write();
|
|
20082
|
-
homeDir =
|
|
20541
|
+
homeDir = os30.homedir();
|
|
20083
20542
|
DAEMON_PID_FILE = path36.join(homeDir, ".node9", "daemon.pid");
|
|
20084
20543
|
DECISIONS_FILE = path36.join(homeDir, ".node9", "decisions.json");
|
|
20085
20544
|
AUDIT_LOG_FILE = path36.join(homeDir, ".node9", "audit.log");
|
|
@@ -20104,7 +20563,7 @@ var init_state2 = __esm({
|
|
|
20104
20563
|
"2h": 2 * 60 * 6e4
|
|
20105
20564
|
};
|
|
20106
20565
|
autoStarted = process.env.NODE9_AUTO_STARTED === "1";
|
|
20107
|
-
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path36.join(
|
|
20566
|
+
ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path36.join(os30.tmpdir(), "node9-activity.sock");
|
|
20108
20567
|
ACTIVITY_RING_SIZE = 100;
|
|
20109
20568
|
activityRing = [];
|
|
20110
20569
|
LARGE_RESPONSE_RING_SIZE = 20;
|
|
@@ -20143,9 +20602,9 @@ var init_state2 = __esm({
|
|
|
20143
20602
|
});
|
|
20144
20603
|
|
|
20145
20604
|
// src/posture/secrets.ts
|
|
20146
|
-
import
|
|
20605
|
+
import fs37 from "fs";
|
|
20147
20606
|
import path37 from "path";
|
|
20148
|
-
import
|
|
20607
|
+
import os31 from "os";
|
|
20149
20608
|
function displayPath(p, home) {
|
|
20150
20609
|
if (p === home) return "~";
|
|
20151
20610
|
const prefix = home.endsWith(path37.sep) ? home : home + path37.sep;
|
|
@@ -20160,7 +20619,7 @@ function safeRead(file) {
|
|
|
20160
20619
|
function candidateFiles(home, cwd) {
|
|
20161
20620
|
const files = /* @__PURE__ */ new Set();
|
|
20162
20621
|
try {
|
|
20163
|
-
for (const name of
|
|
20622
|
+
for (const name of fs37.readdirSync(cwd)) {
|
|
20164
20623
|
if (name === ".env" || name.startsWith(".env.")) files.add(path37.join(cwd, name));
|
|
20165
20624
|
}
|
|
20166
20625
|
} catch {
|
|
@@ -20190,7 +20649,7 @@ function plantedDecoyPaths() {
|
|
|
20190
20649
|
}
|
|
20191
20650
|
}
|
|
20192
20651
|
function checkSecrets(ctx) {
|
|
20193
|
-
const home = ctx.home ||
|
|
20652
|
+
const home = ctx.home || os31.homedir();
|
|
20194
20653
|
const findings = [];
|
|
20195
20654
|
const planted = plantedDecoyPaths();
|
|
20196
20655
|
const plaintext = [];
|
|
@@ -20226,7 +20685,7 @@ function checkSecrets(ctx) {
|
|
|
20226
20685
|
for (const file of credentialMaterial(home)) {
|
|
20227
20686
|
if (planted.has(file)) continue;
|
|
20228
20687
|
try {
|
|
20229
|
-
if (
|
|
20688
|
+
if (fs37.statSync(file).isFile()) {
|
|
20230
20689
|
creds.push(displayPath(file, home));
|
|
20231
20690
|
credPaths.push(file);
|
|
20232
20691
|
}
|
|
@@ -20395,10 +20854,10 @@ var init_templates = __esm({
|
|
|
20395
20854
|
});
|
|
20396
20855
|
|
|
20397
20856
|
// src/posture/egress.ts
|
|
20398
|
-
import
|
|
20857
|
+
import fs38 from "fs";
|
|
20399
20858
|
function sandboxEgressWallActive() {
|
|
20400
20859
|
try {
|
|
20401
|
-
return
|
|
20860
|
+
return fs38.existsSync(ALLOWED_DOMAINS_PATH);
|
|
20402
20861
|
} catch {
|
|
20403
20862
|
return false;
|
|
20404
20863
|
}
|
|
@@ -20568,9 +21027,9 @@ var init_gate = __esm({
|
|
|
20568
21027
|
});
|
|
20569
21028
|
|
|
20570
21029
|
// src/posture/supply-chain.ts
|
|
20571
|
-
import
|
|
21030
|
+
import os32 from "os";
|
|
20572
21031
|
import path38 from "path";
|
|
20573
|
-
import { parse as
|
|
21032
|
+
import { parse as parseToml5 } from "smol-toml";
|
|
20574
21033
|
function isNode9Managed(command, args = []) {
|
|
20575
21034
|
if (!command) return false;
|
|
20576
21035
|
if (path38.basename(command).toLowerCase() === "node9") return true;
|
|
@@ -20584,7 +21043,7 @@ function readServers(file, format, agent) {
|
|
|
20584
21043
|
const capped = readCappedText(file, MAX_CONFIG_BYTES);
|
|
20585
21044
|
if (!capped || capped.truncated) return [];
|
|
20586
21045
|
const text = capped.text;
|
|
20587
|
-
const map = format === "toml" ?
|
|
21046
|
+
const map = format === "toml" ? parseToml5(text)?.mcp_servers : JSON.parse(text)?.mcpServers;
|
|
20588
21047
|
if (!map || typeof map !== "object") return [];
|
|
20589
21048
|
return Object.entries(map).map(([name, v]) => ({
|
|
20590
21049
|
name,
|
|
@@ -20597,7 +21056,7 @@ function readServers(file, format, agent) {
|
|
|
20597
21056
|
}
|
|
20598
21057
|
}
|
|
20599
21058
|
function checkSupplyChain(ctx) {
|
|
20600
|
-
const home = ctx.home ||
|
|
21059
|
+
const home = ctx.home || os32.homedir();
|
|
20601
21060
|
const servers = [];
|
|
20602
21061
|
for (const spec of AGENT_SPECS) {
|
|
20603
21062
|
if (!spec.mcpFile) continue;
|
|
@@ -20696,11 +21155,11 @@ var init_privilege = __esm({
|
|
|
20696
21155
|
});
|
|
20697
21156
|
|
|
20698
21157
|
// src/posture/containment.ts
|
|
20699
|
-
import
|
|
21158
|
+
import fs39 from "fs";
|
|
20700
21159
|
function inContainer() {
|
|
20701
|
-
if (
|
|
21160
|
+
if (fs39.existsSync("/.dockerenv") || fs39.existsSync("/run/.containerenv")) return true;
|
|
20702
21161
|
try {
|
|
20703
|
-
const cgroup =
|
|
21162
|
+
const cgroup = fs39.readFileSync("/proc/1/cgroup", "utf8");
|
|
20704
21163
|
if (/docker|kubepods|containerd|lxc|libpod/.test(cgroup)) return true;
|
|
20705
21164
|
} catch {
|
|
20706
21165
|
}
|
|
@@ -20746,7 +21205,7 @@ var init_containment = __esm({
|
|
|
20746
21205
|
});
|
|
20747
21206
|
|
|
20748
21207
|
// src/posture/inbound.ts
|
|
20749
|
-
import
|
|
21208
|
+
import fs40 from "fs";
|
|
20750
21209
|
function buildNetworkFix(labels) {
|
|
20751
21210
|
const shielded = [
|
|
20752
21211
|
...new Map(
|
|
@@ -20804,7 +21263,7 @@ function collectListeners() {
|
|
|
20804
21263
|
const byPort = /* @__PURE__ */ new Map();
|
|
20805
21264
|
for (const file of ["/proc/net/tcp", "/proc/net/tcp6"]) {
|
|
20806
21265
|
try {
|
|
20807
|
-
for (const l of parseListeners(
|
|
21266
|
+
for (const l of parseListeners(fs40.readFileSync(file, "utf8"))) {
|
|
20808
21267
|
if (!byPort.has(l.port)) byPort.set(l.port, l);
|
|
20809
21268
|
}
|
|
20810
21269
|
} catch {
|
|
@@ -20816,11 +21275,11 @@ function readProc(pid) {
|
|
|
20816
21275
|
let comm = "unknown";
|
|
20817
21276
|
let cmdline = "";
|
|
20818
21277
|
try {
|
|
20819
|
-
comm =
|
|
21278
|
+
comm = fs40.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
|
|
20820
21279
|
} catch {
|
|
20821
21280
|
}
|
|
20822
21281
|
try {
|
|
20823
|
-
cmdline =
|
|
21282
|
+
cmdline = fs40.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
|
|
20824
21283
|
} catch {
|
|
20825
21284
|
}
|
|
20826
21285
|
return { comm, cmdline };
|
|
@@ -20830,21 +21289,21 @@ function resolveProcesses(inodes) {
|
|
|
20830
21289
|
if (inodes.size === 0) return map;
|
|
20831
21290
|
let pids;
|
|
20832
21291
|
try {
|
|
20833
|
-
pids =
|
|
21292
|
+
pids = fs40.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
|
|
20834
21293
|
} catch {
|
|
20835
21294
|
return map;
|
|
20836
21295
|
}
|
|
20837
21296
|
for (const pid of pids) {
|
|
20838
21297
|
let fds;
|
|
20839
21298
|
try {
|
|
20840
|
-
fds =
|
|
21299
|
+
fds = fs40.readdirSync(`/proc/${pid}/fd`);
|
|
20841
21300
|
} catch {
|
|
20842
21301
|
continue;
|
|
20843
21302
|
}
|
|
20844
21303
|
for (const fd of fds) {
|
|
20845
21304
|
let link;
|
|
20846
21305
|
try {
|
|
20847
|
-
link =
|
|
21306
|
+
link = fs40.readlinkSync(`/proc/${pid}/fd/${fd}`);
|
|
20848
21307
|
} catch {
|
|
20849
21308
|
continue;
|
|
20850
21309
|
}
|
|
@@ -20949,9 +21408,9 @@ var init_inbound = __esm({
|
|
|
20949
21408
|
});
|
|
20950
21409
|
|
|
20951
21410
|
// src/posture/coverage.ts
|
|
20952
|
-
import
|
|
21411
|
+
import os33 from "os";
|
|
20953
21412
|
function checkCoverage(ctx) {
|
|
20954
|
-
const home = ctx.home ||
|
|
21413
|
+
const home = ctx.home || os33.homedir();
|
|
20955
21414
|
const findings = [];
|
|
20956
21415
|
const protectedAgents = getAgentWiring(home).filter((r) => r.isProtected);
|
|
20957
21416
|
if (protectedAgents.length === 0) {
|
|
@@ -21313,7 +21772,7 @@ var init_enforcement = __esm({
|
|
|
21313
21772
|
});
|
|
21314
21773
|
|
|
21315
21774
|
// src/posture/index.ts
|
|
21316
|
-
import
|
|
21775
|
+
import os34 from "os";
|
|
21317
21776
|
function dropEnforcementRedundant(findings) {
|
|
21318
21777
|
const coveragePresent = findings.some((f) => f.category === "Coverage");
|
|
21319
21778
|
if (!coveragePresent) return findings;
|
|
@@ -21339,7 +21798,7 @@ async function runChecks(checks, ctx) {
|
|
|
21339
21798
|
}
|
|
21340
21799
|
async function runPosture(opts = {}) {
|
|
21341
21800
|
const ctx = {
|
|
21342
|
-
home: opts.home ??
|
|
21801
|
+
home: opts.home ?? os34.homedir(),
|
|
21343
21802
|
cwd: opts.cwd ?? process.cwd(),
|
|
21344
21803
|
agent: opts.agent
|
|
21345
21804
|
};
|
|
@@ -21566,53 +22025,10 @@ var init_build2 = __esm({
|
|
|
21566
22025
|
}
|
|
21567
22026
|
});
|
|
21568
22027
|
|
|
21569
|
-
// src/mcp-cmd.ts
|
|
21570
|
-
function tokenize4(cmd) {
|
|
21571
|
-
const tokens = [];
|
|
21572
|
-
let current = "";
|
|
21573
|
-
let inDouble = false;
|
|
21574
|
-
let quoted = false;
|
|
21575
|
-
let i = 0;
|
|
21576
|
-
while (i < cmd.length) {
|
|
21577
|
-
const ch = cmd[i];
|
|
21578
|
-
if (inDouble) {
|
|
21579
|
-
if (ch === '"') inDouble = false;
|
|
21580
|
-
else if (ch === "\\" && i + 1 < cmd.length) current += cmd[++i];
|
|
21581
|
-
else current += ch;
|
|
21582
|
-
} else if (ch === '"') {
|
|
21583
|
-
inDouble = true;
|
|
21584
|
-
quoted = true;
|
|
21585
|
-
} else if (ch === " " || ch === " ") {
|
|
21586
|
-
if (current || quoted) {
|
|
21587
|
-
tokens.push(current);
|
|
21588
|
-
current = "";
|
|
21589
|
-
quoted = false;
|
|
21590
|
-
}
|
|
21591
|
-
} else if (ch === "\\" && i + 1 < cmd.length) {
|
|
21592
|
-
current += cmd[++i];
|
|
21593
|
-
} else {
|
|
21594
|
-
current += ch;
|
|
21595
|
-
}
|
|
21596
|
-
i++;
|
|
21597
|
-
}
|
|
21598
|
-
if (current || quoted && !inDouble) tokens.push(current);
|
|
21599
|
-
return tokens;
|
|
21600
|
-
}
|
|
21601
|
-
function quoteArg(s) {
|
|
21602
|
-
if (s === "") return '""';
|
|
21603
|
-
if (/[\s"\\]/.test(s)) return `"${s.replace(/(["\\])/g, "\\$1")}"`;
|
|
21604
|
-
return s;
|
|
21605
|
-
}
|
|
21606
|
-
var init_mcp_cmd = __esm({
|
|
21607
|
-
"src/mcp-cmd.ts"() {
|
|
21608
|
-
"use strict";
|
|
21609
|
-
}
|
|
21610
|
-
});
|
|
21611
|
-
|
|
21612
22028
|
// src/daemon/mcp-tools.ts
|
|
21613
|
-
import
|
|
22029
|
+
import fs41 from "fs";
|
|
21614
22030
|
import path40 from "path";
|
|
21615
|
-
import
|
|
22031
|
+
import os35 from "os";
|
|
21616
22032
|
function deriveServerName(cmd) {
|
|
21617
22033
|
if (!cmd || typeof cmd !== "string") return "MCP Server";
|
|
21618
22034
|
const strip = (s) => s.replace(/^(mcp-server|server|mcp)-/i, "").replace(/-(mcp-server|server|mcp)$/i, "") || s;
|
|
@@ -21638,13 +22054,13 @@ function deriveServerName(cmd) {
|
|
|
21638
22054
|
return strip(base) || "MCP Server";
|
|
21639
22055
|
}
|
|
21640
22056
|
function getMcpToolsFile() {
|
|
21641
|
-
return path40.join(
|
|
22057
|
+
return path40.join(os35.homedir(), ".node9", "mcp-tools.json");
|
|
21642
22058
|
}
|
|
21643
22059
|
function readMcpToolsConfig() {
|
|
21644
22060
|
try {
|
|
21645
22061
|
const file = getMcpToolsFile();
|
|
21646
|
-
if (!
|
|
21647
|
-
const raw =
|
|
22062
|
+
if (!fs41.existsSync(file)) return {};
|
|
22063
|
+
const raw = fs41.readFileSync(file, "utf-8");
|
|
21648
22064
|
return JSON.parse(raw);
|
|
21649
22065
|
} catch {
|
|
21650
22066
|
return {};
|
|
@@ -21654,10 +22070,10 @@ function writeMcpToolsConfig(config) {
|
|
|
21654
22070
|
try {
|
|
21655
22071
|
const file = getMcpToolsFile();
|
|
21656
22072
|
const dir = path40.dirname(file);
|
|
21657
|
-
if (!
|
|
21658
|
-
const tmpPath = `${file}.${
|
|
21659
|
-
|
|
21660
|
-
|
|
22073
|
+
if (!fs41.existsSync(dir)) fs41.mkdirSync(dir, { recursive: true });
|
|
22074
|
+
const tmpPath = `${file}.${os35.hostname()}.${process.pid}.tmp`;
|
|
22075
|
+
fs41.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
|
|
22076
|
+
fs41.renameSync(tmpPath, file);
|
|
21661
22077
|
} catch (e) {
|
|
21662
22078
|
console.error("Failed to write mcp-tools.json", e);
|
|
21663
22079
|
}
|
|
@@ -21711,109 +22127,6 @@ var init_mcp_tools = __esm({
|
|
|
21711
22127
|
}
|
|
21712
22128
|
});
|
|
21713
22129
|
|
|
21714
|
-
// src/mcp-wrap.ts
|
|
21715
|
-
import fs41 from "fs";
|
|
21716
|
-
import os35 from "os";
|
|
21717
|
-
import { parse as parseToml5, stringify as stringifyToml2 } from "smol-toml";
|
|
21718
|
-
function isNode9Command(command) {
|
|
21719
|
-
return /(^|[\\/])node9(\.(exe|cmd|ps1|bat))?$/i.test(command ?? "");
|
|
21720
|
-
}
|
|
21721
|
-
function classifyMcp(s) {
|
|
21722
|
-
if (isNode9Command(s.command)) {
|
|
21723
|
-
return (s.args ?? [])[0] === "mcp-gateway" ? "gatewayed" : "node9-self";
|
|
21724
|
-
}
|
|
21725
|
-
if (typeof s.command !== "string" || s.command.trim() === "") return "remote";
|
|
21726
|
-
return "ungoverned";
|
|
21727
|
-
}
|
|
21728
|
-
function toGateway(s, configName) {
|
|
21729
|
-
const upstream = [s.command ?? "", ...s.args ?? []].map(quoteArg).join(" ");
|
|
21730
|
-
const nameArgs = configName && !configName.startsWith("-") ? ["--config-name", configName] : [];
|
|
21731
|
-
return {
|
|
21732
|
-
...s,
|
|
21733
|
-
command: "node9",
|
|
21734
|
-
args: ["mcp-gateway", ...nameArgs, "--upstream", upstream]
|
|
21735
|
-
};
|
|
21736
|
-
}
|
|
21737
|
-
function fromGateway(s) {
|
|
21738
|
-
if (!isNode9Command(s.command) || (s.args ?? [])[0] !== "mcp-gateway") return null;
|
|
21739
|
-
const args = s.args ?? [];
|
|
21740
|
-
const i = args.indexOf("--upstream");
|
|
21741
|
-
if (i < 0 || !args[i + 1]) return null;
|
|
21742
|
-
const [command, ...rest] = tokenize4(args[i + 1]);
|
|
21743
|
-
if (!command) return null;
|
|
21744
|
-
return { ...s, command, args: rest };
|
|
21745
|
-
}
|
|
21746
|
-
function inventoryMcp(home = os35.homedir()) {
|
|
21747
|
-
const out = [];
|
|
21748
|
-
for (const spec of AGENT_SPECS) {
|
|
21749
|
-
if (!spec.mcpFile) continue;
|
|
21750
|
-
const mcpFile = spec.mcpFile(home);
|
|
21751
|
-
const format = spec.mcpFormat ?? "json";
|
|
21752
|
-
const servers = readMcpServers(mcpFile, format);
|
|
21753
|
-
for (const [name, s] of Object.entries(servers)) {
|
|
21754
|
-
if (!s || typeof s !== "object") continue;
|
|
21755
|
-
out.push({
|
|
21756
|
-
agent: String(spec.id),
|
|
21757
|
-
agentLabel: spec.label,
|
|
21758
|
-
mcpFile,
|
|
21759
|
-
format,
|
|
21760
|
-
name,
|
|
21761
|
-
command: s.command ?? "",
|
|
21762
|
-
args: Array.isArray(s.args) ? s.args : [],
|
|
21763
|
-
state: classifyMcp(s),
|
|
21764
|
-
raw: s
|
|
21765
|
-
});
|
|
21766
|
-
}
|
|
21767
|
-
}
|
|
21768
|
-
return out;
|
|
21769
|
-
}
|
|
21770
|
-
function inventoryServerKeys(inv) {
|
|
21771
|
-
const keys = /* @__PURE__ */ new Set();
|
|
21772
|
-
for (const e of inv) {
|
|
21773
|
-
if (e.state === "gatewayed") {
|
|
21774
|
-
const i = e.args.indexOf("--upstream");
|
|
21775
|
-
if (i >= 0 && e.args[i + 1]) {
|
|
21776
|
-
keys.add(getServerKey(e.args[i + 1]));
|
|
21777
|
-
}
|
|
21778
|
-
} else if (e.state === "ungoverned") {
|
|
21779
|
-
const cmd = [e.command, ...e.args].map(quoteArg).join(" ");
|
|
21780
|
-
keys.add(getServerKey(cmd));
|
|
21781
|
-
}
|
|
21782
|
-
}
|
|
21783
|
-
return keys;
|
|
21784
|
-
}
|
|
21785
|
-
function writeMcpEntry(mcpFile, format, name, entry) {
|
|
21786
|
-
const key = format === "toml" ? "mcp_servers" : "mcpServers";
|
|
21787
|
-
let root = {};
|
|
21788
|
-
if (fs41.existsSync(mcpFile)) {
|
|
21789
|
-
const raw = fs41.readFileSync(mcpFile, "utf-8");
|
|
21790
|
-
root = format === "toml" ? parseToml5(raw) : JSON.parse(raw);
|
|
21791
|
-
const bak = `${mcpFile}.node9-bak`;
|
|
21792
|
-
try {
|
|
21793
|
-
fs41.writeFileSync(bak, raw, { mode: 384, flag: "wx" });
|
|
21794
|
-
} catch (e) {
|
|
21795
|
-
if (e.code !== "EEXIST") throw e;
|
|
21796
|
-
}
|
|
21797
|
-
}
|
|
21798
|
-
const existing = root[key];
|
|
21799
|
-
const servers = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
21800
|
-
servers[name] = entry;
|
|
21801
|
-
root[key] = servers;
|
|
21802
|
-
const serialized = format === "toml" ? stringifyToml2(root) : JSON.stringify(root, null, 2);
|
|
21803
|
-
const tmp = `${mcpFile}.${process.pid}.tmp`;
|
|
21804
|
-
fs41.writeFileSync(tmp, serialized, { mode: 384 });
|
|
21805
|
-
fs41.renameSync(tmp, mcpFile);
|
|
21806
|
-
}
|
|
21807
|
-
var init_mcp_wrap = __esm({
|
|
21808
|
-
"src/mcp-wrap.ts"() {
|
|
21809
|
-
"use strict";
|
|
21810
|
-
init_agent_wiring();
|
|
21811
|
-
init_mcp_cmd();
|
|
21812
|
-
init_mcp_pin();
|
|
21813
|
-
init_mcp_cmd();
|
|
21814
|
-
}
|
|
21815
|
-
});
|
|
21816
|
-
|
|
21817
22130
|
// src/mcp-status.ts
|
|
21818
22131
|
function substituteEnv(input, env = process.env) {
|
|
21819
22132
|
const missing = [];
|
|
@@ -52282,20 +52595,20 @@ init_agent_wiring();
|
|
|
52282
52595
|
// src/credentials.ts
|
|
52283
52596
|
init_config();
|
|
52284
52597
|
init_api_url();
|
|
52285
|
-
import * as
|
|
52286
|
-
import * as
|
|
52598
|
+
import * as fs22 from "fs";
|
|
52599
|
+
import * as os19 from "os";
|
|
52287
52600
|
import * as path22 from "path";
|
|
52288
52601
|
function writeCredentialsAndConfig(apiKey, opts = {}) {
|
|
52289
52602
|
const profileName = opts.profileName || "default";
|
|
52290
|
-
const home = opts.homeDir ??
|
|
52603
|
+
const home = opts.homeDir ?? os19.homedir();
|
|
52291
52604
|
const credPath = path22.join(home, ".node9", "credentials.json");
|
|
52292
|
-
if (!
|
|
52293
|
-
|
|
52605
|
+
if (!fs22.existsSync(path22.dirname(credPath))) {
|
|
52606
|
+
fs22.mkdirSync(path22.dirname(credPath), { recursive: true });
|
|
52294
52607
|
}
|
|
52295
52608
|
let existingCreds = {};
|
|
52296
52609
|
try {
|
|
52297
|
-
if (
|
|
52298
|
-
const raw = JSON.parse(
|
|
52610
|
+
if (fs22.existsSync(credPath)) {
|
|
52611
|
+
const raw = JSON.parse(fs22.readFileSync(credPath, "utf-8"));
|
|
52299
52612
|
existingCreds = raw.apiKey ? { default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL } } : raw;
|
|
52300
52613
|
}
|
|
52301
52614
|
} catch {
|
|
@@ -52305,7 +52618,7 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
|
|
|
52305
52618
|
apiUrl: DEFAULT_API_URL,
|
|
52306
52619
|
...opts.isLocal ? { localOnly: true } : {}
|
|
52307
52620
|
};
|
|
52308
|
-
|
|
52621
|
+
fs22.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), {
|
|
52309
52622
|
mode: 384
|
|
52310
52623
|
});
|
|
52311
52624
|
let effectiveCloud = null;
|
|
@@ -52313,8 +52626,8 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
|
|
|
52313
52626
|
const configPath = path22.join(home, ".node9", "config.json");
|
|
52314
52627
|
let config = {};
|
|
52315
52628
|
try {
|
|
52316
|
-
if (
|
|
52317
|
-
config = JSON.parse(
|
|
52629
|
+
if (fs22.existsSync(configPath)) {
|
|
52630
|
+
config = JSON.parse(fs22.readFileSync(configPath, "utf-8"));
|
|
52318
52631
|
}
|
|
52319
52632
|
} catch {
|
|
52320
52633
|
}
|
|
@@ -52329,10 +52642,10 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
|
|
|
52329
52642
|
terminal: typeof existing.terminal === "boolean" ? existing.terminal : d.terminal,
|
|
52330
52643
|
cloud: !opts.isLocal
|
|
52331
52644
|
};
|
|
52332
|
-
if (!
|
|
52333
|
-
|
|
52645
|
+
if (!fs22.existsSync(path22.dirname(configPath))) {
|
|
52646
|
+
fs22.mkdirSync(path22.dirname(configPath), { recursive: true });
|
|
52334
52647
|
}
|
|
52335
|
-
|
|
52648
|
+
fs22.writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
52336
52649
|
mode: 384
|
|
52337
52650
|
});
|
|
52338
52651
|
effectiveCloud = !opts.isLocal;
|
|
@@ -56756,6 +57069,7 @@ function registerInitCommand(program2) {
|
|
|
56756
57069
|
|
|
56757
57070
|
// src/cli/commands/heal.ts
|
|
56758
57071
|
init_agent_wiring();
|
|
57072
|
+
init_mcp_wrap();
|
|
56759
57073
|
init_setup();
|
|
56760
57074
|
init_hook_baseline();
|
|
56761
57075
|
import chalk21 from "chalk";
|
|
@@ -56767,8 +57081,28 @@ function backupForHeal(file) {
|
|
|
56767
57081
|
} catch {
|
|
56768
57082
|
}
|
|
56769
57083
|
}
|
|
57084
|
+
function reportCorruptedMcpWraps() {
|
|
57085
|
+
const corrupted = findCorruptedMcpWraps();
|
|
57086
|
+
if (corrupted.length === 0) return;
|
|
57087
|
+
console.log(
|
|
57088
|
+
chalk21.yellow(
|
|
57089
|
+
` \u26A0\uFE0F ${corrupted.length} MCP server(s) were corrupted by an older node9 and cannot start:`
|
|
57090
|
+
)
|
|
57091
|
+
);
|
|
57092
|
+
for (const c of corrupted) {
|
|
57093
|
+
console.log(chalk21.yellow(` \u2022 ${c.name} (${c.agentLabel})`));
|
|
57094
|
+
console.log(chalk21.gray(` ${c.mcpFile}`));
|
|
57095
|
+
console.log(chalk21.gray(` stored upstream: ${c.upstream}`));
|
|
57096
|
+
}
|
|
57097
|
+
console.log(
|
|
57098
|
+
chalk21.gray(
|
|
57099
|
+
"\n The original command lost its path separators, so node9 cannot recover it.\n Remove and re-add each server in the agent, then run `node9 init` to re-wrap.\n"
|
|
57100
|
+
)
|
|
57101
|
+
);
|
|
57102
|
+
}
|
|
56770
57103
|
async function runHeal(name) {
|
|
56771
57104
|
console.log(chalk21.cyan.bold("\n\u{1FA79} Node9 Heal\n"));
|
|
57105
|
+
reportCorruptedMcpWraps();
|
|
56772
57106
|
const baseline = loadHookBaseline();
|
|
56773
57107
|
const wiring = getAgentWiring();
|
|
56774
57108
|
let candidates2 = wiring.filter(
|
|
@@ -60535,7 +60869,7 @@ var COLOR = {
|
|
|
60535
60869
|
medium: chalk32.yellow,
|
|
60536
60870
|
advisory: chalk32.gray
|
|
60537
60871
|
};
|
|
60538
|
-
var ACTION_URL = "https://github.com/marketplace/actions/node9-agent-security
|
|
60872
|
+
var ACTION_URL = "https://github.com/marketplace/actions/node9-agent-security?ref=cli_scan_repo";
|
|
60539
60873
|
function renderCta(res) {
|
|
60540
60874
|
const L = [];
|
|
60541
60875
|
L.push(chalk32.dim(" " + "\u2500".repeat(63)));
|