@node9/proxy 1.3.1 → 1.3.2

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.mjs CHANGED
@@ -94,8 +94,8 @@ function sanitizeConfig(raw) {
94
94
  }
95
95
  }
96
96
  const lines = result.error.issues.map((issue) => {
97
- const path22 = issue.path.length > 0 ? issue.path.join(".") : "root";
98
- return ` \u2022 ${path22}: ${issue.message}`;
97
+ const path24 = issue.path.length > 0 ? issue.path.join(".") : "root";
98
+ return ` \u2022 ${path24}: ${issue.message}`;
99
99
  });
100
100
  return {
101
101
  sanitized,
@@ -1157,13 +1157,445 @@ var init_dlp = __esm({
1157
1157
  }
1158
1158
  });
1159
1159
 
1160
- // src/policy/index.ts
1160
+ // src/utils/provenance.ts
1161
1161
  import fs5 from "fs";
1162
1162
  import path5 from "path";
1163
1163
  import os4 from "os";
1164
+ function findInPath(cmd) {
1165
+ if (path5.posix.isAbsolute(cmd)) return cmd;
1166
+ const pathEnv = process.env.PATH ?? "";
1167
+ for (const dir of pathEnv.split(path5.delimiter)) {
1168
+ if (!dir) continue;
1169
+ const full = path5.join(dir, cmd);
1170
+ try {
1171
+ fs5.accessSync(full, fs5.constants.X_OK);
1172
+ return full;
1173
+ } catch {
1174
+ }
1175
+ }
1176
+ return null;
1177
+ }
1178
+ function _classifyPath(resolved, cwd) {
1179
+ if (cwd && resolved.startsWith(cwd + "/")) {
1180
+ return { trustLevel: "user", reason: "binary in project directory" };
1181
+ }
1182
+ const osTmp = os4.tmpdir();
1183
+ const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
1184
+ if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1185
+ return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
1186
+ }
1187
+ if (SYSTEM_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1188
+ return { trustLevel: "system", reason: "" };
1189
+ }
1190
+ if (MANAGED_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1191
+ return { trustLevel: "managed", reason: "" };
1192
+ }
1193
+ if (USER_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1194
+ return { trustLevel: "user", reason: "" };
1195
+ }
1196
+ return { trustLevel: "unknown", reason: "binary in unrecognized location" };
1197
+ }
1198
+ function checkProvenance(cmd, cwd) {
1199
+ const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
1200
+ if (path5.posix.isAbsolute(bare)) {
1201
+ const early = _classifyPath(bare, cwd);
1202
+ if (early.trustLevel === "suspect") {
1203
+ return { resolvedPath: bare, ...early };
1204
+ }
1205
+ }
1206
+ let resolved;
1207
+ try {
1208
+ const found = findInPath(bare);
1209
+ if (!found) {
1210
+ return {
1211
+ resolvedPath: cmd,
1212
+ trustLevel: "unknown",
1213
+ reason: "binary not found in PATH"
1214
+ };
1215
+ }
1216
+ resolved = fs5.realpathSync(found);
1217
+ } catch {
1218
+ return {
1219
+ resolvedPath: cmd,
1220
+ trustLevel: "unknown",
1221
+ reason: "binary not found in PATH"
1222
+ };
1223
+ }
1224
+ try {
1225
+ const stat = fs5.statSync(resolved);
1226
+ if (stat.mode & 2) {
1227
+ return {
1228
+ resolvedPath: resolved,
1229
+ trustLevel: "suspect",
1230
+ reason: "binary is world-writable"
1231
+ };
1232
+ }
1233
+ } catch {
1234
+ return {
1235
+ resolvedPath: resolved,
1236
+ trustLevel: "unknown",
1237
+ reason: "could not stat binary"
1238
+ };
1239
+ }
1240
+ const classify = _classifyPath(resolved, cwd);
1241
+ return { resolvedPath: resolved, ...classify };
1242
+ }
1243
+ var SYSTEM_PREFIXES, MANAGED_PREFIXES, USER_PREFIXES, SUSPECT_PREFIXES;
1244
+ var init_provenance = __esm({
1245
+ "src/utils/provenance.ts"() {
1246
+ "use strict";
1247
+ SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
1248
+ MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
1249
+ USER_PREFIXES = [
1250
+ path5.join(os4.homedir(), "bin"),
1251
+ path5.join(os4.homedir(), ".local", "bin"),
1252
+ path5.join(os4.homedir(), ".cargo", "bin"),
1253
+ path5.join(os4.homedir(), ".npm-global", "bin"),
1254
+ path5.join(os4.homedir(), ".volta", "bin")
1255
+ ];
1256
+ SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
1257
+ }
1258
+ });
1259
+
1260
+ // src/policy/pipe-chain.ts
1261
+ function isSensitivePath(p) {
1262
+ return SENSITIVE_PATTERNS.some((re) => re.test(p));
1263
+ }
1264
+ function splitOnPipe(cmd) {
1265
+ const segments = [];
1266
+ let current = "";
1267
+ let inSingle = false;
1268
+ let inDouble = false;
1269
+ for (let i = 0; i < cmd.length; i++) {
1270
+ const ch = cmd[i];
1271
+ if (ch === "'" && !inDouble) {
1272
+ inSingle = !inSingle;
1273
+ current += ch;
1274
+ } else if (ch === '"' && !inSingle) {
1275
+ inDouble = !inDouble;
1276
+ current += ch;
1277
+ } else if (ch === "|" && !inSingle && !inDouble && cmd[i + 1] !== "|" && (i === 0 || cmd[i - 1] !== "|")) {
1278
+ segments.push(current.trim());
1279
+ current = "";
1280
+ } else {
1281
+ current += ch;
1282
+ }
1283
+ }
1284
+ if (current.trim()) segments.push(current.trim());
1285
+ return segments.filter(Boolean);
1286
+ }
1287
+ function positionalTokens(segment) {
1288
+ return segment.split(/\s+/).slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("@") && t.length > 0);
1289
+ }
1290
+ function analyzePipeChain(command) {
1291
+ const segments = splitOnPipe(command);
1292
+ if (segments.length < 2) {
1293
+ return {
1294
+ isPipeline: false,
1295
+ hasSensitiveSource: false,
1296
+ hasExternalSink: false,
1297
+ hasObfuscation: false,
1298
+ sourceFiles: [],
1299
+ sinkTargets: [],
1300
+ risk: "none"
1301
+ };
1302
+ }
1303
+ const sourceFiles = [];
1304
+ const sinkTargets = [];
1305
+ let hasSensitiveSource = false;
1306
+ let hasExternalSink = false;
1307
+ let hasObfuscation = false;
1308
+ for (const segment of segments) {
1309
+ const tokens = segment.split(/\s+/).filter(Boolean);
1310
+ if (tokens.length === 0) continue;
1311
+ const binary = tokens[0].toLowerCase();
1312
+ const args = positionalTokens(segment);
1313
+ if (SOURCE_COMMANDS.has(binary)) {
1314
+ sourceFiles.push(...args);
1315
+ if (args.some(isSensitivePath)) hasSensitiveSource = true;
1316
+ }
1317
+ if (OBFUSCATORS.has(binary)) hasObfuscation = true;
1318
+ if (SINK_COMMANDS.has(binary)) {
1319
+ const targets = args.filter(
1320
+ (a) => a.includes(".") || a.includes("://") || /^\d+\.\d+/.test(a)
1321
+ );
1322
+ sinkTargets.push(...targets);
1323
+ if (targets.length > 0) hasExternalSink = true;
1324
+ }
1325
+ }
1326
+ const fullCmd = command.toLowerCase();
1327
+ if (!hasSensitiveSource) {
1328
+ const redirMatch = fullCmd.match(/<\s*(\S+)/);
1329
+ if (redirMatch && isSensitivePath(redirMatch[1])) {
1330
+ hasSensitiveSource = true;
1331
+ sourceFiles.push(redirMatch[1]);
1332
+ }
1333
+ }
1334
+ const risk = hasSensitiveSource && hasExternalSink && hasObfuscation ? "critical" : hasSensitiveSource && hasExternalSink ? "high" : hasExternalSink ? "medium" : "none";
1335
+ return {
1336
+ isPipeline: true,
1337
+ hasSensitiveSource,
1338
+ hasExternalSink,
1339
+ hasObfuscation,
1340
+ sourceFiles,
1341
+ sinkTargets,
1342
+ risk
1343
+ };
1344
+ }
1345
+ var SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS;
1346
+ var init_pipe_chain = __esm({
1347
+ "src/policy/pipe-chain.ts"() {
1348
+ "use strict";
1349
+ SOURCE_COMMANDS = /* @__PURE__ */ new Set([
1350
+ "cat",
1351
+ "head",
1352
+ "tail",
1353
+ "grep",
1354
+ "awk",
1355
+ "sed",
1356
+ "cut",
1357
+ "sort",
1358
+ "tee",
1359
+ "less",
1360
+ "more",
1361
+ "strings",
1362
+ "xxd"
1363
+ ]);
1364
+ SINK_COMMANDS = /* @__PURE__ */ new Set([
1365
+ "curl",
1366
+ "wget",
1367
+ "nc",
1368
+ "ncat",
1369
+ "netcat",
1370
+ "ssh",
1371
+ "scp",
1372
+ "rsync",
1373
+ "socat",
1374
+ "ftp",
1375
+ "sftp",
1376
+ "telnet"
1377
+ ]);
1378
+ OBFUSCATORS = /* @__PURE__ */ new Set([
1379
+ "base64",
1380
+ "gzip",
1381
+ "gunzip",
1382
+ "bzip2",
1383
+ "xz",
1384
+ "zstd",
1385
+ "openssl",
1386
+ "gpg",
1387
+ "python",
1388
+ "python3",
1389
+ "perl",
1390
+ "ruby",
1391
+ "node"
1392
+ ]);
1393
+ SENSITIVE_PATTERNS = [
1394
+ /(?:^|\/)\.env(?:\.|$)/i,
1395
+ // .env, .env.local, .env.production
1396
+ /id_rsa|id_ed25519|id_ecdsa|id_dsa/i,
1397
+ // SSH private keys
1398
+ /\.pem$|\.key$|\.p12$|\.pfx$/i,
1399
+ // certificate files
1400
+ /(?:^|\/)\.ssh\//i,
1401
+ // ~/.ssh/ directory
1402
+ /(?:^|\/)\.aws\/credentials/i,
1403
+ // AWS credentials
1404
+ /(?:^|\/)\.netrc$/i,
1405
+ // netrc (stores HTTP credentials)
1406
+ /(?:^|\/)(passwd|shadow|sudoers)$/i,
1407
+ // /etc/passwd, /etc/shadow
1408
+ /(?:^|\/)credentials(?:\.json)?$/i
1409
+ // generic credentials files
1410
+ ];
1411
+ }
1412
+ });
1413
+
1414
+ // src/policy/flag-tables.ts
1415
+ import path6 from "path";
1416
+ function extractPositionalArgs(tokens, binary) {
1417
+ const binaryName = path6.basename(binary).replace(/\.exe$/i, "");
1418
+ const flagsWithValues = FLAGS_WITH_VALUES[binaryName] ?? /* @__PURE__ */ new Set();
1419
+ const positional = [];
1420
+ let skipNext = false;
1421
+ for (const token of tokens) {
1422
+ if (skipNext) {
1423
+ skipNext = false;
1424
+ continue;
1425
+ }
1426
+ if (token.startsWith("--") && token.includes("=")) continue;
1427
+ if (token.startsWith("-") && token.length === 2 && flagsWithValues.has(token)) {
1428
+ skipNext = true;
1429
+ continue;
1430
+ }
1431
+ if (token.startsWith("--") && flagsWithValues.has(token)) {
1432
+ skipNext = true;
1433
+ continue;
1434
+ }
1435
+ const shortFlag = token.slice(0, 2);
1436
+ if (token.startsWith("-") && token.length > 2 && flagsWithValues.has(shortFlag)) continue;
1437
+ if (token.startsWith("-")) continue;
1438
+ if (token.startsWith("@")) continue;
1439
+ positional.push(token);
1440
+ }
1441
+ return positional;
1442
+ }
1443
+ function extractNetworkTargets(tokens, binary) {
1444
+ return extractPositionalArgs(tokens, binary).map((t) => t.includes("@") ? t.split("@")[1] : t).map((t) => {
1445
+ const colonIdx = t.indexOf(":");
1446
+ if (colonIdx === -1) return t;
1447
+ const afterColon = t.slice(colonIdx + 1);
1448
+ if (/^\d+$/.test(afterColon)) return t.slice(0, colonIdx);
1449
+ return t;
1450
+ }).filter(Boolean);
1451
+ }
1452
+ var FLAGS_WITH_VALUES;
1453
+ var init_flag_tables = __esm({
1454
+ "src/policy/flag-tables.ts"() {
1455
+ "use strict";
1456
+ FLAGS_WITH_VALUES = {
1457
+ curl: /* @__PURE__ */ new Set([
1458
+ "-H",
1459
+ "--header",
1460
+ "-A",
1461
+ "--user-agent",
1462
+ "-e",
1463
+ "--referer",
1464
+ "-x",
1465
+ "--proxy",
1466
+ "-u",
1467
+ "--user",
1468
+ "-d",
1469
+ "--data",
1470
+ "--data-raw",
1471
+ "--data-binary",
1472
+ "-o",
1473
+ "--output",
1474
+ "-F",
1475
+ "--form",
1476
+ "--connect-to",
1477
+ "--resolve",
1478
+ "--cacert",
1479
+ "--cert",
1480
+ "--key",
1481
+ "-m",
1482
+ "--max-time"
1483
+ ]),
1484
+ wget: /* @__PURE__ */ new Set([
1485
+ "-O",
1486
+ "--output-document",
1487
+ "-P",
1488
+ "--directory-prefix",
1489
+ "-U",
1490
+ "--user-agent",
1491
+ "-e",
1492
+ "--execute",
1493
+ "--proxy",
1494
+ "--ca-certificate"
1495
+ ]),
1496
+ nc: /* @__PURE__ */ new Set(["-x", "-p", "-s", "-w", "-W", "-I", "-O"]),
1497
+ ncat: /* @__PURE__ */ new Set(["-x", "-p", "-s", "--proxy", "--proxy-auth", "-w", "--wait"]),
1498
+ netcat: /* @__PURE__ */ new Set(["-x", "-p", "-s", "-w"]),
1499
+ ssh: /* @__PURE__ */ new Set([
1500
+ "-i",
1501
+ "-l",
1502
+ "-p",
1503
+ "-o",
1504
+ "-E",
1505
+ "-F",
1506
+ "-J",
1507
+ "-L",
1508
+ "-R",
1509
+ "-W",
1510
+ "-b",
1511
+ "-c",
1512
+ "-D",
1513
+ "-e",
1514
+ "-I",
1515
+ "-S"
1516
+ ]),
1517
+ scp: /* @__PURE__ */ new Set(["-i", "-o", "-P", "-S"]),
1518
+ rsync: /* @__PURE__ */ new Set(["-e", "--rsh", "--rsync-path", "--password-file", "--log-file"]),
1519
+ socat: /* @__PURE__ */ new Set([])
1520
+ // socat uses address syntax, not flags — no value-flags
1521
+ };
1522
+ }
1523
+ });
1524
+
1525
+ // src/policy/ssh-parser.ts
1526
+ function tokenize(cmd) {
1527
+ const tokens = [];
1528
+ let current = "";
1529
+ let inSingle = false;
1530
+ let inDouble = false;
1531
+ for (const ch of cmd) {
1532
+ if (ch === "'" && !inDouble) {
1533
+ inSingle = !inSingle;
1534
+ } else if (ch === '"' && !inSingle) {
1535
+ inDouble = !inDouble;
1536
+ } else if ((ch === " " || ch === " ") && !inSingle && !inDouble) {
1537
+ if (current) {
1538
+ tokens.push(current);
1539
+ current = "";
1540
+ }
1541
+ } else {
1542
+ current += ch;
1543
+ }
1544
+ }
1545
+ if (current) tokens.push(current);
1546
+ return tokens;
1547
+ }
1548
+ function parseHost(raw) {
1549
+ return raw.split("@").pop().split(":")[0];
1550
+ }
1551
+ function extractAllSshHosts(tokens) {
1552
+ const hosts = /* @__PURE__ */ new Set();
1553
+ for (let i = 0; i < tokens.length; i++) {
1554
+ const t = tokens[i];
1555
+ if (t === "-J" && tokens[i + 1]) {
1556
+ for (const hop of tokens[++i].split(",")) {
1557
+ const h = parseHost(hop);
1558
+ if (h) hosts.add(h);
1559
+ }
1560
+ continue;
1561
+ }
1562
+ if (t === "-o" && tokens[i + 1]?.toLowerCase().startsWith("proxyjump=")) {
1563
+ const val = tokens[++i].split("=").slice(1).join("=");
1564
+ for (const hop of val.split(",")) {
1565
+ const h = parseHost(hop);
1566
+ if (h) hosts.add(h);
1567
+ }
1568
+ continue;
1569
+ }
1570
+ if (t === "-o" && tokens[i + 1]?.toLowerCase().startsWith("proxycommand=")) {
1571
+ const raw = tokens[++i].split("=").slice(1).join("=").replace(/^['"]|['"]$/g, "");
1572
+ const subTokens = tokenize(raw);
1573
+ const binary = subTokens[0] ?? "";
1574
+ extractNetworkTargets(subTokens.slice(1), binary).forEach((h) => hosts.add(h));
1575
+ extractAllSshHosts(subTokens.slice(1)).forEach((h) => hosts.add(h));
1576
+ continue;
1577
+ }
1578
+ if (!t.startsWith("-")) {
1579
+ const h = parseHost(t);
1580
+ if (h) hosts.add(h);
1581
+ }
1582
+ }
1583
+ return [...hosts].filter(Boolean);
1584
+ }
1585
+ var init_ssh_parser = __esm({
1586
+ "src/policy/ssh-parser.ts"() {
1587
+ "use strict";
1588
+ init_flag_tables();
1589
+ }
1590
+ });
1591
+
1592
+ // src/policy/index.ts
1593
+ import fs6 from "fs";
1594
+ import path7 from "path";
1595
+ import os5 from "os";
1164
1596
  import pm from "picomatch";
1165
1597
  import { parse } from "sh-syntax";
1166
- function tokenize(toolName) {
1598
+ function tokenize2(toolName) {
1167
1599
  return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
1168
1600
  }
1169
1601
  function matchesPattern(text, patterns) {
@@ -1176,9 +1608,9 @@ function matchesPattern(text, patterns) {
1176
1608
  const withoutDotSlash = text.replace(/^\.\//, "");
1177
1609
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1178
1610
  }
1179
- function getNestedValue(obj, path22) {
1611
+ function getNestedValue(obj, path24) {
1180
1612
  if (!obj || typeof obj !== "object") return null;
1181
- return path22.split(".").reduce((prev, curr) => prev?.[curr], obj);
1613
+ return path24.split(".").reduce((prev, curr) => prev?.[curr], obj);
1182
1614
  }
1183
1615
  function shouldSnapshot(toolName, args, config) {
1184
1616
  if (!config.settings.enableUndo) return false;
@@ -1313,7 +1745,7 @@ async function analyzeShellCommand(command) {
1313
1745
  }
1314
1746
  return { actions, paths, allTokens };
1315
1747
  }
1316
- async function evaluatePolicy(toolName, args, agent) {
1748
+ async function evaluatePolicy(toolName, args, agent, cwd) {
1317
1749
  const config = getConfig();
1318
1750
  if (matchesPattern(toolName, config.policy.ignoredTools)) return { decision: "allow" };
1319
1751
  if (config.policy.smartRules.length > 0) {
@@ -1343,11 +1775,55 @@ async function evaluatePolicy(toolName, args, agent) {
1343
1775
  if (INLINE_EXEC_PATTERN.test(shellCommand.trim())) {
1344
1776
  return { decision: "review", blockedByLabel: "Node9 Standard (Inline Execution)", tier: 3 };
1345
1777
  }
1778
+ const pipeAnalysis = analyzePipeChain(shellCommand);
1779
+ if (pipeAnalysis.isPipeline) {
1780
+ if (pipeAnalysis.risk === "critical") {
1781
+ return {
1782
+ decision: "block",
1783
+ blockedByLabel: "Node9: Pipe-Chain Exfiltration (critical)",
1784
+ reason: `Sensitive file piped through obfuscator to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${pipeAnalysis.sinkTargets.join(", ")}`,
1785
+ tier: 3
1786
+ };
1787
+ }
1788
+ if (pipeAnalysis.risk === "high") {
1789
+ return {
1790
+ decision: "review",
1791
+ blockedByLabel: "Node9: Pipe-Chain Exfiltration (high)",
1792
+ reason: `Sensitive file piped to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${pipeAnalysis.sinkTargets.join(", ")}`,
1793
+ tier: 3
1794
+ };
1795
+ }
1796
+ }
1797
+ const firstToken = analyzed.actions[0] ?? "";
1798
+ if (["ssh", "scp", "rsync"].includes(firstToken)) {
1799
+ const rawTokens = shellCommand.trim().split(/\s+/);
1800
+ const sshHosts = extractAllSshHosts(rawTokens.slice(1));
1801
+ allTokens.push(...sshHosts);
1802
+ }
1803
+ if (firstToken && path7.posix.isAbsolute(firstToken)) {
1804
+ const prov = checkProvenance(firstToken, cwd);
1805
+ if (prov.trustLevel === "suspect") {
1806
+ return {
1807
+ decision: config.settings.mode === "strict" ? "block" : "review",
1808
+ blockedByLabel: "Node9: Suspect Binary",
1809
+ reason: `Binary "${firstToken}" resolved to ${prov.resolvedPath} \u2014 ${prov.reason}`,
1810
+ tier: 3
1811
+ };
1812
+ }
1813
+ if (prov.trustLevel === "unknown" && config.settings.mode === "strict") {
1814
+ return {
1815
+ decision: "review",
1816
+ blockedByLabel: "Node9: Unknown Binary (strict mode)",
1817
+ reason: `Binary "${firstToken}" \u2014 ${prov.reason}`,
1818
+ tier: 3
1819
+ };
1820
+ }
1821
+ }
1346
1822
  if (isSqlTool(toolName, config.policy.toolInspection)) {
1347
1823
  allTokens = allTokens.filter((t) => !SQL_DML_KEYWORDS.has(t.toLowerCase()));
1348
1824
  }
1349
1825
  } else {
1350
- allTokens = tokenize(toolName);
1826
+ allTokens = tokenize2(toolName);
1351
1827
  if (args && typeof args === "object") {
1352
1828
  const flattenedArgs = JSON.stringify(args).toLowerCase();
1353
1829
  const extraTokens = flattenedArgs.split(/[^a-zA-Z0-9]+/).filter((t) => t.length > 1);
@@ -1421,9 +1897,9 @@ async function evaluatePolicy(toolName, args, agent) {
1421
1897
  }
1422
1898
  async function explainPolicy(toolName, args) {
1423
1899
  const steps = [];
1424
- const globalPath = path5.join(os4.homedir(), ".node9", "config.json");
1425
- const projectPath = path5.join(process.cwd(), "node9.config.json");
1426
- const credsPath = path5.join(os4.homedir(), ".node9", "credentials.json");
1900
+ const globalPath = path7.join(os5.homedir(), ".node9", "config.json");
1901
+ const projectPath = path7.join(process.cwd(), "node9.config.json");
1902
+ const credsPath = path7.join(os5.homedir(), ".node9", "credentials.json");
1427
1903
  const waterfall = [
1428
1904
  {
1429
1905
  tier: 1,
@@ -1434,19 +1910,19 @@ async function explainPolicy(toolName, args) {
1434
1910
  {
1435
1911
  tier: 2,
1436
1912
  label: "Cloud policy",
1437
- status: fs5.existsSync(credsPath) ? "active" : "missing",
1438
- note: fs5.existsSync(credsPath) ? "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
1913
+ status: fs6.existsSync(credsPath) ? "active" : "missing",
1914
+ note: fs6.existsSync(credsPath) ? "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
1439
1915
  },
1440
1916
  {
1441
1917
  tier: 3,
1442
1918
  label: "Project config",
1443
- status: fs5.existsSync(projectPath) ? "active" : "missing",
1919
+ status: fs6.existsSync(projectPath) ? "active" : "missing",
1444
1920
  path: projectPath
1445
1921
  },
1446
1922
  {
1447
1923
  tier: 4,
1448
1924
  label: "Global config",
1449
- status: fs5.existsSync(globalPath) ? "active" : "missing",
1925
+ status: fs6.existsSync(globalPath) ? "active" : "missing",
1450
1926
  path: globalPath
1451
1927
  },
1452
1928
  {
@@ -1578,7 +2054,7 @@ async function explainPolicy(toolName, args) {
1578
2054
  });
1579
2055
  }
1580
2056
  } else {
1581
- allTokens = tokenize(toolName);
2057
+ allTokens = tokenize2(toolName);
1582
2058
  let detail = `No toolInspection match for "${toolName}" \u2014 tokens: [${allTokens.join(", ")}]`;
1583
2059
  if (args && typeof args === "object") {
1584
2060
  const flattenedArgs = JSON.stringify(args).toLowerCase();
@@ -1690,21 +2166,24 @@ var init_policy = __esm({
1690
2166
  init_dlp();
1691
2167
  init_config();
1692
2168
  init_regex();
2169
+ init_provenance();
2170
+ init_pipe_chain();
2171
+ init_ssh_parser();
1693
2172
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
1694
2173
  }
1695
2174
  });
1696
2175
 
1697
2176
  // src/auth/state.ts
1698
- import fs6 from "fs";
1699
- import path6 from "path";
1700
- import os5 from "os";
2177
+ import fs7 from "fs";
2178
+ import path8 from "path";
2179
+ import os6 from "os";
1701
2180
  function checkPause() {
1702
2181
  try {
1703
- if (!fs6.existsSync(PAUSED_FILE)) return { paused: false };
1704
- const state = JSON.parse(fs6.readFileSync(PAUSED_FILE, "utf-8"));
2182
+ if (!fs7.existsSync(PAUSED_FILE)) return { paused: false };
2183
+ const state = JSON.parse(fs7.readFileSync(PAUSED_FILE, "utf-8"));
1705
2184
  if (state.expiry > 0 && Date.now() >= state.expiry) {
1706
2185
  try {
1707
- fs6.unlinkSync(PAUSED_FILE);
2186
+ fs7.unlinkSync(PAUSED_FILE);
1708
2187
  } catch {
1709
2188
  }
1710
2189
  return { paused: false };
@@ -1715,11 +2194,11 @@ function checkPause() {
1715
2194
  }
1716
2195
  }
1717
2196
  function atomicWriteSync(filePath, data, options) {
1718
- const dir = path6.dirname(filePath);
1719
- if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
1720
- const tmpPath = `${filePath}.${os5.hostname()}.${process.pid}.tmp`;
1721
- fs6.writeFileSync(tmpPath, data, options);
1722
- fs6.renameSync(tmpPath, filePath);
2197
+ const dir = path8.dirname(filePath);
2198
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
2199
+ const tmpPath = `${filePath}.${os6.hostname()}.${process.pid}.tmp`;
2200
+ fs7.writeFileSync(tmpPath, data, options);
2201
+ fs7.renameSync(tmpPath, filePath);
1723
2202
  }
1724
2203
  function pauseNode9(durationMs, durationStr) {
1725
2204
  const state = { expiry: Date.now() + durationMs, duration: durationStr };
@@ -1727,18 +2206,18 @@ function pauseNode9(durationMs, durationStr) {
1727
2206
  }
1728
2207
  function resumeNode9() {
1729
2208
  try {
1730
- if (fs6.existsSync(PAUSED_FILE)) fs6.unlinkSync(PAUSED_FILE);
2209
+ if (fs7.existsSync(PAUSED_FILE)) fs7.unlinkSync(PAUSED_FILE);
1731
2210
  } catch {
1732
2211
  }
1733
2212
  }
1734
2213
  function getActiveTrustSession(toolName) {
1735
2214
  try {
1736
- if (!fs6.existsSync(TRUST_FILE)) return false;
1737
- const trust = JSON.parse(fs6.readFileSync(TRUST_FILE, "utf-8"));
2215
+ if (!fs7.existsSync(TRUST_FILE)) return false;
2216
+ const trust = JSON.parse(fs7.readFileSync(TRUST_FILE, "utf-8"));
1738
2217
  const now = Date.now();
1739
2218
  const active = trust.entries.filter((e) => e.expiry > now);
1740
2219
  if (active.length !== trust.entries.length) {
1741
- fs6.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
2220
+ fs7.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
1742
2221
  }
1743
2222
  return active.some((e) => e.tool === toolName || matchesPattern(toolName, e.tool));
1744
2223
  } catch {
@@ -1749,8 +2228,8 @@ function writeTrustSession(toolName, durationMs) {
1749
2228
  try {
1750
2229
  let trust = { entries: [] };
1751
2230
  try {
1752
- if (fs6.existsSync(TRUST_FILE)) {
1753
- trust = JSON.parse(fs6.readFileSync(TRUST_FILE, "utf-8"));
2231
+ if (fs7.existsSync(TRUST_FILE)) {
2232
+ trust = JSON.parse(fs7.readFileSync(TRUST_FILE, "utf-8"));
1754
2233
  }
1755
2234
  } catch {
1756
2235
  }
@@ -1766,9 +2245,9 @@ function writeTrustSession(toolName, durationMs) {
1766
2245
  }
1767
2246
  function getPersistentDecision(toolName) {
1768
2247
  try {
1769
- const file = path6.join(os5.homedir(), ".node9", "decisions.json");
1770
- if (!fs6.existsSync(file)) return null;
1771
- const decisions = JSON.parse(fs6.readFileSync(file, "utf-8"));
2248
+ const file = path8.join(os6.homedir(), ".node9", "decisions.json");
2249
+ if (!fs7.existsSync(file)) return null;
2250
+ const decisions = JSON.parse(fs7.readFileSync(file, "utf-8"));
1772
2251
  const d = decisions[toolName];
1773
2252
  if (d === "allow" || d === "deny") return d;
1774
2253
  } catch {
@@ -1780,21 +2259,21 @@ var init_state = __esm({
1780
2259
  "src/auth/state.ts"() {
1781
2260
  "use strict";
1782
2261
  init_policy();
1783
- PAUSED_FILE = path6.join(os5.homedir(), ".node9", "PAUSED");
1784
- TRUST_FILE = path6.join(os5.homedir(), ".node9", "trust.json");
2262
+ PAUSED_FILE = path8.join(os6.homedir(), ".node9", "PAUSED");
2263
+ TRUST_FILE = path8.join(os6.homedir(), ".node9", "trust.json");
1785
2264
  }
1786
2265
  });
1787
2266
 
1788
2267
  // src/auth/daemon.ts
1789
- import fs7 from "fs";
1790
- import path7 from "path";
1791
- import os6 from "os";
2268
+ import fs8 from "fs";
2269
+ import path9 from "path";
2270
+ import os7 from "os";
1792
2271
  import { spawnSync } from "child_process";
1793
2272
  function getInternalToken() {
1794
2273
  try {
1795
- const pidFile = path7.join(os6.homedir(), ".node9", "daemon.pid");
1796
- if (!fs7.existsSync(pidFile)) return null;
1797
- const data = JSON.parse(fs7.readFileSync(pidFile, "utf-8"));
2274
+ const pidFile = path9.join(os7.homedir(), ".node9", "daemon.pid");
2275
+ if (!fs8.existsSync(pidFile)) return null;
2276
+ const data = JSON.parse(fs8.readFileSync(pidFile, "utf-8"));
1798
2277
  process.kill(data.pid, 0);
1799
2278
  return data.internalToken ?? null;
1800
2279
  } catch {
@@ -1802,10 +2281,10 @@ function getInternalToken() {
1802
2281
  }
1803
2282
  }
1804
2283
  function isDaemonRunning() {
1805
- const pidFile = path7.join(os6.homedir(), ".node9", "daemon.pid");
1806
- if (fs7.existsSync(pidFile)) {
2284
+ const pidFile = path9.join(os7.homedir(), ".node9", "daemon.pid");
2285
+ if (fs8.existsSync(pidFile)) {
1807
2286
  try {
1808
- const { pid, port } = JSON.parse(fs7.readFileSync(pidFile, "utf-8"));
2287
+ const { pid, port } = JSON.parse(fs8.readFileSync(pidFile, "utf-8"));
1809
2288
  if (port !== DAEMON_PORT) return false;
1810
2289
  process.kill(pid, 0);
1811
2290
  return true;
@@ -1908,7 +2387,7 @@ var init_daemon = __esm({
1908
2387
  });
1909
2388
 
1910
2389
  // src/context-sniper.ts
1911
- import path8 from "path";
2390
+ import path10 from "path";
1912
2391
  function smartTruncate(str, maxLen = 500) {
1913
2392
  if (str.length <= maxLen) return str;
1914
2393
  const edge = Math.floor(maxLen / 2) - 3;
@@ -1960,7 +2439,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
1960
2439
  intent = "EDIT";
1961
2440
  if (obj.file_path) {
1962
2441
  editFilePath = String(obj.file_path);
1963
- editFileName = path8.basename(editFilePath);
2442
+ editFileName = path10.basename(editFilePath);
1964
2443
  }
1965
2444
  const result = extractContext(String(obj.new_string), matchedWord);
1966
2445
  contextSnippet = result.snippet;
@@ -2017,7 +2496,7 @@ var init_context_sniper = __esm({
2017
2496
 
2018
2497
  // src/ui/native.ts
2019
2498
  import { spawn } from "child_process";
2020
- import path9 from "path";
2499
+ import path11 from "path";
2021
2500
  function formatArgs(args, matchedField, matchedWord) {
2022
2501
  if (args === null || args === void 0) return { message: "(none)", intent: "EXEC" };
2023
2502
  let parsed = args;
@@ -2036,7 +2515,7 @@ function formatArgs(args, matchedField, matchedWord) {
2036
2515
  if (typeof parsed === "object" && !Array.isArray(parsed)) {
2037
2516
  const obj = parsed;
2038
2517
  if (obj.old_string !== void 0 && obj.new_string !== void 0) {
2039
- const file = obj.file_path ? path9.basename(String(obj.file_path)) : "file";
2518
+ const file = obj.file_path ? path11.basename(String(obj.file_path)) : "file";
2040
2519
  const oldPreview = smartTruncate(String(obj.old_string), 120);
2041
2520
  const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
2042
2521
  return {
@@ -2221,8 +2700,8 @@ var init_native = __esm({
2221
2700
  });
2222
2701
 
2223
2702
  // src/auth/cloud.ts
2224
- import fs8 from "fs";
2225
- import os7 from "os";
2703
+ import fs9 from "fs";
2704
+ import os8 from "os";
2226
2705
  function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
2227
2706
  return fetch(`${creds.apiUrl}/audit`, {
2228
2707
  method: "POST",
@@ -2234,9 +2713,9 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
2234
2713
  context: {
2235
2714
  agent: meta?.agent,
2236
2715
  mcpServer: meta?.mcpServer,
2237
- hostname: os7.hostname(),
2716
+ hostname: os8.hostname(),
2238
2717
  cwd: process.cwd(),
2239
- platform: os7.platform()
2718
+ platform: os8.platform()
2240
2719
  }
2241
2720
  }),
2242
2721
  signal: AbortSignal.timeout(5e3)
@@ -2257,9 +2736,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
2257
2736
  context: {
2258
2737
  agent: meta?.agent,
2259
2738
  mcpServer: meta?.mcpServer,
2260
- hostname: os7.hostname(),
2739
+ hostname: os8.hostname(),
2261
2740
  cwd: process.cwd(),
2262
- platform: os7.platform()
2741
+ platform: os8.platform()
2263
2742
  },
2264
2743
  ...riskMetadata && { riskMetadata }
2265
2744
  }),
@@ -2315,14 +2794,14 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
2315
2794
  });
2316
2795
  clearTimeout(timer);
2317
2796
  if (!res.ok) {
2318
- fs8.appendFileSync(
2797
+ fs9.appendFileSync(
2319
2798
  HOOK_DEBUG_LOG,
2320
2799
  `[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
2321
2800
  `
2322
2801
  );
2323
2802
  }
2324
2803
  } catch (err) {
2325
- fs8.appendFileSync(
2804
+ fs9.appendFileSync(
2326
2805
  HOOK_DEBUG_LOG,
2327
2806
  `[resolve-cloud] PATCH failed for ${requestId}: ${err.message}
2328
2807
  `
@@ -2338,8 +2817,8 @@ var init_cloud = __esm({
2338
2817
 
2339
2818
  // src/auth/orchestrator.ts
2340
2819
  import net from "net";
2341
- import path10 from "path";
2342
- import os8 from "os";
2820
+ import path12 from "path";
2821
+ import os9 from "os";
2343
2822
  import { randomUUID } from "crypto";
2344
2823
  function notifyActivity(data) {
2345
2824
  return new Promise((resolve) => {
@@ -2422,7 +2901,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
2422
2901
  }
2423
2902
  if (config.settings.mode === "audit") {
2424
2903
  if (!isIgnoredTool(toolName)) {
2425
- const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
2904
+ const policyResult = await evaluatePolicy(toolName, args, meta?.agent, options?.cwd);
2426
2905
  if (policyResult.decision === "review") {
2427
2906
  appendLocalAudit(toolName, args, "allow", "audit-mode", meta);
2428
2907
  if (approvers.cloud && creds?.apiKey) {
@@ -2700,7 +3179,7 @@ var init_orchestrator = __esm({
2700
3179
  init_state();
2701
3180
  init_daemon();
2702
3181
  init_cloud();
2703
- ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path10.join(os8.tmpdir(), "node9-activity.sock");
3182
+ ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path12.join(os9.tmpdir(), "node9-activity.sock");
2704
3183
  }
2705
3184
  });
2706
3185
 
@@ -4172,9 +4651,9 @@ var init_ui2 = __esm({
4172
4651
 
4173
4652
  // src/daemon/state.ts
4174
4653
  import net2 from "net";
4175
- import fs10 from "fs";
4176
- import path12 from "path";
4177
- import os10 from "os";
4654
+ import fs11 from "fs";
4655
+ import path14 from "path";
4656
+ import os11 from "os";
4178
4657
  import { spawn as spawn2 } from "child_process";
4179
4658
  import { randomUUID as randomUUID2 } from "crypto";
4180
4659
  function getAbandonTimer() {
@@ -4199,11 +4678,11 @@ function markRejectionHandlerRegistered() {
4199
4678
  daemonRejectionHandlerRegistered = true;
4200
4679
  }
4201
4680
  function atomicWriteSync2(filePath, data, options) {
4202
- const dir = path12.dirname(filePath);
4203
- if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
4681
+ const dir = path14.dirname(filePath);
4682
+ if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
4204
4683
  const tmpPath = `${filePath}.${randomUUID2()}.tmp`;
4205
- fs10.writeFileSync(tmpPath, data, options);
4206
- fs10.renameSync(tmpPath, filePath);
4684
+ fs11.writeFileSync(tmpPath, data, options);
4685
+ fs11.renameSync(tmpPath, filePath);
4207
4686
  }
4208
4687
  function redactArgs(value) {
4209
4688
  if (!value || typeof value !== "object") return value;
@@ -4223,16 +4702,16 @@ function appendAuditLog(data) {
4223
4702
  decision: data.decision,
4224
4703
  source: "daemon"
4225
4704
  };
4226
- const dir = path12.dirname(AUDIT_LOG_FILE);
4227
- if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
4228
- fs10.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
4705
+ const dir = path14.dirname(AUDIT_LOG_FILE);
4706
+ if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
4707
+ fs11.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
4229
4708
  } catch {
4230
4709
  }
4231
4710
  }
4232
4711
  function getAuditHistory(limit = 20) {
4233
4712
  try {
4234
- if (!fs10.existsSync(AUDIT_LOG_FILE)) return [];
4235
- const lines = fs10.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
4713
+ if (!fs11.existsSync(AUDIT_LOG_FILE)) return [];
4714
+ const lines = fs11.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
4236
4715
  if (lines.length === 1 && lines[0] === "") return [];
4237
4716
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
4238
4717
  } catch {
@@ -4241,19 +4720,19 @@ function getAuditHistory(limit = 20) {
4241
4720
  }
4242
4721
  function getOrgName() {
4243
4722
  try {
4244
- if (fs10.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
4723
+ if (fs11.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
4245
4724
  } catch {
4246
4725
  }
4247
4726
  return null;
4248
4727
  }
4249
4728
  function hasStoredSlackKey() {
4250
- return fs10.existsSync(CREDENTIALS_FILE);
4729
+ return fs11.existsSync(CREDENTIALS_FILE);
4251
4730
  }
4252
4731
  function writeGlobalSetting(key, value) {
4253
4732
  let config = {};
4254
4733
  try {
4255
- if (fs10.existsSync(GLOBAL_CONFIG_FILE)) {
4256
- config = JSON.parse(fs10.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
4734
+ if (fs11.existsSync(GLOBAL_CONFIG_FILE)) {
4735
+ config = JSON.parse(fs11.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
4257
4736
  }
4258
4737
  } catch {
4259
4738
  }
@@ -4265,8 +4744,8 @@ function writeTrustEntry(toolName, durationMs) {
4265
4744
  try {
4266
4745
  let trust = { entries: [] };
4267
4746
  try {
4268
- if (fs10.existsSync(TRUST_FILE2))
4269
- trust = JSON.parse(fs10.readFileSync(TRUST_FILE2, "utf-8"));
4747
+ if (fs11.existsSync(TRUST_FILE2))
4748
+ trust = JSON.parse(fs11.readFileSync(TRUST_FILE2, "utf-8"));
4270
4749
  } catch {
4271
4750
  }
4272
4751
  trust.entries = trust.entries.filter((e) => e.tool !== toolName && e.expiry > Date.now());
@@ -4277,8 +4756,8 @@ function writeTrustEntry(toolName, durationMs) {
4277
4756
  }
4278
4757
  function readPersistentDecisions() {
4279
4758
  try {
4280
- if (fs10.existsSync(DECISIONS_FILE)) {
4281
- return JSON.parse(fs10.readFileSync(DECISIONS_FILE, "utf-8"));
4759
+ if (fs11.existsSync(DECISIONS_FILE)) {
4760
+ return JSON.parse(fs11.readFileSync(DECISIONS_FILE, "utf-8"));
4282
4761
  }
4283
4762
  } catch {
4284
4763
  }
@@ -4343,7 +4822,7 @@ function abandonPending() {
4343
4822
  });
4344
4823
  if (autoStarted) {
4345
4824
  try {
4346
- fs10.unlinkSync(DAEMON_PID_FILE);
4825
+ fs11.unlinkSync(DAEMON_PID_FILE);
4347
4826
  } catch {
4348
4827
  }
4349
4828
  setTimeout(() => {
@@ -4354,7 +4833,7 @@ function abandonPending() {
4354
4833
  }
4355
4834
  function startActivitySocket() {
4356
4835
  try {
4357
- fs10.unlinkSync(ACTIVITY_SOCKET_PATH2);
4836
+ fs11.unlinkSync(ACTIVITY_SOCKET_PATH2);
4358
4837
  } catch {
4359
4838
  }
4360
4839
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -4396,7 +4875,7 @@ function startActivitySocket() {
4396
4875
  unixServer.listen(ACTIVITY_SOCKET_PATH2);
4397
4876
  process.on("exit", () => {
4398
4877
  try {
4399
- fs10.unlinkSync(ACTIVITY_SOCKET_PATH2);
4878
+ fs11.unlinkSync(ACTIVITY_SOCKET_PATH2);
4400
4879
  } catch {
4401
4880
  }
4402
4881
  });
@@ -4406,13 +4885,13 @@ var init_state2 = __esm({
4406
4885
  "src/daemon/state.ts"() {
4407
4886
  "use strict";
4408
4887
  init_daemon();
4409
- homeDir = os10.homedir();
4410
- DAEMON_PID_FILE = path12.join(homeDir, ".node9", "daemon.pid");
4411
- DECISIONS_FILE = path12.join(homeDir, ".node9", "decisions.json");
4412
- AUDIT_LOG_FILE = path12.join(homeDir, ".node9", "audit.log");
4413
- TRUST_FILE2 = path12.join(homeDir, ".node9", "trust.json");
4414
- GLOBAL_CONFIG_FILE = path12.join(homeDir, ".node9", "config.json");
4415
- CREDENTIALS_FILE = path12.join(homeDir, ".node9", "credentials.json");
4888
+ homeDir = os11.homedir();
4889
+ DAEMON_PID_FILE = path14.join(homeDir, ".node9", "daemon.pid");
4890
+ DECISIONS_FILE = path14.join(homeDir, ".node9", "decisions.json");
4891
+ AUDIT_LOG_FILE = path14.join(homeDir, ".node9", "audit.log");
4892
+ TRUST_FILE2 = path14.join(homeDir, ".node9", "trust.json");
4893
+ GLOBAL_CONFIG_FILE = path14.join(homeDir, ".node9", "config.json");
4894
+ CREDENTIALS_FILE = path14.join(homeDir, ".node9", "credentials.json");
4416
4895
  pending = /* @__PURE__ */ new Map();
4417
4896
  sseClients = /* @__PURE__ */ new Set();
4418
4897
  _abandonTimer = null;
@@ -4426,7 +4905,7 @@ var init_state2 = __esm({
4426
4905
  "2h": 2 * 60 * 6e4
4427
4906
  };
4428
4907
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
4429
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path12.join(os10.tmpdir(), "node9-activity.sock");
4908
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path14.join(os11.tmpdir(), "node9-activity.sock");
4430
4909
  ACTIVITY_RING_SIZE = 100;
4431
4910
  activityRing = [];
4432
4911
  SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
@@ -4435,8 +4914,8 @@ var init_state2 = __esm({
4435
4914
 
4436
4915
  // src/daemon/server.ts
4437
4916
  import http from "http";
4438
- import fs11 from "fs";
4439
- import path13 from "path";
4917
+ import fs12 from "fs";
4918
+ import path15 from "path";
4440
4919
  import { randomUUID as randomUUID3 } from "crypto";
4441
4920
  import { spawnSync as spawnSync2 } from "child_process";
4442
4921
  import chalk2 from "chalk";
@@ -4455,7 +4934,7 @@ function startDaemon() {
4455
4934
  idleTimer = setTimeout(() => {
4456
4935
  if (autoStarted) {
4457
4936
  try {
4458
- fs11.unlinkSync(DAEMON_PID_FILE);
4937
+ fs12.unlinkSync(DAEMON_PID_FILE);
4459
4938
  } catch {
4460
4939
  }
4461
4940
  }
@@ -4597,7 +5076,7 @@ data: ${JSON.stringify(item.data)}
4597
5076
  status: "pending"
4598
5077
  });
4599
5078
  }
4600
- const projectCwd = typeof cwd === "string" && path13.isAbsolute(cwd) ? cwd : void 0;
5079
+ const projectCwd = typeof cwd === "string" && path15.isAbsolute(cwd) ? cwd : void 0;
4601
5080
  const projectConfig = getConfig(projectCwd);
4602
5081
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
4603
5082
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -4901,14 +5380,14 @@ data: ${JSON.stringify(item.data)}
4901
5380
  server.on("error", (e) => {
4902
5381
  if (e.code === "EADDRINUSE") {
4903
5382
  try {
4904
- if (fs11.existsSync(DAEMON_PID_FILE)) {
4905
- const { pid } = JSON.parse(fs11.readFileSync(DAEMON_PID_FILE, "utf-8"));
5383
+ if (fs12.existsSync(DAEMON_PID_FILE)) {
5384
+ const { pid } = JSON.parse(fs12.readFileSync(DAEMON_PID_FILE, "utf-8"));
4906
5385
  process.kill(pid, 0);
4907
5386
  return process.exit(0);
4908
5387
  }
4909
5388
  } catch {
4910
5389
  try {
4911
- fs11.unlinkSync(DAEMON_PID_FILE);
5390
+ fs12.unlinkSync(DAEMON_PID_FILE);
4912
5391
  } catch {
4913
5392
  }
4914
5393
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -4978,28 +5457,28 @@ var init_server = __esm({
4978
5457
  });
4979
5458
 
4980
5459
  // src/daemon/index.ts
4981
- import fs12 from "fs";
5460
+ import fs13 from "fs";
4982
5461
  import chalk3 from "chalk";
4983
5462
  import { spawnSync as spawnSync3 } from "child_process";
4984
5463
  function stopDaemon() {
4985
- if (!fs12.existsSync(DAEMON_PID_FILE)) return console.log(chalk3.yellow("Not running."));
5464
+ if (!fs13.existsSync(DAEMON_PID_FILE)) return console.log(chalk3.yellow("Not running."));
4986
5465
  try {
4987
- const { pid } = JSON.parse(fs12.readFileSync(DAEMON_PID_FILE, "utf-8"));
5466
+ const { pid } = JSON.parse(fs13.readFileSync(DAEMON_PID_FILE, "utf-8"));
4988
5467
  process.kill(pid, "SIGTERM");
4989
5468
  console.log(chalk3.green("\u2705 Stopped."));
4990
5469
  } catch {
4991
5470
  console.log(chalk3.gray("Cleaned up stale PID file."));
4992
5471
  } finally {
4993
5472
  try {
4994
- fs12.unlinkSync(DAEMON_PID_FILE);
5473
+ fs13.unlinkSync(DAEMON_PID_FILE);
4995
5474
  } catch {
4996
5475
  }
4997
5476
  }
4998
5477
  }
4999
5478
  function daemonStatus() {
5000
- if (fs12.existsSync(DAEMON_PID_FILE)) {
5479
+ if (fs13.existsSync(DAEMON_PID_FILE)) {
5001
5480
  try {
5002
- const { pid } = JSON.parse(fs12.readFileSync(DAEMON_PID_FILE, "utf-8"));
5481
+ const { pid } = JSON.parse(fs13.readFileSync(DAEMON_PID_FILE, "utf-8"));
5003
5482
  process.kill(pid, 0);
5004
5483
  console.log(chalk3.green("Node9 daemon: running"));
5005
5484
  return;
@@ -5034,9 +5513,9 @@ __export(tail_exports, {
5034
5513
  });
5035
5514
  import http2 from "http";
5036
5515
  import chalk14 from "chalk";
5037
- import fs19 from "fs";
5038
- import os17 from "os";
5039
- import path20 from "path";
5516
+ import fs20 from "fs";
5517
+ import os18 from "os";
5518
+ import path22 from "path";
5040
5519
  import readline3 from "readline";
5041
5520
  import { spawn as spawn9, execSync as execSync3 } from "child_process";
5042
5521
  function getIcon(tool) {
@@ -5076,9 +5555,9 @@ function renderPending(activity) {
5076
5555
  }
5077
5556
  async function ensureDaemon() {
5078
5557
  let pidPort = null;
5079
- if (fs19.existsSync(PID_FILE)) {
5558
+ if (fs20.existsSync(PID_FILE)) {
5080
5559
  try {
5081
- const { port } = JSON.parse(fs19.readFileSync(PID_FILE, "utf-8"));
5560
+ const { port } = JSON.parse(fs20.readFileSync(PID_FILE, "utf-8"));
5082
5561
  pidPort = port;
5083
5562
  } catch {
5084
5563
  console.error(chalk14.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -5243,8 +5722,8 @@ async function startTail(options = {}) {
5243
5722
  process.stdout.write(SHOW_CURSOR);
5244
5723
  postDecisionHttp(req2.id, decision, csrfToken, port).catch((err) => {
5245
5724
  try {
5246
- fs19.appendFileSync(
5247
- path20.join(os17.homedir(), ".node9", "hook-debug.log"),
5725
+ fs20.appendFileSync(
5726
+ path22.join(os18.homedir(), ".node9", "hook-debug.log"),
5248
5727
  `[tail] POST /decision failed: ${String(err)}
5249
5728
  `
5250
5729
  );
@@ -5435,7 +5914,7 @@ var init_tail = __esm({
5435
5914
  "use strict";
5436
5915
  init_daemon2();
5437
5916
  init_core();
5438
- PID_FILE = path20.join(os17.homedir(), ".node9", "daemon.pid");
5917
+ PID_FILE = path22.join(os18.homedir(), ".node9", "daemon.pid");
5439
5918
  ICONS = {
5440
5919
  bash: "\u{1F4BB}",
5441
5920
  shell: "\u{1F4BB}",
@@ -5473,9 +5952,9 @@ init_core();
5473
5952
  import { Command } from "commander";
5474
5953
 
5475
5954
  // src/setup.ts
5476
- import fs9 from "fs";
5477
- import path11 from "path";
5478
- import os9 from "os";
5955
+ import fs10 from "fs";
5956
+ import path13 from "path";
5957
+ import os10 from "os";
5479
5958
  import chalk from "chalk";
5480
5959
  import { confirm } from "@inquirer/prompts";
5481
5960
  function printDaemonTip() {
@@ -5492,26 +5971,26 @@ function fullPathCommand(subcommand) {
5492
5971
  }
5493
5972
  function readJson(filePath) {
5494
5973
  try {
5495
- if (fs9.existsSync(filePath)) {
5496
- return JSON.parse(fs9.readFileSync(filePath, "utf-8"));
5974
+ if (fs10.existsSync(filePath)) {
5975
+ return JSON.parse(fs10.readFileSync(filePath, "utf-8"));
5497
5976
  }
5498
5977
  } catch {
5499
5978
  }
5500
5979
  return null;
5501
5980
  }
5502
5981
  function writeJson(filePath, data) {
5503
- const dir = path11.dirname(filePath);
5504
- if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
5505
- fs9.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
5982
+ const dir = path13.dirname(filePath);
5983
+ if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
5984
+ fs10.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
5506
5985
  }
5507
5986
  function isNode9Hook(cmd) {
5508
5987
  if (!cmd) return false;
5509
5988
  return /(?:^|[\s/\\])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js (?:check|log)/.test(cmd);
5510
5989
  }
5511
5990
  function teardownClaude() {
5512
- const homeDir2 = os9.homedir();
5513
- const hooksPath = path11.join(homeDir2, ".claude", "settings.json");
5514
- const mcpPath = path11.join(homeDir2, ".claude.json");
5991
+ const homeDir2 = os10.homedir();
5992
+ const hooksPath = path13.join(homeDir2, ".claude", "settings.json");
5993
+ const mcpPath = path13.join(homeDir2, ".claude.json");
5515
5994
  let changed = false;
5516
5995
  const settings = readJson(hooksPath);
5517
5996
  if (settings?.hooks) {
@@ -5559,8 +6038,8 @@ function teardownClaude() {
5559
6038
  }
5560
6039
  }
5561
6040
  function teardownGemini() {
5562
- const homeDir2 = os9.homedir();
5563
- const settingsPath = path11.join(homeDir2, ".gemini", "settings.json");
6041
+ const homeDir2 = os10.homedir();
6042
+ const settingsPath = path13.join(homeDir2, ".gemini", "settings.json");
5564
6043
  const settings = readJson(settingsPath);
5565
6044
  if (!settings) {
5566
6045
  console.log(chalk.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
@@ -5598,8 +6077,8 @@ function teardownGemini() {
5598
6077
  }
5599
6078
  }
5600
6079
  function teardownCursor() {
5601
- const homeDir2 = os9.homedir();
5602
- const mcpPath = path11.join(homeDir2, ".cursor", "mcp.json");
6080
+ const homeDir2 = os10.homedir();
6081
+ const mcpPath = path13.join(homeDir2, ".cursor", "mcp.json");
5603
6082
  const mcpConfig = readJson(mcpPath);
5604
6083
  if (!mcpConfig?.mcpServers) {
5605
6084
  console.log(chalk.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
@@ -5625,9 +6104,9 @@ function teardownCursor() {
5625
6104
  }
5626
6105
  }
5627
6106
  async function setupClaude() {
5628
- const homeDir2 = os9.homedir();
5629
- const mcpPath = path11.join(homeDir2, ".claude.json");
5630
- const hooksPath = path11.join(homeDir2, ".claude", "settings.json");
6107
+ const homeDir2 = os10.homedir();
6108
+ const mcpPath = path13.join(homeDir2, ".claude.json");
6109
+ const hooksPath = path13.join(homeDir2, ".claude", "settings.json");
5631
6110
  const claudeConfig = readJson(mcpPath) ?? {};
5632
6111
  const settings = readJson(hooksPath) ?? {};
5633
6112
  const servers = claudeConfig.mcpServers ?? {};
@@ -5701,8 +6180,8 @@ async function setupClaude() {
5701
6180
  }
5702
6181
  }
5703
6182
  async function setupGemini() {
5704
- const homeDir2 = os9.homedir();
5705
- const settingsPath = path11.join(homeDir2, ".gemini", "settings.json");
6183
+ const homeDir2 = os10.homedir();
6184
+ const settingsPath = path13.join(homeDir2, ".gemini", "settings.json");
5706
6185
  const settings = readJson(settingsPath) ?? {};
5707
6186
  const servers = settings.mcpServers ?? {};
5708
6187
  let anythingChanged = false;
@@ -5784,8 +6263,8 @@ async function setupGemini() {
5784
6263
  }
5785
6264
  }
5786
6265
  async function setupCursor() {
5787
- const homeDir2 = os9.homedir();
5788
- const mcpPath = path11.join(homeDir2, ".cursor", "mcp.json");
6266
+ const homeDir2 = os10.homedir();
6267
+ const mcpPath = path13.join(homeDir2, ".cursor", "mcp.json");
5789
6268
  const mcpConfig = readJson(mcpPath) ?? {};
5790
6269
  const servers = mcpConfig.mcpServers ?? {};
5791
6270
  let anythingChanged = false;
@@ -5842,9 +6321,9 @@ async function setupCursor() {
5842
6321
  // src/cli.ts
5843
6322
  init_daemon2();
5844
6323
  import chalk15 from "chalk";
5845
- import fs20 from "fs";
5846
- import path21 from "path";
5847
- import os18 from "os";
6324
+ import fs21 from "fs";
6325
+ import path23 from "path";
6326
+ import os19 from "os";
5848
6327
  import { confirm as confirm3 } from "@inquirer/prompts";
5849
6328
 
5850
6329
  // src/utils/duration.ts
@@ -6069,32 +6548,32 @@ init_daemon();
6069
6548
  init_config();
6070
6549
  init_policy();
6071
6550
  import chalk5 from "chalk";
6072
- import fs14 from "fs";
6073
- import path15 from "path";
6074
- import os12 from "os";
6551
+ import fs15 from "fs";
6552
+ import path17 from "path";
6553
+ import os13 from "os";
6075
6554
 
6076
6555
  // src/undo.ts
6077
6556
  import { spawnSync as spawnSync4, spawn as spawn5 } from "child_process";
6078
6557
  import crypto2 from "crypto";
6079
- import fs13 from "fs";
6080
- import path14 from "path";
6081
- import os11 from "os";
6082
- var SNAPSHOT_STACK_PATH = path14.join(os11.homedir(), ".node9", "snapshots.json");
6083
- var UNDO_LATEST_PATH = path14.join(os11.homedir(), ".node9", "undo_latest.txt");
6558
+ import fs14 from "fs";
6559
+ import path16 from "path";
6560
+ import os12 from "os";
6561
+ var SNAPSHOT_STACK_PATH = path16.join(os12.homedir(), ".node9", "snapshots.json");
6562
+ var UNDO_LATEST_PATH = path16.join(os12.homedir(), ".node9", "undo_latest.txt");
6084
6563
  var MAX_SNAPSHOTS = 10;
6085
6564
  var GIT_TIMEOUT = 15e3;
6086
6565
  function readStack() {
6087
6566
  try {
6088
- if (fs13.existsSync(SNAPSHOT_STACK_PATH))
6089
- return JSON.parse(fs13.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
6567
+ if (fs14.existsSync(SNAPSHOT_STACK_PATH))
6568
+ return JSON.parse(fs14.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
6090
6569
  } catch {
6091
6570
  }
6092
6571
  return [];
6093
6572
  }
6094
6573
  function writeStack(stack) {
6095
- const dir = path14.dirname(SNAPSHOT_STACK_PATH);
6096
- if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
6097
- fs13.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
6574
+ const dir = path16.dirname(SNAPSHOT_STACK_PATH);
6575
+ if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
6576
+ fs14.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
6098
6577
  }
6099
6578
  function buildArgsSummary(tool, args) {
6100
6579
  if (!args || typeof args !== "object") return "";
@@ -6110,7 +6589,7 @@ function buildArgsSummary(tool, args) {
6110
6589
  function normalizeCwdForHash(cwd) {
6111
6590
  let normalized;
6112
6591
  try {
6113
- normalized = fs13.realpathSync(cwd);
6592
+ normalized = fs14.realpathSync(cwd);
6114
6593
  } catch {
6115
6594
  normalized = cwd;
6116
6595
  }
@@ -6120,16 +6599,16 @@ function normalizeCwdForHash(cwd) {
6120
6599
  }
6121
6600
  function getShadowRepoDir(cwd) {
6122
6601
  const hash = crypto2.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
6123
- return path14.join(os11.homedir(), ".node9", "snapshots", hash);
6602
+ return path16.join(os12.homedir(), ".node9", "snapshots", hash);
6124
6603
  }
6125
6604
  function cleanOrphanedIndexFiles(shadowDir) {
6126
6605
  try {
6127
6606
  const cutoff = Date.now() - 6e4;
6128
- for (const f of fs13.readdirSync(shadowDir)) {
6607
+ for (const f of fs14.readdirSync(shadowDir)) {
6129
6608
  if (f.startsWith("index_")) {
6130
- const fp = path14.join(shadowDir, f);
6609
+ const fp = path16.join(shadowDir, f);
6131
6610
  try {
6132
- if (fs13.statSync(fp).mtimeMs < cutoff) fs13.unlinkSync(fp);
6611
+ if (fs14.statSync(fp).mtimeMs < cutoff) fs14.unlinkSync(fp);
6133
6612
  } catch {
6134
6613
  }
6135
6614
  }
@@ -6141,7 +6620,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
6141
6620
  const hardcoded = [".git", ".node9"];
6142
6621
  const lines = [...hardcoded, ...ignorePaths].join("\n");
6143
6622
  try {
6144
- fs13.writeFileSync(path14.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
6623
+ fs14.writeFileSync(path16.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
6145
6624
  } catch {
6146
6625
  }
6147
6626
  }
@@ -6154,25 +6633,25 @@ function ensureShadowRepo(shadowDir, cwd) {
6154
6633
  timeout: 3e3
6155
6634
  });
6156
6635
  if (check.status === 0) {
6157
- const ptPath = path14.join(shadowDir, "project-path.txt");
6636
+ const ptPath = path16.join(shadowDir, "project-path.txt");
6158
6637
  try {
6159
- const stored = fs13.readFileSync(ptPath, "utf8").trim();
6638
+ const stored = fs14.readFileSync(ptPath, "utf8").trim();
6160
6639
  if (stored === normalizedCwd) return true;
6161
6640
  if (process.env.NODE9_DEBUG === "1")
6162
6641
  console.error(
6163
6642
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
6164
6643
  );
6165
- fs13.rmSync(shadowDir, { recursive: true, force: true });
6644
+ fs14.rmSync(shadowDir, { recursive: true, force: true });
6166
6645
  } catch {
6167
6646
  try {
6168
- fs13.writeFileSync(ptPath, normalizedCwd, "utf8");
6647
+ fs14.writeFileSync(ptPath, normalizedCwd, "utf8");
6169
6648
  } catch {
6170
6649
  }
6171
6650
  return true;
6172
6651
  }
6173
6652
  }
6174
6653
  try {
6175
- fs13.mkdirSync(shadowDir, { recursive: true });
6654
+ fs14.mkdirSync(shadowDir, { recursive: true });
6176
6655
  } catch {
6177
6656
  }
6178
6657
  const init = spawnSync4("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -6181,7 +6660,7 @@ function ensureShadowRepo(shadowDir, cwd) {
6181
6660
  console.error("[Node9] git init --bare failed:", init.stderr?.toString());
6182
6661
  return false;
6183
6662
  }
6184
- const configFile = path14.join(shadowDir, "config");
6663
+ const configFile = path16.join(shadowDir, "config");
6185
6664
  spawnSync4("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
6186
6665
  timeout: 3e3
6187
6666
  });
@@ -6189,7 +6668,7 @@ function ensureShadowRepo(shadowDir, cwd) {
6189
6668
  timeout: 3e3
6190
6669
  });
6191
6670
  try {
6192
- fs13.writeFileSync(path14.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
6671
+ fs14.writeFileSync(path16.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
6193
6672
  } catch {
6194
6673
  }
6195
6674
  return true;
@@ -6212,7 +6691,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
6212
6691
  const shadowDir = getShadowRepoDir(cwd);
6213
6692
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
6214
6693
  writeShadowExcludes(shadowDir, ignorePaths);
6215
- indexFile = path14.join(shadowDir, `index_${process.pid}_${Date.now()}`);
6694
+ indexFile = path16.join(shadowDir, `index_${process.pid}_${Date.now()}`);
6216
6695
  const shadowEnv = {
6217
6696
  ...process.env,
6218
6697
  GIT_DIR: shadowDir,
@@ -6241,7 +6720,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
6241
6720
  const shouldGc = stack.length % 5 === 0;
6242
6721
  if (stack.length > MAX_SNAPSHOTS) stack.splice(0, stack.length - MAX_SNAPSHOTS);
6243
6722
  writeStack(stack);
6244
- fs13.writeFileSync(UNDO_LATEST_PATH, commitHash);
6723
+ fs14.writeFileSync(UNDO_LATEST_PATH, commitHash);
6245
6724
  if (shouldGc) {
6246
6725
  spawn5("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
6247
6726
  }
@@ -6252,7 +6731,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
6252
6731
  } finally {
6253
6732
  if (indexFile) {
6254
6733
  try {
6255
- fs13.unlinkSync(indexFile);
6734
+ fs14.unlinkSync(indexFile);
6256
6735
  } catch {
6257
6736
  }
6258
6737
  }
@@ -6321,9 +6800,9 @@ function applyUndo(hash, cwd) {
6321
6800
  timeout: GIT_TIMEOUT
6322
6801
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
6323
6802
  for (const file of [...tracked, ...untracked]) {
6324
- const fullPath = path14.join(dir, file);
6325
- if (!snapshotFiles.has(file) && fs13.existsSync(fullPath)) {
6326
- fs13.unlinkSync(fullPath);
6803
+ const fullPath = path16.join(dir, file);
6804
+ if (!snapshotFiles.has(file) && fs14.existsSync(fullPath)) {
6805
+ fs14.unlinkSync(fullPath);
6327
6806
  }
6328
6807
  }
6329
6808
  return true;
@@ -6347,9 +6826,9 @@ function registerCheckCommand(program2) {
6347
6826
  } catch (err) {
6348
6827
  const tempConfig = getConfig();
6349
6828
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
6350
- const logPath = path15.join(os12.homedir(), ".node9", "hook-debug.log");
6829
+ const logPath = path17.join(os13.homedir(), ".node9", "hook-debug.log");
6351
6830
  const errMsg = err instanceof Error ? err.message : String(err);
6352
- fs14.appendFileSync(
6831
+ fs15.appendFileSync(
6353
6832
  logPath,
6354
6833
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
6355
6834
  RAW: ${raw}
@@ -6360,10 +6839,10 @@ RAW: ${raw}
6360
6839
  }
6361
6840
  const config = getConfig(payload.cwd || void 0);
6362
6841
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
6363
- const logPath = path15.join(os12.homedir(), ".node9", "hook-debug.log");
6364
- if (!fs14.existsSync(path15.dirname(logPath)))
6365
- fs14.mkdirSync(path15.dirname(logPath), { recursive: true });
6366
- fs14.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
6842
+ const logPath = path17.join(os13.homedir(), ".node9", "hook-debug.log");
6843
+ if (!fs15.existsSync(path17.dirname(logPath)))
6844
+ fs15.mkdirSync(path17.dirname(logPath), { recursive: true });
6845
+ fs15.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
6367
6846
  `);
6368
6847
  }
6369
6848
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -6376,8 +6855,8 @@ RAW: ${raw}
6376
6855
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
6377
6856
  let ttyFd = null;
6378
6857
  try {
6379
- ttyFd = fs14.openSync("/dev/tty", "w");
6380
- const writeTty = (line) => fs14.writeSync(ttyFd, line + "\n");
6858
+ ttyFd = fs15.openSync("/dev/tty", "w");
6859
+ const writeTty = (line) => fs15.writeSync(ttyFd, line + "\n");
6381
6860
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
6382
6861
  writeTty(chalk5.bgRed.white.bold(`
6383
6862
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -6393,7 +6872,7 @@ RAW: ${raw}
6393
6872
  } finally {
6394
6873
  if (ttyFd !== null)
6395
6874
  try {
6396
- fs14.closeSync(ttyFd);
6875
+ fs15.closeSync(ttyFd);
6397
6876
  } catch {
6398
6877
  }
6399
6878
  }
@@ -6424,7 +6903,7 @@ RAW: ${raw}
6424
6903
  if (shouldSnapshot(toolName, toolInput, config)) {
6425
6904
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
6426
6905
  }
6427
- const safeCwdForAuth = typeof payload.cwd === "string" && path15.isAbsolute(payload.cwd) ? payload.cwd : void 0;
6906
+ const safeCwdForAuth = typeof payload.cwd === "string" && path17.isAbsolute(payload.cwd) ? payload.cwd : void 0;
6428
6907
  const result = await authorizeHeadless(toolName, toolInput, meta, {
6429
6908
  cwd: safeCwdForAuth
6430
6909
  });
@@ -6436,12 +6915,12 @@ RAW: ${raw}
6436
6915
  }
6437
6916
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
6438
6917
  try {
6439
- const tty = fs14.openSync("/dev/tty", "w");
6440
- fs14.writeSync(
6918
+ const tty = fs15.openSync("/dev/tty", "w");
6919
+ fs15.writeSync(
6441
6920
  tty,
6442
6921
  chalk5.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
6443
6922
  );
6444
- fs14.closeSync(tty);
6923
+ fs15.closeSync(tty);
6445
6924
  } catch {
6446
6925
  }
6447
6926
  const daemonReady = await autoStartDaemonAndWait();
@@ -6468,9 +6947,9 @@ RAW: ${raw}
6468
6947
  });
6469
6948
  } catch (err) {
6470
6949
  if (process.env.NODE9_DEBUG === "1") {
6471
- const logPath = path15.join(os12.homedir(), ".node9", "hook-debug.log");
6950
+ const logPath = path17.join(os13.homedir(), ".node9", "hook-debug.log");
6472
6951
  const errMsg = err instanceof Error ? err.message : String(err);
6473
- fs14.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
6952
+ fs15.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
6474
6953
  `);
6475
6954
  }
6476
6955
  process.exit(0);
@@ -6507,9 +6986,9 @@ RAW: ${raw}
6507
6986
  init_audit();
6508
6987
  init_config();
6509
6988
  init_policy();
6510
- import fs15 from "fs";
6511
- import path16 from "path";
6512
- import os13 from "os";
6989
+ import fs16 from "fs";
6990
+ import path18 from "path";
6991
+ import os14 from "os";
6513
6992
  function sanitize3(value) {
6514
6993
  return value.replace(/[\x00-\x1F\x7F]/g, "");
6515
6994
  }
@@ -6528,11 +7007,11 @@ function registerLogCommand(program2) {
6528
7007
  decision: "allowed",
6529
7008
  source: "post-hook"
6530
7009
  };
6531
- const logPath = path16.join(os13.homedir(), ".node9", "audit.log");
6532
- if (!fs15.existsSync(path16.dirname(logPath)))
6533
- fs15.mkdirSync(path16.dirname(logPath), { recursive: true });
6534
- fs15.appendFileSync(logPath, JSON.stringify(entry) + "\n");
6535
- const safeCwd = typeof payload.cwd === "string" && path16.isAbsolute(payload.cwd) ? payload.cwd : void 0;
7010
+ const logPath = path18.join(os14.homedir(), ".node9", "audit.log");
7011
+ if (!fs16.existsSync(path18.dirname(logPath)))
7012
+ fs16.mkdirSync(path18.dirname(logPath), { recursive: true });
7013
+ fs16.appendFileSync(logPath, JSON.stringify(entry) + "\n");
7014
+ const safeCwd = typeof payload.cwd === "string" && path18.isAbsolute(payload.cwd) ? payload.cwd : void 0;
6536
7015
  const config = getConfig(safeCwd);
6537
7016
  if (shouldSnapshot(tool, {}, config)) {
6538
7017
  await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
@@ -6541,9 +7020,9 @@ function registerLogCommand(program2) {
6541
7020
  const msg = err instanceof Error ? err.message : String(err);
6542
7021
  process.stderr.write(`[Node9] audit log error: ${msg}
6543
7022
  `);
6544
- const debugPath = path16.join(os13.homedir(), ".node9", "hook-debug.log");
7023
+ const debugPath = path18.join(os14.homedir(), ".node9", "hook-debug.log");
6545
7024
  try {
6546
- fs15.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
7025
+ fs16.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
6547
7026
  `);
6548
7027
  } catch {
6549
7028
  }
@@ -6848,13 +7327,13 @@ function registerConfigShowCommand(program2) {
6848
7327
  // src/cli/commands/doctor.ts
6849
7328
  init_daemon();
6850
7329
  import chalk7 from "chalk";
6851
- import fs16 from "fs";
6852
- import path17 from "path";
6853
- import os14 from "os";
7330
+ import fs17 from "fs";
7331
+ import path19 from "path";
7332
+ import os15 from "os";
6854
7333
  import { execSync as execSync2 } from "child_process";
6855
7334
  function registerDoctorCommand(program2, version2) {
6856
7335
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
6857
- const homeDir2 = os14.homedir();
7336
+ const homeDir2 = os15.homedir();
6858
7337
  let failures = 0;
6859
7338
  function pass(msg) {
6860
7339
  console.log(chalk7.green(" \u2705 ") + msg);
@@ -6903,10 +7382,10 @@ function registerDoctorCommand(program2, version2) {
6903
7382
  );
6904
7383
  }
6905
7384
  section("Configuration");
6906
- const globalConfigPath = path17.join(homeDir2, ".node9", "config.json");
6907
- if (fs16.existsSync(globalConfigPath)) {
7385
+ const globalConfigPath = path19.join(homeDir2, ".node9", "config.json");
7386
+ if (fs17.existsSync(globalConfigPath)) {
6908
7387
  try {
6909
- JSON.parse(fs16.readFileSync(globalConfigPath, "utf-8"));
7388
+ JSON.parse(fs17.readFileSync(globalConfigPath, "utf-8"));
6910
7389
  pass("~/.node9/config.json found and valid");
6911
7390
  } catch {
6912
7391
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -6914,10 +7393,10 @@ function registerDoctorCommand(program2, version2) {
6914
7393
  } else {
6915
7394
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
6916
7395
  }
6917
- const projectConfigPath = path17.join(process.cwd(), "node9.config.json");
6918
- if (fs16.existsSync(projectConfigPath)) {
7396
+ const projectConfigPath = path19.join(process.cwd(), "node9.config.json");
7397
+ if (fs17.existsSync(projectConfigPath)) {
6919
7398
  try {
6920
- JSON.parse(fs16.readFileSync(projectConfigPath, "utf-8"));
7399
+ JSON.parse(fs17.readFileSync(projectConfigPath, "utf-8"));
6921
7400
  pass("node9.config.json found and valid (project)");
6922
7401
  } catch {
6923
7402
  fail(
@@ -6926,8 +7405,8 @@ function registerDoctorCommand(program2, version2) {
6926
7405
  );
6927
7406
  }
6928
7407
  }
6929
- const credsPath = path17.join(homeDir2, ".node9", "credentials.json");
6930
- if (fs16.existsSync(credsPath)) {
7408
+ const credsPath = path19.join(homeDir2, ".node9", "credentials.json");
7409
+ if (fs17.existsSync(credsPath)) {
6931
7410
  pass("Cloud credentials found (~/.node9/credentials.json)");
6932
7411
  } else {
6933
7412
  warn(
@@ -6936,10 +7415,10 @@ function registerDoctorCommand(program2, version2) {
6936
7415
  );
6937
7416
  }
6938
7417
  section("Agent Hooks");
6939
- const claudeSettingsPath = path17.join(homeDir2, ".claude", "settings.json");
6940
- if (fs16.existsSync(claudeSettingsPath)) {
7418
+ const claudeSettingsPath = path19.join(homeDir2, ".claude", "settings.json");
7419
+ if (fs17.existsSync(claudeSettingsPath)) {
6941
7420
  try {
6942
- const cs = JSON.parse(fs16.readFileSync(claudeSettingsPath, "utf-8"));
7421
+ const cs = JSON.parse(fs17.readFileSync(claudeSettingsPath, "utf-8"));
6943
7422
  const hasHook = cs.hooks?.PreToolUse?.some(
6944
7423
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
6945
7424
  );
@@ -6955,10 +7434,10 @@ function registerDoctorCommand(program2, version2) {
6955
7434
  } else {
6956
7435
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
6957
7436
  }
6958
- const geminiSettingsPath = path17.join(homeDir2, ".gemini", "settings.json");
6959
- if (fs16.existsSync(geminiSettingsPath)) {
7437
+ const geminiSettingsPath = path19.join(homeDir2, ".gemini", "settings.json");
7438
+ if (fs17.existsSync(geminiSettingsPath)) {
6960
7439
  try {
6961
- const gs = JSON.parse(fs16.readFileSync(geminiSettingsPath, "utf-8"));
7440
+ const gs = JSON.parse(fs17.readFileSync(geminiSettingsPath, "utf-8"));
6962
7441
  const hasHook = gs.hooks?.BeforeTool?.some(
6963
7442
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
6964
7443
  );
@@ -6974,10 +7453,10 @@ function registerDoctorCommand(program2, version2) {
6974
7453
  } else {
6975
7454
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
6976
7455
  }
6977
- const cursorHooksPath = path17.join(homeDir2, ".cursor", "hooks.json");
6978
- if (fs16.existsSync(cursorHooksPath)) {
7456
+ const cursorHooksPath = path19.join(homeDir2, ".cursor", "hooks.json");
7457
+ if (fs17.existsSync(cursorHooksPath)) {
6979
7458
  try {
6980
- const cur = JSON.parse(fs16.readFileSync(cursorHooksPath, "utf-8"));
7459
+ const cur = JSON.parse(fs17.readFileSync(cursorHooksPath, "utf-8"));
6981
7460
  const hasHook = cur.hooks?.preToolUse?.some(
6982
7461
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
6983
7462
  );
@@ -7015,9 +7494,9 @@ function registerDoctorCommand(program2, version2) {
7015
7494
 
7016
7495
  // src/cli/commands/audit.ts
7017
7496
  import chalk8 from "chalk";
7018
- import fs17 from "fs";
7019
- import path18 from "path";
7020
- import os15 from "os";
7497
+ import fs18 from "fs";
7498
+ import path20 from "path";
7499
+ import os16 from "os";
7021
7500
  function formatRelativeTime(timestamp) {
7022
7501
  const diff = Date.now() - new Date(timestamp).getTime();
7023
7502
  const sec = Math.floor(diff / 1e3);
@@ -7030,14 +7509,14 @@ function formatRelativeTime(timestamp) {
7030
7509
  }
7031
7510
  function registerAuditCommand(program2) {
7032
7511
  program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
7033
- const logPath = path18.join(os15.homedir(), ".node9", "audit.log");
7034
- if (!fs17.existsSync(logPath)) {
7512
+ const logPath = path20.join(os16.homedir(), ".node9", "audit.log");
7513
+ if (!fs18.existsSync(logPath)) {
7035
7514
  console.log(
7036
7515
  chalk8.yellow("No audit logs found. Run node9 with an agent to generate entries.")
7037
7516
  );
7038
7517
  return;
7039
7518
  }
7040
- const raw = fs17.readFileSync(logPath, "utf-8");
7519
+ const raw = fs18.readFileSync(logPath, "utf-8");
7041
7520
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
7042
7521
  let entries = lines.flatMap((line) => {
7043
7522
  try {
@@ -7157,9 +7636,9 @@ function registerDaemonCommand(program2) {
7157
7636
  init_core();
7158
7637
  init_daemon();
7159
7638
  import chalk10 from "chalk";
7160
- import fs18 from "fs";
7161
- import path19 from "path";
7162
- import os16 from "os";
7639
+ import fs19 from "fs";
7640
+ import path21 from "path";
7641
+ import os17 from "os";
7163
7642
  function registerStatusCommand(program2) {
7164
7643
  program2.command("status").description("Show current Node9 mode, policy source, and persistent decisions").action(() => {
7165
7644
  const creds = getCredentials();
@@ -7194,13 +7673,13 @@ function registerStatusCommand(program2) {
7194
7673
  console.log("");
7195
7674
  const modeLabel = settings.mode === "audit" ? chalk10.blue("audit") : settings.mode === "strict" ? chalk10.red("strict") : chalk10.white("standard");
7196
7675
  console.log(` Mode: ${modeLabel}`);
7197
- const projectConfig = path19.join(process.cwd(), "node9.config.json");
7198
- const globalConfig = path19.join(os16.homedir(), ".node9", "config.json");
7676
+ const projectConfig = path21.join(process.cwd(), "node9.config.json");
7677
+ const globalConfig = path21.join(os17.homedir(), ".node9", "config.json");
7199
7678
  console.log(
7200
- ` Local: ${fs18.existsSync(projectConfig) ? chalk10.green("Active (node9.config.json)") : chalk10.gray("Not present")}`
7679
+ ` Local: ${fs19.existsSync(projectConfig) ? chalk10.green("Active (node9.config.json)") : chalk10.gray("Not present")}`
7201
7680
  );
7202
7681
  console.log(
7203
- ` Global: ${fs18.existsSync(globalConfig) ? chalk10.green("Active (~/.node9/config.json)") : chalk10.gray("Not present")}`
7682
+ ` Global: ${fs19.existsSync(globalConfig) ? chalk10.green("Active (~/.node9/config.json)") : chalk10.gray("Not present")}`
7204
7683
  );
7205
7684
  if (mergedConfig.policy.sandboxPaths.length > 0) {
7206
7685
  console.log(
@@ -7379,6 +7858,7 @@ import readline2 from "readline";
7379
7858
  import chalk13 from "chalk";
7380
7859
  import { spawn as spawn8 } from "child_process";
7381
7860
  import { execa as execa2 } from "execa";
7861
+ init_provenance();
7382
7862
  function sanitize4(value) {
7383
7863
  return value.replace(/[\x00-\x1F\x7F]/g, "");
7384
7864
  }
@@ -7391,7 +7871,7 @@ function extractMcpServer(toolName) {
7391
7871
  const match = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
7392
7872
  return match?.[1];
7393
7873
  }
7394
- function tokenize2(cmd) {
7874
+ function tokenize3(cmd) {
7395
7875
  const tokens = [];
7396
7876
  let current = "";
7397
7877
  let inDouble = false;
@@ -7426,7 +7906,7 @@ function tokenize2(cmd) {
7426
7906
  return tokens;
7427
7907
  }
7428
7908
  async function runMcpGateway(upstreamCommand) {
7429
- const commandParts = tokenize2(upstreamCommand);
7909
+ const commandParts = tokenize3(upstreamCommand);
7430
7910
  const cmd = commandParts[0];
7431
7911
  const cmdArgs = commandParts.slice(1);
7432
7912
  let executable = cmd;
@@ -7435,6 +7915,15 @@ async function runMcpGateway(upstreamCommand) {
7435
7915
  if (stdout) executable = stdout.trim();
7436
7916
  } catch {
7437
7917
  }
7918
+ const prov = checkProvenance(executable);
7919
+ if (prov.trustLevel === "suspect") {
7920
+ console.error(
7921
+ chalk13.red(
7922
+ `\u26A0\uFE0F Node9: Upstream MCP server binary is suspect \u2014 ${prov.reason} (${prov.resolvedPath})`
7923
+ )
7924
+ );
7925
+ console.error(chalk13.red(" Verify this binary is trusted before proceeding."));
7926
+ }
7438
7927
  console.error(chalk13.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
7439
7928
  const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
7440
7929
  "NODE_OPTIONS",
@@ -7576,20 +8065,20 @@ function registerMcpGatewayCommand(program2) {
7576
8065
 
7577
8066
  // src/cli.ts
7578
8067
  var { version } = JSON.parse(
7579
- fs20.readFileSync(path21.join(__dirname, "../package.json"), "utf-8")
8068
+ fs21.readFileSync(path23.join(__dirname, "../package.json"), "utf-8")
7580
8069
  );
7581
8070
  var program = new Command();
7582
8071
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
7583
8072
  program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
7584
8073
  const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
7585
- const credPath = path21.join(os18.homedir(), ".node9", "credentials.json");
7586
- if (!fs20.existsSync(path21.dirname(credPath)))
7587
- fs20.mkdirSync(path21.dirname(credPath), { recursive: true });
8074
+ const credPath = path23.join(os19.homedir(), ".node9", "credentials.json");
8075
+ if (!fs21.existsSync(path23.dirname(credPath)))
8076
+ fs21.mkdirSync(path23.dirname(credPath), { recursive: true });
7588
8077
  const profileName = options.profile || "default";
7589
8078
  let existingCreds = {};
7590
8079
  try {
7591
- if (fs20.existsSync(credPath)) {
7592
- const raw = JSON.parse(fs20.readFileSync(credPath, "utf-8"));
8080
+ if (fs21.existsSync(credPath)) {
8081
+ const raw = JSON.parse(fs21.readFileSync(credPath, "utf-8"));
7593
8082
  if (raw.apiKey) {
7594
8083
  existingCreds = {
7595
8084
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
@@ -7601,13 +8090,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
7601
8090
  } catch {
7602
8091
  }
7603
8092
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
7604
- fs20.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
8093
+ fs21.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
7605
8094
  if (profileName === "default") {
7606
- const configPath = path21.join(os18.homedir(), ".node9", "config.json");
8095
+ const configPath = path23.join(os19.homedir(), ".node9", "config.json");
7607
8096
  let config = {};
7608
8097
  try {
7609
- if (fs20.existsSync(configPath))
7610
- config = JSON.parse(fs20.readFileSync(configPath, "utf-8"));
8098
+ if (fs21.existsSync(configPath))
8099
+ config = JSON.parse(fs21.readFileSync(configPath, "utf-8"));
7611
8100
  } catch {
7612
8101
  }
7613
8102
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -7622,9 +8111,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
7622
8111
  approvers.cloud = false;
7623
8112
  }
7624
8113
  s.approvers = approvers;
7625
- if (!fs20.existsSync(path21.dirname(configPath)))
7626
- fs20.mkdirSync(path21.dirname(configPath), { recursive: true });
7627
- fs20.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
8114
+ if (!fs21.existsSync(path23.dirname(configPath)))
8115
+ fs21.mkdirSync(path23.dirname(configPath), { recursive: true });
8116
+ fs21.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
7628
8117
  }
7629
8118
  if (options.profile && profileName !== "default") {
7630
8119
  console.log(chalk15.green(`\u2705 Profile "${profileName}" saved`));
@@ -7710,15 +8199,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
7710
8199
  }
7711
8200
  }
7712
8201
  if (options.purge) {
7713
- const node9Dir = path21.join(os18.homedir(), ".node9");
7714
- if (fs20.existsSync(node9Dir)) {
8202
+ const node9Dir = path23.join(os19.homedir(), ".node9");
8203
+ if (fs21.existsSync(node9Dir)) {
7715
8204
  const confirmed = await confirm3({
7716
8205
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
7717
8206
  default: false
7718
8207
  });
7719
8208
  if (confirmed) {
7720
- fs20.rmSync(node9Dir, { recursive: true });
7721
- if (fs20.existsSync(node9Dir)) {
8209
+ fs21.rmSync(node9Dir, { recursive: true });
8210
+ if (fs21.existsSync(node9Dir)) {
7722
8211
  console.error(
7723
8212
  chalk15.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
7724
8213
  );
@@ -7818,8 +8307,8 @@ program.command("explain").description(
7818
8307
  console.log("");
7819
8308
  });
7820
8309
  program.command("init").description("Create ~/.node9/config.json with default policy (safe to run multiple times)").option("--force", "Overwrite existing config").option("-m, --mode <mode>", "Set initial security mode (standard, strict, audit)", "standard").action((options) => {
7821
- const configPath = path21.join(os18.homedir(), ".node9", "config.json");
7822
- if (fs20.existsSync(configPath) && !options.force) {
8310
+ const configPath = path23.join(os19.homedir(), ".node9", "config.json");
8311
+ if (fs21.existsSync(configPath) && !options.force) {
7823
8312
  console.log(chalk15.yellow(`\u2139\uFE0F Global config already exists: ${configPath}`));
7824
8313
  console.log(chalk15.gray(` Run with --force to overwrite.`));
7825
8314
  return;
@@ -7833,9 +8322,9 @@ program.command("init").description("Create ~/.node9/config.json with default po
7833
8322
  mode: safeMode
7834
8323
  }
7835
8324
  };
7836
- const dir = path21.dirname(configPath);
7837
- if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
7838
- fs20.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
8325
+ const dir = path23.dirname(configPath);
8326
+ if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
8327
+ fs21.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
7839
8328
  console.log(chalk15.green(`\u2705 Global config created: ${configPath}`));
7840
8329
  console.log(chalk15.cyan(` Mode set to: ${safeMode}`));
7841
8330
  console.log(
@@ -7953,9 +8442,9 @@ if (process.argv[2] !== "daemon") {
7953
8442
  const isCheckHook = process.argv[2] === "check";
7954
8443
  if (isCheckHook) {
7955
8444
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
7956
- const logPath = path21.join(os18.homedir(), ".node9", "hook-debug.log");
8445
+ const logPath = path23.join(os19.homedir(), ".node9", "hook-debug.log");
7957
8446
  const msg = reason instanceof Error ? reason.message : String(reason);
7958
- fs20.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
8447
+ fs21.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
7959
8448
  `);
7960
8449
  }
7961
8450
  process.exit(0);