@node9/proxy 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -114,8 +114,8 @@ function sanitizeConfig(raw) {
114
114
  }
115
115
  }
116
116
  const lines = result.error.issues.map((issue) => {
117
- const path22 = issue.path.length > 0 ? issue.path.join(".") : "root";
118
- return ` \u2022 ${path22}: ${issue.message}`;
117
+ const path25 = issue.path.length > 0 ? issue.path.join(".") : "root";
118
+ return ` \u2022 ${path25}: ${issue.message}`;
119
119
  });
120
120
  return {
121
121
  sanitized,
@@ -1178,8 +1178,497 @@ var init_dlp = __esm({
1178
1178
  }
1179
1179
  });
1180
1180
 
1181
+ // src/utils/provenance.ts
1182
+ function findInPath(cmd) {
1183
+ if (import_path5.default.posix.isAbsolute(cmd)) return cmd;
1184
+ const pathEnv = process.env.PATH ?? "";
1185
+ for (const dir of pathEnv.split(import_path5.default.delimiter)) {
1186
+ if (!dir) continue;
1187
+ const full = import_path5.default.join(dir, cmd);
1188
+ try {
1189
+ import_fs5.default.accessSync(full, import_fs5.default.constants.X_OK);
1190
+ return full;
1191
+ } catch {
1192
+ }
1193
+ }
1194
+ return null;
1195
+ }
1196
+ function _classifyPath(resolved, cwd) {
1197
+ if (cwd && resolved.startsWith(cwd + "/")) {
1198
+ return { trustLevel: "user", reason: "binary in project directory" };
1199
+ }
1200
+ const osTmp = import_os4.default.tmpdir();
1201
+ const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
1202
+ if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1203
+ return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
1204
+ }
1205
+ if (SYSTEM_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1206
+ return { trustLevel: "system", reason: "" };
1207
+ }
1208
+ if (MANAGED_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1209
+ return { trustLevel: "managed", reason: "" };
1210
+ }
1211
+ if (USER_PREFIXES.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
1212
+ return { trustLevel: "user", reason: "" };
1213
+ }
1214
+ return { trustLevel: "unknown", reason: "binary in unrecognized location" };
1215
+ }
1216
+ function checkProvenance(cmd, cwd) {
1217
+ const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
1218
+ if (import_path5.default.posix.isAbsolute(bare)) {
1219
+ const early = _classifyPath(bare, cwd);
1220
+ if (early.trustLevel === "suspect") {
1221
+ return { resolvedPath: bare, ...early };
1222
+ }
1223
+ }
1224
+ let resolved;
1225
+ try {
1226
+ const found = findInPath(bare);
1227
+ if (!found) {
1228
+ return {
1229
+ resolvedPath: cmd,
1230
+ trustLevel: "unknown",
1231
+ reason: "binary not found in PATH"
1232
+ };
1233
+ }
1234
+ resolved = import_fs5.default.realpathSync(found);
1235
+ } catch {
1236
+ return {
1237
+ resolvedPath: cmd,
1238
+ trustLevel: "unknown",
1239
+ reason: "binary not found in PATH"
1240
+ };
1241
+ }
1242
+ try {
1243
+ const stat = import_fs5.default.statSync(resolved);
1244
+ if (stat.mode & 2) {
1245
+ return {
1246
+ resolvedPath: resolved,
1247
+ trustLevel: "suspect",
1248
+ reason: "binary is world-writable"
1249
+ };
1250
+ }
1251
+ } catch {
1252
+ return {
1253
+ resolvedPath: resolved,
1254
+ trustLevel: "unknown",
1255
+ reason: "could not stat binary"
1256
+ };
1257
+ }
1258
+ const classify = _classifyPath(resolved, cwd);
1259
+ return { resolvedPath: resolved, ...classify };
1260
+ }
1261
+ var import_fs5, import_path5, import_os4, SYSTEM_PREFIXES, MANAGED_PREFIXES, USER_PREFIXES, SUSPECT_PREFIXES;
1262
+ var init_provenance = __esm({
1263
+ "src/utils/provenance.ts"() {
1264
+ "use strict";
1265
+ import_fs5 = __toESM(require("fs"));
1266
+ import_path5 = __toESM(require("path"));
1267
+ import_os4 = __toESM(require("os"));
1268
+ SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
1269
+ MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
1270
+ USER_PREFIXES = [
1271
+ import_path5.default.join(import_os4.default.homedir(), "bin"),
1272
+ import_path5.default.join(import_os4.default.homedir(), ".local", "bin"),
1273
+ import_path5.default.join(import_os4.default.homedir(), ".cargo", "bin"),
1274
+ import_path5.default.join(import_os4.default.homedir(), ".npm-global", "bin"),
1275
+ import_path5.default.join(import_os4.default.homedir(), ".volta", "bin")
1276
+ ];
1277
+ SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
1278
+ }
1279
+ });
1280
+
1281
+ // src/policy/pipe-chain.ts
1282
+ function isSensitivePath(p) {
1283
+ return SENSITIVE_PATTERNS.some((re) => re.test(p));
1284
+ }
1285
+ function splitOnPipe(cmd) {
1286
+ const segments = [];
1287
+ let current = "";
1288
+ let inSingle = false;
1289
+ let inDouble = false;
1290
+ for (let i = 0; i < cmd.length; i++) {
1291
+ const ch = cmd[i];
1292
+ if (ch === "'" && !inDouble) {
1293
+ inSingle = !inSingle;
1294
+ current += ch;
1295
+ } else if (ch === '"' && !inSingle) {
1296
+ inDouble = !inDouble;
1297
+ current += ch;
1298
+ } else if (ch === "|" && !inSingle && !inDouble && cmd[i + 1] !== "|" && (i === 0 || cmd[i - 1] !== "|")) {
1299
+ segments.push(current.trim());
1300
+ current = "";
1301
+ } else {
1302
+ current += ch;
1303
+ }
1304
+ }
1305
+ if (current.trim()) segments.push(current.trim());
1306
+ return segments.filter(Boolean);
1307
+ }
1308
+ function positionalTokens(segment) {
1309
+ return segment.split(/\s+/).slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("@") && t.length > 0);
1310
+ }
1311
+ function analyzePipeChain(command) {
1312
+ const segments = splitOnPipe(command);
1313
+ if (segments.length < 2) {
1314
+ return {
1315
+ isPipeline: false,
1316
+ hasSensitiveSource: false,
1317
+ hasExternalSink: false,
1318
+ hasObfuscation: false,
1319
+ sourceFiles: [],
1320
+ sinkTargets: [],
1321
+ risk: "none"
1322
+ };
1323
+ }
1324
+ const sourceFiles = [];
1325
+ const sinkTargets = [];
1326
+ let hasSensitiveSource = false;
1327
+ let hasExternalSink = false;
1328
+ let hasObfuscation = false;
1329
+ for (const segment of segments) {
1330
+ const tokens = segment.split(/\s+/).filter(Boolean);
1331
+ if (tokens.length === 0) continue;
1332
+ const binary = tokens[0].toLowerCase();
1333
+ const args = positionalTokens(segment);
1334
+ if (SOURCE_COMMANDS.has(binary)) {
1335
+ sourceFiles.push(...args);
1336
+ if (args.some(isSensitivePath)) hasSensitiveSource = true;
1337
+ }
1338
+ if (OBFUSCATORS.has(binary)) hasObfuscation = true;
1339
+ if (SINK_COMMANDS.has(binary)) {
1340
+ const targets = args.filter(
1341
+ (a) => a.includes(".") || a.includes("://") || /^\d+\.\d+/.test(a)
1342
+ );
1343
+ sinkTargets.push(...targets);
1344
+ if (targets.length > 0) hasExternalSink = true;
1345
+ }
1346
+ }
1347
+ const fullCmd = command.toLowerCase();
1348
+ if (!hasSensitiveSource) {
1349
+ const redirMatch = fullCmd.match(/<\s*(\S+)/);
1350
+ if (redirMatch && isSensitivePath(redirMatch[1])) {
1351
+ hasSensitiveSource = true;
1352
+ sourceFiles.push(redirMatch[1]);
1353
+ }
1354
+ }
1355
+ const risk = hasSensitiveSource && hasExternalSink && hasObfuscation ? "critical" : hasSensitiveSource && hasExternalSink ? "high" : hasExternalSink ? "medium" : "none";
1356
+ return {
1357
+ isPipeline: true,
1358
+ hasSensitiveSource,
1359
+ hasExternalSink,
1360
+ hasObfuscation,
1361
+ sourceFiles,
1362
+ sinkTargets,
1363
+ risk
1364
+ };
1365
+ }
1366
+ var SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS;
1367
+ var init_pipe_chain = __esm({
1368
+ "src/policy/pipe-chain.ts"() {
1369
+ "use strict";
1370
+ SOURCE_COMMANDS = /* @__PURE__ */ new Set([
1371
+ "cat",
1372
+ "head",
1373
+ "tail",
1374
+ "grep",
1375
+ "awk",
1376
+ "sed",
1377
+ "cut",
1378
+ "sort",
1379
+ "tee",
1380
+ "less",
1381
+ "more",
1382
+ "strings",
1383
+ "xxd"
1384
+ ]);
1385
+ SINK_COMMANDS = /* @__PURE__ */ new Set([
1386
+ "curl",
1387
+ "wget",
1388
+ "nc",
1389
+ "ncat",
1390
+ "netcat",
1391
+ "ssh",
1392
+ "scp",
1393
+ "rsync",
1394
+ "socat",
1395
+ "ftp",
1396
+ "sftp",
1397
+ "telnet"
1398
+ ]);
1399
+ OBFUSCATORS = /* @__PURE__ */ new Set([
1400
+ "base64",
1401
+ "gzip",
1402
+ "gunzip",
1403
+ "bzip2",
1404
+ "xz",
1405
+ "zstd",
1406
+ "openssl",
1407
+ "gpg",
1408
+ "python",
1409
+ "python3",
1410
+ "perl",
1411
+ "ruby",
1412
+ "node"
1413
+ ]);
1414
+ SENSITIVE_PATTERNS = [
1415
+ /(?:^|\/)\.env(?:\.|$)/i,
1416
+ // .env, .env.local, .env.production
1417
+ /id_rsa|id_ed25519|id_ecdsa|id_dsa/i,
1418
+ // SSH private keys
1419
+ /\.pem$|\.key$|\.p12$|\.pfx$/i,
1420
+ // certificate files
1421
+ /(?:^|\/)\.ssh\//i,
1422
+ // ~/.ssh/ directory
1423
+ /(?:^|\/)\.aws\/credentials/i,
1424
+ // AWS credentials
1425
+ /(?:^|\/)\.netrc$/i,
1426
+ // netrc (stores HTTP credentials)
1427
+ /(?:^|\/)(passwd|shadow|sudoers)$/i,
1428
+ // /etc/passwd, /etc/shadow
1429
+ /(?:^|\/)credentials(?:\.json)?$/i
1430
+ // generic credentials files
1431
+ ];
1432
+ }
1433
+ });
1434
+
1435
+ // src/policy/flag-tables.ts
1436
+ function extractPositionalArgs(tokens, binary) {
1437
+ const binaryName = import_path6.default.basename(binary).replace(/\.exe$/i, "");
1438
+ const flagsWithValues = FLAGS_WITH_VALUES[binaryName] ?? /* @__PURE__ */ new Set();
1439
+ const positional = [];
1440
+ let skipNext = false;
1441
+ for (const token of tokens) {
1442
+ if (skipNext) {
1443
+ skipNext = false;
1444
+ continue;
1445
+ }
1446
+ if (token.startsWith("--") && token.includes("=")) continue;
1447
+ if (token.startsWith("-") && token.length === 2 && flagsWithValues.has(token)) {
1448
+ skipNext = true;
1449
+ continue;
1450
+ }
1451
+ if (token.startsWith("--") && flagsWithValues.has(token)) {
1452
+ skipNext = true;
1453
+ continue;
1454
+ }
1455
+ const shortFlag = token.slice(0, 2);
1456
+ if (token.startsWith("-") && token.length > 2 && flagsWithValues.has(shortFlag)) continue;
1457
+ if (token.startsWith("-")) continue;
1458
+ if (token.startsWith("@")) continue;
1459
+ positional.push(token);
1460
+ }
1461
+ return positional;
1462
+ }
1463
+ function extractNetworkTargets(tokens, binary) {
1464
+ return extractPositionalArgs(tokens, binary).map((t) => t.includes("@") ? t.split("@")[1] : t).map((t) => {
1465
+ const colonIdx = t.indexOf(":");
1466
+ if (colonIdx === -1) return t;
1467
+ const afterColon = t.slice(colonIdx + 1);
1468
+ if (/^\d+$/.test(afterColon)) return t.slice(0, colonIdx);
1469
+ return t;
1470
+ }).filter(Boolean);
1471
+ }
1472
+ var import_path6, FLAGS_WITH_VALUES;
1473
+ var init_flag_tables = __esm({
1474
+ "src/policy/flag-tables.ts"() {
1475
+ "use strict";
1476
+ import_path6 = __toESM(require("path"));
1477
+ FLAGS_WITH_VALUES = {
1478
+ curl: /* @__PURE__ */ new Set([
1479
+ "-H",
1480
+ "--header",
1481
+ "-A",
1482
+ "--user-agent",
1483
+ "-e",
1484
+ "--referer",
1485
+ "-x",
1486
+ "--proxy",
1487
+ "-u",
1488
+ "--user",
1489
+ "-d",
1490
+ "--data",
1491
+ "--data-raw",
1492
+ "--data-binary",
1493
+ "-o",
1494
+ "--output",
1495
+ "-F",
1496
+ "--form",
1497
+ "--connect-to",
1498
+ "--resolve",
1499
+ "--cacert",
1500
+ "--cert",
1501
+ "--key",
1502
+ "-m",
1503
+ "--max-time"
1504
+ ]),
1505
+ wget: /* @__PURE__ */ new Set([
1506
+ "-O",
1507
+ "--output-document",
1508
+ "-P",
1509
+ "--directory-prefix",
1510
+ "-U",
1511
+ "--user-agent",
1512
+ "-e",
1513
+ "--execute",
1514
+ "--proxy",
1515
+ "--ca-certificate"
1516
+ ]),
1517
+ nc: /* @__PURE__ */ new Set(["-x", "-p", "-s", "-w", "-W", "-I", "-O"]),
1518
+ ncat: /* @__PURE__ */ new Set(["-x", "-p", "-s", "--proxy", "--proxy-auth", "-w", "--wait"]),
1519
+ netcat: /* @__PURE__ */ new Set(["-x", "-p", "-s", "-w"]),
1520
+ ssh: /* @__PURE__ */ new Set([
1521
+ "-i",
1522
+ "-l",
1523
+ "-p",
1524
+ "-o",
1525
+ "-E",
1526
+ "-F",
1527
+ "-J",
1528
+ "-L",
1529
+ "-R",
1530
+ "-W",
1531
+ "-b",
1532
+ "-c",
1533
+ "-D",
1534
+ "-e",
1535
+ "-I",
1536
+ "-S"
1537
+ ]),
1538
+ scp: /* @__PURE__ */ new Set(["-i", "-o", "-P", "-S"]),
1539
+ rsync: /* @__PURE__ */ new Set(["-e", "--rsh", "--rsync-path", "--password-file", "--log-file"]),
1540
+ socat: /* @__PURE__ */ new Set([])
1541
+ // socat uses address syntax, not flags — no value-flags
1542
+ };
1543
+ }
1544
+ });
1545
+
1546
+ // src/policy/ssh-parser.ts
1547
+ function tokenize(cmd) {
1548
+ const tokens = [];
1549
+ let current = "";
1550
+ let inSingle = false;
1551
+ let inDouble = false;
1552
+ for (const ch of cmd) {
1553
+ if (ch === "'" && !inDouble) {
1554
+ inSingle = !inSingle;
1555
+ } else if (ch === '"' && !inSingle) {
1556
+ inDouble = !inDouble;
1557
+ } else if ((ch === " " || ch === " ") && !inSingle && !inDouble) {
1558
+ if (current) {
1559
+ tokens.push(current);
1560
+ current = "";
1561
+ }
1562
+ } else {
1563
+ current += ch;
1564
+ }
1565
+ }
1566
+ if (current) tokens.push(current);
1567
+ return tokens;
1568
+ }
1569
+ function parseHost(raw) {
1570
+ return raw.split("@").pop().split(":")[0];
1571
+ }
1572
+ function extractAllSshHosts(tokens) {
1573
+ const hosts = /* @__PURE__ */ new Set();
1574
+ for (let i = 0; i < tokens.length; i++) {
1575
+ const t = tokens[i];
1576
+ if (t === "-J" && tokens[i + 1]) {
1577
+ for (const hop of tokens[++i].split(",")) {
1578
+ const h = parseHost(hop);
1579
+ if (h) hosts.add(h);
1580
+ }
1581
+ continue;
1582
+ }
1583
+ if (t === "-o" && tokens[i + 1]?.toLowerCase().startsWith("proxyjump=")) {
1584
+ const val = tokens[++i].split("=").slice(1).join("=");
1585
+ for (const hop of val.split(",")) {
1586
+ const h = parseHost(hop);
1587
+ if (h) hosts.add(h);
1588
+ }
1589
+ continue;
1590
+ }
1591
+ if (t === "-o" && tokens[i + 1]?.toLowerCase().startsWith("proxycommand=")) {
1592
+ const raw = tokens[++i].split("=").slice(1).join("=").replace(/^['"]|['"]$/g, "");
1593
+ const subTokens = tokenize(raw);
1594
+ const binary = subTokens[0] ?? "";
1595
+ extractNetworkTargets(subTokens.slice(1), binary).forEach((h) => hosts.add(h));
1596
+ extractAllSshHosts(subTokens.slice(1)).forEach((h) => hosts.add(h));
1597
+ continue;
1598
+ }
1599
+ if (!t.startsWith("-")) {
1600
+ const h = parseHost(t);
1601
+ if (h) hosts.add(h);
1602
+ }
1603
+ }
1604
+ return [...hosts].filter(Boolean);
1605
+ }
1606
+ var init_ssh_parser = __esm({
1607
+ "src/policy/ssh-parser.ts"() {
1608
+ "use strict";
1609
+ init_flag_tables();
1610
+ }
1611
+ });
1612
+
1613
+ // src/auth/trusted-hosts.ts
1614
+ function getTrustedHostsPath() {
1615
+ return import_path7.default.join(import_os5.default.homedir(), ".node9", "trusted-hosts.json");
1616
+ }
1617
+ function readTrustedHosts() {
1618
+ try {
1619
+ const raw = import_fs6.default.readFileSync(getTrustedHostsPath(), "utf8");
1620
+ const parsed = JSON.parse(raw);
1621
+ return Array.isArray(parsed.hosts) ? parsed.hosts : [];
1622
+ } catch {
1623
+ return [];
1624
+ }
1625
+ }
1626
+ function writeTrustedHosts(hosts) {
1627
+ const filePath = getTrustedHostsPath();
1628
+ import_fs6.default.mkdirSync(import_path7.default.dirname(filePath), { recursive: true });
1629
+ const tmp = filePath + ".node9-tmp";
1630
+ import_fs6.default.writeFileSync(tmp, JSON.stringify({ hosts }, null, 2));
1631
+ import_fs6.default.renameSync(tmp, filePath);
1632
+ }
1633
+ function addTrustedHost(host) {
1634
+ const hosts = readTrustedHosts();
1635
+ if (hosts.some((h) => h.host === host)) return;
1636
+ hosts.push({ host, addedAt: Date.now(), addedBy: "user" });
1637
+ writeTrustedHosts(hosts);
1638
+ }
1639
+ function removeTrustedHost(host) {
1640
+ const hosts = readTrustedHosts();
1641
+ const filtered = hosts.filter((h) => h.host !== host);
1642
+ if (filtered.length === hosts.length) return false;
1643
+ writeTrustedHosts(filtered);
1644
+ return true;
1645
+ }
1646
+ function normalizeHost(raw) {
1647
+ return raw.toLowerCase().replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^[^@]+@/, "").replace(/:\d+$/, "");
1648
+ }
1649
+ function isTrustedHost(host) {
1650
+ const normalized = normalizeHost(host);
1651
+ return readTrustedHosts().some((entry) => {
1652
+ const entryHost = entry.host.toLowerCase();
1653
+ if (entryHost.startsWith("*.")) {
1654
+ const domain = entryHost.slice(2);
1655
+ return normalized === domain || normalized.endsWith("." + domain);
1656
+ }
1657
+ return normalized === entryHost;
1658
+ });
1659
+ }
1660
+ var import_fs6, import_path7, import_os5;
1661
+ var init_trusted_hosts = __esm({
1662
+ "src/auth/trusted-hosts.ts"() {
1663
+ "use strict";
1664
+ import_fs6 = __toESM(require("fs"));
1665
+ import_path7 = __toESM(require("path"));
1666
+ import_os5 = __toESM(require("os"));
1667
+ }
1668
+ });
1669
+
1181
1670
  // src/policy/index.ts
1182
- function tokenize(toolName) {
1671
+ function tokenize2(toolName) {
1183
1672
  return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
1184
1673
  }
1185
1674
  function matchesPattern(text, patterns) {
@@ -1192,9 +1681,9 @@ function matchesPattern(text, patterns) {
1192
1681
  const withoutDotSlash = text.replace(/^\.\//, "");
1193
1682
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1194
1683
  }
1195
- function getNestedValue(obj, path22) {
1684
+ function getNestedValue(obj, path25) {
1196
1685
  if (!obj || typeof obj !== "object") return null;
1197
- return path22.split(".").reduce((prev, curr) => prev?.[curr], obj);
1686
+ return path25.split(".").reduce((prev, curr) => prev?.[curr], obj);
1198
1687
  }
1199
1688
  function shouldSnapshot(toolName, args, config) {
1200
1689
  if (!config.settings.enableUndo) return false;
@@ -1329,7 +1818,7 @@ async function analyzeShellCommand(command) {
1329
1818
  }
1330
1819
  return { actions, paths, allTokens };
1331
1820
  }
1332
- async function evaluatePolicy(toolName, args, agent) {
1821
+ async function evaluatePolicy(toolName, args, agent, cwd) {
1333
1822
  const config = getConfig();
1334
1823
  if (matchesPattern(toolName, config.policy.ignoredTools)) return { decision: "allow" };
1335
1824
  if (config.policy.smartRules.length > 0) {
@@ -1359,11 +1848,66 @@ async function evaluatePolicy(toolName, args, agent) {
1359
1848
  if (INLINE_EXEC_PATTERN.test(shellCommand.trim())) {
1360
1849
  return { decision: "review", blockedByLabel: "Node9 Standard (Inline Execution)", tier: 3 };
1361
1850
  }
1851
+ const pipeAnalysis = analyzePipeChain(shellCommand);
1852
+ if (pipeAnalysis.isPipeline && (pipeAnalysis.risk === "critical" || pipeAnalysis.risk === "high")) {
1853
+ const sinks = pipeAnalysis.sinkTargets;
1854
+ const allTrusted = sinks.length > 0 && sinks.every(isTrustedHost);
1855
+ if (pipeAnalysis.risk === "critical") {
1856
+ if (allTrusted) {
1857
+ return {
1858
+ decision: "review",
1859
+ blockedByLabel: "Node9: Pipe-Chain to Trusted Host (obfuscated)",
1860
+ reason: `Obfuscated pipe to trusted host(s): ${sinks.join(", ")} \u2014 requires approval`,
1861
+ tier: 3
1862
+ };
1863
+ }
1864
+ return {
1865
+ decision: "block",
1866
+ blockedByLabel: "Node9: Pipe-Chain Exfiltration (critical)",
1867
+ reason: `Sensitive file piped through obfuscator to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${sinks.join(", ")}`,
1868
+ tier: 3
1869
+ };
1870
+ }
1871
+ if (allTrusted) {
1872
+ return { decision: "allow" };
1873
+ }
1874
+ return {
1875
+ decision: "review",
1876
+ blockedByLabel: "Node9: Pipe-Chain Exfiltration (high)",
1877
+ reason: `Sensitive file piped to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${sinks.join(", ")}`,
1878
+ tier: 3
1879
+ };
1880
+ }
1881
+ const firstToken = analyzed.actions[0] ?? "";
1882
+ if (["ssh", "scp", "rsync"].includes(firstToken)) {
1883
+ const rawTokens = shellCommand.trim().split(/\s+/);
1884
+ const sshHosts = extractAllSshHosts(rawTokens.slice(1));
1885
+ allTokens.push(...sshHosts);
1886
+ }
1887
+ if (firstToken && import_path8.default.posix.isAbsolute(firstToken)) {
1888
+ const prov = checkProvenance(firstToken, cwd);
1889
+ if (prov.trustLevel === "suspect") {
1890
+ return {
1891
+ decision: config.settings.mode === "strict" ? "block" : "review",
1892
+ blockedByLabel: "Node9: Suspect Binary",
1893
+ reason: `Binary "${firstToken}" resolved to ${prov.resolvedPath} \u2014 ${prov.reason}`,
1894
+ tier: 3
1895
+ };
1896
+ }
1897
+ if (prov.trustLevel === "unknown" && config.settings.mode === "strict") {
1898
+ return {
1899
+ decision: "review",
1900
+ blockedByLabel: "Node9: Unknown Binary (strict mode)",
1901
+ reason: `Binary "${firstToken}" \u2014 ${prov.reason}`,
1902
+ tier: 3
1903
+ };
1904
+ }
1905
+ }
1362
1906
  if (isSqlTool(toolName, config.policy.toolInspection)) {
1363
1907
  allTokens = allTokens.filter((t) => !SQL_DML_KEYWORDS.has(t.toLowerCase()));
1364
1908
  }
1365
1909
  } else {
1366
- allTokens = tokenize(toolName);
1910
+ allTokens = tokenize2(toolName);
1367
1911
  if (args && typeof args === "object") {
1368
1912
  const flattenedArgs = JSON.stringify(args).toLowerCase();
1369
1913
  const extraTokens = flattenedArgs.split(/[^a-zA-Z0-9]+/).filter((t) => t.length > 1);
@@ -1437,9 +1981,9 @@ async function evaluatePolicy(toolName, args, agent) {
1437
1981
  }
1438
1982
  async function explainPolicy(toolName, args) {
1439
1983
  const steps = [];
1440
- const globalPath = import_path5.default.join(import_os4.default.homedir(), ".node9", "config.json");
1441
- const projectPath = import_path5.default.join(process.cwd(), "node9.config.json");
1442
- const credsPath = import_path5.default.join(import_os4.default.homedir(), ".node9", "credentials.json");
1984
+ const globalPath = import_path8.default.join(import_os6.default.homedir(), ".node9", "config.json");
1985
+ const projectPath = import_path8.default.join(process.cwd(), "node9.config.json");
1986
+ const credsPath = import_path8.default.join(import_os6.default.homedir(), ".node9", "credentials.json");
1443
1987
  const waterfall = [
1444
1988
  {
1445
1989
  tier: 1,
@@ -1450,19 +1994,19 @@ async function explainPolicy(toolName, args) {
1450
1994
  {
1451
1995
  tier: 2,
1452
1996
  label: "Cloud policy",
1453
- status: import_fs5.default.existsSync(credsPath) ? "active" : "missing",
1454
- note: import_fs5.default.existsSync(credsPath) ? "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
1997
+ status: import_fs7.default.existsSync(credsPath) ? "active" : "missing",
1998
+ note: import_fs7.default.existsSync(credsPath) ? "credentials found (not evaluated in explain mode)" : "not connected \u2014 run: node9 login"
1455
1999
  },
1456
2000
  {
1457
2001
  tier: 3,
1458
2002
  label: "Project config",
1459
- status: import_fs5.default.existsSync(projectPath) ? "active" : "missing",
2003
+ status: import_fs7.default.existsSync(projectPath) ? "active" : "missing",
1460
2004
  path: projectPath
1461
2005
  },
1462
2006
  {
1463
2007
  tier: 4,
1464
2008
  label: "Global config",
1465
- status: import_fs5.default.existsSync(globalPath) ? "active" : "missing",
2009
+ status: import_fs7.default.existsSync(globalPath) ? "active" : "missing",
1466
2010
  path: globalPath
1467
2011
  },
1468
2012
  {
@@ -1594,7 +2138,7 @@ async function explainPolicy(toolName, args) {
1594
2138
  });
1595
2139
  }
1596
2140
  } else {
1597
- allTokens = tokenize(toolName);
2141
+ allTokens = tokenize2(toolName);
1598
2142
  let detail = `No toolInspection match for "${toolName}" \u2014 tokens: [${allTokens.join(", ")}]`;
1599
2143
  if (args && typeof args === "object") {
1600
2144
  const flattenedArgs = JSON.stringify(args).toLowerCase();
@@ -1699,18 +2243,22 @@ function isIgnoredTool(toolName) {
1699
2243
  const config = getConfig();
1700
2244
  return matchesPattern(toolName, config.policy.ignoredTools);
1701
2245
  }
1702
- var import_fs5, import_path5, import_os4, import_picomatch, import_sh_syntax, SQL_DML_KEYWORDS;
2246
+ var import_fs7, import_path8, import_os6, import_picomatch, import_sh_syntax, SQL_DML_KEYWORDS;
1703
2247
  var init_policy = __esm({
1704
2248
  "src/policy/index.ts"() {
1705
2249
  "use strict";
1706
- import_fs5 = __toESM(require("fs"));
1707
- import_path5 = __toESM(require("path"));
1708
- import_os4 = __toESM(require("os"));
2250
+ import_fs7 = __toESM(require("fs"));
2251
+ import_path8 = __toESM(require("path"));
2252
+ import_os6 = __toESM(require("os"));
1709
2253
  import_picomatch = __toESM(require("picomatch"));
1710
2254
  import_sh_syntax = require("sh-syntax");
1711
2255
  init_dlp();
1712
2256
  init_config();
1713
2257
  init_regex();
2258
+ init_provenance();
2259
+ init_pipe_chain();
2260
+ init_ssh_parser();
2261
+ init_trusted_hosts();
1714
2262
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
1715
2263
  }
1716
2264
  });
@@ -1718,11 +2266,11 @@ var init_policy = __esm({
1718
2266
  // src/auth/state.ts
1719
2267
  function checkPause() {
1720
2268
  try {
1721
- if (!import_fs6.default.existsSync(PAUSED_FILE)) return { paused: false };
1722
- const state = JSON.parse(import_fs6.default.readFileSync(PAUSED_FILE, "utf-8"));
2269
+ if (!import_fs8.default.existsSync(PAUSED_FILE)) return { paused: false };
2270
+ const state = JSON.parse(import_fs8.default.readFileSync(PAUSED_FILE, "utf-8"));
1723
2271
  if (state.expiry > 0 && Date.now() >= state.expiry) {
1724
2272
  try {
1725
- import_fs6.default.unlinkSync(PAUSED_FILE);
2273
+ import_fs8.default.unlinkSync(PAUSED_FILE);
1726
2274
  } catch {
1727
2275
  }
1728
2276
  return { paused: false };
@@ -1733,11 +2281,11 @@ function checkPause() {
1733
2281
  }
1734
2282
  }
1735
2283
  function atomicWriteSync(filePath, data, options) {
1736
- const dir = import_path6.default.dirname(filePath);
1737
- if (!import_fs6.default.existsSync(dir)) import_fs6.default.mkdirSync(dir, { recursive: true });
1738
- const tmpPath = `${filePath}.${import_os5.default.hostname()}.${process.pid}.tmp`;
1739
- import_fs6.default.writeFileSync(tmpPath, data, options);
1740
- import_fs6.default.renameSync(tmpPath, filePath);
2284
+ const dir = import_path9.default.dirname(filePath);
2285
+ if (!import_fs8.default.existsSync(dir)) import_fs8.default.mkdirSync(dir, { recursive: true });
2286
+ const tmpPath = `${filePath}.${import_os7.default.hostname()}.${process.pid}.tmp`;
2287
+ import_fs8.default.writeFileSync(tmpPath, data, options);
2288
+ import_fs8.default.renameSync(tmpPath, filePath);
1741
2289
  }
1742
2290
  function pauseNode9(durationMs, durationStr) {
1743
2291
  const state = { expiry: Date.now() + durationMs, duration: durationStr };
@@ -1745,18 +2293,18 @@ function pauseNode9(durationMs, durationStr) {
1745
2293
  }
1746
2294
  function resumeNode9() {
1747
2295
  try {
1748
- if (import_fs6.default.existsSync(PAUSED_FILE)) import_fs6.default.unlinkSync(PAUSED_FILE);
2296
+ if (import_fs8.default.existsSync(PAUSED_FILE)) import_fs8.default.unlinkSync(PAUSED_FILE);
1749
2297
  } catch {
1750
2298
  }
1751
2299
  }
1752
2300
  function getActiveTrustSession(toolName) {
1753
2301
  try {
1754
- if (!import_fs6.default.existsSync(TRUST_FILE)) return false;
1755
- const trust = JSON.parse(import_fs6.default.readFileSync(TRUST_FILE, "utf-8"));
2302
+ if (!import_fs8.default.existsSync(TRUST_FILE)) return false;
2303
+ const trust = JSON.parse(import_fs8.default.readFileSync(TRUST_FILE, "utf-8"));
1756
2304
  const now = Date.now();
1757
2305
  const active = trust.entries.filter((e) => e.expiry > now);
1758
2306
  if (active.length !== trust.entries.length) {
1759
- import_fs6.default.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
2307
+ import_fs8.default.writeFileSync(TRUST_FILE, JSON.stringify({ entries: active }, null, 2));
1760
2308
  }
1761
2309
  return active.some((e) => e.tool === toolName || matchesPattern(toolName, e.tool));
1762
2310
  } catch {
@@ -1767,8 +2315,8 @@ function writeTrustSession(toolName, durationMs) {
1767
2315
  try {
1768
2316
  let trust = { entries: [] };
1769
2317
  try {
1770
- if (import_fs6.default.existsSync(TRUST_FILE)) {
1771
- trust = JSON.parse(import_fs6.default.readFileSync(TRUST_FILE, "utf-8"));
2318
+ if (import_fs8.default.existsSync(TRUST_FILE)) {
2319
+ trust = JSON.parse(import_fs8.default.readFileSync(TRUST_FILE, "utf-8"));
1772
2320
  }
1773
2321
  } catch {
1774
2322
  }
@@ -1784,34 +2332,34 @@ function writeTrustSession(toolName, durationMs) {
1784
2332
  }
1785
2333
  function getPersistentDecision(toolName) {
1786
2334
  try {
1787
- const file = import_path6.default.join(import_os5.default.homedir(), ".node9", "decisions.json");
1788
- if (!import_fs6.default.existsSync(file)) return null;
1789
- const decisions = JSON.parse(import_fs6.default.readFileSync(file, "utf-8"));
2335
+ const file = import_path9.default.join(import_os7.default.homedir(), ".node9", "decisions.json");
2336
+ if (!import_fs8.default.existsSync(file)) return null;
2337
+ const decisions = JSON.parse(import_fs8.default.readFileSync(file, "utf-8"));
1790
2338
  const d = decisions[toolName];
1791
2339
  if (d === "allow" || d === "deny") return d;
1792
2340
  } catch {
1793
2341
  }
1794
2342
  return null;
1795
2343
  }
1796
- var import_fs6, import_path6, import_os5, PAUSED_FILE, TRUST_FILE;
2344
+ var import_fs8, import_path9, import_os7, PAUSED_FILE, TRUST_FILE;
1797
2345
  var init_state = __esm({
1798
2346
  "src/auth/state.ts"() {
1799
2347
  "use strict";
1800
- import_fs6 = __toESM(require("fs"));
1801
- import_path6 = __toESM(require("path"));
1802
- import_os5 = __toESM(require("os"));
2348
+ import_fs8 = __toESM(require("fs"));
2349
+ import_path9 = __toESM(require("path"));
2350
+ import_os7 = __toESM(require("os"));
1803
2351
  init_policy();
1804
- PAUSED_FILE = import_path6.default.join(import_os5.default.homedir(), ".node9", "PAUSED");
1805
- TRUST_FILE = import_path6.default.join(import_os5.default.homedir(), ".node9", "trust.json");
2352
+ PAUSED_FILE = import_path9.default.join(import_os7.default.homedir(), ".node9", "PAUSED");
2353
+ TRUST_FILE = import_path9.default.join(import_os7.default.homedir(), ".node9", "trust.json");
1806
2354
  }
1807
2355
  });
1808
2356
 
1809
2357
  // src/auth/daemon.ts
1810
2358
  function getInternalToken() {
1811
2359
  try {
1812
- const pidFile = import_path7.default.join(import_os6.default.homedir(), ".node9", "daemon.pid");
1813
- if (!import_fs7.default.existsSync(pidFile)) return null;
1814
- const data = JSON.parse(import_fs7.default.readFileSync(pidFile, "utf-8"));
2360
+ const pidFile = import_path10.default.join(import_os8.default.homedir(), ".node9", "daemon.pid");
2361
+ if (!import_fs9.default.existsSync(pidFile)) return null;
2362
+ const data = JSON.parse(import_fs9.default.readFileSync(pidFile, "utf-8"));
1815
2363
  process.kill(data.pid, 0);
1816
2364
  return data.internalToken ?? null;
1817
2365
  } catch {
@@ -1819,10 +2367,10 @@ function getInternalToken() {
1819
2367
  }
1820
2368
  }
1821
2369
  function isDaemonRunning() {
1822
- const pidFile = import_path7.default.join(import_os6.default.homedir(), ".node9", "daemon.pid");
1823
- if (import_fs7.default.existsSync(pidFile)) {
2370
+ const pidFile = import_path10.default.join(import_os8.default.homedir(), ".node9", "daemon.pid");
2371
+ if (import_fs9.default.existsSync(pidFile)) {
1824
2372
  try {
1825
- const { pid, port } = JSON.parse(import_fs7.default.readFileSync(pidFile, "utf-8"));
2373
+ const { pid, port } = JSON.parse(import_fs9.default.readFileSync(pidFile, "utf-8"));
1826
2374
  if (port !== DAEMON_PORT) return false;
1827
2375
  process.kill(pid, 0);
1828
2376
  return true;
@@ -1915,13 +2463,13 @@ async function resolveViaDaemon(id, decision, internalToken) {
1915
2463
  signal: AbortSignal.timeout(3e3)
1916
2464
  });
1917
2465
  }
1918
- var import_fs7, import_path7, import_os6, import_child_process, DAEMON_PORT, DAEMON_HOST;
2466
+ var import_fs9, import_path10, import_os8, import_child_process, DAEMON_PORT, DAEMON_HOST;
1919
2467
  var init_daemon = __esm({
1920
2468
  "src/auth/daemon.ts"() {
1921
2469
  "use strict";
1922
- import_fs7 = __toESM(require("fs"));
1923
- import_path7 = __toESM(require("path"));
1924
- import_os6 = __toESM(require("os"));
2470
+ import_fs9 = __toESM(require("fs"));
2471
+ import_path10 = __toESM(require("path"));
2472
+ import_os8 = __toESM(require("os"));
1925
2473
  import_child_process = require("child_process");
1926
2474
  DAEMON_PORT = 7391;
1927
2475
  DAEMON_HOST = "127.0.0.1";
@@ -1980,7 +2528,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
1980
2528
  intent = "EDIT";
1981
2529
  if (obj.file_path) {
1982
2530
  editFilePath = String(obj.file_path);
1983
- editFileName = import_path8.default.basename(editFilePath);
2531
+ editFileName = import_path11.default.basename(editFilePath);
1984
2532
  }
1985
2533
  const result = extractContext(String(obj.new_string), matchedWord);
1986
2534
  contextSnippet = result.snippet;
@@ -2012,11 +2560,11 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
2012
2560
  ...ruleName && { ruleName }
2013
2561
  };
2014
2562
  }
2015
- var import_path8, CODE_KEYS;
2563
+ var import_path11, CODE_KEYS;
2016
2564
  var init_context_sniper = __esm({
2017
2565
  "src/context-sniper.ts"() {
2018
2566
  "use strict";
2019
- import_path8 = __toESM(require("path"));
2567
+ import_path11 = __toESM(require("path"));
2020
2568
  CODE_KEYS = [
2021
2569
  "command",
2022
2570
  "cmd",
@@ -2055,7 +2603,7 @@ function formatArgs(args, matchedField, matchedWord) {
2055
2603
  if (typeof parsed === "object" && !Array.isArray(parsed)) {
2056
2604
  const obj = parsed;
2057
2605
  if (obj.old_string !== void 0 && obj.new_string !== void 0) {
2058
- const file = obj.file_path ? import_path9.default.basename(String(obj.file_path)) : "file";
2606
+ const file = obj.file_path ? import_path12.default.basename(String(obj.file_path)) : "file";
2059
2607
  const oldPreview = smartTruncate(String(obj.old_string), 120);
2060
2608
  const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
2061
2609
  return {
@@ -2228,12 +2776,12 @@ end run`;
2228
2776
  }
2229
2777
  });
2230
2778
  }
2231
- var import_child_process2, import_path9, isTestEnv;
2779
+ var import_child_process2, import_path12, isTestEnv;
2232
2780
  var init_native = __esm({
2233
2781
  "src/ui/native.ts"() {
2234
2782
  "use strict";
2235
2783
  import_child_process2 = require("child_process");
2236
- import_path9 = __toESM(require("path"));
2784
+ import_path12 = __toESM(require("path"));
2237
2785
  init_context_sniper();
2238
2786
  isTestEnv = () => {
2239
2787
  return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || !!process.env.VITEST || process.env.CI === "true" || !!process.env.CI || process.env.NODE9_TESTING === "1";
@@ -2253,9 +2801,9 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
2253
2801
  context: {
2254
2802
  agent: meta?.agent,
2255
2803
  mcpServer: meta?.mcpServer,
2256
- hostname: import_os7.default.hostname(),
2804
+ hostname: import_os9.default.hostname(),
2257
2805
  cwd: process.cwd(),
2258
- platform: import_os7.default.platform()
2806
+ platform: import_os9.default.platform()
2259
2807
  }
2260
2808
  }),
2261
2809
  signal: AbortSignal.timeout(5e3)
@@ -2276,9 +2824,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
2276
2824
  context: {
2277
2825
  agent: meta?.agent,
2278
2826
  mcpServer: meta?.mcpServer,
2279
- hostname: import_os7.default.hostname(),
2827
+ hostname: import_os9.default.hostname(),
2280
2828
  cwd: process.cwd(),
2281
- platform: import_os7.default.platform()
2829
+ platform: import_os9.default.platform()
2282
2830
  },
2283
2831
  ...riskMetadata && { riskMetadata }
2284
2832
  }),
@@ -2334,26 +2882,26 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
2334
2882
  });
2335
2883
  clearTimeout(timer);
2336
2884
  if (!res.ok) {
2337
- import_fs8.default.appendFileSync(
2885
+ import_fs10.default.appendFileSync(
2338
2886
  HOOK_DEBUG_LOG,
2339
2887
  `[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
2340
2888
  `
2341
2889
  );
2342
2890
  }
2343
2891
  } catch (err) {
2344
- import_fs8.default.appendFileSync(
2892
+ import_fs10.default.appendFileSync(
2345
2893
  HOOK_DEBUG_LOG,
2346
2894
  `[resolve-cloud] PATCH failed for ${requestId}: ${err.message}
2347
2895
  `
2348
2896
  );
2349
2897
  }
2350
2898
  }
2351
- var import_fs8, import_os7;
2899
+ var import_fs10, import_os9;
2352
2900
  var init_cloud = __esm({
2353
2901
  "src/auth/cloud.ts"() {
2354
2902
  "use strict";
2355
- import_fs8 = __toESM(require("fs"));
2356
- import_os7 = __toESM(require("os"));
2903
+ import_fs10 = __toESM(require("fs"));
2904
+ import_os9 = __toESM(require("os"));
2357
2905
  init_audit();
2358
2906
  }
2359
2907
  });
@@ -2440,7 +2988,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
2440
2988
  }
2441
2989
  if (config.settings.mode === "audit") {
2442
2990
  if (!isIgnoredTool(toolName)) {
2443
- const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
2991
+ const policyResult = await evaluatePolicy(toolName, args, meta?.agent, options?.cwd);
2444
2992
  if (policyResult.decision === "review") {
2445
2993
  appendLocalAudit(toolName, args, "allow", "audit-mode", meta);
2446
2994
  if (approvers.cloud && creds?.apiKey) {
@@ -2705,13 +3253,13 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
2705
3253
  }
2706
3254
  return finalResult;
2707
3255
  }
2708
- var import_net, import_path10, import_os8, import_crypto2, ACTIVITY_SOCKET_PATH;
3256
+ var import_net, import_path13, import_os10, import_crypto2, ACTIVITY_SOCKET_PATH;
2709
3257
  var init_orchestrator = __esm({
2710
3258
  "src/auth/orchestrator.ts"() {
2711
3259
  "use strict";
2712
3260
  import_net = __toESM(require("net"));
2713
- import_path10 = __toESM(require("path"));
2714
- import_os8 = __toESM(require("os"));
3261
+ import_path13 = __toESM(require("path"));
3262
+ import_os10 = __toESM(require("os"));
2715
3263
  import_crypto2 = require("crypto");
2716
3264
  init_native();
2717
3265
  init_context_sniper();
@@ -2722,7 +3270,7 @@ var init_orchestrator = __esm({
2722
3270
  init_state();
2723
3271
  init_daemon();
2724
3272
  init_cloud();
2725
- ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path10.default.join(import_os8.default.tmpdir(), "node9-activity.sock");
3273
+ ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path13.default.join(import_os10.default.tmpdir(), "node9-activity.sock");
2726
3274
  }
2727
3275
  });
2728
3276
 
@@ -4215,11 +4763,11 @@ function markRejectionHandlerRegistered() {
4215
4763
  daemonRejectionHandlerRegistered = true;
4216
4764
  }
4217
4765
  function atomicWriteSync2(filePath, data, options) {
4218
- const dir = import_path12.default.dirname(filePath);
4219
- if (!import_fs10.default.existsSync(dir)) import_fs10.default.mkdirSync(dir, { recursive: true });
4766
+ const dir = import_path15.default.dirname(filePath);
4767
+ if (!import_fs12.default.existsSync(dir)) import_fs12.default.mkdirSync(dir, { recursive: true });
4220
4768
  const tmpPath = `${filePath}.${(0, import_crypto3.randomUUID)()}.tmp`;
4221
- import_fs10.default.writeFileSync(tmpPath, data, options);
4222
- import_fs10.default.renameSync(tmpPath, filePath);
4769
+ import_fs12.default.writeFileSync(tmpPath, data, options);
4770
+ import_fs12.default.renameSync(tmpPath, filePath);
4223
4771
  }
4224
4772
  function redactArgs(value) {
4225
4773
  if (!value || typeof value !== "object") return value;
@@ -4239,16 +4787,16 @@ function appendAuditLog(data) {
4239
4787
  decision: data.decision,
4240
4788
  source: "daemon"
4241
4789
  };
4242
- const dir = import_path12.default.dirname(AUDIT_LOG_FILE);
4243
- if (!import_fs10.default.existsSync(dir)) import_fs10.default.mkdirSync(dir, { recursive: true });
4244
- import_fs10.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
4790
+ const dir = import_path15.default.dirname(AUDIT_LOG_FILE);
4791
+ if (!import_fs12.default.existsSync(dir)) import_fs12.default.mkdirSync(dir, { recursive: true });
4792
+ import_fs12.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
4245
4793
  } catch {
4246
4794
  }
4247
4795
  }
4248
4796
  function getAuditHistory(limit = 20) {
4249
4797
  try {
4250
- if (!import_fs10.default.existsSync(AUDIT_LOG_FILE)) return [];
4251
- const lines = import_fs10.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
4798
+ if (!import_fs12.default.existsSync(AUDIT_LOG_FILE)) return [];
4799
+ const lines = import_fs12.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
4252
4800
  if (lines.length === 1 && lines[0] === "") return [];
4253
4801
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
4254
4802
  } catch {
@@ -4257,19 +4805,19 @@ function getAuditHistory(limit = 20) {
4257
4805
  }
4258
4806
  function getOrgName() {
4259
4807
  try {
4260
- if (import_fs10.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
4808
+ if (import_fs12.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
4261
4809
  } catch {
4262
4810
  }
4263
4811
  return null;
4264
4812
  }
4265
4813
  function hasStoredSlackKey() {
4266
- return import_fs10.default.existsSync(CREDENTIALS_FILE);
4814
+ return import_fs12.default.existsSync(CREDENTIALS_FILE);
4267
4815
  }
4268
4816
  function writeGlobalSetting(key, value) {
4269
4817
  let config = {};
4270
4818
  try {
4271
- if (import_fs10.default.existsSync(GLOBAL_CONFIG_FILE)) {
4272
- config = JSON.parse(import_fs10.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
4819
+ if (import_fs12.default.existsSync(GLOBAL_CONFIG_FILE)) {
4820
+ config = JSON.parse(import_fs12.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
4273
4821
  }
4274
4822
  } catch {
4275
4823
  }
@@ -4281,8 +4829,8 @@ function writeTrustEntry(toolName, durationMs) {
4281
4829
  try {
4282
4830
  let trust = { entries: [] };
4283
4831
  try {
4284
- if (import_fs10.default.existsSync(TRUST_FILE2))
4285
- trust = JSON.parse(import_fs10.default.readFileSync(TRUST_FILE2, "utf-8"));
4832
+ if (import_fs12.default.existsSync(TRUST_FILE2))
4833
+ trust = JSON.parse(import_fs12.default.readFileSync(TRUST_FILE2, "utf-8"));
4286
4834
  } catch {
4287
4835
  }
4288
4836
  trust.entries = trust.entries.filter((e) => e.tool !== toolName && e.expiry > Date.now());
@@ -4293,8 +4841,8 @@ function writeTrustEntry(toolName, durationMs) {
4293
4841
  }
4294
4842
  function readPersistentDecisions() {
4295
4843
  try {
4296
- if (import_fs10.default.existsSync(DECISIONS_FILE)) {
4297
- return JSON.parse(import_fs10.default.readFileSync(DECISIONS_FILE, "utf-8"));
4844
+ if (import_fs12.default.existsSync(DECISIONS_FILE)) {
4845
+ return JSON.parse(import_fs12.default.readFileSync(DECISIONS_FILE, "utf-8"));
4298
4846
  }
4299
4847
  } catch {
4300
4848
  }
@@ -4359,7 +4907,7 @@ function abandonPending() {
4359
4907
  });
4360
4908
  if (autoStarted) {
4361
4909
  try {
4362
- import_fs10.default.unlinkSync(DAEMON_PID_FILE);
4910
+ import_fs12.default.unlinkSync(DAEMON_PID_FILE);
4363
4911
  } catch {
4364
4912
  }
4365
4913
  setTimeout(() => {
@@ -4370,7 +4918,7 @@ function abandonPending() {
4370
4918
  }
4371
4919
  function startActivitySocket() {
4372
4920
  try {
4373
- import_fs10.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
4921
+ import_fs12.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
4374
4922
  } catch {
4375
4923
  }
4376
4924
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -4412,29 +4960,29 @@ function startActivitySocket() {
4412
4960
  unixServer.listen(ACTIVITY_SOCKET_PATH2);
4413
4961
  process.on("exit", () => {
4414
4962
  try {
4415
- import_fs10.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
4963
+ import_fs12.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
4416
4964
  } catch {
4417
4965
  }
4418
4966
  });
4419
4967
  }
4420
- var import_net2, import_fs10, import_path12, import_os10, import_child_process3, import_crypto3, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, pending, sseClients, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, SECRET_KEY_RE;
4968
+ var import_net2, import_fs12, import_path15, import_os12, import_child_process3, import_crypto3, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, pending, sseClients, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, SECRET_KEY_RE;
4421
4969
  var init_state2 = __esm({
4422
4970
  "src/daemon/state.ts"() {
4423
4971
  "use strict";
4424
4972
  import_net2 = __toESM(require("net"));
4425
- import_fs10 = __toESM(require("fs"));
4426
- import_path12 = __toESM(require("path"));
4427
- import_os10 = __toESM(require("os"));
4973
+ import_fs12 = __toESM(require("fs"));
4974
+ import_path15 = __toESM(require("path"));
4975
+ import_os12 = __toESM(require("os"));
4428
4976
  import_child_process3 = require("child_process");
4429
4977
  import_crypto3 = require("crypto");
4430
4978
  init_daemon();
4431
- homeDir = import_os10.default.homedir();
4432
- DAEMON_PID_FILE = import_path12.default.join(homeDir, ".node9", "daemon.pid");
4433
- DECISIONS_FILE = import_path12.default.join(homeDir, ".node9", "decisions.json");
4434
- AUDIT_LOG_FILE = import_path12.default.join(homeDir, ".node9", "audit.log");
4435
- TRUST_FILE2 = import_path12.default.join(homeDir, ".node9", "trust.json");
4436
- GLOBAL_CONFIG_FILE = import_path12.default.join(homeDir, ".node9", "config.json");
4437
- CREDENTIALS_FILE = import_path12.default.join(homeDir, ".node9", "credentials.json");
4979
+ homeDir = import_os12.default.homedir();
4980
+ DAEMON_PID_FILE = import_path15.default.join(homeDir, ".node9", "daemon.pid");
4981
+ DECISIONS_FILE = import_path15.default.join(homeDir, ".node9", "decisions.json");
4982
+ AUDIT_LOG_FILE = import_path15.default.join(homeDir, ".node9", "audit.log");
4983
+ TRUST_FILE2 = import_path15.default.join(homeDir, ".node9", "trust.json");
4984
+ GLOBAL_CONFIG_FILE = import_path15.default.join(homeDir, ".node9", "config.json");
4985
+ CREDENTIALS_FILE = import_path15.default.join(homeDir, ".node9", "credentials.json");
4438
4986
  pending = /* @__PURE__ */ new Map();
4439
4987
  sseClients = /* @__PURE__ */ new Set();
4440
4988
  _abandonTimer = null;
@@ -4448,7 +4996,7 @@ var init_state2 = __esm({
4448
4996
  "2h": 2 * 60 * 6e4
4449
4997
  };
4450
4998
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
4451
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path12.default.join(import_os10.default.tmpdir(), "node9-activity.sock");
4999
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path15.default.join(import_os12.default.tmpdir(), "node9-activity.sock");
4452
5000
  ACTIVITY_RING_SIZE = 100;
4453
5001
  activityRing = [];
4454
5002
  SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
@@ -4471,7 +5019,7 @@ function startDaemon() {
4471
5019
  idleTimer = setTimeout(() => {
4472
5020
  if (autoStarted) {
4473
5021
  try {
4474
- import_fs11.default.unlinkSync(DAEMON_PID_FILE);
5022
+ import_fs13.default.unlinkSync(DAEMON_PID_FILE);
4475
5023
  } catch {
4476
5024
  }
4477
5025
  }
@@ -4613,7 +5161,7 @@ data: ${JSON.stringify(item.data)}
4613
5161
  status: "pending"
4614
5162
  });
4615
5163
  }
4616
- const projectCwd = typeof cwd === "string" && import_path13.default.isAbsolute(cwd) ? cwd : void 0;
5164
+ const projectCwd = typeof cwd === "string" && import_path16.default.isAbsolute(cwd) ? cwd : void 0;
4617
5165
  const projectConfig = getConfig(projectCwd);
4618
5166
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
4619
5167
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -4917,14 +5465,14 @@ data: ${JSON.stringify(item.data)}
4917
5465
  server.on("error", (e) => {
4918
5466
  if (e.code === "EADDRINUSE") {
4919
5467
  try {
4920
- if (import_fs11.default.existsSync(DAEMON_PID_FILE)) {
4921
- const { pid } = JSON.parse(import_fs11.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
5468
+ if (import_fs13.default.existsSync(DAEMON_PID_FILE)) {
5469
+ const { pid } = JSON.parse(import_fs13.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
4922
5470
  process.kill(pid, 0);
4923
5471
  return process.exit(0);
4924
5472
  }
4925
5473
  } catch {
4926
5474
  try {
4927
- import_fs11.default.unlinkSync(DAEMON_PID_FILE);
5475
+ import_fs13.default.unlinkSync(DAEMON_PID_FILE);
4928
5476
  } catch {
4929
5477
  }
4930
5478
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -4983,13 +5531,13 @@ data: ${JSON.stringify(item.data)}
4983
5531
  }
4984
5532
  startActivitySocket();
4985
5533
  }
4986
- var import_http, import_fs11, import_path13, import_crypto4, import_child_process4, import_chalk2;
5534
+ var import_http, import_fs13, import_path16, import_crypto4, import_child_process4, import_chalk2;
4987
5535
  var init_server = __esm({
4988
5536
  "src/daemon/server.ts"() {
4989
5537
  "use strict";
4990
5538
  import_http = __toESM(require("http"));
4991
- import_fs11 = __toESM(require("fs"));
4992
- import_path13 = __toESM(require("path"));
5539
+ import_fs13 = __toESM(require("fs"));
5540
+ import_path16 = __toESM(require("path"));
4993
5541
  import_crypto4 = require("crypto");
4994
5542
  import_child_process4 = require("child_process");
4995
5543
  import_chalk2 = __toESM(require("chalk"));
@@ -5002,24 +5550,24 @@ var init_server = __esm({
5002
5550
 
5003
5551
  // src/daemon/index.ts
5004
5552
  function stopDaemon() {
5005
- if (!import_fs12.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
5553
+ if (!import_fs14.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
5006
5554
  try {
5007
- const { pid } = JSON.parse(import_fs12.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
5555
+ const { pid } = JSON.parse(import_fs14.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
5008
5556
  process.kill(pid, "SIGTERM");
5009
5557
  console.log(import_chalk3.default.green("\u2705 Stopped."));
5010
5558
  } catch {
5011
5559
  console.log(import_chalk3.default.gray("Cleaned up stale PID file."));
5012
5560
  } finally {
5013
5561
  try {
5014
- import_fs12.default.unlinkSync(DAEMON_PID_FILE);
5562
+ import_fs14.default.unlinkSync(DAEMON_PID_FILE);
5015
5563
  } catch {
5016
5564
  }
5017
5565
  }
5018
5566
  }
5019
5567
  function daemonStatus() {
5020
- if (import_fs12.default.existsSync(DAEMON_PID_FILE)) {
5568
+ if (import_fs14.default.existsSync(DAEMON_PID_FILE)) {
5021
5569
  try {
5022
- const { pid } = JSON.parse(import_fs12.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
5570
+ const { pid } = JSON.parse(import_fs14.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
5023
5571
  process.kill(pid, 0);
5024
5572
  console.log(import_chalk3.default.green("Node9 daemon: running"));
5025
5573
  return;
@@ -5038,11 +5586,11 @@ function daemonStatus() {
5038
5586
  console.log(import_chalk3.default.yellow("Node9 daemon: not running"));
5039
5587
  }
5040
5588
  }
5041
- var import_fs12, import_chalk3, import_child_process5;
5589
+ var import_fs14, import_chalk3, import_child_process5;
5042
5590
  var init_daemon2 = __esm({
5043
5591
  "src/daemon/index.ts"() {
5044
5592
  "use strict";
5045
- import_fs12 = __toESM(require("fs"));
5593
+ import_fs14 = __toESM(require("fs"));
5046
5594
  import_chalk3 = __toESM(require("chalk"));
5047
5595
  import_child_process5 = require("child_process");
5048
5596
  init_server();
@@ -5069,17 +5617,17 @@ function formatBase(activity) {
5069
5617
  const toolName = activity.tool.slice(0, 16).padEnd(16);
5070
5618
  const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ");
5071
5619
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
5072
- return `${import_chalk14.default.gray(time)} ${icon} ${import_chalk14.default.white.bold(toolName)} ${import_chalk14.default.dim(argsPreview)}`;
5620
+ return `${import_chalk15.default.gray(time)} ${icon} ${import_chalk15.default.white.bold(toolName)} ${import_chalk15.default.dim(argsPreview)}`;
5073
5621
  }
5074
5622
  function renderResult(activity, result) {
5075
5623
  const base = formatBase(activity);
5076
5624
  let status;
5077
5625
  if (result.status === "allow") {
5078
- status = import_chalk14.default.green("\u2713 ALLOW");
5626
+ status = import_chalk15.default.green("\u2713 ALLOW");
5079
5627
  } else if (result.status === "dlp") {
5080
- status = import_chalk14.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
5628
+ status = import_chalk15.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
5081
5629
  } else {
5082
- status = import_chalk14.default.red("\u2717 BLOCK");
5630
+ status = import_chalk15.default.red("\u2717 BLOCK");
5083
5631
  }
5084
5632
  if (process.stdout.isTTY) {
5085
5633
  import_readline3.default.clearLine(process.stdout, 0);
@@ -5089,16 +5637,16 @@ function renderResult(activity, result) {
5089
5637
  }
5090
5638
  function renderPending(activity) {
5091
5639
  if (!process.stdout.isTTY) return;
5092
- process.stdout.write(`${formatBase(activity)} ${import_chalk14.default.yellow("\u25CF \u2026")}\r`);
5640
+ process.stdout.write(`${formatBase(activity)} ${import_chalk15.default.yellow("\u25CF \u2026")}\r`);
5093
5641
  }
5094
5642
  async function ensureDaemon() {
5095
5643
  let pidPort = null;
5096
- if (import_fs19.default.existsSync(PID_FILE)) {
5644
+ if (import_fs21.default.existsSync(PID_FILE)) {
5097
5645
  try {
5098
- const { port } = JSON.parse(import_fs19.default.readFileSync(PID_FILE, "utf-8"));
5646
+ const { port } = JSON.parse(import_fs21.default.readFileSync(PID_FILE, "utf-8"));
5099
5647
  pidPort = port;
5100
5648
  } catch {
5101
- console.error(import_chalk14.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
5649
+ console.error(import_chalk15.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
5102
5650
  }
5103
5651
  }
5104
5652
  const checkPort = pidPort ?? DAEMON_PORT;
@@ -5109,7 +5657,7 @@ async function ensureDaemon() {
5109
5657
  if (res.ok) return checkPort;
5110
5658
  } catch {
5111
5659
  }
5112
- console.log(import_chalk14.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
5660
+ console.log(import_chalk15.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
5113
5661
  const child = (0, import_child_process13.spawn)(process.execPath, [process.argv[1], "daemon"], {
5114
5662
  detached: true,
5115
5663
  stdio: "ignore",
@@ -5126,7 +5674,7 @@ async function ensureDaemon() {
5126
5674
  } catch {
5127
5675
  }
5128
5676
  }
5129
- console.error(import_chalk14.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
5677
+ console.error(import_chalk15.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
5130
5678
  process.exit(1);
5131
5679
  }
5132
5680
  function postDecisionHttp(id, decision, csrfToken, port) {
@@ -5197,7 +5745,7 @@ async function startTail(options = {}) {
5197
5745
  req2.end();
5198
5746
  });
5199
5747
  if (result.ok) {
5200
- console.log(import_chalk14.default.green("\u2713 Flight Recorder buffer cleared."));
5748
+ console.log(import_chalk15.default.green("\u2713 Flight Recorder buffer cleared."));
5201
5749
  } else if (result.code === "ECONNREFUSED") {
5202
5750
  throw new Error("Daemon is not running. Start it with: node9 daemon start");
5203
5751
  } else if (result.code === "ETIMEDOUT") {
@@ -5260,16 +5808,16 @@ async function startTail(options = {}) {
5260
5808
  process.stdout.write(SHOW_CURSOR);
5261
5809
  postDecisionHttp(req2.id, decision, csrfToken, port).catch((err) => {
5262
5810
  try {
5263
- import_fs19.default.appendFileSync(
5264
- import_path20.default.join(import_os17.default.homedir(), ".node9", "hook-debug.log"),
5811
+ import_fs21.default.appendFileSync(
5812
+ import_path23.default.join(import_os19.default.homedir(), ".node9", "hook-debug.log"),
5265
5813
  `[tail] POST /decision failed: ${String(err)}
5266
5814
  `
5267
5815
  );
5268
5816
  } catch {
5269
5817
  }
5270
5818
  });
5271
- const decisionLabel = decision === "allow" ? import_chalk14.default.green("\u2713 ALLOWED (terminal)") : import_chalk14.default.red("\u2717 DENIED (terminal)");
5272
- console.log(`${import_chalk14.default.cyan("\u25C6")} ${import_chalk14.default.bold(req2.toolName.padEnd(16))} ${decisionLabel}`);
5819
+ const decisionLabel = decision === "allow" ? import_chalk15.default.green("\u2713 ALLOWED (terminal)") : import_chalk15.default.red("\u2717 DENIED (terminal)");
5820
+ console.log(`${import_chalk15.default.cyan("\u25C6")} ${import_chalk15.default.bold(req2.toolName.padEnd(16))} ${decisionLabel}`);
5273
5821
  approvalQueue.shift();
5274
5822
  cardActive = false;
5275
5823
  showNextCard();
@@ -5306,16 +5854,16 @@ async function startTail(options = {}) {
5306
5854
  }
5307
5855
  } catch {
5308
5856
  }
5309
- console.log(import_chalk14.default.cyan.bold(`
5310
- \u{1F6F0}\uFE0F Node9 tail `) + import_chalk14.default.dim(`\u2192 ${dashboardUrl}`));
5857
+ console.log(import_chalk15.default.cyan.bold(`
5858
+ \u{1F6F0}\uFE0F Node9 tail `) + import_chalk15.default.dim(`\u2192 ${dashboardUrl}`));
5311
5859
  if (canApprove) {
5312
- console.log(import_chalk14.default.dim("Interactive approvals enabled. [A] Allow [D] Deny"));
5860
+ console.log(import_chalk15.default.dim("Interactive approvals enabled. [A] Allow [D] Deny"));
5313
5861
  }
5314
5862
  if (options.history) {
5315
- console.log(import_chalk14.default.dim("Showing history + live events. Press Ctrl+C to exit.\n"));
5863
+ console.log(import_chalk15.default.dim("Showing history + live events. Press Ctrl+C to exit.\n"));
5316
5864
  } else {
5317
5865
  console.log(
5318
- import_chalk14.default.dim("Showing live events only. Use --history to include past. Press Ctrl+C to exit.\n")
5866
+ import_chalk15.default.dim("Showing live events only. Use --history to include past. Press Ctrl+C to exit.\n")
5319
5867
  );
5320
5868
  }
5321
5869
  process.on("SIGINT", () => {
@@ -5325,13 +5873,13 @@ async function startTail(options = {}) {
5325
5873
  import_readline3.default.clearLine(process.stdout, 0);
5326
5874
  import_readline3.default.cursorTo(process.stdout, 0);
5327
5875
  }
5328
- console.log(import_chalk14.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
5876
+ console.log(import_chalk15.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
5329
5877
  process.exit(0);
5330
5878
  });
5331
5879
  const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
5332
5880
  const req = import_http2.default.get(sseUrl, (res) => {
5333
5881
  if (res.statusCode !== 200) {
5334
- console.error(import_chalk14.default.red(`Failed to connect: HTTP ${res.statusCode}`));
5882
+ console.error(import_chalk15.default.red(`Failed to connect: HTTP ${res.statusCode}`));
5335
5883
  process.exit(1);
5336
5884
  }
5337
5885
  let currentEvent = "";
@@ -5361,7 +5909,7 @@ async function startTail(options = {}) {
5361
5909
  import_readline3.default.clearLine(process.stdout, 0);
5362
5910
  import_readline3.default.cursorTo(process.stdout, 0);
5363
5911
  }
5364
- console.log(import_chalk14.default.red("\n\u274C Daemon disconnected."));
5912
+ console.log(import_chalk15.default.red("\n\u274C Daemon disconnected."));
5365
5913
  process.exit(1);
5366
5914
  });
5367
5915
  });
@@ -5441,25 +5989,25 @@ async function startTail(options = {}) {
5441
5989
  }
5442
5990
  req.on("error", (err) => {
5443
5991
  const msg = err.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err.message;
5444
- console.error(import_chalk14.default.red(`
5992
+ console.error(import_chalk15.default.red(`
5445
5993
  \u274C ${msg}`));
5446
5994
  process.exit(1);
5447
5995
  });
5448
5996
  }
5449
- var import_http2, import_chalk14, import_fs19, import_os17, import_path20, import_readline3, import_child_process13, PID_FILE, ICONS, RESET, BOLD, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, SAVE_CURSOR, RESTORE_CURSOR;
5997
+ var import_http2, import_chalk15, import_fs21, import_os19, import_path23, import_readline3, import_child_process13, PID_FILE, ICONS, RESET, BOLD, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, SAVE_CURSOR, RESTORE_CURSOR;
5450
5998
  var init_tail = __esm({
5451
5999
  "src/tui/tail.ts"() {
5452
6000
  "use strict";
5453
6001
  import_http2 = __toESM(require("http"));
5454
- import_chalk14 = __toESM(require("chalk"));
5455
- import_fs19 = __toESM(require("fs"));
5456
- import_os17 = __toESM(require("os"));
5457
- import_path20 = __toESM(require("path"));
6002
+ import_chalk15 = __toESM(require("chalk"));
6003
+ import_fs21 = __toESM(require("fs"));
6004
+ import_os19 = __toESM(require("os"));
6005
+ import_path23 = __toESM(require("path"));
5458
6006
  import_readline3 = __toESM(require("readline"));
5459
6007
  import_child_process13 = require("child_process");
5460
6008
  init_daemon2();
5461
6009
  init_core();
5462
- PID_FILE = import_path20.default.join(import_os17.default.homedir(), ".node9", "daemon.pid");
6010
+ PID_FILE = import_path23.default.join(import_os19.default.homedir(), ".node9", "daemon.pid");
5463
6011
  ICONS = {
5464
6012
  bash: "\u{1F4BB}",
5465
6013
  shell: "\u{1F4BB}",
@@ -5497,9 +6045,9 @@ var import_commander = require("commander");
5497
6045
  init_core();
5498
6046
 
5499
6047
  // src/setup.ts
5500
- var import_fs9 = __toESM(require("fs"));
5501
- var import_path11 = __toESM(require("path"));
5502
- var import_os9 = __toESM(require("os"));
6048
+ var import_fs11 = __toESM(require("fs"));
6049
+ var import_path14 = __toESM(require("path"));
6050
+ var import_os11 = __toESM(require("os"));
5503
6051
  var import_chalk = __toESM(require("chalk"));
5504
6052
  var import_prompts = require("@inquirer/prompts");
5505
6053
  function printDaemonTip() {
@@ -5516,26 +6064,26 @@ function fullPathCommand(subcommand) {
5516
6064
  }
5517
6065
  function readJson(filePath) {
5518
6066
  try {
5519
- if (import_fs9.default.existsSync(filePath)) {
5520
- return JSON.parse(import_fs9.default.readFileSync(filePath, "utf-8"));
6067
+ if (import_fs11.default.existsSync(filePath)) {
6068
+ return JSON.parse(import_fs11.default.readFileSync(filePath, "utf-8"));
5521
6069
  }
5522
6070
  } catch {
5523
6071
  }
5524
6072
  return null;
5525
6073
  }
5526
6074
  function writeJson(filePath, data) {
5527
- const dir = import_path11.default.dirname(filePath);
5528
- if (!import_fs9.default.existsSync(dir)) import_fs9.default.mkdirSync(dir, { recursive: true });
5529
- import_fs9.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
6075
+ const dir = import_path14.default.dirname(filePath);
6076
+ if (!import_fs11.default.existsSync(dir)) import_fs11.default.mkdirSync(dir, { recursive: true });
6077
+ import_fs11.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
5530
6078
  }
5531
6079
  function isNode9Hook(cmd) {
5532
6080
  if (!cmd) return false;
5533
6081
  return /(?:^|[\s/\\])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js (?:check|log)/.test(cmd);
5534
6082
  }
5535
6083
  function teardownClaude() {
5536
- const homeDir2 = import_os9.default.homedir();
5537
- const hooksPath = import_path11.default.join(homeDir2, ".claude", "settings.json");
5538
- const mcpPath = import_path11.default.join(homeDir2, ".claude.json");
6084
+ const homeDir2 = import_os11.default.homedir();
6085
+ const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
6086
+ const mcpPath = import_path14.default.join(homeDir2, ".claude.json");
5539
6087
  let changed = false;
5540
6088
  const settings = readJson(hooksPath);
5541
6089
  if (settings?.hooks) {
@@ -5583,8 +6131,8 @@ function teardownClaude() {
5583
6131
  }
5584
6132
  }
5585
6133
  function teardownGemini() {
5586
- const homeDir2 = import_os9.default.homedir();
5587
- const settingsPath = import_path11.default.join(homeDir2, ".gemini", "settings.json");
6134
+ const homeDir2 = import_os11.default.homedir();
6135
+ const settingsPath = import_path14.default.join(homeDir2, ".gemini", "settings.json");
5588
6136
  const settings = readJson(settingsPath);
5589
6137
  if (!settings) {
5590
6138
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
@@ -5622,8 +6170,8 @@ function teardownGemini() {
5622
6170
  }
5623
6171
  }
5624
6172
  function teardownCursor() {
5625
- const homeDir2 = import_os9.default.homedir();
5626
- const mcpPath = import_path11.default.join(homeDir2, ".cursor", "mcp.json");
6173
+ const homeDir2 = import_os11.default.homedir();
6174
+ const mcpPath = import_path14.default.join(homeDir2, ".cursor", "mcp.json");
5627
6175
  const mcpConfig = readJson(mcpPath);
5628
6176
  if (!mcpConfig?.mcpServers) {
5629
6177
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
@@ -5649,9 +6197,9 @@ function teardownCursor() {
5649
6197
  }
5650
6198
  }
5651
6199
  async function setupClaude() {
5652
- const homeDir2 = import_os9.default.homedir();
5653
- const mcpPath = import_path11.default.join(homeDir2, ".claude.json");
5654
- const hooksPath = import_path11.default.join(homeDir2, ".claude", "settings.json");
6200
+ const homeDir2 = import_os11.default.homedir();
6201
+ const mcpPath = import_path14.default.join(homeDir2, ".claude.json");
6202
+ const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
5655
6203
  const claudeConfig = readJson(mcpPath) ?? {};
5656
6204
  const settings = readJson(hooksPath) ?? {};
5657
6205
  const servers = claudeConfig.mcpServers ?? {};
@@ -5725,8 +6273,8 @@ async function setupClaude() {
5725
6273
  }
5726
6274
  }
5727
6275
  async function setupGemini() {
5728
- const homeDir2 = import_os9.default.homedir();
5729
- const settingsPath = import_path11.default.join(homeDir2, ".gemini", "settings.json");
6276
+ const homeDir2 = import_os11.default.homedir();
6277
+ const settingsPath = import_path14.default.join(homeDir2, ".gemini", "settings.json");
5730
6278
  const settings = readJson(settingsPath) ?? {};
5731
6279
  const servers = settings.mcpServers ?? {};
5732
6280
  let anythingChanged = false;
@@ -5808,8 +6356,8 @@ async function setupGemini() {
5808
6356
  }
5809
6357
  }
5810
6358
  async function setupCursor() {
5811
- const homeDir2 = import_os9.default.homedir();
5812
- const mcpPath = import_path11.default.join(homeDir2, ".cursor", "mcp.json");
6359
+ const homeDir2 = import_os11.default.homedir();
6360
+ const mcpPath = import_path14.default.join(homeDir2, ".cursor", "mcp.json");
5813
6361
  const mcpConfig = readJson(mcpPath) ?? {};
5814
6362
  const servers = mcpConfig.mcpServers ?? {};
5815
6363
  let anythingChanged = false;
@@ -5865,10 +6413,10 @@ async function setupCursor() {
5865
6413
 
5866
6414
  // src/cli.ts
5867
6415
  init_daemon2();
5868
- var import_chalk15 = __toESM(require("chalk"));
5869
- var import_fs20 = __toESM(require("fs"));
5870
- var import_path21 = __toESM(require("path"));
5871
- var import_os18 = __toESM(require("os"));
6416
+ var import_chalk16 = __toESM(require("chalk"));
6417
+ var import_fs22 = __toESM(require("fs"));
6418
+ var import_path24 = __toESM(require("path"));
6419
+ var import_os20 = __toESM(require("os"));
5872
6420
  var import_prompts3 = require("@inquirer/prompts");
5873
6421
 
5874
6422
  // src/utils/duration.ts
@@ -6089,9 +6637,9 @@ async function autoStartDaemonAndWait() {
6089
6637
 
6090
6638
  // src/cli/commands/check.ts
6091
6639
  var import_chalk5 = __toESM(require("chalk"));
6092
- var import_fs14 = __toESM(require("fs"));
6093
- var import_path15 = __toESM(require("path"));
6094
- var import_os12 = __toESM(require("os"));
6640
+ var import_fs16 = __toESM(require("fs"));
6641
+ var import_path18 = __toESM(require("path"));
6642
+ var import_os14 = __toESM(require("os"));
6095
6643
  init_orchestrator();
6096
6644
  init_daemon();
6097
6645
  init_config();
@@ -6100,25 +6648,25 @@ init_policy();
6100
6648
  // src/undo.ts
6101
6649
  var import_child_process8 = require("child_process");
6102
6650
  var import_crypto5 = __toESM(require("crypto"));
6103
- var import_fs13 = __toESM(require("fs"));
6104
- var import_path14 = __toESM(require("path"));
6105
- var import_os11 = __toESM(require("os"));
6106
- var SNAPSHOT_STACK_PATH = import_path14.default.join(import_os11.default.homedir(), ".node9", "snapshots.json");
6107
- var UNDO_LATEST_PATH = import_path14.default.join(import_os11.default.homedir(), ".node9", "undo_latest.txt");
6651
+ var import_fs15 = __toESM(require("fs"));
6652
+ var import_path17 = __toESM(require("path"));
6653
+ var import_os13 = __toESM(require("os"));
6654
+ var SNAPSHOT_STACK_PATH = import_path17.default.join(import_os13.default.homedir(), ".node9", "snapshots.json");
6655
+ var UNDO_LATEST_PATH = import_path17.default.join(import_os13.default.homedir(), ".node9", "undo_latest.txt");
6108
6656
  var MAX_SNAPSHOTS = 10;
6109
6657
  var GIT_TIMEOUT = 15e3;
6110
6658
  function readStack() {
6111
6659
  try {
6112
- if (import_fs13.default.existsSync(SNAPSHOT_STACK_PATH))
6113
- return JSON.parse(import_fs13.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
6660
+ if (import_fs15.default.existsSync(SNAPSHOT_STACK_PATH))
6661
+ return JSON.parse(import_fs15.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
6114
6662
  } catch {
6115
6663
  }
6116
6664
  return [];
6117
6665
  }
6118
6666
  function writeStack(stack) {
6119
- const dir = import_path14.default.dirname(SNAPSHOT_STACK_PATH);
6120
- if (!import_fs13.default.existsSync(dir)) import_fs13.default.mkdirSync(dir, { recursive: true });
6121
- import_fs13.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
6667
+ const dir = import_path17.default.dirname(SNAPSHOT_STACK_PATH);
6668
+ if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
6669
+ import_fs15.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
6122
6670
  }
6123
6671
  function buildArgsSummary(tool, args) {
6124
6672
  if (!args || typeof args !== "object") return "";
@@ -6134,7 +6682,7 @@ function buildArgsSummary(tool, args) {
6134
6682
  function normalizeCwdForHash(cwd) {
6135
6683
  let normalized;
6136
6684
  try {
6137
- normalized = import_fs13.default.realpathSync(cwd);
6685
+ normalized = import_fs15.default.realpathSync(cwd);
6138
6686
  } catch {
6139
6687
  normalized = cwd;
6140
6688
  }
@@ -6144,16 +6692,16 @@ function normalizeCwdForHash(cwd) {
6144
6692
  }
6145
6693
  function getShadowRepoDir(cwd) {
6146
6694
  const hash = import_crypto5.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
6147
- return import_path14.default.join(import_os11.default.homedir(), ".node9", "snapshots", hash);
6695
+ return import_path17.default.join(import_os13.default.homedir(), ".node9", "snapshots", hash);
6148
6696
  }
6149
6697
  function cleanOrphanedIndexFiles(shadowDir) {
6150
6698
  try {
6151
6699
  const cutoff = Date.now() - 6e4;
6152
- for (const f of import_fs13.default.readdirSync(shadowDir)) {
6700
+ for (const f of import_fs15.default.readdirSync(shadowDir)) {
6153
6701
  if (f.startsWith("index_")) {
6154
- const fp = import_path14.default.join(shadowDir, f);
6702
+ const fp = import_path17.default.join(shadowDir, f);
6155
6703
  try {
6156
- if (import_fs13.default.statSync(fp).mtimeMs < cutoff) import_fs13.default.unlinkSync(fp);
6704
+ if (import_fs15.default.statSync(fp).mtimeMs < cutoff) import_fs15.default.unlinkSync(fp);
6157
6705
  } catch {
6158
6706
  }
6159
6707
  }
@@ -6165,7 +6713,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
6165
6713
  const hardcoded = [".git", ".node9"];
6166
6714
  const lines = [...hardcoded, ...ignorePaths].join("\n");
6167
6715
  try {
6168
- import_fs13.default.writeFileSync(import_path14.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
6716
+ import_fs15.default.writeFileSync(import_path17.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
6169
6717
  } catch {
6170
6718
  }
6171
6719
  }
@@ -6178,25 +6726,25 @@ function ensureShadowRepo(shadowDir, cwd) {
6178
6726
  timeout: 3e3
6179
6727
  });
6180
6728
  if (check.status === 0) {
6181
- const ptPath = import_path14.default.join(shadowDir, "project-path.txt");
6729
+ const ptPath = import_path17.default.join(shadowDir, "project-path.txt");
6182
6730
  try {
6183
- const stored = import_fs13.default.readFileSync(ptPath, "utf8").trim();
6731
+ const stored = import_fs15.default.readFileSync(ptPath, "utf8").trim();
6184
6732
  if (stored === normalizedCwd) return true;
6185
6733
  if (process.env.NODE9_DEBUG === "1")
6186
6734
  console.error(
6187
6735
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
6188
6736
  );
6189
- import_fs13.default.rmSync(shadowDir, { recursive: true, force: true });
6737
+ import_fs15.default.rmSync(shadowDir, { recursive: true, force: true });
6190
6738
  } catch {
6191
6739
  try {
6192
- import_fs13.default.writeFileSync(ptPath, normalizedCwd, "utf8");
6740
+ import_fs15.default.writeFileSync(ptPath, normalizedCwd, "utf8");
6193
6741
  } catch {
6194
6742
  }
6195
6743
  return true;
6196
6744
  }
6197
6745
  }
6198
6746
  try {
6199
- import_fs13.default.mkdirSync(shadowDir, { recursive: true });
6747
+ import_fs15.default.mkdirSync(shadowDir, { recursive: true });
6200
6748
  } catch {
6201
6749
  }
6202
6750
  const init = (0, import_child_process8.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -6205,7 +6753,7 @@ function ensureShadowRepo(shadowDir, cwd) {
6205
6753
  console.error("[Node9] git init --bare failed:", init.stderr?.toString());
6206
6754
  return false;
6207
6755
  }
6208
- const configFile = import_path14.default.join(shadowDir, "config");
6756
+ const configFile = import_path17.default.join(shadowDir, "config");
6209
6757
  (0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
6210
6758
  timeout: 3e3
6211
6759
  });
@@ -6213,7 +6761,7 @@ function ensureShadowRepo(shadowDir, cwd) {
6213
6761
  timeout: 3e3
6214
6762
  });
6215
6763
  try {
6216
- import_fs13.default.writeFileSync(import_path14.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
6764
+ import_fs15.default.writeFileSync(import_path17.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
6217
6765
  } catch {
6218
6766
  }
6219
6767
  return true;
@@ -6236,7 +6784,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
6236
6784
  const shadowDir = getShadowRepoDir(cwd);
6237
6785
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
6238
6786
  writeShadowExcludes(shadowDir, ignorePaths);
6239
- indexFile = import_path14.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
6787
+ indexFile = import_path17.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
6240
6788
  const shadowEnv = {
6241
6789
  ...process.env,
6242
6790
  GIT_DIR: shadowDir,
@@ -6265,7 +6813,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
6265
6813
  const shouldGc = stack.length % 5 === 0;
6266
6814
  if (stack.length > MAX_SNAPSHOTS) stack.splice(0, stack.length - MAX_SNAPSHOTS);
6267
6815
  writeStack(stack);
6268
- import_fs13.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
6816
+ import_fs15.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
6269
6817
  if (shouldGc) {
6270
6818
  (0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
6271
6819
  }
@@ -6276,7 +6824,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
6276
6824
  } finally {
6277
6825
  if (indexFile) {
6278
6826
  try {
6279
- import_fs13.default.unlinkSync(indexFile);
6827
+ import_fs15.default.unlinkSync(indexFile);
6280
6828
  } catch {
6281
6829
  }
6282
6830
  }
@@ -6345,9 +6893,9 @@ function applyUndo(hash, cwd) {
6345
6893
  timeout: GIT_TIMEOUT
6346
6894
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
6347
6895
  for (const file of [...tracked, ...untracked]) {
6348
- const fullPath = import_path14.default.join(dir, file);
6349
- if (!snapshotFiles.has(file) && import_fs13.default.existsSync(fullPath)) {
6350
- import_fs13.default.unlinkSync(fullPath);
6896
+ const fullPath = import_path17.default.join(dir, file);
6897
+ if (!snapshotFiles.has(file) && import_fs15.default.existsSync(fullPath)) {
6898
+ import_fs15.default.unlinkSync(fullPath);
6351
6899
  }
6352
6900
  }
6353
6901
  return true;
@@ -6371,9 +6919,9 @@ function registerCheckCommand(program2) {
6371
6919
  } catch (err) {
6372
6920
  const tempConfig = getConfig();
6373
6921
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
6374
- const logPath = import_path15.default.join(import_os12.default.homedir(), ".node9", "hook-debug.log");
6922
+ const logPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
6375
6923
  const errMsg = err instanceof Error ? err.message : String(err);
6376
- import_fs14.default.appendFileSync(
6924
+ import_fs16.default.appendFileSync(
6377
6925
  logPath,
6378
6926
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
6379
6927
  RAW: ${raw}
@@ -6384,10 +6932,10 @@ RAW: ${raw}
6384
6932
  }
6385
6933
  const config = getConfig(payload.cwd || void 0);
6386
6934
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
6387
- const logPath = import_path15.default.join(import_os12.default.homedir(), ".node9", "hook-debug.log");
6388
- if (!import_fs14.default.existsSync(import_path15.default.dirname(logPath)))
6389
- import_fs14.default.mkdirSync(import_path15.default.dirname(logPath), { recursive: true });
6390
- import_fs14.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
6935
+ const logPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
6936
+ if (!import_fs16.default.existsSync(import_path18.default.dirname(logPath)))
6937
+ import_fs16.default.mkdirSync(import_path18.default.dirname(logPath), { recursive: true });
6938
+ import_fs16.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
6391
6939
  `);
6392
6940
  }
6393
6941
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -6400,8 +6948,8 @@ RAW: ${raw}
6400
6948
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
6401
6949
  let ttyFd = null;
6402
6950
  try {
6403
- ttyFd = import_fs14.default.openSync("/dev/tty", "w");
6404
- const writeTty = (line) => import_fs14.default.writeSync(ttyFd, line + "\n");
6951
+ ttyFd = import_fs16.default.openSync("/dev/tty", "w");
6952
+ const writeTty = (line) => import_fs16.default.writeSync(ttyFd, line + "\n");
6405
6953
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
6406
6954
  writeTty(import_chalk5.default.bgRed.white.bold(`
6407
6955
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -6417,7 +6965,7 @@ RAW: ${raw}
6417
6965
  } finally {
6418
6966
  if (ttyFd !== null)
6419
6967
  try {
6420
- import_fs14.default.closeSync(ttyFd);
6968
+ import_fs16.default.closeSync(ttyFd);
6421
6969
  } catch {
6422
6970
  }
6423
6971
  }
@@ -6448,7 +6996,7 @@ RAW: ${raw}
6448
6996
  if (shouldSnapshot(toolName, toolInput, config)) {
6449
6997
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
6450
6998
  }
6451
- const safeCwdForAuth = typeof payload.cwd === "string" && import_path15.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
6999
+ const safeCwdForAuth = typeof payload.cwd === "string" && import_path18.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
6452
7000
  const result = await authorizeHeadless(toolName, toolInput, meta, {
6453
7001
  cwd: safeCwdForAuth
6454
7002
  });
@@ -6460,12 +7008,12 @@ RAW: ${raw}
6460
7008
  }
6461
7009
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
6462
7010
  try {
6463
- const tty = import_fs14.default.openSync("/dev/tty", "w");
6464
- import_fs14.default.writeSync(
7011
+ const tty = import_fs16.default.openSync("/dev/tty", "w");
7012
+ import_fs16.default.writeSync(
6465
7013
  tty,
6466
7014
  import_chalk5.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
6467
7015
  );
6468
- import_fs14.default.closeSync(tty);
7016
+ import_fs16.default.closeSync(tty);
6469
7017
  } catch {
6470
7018
  }
6471
7019
  const daemonReady = await autoStartDaemonAndWait();
@@ -6492,9 +7040,9 @@ RAW: ${raw}
6492
7040
  });
6493
7041
  } catch (err) {
6494
7042
  if (process.env.NODE9_DEBUG === "1") {
6495
- const logPath = import_path15.default.join(import_os12.default.homedir(), ".node9", "hook-debug.log");
7043
+ const logPath = import_path18.default.join(import_os14.default.homedir(), ".node9", "hook-debug.log");
6496
7044
  const errMsg = err instanceof Error ? err.message : String(err);
6497
- import_fs14.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
7045
+ import_fs16.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
6498
7046
  `);
6499
7047
  }
6500
7048
  process.exit(0);
@@ -6528,9 +7076,9 @@ RAW: ${raw}
6528
7076
  }
6529
7077
 
6530
7078
  // src/cli/commands/log.ts
6531
- var import_fs15 = __toESM(require("fs"));
6532
- var import_path16 = __toESM(require("path"));
6533
- var import_os13 = __toESM(require("os"));
7079
+ var import_fs17 = __toESM(require("fs"));
7080
+ var import_path19 = __toESM(require("path"));
7081
+ var import_os15 = __toESM(require("os"));
6534
7082
  init_audit();
6535
7083
  init_config();
6536
7084
  init_policy();
@@ -6552,11 +7100,11 @@ function registerLogCommand(program2) {
6552
7100
  decision: "allowed",
6553
7101
  source: "post-hook"
6554
7102
  };
6555
- const logPath = import_path16.default.join(import_os13.default.homedir(), ".node9", "audit.log");
6556
- if (!import_fs15.default.existsSync(import_path16.default.dirname(logPath)))
6557
- import_fs15.default.mkdirSync(import_path16.default.dirname(logPath), { recursive: true });
6558
- import_fs15.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
6559
- const safeCwd = typeof payload.cwd === "string" && import_path16.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
7103
+ const logPath = import_path19.default.join(import_os15.default.homedir(), ".node9", "audit.log");
7104
+ if (!import_fs17.default.existsSync(import_path19.default.dirname(logPath)))
7105
+ import_fs17.default.mkdirSync(import_path19.default.dirname(logPath), { recursive: true });
7106
+ import_fs17.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
7107
+ const safeCwd = typeof payload.cwd === "string" && import_path19.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
6560
7108
  const config = getConfig(safeCwd);
6561
7109
  if (shouldSnapshot(tool, {}, config)) {
6562
7110
  await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
@@ -6565,9 +7113,9 @@ function registerLogCommand(program2) {
6565
7113
  const msg = err instanceof Error ? err.message : String(err);
6566
7114
  process.stderr.write(`[Node9] audit log error: ${msg}
6567
7115
  `);
6568
- const debugPath = import_path16.default.join(import_os13.default.homedir(), ".node9", "hook-debug.log");
7116
+ const debugPath = import_path19.default.join(import_os15.default.homedir(), ".node9", "hook-debug.log");
6569
7117
  try {
6570
- import_fs15.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
7118
+ import_fs17.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
6571
7119
  `);
6572
7120
  } catch {
6573
7121
  }
@@ -6871,14 +7419,14 @@ function registerConfigShowCommand(program2) {
6871
7419
 
6872
7420
  // src/cli/commands/doctor.ts
6873
7421
  var import_chalk7 = __toESM(require("chalk"));
6874
- var import_fs16 = __toESM(require("fs"));
6875
- var import_path17 = __toESM(require("path"));
6876
- var import_os14 = __toESM(require("os"));
7422
+ var import_fs18 = __toESM(require("fs"));
7423
+ var import_path20 = __toESM(require("path"));
7424
+ var import_os16 = __toESM(require("os"));
6877
7425
  var import_child_process9 = require("child_process");
6878
7426
  init_daemon();
6879
7427
  function registerDoctorCommand(program2, version2) {
6880
7428
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
6881
- const homeDir2 = import_os14.default.homedir();
7429
+ const homeDir2 = import_os16.default.homedir();
6882
7430
  let failures = 0;
6883
7431
  function pass(msg) {
6884
7432
  console.log(import_chalk7.default.green(" \u2705 ") + msg);
@@ -6927,10 +7475,10 @@ function registerDoctorCommand(program2, version2) {
6927
7475
  );
6928
7476
  }
6929
7477
  section("Configuration");
6930
- const globalConfigPath = import_path17.default.join(homeDir2, ".node9", "config.json");
6931
- if (import_fs16.default.existsSync(globalConfigPath)) {
7478
+ const globalConfigPath = import_path20.default.join(homeDir2, ".node9", "config.json");
7479
+ if (import_fs18.default.existsSync(globalConfigPath)) {
6932
7480
  try {
6933
- JSON.parse(import_fs16.default.readFileSync(globalConfigPath, "utf-8"));
7481
+ JSON.parse(import_fs18.default.readFileSync(globalConfigPath, "utf-8"));
6934
7482
  pass("~/.node9/config.json found and valid");
6935
7483
  } catch {
6936
7484
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -6938,10 +7486,10 @@ function registerDoctorCommand(program2, version2) {
6938
7486
  } else {
6939
7487
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
6940
7488
  }
6941
- const projectConfigPath = import_path17.default.join(process.cwd(), "node9.config.json");
6942
- if (import_fs16.default.existsSync(projectConfigPath)) {
7489
+ const projectConfigPath = import_path20.default.join(process.cwd(), "node9.config.json");
7490
+ if (import_fs18.default.existsSync(projectConfigPath)) {
6943
7491
  try {
6944
- JSON.parse(import_fs16.default.readFileSync(projectConfigPath, "utf-8"));
7492
+ JSON.parse(import_fs18.default.readFileSync(projectConfigPath, "utf-8"));
6945
7493
  pass("node9.config.json found and valid (project)");
6946
7494
  } catch {
6947
7495
  fail(
@@ -6950,8 +7498,8 @@ function registerDoctorCommand(program2, version2) {
6950
7498
  );
6951
7499
  }
6952
7500
  }
6953
- const credsPath = import_path17.default.join(homeDir2, ".node9", "credentials.json");
6954
- if (import_fs16.default.existsSync(credsPath)) {
7501
+ const credsPath = import_path20.default.join(homeDir2, ".node9", "credentials.json");
7502
+ if (import_fs18.default.existsSync(credsPath)) {
6955
7503
  pass("Cloud credentials found (~/.node9/credentials.json)");
6956
7504
  } else {
6957
7505
  warn(
@@ -6960,10 +7508,10 @@ function registerDoctorCommand(program2, version2) {
6960
7508
  );
6961
7509
  }
6962
7510
  section("Agent Hooks");
6963
- const claudeSettingsPath = import_path17.default.join(homeDir2, ".claude", "settings.json");
6964
- if (import_fs16.default.existsSync(claudeSettingsPath)) {
7511
+ const claudeSettingsPath = import_path20.default.join(homeDir2, ".claude", "settings.json");
7512
+ if (import_fs18.default.existsSync(claudeSettingsPath)) {
6965
7513
  try {
6966
- const cs = JSON.parse(import_fs16.default.readFileSync(claudeSettingsPath, "utf-8"));
7514
+ const cs = JSON.parse(import_fs18.default.readFileSync(claudeSettingsPath, "utf-8"));
6967
7515
  const hasHook = cs.hooks?.PreToolUse?.some(
6968
7516
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
6969
7517
  );
@@ -6979,10 +7527,10 @@ function registerDoctorCommand(program2, version2) {
6979
7527
  } else {
6980
7528
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
6981
7529
  }
6982
- const geminiSettingsPath = import_path17.default.join(homeDir2, ".gemini", "settings.json");
6983
- if (import_fs16.default.existsSync(geminiSettingsPath)) {
7530
+ const geminiSettingsPath = import_path20.default.join(homeDir2, ".gemini", "settings.json");
7531
+ if (import_fs18.default.existsSync(geminiSettingsPath)) {
6984
7532
  try {
6985
- const gs = JSON.parse(import_fs16.default.readFileSync(geminiSettingsPath, "utf-8"));
7533
+ const gs = JSON.parse(import_fs18.default.readFileSync(geminiSettingsPath, "utf-8"));
6986
7534
  const hasHook = gs.hooks?.BeforeTool?.some(
6987
7535
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
6988
7536
  );
@@ -6998,10 +7546,10 @@ function registerDoctorCommand(program2, version2) {
6998
7546
  } else {
6999
7547
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
7000
7548
  }
7001
- const cursorHooksPath = import_path17.default.join(homeDir2, ".cursor", "hooks.json");
7002
- if (import_fs16.default.existsSync(cursorHooksPath)) {
7549
+ const cursorHooksPath = import_path20.default.join(homeDir2, ".cursor", "hooks.json");
7550
+ if (import_fs18.default.existsSync(cursorHooksPath)) {
7003
7551
  try {
7004
- const cur = JSON.parse(import_fs16.default.readFileSync(cursorHooksPath, "utf-8"));
7552
+ const cur = JSON.parse(import_fs18.default.readFileSync(cursorHooksPath, "utf-8"));
7005
7553
  const hasHook = cur.hooks?.preToolUse?.some(
7006
7554
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
7007
7555
  );
@@ -7039,9 +7587,9 @@ function registerDoctorCommand(program2, version2) {
7039
7587
 
7040
7588
  // src/cli/commands/audit.ts
7041
7589
  var import_chalk8 = __toESM(require("chalk"));
7042
- var import_fs17 = __toESM(require("fs"));
7043
- var import_path18 = __toESM(require("path"));
7044
- var import_os15 = __toESM(require("os"));
7590
+ var import_fs19 = __toESM(require("fs"));
7591
+ var import_path21 = __toESM(require("path"));
7592
+ var import_os17 = __toESM(require("os"));
7045
7593
  function formatRelativeTime(timestamp) {
7046
7594
  const diff = Date.now() - new Date(timestamp).getTime();
7047
7595
  const sec = Math.floor(diff / 1e3);
@@ -7054,14 +7602,14 @@ function formatRelativeTime(timestamp) {
7054
7602
  }
7055
7603
  function registerAuditCommand(program2) {
7056
7604
  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) => {
7057
- const logPath = import_path18.default.join(import_os15.default.homedir(), ".node9", "audit.log");
7058
- if (!import_fs17.default.existsSync(logPath)) {
7605
+ const logPath = import_path21.default.join(import_os17.default.homedir(), ".node9", "audit.log");
7606
+ if (!import_fs19.default.existsSync(logPath)) {
7059
7607
  console.log(
7060
7608
  import_chalk8.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
7061
7609
  );
7062
7610
  return;
7063
7611
  }
7064
- const raw = import_fs17.default.readFileSync(logPath, "utf-8");
7612
+ const raw = import_fs19.default.readFileSync(logPath, "utf-8");
7065
7613
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
7066
7614
  let entries = lines.flatMap((line) => {
7067
7615
  try {
@@ -7179,9 +7727,9 @@ function registerDaemonCommand(program2) {
7179
7727
 
7180
7728
  // src/cli/commands/status.ts
7181
7729
  var import_chalk10 = __toESM(require("chalk"));
7182
- var import_fs18 = __toESM(require("fs"));
7183
- var import_path19 = __toESM(require("path"));
7184
- var import_os16 = __toESM(require("os"));
7730
+ var import_fs20 = __toESM(require("fs"));
7731
+ var import_path22 = __toESM(require("path"));
7732
+ var import_os18 = __toESM(require("os"));
7185
7733
  init_core();
7186
7734
  init_daemon();
7187
7735
  function registerStatusCommand(program2) {
@@ -7218,13 +7766,13 @@ function registerStatusCommand(program2) {
7218
7766
  console.log("");
7219
7767
  const modeLabel = settings.mode === "audit" ? import_chalk10.default.blue("audit") : settings.mode === "strict" ? import_chalk10.default.red("strict") : import_chalk10.default.white("standard");
7220
7768
  console.log(` Mode: ${modeLabel}`);
7221
- const projectConfig = import_path19.default.join(process.cwd(), "node9.config.json");
7222
- const globalConfig = import_path19.default.join(import_os16.default.homedir(), ".node9", "config.json");
7769
+ const projectConfig = import_path22.default.join(process.cwd(), "node9.config.json");
7770
+ const globalConfig = import_path22.default.join(import_os18.default.homedir(), ".node9", "config.json");
7223
7771
  console.log(
7224
- ` Local: ${import_fs18.default.existsSync(projectConfig) ? import_chalk10.default.green("Active (node9.config.json)") : import_chalk10.default.gray("Not present")}`
7772
+ ` Local: ${import_fs20.default.existsSync(projectConfig) ? import_chalk10.default.green("Active (node9.config.json)") : import_chalk10.default.gray("Not present")}`
7225
7773
  );
7226
7774
  console.log(
7227
- ` Global: ${import_fs18.default.existsSync(globalConfig) ? import_chalk10.default.green("Active (~/.node9/config.json)") : import_chalk10.default.gray("Not present")}`
7775
+ ` Global: ${import_fs20.default.existsSync(globalConfig) ? import_chalk10.default.green("Active (~/.node9/config.json)") : import_chalk10.default.gray("Not present")}`
7228
7776
  );
7229
7777
  if (mergedConfig.policy.sandboxPaths.length > 0) {
7230
7778
  console.log(
@@ -7403,6 +7951,7 @@ var import_chalk13 = __toESM(require("chalk"));
7403
7951
  var import_child_process12 = require("child_process");
7404
7952
  var import_execa3 = require("execa");
7405
7953
  init_orchestrator();
7954
+ init_provenance();
7406
7955
  function sanitize4(value) {
7407
7956
  return value.replace(/[\x00-\x1F\x7F]/g, "");
7408
7957
  }
@@ -7415,7 +7964,7 @@ function extractMcpServer(toolName) {
7415
7964
  const match = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
7416
7965
  return match?.[1];
7417
7966
  }
7418
- function tokenize2(cmd) {
7967
+ function tokenize3(cmd) {
7419
7968
  const tokens = [];
7420
7969
  let current = "";
7421
7970
  let inDouble = false;
@@ -7450,7 +7999,7 @@ function tokenize2(cmd) {
7450
7999
  return tokens;
7451
8000
  }
7452
8001
  async function runMcpGateway(upstreamCommand) {
7453
- const commandParts = tokenize2(upstreamCommand);
8002
+ const commandParts = tokenize3(upstreamCommand);
7454
8003
  const cmd = commandParts[0];
7455
8004
  const cmdArgs = commandParts.slice(1);
7456
8005
  let executable = cmd;
@@ -7459,6 +8008,15 @@ async function runMcpGateway(upstreamCommand) {
7459
8008
  if (stdout) executable = stdout.trim();
7460
8009
  } catch {
7461
8010
  }
8011
+ const prov = checkProvenance(executable);
8012
+ if (prov.trustLevel === "suspect") {
8013
+ console.error(
8014
+ import_chalk13.default.red(
8015
+ `\u26A0\uFE0F Node9: Upstream MCP server binary is suspect \u2014 ${prov.reason} (${prov.resolvedPath})`
8016
+ )
8017
+ );
8018
+ console.error(import_chalk13.default.red(" Verify this binary is trusted before proceeding."));
8019
+ }
7462
8020
  console.error(import_chalk13.default.green(`\u{1F680} Node9 MCP Gateway: Monitoring [${upstreamCommand}]`));
7463
8021
  const UPSTREAM_INJECTOR_VARS = /* @__PURE__ */ new Set([
7464
8022
  "NODE_OPTIONS",
@@ -7598,22 +8156,77 @@ function registerMcpGatewayCommand(program2) {
7598
8156
  });
7599
8157
  }
7600
8158
 
8159
+ // src/cli/commands/trust.ts
8160
+ var import_chalk14 = __toESM(require("chalk"));
8161
+ init_trusted_hosts();
8162
+ function isValidHost(host) {
8163
+ return /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(host);
8164
+ }
8165
+ function registerTrustCommand(program2) {
8166
+ const trustCmd = program2.command("trust").description("Manage trusted network hosts (reduces approval friction for known destinations)");
8167
+ trustCmd.command("add <host>").description("Add a trusted host \u2014 pipe-chain blocks targeting this host are downgraded").action((host) => {
8168
+ const normalized = normalizeHost(host.trim());
8169
+ if (!isValidHost(normalized)) {
8170
+ console.error(
8171
+ import_chalk14.default.red(`
8172
+ \u274C Invalid host: "${host}"
8173
+ `) + import_chalk14.default.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
8174
+ );
8175
+ process.exit(1);
8176
+ }
8177
+ addTrustedHost(normalized);
8178
+ console.log(import_chalk14.default.green(`
8179
+ \u2705 ${normalized} added to trusted hosts.`));
8180
+ console.log(
8181
+ import_chalk14.default.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
8182
+ );
8183
+ });
8184
+ trustCmd.command("remove <host>").description("Remove a trusted host").action((host) => {
8185
+ const normalized = normalizeHost(host.trim());
8186
+ const removed = removeTrustedHost(normalized);
8187
+ if (!removed) {
8188
+ console.error(import_chalk14.default.yellow(`
8189
+ \u26A0\uFE0F "${normalized}" is not in the trusted hosts list.
8190
+ `));
8191
+ process.exit(1);
8192
+ }
8193
+ console.log(import_chalk14.default.green(`
8194
+ \u2705 ${normalized} removed from trusted hosts.
8195
+ `));
8196
+ });
8197
+ trustCmd.command("list").description("Show all trusted hosts").action(() => {
8198
+ const hosts = readTrustedHosts();
8199
+ if (hosts.length === 0) {
8200
+ console.log(import_chalk14.default.gray("\n No trusted hosts configured.\n"));
8201
+ console.log(` Add one: ${import_chalk14.default.cyan("node9 trust add api.mycompany.com")}
8202
+ `);
8203
+ return;
8204
+ }
8205
+ console.log(import_chalk14.default.bold("\n\u{1F513} Trusted Hosts\n"));
8206
+ for (const entry of hosts) {
8207
+ const date = new Date(entry.addedAt).toLocaleDateString();
8208
+ console.log(` ${import_chalk14.default.cyan(entry.host.padEnd(40))} ${import_chalk14.default.gray(`added ${date}`)}`);
8209
+ }
8210
+ console.log("");
8211
+ });
8212
+ }
8213
+
7601
8214
  // src/cli.ts
7602
8215
  var { version } = JSON.parse(
7603
- import_fs20.default.readFileSync(import_path21.default.join(__dirname, "../package.json"), "utf-8")
8216
+ import_fs22.default.readFileSync(import_path24.default.join(__dirname, "../package.json"), "utf-8")
7604
8217
  );
7605
8218
  var program = new import_commander.Command();
7606
8219
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
7607
8220
  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) => {
7608
8221
  const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
7609
- const credPath = import_path21.default.join(import_os18.default.homedir(), ".node9", "credentials.json");
7610
- if (!import_fs20.default.existsSync(import_path21.default.dirname(credPath)))
7611
- import_fs20.default.mkdirSync(import_path21.default.dirname(credPath), { recursive: true });
8222
+ const credPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "credentials.json");
8223
+ if (!import_fs22.default.existsSync(import_path24.default.dirname(credPath)))
8224
+ import_fs22.default.mkdirSync(import_path24.default.dirname(credPath), { recursive: true });
7612
8225
  const profileName = options.profile || "default";
7613
8226
  let existingCreds = {};
7614
8227
  try {
7615
- if (import_fs20.default.existsSync(credPath)) {
7616
- const raw = JSON.parse(import_fs20.default.readFileSync(credPath, "utf-8"));
8228
+ if (import_fs22.default.existsSync(credPath)) {
8229
+ const raw = JSON.parse(import_fs22.default.readFileSync(credPath, "utf-8"));
7617
8230
  if (raw.apiKey) {
7618
8231
  existingCreds = {
7619
8232
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
@@ -7625,13 +8238,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
7625
8238
  } catch {
7626
8239
  }
7627
8240
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
7628
- import_fs20.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
8241
+ import_fs22.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
7629
8242
  if (profileName === "default") {
7630
- const configPath = import_path21.default.join(import_os18.default.homedir(), ".node9", "config.json");
8243
+ const configPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "config.json");
7631
8244
  let config = {};
7632
8245
  try {
7633
- if (import_fs20.default.existsSync(configPath))
7634
- config = JSON.parse(import_fs20.default.readFileSync(configPath, "utf-8"));
8246
+ if (import_fs22.default.existsSync(configPath))
8247
+ config = JSON.parse(import_fs22.default.readFileSync(configPath, "utf-8"));
7635
8248
  } catch {
7636
8249
  }
7637
8250
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -7646,36 +8259,36 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
7646
8259
  approvers.cloud = false;
7647
8260
  }
7648
8261
  s.approvers = approvers;
7649
- if (!import_fs20.default.existsSync(import_path21.default.dirname(configPath)))
7650
- import_fs20.default.mkdirSync(import_path21.default.dirname(configPath), { recursive: true });
7651
- import_fs20.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
8262
+ if (!import_fs22.default.existsSync(import_path24.default.dirname(configPath)))
8263
+ import_fs22.default.mkdirSync(import_path24.default.dirname(configPath), { recursive: true });
8264
+ import_fs22.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
7652
8265
  }
7653
8266
  if (options.profile && profileName !== "default") {
7654
- console.log(import_chalk15.default.green(`\u2705 Profile "${profileName}" saved`));
7655
- console.log(import_chalk15.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
8267
+ console.log(import_chalk16.default.green(`\u2705 Profile "${profileName}" saved`));
8268
+ console.log(import_chalk16.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
7656
8269
  } else if (options.local) {
7657
- console.log(import_chalk15.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
7658
- console.log(import_chalk15.default.gray(` All decisions stay on this machine.`));
8270
+ console.log(import_chalk16.default.green(`\u2705 Privacy mode \u{1F6E1}\uFE0F`));
8271
+ console.log(import_chalk16.default.gray(` All decisions stay on this machine.`));
7659
8272
  } else {
7660
- console.log(import_chalk15.default.green(`\u2705 Logged in \u2014 agent mode`));
7661
- console.log(import_chalk15.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
8273
+ console.log(import_chalk16.default.green(`\u2705 Logged in \u2014 agent mode`));
8274
+ console.log(import_chalk16.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
7662
8275
  }
7663
8276
  });
7664
8277
  program.command("addto").description("Integrate Node9 with an AI agent").addHelpText("after", "\n Supported targets: claude gemini cursor").argument("<target>", "The agent to protect: claude | gemini | cursor").action(async (target) => {
7665
8278
  if (target === "gemini") return await setupGemini();
7666
8279
  if (target === "claude") return await setupClaude();
7667
8280
  if (target === "cursor") return await setupCursor();
7668
- console.error(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
8281
+ console.error(import_chalk16.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
7669
8282
  process.exit(1);
7670
8283
  });
7671
8284
  program.command("setup").description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText("after", "\n Supported targets: claude gemini cursor").argument("[target]", "The agent to protect: claude | gemini | cursor").action(async (target) => {
7672
8285
  if (!target) {
7673
- console.log(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
7674
- console.log(" Usage: " + import_chalk15.default.white("node9 setup <target>") + "\n");
8286
+ console.log(import_chalk16.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
8287
+ console.log(" Usage: " + import_chalk16.default.white("node9 setup <target>") + "\n");
7675
8288
  console.log(" Targets:");
7676
- console.log(" " + import_chalk15.default.green("claude") + " \u2014 Claude Code (hook mode)");
7677
- console.log(" " + import_chalk15.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
7678
- console.log(" " + import_chalk15.default.green("cursor") + " \u2014 Cursor (hook mode)");
8289
+ console.log(" " + import_chalk16.default.green("claude") + " \u2014 Claude Code (hook mode)");
8290
+ console.log(" " + import_chalk16.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
8291
+ console.log(" " + import_chalk16.default.green("cursor") + " \u2014 Cursor (hook mode)");
7679
8292
  console.log("");
7680
8293
  return;
7681
8294
  }
@@ -7683,7 +8296,7 @@ program.command("setup").description('Alias for "addto" \u2014 integrate Node9 w
7683
8296
  if (t === "gemini") return await setupGemini();
7684
8297
  if (t === "claude") return await setupClaude();
7685
8298
  if (t === "cursor") return await setupCursor();
7686
- console.error(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
8299
+ console.error(import_chalk16.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
7687
8300
  process.exit(1);
7688
8301
  });
7689
8302
  program.command("removefrom").description("Remove Node9 hooks from an AI agent configuration").addHelpText("after", "\n Supported targets: claude gemini cursor").argument("<target>", "The agent to remove from: claude | gemini | cursor").action((target) => {
@@ -7692,30 +8305,30 @@ program.command("removefrom").description("Remove Node9 hooks from an AI agent c
7692
8305
  else if (target === "gemini") fn = teardownGemini;
7693
8306
  else if (target === "cursor") fn = teardownCursor;
7694
8307
  else {
7695
- console.error(import_chalk15.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
8308
+ console.error(import_chalk16.default.red(`Unknown target: "${target}". Supported: claude, gemini, cursor`));
7696
8309
  process.exit(1);
7697
8310
  }
7698
- console.log(import_chalk15.default.cyan(`
8311
+ console.log(import_chalk16.default.cyan(`
7699
8312
  \u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
7700
8313
  `));
7701
8314
  try {
7702
8315
  fn();
7703
8316
  } catch (err) {
7704
- console.error(import_chalk15.default.red(` \u26A0\uFE0F Failed: ${err instanceof Error ? err.message : String(err)}`));
8317
+ console.error(import_chalk16.default.red(` \u26A0\uFE0F Failed: ${err instanceof Error ? err.message : String(err)}`));
7705
8318
  process.exit(1);
7706
8319
  }
7707
- console.log(import_chalk15.default.gray("\n Restart the agent for changes to take effect."));
8320
+ console.log(import_chalk16.default.gray("\n Restart the agent for changes to take effect."));
7708
8321
  });
7709
8322
  program.command("uninstall").description("Remove all Node9 hooks and optionally delete config files").option("--purge", "Also delete ~/.node9/ directory (config, audit log, credentials)").action(async (options) => {
7710
- console.log(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
7711
- console.log(import_chalk15.default.bold("Stopping daemon..."));
8323
+ console.log(import_chalk16.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
8324
+ console.log(import_chalk16.default.bold("Stopping daemon..."));
7712
8325
  try {
7713
8326
  stopDaemon();
7714
- console.log(import_chalk15.default.green(" \u2705 Daemon stopped"));
8327
+ console.log(import_chalk16.default.green(" \u2705 Daemon stopped"));
7715
8328
  } catch {
7716
- console.log(import_chalk15.default.blue(" \u2139\uFE0F Daemon was not running"));
8329
+ console.log(import_chalk16.default.blue(" \u2139\uFE0F Daemon was not running"));
7717
8330
  }
7718
- console.log(import_chalk15.default.bold("\nRemoving hooks..."));
8331
+ console.log(import_chalk16.default.bold("\nRemoving hooks..."));
7719
8332
  let teardownFailed = false;
7720
8333
  for (const [label, fn] of [
7721
8334
  ["Claude", teardownClaude],
@@ -7727,45 +8340,45 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
7727
8340
  } catch (err) {
7728
8341
  teardownFailed = true;
7729
8342
  console.error(
7730
- import_chalk15.default.red(
8343
+ import_chalk16.default.red(
7731
8344
  ` \u26A0\uFE0F Failed to remove ${label} hooks: ${err instanceof Error ? err.message : String(err)}`
7732
8345
  )
7733
8346
  );
7734
8347
  }
7735
8348
  }
7736
8349
  if (options.purge) {
7737
- const node9Dir = import_path21.default.join(import_os18.default.homedir(), ".node9");
7738
- if (import_fs20.default.existsSync(node9Dir)) {
8350
+ const node9Dir = import_path24.default.join(import_os20.default.homedir(), ".node9");
8351
+ if (import_fs22.default.existsSync(node9Dir)) {
7739
8352
  const confirmed = await (0, import_prompts3.confirm)({
7740
8353
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
7741
8354
  default: false
7742
8355
  });
7743
8356
  if (confirmed) {
7744
- import_fs20.default.rmSync(node9Dir, { recursive: true });
7745
- if (import_fs20.default.existsSync(node9Dir)) {
8357
+ import_fs22.default.rmSync(node9Dir, { recursive: true });
8358
+ if (import_fs22.default.existsSync(node9Dir)) {
7746
8359
  console.error(
7747
- import_chalk15.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
8360
+ import_chalk16.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
7748
8361
  );
7749
8362
  } else {
7750
- console.log(import_chalk15.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
8363
+ console.log(import_chalk16.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
7751
8364
  }
7752
8365
  } else {
7753
- console.log(import_chalk15.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
8366
+ console.log(import_chalk16.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
7754
8367
  }
7755
8368
  } else {
7756
- console.log(import_chalk15.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
8369
+ console.log(import_chalk16.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
7757
8370
  }
7758
8371
  } else {
7759
8372
  console.log(
7760
- import_chalk15.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
8373
+ import_chalk16.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
7761
8374
  );
7762
8375
  }
7763
8376
  if (teardownFailed) {
7764
- console.error(import_chalk15.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
8377
+ console.error(import_chalk16.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
7765
8378
  process.exit(1);
7766
8379
  }
7767
- console.log(import_chalk15.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
7768
- console.log(import_chalk15.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
8380
+ console.log(import_chalk16.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
8381
+ console.log(import_chalk16.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
7769
8382
  });
7770
8383
  registerDoctorCommand(program, version);
7771
8384
  program.command("explain").description(
@@ -7778,7 +8391,7 @@ program.command("explain").description(
7778
8391
  try {
7779
8392
  args = JSON.parse(trimmed);
7780
8393
  } catch {
7781
- console.error(import_chalk15.default.red(`
8394
+ console.error(import_chalk16.default.red(`
7782
8395
  \u274C Invalid JSON: ${trimmed}
7783
8396
  `));
7784
8397
  process.exit(1);
@@ -7789,63 +8402,63 @@ program.command("explain").description(
7789
8402
  }
7790
8403
  const result = await explainPolicy(tool, args);
7791
8404
  console.log("");
7792
- console.log(import_chalk15.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
8405
+ console.log(import_chalk16.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
7793
8406
  console.log("");
7794
- console.log(` ${import_chalk15.default.bold("Tool:")} ${import_chalk15.default.white(result.tool)}`);
8407
+ console.log(` ${import_chalk16.default.bold("Tool:")} ${import_chalk16.default.white(result.tool)}`);
7795
8408
  if (argsRaw) {
7796
8409
  const preview = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
7797
- console.log(` ${import_chalk15.default.bold("Input:")} ${import_chalk15.default.gray(preview)}`);
8410
+ console.log(` ${import_chalk16.default.bold("Input:")} ${import_chalk16.default.gray(preview)}`);
7798
8411
  }
7799
8412
  console.log("");
7800
- console.log(import_chalk15.default.bold("Config Sources (Waterfall):"));
8413
+ console.log(import_chalk16.default.bold("Config Sources (Waterfall):"));
7801
8414
  for (const tier of result.waterfall) {
7802
- const num = import_chalk15.default.gray(` ${tier.tier}.`);
8415
+ const num = import_chalk16.default.gray(` ${tier.tier}.`);
7803
8416
  const label = tier.label.padEnd(16);
7804
8417
  let statusStr;
7805
8418
  if (tier.tier === 1) {
7806
- statusStr = import_chalk15.default.gray(tier.note ?? "");
8419
+ statusStr = import_chalk16.default.gray(tier.note ?? "");
7807
8420
  } else if (tier.status === "active") {
7808
- const loc = tier.path ? import_chalk15.default.gray(tier.path) : "";
7809
- const note = tier.note ? import_chalk15.default.gray(`(${tier.note})`) : "";
7810
- statusStr = import_chalk15.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
8421
+ const loc = tier.path ? import_chalk16.default.gray(tier.path) : "";
8422
+ const note = tier.note ? import_chalk16.default.gray(`(${tier.note})`) : "";
8423
+ statusStr = import_chalk16.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
7811
8424
  } else {
7812
- statusStr = import_chalk15.default.gray("\u25CB " + (tier.note ?? "not found"));
8425
+ statusStr = import_chalk16.default.gray("\u25CB " + (tier.note ?? "not found"));
7813
8426
  }
7814
- console.log(`${num} ${import_chalk15.default.white(label)} ${statusStr}`);
8427
+ console.log(`${num} ${import_chalk16.default.white(label)} ${statusStr}`);
7815
8428
  }
7816
8429
  console.log("");
7817
- console.log(import_chalk15.default.bold("Policy Evaluation:"));
8430
+ console.log(import_chalk16.default.bold("Policy Evaluation:"));
7818
8431
  for (const step of result.steps) {
7819
8432
  const isFinal = step.isFinal;
7820
8433
  let icon;
7821
- if (step.outcome === "allow") icon = import_chalk15.default.green(" \u2705");
7822
- else if (step.outcome === "review") icon = import_chalk15.default.red(" \u{1F534}");
7823
- else if (step.outcome === "skip") icon = import_chalk15.default.gray(" \u2500 ");
7824
- else icon = import_chalk15.default.gray(" \u25CB ");
8434
+ if (step.outcome === "allow") icon = import_chalk16.default.green(" \u2705");
8435
+ else if (step.outcome === "review") icon = import_chalk16.default.red(" \u{1F534}");
8436
+ else if (step.outcome === "skip") icon = import_chalk16.default.gray(" \u2500 ");
8437
+ else icon = import_chalk16.default.gray(" \u25CB ");
7825
8438
  const name = step.name.padEnd(18);
7826
- const nameStr = isFinal ? import_chalk15.default.white.bold(name) : import_chalk15.default.white(name);
7827
- const detail = isFinal ? import_chalk15.default.white(step.detail) : import_chalk15.default.gray(step.detail);
7828
- const arrow = isFinal ? import_chalk15.default.yellow(" \u2190 STOP") : "";
8439
+ const nameStr = isFinal ? import_chalk16.default.white.bold(name) : import_chalk16.default.white(name);
8440
+ const detail = isFinal ? import_chalk16.default.white(step.detail) : import_chalk16.default.gray(step.detail);
8441
+ const arrow = isFinal ? import_chalk16.default.yellow(" \u2190 STOP") : "";
7829
8442
  console.log(`${icon} ${nameStr} ${detail}${arrow}`);
7830
8443
  }
7831
8444
  console.log("");
7832
8445
  if (result.decision === "allow") {
7833
- console.log(import_chalk15.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk15.default.gray(" \u2014 no approval needed"));
8446
+ console.log(import_chalk16.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk16.default.gray(" \u2014 no approval needed"));
7834
8447
  } else {
7835
8448
  console.log(
7836
- import_chalk15.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk15.default.gray(" \u2014 human approval required")
8449
+ import_chalk16.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk16.default.gray(" \u2014 human approval required")
7837
8450
  );
7838
8451
  if (result.blockedByLabel) {
7839
- console.log(import_chalk15.default.gray(` Reason: ${result.blockedByLabel}`));
8452
+ console.log(import_chalk16.default.gray(` Reason: ${result.blockedByLabel}`));
7840
8453
  }
7841
8454
  }
7842
8455
  console.log("");
7843
8456
  });
7844
8457
  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) => {
7845
- const configPath = import_path21.default.join(import_os18.default.homedir(), ".node9", "config.json");
7846
- if (import_fs20.default.existsSync(configPath) && !options.force) {
7847
- console.log(import_chalk15.default.yellow(`\u2139\uFE0F Global config already exists: ${configPath}`));
7848
- console.log(import_chalk15.default.gray(` Run with --force to overwrite.`));
8458
+ const configPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "config.json");
8459
+ if (import_fs22.default.existsSync(configPath) && !options.force) {
8460
+ console.log(import_chalk16.default.yellow(`\u2139\uFE0F Global config already exists: ${configPath}`));
8461
+ console.log(import_chalk16.default.gray(` Run with --force to overwrite.`));
7849
8462
  return;
7850
8463
  }
7851
8464
  const requestedMode = options.mode.toLowerCase();
@@ -7857,13 +8470,13 @@ program.command("init").description("Create ~/.node9/config.json with default po
7857
8470
  mode: safeMode
7858
8471
  }
7859
8472
  };
7860
- const dir = import_path21.default.dirname(configPath);
7861
- if (!import_fs20.default.existsSync(dir)) import_fs20.default.mkdirSync(dir, { recursive: true });
7862
- import_fs20.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
7863
- console.log(import_chalk15.default.green(`\u2705 Global config created: ${configPath}`));
7864
- console.log(import_chalk15.default.cyan(` Mode set to: ${safeMode}`));
8473
+ const dir = import_path24.default.dirname(configPath);
8474
+ if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
8475
+ import_fs22.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2));
8476
+ console.log(import_chalk16.default.green(`\u2705 Global config created: ${configPath}`));
8477
+ console.log(import_chalk16.default.cyan(` Mode set to: ${safeMode}`));
7865
8478
  console.log(
7866
- import_chalk15.default.gray(` Undo Engine is ENABLED by default. Use 'node9 undo' to revert AI changes.`)
8479
+ import_chalk16.default.gray(` Undo Engine is ENABLED by default. Use 'node9 undo' to revert AI changes.`)
7867
8480
  );
7868
8481
  });
7869
8482
  registerAuditCommand(program);
@@ -7874,7 +8487,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
7874
8487
  try {
7875
8488
  await startTail2(options);
7876
8489
  } catch (err) {
7877
- console.error(import_chalk15.default.red(`\u274C ${err instanceof Error ? err.message : String(err)}`));
8490
+ console.error(import_chalk16.default.red(`\u274C ${err instanceof Error ? err.message : String(err)}`));
7878
8491
  process.exit(1);
7879
8492
  }
7880
8493
  });
@@ -7886,7 +8499,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
7886
8499
  const ms = parseDuration(options.duration);
7887
8500
  if (ms === null) {
7888
8501
  console.error(
7889
- import_chalk15.default.red(`
8502
+ import_chalk16.default.red(`
7890
8503
  \u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
7891
8504
  `)
7892
8505
  );
@@ -7894,20 +8507,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
7894
8507
  }
7895
8508
  pauseNode9(ms, options.duration);
7896
8509
  const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
7897
- console.log(import_chalk15.default.yellow(`
8510
+ console.log(import_chalk16.default.yellow(`
7898
8511
  \u23F8 Node9 paused until ${expiresAt}`));
7899
- console.log(import_chalk15.default.gray(` All tool calls will be allowed without review.`));
7900
- console.log(import_chalk15.default.gray(` Run "node9 resume" to re-enable early.
8512
+ console.log(import_chalk16.default.gray(` All tool calls will be allowed without review.`));
8513
+ console.log(import_chalk16.default.gray(` Run "node9 resume" to re-enable early.
7901
8514
  `));
7902
8515
  });
7903
8516
  program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
7904
8517
  const { paused } = checkPause();
7905
8518
  if (!paused) {
7906
- console.log(import_chalk15.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
8519
+ console.log(import_chalk16.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
7907
8520
  return;
7908
8521
  }
7909
8522
  resumeNode9();
7910
- console.log(import_chalk15.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
8523
+ console.log(import_chalk16.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
7911
8524
  });
7912
8525
  var HOOK_BASED_AGENTS = {
7913
8526
  claude: "claude",
@@ -7920,15 +8533,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
7920
8533
  if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
7921
8534
  const target = HOOK_BASED_AGENTS[firstArg2];
7922
8535
  console.error(
7923
- import_chalk15.default.yellow(`
8536
+ import_chalk16.default.yellow(`
7924
8537
  \u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
7925
8538
  );
7926
- console.error(import_chalk15.default.white(`
8539
+ console.error(import_chalk16.default.white(`
7927
8540
  "${target}" uses its own hook system. Use:`));
7928
8541
  console.error(
7929
- import_chalk15.default.green(` node9 addto ${target} `) + import_chalk15.default.gray("# one-time setup")
8542
+ import_chalk16.default.green(` node9 addto ${target} `) + import_chalk16.default.gray("# one-time setup")
7930
8543
  );
7931
- console.error(import_chalk15.default.green(` ${target} `) + import_chalk15.default.gray("# run normally"));
8544
+ console.error(import_chalk16.default.green(` ${target} `) + import_chalk16.default.gray("# run normally"));
7932
8545
  process.exit(1);
7933
8546
  }
7934
8547
  const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
@@ -7945,7 +8558,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
7945
8558
  }
7946
8559
  );
7947
8560
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
7948
- console.error(import_chalk15.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
8561
+ console.error(import_chalk16.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
7949
8562
  const daemonReady = await autoStartDaemonAndWait();
7950
8563
  if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
7951
8564
  }
@@ -7958,12 +8571,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
7958
8571
  }
7959
8572
  if (!result.approved) {
7960
8573
  console.error(
7961
- import_chalk15.default.red(`
8574
+ import_chalk16.default.red(`
7962
8575
  \u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
7963
8576
  );
7964
8577
  process.exit(1);
7965
8578
  }
7966
- console.error(import_chalk15.default.green("\n\u2705 Approved \u2014 running command...\n"));
8579
+ console.error(import_chalk16.default.green("\n\u2705 Approved \u2014 running command...\n"));
7967
8580
  await runProxy(fullCommand);
7968
8581
  } else {
7969
8582
  program.help();
@@ -7972,14 +8585,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
7972
8585
  registerUndoCommand(program);
7973
8586
  registerShieldCommand(program);
7974
8587
  registerConfigShowCommand(program);
8588
+ registerTrustCommand(program);
7975
8589
  if (process.argv[2] !== "daemon") {
7976
8590
  process.on("unhandledRejection", (reason) => {
7977
8591
  const isCheckHook = process.argv[2] === "check";
7978
8592
  if (isCheckHook) {
7979
8593
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
7980
- const logPath = import_path21.default.join(import_os18.default.homedir(), ".node9", "hook-debug.log");
8594
+ const logPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "hook-debug.log");
7981
8595
  const msg = reason instanceof Error ? reason.message : String(reason);
7982
- import_fs20.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
8596
+ import_fs22.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
7983
8597
  `);
7984
8598
  }
7985
8599
  process.exit(0);