@node9/proxy 1.21.5 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -6305,6 +6305,164 @@ var init_core = __esm({
6305
6305
  }
6306
6306
  });
6307
6307
 
6308
+ // src/mcp-pin.ts
6309
+ function getHomePinsFilePath() {
6310
+ return import_path14.default.join(import_os11.default.homedir(), ".node9", "mcp-pins.json");
6311
+ }
6312
+ function getPinsFilePath() {
6313
+ return getHomePinsFilePath();
6314
+ }
6315
+ function findPinsFilePath(cwd) {
6316
+ const homeDir2 = import_os11.default.homedir();
6317
+ const homePath = getHomePinsFilePath();
6318
+ let current = import_path14.default.resolve(cwd ?? process.cwd());
6319
+ while (true) {
6320
+ if (current === homeDir2) break;
6321
+ const candidate = import_path14.default.join(current, ".node9", "mcp-pins.json");
6322
+ if (import_fs12.default.existsSync(candidate)) {
6323
+ return { path: candidate, source: "repo" };
6324
+ }
6325
+ const next = import_path14.default.dirname(current);
6326
+ if (next === current) break;
6327
+ current = next;
6328
+ }
6329
+ return { path: homePath, source: "home" };
6330
+ }
6331
+ function hashToolDefinitions(tools) {
6332
+ const sorted = [...tools].sort((a, b) => {
6333
+ const nameA = a.name ?? "";
6334
+ const nameB = b.name ?? "";
6335
+ return nameA.localeCompare(nameB);
6336
+ });
6337
+ const canonical = JSON.stringify(sorted);
6338
+ return import_crypto5.default.createHash("sha256").update(canonical).digest("hex");
6339
+ }
6340
+ function getServerKey(upstreamCommand) {
6341
+ return import_crypto5.default.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
6342
+ }
6343
+ function readPinsFile(filePath) {
6344
+ try {
6345
+ const raw = import_fs12.default.readFileSync(filePath, "utf-8");
6346
+ if (!raw.trim()) {
6347
+ return { ok: false, reason: "corrupt", detail: "empty file" };
6348
+ }
6349
+ const parsed = JSON.parse(raw);
6350
+ if (!parsed.servers || typeof parsed.servers !== "object" || Array.isArray(parsed.servers)) {
6351
+ return { ok: false, reason: "corrupt", detail: "invalid structure: missing servers object" };
6352
+ }
6353
+ return { ok: true, pins: { servers: parsed.servers } };
6354
+ } catch (err2) {
6355
+ if (err2.code === "ENOENT") {
6356
+ return { ok: false, reason: "missing" };
6357
+ }
6358
+ return { ok: false, reason: "corrupt", detail: String(err2) };
6359
+ }
6360
+ }
6361
+ function readMcpPinsSafe() {
6362
+ return readPinsFile(getHomePinsFilePath());
6363
+ }
6364
+ function readMcpPins() {
6365
+ const result = readMcpPinsSafe();
6366
+ if (result.ok) return result.pins;
6367
+ if (result.reason === "missing") return { servers: {} };
6368
+ throw new Error(`[node9] MCP pin file is corrupt: ${result.detail}`);
6369
+ }
6370
+ function writePinsFile(filePath, data) {
6371
+ import_fs12.default.mkdirSync(import_path14.default.dirname(filePath), { recursive: true });
6372
+ const tmp = `${filePath}.${import_crypto5.default.randomBytes(6).toString("hex")}.tmp`;
6373
+ const isHome = filePath === getHomePinsFilePath();
6374
+ import_fs12.default.writeFileSync(tmp, JSON.stringify(data, null, 2), isHome ? { mode: 384 } : {});
6375
+ import_fs12.default.renameSync(tmp, filePath);
6376
+ }
6377
+ function writeMcpPins(data) {
6378
+ writePinsFile(getHomePinsFilePath(), data);
6379
+ }
6380
+ function seedMcpPinsIfMissing() {
6381
+ const filePath = getPinsFilePath();
6382
+ if (import_fs12.default.existsSync(filePath)) return;
6383
+ writeMcpPins({ servers: {} });
6384
+ }
6385
+ function checkPin(serverKey, currentHash, cwd) {
6386
+ const found = findPinsFilePath(cwd);
6387
+ let repoEntry;
6388
+ if (found.source === "repo") {
6389
+ const repoResult = readPinsFile(found.path);
6390
+ if (!repoResult.ok) {
6391
+ return "corrupt";
6392
+ }
6393
+ repoEntry = repoResult.pins.servers[serverKey];
6394
+ }
6395
+ if (repoEntry) {
6396
+ return repoEntry.toolsHash === currentHash ? "match" : "mismatch";
6397
+ }
6398
+ const homeResult = readPinsFile(getHomePinsFilePath());
6399
+ if (!homeResult.ok) {
6400
+ if (homeResult.reason === "missing") return "new";
6401
+ return "corrupt";
6402
+ }
6403
+ const homeEntry = homeResult.pins.servers[serverKey];
6404
+ if (!homeEntry) return "new";
6405
+ return homeEntry.toolsHash === currentHash ? "match" : "mismatch";
6406
+ }
6407
+ function updatePin(serverKey, label, toolsHash, toolNames) {
6408
+ const pins = readMcpPins();
6409
+ pins.servers[serverKey] = {
6410
+ label,
6411
+ toolsHash,
6412
+ toolNames,
6413
+ toolCount: toolNames.length,
6414
+ pinnedAt: (/* @__PURE__ */ new Date()).toISOString()
6415
+ };
6416
+ writeMcpPins(pins);
6417
+ }
6418
+ function removePin(serverKey) {
6419
+ const pins = readMcpPins();
6420
+ delete pins.servers[serverKey];
6421
+ writeMcpPins(pins);
6422
+ }
6423
+ function clearAllPins() {
6424
+ writeMcpPins({ servers: {} });
6425
+ }
6426
+ function promotePin(serverKey, cwd) {
6427
+ const homePins = readMcpPins();
6428
+ const homeEntry = homePins.servers[serverKey];
6429
+ if (!homeEntry) {
6430
+ throw new Error(
6431
+ `[node9] Server "${serverKey}" is not pinned in ~/.node9/mcp-pins.json. Run \`node9 mcp pin list\` to see what's pinned.`
6432
+ );
6433
+ }
6434
+ const found = findPinsFilePath(cwd);
6435
+ let repoPath;
6436
+ let repoPins;
6437
+ let created = false;
6438
+ if (found.source === "repo") {
6439
+ repoPath = found.path;
6440
+ const result = readPinsFile(repoPath);
6441
+ if (!result.ok) {
6442
+ const detail = result.reason === "corrupt" ? result.detail : "missing";
6443
+ throw new Error(`[node9] Repo pin file at ${repoPath} is unreadable: ${detail}`);
6444
+ }
6445
+ repoPins = result.pins;
6446
+ } else {
6447
+ repoPath = import_path14.default.join(cwd ?? process.cwd(), ".node9", "mcp-pins.json");
6448
+ repoPins = { servers: {} };
6449
+ created = true;
6450
+ }
6451
+ repoPins.servers[serverKey] = { ...homeEntry };
6452
+ writePinsFile(repoPath, repoPins);
6453
+ return { repoPath, created };
6454
+ }
6455
+ var import_fs12, import_path14, import_os11, import_crypto5;
6456
+ var init_mcp_pin = __esm({
6457
+ "src/mcp-pin.ts"() {
6458
+ "use strict";
6459
+ import_fs12 = __toESM(require("fs"));
6460
+ import_path14 = __toESM(require("path"));
6461
+ import_os11 = __toESM(require("os"));
6462
+ import_crypto5 = __toESM(require("crypto"));
6463
+ }
6464
+ });
6465
+
6308
6466
  // src/setup.ts
