@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.mjs CHANGED
@@ -6281,10 +6281,167 @@ var init_core = __esm({
6281
6281
  }
6282
6282
  });
6283
6283
 
6284
- // src/setup.ts
6284
+ // src/mcp-pin.ts
6285
6285
  import fs12 from "fs";
6286
6286
  import path14 from "path";
6287
6287
  import os11 from "os";
6288
+ import crypto3 from "crypto";
6289
+ function getHomePinsFilePath() {
6290
+ return path14.join(os11.homedir(), ".node9", "mcp-pins.json");
6291
+ }
6292
+ function getPinsFilePath() {
6293
+ return getHomePinsFilePath();
6294
+ }
6295
+ function findPinsFilePath(cwd) {
6296
+ const homeDir2 = os11.homedir();
6297
+ const homePath = getHomePinsFilePath();
6298
+ let current = path14.resolve(cwd ?? process.cwd());
6299
+ while (true) {
6300
+ if (current === homeDir2) break;
6301
+ const candidate = path14.join(current, ".node9", "mcp-pins.json");
6302
+ if (fs12.existsSync(candidate)) {
6303
+ return { path: candidate, source: "repo" };
6304
+ }
6305
+ const next = path14.dirname(current);
6306
+ if (next === current) break;
6307
+ current = next;
6308
+ }
6309
+ return { path: homePath, source: "home" };
6310
+ }
6311
+ function hashToolDefinitions(tools) {
6312
+ const sorted = [...tools].sort((a, b) => {
6313
+ const nameA = a.name ?? "";
6314
+ const nameB = b.name ?? "";
6315
+ return nameA.localeCompare(nameB);
6316
+ });
6317
+ const canonical = JSON.stringify(sorted);
6318
+ return crypto3.createHash("sha256").update(canonical).digest("hex");
6319
+ }
6320
+ function getServerKey(upstreamCommand) {
6321
+ return crypto3.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
6322
+ }
6323
+ function readPinsFile(filePath) {
6324
+ try {
6325
+ const raw = fs12.readFileSync(filePath, "utf-8");
6326
+ if (!raw.trim()) {
6327
+ return { ok: false, reason: "corrupt", detail: "empty file" };
6328
+ }
6329
+ const parsed = JSON.parse(raw);
6330
+ if (!parsed.servers || typeof parsed.servers !== "object" || Array.isArray(parsed.servers)) {
6331
+ return { ok: false, reason: "corrupt", detail: "invalid structure: missing servers object" };
6332
+ }
6333
+ return { ok: true, pins: { servers: parsed.servers } };
6334
+ } catch (err2) {
6335
+ if (err2.code === "ENOENT") {
6336
+ return { ok: false, reason: "missing" };
6337
+ }
6338
+ return { ok: false, reason: "corrupt", detail: String(err2) };
6339
+ }
6340
+ }
6341
+ function readMcpPinsSafe() {
6342
+ return readPinsFile(getHomePinsFilePath());
6343
+ }
6344
+ function readMcpPins() {
6345
+ const result = readMcpPinsSafe();
6346
+ if (result.ok) return result.pins;
6347
+ if (result.reason === "missing") return { servers: {} };
6348
+ throw new Error(`[node9] MCP pin file is corrupt: ${result.detail}`);
6349
+ }
6350
+ function writePinsFile(filePath, data) {
6351
+ fs12.mkdirSync(path14.dirname(filePath), { recursive: true });
6352
+ const tmp = `${filePath}.${crypto3.randomBytes(6).toString("hex")}.tmp`;
6353
+ const isHome = filePath === getHomePinsFilePath();
6354
+ fs12.writeFileSync(tmp, JSON.stringify(data, null, 2), isHome ? { mode: 384 } : {});
6355
+ fs12.renameSync(tmp, filePath);
6356
+ }
6357
+ function writeMcpPins(data) {
6358
+ writePinsFile(getHomePinsFilePath(), data);
6359
+ }
6360
+ function seedMcpPinsIfMissing() {
6361
+ const filePath = getPinsFilePath();
6362
+ if (fs12.existsSync(filePath)) return;
6363
+ writeMcpPins({ servers: {} });
6364
+ }
6365
+ function checkPin(serverKey, currentHash, cwd) {
6366
+ const found = findPinsFilePath(cwd);
6367
+ let repoEntry;
6368
+ if (found.source === "repo") {
6369
+ const repoResult = readPinsFile(found.path);
6370
+ if (!repoResult.ok) {
6371
+ return "corrupt";
6372
+ }
6373
+ repoEntry = repoResult.pins.servers[serverKey];
6374
+ }
6375
+ if (repoEntry) {
6376
+ return repoEntry.toolsHash === currentHash ? "match" : "mismatch";
6377
+ }
6378
+ const homeResult = readPinsFile(getHomePinsFilePath());
6379
+ if (!homeResult.ok) {
6380
+ if (homeResult.reason === "missing") return "new";
6381
+ return "corrupt";
6382
+ }
6383
+ const homeEntry = homeResult.pins.servers[serverKey];
6384
+ if (!homeEntry) return "new";
6385
+ return homeEntry.toolsHash === currentHash ? "match" : "mismatch";
6386
+ }
6387
+ function updatePin(serverKey, label, toolsHash, toolNames) {
6388
+ const pins = readMcpPins();
6389
+ pins.servers[serverKey] = {
6390
+ label,
6391
+ toolsHash,
6392
+ toolNames,
6393
+ toolCount: toolNames.length,
6394
+ pinnedAt: (/* @__PURE__ */ new Date()).toISOString()
6395
+ };
6396
+ writeMcpPins(pins);
6397
+ }
6398
+ function removePin(serverKey) {
6399
+ const pins = readMcpPins();
6400
+ delete pins.servers[serverKey];
6401
+ writeMcpPins(pins);
6402
+ }
6403
+ function clearAllPins() {
6404
+ writeMcpPins({ servers: {} });
6405
+ }
6406
+ function promotePin(serverKey, cwd) {
6407
+ const homePins = readMcpPins();
6408
+ const homeEntry = homePins.servers[serverKey];
6409
+ if (!homeEntry) {
6410
+ throw new Error(
6411
+ `[node9] Server "${serverKey}" is not pinned in ~/.node9/mcp-pins.json. Run \`node9 mcp pin list\` to see what's pinned.`
6412
+ );
6413
+ }
6414
+ const found = findPinsFilePath(cwd);
6415
+ let repoPath;
6416
+ let repoPins;
6417
+ let created = false;
6418
+ if (found.source === "repo") {
6419
+ repoPath = found.path;
6420
+ const result = readPinsFile(repoPath);
6421
+ if (!result.ok) {
6422
+ const detail = result.reason === "corrupt" ? result.detail : "missing";
6423
+ throw new Error(`[node9] Repo pin file at ${repoPath} is unreadable: ${detail}`);
6424
+ }
6425
+ repoPins = result.pins;
6426
+ } else {
6427
+ repoPath = path14.join(cwd ?? process.cwd(), ".node9", "mcp-pins.json");
6428
+ repoPins = { servers: {} };
6429
+ created = true;
6430
+ }
6431
+ repoPins.servers[serverKey] = { ...homeEntry };
6432
+ writePinsFile(repoPath, repoPins);
6433
+ return { repoPath, created };
6434
+ }
6435
+ var init_mcp_pin = __esm({
6436
+ "src/mcp-pin.ts"() {
6437
+ "use strict";
6438
+ }
6439
+ });
6440
+
6441
+ // src/setup.ts
6442
+ import fs13 from "fs";
6443
+ import path15 from "path";
6444
+ import os12 from "os";
6288
6445
  import chalk from "chalk";
6289
6446
  import { confirm } from "@inquirer/prompts";
6290
6447
  import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
@@ -6312,36 +6469,36 @@ function isStaleHookCommand(command) {
6312
6469
  const tokens = command.split(/\s+/);
6313
6470
  for (const tok of tokens) {
6314
6471
  if (!tok.startsWith("/")) continue;
6315
- if (!fs12.existsSync(tok)) return true;
6472
+ if (!fs13.existsSync(tok)) return true;
6316
6473
  }
6317
6474
  return false;
6318
6475
  }
