@node9/proxy 1.12.0 → 1.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -2327,7 +2327,12 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2327
2327
  pathTokens = analyzed.paths;
2328
2328
  const INLINE_EXEC_PATTERN = /^(python3?|bash|sh|zsh|perl|ruby|node|php|lua)\s+(-c|-e|-eval)\s/i;
2329
2329
  if (INLINE_EXEC_PATTERN.test(shellCommand.trim())) {
2330
- return { decision: "review", blockedByLabel: "Node9 Standard (Inline Execution)", tier: 3 };
2330
+ return {
2331
+ decision: "review",
2332
+ blockedByLabel: "Node9 Standard (Inline Execution)",
2333
+ ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute.",
2334
+ tier: 3
2335
+ };
2331
2336
  }
2332
2337
  const evalVerdict = detectDangerousShellExec(shellCommand);
2333
2338
  if (evalVerdict === "block") {
@@ -2335,6 +2340,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2335
2340
  decision: "block",
2336
2341
  blockedByLabel: "Node9: Eval Remote Execution",
2337
2342
  reason: "eval of remote download (curl/wget) is a near-certain supply-chain attack",
2343
+ ruleDescription: "The AI is downloading a script from the internet and running it immediately without inspection. This is a common way malware gets installed.",
2338
2344
  tier: 3
2339
2345
  };
2340
2346
  }
@@ -2343,6 +2349,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2343
2349
  decision: "review",
2344
2350
  blockedByLabel: "Node9: Eval Dynamic Content",
2345
2351
  reason: "eval of dynamic content (variable or subshell expansion) requires approval",
2352
+ ruleDescription: "The AI is running a command that includes a variable or subshell expansion. The actual command executed at runtime may differ from what is shown here.",
2346
2353
  tier: 3
2347
2354
  };
2348
2355
  }
@@ -2472,6 +2479,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2472
2479
  blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
2473
2480
  matchedWord: matchedDangerousWord,
2474
2481
  matchedField,
2482
+ ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`,
2475
2483
  tier: 6
2476
2484
  };
2477
2485
  }
@@ -2624,7 +2632,8 @@ async function explainPolicy(toolName, args) {
2624
2632
  waterfall,
2625
2633
  steps,
2626
2634
  decision: "review",
2627
- blockedByLabel: "Node9 Standard (Inline Execution)"
2635
+ blockedByLabel: "Node9 Standard (Inline Execution)",
2636
+ ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute."
2628
2637
  };
2629
2638
  }
2630
2639
  steps.push({
@@ -2730,7 +2739,8 @@ async function explainPolicy(toolName, args) {
2730
2739
  steps,
2731
2740
  decision: "review",
2732
2741
  blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
2733
- matchedToken: matchedDangerousWord
2742
+ matchedToken: matchedDangerousWord,
2743
+ ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`
2734
2744
  };
2735
2745
  }
