@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.
@@ -741,9 +741,9 @@ function matchesPattern(text, patterns) {
741
741
  const withoutDotSlash = text.replace(/^\.\//, "");
742
742
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
743
743
  }
744
- function getNestedValue(obj, path15) {
744
+ function getNestedValue(obj, path17) {
745
745
  if (!obj || typeof obj !== "object") return null;
746
- const segments2 = path15.split(".");
746
+ const segments2 = path17.split(".");
747
747
  for (const seg of segments2) {
748
748
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
749
749
  }
@@ -945,6 +945,7 @@ function normalizeCommandForPolicyImpl(command) {
945
945
  const source = command.slice(s, e);
946
946
  if (resolved === source) continue;
947
947
  if (resolved === "" || /\s/.test(resolved)) continue;
948
+ if (/^[;&|()<>]+$/.test(resolved)) continue;
948
949
  rewrites.push([s, e, resolved]);
949
950
  const quoteOnly = source.replace(/['"]/g, "");
950
951
  if (quoteOnly !== source) quoteOnlyRewrites.push([s, e, quoteOnly]);
@@ -1138,23 +1139,147 @@ function extractLiteralArgs(callExpr) {
1138
1139
  const args = positionedArgs(words);
1139
1140
  return { name, flags, paths: args.map((a) => a.value), words, args };
1140
1141
  }
1142
+ function payloadKey(payload) {
1143
+ if (!assignmentTable || assignmentTable.size === 0) return payload;
1144
+ const bindings = [];
1145
+ for (const [name, rec] of assignmentTable) {
1146
+ if (rec.value !== null) bindings.push(`${name}=${rec.value}`);
1147
+ }
1148
+ return `${payload}\0${bindings.sort().join("")}`;
1149
+ }
1150
+ function claimPayload(payload) {
1151
+ if (!seenPayloads) return "ok";
1152
+ const key = payloadKey(payload);
1153
+ if (seenPayloads.has(key)) return "seen";
1154
+ if (payloadBudget <= 0) return "exhausted";
1155
+ payloadBudget--;
1156
+ seenPayloads.add(key);
1157
+ return "ok";
1158
+ }
1159
+ function recordTopLevelAssignments(f) {
1160
+ const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
1161
+ for (const stmt of stmts) recordTopLevelStmt(stmt);
1162
+ }
1163
+ function probeBinOp(src) {
1164
+ try {
1165
+ const cmd = syntax.NewParser().Parse(src, "probe")?.Stmts?.[0]?.Cmd;
1166
+ if (cmd && syntax.NodeType(cmd) === "BinaryCmd") return cmd.Op;
1167
+ } catch {
1168
+ }
1169
+ return null;
1170
+ }
1171
+ function recordTopLevelStmt(stmt) {
1172
+ if (!stmt || !stmt.Cmd) return false;
1173
+ const t = syntax.NodeType(stmt.Cmd);
1174
+ if (t === "BinaryCmd") {
1175
+ if (!AND_OR_OPS.has(stmt.Cmd.Op)) return false;
1176
+ if (!recordTopLevelStmt(stmt.Cmd.X)) return false;
1177
+ if (stmt.Cmd.Op === AND_OP) return recordTopLevelStmt(stmt.Cmd.Y);
1178
+ return true;
1179
+ }
1180
+ if (t !== "CallExpr" && t !== "DeclClause") return false;
1181
+ let at = 0;
1182
+ try {
1183
+ at = stmt.Pos().Offset();
1184
+ } catch {
1185
+ at = 0;
1186
+ }
1187
+ recordAssignments(stmt.Cmd, at);
1188
+ if (stmt.Negated) return false;
1189
+ if (t === "DeclClause") return ASSIGNMENT_HEADS.has(stmt.Cmd.Variant?.Value ?? "");
1190
+ return (stmt.Cmd.Args || []).length === 0 && (stmt.Cmd.Assigns || []).length > 0;
1191
+ }
1192
+ function recordAssignments(n, at) {
1193
+ if (!assignmentTable) return;
1194
+ const t = syntax.NodeType(n);
1195
+ let assigns = [];
1196
+ if (t === "CallExpr") {
1197
+ if ((n.Args || []).length > 0) return;
1198
+ assigns = n.Assigns || [];
1199
+ } else if (t === "DeclClause") {
1200
+ if (!ASSIGNMENT_HEADS.has(n.Variant?.Value ?? "")) return;
1201
+ assigns = (n.Args || []).filter((a) => syntax.NodeType(a) === "Assign");
1202
+ } else return;
1203
+ for (const a of assigns) {
1204
+ const name = a?.Name?.Value;
1205
+ if (!name || !a.Value || a.Append) continue;
1206
+ assignmentTable.set(name, { value: resolveWordLiteral(a.Value), at });
1207
+ }
1208
+ }
1209
+ function resolveTrivialSubst(part) {
1210
+ if (syntax.NodeType(part) !== "CmdSubst") return void 0;
1211
+ const stmts = part.Stmts || [];
1212
+ if (stmts.length !== 1) return void 0;
1213
+ const st = stmts[0];
1214
+ if ((st.Redirs || []).length > 0 || st.Negated || st.Background) return void 0;
1215
+ const cmd = st.Cmd;
1216
+ if (!cmd || syntax.NodeType(cmd) !== "CallExpr") return void 0;
1217
+ if ((cmd.Assigns || []).length > 0) return void 0;
1218
+ const words = (cmd.Args || []).map((a) => resolveWordLiteral(a));
1219
+ if (words.length === 0 || words.some((w) => w === null)) return void 0;
1220
+ const head = baseWord(words[0]);
1221
+ const rest = words.slice(1);
1222
+ if (head === "echo") {
1223
+ let i = 0;
1224
+ while (i < rest.length && /^-[neE]+$/.test(rest[i])) i++;
1225
+ return rest.slice(i).join(" ");
1226
+ }
1227
+ if (head === "printf") {
1228
+ if (rest.length !== 2) return void 0;
1229
+ if (!/^%s(\\n)?$/.test(rest[0])) return void 0;
1230
+ return rest[1];
1231
+ }
1232
+ return void 0;
1233
+ }
1234
+ function recordedExpansion(name) {
1235
+ if (!assignmentTable || !name) return void 0;
1236
+ const rec = assignmentTable.get(name);
1237
+ if (rec === void 0 || rec.at >= currentStmtOffset) return void 0;
1238
+ return rec.value;
1239
+ }
1240
+ function isPlainParam(p) {
1241
+ if (syntax.NodeType(p) !== "ParamExp") return false;
1242
+ return !(p.Excl || p.Length || p.Width || p.Index || p.Slice || p.Repl || p.Exp);
1243
+ }
1244
+ function expandPlainParam(p) {
1245
+ if (!assignmentTable) return void 0;
1246
+ if (!isPlainParam(p)) return void 0;
1247
+ const recorded = recordedExpansion(p.Param?.Value);
1248
+ if (recorded !== void 0) return recorded;
1249
+ return HOME_VARIABLES.has(p.Param?.Value) ? "~" : void 0;
1250
+ }
1141
1251
  function resolveWordLiteral(w) {
1142
1252
  const parts = w?.Parts || [];
1143
1253
  let s = "";
1144
1254
  for (const p of parts) {
1145
- const t = syntax.NodeType(p);
1146
- if (t === "Lit") s += (p.Value ?? "").replace(/\\(.)/g, "$1");
1147
- else if (t === "SglQuoted") s += p.Value ?? "";
1148
- else if (t === "DblQuoted") {
1149
- const inner = p.Parts || [];
1150
- if (!inner.every((ip) => syntax.NodeType(ip) === "Lit")) return null;
1151
- s += inner.map((ip) => ip.Value ?? "").join("");
1152
- } else {
1153
- return null;
1154
- }
1255
+ const piece = resolvePart(p, false);
1256
+ if (piece === void 0 || piece === null) return null;
1257
+ s += piece;
1155
1258
  }
1156
1259
  return s;
1157
1260
  }
1261
+ function resolvePart(p, inQuotes) {
1262
+ const t = syntax.NodeType(p);
1263
+ if (t === "Lit") {
1264
+ const raw = p.Value ?? "";
1265
+ if (!inQuotes) return raw.replace(/\\(.)/g, "$1");
1266
+ return assignmentTable ? raw.replace(/\\([$`"\\])/g, "$1") : raw;
1267
+ }
1268
+ if (t === "SglQuoted") return p.Value ?? "";
1269
+ if (t === "ParamExp") return expandPlainParam(p);
1270
+ if (t === "CmdSubst") return assignmentTable ? resolveTrivialSubst(p) : void 0;
1271
+ if (t === "DblQuoted" && !inQuotes) {
1272
+ const inner = p.Parts || [];
1273
+ let out = "";
1274
+ for (const ip of inner) {
1275
+ const piece = resolvePart(ip, true);
1276
+ if (piece === void 0 || piece === null) return piece;
1277
+ out += piece;
1278
+ }
1279
+ return out;
1280
+ }
1281
+ return void 0;
1282
+ }
1158
1283
  function analyzeFsOperation(command) {
1159
1284
  const normalized = normalizeCommandForPolicy(command);
1160
1285
  if (!FS_OP_PRESCREEN_RE.test(normalized)) return null;
@@ -1190,17 +1315,35 @@ function analyzeFsOperationImpl(command, depth = 0) {
1190
1315
  const f = parseShared(command);
1191
1316
  if (f === PARSE_FAIL) return null;
1192
1317
  let result = null;
1318
+ const outerTable = assignmentTable;
1319
+ const outerOffset = currentStmtOffset;
1320
+ const outerSeen = seenPayloads;
1321
+ const outerBudget = payloadBudget;
1322
+ if (depth === 0) {
1323
+ seenPayloads = /* @__PURE__ */ new Set();
1324
+ payloadBudget = PAYLOAD_BUDGET;
1325
+ }
1326
+ assignmentTable = new Map(
1327
+ [...outerTable ?? []].map(([k, r]) => [k, { value: r.value, at: -1 }])
1328
+ );
1329
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
1193
1330
  try {
1331
+ recordTopLevelAssignments(f);
1194
1332
  syntax.Walk(f, (node) => {
1195
1333
  if (!node || result?.verdict === "block") return false;
1196
1334
  const n = node;
1197
1335
  const nodeType = syntax.NodeType(n);
1198
1336
  if (nodeType === "Stmt") {
1337
+ try {
1338
+ currentStmtOffset = n.Pos().Offset();
1339
+ } catch {
1340
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
1341
+ }
1199
1342
  result = stricter(result, jailedRedirectRead(n));
1200
1343
  return result?.verdict !== "block";
1201
1344
  }
1202
1345
  if (nodeType !== "CallExpr") return true;
1203
- const { name, flags, paths, words, args } = extractLiteralArgs(n);
1346
+ const { name, flags, paths, words } = extractLiteralArgs(n);
1204
1347
  if (!name) return true;
1205
1348
  if (name === "rm") {
1206
1349
  const flagStr = flags.join("").toLowerCase();
@@ -1229,9 +1372,30 @@ function analyzeFsOperationImpl(command, depth = 0) {
1229
1372
  }
1230
1373
  }
1231
1374
  }
1232
- if (depth < 1) {
1375
+ if (depth < 24 && name === "find") {
1376
+ for (const action of findActions(words, 0)) {
1377
+ const h = unwrapCommandHead(action);
1378
+ const inner = literalShellPayload(action.slice(h), baseWord(action[h]));
1379
+ if (inner === null) continue;
1380
+ const claim = claimPayload(inner);
1381
+ if (claim === "exhausted") {
1382
+ result = stricter(result, UNANALYSABLE_NESTING);
1383
+ continue;
1384
+ }
1385
+ if (claim === "seen") continue;
1386
+ const v = analyzeFsOperationImpl(inner, depth + 1);
1387
+ result = stricter(result, v);
1388
+ if (result?.verdict === "block") return false;
1389
+ }
1390
+ }
1391
+ if (depth < 24) {
1233
1392
  const payload = literalShellPayload(words, name);
1234
- if (payload !== null) {
1393
+ const claim = payload === null ? "seen" : claimPayload(payload);
1394
+ if (claim === "exhausted") {
1395
+ result = stricter(result, UNANALYSABLE_NESTING);
1396
+ return true;
1397
+ }
1398
+ if (payload !== null && claim === "ok") {
1235
1399
  const inner = analyzeFsOperationImpl(payload, depth + 1);
1236
1400
  if (inner) {
1237
1401
  result = inner;
@@ -1240,7 +1404,7 @@ function analyzeFsOperationImpl(command, depth = 0) {
1240
1404
  return true;
1241
1405
  }
1242
1406
  }
1243
- const readPaths = FS_READ_TOOLS.has(name) ? [...readTargets(name, args, flags, words, 1), ...flagOperandFiles(name, words, 1)] : wrappedReadPaths(words, name);
1407
+ const readPaths = FS_READ_TOOLS.has(name) ? readerPaths(words, 0) : wrappedReadPaths(words, name);
1244
1408
  if (readPaths) {
1245
1409
  for (const p of readPaths) {
1246
1410
  result = stricter(result, matchSensitivePath2(p));
@@ -1255,6 +1419,11 @@ function analyzeFsOperationImpl(command, depth = 0) {
1255
1419
  return result;
1256
1420
  } catch {
1257
1421
  return null;
1422
+ } finally {
1423
+ assignmentTable = outerTable;
1424
+ currentStmtOffset = outerOffset;
1425
+ seenPayloads = outerSeen;
1426
+ if (depth === 0) payloadBudget = outerBudget;
1258
1427
  }
1259
1428
  }
1260
1429
  function stricter(a, b) {
@@ -1311,6 +1480,18 @@ function resolveCopyShape(words, h) {
1311
1480
  }
1312
1481
  return null;
1313
1482
  }
1483
+ function findAction(words, k) {
1484
+ const end = words.findIndex((w, i) => i > k && (w === ";" || w === "+"));
1485
+ return words.slice(k + 1, end < 0 ? words.length : end);
1486
+ }
1487
+ function findActions(words, h) {
1488
+ const out = [];
1489
+ for (let i = h + 1; i < words.length; i++) {
1490
+ const w = words[i];
1491
+ if (w !== null && FIND_EXEC_FLAGS.has(w)) out.push(findAction(words, i));
1492
+ }
1493
+ return out;
1494
+ }
1314
1495
  function findStartPoints(words, h) {
1315
1496
  const k = words.findIndex((w, i) => i > h && w !== null && FIND_EXEC_FLAGS.has(w));
1316
1497
  if (k < 0) return { k, starts: [] };
@@ -1326,8 +1507,13 @@ function copySourcePaths(words) {
1326
1507
  if (fi >= 0) {
1327
1508
  const { k, starts } = findStartPoints(words, fi);
1328
1509
  if (k < 0) return [];
1329
- const action = unwrapCommandHead(words.slice(k + 1));
1330
- return resolveCopyShape(words.slice(k + 1), action) ? starts : [];
1510
+ const out = [];
1511
+ for (const action of findActions(words, fi)) {
1512
+ const h2 = unwrapCommandHead(action);
1513
+ if (!resolveCopyShape(action, h2)) continue;
1514
+ out.push(...starts, ...copySourcePaths(action));
1515
+ }
1516
+ return out;
1331
1517
  }
1332
1518
  if (!COPY_VERB_HEADS.has(baseWord(words[h]))) return [];
1333
1519
  const r = resolveCopyShape(words, h);
@@ -1504,6 +1690,15 @@ function flagEffect(token, shape, known) {
1504
1690
  }
1505
1691
  return NONE;
1506
1692
  }
1693
+ function readerPaths(words, h) {
1694
+ const head = baseWord(words[h]);
1695
+ const from = h + 1;
1696
+ const flags = words.slice(from).filter((w) => w !== null && w.startsWith("-"));
1697
+ return [
1698
+ ...readTargets(head, positionedArgs(words, from), flags, words, from),
1699
+ ...flagOperandFiles(head, words, from)
1700
+ ];
1701
+ }
1507
1702
  function readTargets(verb, args, flags, words = [], from = 1) {
1508
1703
  const shape = PATTERN_VERBS[verb];
1509
1704
  if (!shape) return args.map((a) => a.value);
@@ -1585,18 +1780,21 @@ function flagOperandFiles(verb, words, from) {
1585
1780
  function wrappedReadPaths(words, name) {
1586
1781
  if (name === "find") {
1587
1782
  const { k, starts } = findStartPoints(words, 0);
1588
- return k > 0 && isReaderWord(words[k + 1] ?? null) ? starts : null;
1783
+ if (k < 0) return null;
1784
+ const paths = [];
1785
+ let reads = false;
1786
+ for (const action of findActions(words, 0)) {
1787
+ const h2 = unwrapCommandHead(action);
1788
+ if (!isReaderWord(action[h2] ?? null)) continue;
1789
+ reads = true;
1790
+ paths.push(...readerPaths(action, h2));
1791
+ }
1792
+ return reads ? [...starts, ...paths] : paths;
1589
1793
  }
1590
1794
  if (!COMMAND_WRAPPERS.has(name) && !RUNNER_WRAPPERS.has(name)) return null;
1591
1795
  const h = unwrapCommandHead(words);
1592
1796
  if (h <= 0 || !isReaderWord(words[h] ?? null)) return null;
1593
- const head = baseWord(words[h]);
1594
- const rest = words.slice(h + 1);
1595
- const restFlags = rest.filter((w) => w !== null && w.startsWith("-"));
1596
- return [
1597
- ...readTargets(head, positionedArgs(words, h + 1), restFlags, words, h + 1),
1598
- ...flagOperandFiles(head, words, h + 1)
1599
- ];
1797
+ return readerPaths(words, h);
1600
1798
  }
1601
1799
  function literalShellPayload(words, name) {
1602
1800
  const h = COMMAND_WRAPPERS.has(name) || RUNNER_WRAPPERS.has(name) ? unwrapCommandHead(words) : 0;
@@ -1944,7 +2142,7 @@ function matchCanaryArgs(args, values) {
1944
2142
  return null;
1945
2143
  }
1946
2144
  }
1947
- var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, 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, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, SOURCE_COMMANDS, TERMINAL_ESCAPE_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, 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;
2145
+ var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, 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, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_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, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, SOURCE_COMMANDS, TERMINAL_ESCAPE_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, 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;
1948
2146
  var init_dist = __esm({
1949
2147
  "packages/policy-engine/dist/index.mjs"() {
1950
2148
  "use strict";
@@ -3154,6 +3352,22 @@ var init_dist = __esm({
3154
3352
  ]);
3155
3353
  WRAPPER_TAKES_TARGET = /* @__PURE__ */ new Set(["chroot"]);
3156
3354
  FIND_EXEC_FLAGS = /* @__PURE__ */ new Set(["-exec", "-execdir", "-ok", "-okdir"]);
3355
+ HOME_VARIABLES = /* @__PURE__ */ new Set(["HOME", "USERPROFILE"]);
3356
+ assignmentTable = null;
3357
+ currentStmtOffset = Number.MAX_SAFE_INTEGER;
3358
+ PAYLOAD_BUDGET = 256;
3359
+ payloadBudget = 0;
3360
+ seenPayloads = null;
3361
+ UNANALYSABLE_NESTING = {
3362
+ ruleName: "review-unanalysable-nesting",
3363
+ verdict: "review",
3364
+ reason: "This command nests more wrapped shell payloads than the policy engine will unwrap, so some of what it runs was not read.",
3365
+ path: ""
3366
+ };
3367
+ ASSIGNMENT_HEADS = /* @__PURE__ */ new Set(["export", "declare", "local", "readonly", "typeset"]);
3368
+ AND_OP = probeBinOp("a && b");
3369
+ OR_OP = probeBinOp("a || b");
3370
+ AND_OR_OPS = new Set([AND_OP, OR_OP].filter((o) => o !== null));
3157
3371
  FS_OP_CACHE_MAX = 5e3;
3158
3372
  fsOpCache = /* @__PURE__ */ new Map();
3159
3373
  REDIR_TRUNCATE_OPS = /* @__PURE__ */ new Set([deriveRedirOp(">_f")]);
@@ -4170,8 +4384,8 @@ var init_daemon = __esm({
4170
4384
  import { z } from "zod";
4171
4385
  function formatIssues(issues) {
4172
4386
  const lines = issues.map((issue) => {
4173
- const path15 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
4174
- return ` \u2022 ${path15}: ${issue.message}`;
4387
+ const path17 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
4388
+ return ` \u2022 ${path17}: ${issue.message}`;
4175
4389
  });
4176
4390
  return `Invalid config:
4177
4391
  ${lines.join("\n")}`;
@@ -4184,9 +4398,9 @@ function prunePaths(root, paths) {
4184
4398
  const yi = y[y.length - 1];
4185
4399
  return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
4186
4400
  });
4187
- for (const path15 of ordered) {
4401
+ for (const path17 of ordered) {
4188
4402
  let cur = root;
4189
- for (const key of path15.slice(0, -1)) {
4403
+ for (const key of path17.slice(0, -1)) {
4190
4404
  if (cur === null || typeof cur !== "object") {
4191
4405
  cur = void 0;
4192
4406
  break;
@@ -4194,7 +4408,7 @@ function prunePaths(root, paths) {
4194
4408
  cur = cur[key];
4195
4409
  }
4196
4410
  if (cur === null || typeof cur !== "object") continue;
4197
- const last = path15[path15.length - 1];
4411
+ const last = path17[path17.length - 1];
4198
4412
  if (Array.isArray(cur)) {
4199
4413
  const i = Number(last);
4200
4414
  if (Number.isInteger(i) && i >= 0 && i < cur.length) {
@@ -4230,7 +4444,7 @@ function sanitizeConfig(raw) {
4230
4444
  return issue.keys.map((k) => [...at, k]);
4231
4445
  }
4232
4446
  return [at.slice(0, -level || void 0)];
4233
- }).filter((path15) => path15.length > 0);
4447
+ }).filter((path17) => path17.length > 0);
4234
4448
  if (paths.length === 0 || !prunePaths(working, paths)) break;
4235
4449
  }
4236
4450
  if (ConfigFileSchema.safeParse(working).success) break;
@@ -5233,13 +5447,13 @@ function getConfig(cwd) {
5233
5447
  }
5234
5448
  if (Array.isArray(mc.jailPaths)) {
5235
5449
  for (const jp of mc.jailPaths) {
5236
- const path15 = typeof jp?.path === "string" ? jp.path.trim() : "";
5237
- if (!path15) continue;
5450
+ const path17 = typeof jp?.path === "string" ? jp.path.trim() : "";
5451
+ if (!path17) continue;
5238
5452
  const verdict = jp?.verdict === "review" ? "review" : "block";
5239
- for (const r of pathRules(path15, verdict, "org-managed jail")) {
5453
+ for (const r of pathRules(path17, verdict, "org-managed jail")) {
5240
5454
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
5241
5455
  }
5242
- mergedPolicy.managedJailPaths.push({ path: path15, verdict });
5456
+ mergedPolicy.managedJailPaths.push({ path: path17, verdict });
5243
5457
  }
5244
5458
  }
5245
5459
  if (Array.isArray(mc.trustedHosts)) {
@@ -6693,19 +6907,129 @@ var init_scan_summary = __esm({
6693
6907
  }
6694
6908
  });
6695
6909
 
6696
- // src/utils/platform-shell.ts
6697
- var init_platform_shell = __esm({
6698
- "src/utils/platform-shell.ts"() {
6910
+ // src/agent-wiring.ts
6911
+ import fs10 from "fs";
6912
+ import path12 from "path";
6913
+ import * as yaml from "yaml";
6914
+ import { parse as parseToml } from "smol-toml";
6915
+ var exists, ck, lg, AGENT_SPECS;
6916
+ var init_agent_wiring = __esm({
6917
+ "src/agent-wiring.ts"() {
6699
6918
  "use strict";
6919
+ init_setup();
6920
+ exists = (p) => {
6921
+ try {
6922
+ return fs10.existsSync(p);
6923
+ } catch {
6924
+ return false;
6925
+ }
6926
+ };
6927
+ ck = (key) => ({ key, kind: "check" });
6928
+ lg = (key) => ({ key, kind: "log" });
6929
+ AGENT_SPECS = [
6930
+ {
6931
+ id: "claude",
6932
+ label: "Claude Code",
6933
+ setupCommand: "node9 agents add claude",
6934
+ hookFile: (h) => path12.join(h, ".claude", "settings.json"),
6935
+ hookFormat: "matcher",
6936
+ // UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
6937
+ // that shipped; the spec must name it too, or status/doctor never show the
6938
+ // row and heal (which repairs via setupAgent) has no signal it is missing.
6939
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
6940
+ mcpFile: (h) => path12.join(h, ".claude.json"),
6941
+ present: (h) => exists(path12.join(h, ".claude", "settings.json")) || exists(path12.join(h, ".claude.json"))
6942
+ },
6943
+ {
6944
+ id: "gemini",
6945
+ label: "Gemini CLI",
6946
+ setupCommand: "node9 agents add gemini",
6947
+ hookFile: (h) => path12.join(h, ".gemini", "settings.json"),
6948
+ hookFormat: "matcher",
6949
+ hookEvents: [ck("BeforeTool"), lg("AfterTool")],
6950
+ mcpFile: (h) => path12.join(h, ".gemini", "settings.json"),
6951
+ present: (h) => exists(path12.join(h, ".gemini", "settings.json"))
6952
+ },
6953
+ {
6954
+ id: "codex",
6955
+ label: "Codex",
6956
+ setupCommand: "node9 agents add codex",
6957
+ hookFile: (h) => path12.join(h, ".codex", "hooks.json"),
6958
+ hookFormat: "matcher",
6959
+ hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
6960
+ mcpFile: (h) => path12.join(h, ".codex", "config.toml"),
6961
+ mcpFormat: "toml",
6962
+ present: (h) => exists(path12.join(h, ".codex"))
6963
+ },
6964
+ {
6965
+ id: "antigravity",
6966
+ label: "Antigravity",
6967
+ setupCommand: "node9 agents add antigravity",
6968
+ hookFile: (h) => path12.join(h, ".gemini", "config", "hooks.json"),
6969
+ hookFormat: "matcher",
6970
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
6971
+ mcpFile: (h) => path12.join(h, ".gemini", "config", "mcp_config.json"),
6972
+ present: (h) => exists(path12.join(h, ".gemini", "config", "hooks.json")) || exists(path12.join(h, ".gemini", "antigravity-cli")) || exists(path12.join(h, ".gemini", "antigravity-ide"))
6973
+ },
6974
+ {
6975
+ id: "copilot",
6976
+ label: "GitHub Copilot",
6977
+ setupCommand: "node9 agents add copilot",
6978
+ hookFile: (h) => path12.join(h, ".copilot", "hooks", "node9.json"),
6979
+ hookFormat: "flat",
6980
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
6981
+ mcpFile: (h) => path12.join(h, ".copilot", "mcp-config.json"),
6982
+ present: (h) => exists(path12.join(h, ".copilot"))
6983
+ },
6984
+ {
6985
+ id: "cursor",
6986
+ label: "Cursor",
6987
+ setupCommand: "node9 agents add cursor",
6988
+ // MCP-only — no hook file (see note above).
6989
+ hookFormat: "flat",
6990
+ hookEvents: [],
6991
+ mcpFile: (h) => path12.join(h, ".cursor", "mcp.json"),
6992
+ present: (h) => exists(path12.join(h, ".cursor", "mcp.json"))
6993
+ },
6994
+ {
6995
+ id: "hermes",
6996
+ label: "Hermes Agent",
6997
+ setupCommand: "node9 agents add hermes",
6998
+ hookFile: (h) => hermesConfigPath(h),
6999
+ hookFormat: "yaml",
7000
+ hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
7001
+ labelPad: 14,
7002
+ // 'post_tool_call' is wider than the default
7003
+ present: (h) => exists(hermesConfigPath(h))
7004
+ },
7005
+ {
7006
+ // Plugin-shim agents — protected by a node9-authored plugin/extension file
7007
+ // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
7008
+ id: "opencode",
7009
+ label: "OpenCode",
7010
+ setupCommand: "node9 agents add opencode",
7011
+ hookFormat: "flat",
7012
+ hookEvents: [],
7013
+ shimFile: (h) => path12.join(opencodeConfigDir(h), "plugins", "node9.js"),
7014
+ present: (h) => exists(opencodeConfigDir(h)) || exists(path12.join(opencodeConfigDir(h), "plugins", "node9.js"))
7015
+ },
7016
+ {
7017
+ id: "pi",
7018
+ label: "Pi",
7019
+ setupCommand: "node9 agents add pi",
7020
+ hookFormat: "flat",
7021
+ hookEvents: [],
7022
+ shimFile: (h) => path12.join(h, ".pi", "agent", "extensions", "node9.js"),
7023
+ present: (h) => exists(path12.join(h, ".pi", "agent")) || exists(path12.join(h, ".pi", "agent", "extensions", "node9.js"))
7024
+ }
7025
+ ];
6700
7026
  }
6701
7027
  });
6702
7028
 
6703
- // src/codex-trust.ts
6704
- import { parse as parseToml } from "smol-toml";
6705
- var init_codex_trust = __esm({
6706
- "src/codex-trust.ts"() {
7029
+ // src/mcp-cmd.ts
7030
+ var init_mcp_cmd = __esm({
7031
+ "src/mcp-cmd.ts"() {
6707
7032
  "use strict";
6708
- init_platform_shell();
6709
7033
  }
6710
7034
  });
6711
7035
 
@@ -6716,15 +7040,43 @@ var init_mcp_pin = __esm({
6716
7040
  }
6717
7041
  });
6718
7042
 
7043
+ // src/mcp-wrap.ts
7044
+ import { parse as parseToml2, stringify as stringifyToml } from "smol-toml";
7045
+ var init_mcp_wrap = __esm({
7046
+ "src/mcp-wrap.ts"() {
7047
+ "use strict";
7048
+ init_agent_wiring();
7049
+ init_mcp_cmd();
7050
+ init_mcp_pin();
7051
+ init_mcp_cmd();
7052
+ }
7053
+ });
7054
+
7055
+ // src/utils/platform-shell.ts
7056
+ var init_platform_shell = __esm({
7057
+ "src/utils/platform-shell.ts"() {
7058
+ "use strict";
7059
+ }
7060
+ });
7061
+
7062
+ // src/codex-trust.ts
7063
+ import { parse as parseToml3 } from "smol-toml";
7064
+ var init_codex_trust = __esm({
7065
+ "src/codex-trust.ts"() {
7066
+ "use strict";
7067
+ init_platform_shell();
7068
+ }
7069
+ });
7070
+
6719
7071
  // src/daemon/hook-baseline.ts
6720
- import path12 from "path";
7072
+ import path13 from "path";
6721
7073
  import os11 from "os";
6722
7074
  var BASELINE_FILE, NOTIFIED_FILE;
6723
7075
  var init_hook_baseline = __esm({
6724
7076
  "src/daemon/hook-baseline.ts"() {
6725
7077
  "use strict";
6726
- BASELINE_FILE = path12.join(os11.homedir(), ".node9", "hooks-baseline.json");
6727
- NOTIFIED_FILE = path12.join(os11.homedir(), ".node9", "hook-heal-notified.json");
7078
+ BASELINE_FILE = path13.join(os11.homedir(), ".node9", "hooks-baseline.json");
7079
+ NOTIFIED_FILE = path13.join(os11.homedir(), ".node9", "hook-heal-notified.json");
6728
7080
  }
6729
7081
  });
6730
7082
 
@@ -6750,19 +7102,37 @@ var init_atomic_write = __esm({
6750
7102
  });
6751
7103
 
6752
7104
  // src/setup.ts
7105
+ import path14 from "path";
7106
+ import os12 from "os";
6753
7107
  import chalk3 from "chalk";
6754
7108
  import { confirm as rawConfirm } from "@inquirer/prompts";
6755
- import { parse as parseToml2, stringify as stringifyToml } from "smol-toml";
6756
- import * as yaml from "yaml";
7109
+ import { parse as parseToml4, stringify as stringifyToml2 } from "smol-toml";
7110
+ import * as yaml2 from "yaml";
7111
+ function opencodeConfigDir(home = os12.homedir()) {
7112
+ const xdg = process.env.XDG_CONFIG_HOME;
7113
+ const base = xdg && path14.isAbsolute(xdg) ? xdg : path14.join(home, ".config");
7114
+ return path14.join(base, "opencode");
7115
+ }
7116
+ function hermesHomeDir(homeDir = os12.homedir()) {
7117
+ const env = process.env.HERMES_HOME?.trim();
7118
+ if (env && path14.isAbsolute(env)) return env;
7119
+ return path14.join(homeDir, ".hermes");
7120
+ }
7121
+ function hermesConfigPath(homeDir = os12.homedir()) {
7122
+ return path14.join(hermesHomeDir(homeDir), HERMES_CONFIG_FILENAME);
7123
+ }
7124
+ var HERMES_CONFIG_FILENAME;
6757
7125
  var init_setup = __esm({
6758
7126
  "src/setup.ts"() {
6759
7127
  "use strict";
7128
+ init_mcp_wrap();
6760
7129
  init_codex_trust();
6761
7130
  init_mcp_pin();
6762
7131
  init_hook_baseline();
6763
7132
  init_setup_opencode_shim();
6764
7133
  init_setup_pi_shim();
6765
7134
  init_atomic_write();
7135
+ HERMES_CONFIG_FILENAME = "config.yaml";
6766
7136
  }
6767
7137
  });
6768
7138
 
@@ -6793,9 +7163,9 @@ var init_scan_history = __esm({
6793
7163
 
6794
7164
  // src/cli/commands/scan.ts
6795
7165
  import chalk5 from "chalk";
6796
- import fs10 from "fs";
6797
- import path13 from "path";
6798
- import os12 from "os";
7166
+ import fs11 from "fs";
7167
+ import path15 from "path";
7168
+ import os13 from "os";
6799
7169
  import stringWidth2 from "string-width";
6800
7170
  function claudeModelPrice2(model) {
6801
7171
  const t = pricingFor(model);
@@ -7003,7 +7373,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
7003
7373
  const session = { sessionId, costUSD: 0, toolCalls: 0 };
7004
7374
  let raw;
7005
7375
  try {
7006
- raw = fs10.readFileSync(path13.join(projPath, file), "utf-8");
7376
+ raw = fs11.readFileSync(path15.join(projPath, file), "utf-8");
7007
7377
  } catch {
7008
7378
  return;
7009
7379
  }
@@ -7066,7 +7436,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
7066
7436
  if (block.type !== "tool_result") continue;
7067
7437
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
7068
7438
  if (filePath) {
7069
- const ext = path13.extname(filePath).toLowerCase();
7439
+ const ext = path15.extname(filePath).toLowerCase();
7070
7440
  if (CODE_EXTENSIONS.has(ext)) continue;
7071
7441
  }
7072
7442
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -7136,7 +7506,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
7136
7506
  const rawCmd = String(input.command ?? "").trimStart();
7137
7507
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
7138
7508
  const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
7139
- const inputFileExt = inputFilePath ? path13.extname(inputFilePath).toLowerCase() : "";
7509
+ const inputFileExt = inputFilePath ? path15.extname(inputFilePath).toLowerCase() : "";
7140
7510
  const canaryInInput = recordCanaries(
7141
7511
  input,
7142
7512
  toolName,
@@ -7245,13 +7615,13 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
7245
7615
  result.perSession.push(session);
7246
7616
  }
7247
7617
  async function processClaudeProjectAsync(proj, projectsDir, ruleSources, startDate, result, dedup, canaryVals, onProgress, onLine) {
7248
- const projPath = path13.join(projectsDir, proj);
7618
+ const projPath = path15.join(projectsDir, proj);
7249
7619
  try {
7250
- if (!fs10.statSync(projPath).isDirectory()) return;
7620
+ if (!fs11.statSync(projPath).isDirectory()) return;
7251
7621
  } catch {
7252
7622
  return;
7253
7623
  }
7254
- const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os12.homedir(), "~")).slice(
7624
+ const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os13.homedir(), "~")).slice(
7255
7625
  0,
7256
7626
  40
7257
7627
  );
@@ -7298,12 +7668,12 @@ function emptyClaudeScan() {
7298
7668
  };
7299
7669
  }
7300
7670
  async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
7301
- const projectsDir = path13.join(os12.homedir(), ".claude", "projects");
7671
+ const projectsDir = path15.join(os13.homedir(), ".claude", "projects");
7302
7672
  const result = emptyClaudeScan();
7303
- if (!fs10.existsSync(projectsDir)) return result;
7673
+ if (!fs11.existsSync(projectsDir)) return result;
7304
7674
  let projDirs;
7305
7675
  try {
7306
- projDirs = fs10.readdirSync(projectsDir);
7676
+ projDirs = fs11.readdirSync(projectsDir);
7307
7677
  } catch {
7308
7678
  return result;
7309
7679
  }
@@ -7327,7 +7697,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
7327
7697
  }
7328
7698
  function scanGeminiHistory(startDate, onProgress, onLine) {
7329
7699
  const canaryVals = safeCanaryScanValues();
7330
- const tmpDir = path13.join(os12.homedir(), ".gemini", "tmp");
7700
+ const tmpDir = path15.join(os13.homedir(), ".gemini", "tmp");
7331
7701
  const result = {
7332
7702
  filesScanned: 0,
7333
7703
  sessions: 0,
@@ -7344,33 +7714,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
7344
7714
  perSession: []
7345
7715
  };
7346
7716
  const dedup = emptyScanDedup();
7347
- if (!fs10.existsSync(tmpDir)) return result;
7717
+ if (!fs11.existsSync(tmpDir)) return result;
7348
7718
  let slugDirs;
7349
7719
  try {
7350
- slugDirs = fs10.readdirSync(tmpDir);
7720
+ slugDirs = fs11.readdirSync(tmpDir);
7351
7721
  } catch {
7352
7722
  return result;
7353
7723
  }
7354
7724
  const ruleSources = buildRuleSources();
7355
7725
  for (const slug2 of slugDirs) {
7356
- const slugPath = path13.join(tmpDir, slug2);
7726
+ const slugPath = path15.join(tmpDir, slug2);
7357
7727
  try {
7358
- if (!fs10.statSync(slugPath).isDirectory()) continue;
7728
+ if (!fs11.statSync(slugPath).isDirectory()) continue;
7359
7729
  } catch {
7360
7730
  continue;
7361
7731
  }
7362
7732
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
7363
7733
  try {
7364
7734
  projLabel = stripTerminalEscapes(
7365
- fs10.readFileSync(path13.join(slugPath, ".project_root"), "utf-8").trim()
7366
- ).replace(os12.homedir(), "~").slice(0, 40);
7735
+ fs11.readFileSync(path15.join(slugPath, ".project_root"), "utf-8").trim()
7736
+ ).replace(os13.homedir(), "~").slice(0, 40);
7367
7737
  } catch {
7368
7738
  }
7369
- const chatsDir = path13.join(slugPath, "chats");
7370
- if (!fs10.existsSync(chatsDir)) continue;
7739
+ const chatsDir = path15.join(slugPath, "chats");
7740
+ if (!fs11.existsSync(chatsDir)) continue;
7371
7741
  let chatFiles;
7372
7742
  try {
7373
- chatFiles = fs10.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
7743
+ chatFiles = fs11.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
7374
7744
  } catch {
7375
7745
  continue;
7376
7746
  }
@@ -7383,7 +7753,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
7383
7753
  onProgress?.(result.filesScanned);
7384
7754
  let raw;
7385
7755
  try {
7386
- raw = fs10.readFileSync(path13.join(chatsDir, chatFile), "utf-8");
7756
+ raw = fs11.readFileSync(path15.join(chatsDir, chatFile), "utf-8");
7387
7757
  } catch {
7388
7758
  continue;
7389
7759
  }
@@ -7602,7 +7972,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
7602
7972
  onProgress?.(result.filesScanned);
7603
7973
  let lines;
7604
7974
  try {
7605
- lines = fs10.readFileSync(filePath, "utf-8").split("\n");
7975
+ lines = fs11.readFileSync(filePath, "utf-8").split("\n");
7606
7976
  } catch {
7607
7977
  continue;
7608
7978
  }
@@ -7625,7 +7995,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
7625
7995
  sessionId = String(payload["id"] ?? filePath);
7626
7996
  startTime = String(payload["timestamp"] ?? "");
7627
7997
  const cwd = String(payload["cwd"] ?? "");
7628
- projLabel = stripTerminalEscapes(cwd.replace(os12.homedir(), "~")).slice(0, 40);
7998
+ projLabel = stripTerminalEscapes(cwd.replace(os13.homedir(), "~")).slice(0, 40);
7629
7999
  continue;
7630
8000
  }
7631
8001
  if (entry.type === "event_msg" && payload["type"] === "user_message") {
@@ -7870,23 +8240,23 @@ var init_scan = __esm({
7870
8240
  });
7871
8241
 
7872
8242
  // src/tui/dashboard/data.ts
7873
- import fs11 from "fs";
7874
- import os13 from "os";
7875
- import path14 from "path";
8243
+ import fs12 from "fs";
8244
+ import os14 from "os";
8245
+ import path16 from "path";
7876
8246
  import http from "http";
7877
8247
  function auditLogPath() {
7878
- return path14.join(os13.homedir(), ".node9", "audit.log");
8248
+ return path16.join(os14.homedir(), ".node9", "audit.log");
7879
8249
  }
7880
8250
  function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
7881
8251
  return new Promise((resolve) => {
7882
8252
  const p = customPath ?? auditLogPath();
7883
- if (!fs11.existsSync(p)) {
8253
+ if (!fs12.existsSync(p)) {
7884
8254
  resolve([]);
7885
8255
  return;
7886
8256
  }
7887
8257
  let raw;
7888
8258
  try {
7889
- raw = fs11.readFileSync(p, "utf8");
8259
+ raw = fs12.readFileSync(p, "utf8");
7890
8260
  } catch {
7891
8261
  resolve([]);
7892
8262
  return;
@@ -8018,13 +8388,13 @@ function loadBlast() {
8018
8388
  }
8019
8389
  }
8020
8390
  function shortenPath(p) {
8021
- const home = os13.homedir();
8391
+ const home = os14.homedir();
8022
8392
  return p.startsWith(home) ? p.replace(home, "~") : p;
8023
8393
  }
8024
8394
  async function loadReportAuditAsync(period) {
8025
- const claudeProjectsDir = path14.join(os13.homedir(), ".claude", "projects");
8395
+ const claudeProjectsDir = path16.join(os14.homedir(), ".claude", "projects");
8026
8396
  const codexSessionsDir2 = codexSessionsDir();
8027
- const geminiTmpDir = path14.join(os13.homedir(), ".gemini", "tmp");
8397
+ const geminiTmpDir = path16.join(os14.homedir(), ".gemini", "tmp");
8028
8398
  const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
8029
8399
  const entries = await readAuditEntriesAsync();
8030
8400
  void ensurePricingLoaded();
@@ -9130,8 +9500,8 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-run
9130
9500
  function TopToolsProjects({ audit }) {
9131
9501
  const data = audit?.data;
9132
9502
  const tools = data ? [...data.toolMap.entries()].sort(([, a], [, b]) => b.calls - a.calls).slice(0, ROW_LIMIT) : [];
9133
- const projects = data ? [...data.cost.byProject.entries()].map(([path15, r]) => ({
9134
- name: basenameOf(path15),
9503
+ const projects = data ? [...data.cost.byProject.entries()].map(([path17, r]) => ({
9504
+ name: basenameOf(path17),
9135
9505
  cost: r.cost,
9136
9506
  tokens: r.inputTokens + r.outputTokens
9137
9507
  })).sort((a, b) => b.cost - a.cost).slice(0, ROW_LIMIT) : [];
@@ -9568,8 +9938,8 @@ function pickTopLoopFile(loops) {
9568
9938
  map.set(k, (map.get(k) ?? 0) + (l.count ?? 0));
9569
9939
  }
9570
9940
  if (map.size === 0) return void 0;
9571
- const [path15, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
9572
- return { path: path15, count };
9941
+ const [path17, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
9942
+ return { path: path17, count };
9573
9943
  }
9574
9944
  var EMPTY_FILTERED_SCAN;
9575
9945
  var init_derive = __esm({