@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 CHANGED
@@ -995,6 +995,7 @@ function normalizeCommandForPolicyImpl(command) {
995
995
  const source = command.slice(s, e);
996
996
  if (resolved === source) continue;
997
997
  if (resolved === "" || /\s/.test(resolved)) continue;
998
+ if (/^[;&|()<>]+$/.test(resolved)) continue;
998
999
  rewrites.push([s, e, resolved]);
999
1000
  const quoteOnly = source.replace(/['"]/g, "");
1000
1001
  if (quoteOnly !== source) quoteOnlyRewrites.push([s, e, quoteOnly]);
@@ -1344,23 +1345,147 @@ function extractLiteralArgs(callExpr) {
1344
1345
  const args = positionedArgs(words);
1345
1346
  return { name, flags, paths: args.map((a) => a.value), words, args };
1346
1347
  }
1348
+ function payloadKey(payload) {
1349
+ if (!assignmentTable || assignmentTable.size === 0) return payload;
1350
+ const bindings = [];
1351
+ for (const [name, rec] of assignmentTable) {
1352
+ if (rec.value !== null) bindings.push(`${name}=${rec.value}`);
1353
+ }
1354
+ return `${payload}\0${bindings.sort().join("")}`;
1355
+ }
1356
+ function claimPayload(payload) {
1357
+ if (!seenPayloads) return "ok";
1358
+ const key = payloadKey(payload);
1359
+ if (seenPayloads.has(key)) return "seen";
1360
+ if (payloadBudget <= 0) return "exhausted";
1361
+ payloadBudget--;
1362
+ seenPayloads.add(key);
1363
+ return "ok";
1364
+ }
1365
+ function recordTopLevelAssignments(f) {
1366
+ const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
1367
+ for (const stmt of stmts) recordTopLevelStmt(stmt);
1368
+ }
1369
+ function probeBinOp(src) {
1370
+ try {
1371
+ const cmd = syntax.NewParser().Parse(src, "probe")?.Stmts?.[0]?.Cmd;
1372
+ if (cmd && syntax.NodeType(cmd) === "BinaryCmd") return cmd.Op;
1373
+ } catch {
1374
+ }
1375
+ return null;
1376
+ }
1377
+ function recordTopLevelStmt(stmt) {
1378
+ if (!stmt || !stmt.Cmd) return false;
1379
+ const t = syntax.NodeType(stmt.Cmd);
1380
+ if (t === "BinaryCmd") {
1381
+ if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
1382
+ if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
1383
+ if (stmt.Cmd.Op === AND_OP) return recordTopLevelStmt(stmt.Cmd.Y);
1384
+ return true;
1385
+ }
1386
+ if (t !== "CallExpr" && t !== "DeclClause") return false;
1387
+ let at = 0;
1388
+ try {
1389
+ at = stmt.Pos().Offset();
1390
+ } catch {
1391
+ at = 0;
1392
+ }
1393
+ recordAssignments(stmt.Cmd, at);
1394
+ if (stmt.Negated) return false;
1395
+ if (t === "DeclClause") return ASSIGNMENT_HEADS.has(stmt.Cmd.Variant?.Value ?? "");
1396
+ return (stmt.Cmd.Args || []).length === 0 && (stmt.Cmd.Assigns || []).length > 0;
1397
+ }
1398
+ function recordAssignments(n, at) {
1399
+ if (!assignmentTable) return;
1400
+ const t = syntax.NodeType(n);
1401
+ let assigns = [];
1402
+ if (t === "CallExpr") {
1403
+ if ((n.Args || []).length > 0) return;
1404
+ assigns = n.Assigns || [];
1405
+ } else if (t === "DeclClause") {
1406
+ if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
1407
+ assigns = (n.Args || []).filter((a) => syntax.NodeType(a) === "Assign");
1408
+ } else return;
1409
+ for (const a of assigns) {
1410
+ const name = a?.Name?.Value;
1411
+ if (!name || !a.Value || a.Append) continue;
1412
+ assignmentTable.set(name, { value: resolveWordLiteral(a.Value), at });
1413
+ }
1414
+ }
1415
+ function resolveTrivialSubst(part) {
1416
+ if (syntax.NodeType(part) !== "CmdSubst") return void 0;
1417
+ const stmts = part.Stmts || [];
1418
+ if (stmts.length !== 1) return void 0;
1419
+ const st = stmts[0];
1420
+ if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
1421
+ const cmd = st.Cmd;
1422
+ if (!cmd || syntax.NodeType(cmd) !== "CallExpr") return void 0;
1423
+ if ((cmd.Assigns || []).length > 0) return void 0;
1424
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
1425
+ if (words.length === 0 || words.some((w) => w === null)) return void 0;
1426
+ const head = baseWord(words[0]);
1427
+ const rest = words.slice(1);
1428
+ if (head === "echo") {
1429
+ let i = 0;
1430
+ while (i < rest.length && /^-[neE]+$/.test(rest[i])) i++;
1431
+ return rest.slice(i).join(" ");
1432
+ }
1433
+ if (head === "printf") {
1434
+ if (rest.length !== 2) return void 0;
1435
+ if (!/^%s(\\n)?$/.test(rest[0])) return void 0;
1436
+ return rest[1];
1437
+ }
1438
+ return void 0;
1439
+ }
1440
+ function recordedExpansion(name) {
1441
+ if (!assignmentTable || !name) return void 0;
1442
+ const rec = assignmentTable.get(name);
1443
+ if (rec === void 0 || rec.at >= currentStmtOffset) return void 0;
1444
+ return rec.value;
1445
+ }
1446
+ function isPlainParam(p) {
1447
+ if (syntax.NodeType(p) !== "ParamExp") return false;
1448
+ return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
1449
+ }
1450
+ function expandPlainParam(p) {
1451
+ if (!assignmentTable) return void 0;
1452
+ if (!isPlainParam(p)) return void 0;
1453
+ const recorded = recordedExpansion(p.Param?.Value);
1454
+ if (recorded !== void 0) return recorded;
1455
+ return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
1456
+ }
1347
1457
  function resolveWordLiteral(w) {
1348
1458
  const parts = w?.Parts || [];
1349
1459
  let s = "";
1350
1460
  for (const p of parts) {
1351
- const t = syntax.NodeType(p);
1352
- if (t === "Lit") s += (p.Value ?? "").replace(/\\(.)/g, "$1");
1353
- else if (t === "SglQuoted") s += p.Value ?? "";
1354
- else if (t === "DblQuoted") {
1355
- const inner = p.Parts || [];
1356
- if (!inner.every((ip) => syntax.NodeType(ip) === "Lit")) return null;
1357
- s += inner.map((ip) => ip.Value ?? "").join("");
1358
- } else {
1359
- return null;
1360
- }
1461
+ const piece = resolvePart(p, false);
1462
+ if (piece === void 0 || piece === null) return null;
1463
+ s += piece;
1361
1464
  }
1362
1465
  return s;
1363
1466
  }
1467
+ function resolvePart(p, inQuotes) {
1468
+ const t = syntax.NodeType(p);
1469
+ if (t === "Lit") {
1470
+ const raw = p.Value ?? "";
1471
+ if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
1472
+ return assignmentTable ? raw.replace(/\\([$`"\\])/g, "$1") : raw;
1473
+ }
1474
+ if (t === "SglQuoted") return p.Value ?? "";
1475
+ if (t === "ParamExp") return expandPlainParam(p);
1476
+ if (t === "CmdSubst") return assignmentTable ? resolveTrivialSubst(p) : void 0;
1477
+ if (t === "DblQuoted" && !inQuotes) {
1478
+ const inner = p.Parts || [];
1479
+ let out = "";
1480
+ for (const ip of inner) {
1481
+ const piece = resolvePart(ip, true);
1482
+ if (piece === void 0 || piece === null) return piece;
1483
+ out += piece;
1484
+ }
1485
+ return out;
1486
+ }
1487
+ return void 0;
1488
+ }
1364
1489
  function parseDestHost(token) {
1365
1490
  if (!token) return null;
1366
1491
  let t = token.trim();
@@ -1415,6 +1540,7 @@ function destTokensForBinary(binary, args) {
1415
1540
  case "ssh":
1416
1541
  return positionals.slice(0, 1);
1417
1542
  case "scp":
1543
+ case "rsync":
1418
1544
  return positionals.filter((p) => p.includes(":") || p.includes("@"));
1419
1545
  case "nc":
1420
1546
  case "ncat":
@@ -1604,17 +1730,35 @@ function analyzeFsOperationImpl(command, depth = 0) {
1604
1730
  const f = parseShared(command);
1605
1731
  if (f === PARSE_FAIL) return null;
1606
1732
  let result = null;
1733
+ const outerTable = assignmentTable;
1734
+ const outerOffset = currentStmtOffset;
1735
+ const outerSeen = seenPayloads;
1736
+ const outerBudget = payloadBudget;
1737
+ if (depth === 0) {
1738
+ seenPayloads = /* @__PURE__ */ new Set();
1739
+ payloadBudget = PAYLOAD_BUDGET;
1740
+ }
1741
+ assignmentTable = new Map(
1742
+ [...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
1743
+ );
1744
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
1607
1745
  try {
1746
+ recordTopLevelAssignments(f);
1608
1747
  syntax.Walk(f, (node) => {
1609
1748
  if (!node || result?.verdict === "block") return false;
1610
1749
  const n = node;
1611
1750
  const nodeType = syntax.NodeType(n);
1612
1751
  if (nodeType === "Stmt") {
1752
+ try {
1753
+ currentStmtOffset = n.Pos().Offset();
1754
+ } catch {
1755
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
1756
+ }
1613
1757
  result = stricter(result, jailedRedirectRead(n));
1614
1758
  return result?.verdict !== "block";
1615
1759
  }
1616
1760
  if (nodeType !== "CallExpr") return true;
1617
- const { name, flags, paths, words, args } = extractLiteralArgs(n);
1761
+ const { name, flags, paths, words } = extractLiteralArgs(n);
1618
1762
  if (!name) return true;
1619
1763
  if (name === "rm") {
1620
1764
  const flagStr = flags.join("").toLowerCase();
@@ -1643,9 +1787,30 @@ function analyzeFsOperationImpl(command, depth = 0) {
1643
1787
  }
1644
1788
  }
1645
1789
  }
1646
- if (depth < 1) {
1790
+ if (depth < 24 && name === "find") {
1791
+ for (const action of findActions(words, 0)) {
1792
+ const h = unwrapCommandHead(action);
1793
+ const inner = literalShellPayload(action.slice(h), baseWord(action[h]));
1794
+ if (inner === null) continue;
1795
+ const claim = claimPayload(inner);
1796
+ if (claim === "exhausted") {
1797
+ result = stricter(result, UNANALYSABLE_NESTING);
1798
+ continue;
1799
+ }
1800
+ if (claim === "seen") continue;
1801
+ const v = analyzeFsOperationImpl(inner, depth + 1);
1802
+ result = stricter(result, v);
1803
+ if (result?.verdict === "block") return false;
1804
+ }
1805
+ }
1806
+ if (depth < 24) {
1647
1807
  const payload = literalShellPayload(words, name);
1648
- if (payload !== null) {
1808
+ const claim = payload === null ? "seen" : claimPayload(payload);
1809
+ if (claim === "exhausted") {
1810
+ result = stricter(result, UNANALYSABLE_NESTING);
1811
+ return true;
1812
+ }
1813
+ if (payload !== null && claim === "ok") {
1649
1814
  const inner = analyzeFsOperationImpl(payload, depth + 1);
1650
1815
  if (inner) {
1651
1816
  result = inner;
@@ -1654,7 +1819,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
1654
1819
  return true;
1655
1820
  }
1656
1821
  }
1657
- const readPaths = FS_READ_TOOLS.has(name) ? [...readTargets(name, args, flags, words, 1), ...flagOperandFiles(name, words, 1)] : wrappedReadPaths(words, name);
1822
+ const readPaths = FS_READ_TOOLS.has(name) ? readerPaths(words, 0) : wrappedReadPaths(words, name);
1658
1823
  if (readPaths) {
1659
1824
  for (const p of readPaths) {
1660
1825
  result = stricter(result, matchSensitivePath2(p));
@@ -1669,6 +1834,11 @@ function analyzeFsOperationImpl(command, depth = 0) {
1669
1834
  return result;
1670
1835
  } catch {
1671
1836
  return null;
1837
+ } finally {
1838
+ assignmentTable = outerTable;
1839
+ currentStmtOffset = outerOffset;
1840
+ seenPayloads = outerSeen;
1841
+ if (depth === 0) payloadBudget = outerBudget;
1672
1842
  }
1673
1843
  }
1674
1844
  function stricter(a, b) {
@@ -1725,6 +1895,18 @@ function resolveCopyShape(words, h) {
1725
1895
  }
1726
1896
  return null;
1727
1897
  }
1898
+ function findAction(words, k) {
1899
+ const end = words.findIndex((w, i) => i > k && (w === ";" || w === "+"));
1900
+ return words.slice(k + 1, end < 0 ? words.length : end);
1901
+ }
1902
+ function findActions(words, h) {
1903
+ const out = [];
1904
+ for (let i = h + 1; i < words.length; i++) {
1905
+ const w = words[i];
1906
+ if (w !== null && FIND_EXEC_FLAGS.has(w)) out.push(findAction(words, i));
1907
+ }
1908
+ return out;
1909
+ }
1728
1910
  function findStartPoints(words, h) {
1729
1911
  const k = words.findIndex((w, i) => i > h && w !== null && FIND_EXEC_FLAGS.has(w));
1730
1912
  if (k < 0) return { k, starts: [] };
@@ -1740,8 +1922,13 @@ function copySourcePaths(words) {
1740
1922
  if (fi >= 0) {
1741
1923
  const { k, starts } = findStartPoints(words, fi);
1742
1924
  if (k < 0) return [];
1743
- const action = unwrapCommandHead(words.slice(k + 1));
1744
- return resolveCopyShape(words.slice(k + 1), action) ? starts : [];
1925
+ const out = [];
1926
+ for (const action of findActions(words, fi)) {
1927
+ const h2 = unwrapCommandHead(action);
1928
+ if (!resolveCopyShape(action, h2)) continue;
1929
+ out.push(...starts, ...copySourcePaths(action));
1930
+ }
1931
+ return out;
1745
1932
  }
1746
1933
  if (!COPY_VERB_HEADS.has(baseWord(words[h]))) return [];
1747
1934
  const r = resolveCopyShape(words, h);
@@ -1918,6 +2105,15 @@ function flagEffect(token, shape, known) {
1918
2105
  }
1919
2106
  return NONE;
1920
2107
  }
2108
+ function readerPaths(words, h) {
2109
+ const head = baseWord(words[h]);
2110
+ const from = h + 1;
2111
+ const flags = words.slice(from).filter((w) => w !== null && w.startsWith("-"));
2112
+ return [
2113
+ ...readTargets(head, positionedArgs(words, from), flags, words, from),
2114
+ ...flagOperandFiles(head, words, from)
2115
+ ];
2116
+ }
1921
2117
  function readTargets(verb, args, flags, words = [], from = 1) {
1922
2118
  const shape = PATTERN_VERBS[verb];
1923
2119
  if (!shape) return args.map((a) => a.value);
@@ -1999,18 +2195,21 @@ function flagOperandFiles(verb, words, from) {
1999
2195
  function wrappedReadPaths(words, name) {
2000
2196
  if (name === "find") {
2001
2197
  const { k, starts } = findStartPoints(words, 0);
2002
- return k > 0 && isReaderWord(words[k + 1] ?? null) ? starts : null;
2198
+ if (k < 0) return null;
2199
+ const paths = [];
2200
+ let reads = false;
2201
+ for (const action of findActions(words, 0)) {
2202
+ const h2 = unwrapCommandHead(action);
2203
+ if (!isReaderWord(action[h2] ?? null)) continue;
2204
+ reads = true;
2205
+ paths.push(...readerPaths(action, h2));
2206
+ }
2207
+ return reads ? [...starts, ...paths] : paths;
2003
2208
  }
2004
2209
  if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
2005
2210
  const h = unwrapCommandHead(words);
2006
2211
  if (h <= 0 || !isReaderWord(words[h] ?? null)) return null;
2007
- const head = baseWord(words[h]);
2008
- const rest = words.slice(h + 1);
2009
- const restFlags = rest.filter((w) => w !== null && w.startsWith("-"));
2010
- return [
2011
- ...readTargets(head, positionedArgs(words, h + 1), restFlags, words, h + 1),
2012
- ...flagOperandFiles(head, words, h + 1)
2013
- ];
2212
+ return readerPaths(words, h);
2014
2213
  }
2015
2214
  function literalShellPayload(words, name) {
2016
2215
  const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
@@ -3773,7 +3972,7 @@ function* stringValues(obj, depth = 0) {
3773
3972
  }
3774
3973
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
3775
3974
  }
3776
- var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, 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;
3975
+ var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, 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;
3777
3976
  var init_dist = __esm({
3778
3977
  "packages/policy-engine/dist/index.mjs"() {
3779
3978
  "use strict";
@@ -5158,6 +5357,60 @@ var init_dist = __esm({
5158
5357
  "rsync"
5159
5358
  ]);
5160
5359
  VALUE_FLAGS = {
5360
+ // rsync 3.2.7, its own --help: every flag whose operand could be mistaken for
5361
+ // a host. `-e ssh` is the one that matters most (`ssh` is not the destination).
5362
+ rsync: /* @__PURE__ */ new Set([
5363
+ "-e",
5364
+ "--rsh",
5365
+ "-f",
5366
+ "--filter",
5367
+ "-T",
5368
+ "--temp-dir",
5369
+ "-B",
5370
+ "--block-size",
5371
+ "-M",
5372
+ "--remote-option",
5373
+ "--exclude",
5374
+ "--exclude-from",
5375
+ "--include",
5376
+ "--include-from",
5377
+ "--files-from",
5378
+ "--compare-dest",
5379
+ "--copy-dest",
5380
+ "--link-dest",
5381
+ "--partial-dir",
5382
+ "--log-file",
5383
+ "--password-file",
5384
+ "--bwlimit",
5385
+ "--timeout",
5386
+ "--contimeout",
5387
+ "--port",
5388
+ "--sockopts",
5389
+ "--address",
5390
+ "--chmod",
5391
+ "--chown",
5392
+ "--max-size",
5393
+ "--min-size",
5394
+ "--modify-window",
5395
+ "--out-format",
5396
+ "--log-file-format",
5397
+ "--backup-dir",
5398
+ "--suffix",
5399
+ "--iconv",
5400
+ "--max-delete",
5401
+ "--checksum-choice",
5402
+ "--info",
5403
+ "--debug",
5404
+ "--stderr",
5405
+ "--outbuf",
5406
+ "--skip-compress",
5407
+ "--usermap",
5408
+ "--groupmap",
5409
+ "--mkpath",
5410
+ "--write-batch",
5411
+ "--read-batch",
5412
+ "--only-write-batch"
5413
+ ]),
5161
5414
  curl: /* @__PURE__ */ new Set([
5162
5415
  "-d",
5163
5416
  "--data",
@@ -5241,6 +5494,22 @@ var init_dist = __esm({
5241
5494
  ]),
5242
5495
  nc: /* @__PURE__ */ new Set(["-p", "-s", "-w", "-X", "-x", "-e", "-g", "-G", "-i", "-O", "-T", "-q", "-m"])
5243
5496
  };
5497
+ HOME_VARIABLES = /* @__PURE__ */ new Set(["HOME", "USERPROFILE"]);
5498
+ assignmentTable = null;
5499
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
5500
+ PAYLOAD_BUDGET = 256;
5501
+ payloadBudget = 0;
5502
+ seenPayloads = null;
5503
+ UNANALYSABLE_NESTING = {
5504
+ ruleName: "review-unanalysable-nesting",
5505
+ verdict: "review",
5506
+ reason: "This command nests more wrapped shell payloads than the policy engine will unwrap, so some of what it runs was not read.",
5507
+ path: ""
5508
+ };
5509
+ ASSIGNMENT_HEADS = /* @__PURE__ */ new Set(["export", "declare", "local", "readonly", "typeset"]);
5510
+ AND_OP = probeBinOp("a && b");
5511
+ OR_OP = probeBinOp("a || b");
5512
+ AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
5244
5513
  FS_OP_CACHE_MAX = 5e3;
5245
5514
  fsOpCache = /* @__PURE__ */ new Map();
5246
5515
  stripDotSlash = (p) => p.replace(/^\.\//, "");
@@ -6317,7 +6586,7 @@ var init_dist = __esm({
6317
6586
  { view: "separators-stripped", decoder: "separators", stripped: true }
6318
6587
  ];
6319
6588
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
6320
- CANONICAL_EXTRACTOR_VERSION = "canonical-v17";
6589
+ CANONICAL_EXTRACTOR_VERSION = "canonical-v18";
6321
6590
  DEDUPE_PREVIEW_LEN = 120;
6322
6591
  ENGINE_VERSION = "1.4.0";
6323
6592
  }
@@ -10710,116 +10979,284 @@ var init_core = __esm({
10710
10979
  }
10711
10980
  });
10712
10981
 
10713
- // src/utils/platform-shell.ts
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
10733
- function readTomlSafe(filePath) {
10982
+ // src/agent-wiring.ts
10983
+ function readJson(filePath) {
10984
+ if (!import_fs16.default.existsSync(filePath)) return null;
10734
10985
  try {
10735
- return (0, import_smol_toml.parse)(import_fs16.default.readFileSync(filePath, "utf-8"));
10986
+ return JSON.parse(import_fs16.default.readFileSync(filePath, "utf-8"));
10736
10987
  } catch {
10737
- return null;
10988
+ return "invalid";
10738
10989
  }
10739
10990
  }
10740
- function lastCodexAuditTs(auditLogPath) {
10741
- let text;
10991
+ function matchersHaveNode9Hook(matchers) {
10992
+ return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
10993
+ }
10994
+ function flatHaveNode9Hook(entries) {
10995
+ return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
10996
+ }
10997
+ function readHookRoot(filePath, format) {
10998
+ if (!import_fs16.default.existsSync(filePath)) return "absent";
10999
+ let raw;
10742
11000
  try {
10743
- text = import_fs16.default.readFileSync(auditLogPath, "utf-8");
11001
+ raw = import_fs16.default.readFileSync(filePath, "utf-8");
10744
11002
  } catch {
10745
- return null;
11003
+ return "absent";
10746
11004
  }
10747
- let newest = null;
10748
- for (const line of text.split("\n")) {
10749
- if (!line.includes('"Codex"')) continue;
10750
- try {
10751
- const row = JSON.parse(line);
10752
- if (row.agent !== "Codex" || typeof row.ts !== "string") continue;
10753
- if (newest === null || row.ts > newest) newest = row.ts;
10754
- } catch {
10755
- }
11005
+ try {
11006
+ const parsed = format === "yaml" ? yaml.parse(raw) : JSON.parse(raw);
11007
+ return parsed?.hooks ?? {};
11008
+ } catch {
11009
+ return "invalid";
10756
11010
  }
10757
- return newest;
10758
11011
  }
10759
- function assessCodexTrustFrom(input) {
10760
- const { hooksDisabled, trustEntries, hooksWrittenAt, lastCodexActivityAt } = input;
10761
- let state;
10762
- if (hooksDisabled) state = "disabled";
10763
- else if (trustEntries === 0) state = "never-trusted";
10764
- else if (hooksWrittenAt && lastCodexActivityAt && lastCodexActivityAt > hooksWrittenAt) {
10765
- state = "observed";
10766
- } else state = "unverified";
10767
- return { state, hooksWrittenAt, lastCodexActivityAt, trustEntries };
11012
+ function eventWired(root, ev, format) {
11013
+ const arr = root[ev.key];
11014
+ if (format === "matcher") return matchersHaveNode9Hook(arr);
11015
+ return flatHaveNode9Hook(arr);
10768
11016
  }
10769
- function assessCodexTrust(home = import_os13.default.homedir(), auditLogPath = import_path17.default.join(home, ".node9", "audit.log")) {
10770
- const hooksPath = import_path17.default.join(home, ".codex", "hooks.json");
10771
- const configPath = import_path17.default.join(home, ".codex", "config.toml");
10772
- let hooksWrittenAt = null;
11017
+ function detectMcp(servers) {
11018
+ const entries = Object.entries(servers ?? {});
11019
+ const present = entries.some(([, s]) => s?.command === "node9");
11020
+ const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
11021
+ return { wrapped, present };
11022
+ }
11023
+ function readMcpServers(filePath, format) {
11024
+ if (!import_fs16.default.existsSync(filePath)) return {};
10773
11025
  try {
10774
- hooksWrittenAt = import_fs16.default.statSync(hooksPath).mtime.toISOString();
11026
+ if (format === "toml") {
11027
+ const parsed2 = (0, import_smol_toml.parse)(import_fs16.default.readFileSync(filePath, "utf-8"));
11028
+ return parsed2?.mcp_servers ?? {};
11029
+ }
11030
+ const parsed = readJson(filePath);
11031
+ if (parsed === null || parsed === "invalid") return {};
11032
+ return parsed.mcpServers ?? {};
10775
11033
  } catch {
10776
- hooksWrittenAt = null;
11034
+ return {};
10777
11035
  }
10778
- const config = readTomlSafe(configPath);
10779
- const hooksDisabled = config?.features?.hooks === false || config?.codex_hooks === false;
10780
- const trustEntries = Object.keys(config?.hooks?.state ?? {}).length;
10781
- return assessCodexTrustFrom({
10782
- hooksDisabled,
10783
- trustEntries,
10784
- hooksWrittenAt,
10785
- lastCodexActivityAt: lastCodexAuditTs(auditLogPath)
10786
- });
10787
11036
  }
10788
- function findCodexTui(env = process.env) {
11037
+ function readMcp(filePath, format) {
11038
+ if (!import_fs16.default.existsSync(filePath)) return { wrapped: [], present: false };
10789
11039
  try {
10790
- const r = (0, import_child_process2.spawnSync)(locatorCommand(), ["codex"], { encoding: "utf-8", timeout: 3e3 });
10791
- const first = (r.stdout ?? "").split(/\r?\n/).find((l) => l.trim());
10792
- if (r.status === 0 && first) return "codex";
10793
- } catch {
10794
- }
10795
- if (process.platform === "win32" && env.LOCALAPPDATA) {
10796
- const binDir = import_path17.default.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin");
10797
- try {
10798
- for (const d of import_fs16.default.readdirSync(binDir)) {
10799
- const exe = import_path17.default.join(binDir, d, "codex.exe");
10800
- if (import_fs16.default.existsSync(exe)) return `"${exe}"`;
10801
- }
10802
- } catch {
11040
+ if (format === "toml") {
11041
+ const parsed2 = (0, import_smol_toml.parse)(import_fs16.default.readFileSync(filePath, "utf-8"));
11042
+ return detectMcp(parsed2?.mcp_servers);
10803
11043
  }
11044
+ const parsed = readJson(filePath);
11045
+ if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
11046
+ return detectMcp(parsed.mcpServers);
11047
+ } catch {
11048
+ return { wrapped: [], present: false };
10804
11049
  }
10805
- return null;
10806
11050
  }
10807
- function codexTrustInstruction(tui = findCodexTui()) {
10808
- 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";
10809
- return ` \u279C Codex must trust these hooks once: ${where},
10810
- then choose "Trust all and continue" when Codex asks to review them.
10811
- Until then Codex runs unprotected. Every rewrite of hooks.json needs this again.`;
11051
+ function getAgentWiring(home = import_os13.default.homedir()) {
11052
+ const detected = detectAgents(home);
11053
+ return AGENT_SPECS.map((spec) => {
11054
+ const present = spec.present(home);
11055
+ const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
11056
+ let hooks;
11057
+ let wireState;
11058
+ let hookLabel;
11059
+ let settingsPath;
11060
+ if (spec.shimFile) {
11061
+ const shimWired = exists(spec.shimFile(home));
11062
+ hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
11063
+ wireState = shimWired ? "wired" : present ? "unwired" : "absent";
11064
+ hookLabel = "node9 plugin";
11065
+ settingsPath = spec.shimFile(home);
11066
+ } else {
11067
+ const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
11068
+ const primary = spec.hookEvents[0];
11069
+ const rootPresent = root !== "absent" && root !== "invalid";
11070
+ hooks = spec.hookEvents.map((ev) => ({
11071
+ label: hookLabelOf(ev, pad),
11072
+ wired: rootPresent && eventWired(root, ev, spec.hookFormat)
11073
+ }));
11074
+ if (root === "absent") wireState = "absent";
11075
+ else if (root === "invalid") wireState = "invalid";
11076
+ else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
11077
+ hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
11078
+ settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
11079
+ }
11080
+ const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
11081
+ const anyHookWired = hooks.some((h) => h.wired);
11082
+ return {
11083
+ id: spec.id,
11084
+ label: spec.label,
11085
+ setupCommand: spec.setupCommand,
11086
+ installed: detected[spec.id],
11087
+ present,
11088
+ hooks,
11089
+ wireState,
11090
+ hookLabel,
11091
+ settingsPath,
11092
+ configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
11093
+ mcpServers: mcp ? mcp.wrapped : null,
11094
+ mcpProtected: mcp ? mcp.present : false,
11095
+ isProtected: anyHookWired || (mcp?.present ?? false)
11096
+ };
11097
+ });
10812
11098
  }
10813
- var import_fs16, import_path17, import_os13, import_child_process2, import_smol_toml;
10814
- var init_codex_trust = __esm({
10815
- "src/codex-trust.ts"() {
11099
+ var import_fs16, import_path17, import_os13, yaml, import_smol_toml, exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
11100
+ var init_agent_wiring = __esm({
11101
+ "src/agent-wiring.ts"() {
10816
11102
  "use strict";
10817
11103
  import_fs16 = __toESM(require("fs"));
10818
11104
  import_path17 = __toESM(require("path"));
10819
11105
  import_os13 = __toESM(require("os"));
10820
- import_child_process2 = require("child_process");
11106
+ yaml = __toESM(require("yaml"));
10821
11107
  import_smol_toml = require("smol-toml");
10822
- init_platform_shell();
11108
+ init_setup();
11109
+ exists = (p) => {
11110
+ try {
11111
+ return import_fs16.default.existsSync(p);
11112
+ } catch {
11113
+ return false;
11114
+ }
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) => import_path17.default.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) => import_path17.default.join(h, ".claude.json"),
11132
+ present: (h) => exists(import_path17.default.join(h, ".claude", "settings.json")) || exists(import_path17.default.join(h, ".claude.json"))
11133
+ },
11134
+ {
11135
+ id: "gemini",
11136
+ label: "Gemini CLI",
11137
+ setupCommand: "node9 agents add gemini",
11138
+ hookFile: (h) => import_path17.default.join(h, ".gemini", "settings.json"),
11139
+ hookFormat: "matcher",
11140
+ hookEvents: [ck("BeforeTool"), lg("AfterTool")],
11141
+ mcpFile: (h) => import_path17.default.join(h, ".gemini", "settings.json"),
11142
+ present: (h) => exists(import_path17.default.join(h, ".gemini", "settings.json"))
11143
+ },
11144
+ {
11145
+ id: "codex",
11146
+ label: "Codex",
11147
+ setupCommand: "node9 agents add codex",
11148
+ hookFile: (h) => import_path17.default.join(h, ".codex", "hooks.json"),
11149
+ hookFormat: "matcher",
11150
+ hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
11151
+ mcpFile: (h) => import_path17.default.join(h, ".codex", "config.toml"),
11152
+ mcpFormat: "toml",
11153
+ present: (h) => exists(import_path17.default.join(h, ".codex"))
11154
+ },
11155
+ {
11156
+ id: "antigravity",
11157
+ label: "Antigravity",
11158
+ setupCommand: "node9 agents add antigravity",
11159
+ hookFile: (h) => import_path17.default.join(h, ".gemini", "config", "hooks.json"),
11160
+ hookFormat: "matcher",
11161
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
11162
+ mcpFile: (h) => import_path17.default.join(h, ".gemini", "config", "mcp_config.json"),
11163
+ present: (h) => exists(import_path17.default.join(h, ".gemini", "config", "hooks.json")) || exists(import_path17.default.join(h, ".gemini", "antigravity-cli")) || exists(import_path17.default.join(h, ".gemini", "antigravity-ide"))
11164
+ },
11165
+ {
11166
+ id: "copilot",
11167
+ label: "GitHub Copilot",
11168
+ setupCommand: "node9 agents add copilot",
11169
+ hookFile: (h) => import_path17.default.join(h, ".copilot", "hooks", "node9.json"),
11170
+ hookFormat: "flat",
11171
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
11172
+ mcpFile: (h) => import_path17.default.join(h, ".copilot", "mcp-config.json"),
11173
+ present: (h) => exists(import_path17.default.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) => import_path17.default.join(h, ".cursor", "mcp.json"),
11183
+ present: (h) => exists(import_path17.default.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) => import_path17.default.join(opencodeConfigDir(h), "plugins", "node9.js"),
11205
+ present: (h) => exists(opencodeConfigDir(h)) || exists(import_path17.default.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) => import_path17.default.join(h, ".pi", "agent", "extensions", "node9.js"),
11214
+ present: (h) => exists(import_path17.default.join(h, ".pi", "agent")) || exists(import_path17.default.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;
11246
+ }
11247
+ i++;
11248
+ }
11249
+ if (current || quoted && !inDouble) tokens.push(current);
11250
+ return tokens;
11251
+ }
11252
+ function quoteArg(s) {
11253
+ if (s === "") return '""';
11254
+ if (/[\s"\\]/.test(s)) return `"${s.replace(/(["\\])/g, "\\$1")}"`;
11255
+ return s;
11256
+ }
11257
+ var init_mcp_cmd = __esm({
11258
+ "src/mcp-cmd.ts"() {
11259
+ "use strict";
10823
11260
  }
10824
11261
  });
10825
11262
 
@@ -10981,10 +11418,252 @@ var init_mcp_pin = __esm({
10981
11418
  }
10982
11419
  });
10983
11420
 
11421
+ // src/mcp-wrap.ts
11422
+ function isNode9Command(command) {
11423
+ return /(^|[\\/])node9(\.(exe|cmd|ps1|bat))?$/i.test(command ?? "");
11424
+ }
11425
+ function classifyMcp(s) {
11426
+ if (isNode9Command(s.command)) {
11427
+ return (s.args ?? [])[0] === "mcp-gateway" ? "gatewayed" : "node9-self";
11428
+ }
11429
+ if (typeof s.command !== "string" || s.command.trim() === "") return "remote";
11430
+ return "ungoverned";
11431
+ }
11432
+ function mcpUpstreamString(s) {
11433
+ return [s.command ?? "", ...s.args ?? []].map(quoteArg).join(" ");
11434
+ }
11435
+ function toGateway(s, configName) {
11436
+ const upstream = mcpUpstreamString(s);
11437
+ const nameArgs = configName && !configName.startsWith("-") ? ["--config-name", configName] : [];
11438
+ return {
11439
+ ...s,
11440
+ command: "node9",
11441
+ args: ["mcp-gateway", ...nameArgs, "--upstream", upstream]
11442
+ };
11443
+ }
11444
+ function fromGateway(s) {
11445
+ if (!isNode9Command(s.command) || (s.args ?? [])[0] !== "mcp-gateway") return null;
11446
+ const args = s.args ?? [];
11447
+ const i = args.indexOf("--upstream");
11448
+ if (i < 0 || !args[i + 1]) return null;
11449
+ const [command, ...rest] = tokenize4(args[i + 1]);
11450
+ if (!command) return null;
11451
+ return { ...s, command, args: rest };
11452
+ }
11453
+ function inventoryMcp(home = import_os15.default.homedir()) {
11454
+ const out = [];
11455
+ for (const spec of AGENT_SPECS) {
11456
+ if (!spec.mcpFile) continue;
11457
+ const mcpFile = spec.mcpFile(home);
11458
+ const format = spec.mcpFormat ?? "json";
11459
+ const servers = readMcpServers(mcpFile, format);
11460
+ for (const [name, s] of Object.entries(servers)) {
11461
+ if (!s || typeof s !== "object") continue;
11462
+ out.push({
11463
+ agent: String(spec.id),
11464
+ agentLabel: spec.label,
11465
+ mcpFile,
11466
+ format,
11467
+ name,
11468
+ command: s.command ?? "",
11469
+ args: Array.isArray(s.args) ? s.args : [],
11470
+ state: classifyMcp(s),
11471
+ raw: s
11472
+ });
11473
+ }
11474
+ }
11475
+ return out;
11476
+ }
11477
+ function isCorruptedUpstream(upstream) {
11478
+ if (!upstream) return false;
11479
+ if (upstream.includes("\\") || upstream.includes("/")) return false;
11480
+ return /(^|\s)[A-Za-z]:[^\s]/.test(upstream);
11481
+ }
11482
+ function findCorruptedMcpWraps(home = import_os15.default.homedir()) {
11483
+ const out = [];
11484
+ for (const e of inventoryMcp(home)) {
11485
+ if (e.state !== "gatewayed") continue;
11486
+ const i = e.args.indexOf("--upstream");
11487
+ const upstream = i >= 0 ? e.args[i + 1] ?? "" : "";
11488
+ if (!isCorruptedUpstream(upstream)) continue;
11489
+ out.push({
11490
+ agent: e.agent,
11491
+ agentLabel: e.agentLabel,
11492
+ mcpFile: e.mcpFile,
11493
+ name: e.name,
11494
+ upstream
11495
+ });
11496
+ }
11497
+ return out;
11498
+ }
11499
+ function inventoryServerKeys(inv) {
11500
+ const keys = /* @__PURE__ */ new Set();
11501
+ for (const e of inv) {
11502
+ if (e.state === "gatewayed") {
11503
+ const i = e.args.indexOf("--upstream");
11504
+ if (i >= 0 && e.args[i + 1]) {
11505
+ keys.add(getServerKey(e.args[i + 1]));
11506
+ }
11507
+ } else if (e.state === "ungoverned") {
11508
+ const cmd = [e.command, ...e.args].map(quoteArg).join(" ");
11509
+ keys.add(getServerKey(cmd));
11510
+ }
11511
+ }
11512
+ return keys;
11513
+ }
11514
+ function writeMcpEntry(mcpFile, format, name, entry) {
11515
+ const key = format === "toml" ? "mcp_servers" : "mcpServers";
11516
+ let root = {};
11517
+ if (import_fs18.default.existsSync(mcpFile)) {
11518
+ const raw = import_fs18.default.readFileSync(mcpFile, "utf-8");
11519
+ root = format === "toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(raw);
11520
+ const bak = `${mcpFile}.node9-bak`;
11521
+ try {
11522
+ import_fs18.default.writeFileSync(bak, raw, { mode: 384, flag: "wx" });
11523
+ } catch (e) {
11524
+ if (e.code !== "EEXIST") throw e;
11525
+ }
11526
+ }
11527
+ const existing = root[key];
11528
+ const servers = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
11529
+ servers[name] = entry;
11530
+ root[key] = servers;
11531
+ const serialized = format === "toml" ? (0, import_smol_toml2.stringify)(root) : JSON.stringify(root, null, 2);
11532
+ const tmp = `${mcpFile}.${process.pid}.tmp`;
11533
+ import_fs18.default.writeFileSync(tmp, serialized, { mode: 384 });
11534
+ import_fs18.default.renameSync(tmp, mcpFile);
11535
+ }
11536
+ var import_fs18, import_os15, import_smol_toml2;
11537
+ var init_mcp_wrap = __esm({
11538
+ "src/mcp-wrap.ts"() {
11539
+ "use strict";
11540
+ import_fs18 = __toESM(require("fs"));
11541
+ import_os15 = __toESM(require("os"));
11542
+ import_smol_toml2 = require("smol-toml");
11543
+ init_agent_wiring();
11544
+ init_mcp_cmd();
11545
+ init_mcp_pin();
11546
+ init_mcp_cmd();
11547
+ }
11548
+ });
11549
+
11550
+ // src/utils/platform-shell.ts
11551
+ function locatorCommand() {
11552
+ return process.platform === "win32" ? "where" : "which";
11553
+ }
11554
+ function shellInvocation(command) {
11555
+ if (process.platform === "win32") {
11556
+ return {
11557
+ file: process.env.ComSpec || "cmd.exe",
11558
+ args: ["/d", "/s", "/c", command]
11559
+ };
11560
+ }
11561
+ return { file: "/bin/bash", args: ["-c", command] };
11562
+ }
11563
+ var init_platform_shell = __esm({
11564
+ "src/utils/platform-shell.ts"() {
11565
+ "use strict";
11566
+ }
11567
+ });
11568
+
11569
+ // src/codex-trust.ts
11570
+ function readTomlSafe(filePath) {
11571
+ try {
11572
+ return (0, import_smol_toml3.parse)(import_fs19.default.readFileSync(filePath, "utf-8"));
11573
+ } catch {
11574
+ return null;
11575
+ }
11576
+ }
11577
+ function lastCodexAuditTs(auditLogPath) {
11578
+ let text;
11579
+ try {
11580
+ text = import_fs19.default.readFileSync(auditLogPath, "utf-8");
11581
+ } catch {
11582
+ return null;
11583
+ }
11584
+ let newest = null;
11585
+ for (const line of text.split("\n")) {
11586
+ if (!line.includes('"Codex"')) continue;
11587
+ try {
11588
+ const row = JSON.parse(line);
11589
+ if (row.agent !== "Codex" || typeof row.ts !== "string") continue;
11590
+ if (newest === null || row.ts > newest) newest = row.ts;
11591
+ } catch {
11592
+ }
11593
+ }
11594
+ return newest;
11595
+ }
11596
+ function assessCodexTrustFrom(input) {
11597
+ const { hooksDisabled, trustEntries, hooksWrittenAt, lastCodexActivityAt } = input;
11598
+ let state;
11599
+ if (hooksDisabled) state = "disabled";
11600
+ else if (trustEntries === 0) state = "never-trusted";
11601
+ else if (hooksWrittenAt && lastCodexActivityAt && lastCodexActivityAt > hooksWrittenAt) {
11602
+ state = "observed";
11603
+ } else state = "unverified";
11604
+ return { state, hooksWrittenAt, lastCodexActivityAt, trustEntries };
11605
+ }
11606
+ function assessCodexTrust(home = import_os16.default.homedir(), auditLogPath = import_path19.default.join(home, ".node9", "audit.log")) {
11607
+ const hooksPath = import_path19.default.join(home, ".codex", "hooks.json");
11608
+ const configPath = import_path19.default.join(home, ".codex", "config.toml");
11609
+ let hooksWrittenAt = null;
11610
+ try {
11611
+ hooksWrittenAt = import_fs19.default.statSync(hooksPath).mtime.toISOString();
11612
+ } catch {
11613
+ hooksWrittenAt = null;
11614
+ }
11615
+ const config = readTomlSafe(configPath);
11616
+ const hooksDisabled = config?.features?.hooks === false || config?.codex_hooks === false;
11617
+ const trustEntries = Object.keys(config?.hooks?.state ?? {}).length;
11618
+ return assessCodexTrustFrom({
11619
+ hooksDisabled,
11620
+ trustEntries,
11621
+ hooksWrittenAt,
11622
+ lastCodexActivityAt: lastCodexAuditTs(auditLogPath)
11623
+ });
11624
+ }
11625
+ function findCodexTui(env = process.env) {
11626
+ try {
11627
+ const r = (0, import_child_process2.spawnSync)(locatorCommand(), ["codex"], { encoding: "utf-8", timeout: 3e3 });
11628
+ const first = (r.stdout ?? "").split(/\r?\n/).find((l) => l.trim());
11629
+ if (r.status === 0 && first) return "codex";
11630
+ } catch {
11631
+ }
11632
+ if (process.platform === "win32" && env.LOCALAPPDATA) {
11633
+ const binDir = import_path19.default.join(env.LOCALAPPDATA, "OpenAI", "Codex", "bin");
11634
+ try {
11635
+ for (const d of import_fs19.default.readdirSync(binDir)) {
11636
+ const exe = import_path19.default.join(binDir, d, "codex.exe");
11637
+ if (import_fs19.default.existsSync(exe)) return `"${exe}"`;
11638
+ }
11639
+ } catch {
11640
+ }
11641
+ }
11642
+ return null;
11643
+ }
11644
+ function codexTrustInstruction(tui = findCodexTui()) {
11645
+ 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";
11646
+ return ` \u279C Codex must trust these hooks once: ${where},
11647
+ then choose "Trust all and continue" when Codex asks to review them.
11648
+ Until then Codex runs unprotected. Every rewrite of hooks.json needs this again.`;
11649
+ }
11650
+ var import_fs19, import_path19, import_os16, import_child_process2, import_smol_toml3;
11651
+ var init_codex_trust = __esm({
11652
+ "src/codex-trust.ts"() {
11653
+ "use strict";
11654
+ import_fs19 = __toESM(require("fs"));
11655
+ import_path19 = __toESM(require("path"));
11656
+ import_os16 = __toESM(require("os"));
11657
+ import_child_process2 = require("child_process");
11658
+ import_smol_toml3 = require("smol-toml");
11659
+ init_platform_shell();
11660
+ }
11661
+ });
11662
+
10984
11663
  // src/daemon/hook-baseline.ts
10985
11664
  function loadHookBaseline() {
10986
11665
  try {
10987
- const raw = JSON.parse(import_fs18.default.readFileSync(BASELINE_FILE, "utf-8"));
11666
+ const raw = JSON.parse(import_fs20.default.readFileSync(BASELINE_FILE, "utf-8"));
10988
11667
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
10989
11668
  } catch {
10990
11669
  return {};
@@ -10992,9 +11671,9 @@ function loadHookBaseline() {
10992
11671
  }
10993
11672
  function saveHookBaseline(b) {
10994
11673
  try {
10995
- const dir = import_path19.default.dirname(BASELINE_FILE);
10996
- if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
10997
- import_fs18.default.writeFileSync(BASELINE_FILE, JSON.stringify(b, null, 2), { mode: 384 });
11674
+ const dir = import_path20.default.dirname(BASELINE_FILE);
11675
+ if (!import_fs20.default.existsSync(dir)) import_fs20.default.mkdirSync(dir, { recursive: true });
11676
+ import_fs20.default.writeFileSync(BASELINE_FILE, JSON.stringify(b, null, 2), { mode: 384 });
10998
11677
  } catch {
10999
11678
  }
11000
11679
  }
@@ -11016,14 +11695,14 @@ function seedHookBaselineIfEmpty(governedNow, now) {
11016
11695
  function clearHookBaseline() {
11017
11696
  for (const f of [BASELINE_FILE, NOTIFIED_FILE]) {
11018
11697
  try {
11019
- import_fs18.default.rmSync(f, { force: true });
11698
+ import_fs20.default.rmSync(f, { force: true });
11020
11699
  } catch {
11021
11700
  }
11022
11701
  }
11023
11702
  }
11024
11703
  function loadNotified() {
11025
11704
  try {
11026
- const raw = JSON.parse(import_fs18.default.readFileSync(NOTIFIED_FILE, "utf-8"));
11705
+ const raw = JSON.parse(import_fs20.default.readFileSync(NOTIFIED_FILE, "utf-8"));
11027
11706
  return new Set(Array.isArray(raw) ? raw : []);
11028
11707
  } catch {
11029
11708
  return /* @__PURE__ */ new Set();
@@ -11031,21 +11710,21 @@ function loadNotified() {
11031
11710
  }
11032
11711
  function saveNotified(s) {
11033
11712
  try {
11034
- const dir = import_path19.default.dirname(NOTIFIED_FILE);
11035
- if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
11036
- import_fs18.default.writeFileSync(NOTIFIED_FILE, JSON.stringify([...s]), { mode: 384 });
11713
+ const dir = import_path20.default.dirname(NOTIFIED_FILE);
11714
+ if (!import_fs20.default.existsSync(dir)) import_fs20.default.mkdirSync(dir, { recursive: true });
11715
+ import_fs20.default.writeFileSync(NOTIFIED_FILE, JSON.stringify([...s]), { mode: 384 });
11037
11716
  } catch {
11038
11717
  }
11039
11718
  }
11040
- var import_fs18, import_path19, import_os15, BASELINE_FILE, NOTIFIED_FILE;
11719
+ var import_fs20, import_path20, import_os17, BASELINE_FILE, NOTIFIED_FILE;
11041
11720
  var init_hook_baseline = __esm({
11042
11721
  "src/daemon/hook-baseline.ts"() {
11043
11722
  "use strict";
11044
- import_fs18 = __toESM(require("fs"));
11045
- import_path19 = __toESM(require("path"));
11046
- import_os15 = __toESM(require("os"));
11047
- BASELINE_FILE = import_path19.default.join(import_os15.default.homedir(), ".node9", "hooks-baseline.json");
11048
- NOTIFIED_FILE = import_path19.default.join(import_os15.default.homedir(), ".node9", "hook-heal-notified.json");
11723
+ import_fs20 = __toESM(require("fs"));
11724
+ import_path20 = __toESM(require("path"));
11725
+ import_os17 = __toESM(require("os"));
11726
+ BASELINE_FILE = import_path20.default.join(import_os17.default.homedir(), ".node9", "hooks-baseline.json");
11727
+ NOTIFIED_FILE = import_path20.default.join(import_os17.default.homedir(), ".node9", "hook-heal-notified.json");
11049
11728
  }
11050
11729
  });
11051
11730
 
@@ -11453,7 +12132,7 @@ function printInlineAskNotice() {
11453
12132
  )
11454
12133
  );
11455
12134
  }
11456
- function fullPathCommand(subcommand, platform = process.platform, home = import_os16.default.homedir()) {
12135
+ function fullPathCommand(subcommand, platform = process.platform, home = import_os18.default.homedir()) {
11457
12136
  if (process.env.NODE9_TESTING === "1") return `node9 ${subcommand}`;
11458
12137
  const nodeExec = toForwardSlashes(process.execPath);
11459
12138
  const cliScript = toForwardSlashes(process.argv[1]);
@@ -11465,8 +12144,8 @@ function fullPathCommand(subcommand, platform = process.platform, home = import_
11465
12144
  ensureHookShim(home, nodeExec, cliScript);
11466
12145
  return `"${toForwardSlashes(hookShimPath(home))}" ${subcommand}`;
11467
12146
  }
11468
- function hookShimPath(home = import_os16.default.homedir()) {
11469
- return import_path20.default.join(home, ".node9", "bin", "hook");
12147
+ function hookShimPath(home = import_os18.default.homedir()) {
12148
+ return import_path21.default.join(home, ".node9", "bin", "hook");
11470
12149
  }
11471
12150
  function hookShimBody(nodeExec, cliScript) {
11472
12151
  return `#!/bin/sh
@@ -11480,12 +12159,12 @@ function ensureHookShim(home, nodeExec, cliScript) {
11480
12159
  const shim = hookShimPath(home);
11481
12160
  const body = hookShimBody(nodeExec, cliScript);
11482
12161
  try {
11483
- if (import_fs19.default.readFileSync(shim, "utf-8") === body) return false;
12162
+ if (import_fs21.default.readFileSync(shim, "utf-8") === body) return false;
11484
12163
  } catch {
11485
12164
  }
11486
- import_fs19.default.mkdirSync(import_path20.default.dirname(shim), { recursive: true });
11487
- import_fs19.default.writeFileSync(shim, body, { mode: 493 });
11488
- import_fs19.default.chmodSync(shim, 493);
12165
+ import_fs21.default.mkdirSync(import_path21.default.dirname(shim), { recursive: true });
12166
+ import_fs21.default.writeFileSync(shim, body, { mode: 493 });
12167
+ import_fs21.default.chmodSync(shim, 493);
11489
12168
  return true;
11490
12169
  }
11491
12170
  function toForwardSlashes(p) {
@@ -11504,7 +12183,7 @@ function isStaleHookCommand(command) {
11504
12183
  while ((m = re.exec(command)) !== null) tokens.push(m[1] ?? m[2] ?? "");
11505
12184
  for (const tok of tokens) {
11506
12185
  if (!tok.startsWith("/") && !/^[A-Za-z]:\//.test(tok)) continue;
11507
- if (!import_fs19.default.existsSync(tok)) return true;
12186
+ if (!import_fs21.default.existsSync(tok)) return true;
11508
12187
  }
11509
12188
  return false;
11510
12189
  }
@@ -11524,19 +12203,19 @@ function isChurnProneHookForm(command, platform = process.platform) {
11524
12203
  function needsRewrite(command, platform = process.platform) {
11525
12204
  return isStaleHookCommand(command) || isLegacyHookFormat(command) || isWindowsQuoteBrokenHook(command, platform) || isChurnProneHookForm(command, platform);
11526
12205
  }
11527
- function readJson(filePath) {
12206
+ function readJson2(filePath) {
11528
12207
  try {
11529
- if (import_fs19.default.existsSync(filePath)) {
11530
- return JSON.parse(import_fs19.default.readFileSync(filePath, "utf-8"));
12208
+ if (import_fs21.default.existsSync(filePath)) {
12209
+ return JSON.parse(import_fs21.default.readFileSync(filePath, "utf-8"));
11531
12210
  }
11532
12211
  } catch {
11533
12212
  }
11534
12213
  return null;
11535
12214
  }
11536
12215
  function writeJson(filePath, data) {
11537
- const dir = import_path20.default.dirname(filePath);
11538
- if (!import_fs19.default.existsSync(dir)) import_fs19.default.mkdirSync(dir, { recursive: true });
11539
- import_fs19.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
12216
+ const dir = import_path21.default.dirname(filePath);
12217
+ if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
12218
+ import_fs21.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
11540
12219
  }
11541
12220
  function mcpWrapArgs(upstream) {
11542
12221
  return [MCP_WRAP_SUBCOMMAND, "--upstream", upstream];
@@ -11556,6 +12235,14 @@ function repairLegacyMcpWraps(servers) {
11556
12235
  }
11557
12236
  return repaired;
11558
12237
  }
12238
+ function isCodexAppManagedServer(name, server) {
12239
+ if (!server) return false;
12240
+ const cmd = (server.command ?? "").replace(/\\/g, "/").toLowerCase();
12241
+ if (cmd.includes("/openai/codex/runtimes/")) return true;
12242
+ const envKeys = Object.keys(server.env ?? {});
12243
+ if (envKeys.some((k) => k.startsWith("NODE_REPL_") || k === "CODEX_CLI_PATH")) return true;
12244
+ return name === "node_repl";
12245
+ }
11559
12246
  function isNode9Hook(cmd) {
11560
12247
  if (!cmd) return false;
11561
12248
  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
@@ -11565,11 +12252,11 @@ function isNode9Hook(cmd) {
11565
12252
  /[/\\]\.node9[/\\]bin[/\\]hook"? (?:check|log)/.test(cmd);
11566
12253
  }
11567
12254
  function teardownClaude() {
11568
- const homeDir2 = import_os16.default.homedir();
11569
- const hooksPath = import_path20.default.join(homeDir2, ".claude", "settings.json");
11570
- const mcpPath = import_path20.default.join(homeDir2, ".claude", ".mcp.json");
12255
+ const homeDir2 = import_os18.default.homedir();
12256
+ const hooksPath = import_path21.default.join(homeDir2, ".claude", "settings.json");
12257
+ const mcpPath = import_path21.default.join(homeDir2, ".claude", ".mcp.json");
11571
12258
  let changed = false;
11572
- const settings = readJson(hooksPath);
12259
+ const settings = readJson2(hooksPath);
11573
12260
  if (settings?.hooks) {
11574
12261
  for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
11575
12262
  const before = settings.hooks[event]?.length ?? 0;
@@ -11588,7 +12275,7 @@ function teardownClaude() {
11588
12275
  console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9 hooks found in ~/.claude/settings.json"));
11589
12276
  }
11590
12277
  }
11591
- const claudeConfig = readJson(mcpPath);
12278
+ const claudeConfig = readJson2(mcpPath);
11592
12279
  if (claudeConfig?.mcpServers) {
11593
12280
  let mcpChanged = false;
11594
12281
  if (removeNode9McpServer(claudeConfig.mcpServers)) {
@@ -11615,9 +12302,9 @@ function teardownClaude() {
11615
12302
  }
11616
12303
  }
11617
12304
  function teardownGemini() {
11618
- const homeDir2 = import_os16.default.homedir();
11619
- const settingsPath = import_path20.default.join(homeDir2, ".gemini", "settings.json");
11620
- const settings = readJson(settingsPath);
12305
+ const homeDir2 = import_os18.default.homedir();
12306
+ const settingsPath = import_path21.default.join(homeDir2, ".gemini", "settings.json");
12307
+ const settings = readJson2(settingsPath);
11621
12308
  if (!settings) {
11622
12309
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
11623
12310
  return;
@@ -11659,9 +12346,9 @@ function teardownGemini() {
11659
12346
  }
11660
12347
  }
11661
12348
  function teardownCursor() {
11662
- const homeDir2 = import_os16.default.homedir();
11663
- const mcpPath = import_path20.default.join(homeDir2, ".cursor", "mcp.json");
11664
- const mcpConfig = readJson(mcpPath);
12349
+ const homeDir2 = import_os18.default.homedir();
12350
+ const mcpPath = import_path21.default.join(homeDir2, ".cursor", "mcp.json");
12351
+ const mcpConfig = readJson2(mcpPath);
11665
12352
  if (!mcpConfig?.mcpServers) {
11666
12353
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
11667
12354
  return;
@@ -11692,11 +12379,11 @@ function teardownCursor() {
11692
12379
  }
11693
12380
  async function setupClaude() {
11694
12381
  seedMcpPinsIfMissing();
11695
- const homeDir2 = import_os16.default.homedir();
11696
- const mcpPath = import_path20.default.join(homeDir2, ".claude", ".mcp.json");
11697
- const hooksPath = import_path20.default.join(homeDir2, ".claude", "settings.json");
11698
- const claudeConfig = readJson(mcpPath) ?? {};
11699
- const settings = readJson(hooksPath) ?? {};
12382
+ const homeDir2 = import_os18.default.homedir();
12383
+ const mcpPath = import_path21.default.join(homeDir2, ".claude", ".mcp.json");
12384
+ const hooksPath = import_path21.default.join(homeDir2, ".claude", "settings.json");
12385
+ const claudeConfig = readJson2(mcpPath) ?? {};
12386
+ const settings = readJson2(hooksPath) ?? {};
11700
12387
  const servers = claudeConfig.mcpServers ?? {};
11701
12388
  let hooksChanged = false;
11702
12389
  let anythingChanged = false;
@@ -11817,7 +12504,7 @@ async function setupClaude() {
11817
12504
  const serversToWrap = [];
11818
12505
  for (const [name, server] of Object.entries(servers)) {
11819
12506
  if (!server.command || server.command === "node9") continue;
11820
- const upstream = [server.command, ...server.args ?? []].join(" ");
12507
+ const upstream = mcpUpstreamString(server);
11821
12508
  serversToWrap.push({ name, upstream });
11822
12509
  }
11823
12510
  if (serversToWrap.length > 0) {
@@ -11870,9 +12557,9 @@ async function setupGemini() {
11870
12557
  );
11871
12558
  console.log("");
11872
12559
  seedMcpPinsIfMissing();
11873
- const homeDir2 = import_os16.default.homedir();
11874
- const settingsPath = import_path20.default.join(homeDir2, ".gemini", "settings.json");
11875
- const settings = readJson(settingsPath) ?? {};
12560
+ const homeDir2 = import_os18.default.homedir();
12561
+ const settingsPath = import_path21.default.join(homeDir2, ".gemini", "settings.json");
12562
+ const settings = readJson2(settingsPath) ?? {};
11876
12563
  const servers = settings.mcpServers ?? {};
11877
12564
  let hooksChanged = false;
11878
12565
  let anythingChanged = false;
@@ -11939,7 +12626,7 @@ async function setupGemini() {
11939
12626
  const serversToWrap = [];
11940
12627
  for (const [name, server] of Object.entries(servers)) {
11941
12628
  if (!server.command || server.command === "node9") continue;
11942
- const upstream = [server.command, ...server.args ?? []].join(" ");
12629
+ const upstream = mcpUpstreamString(server);
11943
12630
  serversToWrap.push({ name, upstream });
11944
12631
  }
11945
12632
  if (serversToWrap.length > 0) {
@@ -11985,11 +12672,11 @@ async function setupGemini() {
11985
12672
  }
11986
12673
  async function setupAntigravity() {
11987
12674
  seedMcpPinsIfMissing();
11988
- const homeDir2 = import_os16.default.homedir();
11989
- const hooksPath = import_path20.default.join(homeDir2, ".gemini", "config", "hooks.json");
11990
- const mcpPath = import_path20.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
11991
- const hooksFile = readJson(hooksPath) ?? {};
11992
- const mcpConfig = readJson(mcpPath) ?? {};
12675
+ const homeDir2 = import_os18.default.homedir();
12676
+ const hooksPath = import_path21.default.join(homeDir2, ".gemini", "config", "hooks.json");
12677
+ const mcpPath = import_path21.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
12678
+ const hooksFile = readJson2(hooksPath) ?? {};
12679
+ const mcpConfig = readJson2(mcpPath) ?? {};
11993
12680
  const servers = mcpConfig.mcpServers ?? {};
11994
12681
  let hooksChanged = false;
11995
12682
  let anythingChanged = false;
@@ -12082,7 +12769,7 @@ async function setupAntigravity() {
12082
12769
  }
12083
12770
  anythingChanged = true;
12084
12771
  }
12085
- const legacySettings = readJson(import_path20.default.join(homeDir2, ".gemini", "settings.json"));
12772
+ const legacySettings = readJson2(import_path21.default.join(homeDir2, ".gemini", "settings.json"));
12086
12773
  const legacyHasNode9 = ["BeforeTool", "AfterTool"].some(
12087
12774
  (ev) => legacySettings?.hooks?.[ev]?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)))
12088
12775
  );
@@ -12153,11 +12840,11 @@ async function setupAntigravity() {
12153
12840
  }
12154
12841
  }
12155
12842
  function teardownAntigravity() {
12156
- const homeDir2 = import_os16.default.homedir();
12157
- const hooksPath = import_path20.default.join(homeDir2, ".gemini", "config", "hooks.json");
12158
- const mcpPath = import_path20.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
12843
+ const homeDir2 = import_os18.default.homedir();
12844
+ const hooksPath = import_path21.default.join(homeDir2, ".gemini", "config", "hooks.json");
12845
+ const mcpPath = import_path21.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
12159
12846
  let changed = false;
12160
- const hooksFile = readJson(hooksPath);
12847
+ const hooksFile = readJson2(hooksPath);
12161
12848
  if (hooksFile?.hooks) {
12162
12849
  for (const event of ["PreToolUse", "PostToolUse"]) {
12163
12850
  const before = hooksFile.hooks[event]?.length ?? 0;
@@ -12178,7 +12865,7 @@ function teardownAntigravity() {
12178
12865
  } else {
12179
12866
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/config/hooks.json not found \u2014 nothing to remove"));
12180
12867
  }
12181
- const mcpConfig = readJson(mcpPath);
12868
+ const mcpConfig = readJson2(mcpPath);
12182
12869
  if (mcpConfig?.mcpServers) {
12183
12870
  let mcpChanged = false;
12184
12871
  if (removeNode9McpServer(mcpConfig.mcpServers)) {
@@ -12207,13 +12894,13 @@ function teardownAntigravity() {
12207
12894
  }
12208
12895
  async function setupCopilot() {
12209
12896
  seedMcpPinsIfMissing();
12210
- const homeDir2 = import_os16.default.homedir();
12211
- const hooksPath = import_path20.default.join(homeDir2, ".copilot", "hooks", "node9.json");
12212
- const mcpPath = import_path20.default.join(homeDir2, ".copilot", "mcp-config.json");
12213
- const hooksFile = readJson(hooksPath) ?? { version: 1 };
12897
+ const homeDir2 = import_os18.default.homedir();
12898
+ const hooksPath = import_path21.default.join(homeDir2, ".copilot", "hooks", "node9.json");
12899
+ const mcpPath = import_path21.default.join(homeDir2, ".copilot", "mcp-config.json");
12900
+ const hooksFile = readJson2(hooksPath) ?? { version: 1 };
12214
12901
  if (!hooksFile.version) hooksFile.version = 1;
12215
12902
  if (!hooksFile.hooks) hooksFile.hooks = {};
12216
- const mcpConfig = readJson(mcpPath) ?? {};
12903
+ const mcpConfig = readJson2(mcpPath) ?? {};
12217
12904
  const servers = mcpConfig.mcpServers ?? {};
12218
12905
  let hooksChanged = false;
12219
12906
  let anythingChanged = false;
@@ -12310,10 +12997,10 @@ async function setupCopilot() {
12310
12997
  printInlineAskNotice();
12311
12998
  }
12312
12999
  function teardownCopilot() {
12313
- const homeDir2 = import_os16.default.homedir();
12314
- const hooksPath = import_path20.default.join(homeDir2, ".copilot", "hooks", "node9.json");
12315
- const mcpPath = import_path20.default.join(homeDir2, ".copilot", "mcp-config.json");
12316
- const hooksFile = readJson(hooksPath);
13000
+ const homeDir2 = import_os18.default.homedir();
13001
+ const hooksPath = import_path21.default.join(homeDir2, ".copilot", "hooks", "node9.json");
13002
+ const mcpPath = import_path21.default.join(homeDir2, ".copilot", "mcp-config.json");
13003
+ const hooksFile = readJson2(hooksPath);
12317
13004
  let changed = false;
12318
13005
  if (hooksFile?.hooks) {
12319
13006
  for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
@@ -12325,7 +13012,7 @@ function teardownCopilot() {
12325
13012
  if (changed) {
12326
13013
  if (Object.keys(hooksFile.hooks).length === 0) {
12327
13014
  try {
12328
- import_fs19.default.unlinkSync(hooksPath);
13015
+ import_fs21.default.unlinkSync(hooksPath);
12329
13016
  console.log(import_chalk.default.green(" \u2705 Removed ~/.copilot/hooks/node9.json"));
12330
13017
  } catch {
12331
13018
  writeJson(hooksPath, hooksFile);
@@ -12340,7 +13027,7 @@ function teardownCopilot() {
12340
13027
  } else {
12341
13028
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.copilot/hooks/node9.json not found \u2014 nothing to remove"));
12342
13029
  }
12343
- const mcpConfig = readJson(mcpPath);
13030
+ const mcpConfig = readJson2(mcpPath);
12344
13031
  if (mcpConfig?.mcpServers) {
12345
13032
  let mcpChanged = false;
12346
13033
  if (removeNode9McpServer(mcpConfig.mcpServers)) {
@@ -12367,9 +13054,9 @@ function teardownCopilot() {
12367
13054
  }
12368
13055
  }
12369
13056
  }
12370
- function claudeDesktopConfigPath(homeDir2 = import_os16.default.homedir()) {
13057
+ function claudeDesktopConfigPath(homeDir2 = import_os18.default.homedir()) {
12371
13058
  if (process.platform === "darwin") {
12372
- return import_path20.default.join(
13059
+ return import_path21.default.join(
12373
13060
  homeDir2,
12374
13061
  "Library",
12375
13062
  "Application Support",
@@ -12378,47 +13065,47 @@ function claudeDesktopConfigPath(homeDir2 = import_os16.default.homedir()) {
12378
13065
  );
12379
13066
  }
12380
13067
  if (process.platform === "linux") {
12381
- return import_path20.default.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
13068
+ return import_path21.default.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
12382
13069
  }
12383
13070
  if (process.platform === "win32") {
12384
- const appData = process.env.APPDATA || import_path20.default.join(homeDir2, "AppData", "Roaming");
12385
- return import_path20.default.join(appData, "Claude", "claude_desktop_config.json");
13071
+ const appData = process.env.APPDATA || import_path21.default.join(homeDir2, "AppData", "Roaming");
13072
+ return import_path21.default.join(appData, "Claude", "claude_desktop_config.json");
12386
13073
  }
12387
13074
  return null;
12388
13075
  }
12389
- function opencodeConfigDir(home = import_os16.default.homedir()) {
13076
+ function opencodeConfigDir(home = import_os18.default.homedir()) {
12390
13077
  const xdg = process.env.XDG_CONFIG_HOME;
12391
- const base = xdg && import_path20.default.isAbsolute(xdg) ? xdg : import_path20.default.join(home, ".config");
12392
- return import_path20.default.join(base, "opencode");
13078
+ const base = xdg && import_path21.default.isAbsolute(xdg) ? xdg : import_path21.default.join(home, ".config");
13079
+ return import_path21.default.join(base, "opencode");
12393
13080
  }
12394
13081
  function commonBinDirs(home) {
12395
13082
  return [
12396
- import_path20.default.join(home, ".local", "bin"),
12397
- import_path20.default.join(home, ".bun", "bin"),
12398
- import_path20.default.join(home, ".npm-global", "bin"),
13083
+ import_path21.default.join(home, ".local", "bin"),
13084
+ import_path21.default.join(home, ".bun", "bin"),
13085
+ import_path21.default.join(home, ".npm-global", "bin"),
12399
13086
  "/usr/local/bin",
12400
13087
  "/opt/homebrew/bin"
12401
13088
  ];
12402
13089
  }
12403
- function binaryInPath(binary, home = import_os16.default.homedir()) {
13090
+ function binaryInPath(binary, home = import_os18.default.homedir()) {
12404
13091
  const pathEnv = process.env.PATH ?? "";
12405
- const dirs = [...pathEnv.split(import_path20.default.delimiter), ...commonBinDirs(home)];
13092
+ const dirs = [...pathEnv.split(import_path21.default.delimiter), ...commonBinDirs(home)];
12406
13093
  const seen = /* @__PURE__ */ new Set();
12407
13094
  for (const dir of dirs) {
12408
13095
  if (!dir || seen.has(dir)) continue;
12409
13096
  seen.add(dir);
12410
13097
  try {
12411
- import_fs19.default.accessSync(import_path20.default.join(dir, binary), import_fs19.default.constants.X_OK);
13098
+ import_fs21.default.accessSync(import_path21.default.join(dir, binary), import_fs21.default.constants.X_OK);
12412
13099
  return true;
12413
13100
  } catch {
12414
13101
  }
12415
13102
  }
12416
13103
  return false;
12417
13104
  }
12418
- function detectAgents(homeDir2 = import_os16.default.homedir()) {
13105
+ function detectAgents(homeDir2 = import_os18.default.homedir()) {
12419
13106
  const exists2 = (p) => {
12420
13107
  try {
12421
- return import_fs19.default.existsSync(p);
13108
+ return import_fs21.default.existsSync(p);
12422
13109
  } catch (err2) {
12423
13110
  const code = err2.code;
12424
13111
  if (code !== "ENOENT") {
@@ -12430,7 +13117,7 @@ function detectAgents(homeDir2 = import_os16.default.homedir()) {
12430
13117
  };
12431
13118
  const desktopPath = claudeDesktopConfigPath(homeDir2);
12432
13119
  return {
12433
- claude: exists2(import_path20.default.join(homeDir2, ".claude")) || exists2(import_path20.default.join(homeDir2, ".claude.json")),
13120
+ claude: exists2(import_path21.default.join(homeDir2, ".claude")) || exists2(import_path21.default.join(homeDir2, ".claude.json")),
12434
13121
  // Antigravity (agy) shares the ~/.gemini root, so a bare
12435
13122
  // `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
12436
13123
  // installed on every agy machine — and `node9 init` would then
@@ -12440,19 +13127,19 @@ function detectAgents(homeDir2 = import_os16.default.homedir()) {
12440
13127
  // creates ~/.gemini/settings.json on first run; agy does not touch
12441
13128
  // it (it uses antigravity-cli/settings.json) — that file is the
12442
13129
  // legacy-CLI discriminator.
12443
- gemini: exists2(import_path20.default.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini", homeDir2),
13130
+ gemini: exists2(import_path21.default.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini", homeDir2),
12444
13131
  // agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
12445
13132
  // creates antigravity-ide/. PATH fallback covers installed-but-
12446
13133
  // never-launched (same class as opencode #186).
12447
- antigravity: exists2(import_path20.default.join(homeDir2, ".gemini", "antigravity-cli")) || exists2(import_path20.default.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy", homeDir2),
13134
+ antigravity: exists2(import_path21.default.join(homeDir2, ".gemini", "antigravity-cli")) || exists2(import_path21.default.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy", homeDir2),
12448
13135
  // GitHub Copilot CLI creates ~/.copilot on first launch; PATH
12449
13136
  // fallback covers installed-but-never-launched (same as opencode #186).
12450
- copilot: exists2(import_path20.default.join(homeDir2, ".copilot")) || binaryInPath("copilot", homeDir2),
12451
- cursor: exists2(import_path20.default.join(homeDir2, ".cursor")),
12452
- codex: exists2(import_path20.default.join(homeDir2, ".codex")),
12453
- windsurf: exists2(import_path20.default.join(homeDir2, ".codeium", "windsurf")),
12454
- vscode: exists2(import_path20.default.join(homeDir2, ".vscode")),
12455
- claudeDesktop: desktopPath !== null && exists2(import_path20.default.dirname(desktopPath)),
13137
+ copilot: exists2(import_path21.default.join(homeDir2, ".copilot")) || binaryInPath("copilot", homeDir2),
13138
+ cursor: exists2(import_path21.default.join(homeDir2, ".cursor")),
13139
+ codex: exists2(import_path21.default.join(homeDir2, ".codex")),
13140
+ windsurf: exists2(import_path21.default.join(homeDir2, ".codeium", "windsurf")),
13141
+ vscode: exists2(import_path21.default.join(homeDir2, ".vscode")),
13142
+ claudeDesktop: desktopPath !== null && exists2(import_path21.default.dirname(desktopPath)),
12456
13143
  // Opencode creates its config dir lazily on first launch — fall back to a
12457
13144
  // PATH lookup so installed-but-never-launched CLIs are still wired. Config
12458
13145
  // dir honors $XDG_CONFIG_HOME via opencodeConfigDir (#186, custom-XDG).
@@ -12463,7 +13150,7 @@ function detectAgents(homeDir2 = import_os16.default.homedir()) {
12463
13150
  // dir lazily on first launch — same class of bug as opencode's #186
12464
13151
  // (design R6) — so fall back to PATH lookup for installed-but-never-
12465
13152
  // launched pi.
12466
- pi: exists2(import_path20.default.join(homeDir2, ".pi", "agent")) || binaryInPath("pi", homeDir2),
13153
+ pi: exists2(import_path21.default.join(homeDir2, ".pi", "agent")) || binaryInPath("pi", homeDir2),
12467
13154
  // Hermes Agent (https://github.com/NousResearch/hermes-agent): home dir
12468
13155
  // is $HERMES_HOME (default ~/.hermes) per hermes_constants.py:30. config.yaml
12469
13156
  // appears after `hermes setup` has run; the directory alone exists from
@@ -12474,9 +13161,9 @@ function detectAgents(homeDir2 = import_os16.default.homedir()) {
12474
13161
  }
12475
13162
  async function setupCursor() {
12476
13163
  seedMcpPinsIfMissing();
12477
- const homeDir2 = import_os16.default.homedir();
12478
- const mcpPath = import_path20.default.join(homeDir2, ".cursor", "mcp.json");
12479
- const mcpConfig = readJson(mcpPath) ?? {};
13164
+ const homeDir2 = import_os18.default.homedir();
13165
+ const mcpPath = import_path21.default.join(homeDir2, ".cursor", "mcp.json");
13166
+ const mcpConfig = readJson2(mcpPath) ?? {};
12480
13167
  const servers = mcpConfig.mcpServers ?? {};
12481
13168
  let anythingChanged = false;
12482
13169
  const repairedMcpWraps = repairLegacyMcpWraps(
@@ -12502,7 +13189,7 @@ async function setupCursor() {
12502
13189
  const serversToWrap = [];
12503
13190
  for (const [name, server] of Object.entries(servers)) {
12504
13191
  if (!server.command || server.command === "node9") continue;
12505
- const upstream = [server.command, ...server.args ?? []].join(" ");
13192
+ const upstream = mcpUpstreamString(server);
12506
13193
  serversToWrap.push({ name, upstream });
12507
13194
  }
12508
13195
  if (serversToWrap.length > 0) {
@@ -12558,27 +13245,27 @@ async function setupCursor() {
12558
13245
  }
12559
13246
  function readToml(filePath) {
12560
13247
  try {
12561
- if (import_fs19.default.existsSync(filePath)) {
12562
- return (0, import_smol_toml2.parse)(import_fs19.default.readFileSync(filePath, "utf-8"));
13248
+ if (import_fs21.default.existsSync(filePath)) {
13249
+ return (0, import_smol_toml4.parse)(import_fs21.default.readFileSync(filePath, "utf-8"));
12563
13250
  }
12564
13251
  } catch {
12565
13252
  }
12566
13253
  return null;
12567
13254
  }
12568
13255
  function writeToml(filePath, data) {
12569
- const dir = import_path20.default.dirname(filePath);
12570
- if (!import_fs19.default.existsSync(dir)) import_fs19.default.mkdirSync(dir, { recursive: true });
12571
- import_fs19.default.writeFileSync(filePath, (0, import_smol_toml2.stringify)(data));
13256
+ const dir = import_path21.default.dirname(filePath);
13257
+ if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
13258
+ import_fs21.default.writeFileSync(filePath, (0, import_smol_toml4.stringify)(data));
12572
13259
  }
12573
13260
  async function setupCodex() {
12574
13261
  seedMcpPinsIfMissing();
12575
- const homeDir2 = import_os16.default.homedir();
12576
- const configPath = import_path20.default.join(homeDir2, ".codex", "config.toml");
12577
- const hooksPath = import_path20.default.join(homeDir2, ".codex", "hooks.json");
13262
+ const homeDir2 = import_os18.default.homedir();
13263
+ const configPath = import_path21.default.join(homeDir2, ".codex", "config.toml");
13264
+ const hooksPath = import_path21.default.join(homeDir2, ".codex", "hooks.json");
12578
13265
  const config = readToml(configPath) ?? {};
12579
13266
  const servers = config.mcp_servers ?? {};
12580
13267
  let anythingChanged = false;
12581
- const hooksFile = readJson(hooksPath) ?? {};
13268
+ const hooksFile = readJson2(hooksPath) ?? {};
12582
13269
  if (!hooksFile.hooks) hooksFile.hooks = {};
12583
13270
  let hooksChanged = false;
12584
13271
  if (!hooksFile.hooks.PreToolUse) hooksFile.hooks.PreToolUse = [];
@@ -12671,11 +13358,21 @@ async function setupCodex() {
12671
13358
  anythingChanged = true;
12672
13359
  }
12673
13360
  const serversToWrap = [];
13361
+ const appManaged = [];
12674
13362
  for (const [name, server] of Object.entries(servers)) {
12675
13363
  if (!server.command || server.command === "node9") continue;
12676
- const upstream = [server.command, ...server.args ?? []].join(" ");
13364
+ if (isCodexAppManagedServer(name, server)) {
13365
+ appManaged.push(name);
13366
+ continue;
13367
+ }
13368
+ const upstream = mcpUpstreamString(server);
12677
13369
  serversToWrap.push({ name, upstream });
12678
13370
  }
13371
+ if (appManaged.length > 0) {
13372
+ console.log(
13373
+ import_chalk.default.gray(` \u2139\uFE0F Managed by the Codex app \u2014 not wrapped: ${appManaged.join(", ")}`)
13374
+ );
13375
+ }
12679
13376
  if (serversToWrap.length > 0) {
12680
13377
  console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
12681
13378
  console.log(import_chalk.default.white(` ${configPath}`));
@@ -12740,10 +13437,10 @@ async function setupCodex() {
12740
13437
  }
12741
13438
  }
12742
13439
  function teardownCodex() {
12743
- const homeDir2 = import_os16.default.homedir();
12744
- const configPath = import_path20.default.join(homeDir2, ".codex", "config.toml");
12745
- const hooksPath = import_path20.default.join(homeDir2, ".codex", "hooks.json");
12746
- const hooksFile = readJson(hooksPath);
13440
+ const homeDir2 = import_os18.default.homedir();
13441
+ const configPath = import_path21.default.join(homeDir2, ".codex", "config.toml");
13442
+ const hooksPath = import_path21.default.join(homeDir2, ".codex", "hooks.json");
13443
+ const hooksFile = readJson2(hooksPath);
12747
13444
  if (hooksFile?.hooks) {
12748
13445
  let hooksChanged = false;
12749
13446
  for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
@@ -12789,9 +13486,9 @@ function teardownCodex() {
12789
13486
  }
12790
13487
  }
12791
13488
  function setupHud() {
12792
- const homeDir2 = import_os16.default.homedir();
12793
- const hooksPath = import_path20.default.join(homeDir2, ".claude", "settings.json");
12794
- const settings = readJson(hooksPath) ?? {};
13489
+ const homeDir2 = import_os18.default.homedir();
13490
+ const hooksPath = import_path21.default.join(homeDir2, ".claude", "settings.json");
13491
+ const settings = readJson2(hooksPath) ?? {};
12795
13492
  const hudCommand = fullPathCommand("hud");
12796
13493
  const statusLineObj = { type: "command", command: hudCommand };
12797
13494
  const existing = settings.statusLine;
@@ -12818,9 +13515,9 @@ function setupHud() {
12818
13515
  console.log(import_chalk.default.gray(" Restart Claude Code to activate."));
12819
13516
  }
12820
13517
  function teardownHud() {
12821
- const homeDir2 = import_os16.default.homedir();
12822
- const hooksPath = import_path20.default.join(homeDir2, ".claude", "settings.json");
12823
- const settings = readJson(hooksPath);
13518
+ const homeDir2 = import_os18.default.homedir();
13519
+ const hooksPath = import_path21.default.join(homeDir2, ".claude", "settings.json");
13520
+ const settings = readJson2(hooksPath);
12824
13521
  if (!settings) {
12825
13522
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.claude/settings.json not found \u2014 nothing to remove"));
12826
13523
  return;
@@ -12838,9 +13535,9 @@ function teardownHud() {
12838
13535
  }
12839
13536
  async function setupWindsurf() {
12840
13537
  seedMcpPinsIfMissing();
12841
- const homeDir2 = import_os16.default.homedir();
12842
- const mcpPath = import_path20.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
12843
- const mcpConfig = readJson(mcpPath) ?? {};
13538
+ const homeDir2 = import_os18.default.homedir();
13539
+ const mcpPath = import_path21.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
13540
+ const mcpConfig = readJson2(mcpPath) ?? {};
12844
13541
  const servers = mcpConfig.mcpServers ?? {};
12845
13542
  let anythingChanged = false;
12846
13543
  const repairedMcpWraps = repairLegacyMcpWraps(
@@ -12916,9 +13613,9 @@ async function setupWindsurf() {
12916
13613
  }
12917
13614
  }
12918
13615
  function teardownWindsurf() {
12919
- const homeDir2 = import_os16.default.homedir();
12920
- const mcpPath = import_path20.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
12921
- const mcpConfig = readJson(mcpPath);
13616
+ const homeDir2 = import_os18.default.homedir();
13617
+ const mcpPath = import_path21.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
13618
+ const mcpConfig = readJson2(mcpPath);
12922
13619
  if (!mcpConfig?.mcpServers) {
12923
13620
  console.log(
12924
13621
  import_chalk.default.blue(" \u2139\uFE0F ~/.codeium/windsurf/mcp_config.json not found \u2014 nothing to remove")
@@ -12959,9 +13656,9 @@ function hasNode9McpServerVSCode(servers) {
12959
13656
  }
12960
13657
  async function setupVSCode() {
12961
13658
  seedMcpPinsIfMissing();
12962
- const homeDir2 = import_os16.default.homedir();
12963
- const mcpPath = import_path20.default.join(homeDir2, ".vscode", "mcp.json");
12964
- const mcpConfig = readJson(mcpPath) ?? {};
13659
+ const homeDir2 = import_os18.default.homedir();
13660
+ const mcpPath = import_path21.default.join(homeDir2, ".vscode", "mcp.json");
13661
+ const mcpConfig = readJson2(mcpPath) ?? {};
12965
13662
  const servers = mcpConfig.servers ?? {};
12966
13663
  let anythingChanged = false;
12967
13664
  const repairedMcpWraps = repairLegacyMcpWraps(
@@ -13040,9 +13737,9 @@ async function setupVSCode() {
13040
13737
  }
13041
13738
  }
13042
13739
  function teardownVSCode() {
13043
- const homeDir2 = import_os16.default.homedir();
13044
- const mcpPath = import_path20.default.join(homeDir2, ".vscode", "mcp.json");
13045
- const mcpConfig = readJson(mcpPath);
13740
+ const homeDir2 = import_os18.default.homedir();
13741
+ const mcpPath = import_path21.default.join(homeDir2, ".vscode", "mcp.json");
13742
+ const mcpConfig = readJson2(mcpPath);
13046
13743
  if (!mcpConfig?.servers) {
13047
13744
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.vscode/mcp.json not found \u2014 nothing to remove"));
13048
13745
  return;
@@ -13080,7 +13777,7 @@ async function setupClaudeDesktop() {
13080
13777
  console.log(import_chalk.default.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
13081
13778
  return;
13082
13779
  }
13083
- const config = readJson(configPath) ?? {};
13780
+ const config = readJson2(configPath) ?? {};
13084
13781
  const servers = config.mcpServers ?? {};
13085
13782
  let anythingChanged = false;
13086
13783
  const repairedMcpWraps = repairLegacyMcpWraps(
@@ -13161,7 +13858,7 @@ function teardownClaudeDesktop() {
13161
13858
  console.log(import_chalk.default.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
13162
13859
  return;
13163
13860
  }
13164
- const config = readJson(configPath);
13861
+ const config = readJson2(configPath);
13165
13862
  if (!config?.mcpServers) {
13166
13863
  console.log(import_chalk.default.blue(" \u2139\uFE0F Claude Desktop config not found \u2014 nothing to remove"));
13167
13864
  return;
@@ -13200,7 +13897,7 @@ function node9ArgvForShim() {
13200
13897
  function node9Version() {
13201
13898
  try {
13202
13899
  const pkg = JSON.parse(
13203
- import_fs19.default.readFileSync(import_path20.default.join(__dirname, "..", "package.json"), "utf-8")
13900
+ import_fs21.default.readFileSync(import_path21.default.join(__dirname, "..", "package.json"), "utf-8")
13204
13901
  );
13205
13902
  return pkg.version ?? "0.0.0";
13206
13903
  } catch {
@@ -13209,13 +13906,13 @@ function node9Version() {
13209
13906
  }
13210
13907
  async function setupOpencode() {
13211
13908
  seedMcpPinsIfMissing();
13212
- const homeDir2 = import_os16.default.homedir();
13909
+ const homeDir2 = import_os18.default.homedir();
13213
13910
  const configDir = opencodeConfigDir(homeDir2);
13214
- const pluginsDir = import_path20.default.join(configDir, "plugins");
13215
- const configPath = import_path20.default.join(configDir, "opencode.json");
13216
- const pluginPath = import_path20.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
13911
+ const pluginsDir = import_path21.default.join(configDir, "plugins");
13912
+ const configPath = import_path21.default.join(configDir, "opencode.json");
13913
+ const pluginPath = import_path21.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
13217
13914
  try {
13218
- import_fs19.default.mkdirSync(pluginsDir, { recursive: true });
13915
+ import_fs21.default.mkdirSync(pluginsDir, { recursive: true });
13219
13916
  } catch (err2) {
13220
13917
  const code = err2.code;
13221
13918
  if (code !== "EEXIST") {
@@ -13230,13 +13927,13 @@ async function setupOpencode() {
13230
13927
  let pluginChanged = false;
13231
13928
  const existingShim = (() => {
13232
13929
  try {
13233
- return import_fs19.default.readFileSync(pluginPath, "utf-8");
13930
+ return import_fs21.default.readFileSync(pluginPath, "utf-8");
13234
13931
  } catch {
13235
13932
  return null;
13236
13933
  }
13237
13934
  })();
13238
13935
  if (existingShim !== shimContent) {
13239
- import_fs19.default.writeFileSync(pluginPath, shimContent);
13936
+ import_fs21.default.writeFileSync(pluginPath, shimContent);
13240
13937
  pluginChanged = true;
13241
13938
  if (existingShim) {
13242
13939
  console.log(import_chalk.default.yellow(" \u{1F527} Opencode plugin shim updated to current version"));
@@ -13246,7 +13943,7 @@ async function setupOpencode() {
13246
13943
  );
13247
13944
  }
13248
13945
  }
13249
- const config = readJson(configPath) ?? {};
13946
+ const config = readJson2(configPath) ?? {};
13250
13947
  const mcp = config.mcp ?? {};
13251
13948
  let configChanged = false;
13252
13949
  const desiredCommand = [...node9ArgvForShim(), "mcp-server"];
@@ -13277,20 +13974,20 @@ async function setupOpencode() {
13277
13974
  }
13278
13975
  }
13279
13976
  function teardownOpencode() {
13280
- const homeDir2 = import_os16.default.homedir();
13977
+ const homeDir2 = import_os18.default.homedir();
13281
13978
  const configDir = opencodeConfigDir(homeDir2);
13282
- const pluginsDir = import_path20.default.join(configDir, "plugins");
13283
- const configPath = import_path20.default.join(configDir, "opencode.json");
13284
- const pluginPath = import_path20.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
13979
+ const pluginsDir = import_path21.default.join(configDir, "plugins");
13980
+ const configPath = import_path21.default.join(configDir, "opencode.json");
13981
+ const pluginPath = import_path21.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
13285
13982
  try {
13286
- if (import_fs19.default.existsSync(pluginPath)) {
13287
- import_fs19.default.unlinkSync(pluginPath);
13983
+ if (import_fs21.default.existsSync(pluginPath)) {
13984
+ import_fs21.default.unlinkSync(pluginPath);
13288
13985
  console.log(import_chalk.default.green(" \u2705 Removed node9 plugin from ~/.config/opencode/plugins/"));
13289
13986
  }
13290
13987
  } catch (err2) {
13291
13988
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Could not remove ${pluginPath}: ${String(err2)}`));
13292
13989
  }
13293
- const config = readJson(configPath);
13990
+ const config = readJson2(configPath);
13294
13991
  if (!config) {
13295
13992
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.config/opencode/opencode.json not found \u2014 nothing to remove"));
13296
13993
  return;
@@ -13313,11 +14010,11 @@ function teardownOpencode() {
13313
14010
  }
13314
14011
  async function setupPi() {
13315
14012
  seedMcpPinsIfMissing();
13316
- const homeDir2 = import_os16.default.homedir();
13317
- const extensionsDir = import_path20.default.join(homeDir2, ".pi", "agent", "extensions");
13318
- const extensionPath = import_path20.default.join(extensionsDir, PI_EXTENSION_NAME);
14013
+ const homeDir2 = import_os18.default.homedir();
14014
+ const extensionsDir = import_path21.default.join(homeDir2, ".pi", "agent", "extensions");
14015
+ const extensionPath = import_path21.default.join(extensionsDir, PI_EXTENSION_NAME);
13319
14016
  try {
13320
- import_fs19.default.mkdirSync(extensionsDir, { recursive: true });
14017
+ import_fs21.default.mkdirSync(extensionsDir, { recursive: true });
13321
14018
  } catch (err2) {
13322
14019
  const code = err2.code;
13323
14020
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Could not create ${extensionsDir}: ${code ?? String(err2)}`));
@@ -13329,7 +14026,7 @@ async function setupPi() {
13329
14026
  });
13330
14027
  const existingShim = (() => {
13331
14028
  try {
13332
- return import_fs19.default.readFileSync(extensionPath, "utf-8");
14029
+ return import_fs21.default.readFileSync(extensionPath, "utf-8");
13333
14030
  } catch {
13334
14031
  return null;
13335
14032
  }
@@ -13338,7 +14035,7 @@ async function setupPi() {
13338
14035
  console.log(import_chalk.default.blue(" \u2139\uFE0F Node9 is already fully configured for Pi."));
13339
14036
  return;
13340
14037
  }
13341
- import_fs19.default.writeFileSync(extensionPath, shimContent);
14038
+ import_fs21.default.writeFileSync(extensionPath, shimContent);
13342
14039
  if (existingShim) {
13343
14040
  console.log(import_chalk.default.yellow(" \u{1F527} Pi extension shim updated to current version"));
13344
14041
  } else {
@@ -13351,11 +14048,11 @@ async function setupPi() {
13351
14048
  printDaemonTip();
13352
14049
  }
13353
14050
  function teardownPi() {
13354
- const homeDir2 = import_os16.default.homedir();
13355
- const extensionPath = import_path20.default.join(homeDir2, ".pi", "agent", "extensions", PI_EXTENSION_NAME);
14051
+ const homeDir2 = import_os18.default.homedir();
14052
+ const extensionPath = import_path21.default.join(homeDir2, ".pi", "agent", "extensions", PI_EXTENSION_NAME);
13356
14053
  try {
13357
- if (import_fs19.default.existsSync(extensionPath)) {
13358
- import_fs19.default.unlinkSync(extensionPath);
14054
+ if (import_fs21.default.existsSync(extensionPath)) {
14055
+ import_fs21.default.unlinkSync(extensionPath);
13359
14056
  console.log(import_chalk.default.green(" \u2705 Removed node9 extension from ~/.pi/agent/extensions/"));
13360
14057
  } else {
13361
14058
  console.log(import_chalk.default.blue(" \u2139\uFE0F No Pi extension installed \u2014 nothing to remove"));
@@ -13364,29 +14061,29 @@ function teardownPi() {
13364
14061
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Could not remove ${extensionPath}: ${String(err2)}`));
13365
14062
  }
13366
14063
  }
13367
- function hermesHomeDir(homeDir2 = import_os16.default.homedir()) {
14064
+ function hermesHomeDir(homeDir2 = import_os18.default.homedir()) {
13368
14065
  const env = process.env.HERMES_HOME?.trim();
13369
- if (env && import_path20.default.isAbsolute(env)) return env;
13370
- return import_path20.default.join(homeDir2, ".hermes");
14066
+ if (env && import_path21.default.isAbsolute(env)) return env;
14067
+ return import_path21.default.join(homeDir2, ".hermes");
13371
14068
  }
13372
- function hermesConfigPath(homeDir2 = import_os16.default.homedir()) {
13373
- return import_path20.default.join(hermesHomeDir(homeDir2), HERMES_CONFIG_FILENAME);
14069
+ function hermesConfigPath(homeDir2 = import_os18.default.homedir()) {
14070
+ return import_path21.default.join(hermesHomeDir(homeDir2), HERMES_CONFIG_FILENAME);
13374
14071
  }
13375
- function hermesAllowlistPath(homeDir2 = import_os16.default.homedir()) {
13376
- return import_path20.default.join(hermesHomeDir(homeDir2), HERMES_ALLOWLIST_FILENAME);
14072
+ function hermesAllowlistPath(homeDir2 = import_os18.default.homedir()) {
14073
+ return import_path21.default.join(hermesHomeDir(homeDir2), HERMES_ALLOWLIST_FILENAME);
13377
14074
  }
13378
14075
  function setupHermes() {
13379
- const homeDir2 = import_os16.default.homedir();
14076
+ const homeDir2 = import_os18.default.homedir();
13380
14077
  const configPath = hermesConfigPath(homeDir2);
13381
14078
  const allowlistPath = hermesAllowlistPath(homeDir2);
13382
- if (!import_fs19.default.existsSync(configPath)) {
14079
+ if (!import_fs21.default.existsSync(configPath)) {
13383
14080
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config not found at ${configPath}`));
13384
14081
  console.log(import_chalk.default.gray(" Run `hermes setup` first, then re-run node9 agents add hermes."));
13385
14082
  return;
13386
14083
  }
13387
14084
  let anythingChanged = false;
13388
- const raw = import_fs19.default.readFileSync(configPath, "utf-8");
13389
- const doc = yaml.parseDocument(raw);
14085
+ const raw = import_fs21.default.readFileSync(configPath, "utf-8");
14086
+ const doc = yaml2.parseDocument(raw);
13390
14087
  if (doc.errors.length > 0) {
13391
14088
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config.yaml has YAML parse errors:`));
13392
14089
  for (const err2 of doc.errors.slice(0, 3)) {
@@ -13428,9 +14125,9 @@ function setupHermes() {
13428
14125
  atomicWriteSync(configPath, doc.toString());
13429
14126
  }
13430
14127
  let allowlist = {};
13431
- if (import_fs19.default.existsSync(allowlistPath)) {
14128
+ if (import_fs21.default.existsSync(allowlistPath)) {
13432
14129
  try {
13433
- allowlist = JSON.parse(import_fs19.default.readFileSync(allowlistPath, "utf-8"));
14130
+ allowlist = JSON.parse(import_fs21.default.readFileSync(allowlistPath, "utf-8"));
13434
14131
  } catch {
13435
14132
  allowlist = {};
13436
14133
  }
@@ -13466,15 +14163,15 @@ function setupHermes() {
13466
14163
  }
13467
14164
  }
13468
14165
  function teardownHermes() {
13469
- const homeDir2 = import_os16.default.homedir();
14166
+ const homeDir2 = import_os18.default.homedir();
13470
14167
  const configPath = hermesConfigPath(homeDir2);
13471
14168
  const allowlistPath = hermesAllowlistPath(homeDir2);
13472
- if (!import_fs19.default.existsSync(configPath)) {
14169
+ if (!import_fs21.default.existsSync(configPath)) {
13473
14170
  console.log(import_chalk.default.blue(` \u2139\uFE0F ${configPath} not found \u2014 nothing to remove`));
13474
14171
  return;
13475
14172
  }
13476
- const raw = import_fs19.default.readFileSync(configPath, "utf-8");
13477
- const doc = yaml.parseDocument(raw);
14173
+ const raw = import_fs21.default.readFileSync(configPath, "utf-8");
14174
+ const doc = yaml2.parseDocument(raw);
13478
14175
  if (doc.errors.length > 0) {
13479
14176
  console.log(
13480
14177
  import_chalk.default.yellow(` \u26A0\uFE0F Skipping ${configPath} \u2014 file has YAML parse errors, fix it manually.`)
@@ -13506,16 +14203,16 @@ function teardownHermesConfigDoc(doc, configPath) {
13506
14203
  anythingChanged = true;
13507
14204
  }
13508
14205
  if (anythingChanged) {
13509
- import_fs19.default.writeFileSync(configPath, doc.toString());
14206
+ import_fs21.default.writeFileSync(configPath, doc.toString());
13510
14207
  console.log(import_chalk.default.green(` \u2705 Removed Node9 hooks from ${configPath}`));
13511
14208
  } else {
13512
14209
  console.log(import_chalk.default.blue(` \u2139\uFE0F No Node9 hooks found in ${configPath}`));
13513
14210
  }
13514
14211
  }
13515
14212
  function teardownHermesAllowlist(allowlistPath) {
13516
- if (!import_fs19.default.existsSync(allowlistPath)) return;
14213
+ if (!import_fs21.default.existsSync(allowlistPath)) return;
13517
14214
  try {
13518
- const raw = import_fs19.default.readFileSync(allowlistPath, "utf-8");
14215
+ const raw = import_fs21.default.readFileSync(allowlistPath, "utf-8");
13519
14216
  const allowlist = JSON.parse(raw);
13520
14217
  if (!Array.isArray(allowlist.approvals)) return;
13521
14218
  const before = allowlist.approvals.length;
@@ -13528,44 +14225,44 @@ function teardownHermesAllowlist(allowlistPath) {
13528
14225
  } catch {
13529
14226
  }
13530
14227
  }
13531
- function getAgentsStatus(homeDir2 = import_os16.default.homedir()) {
14228
+ function getAgentsStatus(homeDir2 = import_os18.default.homedir()) {
13532
14229
  const detected = detectAgents(homeDir2);
13533
14230
  const claudeWired = (() => {
13534
- const settings = readJson(import_path20.default.join(homeDir2, ".claude", "settings.json"));
14231
+ const settings = readJson2(import_path21.default.join(homeDir2, ".claude", "settings.json"));
13535
14232
  return !!settings?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
13536
14233
  })();
13537
14234
  const geminiWired = (() => {
13538
- const settings = readJson(import_path20.default.join(homeDir2, ".gemini", "settings.json"));
14235
+ const settings = readJson2(import_path21.default.join(homeDir2, ".gemini", "settings.json"));
13539
14236
  return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
13540
14237
  })();
13541
14238
  const antigravityWired = (() => {
13542
- const hooksFile = readJson(
13543
- import_path20.default.join(homeDir2, ".gemini", "config", "hooks.json")
14239
+ const hooksFile = readJson2(
14240
+ import_path21.default.join(homeDir2, ".gemini", "config", "hooks.json")
13544
14241
  );
13545
14242
  return !!hooksFile?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
13546
14243
  })();
13547
14244
  const copilotWired = (() => {
13548
- const hooksFile = readJson(
13549
- import_path20.default.join(homeDir2, ".copilot", "hooks", "node9.json")
14245
+ const hooksFile = readJson2(
14246
+ import_path21.default.join(homeDir2, ".copilot", "hooks", "node9.json")
13550
14247
  );
13551
14248
  return !!hooksFile?.hooks?.PreToolUse?.some((h) => isNode9Hook(h.command));
13552
14249
  })();
13553
14250
  const cursorWired = (() => {
13554
- const cfg = readJson(import_path20.default.join(homeDir2, ".cursor", "mcp.json"));
14251
+ const cfg = readJson2(import_path21.default.join(homeDir2, ".cursor", "mcp.json"));
13555
14252
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
13556
14253
  })();
13557
14254
  const codexWired = (() => {
13558
- const cfg = readToml(import_path20.default.join(homeDir2, ".codex", "config.toml"));
14255
+ const cfg = readToml(import_path21.default.join(homeDir2, ".codex", "config.toml"));
13559
14256
  return !!(cfg?.mcp_servers && hasNode9McpServer(cfg.mcp_servers));
13560
14257
  })();
13561
14258
  const windsurfWired = (() => {
13562
- const cfg = readJson(
13563
- import_path20.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
14259
+ const cfg = readJson2(
14260
+ import_path21.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
13564
14261
  );
13565
14262
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
13566
14263
  })();
13567
14264
  const vscodeWired = (() => {
13568
- const cfg = readJson(import_path20.default.join(homeDir2, ".vscode", "mcp.json"));
14265
+ const cfg = readJson2(import_path21.default.join(homeDir2, ".vscode", "mcp.json"));
13569
14266
  return !!(cfg?.servers && hasNode9McpServerVSCode(cfg.servers));
13570
14267
  })();
13571
14268
  return [
@@ -13632,7 +14329,7 @@ function getAgentsStatus(homeDir2 = import_os16.default.homedir()) {
13632
14329
  wired: (() => {
13633
14330
  const cfgPath = claudeDesktopConfigPath(homeDir2);
13634
14331
  if (!cfgPath) return false;
13635
- const cfg = readJson(cfgPath);
14332
+ const cfg = readJson2(cfgPath);
13636
14333
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
13637
14334
  })(),
13638
14335
  mode: detected.claudeDesktop ? "mcp" : null
@@ -13642,16 +14339,16 @@ function getAgentsStatus(homeDir2 = import_os16.default.homedir()) {
13642
14339
  label: "Opencode",
13643
14340
  installed: detected.opencode,
13644
14341
  wired: (() => {
13645
- const pluginPath = import_path20.default.join(
14342
+ const pluginPath = import_path21.default.join(
13646
14343
  homeDir2,
13647
14344
  ".config",
13648
14345
  "opencode",
13649
14346
  "plugins",
13650
14347
  OPENCODE_PLUGIN_NAME
13651
14348
  );
13652
- if (import_fs19.default.existsSync(pluginPath)) return true;
13653
- const cfg = readJson(
13654
- import_path20.default.join(homeDir2, ".config", "opencode", "opencode.json")
14349
+ if (import_fs21.default.existsSync(pluginPath)) return true;
14350
+ const cfg = readJson2(
14351
+ import_path21.default.join(homeDir2, ".config", "opencode", "opencode.json")
13655
14352
  );
13656
14353
  return !!cfg?.mcp?.["node9"];
13657
14354
  })(),
@@ -13663,7 +14360,7 @@ function getAgentsStatus(homeDir2 = import_os16.default.homedir()) {
13663
14360
  installed: detected.pi,
13664
14361
  // Pi has no MCP path — only the extension file. "wired" is a
13665
14362
  // simple existence check on the canonical install location.
13666
- wired: import_fs19.default.existsSync(import_path20.default.join(homeDir2, ".pi", "agent", "extensions", PI_EXTENSION_NAME)),
14363
+ wired: import_fs21.default.existsSync(import_path21.default.join(homeDir2, ".pi", "agent", "extensions", PI_EXTENSION_NAME)),
13667
14364
  mode: detected.pi ? "hooks" : null
13668
14365
  },
13669
14366
  {
@@ -13675,8 +14372,8 @@ function getAgentsStatus(homeDir2 = import_os16.default.homedir()) {
13675
14372
  // Document API for a boolean status check.
13676
14373
  wired: (() => {
13677
14374
  try {
13678
- const raw = import_fs19.default.readFileSync(hermesConfigPath(homeDir2), "utf-8");
13679
- const cfg = yaml.parse(raw);
14375
+ const raw = import_fs21.default.readFileSync(hermesConfigPath(homeDir2), "utf-8");
14376
+ const cfg = yaml2.parse(raw);
13680
14377
  const pre = cfg?.hooks?.["pre_tool_call"] ?? [];
13681
14378
  return pre.some((e) => typeof e?.command === "string" && isNode9Hook(e.command));
13682
14379
  } catch {
@@ -13687,18 +14384,19 @@ function getAgentsStatus(homeDir2 = import_os16.default.homedir()) {
13687
14384
  }
13688
14385
  ];
13689
14386
  }
13690
- var import_fs19, import_path20, import_os16, import_chalk, import_prompts, import_smol_toml2, yaml, NODE9_MCP_SERVER_ENTRY, MCP_WRAP_SUBCOMMAND, LEGACY_MCP_WRAP_SUBCOMMAND, CODEX_PRE_TOOL_MATCHERS, OPENCODE_PLUGIN_NAME, PI_EXTENSION_NAME, HERMES_CONFIG_FILENAME, HERMES_ALLOWLIST_FILENAME, HERMES_HOOK_PLAN;
14387
+ var import_fs21, import_path21, import_os18, import_chalk, import_prompts, import_smol_toml4, yaml2, NODE9_MCP_SERVER_ENTRY, MCP_WRAP_SUBCOMMAND, LEGACY_MCP_WRAP_SUBCOMMAND, CODEX_PRE_TOOL_MATCHERS, OPENCODE_PLUGIN_NAME, PI_EXTENSION_NAME, HERMES_CONFIG_FILENAME, HERMES_ALLOWLIST_FILENAME, HERMES_HOOK_PLAN;
13691
14388
  var init_setup = __esm({
13692
14389
  "src/setup.ts"() {
13693
14390
  "use strict";
13694
- import_fs19 = __toESM(require("fs"));
13695
- import_path20 = __toESM(require("path"));
13696
- import_os16 = __toESM(require("os"));
14391
+ import_fs21 = __toESM(require("fs"));
14392
+ import_path21 = __toESM(require("path"));
14393
+ import_os18 = __toESM(require("os"));
13697
14394
  import_chalk = __toESM(require("chalk"));
13698
14395
  import_prompts = require("@inquirer/prompts");
13699
- import_smol_toml2 = require("smol-toml");
14396
+ import_smol_toml4 = require("smol-toml");
14397
+ init_mcp_wrap();
13700
14398
  init_codex_trust();
13701
- yaml = __toESM(require("yaml"));
14399
+ yaml2 = __toESM(require("yaml"));
13702
14400
  init_mcp_pin();
13703
14401
  init_hook_baseline();
13704
14402
  init_setup_opencode_shim();
@@ -13719,244 +14417,6 @@ var init_setup = __esm({
13719
14417
  }
13720
14418
  });
13721
14419
 
13722
- // src/agent-wiring.ts
13723
- function readJson2(filePath) {
13724
- if (!import_fs20.default.existsSync(filePath)) return null;
13725
- try {
13726
- return JSON.parse(import_fs20.default.readFileSync(filePath, "utf-8"));
13727
- } catch {
13728
- return "invalid";
13729
- }
13730
- }
13731
- function matchersHaveNode9Hook(matchers) {
13732
- return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
13733
- }
13734
- function flatHaveNode9Hook(entries) {
13735
- return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
13736
- }
13737
- function readHookRoot(filePath, format) {
13738
- if (!import_fs20.default.existsSync(filePath)) return "absent";
13739
- let raw;
13740
- try {
13741
- raw = import_fs20.default.readFileSync(filePath, "utf-8");
13742
- } catch {
13743
- return "absent";
13744
- }
13745
- try {
13746
- const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
13747
- return parsed?.hooks ?? {};
13748
- } catch {
13749
- return "invalid";
13750
- }
13751
- }
13752
- function eventWired(root, ev, format) {
13753
- const arr = root[ev.key];
13754
- if (format === "matcher") return matchersHaveNode9Hook(arr);
13755
- return flatHaveNode9Hook(arr);
13756
- }
13757
- function detectMcp(servers) {
13758
- const entries = Object.entries(servers ?? {});
13759
- const present = entries.some(([, s]) => s?.command === "node9");
13760
- const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
13761
- return { wrapped, present };
13762
- }
13763
- function readMcpServers(filePath, format) {
13764
- if (!import_fs20.default.existsSync(filePath)) return {};
13765
- try {
13766
- if (format === "toml") {
13767
- const parsed2 = (0, import_smol_toml3.parse)(import_fs20.default.readFileSync(filePath, "utf-8"));
13768
- return parsed2?.mcp_servers ?? {};
13769
- }
13770
- const parsed = readJson2(filePath);
13771
- if (parsed === null || parsed === "invalid") return {};
13772
- return parsed.mcpServers ?? {};
13773
- } catch {
13774
- return {};
13775
- }
13776
- }
13777
- function readMcp(filePath, format) {
13778
- if (!import_fs20.default.existsSync(filePath)) return { wrapped: [], present: false };
13779
- try {
13780
- if (format === "toml") {
13781
- const parsed2 = (0, import_smol_toml3.parse)(import_fs20.default.readFileSync(filePath, "utf-8"));
13782
- return detectMcp(parsed2?.mcp_servers);
13783
- }
13784
- const parsed = readJson2(filePath);
13785
- if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
13786
- return detectMcp(parsed.mcpServers);
13787
- } catch {
13788
- return { wrapped: [], present: false };
13789
- }
13790
- }
13791
- function getAgentWiring(home = import_os17.default.homedir()) {
13792
- const detected = detectAgents(home);
13793
- return AGENT_SPECS.map((spec) => {
13794
- const present = spec.present(home);
13795
- const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
13796
- let hooks;
13797
- let wireState;
13798
- let hookLabel;
13799
- let settingsPath;
13800
- if (spec.shimFile) {
13801
- const shimWired = exists(spec.shimFile(home));
13802
- hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
13803
- wireState = shimWired ? "wired" : present ? "unwired" : "absent";
13804
- hookLabel = "node9 plugin";
13805
- settingsPath = spec.shimFile(home);
13806
- } else {
13807
- const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
13808
- const primary = spec.hookEvents[0];
13809
- const rootPresent = root !== "absent" && root !== "invalid";
13810
- hooks = spec.hookEvents.map((ev) => ({
13811
- label: hookLabelOf(ev, pad),
13812
- wired: rootPresent && eventWired(root, ev, spec.hookFormat)
13813
- }));
13814
- if (root === "absent") wireState = "absent";
13815
- else if (root === "invalid") wireState = "invalid";
13816
- else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
13817
- hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
13818
- settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
13819
- }
13820
- const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
13821
- const anyHookWired = hooks.some((h) => h.wired);
13822
- return {
13823
- id: spec.id,
13824
- label: spec.label,
13825
- setupCommand: spec.setupCommand,
13826
- installed: detected[spec.id],
13827
- present,
13828
- hooks,
13829
- wireState,
13830
- hookLabel,
13831
- settingsPath,
13832
- configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
13833
- mcpServers: mcp ? mcp.wrapped : null,
13834
- mcpProtected: mcp ? mcp.present : false,
13835
- isProtected: anyHookWired || (mcp?.present ?? false)
13836
- };
13837
- });
13838
- }
13839
- var import_fs20, import_path21, import_os17, yaml2, import_smol_toml3, exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
13840
- var init_agent_wiring = __esm({
13841
- "src/agent-wiring.ts"() {
13842
- "use strict";
13843
- import_fs20 = __toESM(require("fs"));
13844
- import_path21 = __toESM(require("path"));
13845
- import_os17 = __toESM(require("os"));
13846
- yaml2 = __toESM(require("yaml"));
13847
- import_smol_toml3 = require("smol-toml");
13848
- init_setup();
13849
- exists = (p) => {
13850
- try {
13851
- return import_fs20.default.existsSync(p);
13852
- } catch {
13853
- return false;
13854
- }
13855
- };
13856
- ck = (key) => ({ key, kind: "check" });
13857
- lg = (key) => ({ key, kind: "log" });
13858
- DEFAULT_LABEL_PAD = 11;
13859
- hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
13860
- AGENT_SPECS = [
13861
- {
13862
- id: "claude",
13863
- label: "Claude Code",
13864
- setupCommand: "node9 agents add claude",
13865
- hookFile: (h) => import_path21.default.join(h, ".claude", "settings.json"),
13866
- hookFormat: "matcher",
13867
- // UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
13868
- // that shipped; the spec must name it too, or status/doctor never show the
13869
- // row and heal (which repairs via setupAgent) has no signal it is missing.
13870
- hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
13871
- mcpFile: (h) => import_path21.default.join(h, ".claude.json"),
13872
- present: (h) => exists(import_path21.default.join(h, ".claude", "settings.json")) || exists(import_path21.default.join(h, ".claude.json"))
13873
- },
13874
- {
13875
- id: "gemini",
13876
- label: "Gemini CLI",
13877
- setupCommand: "node9 agents add gemini",
13878
- hookFile: (h) => import_path21.default.join(h, ".gemini", "settings.json"),
13879
- hookFormat: "matcher",
13880
- hookEvents: [ck("BeforeTool"), lg("AfterTool")],
13881
- mcpFile: (h) => import_path21.default.join(h, ".gemini", "settings.json"),
13882
- present: (h) => exists(import_path21.default.join(h, ".gemini", "settings.json"))
13883
- },
13884
- {
13885
- id: "codex",
13886
- label: "Codex",
13887
- setupCommand: "node9 agents add codex",
13888
- hookFile: (h) => import_path21.default.join(h, ".codex", "hooks.json"),
13889
- hookFormat: "matcher",
13890
- hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
13891
- mcpFile: (h) => import_path21.default.join(h, ".codex", "config.toml"),
13892
- mcpFormat: "toml",
13893
- present: (h) => exists(import_path21.default.join(h, ".codex"))
13894
- },
13895
- {
13896
- id: "antigravity",
13897
- label: "Antigravity",
13898
- setupCommand: "node9 agents add antigravity",
13899
- hookFile: (h) => import_path21.default.join(h, ".gemini", "config", "hooks.json"),
13900
- hookFormat: "matcher",
13901
- hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
13902
- mcpFile: (h) => import_path21.default.join(h, ".gemini", "config", "mcp_config.json"),
13903
- present: (h) => exists(import_path21.default.join(h, ".gemini", "config", "hooks.json")) || exists(import_path21.default.join(h, ".gemini", "antigravity-cli")) || exists(import_path21.default.join(h, ".gemini", "antigravity-ide"))
13904
- },
13905
- {
13906
- id: "copilot",
13907
- label: "GitHub Copilot",
13908
- setupCommand: "node9 agents add copilot",
13909
- hookFile: (h) => import_path21.default.join(h, ".copilot", "hooks", "node9.json"),
13910
- hookFormat: "flat",
13911
- hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
13912
- mcpFile: (h) => import_path21.default.join(h, ".copilot", "mcp-config.json"),
13913
- present: (h) => exists(import_path21.default.join(h, ".copilot"))
13914
- },
13915
- {
13916
- id: "cursor",
13917
- label: "Cursor",
13918
- setupCommand: "node9 agents add cursor",
13919
- // MCP-only — no hook file (see note above).
13920
- hookFormat: "flat",
13921
- hookEvents: [],
13922
- mcpFile: (h) => import_path21.default.join(h, ".cursor", "mcp.json"),
13923
- present: (h) => exists(import_path21.default.join(h, ".cursor", "mcp.json"))
13924
- },
13925
- {
13926
- id: "hermes",
13927
- label: "Hermes Agent",
13928
- setupCommand: "node9 agents add hermes",
13929
- hookFile: (h) => hermesConfigPath(h),
13930
- hookFormat: "yaml",
13931
- hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
13932
- labelPad: 14,
13933
- // 'post_tool_call' is wider than the default
13934
- present: (h) => exists(hermesConfigPath(h))
13935
- },
13936
- {
13937
- // Plugin-shim agents — protected by a node9-authored plugin/extension file
13938
- // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
13939
- id: "opencode",
13940
- label: "OpenCode",
13941
- setupCommand: "node9 agents add opencode",
13942
- hookFormat: "flat",
13943
- hookEvents: [],
13944
- shimFile: (h) => import_path21.default.join(opencodeConfigDir(h), "plugins", "node9.js"),
13945
- present: (h) => exists(opencodeConfigDir(h)) || exists(import_path21.default.join(opencodeConfigDir(h), "plugins", "node9.js"))
13946
- },
13947
- {
13948
- id: "pi",
13949
- label: "Pi",
13950
- setupCommand: "node9 agents add pi",
13951
- hookFormat: "flat",
13952
- hookEvents: [],
13953
- shimFile: (h) => import_path21.default.join(h, ".pi", "agent", "extensions", "node9.js"),
13954
- present: (h) => exists(import_path21.default.join(h, ".pi", "agent")) || exists(import_path21.default.join(h, ".pi", "agent", "extensions", "node9.js"))
13955
- }
13956
- ];
13957
- }
13958
- });
13959
-
13960
14420
  // src/config/keyed-guard.ts
13961
14421
  function isKeyedForPolicy() {
13962
14422
  try {
@@ -13994,7 +14454,7 @@ function normalizeModel(raw) {
13994
14454
  }
13995
14455
  function readCache(opts) {
13996
14456
  try {
13997
- const raw = JSON.parse(import_fs21.default.readFileSync(CACHE_FILE(), "utf-8"));
14457
+ const raw = JSON.parse(import_fs22.default.readFileSync(CACHE_FILE(), "utf-8"));
13998
14458
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
13999
14459
  return null;
14000
14460
  }
@@ -14010,17 +14470,17 @@ function writeCache(prices) {
14010
14470
  try {
14011
14471
  const target = CACHE_FILE();
14012
14472
  const dir = import_path22.default.dirname(target);
14013
- if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
14473
+ if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
14014
14474
  const tmp = target + ".tmp";
14015
14475
  const body = {
14016
14476
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
14017
14477
  prices
14018
14478
  };
14019
- import_fs21.default.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
14020
- import_fs21.default.renameSync(tmp, target);
14479
+ import_fs22.default.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
14480
+ import_fs22.default.renameSync(tmp, target);
14021
14481
  } catch (err2) {
14022
14482
  try {
14023
- import_fs21.default.appendFileSync(
14483
+ import_fs22.default.appendFileSync(
14024
14484
  HOOK_DEBUG_LOG,
14025
14485
  `[pricing] cache write failed: ${err2.message}
14026
14486
  `
@@ -14123,13 +14583,13 @@ function pricingFor(model, options = {}) {
14123
14583
  lookupCache.set(lookupKey, resolved);
14124
14584
  return resolved;
14125
14585
  }
14126
- var import_fs21, import_path22, import_os18, LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, diskChecked, lookupCache;
14586
+ var import_fs22, import_path22, import_os19, LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, diskChecked, lookupCache;
14127
14587
  var init_litellm = __esm({
14128
14588
  "src/pricing/litellm.ts"() {
14129
14589
  "use strict";
14130
- import_fs21 = __toESM(require("fs"));
14590
+ import_fs22 = __toESM(require("fs"));
14131
14591
  import_path22 = __toESM(require("path"));
14132
- import_os18 = __toESM(require("os"));
14592
+ import_os19 = __toESM(require("os"));
14133
14593
  init_audit();
14134
14594
  LITELLM_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
14135
14595
  BUNDLED_PRICING = {
@@ -14181,7 +14641,7 @@ var init_litellm = __esm({
14181
14641
  "gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
14182
14642
  "gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
14183
14643
  };
14184
- CACHE_FILE = () => import_path22.default.join(import_os18.default.homedir(), ".node9", "model-pricing.json");
14644
+ CACHE_FILE = () => import_path22.default.join(import_os19.default.homedir(), ".node9", "model-pricing.json");
14185
14645
  TTL_MS = 24 * 60 * 60 * 1e3;
14186
14646
  memCache = null;
14187
14647
  memCacheAt = 0;
@@ -14192,7 +14652,7 @@ var init_litellm = __esm({
14192
14652
 
14193
14653
  // src/cost-gemini.ts
14194
14654
  function geminiTmpDir() {
14195
- return import_path23.default.join(import_os19.default.homedir(), ".gemini", "tmp");
14655
+ return import_path23.default.join(import_os20.default.homedir(), ".gemini", "tmp");
14196
14656
  }
14197
14657
  function geminiPriceFor(model) {
14198
14658
  let tuple = pricingFor(model);
@@ -14207,14 +14667,14 @@ function geminiPriceFor(model) {
14207
14667
  }
14208
14668
  function safeReaddir(dir) {
14209
14669
  try {
14210
- return import_fs22.default.readdirSync(dir);
14670
+ return import_fs23.default.readdirSync(dir);
14211
14671
  } catch {
14212
14672
  return [];
14213
14673
  }
14214
14674
  }
14215
14675
  function isDir(p) {
14216
14676
  try {
14217
- return import_fs22.default.statSync(p).isDirectory();
14677
+ return import_fs23.default.statSync(p).isDirectory();
14218
14678
  } catch {
14219
14679
  return false;
14220
14680
  }
@@ -14283,12 +14743,12 @@ function parseGeminiSession(lines, project) {
14283
14743
  if (runId) for (const e of byKey.values()) e.runId = runId;
14284
14744
  return [...byKey.values()];
14285
14745
  }
14286
- var import_fs22, import_os19, import_path23, GEMINI_FALLBACK_MODELS, geminiSource;
14746
+ var import_fs23, import_os20, import_path23, GEMINI_FALLBACK_MODELS, geminiSource;
14287
14747
  var init_cost_gemini = __esm({
14288
14748
  "src/cost-gemini.ts"() {
14289
14749
  "use strict";
14290
- import_fs22 = __toESM(require("fs"));
14291
- import_os19 = __toESM(require("os"));
14750
+ import_fs23 = __toESM(require("fs"));
14751
+ import_os20 = __toESM(require("os"));
14292
14752
  import_path23 = __toESM(require("path"));
14293
14753
  init_litellm();
14294
14754
  GEMINI_FALLBACK_MODELS = ["gemini-2.5-flash", "gemini-2.0-flash"];
@@ -14296,7 +14756,7 @@ var init_cost_gemini = __esm({
14296
14756
  id: "gemini",
14297
14757
  available() {
14298
14758
  try {
14299
- return import_fs22.default.existsSync(geminiTmpDir());
14759
+ return import_fs23.default.existsSync(geminiTmpDir());
14300
14760
  } catch {
14301
14761
  return false;
14302
14762
  }
@@ -14305,13 +14765,13 @@ var init_cost_gemini = __esm({
14305
14765
  const combined = /* @__PURE__ */ new Map();
14306
14766
  for (const { file, project } of listGeminiSessionFiles(geminiTmpDir())) {
14307
14767
  try {
14308
- if (sinceMs !== void 0 && import_fs22.default.statSync(file).mtimeMs < sinceMs) continue;
14768
+ if (sinceMs !== void 0 && import_fs23.default.statSync(file).mtimeMs < sinceMs) continue;
14309
14769
  } catch {
14310
14770
  continue;
14311
14771
  }
14312
14772
  let content;
14313
14773
  try {
14314
- content = import_fs22.default.readFileSync(file, "utf8");
14774
+ content = import_fs23.default.readFileSync(file, "utf8");
14315
14775
  } catch {
14316
14776
  continue;
14317
14777
  }
@@ -14337,7 +14797,7 @@ var init_cost_gemini = __esm({
14337
14797
 
14338
14798
  // src/cost-codex.ts
14339
14799
  function codexSessionsDir() {
14340
- return import_path24.default.join(process.env.CODEX_HOME?.trim() || import_path24.default.join(import_os20.default.homedir(), ".codex"), "sessions");
14800
+ return import_path24.default.join(process.env.CODEX_HOME?.trim() || import_path24.default.join(import_os21.default.homedir(), ".codex"), "sessions");
14341
14801
  }
14342
14802
  function codexPriceFor(model) {
14343
14803
  return pricingFor(codexModel(model), { exact: true }) ?? CODEX_FALLBACK;
@@ -14369,14 +14829,14 @@ function codexSessionCost(model, tokens, request2) {
14369
14829
  function statAndFirstLine(file) {
14370
14830
  const CAP = 4 * 1024 * 1024;
14371
14831
  const CHUNK = 64 * 1024;
14372
- const fd = import_fs23.default.openSync(file, "r");
14832
+ const fd = import_fs24.default.openSync(file, "r");
14373
14833
  try {
14374
- const stat = import_fs23.default.fstatSync(fd);
14834
+ const stat = import_fs24.default.fstatSync(fd);
14375
14835
  const limit = Math.min(stat.size, CAP);
14376
14836
  const parts = [];
14377
14837
  for (let pos = 0; pos < limit; pos += CHUNK) {
14378
14838
  const buf = Buffer.alloc(Math.min(CHUNK, limit - pos));
14379
- const read2 = import_fs23.default.readSync(fd, buf, 0, buf.length, pos);
14839
+ const read2 = import_fs24.default.readSync(fd, buf, 0, buf.length, pos);
14380
14840
  if (read2 <= 0) break;
14381
14841
  const slice = buf.subarray(0, read2);
14382
14842
  const nl = slice.indexOf(10);
@@ -14385,14 +14845,14 @@ function statAndFirstLine(file) {
14385
14845
  }
14386
14846
  return { stat, first: Buffer.concat(parts).toString("utf8") };
14387
14847
  } finally {
14388
- import_fs23.default.closeSync(fd);
14848
+ import_fs24.default.closeSync(fd);
14389
14849
  }
14390
14850
  }
14391
14851
  function listCodexSessionFiles(base = codexSessionsDir()) {
14392
14852
  const files = [];
14393
14853
  const walk = (dir) => {
14394
14854
  try {
14395
- for (const entry of import_fs23.default.readdirSync(dir, { withFileTypes: true })) {
14855
+ for (const entry of import_fs24.default.readdirSync(dir, { withFileTypes: true })) {
14396
14856
  const file = import_path24.default.join(dir, entry.name);
14397
14857
  if (entry.isDirectory()) walk(file);
14398
14858
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file);
@@ -14591,24 +15051,24 @@ function parseCodexSession(lines) {
14591
15051
  }
14592
15052
  return [...rows.values()];
14593
15053
  }
14594
- var import_fs23, import_os20, import_path24, CODEX_FALLBACK, codexSource;
15054
+ var import_fs24, import_os21, import_path24, CODEX_FALLBACK, codexSource;
14595
15055
  var init_cost_codex = __esm({
14596
15056
  "src/cost-codex.ts"() {
14597
15057
  "use strict";
14598
- import_fs23 = __toESM(require("fs"));
14599
- import_os20 = __toESM(require("os"));
15058
+ import_fs24 = __toESM(require("fs"));
15059
+ import_os21 = __toESM(require("os"));
14600
15060
  import_path24 = __toESM(require("path"));
14601
15061
  init_litellm();
14602
15062
  CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
14603
15063
  codexSource = {
14604
15064
  id: "codex",
14605
- available: () => import_fs23.default.existsSync(codexSessionsDir()) || import_fs23.default.existsSync(import_path24.default.join(import_path24.default.dirname(codexSessionsDir()), "archived_sessions")),
15065
+ available: () => import_fs24.default.existsSync(codexSessionsDir()) || import_fs24.default.existsSync(import_path24.default.join(import_path24.default.dirname(codexSessionsDir()), "archived_sessions")),
14606
15066
  collect(sinceMs) {
14607
15067
  const entries = [];
14608
15068
  for (const file of listCodexSessionFiles()) {
14609
15069
  try {
14610
- if (sinceMs !== void 0 && import_fs23.default.statSync(file).mtimeMs < sinceMs) continue;
14611
- entries.push(...parseCodexSession(import_fs23.default.readFileSync(file, "utf8").split("\n")));
15070
+ if (sinceMs !== void 0 && import_fs24.default.statSync(file).mtimeMs < sinceMs) continue;
15071
+ entries.push(...parseCodexSession(import_fs24.default.readFileSync(file, "utf8").split("\n")));
14612
15072
  } catch {
14613
15073
  }
14614
15074
  }
@@ -14620,11 +15080,11 @@ var init_cost_codex = __esm({
14620
15080
 
14621
15081
  // src/cost-copilot.ts
14622
15082
  function copilotSessionsDir() {
14623
- return import_path25.default.join(import_os21.default.homedir(), ".copilot", "session-state");
15083
+ return import_path25.default.join(import_os22.default.homedir(), ".copilot", "session-state");
14624
15084
  }
14625
15085
  function safeReaddir2(dir) {
14626
15086
  try {
14627
- return import_fs24.default.readdirSync(dir);
15087
+ return import_fs25.default.readdirSync(dir);
14628
15088
  } catch {
14629
15089
  return [];
14630
15090
  }
@@ -14691,19 +15151,19 @@ function parseCopilotSession(lines) {
14691
15151
  }
14692
15152
  return rows;
14693
15153
  }
14694
- var import_fs24, import_os21, import_path25, copilotSource;
15154
+ var import_fs25, import_os22, import_path25, copilotSource;
14695
15155
  var init_cost_copilot = __esm({
14696
15156
  "src/cost-copilot.ts"() {
14697
15157
  "use strict";
14698
- import_fs24 = __toESM(require("fs"));
14699
- import_os21 = __toESM(require("os"));
15158
+ import_fs25 = __toESM(require("fs"));
15159
+ import_os22 = __toESM(require("os"));
14700
15160
  import_path25 = __toESM(require("path"));
14701
15161
  init_litellm();
14702
15162
  copilotSource = {
14703
15163
  id: "copilot",
14704
15164
  available() {
14705
15165
  try {
14706
- return import_fs24.default.existsSync(copilotSessionsDir());
15166
+ return import_fs25.default.existsSync(copilotSessionsDir());
14707
15167
  } catch {
14708
15168
  return false;
14709
15169
  }
@@ -14714,13 +15174,13 @@ var init_cost_copilot = __esm({
14714
15174
  for (const sid of safeReaddir2(base)) {
14715
15175
  const file = import_path25.default.join(base, sid, "events.jsonl");
14716
15176
  try {
14717
- if (sinceMs !== void 0 && import_fs24.default.statSync(file).mtimeMs < sinceMs) continue;
15177
+ if (sinceMs !== void 0 && import_fs25.default.statSync(file).mtimeMs < sinceMs) continue;
14718
15178
  } catch {
14719
15179
  continue;
14720
15180
  }
14721
15181
  let content;
14722
15182
  try {
14723
- content = import_fs24.default.readFileSync(file, "utf8");
15183
+ content = import_fs25.default.readFileSync(file, "utf8");
14724
15184
  } catch {
14725
15185
  continue;
14726
15186
  }
@@ -15157,7 +15617,7 @@ function buildSensitivePaths(home, cwd) {
15157
15617
  }
15158
15618
  function isReadable(filePath) {
15159
15619
  try {
15160
- import_fs25.default.accessSync(filePath, import_fs25.default.constants.R_OK);
15620
+ import_fs26.default.accessSync(filePath, import_fs26.default.constants.R_OK);
15161
15621
  return true;
15162
15622
  } catch {
15163
15623
  return false;
@@ -15170,13 +15630,13 @@ function scoreLabel(score) {
15170
15630
  return import_chalk3.default.red.bold(`${score}/100 Critical`);
15171
15631
  }
15172
15632
  function runBlast() {
15173
- const home = import_os22.default.homedir();
15633
+ const home = import_os23.default.homedir();
15174
15634
  const cwd = process.cwd();
15175
15635
  const paths = buildSensitivePaths(home, cwd);
15176
15636
  let scoreDeduction = 0;
15177
15637
  const reachable = [];
15178
15638
  for (const p of paths) {
15179
- if (import_fs25.default.existsSync(p.full) && isReadable(p.full)) {
15639
+ if (import_fs26.default.existsSync(p.full) && isReadable(p.full)) {
15180
15640
  reachable.push(p);
15181
15641
  scoreDeduction += p.score;
15182
15642
  }
@@ -15194,7 +15654,7 @@ function runBlast() {
15194
15654
  }
15195
15655
  function registerBlastCommand(program2) {
15196
15656
  program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
15197
- const home = import_os22.default.homedir();
15657
+ const home = import_os23.default.homedir();
15198
15658
  const cwd = process.cwd();
15199
15659
  const { reachable, envFindings, score } = runBlast();
15200
15660
  console.log("");
@@ -15241,14 +15701,14 @@ function registerBlastCommand(program2) {
15241
15701
  console.log("");
15242
15702
  });
15243
15703
  }
15244
- var import_chalk3, import_fs25, import_path26, import_os22;
15704
+ var import_chalk3, import_fs26, import_path26, import_os23;
15245
15705
  var init_blast = __esm({
15246
15706
  "src/cli/commands/blast.ts"() {
15247
15707
  "use strict";
15248
15708
  import_chalk3 = __toESM(require("chalk"));
15249
- import_fs25 = __toESM(require("fs"));
15709
+ import_fs26 = __toESM(require("fs"));
15250
15710
  import_path26 = __toESM(require("path"));
15251
- import_os22 = __toESM(require("os"));
15711
+ import_os23 = __toESM(require("os"));
15252
15712
  init_dlp();
15253
15713
  }
15254
15714
  });
@@ -15394,7 +15854,7 @@ function listSessionFiles(dir, maxDepth = 6) {
15394
15854
  if (depth > maxDepth) return;
15395
15855
  let entries;
15396
15856
  try {
15397
- entries = fs27.readdirSync(d, { withFileTypes: true });
15857
+ entries = fs28.readdirSync(d, { withFileTypes: true });
15398
15858
  } catch {
15399
15859
  return;
15400
15860
  }
@@ -15410,24 +15870,24 @@ function listSessionFiles(dir, maxDepth = 6) {
15410
15870
  function sessionIdOf(relPath) {
15411
15871
  return path28.basename(relPath).replace(/\.jsonl$/, "");
15412
15872
  }
15413
- var fs27, path28;
15873
+ var fs28, path28;
15414
15874
  var init_session_files = __esm({
15415
15875
  "src/session-files.ts"() {
15416
15876
  "use strict";
15417
- fs27 = __toESM(require("fs"));
15877
+ fs28 = __toESM(require("fs"));
15418
15878
  path28 = __toESM(require("path"));
15419
15879
  }
15420
15880
  });
15421
15881
 
15422
15882
  // src/cli/render/scan-history.ts
15423
15883
  function defaultHistoryPath() {
15424
- return import_path27.default.join(import_os23.default.homedir(), ".node9", "scan-history.json");
15884
+ return import_path27.default.join(import_os24.default.homedir(), ".node9", "scan-history.json");
15425
15885
  }
15426
15886
  function readPreviousScan(opts = {}) {
15427
15887
  const filePath = opts.path ?? defaultHistoryPath();
15428
15888
  try {
15429
- if (!import_fs26.default.existsSync(filePath)) return null;
15430
- const raw = import_fs26.default.readFileSync(filePath, "utf8");
15889
+ if (!import_fs27.default.existsSync(filePath)) return null;
15890
+ const raw = import_fs27.default.readFileSync(filePath, "utf8");
15431
15891
  const parsed = JSON.parse(raw);
15432
15892
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
15433
15893
  const last = parsed[parsed.length - 1];
@@ -15441,11 +15901,11 @@ function appendScanHistory(record2, opts = {}) {
15441
15901
  const filePath = opts.path ?? defaultHistoryPath();
15442
15902
  const cap = opts.cap ?? SCAN_HISTORY_CAP;
15443
15903
  try {
15444
- import_fs26.default.mkdirSync(import_path27.default.dirname(filePath), { recursive: true });
15904
+ import_fs27.default.mkdirSync(import_path27.default.dirname(filePath), { recursive: true });
15445
15905
  let history = [];
15446
- if (import_fs26.default.existsSync(filePath)) {
15906
+ if (import_fs27.default.existsSync(filePath)) {
15447
15907
  try {
15448
- const parsed = JSON.parse(import_fs26.default.readFileSync(filePath, "utf8"));
15908
+ const parsed = JSON.parse(import_fs27.default.readFileSync(filePath, "utf8"));
15449
15909
  if (Array.isArray(parsed)) {
15450
15910
  history = parsed.filter(isValidRecord);
15451
15911
  }
@@ -15478,13 +15938,13 @@ function isValidRecord(x) {
15478
15938
  const r = x;
15479
15939
  return typeof r.timestamp === "string" && typeof r.score === "number" && typeof r.blocked === "number" && typeof r.review === "number" && typeof r.leaks === "number" && typeof r.loops === "number" && typeof r.totalCalls === "number";
15480
15940
  }
15481
- var import_fs26, import_path27, import_os23, SCAN_HISTORY_CAP;
15941
+ var import_fs27, import_path27, import_os24, SCAN_HISTORY_CAP;
15482
15942
  var init_scan_history = __esm({
15483
15943
  "src/cli/render/scan-history.ts"() {
15484
15944
  "use strict";
15485
- import_fs26 = __toESM(require("fs"));
15945
+ import_fs27 = __toESM(require("fs"));
15486
15946
  import_path27 = __toESM(require("path"));
15487
- import_os23 = __toESM(require("os"));
15947
+ import_os24 = __toESM(require("os"));
15488
15948
  init_atomic_write();
15489
15949
  SCAN_HISTORY_CAP = 30;
15490
15950
  }
@@ -15498,7 +15958,7 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
15498
15958
  const runId = import_path28.default.basename(filePath, ".jsonl");
15499
15959
  let content;
15500
15960
  try {
15501
- content = import_fs27.default.readFileSync(filePath, "utf8");
15961
+ content = import_fs28.default.readFileSync(filePath, "utf8");
15502
15962
  } catch {
15503
15963
  return /* @__PURE__ */ new Map();
15504
15964
  }
@@ -15598,7 +16058,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
15598
16058
  signal: AbortSignal.timeout(15e3)
15599
16059
  });
15600
16060
  if (!res.ok) {
15601
- import_fs27.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
16061
+ import_fs28.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
15602
16062
  `);
15603
16063
  } else {
15604
16064
  let stored;
@@ -15608,7 +16068,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
15608
16068
  } catch {
15609
16069
  }
15610
16070
  if (typeof stored === "number" && stored < batch.length) {
15611
- import_fs27.default.appendFileSync(
16071
+ import_fs28.default.appendFileSync(
15612
16072
  HOOK_DEBUG_LOG,
15613
16073
  `[cost-sync] dropped ${batch.length - stored} of ${batch.length} rows
15614
16074
  `
@@ -15616,7 +16076,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
15616
16076
  }
15617
16077
  }
15618
16078
  } catch (err2) {
15619
- import_fs27.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${safeMessage(err2)}
16079
+ import_fs28.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${safeMessage(err2)}
15620
16080
  `);
15621
16081
  }
15622
16082
  }
@@ -15629,10 +16089,10 @@ async function syncCost() {
15629
16089
  if (entries.length === 0) return;
15630
16090
  let username = "unknown";
15631
16091
  try {
15632
- username = import_os24.default.userInfo().username;
16092
+ username = import_os25.default.userInfo().username;
15633
16093
  } catch {
15634
16094
  }
15635
- const machineId = `${import_os24.default.hostname()}:${username}`;
16095
+ const machineId = `${import_os25.default.hostname()}:${username}`;
15636
16096
  await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
15637
16097
  }
15638
16098
  function startCostSync() {
@@ -15644,13 +16104,13 @@ function startCostSync() {
15644
16104
  }, SYNC_INTERVAL_MS);
15645
16105
  timer.unref();
15646
16106
  }
15647
- var import_fs27, import_path28, import_os24, SYNC_INTERVAL_MS, claudeSource, COST_SOURCES, COST_BATCH_SIZE;
16107
+ var import_fs28, import_path28, import_os25, SYNC_INTERVAL_MS, claudeSource, COST_SOURCES, COST_BATCH_SIZE;
15648
16108
  var init_costSync = __esm({
15649
16109
  "src/costSync.ts"() {
15650
16110
  "use strict";
15651
- import_fs27 = __toESM(require("fs"));
16111
+ import_fs28 = __toESM(require("fs"));
15652
16112
  import_path28 = __toESM(require("path"));
15653
- import_os24 = __toESM(require("os"));
16113
+ import_os25 = __toESM(require("os"));
15654
16114
  init_config();
15655
16115
  init_audit();
15656
16116
  init_litellm();
@@ -15663,22 +16123,22 @@ var init_costSync = __esm({
15663
16123
  claudeSource = {
15664
16124
  id: "claude",
15665
16125
  available() {
15666
- return import_fs27.default.existsSync(import_path28.default.join(import_os24.default.homedir(), ".claude", "projects"));
16126
+ return import_fs28.default.existsSync(import_path28.default.join(import_os25.default.homedir(), ".claude", "projects"));
15667
16127
  },
15668
16128
  collect(sinceMs) {
15669
- const projectsDir = import_path28.default.join(import_os24.default.homedir(), ".claude", "projects");
15670
- if (!import_fs27.default.existsSync(projectsDir)) return [];
16129
+ const projectsDir = import_path28.default.join(import_os25.default.homedir(), ".claude", "projects");
16130
+ if (!import_fs28.default.existsSync(projectsDir)) return [];
15671
16131
  const combined = /* @__PURE__ */ new Map();
15672
16132
  let dirs;
15673
16133
  try {
15674
- dirs = import_fs27.default.readdirSync(projectsDir);
16134
+ dirs = import_fs28.default.readdirSync(projectsDir);
15675
16135
  } catch {
15676
16136
  return [];
15677
16137
  }
15678
16138
  for (const dir of dirs) {
15679
16139
  const dirPath = import_path28.default.join(projectsDir, dir);
15680
16140
  try {
15681
- if (!import_fs27.default.statSync(dirPath).isDirectory()) continue;
16141
+ if (!import_fs28.default.statSync(dirPath).isDirectory()) continue;
15682
16142
  } catch {
15683
16143
  continue;
15684
16144
  }
@@ -15693,7 +16153,7 @@ var init_costSync = __esm({
15693
16153
  const filePath = import_path28.default.join(dirPath, file);
15694
16154
  if (sinceMs !== void 0) {
15695
16155
  try {
15696
- if (import_fs27.default.statSync(filePath).mtimeMs < sinceMs) continue;
16156
+ if (import_fs28.default.statSync(filePath).mtimeMs < sinceMs) continue;
15697
16157
  } catch {
15698
16158
  continue;
15699
16159
  }
@@ -15744,7 +16204,7 @@ function freshWatermark() {
15744
16204
  function loadWatermark() {
15745
16205
  let raw;
15746
16206
  try {
15747
- raw = import_fs28.default.readFileSync(WATERMARK_FILE(), "utf-8");
16207
+ raw = import_fs29.default.readFileSync(WATERMARK_FILE(), "utf-8");
15748
16208
  } catch {
15749
16209
  return { status: "fresh", wm: freshWatermark() };
15750
16210
  }
@@ -15797,21 +16257,21 @@ function saveWatermark(wm) {
15797
16257
  if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
15798
16258
  const target = WATERMARK_FILE();
15799
16259
  const dir = import_path29.default.dirname(target);
15800
- if (!import_fs28.default.existsSync(dir)) import_fs28.default.mkdirSync(dir, { recursive: true });
16260
+ if (!import_fs29.default.existsSync(dir)) import_fs29.default.mkdirSync(dir, { recursive: true });
15801
16261
  const tmp = target + ".tmp";
15802
- import_fs28.default.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
15803
- import_fs28.default.renameSync(tmp, target);
16262
+ import_fs29.default.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
16263
+ import_fs29.default.renameSync(tmp, target);
15804
16264
  }
15805
16265
  function listJsonlFiles() {
15806
16266
  const root = PROJECTS_DIR();
15807
- if (!import_fs28.default.existsSync(root)) return [];
16267
+ if (!import_fs29.default.existsSync(root)) return [];
15808
16268
  const out = [];
15809
- for (const entry of import_fs28.default.readdirSync(root, { withFileTypes: true })) {
16269
+ for (const entry of import_fs29.default.readdirSync(root, { withFileTypes: true })) {
15810
16270
  if (!entry.isDirectory()) continue;
15811
16271
  const projectDir = import_path29.default.join(root, entry.name);
15812
16272
  let inner;
15813
16273
  try {
15814
- inner = import_fs28.default.readdirSync(projectDir, { withFileTypes: true });
16274
+ inner = import_fs29.default.readdirSync(projectDir, { withFileTypes: true });
15815
16275
  } catch {
15816
16276
  continue;
15817
16277
  }
@@ -15825,7 +16285,7 @@ function listJsonlFiles() {
15825
16285
  }
15826
16286
  function fileSize(p) {
15827
16287
  try {
15828
- return import_fs28.default.statSync(p).size;
16288
+ return import_fs29.default.statSync(p).size;
15829
16289
  } catch {
15830
16290
  return 0;
15831
16291
  }
@@ -15835,7 +16295,7 @@ async function scanDelta(filePath, fromByte, onLine) {
15835
16295
  if (size <= fromByte) return fromByte;
15836
16296
  const lastNl = findLastNewline(filePath, fromByte, size);
15837
16297
  const endsWithNewline = lastNl === size - 1;
15838
- const stream = import_fs28.default.createReadStream(filePath, {
16298
+ const stream = import_fs29.default.createReadStream(filePath, {
15839
16299
  start: fromByte,
15840
16300
  end: size - 1,
15841
16301
  highWaterMark: 64 * 1024
@@ -15864,7 +16324,7 @@ function findLastNewline(filePath, from, size) {
15864
16324
  const CHUNK = 64 * 1024;
15865
16325
  let fd;
15866
16326
  try {
15867
- fd = import_fs28.default.openSync(filePath, "r");
16327
+ fd = import_fs29.default.openSync(filePath, "r");
15868
16328
  } catch {
15869
16329
  return -1;
15870
16330
  }
@@ -15873,7 +16333,7 @@ function findLastNewline(filePath, from, size) {
15873
16333
  let end = size;
15874
16334
  while (end > from) {
15875
16335
  const start = Math.max(from, end - CHUNK);
15876
- const n = import_fs28.default.readSync(fd, buf, 0, end - start, start);
16336
+ const n = import_fs29.default.readSync(fd, buf, 0, end - start, start);
15877
16337
  const idx = buf.subarray(0, n).lastIndexOf(10);
15878
16338
  if (idx !== -1) return start + idx;
15879
16339
  end = start;
@@ -15882,7 +16342,7 @@ function findLastNewline(filePath, from, size) {
15882
16342
  } catch {
15883
16343
  return -1;
15884
16344
  } finally {
15885
- import_fs28.default.closeSync(fd);
16345
+ import_fs29.default.closeSync(fd);
15886
16346
  }
15887
16347
  }
15888
16348
  function safeCanaryCtxValues() {
@@ -16047,7 +16507,7 @@ function emptyTick(uploadAs) {
16047
16507
  function readRawWatermarkPreservingOffsets() {
16048
16508
  let raw;
16049
16509
  try {
16050
- raw = import_fs28.default.readFileSync(WATERMARK_FILE(), "utf-8");
16510
+ raw = import_fs29.default.readFileSync(WATERMARK_FILE(), "utf-8");
16051
16511
  } catch {
16052
16512
  return null;
16053
16513
  }
@@ -16082,7 +16542,7 @@ async function runActualTick(wm) {
16082
16542
  if (!known) {
16083
16543
  let mtimeMs = 0;
16084
16544
  try {
16085
- mtimeMs = import_fs28.default.statSync(filePath).mtime.getTime();
16545
+ mtimeMs = import_fs29.default.statSync(filePath).mtime.getTime();
16086
16546
  } catch {
16087
16547
  continue;
16088
16548
  }
@@ -16133,20 +16593,20 @@ async function runActualTick(wm) {
16133
16593
  }
16134
16594
  return result;
16135
16595
  }
16136
- var import_fs28, import_os25, import_path29, import_readline, PROJECTS_DIR, WATERMARK_FILE, MAX_LINE_BYTES, WATERMARK_SCHEMA_VERSION, LONG_OUTPUT_THRESHOLD_BYTES2;
16596
+ var import_fs29, import_os26, import_path29, import_readline, PROJECTS_DIR, WATERMARK_FILE, MAX_LINE_BYTES, WATERMARK_SCHEMA_VERSION, LONG_OUTPUT_THRESHOLD_BYTES2;
16137
16597
  var init_scan_watermark = __esm({
16138
16598
  "src/daemon/scan-watermark.ts"() {
16139
16599
  "use strict";
16140
- import_fs28 = __toESM(require("fs"));
16141
- import_os25 = __toESM(require("os"));
16600
+ import_fs29 = __toESM(require("fs"));
16601
+ import_os26 = __toESM(require("os"));
16142
16602
  import_path29 = __toESM(require("path"));
16143
16603
  import_readline = __toESM(require("readline"));
16144
16604
  init_dlp();
16145
16605
  init_registry();
16146
16606
  init_config();
16147
16607
  init_dist();
16148
- PROJECTS_DIR = () => import_path29.default.join(import_os25.default.homedir(), ".claude", "projects");
16149
- WATERMARK_FILE = () => import_path29.default.join(import_os25.default.homedir(), ".node9", "scan-watermark.json");
16608
+ PROJECTS_DIR = () => import_path29.default.join(import_os26.default.homedir(), ".claude", "projects");
16609
+ WATERMARK_FILE = () => import_path29.default.join(import_os26.default.homedir(), ".node9", "scan-watermark.json");
16150
16610
  MAX_LINE_BYTES = 2 * 1024 * 1024;
16151
16611
  WATERMARK_SCHEMA_VERSION = 2;
16152
16612
  LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
@@ -16195,10 +16655,10 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
16195
16655
  return now.getTime() - 90 * 864e5;
16196
16656
  }
16197
16657
  function* iterateJsonlFiles(cutoffMs) {
16198
- const projectsDir = import_path30.default.join(import_os26.default.homedir(), ".claude", "projects");
16658
+ const projectsDir = import_path30.default.join(import_os27.default.homedir(), ".claude", "projects");
16199
16659
  let dirs;
16200
16660
  try {
16201
- dirs = import_fs29.default.readdirSync(projectsDir);
16661
+ dirs = import_fs30.default.readdirSync(projectsDir);
16202
16662
  } catch {
16203
16663
  return;
16204
16664
  }
@@ -16206,14 +16666,14 @@ function* iterateJsonlFiles(cutoffMs) {
16206
16666
  const dirPath = import_path30.default.join(projectsDir, dir);
16207
16667
  let stats;
16208
16668
  try {
16209
- stats = import_fs29.default.statSync(dirPath);
16669
+ stats = import_fs30.default.statSync(dirPath);
16210
16670
  } catch {
16211
16671
  continue;
16212
16672
  }
16213
16673
  if (!stats.isDirectory()) continue;
16214
16674
  let files;
16215
16675
  try {
16216
- files = import_fs29.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
16676
+ files = import_fs30.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
16217
16677
  } catch {
16218
16678
  continue;
16219
16679
  }
@@ -16221,7 +16681,7 @@ function* iterateJsonlFiles(cutoffMs) {
16221
16681
  const filePath = import_path30.default.join(dirPath, file);
16222
16682
  let mtime = 0;
16223
16683
  try {
16224
- mtime = import_fs29.default.statSync(filePath).mtimeMs;
16684
+ mtime = import_fs30.default.statSync(filePath).mtimeMs;
16225
16685
  } catch {
16226
16686
  continue;
16227
16687
  }
@@ -16296,7 +16756,7 @@ async function runUploadHistory(opts) {
16296
16756
  filesScanned++;
16297
16757
  let content;
16298
16758
  try {
16299
- content = import_fs29.default.readFileSync(filePath, "utf8");
16759
+ content = import_fs30.default.readFileSync(filePath, "utf8");
16300
16760
  } catch {
16301
16761
  continue;
16302
16762
  }
@@ -16370,10 +16830,10 @@ async function runUploadHistory(opts) {
16370
16830
  const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
16371
16831
  let username = "unknown";
16372
16832
  try {
16373
- username = import_os26.default.userInfo().username;
16833
+ username = import_os27.default.userInfo().username;
16374
16834
  } catch {
16375
16835
  }
16376
- const machineId = `${import_os26.default.hostname()}:${username}`;
16836
+ const machineId = `${import_os27.default.hostname()}:${username}`;
16377
16837
  await postJson(costUrl, creds.apiKey, {
16378
16838
  machineId,
16379
16839
  entries: dailyEntries
@@ -16423,13 +16883,13 @@ async function postJson(url, apiKey, body) {
16423
16883
  req.end();
16424
16884
  });
16425
16885
  }
16426
- var import_fs29, import_https, import_os26, import_path30, import_chalk5, FINDING_TO_SIGNAL2;
16886
+ var import_fs30, import_https, import_os27, import_path30, import_chalk5, FINDING_TO_SIGNAL2;
16427
16887
  var init_scan_upload_history = __esm({
16428
16888
  "src/scan-upload-history.ts"() {
16429
16889
  "use strict";
16430
- import_fs29 = __toESM(require("fs"));
16890
+ import_fs30 = __toESM(require("fs"));
16431
16891
  import_https = __toESM(require("https"));
16432
- import_os26 = __toESM(require("os"));
16892
+ import_os27 = __toESM(require("os"));
16433
16893
  import_path30 = __toESM(require("path"));
16434
16894
  import_chalk5 = __toESM(require("chalk"));
16435
16895
  init_dist();
@@ -16537,7 +16997,7 @@ function findingKey(ruleName, inputPreview, projLabel) {
16537
16997
  return `${ruleName ?? "<unnamed>"}|${inputPreview}|${projLabel}`;
16538
16998
  }
16539
16999
  function displayHome(p) {
16540
- const home = import_os27.default.homedir();
17000
+ const home = import_os28.default.homedir();
16541
17001
  return p.startsWith(home) ? "~" + p.slice(home.length) : p;
16542
17002
  }
16543
17003
  function dlpKey(patternName, redactedSample, projLabel) {
@@ -16734,14 +17194,14 @@ function buildRuleSources() {
16734
17194
  }
16735
17195
  function countScanFiles() {
16736
17196
  let total = 0;
16737
- const claudeDir = import_path31.default.join(import_os27.default.homedir(), ".claude", "projects");
16738
- if (import_fs30.default.existsSync(claudeDir)) {
17197
+ const claudeDir = import_path31.default.join(import_os28.default.homedir(), ".claude", "projects");
17198
+ if (import_fs31.default.existsSync(claudeDir)) {
16739
17199
  try {
16740
- for (const proj of import_fs30.default.readdirSync(claudeDir)) {
17200
+ for (const proj of import_fs31.default.readdirSync(claudeDir)) {
16741
17201
  const p = import_path31.default.join(claudeDir, proj);
16742
17202
  try {
16743
- if (!import_fs30.default.statSync(p).isDirectory()) continue;
16744
- total += import_fs30.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
17203
+ if (!import_fs31.default.statSync(p).isDirectory()) continue;
17204
+ total += import_fs31.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
16745
17205
  } catch {
16746
17206
  continue;
16747
17207
  }
@@ -16749,17 +17209,17 @@ function countScanFiles() {
16749
17209
  } catch {
16750
17210
  }
16751
17211
  }
16752
- const geminiDir = import_path31.default.join(import_os27.default.homedir(), ".gemini", "tmp");
16753
- if (import_fs30.default.existsSync(geminiDir)) {
17212
+ const geminiDir = import_path31.default.join(import_os28.default.homedir(), ".gemini", "tmp");
17213
+ if (import_fs31.default.existsSync(geminiDir)) {
16754
17214
  try {
16755
- for (const slug2 of import_fs30.default.readdirSync(geminiDir)) {
17215
+ for (const slug2 of import_fs31.default.readdirSync(geminiDir)) {
16756
17216
  const p = import_path31.default.join(geminiDir, slug2);
16757
17217
  try {
16758
- if (!import_fs30.default.statSync(p).isDirectory()) continue;
17218
+ if (!import_fs31.default.statSync(p).isDirectory()) continue;
16759
17219
  const chatsDir = import_path31.default.join(p, "chats");
16760
- if (import_fs30.default.existsSync(chatsDir)) {
17220
+ if (import_fs31.default.existsSync(chatsDir)) {
16761
17221
  try {
16762
- total += import_fs30.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
17222
+ total += import_fs31.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
16763
17223
  } catch {
16764
17224
  }
16765
17225
  }
@@ -16771,15 +17231,15 @@ function countScanFiles() {
16771
17231
  }
16772
17232
  }
16773
17233
  for (const surface of ["antigravity-cli", "antigravity-ide"]) {
16774
- const brainDir = import_path31.default.join(import_os27.default.homedir(), ".gemini", surface, "brain");
16775
- if (!import_fs30.default.existsSync(brainDir)) continue;
17234
+ const brainDir = import_path31.default.join(import_os28.default.homedir(), ".gemini", surface, "brain");
17235
+ if (!import_fs31.default.existsSync(brainDir)) continue;
16776
17236
  try {
16777
- for (const conv of import_fs30.default.readdirSync(brainDir)) {
17237
+ for (const conv of import_fs31.default.readdirSync(brainDir)) {
16778
17238
  const convPath = import_path31.default.join(brainDir, conv);
16779
17239
  try {
16780
- if (!import_fs30.default.statSync(convPath).isDirectory()) continue;
17240
+ if (!import_fs31.default.statSync(convPath).isDirectory()) continue;
16781
17241
  const logsDir = import_path31.default.join(convPath, ".system_generated", "logs");
16782
- if (import_fs30.default.existsSync(import_path31.default.join(logsDir, "transcript_full.jsonl")) || import_fs30.default.existsSync(import_path31.default.join(logsDir, "transcript.jsonl"))) {
17242
+ if (import_fs31.default.existsSync(import_path31.default.join(logsDir, "transcript_full.jsonl")) || import_fs31.default.existsSync(import_path31.default.join(logsDir, "transcript.jsonl"))) {
16783
17243
  total += 1;
16784
17244
  }
16785
17245
  } catch {
@@ -16789,11 +17249,11 @@ function countScanFiles() {
16789
17249
  } catch {
16790
17250
  }
16791
17251
  }
16792
- const copilotDir = import_path31.default.join(import_os27.default.homedir(), ".copilot", "session-state");
16793
- if (import_fs30.default.existsSync(copilotDir)) {
17252
+ const copilotDir = import_path31.default.join(import_os28.default.homedir(), ".copilot", "session-state");
17253
+ if (import_fs31.default.existsSync(copilotDir)) {
16794
17254
  try {
16795
- for (const sid of import_fs30.default.readdirSync(copilotDir)) {
16796
- if (import_fs30.default.existsSync(import_path31.default.join(copilotDir, sid, "events.jsonl"))) total += 1;
17255
+ for (const sid of import_fs31.default.readdirSync(copilotDir)) {
17256
+ if (import_fs31.default.existsSync(import_path31.default.join(copilotDir, sid, "events.jsonl"))) total += 1;
16797
17257
  }
16798
17258
  } catch {
16799
17259
  }
@@ -16820,7 +17280,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
16820
17280
  const session = { sessionId, costUSD: 0, toolCalls: 0 };
16821
17281
  let raw;
16822
17282
  try {
16823
- raw = import_fs30.default.readFileSync(import_path31.default.join(projPath, file), "utf-8");
17283
+ raw = import_fs31.default.readFileSync(import_path31.default.join(projPath, file), "utf-8");
16824
17284
  } catch {
16825
17285
  return;
16826
17286
  }
@@ -17064,11 +17524,11 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
17064
17524
  function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, canaryVals, onProgress, onLine) {
17065
17525
  const projPath = import_path31.default.join(projectsDir, proj);
17066
17526
  try {
17067
- if (!import_fs30.default.statSync(projPath).isDirectory()) return;
17527
+ if (!import_fs31.default.statSync(projPath).isDirectory()) return;
17068
17528
  } catch {
17069
17529
  return;
17070
17530
  }
17071
- const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(import_os27.default.homedir(), "~")).slice(
17531
+ const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(import_os28.default.homedir(), "~")).slice(
17072
17532
  0,
17073
17533
  40
17074
17534
  );
@@ -17111,12 +17571,12 @@ function emptyClaudeScan() {
17111
17571
  };
17112
17572
  }
17113
17573
  function scanClaudeHistory(startDate, onProgress, onLine) {
17114
- const projectsDir = import_path31.default.join(import_os27.default.homedir(), ".claude", "projects");
17574
+ const projectsDir = import_path31.default.join(import_os28.default.homedir(), ".claude", "projects");
17115
17575
  const result = emptyClaudeScan();
17116
- if (!import_fs30.default.existsSync(projectsDir)) return result;
17576
+ if (!import_fs31.default.existsSync(projectsDir)) return result;
17117
17577
  let projDirs;
17118
17578
  try {
17119
- projDirs = import_fs30.default.readdirSync(projectsDir);
17579
+ projDirs = import_fs31.default.readdirSync(projectsDir);
17120
17580
  } catch {
17121
17581
  return result;
17122
17582
  }
@@ -17140,7 +17600,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
17140
17600
  }
17141
17601
  function scanGeminiHistory(startDate, onProgress, onLine) {
17142
17602
  const canaryVals = safeCanaryScanValues();
17143
- const tmpDir = import_path31.default.join(import_os27.default.homedir(), ".gemini", "tmp");
17603
+ const tmpDir = import_path31.default.join(import_os28.default.homedir(), ".gemini", "tmp");
17144
17604
  const result = {
17145
17605
  filesScanned: 0,
17146
17606
  sessions: 0,
@@ -17157,10 +17617,10 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
17157
17617
  perSession: []
17158
17618
  };
17159
17619
  const dedup = emptyScanDedup();
17160
- if (!import_fs30.default.existsSync(tmpDir)) return result;
17620
+ if (!import_fs31.default.existsSync(tmpDir)) return result;
17161
17621
  let slugDirs;
17162
17622
  try {
17163
- slugDirs = import_fs30.default.readdirSync(tmpDir);
17623
+ slugDirs = import_fs31.default.readdirSync(tmpDir);
17164
17624
  } catch {
17165
17625
  return result;
17166
17626
  }
@@ -17168,22 +17628,22 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
17168
17628
  for (const slug2 of slugDirs) {
17169
17629
  const slugPath = import_path31.default.join(tmpDir, slug2);
17170
17630
  try {
17171
- if (!import_fs30.default.statSync(slugPath).isDirectory()) continue;
17631
+ if (!import_fs31.default.statSync(slugPath).isDirectory()) continue;
17172
17632
  } catch {
17173
17633
  continue;
17174
17634
  }
17175
17635
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
17176
17636
  try {
17177
17637
  projLabel = stripTerminalEscapes(
17178
- import_fs30.default.readFileSync(import_path31.default.join(slugPath, ".project_root"), "utf-8").trim()
17179
- ).replace(import_os27.default.homedir(), "~").slice(0, 40);
17638
+ import_fs31.default.readFileSync(import_path31.default.join(slugPath, ".project_root"), "utf-8").trim()
17639
+ ).replace(import_os28.default.homedir(), "~").slice(0, 40);
17180
17640
  } catch {
17181
17641
  }
17182
17642
  const chatsDir = import_path31.default.join(slugPath, "chats");
17183
- if (!import_fs30.default.existsSync(chatsDir)) continue;
17643
+ if (!import_fs31.default.existsSync(chatsDir)) continue;
17184
17644
  let chatFiles;
17185
17645
  try {
17186
- chatFiles = import_fs30.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
17646
+ chatFiles = import_fs31.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
17187
17647
  } catch {
17188
17648
  continue;
17189
17649
  }
@@ -17196,7 +17656,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
17196
17656
  onProgress?.(result.filesScanned);
17197
17657
  let raw;
17198
17658
  try {
17199
- raw = import_fs30.default.readFileSync(import_path31.default.join(chatsDir, chatFile), "utf-8");
17659
+ raw = import_fs31.default.readFileSync(import_path31.default.join(chatsDir, chatFile), "utf-8");
17200
17660
  } catch {
17201
17661
  continue;
17202
17662
  }
@@ -17391,13 +17851,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
17391
17851
  return result;
17392
17852
  }
17393
17853
  function antigravityBrainDirs() {
17394
- return ["antigravity-cli", "antigravity-ide"].map((surface) => import_path31.default.join(import_os27.default.homedir(), ".gemini", surface, "brain")).filter((p) => import_fs30.default.existsSync(p));
17854
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => import_path31.default.join(import_os28.default.homedir(), ".gemini", surface, "brain")).filter((p) => import_fs31.default.existsSync(p));
17395
17855
  }
17396
17856
  function antigravityTranscriptPath(convPath) {
17397
17857
  const logsDir = import_path31.default.join(convPath, ".system_generated", "logs");
17398
17858
  for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
17399
17859
  const p = import_path31.default.join(logsDir, name);
17400
- if (import_fs30.default.existsSync(p)) return p;
17860
+ if (import_fs31.default.existsSync(p)) return p;
17401
17861
  }
17402
17862
  return null;
17403
17863
  }
@@ -17426,14 +17886,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
17426
17886
  for (const brainDir of brainDirs) {
17427
17887
  let convDirs;
17428
17888
  try {
17429
- convDirs = import_fs30.default.readdirSync(brainDir);
17889
+ convDirs = import_fs31.default.readdirSync(brainDir);
17430
17890
  } catch {
17431
17891
  continue;
17432
17892
  }
17433
17893
  for (const conv of convDirs) {
17434
17894
  const convPath = import_path31.default.join(brainDir, conv);
17435
17895
  try {
17436
- if (!import_fs30.default.statSync(convPath).isDirectory()) continue;
17896
+ if (!import_fs31.default.statSync(convPath).isDirectory()) continue;
17437
17897
  } catch {
17438
17898
  continue;
17439
17899
  }
@@ -17443,7 +17903,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
17443
17903
  onProgress?.(result.filesScanned);
17444
17904
  let raw;
17445
17905
  try {
17446
- raw = import_fs30.default.readFileSync(transcriptFile, "utf-8");
17906
+ raw = import_fs31.default.readFileSync(transcriptFile, "utf-8");
17447
17907
  } catch {
17448
17908
  continue;
17449
17909
  }
@@ -17511,7 +17971,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
17511
17971
  result.bashCalls++;
17512
17972
  const cwd = String(input.cwd ?? "");
17513
17973
  if (cwd && projLabel === conv.slice(0, 8)) {
17514
- projLabel = stripTerminalEscapes(cwd).replace(import_os27.default.homedir(), "~").slice(0, 40);
17974
+ projLabel = stripTerminalEscapes(cwd).replace(import_os28.default.homedir(), "~").slice(0, 40);
17515
17975
  }
17516
17976
  }
17517
17977
  const rawCmd = String(input.command ?? "").trimStart();
@@ -17623,7 +18083,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
17623
18083
  }
17624
18084
  function scanCopilotHistory(startDate, onProgress, onLine) {
17625
18085
  const canaryVals = safeCanaryScanValues();
17626
- const sessionDir = import_path31.default.join(import_os27.default.homedir(), ".copilot", "session-state");
18086
+ const sessionDir = import_path31.default.join(import_os28.default.homedir(), ".copilot", "session-state");
17627
18087
  const result = {
17628
18088
  filesScanned: 0,
17629
18089
  sessions: 0,
@@ -17641,22 +18101,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
17641
18101
  perSession: []
17642
18102
  };
17643
18103
  const dedup = emptyScanDedup();
17644
- if (!import_fs30.default.existsSync(sessionDir)) return result;
18104
+ if (!import_fs31.default.existsSync(sessionDir)) return result;
17645
18105
  let sessionIds;
17646
18106
  try {
17647
- sessionIds = import_fs30.default.readdirSync(sessionDir);
18107
+ sessionIds = import_fs31.default.readdirSync(sessionDir);
17648
18108
  } catch {
17649
18109
  return result;
17650
18110
  }
17651
18111
  const ruleSources = buildRuleSources();
17652
18112
  for (const sessionId of sessionIds) {
17653
18113
  const eventsPath = import_path31.default.join(sessionDir, sessionId, "events.jsonl");
17654
- if (!import_fs30.default.existsSync(eventsPath)) continue;
18114
+ if (!import_fs31.default.existsSync(eventsPath)) continue;
17655
18115
  result.filesScanned++;
17656
18116
  onProgress?.(result.filesScanned);
17657
18117
  let raw;
17658
18118
  try {
17659
- raw = import_fs30.default.readFileSync(eventsPath, "utf-8");
18119
+ raw = import_fs31.default.readFileSync(eventsPath, "utf-8");
17660
18120
  } catch {
17661
18121
  continue;
17662
18122
  }
@@ -17680,7 +18140,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
17680
18140
  if (ev.type === "session.start") {
17681
18141
  const cwd = ev.data?.context?.cwd;
17682
18142
  if (typeof cwd === "string" && cwd) {
17683
- projLabel = stripTerminalEscapes(cwd).replace(import_os27.default.homedir(), "~").slice(0, 40);
18143
+ projLabel = stripTerminalEscapes(cwd).replace(import_os28.default.homedir(), "~").slice(0, 40);
17684
18144
  }
17685
18145
  continue;
17686
18146
  }
@@ -17858,7 +18318,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
17858
18318
  onProgress?.(result.filesScanned);
17859
18319
  let lines;
17860
18320
  try {
17861
- lines = import_fs30.default.readFileSync(filePath, "utf-8").split("\n");
18321
+ lines = import_fs31.default.readFileSync(filePath, "utf-8").split("\n");
17862
18322
  } catch {
17863
18323
  continue;
17864
18324
  }
@@ -17881,7 +18341,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
17881
18341
  sessionId = String(payload["id"] ?? filePath);
17882
18342
  startTime = String(payload["timestamp"] ?? "");
17883
18343
  const cwd = String(payload["cwd"] ?? "");
17884
- projLabel = stripTerminalEscapes(cwd.replace(import_os27.default.homedir(), "~")).slice(0, 40);
18344
+ projLabel = stripTerminalEscapes(cwd.replace(import_os28.default.homedir(), "~")).slice(0, 40);
17885
18345
  continue;
17886
18346
  }
17887
18347
  if (entry.type === "event_msg" && payload["type"] === "user_message") {
@@ -18048,17 +18508,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
18048
18508
  return result;
18049
18509
  }
18050
18510
  function scanShellConfig() {
18051
- const home = import_os27.default.homedir();
18511
+ const home = import_os28.default.homedir();
18052
18512
  const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
18053
18513
  (f) => import_path31.default.join(home, f)
18054
18514
  );
18055
18515
  const findings = [];
18056
18516
  const seen = /* @__PURE__ */ new Set();
18057
18517
  for (const filePath of configFiles) {
18058
- if (!import_fs30.default.existsSync(filePath)) continue;
18518
+ if (!import_fs31.default.existsSync(filePath)) continue;
18059
18519
  let lines;
18060
18520
  try {
18061
- lines = import_fs30.default.readFileSync(filePath, "utf-8").split("\n");
18521
+ lines = import_fs31.default.readFileSync(filePath, "utf-8").split("\n");
18062
18522
  } catch {
18063
18523
  continue;
18064
18524
  }
@@ -19203,15 +19663,15 @@ function registerScanCommand(program2) {
19203
19663
  }
19204
19664
  );
19205
19665
  }
19206
- var import_chalk6, import_fs30, import_path31, import_os27, import_string_width2, toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
19666
+ var import_chalk6, import_fs31, import_path31, import_os28, import_string_width2, toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
19207
19667
  var init_scan = __esm({
19208
19668
  "src/cli/commands/scan.ts"() {
19209
19669
  "use strict";
19210
19670
  init_keyed_guard();
19211
19671
  import_chalk6 = __toESM(require("chalk"));
19212
- import_fs30 = __toESM(require("fs"));
19672
+ import_fs31 = __toESM(require("fs"));
19213
19673
  import_path31 = __toESM(require("path"));
19214
- import_os27 = __toESM(require("os"));
19674
+ import_os28 = __toESM(require("os"));
19215
19675
  init_shields();
19216
19676
  init_config();
19217
19677
  init_policy();
@@ -19296,7 +19756,7 @@ var init_scan = __esm({
19296
19756
  function readOwnVersion() {
19297
19757
  for (const rel of ["../package.json", "../../package.json"]) {
19298
19758
  try {
19299
- const raw = import_fs31.default.readFileSync(import_path32.default.join(__dirname, rel), "utf-8");
19759
+ const raw = import_fs32.default.readFileSync(import_path32.default.join(__dirname, rel), "utf-8");
19300
19760
  const v = JSON.parse(raw).version;
19301
19761
  if (typeof v === "string" && v.length > 0) return v;
19302
19762
  } catch {
@@ -19307,7 +19767,7 @@ function readOwnVersion() {
19307
19767
  function computeBuildId(entry = process.argv[1] ?? "") {
19308
19768
  let mtimeMs = 0;
19309
19769
  try {
19310
- if (entry) mtimeMs = import_fs31.default.statSync(entry).mtimeMs;
19770
+ if (entry) mtimeMs = import_fs32.default.statSync(entry).mtimeMs;
19311
19771
  } catch {
19312
19772
  }
19313
19773
  return { version: readOwnVersion(), mtimeMs };
@@ -19349,11 +19809,11 @@ function describeBuildDrift(running, installed) {
19349
19809
  const theirVersion = typeof running.version === "string" ? running.version : "unknown";
19350
19810
  return `running daemon is v${theirVersion} (build ${theirs}) but installed is v${installed.version} (build ${mine}) \u2014 it is enforcing a different build`;
19351
19811
  }
19352
- var import_fs31, import_path32, CURRENT_BUILD;
19812
+ var import_fs32, import_path32, CURRENT_BUILD;
19353
19813
  var init_build_id = __esm({
19354
19814
  "src/daemon/build-id.ts"() {
19355
19815
  "use strict";
19356
- import_fs31 = __toESM(require("fs"));
19816
+ import_fs32 = __toESM(require("fs"));
19357
19817
  import_path32 = __toESM(require("path"));
19358
19818
  CURRENT_BUILD = parseBuildId(process.env.NODE9_BUILD_ID_OVERRIDE) ?? computeBuildId();
19359
19819
  }
@@ -19450,11 +19910,11 @@ var init_suggestion_tracker = __esm({
19450
19910
  });
19451
19911
 
19452
19912
  // src/daemon/taint-store.ts
19453
- var import_fs32, import_path33, DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
19913
+ var import_fs33, import_path33, DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
19454
19914
  var init_taint_store = __esm({
19455
19915
  "src/daemon/taint-store.ts"() {
19456
19916
  "use strict";
19457
- import_fs32 = __toESM(require("fs"));
19917
+ import_fs33 = __toESM(require("fs"));
19458
19918
  import_path33 = __toESM(require("path"));
19459
19919
  DEFAULT_TTL_MS = 60 * 60 * 1e3;
19460
19920
  TaintStore = class {
@@ -19521,7 +19981,7 @@ var init_taint_store = __esm({
19521
19981
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
19522
19982
  _resolve(filePath) {
19523
19983
  try {
19524
- return import_fs32.default.realpathSync.native(import_path33.default.resolve(filePath));
19984
+ return import_fs33.default.realpathSync.native(import_path33.default.resolve(filePath));
19525
19985
  } catch {
19526
19986
  return import_path33.default.resolve(filePath);
19527
19987
  }
@@ -19689,8 +20149,8 @@ var init_session_history = __esm({
19689
20149
  // src/daemon/state.ts
19690
20150
  function loadInsightCounts() {
19691
20151
  try {
19692
- if (!import_fs33.default.existsSync(INSIGHT_COUNTS_FILE)) return;
19693
- const data = JSON.parse(import_fs33.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
20152
+ if (!import_fs34.default.existsSync(INSIGHT_COUNTS_FILE)) return;
20153
+ const data = JSON.parse(import_fs34.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
19694
20154
  for (const [tool, count] of Object.entries(data)) {
19695
20155
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
19696
20156
  }
@@ -19747,15 +20207,15 @@ function appendAuditLog(data) {
19747
20207
  source: "daemon"
19748
20208
  };
19749
20209
  const dir = import_path34.default.dirname(AUDIT_LOG_FILE);
19750
- if (!import_fs33.default.existsSync(dir)) import_fs33.default.mkdirSync(dir, { recursive: true });
19751
- import_fs33.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
20210
+ if (!import_fs34.default.existsSync(dir)) import_fs34.default.mkdirSync(dir, { recursive: true });
20211
+ import_fs34.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
19752
20212
  } catch {
19753
20213
  }
19754
20214
  }
19755
20215
  function getAuditHistory(limit = 20) {
19756
20216
  try {
19757
- if (!import_fs33.default.existsSync(AUDIT_LOG_FILE)) return [];
19758
- const lines = import_fs33.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
20217
+ if (!import_fs34.default.existsSync(AUDIT_LOG_FILE)) return [];
20218
+ const lines = import_fs34.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
19759
20219
  if (lines.length === 1 && lines[0] === "") return [];
19760
20220
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
19761
20221
  } catch {
@@ -19764,7 +20224,7 @@ function getAuditHistory(limit = 20) {
19764
20224
  }
19765
20225
  function getOrgName() {
19766
20226
  try {
19767
- if (import_fs33.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
20227
+ if (import_fs34.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
19768
20228
  } catch {
19769
20229
  }
19770
20230
  return null;
@@ -19772,8 +20232,8 @@ function getOrgName() {
19772
20232
  function writeGlobalSetting(key, value) {
19773
20233
  let config = {};
19774
20234
  try {
19775
- if (import_fs33.default.existsSync(GLOBAL_CONFIG_FILE)) {
19776
- config = JSON.parse(import_fs33.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
20235
+ if (import_fs34.default.existsSync(GLOBAL_CONFIG_FILE)) {
20236
+ config = JSON.parse(import_fs34.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
19777
20237
  }
19778
20238
  } catch {
19779
20239
  }
@@ -19785,8 +20245,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
19785
20245
  try {
19786
20246
  let trust = { entries: [] };
19787
20247
  try {
19788
- if (import_fs33.default.existsSync(TRUST_FILE2))
19789
- trust = JSON.parse(import_fs33.default.readFileSync(TRUST_FILE2, "utf-8"));
20248
+ if (import_fs34.default.existsSync(TRUST_FILE2))
20249
+ trust = JSON.parse(import_fs34.default.readFileSync(TRUST_FILE2, "utf-8"));
19790
20250
  } catch {
19791
20251
  }
19792
20252
  trust.entries = trust.entries.filter(
@@ -19803,8 +20263,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
19803
20263
  }
19804
20264
  function readPersistentDecisions() {
19805
20265
  try {
19806
- if (import_fs33.default.existsSync(DECISIONS_FILE)) {
19807
- return JSON.parse(import_fs33.default.readFileSync(DECISIONS_FILE, "utf-8"));
20266
+ if (import_fs34.default.existsSync(DECISIONS_FILE)) {
20267
+ return JSON.parse(import_fs34.default.readFileSync(DECISIONS_FILE, "utf-8"));
19808
20268
  }
19809
20269
  } catch {
19810
20270
  }
@@ -19832,7 +20292,7 @@ function estimateToolCost(tool, args) {
19832
20292
  const filePath = a.file_path ?? a.path;
19833
20293
  if (filePath) {
19834
20294
  try {
19835
- const bytes = import_fs33.default.statSync(filePath).size;
20295
+ const bytes = import_fs34.default.statSync(filePath).size;
19836
20296
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
19837
20297
  } catch {
19838
20298
  }
@@ -19906,7 +20366,7 @@ function abandonPending() {
19906
20366
  });
19907
20367
  if (autoStarted) {
19908
20368
  try {
19909
- import_fs33.default.unlinkSync(DAEMON_PID_FILE);
20369
+ import_fs34.default.unlinkSync(DAEMON_PID_FILE);
19910
20370
  } catch {
19911
20371
  }
19912
20372
  setTimeout(() => {
@@ -19917,7 +20377,7 @@ function abandonPending() {
19917
20377
  }
19918
20378
  function logActivitySocket(msg) {
19919
20379
  try {
19920
- import_fs33.default.appendFileSync(
20380
+ import_fs34.default.appendFileSync(
19921
20381
  import_path34.default.join(homeDir, ".node9", "hook-debug.log"),
19922
20382
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
19923
20383
  `
@@ -19940,13 +20400,13 @@ function shouldRebind(now = Date.now()) {
19940
20400
  function startActivitySocket() {
19941
20401
  bindActivitySocket();
19942
20402
  activityHealthInterval = setInterval(() => {
19943
- if (!import_fs33.default.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
20403
+ if (!import_fs34.default.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
19944
20404
  }, ACTIVITY_HEALTH_PROBE_MS);
19945
20405
  activityHealthInterval.unref();
19946
20406
  process.on("exit", () => {
19947
20407
  if (activityHealthInterval) clearInterval(activityHealthInterval);
19948
20408
  try {
19949
- import_fs33.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
20409
+ import_fs34.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
19950
20410
  } catch {
19951
20411
  }
19952
20412
  });
@@ -19974,7 +20434,7 @@ function attemptRebind(reason) {
19974
20434
  }
19975
20435
  function bindActivitySocket() {
19976
20436
  try {
19977
- import_fs33.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
20437
+ import_fs34.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
19978
20438
  } catch {
19979
20439
  }
19980
20440
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -20068,14 +20528,14 @@ function bindActivitySocket() {
20068
20528
  });
20069
20529
  activitySocketServer = unixServer;
20070
20530
  }
20071
- var import_net2, import_fs33, import_path34, import_os28, import_crypto11, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, sessionTaintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
20531
+ var import_net2, import_fs34, import_path34, import_os29, import_crypto11, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, sessionTaintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
20072
20532
  var init_state2 = __esm({
20073
20533
  "src/daemon/state.ts"() {
20074
20534
  "use strict";
20075
20535
  import_net2 = __toESM(require("net"));
20076
- import_fs33 = __toESM(require("fs"));
20536
+ import_fs34 = __toESM(require("fs"));
20077
20537
  import_path34 = __toESM(require("path"));
20078
- import_os28 = __toESM(require("os"));
20538
+ import_os29 = __toESM(require("os"));
20079
20539
  import_crypto11 = require("crypto");
20080
20540
  init_daemon();
20081
20541
  init_suggestion_tracker();
@@ -20083,7 +20543,7 @@ var init_state2 = __esm({
20083
20543
  init_session_counters();
20084
20544
  init_session_history();
20085
20545
  init_atomic_write();
20086
- homeDir = import_os28.default.homedir();
20546
+ homeDir = import_os29.default.homedir();
20087
20547
  DAEMON_PID_FILE = import_path34.default.join(homeDir, ".node9", "daemon.pid");
20088
20548
  DECISIONS_FILE = import_path34.default.join(homeDir, ".node9", "decisions.json");
20089
20549
  AUDIT_LOG_FILE = import_path34.default.join(homeDir, ".node9", "audit.log");
@@ -20108,7 +20568,7 @@ var init_state2 = __esm({
20108
20568
  "2h": 2 * 60 * 6e4
20109
20569
  };
20110
20570
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
20111
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path34.default.join(import_os28.default.tmpdir(), "node9-activity.sock");
20571
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path34.default.join(import_os29.default.tmpdir(), "node9-activity.sock");
20112
20572
  ACTIVITY_RING_SIZE = 100;
20113
20573
  activityRing = [];
20114
20574
  LARGE_RESPONSE_RING_SIZE = 20;
@@ -20161,7 +20621,7 @@ function safeRead(file) {
20161
20621
  function candidateFiles(home, cwd) {
20162
20622
  const files = /* @__PURE__ */ new Set();
20163
20623
  try {
20164
- for (const name of import_fs34.default.readdirSync(cwd)) {
20624
+ for (const name of import_fs35.default.readdirSync(cwd)) {
20165
20625
  if (name === ".env" || name.startsWith(".env.")) files.add(import_path35.default.join(cwd, name));
20166
20626
  }
20167
20627
  } catch {
@@ -20191,7 +20651,7 @@ function plantedDecoyPaths() {
20191
20651
  }
20192
20652
  }
20193
20653
  function checkSecrets(ctx) {
20194
- const home = ctx.home || import_os29.default.homedir();
20654
+ const home = ctx.home || import_os30.default.homedir();
20195
20655
  const findings = [];
20196
20656
  const planted = plantedDecoyPaths();
20197
20657
  const plaintext = [];
@@ -20227,7 +20687,7 @@ function checkSecrets(ctx) {
20227
20687
  for (const file of credentialMaterial(home)) {
20228
20688
  if (planted.has(file)) continue;
20229
20689
  try {
20230
- if (import_fs34.default.statSync(file).isFile()) {
20690
+ if (import_fs35.default.statSync(file).isFile()) {
20231
20691
  creds.push(displayPath(file, home));
20232
20692
  credPaths.push(file);
20233
20693
  }
@@ -20261,13 +20721,13 @@ function checkSecrets(ctx) {
20261
20721
  }
20262
20722
  return findings;
20263
20723
  }
20264
- var import_fs34, import_path35, import_os29, MAX_FILE_BYTES;
20724
+ var import_fs35, import_path35, import_os30, MAX_FILE_BYTES;
20265
20725
  var init_secrets = __esm({
20266
20726
  "src/posture/secrets.ts"() {
20267
20727
  "use strict";
20268
- import_fs34 = __toESM(require("fs"));
20728
+ import_fs35 = __toESM(require("fs"));
20269
20729
  import_path35 = __toESM(require("path"));
20270
- import_os29 = __toESM(require("os"));
20730
+ import_os30 = __toESM(require("os"));
20271
20731
  init_dist();
20272
20732
  init_agent_wiring();
20273
20733
  init_registry();
@@ -20401,7 +20861,7 @@ var init_templates = __esm({
20401
20861
  // src/posture/egress.ts
20402
20862
  function sandboxEgressWallActive() {
20403
20863
  try {
20404
- return import_fs35.default.existsSync(ALLOWED_DOMAINS_PATH);
20864
+ return import_fs36.default.existsSync(ALLOWED_DOMAINS_PATH);
20405
20865
  } catch {
20406
20866
  return false;
20407
20867
  }
@@ -20522,11 +20982,11 @@ function checkEgress(ctx) {
20522
20982
  })
20523
20983
  ];
20524
20984
  }
20525
- var import_fs35;
20985
+ var import_fs36;
20526
20986
  var init_egress = __esm({
20527
20987
  "src/posture/egress.ts"() {
20528
20988
  "use strict";
20529
- import_fs35 = __toESM(require("fs"));
20989
+ import_fs36 = __toESM(require("fs"));
20530
20990
  init_config();
20531
20991
  init_templates();
20532
20992
  }
@@ -20586,7 +21046,7 @@ function readServers(file, format, agent) {
20586
21046
  const capped = readCappedText(file, MAX_CONFIG_BYTES);
20587
21047
  if (!capped || capped.truncated) return [];
20588
21048
  const text = capped.text;
20589
- const map = format === "toml" ? (0, import_smol_toml4.parse)(text)?.mcp_servers : JSON.parse(text)?.mcpServers;
21049
+ const map = format === "toml" ? (0, import_smol_toml5.parse)(text)?.mcp_servers : JSON.parse(text)?.mcpServers;
20590
21050
  if (!map || typeof map !== "object") return [];
20591
21051
  return Object.entries(map).map(([name, v]) => ({
20592
21052
  name,
@@ -20599,7 +21059,7 @@ function readServers(file, format, agent) {
20599
21059
  }
20600
21060
  }
20601
21061
  function checkSupplyChain(ctx) {
20602
- const home = ctx.home || import_os30.default.homedir();
21062
+ const home = ctx.home || import_os31.default.homedir();
20603
21063
  const servers = [];
20604
21064
  for (const spec of AGENT_SPECS) {
20605
21065
  if (!spec.mcpFile) continue;
@@ -20639,13 +21099,13 @@ function checkSupplyChain(ctx) {
20639
21099
  }
20640
21100
  return findings;
20641
21101
  }
20642
- var import_os30, import_path36, import_smol_toml4, PACKAGE_RUNNERS, MAX_CONFIG_BYTES;
21102
+ var import_os31, import_path36, import_smol_toml5, PACKAGE_RUNNERS, MAX_CONFIG_BYTES;
20643
21103
  var init_supply_chain = __esm({
20644
21104
  "src/posture/supply-chain.ts"() {
20645
21105
  "use strict";
20646
- import_os30 = __toESM(require("os"));
21106
+ import_os31 = __toESM(require("os"));
20647
21107
  import_path36 = __toESM(require("path"));
20648
- import_smol_toml4 = require("smol-toml");
21108
+ import_smol_toml5 = require("smol-toml");
20649
21109
  init_provenance();
20650
21110
  init_agent_wiring();
20651
21111
  init_read_capped();
@@ -20702,9 +21162,9 @@ var init_privilege = __esm({
20702
21162
 
20703
21163
  // src/posture/containment.ts
20704
21164
  function inContainer() {
20705
- if (import_fs36.default.existsSync("/.dockerenv") || import_fs36.default.existsSync("/run/.containerenv")) return true;
21165
+ if (import_fs37.default.existsSync("/.dockerenv") || import_fs37.default.existsSync("/run/.containerenv")) return true;
20706
21166
  try {
20707
- const cgroup = import_fs36.default.readFileSync("/proc/1/cgroup", "utf8");
21167
+ const cgroup = import_fs37.default.readFileSync("/proc/1/cgroup", "utf8");
20708
21168
  if (/docker|kubepods|containerd|lxc|libpod/.test(cgroup)) return true;
20709
21169
  } catch {
20710
21170
  }
@@ -20741,11 +21201,11 @@ Lighter \u2014 harden in place, keep full host access (about +${Math.round(
20741
21201
  }
20742
21202
  ];
20743
21203
  }
20744
- var import_fs36, ISOLATION_WEIGHT;
21204
+ var import_fs37, ISOLATION_WEIGHT;
20745
21205
  var init_containment = __esm({
20746
21206
  "src/posture/containment.ts"() {
20747
21207
  "use strict";
20748
- import_fs36 = __toESM(require("fs"));
21208
+ import_fs37 = __toESM(require("fs"));
20749
21209
  ISOLATION_WEIGHT = 12;
20750
21210
  }
20751
21211
  });
@@ -20808,7 +21268,7 @@ function collectListeners() {
20808
21268
  const byPort = /* @__PURE__ */ new Map();
20809
21269
  for (const file of ["/proc/net/tcp", "/proc/net/tcp6"]) {
20810
21270
  try {
20811
- for (const l of parseListeners(import_fs37.default.readFileSync(file, "utf8"))) {
21271
+ for (const l of parseListeners(import_fs38.default.readFileSync(file, "utf8"))) {
20812
21272
  if (!byPort.has(l.port)) byPort.set(l.port, l);
20813
21273
  }
20814
21274
  } catch {
@@ -20820,11 +21280,11 @@ function readProc(pid) {
20820
21280
  let comm = "unknown";
20821
21281
  let cmdline = "";
20822
21282
  try {
20823
- comm = import_fs37.default.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
21283
+ comm = import_fs38.default.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
20824
21284
  } catch {
20825
21285
  }
20826
21286
  try {
20827
- cmdline = import_fs37.default.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
21287
+ cmdline = import_fs38.default.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
20828
21288
  } catch {
20829
21289
  }
20830
21290
  return { comm, cmdline };
@@ -20834,21 +21294,21 @@ function resolveProcesses(inodes) {
20834
21294
  if (inodes.size === 0) return map;
20835
21295
  let pids;
20836
21296
  try {
20837
- pids = import_fs37.default.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
21297
+ pids = import_fs38.default.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
20838
21298
  } catch {
20839
21299
  return map;
20840
21300
  }
20841
21301
  for (const pid of pids) {
20842
21302
  let fds;
20843
21303
  try {
20844
- fds = import_fs37.default.readdirSync(`/proc/${pid}/fd`);
21304
+ fds = import_fs38.default.readdirSync(`/proc/${pid}/fd`);
20845
21305
  } catch {
20846
21306
  continue;
20847
21307
  }
20848
21308
  for (const fd of fds) {
20849
21309
  let link;
20850
21310
  try {
20851
- link = import_fs37.default.readlinkSync(`/proc/${pid}/fd/${fd}`);
21311
+ link = import_fs38.default.readlinkSync(`/proc/${pid}/fd/${fd}`);
20852
21312
  } catch {
20853
21313
  continue;
20854
21314
  }
@@ -20916,11 +21376,11 @@ function checkInbound(ctx) {
20916
21376
  }
20917
21377
  return findings;
20918
21378
  }
20919
- var import_fs37, DB_EXPOSURE_WEIGHT, KNOWN_SERVICE_PORTS, KNOWN_SERVICE_COMMS, DB_LABEL, SHIELD_FOR_SERVICE;
21379
+ var import_fs38, DB_EXPOSURE_WEIGHT, KNOWN_SERVICE_PORTS, KNOWN_SERVICE_COMMS, DB_LABEL, SHIELD_FOR_SERVICE;
20920
21380
  var init_inbound = __esm({
20921
21381
  "src/posture/inbound.ts"() {
20922
21382
  "use strict";
20923
- import_fs37 = __toESM(require("fs"));
21383
+ import_fs38 = __toESM(require("fs"));
20924
21384
  DB_EXPOSURE_WEIGHT = 4;
20925
21385
  KNOWN_SERVICE_PORTS = {
20926
21386
  5432: "PostgreSQL",
@@ -20955,7 +21415,7 @@ var init_inbound = __esm({
20955
21415
 
20956
21416
  // src/posture/coverage.ts
20957
21417
  function checkCoverage(ctx) {
20958
- const home = ctx.home || import_os31.default.homedir();
21418
+ const home = ctx.home || import_os32.default.homedir();
20959
21419
  const findings = [];
20960
21420
  const protectedAgents = getAgentWiring(home).filter((r) => r.isProtected);
20961
21421
  if (protectedAgents.length === 0) {
@@ -20988,11 +21448,11 @@ function checkCoverage(ctx) {
20988
21448
  }
20989
21449
  return findings;
20990
21450
  }
20991
- var import_os31;
21451
+ var import_os32;
20992
21452
  var init_coverage = __esm({
20993
21453
  "src/posture/coverage.ts"() {
20994
21454
  "use strict";
20995
- import_os31 = __toESM(require("os"));
21455
+ import_os32 = __toESM(require("os"));
20996
21456
  init_config();
20997
21457
  init_agent_wiring();
20998
21458
  }
@@ -21344,7 +21804,7 @@ async function runChecks(checks, ctx) {
21344
21804
  }
21345
21805
  async function runPosture(opts = {}) {
21346
21806
  const ctx = {
21347
- home: opts.home ?? import_os32.default.homedir(),
21807
+ home: opts.home ?? import_os33.default.homedir(),
21348
21808
  cwd: opts.cwd ?? process.cwd(),
21349
21809
  agent: opts.agent
21350
21810
  };
@@ -21370,11 +21830,11 @@ async function runPosture(opts = {}) {
21370
21830
  checksRun: POSTURE_CHECKS.length
21371
21831
  };
21372
21832
  }
21373
- var import_os32, POSTURE_CHECKS;
21833
+ var import_os33, POSTURE_CHECKS;
21374
21834
  var init_posture = __esm({
21375
21835
  "src/posture/index.ts"() {
21376
21836
  "use strict";
21377
- import_os32 = __toESM(require("os"));
21837
+ import_os33 = __toESM(require("os"));
21378
21838
  init_secrets();
21379
21839
  init_egress();
21380
21840
  init_gate();
@@ -21573,49 +22033,6 @@ var init_build2 = __esm({
21573
22033
  }
21574
22034
  });
21575
22035
 
21576
- // src/mcp-cmd.ts
21577
- function tokenize4(cmd) {
21578
- const tokens = [];
21579
- let current = "";
21580
- let inDouble = false;
21581
- let quoted = false;
21582
- let i = 0;
21583
- while (i < cmd.length) {
21584
- const ch = cmd[i];
21585
- if (inDouble) {
21586
- if (ch === '"') inDouble = false;
21587
- else if (ch === "\\" && i + 1 < cmd.length) current += cmd[++i];
21588
- else current += ch;
21589
- } else if (ch === '"') {
21590
- inDouble = true;
21591
- quoted = true;
21592
- } else if (ch === " " || ch === " ") {
21593
- if (current || quoted) {
21594
- tokens.push(current);
21595
- current = "";
21596
- quoted = false;
21597
- }
21598
- } else if (ch === "\\" && i + 1 < cmd.length) {
21599
- current += cmd[++i];
21600
- } else {
21601
- current += ch;
21602
- }
21603
- i++;
21604
- }
21605
- if (current || quoted && !inDouble) tokens.push(current);
21606
- return tokens;
21607
- }
21608
- function quoteArg(s) {
21609
- if (s === "") return '""';
21610
- if (/[\s"\\]/.test(s)) return `"${s.replace(/(["\\])/g, "\\$1")}"`;
21611
- return s;
21612
- }
21613
- var init_mcp_cmd = __esm({
21614
- "src/mcp-cmd.ts"() {
21615
- "use strict";
21616
- }
21617
- });
21618
-
21619
22036
  // src/daemon/mcp-tools.ts
21620
22037
  function deriveServerName(cmd) {
21621
22038
  if (!cmd || typeof cmd !== "string") return "MCP Server";
@@ -21642,13 +22059,13 @@ function deriveServerName(cmd) {
21642
22059
  return strip(base) || "MCP Server";
21643
22060
  }
21644
22061
  function getMcpToolsFile() {
21645
- return import_path38.default.join(import_os33.default.homedir(), ".node9", "mcp-tools.json");
22062
+ return import_path38.default.join(import_os34.default.homedir(), ".node9", "mcp-tools.json");
21646
22063
  }
21647
22064
  function readMcpToolsConfig() {
21648
22065
  try {
21649
22066
  const file = getMcpToolsFile();
21650
- if (!import_fs38.default.existsSync(file)) return {};
21651
- const raw = import_fs38.default.readFileSync(file, "utf-8");
22067
+ if (!import_fs39.default.existsSync(file)) return {};
22068
+ const raw = import_fs39.default.readFileSync(file, "utf-8");
21652
22069
  return JSON.parse(raw);
21653
22070
  } catch {
21654
22071
  return {};
@@ -21658,10 +22075,10 @@ function writeMcpToolsConfig(config) {
21658
22075
  try {
21659
22076
  const file = getMcpToolsFile();
21660
22077
  const dir = import_path38.default.dirname(file);
21661
- if (!import_fs38.default.existsSync(dir)) import_fs38.default.mkdirSync(dir, { recursive: true });
21662
- const tmpPath = `${file}.${import_os33.default.hostname()}.${process.pid}.tmp`;
21663
- import_fs38.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
21664
- import_fs38.default.renameSync(tmpPath, file);
22078
+ if (!import_fs39.default.existsSync(dir)) import_fs39.default.mkdirSync(dir, { recursive: true });
22079
+ const tmpPath = `${file}.${import_os34.default.hostname()}.${process.pid}.tmp`;
22080
+ import_fs39.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
22081
+ import_fs39.default.renameSync(tmpPath, file);
21665
22082
  } catch (e) {
21666
22083
  console.error("Failed to write mcp-tools.json", e);
21667
22084
  }
@@ -21708,117 +22125,13 @@ function approveServer(serverKey, disabledTools) {
21708
22125
  writeMcpToolsConfig(config);
21709
22126
  }
21710
22127
  }
21711
- var import_fs38, import_path38, import_os33;
22128
+ var import_fs39, import_path38, import_os34;
21712
22129
  var init_mcp_tools = __esm({
21713
22130
  "src/daemon/mcp-tools.ts"() {
21714
- "use strict";
21715
- import_fs38 = __toESM(require("fs"));
21716
- import_path38 = __toESM(require("path"));
21717
- import_os33 = __toESM(require("os"));
21718
- init_mcp_cmd();
21719
- }
21720
- });
21721
-
21722
- // src/mcp-wrap.ts
21723
- function isNode9Command(command) {
21724
- return /(^|[\\/])node9(\.(exe|cmd|ps1|bat))?$/i.test(command ?? "");
21725
- }
21726
- function classifyMcp(s) {
21727
- if (isNode9Command(s.command)) {
21728
- return (s.args ?? [])[0] === "mcp-gateway" ? "gatewayed" : "node9-self";
21729
- }
21730
- if (typeof s.command !== "string" || s.command.trim() === "") return "remote";
21731
- return "ungoverned";
21732
- }
21733
- function toGateway(s, configName) {
21734
- const upstream = [s.command ?? "", ...s.args ?? []].map(quoteArg).join(" ");
21735
- const nameArgs = configName && !configName.startsWith("-") ? ["--config-name", configName] : [];
21736
- return {
21737
- ...s,
21738
- command: "node9",
21739
- args: ["mcp-gateway", ...nameArgs, "--upstream", upstream]
21740
- };
21741
- }
21742
- function fromGateway(s) {
21743
- if (!isNode9Command(s.command) || (s.args ?? [])[0] !== "mcp-gateway") return null;
21744
- const args = s.args ?? [];
21745
- const i = args.indexOf("--upstream");
21746
- if (i < 0 || !args[i + 1]) return null;
21747
- const [command, ...rest] = tokenize4(args[i + 1]);
21748
- if (!command) return null;
21749
- return { ...s, command, args: rest };
21750
- }
21751
- function inventoryMcp(home = import_os34.default.homedir()) {
21752
- const out = [];
21753
- for (const spec of AGENT_SPECS) {
21754
- if (!spec.mcpFile) continue;
21755
- const mcpFile = spec.mcpFile(home);
21756
- const format = spec.mcpFormat ?? "json";
21757
- const servers = readMcpServers(mcpFile, format);
21758
- for (const [name, s] of Object.entries(servers)) {
21759
- if (!s || typeof s !== "object") continue;
21760
- out.push({
21761
- agent: String(spec.id),
21762
- agentLabel: spec.label,
21763
- mcpFile,
21764
- format,
21765
- name,
21766
- command: s.command ?? "",
21767
- args: Array.isArray(s.args) ? s.args : [],
21768
- state: classifyMcp(s),
21769
- raw: s
21770
- });
21771
- }
21772
- }
21773
- return out;
21774
- }
21775
- function inventoryServerKeys(inv) {
21776
- const keys = /* @__PURE__ */ new Set();
21777
- for (const e of inv) {
21778
- if (e.state === "gatewayed") {
21779
- const i = e.args.indexOf("--upstream");
21780
- if (i >= 0 && e.args[i + 1]) {
21781
- keys.add(getServerKey(e.args[i + 1]));
21782
- }
21783
- } else if (e.state === "ungoverned") {
21784
- const cmd = [e.command, ...e.args].map(quoteArg).join(" ");
21785
- keys.add(getServerKey(cmd));
21786
- }
21787
- }
21788
- return keys;
21789
- }
21790
- function writeMcpEntry(mcpFile, format, name, entry) {
21791
- const key = format === "toml" ? "mcp_servers" : "mcpServers";
21792
- let root = {};
21793
- if (import_fs39.default.existsSync(mcpFile)) {
21794
- const raw = import_fs39.default.readFileSync(mcpFile, "utf-8");
21795
- root = format === "toml" ? (0, import_smol_toml5.parse)(raw) : JSON.parse(raw);
21796
- const bak = `${mcpFile}.node9-bak`;
21797
- try {
21798
- import_fs39.default.writeFileSync(bak, raw, { mode: 384, flag: "wx" });
21799
- } catch (e) {
21800
- if (e.code !== "EEXIST") throw e;
21801
- }
21802
- }
21803
- const existing = root[key];
21804
- const servers = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
21805
- servers[name] = entry;
21806
- root[key] = servers;
21807
- const serialized = format === "toml" ? (0, import_smol_toml5.stringify)(root) : JSON.stringify(root, null, 2);
21808
- const tmp = `${mcpFile}.${process.pid}.tmp`;
21809
- import_fs39.default.writeFileSync(tmp, serialized, { mode: 384 });
21810
- import_fs39.default.renameSync(tmp, mcpFile);
21811
- }
21812
- var import_fs39, import_os34, import_smol_toml5;
21813
- var init_mcp_wrap = __esm({
21814
- "src/mcp-wrap.ts"() {
21815
22131
  "use strict";
21816
22132
  import_fs39 = __toESM(require("fs"));
22133
+ import_path38 = __toESM(require("path"));
21817
22134
  import_os34 = __toESM(require("os"));
21818
- import_smol_toml5 = require("smol-toml");
21819
- init_agent_wiring();
21820
- init_mcp_cmd();
21821
- init_mcp_pin();
21822
22135
  init_mcp_cmd();
21823
22136
  }
21824
22137
  });
@@ -52291,22 +52604,22 @@ init_hook_baseline();
52291
52604
  init_agent_wiring();
52292
52605
 
52293
52606
  // src/credentials.ts
52294
- var fs21 = __toESM(require("fs"));
52295
- var os18 = __toESM(require("os"));
52607
+ var fs22 = __toESM(require("fs"));
52608
+ var os19 = __toESM(require("os"));
52296
52609
  var path22 = __toESM(require("path"));
52297
52610
  init_config();
52298
52611
  init_api_url();
52299
52612
  function writeCredentialsAndConfig(apiKey, opts = {}) {
52300
52613
  const profileName = opts.profileName || "default";
52301
- const home = opts.homeDir ?? os18.homedir();
52614
+ const home = opts.homeDir ?? os19.homedir();
52302
52615
  const credPath = path22.join(home, ".node9", "credentials.json");
52303
- if (!fs21.existsSync(path22.dirname(credPath))) {
52304
- fs21.mkdirSync(path22.dirname(credPath), { recursive: true });
52616
+ if (!fs22.existsSync(path22.dirname(credPath))) {
52617
+ fs22.mkdirSync(path22.dirname(credPath), { recursive: true });
52305
52618
  }
52306
52619
  let existingCreds = {};
52307
52620
  try {
52308
- if (fs21.existsSync(credPath)) {
52309
- const raw = JSON.parse(fs21.readFileSync(credPath, "utf-8"));
52621
+ if (fs22.existsSync(credPath)) {
52622
+ const raw = JSON.parse(fs22.readFileSync(credPath, "utf-8"));
52310
52623
  existingCreds = raw.apiKey ? { default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL } } : raw;
52311
52624
  }
52312
52625
  } catch {
@@ -52316,7 +52629,7 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
52316
52629
  apiUrl: DEFAULT_API_URL,
52317
52630
  ...opts.isLocal ? { localOnly: true } : {}
52318
52631
  };
52319
- fs21.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), {
52632
+ fs22.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), {
52320
52633
  mode: 384
52321
52634
  });
52322
52635
  let effectiveCloud = null;
@@ -52324,8 +52637,8 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
52324
52637
  const configPath = path22.join(home, ".node9", "config.json");
52325
52638
  let config = {};
52326
52639
  try {
52327
- if (fs21.existsSync(configPath)) {
52328
- config = JSON.parse(fs21.readFileSync(configPath, "utf-8"));
52640
+ if (fs22.existsSync(configPath)) {
52641
+ config = JSON.parse(fs22.readFileSync(configPath, "utf-8"));
52329
52642
  }
52330
52643
  } catch {
52331
52644
  }
@@ -52340,10 +52653,10 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
52340
52653
  terminal: typeof existing.terminal === "boolean" ? existing.terminal : d.terminal,
52341
52654
  cloud: !opts.isLocal
52342
52655
  };
52343
- if (!fs21.existsSync(path22.dirname(configPath))) {
52344
- fs21.mkdirSync(path22.dirname(configPath), { recursive: true });
52656
+ if (!fs22.existsSync(path22.dirname(configPath))) {
52657
+ fs22.mkdirSync(path22.dirname(configPath), { recursive: true });
52345
52658
  }
52346
- fs21.writeFileSync(configPath, JSON.stringify(config, null, 2), {
52659
+ fs22.writeFileSync(configPath, JSON.stringify(config, null, 2), {
52347
52660
  mode: 384
52348
52661
  });
52349
52662
  effectiveCloud = !opts.isLocal;
@@ -56769,6 +57082,7 @@ function registerInitCommand(program2) {
56769
57082
  var import_chalk21 = __toESM(require("chalk"));
56770
57083
  var import_fs62 = __toESM(require("fs"));
56771
57084
  init_agent_wiring();
57085
+ init_mcp_wrap();
56772
57086
  init_setup();
56773
57087
  init_hook_baseline();
56774
57088
  var hasHookSurface = (a) => a.hooks.length > 0;
@@ -56778,8 +57092,28 @@ function backupForHeal(file) {
56778
57092
  } catch {
56779
57093
  }
56780
57094
  }
57095
+ function reportCorruptedMcpWraps() {
57096
+ const corrupted = findCorruptedMcpWraps();
57097
+ if (corrupted.length === 0) return;
57098
+ console.log(
57099
+ import_chalk21.default.yellow(
57100
+ ` \u26A0\uFE0F ${corrupted.length} MCP server(s) were corrupted by an older node9 and cannot start:`
57101
+ )
57102
+ );
57103
+ for (const c of corrupted) {
57104
+ console.log(import_chalk21.default.yellow(` \u2022 ${c.name} (${c.agentLabel})`));
57105
+ console.log(import_chalk21.default.gray(` ${c.mcpFile}`));
57106
+ console.log(import_chalk21.default.gray(` stored upstream: ${c.upstream}`));
57107
+ }
57108
+ console.log(
57109
+ import_chalk21.default.gray(
57110
+ "\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"
57111
+ )
57112
+ );
57113
+ }
56781
57114
  async function runHeal(name) {
56782
57115
  console.log(import_chalk21.default.cyan.bold("\n\u{1FA79} Node9 Heal\n"));
57116
+ reportCorruptedMcpWraps();
56783
57117
  const baseline = loadHookBaseline();
56784
57118
  const wiring = getAgentWiring();
56785
57119
  let candidates2 = wiring.filter(
@@ -60546,7 +60880,7 @@ var COLOR = {
60546
60880
  medium: import_chalk32.default.yellow,
60547
60881
  advisory: import_chalk32.default.gray
60548
60882
  };
60549
- var ACTION_URL = "https://github.com/marketplace/actions/node9-agent-security-check?ref=cli_scan_repo";
60883
+ var ACTION_URL = "https://github.com/marketplace/actions/node9-agent-security?ref=cli_scan_repo";
60550
60884
  function renderCta(res) {
60551
60885
  const L = [];
60552
60886
  L.push(import_chalk32.default.dim(" " + "\u2500".repeat(63)));