2736
2746
  steps.push({
@@ -8269,6 +8279,7 @@ var init_ui2 = __esm({
8269
8279
  // src/cli/daemon-starter.ts
8270
8280
  import { spawn as spawn2, execSync } from "child_process";
8271
8281
  import path16 from "path";
8282
+ import fs13 from "fs";
8272
8283
  function isTestingMode() {
8273
8284
  return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
8274
8285
  }
@@ -8285,20 +8296,32 @@ function openBrowserLocal() {
8285
8296
  async function autoStartDaemonAndWait(openBrowser2 = true) {
8286
8297
  if (isTestingMode()) return false;
8287
8298
  if (!path16.isAbsolute(process.argv[1])) return false;
8299
+ let resolvedArgv1;
8288
8300
  try {
8289
- const child = spawn2(process.execPath, [process.argv[1], "daemon"], {
8301
+ resolvedArgv1 = fs13.realpathSync(process.argv[1]);
8302
+ } catch {
8303
+ return false;
8304
+ }
8305
+ if (!resolvedArgv1.endsWith(".js")) return false;
8306
+ try {
8307
+ const child = spawn2(process.execPath, [resolvedArgv1, "daemon"], {
8290
8308
  detached: true,
8291
8309
  stdio: "ignore",
8292
8310
  // NODE9_BROWSER_OPENED=1 tells the daemon we will open the browser ourselves
8293
8311
  // (openBrowserLocal below), so it must not open a duplicate tab on first approval.
8294
- env: { ...process.env, NODE9_AUTO_STARTED: "1", NODE9_BROWSER_OPENED: "1" }
8312
+ // Only set NODE9_BROWSER_OPENED when we actually intend to open it here.
8313
+ env: {
8314
+ ...process.env,
8315
+ NODE9_AUTO_STARTED: "1",
8316
+ ...openBrowser2 && { NODE9_BROWSER_OPENED: "1" }
8317
+ }
8295
8318
  });
8296
8319
  child.unref();
8297
8320
  for (let i = 0; i < 20; i++) {
8298
8321
  await new Promise((r) => setTimeout(r, 250));
8299
8322
  if (!isDaemonRunning()) continue;
8300
8323
  try {
8301
- const res = await fetch("http://127.0.0.1:7391/settings", {
8324
+ const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/settings`, {
8302
8325
  signal: AbortSignal.timeout(500)
8303
8326
  });
8304
8327
  if (res.ok) {
@@ -8512,7 +8535,7 @@ var init_scan_summary = __esm({
8512
8535
 
8513
8536
  // src/cli/commands/scan.ts
8514
8537
  import chalk2 from "chalk";
8515
- import fs13 from "fs";
8538
+ import fs14 from "fs";
8516
8539
  import path17 from "path";
8517
8540
  import os12 from "os";
8518
8541
  function claudeModelPrice(model) {
@@ -8614,13 +8637,13 @@ function buildRuleSources() {
8614
8637
  function countScanFiles() {
8615
8638
  let total = 0;
8616
8639
  const claudeDir = path17.join(os12.homedir(), ".claude", "projects");
8617
- if (fs13.existsSync(claudeDir)) {
8640
+ if (fs14.existsSync(claudeDir)) {
8618
8641
  try {
8619
- for (const proj of fs13.readdirSync(claudeDir)) {
8642
+ for (const proj of fs14.readdirSync(claudeDir)) {
8620
8643
  const p = path17.join(claudeDir, proj);
8621
8644
  try {
8622
- if (!fs13.statSync(p).isDirectory()) continue;
8623
- total += fs13.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
8645
+ if (!fs14.statSync(p).isDirectory()) continue;
8646
+ total += fs14.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
8624
8647
  } catch {
8625
8648
  continue;
8626
8649
  }
@@ -8629,16 +8652,16 @@ function countScanFiles() {
8629
8652
  }
8630
8653
  }
8631
8654
  const geminiDir = path17.join(os12.homedir(), ".gemini", "tmp");
8632
- if (fs13.existsSync(geminiDir)) {
8655
+ if (fs14.existsSync(geminiDir)) {
8633
8656
  try {
8634
- for (const slug of fs13.readdirSync(geminiDir)) {
8657
+ for (const slug of fs14.readdirSync(geminiDir)) {
8635
8658
  const p = path17.join(geminiDir, slug);
8636
8659
  try {
8637
- if (!fs13.statSync(p).isDirectory()) continue;
8660
+ if (!fs14.statSync(p).isDirectory()) continue;
8638
8661
  const chatsDir = path17.join(p, "chats");
8639
- if (fs13.existsSync(chatsDir)) {
8662
+ if (fs14.existsSync(chatsDir)) {
8640
8663
  try {
8641
- total += fs13.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
8664
+ total += fs14.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
8642
8665
  } catch {
8643
8666
  }
8644
8667
  }
@@ -8650,21 +8673,21 @@ function countScanFiles() {
8650
8673
  }
8651
8674
  }
8652
8675
  const codexDir = path17.join(os12.homedir(), ".codex", "sessions");
8653
- if (fs13.existsSync(codexDir)) {
8676
+ if (fs14.existsSync(codexDir)) {
8654
8677
  try {
8655
- for (const year of fs13.readdirSync(codexDir)) {
8678
+ for (const year of fs14.readdirSync(codexDir)) {
8656
8679
  const yp = path17.join(codexDir, year);
8657
8680
  try {
8658
- if (!fs13.statSync(yp).isDirectory()) continue;
8659
- for (const month of fs13.readdirSync(yp)) {
8681
+ if (!fs14.statSync(yp).isDirectory()) continue;
8682
+ for (const month of fs14.readdirSync(yp)) {
8660
8683
  const mp = path17.join(yp, month);
8661
8684
  try {
8662
- if (!fs13.statSync(mp).isDirectory()) continue;
8663
- for (const day of fs13.readdirSync(mp)) {
8685
+ if (!fs14.statSync(mp).isDirectory()) continue;
8686
+ for (const day of fs14.readdirSync(mp)) {
8664
8687
  const dp = path17.join(mp, day);
8665
8688
  try {
8666
- if (!fs13.statSync(dp).isDirectory()) continue;
8667
- total += fs13.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
8689
+ if (!fs14.statSync(dp).isDirectory()) continue;
8690
+ total += fs14.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
8668
8691
  } catch {
8669
8692
  continue;
8670
8693
  }
@@ -8682,17 +8705,18 @@ function countScanFiles() {
8682
8705
  }
8683
8706
  return total;
8684
8707
  }
8685
- function renderProgressBar(done, total) {
8708
+ function renderProgressBar(done, total, lines) {
8686
8709
  const width = 28;
8687
8710
  const pct = total > 0 ? done / total : 0;
8688
8711
  const filled = Math.min(width, Math.round(pct * width));
8689
8712
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
8690
- const label = total > 0 ? `${done}/${total} files` : `${done} files`;
8713
+ const fileLabel = total > 0 ? `${done}/${total} files` : `${done} files`;
8714
+ const lineLabel = lines > 0 ? chalk2.dim(` ${lines.toLocaleString()} lines`) : "";
8691
8715
  process.stdout.write(
8692
- `\r ${chalk2.cyan("Scanning")} [${chalk2.cyan(bar)}] ${chalk2.dim(label)} `
8716
+ `\r ${chalk2.cyan("Scanning")} [${chalk2.cyan(bar)}] ${chalk2.dim(fileLabel)}${lineLabel} `
8693
8717
  );
8694
8718
  }
8695
- function scanClaudeHistory(startDate, onProgress) {
8719
+ function scanClaudeHistory(startDate, onProgress, onLine) {
8696
8720
  const projectsDir = path17.join(os12.homedir(), ".claude", "projects");
8697
8721
  const result = {
8698
8722
  filesScanned: 0,
@@ -8706,10 +8730,10 @@ function scanClaudeHistory(startDate, onProgress) {
8706
8730
  firstDate: null,
8707
8731
  lastDate: null
8708
8732
  };
8709
- if (!fs13.existsSync(projectsDir)) return result;
8733
+ if (!fs14.existsSync(projectsDir)) return result;
8710
8734
  let projDirs;
8711
8735
  try {
8712
- projDirs = fs13.readdirSync(projectsDir);
8736
+ projDirs = fs14.readdirSync(projectsDir);
8713
8737
  } catch {
8714
8738
  return result;
8715
8739
  }
@@ -8717,14 +8741,14 @@ function scanClaudeHistory(startDate, onProgress) {
8717
8741
  for (const proj of projDirs) {
8718
8742
  const projPath = path17.join(projectsDir, proj);
8719
8743
  try {
8720
- if (!fs13.statSync(projPath).isDirectory()) continue;
8744
+ if (!fs14.statSync(projPath).isDirectory()) continue;
8721
8745
  } catch {
8722
8746
  continue;
8723
8747
  }
8724
8748
  const projLabel = decodeURIComponent(proj).replace(os12.homedir(), "~").slice(0, 40);
8725
8749
  let files;
8726
8750
  try {
8727
- files = fs13.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
8751
+ files = fs14.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
8728
8752
  } catch {
8729
8753
  continue;
8730
8754
  }
@@ -8735,13 +8759,14 @@ function scanClaudeHistory(startDate, onProgress) {
8735
8759
  const sessionId = file.replace(/\.jsonl$/, "");
8736
8760
  let raw;
8737
8761
  try {
8738
- raw = fs13.readFileSync(path17.join(projPath, file), "utf-8");
8762
+ raw = fs14.readFileSync(path17.join(projPath, file), "utf-8");
8739
8763
  } catch {
8740
8764
  continue;
8741
8765
  }
8742
8766
  const sessionCalls = [];
8743
8767
  for (const line of raw.split("\n")) {
8744
8768
  if (!line.trim()) continue;
8769
+ onLine?.();
8745
8770
  let entry;
8746
8771
  try {
8747
8772
  entry = JSON.parse(line);
@@ -8887,7 +8912,7 @@ function scanClaudeHistory(startDate, onProgress) {
8887
8912
  }
8888
8913
  return result;
8889
8914
  }
8890
- function scanGeminiHistory(startDate, onProgress) {
8915
+ function scanGeminiHistory(startDate, onProgress, onLine) {
8891
8916
  const tmpDir = path17.join(os12.homedir(), ".gemini", "tmp");
8892
8917
  const result = {
8893
8918
  filesScanned: 0,
@@ -8901,10 +8926,10 @@ function scanGeminiHistory(startDate, onProgress) {
8901
8926
  firstDate: null,
8902
8927
  lastDate: null
8903
8928
  };
8904
- if (!fs13.existsSync(tmpDir)) return result;
8929
+ if (!fs14.existsSync(tmpDir)) return result;
8905
8930
  let slugDirs;
8906
8931
  try {
8907
- slugDirs = fs13.readdirSync(tmpDir);
8932
+ slugDirs = fs14.readdirSync(tmpDir);
8908
8933
  } catch {
8909
8934
  return result;
8910
8935
  }
@@ -8912,20 +8937,20 @@ function scanGeminiHistory(startDate, onProgress) {
8912
8937
  for (const slug of slugDirs) {
8913
8938
  const slugPath = path17.join(tmpDir, slug);
8914
8939
  try {
8915
- if (!fs13.statSync(slugPath).isDirectory()) continue;
8940
+ if (!fs14.statSync(slugPath).isDirectory()) continue;
8916
8941
  } catch {
8917
8942
  continue;
8918
8943
  }
8919
8944
  let projLabel = slug;
8920
8945
  try {
8921
- projLabel = fs13.readFileSync(path17.join(slugPath, ".project_root"), "utf-8").trim().replace(os12.homedir(), "~").slice(0, 40);
8946
+ projLabel = fs14.readFileSync(path17.join(slugPath, ".project_root"), "utf-8").trim().replace(os12.homedir(), "~").slice(0, 40);
8922
8947
  } catch {
8923
8948
  }
8924
8949
  const chatsDir = path17.join(slugPath, "chats");
8925
- if (!fs13.existsSync(chatsDir)) continue;
8950
+ if (!fs14.existsSync(chatsDir)) continue;
8926
8951
  let chatFiles;
8927
8952
  try {
8928
- chatFiles = fs13.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
8953
+ chatFiles = fs14.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
8929
8954
  } catch {
8930
8955
  continue;
8931
8956
  }
@@ -8935,7 +8960,7 @@ function scanGeminiHistory(startDate, onProgress) {
8935
8960
  const sessionId = chatFile.replace(/\.json$/, "");
8936
8961
  let raw;
8937
8962
  try {
8938
- raw = fs13.readFileSync(path17.join(chatsDir, chatFile), "utf-8");
8963
+ raw = fs14.readFileSync(path17.join(chatsDir, chatFile), "utf-8");
8939
8964
  } catch {
8940
8965
  continue;
8941
8966
  }
@@ -8948,6 +8973,7 @@ function scanGeminiHistory(startDate, onProgress) {
8948
8973
  }
8949
8974
  result.sessions++;
8950
8975
  for (const msg of session.messages ?? []) {
8976
+ onLine?.();
8951
8977
  if (msg.type === "user") {
8952
8978
  const content = msg.content;
8953
8979
  const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
@@ -9083,7 +9109,7 @@ function scanGeminiHistory(startDate, onProgress) {
9083
9109
  }
9084
9110
  return result;
9085
9111
  }
9086
- function scanCodexHistory(startDate, onProgress) {
9112
+ function scanCodexHistory(startDate, onProgress, onLine) {
9087
9113
  const sessionsBase = path17.join(os12.homedir(), ".codex", "sessions");
9088
9114
  const result = {
9089
9115
  filesScanned: 0,
@@ -9097,31 +9123,31 @@ function scanCodexHistory(startDate, onProgress) {
9097
9123
  firstDate: null,
9098
9124
  lastDate: null
9099
9125
  };
9100
- if (!fs13.existsSync(sessionsBase)) return result;
9126
+ if (!fs14.existsSync(sessionsBase)) return result;
9101
9127
  const jsonlFiles = [];
9102
9128
  try {
9103
- for (const year of fs13.readdirSync(sessionsBase)) {
9129
+ for (const year of fs14.readdirSync(sessionsBase)) {
9104
9130
  const yearPath = path17.join(sessionsBase, year);
9105
9131
  try {
9106
- if (!fs13.statSync(yearPath).isDirectory()) continue;
9132
+ if (!fs14.statSync(yearPath).isDirectory()) continue;
9107
9133
  } catch {
9108
9134
  continue;
9109
9135
  }
9110
- for (const month of fs13.readdirSync(yearPath)) {
9136
+ for (const month of fs14.readdirSync(yearPath)) {
9111
9137
  const monthPath = path17.join(yearPath, month);
9112
9138
  try {
9113
- if (!fs13.statSync(monthPath).isDirectory()) continue;
9139
+ if (!fs14.statSync(monthPath).isDirectory()) continue;
9114
9140
  } catch {
9115
9141
  continue;
9116
9142
  }
9117
- for (const day of fs13.readdirSync(monthPath)) {
9143
+ for (const day of fs14.readdirSync(monthPath)) {
9118
9144
  const dayPath = path17.join(monthPath, day);
9119
9145
  try {
9120
- if (!fs13.statSync(dayPath).isDirectory()) continue;
9146
+ if (!fs14.statSync(dayPath).isDirectory()) continue;
9121
9147
  } catch {
9122
9148
  continue;
9123
9149
  }
9124
- for (const file of fs13.readdirSync(dayPath)) {
9150
+ for (const file of fs14.readdirSync(dayPath)) {
9125
9151
  if (file.endsWith(".jsonl")) jsonlFiles.push(path17.join(dayPath, file));
9126
9152
  }
9127
9153
  }
@@ -9136,7 +9162,7 @@ function scanCodexHistory(startDate, onProgress) {
9136
9162
  onProgress?.(result.filesScanned);
9137
9163
  let lines;
9138
9164
  try {
9139
- lines = fs13.readFileSync(filePath, "utf-8").split("\n");
9165
+ lines = fs14.readFileSync(filePath, "utf-8").split("\n");
9140
9166
  } catch {
9141
9167
  continue;
9142
9168
  }
@@ -9150,6 +9176,7 @@ function scanCodexHistory(startDate, onProgress) {
9150
9176
  let lastTotalOutput = 0;
9151
9177
  for (const line of lines) {
9152
9178
  if (!line.trim()) continue;
9179
+ onLine?.();
9153
9180
  let entry;
9154
9181
  try {
9155
9182
  entry = JSON.parse(line);
@@ -9354,17 +9381,17 @@ function printRuleGroup(rule, topN, drillDown, previewWidth) {
9354
9381
  }
9355
9382
  }
9356
9383
  function registerScanCommand(program2) {
9357
- program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last 90 days)").option("--days <n>", "Scan last N days of history", "90").option("--top <n>", "Max findings to show per rule (default: 5)", "5").option("--drill-down", "Show all findings with full commands and session IDs").action(async (options) => {
9384
+ program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last 30 days)").option("--days <n>", "Scan last N days of history", "30").option("--top <n>", "Max findings to show per rule (default: 5)", "5").option("--drill-down", "Show all findings with full commands and session IDs").action(async (options) => {
9358
9385
  const drillDown = options.drillDown ?? false;
9359
9386
  const topN = drillDown ? Infinity : Math.max(1, parseInt(options.top, 10) || 5);
9360
9387
  const previewWidth = 70;
9361
9388
  const startDate = options.all ? null : (() => {
9362
9389
  const d = /* @__PURE__ */ new Date();
9363
- d.setDate(d.getDate() - (parseInt(options.days, 10) || 90));
9390
+ d.setDate(d.getDate() - (parseInt(options.days, 10) || 30));
9364
9391
  d.setHours(0, 0, 0, 0);
9365
9392
  return d;
9366
9393
  })();
9367
- const isInstalled = fs13.existsSync(path17.join(os12.homedir(), ".node9", "audit.log"));
9394
+ const isInstalled = fs14.existsSync(path17.join(os12.homedir(), ".node9", "audit.log"));
9368
9395
  console.log("");
9369
9396
  if (!isInstalled) {
9370
9397
  console.log(
@@ -9381,19 +9408,32 @@ function registerScanCommand(program2) {
9381
9408
  console.log("");
9382
9409
  const totalFiles = countScanFiles();
9383
9410
  let filesScanned = 0;
9411
+ let linesScanned = 0;
9412
+ let lastRender = 0;
9384
9413
  const onProgress = (done) => {
9385
9414
  filesScanned = done;
9386
- renderProgressBar(filesScanned, totalFiles);
9415
+ renderProgressBar(filesScanned, totalFiles, linesScanned);
9416
+ lastRender = Date.now();
9417
+ };
9418
+ const onLine = () => {
9419
+ linesScanned++;
9420
+ const now = Date.now();
9421
+ if (now - lastRender >= 80) {
9422
+ lastRender = now;
9423
+ renderProgressBar(filesScanned, totalFiles, linesScanned);
9424
+ }
9387
9425
  };
9388
- renderProgressBar(0, totalFiles);
9389
- const claudeScan = scanClaudeHistory(startDate, onProgress);
9426
+ renderProgressBar(0, totalFiles, 0);
9427
+ const claudeScan = scanClaudeHistory(startDate, onProgress, onLine);
9390
9428
  const geminiScan = scanGeminiHistory(
9391
9429
  startDate,
9392
- (done) => onProgress(claudeScan.filesScanned + done)
9430
+ (done) => onProgress(claudeScan.filesScanned + done),
9431
+ onLine
9393
9432
  );
9394
9433
  const codexScan = scanCodexHistory(
9395
9434
  startDate,
9396
- (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done)
9435
+ (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
9436
+ onLine
9397
9437
  );
9398
9438
  const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
9399
9439
  const summary = buildScanSummary([
@@ -9411,7 +9451,7 @@ function registerScanCommand(program2) {
9411
9451
  );
9412
9452
  return;
9413
9453
  }
9414
- const rangeLabel = options.all ? chalk2.dim("all time") : chalk2.dim(`last ${options.days ?? 90} days`);
9454
+ const rangeLabel = options.all ? chalk2.dim("all time") : chalk2.dim(`last ${options.days ?? 30} days`);
9415
9455
  const dateRange = scan.firstDate && scan.lastDate ? chalk2.dim(` ${fmtTs(scan.firstDate)} \u2013 ${fmtTs(scan.lastDate)}`) : "";
9416
9456
  const breakdownParts = [];
9417
9457
  if (claudeScan.sessions > 0)
@@ -9589,29 +9629,33 @@ function registerScanCommand(program2) {
9589
9629
  console.log(" " + chalk2.dim("\u2192 ") + chalk2.underline("https://node9.ai"));
9590
9630
  }
9591
9631
  console.log("");
9592
- if (!isTestingMode() && isDaemonRunning()) {
9593
- const internalToken = getInternalToken();
9594
- if (internalToken) {
9595
- try {
9596
- const summary2 = buildScanSummary([
9597
- { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
9598
- { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
9599
- { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
9600
- ]);
9601
- const payload = { status: "complete", summary: summary2 };
9602
- await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
9603
- method: "POST",
9604
- headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
9605
- body: JSON.stringify(payload),
9606
- signal: AbortSignal.timeout(3e3)
9607
- });
9608
- const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
9609
- console.log(" " + chalk2.cyan("\u{1F310} View in browser:") + " " + chalk2.underline(url));
9610
- console.log("");
9611
- openBrowserLocal();
9612
- } catch {
9632
+ const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
9633
+ if (!isTestingMode()) {
9634
+ if (isDaemonRunning()) {
9635
+ const internalToken = getInternalToken();
9636
+ if (internalToken) {
9637
+ try {
9638
+ const summary2 = buildScanSummary([
9639
+ { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
9640
+ { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
9641
+ { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
9642
+ ]);
9643
+ await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
9644
+ method: "POST",
9645
+ headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
9646
+ body: JSON.stringify({ status: "complete", summary: summary2 }),
9647
+ signal: AbortSignal.timeout(3e3)
9648
+ });
9649
+ openBrowserLocal();
9650
+ } catch {
9651
+ }
9613
9652
  }
9614
9653
  }
9654
+ console.log(" " + chalk2.cyan("\u{1F310} View in browser:") + " " + chalk2.underline(url));
9655
+ if (!isDaemonRunning()) {
9656
+ console.log(" " + chalk2.dim(" run node9 daemon --background to activate"));
9657
+ }
9658
+ console.log("");
9615
9659
  }
9616
9660
  });
9617
9661
  }
@@ -9754,7 +9798,7 @@ var init_suggestion_tracker = __esm({
9754
9798
  });
9755
9799
 
9756
9800
  // src/daemon/taint-store.ts
9757
- import fs14 from "fs";
9801
+ import fs15 from "fs";
9758
9802
  import path18 from "path";
9759
9803
  var DEFAULT_TTL_MS, TaintStore;
9760
9804
  var init_taint_store = __esm({
@@ -9824,7 +9868,7 @@ var init_taint_store = __esm({
9824
9868
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
9825
9869
  _resolve(filePath) {
9826
9870
  try {
9827
- return fs14.realpathSync.native(path18.resolve(filePath));
9871
+ return fs15.realpathSync.native(path18.resolve(filePath));
9828
9872
  } catch {
9829
9873
  return path18.resolve(filePath);
9830
9874
  }
@@ -9943,15 +9987,15 @@ var init_session_history = __esm({
9943
9987
 
9944
9988
  // src/daemon/state.ts
9945
9989
  import net2 from "net";
9946
- import fs15 from "fs";
9990
+ import fs16 from "fs";
9947
9991
  import path19 from "path";
9948
9992
  import os13 from "os";
9949
9993
  import { spawn as spawn3 } from "child_process";
9950
9994
  import { randomUUID as randomUUID3 } from "crypto";
9951
9995
  function loadInsightCounts() {
9952
9996
  try {
9953
- if (!fs15.existsSync(INSIGHT_COUNTS_FILE)) return;
9954
- const data = JSON.parse(fs15.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
9997
+ if (!fs16.existsSync(INSIGHT_COUNTS_FILE)) return;
9998
+ const data = JSON.parse(fs16.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
9955
9999
  for (const [tool, count] of Object.entries(data)) {
9956
10000
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
9957
10001
  }
@@ -9995,22 +10039,22 @@ function setCachedScanResult(result) {
9995
10039
  }
9996
10040
  function atomicWriteSync2(filePath, data, options) {
9997
10041
  const dir = path19.dirname(filePath);
9998
- if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
10042
+ if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
9999
10043
  const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
10000
10044
  try {
10001
- fs15.writeFileSync(tmpPath, data, options);
10045
+ fs16.writeFileSync(tmpPath, data, options);
10002
10046
  } catch (err2) {
10003
10047
  try {
10004
- fs15.unlinkSync(tmpPath);
10048
+ fs16.unlinkSync(tmpPath);
10005
10049
  } catch {
10006
10050
  }
10007
10051
  throw err2;
10008
10052
  }
10009
10053
  try {
10010
- fs15.renameSync(tmpPath, filePath);
10054
+ fs16.renameSync(tmpPath, filePath);
10011
10055
  } catch (err2) {
10012
10056
  try {
10013
- fs15.unlinkSync(tmpPath);
10057
+ fs16.unlinkSync(tmpPath);
10014
10058
  } catch {
10015
10059
  }
10016
10060
  throw err2;
@@ -10035,15 +10079,15 @@ function appendAuditLog(data) {
10035
10079
  source: "daemon"
10036
10080
  };
10037
10081
  const dir = path19.dirname(AUDIT_LOG_FILE);
10038
- if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
10039
- fs15.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10082
+ if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
10083
+ fs16.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10040
10084
  } catch {
10041
10085
  }
10042
10086
  }
10043
10087
  function getAuditHistory(limit = 20) {
10044
10088
  try {
10045
- if (!fs15.existsSync(AUDIT_LOG_FILE)) return [];
10046
- const lines = fs15.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
10089
+ if (!fs16.existsSync(AUDIT_LOG_FILE)) return [];
10090
+ const lines = fs16.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
10047
10091
  if (lines.length === 1 && lines[0] === "") return [];
10048
10092
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
10049
10093
  } catch {
@@ -10052,19 +10096,19 @@ function getAuditHistory(limit = 20) {
10052
10096
  }
10053
10097
  function getOrgName() {
10054
10098
  try {
10055
- if (fs15.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
10099
+ if (fs16.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
10056
10100
  } catch {
10057
10101
  }
10058
10102
  return null;
10059
10103
  }
10060
10104
  function hasStoredSlackKey() {
10061
- return fs15.existsSync(CREDENTIALS_FILE);
10105
+ return fs16.existsSync(CREDENTIALS_FILE);
10062
10106
  }
10063
10107
  function writeGlobalSetting(key, value) {
10064
10108
  let config = {};
10065
10109
  try {
10066
- if (fs15.existsSync(GLOBAL_CONFIG_FILE)) {
10067
- config = JSON.parse(fs15.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
10110
+ if (fs16.existsSync(GLOBAL_CONFIG_FILE)) {
10111
+ config = JSON.parse(fs16.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
10068
10112
  }
10069
10113
  } catch {
10070
10114
  }
@@ -10076,8 +10120,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
10076
10120
  try {
10077
10121
  let trust = { entries: [] };
10078
10122
  try {
10079
- if (fs15.existsSync(TRUST_FILE2))
10080
- trust = JSON.parse(fs15.readFileSync(TRUST_FILE2, "utf-8"));
10123
+ if (fs16.existsSync(TRUST_FILE2))
10124
+ trust = JSON.parse(fs16.readFileSync(TRUST_FILE2, "utf-8"));
10081
10125
  } catch {
10082
10126
  }
10083
10127
  trust.entries = trust.entries.filter(
@@ -10094,8 +10138,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
10094
10138
  }
10095
10139
  function readPersistentDecisions() {
10096
10140
  try {
10097
- if (fs15.existsSync(DECISIONS_FILE)) {
10098
- return JSON.parse(fs15.readFileSync(DECISIONS_FILE, "utf-8"));
10141
+ if (fs16.existsSync(DECISIONS_FILE)) {
10142
+ return JSON.parse(fs16.readFileSync(DECISIONS_FILE, "utf-8"));
10099
10143
  }
10100
10144
  } catch {
10101
10145
  }
@@ -10132,7 +10176,7 @@ function estimateToolCost(tool, args) {
10132
10176
  const filePath = a.file_path ?? a.path;
10133
10177
  if (filePath) {
10134
10178
  try {
10135
- const bytes = fs15.statSync(filePath).size;
10179
+ const bytes = fs16.statSync(filePath).size;
10136
10180
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
10137
10181
  } catch {
10138
10182
  }
@@ -10190,7 +10234,7 @@ function abandonPending() {
10190
10234
  });
10191
10235
  if (autoStarted) {
10192
10236
  try {
10193
- fs15.unlinkSync(DAEMON_PID_FILE);
10237
+ fs16.unlinkSync(DAEMON_PID_FILE);
10194
10238
  } catch {
10195
10239
  }
10196
10240
  setTimeout(() => {
@@ -10201,7 +10245,7 @@ function abandonPending() {
10201
10245
  }
10202
10246
  function startActivitySocket() {
10203
10247
  try {
10204
- fs15.unlinkSync(ACTIVITY_SOCKET_PATH2);
10248
+ fs16.unlinkSync(ACTIVITY_SOCKET_PATH2);
10205
10249
  } catch {
10206
10250
  }
10207
10251
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -10283,7 +10327,7 @@ function startActivitySocket() {
10283
10327
  unixServer.listen(ACTIVITY_SOCKET_PATH2);
10284
10328
  process.on("exit", () => {
10285
10329
  try {
10286
- fs15.unlinkSync(ACTIVITY_SOCKET_PATH2);
10330
+ fs16.unlinkSync(ACTIVITY_SOCKET_PATH2);
10287
10331
  } catch {
10288
10332
  }
10289
10333
  });
@@ -10349,14 +10393,14 @@ var init_state2 = __esm({
10349
10393
  });
10350
10394
 
10351
10395
  // src/config/patch.ts
10352
- import fs16 from "fs";
10396
+ import fs17 from "fs";
10353
10397
  import path20 from "path";
10354
10398
  import os14 from "os";
10355
10399
  function patchConfig(configPath, patch) {
10356
10400
  let config = {};
10357
10401
  try {
10358
- if (fs16.existsSync(configPath)) {
10359
- config = JSON.parse(fs16.readFileSync(configPath, "utf8"));
10402
+ if (fs17.existsSync(configPath)) {
10403
+ config = JSON.parse(fs17.readFileSync(configPath, "utf8"));
10360
10404
  }
10361
10405
  } catch {
10362
10406
  throw new Error(`Cannot read config at ${configPath} \u2014 file may be corrupted`);
@@ -10376,22 +10420,22 @@ function patchConfig(configPath, patch) {
10376
10420
  }
10377
10421
  }
10378
10422
  const dir = path20.dirname(configPath);
10379
- fs16.mkdirSync(dir, { recursive: true });
10423
+ fs17.mkdirSync(dir, { recursive: true });
10380
10424
  const tmp = configPath + ".node9-tmp";
10381
10425
  try {
10382
- fs16.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
10426
+ fs17.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
10383
10427
  } catch (err2) {
10384
10428
  try {
10385
- fs16.unlinkSync(tmp);
10429
+ fs17.unlinkSync(tmp);
10386
10430
  } catch {
10387
10431
  }
10388
10432
  throw err2;
10389
10433
  }
10390
10434
  try {
10391
- fs16.renameSync(tmp, configPath);
10435
+ fs17.renameSync(tmp, configPath);
10392
10436
  } catch (err2) {
10393
10437
  try {
10394
- fs16.unlinkSync(tmp);
10438
+ fs17.unlinkSync(tmp);
10395
10439
  } catch {
10396
10440
  }
10397
10441
  throw err2;
@@ -10406,7 +10450,7 @@ var init_patch = __esm({
10406
10450
  });
10407
10451
 
10408
10452
  // src/costSync.ts
10409
- import fs17 from "fs";
10453
+ import fs18 from "fs";
10410
10454
  import path21 from "path";
10411
10455
  import os15 from "os";
10412
10456
  function normalizeModel(raw) {
@@ -10424,7 +10468,7 @@ function pricingFor(model) {
10424
10468
  function parseJSONLFile(filePath) {
10425
10469
  let content;
10426
10470
  try {
10427
- content = fs17.readFileSync(filePath, "utf8");
10471
+ content = fs18.readFileSync(filePath, "utf8");
10428
10472
  } catch {
10429
10473
  return /* @__PURE__ */ new Map();
10430
10474
  }
@@ -10477,24 +10521,24 @@ function parseJSONLFile(filePath) {
10477
10521
  }
10478
10522
  function collectEntries() {
10479
10523
  const projectsDir = path21.join(os15.homedir(), ".claude", "projects");
10480
- if (!fs17.existsSync(projectsDir)) return [];
10524
+ if (!fs18.existsSync(projectsDir)) return [];
10481
10525
  const combined = /* @__PURE__ */ new Map();
10482
10526
  let dirs;
10483
10527
  try {
10484
- dirs = fs17.readdirSync(projectsDir);
10528
+ dirs = fs18.readdirSync(projectsDir);
10485
10529
  } catch {
10486
10530
  return [];
10487
10531
  }
10488
10532
  for (const dir of dirs) {
10489
10533
  const dirPath = path21.join(projectsDir, dir);
10490
10534
  try {
10491
- if (!fs17.statSync(dirPath).isDirectory()) continue;
10535
+ if (!fs18.statSync(dirPath).isDirectory()) continue;
10492
10536
  } catch {
10493
10537
  continue;
10494
10538
  }
10495
10539
  let files;
10496
10540
  try {
10497
- files = fs17.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
10541
+ files = fs18.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
10498
10542
  } catch {
10499
10543
  continue;
10500
10544
  }
@@ -10535,11 +10579,11 @@ async function syncCost() {
10535
10579
  signal: AbortSignal.timeout(15e3)
10536
10580
  });
10537
10581
  if (!res.ok) {
10538
- fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
10582
+ fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
10539
10583
  `);
10540
10584
  }
10541
10585
  } catch (err2) {
10542
- fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
10586
+ fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
10543
10587
  `);
10544
10588
  }
10545
10589
  }
@@ -10572,7 +10616,7 @@ var init_costSync = __esm({
10572
10616
  });
10573
10617
 
10574
10618
  // src/daemon/sync.ts
10575
- import fs18 from "fs";
10619
+ import fs19 from "fs";
10576
10620
  import https from "https";
10577
10621
  import os16 from "os";
10578
10622
  import path22 from "path";
@@ -10585,7 +10629,7 @@ function readCredentials() {
10585
10629
  }
10586
10630
  try {
10587
10631
  const credPath = path22.join(os16.homedir(), ".node9", "credentials.json");
10588
- const creds = JSON.parse(fs18.readFileSync(credPath, "utf-8"));
10632
+ const creds = JSON.parse(fs19.readFileSync(credPath, "utf-8"));
10589
10633
  const profileName = process.env.NODE9_PROFILE ?? "default";
10590
10634
  const profile = creds[profileName];
10591
10635
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -10648,8 +10692,8 @@ async function syncOnce() {
10648
10692
  const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
10649
10693
  const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
10650
10694
  const dir = path22.dirname(rulesCacheFile());
10651
- if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
10652
- fs18.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10695
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
10696
+ fs19.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10653
10697
  } catch {
10654
10698
  }
10655
10699
  }
@@ -10662,8 +10706,8 @@ async function runCloudSync() {
10662
10706
  const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
10663
10707
  const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
10664
10708
  const dir = path22.dirname(rulesCacheFile());
10665
- if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
10666
- fs18.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10709
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
10710
+ fs19.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10667
10711
  return { ok: true, rules: rules.length, fetchedAt: cache.fetchedAt };
10668
10712
  } catch (err2) {
10669
10713
  return { ok: false, reason: err2 instanceof Error ? err2.message : String(err2) };
@@ -10671,7 +10715,7 @@ async function runCloudSync() {
10671
10715
  }
10672
10716
  function getCloudSyncStatus() {
10673
10717
  try {
10674
- const raw = JSON.parse(fs18.readFileSync(rulesCacheFile(), "utf-8"));
10718
+ const raw = JSON.parse(fs19.readFileSync(rulesCacheFile(), "utf-8"));
10675
10719
  if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
10676
10720
  return { cached: true, rules: raw.rules.length, fetchedAt: raw.fetchedAt };
10677
10721
  } catch {
@@ -10680,7 +10724,7 @@ function getCloudSyncStatus() {
10680
10724
  }
10681
10725
  function getCloudRules() {
10682
10726
  try {
10683
- const raw = JSON.parse(fs18.readFileSync(rulesCacheFile(), "utf-8"));
10727
+ const raw = JSON.parse(fs19.readFileSync(rulesCacheFile(), "utf-8"));
10684
10728
  return Array.isArray(raw.rules) ? raw.rules : null;
10685
10729
  } catch {
10686
10730
  return null;
@@ -10708,50 +10752,50 @@ var init_sync = __esm({
10708
10752
  });
10709
10753
 
10710
10754
  // src/daemon/dlp-scanner.ts
10711
- import fs19 from "fs";
10755
+ import fs20 from "fs";
10712
10756
  import path23 from "path";
10713
10757
  import os17 from "os";
10714
10758
  function loadIndex() {
10715
10759
  try {
10716
- return JSON.parse(fs19.readFileSync(INDEX_FILE, "utf-8"));
10760
+ return JSON.parse(fs20.readFileSync(INDEX_FILE, "utf-8"));
10717
10761
  } catch {
10718
10762
  return {};
10719
10763
  }
10720
10764
  }
10721
10765
  function saveIndex(index) {
10722
10766
  try {
10723
- fs19.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
10767
+ fs20.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
10724
10768
  } catch {
10725
10769
  }
10726
10770
  }
10727
10771
  function appendAuditEntry(entry) {
10728
10772
  try {
10729
- fs19.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10773
+ fs20.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10730
10774
  } catch {
10731
10775
  }
10732
10776
  }
10733
10777
  function runDlpScan() {
10734
- if (!fs19.existsSync(PROJECTS_DIR)) return;
10778
+ if (!fs20.existsSync(PROJECTS_DIR)) return;
10735
10779
  const index = loadIndex();
10736
10780
  let updated = false;
10737
10781
  let projDirs;
10738
10782
  try {
10739
- projDirs = fs19.readdirSync(PROJECTS_DIR);
10783
+ projDirs = fs20.readdirSync(PROJECTS_DIR);
10740
10784
  } catch {
10741
10785
  return;
10742
10786
  }
10743
10787
  for (const proj of projDirs) {
10744
10788
  const projPath = path23.join(PROJECTS_DIR, proj);
10745
10789
  try {
10746
- if (!fs19.lstatSync(projPath).isDirectory()) continue;
10747
- const real = fs19.realpathSync(projPath);
10790
+ if (!fs20.lstatSync(projPath).isDirectory()) continue;
10791
+ const real = fs20.realpathSync(projPath);
10748
10792
  if (!real.startsWith(PROJECTS_DIR + path23.sep) && real !== PROJECTS_DIR) continue;
10749
10793
  } catch {
10750
10794
  continue;
10751
10795
  }
10752
10796
  let files;
10753
10797
  try {
10754
- files = fs19.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10798
+ files = fs20.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10755
10799
  } catch {
10756
10800
  continue;
10757
10801
  }
@@ -10760,21 +10804,21 @@ function runDlpScan() {
10760
10804
  const lastOffset = index[filePath] ?? 0;
10761
10805
  let size;
10762
10806
  try {
10763
- size = fs19.statSync(filePath).size;
10807
+ size = fs20.statSync(filePath).size;
10764
10808
  } catch {
10765
10809
  continue;
10766
10810
  }
10767
10811
  if (size <= lastOffset) continue;
10768
10812
  let fd;
10769
10813
  try {
10770
- fd = fs19.openSync(filePath, "r");
10814
+ fd = fs20.openSync(filePath, "r");
10771
10815
  } catch {
10772
10816
  continue;
10773
10817
  }
10774
10818
  try {
10775
10819
  const chunkSize = size - lastOffset;
10776
10820
  const buf = Buffer.alloc(chunkSize);
10777
- fs19.readSync(fd, buf, 0, chunkSize, lastOffset);
10821
+ fs20.readSync(fd, buf, 0, chunkSize, lastOffset);
10778
10822
  const chunk = buf.toString("utf-8");
10779
10823
  for (const line of chunk.split("\n")) {
10780
10824
  if (!line.trim()) continue;
@@ -10819,7 +10863,7 @@ Run: node9 report --period 30d`
10819
10863
  updated = true;
10820
10864
  } finally {
10821
10865
  try {
10822
- fs19.closeSync(fd);
10866
+ fs20.closeSync(fd);
10823
10867
  } catch {
10824
10868
  }
10825
10869
  }
@@ -10858,7 +10902,7 @@ var init_dlp_scanner = __esm({
10858
10902
  });
10859
10903
 
10860
10904
  // src/daemon/mcp-tools.ts
10861
- import fs20 from "fs";
10905
+ import fs21 from "fs";
10862
10906
  import path24 from "path";
10863
10907
  import os18 from "os";
10864
10908
  function getMcpToolsFile() {
@@ -10867,8 +10911,8 @@ function getMcpToolsFile() {
10867
10911
  function readMcpToolsConfig() {
10868
10912
  try {
10869
10913
  const file = getMcpToolsFile();
10870
- if (!fs20.existsSync(file)) return {};
10871
- const raw = fs20.readFileSync(file, "utf-8");
10914
+ if (!fs21.existsSync(file)) return {};
10915
+ const raw = fs21.readFileSync(file, "utf-8");
10872
10916
  return JSON.parse(raw);
10873
10917
  } catch {
10874
10918
  return {};
@@ -10878,10 +10922,10 @@ function writeMcpToolsConfig(config) {
10878
10922
  try {
10879
10923
  const file = getMcpToolsFile();
10880
10924
  const dir = path24.dirname(file);
10881
- if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
10925
+ if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
10882
10926
  const tmpPath = `${file}.${os18.hostname()}.${process.pid}.tmp`;
10883
- fs20.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
10884
- fs20.renameSync(tmpPath, file);
10927
+ fs21.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
10928
+ fs21.renameSync(tmpPath, file);
10885
10929
  } catch (e) {
10886
10930
  console.error("Failed to write mcp-tools.json", e);
10887
10931
  }
@@ -10928,7 +10972,7 @@ var init_mcp_tools = __esm({
10928
10972
 
10929
10973
  // src/daemon/server.ts
10930
10974
  import http from "http";
10931
- import fs21 from "fs";
10975
+ import fs22 from "fs";
10932
10976
  import path25 from "path";
10933
10977
  import os19 from "os";
10934
10978
  import { randomUUID as randomUUID4 } from "crypto";
@@ -10953,7 +10997,7 @@ function startDaemon() {
10953
10997
  idleTimer = setTimeout(() => {
10954
10998
  if (autoStarted) {
10955
10999
  try {
10956
- fs21.unlinkSync(DAEMON_PID_FILE);
11000
+ fs22.unlinkSync(DAEMON_PID_FILE);
10957
11001
  } catch {
10958
11002
  }
10959
11003
  }
@@ -11489,7 +11533,7 @@ data: ${JSON.stringify(item.data)}
11489
11533
  const periodParam = reqUrl.searchParams.get("period") || "7d";
11490
11534
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
11491
11535
  const logPath = path25.join(os19.homedir(), ".node9", "audit.log");
11492
- if (!fs21.existsSync(logPath)) {
11536
+ if (!fs22.existsSync(logPath)) {
11493
11537
  res.writeHead(200, { "Content-Type": "application/json" });
11494
11538
  return res.end(
11495
11539
  JSON.stringify({
@@ -11502,7 +11546,7 @@ data: ${JSON.stringify(item.data)}
11502
11546
  );
11503
11547
  }
11504
11548
  try {
11505
- const raw = fs21.readFileSync(logPath, "utf-8");
11549
+ const raw = fs22.readFileSync(logPath, "utf-8");
11506
11550
  const allEntries = raw.split("\n").flatMap((line) => {
11507
11551
  if (!line.trim()) return [];
11508
11552
  try {
@@ -11606,7 +11650,7 @@ data: ${JSON.stringify(item.data)}
11606
11650
  }
11607
11651
  try {
11608
11652
  const d = /* @__PURE__ */ new Date();
11609
- d.setDate(d.getDate() - 90);
11653
+ d.setDate(d.getDate() - 30);
11610
11654
  d.setHours(0, 0, 0, 0);
11611
11655
  const EMPTY_SCAN = {
11612
11656
  filesScanned: 0,
@@ -11797,7 +11841,7 @@ data: ${JSON.stringify(item.data)}
11797
11841
  if (!validToken(req)) return res.writeHead(403).end();
11798
11842
  try {
11799
11843
  const { serverKey, disabledTools } = JSON.parse(await readBody(req));
11800
- if (typeof serverKey !== "string" || !Array.isArray(disabledTools)) {
11844
+ if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(disabledTools)) {
11801
11845
  res.writeHead(400).end();
11802
11846
  return;
11803
11847
  }
@@ -11819,7 +11863,7 @@ data: ${JSON.stringify(item.data)}
11819
11863
  if (req.headers["x-node9-internal"] !== internalToken) return res.writeHead(403).end();
11820
11864
  try {
11821
11865
  const { serverKey, tools } = JSON.parse(await readBody(req));
11822
- if (typeof serverKey !== "string" || !Array.isArray(tools)) {
11866
+ if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(tools)) {
11823
11867
  res.writeHead(400).end();
11824
11868
  return;
11825
11869
  }
@@ -11925,14 +11969,14 @@ data: ${JSON.stringify(item.data)}
11925
11969
  server.on("error", (e) => {
11926
11970
  if (e.code === "EADDRINUSE") {
11927
11971
  try {
11928
- if (fs21.existsSync(DAEMON_PID_FILE)) {
11929
- const { pid } = JSON.parse(fs21.readFileSync(DAEMON_PID_FILE, "utf-8"));
11972
+ if (fs22.existsSync(DAEMON_PID_FILE)) {
11973
+ const { pid } = JSON.parse(fs22.readFileSync(DAEMON_PID_FILE, "utf-8"));
11930
11974
  process.kill(pid, 0);
11931
11975
  return process.exit(0);
11932
11976
  }
11933
11977
  } catch {
11934
11978
  try {
11935
- fs21.unlinkSync(DAEMON_PID_FILE);
11979
+ fs22.unlinkSync(DAEMON_PID_FILE);
11936
11980
  } catch {
11937
11981
  }
11938
11982
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -12011,15 +12055,15 @@ var init_server = __esm({
12011
12055
  });
12012
12056
 
12013
12057
  // src/daemon/service.ts
12014
- import fs22 from "fs";
12058
+ import fs23 from "fs";
12015
12059
  import path26 from "path";
12016
12060
  import os20 from "os";
12017
12061
  import { spawnSync as spawnSync3, execFileSync } from "child_process";
12018
12062
  function resolveNode9Binary() {
12019
12063
  try {
12020
12064
  const script = process.argv[1];
12021
- if (typeof script === "string" && path26.isAbsolute(script) && fs22.existsSync(script)) {
12022
- return fs22.realpathSync(script);
12065
+ if (typeof script === "string" && path26.isAbsolute(script) && fs23.existsSync(script)) {
12066
+ return fs23.realpathSync(script);
12023
12067
  }
12024
12068
  } catch {
12025
12069
  }
@@ -12077,8 +12121,8 @@ function launchdPlist(binaryPath) {
12077
12121
  }
12078
12122
  function installLaunchd(binaryPath) {
12079
12123
  const dir = path26.dirname(LAUNCHD_PLIST);
12080
- if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
12081
- fs22.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
12124
+ if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
12125
+ fs23.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
12082
12126
  spawnSync3("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
12083
12127
  const r = spawnSync3("launchctl", ["load", "-w", LAUNCHD_PLIST], {
12084
12128
  encoding: "utf8",
@@ -12089,13 +12133,13 @@ function installLaunchd(binaryPath) {
12089
12133
  }
12090
12134
  }
12091
12135
  function uninstallLaunchd() {
12092
- if (fs22.existsSync(LAUNCHD_PLIST)) {
12136
+ if (fs23.existsSync(LAUNCHD_PLIST)) {
12093
12137
  spawnSync3("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
12094
- fs22.unlinkSync(LAUNCHD_PLIST);
12138
+ fs23.unlinkSync(LAUNCHD_PLIST);
12095
12139
  }
12096
12140
  }
12097
12141
  function isLaunchdInstalled() {
12098
- return fs22.existsSync(LAUNCHD_PLIST);
12142
+ return fs23.existsSync(LAUNCHD_PLIST);
12099
12143
  }
12100
12144
  function systemdUnit(binaryPath) {
12101
12145
  return `[Unit]
@@ -12115,10 +12159,10 @@ WantedBy=default.target
12115
12159
  `;
12116
12160
  }
12117
12161
  function installSystemd(binaryPath) {
12118
- if (!fs22.existsSync(SYSTEMD_UNIT_DIR)) {
12119
- fs22.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
12162
+ if (!fs23.existsSync(SYSTEMD_UNIT_DIR)) {
12163
+ fs23.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
12120
12164
  }
12121
- fs22.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
12165
+ fs23.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
12122
12166
  try {
12123
12167
  execFileSync("loginctl", ["enable-linger", os20.userInfo().username], { timeout: 3e3 });
12124
12168
  } catch {
@@ -12140,23 +12184,23 @@ function installSystemd(binaryPath) {
12140
12184
  }
12141
12185
  }
12142
12186
  function uninstallSystemd() {
12143
- if (fs22.existsSync(SYSTEMD_UNIT)) {
12187
+ if (fs23.existsSync(SYSTEMD_UNIT)) {
12144
12188
  spawnSync3("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
12145
12189
  encoding: "utf8",
12146
12190
  timeout: 5e3
12147
12191
  });
12148
12192
  spawnSync3("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
12149
- fs22.unlinkSync(SYSTEMD_UNIT);
12193
+ fs23.unlinkSync(SYSTEMD_UNIT);
12150
12194
  }
12151
12195
  }
12152
12196
  function isSystemdInstalled() {
12153
- return fs22.existsSync(SYSTEMD_UNIT);
12197
+ return fs23.existsSync(SYSTEMD_UNIT);
12154
12198
  }
12155
12199
  function stopRunningDaemon() {
12156
12200
  const pidFile = path26.join(os20.homedir(), ".node9", "daemon.pid");
12157
- if (!fs22.existsSync(pidFile)) return;
12201
+ if (!fs23.existsSync(pidFile)) return;
12158
12202
  try {
12159
- const data = JSON.parse(fs22.readFileSync(pidFile, "utf-8"));
12203
+ const data = JSON.parse(fs23.readFileSync(pidFile, "utf-8"));
12160
12204
  const pid = data.pid;
12161
12205
  const MAX_PID2 = 4194304;
12162
12206
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -12176,7 +12220,7 @@ function stopRunningDaemon() {
12176
12220
  }
12177
12221
  }
12178
12222
  try {
12179
- fs22.unlinkSync(pidFile);
12223
+ fs23.unlinkSync(pidFile);
12180
12224
  } catch {
12181
12225
  }
12182
12226
  } catch {
@@ -12258,13 +12302,13 @@ var init_service = __esm({
12258
12302
  });
12259
12303
 
12260
12304
  // src/daemon/index.ts
12261
- import fs23 from "fs";
12305
+ import fs24 from "fs";
12262
12306
  import chalk4 from "chalk";
12263
12307
  import { spawnSync as spawnSync4 } from "child_process";
12264
12308
  function stopDaemon() {
12265
- if (!fs23.existsSync(DAEMON_PID_FILE)) return console.log(chalk4.yellow("Not running."));
12309
+ if (!fs24.existsSync(DAEMON_PID_FILE)) return console.log(chalk4.yellow("Not running."));
12266
12310
  try {
12267
- const data = JSON.parse(fs23.readFileSync(DAEMON_PID_FILE, "utf-8"));
12311
+ const data = JSON.parse(fs24.readFileSync(DAEMON_PID_FILE, "utf-8"));
12268
12312
  const pid = data.pid;
12269
12313
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
12270
12314
  console.log(chalk4.gray("Cleaned up invalid PID file."));
@@ -12276,7 +12320,7 @@ function stopDaemon() {
12276
12320
  console.log(chalk4.gray("Cleaned up stale PID file."));
12277
12321
  } finally {
12278
12322
  try {
12279
- fs23.unlinkSync(DAEMON_PID_FILE);
12323
+ fs24.unlinkSync(DAEMON_PID_FILE);
12280
12324
  } catch {
12281
12325
  }
12282
12326
  }
@@ -12285,9 +12329,9 @@ function daemonStatus() {
12285
12329
  const serviceInstalled = isDaemonServiceInstalled();
12286
12330
  const serviceLabel = serviceInstalled ? chalk4.green("installed (starts on login)") : chalk4.yellow("not installed \u2014 run: node9 daemon install");
12287
12331
  let processStatus;
12288
- if (fs23.existsSync(DAEMON_PID_FILE)) {
12332
+ if (fs24.existsSync(DAEMON_PID_FILE)) {
12289
12333
  try {
12290
- const data = JSON.parse(fs23.readFileSync(DAEMON_PID_FILE, "utf-8"));
12334
+ const data = JSON.parse(fs24.readFileSync(DAEMON_PID_FILE, "utf-8"));
12291
12335
  const pid = data.pid;
12292
12336
  const port = data.port;
12293
12337
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -12337,7 +12381,7 @@ __export(tail_exports, {
12337
12381
  });
12338
12382
  import http2 from "http";
12339
12383
  import chalk25 from "chalk";
12340
- import fs38 from "fs";
12384
+ import fs39 from "fs";
12341
12385
  import os35 from "os";
12342
12386
  import path42 from "path";
12343
12387
  import readline5 from "readline";
@@ -12358,19 +12402,19 @@ function getModelContextLimit(model) {
12358
12402
  }
12359
12403
  function readSessionUsage() {
12360
12404
  const projectsDir = path42.join(os35.homedir(), ".claude", "projects");
12361
- if (!fs38.existsSync(projectsDir)) return null;
12405
+ if (!fs39.existsSync(projectsDir)) return null;
12362
12406
  let latestFile = null;
12363
12407
  let latestMtime = 0;
12364
12408
  try {
12365
- for (const dir of fs38.readdirSync(projectsDir)) {
12409
+ for (const dir of fs39.readdirSync(projectsDir)) {
12366
12410
  const dirPath = path42.join(projectsDir, dir);
12367
12411
  try {
12368
- if (!fs38.statSync(dirPath).isDirectory()) continue;
12369
- for (const file of fs38.readdirSync(dirPath)) {
12412
+ if (!fs39.statSync(dirPath).isDirectory()) continue;
12413
+ for (const file of fs39.readdirSync(dirPath)) {
12370
12414
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
12371
12415
  const filePath = path42.join(dirPath, file);
12372
12416
  try {
12373
- const mtime = fs38.statSync(filePath).mtimeMs;
12417
+ const mtime = fs39.statSync(filePath).mtimeMs;
12374
12418
  if (mtime > latestMtime) {
12375
12419
  latestMtime = mtime;
12376
12420
  latestFile = filePath;
@@ -12385,7 +12429,7 @@ function readSessionUsage() {
12385
12429
  }
12386
12430
  if (!latestFile) return null;
12387
12431
  try {
12388
- const lines = fs38.readFileSync(latestFile, "utf-8").split("\n");
12432
+ const lines = fs39.readFileSync(latestFile, "utf-8").split("\n");
12389
12433
  let lastModel = "";
12390
12434
  let lastInput = 0;
12391
12435
  let lastOutput = 0;
@@ -12474,9 +12518,9 @@ function renderPending(activity) {
12474
12518
  }
12475
12519
  async function ensureDaemon() {
12476
12520
  let pidPort = null;
12477
- if (fs38.existsSync(PID_FILE)) {
12521
+ if (fs39.existsSync(PID_FILE)) {
12478
12522
  try {
12479
- const { port } = JSON.parse(fs38.readFileSync(PID_FILE, "utf-8"));
12523
+ const { port } = JSON.parse(fs39.readFileSync(PID_FILE, "utf-8"));
12480
12524
  pidPort = port;
12481
12525
  } catch {
12482
12526
  console.error(chalk25.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -12631,7 +12675,7 @@ function buildRecoveryCardLines(req) {
12631
12675
  function readApproversFromDisk() {
12632
12676
  const configPath = path42.join(os35.homedir(), ".node9", "config.json");
12633
12677
  try {
12634
- const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
12678
+ const raw = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
12635
12679
  const settings = raw.settings ?? {};
12636
12680
  return settings.approvers ?? {};
12637
12681
  } catch {
@@ -12649,13 +12693,13 @@ function approverStatusLine() {
12649
12693
  function toggleApprover(channel) {
12650
12694
  const configPath = path42.join(os35.homedir(), ".node9", "config.json");
12651
12695
  try {
12652
- const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
12696
+ const raw = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
12653
12697
  const settings = raw.settings ?? {};
12654
12698
  const approvers = settings.approvers ?? {};
12655
12699
  approvers[channel] = approvers[channel] === false;
12656
12700
  settings.approvers = approvers;
12657
12701
  raw.settings = settings;
12658
- fs38.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
12702
+ fs39.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
12659
12703
  } catch (err2) {
12660
12704
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
12661
12705
  `);
@@ -12825,7 +12869,7 @@ async function startTail(options = {}) {
12825
12869
  }
12826
12870
  postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
12827
12871
  try {
12828
- fs38.appendFileSync(
12872
+ fs39.appendFileSync(
12829
12873
  path42.join(os35.homedir(), ".node9", "hook-debug.log"),
12830
12874
  `[tail] POST /decision failed: ${String(err2)}
12831
12875
  `
@@ -12909,7 +12953,7 @@ async function startTail(options = {}) {
12909
12953
  }
12910
12954
  const auditLog = path42.join(os35.homedir(), ".node9", "audit.log");
12911
12955
  try {
12912
- const unackedDlp = fs38.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
12956
+ const unackedDlp = fs39.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
12913
12957
  if (unackedDlp > 0) {
12914
12958
  console.log("");
12915
12959
  console.log(
@@ -13149,7 +13193,7 @@ __export(hud_exports, {
13149
13193
  main: () => main,
13150
13194
  renderEnvironmentLine: () => renderEnvironmentLine
13151
13195
  });
13152
- import fs39 from "fs";
13196
+ import fs40 from "fs";
13153
13197
  import path43 from "path";
13154
13198
  import os36 from "os";
13155
13199
  import http3 from "http";
@@ -13227,9 +13271,9 @@ function formatTimeLeft(resetsAt) {
13227
13271
  return ` (${m}m left)`;
13228
13272
  }
13229
13273
  function safeReadJson(filePath) {
13230
- if (!fs39.existsSync(filePath)) return null;
13274
+ if (!fs40.existsSync(filePath)) return null;
13231
13275
  try {
13232
- return JSON.parse(fs39.readFileSync(filePath, "utf-8"));
13276
+ return JSON.parse(fs40.readFileSync(filePath, "utf-8"));
13233
13277
  } catch {
13234
13278
  return null;
13235
13279
  }
@@ -13250,10 +13294,10 @@ function countHooksInFile(filePath) {
13250
13294
  return Object.keys(cfg.hooks).length;
13251
13295
  }
13252
13296
  function countRulesInDir(rulesDir) {
13253
- if (!fs39.existsSync(rulesDir)) return 0;
13297
+ if (!fs40.existsSync(rulesDir)) return 0;
13254
13298
  let count = 0;
13255
13299
  try {
13256
- for (const entry of fs39.readdirSync(rulesDir, { withFileTypes: true })) {
13300
+ for (const entry of fs40.readdirSync(rulesDir, { withFileTypes: true })) {
13257
13301
  if (entry.isDirectory()) {
13258
13302
  count += countRulesInDir(path43.join(rulesDir, entry.name));
13259
13303
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -13279,7 +13323,7 @@ function countConfigs(cwd) {
13279
13323
  let hooksCount = 0;
13280
13324
  const userMcpServers = /* @__PURE__ */ new Set();
13281
13325
  const projectMcpServers = /* @__PURE__ */ new Set();
13282
- if (fs39.existsSync(path43.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
13326
+ if (fs40.existsSync(path43.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
13283
13327
  rulesCount += countRulesInDir(path43.join(claudeDir, "rules"));
13284
13328
  const userSettings = path43.join(claudeDir, "settings.json");
13285
13329
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
@@ -13290,18 +13334,18 @@ function countConfigs(cwd) {
13290
13334
  userMcpServers.delete(name);
13291
13335
  }
13292
13336
  if (cwd) {
13293
- if (fs39.existsSync(path43.join(cwd, "CLAUDE.md"))) claudeMdCount++;
13294
- if (fs39.existsSync(path43.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
13337
+ if (fs40.existsSync(path43.join(cwd, "CLAUDE.md"))) claudeMdCount++;
13338
+ if (fs40.existsSync(path43.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
13295
13339
  const projectClaudeDir = path43.join(cwd, ".claude");
13296
13340
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
13297
13341
  if (!overlapsUserScope) {
13298
- if (fs39.existsSync(path43.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
13342
+ if (fs40.existsSync(path43.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
13299
13343
  rulesCount += countRulesInDir(path43.join(projectClaudeDir, "rules"));
13300
13344
  const projSettings = path43.join(projectClaudeDir, "settings.json");
13301
13345
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
13302
13346
  hooksCount += countHooksInFile(projSettings);
13303
13347
  }
13304
- if (fs39.existsSync(path43.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
13348
+ if (fs40.existsSync(path43.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
13305
13349
  const localSettings = path43.join(projectClaudeDir, "settings.local.json");
13306
13350
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
13307
13351
  hooksCount += countHooksInFile(localSettings);
@@ -13339,11 +13383,11 @@ function readActiveShieldsHud() {
13339
13383
  }
13340
13384
  try {
13341
13385
  const shieldsPath = path43.join(os36.homedir(), ".node9", "shields.json");
13342
- if (!fs39.existsSync(shieldsPath)) {
13386
+ if (!fs40.existsSync(shieldsPath)) {
13343
13387
  shieldsCache = { value: [], ts: now };
13344
13388
  return [];
13345
13389
  }
13346
- const parsed = JSON.parse(fs39.readFileSync(shieldsPath, "utf-8"));
13390
+ const parsed = JSON.parse(fs40.readFileSync(shieldsPath, "utf-8"));
13347
13391
  if (!Array.isArray(parsed.active)) {
13348
13392
  shieldsCache = { value: [], ts: now };
13349
13393
  return [];
@@ -13445,17 +13489,17 @@ function renderContextLine(stdin) {
13445
13489
  async function main() {
13446
13490
  try {
13447
13491
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
13448
- if (fs39.existsSync(path43.join(os36.homedir(), ".node9", "hud-debug"))) {
13492
+ if (fs40.existsSync(path43.join(os36.homedir(), ".node9", "hud-debug"))) {
13449
13493
  try {
13450
13494
  const logPath = path43.join(os36.homedir(), ".node9", "hud-debug.log");
13451
13495
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
13452
13496
  let size = 0;
13453
13497
  try {
13454
- size = fs39.statSync(logPath).size;
13498
+ size = fs40.statSync(logPath).size;
13455
13499
  } catch {
13456
13500
  }
13457
13501
  if (size < MAX_LOG_SIZE) {
13458
- fs39.appendFileSync(
13502
+ fs40.appendFileSync(
13459
13503
  logPath,
13460
13504
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
13461
13505
  );
@@ -13479,8 +13523,8 @@ async function main() {
13479
13523
  path43.join(cwd, "node9.config.json"),
13480
13524
  path43.join(os36.homedir(), ".node9", "config.json")
13481
13525
  ]) {
13482
- if (!fs39.existsSync(configPath)) continue;
13483
- const cfg = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
13526
+ if (!fs40.existsSync(configPath)) continue;
13527
+ const cfg = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
13484
13528
  const hud = cfg.settings?.hud;
13485
13529
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
13486
13530
  }
@@ -14548,7 +14592,7 @@ function getAgentsStatus(homeDir2 = os11.homedir()) {
14548
14592
  // src/cli.ts
14549
14593
  init_daemon2();
14550
14594
  import chalk26 from "chalk";
14551
- import fs40 from "fs";
14595
+ import fs41 from "fs";
14552
14596
  import path44 from "path";
14553
14597
  import os37 from "os";
14554
14598
  import { confirm as confirm2 } from "@inquirer/prompts";
@@ -14739,7 +14783,7 @@ init_daemon();
14739
14783
  init_config();
14740
14784
  init_policy();
14741
14785
  import chalk6 from "chalk";
14742
- import fs26 from "fs";
14786
+ import fs27 from "fs";
14743
14787
  import { spawn as spawn6 } from "child_process";
14744
14788
  import path29 from "path";
14745
14789
  import os23 from "os";
@@ -14747,7 +14791,7 @@ import os23 from "os";
14747
14791
  // src/undo.ts
14748
14792
  import { spawnSync as spawnSync5, spawn as spawn5 } from "child_process";
14749
14793
  import crypto3 from "crypto";
14750
- import fs24 from "fs";
14794
+ import fs25 from "fs";
14751
14795
  import net3 from "net";
14752
14796
  import path27 from "path";
14753
14797
  import os21 from "os";
@@ -14777,16 +14821,16 @@ var MAX_SNAPSHOTS = 10;
14777
14821
  var GIT_TIMEOUT = 15e3;
14778
14822
  function readStack() {
14779
14823
  try {
14780
- if (fs24.existsSync(SNAPSHOT_STACK_PATH))
14781
- return JSON.parse(fs24.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
14824
+ if (fs25.existsSync(SNAPSHOT_STACK_PATH))
14825
+ return JSON.parse(fs25.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
14782
14826
  } catch {
14783
14827
  }
14784
14828
  return [];
14785
14829
  }
14786
14830
  function writeStack(stack) {
14787
14831
  const dir = path27.dirname(SNAPSHOT_STACK_PATH);
14788
- if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
14789
- fs24.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
14832
+ if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
14833
+ fs25.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
14790
14834
  }
14791
14835
  function extractFilePath(args) {
14792
14836
  if (!args || typeof args !== "object") return null;
@@ -14808,7 +14852,7 @@ function buildArgsSummary(tool, args) {
14808
14852
  function findProjectRoot(filePath) {
14809
14853
  let dir = path27.dirname(filePath);
14810
14854
  while (true) {
14811
- if (fs24.existsSync(path27.join(dir, ".git")) || fs24.existsSync(path27.join(dir, "package.json"))) {
14855
+ if (fs25.existsSync(path27.join(dir, ".git")) || fs25.existsSync(path27.join(dir, "package.json"))) {
14812
14856
  return dir;
14813
14857
  }
14814
14858
  const parent = path27.dirname(dir);
@@ -14819,7 +14863,7 @@ function findProjectRoot(filePath) {
14819
14863
  function normalizeCwdForHash(cwd) {
14820
14864
  let normalized;
14821
14865
  try {
14822
- normalized = fs24.realpathSync(cwd);
14866
+ normalized = fs25.realpathSync(cwd);
14823
14867
  } catch {
14824
14868
  normalized = cwd;
14825
14869
  }
@@ -14834,11 +14878,11 @@ function getShadowRepoDir(cwd) {
14834
14878
  function cleanOrphanedIndexFiles(shadowDir) {
14835
14879
  try {
14836
14880
  const cutoff = Date.now() - 6e4;
14837
- for (const f of fs24.readdirSync(shadowDir)) {
14881
+ for (const f of fs25.readdirSync(shadowDir)) {
14838
14882
  if (f.startsWith("index_")) {
14839
14883
  const fp = path27.join(shadowDir, f);
14840
14884
  try {
14841
- if (fs24.statSync(fp).mtimeMs < cutoff) fs24.unlinkSync(fp);
14885
+ if (fs25.statSync(fp).mtimeMs < cutoff) fs25.unlinkSync(fp);
14842
14886
  } catch {
14843
14887
  }
14844
14888
  }
@@ -14850,7 +14894,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
14850
14894
  const hardcoded = [".git", ".node9"];
14851
14895
  const lines = [...hardcoded, ...ignorePaths].join("\n");
14852
14896
  try {
14853
- fs24.writeFileSync(path27.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
14897
+ fs25.writeFileSync(path27.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
14854
14898
  } catch {
14855
14899
  }
14856
14900
  }
@@ -14865,23 +14909,23 @@ function ensureShadowRepo(shadowDir, cwd) {
14865
14909
  if (check.status === 0) {
14866
14910
  const ptPath = path27.join(shadowDir, "project-path.txt");
14867
14911
  try {
14868
- const stored = fs24.readFileSync(ptPath, "utf8").trim();
14912
+ const stored = fs25.readFileSync(ptPath, "utf8").trim();
14869
14913
  if (stored === normalizedCwd) return true;
14870
14914
  if (process.env.NODE9_DEBUG === "1")
14871
14915
  console.error(
14872
14916
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
14873
14917
  );
14874
- fs24.rmSync(shadowDir, { recursive: true, force: true });
14918
+ fs25.rmSync(shadowDir, { recursive: true, force: true });
14875
14919
  } catch {
14876
14920
  try {
14877
- fs24.writeFileSync(ptPath, normalizedCwd, "utf8");
14921
+ fs25.writeFileSync(ptPath, normalizedCwd, "utf8");
14878
14922
  } catch {
14879
14923
  }
14880
14924
  return true;
14881
14925
  }
14882
14926
  }
14883
14927
  try {
14884
- fs24.mkdirSync(shadowDir, { recursive: true });
14928
+ fs25.mkdirSync(shadowDir, { recursive: true });
14885
14929
  } catch {
14886
14930
  }
14887
14931
  const init = spawnSync5("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -14898,7 +14942,7 @@ function ensureShadowRepo(shadowDir, cwd) {
14898
14942
  timeout: 3e3
14899
14943
  });
14900
14944
  try {
14901
- fs24.writeFileSync(path27.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
14945
+ fs25.writeFileSync(path27.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
14902
14946
  } catch {
14903
14947
  }
14904
14948
  return true;
@@ -14995,7 +15039,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14995
15039
  writeStack(stack);
14996
15040
  const entry = stack[stack.length - 1];
14997
15041
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
14998
- fs24.writeFileSync(UNDO_LATEST_PATH, commitHash);
15042
+ fs25.writeFileSync(UNDO_LATEST_PATH, commitHash);
14999
15043
  if (shouldGc) {
15000
15044
  spawn5("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
15001
15045
  }
@@ -15006,7 +15050,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15006
15050
  } finally {
15007
15051
  if (indexFile) {
15008
15052
  try {
15009
- fs24.unlinkSync(indexFile);
15053
+ fs25.unlinkSync(indexFile);
15010
15054
  } catch {
15011
15055
  }
15012
15056
  }
@@ -15083,8 +15127,8 @@ function applyUndo(hash, cwd) {
15083
15127
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
15084
15128
  for (const file of [...tracked, ...untracked]) {
15085
15129
  const fullPath = path27.join(dir, file);
15086
- if (!snapshotFiles.has(file) && fs24.existsSync(fullPath)) {
15087
- fs24.unlinkSync(fullPath);
15130
+ if (!snapshotFiles.has(file) && fs25.existsSync(fullPath)) {
15131
+ fs25.unlinkSync(fullPath);
15088
15132
  }
15089
15133
  }
15090
15134
  return true;
@@ -15097,7 +15141,7 @@ function applyUndo(hash, cwd) {
15097
15141
  init_daemon_starter();
15098
15142
 
15099
15143
  // src/skill-pin.ts
15100
- import fs25 from "fs";
15144
+ import fs26 from "fs";
15101
15145
  import path28 from "path";
15102
15146
  import os22 from "os";
15103
15147
  import crypto4 from "crypto";
@@ -15116,7 +15160,7 @@ function walkDir(root) {
15116
15160
  if (out.length >= MAX_FILES) return;
15117
15161
  let entries;
15118
15162
  try {
15119
- entries = fs25.readdirSync(dir, { withFileTypes: true });
15163
+ entries = fs26.readdirSync(dir, { withFileTypes: true });
15120
15164
  } catch {
15121
15165
  return;
15122
15166
  }
@@ -15127,7 +15171,7 @@ function walkDir(root) {
15127
15171
  const rel = relDir ? path28.posix.join(relDir, entry.name) : entry.name;
15128
15172
  let lst;
15129
15173
  try {
15130
- lst = fs25.lstatSync(full);
15174
+ lst = fs26.lstatSync(full);
15131
15175
  } catch {
15132
15176
  continue;
15133
15177
  }
@@ -15139,7 +15183,7 @@ function walkDir(root) {
15139
15183
  if (!lst.isFile()) continue;
15140
15184
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
15141
15185
  try {
15142
- const buf = fs25.readFileSync(full);
15186
+ const buf = fs26.readFileSync(full);
15143
15187
  totalBytes += buf.length;
15144
15188
  out.push({ rel, hash: sha256Bytes(buf) });
15145
15189
  } catch {
@@ -15153,14 +15197,14 @@ function walkDir(root) {
15153
15197
  function hashSkillRoot(absPath) {
15154
15198
  let lst;
15155
15199
  try {
15156
- lst = fs25.lstatSync(absPath);
15200
+ lst = fs26.lstatSync(absPath);
15157
15201
  } catch {
15158
15202
  return { exists: false, contentHash: "", fileCount: 0 };
15159
15203
  }
15160
15204
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
15161
15205
  if (lst.isFile()) {
15162
15206
  try {
15163
- return { exists: true, contentHash: sha256Bytes(fs25.readFileSync(absPath)), fileCount: 1 };
15207
+ return { exists: true, contentHash: sha256Bytes(fs26.readFileSync(absPath)), fileCount: 1 };
15164
15208
  } catch {
15165
15209
  return { exists: false, contentHash: "", fileCount: 0 };
15166
15210
  }
@@ -15178,7 +15222,7 @@ function getRootKey(absPath) {
15178
15222
  function readSkillPinsSafe() {
15179
15223
  const filePath = getPinsFilePath();
15180
15224
  try {
15181
- const raw = fs25.readFileSync(filePath, "utf-8");
15225
+ const raw = fs26.readFileSync(filePath, "utf-8");
15182
15226
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
15183
15227
  const parsed = JSON.parse(raw);
15184
15228
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -15198,10 +15242,10 @@ function readSkillPins() {
15198
15242
  }
15199
15243
  function writeSkillPins(data) {
15200
15244
  const filePath = getPinsFilePath();
15201
- fs25.mkdirSync(path28.dirname(filePath), { recursive: true });
15245
+ fs26.mkdirSync(path28.dirname(filePath), { recursive: true });
15202
15246
  const tmp = `${filePath}.${crypto4.randomBytes(6).toString("hex")}.tmp`;
15203
- fs25.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15204
- fs25.renameSync(tmp, filePath);
15247
+ fs26.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15248
+ fs26.renameSync(tmp, filePath);
15205
15249
  }
15206
15250
  function removePin(rootKey) {
15207
15251
  const pins = readSkillPins();
@@ -15249,7 +15293,7 @@ function defaultSkillRoots(_cwd) {
15249
15293
  const roots = [];
15250
15294
  let registries;
15251
15295
  try {
15252
- registries = fs25.readdirSync(marketplaces, { withFileTypes: true });
15296
+ registries = fs26.readdirSync(marketplaces, { withFileTypes: true });
15253
15297
  } catch {
15254
15298
  return [];
15255
15299
  }
@@ -15258,7 +15302,7 @@ function defaultSkillRoots(_cwd) {
15258
15302
  const pluginsDir = path28.join(marketplaces, registry.name, "plugins");
15259
15303
  let plugins;
15260
15304
  try {
15261
- plugins = fs25.readdirSync(pluginsDir, { withFileTypes: true });
15305
+ plugins = fs26.readdirSync(pluginsDir, { withFileTypes: true });
15262
15306
  } catch {
15263
15307
  continue;
15264
15308
  }
@@ -15294,7 +15338,7 @@ function registerCheckCommand(program2) {
15294
15338
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
15295
15339
  const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15296
15340
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15297
- fs26.appendFileSync(
15341
+ fs27.appendFileSync(
15298
15342
  logPath,
15299
15343
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
15300
15344
  RAW: ${raw}
@@ -15309,8 +15353,8 @@ RAW: ${raw}
15309
15353
  const scriptPath = process.argv[1];
15310
15354
  if (typeof scriptPath !== "string" || !path29.isAbsolute(scriptPath))
15311
15355
  throw new Error("node9: argv[1] is not an absolute path");
15312
- const resolvedScript = fs26.realpathSync(scriptPath);
15313
- const packageDist = fs26.realpathSync(path29.resolve(__dirname, "../.."));
15356
+ const resolvedScript = fs27.realpathSync(scriptPath);
15357
+ const packageDist = fs27.realpathSync(path29.resolve(__dirname, "../.."));
15314
15358
  if (!resolvedScript.startsWith(packageDist + path29.sep) && resolvedScript !== packageDist)
15315
15359
  throw new Error(
15316
15360
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
@@ -15336,7 +15380,7 @@ RAW: ${raw}
15336
15380
  const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15337
15381
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
15338
15382
  try {
15339
- fs26.appendFileSync(
15383
+ fs27.appendFileSync(
15340
15384
  logPath,
15341
15385
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
15342
15386
  `
@@ -15347,9 +15391,9 @@ RAW: ${raw}
15347
15391
  }
15348
15392
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
15349
15393
  const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15350
- if (!fs26.existsSync(path29.dirname(logPath)))
15351
- fs26.mkdirSync(path29.dirname(logPath), { recursive: true });
15352
- fs26.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15394
+ if (!fs27.existsSync(path29.dirname(logPath)))
15395
+ fs27.mkdirSync(path29.dirname(logPath), { recursive: true });
15396
+ fs27.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15353
15397
  `);
15354
15398
  }
15355
15399
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -15362,8 +15406,8 @@ RAW: ${raw}
15362
15406
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
15363
15407
  let ttyFd = null;
15364
15408
  try {
15365
- ttyFd = fs26.openSync("/dev/tty", "w");
15366
- const writeTty = (line) => fs26.writeSync(ttyFd, line + "\n");
15409
+ ttyFd = fs27.openSync("/dev/tty", "w");
15410
+ const writeTty = (line) => fs27.writeSync(ttyFd, line + "\n");
15367
15411
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
15368
15412
  writeTty(chalk6.bgRed.white.bold(`
15369
15413
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -15382,7 +15426,7 @@ RAW: ${raw}
15382
15426
  } finally {
15383
15427
  if (ttyFd !== null)
15384
15428
  try {
15385
- fs26.closeSync(ttyFd);
15429
+ fs27.closeSync(ttyFd);
15386
15430
  } catch {
15387
15431
  }
15388
15432
  }
@@ -15420,13 +15464,13 @@ RAW: ${raw}
15420
15464
  const flagPath = path29.join(sessionsDir, `${safeSessionId}.json`);
15421
15465
  let flag = null;
15422
15466
  try {
15423
- flag = JSON.parse(fs26.readFileSync(flagPath, "utf-8"));
15467
+ flag = JSON.parse(fs27.readFileSync(flagPath, "utf-8"));
15424
15468
  } catch {
15425
15469
  }
15426
15470
  const writeFlag = (data2) => {
15427
15471
  try {
15428
- fs26.mkdirSync(sessionsDir, { recursive: true });
15429
- fs26.writeFileSync(
15472
+ fs27.mkdirSync(sessionsDir, { recursive: true });
15473
+ fs27.writeFileSync(
15430
15474
  flagPath,
15431
15475
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
15432
15476
  { mode: 384 }
@@ -15437,8 +15481,8 @@ RAW: ${raw}
15437
15481
  const sendSkillWarn = (detail, recoveryCmd) => {
15438
15482
  let ttyFd = null;
15439
15483
  try {
15440
- ttyFd = fs26.openSync("/dev/tty", "w");
15441
- const w = (line) => fs26.writeSync(ttyFd, line + "\n");
15484
+ ttyFd = fs27.openSync("/dev/tty", "w");
15485
+ const w = (line) => fs27.writeSync(ttyFd, line + "\n");
15442
15486
  w(chalk6.yellow(`
15443
15487
  \u26A0\uFE0F Node9: installed skill drift detected`));
15444
15488
  w(chalk6.gray(` ${detail}`));
@@ -15453,7 +15497,7 @@ RAW: ${raw}
15453
15497
  } finally {
15454
15498
  if (ttyFd !== null)
15455
15499
  try {
15456
- fs26.closeSync(ttyFd);
15500
+ fs27.closeSync(ttyFd);
15457
15501
  } catch {
15458
15502
  }
15459
15503
  }
@@ -15510,10 +15554,10 @@ RAW: ${raw}
15510
15554
  }
15511
15555
  try {
15512
15556
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
15513
- for (const name of fs26.readdirSync(sessionsDir)) {
15557
+ for (const name of fs27.readdirSync(sessionsDir)) {
15514
15558
  const p = path29.join(sessionsDir, name);
15515
15559
  try {
15516
- if (fs26.statSync(p).mtimeMs < cutoff) fs26.unlinkSync(p);
15560
+ if (fs27.statSync(p).mtimeMs < cutoff) fs27.unlinkSync(p);
15517
15561
  } catch {
15518
15562
  }
15519
15563
  }
@@ -15525,7 +15569,7 @@ RAW: ${raw}
15525
15569
  try {
15526
15570
  const dbg = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15527
15571
  const msg = err2 instanceof Error ? err2.message : String(err2);
15528
- fs26.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15572
+ fs27.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15529
15573
  `);
15530
15574
  } catch {
15531
15575
  }
@@ -15547,12 +15591,12 @@ RAW: ${raw}
15547
15591
  }
15548
15592
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
15549
15593
  try {
15550
- const tty = fs26.openSync("/dev/tty", "w");
15551
- fs26.writeSync(
15594
+ const tty = fs27.openSync("/dev/tty", "w");
15595
+ fs27.writeSync(
15552
15596
  tty,
15553
15597
  chalk6.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
15554
15598
  );
15555
- fs26.closeSync(tty);
15599
+ fs27.closeSync(tty);
15556
15600
  } catch {
15557
15601
  }
15558
15602
  const daemonReady = await autoStartDaemonAndWait();
@@ -15581,7 +15625,7 @@ RAW: ${raw}
15581
15625
  if (process.env.NODE9_DEBUG === "1") {
15582
15626
  const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15583
15627
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15584
- fs26.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15628
+ fs27.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15585
15629
  `);
15586
15630
  }
15587
15631
  process.exit(0);
@@ -15617,7 +15661,7 @@ RAW: ${raw}
15617
15661
  // src/cli/commands/log.ts
15618
15662
  init_audit();
15619
15663
  init_config();
15620
- import fs27 from "fs";
15664
+ import fs28 from "fs";
15621
15665
  import path30 from "path";
15622
15666
  import os24 from "os";
15623
15667
  init_daemon();
@@ -15693,9 +15737,9 @@ function registerLogCommand(program2) {
15693
15737
  source: "post-hook"
15694
15738
  };
15695
15739
  const logPath = path30.join(os24.homedir(), ".node9", "audit.log");
15696
- if (!fs27.existsSync(path30.dirname(logPath)))
15697
- fs27.mkdirSync(path30.dirname(logPath), { recursive: true });
15698
- fs27.appendFileSync(logPath, JSON.stringify(entry) + "\n");
15740
+ if (!fs28.existsSync(path30.dirname(logPath)))
15741
+ fs28.mkdirSync(path30.dirname(logPath), { recursive: true });
15742
+ fs28.appendFileSync(logPath, JSON.stringify(entry) + "\n");
15699
15743
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
15700
15744
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
15701
15745
  if (command) {
@@ -15751,7 +15795,7 @@ function registerLogCommand(program2) {
15751
15795
  `);
15752
15796
  const debugPath = path30.join(os24.homedir(), ".node9", "hook-debug.log");
15753
15797
  try {
15754
- fs27.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
15798
+ fs28.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
15755
15799
  `);
15756
15800
  } catch {
15757
15801
  }
@@ -16152,7 +16196,7 @@ function registerConfigShowCommand(program2) {
16152
16196
  // src/cli/commands/doctor.ts
16153
16197
  init_daemon();
16154
16198
  import chalk8 from "chalk";
16155
- import fs28 from "fs";
16199
+ import fs29 from "fs";
16156
16200
  import path31 from "path";
16157
16201
  import os25 from "os";
16158
16202
  import { execSync as execSync2 } from "child_process";
@@ -16208,9 +16252,9 @@ function registerDoctorCommand(program2, version2) {
16208
16252
  }
16209
16253
  section("Configuration");
16210
16254
  const globalConfigPath = path31.join(homeDir2, ".node9", "config.json");
16211
- if (fs28.existsSync(globalConfigPath)) {
16255
+ if (fs29.existsSync(globalConfigPath)) {
16212
16256
  try {
16213
- JSON.parse(fs28.readFileSync(globalConfigPath, "utf-8"));
16257
+ JSON.parse(fs29.readFileSync(globalConfigPath, "utf-8"));
16214
16258
  pass("~/.node9/config.json found and valid");
16215
16259
  } catch {
16216
16260
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -16219,9 +16263,9 @@ function registerDoctorCommand(program2, version2) {
16219
16263
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
16220
16264
  }
16221
16265
  const projectConfigPath = path31.join(process.cwd(), "node9.config.json");
16222
- if (fs28.existsSync(projectConfigPath)) {
16266
+ if (fs29.existsSync(projectConfigPath)) {
16223
16267
  try {
16224
- JSON.parse(fs28.readFileSync(projectConfigPath, "utf-8"));
16268
+ JSON.parse(fs29.readFileSync(projectConfigPath, "utf-8"));
16225
16269
  pass("node9.config.json found and valid (project)");
16226
16270
  } catch {
16227
16271
  fail(
@@ -16231,7 +16275,7 @@ function registerDoctorCommand(program2, version2) {
16231
16275
  }
16232
16276
  }
16233
16277
  const credsPath = path31.join(homeDir2, ".node9", "credentials.json");
16234
- if (fs28.existsSync(credsPath)) {
16278
+ if (fs29.existsSync(credsPath)) {
16235
16279
  pass("Cloud credentials found (~/.node9/credentials.json)");
16236
16280
  } else {
16237
16281
  warn(
@@ -16241,9 +16285,9 @@ function registerDoctorCommand(program2, version2) {
16241
16285
  }
16242
16286
  section("Agent Hooks");
16243
16287
  const claudeSettingsPath = path31.join(homeDir2, ".claude", "settings.json");
16244
- if (fs28.existsSync(claudeSettingsPath)) {
16288
+ if (fs29.existsSync(claudeSettingsPath)) {
16245
16289
  try {
16246
- const cs = JSON.parse(fs28.readFileSync(claudeSettingsPath, "utf-8"));
16290
+ const cs = JSON.parse(fs29.readFileSync(claudeSettingsPath, "utf-8"));
16247
16291
  const hasHook = cs.hooks?.PreToolUse?.some(
16248
16292
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16249
16293
  );
@@ -16260,9 +16304,9 @@ function registerDoctorCommand(program2, version2) {
16260
16304
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
16261
16305
  }
16262
16306
  const geminiSettingsPath = path31.join(homeDir2, ".gemini", "settings.json");
16263
- if (fs28.existsSync(geminiSettingsPath)) {
16307
+ if (fs29.existsSync(geminiSettingsPath)) {
16264
16308
  try {
16265
- const gs = JSON.parse(fs28.readFileSync(geminiSettingsPath, "utf-8"));
16309
+ const gs = JSON.parse(fs29.readFileSync(geminiSettingsPath, "utf-8"));
16266
16310
  const hasHook = gs.hooks?.BeforeTool?.some(
16267
16311
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16268
16312
  );
@@ -16279,9 +16323,9 @@ function registerDoctorCommand(program2, version2) {
16279
16323
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
16280
16324
  }
16281
16325
  const cursorHooksPath = path31.join(homeDir2, ".cursor", "hooks.json");
16282
- if (fs28.existsSync(cursorHooksPath)) {
16326
+ if (fs29.existsSync(cursorHooksPath)) {
16283
16327
  try {
16284
- const cur = JSON.parse(fs28.readFileSync(cursorHooksPath, "utf-8"));
16328
+ const cur = JSON.parse(fs29.readFileSync(cursorHooksPath, "utf-8"));
16285
16329
  const hasHook = cur.hooks?.preToolUse?.some(
16286
16330
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
16287
16331
  );
@@ -16319,7 +16363,7 @@ function registerDoctorCommand(program2, version2) {
16319
16363
 
16320
16364
  // src/cli/commands/audit.ts
16321
16365
  import chalk9 from "chalk";
16322
- import fs29 from "fs";
16366
+ import fs30 from "fs";
16323
16367
  import path32 from "path";
16324
16368
  import os26 from "os";
16325
16369
  function formatRelativeTime(timestamp) {
@@ -16335,13 +16379,13 @@ function formatRelativeTime(timestamp) {
16335
16379
  function registerAuditCommand(program2) {
16336
16380
  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) => {
16337
16381
  const logPath = path32.join(os26.homedir(), ".node9", "audit.log");
16338
- if (!fs29.existsSync(logPath)) {
16382
+ if (!fs30.existsSync(logPath)) {
16339
16383
  console.log(
16340
16384
  chalk9.yellow("No audit logs found. Run node9 with an agent to generate entries.")
16341
16385
  );
16342
16386
  return;
16343
16387
  }
16344
- const raw = fs29.readFileSync(logPath, "utf-8");
16388
+ const raw = fs30.readFileSync(logPath, "utf-8");
16345
16389
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
16346
16390
  let entries = lines.flatMap((line) => {
16347
16391
  try {
@@ -16395,7 +16439,7 @@ function registerAuditCommand(program2) {
16395
16439
 
16396
16440
  // src/cli/commands/report.ts
16397
16441
  import chalk10 from "chalk";
16398
- import fs30 from "fs";
16442
+ import fs31 from "fs";
16399
16443
  import path33 from "path";
16400
16444
  import os27 from "os";
16401
16445
  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;
@@ -16444,8 +16488,8 @@ function getDateRange(period) {
16444
16488
  }
16445
16489
  }
16446
16490
  function parseAuditLog(logPath) {
16447
- if (!fs30.existsSync(logPath)) return [];
16448
- const raw = fs30.readFileSync(logPath, "utf-8");
16491
+ if (!fs31.existsSync(logPath)) return [];
16492
+ const raw = fs31.readFileSync(logPath, "utf-8");
16449
16493
  return raw.split("\n").flatMap((line) => {
16450
16494
  if (!line.trim()) return [];
16451
16495
  try {
@@ -16525,10 +16569,10 @@ function loadClaudeCost(start, end) {
16525
16569
  cacheReadTokens: 0
16526
16570
  };
16527
16571
  const projectsDir = path33.join(os27.homedir(), ".claude", "projects");
16528
- if (!fs30.existsSync(projectsDir)) return empty;
16572
+ if (!fs31.existsSync(projectsDir)) return empty;
16529
16573
  let dirs;
16530
16574
  try {
16531
- dirs = fs30.readdirSync(projectsDir);
16575
+ dirs = fs31.readdirSync(projectsDir);
16532
16576
  } catch {
16533
16577
  return empty;
16534
16578
  }
@@ -16543,15 +16587,15 @@ function loadClaudeCost(start, end) {
16543
16587
  const projPath = path33.join(projectsDir, proj);
16544
16588
  let files;
16545
16589
  try {
16546
- const stat = fs30.statSync(projPath);
16590
+ const stat = fs31.statSync(projPath);
16547
16591
  if (!stat.isDirectory()) continue;
16548
- files = fs30.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16592
+ files = fs31.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16549
16593
  } catch {
16550
16594
  continue;
16551
16595
  }
16552
16596
  for (const file of files) {
16553
16597
  try {
16554
- const raw = fs30.readFileSync(path33.join(projPath, file), "utf-8");
16598
+ const raw = fs31.readFileSync(path33.join(projPath, file), "utf-8");
16555
16599
  for (const line of raw.split("\n")) {
16556
16600
  if (!line.trim()) continue;
16557
16601
  let entry;
@@ -16596,31 +16640,31 @@ function loadCodexCost(start, end) {
16596
16640
  const byDay = /* @__PURE__ */ new Map();
16597
16641
  let total = 0;
16598
16642
  let toolCalls = 0;
16599
- if (!fs30.existsSync(sessionsBase)) return { total, byDay, toolCalls };
16643
+ if (!fs31.existsSync(sessionsBase)) return { total, byDay, toolCalls };
16600
16644
  const jsonlFiles = [];
16601
16645
  try {
16602
- for (const year of fs30.readdirSync(sessionsBase)) {
16646
+ for (const year of fs31.readdirSync(sessionsBase)) {
16603
16647
  const yearPath = path33.join(sessionsBase, year);
16604
16648
  try {
16605
- if (!fs30.statSync(yearPath).isDirectory()) continue;
16649
+ if (!fs31.statSync(yearPath).isDirectory()) continue;
16606
16650
  } catch {
16607
16651
  continue;
16608
16652
  }
16609
- for (const month of fs30.readdirSync(yearPath)) {
16653
+ for (const month of fs31.readdirSync(yearPath)) {
16610
16654
  const monthPath = path33.join(yearPath, month);
16611
16655
  try {
16612
- if (!fs30.statSync(monthPath).isDirectory()) continue;
16656
+ if (!fs31.statSync(monthPath).isDirectory()) continue;
16613
16657
  } catch {
16614
16658
  continue;
16615
16659
  }
16616
- for (const day of fs30.readdirSync(monthPath)) {
16660
+ for (const day of fs31.readdirSync(monthPath)) {
16617
16661
  const dayPath = path33.join(monthPath, day);
16618
16662
  try {
16619
- if (!fs30.statSync(dayPath).isDirectory()) continue;
16663
+ if (!fs31.statSync(dayPath).isDirectory()) continue;
16620
16664
  } catch {
16621
16665
  continue;
16622
16666
  }
16623
- for (const file of fs30.readdirSync(dayPath)) {
16667
+ for (const file of fs31.readdirSync(dayPath)) {
16624
16668
  if (file.endsWith(".jsonl")) jsonlFiles.push(path33.join(dayPath, file));
16625
16669
  }
16626
16670
  }
@@ -16632,7 +16676,7 @@ function loadCodexCost(start, end) {
16632
16676
  for (const filePath of jsonlFiles) {
16633
16677
  let lines;
16634
16678
  try {
16635
- lines = fs30.readFileSync(filePath, "utf-8").split("\n");
16679
+ lines = fs31.readFileSync(filePath, "utf-8").split("\n");
16636
16680
  } catch {
16637
16681
  continue;
16638
16682
  }
@@ -17189,12 +17233,12 @@ function registerDaemonCommand(program2) {
17189
17233
  init_core();
17190
17234
  init_daemon();
17191
17235
  import chalk12 from "chalk";
17192
- import fs31 from "fs";
17236
+ import fs32 from "fs";
17193
17237
  import path34 from "path";
17194
17238
  import os28 from "os";
17195
17239
  function readJson2(filePath) {
17196
17240
  try {
17197
- if (fs31.existsSync(filePath)) return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
17241
+ if (fs32.existsSync(filePath)) return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
17198
17242
  } catch {
17199
17243
  }
17200
17244
  return null;
@@ -17262,10 +17306,10 @@ function registerStatusCommand(program2) {
17262
17306
  const projectConfig = path34.join(process.cwd(), "node9.config.json");
17263
17307
  const globalConfig = path34.join(os28.homedir(), ".node9", "config.json");
17264
17308
  console.log(
17265
- ` Local: ${fs31.existsSync(projectConfig) ? chalk12.green("Active (node9.config.json)") : chalk12.gray("Not present")}`
17309
+ ` Local: ${fs32.existsSync(projectConfig) ? chalk12.green("Active (node9.config.json)") : chalk12.gray("Not present")}`
17266
17310
  );
17267
17311
  console.log(
17268
- ` Global: ${fs31.existsSync(globalConfig) ? chalk12.green("Active (~/.node9/config.json)") : chalk12.gray("Not present")}`
17312
+ ` Global: ${fs32.existsSync(globalConfig) ? chalk12.green("Active (~/.node9/config.json)") : chalk12.gray("Not present")}`
17269
17313
  );
17270
17314
  if (mergedConfig.policy.sandboxPaths.length > 0) {
17271
17315
  console.log(
@@ -17340,7 +17384,7 @@ function registerStatusCommand(program2) {
17340
17384
  // src/cli/commands/init.ts
17341
17385
  init_core();
17342
17386
  import chalk13 from "chalk";
17343
- import fs32 from "fs";
17387
+ import fs33 from "fs";
17344
17388
  import path35 from "path";
17345
17389
  import os29 from "os";
17346
17390
  import https3 from "https";
@@ -17403,14 +17447,14 @@ function registerInitCommand(program2) {
17403
17447
  console.log("");
17404
17448
  }
17405
17449
  const configPath = path35.join(os29.homedir(), ".node9", "config.json");
17406
- if (fs32.existsSync(configPath) && !options.force) {
17450
+ if (fs33.existsSync(configPath) && !options.force) {
17407
17451
  try {
17408
- const existing = JSON.parse(fs32.readFileSync(configPath, "utf-8"));
17452
+ const existing = JSON.parse(fs33.readFileSync(configPath, "utf-8"));
17409
17453
  const settings = existing.settings ?? {};
17410
17454
  if (settings.mode !== chosenMode) {
17411
17455
  settings.mode = chosenMode;
17412
17456
  existing.settings = settings;
17413
- fs32.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
17457
+ fs33.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
17414
17458
  console.log(chalk13.green(`\u2705 Mode updated: ${chosenMode}`));
17415
17459
  } else {
17416
17460
  console.log(chalk13.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -17424,8 +17468,8 @@ function registerInitCommand(program2) {
17424
17468
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
17425
17469
  };
17426
17470
  const dir = path35.dirname(configPath);
17427
- if (!fs32.existsSync(dir)) fs32.mkdirSync(dir, { recursive: true });
17428
- fs32.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
17471
+ if (!fs33.existsSync(dir)) fs33.mkdirSync(dir, { recursive: true });
17472
+ fs33.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
17429
17473
  console.log(chalk13.green(`\u2705 Config created: ${configPath}`));
17430
17474
  console.log(chalk13.gray(` Mode: ${chosenMode}`));
17431
17475
  }
@@ -17865,7 +17909,7 @@ import { execa as execa2 } from "execa";
17865
17909
  init_provenance();
17866
17910
 
17867
17911
  // src/mcp-pin.ts
17868
- import fs33 from "fs";
17912
+ import fs34 from "fs";
17869
17913
  import path37 from "path";
17870
17914
  import os30 from "os";
17871
17915
  import crypto5 from "crypto";
@@ -17887,7 +17931,7 @@ function getServerKey(upstreamCommand) {
17887
17931
  function readMcpPinsSafe() {
17888
17932
  const filePath = getPinsFilePath2();
17889
17933
  try {
17890
- const raw = fs33.readFileSync(filePath, "utf-8");
17934
+ const raw = fs34.readFileSync(filePath, "utf-8");
17891
17935
  if (!raw.trim()) {
17892
17936
  return { ok: false, reason: "corrupt", detail: "empty file" };
17893
17937
  }
@@ -17911,10 +17955,10 @@ function readMcpPins() {
17911
17955
  }
17912
17956
  function writeMcpPins(data) {
17913
17957
  const filePath = getPinsFilePath2();
17914
- fs33.mkdirSync(path37.dirname(filePath), { recursive: true });
17958
+ fs34.mkdirSync(path37.dirname(filePath), { recursive: true });
17915
17959
  const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
17916
- fs33.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17917
- fs33.renameSync(tmp, filePath);
17960
+ fs34.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17961
+ fs34.renameSync(tmp, filePath);
17918
17962
  }
17919
17963
  function checkPin(serverKey, currentHash) {
17920
17964
  const result = readMcpPinsSafe();
@@ -18362,7 +18406,7 @@ function registerMcpGatewayCommand(program2) {
18362
18406
 
18363
18407
  // src/mcp-server/index.ts
18364
18408
  import readline4 from "readline";
18365
- import fs34 from "fs";
18409
+ import fs35 from "fs";
18366
18410
  import os31 from "os";
18367
18411
  import path38 from "path";
18368
18412
  import { spawnSync as spawnSync7 } from "child_process";
@@ -18618,10 +18662,10 @@ function handleStatus() {
18618
18662
  const projectConfig = path38.join(process.cwd(), "node9.config.json");
18619
18663
  const globalConfig = path38.join(os31.homedir(), ".node9", "config.json");
18620
18664
  lines.push(
18621
- `Project config (node9.config.json): ${fs34.existsSync(projectConfig) ? "present" : "not found"}`
18665
+ `Project config (node9.config.json): ${fs35.existsSync(projectConfig) ? "present" : "not found"}`
18622
18666
  );
18623
18667
  lines.push(
18624
- `Global config (~/.node9/config.json): ${fs34.existsSync(globalConfig) ? "present" : "not found"}`
18668
+ `Global config (~/.node9/config.json): ${fs35.existsSync(globalConfig) ? "present" : "not found"}`
18625
18669
  );
18626
18670
  return lines.join("\n");
18627
18671
  }
@@ -18699,8 +18743,8 @@ var GLOBAL_CONFIG_PATH2 = path38.join(os31.homedir(), ".node9", "config.json");
18699
18743
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
18700
18744
  function readGlobalConfigRaw() {
18701
18745
  try {
18702
- if (fs34.existsSync(GLOBAL_CONFIG_PATH2)) {
18703
- return JSON.parse(fs34.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
18746
+ if (fs35.existsSync(GLOBAL_CONFIG_PATH2)) {
18747
+ return JSON.parse(fs35.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
18704
18748
  }
18705
18749
  } catch {
18706
18750
  }
@@ -18708,8 +18752,8 @@ function readGlobalConfigRaw() {
18708
18752
  }
18709
18753
  function writeGlobalConfigRaw(data) {
18710
18754
  const dir = path38.dirname(GLOBAL_CONFIG_PATH2);
18711
- if (!fs34.existsSync(dir)) fs34.mkdirSync(dir, { recursive: true });
18712
- fs34.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
18755
+ if (!fs35.existsSync(dir)) fs35.mkdirSync(dir, { recursive: true });
18756
+ fs35.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
18713
18757
  }
18714
18758
  function handleApproverList() {
18715
18759
  const config = getConfig();
@@ -18754,8 +18798,8 @@ function handleAuditGet(args) {
18754
18798
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
18755
18799
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
18756
18800
  const auditPath = path38.join(os31.homedir(), ".node9", "audit.log");
18757
- if (!fs34.existsSync(auditPath)) return "No audit log found.";
18758
- const rawLines = fs34.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
18801
+ if (!fs35.existsSync(auditPath)) return "No audit log found.";
18802
+ const rawLines = fs35.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
18759
18803
  const parsed = [];
18760
18804
  for (const line of rawLines) {
18761
18805
  try {
@@ -19309,7 +19353,7 @@ init_scan();
19309
19353
 
19310
19354
  // src/cli/commands/sessions.ts
19311
19355
  import chalk22 from "chalk";
19312
- import fs35 from "fs";
19356
+ import fs36 from "fs";
19313
19357
  import path39 from "path";
19314
19358
  import os32 from "os";
19315
19359
  var CLAUDE_PRICING3 = {
@@ -19427,7 +19471,7 @@ function loadAuditEntries(auditPath) {
19427
19471
  const aPath = auditPath ?? path39.join(os32.homedir(), ".node9", "audit.log");
19428
19472
  let raw;
19429
19473
  try {
19430
- raw = fs35.readFileSync(aPath, "utf-8");
19474
+ raw = fs36.readFileSync(aPath, "utf-8");
19431
19475
  } catch {
19432
19476
  return [];
19433
19477
  }
@@ -19464,7 +19508,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
19464
19508
  }
19465
19509
  function buildGeminiSessions(days, allAuditEntries) {
19466
19510
  const tmpDir = path39.join(os32.homedir(), ".gemini", "tmp");
19467
- if (!fs35.existsSync(tmpDir)) return [];
19511
+ if (!fs36.existsSync(tmpDir)) return [];
19468
19512
  const cutoff = days !== null ? (() => {
19469
19513
  const d = /* @__PURE__ */ new Date();
19470
19514
  d.setDate(d.getDate() - days);
@@ -19473,7 +19517,7 @@ function buildGeminiSessions(days, allAuditEntries) {
19473
19517
  })() : null;
19474
19518
  let slugDirs;
19475
19519
  try {
19476
- slugDirs = fs35.readdirSync(tmpDir);
19520
+ slugDirs = fs36.readdirSync(tmpDir);
19477
19521
  } catch {
19478
19522
  return [];
19479
19523
  }
@@ -19481,27 +19525,27 @@ function buildGeminiSessions(days, allAuditEntries) {
19481
19525
  for (const slug of slugDirs) {
19482
19526
  const slugPath = path39.join(tmpDir, slug);
19483
19527
  try {
19484
- if (!fs35.statSync(slugPath).isDirectory()) continue;
19528
+ if (!fs36.statSync(slugPath).isDirectory()) continue;
19485
19529
  } catch {
19486
19530
  continue;
19487
19531
  }
19488
19532
  let projectRoot = path39.join(os32.homedir(), slug);
19489
19533
  try {
19490
- projectRoot = fs35.readFileSync(path39.join(slugPath, ".project_root"), "utf-8").trim();
19534
+ projectRoot = fs36.readFileSync(path39.join(slugPath, ".project_root"), "utf-8").trim();
19491
19535
  } catch {
19492
19536
  }
19493
19537
  const chatsDir = path39.join(slugPath, "chats");
19494
- if (!fs35.existsSync(chatsDir)) continue;
19538
+ if (!fs36.existsSync(chatsDir)) continue;
19495
19539
  let chatFiles;
19496
19540
  try {
19497
- chatFiles = fs35.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
19541
+ chatFiles = fs36.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
19498
19542
  } catch {
19499
19543
  continue;
19500
19544
  }
19501
19545
  for (const chatFile of chatFiles) {
19502
19546
  let raw;
19503
19547
  try {
19504
- raw = fs35.readFileSync(path39.join(chatsDir, chatFile), "utf-8");
19548
+ raw = fs36.readFileSync(path39.join(chatsDir, chatFile), "utf-8");
19505
19549
  } catch {
19506
19550
  continue;
19507
19551
  }
@@ -19582,7 +19626,7 @@ function buildGeminiSessions(days, allAuditEntries) {
19582
19626
  }
19583
19627
  function buildCodexSessions(days, allAuditEntries) {
19584
19628
  const sessionsBase = path39.join(os32.homedir(), ".codex", "sessions");
19585
- if (!fs35.existsSync(sessionsBase)) return [];
19629
+ if (!fs36.existsSync(sessionsBase)) return [];
19586
19630
  const cutoff = days !== null ? (() => {
19587
19631
  const d = /* @__PURE__ */ new Date();
19588
19632
  d.setDate(d.getDate() - days);
@@ -19591,28 +19635,28 @@ function buildCodexSessions(days, allAuditEntries) {
19591
19635
  })() : null;
19592
19636
  const jsonlFiles = [];
19593
19637
  try {
19594
- for (const year of fs35.readdirSync(sessionsBase)) {
19638
+ for (const year of fs36.readdirSync(sessionsBase)) {
19595
19639
  const yearPath = path39.join(sessionsBase, year);
19596
19640
  try {
19597
- if (!fs35.statSync(yearPath).isDirectory()) continue;
19641
+ if (!fs36.statSync(yearPath).isDirectory()) continue;
19598
19642
  } catch {
19599
19643
  continue;
19600
19644
  }
19601
- for (const month of fs35.readdirSync(yearPath)) {
19645
+ for (const month of fs36.readdirSync(yearPath)) {
19602
19646
  const monthPath = path39.join(yearPath, month);
19603
19647
  try {
19604
- if (!fs35.statSync(monthPath).isDirectory()) continue;
19648
+ if (!fs36.statSync(monthPath).isDirectory()) continue;
19605
19649
  } catch {
19606
19650
  continue;
19607
19651
  }
19608
- for (const day of fs35.readdirSync(monthPath)) {
19652
+ for (const day of fs36.readdirSync(monthPath)) {
19609
19653
  const dayPath = path39.join(monthPath, day);
19610
19654
  try {
19611
- if (!fs35.statSync(dayPath).isDirectory()) continue;
19655
+ if (!fs36.statSync(dayPath).isDirectory()) continue;
19612
19656
  } catch {
19613
19657
  continue;
19614
19658
  }
19615
- for (const file of fs35.readdirSync(dayPath)) {
19659
+ for (const file of fs36.readdirSync(dayPath)) {
19616
19660
  if (file.endsWith(".jsonl")) jsonlFiles.push(path39.join(dayPath, file));
19617
19661
  }
19618
19662
  }
@@ -19625,7 +19669,7 @@ function buildCodexSessions(days, allAuditEntries) {
19625
19669
  for (const filePath of jsonlFiles) {
19626
19670
  let lines;
19627
19671
  try {
19628
- lines = fs35.readFileSync(filePath, "utf-8").split("\n");
19672
+ lines = fs36.readFileSync(filePath, "utf-8").split("\n");
19629
19673
  } catch {
19630
19674
  continue;
19631
19675
  }
@@ -19706,7 +19750,7 @@ function buildSessions(days, historyPath) {
19706
19750
  const hPath = historyPath ?? path39.join(os32.homedir(), ".claude", "history.jsonl");
19707
19751
  let historyRaw;
19708
19752
  try {
19709
- historyRaw = fs35.readFileSync(hPath, "utf-8");
19753
+ historyRaw = fs36.readFileSync(hPath, "utf-8");
19710
19754
  } catch {
19711
19755
  return [];
19712
19756
  }
@@ -19731,7 +19775,7 @@ function buildSessions(days, historyPath) {
19731
19775
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
19732
19776
  let sessionLines = [];
19733
19777
  try {
19734
- sessionLines = fs35.readFileSync(jsonlFile, "utf-8").split("\n");
19778
+ sessionLines = fs36.readFileSync(jsonlFile, "utf-8").split("\n");
19735
19779
  } catch {
19736
19780
  }
19737
19781
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -19998,7 +20042,7 @@ function registerSessionsCommand(program2) {
19998
20042
  console.log(chalk22.cyan.bold("\u{1F4CB} node9 sessions") + chalk22.dim(" \u2014 what your AI agent did"));
19999
20043
  console.log("");
20000
20044
  const historyPath = path39.join(os32.homedir(), ".claude", "history.jsonl");
20001
- if (!fs35.existsSync(historyPath)) {
20045
+ if (!fs36.existsSync(historyPath)) {
20002
20046
  console.log(chalk22.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
20003
20047
  console.log(chalk22.gray(" Install Claude Code, run a few sessions, then try again.\n"));
20004
20048
  return;
@@ -20035,12 +20079,12 @@ function registerSessionsCommand(program2) {
20035
20079
 
20036
20080
  // src/cli/commands/skill-pin.ts
20037
20081
  import chalk23 from "chalk";
20038
- import fs36 from "fs";
20082
+ import fs37 from "fs";
20039
20083
  import os33 from "os";
20040
20084
  import path40 from "path";
20041
20085
  function wipeSkillSessions() {
20042
20086
  try {
20043
- fs36.rmSync(path40.join(os33.homedir(), ".node9", "skill-sessions"), {
20087
+ fs37.rmSync(path40.join(os33.homedir(), ".node9", "skill-sessions"), {
20044
20088
  recursive: true,
20045
20089
  force: true
20046
20090
  });
@@ -20123,7 +20167,7 @@ function registerSkillPinCommand(program2) {
20123
20167
 
20124
20168
  // src/cli/commands/dlp.ts
20125
20169
  import chalk24 from "chalk";
20126
- import fs37 from "fs";
20170
+ import fs38 from "fs";
20127
20171
  import path41 from "path";
20128
20172
  import os34 from "os";
20129
20173
  var AUDIT_LOG = path41.join(os34.homedir(), ".node9", "audit.log");
@@ -20134,7 +20178,7 @@ function stripAnsi(s) {
20134
20178
  }
20135
20179
  function loadResolved() {
20136
20180
  try {
20137
- const raw = JSON.parse(fs37.readFileSync(RESOLVED_FILE, "utf-8"));
20181
+ const raw = JSON.parse(fs38.readFileSync(RESOLVED_FILE, "utf-8"));
20138
20182
  return new Set(raw);
20139
20183
  } catch {
20140
20184
  return /* @__PURE__ */ new Set();
@@ -20142,13 +20186,13 @@ function loadResolved() {
20142
20186
  }
20143
20187
  function saveResolved(resolved) {
20144
20188
  try {
20145
- fs37.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
20189
+ fs38.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
20146
20190
  } catch {
20147
20191
  }
20148
20192
  }
20149
20193
  function loadDlpFindings() {
20150
- if (!fs37.existsSync(AUDIT_LOG)) return [];
20151
- return fs37.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
20194
+ if (!fs38.existsSync(AUDIT_LOG)) return [];
20195
+ return fs38.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
20152
20196
  if (!line.trim()) return [];
20153
20197
  try {
20154
20198
  const e = JSON.parse(line);
@@ -20246,20 +20290,20 @@ function registerDlpCommand(program2) {
20246
20290
 
20247
20291
  // src/cli.ts
20248
20292
  var { version } = JSON.parse(
20249
- fs40.readFileSync(path44.join(__dirname, "../package.json"), "utf-8")
20293
+ fs41.readFileSync(path44.join(__dirname, "../package.json"), "utf-8")
20250
20294
  );
20251
20295
  var program = new Command();
20252
20296
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
20253
20297
  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) => {
20254
20298
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
20255
20299
  const credPath = path44.join(os37.homedir(), ".node9", "credentials.json");
20256
- if (!fs40.existsSync(path44.dirname(credPath)))
20257
- fs40.mkdirSync(path44.dirname(credPath), { recursive: true });
20300
+ if (!fs41.existsSync(path44.dirname(credPath)))
20301
+ fs41.mkdirSync(path44.dirname(credPath), { recursive: true });
20258
20302
  const profileName = options.profile || "default";
20259
20303
  let existingCreds = {};
20260
20304
  try {
20261
- if (fs40.existsSync(credPath)) {
20262
- const raw = JSON.parse(fs40.readFileSync(credPath, "utf-8"));
20305
+ if (fs41.existsSync(credPath)) {
20306
+ const raw = JSON.parse(fs41.readFileSync(credPath, "utf-8"));
20263
20307
  if (raw.apiKey) {
20264
20308
  existingCreds = {
20265
20309
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -20271,13 +20315,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
20271
20315
  } catch {
20272
20316
  }
20273
20317
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
20274
- fs40.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
20318
+ fs41.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
20275
20319
  if (profileName === "default") {
20276
20320
  const configPath = path44.join(os37.homedir(), ".node9", "config.json");
20277
20321
  let config = {};
20278
20322
  try {
20279
- if (fs40.existsSync(configPath))
20280
- config = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
20323
+ if (fs41.existsSync(configPath))
20324
+ config = JSON.parse(fs41.readFileSync(configPath, "utf-8"));
20281
20325
  } catch {
20282
20326
  }
20283
20327
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -20292,9 +20336,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
20292
20336
  approvers.cloud = false;
20293
20337
  }
20294
20338
  s.approvers = approvers;
20295
- if (!fs40.existsSync(path44.dirname(configPath)))
20296
- fs40.mkdirSync(path44.dirname(configPath), { recursive: true });
20297
- fs40.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
20339
+ if (!fs41.existsSync(path44.dirname(configPath)))
20340
+ fs41.mkdirSync(path44.dirname(configPath), { recursive: true });
20341
+ fs41.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
20298
20342
  }
20299
20343
  if (options.profile && profileName !== "default") {
20300
20344
  console.log(chalk26.green(`\u2705 Profile "${profileName}" saved`));
@@ -20432,14 +20476,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
20432
20476
  }
20433
20477
  if (options.purge) {
20434
20478
  const node9Dir = path44.join(os37.homedir(), ".node9");
20435
- if (fs40.existsSync(node9Dir)) {
20479
+ if (fs41.existsSync(node9Dir)) {
20436
20480
  const confirmed = await confirm2({
20437
20481
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
20438
20482
  default: false
20439
20483
  });
20440
20484
  if (confirmed) {
20441
- fs40.rmSync(node9Dir, { recursive: true });
20442
- if (fs40.existsSync(node9Dir)) {
20485
+ fs41.rmSync(node9Dir, { recursive: true });
20486
+ if (fs41.existsSync(node9Dir)) {
20443
20487
  console.error(
20444
20488
  chalk26.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
20445
20489
  );
@@ -20583,12 +20627,12 @@ Run "node9 addto claude" to register it as the statusLine.`
20583
20627
  if (subcommand === "debug") {
20584
20628
  const flagFile = path44.join(os37.homedir(), ".node9", "hud-debug");
20585
20629
  if (state === "on") {
20586
- fs40.mkdirSync(path44.dirname(flagFile), { recursive: true });
20587
- fs40.writeFileSync(flagFile, "");
20630
+ fs41.mkdirSync(path44.dirname(flagFile), { recursive: true });
20631
+ fs41.writeFileSync(flagFile, "");
20588
20632
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
20589
20633
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
20590
20634
  } else if (state === "off") {
20591
- if (fs40.existsSync(flagFile)) fs40.unlinkSync(flagFile);
20635
+ if (fs41.existsSync(flagFile)) fs41.unlinkSync(flagFile);
20592
20636
  console.log("HUD debug logging disabled.");
20593
20637
  } else {
20594
20638
  console.error("Usage: node9 hud debug on|off");
@@ -20702,7 +20746,7 @@ if (process.argv[2] !== "daemon") {
20702
20746
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
20703
20747
  const logPath = path44.join(os37.homedir(), ".node9", "hook-debug.log");
20704
20748
  const msg = reason instanceof Error ? reason.message : String(reason);
20705
- fs40.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
20749
+ fs41.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
20706
20750
  `);
20707
20751
  }
20708
20752
  process.exit(0);