6319
6476
  function readJson(filePath) {
6320
6477
  try {
6321
- if (fs12.existsSync(filePath)) {
6322
- return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
6478
+ if (fs13.existsSync(filePath)) {
6479
+ return JSON.parse(fs13.readFileSync(filePath, "utf-8"));
6323
6480
  }
6324
6481
  } catch {
6325
6482
  }
6326
6483
  return null;
6327
6484
  }
6328
6485
  function writeJson(filePath, data) {
6329
- const dir = path14.dirname(filePath);
6330
- if (!fs12.existsSync(dir)) fs12.mkdirSync(dir, { recursive: true });
6331
- fs12.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
6486
+ const dir = path15.dirname(filePath);
6487
+ if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
6488
+ fs13.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
6332
6489
  }
6333
6490
  function isNode9Hook(cmd) {
6334
6491
  if (!cmd) return false;
6335
6492
  return /(?:^|[\s/\\])node9 (?:check|log)/.test(cmd) || /(?:^|[\s/\\])cli\.js (?:check|log)/.test(cmd);
6336
6493
  }
6337
6494
  function teardownClaude() {
6338
- const homeDir2 = os11.homedir();
6339
- const hooksPath = path14.join(homeDir2, ".claude", "settings.json");
6340
- const mcpPath = path14.join(homeDir2, ".claude", ".mcp.json");
6495
+ const homeDir2 = os12.homedir();
6496
+ const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
6497
+ const mcpPath = path15.join(homeDir2, ".claude", ".mcp.json");
6341
6498
  let changed = false;
6342
6499
  const settings = readJson(hooksPath);
6343
6500
  if (settings?.hooks) {
6344
- for (const event of ["PreToolUse", "PostToolUse"]) {
6501
+ for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
6345
6502
  const before = settings.hooks[event]?.length ?? 0;
6346
6503
  settings.hooks[event] = settings.hooks[event]?.filter(
6347
6504
  (m) => !m.hooks.some((h) => isNode9Hook(h.command))
@@ -6385,8 +6542,8 @@ function teardownClaude() {
6385
6542
  }
6386
6543
  }
6387
6544
  function teardownGemini() {
6388
- const homeDir2 = os11.homedir();
6389
- const settingsPath = path14.join(homeDir2, ".gemini", "settings.json");
6545
+ const homeDir2 = os12.homedir();
6546
+ const settingsPath = path15.join(homeDir2, ".gemini", "settings.json");
6390
6547
  const settings = readJson(settingsPath);
6391
6548
  if (!settings) {
6392
6549
  console.log(chalk.blue(" \u2139\uFE0F ~/.gemini/settings.json not found \u2014 nothing to remove"));
@@ -6429,8 +6586,8 @@ function teardownGemini() {
6429
6586
  }
6430
6587
  }
6431
6588
  function teardownCursor() {
6432
- const homeDir2 = os11.homedir();
6433
- const mcpPath = path14.join(homeDir2, ".cursor", "mcp.json");
6589
+ const homeDir2 = os12.homedir();
6590
+ const mcpPath = path15.join(homeDir2, ".cursor", "mcp.json");
6434
6591
  const mcpConfig = readJson(mcpPath);
6435
6592
  if (!mcpConfig?.mcpServers) {
6436
6593
  console.log(chalk.blue(" \u2139\uFE0F ~/.cursor/mcp.json not found \u2014 nothing to remove"));
@@ -6461,9 +6618,10 @@ function teardownCursor() {
6461
6618
  }
6462
6619
  }
6463
6620
  async function setupClaude() {
6464
- const homeDir2 = os11.homedir();
6465
- const mcpPath = path14.join(homeDir2, ".claude", ".mcp.json");
6466
- const hooksPath = path14.join(homeDir2, ".claude", "settings.json");
6621
+ seedMcpPinsIfMissing();
6622
+ const homeDir2 = os12.homedir();
6623
+ const mcpPath = path15.join(homeDir2, ".claude", ".mcp.json");
6624
+ const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
6467
6625
  const claudeConfig = readJson(mcpPath) ?? {};
6468
6626
  const settings = readJson(hooksPath) ?? {};
6469
6627
  const servers = claudeConfig.mcpServers ?? {};
@@ -6522,6 +6680,33 @@ async function setupClaude() {
6522
6680
  }
6523
6681
  }
6524
6682
  }
6683
+ const hasPromptHook = settings.hooks.UserPromptSubmit?.some(
6684
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
6685
+ );
6686
+ if (!hasPromptHook) {
6687
+ if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
6688
+ settings.hooks.UserPromptSubmit.push({
6689
+ matcher: ".*",
6690
+ hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
6691
+ });
6692
+ console.log(chalk.green(" \u2705 UserPromptSubmit hook added \u2192 node9 check (prompt DLP)"));
6693
+ hooksChanged = true;
6694
+ anythingChanged = true;
6695
+ } else if (settings.hooks.UserPromptSubmit) {
6696
+ for (const matcher of settings.hooks.UserPromptSubmit) {
6697
+ for (const h of matcher.hooks) {
6698
+ const cmd = h.command ?? "";
6699
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
6700
+ h.command = fullPathCommand("check");
6701
+ console.log(
6702
+ chalk.yellow(" \u{1F527} UserPromptSubmit hook repaired (stale path \u2192 current binary)")
6703
+ );
6704
+ hooksChanged = true;
6705
+ anythingChanged = true;
6706
+ }
6707
+ }
6708
+ }
6709
+ }
6525
6710
  if (!hasNode9McpServer(servers)) {
6526
6711
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
6527
6712
  claudeConfig.mcpServers = servers;
@@ -6586,8 +6771,9 @@ async function setupClaude() {
6586
6771
  }
6587
6772
  }
6588
6773
  async function setupGemini() {
6589
- const homeDir2 = os11.homedir();
6590
- const settingsPath = path14.join(homeDir2, ".gemini", "settings.json");
6774
+ seedMcpPinsIfMissing();
6775
+ const homeDir2 = os12.homedir();
6776
+ const settingsPath = path15.join(homeDir2, ".gemini", "settings.json");
6591
6777
  const settings = readJson(settingsPath) ?? {};
6592
6778
  const servers = settings.mcpServers ?? {};
6593
6779
  let hooksChanged = false;
@@ -6682,9 +6868,9 @@ async function setupGemini() {
6682
6868
  printDaemonTip();
6683
6869
  }
6684
6870
  }
6685
- function claudeDesktopConfigPath(homeDir2 = os11.homedir()) {
6871
+ function claudeDesktopConfigPath(homeDir2 = os12.homedir()) {
6686
6872
  if (process.platform === "darwin") {
6687
- return path14.join(
6873
+ return path15.join(
6688
6874
  homeDir2,
6689
6875
  "Library",
6690
6876
  "Application Support",
@@ -6693,18 +6879,18 @@ function claudeDesktopConfigPath(homeDir2 = os11.homedir()) {
6693
6879
  );
6694
6880
  }
6695
6881
  if (process.platform === "linux") {
6696
- return path14.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
6882
+ return path15.join(homeDir2, ".config", "Claude", "claude_desktop_config.json");
6697
6883
  }
6698
6884
  if (process.platform === "win32") {
6699
- const appData = process.env.APPDATA ?? path14.join(homeDir2, "AppData", "Roaming");
6700
- return path14.join(appData, "Claude", "claude_desktop_config.json");
6885
+ const appData = process.env.APPDATA ?? path15.join(homeDir2, "AppData", "Roaming");
6886
+ return path15.join(appData, "Claude", "claude_desktop_config.json");
6701
6887
  }
6702
6888
  return null;
6703
6889
  }
6704
- function detectAgents(homeDir2 = os11.homedir()) {
6890
+ function detectAgents(homeDir2 = os12.homedir()) {
6705
6891
  const exists = (p) => {
6706
6892
  try {
6707
- return fs12.existsSync(p);
6893
+ return fs13.existsSync(p);
6708
6894
  } catch (err2) {
6709
6895
  const code = err2.code;
6710
6896
  if (code !== "ENOENT") {
@@ -6716,18 +6902,19 @@ function detectAgents(homeDir2 = os11.homedir()) {
6716
6902
  };
6717
6903
  const desktopPath = claudeDesktopConfigPath(homeDir2);
6718
6904
  return {
6719
- claude: exists(path14.join(homeDir2, ".claude")) || exists(path14.join(homeDir2, ".claude.json")),
6720
- gemini: exists(path14.join(homeDir2, ".gemini")),
6721
- cursor: exists(path14.join(homeDir2, ".cursor")),
6722
- codex: exists(path14.join(homeDir2, ".codex")),
6723
- windsurf: exists(path14.join(homeDir2, ".codeium", "windsurf")),
6724
- vscode: exists(path14.join(homeDir2, ".vscode")),
6725
- claudeDesktop: desktopPath !== null && exists(path14.dirname(desktopPath))
6905
+ claude: exists(path15.join(homeDir2, ".claude")) || exists(path15.join(homeDir2, ".claude.json")),
6906
+ gemini: exists(path15.join(homeDir2, ".gemini")),
6907
+ cursor: exists(path15.join(homeDir2, ".cursor")),
6908
+ codex: exists(path15.join(homeDir2, ".codex")),
6909
+ windsurf: exists(path15.join(homeDir2, ".codeium", "windsurf")),
6910
+ vscode: exists(path15.join(homeDir2, ".vscode")),
6911
+ claudeDesktop: desktopPath !== null && exists(path15.dirname(desktopPath))
6726
6912
  };
6727
6913
  }
6728
6914
  async function setupCursor() {
6729
- const homeDir2 = os11.homedir();
6730
- const mcpPath = path14.join(homeDir2, ".cursor", "mcp.json");
6915
+ seedMcpPinsIfMissing();
6916
+ const homeDir2 = os12.homedir();
6917
+ const mcpPath = path15.join(homeDir2, ".cursor", "mcp.json");
6731
6918
  const mcpConfig = readJson(mcpPath) ?? {};
6732
6919
  const servers = mcpConfig.mcpServers ?? {};
6733
6920
  let anythingChanged = false;
@@ -6793,24 +6980,95 @@ async function setupCursor() {
6793
6980
  }
6794
6981
  function readToml(filePath) {
6795
6982
  try {
6796
- if (fs12.existsSync(filePath)) {
6797
- return parseToml(fs12.readFileSync(filePath, "utf-8"));
6983
+ if (fs13.existsSync(filePath)) {
6984
+ return parseToml(fs13.readFileSync(filePath, "utf-8"));
6798
6985
  }
6799
6986
  } catch {
6800
6987
  }
6801
6988
  return null;
6802
6989
  }
6803
6990
  function writeToml(filePath, data) {
6804
- const dir = path14.dirname(filePath);
6805
- if (!fs12.existsSync(dir)) fs12.mkdirSync(dir, { recursive: true });
6806
- fs12.writeFileSync(filePath, stringifyToml(data));
6991
+ const dir = path15.dirname(filePath);
6992
+ if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
6993
+ fs13.writeFileSync(filePath, stringifyToml(data));
6807
6994
  }
6808
6995
  async function setupCodex() {
6809
- const homeDir2 = os11.homedir();
6810
- const configPath = path14.join(homeDir2, ".codex", "config.toml");
6996
+ seedMcpPinsIfMissing();
6997
+ const homeDir2 = os12.homedir();
6998
+ const configPath = path15.join(homeDir2, ".codex", "config.toml");
6999
+ const hooksPath = path15.join(homeDir2, ".codex", "hooks.json");
6811
7000
  const config = readToml(configPath) ?? {};
6812
7001
  const servers = config.mcp_servers ?? {};
6813
7002
  let anythingChanged = false;
7003
+ const hooksFile = readJson(hooksPath) ?? {};
7004
+ if (!hooksFile.hooks) hooksFile.hooks = {};
7005
+ let hooksChanged = false;
7006
+ if (!hooksFile.hooks.PreToolUse) hooksFile.hooks.PreToolUse = [];
7007
+ for (const matcher of CODEX_PRE_TOOL_MATCHERS) {
7008
+ const existing = hooksFile.hooks.PreToolUse.find((m) => m.matcher === matcher);
7009
+ if (!existing) {
7010
+ hooksFile.hooks.PreToolUse.push({
7011
+ matcher,
7012
+ hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
7013
+ });
7014
+ hooksChanged = true;
7015
+ } else {
7016
+ for (const h of existing.hooks) {
7017
+ const cmd = h.command ?? "";
7018
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
7019
+ h.command = fullPathCommand("check");
7020
+ hooksChanged = true;
7021
+ }
7022
+ }
7023
+ }
7024
+ }
7025
+ if (!hooksFile.hooks.UserPromptSubmit) hooksFile.hooks.UserPromptSubmit = [];
7026
+ const hasPromptHook = hooksFile.hooks.UserPromptSubmit.some(
7027
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7028
+ );
7029
+ if (!hasPromptHook) {
7030
+ hooksFile.hooks.UserPromptSubmit.push({
7031
+ hooks: [{ type: "command", command: fullPathCommand("check"), timeout: 600 }]
7032
+ });
7033
+ hooksChanged = true;
7034
+ } else {
7035
+ for (const m of hooksFile.hooks.UserPromptSubmit) {
7036
+ for (const h of m.hooks) {
7037
+ const cmd = h.command ?? "";
7038
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
7039
+ h.command = fullPathCommand("check");
7040
+ hooksChanged = true;
7041
+ }
7042
+ }
7043
+ }
7044
+ }
7045
+ if (!hooksFile.hooks.PostToolUse) hooksFile.hooks.PostToolUse = [];
7046
+ const hasPostHook = hooksFile.hooks.PostToolUse.some(
7047
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7048
+ );
7049
+ if (!hasPostHook) {
7050
+ hooksFile.hooks.PostToolUse.push({
7051
+ matcher: ".*",
7052
+ hooks: [{ type: "command", command: fullPathCommand("log"), timeout: 600 }]
7053
+ });
7054
+ hooksChanged = true;
7055
+ } else {
7056
+ for (const m of hooksFile.hooks.PostToolUse) {
7057
+ for (const h of m.hooks) {
7058
+ const cmd = h.command ?? "";
7059
+ if (isNode9Hook(cmd) && isStaleHookCommand(cmd)) {
7060
+ h.command = fullPathCommand("log");
7061
+ hooksChanged = true;
7062
+ }
7063
+ }
7064
+ }
7065
+ }
7066
+ if (hooksChanged) {
7067
+ writeJson(hooksPath, hooksFile);
7068
+ console.log(chalk.green(" \u2705 Codex hooks added \u2192 node9 check / node9 log"));
7069
+ anythingChanged = true;
7070
+ }
7071
+ const hooksInstalled = (hooksFile.hooks?.PreToolUse?.length ?? 0) > 0;
6814
7072
  if (!hasNode9McpServer(servers)) {
6815
7073
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
6816
7074
  config.mcp_servers = servers;
@@ -6850,30 +7108,63 @@ async function setupCodex() {
6850
7108
  }
6851
7109
  console.log("");
6852
7110
  }
6853
- console.log(
6854
- chalk.yellow(
6855
- " \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."
6856
- )
6857
- );
6858
- console.log("");
6859
- if (!anythingChanged && serversToWrap.length === 0) {
7111
+ const hooksDisabled = config.features?.hooks === false || config.codex_hooks === false;
7112
+ if (hooksDisabled) {
6860
7113
  console.log(
6861
- chalk.blue(
6862
- "\u2139\uFE0F No MCP servers found to wrap. Add MCP servers to ~/.codex/config.toml and re-run."
7114
+ chalk.yellow(
7115
+ " \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."
7116
+ )
7117
+ );
7118
+ console.log("");
7119
+ }
7120
+ const printCodexTrustReminder = () => {
7121
+ console.log(
7122
+ chalk.yellow(
7123
+ " \u279C Open Codex and run /hooks to review and trust the Node9 entries.\n Until trusted, only MCP proxy wrapping is active."
6863
7124
  )
6864
7125
  );
7126
+ };
7127
+ if (!anythingChanged && serversToWrap.length === 0) {
7128
+ if (hooksInstalled) {
7129
+ console.log(chalk.blue("\u2139\uFE0F Codex hooks already installed."));
7130
+ printCodexTrustReminder();
7131
+ } else {
7132
+ console.log(
7133
+ chalk.blue(
7134
+ "\u2139\uFE0F No MCP servers found to wrap. Add MCP servers to ~/.codex/config.toml and re-run."
7135
+ )
7136
+ );
7137
+ }
6865
7138
  printDaemonTip();
6866
7139
  return;
6867
7140
  }
6868
7141
  if (anythingChanged) {
6869
- console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Codex via MCP proxy!"));
7142
+ console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9 hooks installed for Codex."));
7143
+ printCodexTrustReminder();
6870
7144
  console.log(chalk.gray(" Restart Codex for changes to take effect."));
6871
7145
  printDaemonTip();
6872
7146
  }
6873
7147
  }
6874
7148
  function teardownCodex() {
6875
- const homeDir2 = os11.homedir();
6876
- const configPath = path14.join(homeDir2, ".codex", "config.toml");
7149
+ const homeDir2 = os12.homedir();
7150
+ const configPath = path15.join(homeDir2, ".codex", "config.toml");
7151
+ const hooksPath = path15.join(homeDir2, ".codex", "hooks.json");
7152
+ const hooksFile = readJson(hooksPath);
7153
+ if (hooksFile?.hooks) {
7154
+ let hooksChanged = false;
7155
+ for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
7156
+ const before = hooksFile.hooks[event]?.length ?? 0;
7157
+ hooksFile.hooks[event] = hooksFile.hooks[event]?.filter(
7158
+ (m) => !m.hooks.some((h) => isNode9Hook(h.command))
7159
+ );
7160
+ if ((hooksFile.hooks[event]?.length ?? 0) < before) hooksChanged = true;
7161
+ if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
7162
+ }
7163
+ if (hooksChanged) {
7164
+ writeJson(hooksPath, hooksFile);
7165
+ console.log(chalk.green(" \u2705 Removed Node9 hooks from ~/.codex/hooks.json"));
7166
+ }
7167
+ }
6877
7168
  const config = readToml(configPath);
6878
7169
  if (!config?.mcp_servers) {
6879
7170
  console.log(chalk.blue(" \u2139\uFE0F ~/.codex/config.toml not found \u2014 nothing to remove"));
@@ -6904,8 +7195,8 @@ function teardownCodex() {
6904
7195
  }
6905
7196
  }
6906
7197
  function setupHud() {
6907
- const homeDir2 = os11.homedir();
6908
- const hooksPath = path14.join(homeDir2, ".claude", "settings.json");
7198
+ const homeDir2 = os12.homedir();
7199
+ const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
6909
7200
  const settings = readJson(hooksPath) ?? {};
6910
7201
  const hudCommand = fullPathCommand("hud");
6911
7202
  const statusLineObj = { type: "command", command: hudCommand };
@@ -6931,8 +7222,8 @@ function setupHud() {
6931
7222
  console.log(chalk.gray(" Restart Claude Code to activate."));
6932
7223
  }
6933
7224
  function teardownHud() {
6934
- const homeDir2 = os11.homedir();
6935
- const hooksPath = path14.join(homeDir2, ".claude", "settings.json");
7225
+ const homeDir2 = os12.homedir();
7226
+ const hooksPath = path15.join(homeDir2, ".claude", "settings.json");
6936
7227
  const settings = readJson(hooksPath);
6937
7228
  if (!settings) {
6938
7229
  console.log(chalk.blue(" \u2139\uFE0F ~/.claude/settings.json not found \u2014 nothing to remove"));
@@ -6950,8 +7241,9 @@ function teardownHud() {
6950
7241
  console.log(chalk.gray(" Restart Claude Code for changes to take effect."));
6951
7242
  }
6952
7243
  async function setupWindsurf() {
6953
- const homeDir2 = os11.homedir();
6954
- const mcpPath = path14.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
7244
+ seedMcpPinsIfMissing();
7245
+ const homeDir2 = os12.homedir();
7246
+ const mcpPath = path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
6955
7247
  const mcpConfig = readJson(mcpPath) ?? {};
6956
7248
  const servers = mcpConfig.mcpServers ?? {};
6957
7249
  let anythingChanged = false;
@@ -7011,8 +7303,8 @@ async function setupWindsurf() {
7011
7303
  }
7012
7304
  }
7013
7305
  function teardownWindsurf() {
7014
- const homeDir2 = os11.homedir();
7015
- const mcpPath = path14.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
7306
+ const homeDir2 = os12.homedir();
7307
+ const mcpPath = path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json");
7016
7308
  const mcpConfig = readJson(mcpPath);
7017
7309
  if (!mcpConfig?.mcpServers) {
7018
7310
  console.log(
@@ -7053,8 +7345,9 @@ function hasNode9McpServerVSCode(servers) {
7053
7345
  return !!entry && entry.command === "node9" && Array.isArray(entry.args) && entry.args[0] === "mcp-server";
7054
7346
  }
7055
7347
  async function setupVSCode() {
7056
- const homeDir2 = os11.homedir();
7057
- const mcpPath = path14.join(homeDir2, ".vscode", "mcp.json");
7348
+ seedMcpPinsIfMissing();
7349
+ const homeDir2 = os12.homedir();
7350
+ const mcpPath = path15.join(homeDir2, ".vscode", "mcp.json");
7058
7351
  const mcpConfig = readJson(mcpPath) ?? {};
7059
7352
  const servers = mcpConfig.servers ?? {};
7060
7353
  let anythingChanged = false;
@@ -7115,8 +7408,8 @@ async function setupVSCode() {
7115
7408
  }
7116
7409
  }
7117
7410
  function teardownVSCode() {
7118
- const homeDir2 = os11.homedir();
7119
- const mcpPath = path14.join(homeDir2, ".vscode", "mcp.json");
7411
+ const homeDir2 = os12.homedir();
7412
+ const mcpPath = path15.join(homeDir2, ".vscode", "mcp.json");
7120
7413
  const mcpConfig = readJson(mcpPath);
7121
7414
  if (!mcpConfig?.servers) {
7122
7415
  console.log(chalk.blue(" \u2139\uFE0F ~/.vscode/mcp.json not found \u2014 nothing to remove"));
@@ -7149,6 +7442,7 @@ function teardownVSCode() {
7149
7442
  }
7150
7443
  }
7151
7444
  async function setupClaudeDesktop() {
7445
+ seedMcpPinsIfMissing();
7152
7446
  const configPath = claudeDesktopConfigPath();
7153
7447
  if (!configPath) {
7154
7448
  console.log(chalk.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
@@ -7247,32 +7541,32 @@ function teardownClaudeDesktop() {
7247
7541
  console.log(chalk.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in Claude Desktop config"));
7248
7542
  }
7249
7543
  }
7250
- function getAgentsStatus(homeDir2 = os11.homedir()) {
7544
+ function getAgentsStatus(homeDir2 = os12.homedir()) {
7251
7545
  const detected = detectAgents(homeDir2);
7252
7546
  const claudeWired = (() => {
7253
- const settings = readJson(path14.join(homeDir2, ".claude", "settings.json"));
7547
+ const settings = readJson(path15.join(homeDir2, ".claude", "settings.json"));
7254
7548
  return !!settings?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
7255
7549
  })();
7256
7550
  const geminiWired = (() => {
7257
- const settings = readJson(path14.join(homeDir2, ".gemini", "settings.json"));
7551
+ const settings = readJson(path15.join(homeDir2, ".gemini", "settings.json"));
7258
7552
  return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
7259
7553
  })();
7260
7554
  const cursorWired = (() => {
7261
- const cfg = readJson(path14.join(homeDir2, ".cursor", "mcp.json"));
7555
+ const cfg = readJson(path15.join(homeDir2, ".cursor", "mcp.json"));
7262
7556
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
7263
7557
  })();
7264
7558
  const codexWired = (() => {
7265
- const cfg = readToml(path14.join(homeDir2, ".codex", "config.toml"));
7559
+ const cfg = readToml(path15.join(homeDir2, ".codex", "config.toml"));
7266
7560
  return !!(cfg?.mcp_servers && hasNode9McpServer(cfg.mcp_servers));
7267
7561
  })();
7268
7562
  const windsurfWired = (() => {
7269
7563
  const cfg = readJson(
7270
- path14.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
7564
+ path15.join(homeDir2, ".codeium", "windsurf", "mcp_config.json")
7271
7565
  );
7272
7566
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
7273
7567
  })();
7274
7568
  const vscodeWired = (() => {
7275
- const cfg = readJson(path14.join(homeDir2, ".vscode", "mcp.json"));
7569
+ const cfg = readJson(path15.join(homeDir2, ".vscode", "mcp.json"));
7276
7570
  return !!(cfg?.servers && hasNode9McpServerVSCode(cfg.servers));
7277
7571
  })();
7278
7572
  return [
@@ -7332,11 +7626,13 @@ function getAgentsStatus(homeDir2 = os11.homedir()) {
7332
7626
  }
7333
7627
  ];
7334
7628
  }
7335
- var NODE9_MCP_SERVER_ENTRY;
7629
+ var NODE9_MCP_SERVER_ENTRY, CODEX_PRE_TOOL_MATCHERS;
7336
7630
  var init_setup = __esm({
7337
7631
  "src/setup.ts"() {
7338
7632
  "use strict";
7633
+ init_mcp_pin();
7339
7634
  NODE9_MCP_SERVER_ENTRY = { command: "node9", args: ["mcp-server"] };
7635
+ CODEX_PRE_TOOL_MATCHERS = ["^Bash$", "^apply_patch$", "^mcp__.*"];
7340
7636
  }
7341
7637
  });
7342
7638
 
@@ -7528,85 +7824,85 @@ var init_scan_summary = __esm({
7528
7824
 
7529
7825
  // src/cli/commands/blast.ts
7530
7826
  import chalk2 from "chalk";
7531
- import fs13 from "fs";
7532
- import path15 from "path";
7533
- import os12 from "os";
7827
+ import fs14 from "fs";
7828
+ import path16 from "path";
7829
+ import os13 from "os";
7534
7830
  function buildSensitivePaths(home, cwd) {
7535
7831
  return [
7536
7832
  {
7537
- full: path15.join(home, ".ssh", "id_rsa"),
7833
+ full: path16.join(home, ".ssh", "id_rsa"),
7538
7834
  label: "~/.ssh/id_rsa",
7539
7835
  description: "RSA private key \u2014 grants SSH access to your servers",
7540
7836
  score: 20
7541
7837
  },
7542
7838
  {
7543
- full: path15.join(home, ".ssh", "id_ed25519"),
7839
+ full: path16.join(home, ".ssh", "id_ed25519"),
7544
7840
  label: "~/.ssh/id_ed25519",
7545
7841
  description: "Ed25519 private key \u2014 grants SSH access to your servers",
7546
7842
  score: 20
7547
7843
  },
7548
7844
  {
7549
- full: path15.join(home, ".ssh", "id_ecdsa"),
7845
+ full: path16.join(home, ".ssh", "id_ecdsa"),
7550
7846
  label: "~/.ssh/id_ecdsa",
7551
7847
  description: "ECDSA private key \u2014 grants SSH access to your servers",
7552
7848
  score: 20
7553
7849
  },
7554
7850
  {
7555
- full: path15.join(home, ".aws", "credentials"),
7851
+ full: path16.join(home, ".aws", "credentials"),
7556
7852
  label: "~/.aws/credentials",
7557
7853
  description: "AWS access keys \u2014 full cloud account access",
7558
7854
  score: 20
7559
7855
  },
7560
7856
  {
7561
- full: path15.join(home, ".aws", "config"),
7857
+ full: path16.join(home, ".aws", "config"),
7562
7858
  label: "~/.aws/config",
7563
7859
  description: "AWS configuration \u2014 account and region settings",
7564
7860
  score: 5
7565
7861
  },
7566
7862
  {
7567
- full: path15.join(home, ".config", "gcloud", "credentials.db"),
7863
+ full: path16.join(home, ".config", "gcloud", "credentials.db"),
7568
7864
  label: "~/.config/gcloud/credentials.db",
7569
7865
  description: "Google Cloud credentials",
7570
7866
  score: 15
7571
7867
  },
7572
7868
  {
7573
- full: path15.join(home, ".docker", "config.json"),
7869
+ full: path16.join(home, ".docker", "config.json"),
7574
7870
  label: "~/.docker/config.json",
7575
7871
  description: "Docker registry auth tokens",
7576
7872
  score: 10
7577
7873
  },
7578
7874
  {
7579
- full: path15.join(home, ".netrc"),
7875
+ full: path16.join(home, ".netrc"),
7580
7876
  label: "~/.netrc",
7581
7877
  description: "FTP/HTTP credentials in plain text",
7582
7878
  score: 15
7583
7879
  },
7584
7880
  {
7585
- full: path15.join(home, ".npmrc"),
7881
+ full: path16.join(home, ".npmrc"),
7586
7882
  label: "~/.npmrc",
7587
7883
  description: "npm auth token \u2014 can publish packages as you",
7588
7884
  score: 10
7589
7885
  },
7590
7886
  {
7591
- full: path15.join(home, ".node9", "credentials.json"),
7887
+ full: path16.join(home, ".node9", "credentials.json"),
7592
7888
  label: "~/.node9/credentials.json",
7593
7889
  description: "Node9 cloud API key",
7594
7890
  score: 10
7595
7891
  },
7596
7892
  {
7597
- full: path15.join(cwd, ".env"),
7893
+ full: path16.join(cwd, ".env"),
7598
7894
  label: ".env (current folder)",
7599
7895
  description: "App secrets \u2014 database passwords, API keys",
7600
7896
  score: 20
7601
7897
  },
7602
7898
  {
7603
- full: path15.join(cwd, ".env.local"),
7899
+ full: path16.join(cwd, ".env.local"),
7604
7900
  label: ".env.local (current folder)",
7605
7901
  description: "Local overrides \u2014 often contains real credentials",
7606
7902
  score: 15
7607
7903
  },
7608
7904
  {
7609
- full: path15.join(cwd, ".env.production"),
7905
+ full: path16.join(cwd, ".env.production"),
7610
7906
  label: ".env.production (current folder)",
7611
7907
  description: "Production secrets",
7612
7908
  score: 20
@@ -7615,7 +7911,7 @@ function buildSensitivePaths(home, cwd) {
7615
7911
  }
7616
7912
  function isReadable(filePath) {
7617
7913
  try {
7618
- fs13.accessSync(filePath, fs13.constants.R_OK);
7914
+ fs14.accessSync(filePath, fs14.constants.R_OK);
7619
7915
  return true;
7620
7916
  } catch {
7621
7917
  return false;
@@ -7628,13 +7924,13 @@ function scoreLabel(score) {
7628
7924
  return chalk2.red.bold(`${score}/100 Critical`);
7629
7925
  }
7630
7926
  function runBlast() {
7631
- const home = os12.homedir();
7927
+ const home = os13.homedir();
7632
7928
  const cwd = process.cwd();
7633
7929
  const paths = buildSensitivePaths(home, cwd);
7634
7930
  let scoreDeduction = 0;
7635
7931
  const reachable = [];
7636
7932
  for (const p of paths) {
7637
- if (fs13.existsSync(p.full) && isReadable(p.full)) {
7933
+ if (fs14.existsSync(p.full) && isReadable(p.full)) {
7638
7934
  reachable.push(p);
7639
7935
  scoreDeduction += p.score;
7640
7936
  }
@@ -7652,7 +7948,7 @@ function runBlast() {
7652
7948
  }
7653
7949
  function registerBlastCommand(program2) {
7654
7950
  program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
7655
- const home = os12.homedir();
7951
+ const home = os13.homedir();
7656
7952
  const cwd = process.cwd();
7657
7953
  const { reachable, envFindings, score } = runBlast();
7658
7954
  console.log("");
@@ -7828,17 +8124,17 @@ var init_scan_json = __esm({
7828
8124
  });
7829
8125
 
7830
8126
  // src/cli/render/scan-history.ts
7831
- import fs14 from "fs";
7832
- import path16 from "path";
7833
- import os13 from "os";
8127
+ import fs15 from "fs";
8128
+ import path17 from "path";
8129
+ import os14 from "os";
7834
8130
  function defaultHistoryPath() {
7835
- return path16.join(os13.homedir(), ".node9", "scan-history.json");
8131
+ return path17.join(os14.homedir(), ".node9", "scan-history.json");
7836
8132
  }
7837
8133
  function readPreviousScan(opts = {}) {
7838
8134
  const filePath = opts.path ?? defaultHistoryPath();
7839
8135
  try {
7840
- if (!fs14.existsSync(filePath)) return null;
7841
- const raw = fs14.readFileSync(filePath, "utf8");
8136
+ if (!fs15.existsSync(filePath)) return null;
8137
+ const raw = fs15.readFileSync(filePath, "utf8");
7842
8138
  const parsed = JSON.parse(raw);
7843
8139
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
7844
8140
  const last = parsed[parsed.length - 1];
@@ -7852,11 +8148,11 @@ function appendScanHistory(record, opts = {}) {
7852
8148
  const filePath = opts.path ?? defaultHistoryPath();
7853
8149
  const cap = opts.cap ?? SCAN_HISTORY_CAP;
7854
8150
  try {
7855
- fs14.mkdirSync(path16.dirname(filePath), { recursive: true });
8151
+ fs15.mkdirSync(path17.dirname(filePath), { recursive: true });
7856
8152
  let history = [];
7857
- if (fs14.existsSync(filePath)) {
8153
+ if (fs15.existsSync(filePath)) {
7858
8154
  try {
7859
- const parsed = JSON.parse(fs14.readFileSync(filePath, "utf8"));
8155
+ const parsed = JSON.parse(fs15.readFileSync(filePath, "utf8"));
7860
8156
  if (Array.isArray(parsed)) {
7861
8157
  history = parsed.filter(isValidRecord);
7862
8158
  }
@@ -7867,7 +8163,7 @@ function appendScanHistory(record, opts = {}) {
7867
8163
  if (history.length > cap) {
7868
8164
  history = history.slice(history.length - cap);
7869
8165
  }
7870
- fs14.writeFileSync(filePath, JSON.stringify(history, null, 2));
8166
+ fs15.writeFileSync(filePath, JSON.stringify(history, null, 2));
7871
8167
  } catch (err2) {
7872
8168
  process.stderr.write(
7873
8169
  `[node9] Warning: could not write scan-history.json: ${err2.message}
@@ -7898,15 +8194,15 @@ var init_scan_history = __esm({
7898
8194
  });
7899
8195
 
7900
8196
  // src/pricing/litellm.ts
7901
- import fs15 from "fs";
7902
- import path17 from "path";
7903
- import os14 from "os";
8197
+ import fs16 from "fs";
8198
+ import path18 from "path";
8199
+ import os15 from "os";
7904
8200
  function normalizeModel(raw) {
7905
8201
  return raw.replace(/-\d{8}$/, "").toLowerCase();
7906
8202
  }
7907
8203
  function readCache() {
7908
8204
  try {
7909
- const raw = JSON.parse(fs15.readFileSync(CACHE_FILE(), "utf-8"));
8205
+ const raw = JSON.parse(fs16.readFileSync(CACHE_FILE(), "utf-8"));
7910
8206
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
7911
8207
  return null;
7912
8208
  }
@@ -7920,18 +8216,18 @@ function readCache() {
7920
8216
  function writeCache(prices) {
7921
8217
  try {
7922
8218
  const target = CACHE_FILE();
7923
- const dir = path17.dirname(target);
7924
- if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
8219
+ const dir = path18.dirname(target);
8220
+ if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
7925
8221
  const tmp = target + ".tmp";
7926
8222
  const body = {
7927
8223
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
7928
8224
  prices
7929
8225
  };
7930
- fs15.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
7931
- fs15.renameSync(tmp, target);
8226
+ fs16.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
8227
+ fs16.renameSync(tmp, target);
7932
8228
  } catch (err2) {
7933
8229
  try {
7934
- fs15.appendFileSync(
8230
+ fs16.appendFileSync(
7935
8231
  HOOK_DEBUG_LOG,
7936
8232
  `[pricing] cache write failed: ${err2.message}
7937
8233
  `
@@ -8054,7 +8350,7 @@ var init_litellm = __esm({
8054
8350
  "gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
8055
8351
  "gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
8056
8352
  };
8057
- CACHE_FILE = () => path17.join(os14.homedir(), ".node9", "model-pricing.json");
8353
+ CACHE_FILE = () => path18.join(os15.homedir(), ".node9", "model-pricing.json");
8058
8354
  TTL_MS = 24 * 60 * 60 * 1e3;
8059
8355
  memCache = null;
8060
8356
  memCacheAt = 0;
@@ -8063,17 +8359,17 @@ var init_litellm = __esm({
8063
8359
  });
8064
8360
 
8065
8361
  // src/costSync.ts
8066
- import fs16 from "fs";
8067
- import path18 from "path";
8068
- import os15 from "os";
8362
+ import fs17 from "fs";
8363
+ import path19 from "path";
8364
+ import os16 from "os";
8069
8365
  function decodeProjectDirName(dirName) {
8070
8366
  return dirName.replace(/-/g, "/");
8071
8367
  }
8072
8368
  function parseJSONLFile(filePath, fallbackWorkingDir) {
8073
- const runId = path18.basename(filePath, ".jsonl");
8369
+ const runId = path19.basename(filePath, ".jsonl");
8074
8370
  let content;
8075
8371
  try {
8076
- content = fs16.readFileSync(filePath, "utf8");
8372
+ content = fs17.readFileSync(filePath, "utf8");
8077
8373
  } catch {
8078
8374
  return /* @__PURE__ */ new Map();
8079
8375
  }
@@ -8129,34 +8425,34 @@ function parseJSONLFile(filePath, fallbackWorkingDir) {
8129
8425
  return daily;
8130
8426
  }
8131
8427
  function collectEntries(sinceMs) {
8132
- const projectsDir = path18.join(os15.homedir(), ".claude", "projects");
8133
- if (!fs16.existsSync(projectsDir)) return [];
8428
+ const projectsDir = path19.join(os16.homedir(), ".claude", "projects");
8429
+ if (!fs17.existsSync(projectsDir)) return [];
8134
8430
  const combined = /* @__PURE__ */ new Map();
8135
8431
  let dirs;
8136
8432
  try {
8137
- dirs = fs16.readdirSync(projectsDir);
8433
+ dirs = fs17.readdirSync(projectsDir);
8138
8434
  } catch {
8139
8435
  return [];
8140
8436
  }
8141
8437
  for (const dir of dirs) {
8142
- const dirPath = path18.join(projectsDir, dir);
8438
+ const dirPath = path19.join(projectsDir, dir);
8143
8439
  try {
8144
- if (!fs16.statSync(dirPath).isDirectory()) continue;
8440
+ if (!fs17.statSync(dirPath).isDirectory()) continue;
8145
8441
  } catch {
8146
8442
  continue;
8147
8443
  }
8148
8444
  let files;
8149
8445
  try {
8150
- files = fs16.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8446
+ files = fs17.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8151
8447
  } catch {
8152
8448
  continue;
8153
8449
  }
8154
8450
  const fallbackWorkingDir = decodeProjectDirName(dir);
8155
8451
  for (const file of files) {
8156
- const filePath = path18.join(dirPath, file);
8452
+ const filePath = path19.join(dirPath, file);
8157
8453
  if (sinceMs !== void 0) {
8158
8454
  try {
8159
- if (fs16.statSync(filePath).mtimeMs < sinceMs) continue;
8455
+ if (fs17.statSync(filePath).mtimeMs < sinceMs) continue;
8160
8456
  } catch {
8161
8457
  continue;
8162
8458
  }
@@ -8186,10 +8482,10 @@ async function syncCost() {
8186
8482
  if (entries.length === 0) return;
8187
8483
  let username = "unknown";
8188
8484
  try {
8189
- username = os15.userInfo().username;
8485
+ username = os16.userInfo().username;
8190
8486
  } catch {
8191
8487
  }
8192
- const machineId = `${os15.hostname()}:${username}`;
8488
+ const machineId = `${os16.hostname()}:${username}`;
8193
8489
  try {
8194
8490
  const res = await fetch(`${creds.apiUrl}/cost-sync`, {
8195
8491
  method: "POST",
@@ -8198,11 +8494,11 @@ async function syncCost() {
8198
8494
  signal: AbortSignal.timeout(15e3)
8199
8495
  });
8200
8496
  if (!res.ok) {
8201
- fs16.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
8497
+ fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
8202
8498
  `);
8203
8499
  }
8204
8500
  } catch (err2) {
8205
- fs16.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
8501
+ fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
8206
8502
  `);
8207
8503
  }
8208
8504
  }
@@ -8238,9 +8534,9 @@ __export(scan_watermark_exports, {
8238
8534
  tickForensicBroadcast: () => tickForensicBroadcast,
8239
8535
  tickScanWatcher: () => tickScanWatcher
8240
8536
  });
8241
- import fs17 from "fs";
8242
- import os16 from "os";
8243
- import path19 from "path";
8537
+ import fs18 from "fs";
8538
+ import os17 from "os";
8539
+ import path20 from "path";
8244
8540
  import readline from "readline";
8245
8541
  function freshWatermark() {
8246
8542
  return {
@@ -8253,7 +8549,7 @@ function freshWatermark() {
8253
8549
  function loadWatermark() {
8254
8550
  let raw;
8255
8551
  try {
8256
- raw = fs17.readFileSync(WATERMARK_FILE(), "utf-8");
8552
+ raw = fs18.readFileSync(WATERMARK_FILE(), "utf-8");
8257
8553
  } catch {
8258
8554
  return { status: "fresh", wm: freshWatermark() };
8259
8555
  }
@@ -8305,28 +8601,28 @@ function loadWatermark() {
8305
8601
  function saveWatermark(wm) {
8306
8602
  if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
8307
8603
  const target = WATERMARK_FILE();
8308
- const dir = path19.dirname(target);
8309
- if (!fs17.existsSync(dir)) fs17.mkdirSync(dir, { recursive: true });
8604
+ const dir = path20.dirname(target);
8605
+ if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
8310
8606
  const tmp = target + ".tmp";
8311
- fs17.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
8312
- fs17.renameSync(tmp, target);
8607
+ fs18.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
8608
+ fs18.renameSync(tmp, target);
8313
8609
  }
8314
8610
  function listJsonlFiles() {
8315
8611
  const root = PROJECTS_DIR();
8316
- if (!fs17.existsSync(root)) return [];
8612
+ if (!fs18.existsSync(root)) return [];
8317
8613
  const out = [];
8318
- for (const entry of fs17.readdirSync(root, { withFileTypes: true })) {
8614
+ for (const entry of fs18.readdirSync(root, { withFileTypes: true })) {
8319
8615
  if (!entry.isDirectory()) continue;
8320
- const projectDir = path19.join(root, entry.name);
8616
+ const projectDir = path20.join(root, entry.name);
8321
8617
  let inner;
8322
8618
  try {
8323
- inner = fs17.readdirSync(projectDir, { withFileTypes: true });
8619
+ inner = fs18.readdirSync(projectDir, { withFileTypes: true });
8324
8620
  } catch {
8325
8621
  continue;
8326
8622
  }
8327
8623
  for (const file of inner) {
8328
8624
  if (file.isFile() && file.name.endsWith(".jsonl")) {
8329
- out.push(path19.join(projectDir, file.name));
8625
+ out.push(path20.join(projectDir, file.name));
8330
8626
  }
8331
8627
  }
8332
8628
  }
@@ -8334,7 +8630,7 @@ function listJsonlFiles() {
8334
8630
  }
8335
8631
  function fileSize(p) {
8336
8632
  try {
8337
- return fs17.statSync(p).size;
8633
+ return fs18.statSync(p).size;
8338
8634
  } catch {
8339
8635
  return 0;
8340
8636
  }
@@ -8342,7 +8638,7 @@ function fileSize(p) {
8342
8638
  async function scanDelta(filePath, fromByte, onLine) {
8343
8639
  const size = fileSize(filePath);
8344
8640
  if (size <= fromByte) return fromByte;
8345
- const stream = fs17.createReadStream(filePath, {
8641
+ const stream = fs18.createReadStream(filePath, {
8346
8642
  start: fromByte,
8347
8643
  end: size - 1,
8348
8644
  highWaterMark: 64 * 1024
@@ -8454,7 +8750,7 @@ async function tickForensicBroadcast(offsets) {
8454
8750
  continue;
8455
8751
  }
8456
8752
  if (size <= offset) continue;
8457
- const sessionId = path19.basename(file, ".jsonl");
8753
+ const sessionId = path20.basename(file, ".jsonl");
8458
8754
  const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
8459
8755
  out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
8460
8756
  });
@@ -8513,7 +8809,7 @@ function emptyTick(uploadAs) {
8513
8809
  function readRawWatermarkPreservingOffsets() {
8514
8810
  let raw;
8515
8811
  try {
8516
- raw = fs17.readFileSync(WATERMARK_FILE(), "utf-8");
8812
+ raw = fs18.readFileSync(WATERMARK_FILE(), "utf-8");
8517
8813
  } catch {
8518
8814
  return null;
8519
8815
  }
@@ -8547,13 +8843,13 @@ async function runActualTick(wm) {
8547
8843
  if (!known) {
8548
8844
  let mtimeMs = 0;
8549
8845
  try {
8550
- mtimeMs = fs17.statSync(filePath).mtime.getTime();
8846
+ mtimeMs = fs18.statSync(filePath).mtime.getTime();
8551
8847
  } catch {
8552
8848
  continue;
8553
8849
  }
8554
8850
  if (mtimeMs >= watermarkCreatedAt) {
8555
8851
  filesNew++;
8556
- const sessionId2 = path19.basename(filePath, ".jsonl");
8852
+ const sessionId2 = path20.basename(filePath, ".jsonl");
8557
8853
  const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
8558
8854
  totalToolCalls++;
8559
8855
  toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
@@ -8571,7 +8867,7 @@ async function runActualTick(wm) {
8571
8867
  filesSkipped++;
8572
8868
  continue;
8573
8869
  }
8574
- const sessionId = path19.basename(filePath, ".jsonl");
8870
+ const sessionId = path20.basename(filePath, ".jsonl");
8575
8871
  const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
8576
8872
  totalToolCalls++;
8577
8873
  toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
@@ -8599,8 +8895,8 @@ var init_scan_watermark = __esm({
8599
8895
  "use strict";
8600
8896
  init_dlp();
8601
8897
  init_dist();
8602
- PROJECTS_DIR = () => path19.join(os16.homedir(), ".claude", "projects");
8603
- WATERMARK_FILE = () => path19.join(os16.homedir(), ".node9", "scan-watermark.json");
8898
+ PROJECTS_DIR = () => path20.join(os17.homedir(), ".claude", "projects");
8899
+ WATERMARK_FILE = () => path20.join(os17.homedir(), ".node9", "scan-watermark.json");
8604
8900
  MAX_LINE_BYTES = 2 * 1024 * 1024;
8605
8901
  WATERMARK_SCHEMA_VERSION = 2;
8606
8902
  LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
@@ -8615,10 +8911,10 @@ __export(scan_upload_history_exports, {
8615
8911
  parseSinceCutoff: () => parseSinceCutoff,
8616
8912
  runUploadHistory: () => runUploadHistory
8617
8913
  });
8618
- import fs18 from "fs";
8914
+ import fs19 from "fs";
8619
8915
  import https from "https";
8620
- import os17 from "os";
8621
- import path20 from "path";
8916
+ import os18 from "os";
8917
+ import path21 from "path";
8622
8918
  import chalk4 from "chalk";
8623
8919
  function emptySignals2() {
8624
8920
  return {
@@ -8653,40 +8949,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
8653
8949
  return now.getTime() - 90 * 864e5;
8654
8950
  }
8655
8951
  function* iterateJsonlFiles(cutoffMs) {
8656
- const projectsDir = path20.join(os17.homedir(), ".claude", "projects");
8952
+ const projectsDir = path21.join(os18.homedir(), ".claude", "projects");
8657
8953
  let dirs;
8658
8954
  try {
8659
- dirs = fs18.readdirSync(projectsDir);
8955
+ dirs = fs19.readdirSync(projectsDir);
8660
8956
  } catch {
8661
8957
  return;
8662
8958
  }
8663
8959
  for (const dir of dirs) {
8664
- const dirPath = path20.join(projectsDir, dir);
8960
+ const dirPath = path21.join(projectsDir, dir);
8665
8961
  let stats;
8666
8962
  try {
8667
- stats = fs18.statSync(dirPath);
8963
+ stats = fs19.statSync(dirPath);
8668
8964
  } catch {
8669
8965
  continue;
8670
8966
  }
8671
8967
  if (!stats.isDirectory()) continue;
8672
8968
  let files;
8673
8969
  try {
8674
- files = fs18.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8970
+ files = fs19.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
8675
8971
  } catch {
8676
8972
  continue;
8677
8973
  }
8678
8974
  for (const file of files) {
8679
- const filePath = path20.join(dirPath, file);
8975
+ const filePath = path21.join(dirPath, file);
8680
8976
  let mtime = 0;
8681
8977
  try {
8682
- mtime = fs18.statSync(filePath).mtimeMs;
8978
+ mtime = fs19.statSync(filePath).mtimeMs;
8683
8979
  } catch {
8684
8980
  continue;
8685
8981
  }
8686
8982
  if (mtime < cutoffMs) continue;
8687
8983
  yield {
8688
8984
  filePath,
8689
- sessionId: path20.basename(file, ".jsonl"),
8985
+ sessionId: path21.basename(file, ".jsonl"),
8690
8986
  projectDir: dir
8691
8987
  };
8692
8988
  }
@@ -8739,7 +9035,7 @@ async function runUploadHistory(opts) {
8739
9035
  filesScanned++;
8740
9036
  let content;
8741
9037
  try {
8742
- content = fs18.readFileSync(filePath, "utf8");
9038
+ content = fs19.readFileSync(filePath, "utf8");
8743
9039
  } catch {
8744
9040
  continue;
8745
9041
  }
@@ -8825,10 +9121,10 @@ async function runUploadHistory(opts) {
8825
9121
  const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
8826
9122
  let username = "unknown";
8827
9123
  try {
8828
- username = os17.userInfo().username;
9124
+ username = os18.userInfo().username;
8829
9125
  } catch {
8830
9126
  }
8831
- const machineId = `${os17.hostname()}:${username}`;
9127
+ const machineId = `${os18.hostname()}:${username}`;
8832
9128
  await postJson(costUrl, creds.apiKey, {
8833
9129
  machineId,
8834
9130
  entries: dailyEntries
@@ -8902,9 +9198,9 @@ var init_scan_upload_history = __esm({
8902
9198
 
8903
9199
  // src/cli/commands/scan.ts
8904
9200
  import chalk5 from "chalk";
8905
- import fs19 from "fs";
8906
- import path21 from "path";
8907
- import os18 from "os";
9201
+ import fs20 from "fs";
9202
+ import path22 from "path";
9203
+ import os19 from "os";
8908
9204
  import stringWidth2 from "string-width";
8909
9205
  function claudeModelPrice(model) {
8910
9206
  const base = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
@@ -9131,14 +9427,14 @@ function buildRuleSources() {
9131
9427
  }
9132
9428
  function countScanFiles() {
9133
9429
  let total = 0;
9134
- const claudeDir = path21.join(os18.homedir(), ".claude", "projects");
9135
- if (fs19.existsSync(claudeDir)) {
9430
+ const claudeDir = path22.join(os19.homedir(), ".claude", "projects");
9431
+ if (fs20.existsSync(claudeDir)) {
9136
9432
  try {
9137
- for (const proj of fs19.readdirSync(claudeDir)) {
9138
- const p = path21.join(claudeDir, proj);
9433
+ for (const proj of fs20.readdirSync(claudeDir)) {
9434
+ const p = path22.join(claudeDir, proj);
9139
9435
  try {
9140
- if (!fs19.statSync(p).isDirectory()) continue;
9141
- total += fs19.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
9436
+ if (!fs20.statSync(p).isDirectory()) continue;
9437
+ total += fs20.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
9142
9438
  } catch {
9143
9439
  continue;
9144
9440
  }
@@ -9146,17 +9442,17 @@ function countScanFiles() {
9146
9442
  } catch {
9147
9443
  }
9148
9444
  }
9149
- const geminiDir = path21.join(os18.homedir(), ".gemini", "tmp");
9150
- if (fs19.existsSync(geminiDir)) {
9445
+ const geminiDir = path22.join(os19.homedir(), ".gemini", "tmp");
9446
+ if (fs20.existsSync(geminiDir)) {
9151
9447
  try {
9152
- for (const slug of fs19.readdirSync(geminiDir)) {
9153
- const p = path21.join(geminiDir, slug);
9448
+ for (const slug of fs20.readdirSync(geminiDir)) {
9449
+ const p = path22.join(geminiDir, slug);
9154
9450
  try {
9155
- if (!fs19.statSync(p).isDirectory()) continue;
9156
- const chatsDir = path21.join(p, "chats");
9157
- if (fs19.existsSync(chatsDir)) {
9451
+ if (!fs20.statSync(p).isDirectory()) continue;
9452
+ const chatsDir = path22.join(p, "chats");
9453
+ if (fs20.existsSync(chatsDir)) {
9158
9454
  try {
9159
- total += fs19.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
9455
+ total += fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
9160
9456
  } catch {
9161
9457
  }
9162
9458
  }
@@ -9167,22 +9463,22 @@ function countScanFiles() {
9167
9463
  } catch {
9168
9464
  }
9169
9465
  }
9170
- const codexDir = path21.join(os18.homedir(), ".codex", "sessions");
9171
- if (fs19.existsSync(codexDir)) {
9466
+ const codexDir = path22.join(os19.homedir(), ".codex", "sessions");
9467
+ if (fs20.existsSync(codexDir)) {
9172
9468
  try {
9173
- for (const year of fs19.readdirSync(codexDir)) {
9174
- const yp = path21.join(codexDir, year);
9469
+ for (const year of fs20.readdirSync(codexDir)) {
9470
+ const yp = path22.join(codexDir, year);
9175
9471
  try {
9176
- if (!fs19.statSync(yp).isDirectory()) continue;
9177
- for (const month of fs19.readdirSync(yp)) {
9178
- const mp = path21.join(yp, month);
9472
+ if (!fs20.statSync(yp).isDirectory()) continue;
9473
+ for (const month of fs20.readdirSync(yp)) {
9474
+ const mp = path22.join(yp, month);
9179
9475
  try {
9180
- if (!fs19.statSync(mp).isDirectory()) continue;
9181
- for (const day of fs19.readdirSync(mp)) {
9182
- const dp = path21.join(mp, day);
9476
+ if (!fs20.statSync(mp).isDirectory()) continue;
9477
+ for (const day of fs20.readdirSync(mp)) {
9478
+ const dp = path22.join(mp, day);
9183
9479
  try {
9184
- if (!fs19.statSync(dp).isDirectory()) continue;
9185
- total += fs19.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
9480
+ if (!fs20.statSync(dp).isDirectory()) continue;
9481
+ total += fs20.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
9186
9482
  } catch {
9187
9483
  continue;
9188
9484
  }
@@ -9218,7 +9514,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9218
9514
  const sessionId = file.replace(/\.jsonl$/, "");
9219
9515
  let raw;
9220
9516
  try {
9221
- raw = fs19.readFileSync(path21.join(projPath, file), "utf-8");
9517
+ raw = fs20.readFileSync(path22.join(projPath, file), "utf-8");
9222
9518
  } catch {
9223
9519
  return;
9224
9520
  }
@@ -9270,7 +9566,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9270
9566
  if (block.type !== "tool_result") continue;
9271
9567
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
9272
9568
  if (filePath) {
9273
- const ext = path21.extname(filePath).toLowerCase();
9569
+ const ext = path22.extname(filePath).toLowerCase();
9274
9570
  if (CODE_EXTENSIONS.has(ext)) continue;
9275
9571
  }
9276
9572
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -9327,7 +9623,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9327
9623
  const rawCmd = String(input.command ?? "").trimStart();
9328
9624
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
9329
9625
  const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
9330
- const inputFileExt = inputFilePath ? path21.extname(inputFilePath).toLowerCase() : "";
9626
+ const inputFileExt = inputFilePath ? path22.extname(inputFilePath).toLowerCase() : "";
9331
9627
  if (CODE_EXTENSIONS.has(inputFileExt)) continue;
9332
9628
  const dlpMatch = scanArgs(input);
9333
9629
  if (dlpMatch) {
@@ -9424,19 +9720,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
9424
9720
  }
9425
9721
  }
9426
9722
  function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
9427
- const projPath = path21.join(projectsDir, proj);
9723
+ const projPath = path22.join(projectsDir, proj);
9428
9724
  try {
9429
- if (!fs19.statSync(projPath).isDirectory()) return;
9725
+ if (!fs20.statSync(projPath).isDirectory()) return;
9430
9726
  } catch {
9431
9727
  return;
9432
9728
  }
9433
- const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os18.homedir(), "~")).slice(
9729
+ const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os19.homedir(), "~")).slice(
9434
9730
  0,
9435
9731
  40
9436
9732
  );
9437
9733
  let files;
9438
9734
  try {
9439
- files = fs19.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
9735
+ files = fs20.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
9440
9736
  } catch {
9441
9737
  return;
9442
9738
  }
@@ -9470,12 +9766,12 @@ function emptyClaudeScan() {
9470
9766
  };
9471
9767
  }
9472
9768
  function scanClaudeHistory(startDate, onProgress, onLine) {
9473
- const projectsDir = path21.join(os18.homedir(), ".claude", "projects");
9769
+ const projectsDir = path22.join(os19.homedir(), ".claude", "projects");
9474
9770
  const result = emptyClaudeScan();
9475
- if (!fs19.existsSync(projectsDir)) return result;
9771
+ if (!fs20.existsSync(projectsDir)) return result;
9476
9772
  let projDirs;
9477
9773
  try {
9478
- projDirs = fs19.readdirSync(projectsDir);
9774
+ projDirs = fs20.readdirSync(projectsDir);
9479
9775
  } catch {
9480
9776
  return result;
9481
9777
  }
@@ -9496,7 +9792,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
9496
9792
  return result;
9497
9793
  }
9498
9794
  function scanGeminiHistory(startDate, onProgress, onLine) {
9499
- const tmpDir = path21.join(os18.homedir(), ".gemini", "tmp");
9795
+ const tmpDir = path22.join(os19.homedir(), ".gemini", "tmp");
9500
9796
  const result = {
9501
9797
  filesScanned: 0,
9502
9798
  sessions: 0,
@@ -9511,33 +9807,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
9511
9807
  sessionsWithEarlySecrets: 0
9512
9808
  };
9513
9809
  const dedup = emptyScanDedup();
9514
- if (!fs19.existsSync(tmpDir)) return result;
9810
+ if (!fs20.existsSync(tmpDir)) return result;
9515
9811
  let slugDirs;
9516
9812
  try {
9517
- slugDirs = fs19.readdirSync(tmpDir);
9813
+ slugDirs = fs20.readdirSync(tmpDir);
9518
9814
  } catch {
9519
9815
  return result;
9520
9816
  }
9521
9817
  const ruleSources = buildRuleSources();
9522
9818
  for (const slug of slugDirs) {
9523
- const slugPath = path21.join(tmpDir, slug);
9819
+ const slugPath = path22.join(tmpDir, slug);
9524
9820
  try {
9525
- if (!fs19.statSync(slugPath).isDirectory()) continue;
9821
+ if (!fs20.statSync(slugPath).isDirectory()) continue;
9526
9822
  } catch {
9527
9823
  continue;
9528
9824
  }
9529
9825
  let projLabel = stripTerminalEscapes(slug).slice(0, 40);
9530
9826
  try {
9531
9827
  projLabel = stripTerminalEscapes(
9532
- fs19.readFileSync(path21.join(slugPath, ".project_root"), "utf-8").trim()
9533
- ).replace(os18.homedir(), "~").slice(0, 40);
9828
+ fs20.readFileSync(path22.join(slugPath, ".project_root"), "utf-8").trim()
9829
+ ).replace(os19.homedir(), "~").slice(0, 40);
9534
9830
  } catch {
9535
9831
  }
9536
- const chatsDir = path21.join(slugPath, "chats");
9537
- if (!fs19.existsSync(chatsDir)) continue;
9832
+ const chatsDir = path22.join(slugPath, "chats");
9833
+ if (!fs20.existsSync(chatsDir)) continue;
9538
9834
  let chatFiles;
9539
9835
  try {
9540
- chatFiles = fs19.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
9836
+ chatFiles = fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
9541
9837
  } catch {
9542
9838
  continue;
9543
9839
  }
@@ -9547,7 +9843,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
9547
9843
  const sessionId = chatFile.replace(/\.json$/, "");
9548
9844
  let raw;
9549
9845
  try {
9550
- raw = fs19.readFileSync(path21.join(chatsDir, chatFile), "utf-8");
9846
+ raw = fs20.readFileSync(path22.join(chatsDir, chatFile), "utf-8");
9551
9847
  } catch {
9552
9848
  continue;
9553
9849
  }
@@ -9709,7 +10005,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
9709
10005
  return result;
9710
10006
  }
9711
10007
  function scanCodexHistory(startDate, onProgress, onLine) {
9712
- const sessionsBase = path21.join(os18.homedir(), ".codex", "sessions");
10008
+ const sessionsBase = path22.join(os19.homedir(), ".codex", "sessions");
9713
10009
  const result = {
9714
10010
  filesScanned: 0,
9715
10011
  sessions: 0,
@@ -9724,32 +10020,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9724
10020
  sessionsWithEarlySecrets: 0
9725
10021
  };
9726
10022
  const dedup = emptyScanDedup();
9727
- if (!fs19.existsSync(sessionsBase)) return result;
10023
+ if (!fs20.existsSync(sessionsBase)) return result;
9728
10024
  const jsonlFiles = [];
9729
10025
  try {
9730
- for (const year of fs19.readdirSync(sessionsBase)) {
9731
- const yearPath = path21.join(sessionsBase, year);
10026
+ for (const year of fs20.readdirSync(sessionsBase)) {
10027
+ const yearPath = path22.join(sessionsBase, year);
9732
10028
  try {
9733
- if (!fs19.statSync(yearPath).isDirectory()) continue;
10029
+ if (!fs20.statSync(yearPath).isDirectory()) continue;
9734
10030
  } catch {
9735
10031
  continue;
9736
10032
  }
9737
- for (const month of fs19.readdirSync(yearPath)) {
9738
- const monthPath = path21.join(yearPath, month);
10033
+ for (const month of fs20.readdirSync(yearPath)) {
10034
+ const monthPath = path22.join(yearPath, month);
9739
10035
  try {
9740
- if (!fs19.statSync(monthPath).isDirectory()) continue;
10036
+ if (!fs20.statSync(monthPath).isDirectory()) continue;
9741
10037
  } catch {
9742
10038
  continue;
9743
10039
  }
9744
- for (const day of fs19.readdirSync(monthPath)) {
9745
- const dayPath = path21.join(monthPath, day);
10040
+ for (const day of fs20.readdirSync(monthPath)) {
10041
+ const dayPath = path22.join(monthPath, day);
9746
10042
  try {
9747
- if (!fs19.statSync(dayPath).isDirectory()) continue;
10043
+ if (!fs20.statSync(dayPath).isDirectory()) continue;
9748
10044
  } catch {
9749
10045
  continue;
9750
10046
  }
9751
- for (const file of fs19.readdirSync(dayPath)) {
9752
- if (file.endsWith(".jsonl")) jsonlFiles.push(path21.join(dayPath, file));
10047
+ for (const file of fs20.readdirSync(dayPath)) {
10048
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path22.join(dayPath, file));
9753
10049
  }
9754
10050
  }
9755
10051
  }
@@ -9763,7 +10059,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9763
10059
  onProgress?.(result.filesScanned);
9764
10060
  let lines;
9765
10061
  try {
9766
- lines = fs19.readFileSync(filePath, "utf-8").split("\n");
10062
+ lines = fs20.readFileSync(filePath, "utf-8").split("\n");
9767
10063
  } catch {
9768
10064
  continue;
9769
10065
  }
@@ -9789,7 +10085,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9789
10085
  sessionId = String(payload["id"] ?? filePath);
9790
10086
  startTime = String(payload["timestamp"] ?? "");
9791
10087
  const cwd = String(payload["cwd"] ?? "");
9792
- projLabel = stripTerminalEscapes(cwd.replace(os18.homedir(), "~")).slice(0, 40);
10088
+ projLabel = stripTerminalEscapes(cwd.replace(os19.homedir(), "~")).slice(0, 40);
9793
10089
  continue;
9794
10090
  }
9795
10091
  if (entry.type === "event_msg" && payload["type"] === "token_count") {
@@ -9942,17 +10238,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
9942
10238
  return result;
9943
10239
  }
9944
10240
  function scanShellConfig() {
9945
- const home = os18.homedir();
10241
+ const home = os19.homedir();
9946
10242
  const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
9947
- (f) => path21.join(home, f)
10243
+ (f) => path22.join(home, f)
9948
10244
  );
9949
10245
  const findings = [];
9950
10246
  const seen = /* @__PURE__ */ new Set();
9951
10247
  for (const filePath of configFiles) {
9952
- if (!fs19.existsSync(filePath)) continue;
10248
+ if (!fs20.existsSync(filePath)) continue;
9953
10249
  let lines;
9954
10250
  try {
9955
- lines = fs19.readFileSync(filePath, "utf-8").split("\n");
10251
+ lines = fs20.readFileSync(filePath, "utf-8").split("\n");
9956
10252
  } catch {
9957
10253
  continue;
9958
10254
  }
@@ -10732,7 +11028,7 @@ function registerScanCommand(program2) {
10732
11028
  if (!drillDown) {
10733
11029
  const useInk2 = !options.classic;
10734
11030
  if (useInk2) {
10735
- const scanInkPath = path21.join(__dirname, "scan-ink.mjs");
11031
+ const scanInkPath = path22.join(__dirname, "scan-ink.mjs");
10736
11032
  const dynamicImport = new Function("id", "return import(id)");
10737
11033
  const mod = await dynamicImport(`file://${scanInkPath}`);
10738
11034
  const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
@@ -11150,8 +11446,8 @@ var init_suggestion_tracker = __esm({
11150
11446
  });
11151
11447
 
11152
11448
  // src/daemon/taint-store.ts
11153
- import fs20 from "fs";
11154
- import path22 from "path";
11449
+ import fs21 from "fs";
11450
+ import path23 from "path";
11155
11451
  var DEFAULT_TTL_MS, TaintStore;
11156
11452
  var init_taint_store = __esm({
11157
11453
  "src/daemon/taint-store.ts"() {
@@ -11220,9 +11516,9 @@ var init_taint_store = __esm({
11220
11516
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
11221
11517
  _resolve(filePath) {
11222
11518
  try {
11223
- return fs20.realpathSync.native(path22.resolve(filePath));
11519
+ return fs21.realpathSync.native(path23.resolve(filePath));
11224
11520
  } catch {
11225
- return path22.resolve(filePath);
11521
+ return path23.resolve(filePath);
11226
11522
  }
11227
11523
  }
11228
11524
  };
@@ -11339,14 +11635,14 @@ var init_session_history = __esm({
11339
11635
 
11340
11636
  // src/daemon/state.ts
11341
11637
  import net2 from "net";
11342
- import fs21 from "fs";
11343
- import path23 from "path";
11344
- import os19 from "os";
11638
+ import fs22 from "fs";
11639
+ import path24 from "path";
11640
+ import os20 from "os";
11345
11641
  import { randomUUID as randomUUID3 } from "crypto";
11346
11642
  function loadInsightCounts() {
11347
11643
  try {
11348
- if (!fs21.existsSync(INSIGHT_COUNTS_FILE)) return;
11349
- const data = JSON.parse(fs21.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
11644
+ if (!fs22.existsSync(INSIGHT_COUNTS_FILE)) return;
11645
+ const data = JSON.parse(fs22.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
11350
11646
  for (const [tool, count] of Object.entries(data)) {
11351
11647
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
11352
11648
  }
@@ -11385,23 +11681,23 @@ function markRejectionHandlerRegistered() {
11385
11681
  daemonRejectionHandlerRegistered = true;
11386
11682
  }
11387
11683
  function atomicWriteSync2(filePath, data, options) {
11388
- const dir = path23.dirname(filePath);
11389
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
11684
+ const dir = path24.dirname(filePath);
11685
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
11390
11686
  const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
11391
11687
  try {
11392
- fs21.writeFileSync(tmpPath, data, options);
11688
+ fs22.writeFileSync(tmpPath, data, options);
11393
11689
  } catch (err2) {
11394
11690
  try {
11395
- fs21.unlinkSync(tmpPath);
11691
+ fs22.unlinkSync(tmpPath);
11396
11692
  } catch {
11397
11693
  }
11398
11694
  throw err2;
11399
11695
  }
11400
11696
  try {
11401
- fs21.renameSync(tmpPath, filePath);
11697
+ fs22.renameSync(tmpPath, filePath);
11402
11698
  } catch (err2) {
11403
11699
  try {
11404
- fs21.unlinkSync(tmpPath);
11700
+ fs22.unlinkSync(tmpPath);
11405
11701
  } catch {
11406
11702
  }
11407
11703
  throw err2;
@@ -11425,16 +11721,16 @@ function appendAuditLog(data) {
11425
11721
  decision: data.decision,
11426
11722
  source: "daemon"
11427
11723
  };
11428
- const dir = path23.dirname(AUDIT_LOG_FILE);
11429
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
11430
- fs21.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
11724
+ const dir = path24.dirname(AUDIT_LOG_FILE);
11725
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
11726
+ fs22.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
11431
11727
  } catch {
11432
11728
  }
11433
11729
  }
11434
11730
  function getAuditHistory(limit = 20) {
11435
11731
  try {
11436
- if (!fs21.existsSync(AUDIT_LOG_FILE)) return [];
11437
- const lines = fs21.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
11732
+ if (!fs22.existsSync(AUDIT_LOG_FILE)) return [];
11733
+ const lines = fs22.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
11438
11734
  if (lines.length === 1 && lines[0] === "") return [];
11439
11735
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
11440
11736
  } catch {
@@ -11443,7 +11739,7 @@ function getAuditHistory(limit = 20) {
11443
11739
  }
11444
11740
  function getOrgName() {
11445
11741
  try {
11446
- if (fs21.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
11742
+ if (fs22.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
11447
11743
  } catch {
11448
11744
  }
11449
11745
  return null;
@@ -11451,8 +11747,8 @@ function getOrgName() {
11451
11747
  function writeGlobalSetting(key, value) {
11452
11748
  let config = {};
11453
11749
  try {
11454
- if (fs21.existsSync(GLOBAL_CONFIG_FILE)) {
11455
- config = JSON.parse(fs21.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
11750
+ if (fs22.existsSync(GLOBAL_CONFIG_FILE)) {
11751
+ config = JSON.parse(fs22.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
11456
11752
  }
11457
11753
  } catch {
11458
11754
  }
@@ -11464,8 +11760,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
11464
11760
  try {
11465
11761
  let trust = { entries: [] };
11466
11762
  try {
11467
- if (fs21.existsSync(TRUST_FILE2))
11468
- trust = JSON.parse(fs21.readFileSync(TRUST_FILE2, "utf-8"));
11763
+ if (fs22.existsSync(TRUST_FILE2))
11764
+ trust = JSON.parse(fs22.readFileSync(TRUST_FILE2, "utf-8"));
11469
11765
  } catch {
11470
11766
  }
11471
11767
  trust.entries = trust.entries.filter(
@@ -11482,8 +11778,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
11482
11778
  }
11483
11779
  function readPersistentDecisions() {
11484
11780
  try {
11485
- if (fs21.existsSync(DECISIONS_FILE)) {
11486
- return JSON.parse(fs21.readFileSync(DECISIONS_FILE, "utf-8"));
11781
+ if (fs22.existsSync(DECISIONS_FILE)) {
11782
+ return JSON.parse(fs22.readFileSync(DECISIONS_FILE, "utf-8"));
11487
11783
  }
11488
11784
  } catch {
11489
11785
  }
@@ -11511,7 +11807,7 @@ function estimateToolCost(tool, args) {
11511
11807
  const filePath = a.file_path ?? a.path;
11512
11808
  if (filePath) {
11513
11809
  try {
11514
- const bytes = fs21.statSync(filePath).size;
11810
+ const bytes = fs22.statSync(filePath).size;
11515
11811
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
11516
11812
  } catch {
11517
11813
  }
@@ -11582,7 +11878,7 @@ function abandonPending() {
11582
11878
  });
11583
11879
  if (autoStarted) {
11584
11880
  try {
11585
- fs21.unlinkSync(DAEMON_PID_FILE);
11881
+ fs22.unlinkSync(DAEMON_PID_FILE);
11586
11882
  } catch {
11587
11883
  }
11588
11884
  setTimeout(() => {
@@ -11593,8 +11889,8 @@ function abandonPending() {
11593
11889
  }
11594
11890
  function logActivitySocket(msg) {
11595
11891
  try {
11596
- fs21.appendFileSync(
11597
- path23.join(homeDir, ".node9", "hook-debug.log"),
11892
+ fs22.appendFileSync(
11893
+ path24.join(homeDir, ".node9", "hook-debug.log"),
11598
11894
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
11599
11895
  `
11600
11896
  );
@@ -11616,13 +11912,13 @@ function shouldRebind(now = Date.now()) {
11616
11912
  function startActivitySocket() {
11617
11913
  bindActivitySocket();
11618
11914
  activityHealthInterval = setInterval(() => {
11619
- if (!fs21.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
11915
+ if (!fs22.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
11620
11916
  }, ACTIVITY_HEALTH_PROBE_MS);
11621
11917
  activityHealthInterval.unref();
11622
11918
  process.on("exit", () => {
11623
11919
  if (activityHealthInterval) clearInterval(activityHealthInterval);
11624
11920
  try {
11625
- fs21.unlinkSync(ACTIVITY_SOCKET_PATH2);
11921
+ fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
11626
11922
  } catch {
11627
11923
  }
11628
11924
  });
@@ -11650,7 +11946,7 @@ function attemptRebind(reason) {
11650
11946
  }
11651
11947
  function bindActivitySocket() {
11652
11948
  try {
11653
- fs21.unlinkSync(ACTIVITY_SOCKET_PATH2);
11949
+ fs22.unlinkSync(ACTIVITY_SOCKET_PATH2);
11654
11950
  } catch {
11655
11951
  }
11656
11952
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -11763,14 +12059,14 @@ var init_state2 = __esm({
11763
12059
  init_taint_store();
11764
12060
  init_session_counters();
11765
12061
  init_session_history();
11766
- homeDir = os19.homedir();
11767
- DAEMON_PID_FILE = path23.join(homeDir, ".node9", "daemon.pid");
11768
- DECISIONS_FILE = path23.join(homeDir, ".node9", "decisions.json");
11769
- AUDIT_LOG_FILE = path23.join(homeDir, ".node9", "audit.log");
11770
- TRUST_FILE2 = path23.join(homeDir, ".node9", "trust.json");
11771
- GLOBAL_CONFIG_FILE = path23.join(homeDir, ".node9", "config.json");
11772
- CREDENTIALS_FILE = path23.join(homeDir, ".node9", "credentials.json");
11773
- INSIGHT_COUNTS_FILE = path23.join(homeDir, ".node9", "insight-counts.json");
12062
+ homeDir = os20.homedir();
12063
+ DAEMON_PID_FILE = path24.join(homeDir, ".node9", "daemon.pid");
12064
+ DECISIONS_FILE = path24.join(homeDir, ".node9", "decisions.json");
12065
+ AUDIT_LOG_FILE = path24.join(homeDir, ".node9", "audit.log");
12066
+ TRUST_FILE2 = path24.join(homeDir, ".node9", "trust.json");
12067
+ GLOBAL_CONFIG_FILE = path24.join(homeDir, ".node9", "config.json");
12068
+ CREDENTIALS_FILE = path24.join(homeDir, ".node9", "credentials.json");
12069
+ INSIGHT_COUNTS_FILE = path24.join(homeDir, ".node9", "insight-counts.json");
11774
12070
  pending = /* @__PURE__ */ new Map();
11775
12071
  sseClients = /* @__PURE__ */ new Set();
11776
12072
  suggestionTracker = new SuggestionTracker(3);
@@ -11787,7 +12083,7 @@ var init_state2 = __esm({
11787
12083
  "2h": 2 * 60 * 6e4
11788
12084
  };
11789
12085
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
11790
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path23.join(os19.tmpdir(), "node9-activity.sock");
12086
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path24.join(os20.tmpdir(), "node9-activity.sock");
11791
12087
  ACTIVITY_RING_SIZE = 100;
11792
12088
  activityRing = [];
11793
12089
  LARGE_RESPONSE_RING_SIZE = 20;
@@ -11826,10 +12122,10 @@ var init_state2 = __esm({
11826
12122
  });
11827
12123
 
11828
12124
  // src/daemon/sync.ts
11829
- import fs22 from "fs";
12125
+ import fs23 from "fs";
11830
12126
  import https2 from "https";
11831
- import os20 from "os";
11832
- import path24 from "path";
12127
+ import os21 from "os";
12128
+ import path25 from "path";
11833
12129
  function emptySignals3() {
11834
12130
  return {
11835
12131
  dlpFindings: 0,
@@ -11869,8 +12165,8 @@ function readCredentials() {
11869
12165
  };
11870
12166
  }
11871
12167
  try {
11872
- const credPath = path24.join(os20.homedir(), ".node9", "credentials.json");
11873
- const creds = JSON.parse(fs22.readFileSync(credPath, "utf-8"));
12168
+ const credPath = path25.join(os21.homedir(), ".node9", "credentials.json");
12169
+ const creds = JSON.parse(fs23.readFileSync(credPath, "utf-8"));
11874
12170
  const profileName = process.env.NODE9_PROFILE ?? "default";
11875
12171
  const profile = creds[profileName];
11876
12172
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -11896,7 +12192,7 @@ function readCredentials() {
11896
12192
  }
11897
12193
  function readCachedEtag() {
11898
12194
  try {
11899
- const raw = JSON.parse(fs22.readFileSync(rulesCacheFile(), "utf-8"));
12195
+ const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
11900
12196
  return typeof raw.etag === "string" ? raw.etag : void 0;
11901
12197
  } catch {
11902
12198
  return void 0;
@@ -11957,9 +12253,9 @@ function extractRules(body) {
11957
12253
  return [];
11958
12254
  }
11959
12255
  function writeCache2(cache) {
11960
- const dir = path24.dirname(rulesCacheFile());
11961
- if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
11962
- fs22.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
12256
+ const dir = path25.dirname(rulesCacheFile());
12257
+ if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
12258
+ fs23.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
11963
12259
  }
11964
12260
  async function syncOnce() {
11965
12261
  const creds = readCredentials();
@@ -12116,7 +12412,7 @@ async function runCloudSync() {
12116
12412
  }
12117
12413
  function getCloudSyncStatus() {
12118
12414
  try {
12119
- const raw = JSON.parse(fs22.readFileSync(rulesCacheFile(), "utf-8"));
12415
+ const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
12120
12416
  if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
12121
12417
  return {
12122
12418
  cached: true,
@@ -12133,7 +12429,7 @@ function getCloudSyncStatus() {
12133
12429
  }
12134
12430
  function getCloudRules() {
12135
12431
  try {
12136
- const raw = JSON.parse(fs22.readFileSync(rulesCacheFile(), "utf-8"));
12432
+ const raw = JSON.parse(fs23.readFileSync(rulesCacheFile(), "utf-8"));
12137
12433
  return Array.isArray(raw.rules) ? raw.rules : null;
12138
12434
  } catch {
12139
12435
  return null;
@@ -12189,7 +12485,7 @@ var init_sync = __esm({
12189
12485
  loop: "loops",
12190
12486
  "long-output-redacted": "longOutputRedactions"
12191
12487
  };
12192
- rulesCacheFile = () => path24.join(os20.homedir(), ".node9", "rules-cache.json");
12488
+ rulesCacheFile = () => path25.join(os21.homedir(), ".node9", "rules-cache.json");
12193
12489
  DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
12194
12490
  DEFAULT_INTERVAL_HOURS = 5;
12195
12491
  MIN_INTERVAL_HOURS = 1;
@@ -12200,73 +12496,73 @@ var init_sync = __esm({
12200
12496
  });
12201
12497
 
12202
12498
  // src/daemon/dlp-scanner.ts
12203
- import fs23 from "fs";
12204
- import path25 from "path";
12205
- import os21 from "os";
12499
+ import fs24 from "fs";
12500
+ import path26 from "path";
12501
+ import os22 from "os";
12206
12502
  function loadIndex() {
12207
12503
  try {
12208
- return JSON.parse(fs23.readFileSync(INDEX_FILE, "utf-8"));
12504
+ return JSON.parse(fs24.readFileSync(INDEX_FILE, "utf-8"));
12209
12505
  } catch {
12210
12506
  return {};
12211
12507
  }
12212
12508
  }
12213
12509
  function saveIndex(index) {
12214
12510
  try {
12215
- fs23.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
12511
+ fs24.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
12216
12512
  } catch {
12217
12513
  }
12218
12514
  }
12219
12515
  function appendAuditEntry(entry) {
12220
12516
  try {
12221
- fs23.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
12517
+ fs24.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
12222
12518
  } catch {
12223
12519
  }
12224
12520
  }
12225
12521
  function runDlpScan() {
12226
- if (!fs23.existsSync(PROJECTS_DIR2)) return;
12522
+ if (!fs24.existsSync(PROJECTS_DIR2)) return;
12227
12523
  const index = loadIndex();
12228
12524
  let updated = false;
12229
12525
  let projDirs;
12230
12526
  try {
12231
- projDirs = fs23.readdirSync(PROJECTS_DIR2);
12527
+ projDirs = fs24.readdirSync(PROJECTS_DIR2);
12232
12528
  } catch {
12233
12529
  return;
12234
12530
  }
12235
12531
  for (const proj of projDirs) {
12236
- const projPath = path25.join(PROJECTS_DIR2, proj);
12532
+ const projPath = path26.join(PROJECTS_DIR2, proj);
12237
12533
  try {
12238
- if (!fs23.lstatSync(projPath).isDirectory()) continue;
12239
- const real = fs23.realpathSync(projPath);
12240
- if (!real.startsWith(PROJECTS_DIR2 + path25.sep) && real !== PROJECTS_DIR2) continue;
12534
+ if (!fs24.lstatSync(projPath).isDirectory()) continue;
12535
+ const real = fs24.realpathSync(projPath);
12536
+ if (!real.startsWith(PROJECTS_DIR2 + path26.sep) && real !== PROJECTS_DIR2) continue;
12241
12537
  } catch {
12242
12538
  continue;
12243
12539
  }
12244
12540
  let files;
12245
12541
  try {
12246
- files = fs23.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12542
+ files = fs24.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12247
12543
  } catch {
12248
12544
  continue;
12249
12545
  }
12250
12546
  for (const file of files) {
12251
- const filePath = path25.join(projPath, file);
12547
+ const filePath = path26.join(projPath, file);
12252
12548
  const lastOffset = index[filePath] ?? 0;
12253
12549
  let size;
12254
12550
  try {
12255
- size = fs23.statSync(filePath).size;
12551
+ size = fs24.statSync(filePath).size;
12256
12552
  } catch {
12257
12553
  continue;
12258
12554
  }
12259
12555
  if (size <= lastOffset) continue;
12260
12556
  let fd;
12261
12557
  try {
12262
- fd = fs23.openSync(filePath, "r");
12558
+ fd = fs24.openSync(filePath, "r");
12263
12559
  } catch {
12264
12560
  continue;
12265
12561
  }
12266
12562
  try {
12267
12563
  const chunkSize = size - lastOffset;
12268
12564
  const buf = Buffer.alloc(chunkSize);
12269
- fs23.readSync(fd, buf, 0, chunkSize, lastOffset);
12565
+ fs24.readSync(fd, buf, 0, chunkSize, lastOffset);
12270
12566
  const chunk = buf.toString("utf-8");
12271
12567
  for (const line of chunk.split("\n")) {
12272
12568
  if (!line.trim()) continue;
@@ -12286,7 +12582,7 @@ function runDlpScan() {
12286
12582
  if (typeof text !== "string") continue;
12287
12583
  const match = scanText(text);
12288
12584
  if (!match) continue;
12289
- const projLabel = decodeURIComponent(proj).replace(os21.homedir(), "~").slice(0, 40);
12585
+ const projLabel = decodeURIComponent(proj).replace(os22.homedir(), "~").slice(0, 40);
12290
12586
  const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
12291
12587
  appendAuditEntry({
12292
12588
  ts,
@@ -12311,7 +12607,7 @@ Run: node9 report --period 30d`
12311
12607
  updated = true;
12312
12608
  } finally {
12313
12609
  try {
12314
- fs23.closeSync(fd);
12610
+ fs24.closeSync(fd);
12315
12611
  } catch {
12316
12612
  }
12317
12613
  }
@@ -12344,23 +12640,23 @@ var init_dlp_scanner = __esm({
12344
12640
  init_dlp();
12345
12641
  init_native();
12346
12642
  init_state2();
12347
- INDEX_FILE = path25.join(os21.homedir(), ".node9", "dlp-index.json");
12348
- PROJECTS_DIR2 = path25.join(os21.homedir(), ".claude", "projects");
12643
+ INDEX_FILE = path26.join(os22.homedir(), ".node9", "dlp-index.json");
12644
+ PROJECTS_DIR2 = path26.join(os22.homedir(), ".claude", "projects");
12349
12645
  }
12350
12646
  });
12351
12647
 
12352
12648
  // src/daemon/mcp-tools.ts
12353
- import fs24 from "fs";
12354
- import path26 from "path";
12355
- import os22 from "os";
12649
+ import fs25 from "fs";
12650
+ import path27 from "path";
12651
+ import os23 from "os";
12356
12652
  function getMcpToolsFile() {
12357
- return path26.join(os22.homedir(), ".node9", "mcp-tools.json");
12653
+ return path27.join(os23.homedir(), ".node9", "mcp-tools.json");
12358
12654
  }
12359
12655
  function readMcpToolsConfig() {
12360
12656
  try {
12361
12657
  const file = getMcpToolsFile();
12362
- if (!fs24.existsSync(file)) return {};
12363
- const raw = fs24.readFileSync(file, "utf-8");
12658
+ if (!fs25.existsSync(file)) return {};
12659
+ const raw = fs25.readFileSync(file, "utf-8");
12364
12660
  return JSON.parse(raw);
12365
12661
  } catch {
12366
12662
  return {};
@@ -12369,11 +12665,11 @@ function readMcpToolsConfig() {
12369
12665
  function writeMcpToolsConfig(config) {
12370
12666
  try {
12371
12667
  const file = getMcpToolsFile();
12372
- const dir = path26.dirname(file);
12373
- if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
12374
- const tmpPath = `${file}.${os22.hostname()}.${process.pid}.tmp`;
12375
- fs24.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
12376
- fs24.renameSync(tmpPath, file);
12668
+ const dir = path27.dirname(file);
12669
+ if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
12670
+ const tmpPath = `${file}.${os23.hostname()}.${process.pid}.tmp`;
12671
+ fs25.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
12672
+ fs25.renameSync(tmpPath, file);
12377
12673
  } catch (e) {
12378
12674
  console.error("Failed to write mcp-tools.json", e);
12379
12675
  }
@@ -12420,9 +12716,9 @@ var init_mcp_tools = __esm({
12420
12716
 
12421
12717
  // src/daemon/server.ts
12422
12718
  import http from "http";
12423
- import fs25 from "fs";
12424
- import path27 from "path";
12425
- import os23 from "os";
12719
+ import fs26 from "fs";
12720
+ import path28 from "path";
12721
+ import os24 from "os";
12426
12722
  import { randomUUID as randomUUID4 } from "crypto";
12427
12723
  import { spawnSync } from "child_process";
12428
12724
  import chalk6 from "chalk";
@@ -12443,7 +12739,7 @@ function startDaemon() {
12443
12739
  idleTimer = setTimeout(() => {
12444
12740
  if (autoStarted) {
12445
12741
  try {
12446
- fs25.unlinkSync(DAEMON_PID_FILE);
12742
+ fs26.unlinkSync(DAEMON_PID_FILE);
12447
12743
  } catch {
12448
12744
  }
12449
12745
  }
@@ -12588,7 +12884,7 @@ data: ${JSON.stringify(item.data)}
12588
12884
  mcpServer: entry.mcpServer
12589
12885
  });
12590
12886
  }
12591
- const projectCwd = typeof cwd === "string" && path27.isAbsolute(cwd) ? cwd : void 0;
12887
+ const projectCwd = typeof cwd === "string" && path28.isAbsolute(cwd) ? cwd : void 0;
12592
12888
  const projectConfig = getConfig(projectCwd);
12593
12889
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
12594
12890
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -12880,8 +13176,8 @@ data: ${JSON.stringify(item.data)}
12880
13176
  if (!validToken(req)) return res.writeHead(403).end();
12881
13177
  const periodParam = reqUrl.searchParams.get("period") || "7d";
12882
13178
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
12883
- const logPath = path27.join(os23.homedir(), ".node9", "audit.log");
12884
- if (!fs25.existsSync(logPath)) {
13179
+ const logPath = path28.join(os24.homedir(), ".node9", "audit.log");
13180
+ if (!fs26.existsSync(logPath)) {
12885
13181
  res.writeHead(200, { "Content-Type": "application/json" });
12886
13182
  return res.end(
12887
13183
  JSON.stringify({
@@ -12894,7 +13190,7 @@ data: ${JSON.stringify(item.data)}
12894
13190
  );
12895
13191
  }
12896
13192
  try {
12897
- const raw = fs25.readFileSync(logPath, "utf-8");
13193
+ const raw = fs26.readFileSync(logPath, "utf-8");
12898
13194
  const allEntries = raw.split("\n").flatMap((line) => {
12899
13195
  if (!line.trim()) return [];
12900
13196
  try {
@@ -13217,14 +13513,14 @@ data: ${JSON.stringify(item.data)}
13217
13513
  server.on("error", (e) => {
13218
13514
  if (e.code === "EADDRINUSE") {
13219
13515
  try {
13220
- if (fs25.existsSync(DAEMON_PID_FILE)) {
13221
- const { pid } = JSON.parse(fs25.readFileSync(DAEMON_PID_FILE, "utf-8"));
13516
+ if (fs26.existsSync(DAEMON_PID_FILE)) {
13517
+ const { pid } = JSON.parse(fs26.readFileSync(DAEMON_PID_FILE, "utf-8"));
13222
13518
  process.kill(pid, 0);
13223
13519
  return process.exit(0);
13224
13520
  }
13225
13521
  } catch {
13226
13522
  try {
13227
- fs25.unlinkSync(DAEMON_PID_FILE);
13523
+ fs26.unlinkSync(DAEMON_PID_FILE);
13228
13524
  } catch {
13229
13525
  }
13230
13526
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -13312,15 +13608,15 @@ var init_server = __esm({
13312
13608
  });
13313
13609
 
13314
13610
  // src/daemon/service.ts
13315
- import fs26 from "fs";
13316
- import path28 from "path";
13317
- import os24 from "os";
13611
+ import fs27 from "fs";
13612
+ import path29 from "path";
13613
+ import os25 from "os";
13318
13614
  import { spawnSync as spawnSync2, execFileSync } from "child_process";
13319
13615
  function resolveNode9Binary() {
13320
13616
  try {
13321
13617
  const script = process.argv[1];
13322
- if (typeof script === "string" && path28.isAbsolute(script) && fs26.existsSync(script)) {
13323
- return fs26.realpathSync(script);
13618
+ if (typeof script === "string" && path29.isAbsolute(script) && fs27.existsSync(script)) {
13619
+ return fs27.realpathSync(script);
13324
13620
  }
13325
13621
  } catch {
13326
13622
  }
@@ -13338,11 +13634,11 @@ function xmlEscape(s) {
13338
13634
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
13339
13635
  }
13340
13636
  function launchdPlist(binaryPath) {
13341
- const logDir = path28.join(os24.homedir(), ".node9");
13637
+ const logDir = path29.join(os25.homedir(), ".node9");
13342
13638
  const nodePath = xmlEscape(process.execPath);
13343
13639
  const scriptPath = xmlEscape(binaryPath);
13344
- const outLog = xmlEscape(path28.join(logDir, "daemon.log"));
13345
- const errLog = xmlEscape(path28.join(logDir, "daemon-error.log"));
13640
+ const outLog = xmlEscape(path29.join(logDir, "daemon.log"));
13641
+ const errLog = xmlEscape(path29.join(logDir, "daemon-error.log"));
13346
13642
  return `<?xml version="1.0" encoding="UTF-8"?>
13347
13643
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
13348
13644
  <plist version="1.0">
@@ -13375,9 +13671,9 @@ function launchdPlist(binaryPath) {
13375
13671
  `;
13376
13672
  }
13377
13673
  function installLaunchd(binaryPath) {
13378
- const dir = path28.dirname(LAUNCHD_PLIST);
13379
- if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
13380
- fs26.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
13674
+ const dir = path29.dirname(LAUNCHD_PLIST);
13675
+ if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
13676
+ fs27.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
13381
13677
  spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
13382
13678
  const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
13383
13679
  encoding: "utf8",
@@ -13388,13 +13684,13 @@ function installLaunchd(binaryPath) {
13388
13684
  }
13389
13685
  }
13390
13686
  function uninstallLaunchd() {
13391
- if (fs26.existsSync(LAUNCHD_PLIST)) {
13687
+ if (fs27.existsSync(LAUNCHD_PLIST)) {
13392
13688
  spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
13393
- fs26.unlinkSync(LAUNCHD_PLIST);
13689
+ fs27.unlinkSync(LAUNCHD_PLIST);
13394
13690
  }
13395
13691
  }
13396
13692
  function isLaunchdInstalled() {
13397
- return fs26.existsSync(LAUNCHD_PLIST);
13693
+ return fs27.existsSync(LAUNCHD_PLIST);
13398
13694
  }
13399
13695
  function systemdUnit(binaryPath) {
13400
13696
  return `[Unit]
@@ -13413,12 +13709,12 @@ WantedBy=default.target
13413
13709
  `;
13414
13710
  }
13415
13711
  function installSystemd(binaryPath) {
13416
- if (!fs26.existsSync(SYSTEMD_UNIT_DIR)) {
13417
- fs26.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
13712
+ if (!fs27.existsSync(SYSTEMD_UNIT_DIR)) {
13713
+ fs27.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
13418
13714
  }
13419
- fs26.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
13715
+ fs27.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
13420
13716
  try {
13421
- execFileSync("loginctl", ["enable-linger", os24.userInfo().username], { timeout: 3e3 });
13717
+ execFileSync("loginctl", ["enable-linger", os25.userInfo().username], { timeout: 3e3 });
13422
13718
  } catch {
13423
13719
  }
13424
13720
  const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
@@ -13438,23 +13734,23 @@ function installSystemd(binaryPath) {
13438
13734
  }
13439
13735
  }
13440
13736
  function uninstallSystemd() {
13441
- if (fs26.existsSync(SYSTEMD_UNIT)) {
13737
+ if (fs27.existsSync(SYSTEMD_UNIT)) {
13442
13738
  spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
13443
13739
  encoding: "utf8",
13444
13740
  timeout: 5e3
13445
13741
  });
13446
13742
  spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
13447
- fs26.unlinkSync(SYSTEMD_UNIT);
13743
+ fs27.unlinkSync(SYSTEMD_UNIT);
13448
13744
  }
13449
13745
  }
13450
13746
  function isSystemdInstalled() {
13451
- return fs26.existsSync(SYSTEMD_UNIT);
13747
+ return fs27.existsSync(SYSTEMD_UNIT);
13452
13748
  }
13453
13749
  function stopRunningDaemon() {
13454
- const pidFile = path28.join(os24.homedir(), ".node9", "daemon.pid");
13455
- if (!fs26.existsSync(pidFile)) return;
13750
+ const pidFile = path29.join(os25.homedir(), ".node9", "daemon.pid");
13751
+ if (!fs27.existsSync(pidFile)) return;
13456
13752
  try {
13457
- const data = JSON.parse(fs26.readFileSync(pidFile, "utf-8"));
13753
+ const data = JSON.parse(fs27.readFileSync(pidFile, "utf-8"));
13458
13754
  const pid = data.pid;
13459
13755
  const MAX_PID2 = 4194304;
13460
13756
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -13474,7 +13770,7 @@ function stopRunningDaemon() {
13474
13770
  }
13475
13771
  }
13476
13772
  try {
13477
- fs26.unlinkSync(pidFile);
13773
+ fs27.unlinkSync(pidFile);
13478
13774
  } catch {
13479
13775
  }
13480
13776
  } catch {
@@ -13549,19 +13845,19 @@ var init_service = __esm({
13549
13845
  "src/daemon/service.ts"() {
13550
13846
  "use strict";
13551
13847
  LAUNCHD_LABEL = "ai.node9.daemon";
13552
- LAUNCHD_PLIST = path28.join(os24.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
13553
- SYSTEMD_UNIT_DIR = path28.join(os24.homedir(), ".config", "systemd", "user");
13554
- SYSTEMD_UNIT = path28.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
13848
+ LAUNCHD_PLIST = path29.join(os25.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
13849
+ SYSTEMD_UNIT_DIR = path29.join(os25.homedir(), ".config", "systemd", "user");
13850
+ SYSTEMD_UNIT = path29.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
13555
13851
  }
13556
13852
  });
13557
13853
 
13558
13854
  // src/daemon/index.ts
13559
- import fs27 from "fs";
13855
+ import fs28 from "fs";
13560
13856
  import chalk7 from "chalk";
13561
13857
  function stopDaemon() {
13562
- if (!fs27.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
13858
+ if (!fs28.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
13563
13859
  try {
13564
- const data = JSON.parse(fs27.readFileSync(DAEMON_PID_FILE, "utf-8"));
13860
+ const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
13565
13861
  const pid = data.pid;
13566
13862
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
13567
13863
  console.log(chalk7.gray("Cleaned up invalid PID file."));
@@ -13573,7 +13869,7 @@ function stopDaemon() {
13573
13869
  console.log(chalk7.gray("Cleaned up stale PID file."));
13574
13870
  } finally {
13575
13871
  try {
13576
- fs27.unlinkSync(DAEMON_PID_FILE);
13872
+ fs28.unlinkSync(DAEMON_PID_FILE);
13577
13873
  } catch {
13578
13874
  }
13579
13875
  }
@@ -13582,9 +13878,9 @@ function daemonStatus() {
13582
13878
  const serviceInstalled = isDaemonServiceInstalled();
13583
13879
  const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
13584
13880
  let processStatus;
13585
- if (fs27.existsSync(DAEMON_PID_FILE)) {
13881
+ if (fs28.existsSync(DAEMON_PID_FILE)) {
13586
13882
  try {
13587
- const data = JSON.parse(fs27.readFileSync(DAEMON_PID_FILE, "utf-8"));
13883
+ const data = JSON.parse(fs28.readFileSync(DAEMON_PID_FILE, "utf-8"));
13588
13884
  const pid = data.pid;
13589
13885
  const port = data.port;
13590
13886
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -13629,7 +13925,7 @@ __export(tail_exports, {
13629
13925
  });
13630
13926
  import http2 from "http";
13631
13927
  import chalk29 from "chalk";
13632
- import fs45 from "fs";
13928
+ import fs46 from "fs";
13633
13929
  import os41 from "os";
13634
13930
  import path47 from "path";
13635
13931
  import readline6 from "readline";
@@ -13656,19 +13952,19 @@ function getModelContextLimit(model) {
13656
13952
  }
13657
13953
  function readSessionUsage() {
13658
13954
  const projectsDir = path47.join(os41.homedir(), ".claude", "projects");
13659
- if (!fs45.existsSync(projectsDir)) return null;
13955
+ if (!fs46.existsSync(projectsDir)) return null;
13660
13956
  let latestFile = null;
13661
13957
  let latestMtime = 0;
13662
13958
  try {
13663
- for (const dir of fs45.readdirSync(projectsDir)) {
13959
+ for (const dir of fs46.readdirSync(projectsDir)) {
13664
13960
  const dirPath = path47.join(projectsDir, dir);
13665
13961
  try {
13666
- if (!fs45.statSync(dirPath).isDirectory()) continue;
13667
- for (const file of fs45.readdirSync(dirPath)) {
13962
+ if (!fs46.statSync(dirPath).isDirectory()) continue;
13963
+ for (const file of fs46.readdirSync(dirPath)) {
13668
13964
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
13669
13965
  const filePath = path47.join(dirPath, file);
13670
13966
  try {
13671
- const mtime = fs45.statSync(filePath).mtimeMs;
13967
+ const mtime = fs46.statSync(filePath).mtimeMs;
13672
13968
  if (mtime > latestMtime) {
13673
13969
  latestMtime = mtime;
13674
13970
  latestFile = filePath;
@@ -13683,7 +13979,7 @@ function readSessionUsage() {
13683
13979
  }
13684
13980
  if (!latestFile) return null;
13685
13981
  try {
13686
- const lines = fs45.readFileSync(latestFile, "utf-8").split("\n");
13982
+ const lines = fs46.readFileSync(latestFile, "utf-8").split("\n");
13687
13983
  let lastModel = "";
13688
13984
  let lastInput = 0;
13689
13985
  let lastOutput = 0;
@@ -13783,9 +14079,9 @@ function renderPending(activity) {
13783
14079
  }
13784
14080
  async function ensureDaemon() {
13785
14081
  let pidPort = null;
13786
- if (fs45.existsSync(PID_FILE)) {
14082
+ if (fs46.existsSync(PID_FILE)) {
13787
14083
  try {
13788
- const { port } = JSON.parse(fs45.readFileSync(PID_FILE, "utf-8"));
14084
+ const { port } = JSON.parse(fs46.readFileSync(PID_FILE, "utf-8"));
13789
14085
  pidPort = port;
13790
14086
  } catch {
13791
14087
  console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -13943,7 +14239,7 @@ function buildRecoveryCardLines(req) {
13943
14239
  function readApproversFromDisk() {
13944
14240
  const configPath = path47.join(os41.homedir(), ".node9", "config.json");
13945
14241
  try {
13946
- const raw = JSON.parse(fs45.readFileSync(configPath, "utf-8"));
14242
+ const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
13947
14243
  const settings = raw.settings ?? {};
13948
14244
  return settings.approvers ?? {};
13949
14245
  } catch {
@@ -13961,13 +14257,13 @@ function approverStatusLine() {
13961
14257
  function toggleApprover(channel) {
13962
14258
  const configPath = path47.join(os41.homedir(), ".node9", "config.json");
13963
14259
  try {
13964
- const raw = JSON.parse(fs45.readFileSync(configPath, "utf-8"));
14260
+ const raw = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
13965
14261
  const settings = raw.settings ?? {};
13966
14262
  const approvers = settings.approvers ?? {};
13967
14263
  approvers[channel] = approvers[channel] === false;
13968
14264
  settings.approvers = approvers;
13969
14265
  raw.settings = settings;
13970
- fs45.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
14266
+ fs46.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
13971
14267
  } catch (err2) {
13972
14268
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
13973
14269
  `);
@@ -14139,7 +14435,7 @@ async function startTail(options = {}) {
14139
14435
  }
14140
14436
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
14141
14437
  try {
14142
- fs45.appendFileSync(
14438
+ fs46.appendFileSync(
14143
14439
  path47.join(os41.homedir(), ".node9", "hook-debug.log"),
14144
14440
  `[tail] POST /decision failed: ${String(err2)}
14145
14441
  `
@@ -14206,7 +14502,7 @@ async function startTail(options = {}) {
14206
14502
  }
14207
14503
  const auditLog = path47.join(os41.homedir(), ".node9", "audit.log");
14208
14504
  try {
14209
- const unackedDlp = fs45.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
14505
+ const unackedDlp = fs46.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
14210
14506
  if (unackedDlp > 0) {
14211
14507
  console.log("");
14212
14508
  console.log(
@@ -14246,7 +14542,7 @@ async function startTail(options = {}) {
14246
14542
  if (stallWarned) return;
14247
14543
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
14248
14544
  try {
14249
- const auditMtime = fs45.statSync(auditLog).mtimeMs;
14545
+ const auditMtime = fs46.statSync(auditLog).mtimeMs;
14250
14546
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
14251
14547
  console.log("");
14252
14548
  console.log(
@@ -14485,7 +14781,7 @@ __export(hud_exports, {
14485
14781
  main: () => main,
14486
14782
  renderEnvironmentLine: () => renderEnvironmentLine
14487
14783
  });
14488
- import fs46 from "fs";
14784
+ import fs47 from "fs";
14489
14785
  import path48 from "path";
14490
14786
  import os42 from "os";
14491
14787
  import http3 from "http";
@@ -14563,9 +14859,9 @@ function formatTimeLeft(resetsAt) {
14563
14859
  return ` (${m}m left)`;
14564
14860
  }
14565
14861
  function safeReadJson(filePath) {
14566
- if (!fs46.existsSync(filePath)) return null;
14862
+ if (!fs47.existsSync(filePath)) return null;
14567
14863
  try {
14568
- return JSON.parse(fs46.readFileSync(filePath, "utf-8"));
14864
+ return JSON.parse(fs47.readFileSync(filePath, "utf-8"));
14569
14865
  } catch {
14570
14866
  return null;
14571
14867
  }
@@ -14586,10 +14882,10 @@ function countHooksInFile(filePath) {
14586
14882
  return Object.keys(cfg.hooks).length;
14587
14883
  }
14588
14884
  function countRulesInDir(rulesDir) {
14589
- if (!fs46.existsSync(rulesDir)) return 0;
14885
+ if (!fs47.existsSync(rulesDir)) return 0;
14590
14886
  let count = 0;
14591
14887
  try {
14592
- for (const entry of fs46.readdirSync(rulesDir, { withFileTypes: true })) {
14888
+ for (const entry of fs47.readdirSync(rulesDir, { withFileTypes: true })) {
14593
14889
  if (entry.isDirectory()) {
14594
14890
  count += countRulesInDir(path48.join(rulesDir, entry.name));
14595
14891
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -14615,7 +14911,7 @@ function countConfigs(cwd) {
14615
14911
  let hooksCount = 0;
14616
14912
  const userMcpServers = /* @__PURE__ */ new Set();
14617
14913
  const projectMcpServers = /* @__PURE__ */ new Set();
14618
- if (fs46.existsSync(path48.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
14914
+ if (fs47.existsSync(path48.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
14619
14915
  rulesCount += countRulesInDir(path48.join(claudeDir, "rules"));
14620
14916
  const userSettings = path48.join(claudeDir, "settings.json");
14621
14917
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
@@ -14626,18 +14922,18 @@ function countConfigs(cwd) {
14626
14922
  userMcpServers.delete(name);
14627
14923
  }
14628
14924
  if (cwd) {
14629
- if (fs46.existsSync(path48.join(cwd, "CLAUDE.md"))) claudeMdCount++;
14630
- if (fs46.existsSync(path48.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
14925
+ if (fs47.existsSync(path48.join(cwd, "CLAUDE.md"))) claudeMdCount++;
14926
+ if (fs47.existsSync(path48.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
14631
14927
  const projectClaudeDir = path48.join(cwd, ".claude");
14632
14928
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
14633
14929
  if (!overlapsUserScope) {
14634
- if (fs46.existsSync(path48.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
14930
+ if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
14635
14931
  rulesCount += countRulesInDir(path48.join(projectClaudeDir, "rules"));
14636
14932
  const projSettings = path48.join(projectClaudeDir, "settings.json");
14637
14933
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
14638
14934
  hooksCount += countHooksInFile(projSettings);
14639
14935
  }
14640
- if (fs46.existsSync(path48.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
14936
+ if (fs47.existsSync(path48.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
14641
14937
  const localSettings = path48.join(projectClaudeDir, "settings.local.json");
14642
14938
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
14643
14939
  hooksCount += countHooksInFile(localSettings);
@@ -14675,11 +14971,11 @@ function readActiveShieldsHud() {
14675
14971
  }
14676
14972
  try {
14677
14973
  const shieldsPath = path48.join(os42.homedir(), ".node9", "shields.json");
14678
- if (!fs46.existsSync(shieldsPath)) {
14974
+ if (!fs47.existsSync(shieldsPath)) {
14679
14975
  shieldsCache = { value: [], ts: now };
14680
14976
  return [];
14681
14977
  }
14682
- const parsed = JSON.parse(fs46.readFileSync(shieldsPath, "utf-8"));
14978
+ const parsed = JSON.parse(fs47.readFileSync(shieldsPath, "utf-8"));
14683
14979
  if (!Array.isArray(parsed.active)) {
14684
14980
  shieldsCache = { value: [], ts: now };
14685
14981
  return [];
@@ -14781,17 +15077,17 @@ function renderContextLine(stdin) {
14781
15077
  async function main() {
14782
15078
  try {
14783
15079
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
14784
- if (fs46.existsSync(path48.join(os42.homedir(), ".node9", "hud-debug"))) {
15080
+ if (fs47.existsSync(path48.join(os42.homedir(), ".node9", "hud-debug"))) {
14785
15081
  try {
14786
15082
  const logPath = path48.join(os42.homedir(), ".node9", "hud-debug.log");
14787
15083
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
14788
15084
  let size = 0;
14789
15085
  try {
14790
- size = fs46.statSync(logPath).size;
15086
+ size = fs47.statSync(logPath).size;
14791
15087
  } catch {
14792
15088
  }
14793
15089
  if (size < MAX_LOG_SIZE) {
14794
- fs46.appendFileSync(
15090
+ fs47.appendFileSync(
14795
15091
  logPath,
14796
15092
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
14797
15093
  );
@@ -14815,8 +15111,8 @@ async function main() {
14815
15111
  path48.join(cwd, "node9.config.json"),
14816
15112
  path48.join(os42.homedir(), ".node9", "config.json")
14817
15113
  ]) {
14818
- if (!fs46.existsSync(configPath)) continue;
14819
- const cfg = JSON.parse(fs46.readFileSync(configPath, "utf-8"));
15114
+ if (!fs47.existsSync(configPath)) continue;
15115
+ const cfg = JSON.parse(fs47.readFileSync(configPath, "utf-8"));
14820
15116
  const hud = cfg.settings?.hud;
14821
15117
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
14822
15118
  }
@@ -14863,7 +15159,7 @@ init_setup();
14863
15159
  init_daemon2();
14864
15160
  import { Command } from "commander";
14865
15161
  import chalk30 from "chalk";
14866
- import fs47 from "fs";
15162
+ import fs48 from "fs";
14867
15163
  import path49 from "path";
14868
15164
  import os43 from "os";
14869
15165
  import { confirm as confirm2 } from "@inquirer/prompts";
@@ -15048,17 +15344,17 @@ async function runProxy(targetCommand) {
15048
15344
  // src/cli/daemon-starter.ts
15049
15345
  init_daemon();
15050
15346
  import { spawn as spawn3 } from "child_process";
15051
- import path29 from "path";
15052
- import fs28 from "fs";
15347
+ import path30 from "path";
15348
+ import fs29 from "fs";
15053
15349
  function isTestingMode() {
15054
15350
  return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
15055
15351
  }
15056
15352
  async function autoStartDaemonAndWait() {
15057
15353
  if (isTestingMode()) return false;
15058
- if (!path29.isAbsolute(process.argv[1])) return false;
15354
+ if (!path30.isAbsolute(process.argv[1])) return false;
15059
15355
  let resolvedArgv1;
15060
15356
  try {
15061
- resolvedArgv1 = fs28.realpathSync(process.argv[1]);
15357
+ resolvedArgv1 = fs29.realpathSync(process.argv[1]);
15062
15358
  } catch {
15063
15359
  return false;
15064
15360
  }
@@ -15089,19 +15385,19 @@ init_daemon();
15089
15385
  init_config();
15090
15386
  init_policy();
15091
15387
  import chalk9 from "chalk";
15092
- import fs31 from "fs";
15388
+ import fs32 from "fs";
15093
15389
  import { spawn as spawn5 } from "child_process";
15094
- import path32 from "path";
15095
- import os27 from "os";
15390
+ import path33 from "path";
15391
+ import os28 from "os";
15096
15392
 
15097
15393
  // src/undo.ts
15098
15394
  import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
15099
- import crypto3 from "crypto";
15100
- import fs29 from "fs";
15395
+ import crypto4 from "crypto";
15396
+ import fs30 from "fs";
15101
15397
  import net3 from "net";
15102
- import path30 from "path";
15103
- import os25 from "os";
15104
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path30.join(os25.tmpdir(), "node9-activity.sock");
15398
+ import path31 from "path";
15399
+ import os26 from "os";
15400
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path31.join(os26.tmpdir(), "node9-activity.sock");
15105
15401
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
15106
15402
  try {
15107
15403
  const payload = JSON.stringify({
@@ -15121,22 +15417,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
15121
15417
  } catch {
15122
15418
  }
15123
15419
  }
15124
- var SNAPSHOT_STACK_PATH = path30.join(os25.homedir(), ".node9", "snapshots.json");
15125
- var UNDO_LATEST_PATH = path30.join(os25.homedir(), ".node9", "undo_latest.txt");
15420
+ var SNAPSHOT_STACK_PATH = path31.join(os26.homedir(), ".node9", "snapshots.json");
15421
+ var UNDO_LATEST_PATH = path31.join(os26.homedir(), ".node9", "undo_latest.txt");
15126
15422
  var MAX_SNAPSHOTS = 10;
15127
15423
  var GIT_TIMEOUT = 15e3;
15128
15424
  function readStack() {
15129
15425
  try {
15130
- if (fs29.existsSync(SNAPSHOT_STACK_PATH))
15131
- return JSON.parse(fs29.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
15426
+ if (fs30.existsSync(SNAPSHOT_STACK_PATH))
15427
+ return JSON.parse(fs30.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
15132
15428
  } catch {
15133
15429
  }
15134
15430
  return [];
15135
15431
  }
15136
15432
  function writeStack(stack) {
15137
- const dir = path30.dirname(SNAPSHOT_STACK_PATH);
15138
- if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
15139
- fs29.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
15433
+ const dir = path31.dirname(SNAPSHOT_STACK_PATH);
15434
+ if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
15435
+ fs30.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
15140
15436
  }
15141
15437
  function extractFilePath(args) {
15142
15438
  if (!args || typeof args !== "object") return null;
@@ -15156,12 +15452,12 @@ function buildArgsSummary(tool, args) {
15156
15452
  return "";
15157
15453
  }
15158
15454
  function findProjectRoot(filePath) {
15159
- let dir = path30.dirname(filePath);
15455
+ let dir = path31.dirname(filePath);
15160
15456
  while (true) {
15161
- if (fs29.existsSync(path30.join(dir, ".git")) || fs29.existsSync(path30.join(dir, "package.json"))) {
15457
+ if (fs30.existsSync(path31.join(dir, ".git")) || fs30.existsSync(path31.join(dir, "package.json"))) {
15162
15458
  return dir;
15163
15459
  }
15164
- const parent = path30.dirname(dir);
15460
+ const parent = path31.dirname(dir);
15165
15461
  if (parent === dir) return process.cwd();
15166
15462
  dir = parent;
15167
15463
  }
@@ -15169,7 +15465,7 @@ function findProjectRoot(filePath) {
15169
15465
  function normalizeCwdForHash(cwd) {
15170
15466
  let normalized;
15171
15467
  try {
15172
- normalized = fs29.realpathSync(cwd);
15468
+ normalized = fs30.realpathSync(cwd);
15173
15469
  } catch {
15174
15470
  normalized = cwd;
15175
15471
  }
@@ -15178,17 +15474,17 @@ function normalizeCwdForHash(cwd) {
15178
15474
  return normalized;
15179
15475
  }
15180
15476
  function getShadowRepoDir(cwd) {
15181
- const hash = crypto3.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
15182
- return path30.join(os25.homedir(), ".node9", "snapshots", hash);
15477
+ const hash = crypto4.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
15478
+ return path31.join(os26.homedir(), ".node9", "snapshots", hash);
15183
15479
  }
15184
15480
  function cleanOrphanedIndexFiles(shadowDir) {
15185
15481
  try {
15186
15482
  const cutoff = Date.now() - 6e4;
15187
- for (const f of fs29.readdirSync(shadowDir)) {
15483
+ for (const f of fs30.readdirSync(shadowDir)) {
15188
15484
  if (f.startsWith("index_")) {
15189
- const fp = path30.join(shadowDir, f);
15485
+ const fp = path31.join(shadowDir, f);
15190
15486
  try {
15191
- if (fs29.statSync(fp).mtimeMs < cutoff) fs29.unlinkSync(fp);
15487
+ if (fs30.statSync(fp).mtimeMs < cutoff) fs30.unlinkSync(fp);
15192
15488
  } catch {
15193
15489
  }
15194
15490
  }
@@ -15200,7 +15496,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
15200
15496
  const hardcoded = [".git", ".node9"];
15201
15497
  const lines = [...hardcoded, ...ignorePaths].join("\n");
15202
15498
  try {
15203
- fs29.writeFileSync(path30.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
15499
+ fs30.writeFileSync(path31.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
15204
15500
  } catch {
15205
15501
  }
15206
15502
  }
@@ -15213,25 +15509,25 @@ function ensureShadowRepo(shadowDir, cwd) {
15213
15509
  timeout: 3e3
15214
15510
  });
15215
15511
  if (check.status === 0) {
15216
- const ptPath = path30.join(shadowDir, "project-path.txt");
15512
+ const ptPath = path31.join(shadowDir, "project-path.txt");
15217
15513
  try {
15218
- const stored = fs29.readFileSync(ptPath, "utf8").trim();
15514
+ const stored = fs30.readFileSync(ptPath, "utf8").trim();
15219
15515
  if (stored === normalizedCwd) return true;
15220
15516
  if (process.env.NODE9_DEBUG === "1")
15221
15517
  console.error(
15222
15518
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
15223
15519
  );
15224
- fs29.rmSync(shadowDir, { recursive: true, force: true });
15520
+ fs30.rmSync(shadowDir, { recursive: true, force: true });
15225
15521
  } catch {
15226
15522
  try {
15227
- fs29.writeFileSync(ptPath, normalizedCwd, "utf8");
15523
+ fs30.writeFileSync(ptPath, normalizedCwd, "utf8");
15228
15524
  } catch {
15229
15525
  }
15230
15526
  return true;
15231
15527
  }
15232
15528
  }
15233
15529
  try {
15234
- fs29.mkdirSync(shadowDir, { recursive: true });
15530
+ fs30.mkdirSync(shadowDir, { recursive: true });
15235
15531
  } catch {
15236
15532
  }
15237
15533
  const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -15240,7 +15536,7 @@ function ensureShadowRepo(shadowDir, cwd) {
15240
15536
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
15241
15537
  return false;
15242
15538
  }
15243
- const configFile = path30.join(shadowDir, "config");
15539
+ const configFile = path31.join(shadowDir, "config");
15244
15540
  spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
15245
15541
  timeout: 3e3
15246
15542
  });
@@ -15248,7 +15544,7 @@ function ensureShadowRepo(shadowDir, cwd) {
15248
15544
  timeout: 3e3
15249
15545
  });
15250
15546
  try {
15251
- fs29.writeFileSync(path30.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
15547
+ fs30.writeFileSync(path31.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
15252
15548
  } catch {
15253
15549
  }
15254
15550
  return true;
@@ -15271,12 +15567,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15271
15567
  let indexFile = null;
15272
15568
  try {
15273
15569
  const rawFilePath = extractFilePath(args);
15274
- const absFilePath = rawFilePath && path30.isAbsolute(rawFilePath) ? rawFilePath : null;
15570
+ const absFilePath = rawFilePath && path31.isAbsolute(rawFilePath) ? rawFilePath : null;
15275
15571
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
15276
15572
  const shadowDir = getShadowRepoDir(cwd);
15277
15573
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
15278
15574
  writeShadowExcludes(shadowDir, ignorePaths);
15279
- indexFile = path30.join(shadowDir, `index_${process.pid}_${Date.now()}`);
15575
+ indexFile = path31.join(shadowDir, `index_${process.pid}_${Date.now()}`);
15280
15576
  const shadowEnv = {
15281
15577
  ...process.env,
15282
15578
  GIT_DIR: shadowDir,
@@ -15348,7 +15644,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15348
15644
  writeStack(stack);
15349
15645
  const entry = stack[stack.length - 1];
15350
15646
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
15351
- fs29.writeFileSync(UNDO_LATEST_PATH, commitHash);
15647
+ fs30.writeFileSync(UNDO_LATEST_PATH, commitHash);
15352
15648
  if (shouldGc) {
15353
15649
  spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
15354
15650
  }
@@ -15359,7 +15655,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15359
15655
  } finally {
15360
15656
  if (indexFile) {
15361
15657
  try {
15362
- fs29.unlinkSync(indexFile);
15658
+ fs30.unlinkSync(indexFile);
15363
15659
  } catch {
15364
15660
  }
15365
15661
  }
@@ -15435,9 +15731,9 @@ function applyUndo(hash, cwd) {
15435
15731
  timeout: GIT_TIMEOUT
15436
15732
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
15437
15733
  for (const file of [...tracked, ...untracked]) {
15438
- const fullPath = path30.join(dir, file);
15439
- if (!snapshotFiles.has(file) && fs29.existsSync(fullPath)) {
15440
- fs29.unlinkSync(fullPath);
15734
+ const fullPath = path31.join(dir, file);
15735
+ if (!snapshotFiles.has(file) && fs30.existsSync(fullPath)) {
15736
+ fs30.unlinkSync(fullPath);
15441
15737
  }
15442
15738
  }
15443
15739
  return true;
@@ -15447,17 +15743,17 @@ function applyUndo(hash, cwd) {
15447
15743
  }
15448
15744
 
15449
15745
  // src/skill-pin.ts
15450
- import fs30 from "fs";
15451
- import path31 from "path";
15452
- import os26 from "os";
15453
- import crypto4 from "crypto";
15454
- function getPinsFilePath() {
15455
- return path31.join(os26.homedir(), ".node9", "skill-pins.json");
15746
+ import fs31 from "fs";
15747
+ import path32 from "path";
15748
+ import os27 from "os";
15749
+ import crypto5 from "crypto";
15750
+ function getPinsFilePath2() {
15751
+ return path32.join(os27.homedir(), ".node9", "skill-pins.json");
15456
15752
  }
15457
15753
  var MAX_FILES = 5e3;
15458
15754
  var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
15459
15755
  function sha256Bytes(buf) {
15460
- return crypto4.createHash("sha256").update(buf).digest("hex");
15756
+ return crypto5.createHash("sha256").update(buf).digest("hex");
15461
15757
  }
15462
15758
  function walkDir(root) {
15463
15759
  const out = [];
@@ -15466,18 +15762,18 @@ function walkDir(root) {
15466
15762
  if (out.length >= MAX_FILES) return;
15467
15763
  let entries;
15468
15764
  try {
15469
- entries = fs30.readdirSync(dir, { withFileTypes: true });
15765
+ entries = fs31.readdirSync(dir, { withFileTypes: true });
15470
15766
  } catch {
15471
15767
  return;
15472
15768
  }
15473
15769
  entries.sort((a, b) => a.name.localeCompare(b.name));
15474
15770
  for (const entry of entries) {
15475
15771
  if (out.length >= MAX_FILES) return;
15476
- const full = path31.join(dir, entry.name);
15477
- const rel = relDir ? path31.posix.join(relDir, entry.name) : entry.name;
15772
+ const full = path32.join(dir, entry.name);
15773
+ const rel = relDir ? path32.posix.join(relDir, entry.name) : entry.name;
15478
15774
  let lst;
15479
15775
  try {
15480
- lst = fs30.lstatSync(full);
15776
+ lst = fs31.lstatSync(full);
15481
15777
  } catch {
15482
15778
  continue;
15483
15779
  }
@@ -15489,7 +15785,7 @@ function walkDir(root) {
15489
15785
  if (!lst.isFile()) continue;
15490
15786
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
15491
15787
  try {
15492
- const buf = fs30.readFileSync(full);
15788
+ const buf = fs31.readFileSync(full);
15493
15789
  totalBytes += buf.length;
15494
15790
  out.push({ rel, hash: sha256Bytes(buf) });
15495
15791
  } catch {
@@ -15503,32 +15799,32 @@ function walkDir(root) {
15503
15799
  function hashSkillRoot(absPath) {
15504
15800
  let lst;
15505
15801
  try {
15506
- lst = fs30.lstatSync(absPath);
15802
+ lst = fs31.lstatSync(absPath);
15507
15803
  } catch {
15508
15804
  return { exists: false, contentHash: "", fileCount: 0 };
15509
15805
  }
15510
15806
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
15511
15807
  if (lst.isFile()) {
15512
15808
  try {
15513
- return { exists: true, contentHash: sha256Bytes(fs30.readFileSync(absPath)), fileCount: 1 };
15809
+ return { exists: true, contentHash: sha256Bytes(fs31.readFileSync(absPath)), fileCount: 1 };
15514
15810
  } catch {
15515
15811
  return { exists: false, contentHash: "", fileCount: 0 };
15516
15812
  }
15517
15813
  }
15518
15814
  if (lst.isDirectory()) {
15519
15815
  const entries = walkDir(absPath);
15520
- const contentHash = crypto4.createHash("sha256").update(entries.join("\n")).digest("hex");
15816
+ const contentHash = crypto5.createHash("sha256").update(entries.join("\n")).digest("hex");
15521
15817
  return { exists: true, contentHash, fileCount: entries.length };
15522
15818
  }
15523
15819
  return { exists: false, contentHash: "", fileCount: 0 };
15524
15820
  }
15525
15821
  function getRootKey(absPath) {
15526
- return crypto4.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
15822
+ return crypto5.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
15527
15823
  }
15528
15824
  function readSkillPinsSafe() {
15529
- const filePath = getPinsFilePath();
15825
+ const filePath = getPinsFilePath2();
15530
15826
  try {
15531
- const raw = fs30.readFileSync(filePath, "utf-8");
15827
+ const raw = fs31.readFileSync(filePath, "utf-8");
15532
15828
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
15533
15829
  const parsed = JSON.parse(raw);
15534
15830
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -15547,18 +15843,18 @@ function readSkillPins() {
15547
15843
  throw new Error(`[node9] skill pin file is corrupt: ${result.detail}`);
15548
15844
  }
15549
15845
  function writeSkillPins(data) {
15550
- const filePath = getPinsFilePath();
15551
- fs30.mkdirSync(path31.dirname(filePath), { recursive: true });
15552
- const tmp = `${filePath}.${crypto4.randomBytes(6).toString("hex")}.tmp`;
15553
- fs30.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15554
- fs30.renameSync(tmp, filePath);
15846
+ const filePath = getPinsFilePath2();
15847
+ fs31.mkdirSync(path32.dirname(filePath), { recursive: true });
15848
+ const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
15849
+ fs31.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15850
+ fs31.renameSync(tmp, filePath);
15555
15851
  }
15556
- function removePin(rootKey) {
15852
+ function removePin2(rootKey) {
15557
15853
  const pins = readSkillPins();
15558
15854
  delete pins.roots[rootKey];
15559
15855
  writeSkillPins(pins);
15560
15856
  }
15561
- function clearAllPins() {
15857
+ function clearAllPins2() {
15562
15858
  writeSkillPins({ roots: {} });
15563
15859
  }
15564
15860
  function verifyAndPinRoots(roots) {
@@ -15595,43 +15891,48 @@ function verifyAndPinRoots(roots) {
15595
15891
  return { kind: "verified" };
15596
15892
  }
15597
15893
  function defaultSkillRoots(_cwd) {
15598
- const marketplaces = path31.join(os26.homedir(), ".claude", "plugins", "marketplaces");
15894
+ const marketplaces = path32.join(os27.homedir(), ".claude", "plugins", "marketplaces");
15599
15895
  const roots = [];
15600
15896
  let registries;
15601
15897
  try {
15602
- registries = fs30.readdirSync(marketplaces, { withFileTypes: true });
15898
+ registries = fs31.readdirSync(marketplaces, { withFileTypes: true });
15603
15899
  } catch {
15604
15900
  return [];
15605
15901
  }
15606
15902
  for (const registry of registries) {
15607
15903
  if (!registry.isDirectory()) continue;
15608
- const pluginsDir = path31.join(marketplaces, registry.name, "plugins");
15904
+ const pluginsDir = path32.join(marketplaces, registry.name, "plugins");
15609
15905
  let plugins;
15610
15906
  try {
15611
- plugins = fs30.readdirSync(pluginsDir, { withFileTypes: true });
15907
+ plugins = fs31.readdirSync(pluginsDir, { withFileTypes: true });
15612
15908
  } catch {
15613
15909
  continue;
15614
15910
  }
15615
15911
  for (const plugin of plugins) {
15616
15912
  if (!plugin.isDirectory()) continue;
15617
- roots.push(path31.join(pluginsDir, plugin.name));
15913
+ roots.push(path32.join(pluginsDir, plugin.name));
15618
15914
  }
15619
15915
  }
15620
15916
  return roots;
15621
15917
  }
15622
15918
  function resolveUserSkillRoot(entry, cwd) {
15623
15919
  if (!entry) return null;
15624
- if (entry.startsWith("~/") || entry === "~") return path31.join(os26.homedir(), entry.slice(1));
15625
- if (path31.isAbsolute(entry)) return entry;
15626
- if (!cwd || !path31.isAbsolute(cwd)) return null;
15627
- return path31.join(cwd, entry);
15920
+ if (entry.startsWith("~/") || entry === "~") return path32.join(os27.homedir(), entry.slice(1));
15921
+ if (path32.isAbsolute(entry)) return entry;
15922
+ if (!cwd || !path32.isAbsolute(cwd)) return null;
15923
+ return path32.join(cwd, entry);
15628
15924
  }
15629
15925
 
15630
15926
  // src/cli/commands/check.ts
15927
+ init_dlp();
15928
+ init_audit();
15631
15929
  function sanitize2(value) {
15632
15930
  return value.replace(/[\x00-\x1F\x7F]/g, "");
15633
15931
  }
15634
15932
  function detectAiAgent(payload) {
15933
+ if (payload.turn_id !== void 0) {
15934
+ return "Codex";
15935
+ }
15635
15936
  if (payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0) {
15636
15937
  return "Claude Code";
15637
15938
  }
@@ -15666,9 +15967,9 @@ function registerCheckCommand(program2) {
15666
15967
  } catch (err2) {
15667
15968
  const tempConfig = getConfig();
15668
15969
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
15669
- const logPath = path32.join(os27.homedir(), ".node9", "hook-debug.log");
15970
+ const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
15670
15971
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15671
- fs31.appendFileSync(
15972
+ fs32.appendFileSync(
15672
15973
  logPath,
15673
15974
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
15674
15975
  RAW: ${raw}
@@ -15677,15 +15978,76 @@ RAW: ${raw}
15677
15978
  }
15678
15979
  process.exit(0);
15679
15980
  }
15680
- const config = getConfig(payload.cwd || void 0);
15981
+ if (payload.hook_event_name === "UserPromptSubmit") {
15982
+ const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
15983
+ if (process.env.NODE9_DEBUG === "1") {
15984
+ try {
15985
+ const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
15986
+ if (!fs32.existsSync(path33.dirname(logPath)))
15987
+ fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
15988
+ const sanitized = JSON.stringify({
15989
+ ...payload,
15990
+ prompt: `<redacted, ${prompt.length} bytes>`
15991
+ });
15992
+ fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
15993
+ `);
15994
+ } catch {
15995
+ }
15996
+ }
15997
+ if (!prompt) process.exit(0);
15998
+ const dlpMatch = scanArgs({ prompt });
15999
+ if (!dlpMatch) process.exit(0);
16000
+ const agent2 = detectAiAgent(payload);
16001
+ const sessionId2 = typeof payload.session_id === "string" ? payload.session_id : void 0;
16002
+ appendLocalAudit(
16003
+ "UserPromptSubmit",
16004
+ { prompt },
16005
+ "deny",
16006
+ "dlp-block",
16007
+ { agent: agent2, sessionId: sessionId2 },
16008
+ true
16009
+ );
16010
+ const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
16011
+ try {
16012
+ const ttyFd = fs32.openSync("/dev/tty", "w");
16013
+ fs32.writeSync(
16014
+ ttyFd,
16015
+ chalk9.bgRed.white.bold(`
16016
+ \u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
16017
+ `) + chalk9.red(` ${dlpMatch.patternName} detected in your prompt.
16018
+ `) + chalk9.gray(` Match: ${dlpMatch.redactedSample}
16019
+ `) + chalk9.cyan(` Edit the prompt to remove the credential and resubmit.
16020
+
16021
+ `)
16022
+ );
16023
+ fs32.closeSync(ttyFd);
16024
+ } catch {
16025
+ }
16026
+ const isCodex = agent2 === "Codex";
16027
+ process.stdout.write(
16028
+ JSON.stringify({
16029
+ decision: "block",
16030
+ reason,
16031
+ systemMessage: reason,
16032
+ hookSpecificOutput: isCodex ? { hookEventName: "UserPromptSubmit" } : {
16033
+ hookEventName: "UserPromptSubmit",
16034
+ permissionDecision: "deny",
16035
+ permissionDecisionReason: reason
16036
+ }
16037
+ }) + "\n"
16038
+ );
16039
+ process.exit(2);
16040
+ }
16041
+ const safeCwdForConfig = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16042
+ const config = getConfig(safeCwdForConfig);
15681
16043
  if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
15682
16044
  try {
15683
16045
  const scriptPath = process.argv[1];
15684
- if (typeof scriptPath !== "string" || !path32.isAbsolute(scriptPath))
16046
+ if (typeof scriptPath !== "string" || !path33.isAbsolute(scriptPath))
15685
16047
  throw new Error("node9: argv[1] is not an absolute path");
15686
- const resolvedScript = fs31.realpathSync(scriptPath);
15687
- const packageDist = fs31.realpathSync(path32.resolve(__dirname, "../.."));
15688
- if (!resolvedScript.startsWith(packageDist + path32.sep) && resolvedScript !== packageDist)
16048
+ const resolvedScript = fs32.realpathSync(scriptPath);
16049
+ const packageDist = fs32.realpathSync(path33.resolve(__dirname, "../.."));
16050
+ if (!resolvedScript.startsWith(packageDist + path33.sep) && resolvedScript !== packageDist)
15689
16051
  throw new Error(
15690
16052
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
15691
16053
  );
@@ -15707,10 +16069,10 @@ RAW: ${raw}
15707
16069
  });
15708
16070
  d.unref();
15709
16071
  } catch (spawnErr) {
15710
- const logPath = path32.join(os27.homedir(), ".node9", "hook-debug.log");
16072
+ const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
15711
16073
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
15712
16074
  try {
15713
- fs31.appendFileSync(
16075
+ fs32.appendFileSync(
15714
16076
  logPath,
15715
16077
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
15716
16078
  `
@@ -15720,10 +16082,10 @@ RAW: ${raw}
15720
16082
  }
15721
16083
  }
15722
16084
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
15723
- const logPath = path32.join(os27.homedir(), ".node9", "hook-debug.log");
15724
- if (!fs31.existsSync(path32.dirname(logPath)))
15725
- fs31.mkdirSync(path32.dirname(logPath), { recursive: true });
15726
- fs31.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
16085
+ const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
16086
+ if (!fs32.existsSync(path33.dirname(logPath)))
16087
+ fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
16088
+ fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15727
16089
  `);
15728
16090
  }
15729
16091
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -15736,8 +16098,8 @@ RAW: ${raw}
15736
16098
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
15737
16099
  let ttyFd = null;
15738
16100
  try {
15739
- ttyFd = fs31.openSync("/dev/tty", "w");
15740
- const writeTty = (line) => fs31.writeSync(ttyFd, line + "\n");
16101
+ ttyFd = fs32.openSync("/dev/tty", "w");
16102
+ const writeTty = (line) => fs32.writeSync(ttyFd, line + "\n");
15741
16103
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
15742
16104
  writeTty(chalk9.bgRed.white.bold(`
15743
16105
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -15756,7 +16118,7 @@ RAW: ${raw}
15756
16118
  } finally {
15757
16119
  if (ttyFd !== null)
15758
16120
  try {
15759
- fs31.closeSync(ttyFd);
16121
+ fs32.closeSync(ttyFd);
15760
16122
  } catch {
15761
16123
  }
15762
16124
  }
@@ -15792,17 +16154,17 @@ RAW: ${raw}
15792
16154
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
15793
16155
  if (skillPinCfg.enabled && safeSessionId) {
15794
16156
  try {
15795
- const sessionsDir = path32.join(os27.homedir(), ".node9", "skill-sessions");
15796
- const flagPath = path32.join(sessionsDir, `${safeSessionId}.json`);
16157
+ const sessionsDir = path33.join(os28.homedir(), ".node9", "skill-sessions");
16158
+ const flagPath = path33.join(sessionsDir, `${safeSessionId}.json`);
15797
16159
  let flag = null;
15798
16160
  try {
15799
- flag = JSON.parse(fs31.readFileSync(flagPath, "utf-8"));
16161
+ flag = JSON.parse(fs32.readFileSync(flagPath, "utf-8"));
15800
16162
  } catch {
15801
16163
  }
15802
16164
  const writeFlag = (data2) => {
15803
16165
  try {
15804
- fs31.mkdirSync(sessionsDir, { recursive: true });
15805
- fs31.writeFileSync(
16166
+ fs32.mkdirSync(sessionsDir, { recursive: true });
16167
+ fs32.writeFileSync(
15806
16168
  flagPath,
15807
16169
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
15808
16170
  { mode: 384 }
@@ -15813,8 +16175,8 @@ RAW: ${raw}
15813
16175
  const sendSkillWarn = (detail, recoveryCmd) => {
15814
16176
  let ttyFd = null;
15815
16177
  try {
15816
- ttyFd = fs31.openSync("/dev/tty", "w");
15817
- const w = (line) => fs31.writeSync(ttyFd, line + "\n");
16178
+ ttyFd = fs32.openSync("/dev/tty", "w");
16179
+ const w = (line) => fs32.writeSync(ttyFd, line + "\n");
15818
16180
  w(chalk9.yellow(`
15819
16181
  \u26A0\uFE0F Node9: installed skill drift detected`));
15820
16182
  w(chalk9.gray(` ${detail}`));
@@ -15829,7 +16191,7 @@ RAW: ${raw}
15829
16191
  } finally {
15830
16192
  if (ttyFd !== null)
15831
16193
  try {
15832
- fs31.closeSync(ttyFd);
16194
+ fs32.closeSync(ttyFd);
15833
16195
  } catch {
15834
16196
  }
15835
16197
  }
@@ -15845,7 +16207,7 @@ RAW: ${raw}
15845
16207
  return;
15846
16208
  }
15847
16209
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
15848
- const absoluteCwd = typeof payload.cwd === "string" && path32.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16210
+ const absoluteCwd = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15849
16211
  const extraRoots = skillPinCfg.roots;
15850
16212
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
15851
16213
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -15886,10 +16248,10 @@ RAW: ${raw}
15886
16248
  }
15887
16249
  try {
15888
16250
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
15889
- for (const name of fs31.readdirSync(sessionsDir)) {
15890
- const p = path32.join(sessionsDir, name);
16251
+ for (const name of fs32.readdirSync(sessionsDir)) {
16252
+ const p = path33.join(sessionsDir, name);
15891
16253
  try {
15892
- if (fs31.statSync(p).mtimeMs < cutoff) fs31.unlinkSync(p);
16254
+ if (fs32.statSync(p).mtimeMs < cutoff) fs32.unlinkSync(p);
15893
16255
  } catch {
15894
16256
  }
15895
16257
  }
@@ -15899,9 +16261,9 @@ RAW: ${raw}
15899
16261
  } catch (err2) {
15900
16262
  if (process.env.NODE9_DEBUG === "1") {
15901
16263
  try {
15902
- const dbg = path32.join(os27.homedir(), ".node9", "hook-debug.log");
16264
+ const dbg = path33.join(os28.homedir(), ".node9", "hook-debug.log");
15903
16265
  const msg = err2 instanceof Error ? err2.message : String(err2);
15904
- fs31.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
16266
+ fs32.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15905
16267
  `);
15906
16268
  } catch {
15907
16269
  }
@@ -15911,7 +16273,7 @@ RAW: ${raw}
15911
16273
  if (shouldSnapshot(toolName, toolInput, config)) {
15912
16274
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
15913
16275
  }
15914
- const safeCwdForAuth = typeof payload.cwd === "string" && path32.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16276
+ const safeCwdForAuth = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15915
16277
  const result = await authorizeHeadless(toolName, toolInput, meta, {
15916
16278
  cwd: safeCwdForAuth
15917
16279
  });
@@ -15923,12 +16285,12 @@ RAW: ${raw}
15923
16285
  }
15924
16286
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
15925
16287
  try {
15926
- const tty = fs31.openSync("/dev/tty", "w");
15927
- fs31.writeSync(
16288
+ const tty = fs32.openSync("/dev/tty", "w");
16289
+ fs32.writeSync(
15928
16290
  tty,
15929
16291
  chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
15930
16292
  );
15931
- fs31.closeSync(tty);
16293
+ fs32.closeSync(tty);
15932
16294
  } catch {
15933
16295
  }
15934
16296
  const daemonReady = await autoStartDaemonAndWait();
@@ -15955,9 +16317,9 @@ RAW: ${raw}
15955
16317
  });
15956
16318
  } catch (err2) {
15957
16319
  if (process.env.NODE9_DEBUG === "1") {
15958
- const logPath = path32.join(os27.homedir(), ".node9", "hook-debug.log");
16320
+ const logPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
15959
16321
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15960
- fs31.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
16322
+ fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15961
16323
  `);
15962
16324
  }
15963
16325
  process.exit(0);
@@ -15993,9 +16355,9 @@ RAW: ${raw}
15993
16355
  // src/cli/commands/log.ts
15994
16356
  init_audit();
15995
16357
  init_config();
15996
- import fs32 from "fs";
15997
- import path33 from "path";
15998
- import os28 from "os";
16358
+ import fs33 from "fs";
16359
+ import path34 from "path";
16360
+ import os29 from "os";
15999
16361
  init_daemon();
16000
16362
 
16001
16363
  // src/utils/cp-mv-parser.ts
@@ -16061,7 +16423,7 @@ function registerLogCommand(program2) {
16061
16423
  const payload = JSON.parse(raw);
16062
16424
  const tool = sanitize3(payload.tool_name ?? payload.name ?? "unknown");
16063
16425
  const rawInput = payload.tool_input ?? payload.args ?? {};
16064
- 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;
16426
+ 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;
16065
16427
  const entry = {
16066
16428
  ts: (/* @__PURE__ */ new Date()).toISOString(),
16067
16429
  tool,
@@ -16071,10 +16433,10 @@ function registerLogCommand(program2) {
16071
16433
  };
16072
16434
  if (agent) entry.agent = agent;
16073
16435
  if (payload.session_id) entry.sessionId = payload.session_id;
16074
- const logPath = path33.join(os28.homedir(), ".node9", "audit.log");
16075
- if (!fs32.existsSync(path33.dirname(logPath)))
16076
- fs32.mkdirSync(path33.dirname(logPath), { recursive: true });
16077
- fs32.appendFileSync(logPath, JSON.stringify(entry) + "\n");
16436
+ const logPath = path34.join(os29.homedir(), ".node9", "audit.log");
16437
+ if (!fs33.existsSync(path34.dirname(logPath)))
16438
+ fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
16439
+ fs33.appendFileSync(logPath, JSON.stringify(entry) + "\n");
16078
16440
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
16079
16441
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
16080
16442
  if (command) {
@@ -16107,7 +16469,7 @@ function registerLogCommand(program2) {
16107
16469
  }
16108
16470
  }
16109
16471
  }
16110
- const safeCwd = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16472
+ const safeCwd = typeof payload.cwd === "string" && path34.isAbsolute(payload.cwd) ? payload.cwd : void 0;
16111
16473
  const config = getConfig(safeCwd);
16112
16474
  if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
16113
16475
  const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
@@ -16128,9 +16490,9 @@ function registerLogCommand(program2) {
16128
16490
  const msg = err2 instanceof Error ? err2.message : String(err2);
16129
16491
  process.stderr.write(`[Node9] audit log error: ${msg}
16130
16492
  `);
16131
- const debugPath = path33.join(os28.homedir(), ".node9", "hook-debug.log");
16493
+ const debugPath = path34.join(os29.homedir(), ".node9", "hook-debug.log");
16132
16494
  try {
16133
- fs32.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
16495
+ fs33.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
16134
16496
  `);
16135
16497
  } catch {
16136
16498
  }
@@ -16531,13 +16893,13 @@ function registerConfigShowCommand(program2) {
16531
16893
  // src/cli/commands/doctor.ts
16532
16894
  init_daemon();
16533
16895
  import chalk11 from "chalk";
16534
- import fs33 from "fs";
16535
- import path34 from "path";
16536
- import os29 from "os";
16896
+ import fs34 from "fs";
16897
+ import path35 from "path";
16898
+ import os30 from "os";
16537
16899
  import { execSync } from "child_process";
16538
16900
  function registerDoctorCommand(program2, version2) {
16539
16901
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
16540
- const homeDir2 = os29.homedir();
16902
+ const homeDir2 = os30.homedir();
16541
16903
  let failures = 0;
16542
16904
  function pass(msg) {
16543
16905
  console.log(chalk11.green(" \u2705 ") + msg);
@@ -16586,10 +16948,10 @@ function registerDoctorCommand(program2, version2) {
16586
16948
  );
16587
16949
  }
16588
16950
  section("Configuration");
16589
- const globalConfigPath = path34.join(homeDir2, ".node9", "config.json");
16590
- if (fs33.existsSync(globalConfigPath)) {
16951
+ const globalConfigPath = path35.join(homeDir2, ".node9", "config.json");
16952
+ if (fs34.existsSync(globalConfigPath)) {
16591
16953
  try {
16592
- JSON.parse(fs33.readFileSync(globalConfigPath, "utf-8"));
16954
+ JSON.parse(fs34.readFileSync(globalConfigPath, "utf-8"));
16593
16955
  pass("~/.node9/config.json found and valid");
16594
16956
  } catch {
16595
16957
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -16597,10 +16959,10 @@ function registerDoctorCommand(program2, version2) {
16597
16959
  } else {
16598
16960
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
16599
16961
  }
16600
- const projectConfigPath = path34.join(process.cwd(), "node9.config.json");
16601
- if (fs33.existsSync(projectConfigPath)) {
16962
+ const projectConfigPath = path35.join(process.cwd(), "node9.config.json");
16963
+ if (fs34.existsSync(projectConfigPath)) {
16602
16964
  try {
16603
- JSON.parse(fs33.readFileSync(projectConfigPath, "utf-8"));
16965
+ JSON.parse(fs34.readFileSync(projectConfigPath, "utf-8"));
16604
16966
  pass("node9.config.json found and valid (project)");
16605
16967
  } catch {
16606
16968
  fail(
@@ -16609,8 +16971,8 @@ function registerDoctorCommand(program2, version2) {
16609
16971
  );
16610
16972
  }
16611
16973
  }
16612
- const credsPath = path34.join(homeDir2, ".node9", "credentials.json");
16613
- if (fs33.existsSync(credsPath)) {
16974
+ const credsPath = path35.join(homeDir2, ".node9", "credentials.json");
16975
+ if (fs34.existsSync(credsPath)) {
16614
16976
  pass("Cloud credentials found (~/.node9/credentials.json)");
16615
16977
  } else {
16616
16978
  warn(
@@ -16619,10 +16981,10 @@ function registerDoctorCommand(program2, version2) {
16619
16981
  );
16620
16982
  }
16621
16983
  section("Agent Hooks");
16622
- const claudeSettingsPath = path34.join(homeDir2, ".claude", "settings.json");
16623
- if (fs33.existsSync(claudeSettingsPath)) {
16984
+ const claudeSettingsPath = path35.join(homeDir2, ".claude", "settings.json");
16985
+ if (fs34.existsSync(claudeSettingsPath)) {
16624
16986
  try {
16625
- const cs = JSON.parse(fs33.readFileSync(claudeSettingsPath, "utf-8"));
16987
+ const cs = JSON.parse(fs34.readFileSync(claudeSettingsPath, "utf-8"));
16626
16988
  const hasHook = cs.hooks?.PreToolUse?.some(
16627
16989
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16628
16990
  );
@@ -16638,10 +17000,10 @@ function registerDoctorCommand(program2, version2) {
16638
17000
  } else {
16639
17001
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
16640
17002
  }
16641
- const geminiSettingsPath = path34.join(homeDir2, ".gemini", "settings.json");
16642
- if (fs33.existsSync(geminiSettingsPath)) {
17003
+ const geminiSettingsPath = path35.join(homeDir2, ".gemini", "settings.json");
17004
+ if (fs34.existsSync(geminiSettingsPath)) {
16643
17005
  try {
16644
- const gs = JSON.parse(fs33.readFileSync(geminiSettingsPath, "utf-8"));
17006
+ const gs = JSON.parse(fs34.readFileSync(geminiSettingsPath, "utf-8"));
16645
17007
  const hasHook = gs.hooks?.BeforeTool?.some(
16646
17008
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16647
17009
  );
@@ -16657,10 +17019,10 @@ function registerDoctorCommand(program2, version2) {
16657
17019
  } else {
16658
17020
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
16659
17021
  }
16660
- const cursorHooksPath = path34.join(homeDir2, ".cursor", "hooks.json");
16661
- if (fs33.existsSync(cursorHooksPath)) {
17022
+ const cursorHooksPath = path35.join(homeDir2, ".cursor", "hooks.json");
17023
+ if (fs34.existsSync(cursorHooksPath)) {
16662
17024
  try {
16663
- const cur = JSON.parse(fs33.readFileSync(cursorHooksPath, "utf-8"));
17025
+ const cur = JSON.parse(fs34.readFileSync(cursorHooksPath, "utf-8"));
16664
17026
  const hasHook = cur.hooks?.preToolUse?.some(
16665
17027
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
16666
17028
  );
@@ -16700,9 +17062,9 @@ function registerDoctorCommand(program2, version2) {
16700
17062
 
16701
17063
  // src/cli/commands/audit.ts
16702
17064
  import chalk12 from "chalk";
16703
- import fs34 from "fs";
16704
- import path35 from "path";
16705
- import os30 from "os";
17065
+ import fs35 from "fs";
17066
+ import path36 from "path";
17067
+ import os31 from "os";
16706
17068
  function formatRelativeTime(timestamp) {
16707
17069
  const diff = Date.now() - new Date(timestamp).getTime();
16708
17070
  const sec = Math.floor(diff / 1e3);
@@ -16715,14 +17077,14 @@ function formatRelativeTime(timestamp) {
16715
17077
  }
16716
17078
  function registerAuditCommand(program2) {
16717
17079
  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) => {
16718
- const logPath = path35.join(os30.homedir(), ".node9", "audit.log");
16719
- if (!fs34.existsSync(logPath)) {
17080
+ const logPath = path36.join(os31.homedir(), ".node9", "audit.log");
17081
+ if (!fs35.existsSync(logPath)) {
16720
17082
  console.log(
16721
17083
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
16722
17084
  );
16723
17085
  return;
16724
17086
  }
16725
- const raw = fs34.readFileSync(logPath, "utf-8");
17087
+ const raw = fs35.readFileSync(logPath, "utf-8");
16726
17088
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
16727
17089
  let entries = lines.flatMap((line) => {
16728
17090
  try {
@@ -16780,9 +17142,9 @@ import chalk13 from "chalk";
16780
17142
  // src/cli/aggregate/report-audit.ts
16781
17143
  init_costSync();
16782
17144
  init_litellm();
16783
- import fs35 from "fs";
16784
- import os31 from "os";
16785
- import path36 from "path";
17145
+ import fs36 from "fs";
17146
+ import os32 from "os";
17147
+ import path37 from "path";
16786
17148
  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;
16787
17149
  function buildTestTimestamps(allEntries) {
16788
17150
  const testTs = /* @__PURE__ */ new Set();
@@ -16862,8 +17224,8 @@ function getDateRange(period, now) {
16862
17224
  }
16863
17225
  }
16864
17226
  function parseAuditLog(logPath) {
16865
- if (!fs35.existsSync(logPath)) return [];
16866
- const raw = fs35.readFileSync(logPath, "utf-8");
17227
+ if (!fs36.existsSync(logPath)) return [];
17228
+ const raw = fs36.readFileSync(logPath, "utf-8");
16867
17229
  return raw.split("\n").flatMap((line) => {
16868
17230
  if (!line.trim()) return [];
16869
17231
  try {
@@ -16923,25 +17285,25 @@ function freezeClaudeCost(acc) {
16923
17285
  };
16924
17286
  }
16925
17287
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
16926
- const projPath = path36.join(projectsDir, proj);
17288
+ const projPath = path37.join(projectsDir, proj);
16927
17289
  let files;
16928
17290
  try {
16929
- const stat = fs35.statSync(projPath);
17291
+ const stat = fs36.statSync(projPath);
16930
17292
  if (!stat.isDirectory()) return;
16931
- files = fs35.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
17293
+ files = fs36.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16932
17294
  } catch {
16933
17295
  return;
16934
17296
  }
16935
17297
  const startMs = start.getTime();
16936
17298
  for (const file of files) {
16937
- const filePath = path36.join(projPath, file);
17299
+ const filePath = path37.join(projPath, file);
16938
17300
  try {
16939
- if (fs35.statSync(filePath).mtimeMs < startMs) continue;
17301
+ if (fs36.statSync(filePath).mtimeMs < startMs) continue;
16940
17302
  } catch {
16941
17303
  continue;
16942
17304
  }
16943
17305
  try {
16944
- const raw = fs35.readFileSync(filePath, "utf-8");
17306
+ const raw = fs36.readFileSync(filePath, "utf-8");
16945
17307
  for (const line of raw.split("\n")) {
16946
17308
  if (!line.trim()) continue;
16947
17309
  let entry;
@@ -16991,10 +17353,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
16991
17353
  }
16992
17354
  function loadClaudeCost(start, end, projectsDir) {
16993
17355
  const acc = emptyClaudeCostAccumulator();
16994
- if (!fs35.existsSync(projectsDir)) return freezeClaudeCost(acc);
17356
+ if (!fs36.existsSync(projectsDir)) return freezeClaudeCost(acc);
16995
17357
  let dirs;
16996
17358
  try {
16997
- dirs = fs35.readdirSync(projectsDir);
17359
+ dirs = fs36.readdirSync(projectsDir);
16998
17360
  } catch {
16999
17361
  return freezeClaudeCost(acc);
17000
17362
  }
@@ -17006,7 +17368,7 @@ function loadClaudeCost(start, end, projectsDir) {
17006
17368
  function processCodexCostFile(filePath, start, end, acc) {
17007
17369
  let lines;
17008
17370
  try {
17009
- lines = fs35.readFileSync(filePath, "utf-8").split("\n");
17371
+ lines = fs36.readFileSync(filePath, "utf-8").split("\n");
17010
17372
  } catch {
17011
17373
  return;
17012
17374
  }
@@ -17051,31 +17413,31 @@ function processCodexCostFile(filePath, start, end, acc) {
17051
17413
  }
17052
17414
  function listCodexSessionFiles(sessionsBase) {
17053
17415
  const jsonlFiles = [];
17054
- if (!fs35.existsSync(sessionsBase)) return jsonlFiles;
17416
+ if (!fs36.existsSync(sessionsBase)) return jsonlFiles;
17055
17417
  try {
17056
- for (const year of fs35.readdirSync(sessionsBase)) {
17057
- const yearPath = path36.join(sessionsBase, year);
17418
+ for (const year of fs36.readdirSync(sessionsBase)) {
17419
+ const yearPath = path37.join(sessionsBase, year);
17058
17420
  try {
17059
- if (!fs35.statSync(yearPath).isDirectory()) continue;
17421
+ if (!fs36.statSync(yearPath).isDirectory()) continue;
17060
17422
  } catch {
17061
17423
  continue;
17062
17424
  }
17063
- for (const month of fs35.readdirSync(yearPath)) {
17064
- const monthPath = path36.join(yearPath, month);
17425
+ for (const month of fs36.readdirSync(yearPath)) {
17426
+ const monthPath = path37.join(yearPath, month);
17065
17427
  try {
17066
- if (!fs35.statSync(monthPath).isDirectory()) continue;
17428
+ if (!fs36.statSync(monthPath).isDirectory()) continue;
17067
17429
  } catch {
17068
17430
  continue;
17069
17431
  }
17070
- for (const day of fs35.readdirSync(monthPath)) {
17071
- const dayPath = path36.join(monthPath, day);
17432
+ for (const day of fs36.readdirSync(monthPath)) {
17433
+ const dayPath = path37.join(monthPath, day);
17072
17434
  try {
17073
- if (!fs35.statSync(dayPath).isDirectory()) continue;
17435
+ if (!fs36.statSync(dayPath).isDirectory()) continue;
17074
17436
  } catch {
17075
17437
  continue;
17076
17438
  }
17077
- for (const file of fs35.readdirSync(dayPath)) {
17078
- if (file.endsWith(".jsonl")) jsonlFiles.push(path36.join(dayPath, file));
17439
+ for (const file of fs36.readdirSync(dayPath)) {
17440
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path37.join(dayPath, file));
17079
17441
  }
17080
17442
  }
17081
17443
  }
@@ -17128,13 +17490,13 @@ function freezeGeminiCost(acc) {
17128
17490
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
17129
17491
  const startMs = start.getTime();
17130
17492
  try {
17131
- if (fs35.statSync(filePath).mtimeMs < startMs) return;
17493
+ if (fs36.statSync(filePath).mtimeMs < startMs) return;
17132
17494
  } catch {
17133
17495
  return;
17134
17496
  }
17135
17497
  let raw;
17136
17498
  try {
17137
- raw = fs35.readFileSync(filePath, "utf-8");
17499
+ raw = fs36.readFileSync(filePath, "utf-8");
17138
17500
  } catch {
17139
17501
  return;
17140
17502
  }
@@ -17183,30 +17545,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
17183
17545
  const out = [];
17184
17546
  let dirs;
17185
17547
  try {
17186
- if (!fs35.statSync(geminiTmpDir).isDirectory()) return out;
17187
- dirs = fs35.readdirSync(geminiTmpDir);
17548
+ if (!fs36.statSync(geminiTmpDir).isDirectory()) return out;
17549
+ dirs = fs36.readdirSync(geminiTmpDir);
17188
17550
  } catch {
17189
17551
  return out;
17190
17552
  }
17191
17553
  for (const proj of dirs) {
17192
- const chatsDir = path36.join(geminiTmpDir, proj, "chats");
17554
+ const chatsDir = path37.join(geminiTmpDir, proj, "chats");
17193
17555
  let files;
17194
17556
  try {
17195
- if (!fs35.statSync(chatsDir).isDirectory()) continue;
17196
- files = fs35.readdirSync(chatsDir);
17557
+ if (!fs36.statSync(chatsDir).isDirectory()) continue;
17558
+ files = fs36.readdirSync(chatsDir);
17197
17559
  } catch {
17198
17560
  continue;
17199
17561
  }
17200
17562
  for (const f of files) {
17201
17563
  if (!f.endsWith(".jsonl")) continue;
17202
- out.push({ projectKey: proj, file: path36.join(chatsDir, f) });
17564
+ out.push({ projectKey: proj, file: path37.join(chatsDir, f) });
17203
17565
  }
17204
17566
  }
17205
17567
  return out;
17206
17568
  }
17207
17569
  function loadGeminiCost(start, end, geminiTmpDir) {
17208
17570
  const acc = emptyGeminiAccumulator();
17209
- if (!fs35.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
17571
+ if (!fs36.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
17210
17572
  for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
17211
17573
  processGeminiCostFile(file, projectKey, start, end, acc);
17212
17574
  }
@@ -17214,11 +17576,11 @@ function loadGeminiCost(start, end, geminiTmpDir) {
17214
17576
  }
17215
17577
  function aggregateReportFromAudit(period, opts = {}) {
17216
17578
  const now = opts.now ?? /* @__PURE__ */ new Date();
17217
- const auditLogPath = opts.auditLogPath ?? path36.join(os31.homedir(), ".node9", "audit.log");
17218
- const claudeProjectsDir = opts.claudeProjectsDir ?? path36.join(os31.homedir(), ".claude", "projects");
17219
- const codexSessionsDir = opts.codexSessionsDir ?? path36.join(os31.homedir(), ".codex", "sessions");
17220
- const geminiTmpDir = opts.geminiTmpDir ?? path36.join(os31.homedir(), ".gemini", "tmp");
17221
- const hasAuditFile = fs35.existsSync(auditLogPath);
17579
+ const auditLogPath = opts.auditLogPath ?? path37.join(os32.homedir(), ".node9", "audit.log");
17580
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path37.join(os32.homedir(), ".claude", "projects");
17581
+ const codexSessionsDir = opts.codexSessionsDir ?? path37.join(os32.homedir(), ".codex", "sessions");
17582
+ const geminiTmpDir = opts.geminiTmpDir ?? path37.join(os32.homedir(), ".gemini", "tmp");
17583
+ const hasAuditFile = fs36.existsSync(auditLogPath);
17222
17584
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
17223
17585
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
17224
17586
  const { start, end } = getDateRange(period, now);
@@ -17916,12 +18278,12 @@ function registerDaemonCommand(program2) {
17916
18278
  init_core();
17917
18279
  init_daemon();
17918
18280
  import chalk15 from "chalk";
17919
- import fs36 from "fs";
17920
- import path37 from "path";
17921
- import os32 from "os";
18281
+ import fs37 from "fs";
18282
+ import path38 from "path";
18283
+ import os33 from "os";
17922
18284
  function readJson2(filePath) {
17923
18285
  try {
17924
- if (fs36.existsSync(filePath)) return JSON.parse(fs36.readFileSync(filePath, "utf-8"));
18286
+ if (fs37.existsSync(filePath)) return JSON.parse(fs37.readFileSync(filePath, "utf-8"));
17925
18287
  } catch {
17926
18288
  }
17927
18289
  return null;
@@ -17986,28 +18348,28 @@ function registerStatusCommand(program2) {
17986
18348
  console.log("");
17987
18349
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
17988
18350
  console.log(` Mode: ${modeLabel}`);
17989
- const projectConfig = path37.join(process.cwd(), "node9.config.json");
17990
- const globalConfig = path37.join(os32.homedir(), ".node9", "config.json");
18351
+ const projectConfig = path38.join(process.cwd(), "node9.config.json");
18352
+ const globalConfig = path38.join(os33.homedir(), ".node9", "config.json");
17991
18353
  console.log(
17992
- ` Local: ${fs36.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
18354
+ ` Local: ${fs37.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
17993
18355
  );
17994
18356
  console.log(
17995
- ` Global: ${fs36.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
18357
+ ` Global: ${fs37.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
17996
18358
  );
17997
18359
  if (mergedConfig.policy.sandboxPaths.length > 0) {
17998
18360
  console.log(
17999
18361
  ` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
18000
18362
  );
18001
18363
  }
18002
- const homeDir2 = os32.homedir();
18364
+ const homeDir2 = os33.homedir();
18003
18365
  const claudeSettings = readJson2(
18004
- path37.join(homeDir2, ".claude", "settings.json")
18366
+ path38.join(homeDir2, ".claude", "settings.json")
18005
18367
  );
18006
- const claudeConfig = readJson2(path37.join(homeDir2, ".claude.json"));
18368
+ const claudeConfig = readJson2(path38.join(homeDir2, ".claude.json"));
18007
18369
  const geminiSettings = readJson2(
18008
- path37.join(homeDir2, ".gemini", "settings.json")
18370
+ path38.join(homeDir2, ".gemini", "settings.json")
18009
18371
  );
18010
- const cursorConfig = readJson2(path37.join(homeDir2, ".cursor", "mcp.json"));
18372
+ const cursorConfig = readJson2(path38.join(homeDir2, ".cursor", "mcp.json"));
18011
18373
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
18012
18374
  if (agentFound) {
18013
18375
  console.log("");
@@ -18070,9 +18432,9 @@ init_setup();
18070
18432
  init_shields();
18071
18433
  init_service();
18072
18434
  import chalk16 from "chalk";
18073
- import fs37 from "fs";
18074
- import path38 from "path";
18075
- import os33 from "os";
18435
+ import fs38 from "fs";
18436
+ import path39 from "path";
18437
+ import os34 from "os";
18076
18438
  import https4 from "https";
18077
18439
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
18078
18440
  function fireTelemetryPing(agents) {
@@ -18148,15 +18510,15 @@ function registerInitCommand(program2) {
18148
18510
  }
18149
18511
  console.log("");
18150
18512
  }
18151
- const configPath = path38.join(os33.homedir(), ".node9", "config.json");
18152
- if (fs37.existsSync(configPath) && !options.force) {
18513
+ const configPath = path39.join(os34.homedir(), ".node9", "config.json");
18514
+ if (fs38.existsSync(configPath) && !options.force) {
18153
18515
  try {
18154
- const existing = JSON.parse(fs37.readFileSync(configPath, "utf-8"));
18516
+ const existing = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
18155
18517
  const settings = existing.settings ?? {};
18156
18518
  if (settings.mode !== chosenMode) {
18157
18519
  settings.mode = chosenMode;
18158
18520
  existing.settings = settings;
18159
- fs37.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
18521
+ fs38.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
18160
18522
  console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
18161
18523
  } else {
18162
18524
  console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -18169,9 +18531,9 @@ function registerInitCommand(program2) {
18169
18531
  ...DEFAULT_CONFIG,
18170
18532
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
18171
18533
  };
18172
- const dir = path38.dirname(configPath);
18173
- if (!fs37.existsSync(dir)) fs37.mkdirSync(dir, { recursive: true });
18174
- fs37.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
18534
+ const dir = path39.dirname(configPath);
18535
+ if (!fs38.existsSync(dir)) fs38.mkdirSync(dir, { recursive: true });
18536
+ fs38.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
18175
18537
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
18176
18538
  console.log(chalk16.gray(` Mode: ${chosenMode}`));
18177
18539
  }
@@ -18269,7 +18631,7 @@ function registerInitCommand(program2) {
18269
18631
  }
18270
18632
 
18271
18633
  // src/cli/commands/undo.ts
18272
- import path39 from "path";
18634
+ import path40 from "path";
18273
18635
  import chalk18 from "chalk";
18274
18636
 
18275
18637
  // src/tui/undo-navigator.ts
@@ -18428,7 +18790,7 @@ function findMatchingCwd(startDir, history) {
18428
18790
  let dir = startDir;
18429
18791
  while (true) {
18430
18792
  if (cwds.has(dir)) return dir;
18431
- const parent = path39.dirname(dir);
18793
+ const parent = path40.dirname(dir);
18432
18794
  if (parent === dir) return null;
18433
18795
  dir = parent;
18434
18796
  }
@@ -18561,90 +18923,7 @@ import chalk19 from "chalk";
18561
18923
  import { spawn as spawn7 } from "child_process";
18562
18924
  import { execa as execa2 } from "execa";
18563
18925
  init_provenance();
18564
-
18565
- // src/mcp-pin.ts
18566
- import fs38 from "fs";
18567
- import path40 from "path";
18568
- import os34 from "os";
18569
- import crypto5 from "crypto";
18570
- function getPinsFilePath2() {
18571
- return path40.join(os34.homedir(), ".node9", "mcp-pins.json");
18572
- }
18573
- function hashToolDefinitions(tools) {
18574
- const sorted = [...tools].sort((a, b) => {
18575
- const nameA = a.name ?? "";
18576
- const nameB = b.name ?? "";
18577
- return nameA.localeCompare(nameB);
18578
- });
18579
- const canonical = JSON.stringify(sorted);
18580
- return crypto5.createHash("sha256").update(canonical).digest("hex");
18581
- }
18582
- function getServerKey(upstreamCommand) {
18583
- return crypto5.createHash("sha256").update(upstreamCommand).digest("hex").slice(0, 16);
18584
- }
18585
- function readMcpPinsSafe() {
18586
- const filePath = getPinsFilePath2();
18587
- try {
18588
- const raw = fs38.readFileSync(filePath, "utf-8");
18589
- if (!raw.trim()) {
18590
- return { ok: false, reason: "corrupt", detail: "empty file" };
18591
- }
18592
- const parsed = JSON.parse(raw);
18593
- if (!parsed.servers || typeof parsed.servers !== "object" || Array.isArray(parsed.servers)) {
18594
- return { ok: false, reason: "corrupt", detail: "invalid structure: missing servers object" };
18595
- }
18596
- return { ok: true, pins: { servers: parsed.servers } };
18597
- } catch (err2) {
18598
- if (err2.code === "ENOENT") {
18599
- return { ok: false, reason: "missing" };
18600
- }
18601
- return { ok: false, reason: "corrupt", detail: String(err2) };
18602
- }
18603
- }
18604
- function readMcpPins() {
18605
- const result = readMcpPinsSafe();
18606
- if (result.ok) return result.pins;
18607
- if (result.reason === "missing") return { servers: {} };
18608
- throw new Error(`[node9] MCP pin file is corrupt: ${result.detail}`);
18609
- }
18610
- function writeMcpPins(data) {
18611
- const filePath = getPinsFilePath2();
18612
- fs38.mkdirSync(path40.dirname(filePath), { recursive: true });
18613
- const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
18614
- fs38.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
18615
- fs38.renameSync(tmp, filePath);
18616
- }
18617
- function checkPin(serverKey, currentHash) {
18618
- const result = readMcpPinsSafe();
18619
- if (!result.ok) {
18620
- if (result.reason === "missing") return "new";
18621
- return "corrupt";
18622
- }
18623
- const entry = result.pins.servers[serverKey];
18624
- if (!entry) return "new";
18625
- return entry.toolsHash === currentHash ? "match" : "mismatch";
18626
- }
18627
- function updatePin(serverKey, label, toolsHash, toolNames) {
18628
- const pins = readMcpPins();
18629
- pins.servers[serverKey] = {
18630
- label,
18631
- toolsHash,
18632
- toolNames,
18633
- toolCount: toolNames.length,
18634
- pinnedAt: (/* @__PURE__ */ new Date()).toISOString()
18635
- };
18636
- writeMcpPins(pins);
18637
- }
18638
- function removePin2(serverKey) {
18639
- const pins = readMcpPins();
18640
- delete pins.servers[serverKey];
18641
- writeMcpPins(pins);
18642
- }
18643
- function clearAllPins2() {
18644
- writeMcpPins({ servers: {} });
18645
- }
18646
-
18647
- // src/mcp-gateway/index.ts
18926
+ init_mcp_pin();
18648
18927
  init_mcp_tools();
18649
18928
  init_daemon();
18650
18929
  function sanitize4(value) {
@@ -18706,6 +18985,7 @@ function tokenize4(cmd) {
18706
18985
  return tokens;
18707
18986
  }
18708
18987
  async function runMcpGateway(upstreamCommand) {
18988
+ const gatewayCwd = process.cwd();
18709
18989
  const commandParts = tokenize4(upstreamCommand);
18710
18990
  const cmd = commandParts[0];
18711
18991
  const cmdArgs = commandParts.slice(1);
@@ -18928,7 +19208,7 @@ async function runMcpGateway(upstreamCommand) {
18928
19208
  if (parsed.result && Array.isArray(parsed.result.tools)) {
18929
19209
  const tools = parsed.result.tools || [];
18930
19210
  const currentHash = hashToolDefinitions(tools);
18931
- const pinStatus = checkPin(serverKey, currentHash);
19211
+ const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
18932
19212
  const token = getInternalToken();
18933
19213
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
18934
19214
  const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
@@ -19812,27 +20092,46 @@ function registerTrustCommand(program2) {
19812
20092
  }
19813
20093
 
19814
20094
  // src/cli/commands/mcp-pin.ts
20095
+ init_mcp_pin();
19815
20096
  import chalk21 from "chalk";
20097
+ import fs40 from "fs";
19816
20098
  function registerMcpPinCommand(program2) {
19817
20099
  const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
19818
20100
  const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
19819
20101
  pinSubCmd.command("list").description("Show all pinned MCP servers and their tool definition hashes").action(() => {
19820
- const result = readMcpPinsSafe();
19821
- if (!result.ok) {
19822
- if (result.reason === "missing") {
19823
- console.log(chalk21.gray("\nNo MCP servers are pinned yet."));
19824
- console.log(
19825
- chalk21.gray("Pins are created automatically when the MCP gateway first connects.\n")
19826
- );
19827
- return;
20102
+ const found = findPinsFilePath(process.cwd());
20103
+ const homeResult = readMcpPinsSafe();
20104
+ let repoEntries = {};
20105
+ let repoCorrupt = false;
20106
+ if (found.source === "repo") {
20107
+ try {
20108
+ const raw = fs40.readFileSync(found.path, "utf-8");
20109
+ const parsed = JSON.parse(raw);
20110
+ repoEntries = parsed.servers ?? {};
20111
+ } catch {
20112
+ repoCorrupt = true;
19828
20113
  }
20114
+ }
20115
+ if (repoCorrupt) {
19829
20116
  console.error(chalk21.red(`
19830
- \u274C Pin file is corrupt: ${result.detail}`));
20117
+ \u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
20118
+ process.exit(1);
20119
+ }
20120
+ if (!homeResult.ok && homeResult.reason === "corrupt") {
20121
+ console.error(chalk21.red(`
20122
+ \u274C Home pin file is corrupt: ${homeResult.detail}`));
19831
20123
  console.error(chalk21.yellow(" Run: node9 mcp pin reset\n"));
19832
20124
  process.exit(1);
19833
20125
  }
19834
- const entries = Object.entries(result.pins.servers);
19835
- if (entries.length === 0) {
20126
+ const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
20127
+ const merged = /* @__PURE__ */ new Map();
20128
+ for (const [key, entry] of Object.entries(homeEntries)) {
20129
+ merged.set(key, { entry, source: "home" });
20130
+ }
20131
+ for (const [key, entry] of Object.entries(repoEntries)) {
20132
+ merged.set(key, { entry, source: "repo" });
20133
+ }
20134
+ if (merged.size === 0) {
19836
20135
  console.log(chalk21.gray("\nNo MCP servers are pinned yet."));
19837
20136
  console.log(
19838
20137
  chalk21.gray("Pins are created automatically when the MCP gateway first connects.\n")
@@ -19840,13 +20139,47 @@ function registerMcpPinCommand(program2) {
19840
20139
  return;
19841
20140
  }
19842
20141
  console.log(chalk21.bold("\n\u{1F512} Pinned MCP Servers\n"));
19843
- for (const [key, entry] of entries) {
19844
- console.log(` ${chalk21.cyan(key)} ${chalk21.gray(entry.label)}`);
20142
+ const showSource = found.source === "repo";
20143
+ for (const [key, { entry, source }] of merged) {
20144
+ const tag = showSource ? ` ${chalk21.yellow(`[${source}]`)}` : "";
20145
+ console.log(` ${chalk21.cyan(key)}${tag} ${chalk21.gray(entry.label)}`);
19845
20146
  console.log(` Tools (${entry.toolCount}): ${chalk21.white(entry.toolNames.join(", "))}`);
19846
20147
  console.log(` Hash: ${chalk21.gray(entry.toolsHash.slice(0, 16))}...`);
19847
20148
  console.log(` Pinned: ${chalk21.gray(entry.pinnedAt)}`);
19848
20149
  console.log("");
19849
20150
  }
20151
+ if (showSource) {
20152
+ console.log(chalk21.gray(` [repo] entries come from ${found.path}`));
20153
+ console.log(chalk21.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
20154
+ }
20155
+ });
20156
+ pinSubCmd.command("promote <serverKey>").description(
20157
+ "Copy a pin from ~/.node9/mcp-pins.json into <repo>/.node9/mcp-pins.json so teammates share the same vetted baseline"
20158
+ ).action((serverKey) => {
20159
+ try {
20160
+ const { repoPath, created } = promotePin(serverKey, process.cwd());
20161
+ if (created) {
20162
+ console.log(
20163
+ chalk21.green(
20164
+ `
20165
+ \u2705 Created ${repoPath} with the promoted pin for ${chalk21.cyan(serverKey)}.`
20166
+ )
20167
+ );
20168
+ } else {
20169
+ console.log(chalk21.green(`
20170
+ \u2705 Promoted ${chalk21.cyan(serverKey)} into ${repoPath}.`));
20171
+ }
20172
+ console.log(chalk21.gray(" Review the change and commit it:"));
20173
+ console.log(chalk21.cyan(` git add ${repoPath}`));
20174
+ console.log(chalk21.cyan(` git commit -m "pin ${serverKey} (node9)"`));
20175
+ console.log("");
20176
+ } catch (err2) {
20177
+ const msg = err2 instanceof Error ? err2.message : String(err2);
20178
+ console.error(chalk21.red(`
20179
+ \u274C ${msg}
20180
+ `));
20181
+ process.exit(1);
20182
+ }
19850
20183
  });
19851
20184
  pinSubCmd.command("update <serverKey>").description(
19852
20185
  "Remove a pin so the next gateway connection re-pins with current tool definitions"
@@ -19868,7 +20201,7 @@ function registerMcpPinCommand(program2) {
19868
20201
  process.exit(1);
19869
20202
  }
19870
20203
  const label = pins.servers[serverKey].label;
19871
- removePin2(serverKey);
20204
+ removePin(serverKey);
19872
20205
  console.log(chalk21.green(`
19873
20206
  \u{1F513} Pin removed for ${chalk21.cyan(serverKey)}`));
19874
20207
  console.log(chalk21.gray(` Server: ${label}`));
@@ -19881,7 +20214,7 @@ function registerMcpPinCommand(program2) {
19881
20214
  return;
19882
20215
  }
19883
20216
  const count = result.ok ? Object.keys(result.pins.servers).length : "?";
19884
- clearAllPins2();
20217
+ clearAllPins();
19885
20218
  console.log(chalk21.green(`
19886
20219
  \u{1F513} Cleared ${count} MCP pin(s).`));
19887
20220
  console.log(chalk21.gray(" Next connection to each server will re-pin.\n"));
@@ -20064,7 +20397,7 @@ init_scan();
20064
20397
 
20065
20398
  // src/cli/commands/sessions.ts
20066
20399
  import chalk24 from "chalk";
20067
- import fs40 from "fs";
20400
+ import fs41 from "fs";
20068
20401
  import path42 from "path";
20069
20402
  import os36 from "os";
20070
20403
  var CLAUDE_PRICING3 = {
@@ -20182,7 +20515,7 @@ function loadAuditEntries(auditPath) {
20182
20515
  const aPath = auditPath ?? path42.join(os36.homedir(), ".node9", "audit.log");
20183
20516
  let raw;
20184
20517
  try {
20185
- raw = fs40.readFileSync(aPath, "utf-8");
20518
+ raw = fs41.readFileSync(aPath, "utf-8");
20186
20519
  } catch {
20187
20520
  return [];
20188
20521
  }
@@ -20219,7 +20552,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
20219
20552
  }
20220
20553
  function buildGeminiSessions(days, allAuditEntries) {
20221
20554
  const tmpDir = path42.join(os36.homedir(), ".gemini", "tmp");
20222
- if (!fs40.existsSync(tmpDir)) return [];
20555
+ if (!fs41.existsSync(tmpDir)) return [];
20223
20556
  const cutoff = days !== null ? (() => {
20224
20557
  const d = /* @__PURE__ */ new Date();
20225
20558
  d.setDate(d.getDate() - days);
@@ -20228,7 +20561,7 @@ function buildGeminiSessions(days, allAuditEntries) {
20228
20561
  })() : null;
20229
20562
  let slugDirs;
20230
20563
  try {
20231
- slugDirs = fs40.readdirSync(tmpDir);
20564
+ slugDirs = fs41.readdirSync(tmpDir);
20232
20565
  } catch {
20233
20566
  return [];
20234
20567
  }
@@ -20236,27 +20569,27 @@ function buildGeminiSessions(days, allAuditEntries) {
20236
20569
  for (const slug of slugDirs) {
20237
20570
  const slugPath = path42.join(tmpDir, slug);
20238
20571
  try {
20239
- if (!fs40.statSync(slugPath).isDirectory()) continue;
20572
+ if (!fs41.statSync(slugPath).isDirectory()) continue;
20240
20573
  } catch {
20241
20574
  continue;
20242
20575
  }
20243
20576
  let projectRoot = path42.join(os36.homedir(), slug);
20244
20577
  try {
20245
- projectRoot = fs40.readFileSync(path42.join(slugPath, ".project_root"), "utf-8").trim();
20578
+ projectRoot = fs41.readFileSync(path42.join(slugPath, ".project_root"), "utf-8").trim();
20246
20579
  } catch {
20247
20580
  }
20248
20581
  const chatsDir = path42.join(slugPath, "chats");
20249
- if (!fs40.existsSync(chatsDir)) continue;
20582
+ if (!fs41.existsSync(chatsDir)) continue;
20250
20583
  let chatFiles;
20251
20584
  try {
20252
- chatFiles = fs40.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
20585
+ chatFiles = fs41.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
20253
20586
  } catch {
20254
20587
  continue;
20255
20588
  }
20256
20589
  for (const chatFile of chatFiles) {
20257
20590
  let raw;
20258
20591
  try {
20259
- raw = fs40.readFileSync(path42.join(chatsDir, chatFile), "utf-8");
20592
+ raw = fs41.readFileSync(path42.join(chatsDir, chatFile), "utf-8");
20260
20593
  } catch {
20261
20594
  continue;
20262
20595
  }
@@ -20337,7 +20670,7 @@ function buildGeminiSessions(days, allAuditEntries) {
20337
20670
  }
20338
20671
  function buildCodexSessions(days, allAuditEntries) {
20339
20672
  const sessionsBase = path42.join(os36.homedir(), ".codex", "sessions");
20340
- if (!fs40.existsSync(sessionsBase)) return [];
20673
+ if (!fs41.existsSync(sessionsBase)) return [];
20341
20674
  const cutoff = days !== null ? (() => {
20342
20675
  const d = /* @__PURE__ */ new Date();
20343
20676
  d.setDate(d.getDate() - days);
@@ -20346,28 +20679,28 @@ function buildCodexSessions(days, allAuditEntries) {
20346
20679
  })() : null;
20347
20680
  const jsonlFiles = [];
20348
20681
  try {
20349
- for (const year of fs40.readdirSync(sessionsBase)) {
20682
+ for (const year of fs41.readdirSync(sessionsBase)) {
20350
20683
  const yearPath = path42.join(sessionsBase, year);
20351
20684
  try {
20352
- if (!fs40.statSync(yearPath).isDirectory()) continue;
20685
+ if (!fs41.statSync(yearPath).isDirectory()) continue;
20353
20686
  } catch {
20354
20687
  continue;
20355
20688
  }
20356
- for (const month of fs40.readdirSync(yearPath)) {
20689
+ for (const month of fs41.readdirSync(yearPath)) {
20357
20690
  const monthPath = path42.join(yearPath, month);
20358
20691
  try {
20359
- if (!fs40.statSync(monthPath).isDirectory()) continue;
20692
+ if (!fs41.statSync(monthPath).isDirectory()) continue;
20360
20693
  } catch {
20361
20694
  continue;
20362
20695
  }
20363
- for (const day of fs40.readdirSync(monthPath)) {
20696
+ for (const day of fs41.readdirSync(monthPath)) {
20364
20697
  const dayPath = path42.join(monthPath, day);
20365
20698
  try {
20366
- if (!fs40.statSync(dayPath).isDirectory()) continue;
20699
+ if (!fs41.statSync(dayPath).isDirectory()) continue;
20367
20700
  } catch {
20368
20701
  continue;
20369
20702
  }
20370
- for (const file of fs40.readdirSync(dayPath)) {
20703
+ for (const file of fs41.readdirSync(dayPath)) {
20371
20704
  if (file.endsWith(".jsonl")) jsonlFiles.push(path42.join(dayPath, file));
20372
20705
  }
20373
20706
  }
@@ -20380,7 +20713,7 @@ function buildCodexSessions(days, allAuditEntries) {
20380
20713
  for (const filePath of jsonlFiles) {
20381
20714
  let lines;
20382
20715
  try {
20383
- lines = fs40.readFileSync(filePath, "utf-8").split("\n");
20716
+ lines = fs41.readFileSync(filePath, "utf-8").split("\n");
20384
20717
  } catch {
20385
20718
  continue;
20386
20719
  }
@@ -20461,7 +20794,7 @@ function buildSessions(days, historyPath) {
20461
20794
  const hPath = historyPath ?? path42.join(os36.homedir(), ".claude", "history.jsonl");
20462
20795
  let historyRaw;
20463
20796
  try {
20464
- historyRaw = fs40.readFileSync(hPath, "utf-8");
20797
+ historyRaw = fs41.readFileSync(hPath, "utf-8");
20465
20798
  } catch {
20466
20799
  return [];
20467
20800
  }
@@ -20486,7 +20819,7 @@ function buildSessions(days, historyPath) {
20486
20819
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
20487
20820
  let sessionLines = [];
20488
20821
  try {
20489
- sessionLines = fs40.readFileSync(jsonlFile, "utf-8").split("\n");
20822
+ sessionLines = fs41.readFileSync(jsonlFile, "utf-8").split("\n");
20490
20823
  } catch {
20491
20824
  }
20492
20825
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -20753,7 +21086,7 @@ function registerSessionsCommand(program2) {
20753
21086
  console.log(chalk24.cyan.bold("\u{1F4CB} node9 sessions") + chalk24.dim(" \u2014 what your AI agent did"));
20754
21087
  console.log("");
20755
21088
  const historyPath = path42.join(os36.homedir(), ".claude", "history.jsonl");
20756
- if (!fs40.existsSync(historyPath)) {
21089
+ if (!fs41.existsSync(historyPath)) {
20757
21090
  console.log(chalk24.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
20758
21091
  console.log(chalk24.gray(" Install Claude Code, run a few sessions, then try again.\n"));
20759
21092
  return;
@@ -20790,12 +21123,12 @@ function registerSessionsCommand(program2) {
20790
21123
 
20791
21124
  // src/cli/commands/skill-pin.ts
20792
21125
  import chalk25 from "chalk";
20793
- import fs41 from "fs";
21126
+ import fs42 from "fs";
20794
21127
  import os37 from "os";
20795
21128
  import path43 from "path";
20796
21129
  function wipeSkillSessions() {
20797
21130
  try {
20798
- fs41.rmSync(path43.join(os37.homedir(), ".node9", "skill-sessions"), {
21131
+ fs42.rmSync(path43.join(os37.homedir(), ".node9", "skill-sessions"), {
20799
21132
  recursive: true,
20800
21133
  force: true
20801
21134
  });
@@ -20853,7 +21186,7 @@ function registerSkillPinCommand(program2) {
20853
21186
  process.exit(1);
20854
21187
  }
20855
21188
  const rootPath = pins.roots[rootKey].rootPath;
20856
- removePin(rootKey);
21189
+ removePin2(rootKey);
20857
21190
  wipeSkillSessions();
20858
21191
  console.log(chalk25.green(`
20859
21192
  \u{1F513} Pin removed for ${chalk25.cyan(rootKey)}`));
@@ -20868,7 +21201,7 @@ function registerSkillPinCommand(program2) {
20868
21201
  return;
20869
21202
  }
20870
21203
  const count = result.ok ? Object.keys(result.pins.roots).length : "?";
20871
- clearAllPins();
21204
+ clearAllPins2();
20872
21205
  wipeSkillSessions();
20873
21206
  console.log(chalk25.green(`
20874
21207
  \u{1F513} Cleared ${count} skill pin(s).`));
@@ -20877,15 +21210,15 @@ function registerSkillPinCommand(program2) {
20877
21210
  }
20878
21211
 
20879
21212
  // src/cli/commands/decisions.ts
20880
- import fs42 from "fs";
21213
+ import fs43 from "fs";
20881
21214
  import os38 from "os";
20882
21215
  import path44 from "path";
20883
21216
  import chalk26 from "chalk";
20884
21217
  var DECISIONS_FILE2 = path44.join(os38.homedir(), ".node9", "decisions.json");
20885
21218
  function readDecisions() {
20886
21219
  try {
20887
- if (!fs42.existsSync(DECISIONS_FILE2)) return {};
20888
- const raw = fs42.readFileSync(DECISIONS_FILE2, "utf-8");
21220
+ if (!fs43.existsSync(DECISIONS_FILE2)) return {};
21221
+ const raw = fs43.readFileSync(DECISIONS_FILE2, "utf-8");
20889
21222
  const parsed = JSON.parse(raw);
20890
21223
  const out = {};
20891
21224
  for (const [k, v] of Object.entries(parsed)) {
@@ -20898,10 +21231,10 @@ function readDecisions() {
20898
21231
  }
20899
21232
  function writeDecisions(d) {
20900
21233
  const dir = path44.dirname(DECISIONS_FILE2);
20901
- if (!fs42.existsSync(dir)) fs42.mkdirSync(dir, { recursive: true });
21234
+ if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
20902
21235
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
20903
- fs42.writeFileSync(tmp, JSON.stringify(d, null, 2));
20904
- fs42.renameSync(tmp, DECISIONS_FILE2);
21236
+ fs43.writeFileSync(tmp, JSON.stringify(d, null, 2));
21237
+ fs43.renameSync(tmp, DECISIONS_FILE2);
20905
21238
  }
20906
21239
  function registerDecisionsCommand(program2) {
20907
21240
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -20958,7 +21291,7 @@ Persistent decisions (${entries.length})
20958
21291
 
20959
21292
  // src/cli/commands/dlp.ts
20960
21293
  import chalk27 from "chalk";
20961
- import fs43 from "fs";
21294
+ import fs44 from "fs";
20962
21295
  import path45 from "path";
20963
21296
  import os39 from "os";
20964
21297
  var AUDIT_LOG = path45.join(os39.homedir(), ".node9", "audit.log");
@@ -20969,7 +21302,7 @@ function stripAnsi(s) {
20969
21302
  }
20970
21303
  function loadResolved() {
20971
21304
  try {
20972
- const raw = JSON.parse(fs43.readFileSync(RESOLVED_FILE, "utf-8"));
21305
+ const raw = JSON.parse(fs44.readFileSync(RESOLVED_FILE, "utf-8"));
20973
21306
  return new Set(raw);
20974
21307
  } catch {
20975
21308
  return /* @__PURE__ */ new Set();
@@ -20977,13 +21310,13 @@ function loadResolved() {
20977
21310
  }
20978
21311
  function saveResolved(resolved) {
20979
21312
  try {
20980
- fs43.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
21313
+ fs44.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
20981
21314
  } catch {
20982
21315
  }
20983
21316
  }
20984
21317
  function loadDlpFindings() {
20985
- if (!fs43.existsSync(AUDIT_LOG)) return [];
20986
- return fs43.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
21318
+ if (!fs44.existsSync(AUDIT_LOG)) return [];
21319
+ return fs44.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
20987
21320
  if (!line.trim()) return [];
20988
21321
  try {
20989
21322
  const e = JSON.parse(line);
@@ -21082,13 +21415,13 @@ function registerDlpCommand(program2) {
21082
21415
  // src/cli/commands/mask.ts
21083
21416
  init_dlp();
21084
21417
  import chalk28 from "chalk";
21085
- import fs44 from "fs";
21418
+ import fs45 from "fs";
21086
21419
  import path46 from "path";
21087
21420
  import os40 from "os";
21088
21421
  function findJsonlFiles(dir) {
21089
21422
  const results = [];
21090
- if (!fs44.existsSync(dir)) return results;
21091
- for (const entry of fs44.readdirSync(dir, { withFileTypes: true })) {
21423
+ if (!fs45.existsSync(dir)) return results;
21424
+ for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
21092
21425
  const full = path46.join(dir, entry.name);
21093
21426
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
21094
21427
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
@@ -21132,7 +21465,7 @@ function redactJson(obj) {
21132
21465
  function processFile(filePath, dryRun) {
21133
21466
  let raw;
21134
21467
  try {
21135
- raw = fs44.readFileSync(filePath, "utf-8");
21468
+ raw = fs45.readFileSync(filePath, "utf-8");
21136
21469
  } catch {
21137
21470
  return { redactedLines: 0, patterns: [] };
21138
21471
  }
@@ -21164,14 +21497,14 @@ function processFile(filePath, dryRun) {
21164
21497
  }
21165
21498
  }
21166
21499
  if (!dryRun && redactedLines > 0) {
21167
- fs44.writeFileSync(filePath, newLines.join("\n"), "utf-8");
21500
+ fs45.writeFileSync(filePath, newLines.join("\n"), "utf-8");
21168
21501
  }
21169
21502
  return { redactedLines, patterns };
21170
21503
  }
21171
21504
  function processJsonFile(filePath, dryRun) {
21172
21505
  let raw;
21173
21506
  try {
21174
- raw = fs44.readFileSync(filePath, "utf-8");
21507
+ raw = fs45.readFileSync(filePath, "utf-8");
21175
21508
  } catch {
21176
21509
  return { redactedLines: 0, patterns: [] };
21177
21510
  }
@@ -21184,14 +21517,14 @@ function processJsonFile(filePath, dryRun) {
21184
21517
  const { value, modified, found } = redactJson(parsed);
21185
21518
  if (!modified) return { redactedLines: 0, patterns: [] };
21186
21519
  if (!dryRun) {
21187
- fs44.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
21520
+ fs45.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
21188
21521
  }
21189
21522
  return { redactedLines: 1, patterns: found };
21190
21523
  }
21191
21524
  function findJsonFiles(dir) {
21192
21525
  const results = [];
21193
- if (!fs44.existsSync(dir)) return results;
21194
- for (const entry of fs44.readdirSync(dir, { withFileTypes: true })) {
21526
+ if (!fs45.existsSync(dir)) return results;
21527
+ for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
21195
21528
  const full = path46.join(dir, entry.name);
21196
21529
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
21197
21530
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
@@ -21211,7 +21544,7 @@ function registerMaskCommand(program2) {
21211
21544
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
21212
21545
  const filtered = cutoff ? allFiles.filter((f) => {
21213
21546
  try {
21214
- return fs44.statSync(f.path).mtime >= cutoff;
21547
+ return fs45.statSync(f.path).mtime >= cutoff;
21215
21548
  } catch {
21216
21549
  return false;
21217
21550
  }
@@ -21267,20 +21600,20 @@ function registerMaskCommand(program2) {
21267
21600
  // src/cli.ts
21268
21601
  init_blast();
21269
21602
  var { version } = JSON.parse(
21270
- fs47.readFileSync(path49.join(__dirname, "../package.json"), "utf-8")
21603
+ fs48.readFileSync(path49.join(__dirname, "../package.json"), "utf-8")
21271
21604
  );
21272
21605
  var program = new Command();
21273
21606
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
21274
21607
  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) => {
21275
21608
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
21276
21609
  const credPath = path49.join(os43.homedir(), ".node9", "credentials.json");
21277
- if (!fs47.existsSync(path49.dirname(credPath)))
21278
- fs47.mkdirSync(path49.dirname(credPath), { recursive: true });
21610
+ if (!fs48.existsSync(path49.dirname(credPath)))
21611
+ fs48.mkdirSync(path49.dirname(credPath), { recursive: true });
21279
21612
  const profileName = options.profile || "default";
21280
21613
  let existingCreds = {};
21281
21614
  try {
21282
- if (fs47.existsSync(credPath)) {
21283
- const raw = JSON.parse(fs47.readFileSync(credPath, "utf-8"));
21615
+ if (fs48.existsSync(credPath)) {
21616
+ const raw = JSON.parse(fs48.readFileSync(credPath, "utf-8"));
21284
21617
  if (raw.apiKey) {
21285
21618
  existingCreds = {
21286
21619
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -21292,13 +21625,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
21292
21625
  } catch {
21293
21626
  }
21294
21627
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
21295
- fs47.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
21628
+ fs48.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
21296
21629
  if (profileName === "default") {
21297
21630
  const configPath = path49.join(os43.homedir(), ".node9", "config.json");
21298
21631
  let config = {};
21299
21632
  try {
21300
- if (fs47.existsSync(configPath))
21301
- config = JSON.parse(fs47.readFileSync(configPath, "utf-8"));
21633
+ if (fs48.existsSync(configPath))
21634
+ config = JSON.parse(fs48.readFileSync(configPath, "utf-8"));
21302
21635
  } catch {
21303
21636
  }
21304
21637
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -21313,9 +21646,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
21313
21646
  approvers.cloud = false;
21314
21647
  }
21315
21648
  s.approvers = approvers;
21316
- if (!fs47.existsSync(path49.dirname(configPath)))
21317
- fs47.mkdirSync(path49.dirname(configPath), { recursive: true });
21318
- fs47.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
21649
+ if (!fs48.existsSync(path49.dirname(configPath)))
21650
+ fs48.mkdirSync(path49.dirname(configPath), { recursive: true });
21651
+ fs48.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
21319
21652
  }
21320
21653
  if (options.profile && profileName !== "default") {
21321
21654
  console.log(chalk30.green(`\u2705 Profile "${profileName}" saved`));
@@ -21453,14 +21786,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
21453
21786
  }
21454
21787
  if (options.purge) {
21455
21788
  const node9Dir = path49.join(os43.homedir(), ".node9");
21456
- if (fs47.existsSync(node9Dir)) {
21789
+ if (fs48.existsSync(node9Dir)) {
21457
21790
  const confirmed = await confirm2({
21458
21791
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
21459
21792
  default: false
21460
21793
  });
21461
21794
  if (confirmed) {
21462
- fs47.rmSync(node9Dir, { recursive: true });
21463
- if (fs47.existsSync(node9Dir)) {
21795
+ fs48.rmSync(node9Dir, { recursive: true });
21796
+ if (fs48.existsSync(node9Dir)) {
21464
21797
  console.error(
21465
21798
  chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
21466
21799
  );
@@ -21615,12 +21948,12 @@ Run "node9 addto claude" to register it as the statusLine.`
21615
21948
  if (subcommand === "debug") {
21616
21949
  const flagFile = path49.join(os43.homedir(), ".node9", "hud-debug");
21617
21950
  if (state === "on") {
21618
- fs47.mkdirSync(path49.dirname(flagFile), { recursive: true });
21619
- fs47.writeFileSync(flagFile, "");
21951
+ fs48.mkdirSync(path49.dirname(flagFile), { recursive: true });
21952
+ fs48.writeFileSync(flagFile, "");
21620
21953
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
21621
21954
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
21622
21955
  } else if (state === "off") {
21623
- if (fs47.existsSync(flagFile)) fs47.unlinkSync(flagFile);
21956
+ if (fs48.existsSync(flagFile)) fs48.unlinkSync(flagFile);
21624
21957
  console.log("HUD debug logging disabled.");
21625
21958
  } else {
21626
21959
  console.error("Usage: node9 hud debug on|off");
@@ -21736,7 +22069,7 @@ if (process.argv[2] !== "daemon") {
21736
22069
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
21737
22070
  const logPath = path49.join(os43.homedir(), ".node9", "hook-debug.log");
21738
22071
  const msg = reason instanceof Error ? reason.message : String(reason);
21739
- fs47.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
22072
+ fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
21740
22073
  `);
21741
22074
  }
21742
22075
  process.exit(0);