6309
6467
  function hasNode9McpServer(servers) {
6310
6468
  const entry = servers["node9"];
@@ -6330,36 +6488,36 @@ function isStaleHookCommand(command) {
6330
6488
  const tokens = command.split(/\s+/);
6331
6489
  for (const tok of tokens) {
6332
6490
  if (!tok.startsWith("/")) continue;
6333
- if (!import_fs12.default.existsSync(tok)) return true;
6491
+ if (!import_fs13.default.existsSync(tok)) return true;
6334
6492
  }
6335
6493
  return false;
6336
6494
  }
6337
6495
  function readJson(filePath) {
6338
6496
  try {
6339
- if (import_fs12.default.existsSync(filePath)) {
6340
- return JSON.parse(import_fs12.default.readFileSync(filePath, "utf-8"));
6497
+ if (import_fs13.default.existsSync(filePath)) {
6498
+ return JSON.parse(import_fs13.default.readFileSync(filePath, "utf-8"));
6341
6499
  }
6342
6500
  } catch {
6343
6501
  }
6344
6502
  return null;
6345
6503
  }
6346
6504
  function writeJson(filePath, data) {
6347
- const dir = import_path14.default.dirname(filePath);
6348
- if (!import_fs12.default.existsSync(dir)) import_fs12.default.mkdirSync(dir, { recursive: true });
6349
- import_fs12.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
6505
+ const dir = import_path15.default.dirname(filePath);
6506
+ if (!import_fs13.default.existsSync(dir)) import_fs13.default.mkdirSync(dir, { recursive: true });
6507
+ import_fs13.default.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
6350
6508
  }
6351
6509
  function isNode9Hook(cmd) {
6352
6510
  if (!cmd) return false;
6353
6511
  return /(?:^|[\s/\\])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js (?:check|log)/.test(cmd);
6354
6512
  }
6355
6513
  function teardownClaude() {
6356
- const homeDir2 = import_os11.default.homedir();
6357
- const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
6358
- const mcpPath = import_path14.default.join(homeDir2, ".claude", ".mcp.json");
6514
+ const homeDir2 = import_os12.default.homedir();
6515
+ const hooksPath = import_path15.default.join(homeDir2, ".claude", "settings.json");
6516
+ const mcpPath = import_path15.default.join(homeDir2, ".claude", ".mcp.json");
6359
6517
  let changed = false;
6360
6518
  const settings = readJson(hooksPath);
6361
6519
  if (settings?.hooks) {
6362
- for (const event of ["PreToolUse", "PostToolUse"]) {
6520
+ for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
6363
6521
  const before = settings.hooks[event]?.length ?? 0;
6364
6522
  settings.hooks[event] = settings.hooks[event]?.filter(
6365
6523
  (m) => !m.hooks.some((h) => isNode9Hook(h.command))
@@ -6403,8 +6561,8 @@ function teardownClaude() {
6403
6561
  }
6404
6562
  }
6405
6563
  function teardownGemini() {
6406
- const homeDir2 = import_os11.default.homedir();
6407
- const settingsPath = import_path14.default.join(homeDir2, ".gemini", "settings.json");
6564
+ const homeDir2 = import_os12.default.homedir();
6565
+ const settingsPath = import_path15.default.join(homeDir2, ".gemini", "settings.json");
6408
6566
  const settings = readJson(settingsPath);
6409
6567
  if (!settings) {
6410
6568
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
@@ -6447,8 +6605,8 @@ function teardownGemini() {
6447
6605
  }
6448
6606
  }
6449
6607
  function teardownCursor() {
6450
- const homeDir2 = import_os11.default.homedir();
6451
- const mcpPath = import_path14.default.join(homeDir2, ".cursor", "mcp.json");
6608
+ const homeDir2 = import_os12.default.homedir();
6609
+ const mcpPath = import_path15.default.join(homeDir2, ".cursor", "mcp.json");
6452
6610
  const mcpConfig = readJson(mcpPath);
6453
6611
  if (!mcpConfig?.mcpServers) {
6454
6612
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
@@ -6479,9 +6637,10 @@ function teardownCursor() {
6479
6637
  }
6480
6638
  }
6481
6639
  async function setupClaude() {
6482
- const homeDir2 = import_os11.default.homedir();
6483
- const mcpPath = import_path14.default.join(homeDir2, ".claude", ".mcp.json");
6484
- const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
6640
+ seedMcpPinsIfMissing();
6641
+ const homeDir2 = import_os12.default.homedir();
6642
+ const mcpPath = import_path15.default.join(homeDir2, ".claude", ".mcp.json");
6643
+ const hooksPath = import_path15.default.join(homeDir2, ".claude", "settings.json");
6485
6644
  const claudeConfig = readJson(mcpPath) ?? {};
6486
6645
  const settings = readJson(hooksPath) ?? {};
6487
6646
  const servers = claudeConfig.mcpServers ?? {};
@@ -6540,6 +6699,33 @@ async function setupClaude() {
6540
6699
  }
6541
6700
  }
6542
6701
  }
6702
+ const hasPromptHook = settings.hooks.UserPromptSubmit?.some(
6703
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
6704
+ );
6705
+ if (!hasPromptHook) {
6706
+ if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
6707
+ settings.hooks.UserPromptSubmit.push({
6708
+ matcher: ".*",
6709
+ hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
6710
+ });
6711
+ console.log(import_chalk.default.green(" \u2705 UserPromptSubmit hook added \u2192 node9 check (prompt DLP)"));
6712
+ hooksChanged = true;
6713
+ anythingChanged = true;
6714
+ } else if (settings.hooks.UserPromptSubmit) {
6715
+ for (const matcher of settings.hooks.UserPromptSubmit) {
6716
+ for (const h of matcher.hooks) {
6717
+ const cmd = h.command ?? "";
6718
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
6719
+ h.command = fullPathCommand("check");
6720
+ console.log(
6721
+ import_chalk.default.yellow(" \u{1F527} UserPromptSubmit hook repaired (stale path \u2192 current binary)")
6722
+ );
6723
+ hooksChanged = true;
6724
+ anythingChanged = true;
6725
+ }
6726
+ }
6727
+ }
6728
+ }
6543
6729
  if (!hasNode9McpServer(servers)) {
6544
6730
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
6545
6731
  claudeConfig.mcpServers = servers;
@@ -6604,8 +6790,9 @@ async function setupClaude() {
6604
6790
  }
6605
6791
  }
6606
6792
  async function setupGemini() {
6607
- const homeDir2 = import_os11.default.homedir();
6608
- const settingsPath = import_path14.default.join(homeDir2, ".gemini", "settings.json");
6793
+ seedMcpPinsIfMissing();
6794
+ const homeDir2 = import_os12.default.homedir();
6795
+ const settingsPath = import_path15.default.join(homeDir2, ".gemini", "settings.json");
6609
6796
  const settings = readJson(settingsPath) ?? {};
6610
6797
  const servers = settings.mcpServers ?? {};
6611
6798
  let hooksChanged = false;
@@ -6700,9 +6887,9 @@ async function setupGemini() {
6700
6887
  printDaemonTip();
6701
6888
  }
6702
6889
  }
6703
- function claudeDesktopConfigPath(homeDir2 = import_os11.default.homedir()) {
6890
+ function claudeDesktopConfigPath(homeDir2 = import_os12.default.homedir()) {
6704
6891
  if (process.platform === "darwin") {
6705
- return import_path14.default.join(
6892
+ return import_path15.default.join(
6706
6893
  homeDir2,
6707
6894
  "Library",
6708
6895
  "Application Support",
@@ -6711,18 +6898,18 @@ function claudeDesktopConfigPath(homeDir2 = import_os11.default.homedir()) {
6711
6898
  );
6712
6899
  }
6713
6900
  if (process.platform === "linux") {
6714
- return import_path14.default.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
6901
+ return import_path15.default.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
6715
6902
  }
6716
6903
  if (process.platform === "win32") {
6717
- const appData = process.env.APPDATA ?? import_path14.default.join(homeDir2, "AppData", "Roaming");
6718
- return import_path14.default.join(appData, "Claude", "claude_desktop_config.json");
6904
+ const appData = process.env.APPDATA ?? import_path15.default.join(homeDir2, "AppData", "Roaming");
6905
+ return import_path15.default.join(appData, "Claude", "claude_desktop_config.json");
6719
6906
  }
6720
6907
  return null;
6721
6908
  }
6722
- function detectAgents(homeDir2 = import_os11.default.homedir()) {
6909
+ function detectAgents(homeDir2 = import_os12.default.homedir()) {
6723
6910
  const exists = (p) => {
6724
6911
  try {
6725
- return import_fs12.default.existsSync(p);
6912
+ return import_fs13.default.existsSync(p);
6726
6913
  } catch (err2) {
6727
6914
  const code = err2.code;
6728
6915
  if (code !== "ENOENT") {
@@ -6734,18 +6921,19 @@ function detectAgents(homeDir2 = import_os11.default.homedir()) {
6734
6921
  };
6735
6922
  const desktopPath = claudeDesktopConfigPath(homeDir2);
6736
6923
  return {
6737
- claude: exists(import_path14.default.join(homeDir2, ".claude")) || exists(import_path14.default.join(homeDir2, ".claude.json")),
6738
- gemini: exists(import_path14.default.join(homeDir2, ".gemini")),
6739
- cursor: exists(import_path14.default.join(homeDir2, ".cursor")),
6740
- codex: exists(import_path14.default.join(homeDir2, ".codex")),
6741
- windsurf: exists(import_path14.default.join(homeDir2, ".codeium", "windsurf")),
6742
- vscode: exists(import_path14.default.join(homeDir2, ".vscode")),
6743
- claudeDesktop: desktopPath !== null && exists(import_path14.default.dirname(desktopPath))
6924
+ claude: exists(import_path15.default.join(homeDir2, ".claude")) || exists(import_path15.default.join(homeDir2, ".claude.json")),
6925
+ gemini: exists(import_path15.default.join(homeDir2, ".gemini")),
6926
+ cursor: exists(import_path15.default.join(homeDir2, ".cursor")),
6927
+ codex: exists(import_path15.default.join(homeDir2, ".codex")),
6928
+ windsurf: exists(import_path15.default.join(homeDir2, ".codeium", "windsurf")),
6929
+ vscode: exists(import_path15.default.join(homeDir2, ".vscode")),
6930
+ claudeDesktop: desktopPath !== null && exists(import_path15.default.dirname(desktopPath))
6744
6931
  };
6745
6932
  }
6746
6933
  async function setupCursor() {
6747
- const homeDir2 = import_os11.default.homedir();
6748
- const mcpPath = import_path14.default.join(homeDir2, ".cursor", "mcp.json");
6934
+ seedMcpPinsIfMissing();
6935
+ const homeDir2 = import_os12.default.homedir();
6936
+ const mcpPath = import_path15.default.join(homeDir2, ".cursor", "mcp.json");
6749
6937
  const mcpConfig = readJson(mcpPath) ?? {};
6750
6938
  const servers = mcpConfig.mcpServers ?? {};
6751
6939
  let anythingChanged = false;
@@ -6811,24 +6999,95 @@ async function setupCursor() {
6811
6999
  }
6812
7000
  function readToml(filePath) {
6813
7001
  try {
6814
- if (import_fs12.default.existsSync(filePath)) {
6815
- return (0, import_smol_toml.parse)(import_fs12.default.readFileSync(filePath, "utf-8"));
7002
+ if (import_fs13.default.existsSync(filePath)) {
7003
+ return (0, import_smol_toml.parse)(import_fs13.default.readFileSync(filePath, "utf-8"));
6816
7004
  }
6817
7005
  } catch {
6818
7006
  }
6819
7007
  return null;
6820
7008
  }
6821
7009
  function writeToml(filePath, data) {
6822
- const dir = import_path14.default.dirname(filePath);
6823
- if (!import_fs12.default.existsSync(dir)) import_fs12.default.mkdirSync(dir, { recursive: true });
6824
- import_fs12.default.writeFileSync(filePath, (0, import_smol_toml.stringify)(data));
7010
+ const dir = import_path15.default.dirname(filePath);
7011
+ if (!import_fs13.default.existsSync(dir)) import_fs13.default.mkdirSync(dir, { recursive: true });
7012
+ import_fs13.default.writeFileSync(filePath, (0, import_smol_toml.stringify)(data));
6825
7013
  }
6826
7014
  async function setupCodex() {
6827
- const homeDir2 = import_os11.default.homedir();
6828
- const configPath = import_path14.default.join(homeDir2, ".codex", "config.toml");
7015
+ seedMcpPinsIfMissing();
7016
+ const homeDir2 = import_os12.default.homedir();
7017
+ const configPath = import_path15.default.join(homeDir2, ".codex", "config.toml");
7018
+ const hooksPath = import_path15.default.join(homeDir2, ".codex", "hooks.json");
6829
7019
  const config = readToml(configPath) ?? {};
6830
7020
  const servers = config.mcp_servers ?? {};
6831
7021
  let anythingChanged = false;
7022
+ const hooksFile = readJson(hooksPath) ?? {};
7023
+ if (!hooksFile.hooks) hooksFile.hooks = {};
7024
+ let hooksChanged = false;
7025
+ if (!hooksFile.hooks.PreToolUse) hooksFile.hooks.PreToolUse = [];
7026
+ for (const matcher of CODEX_PRE_TOOL_MATCHERS) {
7027
+ const existing = hooksFile.hooks.PreToolUse.find((m) => m.matcher === matcher);
7028
+ if (!existing) {
7029
+ hooksFile.hooks.PreToolUse.push({
7030
+ matcher,
7031
+ hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
7032
+ });
7033
+ hooksChanged = true;
7034
+ } else {
7035
+ for (const h of existing.hooks) {
7036
+ const cmd = h.command ?? "";
7037
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
7038
+ h.command = fullPathCommand("check");
7039
+ hooksChanged = true;
7040
+ }
7041
+ }
7042
+ }
7043
+ }
7044
+ if (!hooksFile.hooks.UserPromptSubmit) hooksFile.hooks.UserPromptSubmit = [];
7045
+ const hasPromptHook = hooksFile.hooks.UserPromptSubmit.some(
7046
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7047
+ );
7048
+ if (!hasPromptHook) {
7049
+ hooksFile.hooks.UserPromptSubmit.push({
7050
+ hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
7051
+ });
7052
+ hooksChanged = true;
7053
+ } else {
7054
+ for (const m of hooksFile.hooks.UserPromptSubmit) {
7055
+ for (const h of m.hooks) {
7056
+ const cmd = h.command ?? "";
7057
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
7058
+ h.command = fullPathCommand("check");
7059
+ hooksChanged = true;
7060
+ }
7061
+ }
7062
+ }
7063
+ }
7064
+ if (!hooksFile.hooks.PostToolUse) hooksFile.hooks.PostToolUse = [];
7065
+ const hasPostHook = hooksFile.hooks.PostToolUse.some(
7066
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7067
+ );
7068
+ if (!hasPostHook) {
7069
+ hooksFile.hooks.PostToolUse.push({
7070
+ matcher: ".*",
7071
+ hooks: [{ type: "command", command: fullPathCommand("log"), timeout: 600 }]
7072
+ });
7073
+ hooksChanged = true;
7074
+ } else {
7075
+ for (const m of hooksFile.hooks.PostToolUse) {
7076
+ for (const h of m.hooks) {
7077
+ const cmd = h.command ?? "";
7078
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
7079
+ h.command = fullPathCommand("log");
7080
+ hooksChanged = true;
7081
+ }
7082
+ }
7083
+ }
7084
+ }
7085
+ if (hooksChanged) {
7086
+ writeJson(hooksPath, hooksFile);
7087
+ console.log(import_chalk.default.green(" \u2705 Codex hooks added \u2192 node9 check / node9 log"));
7088
+ anythingChanged = true;
7089
+ }
7090
+ const hooksInstalled = (hooksFile.hooks?.PreToolUse?.length ?? 0) > 0;
6832
7091
  if (!hasNode9McpServer(servers)) {
6833
7092
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
6834
7093
  config.mcp_servers = servers;
@@ -6868,30 +7127,63 @@ async function setupCodex() {
6868
7127
  }
6869
7128
  console.log("");
6870
7129
  }
6871
- console.log(
6872
- import_chalk.default.yellow(
6873
- " \u26A0\uFE0F Note: Codex does not yet support native pre-execution hooks.\n MCP proxy wrapping is the only supported protection mode for Codex.\n Native bash and file operations are not monitored."
6874
- )
6875
- );
6876
- console.log("");
6877
- if (!anythingChanged && serversToWrap.length === 0) {
7130
+ const hooksDisabled = config.features?.hooks === false || config.codex_hooks === false;
7131
+ if (hooksDisabled) {
6878
7132
  console.log(
6879
- import_chalk.default.blue(
6880
- "\u2139\uFE0F No MCP servers found to wrap. Add MCP servers to ~/.codex/config.toml and re-run."
7133
+ import_chalk.default.yellow(
7134
+ " \u26A0\uFE0F Codex hooks are disabled in ~/.codex/config.toml ([features].hooks = false).\n Re-enable hooks to activate Node9 shield evaluation on Bash, apply_patch,\n MCP tool calls, and prompt submissions. Until then, only MCP proxy wrapping\n is active."
7135
+ )
7136
+ );
7137
+ console.log("");
7138
+ }
7139
+ const printCodexTrustReminder = () => {
7140
+ console.log(
7141
+ import_chalk.default.yellow(
7142
+ " \u279C Open Codex and run /hooks to review and trust the Node9 entries.\n Until trusted, only MCP proxy wrapping is active."
6881
7143
  )
6882
7144
  );
7145
+ };
7146
+ if (!anythingChanged && serversToWrap.length === 0) {
7147
+ if (hooksInstalled) {
7148
+ console.log(import_chalk.default.blue("\u2139\uFE0F Codex hooks already installed."));
7149
+ printCodexTrustReminder();
7150
+ } else {
7151
+ console.log(
7152
+ import_chalk.default.blue(
7153
+ "\u2139\uFE0F No MCP servers found to wrap. Add MCP servers to ~/.codex/config.toml and re-run."
7154
+ )
7155
+ );
7156
+ }
6883
7157
  printDaemonTip();
6884
7158
  return;
6885
7159
  }
6886
7160
  if (anythingChanged) {
6887
- console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Codex via MCP proxy!"));
7161
+ console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 hooks installed for Codex."));
7162
+ printCodexTrustReminder();
6888
7163
  console.log(import_chalk.default.gray(" Restart Codex for changes to take effect."));
6889
7164
  printDaemonTip();
6890
7165
  }
6891
7166
  }
6892
7167
  function teardownCodex() {
6893
- const homeDir2 = import_os11.default.homedir();
6894
- const configPath = import_path14.default.join(homeDir2, ".codex", "config.toml");
7168
+ const homeDir2 = import_os12.default.homedir();
7169
+ const configPath = import_path15.default.join(homeDir2, ".codex", "config.toml");
7170
+ const hooksPath = import_path15.default.join(homeDir2, ".codex", "hooks.json");
7171
+ const hooksFile = readJson(hooksPath);
7172
+ if (hooksFile?.hooks) {
7173
+ let hooksChanged = false;
7174
+ for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
7175
+ const before = hooksFile.hooks[event]?.length ?? 0;
7176
+ hooksFile.hooks[event] = hooksFile.hooks[event]?.filter(
7177
+ (m) => !m.hooks.some((h) => isNode9Hook(h.command))
7178
+ );
7179
+ if ((hooksFile.hooks[event]?.length ?? 0) < before) hooksChanged = true;
7180
+ if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
7181
+ }
7182
+ if (hooksChanged) {
7183
+ writeJson(hooksPath, hooksFile);
7184
+ console.log(import_chalk.default.green(" \u2705 Removed Node9 hooks from ~/.codex/hooks.json"));
7185
+ }
7186
+ }
6895
7187
  const config = readToml(configPath);
6896
7188
  if (!config?.mcp_servers) {
6897
7189
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.codex/config.toml not found \u2014 nothing to remove"));
@@ -6922,8 +7214,8 @@ function teardownCodex() {
6922
7214
  }
6923
7215
  }
6924
7216
  function setupHud() {
6925
- const homeDir2 = import_os11.default.homedir();
6926
- const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
7217
+ const homeDir2 = import_os12.default.homedir();
7218
+ const hooksPath = import_path15.default.join(homeDir2, ".claude", "settings.json");
6927
7219
  const settings = readJson(hooksPath) ?? {};
6928
7220
  const hudCommand = fullPathCommand("hud");
6929
7221
  const statusLineObj = { type: "command", command: hudCommand };
@@ -6949,8 +7241,8 @@ function setupHud() {
6949
7241
  console.log(import_chalk.default.gray(" Restart Claude Code to activate."));
6950
7242
  }
6951
7243
  function teardownHud() {
6952
- const homeDir2 = import_os11.default.homedir();
6953
- const hooksPath = import_path14.default.join(homeDir2, ".claude", "settings.json");
7244
+ const homeDir2 = import_os12.default.homedir();
7245
+ const hooksPath = import_path15.default.join(homeDir2, ".claude", "settings.json");
6954
7246
  const settings = readJson(hooksPath);
6955
7247
  if (!settings) {
6956
7248
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.claude/settings.json not found \u2014 nothing to remove"));
@@ -6968,8 +7260,9 @@ function teardownHud() {
6968
7260
  console.log(import_chalk.default.gray(" Restart Claude Code for changes to take effect."));
6969
7261
  }
6970
7262
  async function setupWindsurf() {
6971
- const homeDir2 = import_os11.default.homedir();
6972
- const mcpPath = import_path14.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
7263
+ seedMcpPinsIfMissing();
7264
+ const homeDir2 = import_os12.default.homedir();
7265
+ const mcpPath = import_path15.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
6973
7266
  const mcpConfig = readJson(mcpPath) ?? {};
6974
7267
  const servers = mcpConfig.mcpServers ?? {};
6975
7268
  let anythingChanged = false;
@@ -7029,8 +7322,8 @@ async function setupWindsurf() {
7029
7322
  }
7030
7323
  }
7031
7324
  function teardownWindsurf() {
7032
- const homeDir2 = import_os11.default.homedir();
7033
- const mcpPath = import_path14.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
7325
+ const homeDir2 = import_os12.default.homedir();
7326
+ const mcpPath = import_path15.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
7034
7327
  const mcpConfig = readJson(mcpPath);
7035
7328
  if (!mcpConfig?.mcpServers) {
7036
7329
  console.log(
@@ -7071,8 +7364,9 @@ function hasNode9McpServerVSCode(servers) {
7071
7364
  return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
7072
7365
  }
7073
7366
  async function setupVSCode() {
7074
- const homeDir2 = import_os11.default.homedir();
7075
- const mcpPath = import_path14.default.join(homeDir2, ".vscode", "mcp.json");
7367
+ seedMcpPinsIfMissing();
7368
+ const homeDir2 = import_os12.default.homedir();
7369
+ const mcpPath = import_path15.default.join(homeDir2, ".vscode", "mcp.json");
7076
7370
  const mcpConfig = readJson(mcpPath) ?? {};
7077
7371
  const servers = mcpConfig.servers ?? {};
7078
7372
  let anythingChanged = false;
@@ -7133,8 +7427,8 @@ async function setupVSCode() {
7133
7427
  }
7134
7428
  }
7135
7429
  function teardownVSCode() {
7136
- const homeDir2 = import_os11.default.homedir();
7137
- const mcpPath = import_path14.default.join(homeDir2, ".vscode", "mcp.json");
7430
+ const homeDir2 = import_os12.default.homedir();
7431
+ const mcpPath = import_path15.default.join(homeDir2, ".vscode", "mcp.json");
7138
7432
  const mcpConfig = readJson(mcpPath);
7139
7433
  if (!mcpConfig?.servers) {
7140
7434
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.vscode/mcp.json not found \u2014 nothing to remove"));
@@ -7167,6 +7461,7 @@ function teardownVSCode() {
7167
7461
  }
7168
7462
  }
7169
7463
  async function setupClaudeDesktop() {
7464
+ seedMcpPinsIfMissing();
7170
7465
  const configPath = claudeDesktopConfigPath();
7171
7466
  if (!configPath) {
7172
7467
  console.log(import_chalk.default.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
@@ -7265,32 +7560,32 @@ function teardownClaudeDesktop() {
7265
7560
  console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in Claude Desktop config"));
7266
7561
  }
7267
7562
  }
7268
- function getAgentsStatus(homeDir2 = import_os11.default.homedir()) {
7563
+ function getAgentsStatus(homeDir2 = import_os12.default.homedir()) {
7269
7564
  const detected = detectAgents(homeDir2);
7270
7565
  const claudeWired = (() => {
7271
- const settings = readJson(import_path14.default.join(homeDir2, ".claude", "settings.json"));
7566
+ const settings = readJson(import_path15.default.join(homeDir2, ".claude", "settings.json"));
7272
7567
  return !!settings?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
7273
7568
  })();
7274
7569
  const geminiWired = (() => {
7275
- const settings = readJson(import_path14.default.join(homeDir2, ".gemini", "settings.json"));
7570
+ const settings = readJson(import_path15.default.join(homeDir2, ".gemini", "settings.json"));
7276
7571
  return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
7277
7572
  })();
7278
7573
  const cursorWired = (() => {
7279
- const cfg = readJson(import_path14.default.join(homeDir2, ".cursor", "mcp.json"));
7574
+ const cfg = readJson(import_path15.default.join(homeDir2, ".cursor", "mcp.json"));
7280
7575
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
7281
7576
  })();
7282
7577
  const codexWired = (() => {
7283
- const cfg = readToml(import_path14.default.join(homeDir2, ".codex", "config.toml"));
7578
+ const cfg = readToml(import_path15.default.join(homeDir2, ".codex", "config.toml"));
7284
7579
  return !!(cfg?.mcp_servers && hasNode9McpServer(cfg.mcp_servers));
7285
7580
  })();
7286
7581
  const windsurfWired = (() => {
7287
7582
  const cfg = readJson(
7288
- import_path14.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
7583
+ import_path15.default.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
7289
7584
  );
7290
7585
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
7291
7586
  })();
7292
7587
  const vscodeWired = (() => {
7293
- const cfg = readJson(import_path14.default.join(homeDir2, ".vscode", "mcp.json"));
7588
+ const cfg = readJson(import_path15.default.join(homeDir2, ".vscode", "mcp.json"));
7294
7589
  return !!(cfg?.servers && hasNode9McpServerVSCode(cfg.servers));
7295
7590
  })();
7296
7591
  return [
@@ -7350,17 +7645,19 @@ function getAgentsStatus(homeDir2 = import_os11.default.homedir()) {
7350
7645
  }
7351
7646
  ];
7352
7647
  }
7353
- var import_fs12, import_path14, import_os11, import_chalk, import_prompts, import_smol_toml, NODE9_MCP_SERVER_ENTRY;
7648
+ var import_fs13, import_path15, import_os12, import_chalk, import_prompts, import_smol_toml, NODE9_MCP_SERVER_ENTRY, CODEX_PRE_TOOL_MATCHERS;
7354
7649
  var init_setup = __esm({
7355
7650
  "src/setup.ts"() {
7356
7651
  "use strict";
7357
- import_fs12 = __toESM(require("fs"));
7358
- import_path14 = __toESM(require("path"));
7359
- import_os11 = __toESM(require("os"));
7652
+ import_fs13 = __toESM(require("fs"));
7653
+ import_path15 = __toESM(require("path"));
7654
+ import_os12 = __toESM(require("os"));
7360
7655
  import_chalk = __toESM(require("chalk"));
7361
7656
  import_prompts = require("@inquirer/prompts");
7362
7657
  import_smol_toml = require("smol-toml");
7658
+ init_mcp_pin();
7363
7659
  NODE9_MCP_SERVER_ENTRY = { command: "node9", args: ["mcp-server"] };
7660
+ CODEX_PRE_TOOL_MATCHERS = ["^Bash$", "^apply_patch$", "^mcp__.*"];
7364
7661
  }
7365
7662
  });
7366
7663
 
@@ -7554,79 +7851,79 @@ var init_scan_summary = __esm({
7554
7851
  function buildSensitivePaths(home, cwd) {
7555
7852
  return [
7556
7853
  {
7557
- full: import_path15.default.join(home, ".ssh", "id_rsa"),
7854
+ full: import_path16.default.join(home, ".ssh", "id_rsa"),
7558
7855
  label: "~/.ssh/id_rsa",
7559
7856
  description: "RSA private key \u2014 grants SSH access to your servers",
7560
7857
  score: 20
7561
7858
  },
7562
7859
  {
7563
- full: import_path15.default.join(home, ".ssh", "id_ed25519"),
7860
+ full: import_path16.default.join(home, ".ssh", "id_ed25519"),
7564
7861
  label: "~/.ssh/id_ed25519",
7565
7862
  description: "Ed25519 private key \u2014 grants SSH access to your servers",
7566
7863
  score: 20
7567
7864
  },
7568
7865
  {
7569
- full: import_path15.default.join(home, ".ssh", "id_ecdsa"),
7866
+ full: import_path16.default.join(home, ".ssh", "id_ecdsa"),
7570
7867
  label: "~/.ssh/id_ecdsa",
7571
7868
  description: "ECDSA private key \u2014 grants SSH access to your servers",
7572
7869
  score: 20
7573
7870
  },
7574
7871
  {
7575
- full: import_path15.default.join(home, ".aws", "credentials"),
7872
+ full: import_path16.default.join(home, ".aws", "credentials"),
7576
7873
  label: "~/.aws/credentials",
7577
7874
  description: "AWS access keys \u2014 full cloud account access",
7578
7875
  score: 20
7579
7876
  },
7580
7877
  {
7581
- full: import_path15.default.join(home, ".aws", "config"),
7878
+ full: import_path16.default.join(home, ".aws", "config"),
7582
7879
  label: "~/.aws/config",
7583
7880
  description: "AWS configuration \u2014 account and region settings",
7584
7881
  score: 5
7585
7882
  },
7586
7883
  {
7587
- full: import_path15.default.join(home, ".config", "gcloud", "credentials.db"),
7884
+ full: import_path16.default.join(home, ".config", "gcloud", "credentials.db"),
7588
7885
  label: "~/.config/gcloud/credentials.db",
7589
7886
  description: "Google Cloud credentials",
7590
7887
  score: 15
7591
7888
  },
7592
7889
  {
7593
- full: import_path15.default.join(home, ".docker", "config.json"),
7890
+ full: import_path16.default.join(home, ".docker", "config.json"),
7594
7891
  label: "~/.docker/config.json",
7595
7892
  description: "Docker registry auth tokens",
7596
7893
  score: 10
7597
7894
  },
7598
7895
  {
7599
- full: import_path15.default.join(home, ".netrc"),
7896
+ full: import_path16.default.join(home, ".netrc"),
7600
7897
  label: "~/.netrc",
7601
7898
  description: "FTP/HTTP credentials in plain text",
7602
7899
  score: 15
7603
7900
  },
7604
7901
  {
7605
- full: import_path15.default.join(home, ".npmrc"),
7902
+ full: import_path16.default.join(home, ".npmrc"),
7606
7903
  label: "~/.npmrc",
7607
7904
  description: "npm auth token \u2014 can publish packages as you",
7608
7905
  score: 10
7609
7906
  },
7610
7907
  {
7611
- full: import_path15.default.join(home, ".node9", "credentials.json"),
7908
+ full: import_path16.default.join(home, ".node9", "credentials.json"),
7612
7909
  label: "~/.node9/credentials.json",
7613
7910
  description: "Node9 cloud API key",
7614
7911
  score: 10
7615
7912
  },
7616
7913
  {
7617
- full: import_path15.default.join(cwd, ".env"),
7914
+ full: import_path16.default.join(cwd, ".env"),
7618
7915
  label: ".env (current folder)",
7619
7916
  description: "App secrets \u2014 database passwords, API keys",
7620
7917
  score: 20
7621
7918
  },
7622
7919
  {
7623
- full: import_path15.default.join(cwd, ".env.local"),
7920
+ full: import_path16.default.join(cwd, ".env.local"),
7624
7921
  label: ".env.local (current folder)",
7625
7922
  description: "Local overrides \u2014 often contains real credentials",
7626
7923
  score: 15
7627
7924
  },
7628
7925
  {
7629
- full: import_path15.default.join(cwd, ".env.production"),
7926
+ full: import_path16.default.join(cwd, ".env.production"),
7630
7927
  label: ".env.production (current folder)",
7631
7928
  description: "Production secrets",
7632
7929
  score: 20
@@ -7635,7 +7932,7 @@ function buildSensitivePaths(home, cwd) {
7635
7932
  }
7636
7933
  function isReadable(filePath) {
7637
7934
  try {
7638
- import_fs13.default.accessSync(filePath, import_fs13.default.constants.R_OK);
7935
+ import_fs14.default.accessSync(filePath, import_fs14.default.constants.R_OK);
7639
7936
  return true;
7640
7937
  } catch {
7641
7938
  return false;
@@ -7648,13 +7945,13 @@ function scoreLabel(score) {
7648
7945
  return import_chalk2.default.red.bold(`${score}/100 Critical`);
7649
7946
  }
7650
7947
  function runBlast() {
7651
- const home = import_os12.default.homedir();
7948
+ const home = import_os13.default.homedir();
7652
7949
  const cwd = process.cwd();
7653
7950
  const paths = buildSensitivePaths(home, cwd);
7654
7951
  let scoreDeduction = 0;
7655
7952
  const reachable = [];
7656
7953
  for (const p of paths) {
7657
- if (import_fs13.default.existsSync(p.full) && isReadable(p.full)) {
7954
+ if (import_fs14.default.existsSync(p.full) && isReadable(p.full)) {
7658
7955
  reachable.push(p);
7659
7956
  scoreDeduction += p.score;
7660
7957
  }
@@ -7672,7 +7969,7 @@ function runBlast() {
7672
7969
  }
7673
7970
  function registerBlastCommand(program2) {
7674
7971
  program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
7675
- const home = import_os12.default.homedir();
7972
+ const home = import_os13.default.homedir();
7676
7973
  const cwd = process.cwd();
7677
7974
  const { reachable, envFindings, score } = runBlast();
7678
7975
  console.log("");
@@ -7719,14 +8016,14 @@ function registerBlastCommand(program2) {
7719
8016
  console.log("");
7720
8017
  });
7721
8018
  }
7722
- var import_chalk2, import_fs13, import_path15, import_os12;
8019
+ var import_chalk2, import_fs14, import_path16, import_os13;
7723
8020
  var init_blast = __esm({
7724
8021
  "src/cli/commands/blast.ts"() {
7725
8022
  "use strict";
7726
8023
  import_chalk2 = __toESM(require("chalk"));
7727
- import_fs13 = __toESM(require("fs"));
7728
- import_path15 = __toESM(require("path"));
7729
- import_os12 = __toESM(require("os"));
8024
+ import_fs14 = __toESM(require("fs"));
8025
+ import_path16 = __toESM(require("path"));
8026
+ import_os13 = __toESM(require("os"));
7730
8027
  init_dlp();
7731
8028
  }
7732
8029
  });
@@ -7854,13 +8151,13 @@ var init_scan_json = __esm({
7854
8151
 
7855
8152
  // src/cli/render/scan-history.ts
7856
8153
  function defaultHistoryPath() {
7857
- return import_path16.default.join(import_os13.default.homedir(), ".node9", "scan-history.json");
8154
+ return import_path17.default.join(import_os14.default.homedir(), ".node9", "scan-history.json");
7858
8155
  }
7859
8156
  function readPreviousScan(opts = {}) {
7860
8157
  const filePath = opts.path ?? defaultHistoryPath();
7861
8158
  try {
7862
- if (!import_fs14.default.existsSync(filePath)) return null;
7863
- const raw = import_fs14.default.readFileSync(filePath, "utf8");
8159
+ if (!import_fs15.default.existsSync(filePath)) return null;
8160
+ const raw = import_fs15.default.readFileSync(filePath, "utf8");
7864
8161
  const parsed = JSON.parse(raw);
7865
8162
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
7866
8163
  const last = parsed[parsed.length - 1];
@@ -7874,11 +8171,11 @@ function appendScanHistory(record, opts = {}) {
7874
8171
  const filePath = opts.path ?? defaultHistoryPath();
7875
8172
  const cap = opts.cap ?? SCAN_HISTORY_CAP;
7876
8173
  try {
7877
- import_fs14.default.mkdirSync(import_path16.default.dirname(filePath), { recursive: true });
8174
+ import_fs15.default.mkdirSync(import_path17.default.dirname(filePath), { recursive: true });
7878
8175
  let history = [];
7879
- if (import_fs14.default.existsSync(filePath)) {
8176
+ if (import_fs15.default.existsSync(filePath)) {
7880
8177
  try {
7881
- const parsed = JSON.parse(import_fs14.default.readFileSync(filePath, "utf8"));
8178
+ const parsed = JSON.parse(import_fs15.default.readFileSync(filePath, "utf8"));
7882
8179
  if (Array.isArray(parsed)) {
7883
8180
  history = parsed.filter(isValidRecord);
7884
8181
  }
@@ -7889,7 +8186,7 @@ function appendScanHistory(record, opts = {}) {
7889
8186
  if (history.length > cap) {
7890
8187
  history = history.slice(history.length - cap);
7891
8188
  }
7892
- import_fs14.default.writeFileSync(filePath, JSON.stringify(history, null, 2));
8189
+ import_fs15.default.writeFileSync(filePath, JSON.stringify(history, null, 2));
7893
8190
  } catch (err2) {
7894
8191
  process.stderr.write(
7895
8192
  `[node9] Warning: could not write scan-history.json: ${err2.message}
@@ -7911,13 +8208,13 @@ function isValidRecord(x) {
7911
8208
  const r = x;
7912
8209
  return typeof r.timestamp === "string" && typeof r.score === "number" && typeof r.blocked === "number" && typeof r.review === "number" && typeof r.leaks === "number" && typeof r.loops === "number" && typeof r.totalCalls === "number";
7913
8210
  }
7914
- var import_fs14, import_path16, import_os13, SCAN_HISTORY_CAP;
8211
+ var import_fs15, import_path17, import_os14, SCAN_HISTORY_CAP;
7915
8212
  var init_scan_history = __esm({
7916
8213
  "src/cli/render/scan-history.ts"() {
7917
8214
  "use strict";
7918
- import_fs14 = __toESM(require("fs"));
7919
- import_path16 = __toESM(require("path"));
7920
- import_os13 = __toESM(require("os"));
8215
+ import_fs15 = __toESM(require("fs"));
8216
+ import_path17 = __toESM(require("path"));
8217
+ import_os14 = __toESM(require("os"));
7921
8218
  SCAN_HISTORY_CAP = 30;
7922
8219
  }
7923
8220
  });
@@ -7928,7 +8225,7 @@ function normalizeModel(raw) {
7928
8225
  }
7929
8226
  function readCache() {
7930
8227
  try {
7931
- const raw = JSON.parse(import_fs15.default.readFileSync(CACHE_FILE(), "utf-8"));
8228
+ const raw = JSON.parse(import_fs16.default.readFileSync(CACHE_FILE(), "utf-8"));
7932
8229
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
7933
8230
  return null;
7934
8231
  }
@@ -7942,18 +8239,18 @@ function readCache() {
7942
8239
  function writeCache(prices) {
7943
8240
  try {
7944
8241
  const target = CACHE_FILE();
7945
- const dir = import_path17.default.dirname(target);
7946
- if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
8242
+ const dir = import_path18.default.dirname(target);
8243
+ if (!import_fs16.default.existsSync(dir)) import_fs16.default.mkdirSync(dir, { recursive: true });
7947
8244
  const tmp = target + ".tmp";
7948
8245
  const body = {
7949
8246
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
7950
8247
  prices
7951
8248
  };
7952
- import_fs15.default.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
7953
- import_fs15.default.renameSync(tmp, target);
8249
+ import_fs16.default.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
8250
+ import_fs16.default.renameSync(tmp, target);
7954
8251
  } catch (err2) {
7955
8252
  try {
7956
- import_fs15.default.appendFileSync(
8253
+ import_fs16.default.appendFileSync(
7957
8254
  HOOK_DEBUG_LOG,
7958
8255
  `[pricing] cache write failed: ${err2.message}
7959
8256
  `
@@ -8046,13 +8343,13 @@ function pricingFor(model) {
8046
8343
  lookupCache.set(norm, resolved);
8047
8344
  return resolved;
8048
8345
  }
8049
- var import_fs15, import_path17, import_os14, LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, lookupCache;
8346
+ var import_fs16, import_path18, import_os15, LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, lookupCache;
8050
8347
  var init_litellm = __esm({
8051
8348
  "src/pricing/litellm.ts"() {
8052
8349
  "use strict";
8053
- import_fs15 = __toESM(require("fs"));
8054
- import_path17 = __toESM(require("path"));
8055
- import_os14 = __toESM(require("os"));
8350
+ import_fs16 = __toESM(require("fs"));
8351
+ import_path18 = __toESM(require("path"));
8352
+ import_os15 = __toESM(require("os"));
8056
8353
  init_audit();
8057
8354
  LITELLM_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
8058
8355
  BUNDLED_PRICING = {
@@ -8079,7 +8376,7 @@ var init_litellm = __esm({
8079
8376
  "gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
8080
8377
  "gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
8081
8378
  };
8082
- CACHE_FILE = () => import_path17.default.join(import_os14.default.homedir(), ".node9", "model-pricing.json");
8379
+ CACHE_FILE = () => import_path18.default.join(import_os15.default.homedir(), ".node9", "model-pricing.json");
8083
8380
  TTL_MS = 24 * 60 * 60 * 1e3;
8084
8381
  memCache = null;
8085
8382
  memCacheAt = 0;
@@ -8092,10 +8389,10 @@ function decodeProjectDirName(dirName) {
8092
8389
  return dirName.replace(/-/g, "/");
8093
8390
  }
8094
8391
  function parseJSONLFile(filePath, fallbackWorkingDir) {
8095
- const runId = import_path18.default.basename(filePath, ".jsonl");
8392
+ const runId = import_path19.default.basename(filePath, ".jsonl");
8096
8393
  let content;
8097
8394
  try {
8098
- content = import_fs16.default.readFileSync(filePath, "utf8");
8395
+ content = import_fs17.default.readFileSync(filePath, "utf8");
8099
8396
  } catch {
8100
8397
  return /* @__PURE__ */ new Map();
8101
8398
  }
@@ -8151,34 +8448,34 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
8151
8448
  return daily;
8152
8449
  }
8153
8450
  function collectEntries(sinceMs) {
8154
- const projectsDir = import_path18.default.join(import_os15.default.homedir(), ".claude", "projects");
8155
- if (!import_fs16.default.existsSync(projectsDir)) return [];
8451
+ const projectsDir = import_path19.default.join(import_os16.default.homedir(), ".claude", "projects");
8452
+ if (!import_fs17.default.existsSync(projectsDir)) return [];
8156
8453
  const combined = /* @__PURE__ */ new Map();
8157
8454
  let dirs;
8158
8455
  try {
8159
- dirs = import_fs16.default.readdirSync(projectsDir);
8456
+ dirs = import_fs17.default.readdirSync(projectsDir);
8160
8457
  } catch {
8161
8458
  return [];
8162
8459
  }
8163
8460
  for (const dir of dirs) {
8164
- const dirPath = import_path18.default.join(projectsDir, dir);
8461
+ const dirPath = import_path19.default.join(projectsDir, dir);
8165
8462
  try {
8166
- if (!import_fs16.default.statSync(dirPath).isDirectory()) continue;
8463
+ if (!import_fs17.default.statSync(dirPath).isDirectory()) continue;
8167
8464
  } catch {
8168
8465
  continue;
8169
8466
  }
8170
8467
  let files;
8171
8468
  try {
8172
- files = import_fs16.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8469
+ files = import_fs17.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8173
8470
  } catch {
8174
8471
  continue;
8175
8472
  }
8176
8473
  const fallbackWorkingDir = decodeProjectDirName(dir);
8177
8474
  for (const file of files) {
8178
- const filePath = import_path18.default.join(dirPath, file);
8475
+ const filePath = import_path19.default.join(dirPath, file);
8179
8476
  if (sinceMs !== void 0) {
8180
8477
  try {
8181
- if (import_fs16.default.statSync(filePath).mtimeMs < sinceMs) continue;
8478
+ if (import_fs17.default.statSync(filePath).mtimeMs < sinceMs) continue;
8182
8479
  } catch {
8183
8480
  continue;
8184
8481
  }
@@ -8208,10 +8505,10 @@ async function syncCost() {
8208
8505
  if (entries.length === 0) return;
8209
8506
  let username = "unknown";
8210
8507
  try {
8211
- username = import_os15.default.userInfo().username;
8508
+ username = import_os16.default.userInfo().username;
8212
8509
  } catch {
8213
8510
  }
8214
- const machineId = `${import_os15.default.hostname()}:${username}`;
8511
+ const machineId = `${import_os16.default.hostname()}:${username}`;
8215
8512
  try {
8216
8513
  const res = await fetch(`${creds.apiUrl}/cost-sync`, {
8217
8514
  method: "POST",
@@ -8220,11 +8517,11 @@ async function syncCost() {
8220
8517
  signal: AbortSignal.timeout(15e3)
8221
8518
  });
8222
8519
  if (!res.ok) {
8223
- import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
8520
+ import_fs17.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
8224
8521
  `);
8225
8522
  }
8226
8523
  } catch (err2) {
8227
- import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
8524
+ import_fs17.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
8228
8525
  `);
8229
8526
  }
8230
8527
  }
@@ -8237,13 +8534,13 @@ function startCostSync() {
8237
8534
  }, SYNC_INTERVAL_MS);
8238
8535
  timer.unref();
8239
8536
  }
8240
- var import_fs16, import_path18, import_os15, SYNC_INTERVAL_MS;
8537
+ var import_fs17, import_path19, import_os16, SYNC_INTERVAL_MS;
8241
8538
  var init_costSync = __esm({
8242
8539
  "src/costSync.ts"() {
8243
8540
  "use strict";
8244
- import_fs16 = __toESM(require("fs"));
8245
- import_path18 = __toESM(require("path"));
8246
- import_os15 = __toESM(require("os"));
8541
+ import_fs17 = __toESM(require("fs"));
8542
+ import_path19 = __toESM(require("path"));
8543
+ import_os16 = __toESM(require("os"));
8247
8544
  init_config();
8248
8545
  init_audit();
8249
8546
  init_litellm();
@@ -8274,7 +8571,7 @@ function freshWatermark() {
8274
8571
  function loadWatermark() {
8275
8572
  let raw;
8276
8573
  try {
8277
- raw = import_fs17.default.readFileSync(WATERMARK_FILE(), "utf-8");
8574
+ raw = import_fs18.default.readFileSync(WATERMARK_FILE(), "utf-8");
8278
8575
  } catch {
8279
8576
  return { status: "fresh", wm: freshWatermark() };
8280
8577
  }
@@ -8326,28 +8623,28 @@ function loadWatermark() {
8326
8623
  function saveWatermark(wm) {
8327
8624
  if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
8328
8625
  const target = WATERMARK_FILE();
8329
- const dir = import_path19.default.dirname(target);
8330
- if (!import_fs17.default.existsSync(dir)) import_fs17.default.mkdirSync(dir, { recursive: true });
8626
+ const dir = import_path20.default.dirname(target);
8627
+ if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
8331
8628
  const tmp = target + ".tmp";
8332
- import_fs17.default.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
8333
- import_fs17.default.renameSync(tmp, target);
8629
+ import_fs18.default.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
8630
+ import_fs18.default.renameSync(tmp, target);
8334
8631
  }
8335
8632
  function listJsonlFiles() {
8336
8633
  const root = PROJECTS_DIR();
8337
- if (!import_fs17.default.existsSync(root)) return [];
8634
+ if (!import_fs18.default.existsSync(root)) return [];
8338
8635
  const out = [];
8339
- for (const entry of import_fs17.default.readdirSync(root, { withFileTypes: true })) {
8636
+ for (const entry of import_fs18.default.readdirSync(root, { withFileTypes: true })) {
8340
8637
  if (!entry.isDirectory()) continue;
8341
- const projectDir = import_path19.default.join(root, entry.name);
8638
+ const projectDir = import_path20.default.join(root, entry.name);
8342
8639
  let inner;
8343
8640
  try {
8344
- inner = import_fs17.default.readdirSync(projectDir, { withFileTypes: true });
8641
+ inner = import_fs18.default.readdirSync(projectDir, { withFileTypes: true });
8345
8642
  } catch {
8346
8643
  continue;
8347
8644
  }
8348
8645
  for (const file of inner) {
8349
8646
  if (file.isFile() && file.name.endsWith(".jsonl")) {
8350
- out.push(import_path19.default.join(projectDir, file.name));
8647
+ out.push(import_path20.default.join(projectDir, file.name));
8351
8648
  }
8352
8649
  }
8353
8650
  }
@@ -8355,7 +8652,7 @@ function listJsonlFiles() {
8355
8652
  }
8356
8653
  function fileSize(p) {
8357
8654
  try {
8358
- return import_fs17.default.statSync(p).size;
8655
+ return import_fs18.default.statSync(p).size;
8359
8656
  } catch {
8360
8657
  return 0;
8361
8658
  }
@@ -8363,7 +8660,7 @@ function fileSize(p) {
8363
8660
  async function scanDelta(filePath, fromByte, onLine) {
8364
8661
  const size = fileSize(filePath);
8365
8662
  if (size <= fromByte) return fromByte;
8366
- const stream = import_fs17.default.createReadStream(filePath, {
8663
+ const stream = import_fs18.default.createReadStream(filePath, {
8367
8664
  start: fromByte,
8368
8665
  end: size - 1,
8369
8666
  highWaterMark: 64 * 1024
@@ -8475,7 +8772,7 @@ async function tickForensicBroadcast(offsets) {
8475
8772
  continue;
8476
8773
  }
8477
8774
  if (size <= offset) continue;
8478
- const sessionId = import_path19.default.basename(file, ".jsonl");
8775
+ const sessionId = import_path20.default.basename(file, ".jsonl");
8479
8776
  const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
8480
8777
  out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
8481
8778
  });
@@ -8534,7 +8831,7 @@ function emptyTick(uploadAs) {
8534
8831
  function readRawWatermarkPreservingOffsets() {
8535
8832
  let raw;
8536
8833
  try {
8537
- raw = import_fs17.default.readFileSync(WATERMARK_FILE(), "utf-8");
8834
+ raw = import_fs18.default.readFileSync(WATERMARK_FILE(), "utf-8");
8538
8835
  } catch {
8539
8836
  return null;
8540
8837
  }
@@ -8568,13 +8865,13 @@ async function runActualTick(wm) {
8568
8865
  if (!known) {
8569
8866
  let mtimeMs = 0;
8570
8867
  try {
8571
- mtimeMs = import_fs17.default.statSync(filePath).mtime.getTime();
8868
+ mtimeMs = import_fs18.default.statSync(filePath).mtime.getTime();
8572
8869
  } catch {
8573
8870
  continue;
8574
8871
  }
8575
8872
  if (mtimeMs >= watermarkCreatedAt) {
8576
8873
  filesNew++;
8577
- const sessionId2 = import_path19.default.basename(filePath, ".jsonl");
8874
+ const sessionId2 = import_path20.default.basename(filePath, ".jsonl");
8578
8875
  const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
8579
8876
  totalToolCalls++;
8580
8877
  toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
@@ -8592,7 +8889,7 @@ async function runActualTick(wm) {
8592
8889
  filesSkipped++;
8593
8890
  continue;
8594
8891
  }
8595
- const sessionId = import_path19.default.basename(filePath, ".jsonl");
8892
+ const sessionId = import_path20.default.basename(filePath, ".jsonl");
8596
8893
  const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
8597
8894
  totalToolCalls++;
8598
8895
  toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
@@ -8614,18 +8911,18 @@ async function runActualTick(wm) {
8614
8911
  schemaFuture: false
8615
8912
  };
8616
8913
  }
8617
- var import_fs17, import_os16, import_path19, import_readline, PROJECTS_DIR, WATERMARK_FILE, MAX_LINE_BYTES, WATERMARK_SCHEMA_VERSION, LONG_OUTPUT_THRESHOLD_BYTES2;
8914
+ var import_fs18, import_os17, import_path20, import_readline, PROJECTS_DIR, WATERMARK_FILE, MAX_LINE_BYTES, WATERMARK_SCHEMA_VERSION, LONG_OUTPUT_THRESHOLD_BYTES2;
8618
8915
  var init_scan_watermark = __esm({
8619
8916
  "src/daemon/scan-watermark.ts"() {
8620
8917
  "use strict";
8621
- import_fs17 = __toESM(require("fs"));
8622
- import_os16 = __toESM(require("os"));
8623
- import_path19 = __toESM(require("path"));
8918
+ import_fs18 = __toESM(require("fs"));
8919
+ import_os17 = __toESM(require("os"));
8920
+ import_path20 = __toESM(require("path"));
8624
8921
  import_readline = __toESM(require("readline"));
8625
8922
  init_dlp();
8626
8923
  init_dist();
8627
- PROJECTS_DIR = () => import_path19.default.join(import_os16.default.homedir(), ".claude", "projects");
8628
- WATERMARK_FILE = () => import_path19.default.join(import_os16.default.homedir(), ".node9", "scan-watermark.json");
8924
+ PROJECTS_DIR = () => import_path20.default.join(import_os17.default.homedir(), ".claude", "projects");
8925
+ WATERMARK_FILE = () => import_path20.default.join(import_os17.default.homedir(), ".node9", "scan-watermark.json");
8629
8926
  MAX_LINE_BYTES = 2 * 1024 * 1024;
8630
8927
  WATERMARK_SCHEMA_VERSION = 2;
8631
8928
  LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
@@ -8673,40 +8970,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
8673
8970
  return now.getTime() - 90 * 864e5;
8674
8971
  }
8675
8972
  function* iterateJsonlFiles(cutoffMs) {
8676
- const projectsDir = import_path20.default.join(import_os17.default.homedir(), ".claude", "projects");
8973
+ const projectsDir = import_path21.default.join(import_os18.default.homedir(), ".claude", "projects");
8677
8974
  let dirs;
8678
8975
  try {
8679
- dirs = import_fs18.default.readdirSync(projectsDir);
8976
+ dirs = import_fs19.default.readdirSync(projectsDir);
8680
8977
  } catch {
8681
8978
  return;
8682
8979
  }
8683
8980
  for (const dir of dirs) {
8684
- const dirPath = import_path20.default.join(projectsDir, dir);
8981
+ const dirPath = import_path21.default.join(projectsDir, dir);
8685
8982
  let stats;
8686
8983
  try {
8687
- stats = import_fs18.default.statSync(dirPath);
8984
+ stats = import_fs19.default.statSync(dirPath);
8688
8985
  } catch {
8689
8986
  continue;
8690
8987
  }
8691
8988
  if (!stats.isDirectory()) continue;
8692
8989
  let files;
8693
8990
  try {
8694
- files = import_fs18.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8991
+ files = import_fs19.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8695
8992
  } catch {
8696
8993
  continue;
8697
8994
  }
8698
8995
  for (const file of files) {
8699
- const filePath = import_path20.default.join(dirPath, file);
8996
+ const filePath = import_path21.default.join(dirPath, file);
8700
8997
  let mtime = 0;
8701
8998
  try {
8702
- mtime = import_fs18.default.statSync(filePath).mtimeMs;
8999
+ mtime = import_fs19.default.statSync(filePath).mtimeMs;
8703
9000
  } catch {
8704
9001
  continue;
8705
9002
  }
8706
9003
  if (mtime < cutoffMs) continue;
8707
9004
  yield {
8708
9005
  filePath,
8709
- sessionId: import_path20.default.basename(file, ".jsonl"),
9006
+ sessionId: import_path21.default.basename(file, ".jsonl"),
8710
9007
  projectDir: dir
8711
9008
  };
8712
9009
  }
@@ -8759,7 +9056,7 @@ async function runUploadHistory(opts) {
8759
9056
  filesScanned++;
8760
9057
  let content;
8761
9058
  try {
8762
- content = import_fs18.default.readFileSync(filePath, "utf8");
9059
+ content = import_fs19.default.readFileSync(filePath, "utf8");
8763
9060
  } catch {
8764
9061
  continue;
8765
9062
  }
@@ -8845,10 +9142,10 @@ async function runUploadHistory(opts) {
8845
9142
  const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
8846
9143
  let username = "unknown";
8847
9144
  try {
8848
- username = import_os17.default.userInfo().username;
9145
+ username = import_os18.default.userInfo().username;
8849
9146
  } catch {
8850
9147
  }
8851
- const machineId = `${import_os17.default.hostname()}:${username}`;
9148
+ const machineId = `${import_os18.default.hostname()}:${username}`;
8852
9149
  await postJson(costUrl, creds.apiKey, {
8853
9150
  machineId,
8854
9151
  entries: dailyEntries
@@ -8898,14 +9195,14 @@ async function postJson(url, apiKey, body) {
8898
9195
  req.end();
8899
9196
  });
8900
9197
  }
8901
- var import_fs18, import_https, import_os17, import_path20, import_chalk4, FINDING_TO_SIGNAL2;
9198
+ var import_fs19, import_https, import_os18, import_path21, import_chalk4, FINDING_TO_SIGNAL2;
8902
9199
  var init_scan_upload_history = __esm({
8903
9200
  "src/scan-upload-history.ts"() {
8904
9201
  "use strict";
8905
- import_fs18 = __toESM(require("fs"));
9202
+ import_fs19 = __toESM(require("fs"));
8906
9203
  import_https = __toESM(require("https"));
8907
- import_os17 = __toESM(require("os"));
8908
- import_path20 = __toESM(require("path"));
9204
+ import_os18 = __toESM(require("os"));
9205
+ import_path21 = __toESM(require("path"));
8909
9206
  import_chalk4 = __toESM(require("chalk"));
8910
9207
  init_dist();
8911
9208
  init_config();
@@ -9151,14 +9448,14 @@ function buildRuleSources() {
9151
9448
  }
9152
9449
  function countScanFiles() {
9153
9450
  let total = 0;
9154
- const claudeDir = import_path21.default.join(import_os18.default.homedir(), ".claude", "projects");
9155
- if (import_fs19.default.existsSync(claudeDir)) {
9451
+ const claudeDir = import_path22.default.join(import_os19.default.homedir(), ".claude", "projects");
9452
+ if (import_fs20.default.existsSync(claudeDir)) {
9156
9453
  try {
9157
- for (const proj of import_fs19.default.readdirSync(claudeDir)) {
9158
- const p = import_path21.default.join(claudeDir, proj);
9454
+ for (const proj of import_fs20.default.readdirSync(claudeDir)) {
9455
+ const p = import_path22.default.join(claudeDir, proj);
9159
9456
  try {
9160
- if (!import_fs19.default.statSync(p).isDirectory()) continue;
9161
- total += import_fs19.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
9457
+ if (!import_fs20.default.statSync(p).isDirectory()) continue;
9458
+ total += import_fs20.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
9162
9459
  } catch {
9163
9460
  continue;
9164
9461
  }
@@ -9166,17 +9463,17 @@ function countScanFiles() {
9166
9463
  } catch {
9167
9464
  }
9168
9465
  }
9169
- const geminiDir = import_path21.default.join(import_os18.default.homedir(), ".gemini", "tmp");
9170
- if (import_fs19.default.existsSync(geminiDir)) {
9466
+ const geminiDir = import_path22.default.join(import_os19.default.homedir(), ".gemini", "tmp");
9467
+ if (import_fs20.default.existsSync(geminiDir)) {
9171
9468
  try {
9172
- for (const slug of import_fs19.default.readdirSync(geminiDir)) {
9173
- const p = import_path21.default.join(geminiDir, slug);
9469
+ for (const slug of import_fs20.default.readdirSync(geminiDir)) {
9470
+ const p = import_path22.default.join(geminiDir, slug);
9174
9471
  try {
9175
- if (!import_fs19.default.statSync(p).isDirectory()) continue;
9176
- const chatsDir = import_path21.default.join(p, "chats");
9177
- if (import_fs19.default.existsSync(chatsDir)) {
9472
+ if (!import_fs20.default.statSync(p).isDirectory()) continue;
9473
+ const chatsDir = import_path22.default.join(p, "chats");
9474
+ if (import_fs20.default.existsSync(chatsDir)) {
9178
9475
  try {
9179
- total += import_fs19.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
9476
+ total += import_fs20.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
9180
9477
  } catch {
9181
9478
  }
9182
9479
  }
@@ -9187,22 +9484,22 @@ function countScanFiles() {
9187
9484
  } catch {
9188
9485
  }
9189
9486
  }
9190
- const codexDir = import_path21.default.join(import_os18.default.homedir(), ".codex", "sessions");
9191
- if (import_fs19.default.existsSync(codexDir)) {
9487
+ const codexDir = import_path22.default.join(import_os19.default.homedir(), ".codex", "sessions");
9488
+ if (import_fs20.default.existsSync(codexDir)) {
9192
9489
  try {
9193
- for (const year of import_fs19.default.readdirSync(codexDir)) {
9194
- const yp = import_path21.default.join(codexDir, year);
9490
+ for (const year of import_fs20.default.readdirSync(codexDir)) {
9491
+ const yp = import_path22.default.join(codexDir, year);
9195
9492
  try {
9196
- if (!import_fs19.default.statSync(yp).isDirectory()) continue;
9197
- for (const month of import_fs19.default.readdirSync(yp)) {
9198
- const mp = import_path21.default.join(yp, month);
9493
+ if (!import_fs20.default.statSync(yp).isDirectory()) continue;
9494
+ for (const month of import_fs20.default.readdirSync(yp)) {
9495
+ const mp = import_path22.default.join(yp, month);
9199
9496
  try {
9200
- if (!import_fs19.default.statSync(mp).isDirectory()) continue;
9201
- for (const day of import_fs19.default.readdirSync(mp)) {
9202
- const dp = import_path21.default.join(mp, day);
9497
+ if (!import_fs20.default.statSync(mp).isDirectory()) continue;
9498
+ for (const day of import_fs20.default.readdirSync(mp)) {
9499
+ const dp = import_path22.default.join(mp, day);
9203
9500
  try {
9204
- if (!import_fs19.default.statSync(dp).isDirectory()) continue;
9205
- total += import_fs19.default.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
9501
+ if (!import_fs20.default.statSync(dp).isDirectory()) continue;
9502
+ total += import_fs20.default.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
9206
9503
  } catch {
9207
9504
  continue;
9208
9505
  }
@@ -9238,7 +9535,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9238
9535
  const sessionId = file.replace(/\.jsonl$/, "");
9239
9536
  let raw;
9240
9537
  try {
9241
- raw = import_fs19.default.readFileSync(import_path21.default.join(projPath, file), "utf-8");
9538
+ raw = import_fs20.default.readFileSync(import_path22.default.join(projPath, file), "utf-8");
9242
9539
  } catch {
9243
9540
  return;
9244
9541
  }
@@ -9290,7 +9587,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9290
9587
  if (block.type !== "tool_result") continue;
9291
9588
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
9292
9589
  if (filePath) {
9293
- const ext = import_path21.default.extname(filePath).toLowerCase();
9590
+ const ext = import_path22.default.extname(filePath).toLowerCase();
9294
9591
  if (CODE_EXTENSIONS.has(ext)) continue;
9295
9592
  }
9296
9593
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -9347,7 +9644,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9347
9644
  const rawCmd = String(input.command ?? "").trimStart();
9348
9645
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
9349
9646
  const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
9350
- const inputFileExt = inputFilePath ? import_path21.default.extname(inputFilePath).toLowerCase() : "";
9647
+ const inputFileExt = inputFilePath ? import_path22.default.extname(inputFilePath).toLowerCase() : "";
9351
9648
  if (CODE_EXTENSIONS.has(inputFileExt)) continue;
9352
9649
  const dlpMatch = scanArgs(input);
9353
9650
  if (dlpMatch) {
@@ -9444,19 +9741,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9444
9741
  }
9445
9742
  }
9446
9743
  function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
9447
- const projPath = import_path21.default.join(projectsDir, proj);
9744
+ const projPath = import_path22.default.join(projectsDir, proj);
9448
9745
  try {
9449
- if (!import_fs19.default.statSync(projPath).isDirectory()) return;
9746
+ if (!import_fs20.default.statSync(projPath).isDirectory()) return;
9450
9747
  } catch {
9451
9748
  return;
9452
9749
  }
9453
- const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(import_os18.default.homedir(), "~")).slice(
9750
+ const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(import_os19.default.homedir(), "~")).slice(
9454
9751
  0,
9455
9752
  40
9456
9753
  );
9457
9754
  let files;
9458
9755
  try {
9459
- files = import_fs19.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
9756
+ files = import_fs20.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
9460
9757
  } catch {
9461
9758
  return;
9462
9759
  }
@@ -9490,12 +9787,12 @@ function emptyClaudeScan() {
9490
9787
  };
9491
9788
  }
9492
9789
  function scanClaudeHistory(startDate, onProgress, onLine) {
9493
- const projectsDir = import_path21.default.join(import_os18.default.homedir(), ".claude", "projects");
9790
+ const projectsDir = import_path22.default.join(import_os19.default.homedir(), ".claude", "projects");
9494
9791
  const result = emptyClaudeScan();
9495
- if (!import_fs19.default.existsSync(projectsDir)) return result;
9792
+ if (!import_fs20.default.existsSync(projectsDir)) return result;
9496
9793
  let projDirs;
9497
9794
  try {
9498
- projDirs = import_fs19.default.readdirSync(projectsDir);
9795
+ projDirs = import_fs20.default.readdirSync(projectsDir);
9499
9796
  } catch {
9500
9797
  return result;
9501
9798
  }
@@ -9516,7 +9813,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
9516
9813
  return result;
9517
9814
  }
9518
9815
  function scanGeminiHistory(startDate, onProgress, onLine) {
9519
- const tmpDir = import_path21.default.join(import_os18.default.homedir(), ".gemini", "tmp");
9816
+ const tmpDir = import_path22.default.join(import_os19.default.homedir(), ".gemini", "tmp");
9520
9817
  const result = {
9521
9818
  filesScanned: 0,
9522
9819
  sessions: 0,
@@ -9531,33 +9828,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
9531
9828
  sessionsWithEarlySecrets: 0
9532
9829
  };
9533
9830
  const dedup = emptyScanDedup();
9534
- if (!import_fs19.default.existsSync(tmpDir)) return result;
9831
+ if (!import_fs20.default.existsSync(tmpDir)) return result;
9535
9832
  let slugDirs;
9536
9833
  try {
9537
- slugDirs = import_fs19.default.readdirSync(tmpDir);
9834
+ slugDirs = import_fs20.default.readdirSync(tmpDir);
9538
9835
  } catch {
9539
9836
  return result;
9540
9837
  }
9541
9838
  const ruleSources = buildRuleSources();
9542
9839
  for (const slug of slugDirs) {
9543
- const slugPath = import_path21.default.join(tmpDir, slug);
9840
+ const slugPath = import_path22.default.join(tmpDir, slug);
9544
9841
  try {
9545
- if (!import_fs19.default.statSync(slugPath).isDirectory()) continue;
9842
+ if (!import_fs20.default.statSync(slugPath).isDirectory()) continue;
9546
9843
  } catch {
9547
9844
  continue;
9548
9845
  }
9549
9846
  let projLabel = stripTerminalEscapes(slug).slice(0, 40);
9550
9847
  try {
9551
9848
  projLabel = stripTerminalEscapes(
9552
- import_fs19.default.readFileSync(import_path21.default.join(slugPath, ".project_root"), "utf-8").trim()
9553
- ).replace(import_os18.default.homedir(), "~").slice(0, 40);
9849
+ import_fs20.default.readFileSync(import_path22.default.join(slugPath, ".project_root"), "utf-8").trim()
9850
+ ).replace(import_os19.default.homedir(), "~").slice(0, 40);
9554
9851
  } catch {
9555
9852
  }
9556
- const chatsDir = import_path21.default.join(slugPath, "chats");
9557
- if (!import_fs19.default.existsSync(chatsDir)) continue;
9853
+ const chatsDir = import_path22.default.join(slugPath, "chats");
9854
+ if (!import_fs20.default.existsSync(chatsDir)) continue;
9558
9855
  let chatFiles;
9559
9856
  try {
9560
- chatFiles = import_fs19.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
9857
+ chatFiles = import_fs20.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
9561
9858
  } catch {
9562
9859
  continue;
9563
9860
  }
@@ -9567,7 +9864,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
9567
9864
  const sessionId = chatFile.replace(/\.json$/, "");
9568
9865
  let raw;
9569
9866
  try {
9570
- raw = import_fs19.default.readFileSync(import_path21.default.join(chatsDir, chatFile), "utf-8");
9867
+ raw = import_fs20.default.readFileSync(import_path22.default.join(chatsDir, chatFile), "utf-8");
9571
9868
  } catch {
9572
9869
  continue;
9573
9870
  }
@@ -9729,7 +10026,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
9729
10026
  return result;
9730
10027
  }
9731
10028
  function scanCodexHistory(startDate, onProgress, onLine) {
9732
- const sessionsBase = import_path21.default.join(import_os18.default.homedir(), ".codex", "sessions");
10029
+ const sessionsBase = import_path22.default.join(import_os19.default.homedir(), ".codex", "sessions");
9733
10030
  const result = {
9734
10031
  filesScanned: 0,
9735
10032
  sessions: 0,
@@ -9744,32 +10041,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9744
10041
  sessionsWithEarlySecrets: 0
9745
10042
  };
9746
10043
  const dedup = emptyScanDedup();
9747
- if (!import_fs19.default.existsSync(sessionsBase)) return result;
10044
+ if (!import_fs20.default.existsSync(sessionsBase)) return result;
9748
10045
  const jsonlFiles = [];
9749
10046
  try {
9750
- for (const year of import_fs19.default.readdirSync(sessionsBase)) {
9751
- const yearPath = import_path21.default.join(sessionsBase, year);
10047
+ for (const year of import_fs20.default.readdirSync(sessionsBase)) {
10048
+ const yearPath = import_path22.default.join(sessionsBase, year);
9752
10049
  try {
9753
- if (!import_fs19.default.statSync(yearPath).isDirectory()) continue;
10050
+ if (!import_fs20.default.statSync(yearPath).isDirectory()) continue;
9754
10051
  } catch {
9755
10052
  continue;
9756
10053
  }
9757
- for (const month of import_fs19.default.readdirSync(yearPath)) {
9758
- const monthPath = import_path21.default.join(yearPath, month);
10054
+ for (const month of import_fs20.default.readdirSync(yearPath)) {
10055
+ const monthPath = import_path22.default.join(yearPath, month);
9759
10056
  try {
9760
- if (!import_fs19.default.statSync(monthPath).isDirectory()) continue;
10057
+ if (!import_fs20.default.statSync(monthPath).isDirectory()) continue;
9761
10058
  } catch {
9762
10059
  continue;
9763
10060
  }
9764
- for (const day of import_fs19.default.readdirSync(monthPath)) {
9765
- const dayPath = import_path21.default.join(monthPath, day);
10061
+ for (const day of import_fs20.default.readdirSync(monthPath)) {
10062
+ const dayPath = import_path22.default.join(monthPath, day);
9766
10063
  try {
9767
- if (!import_fs19.default.statSync(dayPath).isDirectory()) continue;
10064
+ if (!import_fs20.default.statSync(dayPath).isDirectory()) continue;
9768
10065
  } catch {
9769
10066
  continue;
9770
10067
  }
9771
- for (const file of import_fs19.default.readdirSync(dayPath)) {
9772
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path21.default.join(dayPath, file));
10068
+ for (const file of import_fs20.default.readdirSync(dayPath)) {
10069
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path22.default.join(dayPath, file));
9773
10070
  }
9774
10071
  }
9775
10072
  }
@@ -9783,7 +10080,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9783
10080
  onProgress?.(result.filesScanned);
9784
10081
  let lines;
9785
10082
  try {
9786
- lines = import_fs19.default.readFileSync(filePath, "utf-8").split("\n");
10083
+ lines = import_fs20.default.readFileSync(filePath, "utf-8").split("\n");
9787
10084
  } catch {
9788
10085
  continue;
9789
10086
  }
@@ -9809,7 +10106,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9809
10106
  sessionId = String(payload["id"] ?? filePath);
9810
10107
  startTime = String(payload["timestamp"] ?? "");
9811
10108
  const cwd = String(payload["cwd"] ?? "");
9812
- projLabel = stripTerminalEscapes(cwd.replace(import_os18.default.homedir(), "~")).slice(0, 40);
10109
+ projLabel = stripTerminalEscapes(cwd.replace(import_os19.default.homedir(), "~")).slice(0, 40);
9813
10110
  continue;
9814
10111
  }
9815
10112
  if (entry.type === "event_msg" && payload["type"] === "token_count") {
@@ -9962,17 +10259,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9962
10259
  return result;
9963
10260
  }
9964
10261
  function scanShellConfig() {
9965
- const home = import_os18.default.homedir();
10262
+ const home = import_os19.default.homedir();
9966
10263
  const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
9967
- (f) => import_path21.default.join(home, f)
10264
+ (f) => import_path22.default.join(home, f)
9968
10265
  );
9969
10266
  const findings = [];
9970
10267
  const seen = /* @__PURE__ */ new Set();
9971
10268
  for (const filePath of configFiles) {
9972
- if (!import_fs19.default.existsSync(filePath)) continue;
10269
+ if (!import_fs20.default.existsSync(filePath)) continue;
9973
10270
  let lines;
9974
10271
  try {
9975
- lines = import_fs19.default.readFileSync(filePath, "utf-8").split("\n");
10272
+ lines = import_fs20.default.readFileSync(filePath, "utf-8").split("\n");
9976
10273
  } catch {
9977
10274
  continue;
9978
10275
  }
@@ -10752,7 +11049,7 @@ function registerScanCommand(program2) {
10752
11049
  if (!drillDown) {
10753
11050
  const useInk2 = !options.classic;
10754
11051
  if (useInk2) {
10755
- const scanInkPath = import_path21.default.join(__dirname, "scan-ink.mjs");
11052
+ const scanInkPath = import_path22.default.join(__dirname, "scan-ink.mjs");
10756
11053
  const dynamicImport = new Function("id", "return import(id)");
10757
11054
  const mod = await dynamicImport(`file://${scanInkPath}`);
10758
11055
  const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
@@ -10977,14 +11274,14 @@ function registerScanCommand(program2) {
10977
11274
  }
10978
11275
  );
10979
11276
  }
10980
- var import_chalk5, import_fs19, import_path21, import_os18, import_string_width2, CLAUDE_PRICING, GEMINI_PRICING, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, FIXTURE_TOKEN_PATTERNS, TERMINAL_ESCAPE_RE2, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
11277
+ var import_chalk5, import_fs20, import_path22, import_os19, import_string_width2, CLAUDE_PRICING, GEMINI_PRICING, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, FIXTURE_TOKEN_PATTERNS, TERMINAL_ESCAPE_RE2, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
10981
11278
  var init_scan = __esm({
10982
11279
  "src/cli/commands/scan.ts"() {
10983
11280
  "use strict";
10984
11281
  import_chalk5 = __toESM(require("chalk"));
10985
- import_fs19 = __toESM(require("fs"));
10986
- import_path21 = __toESM(require("path"));
10987
- import_os18 = __toESM(require("os"));
11282
+ import_fs20 = __toESM(require("fs"));
11283
+ import_path22 = __toESM(require("path"));
11284
+ import_os19 = __toESM(require("os"));
10988
11285
  init_shields();
10989
11286
  init_config();
10990
11287
  init_policy();
@@ -11111,11 +11408,11 @@ function commonPathPrefix(paths) {
11111
11408
  const prefix = common.join("/").replace(/\/?$/, "/");
11112
11409
  return prefix.length > 1 ? prefix : null;
11113
11410
  }
11114
- var import_crypto5, SuggestionTracker;
11411
+ var import_crypto6, SuggestionTracker;
11115
11412
  var init_suggestion_tracker = __esm({
11116
11413
  "src/daemon/suggestion-tracker.ts"() {
11117
11414
  "use strict";
11118
- import_crypto5 = require("crypto");
11415
+ import_crypto6 = require("crypto");
11119
11416
  SuggestionTracker = class {
11120
11417
  events = /* @__PURE__ */ new Map();
11121
11418
  threshold;
@@ -11161,7 +11458,7 @@ var init_suggestion_tracker = __esm({
11161
11458
  }
11162
11459
  } : { type: "ignoredTool", toolName };
11163
11460
  return {
11164
- id: (0, import_crypto5.randomUUID)(),
11461
+ id: (0, import_crypto6.randomUUID)(),
11165
11462
  toolName,
11166
11463
  allowCount: events.length,
11167
11464
  suggestedRule,
@@ -11175,12 +11472,12 @@ var init_suggestion_tracker = __esm({
11175
11472
  });
11176
11473
 
11177
11474
  // src/daemon/taint-store.ts
11178
- var import_fs20, import_path22, DEFAULT_TTL_MS, TaintStore;
11475
+ var import_fs21, import_path23, DEFAULT_TTL_MS, TaintStore;
11179
11476
  var init_taint_store = __esm({
11180
11477
  "src/daemon/taint-store.ts"() {
11181
11478
  "use strict";
11182
- import_fs20 = __toESM(require("fs"));
11183
- import_path22 = __toESM(require("path"));
11479
+ import_fs21 = __toESM(require("fs"));
11480
+ import_path23 = __toESM(require("path"));
11184
11481
  DEFAULT_TTL_MS = 60 * 60 * 1e3;
11185
11482
  TaintStore = class {
11186
11483
  records = /* @__PURE__ */ new Map();
@@ -11245,9 +11542,9 @@ var init_taint_store = __esm({
11245
11542
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
11246
11543
  _resolve(filePath) {
11247
11544
  try {
11248
- return import_fs20.default.realpathSync.native(import_path22.default.resolve(filePath));
11545
+ return import_fs21.default.realpathSync.native(import_path23.default.resolve(filePath));
11249
11546
  } catch {
11250
- return import_path22.default.resolve(filePath);
11547
+ return import_path23.default.resolve(filePath);
11251
11548
  }
11252
11549
  }
11253
11550
  };
@@ -11365,8 +11662,8 @@ var init_session_history = __esm({
11365
11662
  // src/daemon/state.ts
11366
11663
  function loadInsightCounts() {
11367
11664
  try {
11368
- if (!import_fs21.default.existsSync(INSIGHT_COUNTS_FILE)) return;
11369
- const data = JSON.parse(import_fs21.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
11665
+ if (!import_fs22.default.existsSync(INSIGHT_COUNTS_FILE)) return;
11666
+ const data = JSON.parse(import_fs22.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
11370
11667
  for (const [tool, count] of Object.entries(data)) {
11371
11668
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
11372
11669
  }
@@ -11405,23 +11702,23 @@ function markRejectionHandlerRegistered() {
11405
11702
  daemonRejectionHandlerRegistered = true;
11406
11703
  }
11407
11704
  function atomicWriteSync2(filePath, data, options) {
11408
- const dir = import_path23.default.dirname(filePath);
11409
- if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
11410
- const tmpPath = `${filePath}.${(0, import_crypto6.randomUUID)()}.tmp`;
11705
+ const dir = import_path24.default.dirname(filePath);
11706
+ if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
11707
+ const tmpPath = `${filePath}.${(0, import_crypto7.randomUUID)()}.tmp`;
11411
11708
  try {
11412
- import_fs21.default.writeFileSync(tmpPath, data, options);
11709
+ import_fs22.default.writeFileSync(tmpPath, data, options);
11413
11710
  } catch (err2) {
11414
11711
  try {
11415
- import_fs21.default.unlinkSync(tmpPath);
11712
+ import_fs22.default.unlinkSync(tmpPath);
11416
11713
  } catch {
11417
11714
  }
11418
11715
  throw err2;
11419
11716
  }
11420
11717
  try {
11421
- import_fs21.default.renameSync(tmpPath, filePath);
11718
+ import_fs22.default.renameSync(tmpPath, filePath);
11422
11719
  } catch (err2) {
11423
11720
  try {
11424
- import_fs21.default.unlinkSync(tmpPath);
11721
+ import_fs22.default.unlinkSync(tmpPath);
11425
11722
  } catch {
11426
11723
  }
11427
11724
  throw err2;
@@ -11445,16 +11742,16 @@ function appendAuditLog(data) {
11445
11742
  decision: data.decision,
11446
11743
  source: "daemon"
11447
11744
  };
11448
- const dir = import_path23.default.dirname(AUDIT_LOG_FILE);
11449
- if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
11450
- import_fs21.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
11745
+ const dir = import_path24.default.dirname(AUDIT_LOG_FILE);
11746
+ if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
11747
+ import_fs22.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
11451
11748
  } catch {
11452
11749
  }
11453
11750
  }
11454
11751
  function getAuditHistory(limit = 20) {
11455
11752
  try {
11456
- if (!import_fs21.default.existsSync(AUDIT_LOG_FILE)) return [];
11457
- const lines = import_fs21.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
11753
+ if (!import_fs22.default.existsSync(AUDIT_LOG_FILE)) return [];
11754
+ const lines = import_fs22.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
11458
11755
  if (lines.length === 1 && lines[0] === "") return [];
11459
11756
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
11460
11757
  } catch {
@@ -11463,7 +11760,7 @@ function getAuditHistory(limit = 20) {
11463
11760
  }
11464
11761
  function getOrgName() {
11465
11762
  try {
11466
- if (import_fs21.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
11763
+ if (import_fs22.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
11467
11764
  } catch {
11468
11765
  }
11469
11766
  return null;
@@ -11471,8 +11768,8 @@ function getOrgName() {
11471
11768
  function writeGlobalSetting(key, value) {
11472
11769
  let config = {};
11473
11770
  try {
11474
- if (import_fs21.default.existsSync(GLOBAL_CONFIG_FILE)) {
11475
- config = JSON.parse(import_fs21.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
11771
+ if (import_fs22.default.existsSync(GLOBAL_CONFIG_FILE)) {
11772
+ config = JSON.parse(import_fs22.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
11476
11773
  }
11477
11774
  } catch {
11478
11775
  }
@@ -11484,8 +11781,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
11484
11781
  try {
11485
11782
  let trust = { entries: [] };
11486
11783
  try {
11487
- if (import_fs21.default.existsSync(TRUST_FILE2))
11488
- trust = JSON.parse(import_fs21.default.readFileSync(TRUST_FILE2, "utf-8"));
11784
+ if (import_fs22.default.existsSync(TRUST_FILE2))
11785
+ trust = JSON.parse(import_fs22.default.readFileSync(TRUST_FILE2, "utf-8"));
11489
11786
  } catch {
11490
11787
  }
11491
11788
  trust.entries = trust.entries.filter(
@@ -11502,8 +11799,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
11502
11799
  }
11503
11800
  function readPersistentDecisions() {
11504
11801
  try {
11505
- if (import_fs21.default.existsSync(DECISIONS_FILE)) {
11506
- return JSON.parse(import_fs21.default.readFileSync(DECISIONS_FILE, "utf-8"));
11802
+ if (import_fs22.default.existsSync(DECISIONS_FILE)) {
11803
+ return JSON.parse(import_fs22.default.readFileSync(DECISIONS_FILE, "utf-8"));
11507
11804
  }
11508
11805
  } catch {
11509
11806
  }
@@ -11531,7 +11828,7 @@ function estimateToolCost(tool, args) {
11531
11828
  const filePath = a.file_path ?? a.path;
11532
11829
  if (filePath) {
11533
11830
  try {
11534
- const bytes = import_fs21.default.statSync(filePath).size;
11831
+ const bytes = import_fs22.default.statSync(filePath).size;
11535
11832
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
11536
11833
  } catch {
11537
11834
  }
@@ -11582,7 +11879,7 @@ function broadcastForensic(finding) {
11582
11879
  const severity = CRITICAL_FORENSIC_CATEGORIES.has(finding.type) ? "critical" : "warning";
11583
11880
  const event = {
11584
11881
  type: "forensic",
11585
- id: `fnd_${(0, import_crypto6.randomUUID)()}`,
11882
+ id: `fnd_${(0, import_crypto7.randomUUID)()}`,
11586
11883
  ts: Date.now(),
11587
11884
  sessionId: finding.sessionId,
11588
11885
  category: finding.type,
@@ -11602,7 +11899,7 @@ function abandonPending() {
11602
11899
  });
11603
11900
  if (autoStarted) {
11604
11901
  try {
11605
- import_fs21.default.unlinkSync(DAEMON_PID_FILE);
11902
+ import_fs22.default.unlinkSync(DAEMON_PID_FILE);
11606
11903
  } catch {
11607
11904
  }
11608
11905
  setTimeout(() => {
@@ -11613,8 +11910,8 @@ function abandonPending() {
11613
11910
  }
11614
11911
  function logActivitySocket(msg) {
11615
11912
  try {
11616
- import_fs21.default.appendFileSync(
11617
- import_path23.default.join(homeDir, ".node9", "hook-debug.log"),
11913
+ import_fs22.default.appendFileSync(
11914
+ import_path24.default.join(homeDir, ".node9", "hook-debug.log"),
11618
11915
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
11619
11916
  `
11620
11917
  );
@@ -11636,13 +11933,13 @@ function shouldRebind(now = Date.now()) {
11636
11933
  function startActivitySocket() {
11637
11934
  bindActivitySocket();
11638
11935
  activityHealthInterval = setInterval(() => {
11639
- if (!import_fs21.default.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
11936
+ if (!import_fs22.default.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
11640
11937
  }, ACTIVITY_HEALTH_PROBE_MS);
11641
11938
  activityHealthInterval.unref();
11642
11939
  process.on("exit", () => {
11643
11940
  if (activityHealthInterval) clearInterval(activityHealthInterval);
11644
11941
  try {
11645
- import_fs21.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
11942
+ import_fs22.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
11646
11943
  } catch {
11647
11944
  }
11648
11945
  });
@@ -11670,7 +11967,7 @@ function attemptRebind(reason) {
11670
11967
  }
11671
11968
  function bindActivitySocket() {
11672
11969
  try {
11673
- import_fs21.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
11970
+ import_fs22.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
11674
11971
  } catch {
11675
11972
  }
11676
11973
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -11774,28 +12071,28 @@ function bindActivitySocket() {
11774
12071
  });
11775
12072
  activitySocketServer = unixServer;
11776
12073
  }
11777
- var import_net2, import_fs21, import_path23, import_os19, import_crypto6, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
12074
+ var import_net2, import_fs22, import_path24, import_os20, import_crypto7, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
11778
12075
  var init_state2 = __esm({
11779
12076
  "src/daemon/state.ts"() {
11780
12077
  "use strict";
11781
12078
  import_net2 = __toESM(require("net"));
11782
- import_fs21 = __toESM(require("fs"));
11783
- import_path23 = __toESM(require("path"));
11784
- import_os19 = __toESM(require("os"));
11785
- import_crypto6 = require("crypto");
12079
+ import_fs22 = __toESM(require("fs"));
12080
+ import_path24 = __toESM(require("path"));
12081
+ import_os20 = __toESM(require("os"));
12082
+ import_crypto7 = require("crypto");
11786
12083
  init_daemon();
11787
12084
  init_suggestion_tracker();
11788
12085
  init_taint_store();
11789
12086
  init_session_counters();
11790
12087
  init_session_history();
11791
- homeDir = import_os19.default.homedir();
11792
- DAEMON_PID_FILE = import_path23.default.join(homeDir, ".node9", "daemon.pid");
11793
- DECISIONS_FILE = import_path23.default.join(homeDir, ".node9", "decisions.json");
11794
- AUDIT_LOG_FILE = import_path23.default.join(homeDir, ".node9", "audit.log");
11795
- TRUST_FILE2 = import_path23.default.join(homeDir, ".node9", "trust.json");
11796
- GLOBAL_CONFIG_FILE = import_path23.default.join(homeDir, ".node9", "config.json");
11797
- CREDENTIALS_FILE = import_path23.default.join(homeDir, ".node9", "credentials.json");
11798
- INSIGHT_COUNTS_FILE = import_path23.default.join(homeDir, ".node9", "insight-counts.json");
12088
+ homeDir = import_os20.default.homedir();
12089
+ DAEMON_PID_FILE = import_path24.default.join(homeDir, ".node9", "daemon.pid");
12090
+ DECISIONS_FILE = import_path24.default.join(homeDir, ".node9", "decisions.json");
12091
+ AUDIT_LOG_FILE = import_path24.default.join(homeDir, ".node9", "audit.log");
12092
+ TRUST_FILE2 = import_path24.default.join(homeDir, ".node9", "trust.json");
12093
+ GLOBAL_CONFIG_FILE = import_path24.default.join(homeDir, ".node9", "config.json");
12094
+ CREDENTIALS_FILE = import_path24.default.join(homeDir, ".node9", "credentials.json");
12095
+ INSIGHT_COUNTS_FILE = import_path24.default.join(homeDir, ".node9", "insight-counts.json");
11799
12096
  pending = /* @__PURE__ */ new Map();
11800
12097
  sseClients = /* @__PURE__ */ new Set();
11801
12098
  suggestionTracker = new SuggestionTracker(3);
@@ -11812,7 +12109,7 @@ var init_state2 = __esm({
11812
12109
  "2h": 2 * 60 * 6e4
11813
12110
  };
11814
12111
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
11815
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path23.default.join(import_os19.default.tmpdir(), "node9-activity.sock");
12112
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path24.default.join(import_os20.default.tmpdir(), "node9-activity.sock");
11816
12113
  ACTIVITY_RING_SIZE = 100;
11817
12114
  activityRing = [];
11818
12115
  LARGE_RESPONSE_RING_SIZE = 20;
@@ -11890,8 +12187,8 @@ function readCredentials() {
11890
12187
  };
11891
12188
  }
11892
12189
  try {
11893
- const credPath = import_path24.default.join(import_os20.default.homedir(), ".node9", "credentials.json");
11894
- const creds = JSON.parse(import_fs22.default.readFileSync(credPath, "utf-8"));
12190
+ const credPath = import_path25.default.join(import_os21.default.homedir(), ".node9", "credentials.json");
12191
+ const creds = JSON.parse(import_fs23.default.readFileSync(credPath, "utf-8"));
11895
12192
  const profileName = process.env.NODE9_PROFILE ?? "default";
11896
12193
  const profile = creds[profileName];
11897
12194
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -11917,7 +12214,7 @@ function readCredentials() {
11917
12214
  }
11918
12215
  function readCachedEtag() {
11919
12216
  try {
11920
- const raw = JSON.parse(import_fs22.default.readFileSync(rulesCacheFile(), "utf-8"));
12217
+ const raw = JSON.parse(import_fs23.default.readFileSync(rulesCacheFile(), "utf-8"));
11921
12218
  return typeof raw.etag === "string" ? raw.etag : void 0;
11922
12219
  } catch {
11923
12220
  return void 0;
@@ -11978,9 +12275,9 @@ function extractRules(body) {
11978
12275
  return [];
11979
12276
  }
11980
12277
  function writeCache2(cache) {
11981
- const dir = import_path24.default.dirname(rulesCacheFile());
11982
- if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
11983
- import_fs22.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
12278
+ const dir = import_path25.default.dirname(rulesCacheFile());
12279
+ if (!import_fs23.default.existsSync(dir)) import_fs23.default.mkdirSync(dir, { recursive: true });
12280
+ import_fs23.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
11984
12281
  }
11985
12282
  async function syncOnce() {
11986
12283
  const creds = readCredentials();
@@ -12137,7 +12434,7 @@ async function runCloudSync() {
12137
12434
  }
12138
12435
  function getCloudSyncStatus() {
12139
12436
  try {
12140
- const raw = JSON.parse(import_fs22.default.readFileSync(rulesCacheFile(), "utf-8"));
12437
+ const raw = JSON.parse(import_fs23.default.readFileSync(rulesCacheFile(), "utf-8"));
12141
12438
  if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
12142
12439
  return {
12143
12440
  cached: true,
@@ -12154,7 +12451,7 @@ function getCloudSyncStatus() {
12154
12451
  }
12155
12452
  function getCloudRules() {
12156
12453
  try {
12157
- const raw = JSON.parse(import_fs22.default.readFileSync(rulesCacheFile(), "utf-8"));
12454
+ const raw = JSON.parse(import_fs23.default.readFileSync(rulesCacheFile(), "utf-8"));
12158
12455
  return Array.isArray(raw.rules) ? raw.rules : null;
12159
12456
  } catch {
12160
12457
  return null;
@@ -12188,14 +12485,14 @@ function startForensicBroadcast() {
12188
12485
  const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
12189
12486
  recurring.unref();
12190
12487
  }
12191
- var import_fs22, import_https2, import_os20, import_path24, FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
12488
+ var import_fs23, import_https2, import_os21, import_path25, FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
12192
12489
  var init_sync = __esm({
12193
12490
  "src/daemon/sync.ts"() {
12194
12491
  "use strict";
12195
- import_fs22 = __toESM(require("fs"));
12492
+ import_fs23 = __toESM(require("fs"));
12196
12493
  import_https2 = __toESM(require("https"));
12197
- import_os20 = __toESM(require("os"));
12198
- import_path24 = __toESM(require("path"));
12494
+ import_os21 = __toESM(require("os"));
12495
+ import_path25 = __toESM(require("path"));
12199
12496
  init_config();
12200
12497
  init_blast();
12201
12498
  init_dist();
@@ -12214,7 +12511,7 @@ var init_sync = __esm({
12214
12511
  loop: "loops",
12215
12512
  "long-output-redacted": "longOutputRedactions"
12216
12513
  };
12217
- rulesCacheFile = () => import_path24.default.join(import_os20.default.homedir(), ".node9", "rules-cache.json");
12514
+ rulesCacheFile = () => import_path25.default.join(import_os21.default.homedir(), ".node9", "rules-cache.json");
12218
12515
  DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
12219
12516
  DEFAULT_INTERVAL_HOURS = 5;
12220
12517
  MIN_INTERVAL_HOURS = 1;
@@ -12227,68 +12524,68 @@ var init_sync = __esm({
12227
12524
  // src/daemon/dlp-scanner.ts
12228
12525
  function loadIndex() {
12229
12526
  try {
12230
- return JSON.parse(import_fs23.default.readFileSync(INDEX_FILE, "utf-8"));
12527
+ return JSON.parse(import_fs24.default.readFileSync(INDEX_FILE, "utf-8"));
12231
12528
  } catch {
12232
12529
  return {};
12233
12530
  }
12234
12531
  }
12235
12532
  function saveIndex(index) {
12236
12533
  try {
12237
- import_fs23.default.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
12534
+ import_fs24.default.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
12238
12535
  } catch {
12239
12536
  }
12240
12537
  }
12241
12538
  function appendAuditEntry(entry) {
12242
12539
  try {
12243
- import_fs23.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
12540
+ import_fs24.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
12244
12541
  } catch {
12245
12542
  }
12246
12543
  }
12247
12544
  function runDlpScan() {
12248
- if (!import_fs23.default.existsSync(PROJECTS_DIR2)) return;
12545
+ if (!import_fs24.default.existsSync(PROJECTS_DIR2)) return;
12249
12546
  const index = loadIndex();
12250
12547
  let updated = false;
12251
12548
  let projDirs;
12252
12549
  try {
12253
- projDirs = import_fs23.default.readdirSync(PROJECTS_DIR2);
12550
+ projDirs = import_fs24.default.readdirSync(PROJECTS_DIR2);
12254
12551
  } catch {
12255
12552
  return;
12256
12553
  }
12257
12554
  for (const proj of projDirs) {
12258
- const projPath = import_path25.default.join(PROJECTS_DIR2, proj);
12555
+ const projPath = import_path26.default.join(PROJECTS_DIR2, proj);
12259
12556
  try {
12260
- if (!import_fs23.default.lstatSync(projPath).isDirectory()) continue;
12261
- const real = import_fs23.default.realpathSync(projPath);
12262
- if (!real.startsWith(PROJECTS_DIR2 + import_path25.default.sep) && real !== PROJECTS_DIR2) continue;
12557
+ if (!import_fs24.default.lstatSync(projPath).isDirectory()) continue;
12558
+ const real = import_fs24.default.realpathSync(projPath);
12559
+ if (!real.startsWith(PROJECTS_DIR2 + import_path26.default.sep) && real !== PROJECTS_DIR2) continue;
12263
12560
  } catch {
12264
12561
  continue;
12265
12562
  }
12266
12563
  let files;
12267
12564
  try {
12268
- files = import_fs23.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12565
+ files = import_fs24.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12269
12566
  } catch {
12270
12567
  continue;
12271
12568
  }
12272
12569
  for (const file of files) {
12273
- const filePath = import_path25.default.join(projPath, file);
12570
+ const filePath = import_path26.default.join(projPath, file);
12274
12571
  const lastOffset = index[filePath] ?? 0;
12275
12572
  let size;
12276
12573
  try {
12277
- size = import_fs23.default.statSync(filePath).size;
12574
+ size = import_fs24.default.statSync(filePath).size;
12278
12575
  } catch {
12279
12576
  continue;
12280
12577
  }
12281
12578
  if (size <= lastOffset) continue;
12282
12579
  let fd;
12283
12580
  try {
12284
- fd = import_fs23.default.openSync(filePath, "r");
12581
+ fd = import_fs24.default.openSync(filePath, "r");
12285
12582
  } catch {
12286
12583
  continue;
12287
12584
  }
12288
12585
  try {
12289
12586
  const chunkSize = size - lastOffset;
12290
12587
  const buf = Buffer.alloc(chunkSize);
12291
- import_fs23.default.readSync(fd, buf, 0, chunkSize, lastOffset);
12588
+ import_fs24.default.readSync(fd, buf, 0, chunkSize, lastOffset);
12292
12589
  const chunk = buf.toString("utf-8");
12293
12590
  for (const line of chunk.split("\n")) {
12294
12591
  if (!line.trim()) continue;
@@ -12308,7 +12605,7 @@ function runDlpScan() {
12308
12605
  if (typeof text !== "string") continue;
12309
12606
  const match = scanText(text);
12310
12607
  if (!match) continue;
12311
- const projLabel = decodeURIComponent(proj).replace(import_os21.default.homedir(), "~").slice(0, 40);
12608
+ const projLabel = decodeURIComponent(proj).replace(import_os22.default.homedir(), "~").slice(0, 40);
12312
12609
  const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
12313
12610
  appendAuditEntry({
12314
12611
  ts,
@@ -12333,7 +12630,7 @@ Run: node9 report --period 30d`
12333
12630
  updated = true;
12334
12631
  } finally {
12335
12632
  try {
12336
- import_fs23.default.closeSync(fd);
12633
+ import_fs24.default.closeSync(fd);
12337
12634
  } catch {
12338
12635
  }
12339
12636
  }
@@ -12359,30 +12656,30 @@ function startDlpScanner() {
12359
12656
  );
12360
12657
  timer.unref();
12361
12658
  }
12362
- var import_fs23, import_path25, import_os21, INDEX_FILE, PROJECTS_DIR2;
12659
+ var import_fs24, import_path26, import_os22, INDEX_FILE, PROJECTS_DIR2;
12363
12660
  var init_dlp_scanner = __esm({
12364
12661
  "src/daemon/dlp-scanner.ts"() {
12365
12662
  "use strict";
12366
- import_fs23 = __toESM(require("fs"));
12367
- import_path25 = __toESM(require("path"));
12368
- import_os21 = __toESM(require("os"));
12663
+ import_fs24 = __toESM(require("fs"));
12664
+ import_path26 = __toESM(require("path"));
12665
+ import_os22 = __toESM(require("os"));
12369
12666
  init_dlp();
12370
12667
  init_native();
12371
12668
  init_state2();
12372
- INDEX_FILE = import_path25.default.join(import_os21.default.homedir(), ".node9", "dlp-index.json");
12373
- PROJECTS_DIR2 = import_path25.default.join(import_os21.default.homedir(), ".claude", "projects");
12669
+ INDEX_FILE = import_path26.default.join(import_os22.default.homedir(), ".node9", "dlp-index.json");
12670
+ PROJECTS_DIR2 = import_path26.default.join(import_os22.default.homedir(), ".claude", "projects");
12374
12671
  }
12375
12672
  });
12376
12673
 
12377
12674
  // src/daemon/mcp-tools.ts
12378
12675
  function getMcpToolsFile() {
12379
- return import_path26.default.join(import_os22.default.homedir(), ".node9", "mcp-tools.json");
12676
+ return import_path27.default.join(import_os23.default.homedir(), ".node9", "mcp-tools.json");
12380
12677
  }
12381
12678
  function readMcpToolsConfig() {
12382
12679
  try {
12383
12680
  const file = getMcpToolsFile();
12384
- if (!import_fs24.default.existsSync(file)) return {};
12385
- const raw = import_fs24.default.readFileSync(file, "utf-8");
12681
+ if (!import_fs25.default.existsSync(file)) return {};
12682
+ const raw = import_fs25.default.readFileSync(file, "utf-8");
12386
12683
  return JSON.parse(raw);
12387
12684
  } catch {
12388
12685
  return {};
@@ -12391,11 +12688,11 @@ function readMcpToolsConfig() {
12391
12688
  function writeMcpToolsConfig(config) {
12392
12689
  try {
12393
12690
  const file = getMcpToolsFile();
12394
- const dir = import_path26.default.dirname(file);
12395
- if (!import_fs24.default.existsSync(dir)) import_fs24.default.mkdirSync(dir, { recursive: true });
12396
- const tmpPath = `${file}.${import_os22.default.hostname()}.${process.pid}.tmp`;
12397
- import_fs24.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
12398
- import_fs24.default.renameSync(tmpPath, file);
12691
+ const dir = import_path27.default.dirname(file);
12692
+ if (!import_fs25.default.existsSync(dir)) import_fs25.default.mkdirSync(dir, { recursive: true });
12693
+ const tmpPath = `${file}.${import_os23.default.hostname()}.${process.pid}.tmp`;
12694
+ import_fs25.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
12695
+ import_fs25.default.renameSync(tmpPath, file);
12399
12696
  } catch (e) {
12400
12697
  console.error("Failed to write mcp-tools.json", e);
12401
12698
  }
@@ -12434,13 +12731,13 @@ function approveServer(serverKey, disabledTools) {
12434
12731
  writeMcpToolsConfig(config);
12435
12732
  }
12436
12733
  }
12437
- var import_fs24, import_path26, import_os22;
12734
+ var import_fs25, import_path27, import_os23;
12438
12735
  var init_mcp_tools = __esm({
12439
12736
  "src/daemon/mcp-tools.ts"() {
12440
12737
  "use strict";
12441
- import_fs24 = __toESM(require("fs"));
12442
- import_path26 = __toESM(require("path"));
12443
- import_os22 = __toESM(require("os"));
12738
+ import_fs25 = __toESM(require("fs"));
12739
+ import_path27 = __toESM(require("path"));
12740
+ import_os23 = __toESM(require("os"));
12444
12741
  }
12445
12742
  });
12446
12743
 
@@ -12451,7 +12748,7 @@ function startDaemon() {
12451
12748
  startForensicBroadcast();
12452
12749
  startDlpScanner();
12453
12750
  loadInsightCounts();
12454
- const internalToken = (0, import_crypto7.randomUUID)();
12751
+ const internalToken = (0, import_crypto8.randomUUID)();
12455
12752
  const validToken = (req) => req.headers["x-node9-internal"] === internalToken || req.headers["x-node9-token"] === internalToken;
12456
12753
  const IDLE_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
12457
12754
  const watchMode = process.env.NODE9_WATCH_MODE === "1";
@@ -12462,7 +12759,7 @@ function startDaemon() {
12462
12759
  idleTimer = setTimeout(() => {
12463
12760
  if (autoStarted) {
12464
12761
  try {
12465
- import_fs25.default.unlinkSync(DAEMON_PID_FILE);
12762
+ import_fs26.default.unlinkSync(DAEMON_PID_FILE);
12466
12763
  } catch {
12467
12764
  }
12468
12765
  }
@@ -12563,7 +12860,7 @@ data: ${JSON.stringify(item.data)}
12563
12860
  cwd,
12564
12861
  localSmartRuleMatched = false
12565
12862
  } = JSON.parse(body);
12566
- const id = fromCLI && typeof activityId === "string" && activityId || (0, import_crypto7.randomUUID)();
12863
+ const id = fromCLI && typeof activityId === "string" && activityId || (0, import_crypto8.randomUUID)();
12567
12864
  const entry = {
12568
12865
  id,
12569
12866
  toolName,
@@ -12607,7 +12904,7 @@ data: ${JSON.stringify(item.data)}
12607
12904
  mcpServer: entry.mcpServer
12608
12905
  });
12609
12906
  }
12610
- const projectCwd = typeof cwd === "string" && import_path27.default.isAbsolute(cwd) ? cwd : void 0;
12907
+ const projectCwd = typeof cwd === "string" && import_path28.default.isAbsolute(cwd) ? cwd : void 0;
12611
12908
  const projectConfig = getConfig(projectCwd);
12612
12909
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
12613
12910
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -12899,8 +13196,8 @@ data: ${JSON.stringify(item.data)}
12899
13196
  if (!validToken(req)) return res.writeHead(403).end();
12900
13197
  const periodParam = reqUrl.searchParams.get("period") || "7d";
12901
13198
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
12902
- const logPath = import_path27.default.join(import_os23.default.homedir(), ".node9", "audit.log");
12903
- if (!import_fs25.default.existsSync(logPath)) {
13199
+ const logPath = import_path28.default.join(import_os24.default.homedir(), ".node9", "audit.log");
13200
+ if (!import_fs26.default.existsSync(logPath)) {
12904
13201
  res.writeHead(200, { "Content-Type": "application/json" });
12905
13202
  return res.end(
12906
13203
  JSON.stringify({
@@ -12913,7 +13210,7 @@ data: ${JSON.stringify(item.data)}
12913
13210
  );
12914
13211
  }
12915
13212
  try {
12916
- const raw = import_fs25.default.readFileSync(logPath, "utf-8");
13213
+ const raw = import_fs26.default.readFileSync(logPath, "utf-8");
12917
13214
  const allEntries = raw.split("\n").flatMap((line) => {
12918
13215
  if (!line.trim()) return [];
12919
13216
  try {
@@ -13148,7 +13445,7 @@ data: ${JSON.stringify(item.data)}
13148
13445
  args: { toolCount: tools.length, status },
13149
13446
  decision: "mcp-discovered"
13150
13447
  });
13151
- const id = (0, import_crypto7.randomUUID)();
13448
+ const id = (0, import_crypto8.randomUUID)();
13152
13449
  const entry = {
13153
13450
  id,
13154
13451
  type: "mcp-discovery",
@@ -13236,14 +13533,14 @@ data: ${JSON.stringify(item.data)}
13236
13533
  server.on("error", (e) => {
13237
13534
  if (e.code === "EADDRINUSE") {
13238
13535
  try {
13239
- if (import_fs25.default.existsSync(DAEMON_PID_FILE)) {
13240
- const { pid } = JSON.parse(import_fs25.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
13536
+ if (import_fs26.default.existsSync(DAEMON_PID_FILE)) {
13537
+ const { pid } = JSON.parse(import_fs26.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
13241
13538
  process.kill(pid, 0);
13242
13539
  return process.exit(0);
13243
13540
  }
13244
13541
  } catch {
13245
13542
  try {
13246
- import_fs25.default.unlinkSync(DAEMON_PID_FILE);
13543
+ import_fs26.default.unlinkSync(DAEMON_PID_FILE);
13247
13544
  } catch {
13248
13545
  }
13249
13546
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -13315,15 +13612,15 @@ data: ${JSON.stringify(item.data)}
13315
13612
  }
13316
13613
  startActivitySocket();
13317
13614
  }
13318
- var import_http, import_fs25, import_path27, import_os23, import_crypto7, import_child_process2, import_chalk6;
13615
+ var import_http, import_fs26, import_path28, import_os24, import_crypto8, import_child_process2, import_chalk6;
13319
13616
  var init_server = __esm({
13320
13617
  "src/daemon/server.ts"() {
13321
13618
  "use strict";
13322
13619
  import_http = __toESM(require("http"));
13323
- import_fs25 = __toESM(require("fs"));
13324
- import_path27 = __toESM(require("path"));
13325
- import_os23 = __toESM(require("os"));
13326
- import_crypto7 = require("crypto");
13620
+ import_fs26 = __toESM(require("fs"));
13621
+ import_path28 = __toESM(require("path"));
13622
+ import_os24 = __toESM(require("os"));
13623
+ import_crypto8 = require("crypto");
13327
13624
  import_child_process2 = require("child_process");
13328
13625
  import_chalk6 = __toESM(require("chalk"));
13329
13626
  init_core();
@@ -13342,8 +13639,8 @@ var init_server = __esm({
13342
13639
  function resolveNode9Binary() {
13343
13640
  try {
13344
13641
  const script = process.argv[1];
13345
- if (typeof script === "string" && import_path28.default.isAbsolute(script) && import_fs26.default.existsSync(script)) {
13346
- return import_fs26.default.realpathSync(script);
13642
+ if (typeof script === "string" && import_path29.default.isAbsolute(script) && import_fs27.default.existsSync(script)) {
13643
+ return import_fs27.default.realpathSync(script);
13347
13644
  }
13348
13645
  } catch {
13349
13646
  }
@@ -13361,11 +13658,11 @@ function xmlEscape(s) {
13361
13658
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
13362
13659
  }
13363
13660
  function launchdPlist(binaryPath) {
13364
- const logDir = import_path28.default.join(import_os24.default.homedir(), ".node9");
13661
+ const logDir = import_path29.default.join(import_os25.default.homedir(), ".node9");
13365
13662
  const nodePath = xmlEscape(process.execPath);
13366
13663
  const scriptPath = xmlEscape(binaryPath);
13367
- const outLog = xmlEscape(import_path28.default.join(logDir, "daemon.log"));
13368
- const errLog = xmlEscape(import_path28.default.join(logDir, "daemon-error.log"));
13664
+ const outLog = xmlEscape(import_path29.default.join(logDir, "daemon.log"));
13665
+ const errLog = xmlEscape(import_path29.default.join(logDir, "daemon-error.log"));
13369
13666
  return `<?xml version="1.0" encoding="UTF-8"?>
13370
13667
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
13371
13668
  <plist version="1.0">
@@ -13398,9 +13695,9 @@ function launchdPlist(binaryPath) {
13398
13695
  `;
13399
13696
  }
13400
13697
  function installLaunchd(binaryPath) {
13401
- const dir = import_path28.default.dirname(LAUNCHD_PLIST);
13402
- if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
13403
- import_fs26.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
13698
+ const dir = import_path29.default.dirname(LAUNCHD_PLIST);
13699
+ if (!import_fs27.default.existsSync(dir)) import_fs27.default.mkdirSync(dir, { recursive: true });
13700
+ import_fs27.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
13404
13701
  (0, import_child_process3.spawnSync)("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
13405
13702
  const r = (0, import_child_process3.spawnSync)("launchctl", ["load", "-w", LAUNCHD_PLIST], {
13406
13703
  encoding: "utf8",
@@ -13411,13 +13708,13 @@ function installLaunchd(binaryPath) {
13411
13708
  }
13412
13709
  }
13413
13710
  function uninstallLaunchd() {
13414
- if (import_fs26.default.existsSync(LAUNCHD_PLIST)) {
13711
+ if (import_fs27.default.existsSync(LAUNCHD_PLIST)) {
13415
13712
  (0, import_child_process3.spawnSync)("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
13416
- import_fs26.default.unlinkSync(LAUNCHD_PLIST);
13713
+ import_fs27.default.unlinkSync(LAUNCHD_PLIST);
13417
13714
  }
13418
13715
  }
13419
13716
  function isLaunchdInstalled() {
13420
- return import_fs26.default.existsSync(LAUNCHD_PLIST);
13717
+ return import_fs27.default.existsSync(LAUNCHD_PLIST);
13421
13718
  }
13422
13719
  function systemdUnit(binaryPath) {
13423
13720
  return `[Unit]
@@ -13436,12 +13733,12 @@ WantedBy=default.target
13436
13733
  `;
13437
13734
  }
13438
13735
  function installSystemd(binaryPath) {
13439
- if (!import_fs26.default.existsSync(SYSTEMD_UNIT_DIR)) {
13440
- import_fs26.default.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
13736
+ if (!import_fs27.default.existsSync(SYSTEMD_UNIT_DIR)) {
13737
+ import_fs27.default.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
13441
13738
  }
13442
- import_fs26.default.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
13739
+ import_fs27.default.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
13443
13740
  try {
13444
- (0, import_child_process3.execFileSync)("loginctl", ["enable-linger", import_os24.default.userInfo().username], { timeout: 3e3 });
13741
+ (0, import_child_process3.execFileSync)("loginctl", ["enable-linger", import_os25.default.userInfo().username], { timeout: 3e3 });
13445
13742
  } catch {
13446
13743
  }
13447
13744
  const reload = (0, import_child_process3.spawnSync)("systemctl", ["--user", "daemon-reload"], {
@@ -13461,23 +13758,23 @@ function installSystemd(binaryPath) {
13461
13758
  }
13462
13759
  }
13463
13760
  function uninstallSystemd() {
13464
- if (import_fs26.default.existsSync(SYSTEMD_UNIT)) {
13761
+ if (import_fs27.default.existsSync(SYSTEMD_UNIT)) {
13465
13762
  (0, import_child_process3.spawnSync)("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
13466
13763
  encoding: "utf8",
13467
13764
  timeout: 5e3
13468
13765
  });
13469
13766
  (0, import_child_process3.spawnSync)("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
13470
- import_fs26.default.unlinkSync(SYSTEMD_UNIT);
13767
+ import_fs27.default.unlinkSync(SYSTEMD_UNIT);
13471
13768
  }
13472
13769
  }
13473
13770
  function isSystemdInstalled() {
13474
- return import_fs26.default.existsSync(SYSTEMD_UNIT);
13771
+ return import_fs27.default.existsSync(SYSTEMD_UNIT);
13475
13772
  }
13476
13773
  function stopRunningDaemon() {
13477
- const pidFile = import_path28.default.join(import_os24.default.homedir(), ".node9", "daemon.pid");
13478
- if (!import_fs26.default.existsSync(pidFile)) return;
13774
+ const pidFile = import_path29.default.join(import_os25.default.homedir(), ".node9", "daemon.pid");
13775
+ if (!import_fs27.default.existsSync(pidFile)) return;
13479
13776
  try {
13480
- const data = JSON.parse(import_fs26.default.readFileSync(pidFile, "utf-8"));
13777
+ const data = JSON.parse(import_fs27.default.readFileSync(pidFile, "utf-8"));
13481
13778
  const pid = data.pid;
13482
13779
  const MAX_PID2 = 4194304;
13483
13780
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -13497,7 +13794,7 @@ function stopRunningDaemon() {
13497
13794
  }
13498
13795
  }
13499
13796
  try {
13500
- import_fs26.default.unlinkSync(pidFile);
13797
+ import_fs27.default.unlinkSync(pidFile);
13501
13798
  } catch {
13502
13799
  }
13503
13800
  } catch {
@@ -13567,26 +13864,26 @@ function isDaemonServiceInstalled() {
13567
13864
  if (process.platform === "linux") return isSystemdInstalled();
13568
13865
  return false;
13569
13866
  }
13570
- var import_fs26, import_path28, import_os24, import_child_process3, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
13867
+ var import_fs27, import_path29, import_os25, import_child_process3, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
13571
13868
  var init_service = __esm({
13572
13869
  "src/daemon/service.ts"() {
13573
13870
  "use strict";
13574
- import_fs26 = __toESM(require("fs"));
13575
- import_path28 = __toESM(require("path"));
13576
- import_os24 = __toESM(require("os"));
13871
+ import_fs27 = __toESM(require("fs"));
13872
+ import_path29 = __toESM(require("path"));
13873
+ import_os25 = __toESM(require("os"));
13577
13874
  import_child_process3 = require("child_process");
13578
13875
  LAUNCHD_LABEL = "ai.node9.daemon";
13579
- LAUNCHD_PLIST = import_path28.default.join(import_os24.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
13580
- SYSTEMD_UNIT_DIR = import_path28.default.join(import_os24.default.homedir(), ".config", "systemd", "user");
13581
- SYSTEMD_UNIT = import_path28.default.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
13876
+ LAUNCHD_PLIST = import_path29.default.join(import_os25.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
13877
+ SYSTEMD_UNIT_DIR = import_path29.default.join(import_os25.default.homedir(), ".config", "systemd", "user");
13878
+ SYSTEMD_UNIT = import_path29.default.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
13582
13879
  }
13583
13880
  });
13584
13881
 
13585
13882
  // src/daemon/index.ts
13586
13883
  function stopDaemon() {
13587
- if (!import_fs27.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk7.default.yellow("Not running."));
13884
+ if (!import_fs28.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk7.default.yellow("Not running."));
13588
13885
  try {
13589
- const data = JSON.parse(import_fs27.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
13886
+ const data = JSON.parse(import_fs28.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
13590
13887
  const pid = data.pid;
13591
13888
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
13592
13889
  console.log(import_chalk7.default.gray("Cleaned up invalid PID file."));
@@ -13598,7 +13895,7 @@ function stopDaemon() {
13598
13895
  console.log(import_chalk7.default.gray("Cleaned up stale PID file."));
13599
13896
  } finally {
13600
13897
  try {
13601
- import_fs27.default.unlinkSync(DAEMON_PID_FILE);
13898
+ import_fs28.default.unlinkSync(DAEMON_PID_FILE);
13602
13899
  } catch {
13603
13900
  }
13604
13901
  }
@@ -13607,9 +13904,9 @@ function daemonStatus() {
13607
13904
  const serviceInstalled = isDaemonServiceInstalled();
13608
13905
  const serviceLabel = serviceInstalled ? import_chalk7.default.green("installed (starts on login)") : import_chalk7.default.yellow("not installed \u2014 run: node9 daemon install");
13609
13906
  let processStatus;
13610
- if (import_fs27.default.existsSync(DAEMON_PID_FILE)) {
13907
+ if (import_fs28.default.existsSync(DAEMON_PID_FILE)) {
13611
13908
  try {
13612
- const data = JSON.parse(import_fs27.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
13909
+ const data = JSON.parse(import_fs28.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
13613
13910
  const pid = data.pid;
13614
13911
  const port = data.port;
13615
13912
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -13631,11 +13928,11 @@ function daemonStatus() {
13631
13928
  console.log(` Service : ${serviceLabel}
13632
13929
  `);
13633
13930
  }
13634
- var import_fs27, import_chalk7, MAX_PID;
13931
+ var import_fs28, import_chalk7, MAX_PID;
13635
13932
  var init_daemon2 = __esm({
13636
13933
  "src/daemon/index.ts"() {
13637
13934
  "use strict";
13638
- import_fs27 = __toESM(require("fs"));
13935
+ import_fs28 = __toESM(require("fs"));
13639
13936
  import_chalk7 = __toESM(require("chalk"));
13640
13937
  init_server();
13641
13938
  init_state2();
@@ -13676,19 +13973,19 @@ function getModelContextLimit(model) {
13676
13973
  }
13677
13974
  function readSessionUsage() {
13678
13975
  const projectsDir = import_path47.default.join(import_os41.default.homedir(), ".claude", "projects");
13679
- if (!import_fs45.default.existsSync(projectsDir)) return null;
13976
+ if (!import_fs46.default.existsSync(projectsDir)) return null;
13680
13977
  let latestFile = null;
13681
13978
  let latestMtime = 0;
13682
13979
  try {
13683
- for (const dir of import_fs45.default.readdirSync(projectsDir)) {
13980
+ for (const dir of import_fs46.default.readdirSync(projectsDir)) {
13684
13981
  const dirPath = import_path47.default.join(projectsDir, dir);
13685
13982
  try {
13686
- if (!import_fs45.default.statSync(dirPath).isDirectory()) continue;
13687
- for (const file of import_fs45.default.readdirSync(dirPath)) {
13983
+ if (!import_fs46.default.statSync(dirPath).isDirectory()) continue;
13984
+ for (const file of import_fs46.default.readdirSync(dirPath)) {
13688
13985
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
13689
13986
  const filePath = import_path47.default.join(dirPath, file);
13690
13987
  try {
13691
- const mtime = import_fs45.default.statSync(filePath).mtimeMs;
13988
+ const mtime = import_fs46.default.statSync(filePath).mtimeMs;
13692
13989
  if (mtime > latestMtime) {
13693
13990
  latestMtime = mtime;
13694
13991
  latestFile = filePath;
@@ -13703,7 +14000,7 @@ function readSessionUsage() {
13703
14000
  }
13704
14001
  if (!latestFile) return null;
13705
14002
  try {
13706
- const lines = import_fs45.default.readFileSync(latestFile, "utf-8").split("\n");
14003
+ const lines = import_fs46.default.readFileSync(latestFile, "utf-8").split("\n");
13707
14004
  let lastModel = "";
13708
14005
  let lastInput = 0;
13709
14006
  let lastOutput = 0;
@@ -13803,9 +14100,9 @@ function renderPending(activity) {
13803
14100
  }
13804
14101
  async function ensureDaemon() {
13805
14102
  let pidPort = null;
13806
- if (import_fs45.default.existsSync(PID_FILE)) {
14103
+ if (import_fs46.default.existsSync(PID_FILE)) {
13807
14104
  try {
13808
- const { port } = JSON.parse(import_fs45.default.readFileSync(PID_FILE, "utf-8"));
14105
+ const { port } = JSON.parse(import_fs46.default.readFileSync(PID_FILE, "utf-8"));
13809
14106
  pidPort = port;
13810
14107
  } catch {
13811
14108
  console.error(import_chalk29.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -13963,7 +14260,7 @@ function buildRecoveryCardLines(req) {
13963
14260
  function readApproversFromDisk() {
13964
14261
  const configPath = import_path47.default.join(import_os41.default.homedir(), ".node9", "config.json");
13965
14262
  try {
13966
- const raw = JSON.parse(import_fs45.default.readFileSync(configPath, "utf-8"));
14263
+ const raw = JSON.parse(import_fs46.default.readFileSync(configPath, "utf-8"));
13967
14264
  const settings = raw.settings ?? {};
13968
14265
  return settings.approvers ?? {};
13969
14266
  } catch {
@@ -13981,13 +14278,13 @@ function approverStatusLine() {
13981
14278
  function toggleApprover(channel) {
13982
14279
  const configPath = import_path47.default.join(import_os41.default.homedir(), ".node9", "config.json");
13983
14280
  try {
13984
- const raw = JSON.parse(import_fs45.default.readFileSync(configPath, "utf-8"));
14281
+ const raw = JSON.parse(import_fs46.default.readFileSync(configPath, "utf-8"));
13985
14282
  const settings = raw.settings ?? {};
13986
14283
  const approvers = settings.approvers ?? {};
13987
14284
  approvers[channel] = approvers[channel] === false;
13988
14285
  settings.approvers = approvers;
13989
14286
  raw.settings = settings;
13990
- import_fs45.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
14287
+ import_fs46.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
13991
14288
  } catch (err2) {
13992
14289
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
13993
14290
  `);
@@ -14159,7 +14456,7 @@ async function startTail(options = {}) {
14159
14456
  }
14160
14457
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
14161
14458
  try {
14162
- import_fs45.default.appendFileSync(
14459
+ import_fs46.default.appendFileSync(
14163
14460
  import_path47.default.join(import_os41.default.homedir(), ".node9", "hook-debug.log"),
14164
14461
  `[tail] POST /decision failed: ${String(err2)}
14165
14462
  `
@@ -14226,7 +14523,7 @@ async function startTail(options = {}) {
14226
14523
  }
14227
14524
  const auditLog = import_path47.default.join(import_os41.default.homedir(), ".node9", "audit.log");
14228
14525
  try {
14229
- const unackedDlp = import_fs45.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
14526
+ const unackedDlp = import_fs46.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
14230
14527
  if (unackedDlp > 0) {
14231
14528
  console.log("");
14232
14529
  console.log(
@@ -14266,7 +14563,7 @@ async function startTail(options = {}) {
14266
14563
  if (stallWarned) return;
14267
14564
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
14268
14565
  try {
14269
- const auditMtime = import_fs45.default.statSync(auditLog).mtimeMs;
14566
+ const auditMtime = import_fs46.default.statSync(auditLog).mtimeMs;
14270
14567
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
14271
14568
  console.log("");
14272
14569
  console.log(
@@ -14451,13 +14748,13 @@ async function startTail(options = {}) {
14451
14748
  process.exit(1);
14452
14749
  });
14453
14750
  }
14454
- var import_http2, import_chalk29, import_fs45, import_os41, import_path47, import_readline6, import_child_process12, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
14751
+ var import_http2, import_chalk29, import_fs46, import_os41, import_path47, import_readline6, import_child_process12, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
14455
14752
  var init_tail = __esm({
14456
14753
  "src/tui/tail.ts"() {
14457
14754
  "use strict";
14458
14755
  import_http2 = __toESM(require("http"));
14459
14756
  import_chalk29 = __toESM(require("chalk"));
14460
- import_fs45 = __toESM(require("fs"));
14757
+ import_fs46 = __toESM(require("fs"));
14461
14758
  import_os41 = __toESM(require("os"));
14462
14759
  import_path47 = __toESM(require("path"));
14463
14760
  import_readline6 = __toESM(require("readline"));
@@ -14586,9 +14883,9 @@ function formatTimeLeft(resetsAt) {
14586
14883
  return ` (${m}m left)`;
14587
14884
  }
14588
14885
  function safeReadJson(filePath) {
14589
- if (!import_fs46.default.existsSync(filePath)) return null;
14886
+ if (!import_fs47.default.existsSync(filePath)) return null;
14590
14887
  try {
14591
- return JSON.parse(import_fs46.default.readFileSync(filePath, "utf-8"));
14888
+ return JSON.parse(import_fs47.default.readFileSync(filePath, "utf-8"));
14592
14889
  } catch {
14593
14890
  return null;
14594
14891
  }
@@ -14609,10 +14906,10 @@ function countHooksInFile(filePath) {
14609
14906
  return Object.keys(cfg.hooks).length;
14610
14907
  }
14611
14908
  function countRulesInDir(rulesDir) {
14612
- if (!import_fs46.default.existsSync(rulesDir)) return 0;
14909
+ if (!import_fs47.default.existsSync(rulesDir)) return 0;
14613
14910
  let count = 0;
14614
14911
  try {
14615
- for (const entry of import_fs46.default.readdirSync(rulesDir, { withFileTypes: true })) {
14912
+ for (const entry of import_fs47.default.readdirSync(rulesDir, { withFileTypes: true })) {
14616
14913
  if (entry.isDirectory()) {
14617
14914
  count += countRulesInDir(import_path48.default.join(rulesDir, entry.name));
14618
14915
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -14638,7 +14935,7 @@ function countConfigs(cwd) {
14638
14935
  let hooksCount = 0;
14639
14936
  const userMcpServers = /* @__PURE__ */ new Set();
14640
14937
  const projectMcpServers = /* @__PURE__ */ new Set();
14641
- if (import_fs46.default.existsSync(import_path48.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
14938
+ if (import_fs47.default.existsSync(import_path48.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
14642
14939
  rulesCount += countRulesInDir(import_path48.default.join(claudeDir, "rules"));
14643
14940
  const userSettings = import_path48.default.join(claudeDir, "settings.json");
14644
14941
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
@@ -14649,18 +14946,18 @@ function countConfigs(cwd) {
14649
14946
  userMcpServers.delete(name);
14650
14947
  }
14651
14948
  if (cwd) {
14652
- if (import_fs46.default.existsSync(import_path48.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
14653
- if (import_fs46.default.existsSync(import_path48.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
14949
+ if (import_fs47.default.existsSync(import_path48.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
14950
+ if (import_fs47.default.existsSync(import_path48.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
14654
14951
  const projectClaudeDir = import_path48.default.join(cwd, ".claude");
14655
14952
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
14656
14953
  if (!overlapsUserScope) {
14657
- if (import_fs46.default.existsSync(import_path48.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
14954
+ if (import_fs47.default.existsSync(import_path48.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
14658
14955
  rulesCount += countRulesInDir(import_path48.default.join(projectClaudeDir, "rules"));
14659
14956
  const projSettings = import_path48.default.join(projectClaudeDir, "settings.json");
14660
14957
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
14661
14958
  hooksCount += countHooksInFile(projSettings);
14662
14959
  }
14663
- if (import_fs46.default.existsSync(import_path48.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
14960
+ if (import_fs47.default.existsSync(import_path48.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
14664
14961
  const localSettings = import_path48.default.join(projectClaudeDir, "settings.local.json");
14665
14962
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
14666
14963
  hooksCount += countHooksInFile(localSettings);
@@ -14698,11 +14995,11 @@ function readActiveShieldsHud() {
14698
14995
  }
14699
14996
  try {
14700
14997
  const shieldsPath = import_path48.default.join(import_os42.default.homedir(), ".node9", "shields.json");
14701
- if (!import_fs46.default.existsSync(shieldsPath)) {
14998
+ if (!import_fs47.default.existsSync(shieldsPath)) {
14702
14999
  shieldsCache = { value: [], ts: now };
14703
15000
  return [];
14704
15001
  }
14705
- const parsed = JSON.parse(import_fs46.default.readFileSync(shieldsPath, "utf-8"));
15002
+ const parsed = JSON.parse(import_fs47.default.readFileSync(shieldsPath, "utf-8"));
14706
15003
  if (!Array.isArray(parsed.active)) {
14707
15004
  shieldsCache = { value: [], ts: now };
14708
15005
  return [];
@@ -14804,17 +15101,17 @@ function renderContextLine(stdin) {
14804
15101
  async function main() {
14805
15102
  try {
14806
15103
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
14807
- if (import_fs46.default.existsSync(import_path48.default.join(import_os42.default.homedir(), ".node9", "hud-debug"))) {
15104
+ if (import_fs47.default.existsSync(import_path48.default.join(import_os42.default.homedir(), ".node9", "hud-debug"))) {
14808
15105
  try {
14809
15106
  const logPath = import_path48.default.join(import_os42.default.homedir(), ".node9", "hud-debug.log");
14810
15107
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
14811
15108
  let size = 0;
14812
15109
  try {
14813
- size = import_fs46.default.statSync(logPath).size;
15110
+ size = import_fs47.default.statSync(logPath).size;
14814
15111
  } catch {
14815
15112
  }
14816
15113
  if (size < MAX_LOG_SIZE) {
14817
- import_fs46.default.appendFileSync(
15114
+ import_fs47.default.appendFileSync(
14818
15115
  logPath,
14819
15116
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
14820
15117
  );
@@ -14838,8 +15135,8 @@ async function main() {
14838
15135
  import_path48.default.join(cwd, "node9.config.json"),
14839
15136
  import_path48.default.join(import_os42.default.homedir(), ".node9", "config.json")
14840
15137
  ]) {
14841
- if (!import_fs46.default.existsSync(configPath)) continue;
14842
- const cfg = JSON.parse(import_fs46.default.readFileSync(configPath, "utf-8"));
15138
+ if (!import_fs47.default.existsSync(configPath)) continue;
15139
+ const cfg = JSON.parse(import_fs47.default.readFileSync(configPath, "utf-8"));
14843
15140
  const hud = cfg.settings?.hud;
14844
15141
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
14845
15142
  }
@@ -14857,11 +15154,11 @@ async function main() {
14857
15154
  renderOffline();
14858
15155
  }
14859
15156
  }
14860
- var import_fs46, import_path48, import_os42, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
15157
+ var import_fs47, import_path48, import_os42, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
14861
15158
  var init_hud = __esm({
14862
15159
  "src/cli/hud.ts"() {
14863
15160
  "use strict";
14864
- import_fs46 = __toESM(require("fs"));
15161
+ import_fs47 = __toESM(require("fs"));
14865
15162
  import_path48 = __toESM(require("path"));
14866
15163
  import_os42 = __toESM(require("os"));
14867
15164
  import_http3 = __toESM(require("http"));
@@ -14890,7 +15187,7 @@ init_core();
14890
15187
  init_setup();
14891
15188
  init_daemon2();
14892
15189
  var import_chalk30 = __toESM(require("chalk"));
14893
- var import_fs47 = __toESM(require("fs"));
15190
+ var import_fs48 = __toESM(require("fs"));
14894
15191
  var import_path49 = __toESM(require("path"));
14895
15192
  var import_os43 = __toESM(require("os"));
14896
15193
  var import_prompts2 = require("@inquirer/prompts");
@@ -15074,18 +15371,18 @@ async function runProxy(targetCommand) {
15074
15371
 
15075
15372
  // src/cli/daemon-starter.ts
15076
15373
  var import_child_process5 = require("child_process");
15077
- var import_path29 = __toESM(require("path"));
15078
- var import_fs28 = __toESM(require("fs"));
15374
+ var import_path30 = __toESM(require("path"));
15375
+ var import_fs29 = __toESM(require("fs"));
15079
15376
  init_daemon();
15080
15377
  function isTestingMode() {
15081
15378
  return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
15082
15379
  }
15083
15380
  async function autoStartDaemonAndWait() {
15084
15381
  if (isTestingMode()) return false;
15085
- if (!import_path29.default.isAbsolute(process.argv[1])) return false;
15382
+ if (!import_path30.default.isAbsolute(process.argv[1])) return false;
15086
15383
  let resolvedArgv1;
15087
15384
  try {
15088
- resolvedArgv1 = import_fs28.default.realpathSync(process.argv[1]);
15385
+ resolvedArgv1 = import_fs29.default.realpathSync(process.argv[1]);
15089
15386
  } catch {
15090
15387
  return false;
15091
15388
  }
@@ -15112,10 +15409,10 @@ async function autoStartDaemonAndWait() {
15112
15409
 
15113
15410
  // src/cli/commands/check.ts
15114
15411
  var import_chalk9 = __toESM(require("chalk"));
15115
- var import_fs31 = __toESM(require("fs"));
15412
+ var import_fs32 = __toESM(require("fs"));
15116
15413
  var import_child_process7 = require("child_process");
15117
- var import_path32 = __toESM(require("path"));
15118
- var import_os27 = __toESM(require("os"));
15414
+ var import_path33 = __toESM(require("path"));
15415
+ var import_os28 = __toESM(require("os"));
15119
15416
  init_orchestrator();
15120
15417
  init_daemon();
15121
15418
  init_config();
@@ -15123,12 +15420,12 @@ init_policy();
15123
15420
 
15124
15421
  // src/undo.ts
15125
15422
  var import_child_process6 = require("child_process");
15126
- var import_crypto8 = __toESM(require("crypto"));
15127
- var import_fs29 = __toESM(require("fs"));
15423
+ var import_crypto9 = __toESM(require("crypto"));
15424
+ var import_fs30 = __toESM(require("fs"));
15128
15425
  var import_net3 = __toESM(require("net"));
15129
- var import_path30 = __toESM(require("path"));
15130
- var import_os25 = __toESM(require("os"));
15131
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path30.default.join(import_os25.default.tmpdir(), "node9-activity.sock");
15426
+ var import_path31 = __toESM(require("path"));
15427
+ var import_os26 = __toESM(require("os"));
15428
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path31.default.join(import_os26.default.tmpdir(), "node9-activity.sock");
15132
15429
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
15133
15430
  try {
15134
15431
  const payload = JSON.stringify({
@@ -15148,22 +15445,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
15148
15445
  } catch {
15149
15446
  }
15150
15447
  }
15151
- var SNAPSHOT_STACK_PATH = import_path30.default.join(import_os25.default.homedir(), ".node9", "snapshots.json");
15152
- var UNDO_LATEST_PATH = import_path30.default.join(import_os25.default.homedir(), ".node9", "undo_latest.txt");
15448
+ var SNAPSHOT_STACK_PATH = import_path31.default.join(import_os26.default.homedir(), ".node9", "snapshots.json");
15449
+ var UNDO_LATEST_PATH = import_path31.default.join(import_os26.default.homedir(), ".node9", "undo_latest.txt");
15153
15450
  var MAX_SNAPSHOTS = 10;
15154
15451
  var GIT_TIMEOUT = 15e3;
15155
15452
  function readStack() {
15156
15453
  try {
15157
- if (import_fs29.default.existsSync(SNAPSHOT_STACK_PATH))
15158
- return JSON.parse(import_fs29.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
15454
+ if (import_fs30.default.existsSync(SNAPSHOT_STACK_PATH))
15455
+ return JSON.parse(import_fs30.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
15159
15456
  } catch {
15160
15457
  }
15161
15458
  return [];
15162
15459
  }
15163
15460
  function writeStack(stack) {
15164
- const dir = import_path30.default.dirname(SNAPSHOT_STACK_PATH);
15165
- if (!import_fs29.default.existsSync(dir)) import_fs29.default.mkdirSync(dir, { recursive: true });
15166
- import_fs29.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
15461
+ const dir = import_path31.default.dirname(SNAPSHOT_STACK_PATH);
15462
+ if (!import_fs30.default.existsSync(dir)) import_fs30.default.mkdirSync(dir, { recursive: true });
15463
+ import_fs30.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
15167
15464
  }
15168
15465
  function extractFilePath(args) {
15169
15466
  if (!args || typeof args !== "object") return null;
@@ -15183,12 +15480,12 @@ function buildArgsSummary(tool, args) {
15183
15480
  return "";
15184
15481
  }
15185
15482
  function findProjectRoot(filePath) {
15186
- let dir = import_path30.default.dirname(filePath);
15483
+ let dir = import_path31.default.dirname(filePath);
15187
15484
  while (true) {
15188
- if (import_fs29.default.existsSync(import_path30.default.join(dir, ".git")) || import_fs29.default.existsSync(import_path30.default.join(dir, "package.json"))) {
15485
+ if (import_fs30.default.existsSync(import_path31.default.join(dir, ".git")) || import_fs30.default.existsSync(import_path31.default.join(dir, "package.json"))) {
15189
15486
  return dir;
15190
15487
  }
15191
- const parent = import_path30.default.dirname(dir);
15488
+ const parent = import_path31.default.dirname(dir);
15192
15489
  if (parent === dir) return process.cwd();
15193
15490
  dir = parent;
15194
15491
  }
@@ -15196,7 +15493,7 @@ function findProjectRoot(filePath) {
15196
15493
  function normalizeCwdForHash(cwd) {
15197
15494
  let normalized;
15198
15495
  try {
15199
- normalized = import_fs29.default.realpathSync(cwd);
15496
+ normalized = import_fs30.default.realpathSync(cwd);
15200
15497
  } catch {
15201
15498
  normalized = cwd;
15202
15499
  }
@@ -15205,17 +15502,17 @@ function normalizeCwdForHash(cwd) {
15205
15502
  return normalized;
15206
15503
  }
15207
15504
  function getShadowRepoDir(cwd) {
15208
- const hash = import_crypto8.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
15209
- return import_path30.default.join(import_os25.default.homedir(), ".node9", "snapshots", hash);
15505
+ const hash = import_crypto9.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
15506
+ return import_path31.default.join(import_os26.default.homedir(), ".node9", "snapshots", hash);
15210
15507
  }
15211
15508
  function cleanOrphanedIndexFiles(shadowDir) {
15212
15509
  try {
15213
15510
  const cutoff = Date.now() - 6e4;
15214
- for (const f of import_fs29.default.readdirSync(shadowDir)) {
15511
+ for (const f of import_fs30.default.readdirSync(shadowDir)) {
15215
15512
  if (f.startsWith("index_")) {
15216
- const fp = import_path30.default.join(shadowDir, f);
15513
+ const fp = import_path31.default.join(shadowDir, f);
15217
15514
  try {
15218
- if (import_fs29.default.statSync(fp).mtimeMs < cutoff) import_fs29.default.unlinkSync(fp);
15515
+ if (import_fs30.default.statSync(fp).mtimeMs < cutoff) import_fs30.default.unlinkSync(fp);
15219
15516
  } catch {
15220
15517
  }
15221
15518
  }
@@ -15227,7 +15524,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
15227
15524
  const hardcoded = [".git", ".node9"];
15228
15525
  const lines = [...hardcoded, ...ignorePaths].join("\n");
15229
15526
  try {
15230
- import_fs29.default.writeFileSync(import_path30.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
15527
+ import_fs30.default.writeFileSync(import_path31.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
15231
15528
  } catch {
15232
15529
  }
15233
15530
  }
@@ -15240,25 +15537,25 @@ function ensureShadowRepo(shadowDir, cwd) {
15240
15537
  timeout: 3e3
15241
15538
  });
15242
15539
  if (check.status === 0) {
15243
- const ptPath = import_path30.default.join(shadowDir, "project-path.txt");
15540
+ const ptPath = import_path31.default.join(shadowDir, "project-path.txt");
15244
15541
  try {
15245
- const stored = import_fs29.default.readFileSync(ptPath, "utf8").trim();
15542
+ const stored = import_fs30.default.readFileSync(ptPath, "utf8").trim();
15246
15543
  if (stored === normalizedCwd) return true;
15247
15544
  if (process.env.NODE9_DEBUG === "1")
15248
15545
  console.error(
15249
15546
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
15250
15547
  );
15251
- import_fs29.default.rmSync(shadowDir, { recursive: true, force: true });
15548
+ import_fs30.default.rmSync(shadowDir, { recursive: true, force: true });
15252
15549
  } catch {
15253
15550
  try {
15254
- import_fs29.default.writeFileSync(ptPath, normalizedCwd, "utf8");
15551
+ import_fs30.default.writeFileSync(ptPath, normalizedCwd, "utf8");
15255
15552
  } catch {
15256
15553
  }
15257
15554
  return true;
15258
15555
  }
15259
15556
  }
15260
15557
  try {
15261
- import_fs29.default.mkdirSync(shadowDir, { recursive: true });
15558
+ import_fs30.default.mkdirSync(shadowDir, { recursive: true });
15262
15559
  } catch {
15263
15560
  }
15264
15561
  const init = (0, import_child_process6.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -15267,7 +15564,7 @@ function ensureShadowRepo(shadowDir, cwd) {
15267
15564
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
15268
15565
  return false;
15269
15566
  }
15270
- const configFile = import_path30.default.join(shadowDir, "config");
15567
+ const configFile = import_path31.default.join(shadowDir, "config");
15271
15568
  (0, import_child_process6.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
15272
15569
  timeout: 3e3
15273
15570
  });
@@ -15275,7 +15572,7 @@ function ensureShadowRepo(shadowDir, cwd) {
15275
15572
  timeout: 3e3
15276
15573
  });
15277
15574
  try {
15278
- import_fs29.default.writeFileSync(import_path30.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
15575
+ import_fs30.default.writeFileSync(import_path31.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
15279
15576
  } catch {
15280
15577
  }
15281
15578
  return true;
@@ -15298,12 +15595,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15298
15595
  let indexFile = null;
15299
15596
  try {
15300
15597
  const rawFilePath = extractFilePath(args);
15301
- const absFilePath = rawFilePath && import_path30.default.isAbsolute(rawFilePath) ? rawFilePath : null;
15598
+ const absFilePath = rawFilePath && import_path31.default.isAbsolute(rawFilePath) ? rawFilePath : null;
15302
15599
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
15303
15600
  const shadowDir = getShadowRepoDir(cwd);
15304
15601
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
15305
15602
  writeShadowExcludes(shadowDir, ignorePaths);
15306
- indexFile = import_path30.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
15603
+ indexFile = import_path31.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
15307
15604
  const shadowEnv = {
15308
15605
  ...process.env,
15309
15606
  GIT_DIR: shadowDir,
@@ -15375,7 +15672,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15375
15672
  writeStack(stack);
15376
15673
  const entry = stack[stack.length - 1];
15377
15674
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
15378
- import_fs29.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
15675
+ import_fs30.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
15379
15676
  if (shouldGc) {
15380
15677
  (0, import_child_process6.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
15381
15678
  }
@@ -15386,7 +15683,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15386
15683
  } finally {
15387
15684
  if (indexFile) {
15388
15685
  try {
15389
- import_fs29.default.unlinkSync(indexFile);
15686
+ import_fs30.default.unlinkSync(indexFile);
15390
15687
  } catch {
15391
15688
  }
15392
15689
  }
@@ -15462,9 +15759,9 @@ function applyUndo(hash, cwd) {
15462
15759
  timeout: GIT_TIMEOUT
15463
15760
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
15464
15761
  for (const file of [...tracked, ...untracked]) {
15465
- const fullPath = import_path30.default.join(dir, file);
15466
- if (!snapshotFiles.has(file) && import_fs29.default.existsSync(fullPath)) {
15467
- import_fs29.default.unlinkSync(fullPath);
15762
+ const fullPath = import_path31.default.join(dir, file);
15763
+ if (!snapshotFiles.has(file) && import_fs30.default.existsSync(fullPath)) {
15764
+ import_fs30.default.unlinkSync(fullPath);
15468
15765
  }
15469
15766
  }
15470
15767
  return true;
@@ -15474,17 +15771,17 @@ function applyUndo(hash, cwd) {
15474
15771
  }
15475
15772
 
15476
15773
  // src/skill-pin.ts
15477
- var import_fs30 = __toESM(require("fs"));
15478
- var import_path31 = __toESM(require("path"));
15479
- var import_os26 = __toESM(require("os"));
15480
- var import_crypto9 = __toESM(require("crypto"));
15481
- function getPinsFilePath() {
15482
- return import_path31.default.join(import_os26.default.homedir(), ".node9", "skill-pins.json");
15774
+ var import_fs31 = __toESM(require("fs"));
15775
+ var import_path32 = __toESM(require("path"));
15776
+ var import_os27 = __toESM(require("os"));
15777
+ var import_crypto10 = __toESM(require("crypto"));
15778
+ function getPinsFilePath2() {
15779
+ return import_path32.default.join(import_os27.default.homedir(), ".node9", "skill-pins.json");
15483
15780
  }
15484
15781
  var MAX_FILES = 5e3;
15485
15782
  var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
15486
15783
  function sha256Bytes(buf) {
15487
- return import_crypto9.default.createHash("sha256").update(buf).digest("hex");
15784
+ return import_crypto10.default.createHash("sha256").update(buf).digest("hex");
15488
15785
  }
15489
15786
  function walkDir(root) {
15490
15787
  const out = [];
@@ -15493,18 +15790,18 @@ function walkDir(root) {
15493
15790
  if (out.length >= MAX_FILES) return;
15494
15791
  let entries;
15495
15792
  try {
15496
- entries = import_fs30.default.readdirSync(dir, { withFileTypes: true });
15793
+ entries = import_fs31.default.readdirSync(dir, { withFileTypes: true });
15497
15794
  } catch {
15498
15795
  return;
15499
15796
  }
15500
15797
  entries.sort((a, b) => a.name.localeCompare(b.name));
15501
15798
  for (const entry of entries) {
15502
15799
  if (out.length >= MAX_FILES) return;
15503
- const full = import_path31.default.join(dir, entry.name);
15504
- const rel = relDir ? import_path31.default.posix.join(relDir, entry.name) : entry.name;
15800
+ const full = import_path32.default.join(dir, entry.name);
15801
+ const rel = relDir ? import_path32.default.posix.join(relDir, entry.name) : entry.name;
15505
15802
  let lst;
15506
15803
  try {
15507
- lst = import_fs30.default.lstatSync(full);
15804
+ lst = import_fs31.default.lstatSync(full);
15508
15805
  } catch {
15509
15806
  continue;
15510
15807
  }
@@ -15516,7 +15813,7 @@ function walkDir(root) {
15516
15813
  if (!lst.isFile()) continue;
15517
15814
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
15518
15815
  try {
15519
- const buf = import_fs30.default.readFileSync(full);
15816
+ const buf = import_fs31.default.readFileSync(full);
15520
15817
  totalBytes += buf.length;
15521
15818
  out.push({ rel, hash: sha256Bytes(buf) });
15522
15819
  } catch {
@@ -15530,32 +15827,32 @@ function walkDir(root) {
15530
15827
  function hashSkillRoot(absPath) {
15531
15828
  let lst;
15532
15829
  try {
15533
- lst = import_fs30.default.lstatSync(absPath);
15830
+ lst = import_fs31.default.lstatSync(absPath);
15534
15831
  } catch {
15535
15832
  return { exists: false, contentHash: "", fileCount: 0 };
15536
15833
  }
15537
15834
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
15538
15835
  if (lst.isFile()) {
15539
15836
  try {
15540
- return { exists: true, contentHash: sha256Bytes(import_fs30.default.readFileSync(absPath)), fileCount: 1 };
15837
+ return { exists: true, contentHash: sha256Bytes(import_fs31.default.readFileSync(absPath)), fileCount: 1 };
15541
15838
  } catch {
15542
15839
  return { exists: false, contentHash: "", fileCount: 0 };
15543
15840
  }
15544
15841
  }
15545
15842
  if (lst.isDirectory()) {
15546
15843
  const entries = walkDir(absPath);
15547
- const contentHash = import_crypto9.default.createHash("sha256").update(entries.join("\n")).digest("hex");
15844
+ const contentHash = import_crypto10.default.createHash("sha256").update(entries.join("\n")).digest("hex");
15548
15845
  return { exists: true, contentHash, fileCount: entries.length };
15549
15846
  }
15550
15847
  return { exists: false, contentHash: "", fileCount: 0 };
15551
15848
  }
15552
15849
  function getRootKey(absPath) {
15553
- return import_crypto9.default.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
15850
+ return import_crypto10.default.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
15554
15851
  }
15555
15852
  function readSkillPinsSafe() {
15556
- const filePath = getPinsFilePath();
15853
+ const filePath = getPinsFilePath2();
15557
15854
  try {
15558
- const raw = import_fs30.default.readFileSync(filePath, "utf-8");
15855
+ const raw = import_fs31.default.readFileSync(filePath, "utf-8");
15559
15856
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
15560
15857
  const parsed = JSON.parse(raw);
15561
15858
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -15574,18 +15871,18 @@ function readSkillPins() {
15574
15871
  throw new Error(`[node9] skill pin file is corrupt: ${result.detail}`);
15575
15872
  }
15576
15873
  function writeSkillPins(data) {
15577
- const filePath = getPinsFilePath();
15578
- import_fs30.default.mkdirSync(import_path31.default.dirname(filePath), { recursive: true });
15579
- const tmp = `${filePath}.${import_crypto9.default.randomBytes(6).toString("hex")}.tmp`;
15580
- import_fs30.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15581
- import_fs30.default.renameSync(tmp, filePath);
15874
+ const filePath = getPinsFilePath2();
15875
+ import_fs31.default.mkdirSync(import_path32.default.dirname(filePath), { recursive: true });
15876
+ const tmp = `${filePath}.${import_crypto10.default.randomBytes(6).toString("hex")}.tmp`;
15877
+ import_fs31.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15878
+ import_fs31.default.renameSync(tmp, filePath);
15582
15879
  }
15583
- function removePin(rootKey) {
15880
+ function removePin2(rootKey) {
15584
15881
  const pins = readSkillPins();
15585
15882
  delete pins.roots[rootKey];
15586
15883
  writeSkillPins(pins);
15587
15884
  }
15588
- function clearAllPins() {
15885
+ function clearAllPins2() {
15589
15886
  writeSkillPins({ roots: {} });
15590
15887
  }
15591
15888
  function verifyAndPinRoots(roots) {
@@ -15622,43 +15919,48 @@ function verifyAndPinRoots(roots) {
15622
15919
  return { kind: "verified" };
15623
15920
  }
15624
15921
  function defaultSkillRoots(_cwd) {
15625
- const marketplaces = import_path31.default.join(import_os26.default.homedir(), ".claude", "plugins", "marketplaces");
15922
+ const marketplaces = import_path32.default.join(import_os27.default.homedir(), ".claude", "plugins", "marketplaces");
15626
15923
  const roots = [];
15627
15924
  let registries;
15628
15925
  try {
15629
- registries = import_fs30.default.readdirSync(marketplaces, { withFileTypes: true });
15926
+ registries = import_fs31.default.readdirSync(marketplaces, { withFileTypes: true });
15630
15927
  } catch {
15631
15928
  return [];
15632
15929
  }
15633
15930
  for (const registry of registries) {
15634
15931
  if (!registry.isDirectory()) continue;
15635
- const pluginsDir = import_path31.default.join(marketplaces, registry.name, "plugins");
15932
+ const pluginsDir = import_path32.default.join(marketplaces, registry.name, "plugins");
15636
15933
  let plugins;
15637
15934
  try {
15638
- plugins = import_fs30.default.readdirSync(pluginsDir, { withFileTypes: true });
15935
+ plugins = import_fs31.default.readdirSync(pluginsDir, { withFileTypes: true });
15639
15936
  } catch {
15640
15937
  continue;
15641
15938
  }
15642
15939
  for (const plugin of plugins) {
15643
15940
  if (!plugin.isDirectory()) continue;
15644
- roots.push(import_path31.default.join(pluginsDir, plugin.name));
15941
+ roots.push(import_path32.default.join(pluginsDir, plugin.name));
15645
15942
  }
15646
15943
  }
15647
15944
  return roots;
15648
15945
  }
15649
15946
  function resolveUserSkillRoot(entry, cwd) {
15650
15947
  if (!entry) return null;
15651
- if (entry.startsWith("~/") || entry === "~") return import_path31.default.join(import_os26.default.homedir(), entry.slice(1));
15652
- if (import_path31.default.isAbsolute(entry)) return entry;
15653
- if (!cwd || !import_path31.default.isAbsolute(cwd)) return null;
15654
- return import_path31.default.join(cwd, entry);
15948
+ if (entry.startsWith("~/") || entry === "~") return import_path32.default.join(import_os27.default.homedir(), entry.slice(1));
15949
+ if (import_path32.default.isAbsolute(entry)) return entry;
15950
+ if (!cwd || !import_path32.default.isAbsolute(cwd)) return null;
15951
+ return import_path32.default.join(cwd, entry);
15655
15952
  }
15656
15953
 
15657
15954
  // src/cli/commands/check.ts
15955
+ init_dlp();
15956
+ init_audit();
15658
15957
  function sanitize2(value) {
15659
15958
  return value.replace(/[\x00-\x1F\x7F]/g, "");
15660
15959
  }
15661
15960
  function detectAiAgent(payload) {
15961
+ if (payload.turn_id !== void 0) {
15962
+ return "Codex";
15963
+ }
15662
15964
  if (payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0) {
15663
15965
  return "Claude Code";
15664
15966
  }
@@ -15693,9 +15995,9 @@ function registerCheckCommand(program2) {
15693
15995
  } catch (err2) {
15694
15996
  const tempConfig = getConfig();
15695
15997
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
15696
- const logPath = import_path32.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
15998
+ const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
15697
15999
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15698
- import_fs31.default.appendFileSync(
16000
+ import_fs32.default.appendFileSync(
15699
16001
  logPath,
15700
16002
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
15701
16003
  RAW: ${raw}
@@ -15704,15 +16006,76 @@ RAW: ${raw}
15704
16006
  }
15705
16007
  process.exit(0);
15706
16008
  }
15707
- const config = getConfig(payload.cwd || void 0);
16009
+ if (payload.hook_event_name === "UserPromptSubmit") {
16010
+ const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
16011
+ if (process.env.NODE9_DEBUG === "1") {
16012
+ try {
16013
+ const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
16014
+ if (!import_fs32.default.existsSync(import_path33.default.dirname(logPath)))
16015
+ import_fs32.default.mkdirSync(import_path33.default.dirname(logPath), { recursive: true });
16016
+ const sanitized = JSON.stringify({
16017
+ ...payload,
16018
+ prompt: `<redacted, ${prompt.length} bytes>`
16019
+ });
16020
+ import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
16021
+ `);
16022
+ } catch {
16023
+ }
16024
+ }
16025
+ if (!prompt) process.exit(0);
16026
+ const dlpMatch = scanArgs({ prompt });
16027
+ if (!dlpMatch) process.exit(0);
16028
+ const agent2 = detectAiAgent(payload);
16029
+ const sessionId2 = typeof payload.session_id === "string" ? payload.session_id : void 0;
16030
+ appendLocalAudit(
16031
+ "UserPromptSubmit",
16032
+ { prompt },
16033
+ "deny",
16034
+ "dlp-block",
16035
+ { agent: agent2, sessionId: sessionId2 },
16036
+ true
16037
+ );
16038
+ const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
16039
+ try {
16040
+ const ttyFd = import_fs32.default.openSync("/dev/tty", "w");
16041
+ import_fs32.default.writeSync(
16042
+ ttyFd,
16043
+ import_chalk9.default.bgRed.white.bold(`
16044
+ \u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
16045
+ `) + import_chalk9.default.red(` ${dlpMatch.patternName} detected in your prompt.
16046
+ `) + import_chalk9.default.gray(` Match: ${dlpMatch.redactedSample}
16047
+ `) + import_chalk9.default.cyan(` Edit the prompt to remove the credential and resubmit.
16048
+
16049
+ `)
16050
+ );
16051
+ import_fs32.default.closeSync(ttyFd);
16052
+ } catch {
16053
+ }
16054
+ const isCodex = agent2 === "Codex";
16055
+ process.stdout.write(
16056
+ JSON.stringify({
16057
+ decision: "block",
16058
+ reason,
16059
+ systemMessage: reason,
16060
+ hookSpecificOutput: isCodex ? { hookEventName: "UserPromptSubmit" } : {
16061
+ hookEventName: "UserPromptSubmit",
16062
+ permissionDecision: "deny",
16063
+ permissionDecisionReason: reason
16064
+ }
16065
+ }) + "\n"
16066
+ );
16067
+ process.exit(2);
16068
+ }
16069
+ const safeCwdForConfig = typeof payload.cwd === "string" && import_path33.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16070
+ const config = getConfig(safeCwdForConfig);
15708
16071
  if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
15709
16072
  try {
15710
16073
  const scriptPath = process.argv[1];
15711
- if (typeof scriptPath !== "string" || !import_path32.default.isAbsolute(scriptPath))
16074
+ if (typeof scriptPath !== "string" || !import_path33.default.isAbsolute(scriptPath))
15712
16075
  throw new Error("node9: argv[1] is not an absolute path");
15713
- const resolvedScript = import_fs31.default.realpathSync(scriptPath);
15714
- const packageDist = import_fs31.default.realpathSync(import_path32.default.resolve(__dirname, "../.."));
15715
- if (!resolvedScript.startsWith(packageDist + import_path32.default.sep) && resolvedScript !== packageDist)
16076
+ const resolvedScript = import_fs32.default.realpathSync(scriptPath);
16077
+ const packageDist = import_fs32.default.realpathSync(import_path33.default.resolve(__dirname, "../.."));
16078
+ if (!resolvedScript.startsWith(packageDist + import_path33.default.sep) && resolvedScript !== packageDist)
15716
16079
  throw new Error(
15717
16080
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
15718
16081
  );
@@ -15734,10 +16097,10 @@ RAW: ${raw}
15734
16097
  });
15735
16098
  d.unref();
15736
16099
  } catch (spawnErr) {
15737
- const logPath = import_path32.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
16100
+ const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
15738
16101
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
15739
16102
  try {
15740
- import_fs31.default.appendFileSync(
16103
+ import_fs32.default.appendFileSync(
15741
16104
  logPath,
15742
16105
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
15743
16106
  `
@@ -15747,10 +16110,10 @@ RAW: ${raw}
15747
16110
  }
15748
16111
  }
15749
16112
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
15750
- const logPath = import_path32.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
15751
- if (!import_fs31.default.existsSync(import_path32.default.dirname(logPath)))
15752
- import_fs31.default.mkdirSync(import_path32.default.dirname(logPath), { recursive: true });
15753
- import_fs31.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
16113
+ const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
16114
+ if (!import_fs32.default.existsSync(import_path33.default.dirname(logPath)))
16115
+ import_fs32.default.mkdirSync(import_path33.default.dirname(logPath), { recursive: true });
16116
+ import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15754
16117
  `);
15755
16118
  }
15756
16119
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -15763,8 +16126,8 @@ RAW: ${raw}
15763
16126
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
15764
16127
  let ttyFd = null;
15765
16128
  try {
15766
- ttyFd = import_fs31.default.openSync("/dev/tty", "w");
15767
- const writeTty = (line) => import_fs31.default.writeSync(ttyFd, line + "\n");
16129
+ ttyFd = import_fs32.default.openSync("/dev/tty", "w");
16130
+ const writeTty = (line) => import_fs32.default.writeSync(ttyFd, line + "\n");
15768
16131
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
15769
16132
  writeTty(import_chalk9.default.bgRed.white.bold(`
15770
16133
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -15783,7 +16146,7 @@ RAW: ${raw}
15783
16146
  } finally {
15784
16147
  if (ttyFd !== null)
15785
16148
  try {
15786
- import_fs31.default.closeSync(ttyFd);
16149
+ import_fs32.default.closeSync(ttyFd);
15787
16150
  } catch {
15788
16151
  }
15789
16152
  }
@@ -15819,17 +16182,17 @@ RAW: ${raw}
15819
16182
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
15820
16183
  if (skillPinCfg.enabled && safeSessionId) {
15821
16184
  try {
15822
- const sessionsDir = import_path32.default.join(import_os27.default.homedir(), ".node9", "skill-sessions");
15823
- const flagPath = import_path32.default.join(sessionsDir, `${safeSessionId}.json`);
16185
+ const sessionsDir = import_path33.default.join(import_os28.default.homedir(), ".node9", "skill-sessions");
16186
+ const flagPath = import_path33.default.join(sessionsDir, `${safeSessionId}.json`);
15824
16187
  let flag = null;
15825
16188
  try {
15826
- flag = JSON.parse(import_fs31.default.readFileSync(flagPath, "utf-8"));
16189
+ flag = JSON.parse(import_fs32.default.readFileSync(flagPath, "utf-8"));
15827
16190
  } catch {
15828
16191
  }
15829
16192
  const writeFlag = (data2) => {
15830
16193
  try {
15831
- import_fs31.default.mkdirSync(sessionsDir, { recursive: true });
15832
- import_fs31.default.writeFileSync(
16194
+ import_fs32.default.mkdirSync(sessionsDir, { recursive: true });
16195
+ import_fs32.default.writeFileSync(
15833
16196
  flagPath,
15834
16197
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
15835
16198
  { mode: 384 }
@@ -15840,8 +16203,8 @@ RAW: ${raw}
15840
16203
  const sendSkillWarn = (detail, recoveryCmd) => {
15841
16204
  let ttyFd = null;
15842
16205
  try {
15843
- ttyFd = import_fs31.default.openSync("/dev/tty", "w");
15844
- const w = (line) => import_fs31.default.writeSync(ttyFd, line + "\n");
16206
+ ttyFd = import_fs32.default.openSync("/dev/tty", "w");
16207
+ const w = (line) => import_fs32.default.writeSync(ttyFd, line + "\n");
15845
16208
  w(import_chalk9.default.yellow(`
15846
16209
  \u26A0\uFE0F Node9: installed skill drift detected`));
15847
16210
  w(import_chalk9.default.gray(` ${detail}`));
@@ -15856,7 +16219,7 @@ RAW: ${raw}
15856
16219
  } finally {
15857
16220
  if (ttyFd !== null)
15858
16221
  try {
15859
- import_fs31.default.closeSync(ttyFd);
16222
+ import_fs32.default.closeSync(ttyFd);
15860
16223
  } catch {
15861
16224
  }
15862
16225
  }
@@ -15872,7 +16235,7 @@ RAW: ${raw}
15872
16235
  return;
15873
16236
  }
15874
16237
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
15875
- const absoluteCwd = typeof payload.cwd === "string" && import_path32.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16238
+ const absoluteCwd = typeof payload.cwd === "string" && import_path33.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15876
16239
  const extraRoots = skillPinCfg.roots;
15877
16240
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
15878
16241
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -15913,10 +16276,10 @@ RAW: ${raw}
15913
16276
  }
15914
16277
  try {
15915
16278
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
15916
- for (const name of import_fs31.default.readdirSync(sessionsDir)) {
15917
- const p = import_path32.default.join(sessionsDir, name);
16279
+ for (const name of import_fs32.default.readdirSync(sessionsDir)) {
16280
+ const p = import_path33.default.join(sessionsDir, name);
15918
16281
  try {
15919
- if (import_fs31.default.statSync(p).mtimeMs < cutoff) import_fs31.default.unlinkSync(p);
16282
+ if (import_fs32.default.statSync(p).mtimeMs < cutoff) import_fs32.default.unlinkSync(p);
15920
16283
  } catch {
15921
16284
  }
15922
16285
  }
@@ -15926,9 +16289,9 @@ RAW: ${raw}
15926
16289
  } catch (err2) {
15927
16290
  if (process.env.NODE9_DEBUG === "1") {
15928
16291
  try {
15929
- const dbg = import_path32.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
16292
+ const dbg = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
15930
16293
  const msg = err2 instanceof Error ? err2.message : String(err2);
15931
- import_fs31.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
16294
+ import_fs32.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15932
16295
  `);
15933
16296
  } catch {
15934
16297
  }
@@ -15938,7 +16301,7 @@ RAW: ${raw}
15938
16301
  if (shouldSnapshot(toolName, toolInput, config)) {
15939
16302
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
15940
16303
  }
15941
- const safeCwdForAuth = typeof payload.cwd === "string" && import_path32.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16304
+ const safeCwdForAuth = typeof payload.cwd === "string" && import_path33.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15942
16305
  const result = await authorizeHeadless(toolName, toolInput, meta, {
15943
16306
  cwd: safeCwdForAuth
15944
16307
  });
@@ -15950,12 +16313,12 @@ RAW: ${raw}
15950
16313
  }
15951
16314
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
15952
16315
  try {
15953
- const tty = import_fs31.default.openSync("/dev/tty", "w");
15954
- import_fs31.default.writeSync(
16316
+ const tty = import_fs32.default.openSync("/dev/tty", "w");
16317
+ import_fs32.default.writeSync(
15955
16318
  tty,
15956
16319
  import_chalk9.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
15957
16320
  );
15958
- import_fs31.default.closeSync(tty);
16321
+ import_fs32.default.closeSync(tty);
15959
16322
  } catch {
15960
16323
  }
15961
16324
  const daemonReady = await autoStartDaemonAndWait();
@@ -15982,9 +16345,9 @@ RAW: ${raw}
15982
16345
  });
15983
16346
  } catch (err2) {
15984
16347
  if (process.env.NODE9_DEBUG === "1") {
15985
- const logPath = import_path32.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
16348
+ const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
15986
16349
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15987
- import_fs31.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
16350
+ import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15988
16351
  `);
15989
16352
  }
15990
16353
  process.exit(0);
@@ -16018,9 +16381,9 @@ RAW: ${raw}
16018
16381
  }
16019
16382
 
16020
16383
  // src/cli/commands/log.ts
16021
- var import_fs32 = __toESM(require("fs"));
16022
- var import_path33 = __toESM(require("path"));
16023
- var import_os28 = __toESM(require("os"));
16384
+ var import_fs33 = __toESM(require("fs"));
16385
+ var import_path34 = __toESM(require("path"));
16386
+ var import_os29 = __toESM(require("os"));
16024
16387
  init_audit();
16025
16388
  init_config();
16026
16389
  init_daemon();
@@ -16088,7 +16451,7 @@ function registerLogCommand(program2) {
16088
16451
  const payload = JSON.parse(raw);
16089
16452
  const tool = sanitize3(payload.tool_name ?? payload.name ?? "unknown");
16090
16453
  const rawInput = payload.tool_input ?? payload.args ?? {};
16091
- const agent = payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0 ? "Claude Code" : payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0 ? "Gemini CLI" : void 0;
16454
+ const agent = payload.turn_id !== void 0 ? "Codex" : payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0 ? "Claude Code" : payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0 ? "Gemini CLI" : void 0;
16092
16455
  const entry = {
16093
16456
  ts: (/* @__PURE__ */ new Date()).toISOString(),
16094
16457
  tool,
@@ -16098,10 +16461,10 @@ function registerLogCommand(program2) {
16098
16461
  };
16099
16462
  if (agent) entry.agent = agent;
16100
16463
  if (payload.session_id) entry.sessionId = payload.session_id;
16101
- const logPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "audit.log");
16102
- if (!import_fs32.default.existsSync(import_path33.default.dirname(logPath)))
16103
- import_fs32.default.mkdirSync(import_path33.default.dirname(logPath), { recursive: true });
16104
- import_fs32.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
16464
+ const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "audit.log");
16465
+ if (!import_fs33.default.existsSync(import_path34.default.dirname(logPath)))
16466
+ import_fs33.default.mkdirSync(import_path34.default.dirname(logPath), { recursive: true });
16467
+ import_fs33.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
16105
16468
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
16106
16469
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
16107
16470
  if (command) {
@@ -16134,7 +16497,7 @@ function registerLogCommand(program2) {
16134
16497
  }
16135
16498
  }
16136
16499
  }
16137
- const safeCwd = typeof payload.cwd === "string" && import_path33.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16500
+ const safeCwd = typeof payload.cwd === "string" && import_path34.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16138
16501
  const config = getConfig(safeCwd);
16139
16502
  if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
16140
16503
  const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
@@ -16155,9 +16518,9 @@ function registerLogCommand(program2) {
16155
16518
  const msg = err2 instanceof Error ? err2.message : String(err2);
16156
16519
  process.stderr.write(`[Node9] audit log error: ${msg}
16157
16520
  `);
16158
- const debugPath = import_path33.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
16521
+ const debugPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
16159
16522
  try {
16160
- import_fs32.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
16523
+ import_fs33.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
16161
16524
  `);
16162
16525
  } catch {
16163
16526
  }
@@ -16557,14 +16920,14 @@ function registerConfigShowCommand(program2) {
16557
16920
 
16558
16921
  // src/cli/commands/doctor.ts
16559
16922
  var import_chalk11 = __toESM(require("chalk"));
16560
- var import_fs33 = __toESM(require("fs"));
16561
- var import_path34 = __toESM(require("path"));
16562
- var import_os29 = __toESM(require("os"));
16923
+ var import_fs34 = __toESM(require("fs"));
16924
+ var import_path35 = __toESM(require("path"));
16925
+ var import_os30 = __toESM(require("os"));
16563
16926
  var import_child_process8 = require("child_process");
16564
16927
  init_daemon();
16565
16928
  function registerDoctorCommand(program2, version2) {
16566
16929
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
16567
- const homeDir2 = import_os29.default.homedir();
16930
+ const homeDir2 = import_os30.default.homedir();
16568
16931
  let failures = 0;
16569
16932
  function pass(msg) {
16570
16933
  console.log(import_chalk11.default.green(" \u2705 ") + msg);
@@ -16613,10 +16976,10 @@ function registerDoctorCommand(program2, version2) {
16613
16976
  );
16614
16977
  }
16615
16978
  section("Configuration");
16616
- const globalConfigPath = import_path34.default.join(homeDir2, ".node9", "config.json");
16617
- if (import_fs33.default.existsSync(globalConfigPath)) {
16979
+ const globalConfigPath = import_path35.default.join(homeDir2, ".node9", "config.json");
16980
+ if (import_fs34.default.existsSync(globalConfigPath)) {
16618
16981
  try {
16619
- JSON.parse(import_fs33.default.readFileSync(globalConfigPath, "utf-8"));
16982
+ JSON.parse(import_fs34.default.readFileSync(globalConfigPath, "utf-8"));
16620
16983
  pass("~/.node9/config.json found and valid");
16621
16984
  } catch {
16622
16985
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -16624,10 +16987,10 @@ function registerDoctorCommand(program2, version2) {
16624
16987
  } else {
16625
16988
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
16626
16989
  }
16627
- const projectConfigPath = import_path34.default.join(process.cwd(), "node9.config.json");
16628
- if (import_fs33.default.existsSync(projectConfigPath)) {
16990
+ const projectConfigPath = import_path35.default.join(process.cwd(), "node9.config.json");
16991
+ if (import_fs34.default.existsSync(projectConfigPath)) {
16629
16992
  try {
16630
- JSON.parse(import_fs33.default.readFileSync(projectConfigPath, "utf-8"));
16993
+ JSON.parse(import_fs34.default.readFileSync(projectConfigPath, "utf-8"));
16631
16994
  pass("node9.config.json found and valid (project)");
16632
16995
  } catch {
16633
16996
  fail(
@@ -16636,8 +16999,8 @@ function registerDoctorCommand(program2, version2) {
16636
16999
  );
16637
17000
  }
16638
17001
  }
16639
- const credsPath = import_path34.default.join(homeDir2, ".node9", "credentials.json");
16640
- if (import_fs33.default.existsSync(credsPath)) {
17002
+ const credsPath = import_path35.default.join(homeDir2, ".node9", "credentials.json");
17003
+ if (import_fs34.default.existsSync(credsPath)) {
16641
17004
  pass("Cloud credentials found (~/.node9/credentials.json)");
16642
17005
  } else {
16643
17006
  warn(
@@ -16646,10 +17009,10 @@ function registerDoctorCommand(program2, version2) {
16646
17009
  );
16647
17010
  }
16648
17011
  section("Agent Hooks");
16649
- const claudeSettingsPath = import_path34.default.join(homeDir2, ".claude", "settings.json");
16650
- if (import_fs33.default.existsSync(claudeSettingsPath)) {
17012
+ const claudeSettingsPath = import_path35.default.join(homeDir2, ".claude", "settings.json");
17013
+ if (import_fs34.default.existsSync(claudeSettingsPath)) {
16651
17014
  try {
16652
- const cs = JSON.parse(import_fs33.default.readFileSync(claudeSettingsPath, "utf-8"));
17015
+ const cs = JSON.parse(import_fs34.default.readFileSync(claudeSettingsPath, "utf-8"));
16653
17016
  const hasHook = cs.hooks?.PreToolUse?.some(
16654
17017
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16655
17018
  );
@@ -16665,10 +17028,10 @@ function registerDoctorCommand(program2, version2) {
16665
17028
  } else {
16666
17029
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
16667
17030
  }
16668
- const geminiSettingsPath = import_path34.default.join(homeDir2, ".gemini", "settings.json");
16669
- if (import_fs33.default.existsSync(geminiSettingsPath)) {
17031
+ const geminiSettingsPath = import_path35.default.join(homeDir2, ".gemini", "settings.json");
17032
+ if (import_fs34.default.existsSync(geminiSettingsPath)) {
16670
17033
  try {
16671
- const gs = JSON.parse(import_fs33.default.readFileSync(geminiSettingsPath, "utf-8"));
17034
+ const gs = JSON.parse(import_fs34.default.readFileSync(geminiSettingsPath, "utf-8"));
16672
17035
  const hasHook = gs.hooks?.BeforeTool?.some(
16673
17036
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16674
17037
  );
@@ -16684,10 +17047,10 @@ function registerDoctorCommand(program2, version2) {
16684
17047
  } else {
16685
17048
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
16686
17049
  }
16687
- const cursorHooksPath = import_path34.default.join(homeDir2, ".cursor", "hooks.json");
16688
- if (import_fs33.default.existsSync(cursorHooksPath)) {
17050
+ const cursorHooksPath = import_path35.default.join(homeDir2, ".cursor", "hooks.json");
17051
+ if (import_fs34.default.existsSync(cursorHooksPath)) {
16689
17052
  try {
16690
- const cur = JSON.parse(import_fs33.default.readFileSync(cursorHooksPath, "utf-8"));
17053
+ const cur = JSON.parse(import_fs34.default.readFileSync(cursorHooksPath, "utf-8"));
16691
17054
  const hasHook = cur.hooks?.preToolUse?.some(
16692
17055
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
16693
17056
  );
@@ -16727,9 +17090,9 @@ function registerDoctorCommand(program2, version2) {
16727
17090
 
16728
17091
  // src/cli/commands/audit.ts
16729
17092
  var import_chalk12 = __toESM(require("chalk"));
16730
- var import_fs34 = __toESM(require("fs"));
16731
- var import_path35 = __toESM(require("path"));
16732
- var import_os30 = __toESM(require("os"));
17093
+ var import_fs35 = __toESM(require("fs"));
17094
+ var import_path36 = __toESM(require("path"));
17095
+ var import_os31 = __toESM(require("os"));
16733
17096
  function formatRelativeTime(timestamp) {
16734
17097
  const diff = Date.now() - new Date(timestamp).getTime();
16735
17098
  const sec = Math.floor(diff / 1e3);
@@ -16742,14 +17105,14 @@ function formatRelativeTime(timestamp) {
16742
17105
  }
16743
17106
  function registerAuditCommand(program2) {
16744
17107
  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) => {
16745
- const logPath = import_path35.default.join(import_os30.default.homedir(), ".node9", "audit.log");
16746
- if (!import_fs34.default.existsSync(logPath)) {
17108
+ const logPath = import_path36.default.join(import_os31.default.homedir(), ".node9", "audit.log");
17109
+ if (!import_fs35.default.existsSync(logPath)) {
16747
17110
  console.log(
16748
17111
  import_chalk12.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
16749
17112
  );
16750
17113
  return;
16751
17114
  }
16752
- const raw = import_fs34.default.readFileSync(logPath, "utf-8");
17115
+ const raw = import_fs35.default.readFileSync(logPath, "utf-8");
16753
17116
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
16754
17117
  let entries = lines.flatMap((line) => {
16755
17118
  try {
@@ -16805,9 +17168,9 @@ function registerAuditCommand(program2) {
16805
17168
  var import_chalk13 = __toESM(require("chalk"));
16806
17169
 
16807
17170
  // src/cli/aggregate/report-audit.ts
16808
- var import_fs35 = __toESM(require("fs"));
16809
- var import_os31 = __toESM(require("os"));
16810
- var import_path36 = __toESM(require("path"));
17171
+ var import_fs36 = __toESM(require("fs"));
17172
+ var import_os32 = __toESM(require("os"));
17173
+ var import_path37 = __toESM(require("path"));
16811
17174
  init_costSync();
16812
17175
  init_litellm();
16813
17176
  var TEST_COMMAND_RE3 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
@@ -16889,8 +17252,8 @@ function getDateRange(period, now) {
16889
17252
  }
16890
17253
  }
16891
17254
  function parseAuditLog(logPath) {
16892
- if (!import_fs35.default.existsSync(logPath)) return [];
16893
- const raw = import_fs35.default.readFileSync(logPath, "utf-8");
17255
+ if (!import_fs36.default.existsSync(logPath)) return [];
17256
+ const raw = import_fs36.default.readFileSync(logPath, "utf-8");
16894
17257
  return raw.split("\n").flatMap((line) => {
16895
17258
  if (!line.trim()) return [];
16896
17259
  try {
@@ -16950,25 +17313,25 @@ function freezeClaudeCost(acc) {
16950
17313
  };
16951
17314
  }
16952
17315
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
16953
- const projPath = import_path36.default.join(projectsDir, proj);
17316
+ const projPath = import_path37.default.join(projectsDir, proj);
16954
17317
  let files;
16955
17318
  try {
16956
- const stat = import_fs35.default.statSync(projPath);
17319
+ const stat = import_fs36.default.statSync(projPath);
16957
17320
  if (!stat.isDirectory()) return;
16958
- files = import_fs35.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
17321
+ files = import_fs36.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16959
17322
  } catch {
16960
17323
  return;
16961
17324
  }
16962
17325
  const startMs = start.getTime();
16963
17326
  for (const file of files) {
16964
- const filePath = import_path36.default.join(projPath, file);
17327
+ const filePath = import_path37.default.join(projPath, file);
16965
17328
  try {
16966
- if (import_fs35.default.statSync(filePath).mtimeMs < startMs) continue;
17329
+ if (import_fs36.default.statSync(filePath).mtimeMs < startMs) continue;
16967
17330
  } catch {
16968
17331
  continue;
16969
17332
  }
16970
17333
  try {
16971
- const raw = import_fs35.default.readFileSync(filePath, "utf-8");
17334
+ const raw = import_fs36.default.readFileSync(filePath, "utf-8");
16972
17335
  for (const line of raw.split("\n")) {
16973
17336
  if (!line.trim()) continue;
16974
17337
  let entry;
@@ -17018,10 +17381,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
17018
17381
  }
17019
17382
  function loadClaudeCost(start, end, projectsDir) {
17020
17383
  const acc = emptyClaudeCostAccumulator();
17021
- if (!import_fs35.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
17384
+ if (!import_fs36.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
17022
17385
  let dirs;
17023
17386
  try {
17024
- dirs = import_fs35.default.readdirSync(projectsDir);
17387
+ dirs = import_fs36.default.readdirSync(projectsDir);
17025
17388
  } catch {
17026
17389
  return freezeClaudeCost(acc);
17027
17390
  }
@@ -17033,7 +17396,7 @@ function loadClaudeCost(start, end, projectsDir) {
17033
17396
  function processCodexCostFile(filePath, start, end, acc) {
17034
17397
  let lines;
17035
17398
  try {
17036
- lines = import_fs35.default.readFileSync(filePath, "utf-8").split("\n");
17399
+ lines = import_fs36.default.readFileSync(filePath, "utf-8").split("\n");
17037
17400
  } catch {
17038
17401
  return;
17039
17402
  }
@@ -17078,31 +17441,31 @@ function processCodexCostFile(filePath, start, end, acc) {
17078
17441
  }
17079
17442
  function listCodexSessionFiles(sessionsBase) {
17080
17443
  const jsonlFiles = [];
17081
- if (!import_fs35.default.existsSync(sessionsBase)) return jsonlFiles;
17444
+ if (!import_fs36.default.existsSync(sessionsBase)) return jsonlFiles;
17082
17445
  try {
17083
- for (const year of import_fs35.default.readdirSync(sessionsBase)) {
17084
- const yearPath = import_path36.default.join(sessionsBase, year);
17446
+ for (const year of import_fs36.default.readdirSync(sessionsBase)) {
17447
+ const yearPath = import_path37.default.join(sessionsBase, year);
17085
17448
  try {
17086
- if (!import_fs35.default.statSync(yearPath).isDirectory()) continue;
17449
+ if (!import_fs36.default.statSync(yearPath).isDirectory()) continue;
17087
17450
  } catch {
17088
17451
  continue;
17089
17452
  }
17090
- for (const month of import_fs35.default.readdirSync(yearPath)) {
17091
- const monthPath = import_path36.default.join(yearPath, month);
17453
+ for (const month of import_fs36.default.readdirSync(yearPath)) {
17454
+ const monthPath = import_path37.default.join(yearPath, month);
17092
17455
  try {
17093
- if (!import_fs35.default.statSync(monthPath).isDirectory()) continue;
17456
+ if (!import_fs36.default.statSync(monthPath).isDirectory()) continue;
17094
17457
  } catch {
17095
17458
  continue;
17096
17459
  }
17097
- for (const day of import_fs35.default.readdirSync(monthPath)) {
17098
- const dayPath = import_path36.default.join(monthPath, day);
17460
+ for (const day of import_fs36.default.readdirSync(monthPath)) {
17461
+ const dayPath = import_path37.default.join(monthPath, day);
17099
17462
  try {
17100
- if (!import_fs35.default.statSync(dayPath).isDirectory()) continue;
17463
+ if (!import_fs36.default.statSync(dayPath).isDirectory()) continue;
17101
17464
  } catch {
17102
17465
  continue;
17103
17466
  }
17104
- for (const file of import_fs35.default.readdirSync(dayPath)) {
17105
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path36.default.join(dayPath, file));
17467
+ for (const file of import_fs36.default.readdirSync(dayPath)) {
17468
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path37.default.join(dayPath, file));
17106
17469
  }
17107
17470
  }
17108
17471
  }
@@ -17155,13 +17518,13 @@ function freezeGeminiCost(acc) {
17155
17518
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
17156
17519
  const startMs = start.getTime();
17157
17520
  try {
17158
- if (import_fs35.default.statSync(filePath).mtimeMs < startMs) return;
17521
+ if (import_fs36.default.statSync(filePath).mtimeMs < startMs) return;
17159
17522
  } catch {
17160
17523
  return;
17161
17524
  }
17162
17525
  let raw;
17163
17526
  try {
17164
- raw = import_fs35.default.readFileSync(filePath, "utf-8");
17527
+ raw = import_fs36.default.readFileSync(filePath, "utf-8");
17165
17528
  } catch {
17166
17529
  return;
17167
17530
  }
@@ -17210,30 +17573,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
17210
17573
  const out = [];
17211
17574
  let dirs;
17212
17575
  try {
17213
- if (!import_fs35.default.statSync(geminiTmpDir).isDirectory()) return out;
17214
- dirs = import_fs35.default.readdirSync(geminiTmpDir);
17576
+ if (!import_fs36.default.statSync(geminiTmpDir).isDirectory()) return out;
17577
+ dirs = import_fs36.default.readdirSync(geminiTmpDir);
17215
17578
  } catch {
17216
17579
  return out;
17217
17580
  }
17218
17581
  for (const proj of dirs) {
17219
- const chatsDir = import_path36.default.join(geminiTmpDir, proj, "chats");
17582
+ const chatsDir = import_path37.default.join(geminiTmpDir, proj, "chats");
17220
17583
  let files;
17221
17584
  try {
17222
- if (!import_fs35.default.statSync(chatsDir).isDirectory()) continue;
17223
- files = import_fs35.default.readdirSync(chatsDir);
17585
+ if (!import_fs36.default.statSync(chatsDir).isDirectory()) continue;
17586
+ files = import_fs36.default.readdirSync(chatsDir);
17224
17587
  } catch {
17225
17588
  continue;
17226
17589
  }
17227
17590
  for (const f of files) {
17228
17591
  if (!f.endsWith(".jsonl")) continue;
17229
- out.push({ projectKey: proj, file: import_path36.default.join(chatsDir, f) });
17592
+ out.push({ projectKey: proj, file: import_path37.default.join(chatsDir, f) });
17230
17593
  }
17231
17594
  }
17232
17595
  return out;
17233
17596
  }
17234
17597
  function loadGeminiCost(start, end, geminiTmpDir) {
17235
17598
  const acc = emptyGeminiAccumulator();
17236
- if (!import_fs35.default.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
17599
+ if (!import_fs36.default.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
17237
17600
  for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
17238
17601
  processGeminiCostFile(file, projectKey, start, end, acc);
17239
17602
  }
@@ -17241,11 +17604,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
17241
17604
  }
17242
17605
  function aggregateReportFromAudit(period, opts = {}) {
17243
17606
  const now = opts.now ?? /* @__PURE__ */ new Date();
17244
- const auditLogPath = opts.auditLogPath ?? import_path36.default.join(import_os31.default.homedir(), ".node9", "audit.log");
17245
- const claudeProjectsDir = opts.claudeProjectsDir ?? import_path36.default.join(import_os31.default.homedir(), ".claude", "projects");
17246
- const codexSessionsDir = opts.codexSessionsDir ?? import_path36.default.join(import_os31.default.homedir(), ".codex", "sessions");
17247
- const geminiTmpDir = opts.geminiTmpDir ?? import_path36.default.join(import_os31.default.homedir(), ".gemini", "tmp");
17248
- const hasAuditFile = import_fs35.default.existsSync(auditLogPath);
17607
+ const auditLogPath = opts.auditLogPath ?? import_path37.default.join(import_os32.default.homedir(), ".node9", "audit.log");
17608
+ const claudeProjectsDir = opts.claudeProjectsDir ?? import_path37.default.join(import_os32.default.homedir(), ".claude", "projects");
17609
+ const codexSessionsDir = opts.codexSessionsDir ?? import_path37.default.join(import_os32.default.homedir(), ".codex", "sessions");
17610
+ const geminiTmpDir = opts.geminiTmpDir ?? import_path37.default.join(import_os32.default.homedir(), ".gemini", "tmp");
17611
+ const hasAuditFile = import_fs36.default.existsSync(auditLogPath);
17249
17612
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
17250
17613
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
17251
17614
  const { start, end } = getDateRange(period, now);
@@ -17941,14 +18304,14 @@ function registerDaemonCommand(program2) {
17941
18304
 
17942
18305
  // src/cli/commands/status.ts
17943
18306
  var import_chalk15 = __toESM(require("chalk"));
17944
- var import_fs36 = __toESM(require("fs"));
17945
- var import_path37 = __toESM(require("path"));
17946
- var import_os32 = __toESM(require("os"));
18307
+ var import_fs37 = __toESM(require("fs"));
18308
+ var import_path38 = __toESM(require("path"));
18309
+ var import_os33 = __toESM(require("os"));
17947
18310
  init_core();
17948
18311
  init_daemon();
17949
18312
  function readJson2(filePath) {
17950
18313
  try {
17951
- if (import_fs36.default.existsSync(filePath)) return JSON.parse(import_fs36.default.readFileSync(filePath, "utf-8"));
18314
+ if (import_fs37.default.existsSync(filePath)) return JSON.parse(import_fs37.default.readFileSync(filePath, "utf-8"));
17952
18315
  } catch {
17953
18316
  }
17954
18317
  return null;
@@ -18013,28 +18376,28 @@ function registerStatusCommand(program2) {
18013
18376
  console.log("");
18014
18377
  const modeLabel = settings.mode === "audit" ? import_chalk15.default.blue("audit") : settings.mode === "strict" ? import_chalk15.default.red("strict") : import_chalk15.default.white("standard");
18015
18378
  console.log(` Mode: ${modeLabel}`);
18016
- const projectConfig = import_path37.default.join(process.cwd(), "node9.config.json");
18017
- const globalConfig = import_path37.default.join(import_os32.default.homedir(), ".node9", "config.json");
18379
+ const projectConfig = import_path38.default.join(process.cwd(), "node9.config.json");
18380
+ const globalConfig = import_path38.default.join(import_os33.default.homedir(), ".node9", "config.json");
18018
18381
  console.log(
18019
- ` Local: ${import_fs36.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
18382
+ ` Local: ${import_fs37.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
18020
18383
  );
18021
18384
  console.log(
18022
- ` Global: ${import_fs36.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
18385
+ ` Global: ${import_fs37.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
18023
18386
  );
18024
18387
  if (mergedConfig.policy.sandboxPaths.length > 0) {
18025
18388
  console.log(
18026
18389
  ` Sandbox: ${import_chalk15.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
18027
18390
  );
18028
18391
  }
18029
- const homeDir2 = import_os32.default.homedir();
18392
+ const homeDir2 = import_os33.default.homedir();
18030
18393
  const claudeSettings = readJson2(
18031
- import_path37.default.join(homeDir2, ".claude", "settings.json")
18394
+ import_path38.default.join(homeDir2, ".claude", "settings.json")
18032
18395
  );
18033
- const claudeConfig = readJson2(import_path37.default.join(homeDir2, ".claude.json"));
18396
+ const claudeConfig = readJson2(import_path38.default.join(homeDir2, ".claude.json"));
18034
18397
  const geminiSettings = readJson2(
18035
- import_path37.default.join(homeDir2, ".gemini", "settings.json")
18398
+ import_path38.default.join(homeDir2, ".gemini", "settings.json")
18036
18399
  );
18037
- const cursorConfig = readJson2(import_path37.default.join(homeDir2, ".cursor", "mcp.json"));
18400
+ const cursorConfig = readJson2(import_path38.default.join(homeDir2, ".cursor", "mcp.json"));
18038
18401
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
18039
18402
  if (agentFound) {
18040
18403
  console.log("");
@@ -18093,9 +18456,9 @@ function registerStatusCommand(program2) {
18093
18456
 
18094
18457
  // src/cli/commands/init.ts
18095
18458
  var import_chalk16 = __toESM(require("chalk"));
18096
- var import_fs37 = __toESM(require("fs"));
18097
- var import_path38 = __toESM(require("path"));
18098
- var import_os33 = __toESM(require("os"));
18459
+ var import_fs38 = __toESM(require("fs"));
18460
+ var import_path39 = __toESM(require("path"));
18461
+ var import_os34 = __toESM(require("os"));
18099
18462
  var import_https4 = __toESM(require("https"));
18100
18463
  init_core();
18101
18464
  init_setup();
@@ -18175,15 +18538,15 @@ function registerInitCommand(program2) {
18175
18538
  }
18176
18539
  console.log("");
18177
18540
  }
18178
- const configPath = import_path38.default.join(import_os33.default.homedir(), ".node9", "config.json");
18179
- if (import_fs37.default.existsSync(configPath) && !options.force) {
18541
+ const configPath = import_path39.default.join(import_os34.default.homedir(), ".node9", "config.json");
18542
+ if (import_fs38.default.existsSync(configPath) && !options.force) {
18180
18543
  try {
18181
- const existing = JSON.parse(import_fs37.default.readFileSync(configPath, "utf-8"));
18544
+ const existing = JSON.parse(import_fs38.default.readFileSync(configPath, "utf-8"));
18182
18545
  const settings = existing.settings ?? {};
18183
18546
  if (settings.mode !== chosenMode) {
18184
18547
  settings.mode = chosenMode;
18185
18548
  existing.settings = settings;
18186
- import_fs37.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
18549
+ import_fs38.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
18187
18550
  console.log(import_chalk16.default.green(`\u2705 Mode updated: ${chosenMode}`));
18188
18551
  } else {
18189
18552
  console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -18196,9 +18559,9 @@ function registerInitCommand(program2) {
18196
18559
  ...DEFAULT_CONFIG,
18197
18560
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
18198
18561
  };
18199
- const dir = import_path38.default.dirname(configPath);
18200
- if (!import_fs37.default.existsSync(dir)) import_fs37.default.mkdirSync(dir, { recursive: true });
18201
- import_fs37.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
18562
+ const dir = import_path39.default.dirname(configPath);
18563
+ if (!import_fs38.default.existsSync(dir)) import_fs38.default.mkdirSync(dir, { recursive: true });
18564
+ import_fs38.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
18202
18565
  console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath}`));
18203
18566
  console.log(import_chalk16.default.gray(` Mode: ${chosenMode}`));
18204
18567
  }
@@ -18296,7 +18659,7 @@ function registerInitCommand(program2) {
18296
18659
  }
18297
18660
 
18298
18661
  // src/cli/commands/undo.ts
18299
- var import_path39 = __toESM(require("path"));
18662
+ var import_path40 = __toESM(require("path"));
18300
18663
  var import_chalk18 = __toESM(require("chalk"));
18301
18664
 
18302
18665
  // src/tui/undo-navigator.ts
@@ -18455,7 +18818,7 @@ function findMatchingCwd(startDir, history) {
18455
18818
  let dir = startDir;
18456
18819
  while (true) {
18457
18820
  if (cwds.has(dir)) return dir;
18458
- const parent = import_path39.default.dirname(dir);
18821
+ const parent = import_path40.default.dirname(dir);
18459
18822
  if (parent === dir) return null;
18460
18823
  dir = parent;
18461
18824
  }
@@ -18588,90 +18951,7 @@ var import_child_process10 = require("child_process");
18588
18951
  var import_execa3 = require("execa");
18589
18952
  init_orchestrator();
18590
18953
  init_provenance();
18591
-
18592
- // src/mcp-pin.ts
18593
- var import_fs38 = __toESM(require("fs"));
18594
- var import_path40 = __toESM(require("path"));
18595
- var import_os34 = __toESM(require("os"));
18596
- var import_crypto10 = __toESM(require("crypto"));
18597
- function getPinsFilePath2() {
18598
- return import_path40.default.join(import_os34.default.homedir(), ".node9", "mcp-pins.json");
18599
- }
18600
- function hashToolDefinitions(tools) {
18601
- const sorted = [...tools].sort((a, b) => {
18602
- const nameA = a.name ?? "";
18603
- const nameB = b.name ?? "";
18604
- return nameA.localeCompare(nameB);
18605
- });
18606
- const canonical = JSON.stringify(sorted);
18607
- return import_crypto10.default.createHash("sha256").update(canonical).digest("hex");
18608
- }
18609
- function getServerKey(upstreamCommand) {
18610
- return import_crypto10.default.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
18611
- }
18612
- function readMcpPinsSafe() {
18613
- const filePath = getPinsFilePath2();
18614
- try {
18615
- const raw = import_fs38.default.readFileSync(filePath, "utf-8");
18616
- if (!raw.trim()) {
18617
- return { ok: false, reason: "corrupt", detail: "empty file" };
18618
- }
18619
- const parsed = JSON.parse(raw);
18620
- if (!parsed.servers || typeof parsed.servers !== "object" || Array.isArray(parsed.servers)) {
18621
- return { ok: false, reason: "corrupt", detail: "invalid structure: missing servers object" };
18622
- }
18623
- return { ok: true, pins: { servers: parsed.servers } };
18624
- } catch (err2) {
18625
- if (err2.code === "ENOENT") {
18626
- return { ok: false, reason: "missing" };
18627
- }
18628
- return { ok: false, reason: "corrupt", detail: String(err2) };
18629
- }
18630
- }
18631
- function readMcpPins() {
18632
- const result = readMcpPinsSafe();
18633
- if (result.ok) return result.pins;
18634
- if (result.reason === "missing") return { servers: {} };
18635
- throw new Error(`[node9] MCP pin file is corrupt: ${result.detail}`);
18636
- }
18637
- function writeMcpPins(data) {
18638
- const filePath = getPinsFilePath2();
18639
- import_fs38.default.mkdirSync(import_path40.default.dirname(filePath), { recursive: true });
18640
- const tmp = `${filePath}.${import_crypto10.default.randomBytes(6).toString("hex")}.tmp`;
18641
- import_fs38.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
18642
- import_fs38.default.renameSync(tmp, filePath);
18643
- }
18644
- function checkPin(serverKey, currentHash) {
18645
- const result = readMcpPinsSafe();
18646
- if (!result.ok) {
18647
- if (result.reason === "missing") return "new";
18648
- return "corrupt";
18649
- }
18650
- const entry = result.pins.servers[serverKey];
18651
- if (!entry) return "new";
18652
- return entry.toolsHash === currentHash ? "match" : "mismatch";
18653
- }
18654
- function updatePin(serverKey, label, toolsHash, toolNames) {
18655
- const pins = readMcpPins();
18656
- pins.servers[serverKey] = {
18657
- label,
18658
- toolsHash,
18659
- toolNames,
18660
- toolCount: toolNames.length,
18661
- pinnedAt: (/* @__PURE__ */ new Date()).toISOString()
18662
- };
18663
- writeMcpPins(pins);
18664
- }
18665
- function removePin2(serverKey) {
18666
- const pins = readMcpPins();
18667
- delete pins.servers[serverKey];
18668
- writeMcpPins(pins);
18669
- }
18670
- function clearAllPins2() {
18671
- writeMcpPins({ servers: {} });
18672
- }
18673
-
18674
- // src/mcp-gateway/index.ts
18954
+ init_mcp_pin();
18675
18955
  init_mcp_tools();
18676
18956
  init_daemon();
18677
18957
  function sanitize4(value) {
@@ -18733,6 +19013,7 @@ function tokenize4(cmd) {
18733
19013
  return tokens;
18734
19014
  }
18735
19015
  async function runMcpGateway(upstreamCommand) {
19016
+ const gatewayCwd = process.cwd();
18736
19017
  const commandParts = tokenize4(upstreamCommand);
18737
19018
  const cmd = commandParts[0];
18738
19019
  const cmdArgs = commandParts.slice(1);
@@ -18955,7 +19236,7 @@ async function runMcpGateway(upstreamCommand) {
18955
19236
  if (parsed.result && Array.isArray(parsed.result.tools)) {
18956
19237
  const tools = parsed.result.tools || [];
18957
19238
  const currentHash = hashToolDefinitions(tools);
18958
- const pinStatus = checkPin(serverKey, currentHash);
19239
+ const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
18959
19240
  const token = getInternalToken();
18960
19241
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
18961
19242
  const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
@@ -19840,26 +20121,45 @@ function registerTrustCommand(program2) {
19840
20121
 
19841
20122
  // src/cli/commands/mcp-pin.ts
19842
20123
  var import_chalk21 = __toESM(require("chalk"));
20124
+ init_mcp_pin();
20125
+ var import_fs40 = __toESM(require("fs"));
19843
20126
  function registerMcpPinCommand(program2) {
19844
20127
  const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
19845
20128
  const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
19846
20129
  pinSubCmd.command("list").description("Show all pinned MCP servers and their tool definition hashes").action(() => {
19847
- const result = readMcpPinsSafe();
19848
- if (!result.ok) {
19849
- if (result.reason === "missing") {
19850
- console.log(import_chalk21.default.gray("\nNo MCP servers are pinned yet."));
19851
- console.log(
19852
- import_chalk21.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
19853
- );
19854
- return;
20130
+ const found = findPinsFilePath(process.cwd());
20131
+ const homeResult = readMcpPinsSafe();
20132
+ let repoEntries = {};
20133
+ let repoCorrupt = false;
20134
+ if (found.source === "repo") {
20135
+ try {
20136
+ const raw = import_fs40.default.readFileSync(found.path, "utf-8");
20137
+ const parsed = JSON.parse(raw);
20138
+ repoEntries = parsed.servers ?? {};
20139
+ } catch {
20140
+ repoCorrupt = true;
19855
20141
  }
20142
+ }
20143
+ if (repoCorrupt) {
19856
20144
  console.error(import_chalk21.default.red(`
19857
- \u274C Pin file is corrupt: ${result.detail}`));
20145
+ \u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
20146
+ process.exit(1);
20147
+ }
20148
+ if (!homeResult.ok && homeResult.reason === "corrupt") {
20149
+ console.error(import_chalk21.default.red(`
20150
+ \u274C Home pin file is corrupt: ${homeResult.detail}`));
19858
20151
  console.error(import_chalk21.default.yellow(" Run: node9 mcp pin reset\n"));
19859
20152
  process.exit(1);
19860
20153
  }
19861
- const entries = Object.entries(result.pins.servers);
19862
- if (entries.length === 0) {
20154
+ const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
20155
+ const merged = /* @__PURE__ */ new Map();
20156
+ for (const [key, entry] of Object.entries(homeEntries)) {
20157
+ merged.set(key, { entry, source: "home" });
20158
+ }
20159
+ for (const [key, entry] of Object.entries(repoEntries)) {
20160
+ merged.set(key, { entry, source: "repo" });
20161
+ }
20162
+ if (merged.size === 0) {
19863
20163
  console.log(import_chalk21.default.gray("\nNo MCP servers are pinned yet."));
19864
20164
  console.log(
19865
20165
  import_chalk21.default.gray("Pins are created automatically when the MCP gateway first connects.\n")
@@ -19867,13 +20167,47 @@ function registerMcpPinCommand(program2) {
19867
20167
  return;
19868
20168
  }
19869
20169
  console.log(import_chalk21.default.bold("\n\u{1F512} Pinned MCP Servers\n"));
19870
- for (const [key, entry] of entries) {
19871
- console.log(` ${import_chalk21.default.cyan(key)} ${import_chalk21.default.gray(entry.label)}`);
20170
+ const showSource = found.source === "repo";
20171
+ for (const [key, { entry, source }] of merged) {
20172
+ const tag = showSource ? ` ${import_chalk21.default.yellow(`[${source}]`)}` : "";
20173
+ console.log(` ${import_chalk21.default.cyan(key)}${tag} ${import_chalk21.default.gray(entry.label)}`);
19872
20174
  console.log(` Tools (${entry.toolCount}): ${import_chalk21.default.white(entry.toolNames.join(", "))}`);
19873
20175
  console.log(` Hash: ${import_chalk21.default.gray(entry.toolsHash.slice(0, 16))}...`);
19874
20176
  console.log(` Pinned: ${import_chalk21.default.gray(entry.pinnedAt)}`);
19875
20177
  console.log("");
19876
20178
  }
20179
+ if (showSource) {
20180
+ console.log(import_chalk21.default.gray(` [repo] entries come from ${found.path}`));
20181
+ console.log(import_chalk21.default.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
20182
+ }
20183
+ });
20184
+ pinSubCmd.command("promote <serverKey>").description(
20185
+ "Copy a pin from ~/.node9/mcp-pins.json into <repo>/.node9/mcp-pins.json so teammates share the same vetted baseline"
20186
+ ).action((serverKey) => {
20187
+ try {
20188
+ const { repoPath, created } = promotePin(serverKey, process.cwd());
20189
+ if (created) {
20190
+ console.log(
20191
+ import_chalk21.default.green(
20192
+ `
20193
+ \u2705 Created ${repoPath} with the promoted pin for ${import_chalk21.default.cyan(serverKey)}.`
20194
+ )
20195
+ );
20196
+ } else {
20197
+ console.log(import_chalk21.default.green(`
20198
+ \u2705 Promoted ${import_chalk21.default.cyan(serverKey)} into ${repoPath}.`));
20199
+ }
20200
+ console.log(import_chalk21.default.gray(" Review the change and commit it:"));
20201
+ console.log(import_chalk21.default.cyan(` git add ${repoPath}`));
20202
+ console.log(import_chalk21.default.cyan(` git commit -m "pin ${serverKey} (node9)"`));
20203
+ console.log("");
20204
+ } catch (err2) {
20205
+ const msg = err2 instanceof Error ? err2.message : String(err2);
20206
+ console.error(import_chalk21.default.red(`
20207
+ \u274C ${msg}
20208
+ `));
20209
+ process.exit(1);
20210
+ }
19877
20211
  });
19878
20212
  pinSubCmd.command("update <serverKey>").description(
19879
20213
  "Remove a pin so the next gateway connection re-pins with current tool definitions"
@@ -19895,7 +20229,7 @@ function registerMcpPinCommand(program2) {
19895
20229
  process.exit(1);
19896
20230
  }
19897
20231
  const label = pins.servers[serverKey].label;
19898
- removePin2(serverKey);
20232
+ removePin(serverKey);
19899
20233
  console.log(import_chalk21.default.green(`
19900
20234
  \u{1F513} Pin removed for ${import_chalk21.default.cyan(serverKey)}`));
19901
20235
  console.log(import_chalk21.default.gray(` Server: ${label}`));
@@ -19908,7 +20242,7 @@ function registerMcpPinCommand(program2) {
19908
20242
  return;
19909
20243
  }
19910
20244
  const count = result.ok ? Object.keys(result.pins.servers).length : "?";
19911
- clearAllPins2();
20245
+ clearAllPins();
19912
20246
  console.log(import_chalk21.default.green(`
19913
20247
  \u{1F513} Cleared ${count} MCP pin(s).`));
19914
20248
  console.log(import_chalk21.default.gray(" Next connection to each server will re-pin.\n"));
@@ -20091,7 +20425,7 @@ init_scan();
20091
20425
 
20092
20426
  // src/cli/commands/sessions.ts
20093
20427
  var import_chalk24 = __toESM(require("chalk"));
20094
- var import_fs40 = __toESM(require("fs"));
20428
+ var import_fs41 = __toESM(require("fs"));
20095
20429
  var import_path42 = __toESM(require("path"));
20096
20430
  var import_os36 = __toESM(require("os"));
20097
20431
  var CLAUDE_PRICING3 = {
@@ -20209,7 +20543,7 @@ function loadAuditEntries(auditPath) {
20209
20543
  const aPath = auditPath ?? import_path42.default.join(import_os36.default.homedir(), ".node9", "audit.log");
20210
20544
  let raw;
20211
20545
  try {
20212
- raw = import_fs40.default.readFileSync(aPath, "utf-8");
20546
+ raw = import_fs41.default.readFileSync(aPath, "utf-8");
20213
20547
  } catch {
20214
20548
  return [];
20215
20549
  }
@@ -20246,7 +20580,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
20246
20580
  }
20247
20581
  function buildGeminiSessions(days, allAuditEntries) {
20248
20582
  const tmpDir = import_path42.default.join(import_os36.default.homedir(), ".gemini", "tmp");
20249
- if (!import_fs40.default.existsSync(tmpDir)) return [];
20583
+ if (!import_fs41.default.existsSync(tmpDir)) return [];
20250
20584
  const cutoff = days !== null ? (() => {
20251
20585
  const d = /* @__PURE__ */ new Date();
20252
20586
  d.setDate(d.getDate() - days);
@@ -20255,7 +20589,7 @@ function buildGeminiSessions(days, allAuditEntries) {
20255
20589
  })() : null;
20256
20590
  let slugDirs;
20257
20591
  try {
20258
- slugDirs = import_fs40.default.readdirSync(tmpDir);
20592
+ slugDirs = import_fs41.default.readdirSync(tmpDir);
20259
20593
  } catch {
20260
20594
  return [];
20261
20595
  }
@@ -20263,27 +20597,27 @@ function buildGeminiSessions(days, allAuditEntries) {
20263
20597
  for (const slug of slugDirs) {
20264
20598
  const slugPath = import_path42.default.join(tmpDir, slug);
20265
20599
  try {
20266
- if (!import_fs40.default.statSync(slugPath).isDirectory()) continue;
20600
+ if (!import_fs41.default.statSync(slugPath).isDirectory()) continue;
20267
20601
  } catch {
20268
20602
  continue;
20269
20603
  }
20270
20604
  let projectRoot = import_path42.default.join(import_os36.default.homedir(), slug);
20271
20605
  try {
20272
- projectRoot = import_fs40.default.readFileSync(import_path42.default.join(slugPath, ".project_root"), "utf-8").trim();
20606
+ projectRoot = import_fs41.default.readFileSync(import_path42.default.join(slugPath, ".project_root"), "utf-8").trim();
20273
20607
  } catch {
20274
20608
  }
20275
20609
  const chatsDir = import_path42.default.join(slugPath, "chats");
20276
- if (!import_fs40.default.existsSync(chatsDir)) continue;
20610
+ if (!import_fs41.default.existsSync(chatsDir)) continue;
20277
20611
  let chatFiles;
20278
20612
  try {
20279
- chatFiles = import_fs40.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
20613
+ chatFiles = import_fs41.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
20280
20614
  } catch {
20281
20615
  continue;
20282
20616
  }
20283
20617
  for (const chatFile of chatFiles) {
20284
20618
  let raw;
20285
20619
  try {
20286
- raw = import_fs40.default.readFileSync(import_path42.default.join(chatsDir, chatFile), "utf-8");
20620
+ raw = import_fs41.default.readFileSync(import_path42.default.join(chatsDir, chatFile), "utf-8");
20287
20621
  } catch {
20288
20622
  continue;
20289
20623
  }
@@ -20364,7 +20698,7 @@ function buildGeminiSessions(days, allAuditEntries) {
20364
20698
  }
20365
20699
  function buildCodexSessions(days, allAuditEntries) {
20366
20700
  const sessionsBase = import_path42.default.join(import_os36.default.homedir(), ".codex", "sessions");
20367
- if (!import_fs40.default.existsSync(sessionsBase)) return [];
20701
+ if (!import_fs41.default.existsSync(sessionsBase)) return [];
20368
20702
  const cutoff = days !== null ? (() => {
20369
20703
  const d = /* @__PURE__ */ new Date();
20370
20704
  d.setDate(d.getDate() - days);
@@ -20373,28 +20707,28 @@ function buildCodexSessions(days, allAuditEntries) {
20373
20707
  })() : null;
20374
20708
  const jsonlFiles = [];
20375
20709
  try {
20376
- for (const year of import_fs40.default.readdirSync(sessionsBase)) {
20710
+ for (const year of import_fs41.default.readdirSync(sessionsBase)) {
20377
20711
  const yearPath = import_path42.default.join(sessionsBase, year);
20378
20712
  try {
20379
- if (!import_fs40.default.statSync(yearPath).isDirectory()) continue;
20713
+ if (!import_fs41.default.statSync(yearPath).isDirectory()) continue;
20380
20714
  } catch {
20381
20715
  continue;
20382
20716
  }
20383
- for (const month of import_fs40.default.readdirSync(yearPath)) {
20717
+ for (const month of import_fs41.default.readdirSync(yearPath)) {
20384
20718
  const monthPath = import_path42.default.join(yearPath, month);
20385
20719
  try {
20386
- if (!import_fs40.default.statSync(monthPath).isDirectory()) continue;
20720
+ if (!import_fs41.default.statSync(monthPath).isDirectory()) continue;
20387
20721
  } catch {
20388
20722
  continue;
20389
20723
  }
20390
- for (const day of import_fs40.default.readdirSync(monthPath)) {
20724
+ for (const day of import_fs41.default.readdirSync(monthPath)) {
20391
20725
  const dayPath = import_path42.default.join(monthPath, day);
20392
20726
  try {
20393
- if (!import_fs40.default.statSync(dayPath).isDirectory()) continue;
20727
+ if (!import_fs41.default.statSync(dayPath).isDirectory()) continue;
20394
20728
  } catch {
20395
20729
  continue;
20396
20730
  }
20397
- for (const file of import_fs40.default.readdirSync(dayPath)) {
20731
+ for (const file of import_fs41.default.readdirSync(dayPath)) {
20398
20732
  if (file.endsWith(".jsonl")) jsonlFiles.push(import_path42.default.join(dayPath, file));
20399
20733
  }
20400
20734
  }
@@ -20407,7 +20741,7 @@ function buildCodexSessions(days, allAuditEntries) {
20407
20741
  for (const filePath of jsonlFiles) {
20408
20742
  let lines;
20409
20743
  try {
20410
- lines = import_fs40.default.readFileSync(filePath, "utf-8").split("\n");
20744
+ lines = import_fs41.default.readFileSync(filePath, "utf-8").split("\n");
20411
20745
  } catch {
20412
20746
  continue;
20413
20747
  }
@@ -20488,7 +20822,7 @@ function buildSessions(days, historyPath) {
20488
20822
  const hPath = historyPath ?? import_path42.default.join(import_os36.default.homedir(), ".claude", "history.jsonl");
20489
20823
  let historyRaw;
20490
20824
  try {
20491
- historyRaw = import_fs40.default.readFileSync(hPath, "utf-8");
20825
+ historyRaw = import_fs41.default.readFileSync(hPath, "utf-8");
20492
20826
  } catch {
20493
20827
  return [];
20494
20828
  }
@@ -20513,7 +20847,7 @@ function buildSessions(days, historyPath) {
20513
20847
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
20514
20848
  let sessionLines = [];
20515
20849
  try {
20516
- sessionLines = import_fs40.default.readFileSync(jsonlFile, "utf-8").split("\n");
20850
+ sessionLines = import_fs41.default.readFileSync(jsonlFile, "utf-8").split("\n");
20517
20851
  } catch {
20518
20852
  }
20519
20853
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -20780,7 +21114,7 @@ function registerSessionsCommand(program2) {
20780
21114
  console.log(import_chalk24.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk24.default.dim(" \u2014 what your AI agent did"));
20781
21115
  console.log("");
20782
21116
  const historyPath = import_path42.default.join(import_os36.default.homedir(), ".claude", "history.jsonl");
20783
- if (!import_fs40.default.existsSync(historyPath)) {
21117
+ if (!import_fs41.default.existsSync(historyPath)) {
20784
21118
  console.log(import_chalk24.default.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
20785
21119
  console.log(import_chalk24.default.gray(" Install Claude Code, run a few sessions, then try again.\n"));
20786
21120
  return;
@@ -20817,12 +21151,12 @@ function registerSessionsCommand(program2) {
20817
21151
 
20818
21152
  // src/cli/commands/skill-pin.ts
20819
21153
  var import_chalk25 = __toESM(require("chalk"));
20820
- var import_fs41 = __toESM(require("fs"));
21154
+ var import_fs42 = __toESM(require("fs"));
20821
21155
  var import_os37 = __toESM(require("os"));
20822
21156
  var import_path43 = __toESM(require("path"));
20823
21157
  function wipeSkillSessions() {
20824
21158
  try {
20825
- import_fs41.default.rmSync(import_path43.default.join(import_os37.default.homedir(), ".node9", "skill-sessions"), {
21159
+ import_fs42.default.rmSync(import_path43.default.join(import_os37.default.homedir(), ".node9", "skill-sessions"), {
20826
21160
  recursive: true,
20827
21161
  force: true
20828
21162
  });
@@ -20880,7 +21214,7 @@ function registerSkillPinCommand(program2) {
20880
21214
  process.exit(1);
20881
21215
  }
20882
21216
  const rootPath = pins.roots[rootKey].rootPath;
20883
- removePin(rootKey);
21217
+ removePin2(rootKey);
20884
21218
  wipeSkillSessions();
20885
21219
  console.log(import_chalk25.default.green(`
20886
21220
  \u{1F513} Pin removed for ${import_chalk25.default.cyan(rootKey)}`));
@@ -20895,7 +21229,7 @@ function registerSkillPinCommand(program2) {
20895
21229
  return;
20896
21230
  }
20897
21231
  const count = result.ok ? Object.keys(result.pins.roots).length : "?";
20898
- clearAllPins();
21232
+ clearAllPins2();
20899
21233
  wipeSkillSessions();
20900
21234
  console.log(import_chalk25.default.green(`
20901
21235
  \u{1F513} Cleared ${count} skill pin(s).`));
@@ -20904,15 +21238,15 @@ function registerSkillPinCommand(program2) {
20904
21238
  }
20905
21239
 
20906
21240
  // src/cli/commands/decisions.ts
20907
- var import_fs42 = __toESM(require("fs"));
21241
+ var import_fs43 = __toESM(require("fs"));
20908
21242
  var import_os38 = __toESM(require("os"));
20909
21243
  var import_path44 = __toESM(require("path"));
20910
21244
  var import_chalk26 = __toESM(require("chalk"));
20911
21245
  var DECISIONS_FILE2 = import_path44.default.join(import_os38.default.homedir(), ".node9", "decisions.json");
20912
21246
  function readDecisions() {
20913
21247
  try {
20914
- if (!import_fs42.default.existsSync(DECISIONS_FILE2)) return {};
20915
- const raw = import_fs42.default.readFileSync(DECISIONS_FILE2, "utf-8");
21248
+ if (!import_fs43.default.existsSync(DECISIONS_FILE2)) return {};
21249
+ const raw = import_fs43.default.readFileSync(DECISIONS_FILE2, "utf-8");
20916
21250
  const parsed = JSON.parse(raw);
20917
21251
  const out = {};
20918
21252
  for (const [k, v] of Object.entries(parsed)) {
@@ -20925,10 +21259,10 @@ function readDecisions() {
20925
21259
  }
20926
21260
  function writeDecisions(d) {
20927
21261
  const dir = import_path44.default.dirname(DECISIONS_FILE2);
20928
- if (!import_fs42.default.existsSync(dir)) import_fs42.default.mkdirSync(dir, { recursive: true });
21262
+ if (!import_fs43.default.existsSync(dir)) import_fs43.default.mkdirSync(dir, { recursive: true });
20929
21263
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
20930
- import_fs42.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
20931
- import_fs42.default.renameSync(tmp, DECISIONS_FILE2);
21264
+ import_fs43.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
21265
+ import_fs43.default.renameSync(tmp, DECISIONS_FILE2);
20932
21266
  }
20933
21267
  function registerDecisionsCommand(program2) {
20934
21268
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -20985,7 +21319,7 @@ Persistent decisions (${entries.length})
20985
21319
 
20986
21320
  // src/cli/commands/dlp.ts
20987
21321
  var import_chalk27 = __toESM(require("chalk"));
20988
- var import_fs43 = __toESM(require("fs"));
21322
+ var import_fs44 = __toESM(require("fs"));
20989
21323
  var import_path45 = __toESM(require("path"));
20990
21324
  var import_os39 = __toESM(require("os"));
20991
21325
  var AUDIT_LOG = import_path45.default.join(import_os39.default.homedir(), ".node9", "audit.log");
@@ -20996,7 +21330,7 @@ function stripAnsi(s) {
20996
21330
  }
20997
21331
  function loadResolved() {
20998
21332
  try {
20999
- const raw = JSON.parse(import_fs43.default.readFileSync(RESOLVED_FILE, "utf-8"));
21333
+ const raw = JSON.parse(import_fs44.default.readFileSync(RESOLVED_FILE, "utf-8"));
21000
21334
  return new Set(raw);
21001
21335
  } catch {
21002
21336
  return /* @__PURE__ */ new Set();
@@ -21004,13 +21338,13 @@ function loadResolved() {
21004
21338
  }
21005
21339
  function saveResolved(resolved) {
21006
21340
  try {
21007
- import_fs43.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
21341
+ import_fs44.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
21008
21342
  } catch {
21009
21343
  }
21010
21344
  }
21011
21345
  function loadDlpFindings() {
21012
- if (!import_fs43.default.existsSync(AUDIT_LOG)) return [];
21013
- return import_fs43.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
21346
+ if (!import_fs44.default.existsSync(AUDIT_LOG)) return [];
21347
+ return import_fs44.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
21014
21348
  if (!line.trim()) return [];
21015
21349
  try {
21016
21350
  const e = JSON.parse(line);
@@ -21108,14 +21442,14 @@ function registerDlpCommand(program2) {
21108
21442
 
21109
21443
  // src/cli/commands/mask.ts
21110
21444
  var import_chalk28 = __toESM(require("chalk"));
21111
- var import_fs44 = __toESM(require("fs"));
21445
+ var import_fs45 = __toESM(require("fs"));
21112
21446
  var import_path46 = __toESM(require("path"));
21113
21447
  var import_os40 = __toESM(require("os"));
21114
21448
  init_dlp();
21115
21449
  function findJsonlFiles(dir) {
21116
21450
  const results = [];
21117
- if (!import_fs44.default.existsSync(dir)) return results;
21118
- for (const entry of import_fs44.default.readdirSync(dir, { withFileTypes: true })) {
21451
+ if (!import_fs45.default.existsSync(dir)) return results;
21452
+ for (const entry of import_fs45.default.readdirSync(dir, { withFileTypes: true })) {
21119
21453
  const full = import_path46.default.join(dir, entry.name);
21120
21454
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
21121
21455
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
@@ -21159,7 +21493,7 @@ function redactJson(obj) {
21159
21493
  function processFile(filePath, dryRun) {
21160
21494
  let raw;
21161
21495
  try {
21162
- raw = import_fs44.default.readFileSync(filePath, "utf-8");
21496
+ raw = import_fs45.default.readFileSync(filePath, "utf-8");
21163
21497
  } catch {
21164
21498
  return { redactedLines: 0, patterns: [] };
21165
21499
  }
@@ -21191,14 +21525,14 @@ function processFile(filePath, dryRun) {
21191
21525
  }
21192
21526
  }
21193
21527
  if (!dryRun && redactedLines > 0) {
21194
- import_fs44.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
21528
+ import_fs45.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
21195
21529
  }
21196
21530
  return { redactedLines, patterns };
21197
21531
  }
21198
21532
  function processJsonFile(filePath, dryRun) {
21199
21533
  let raw;
21200
21534
  try {
21201
- raw = import_fs44.default.readFileSync(filePath, "utf-8");
21535
+ raw = import_fs45.default.readFileSync(filePath, "utf-8");
21202
21536
  } catch {
21203
21537
  return { redactedLines: 0, patterns: [] };
21204
21538
  }
@@ -21211,14 +21545,14 @@ function processJsonFile(filePath, dryRun) {
21211
21545
  const { value, modified, found } = redactJson(parsed);
21212
21546
  if (!modified) return { redactedLines: 0, patterns: [] };
21213
21547
  if (!dryRun) {
21214
- import_fs44.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
21548
+ import_fs45.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
21215
21549
  }
21216
21550
  return { redactedLines: 1, patterns: found };
21217
21551
  }
21218
21552
  function findJsonFiles(dir) {
21219
21553
  const results = [];
21220
- if (!import_fs44.default.existsSync(dir)) return results;
21221
- for (const entry of import_fs44.default.readdirSync(dir, { withFileTypes: true })) {
21554
+ if (!import_fs45.default.existsSync(dir)) return results;
21555
+ for (const entry of import_fs45.default.readdirSync(dir, { withFileTypes: true })) {
21222
21556
  const full = import_path46.default.join(dir, entry.name);
21223
21557
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
21224
21558
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
@@ -21238,7 +21572,7 @@ function registerMaskCommand(program2) {
21238
21572
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
21239
21573
  const filtered = cutoff ? allFiles.filter((f) => {
21240
21574
  try {
21241
- return import_fs44.default.statSync(f.path).mtime >= cutoff;
21575
+ return import_fs45.default.statSync(f.path).mtime >= cutoff;
21242
21576
  } catch {
21243
21577
  return false;
21244
21578
  }
@@ -21294,20 +21628,20 @@ function registerMaskCommand(program2) {
21294
21628
  // src/cli.ts
21295
21629
  init_blast();
21296
21630
  var { version } = JSON.parse(
21297
- import_fs47.default.readFileSync(import_path49.default.join(__dirname, "../package.json"), "utf-8")
21631
+ import_fs48.default.readFileSync(import_path49.default.join(__dirname, "../package.json"), "utf-8")
21298
21632
  );
21299
21633
  var program = new import_commander.Command();
21300
21634
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
21301
21635
  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) => {
21302
21636
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
21303
21637
  const credPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "credentials.json");
21304
- if (!import_fs47.default.existsSync(import_path49.default.dirname(credPath)))
21305
- import_fs47.default.mkdirSync(import_path49.default.dirname(credPath), { recursive: true });
21638
+ if (!import_fs48.default.existsSync(import_path49.default.dirname(credPath)))
21639
+ import_fs48.default.mkdirSync(import_path49.default.dirname(credPath), { recursive: true });
21306
21640
  const profileName = options.profile || "default";
21307
21641
  let existingCreds = {};
21308
21642
  try {
21309
- if (import_fs47.default.existsSync(credPath)) {
21310
- const raw = JSON.parse(import_fs47.default.readFileSync(credPath, "utf-8"));
21643
+ if (import_fs48.default.existsSync(credPath)) {
21644
+ const raw = JSON.parse(import_fs48.default.readFileSync(credPath, "utf-8"));
21311
21645
  if (raw.apiKey) {
21312
21646
  existingCreds = {
21313
21647
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -21319,13 +21653,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
21319
21653
  } catch {
21320
21654
  }
21321
21655
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
21322
- import_fs47.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
21656
+ import_fs48.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
21323
21657
  if (profileName === "default") {
21324
21658
  const configPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "config.json");
21325
21659
  let config = {};
21326
21660
  try {
21327
- if (import_fs47.default.existsSync(configPath))
21328
- config = JSON.parse(import_fs47.default.readFileSync(configPath, "utf-8"));
21661
+ if (import_fs48.default.existsSync(configPath))
21662
+ config = JSON.parse(import_fs48.default.readFileSync(configPath, "utf-8"));
21329
21663
  } catch {
21330
21664
  }
21331
21665
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -21340,9 +21674,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
21340
21674
  approvers.cloud = false;
21341
21675
  }
21342
21676
  s.approvers = approvers;
21343
- if (!import_fs47.default.existsSync(import_path49.default.dirname(configPath)))
21344
- import_fs47.default.mkdirSync(import_path49.default.dirname(configPath), { recursive: true });
21345
- import_fs47.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
21677
+ if (!import_fs48.default.existsSync(import_path49.default.dirname(configPath)))
21678
+ import_fs48.default.mkdirSync(import_path49.default.dirname(configPath), { recursive: true });
21679
+ import_fs48.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
21346
21680
  }
21347
21681
  if (options.profile && profileName !== "default") {
21348
21682
  console.log(import_chalk30.default.green(`\u2705 Profile "${profileName}" saved`));
@@ -21480,14 +21814,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
21480
21814
  }
21481
21815
  if (options.purge) {
21482
21816
  const node9Dir = import_path49.default.join(import_os43.default.homedir(), ".node9");
21483
- if (import_fs47.default.existsSync(node9Dir)) {
21817
+ if (import_fs48.default.existsSync(node9Dir)) {
21484
21818
  const confirmed = await (0, import_prompts2.confirm)({
21485
21819
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
21486
21820
  default: false
21487
21821
  });
21488
21822
  if (confirmed) {
21489
- import_fs47.default.rmSync(node9Dir, { recursive: true });
21490
- if (import_fs47.default.existsSync(node9Dir)) {
21823
+ import_fs48.default.rmSync(node9Dir, { recursive: true });
21824
+ if (import_fs48.default.existsSync(node9Dir)) {
21491
21825
  console.error(
21492
21826
  import_chalk30.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
21493
21827
  );
@@ -21642,12 +21976,12 @@ Run "node9 addto claude" to register it as the statusLine.`
21642
21976
  if (subcommand === "debug") {
21643
21977
  const flagFile = import_path49.default.join(import_os43.default.homedir(), ".node9", "hud-debug");
21644
21978
  if (state === "on") {
21645
- import_fs47.default.mkdirSync(import_path49.default.dirname(flagFile), { recursive: true });
21646
- import_fs47.default.writeFileSync(flagFile, "");
21979
+ import_fs48.default.mkdirSync(import_path49.default.dirname(flagFile), { recursive: true });
21980
+ import_fs48.default.writeFileSync(flagFile, "");
21647
21981
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
21648
21982
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
21649
21983
  } else if (state === "off") {
21650
- if (import_fs47.default.existsSync(flagFile)) import_fs47.default.unlinkSync(flagFile);
21984
+ if (import_fs48.default.existsSync(flagFile)) import_fs48.default.unlinkSync(flagFile);
21651
21985
  console.log("HUD debug logging disabled.");
21652
21986
  } else {
21653
21987
  console.error("Usage: node9 hud debug on|off");
@@ -21763,7 +22097,7 @@ if (process.argv[2] !== "daemon") {
21763
22097
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
21764
22098
  const logPath = import_path49.default.join(import_os43.default.homedir(), ".node9", "hook-debug.log");
21765
22099
  const msg = reason instanceof Error ? reason.message : String(reason);
21766
- import_fs47.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
22100
+ import_fs48.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
21767
22101
  `);
21768
22102
  }
21769
22103
  process.exit(0);