@node9/proxy 1.12.0 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2344,7 +2344,12 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2344
2344
  pathTokens = analyzed.paths;
2345
2345
  const INLINE_EXEC_PATTERN = /^(python3?|bash|sh|zsh|perl|ruby|node|php|lua)\s+(-c|-e|-eval)\s/i;
2346
2346
  if (INLINE_EXEC_PATTERN.test(shellCommand.trim())) {
2347
- return { decision: "review", blockedByLabel: "Node9 Standard (Inline Execution)", tier: 3 };
2347
+ return {
2348
+ decision: "review",
2349
+ blockedByLabel: "Node9 Standard (Inline Execution)",
2350
+ ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute.",
2351
+ tier: 3
2352
+ };
2348
2353
  }
2349
2354
  const evalVerdict = detectDangerousShellExec(shellCommand);
2350
2355
  if (evalVerdict === "block") {
@@ -2352,6 +2357,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2352
2357
  decision: "block",
2353
2358
  blockedByLabel: "Node9: Eval Remote Execution",
2354
2359
  reason: "eval of remote download (curl/wget) is a near-certain supply-chain attack",
2360
+ ruleDescription: "The AI is downloading a script from the internet and running it immediately without inspection. This is a common way malware gets installed.",
2355
2361
  tier: 3
2356
2362
  };
2357
2363
  }
@@ -2360,6 +2366,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2360
2366
  decision: "review",
2361
2367
  blockedByLabel: "Node9: Eval Dynamic Content",
2362
2368
  reason: "eval of dynamic content (variable or subshell expansion) requires approval",
2369
+ 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.",
2363
2370
  tier: 3
2364
2371
  };
2365
2372
  }
@@ -2489,6 +2496,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2489
2496
  blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
2490
2497
  matchedWord: matchedDangerousWord,
2491
2498
  matchedField,
2499
+ ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`,
2492
2500
  tier: 6
2493
2501
  };
2494
2502
  }
@@ -2641,7 +2649,8 @@ async function explainPolicy(toolName, args) {
2641
2649
  waterfall,
2642
2650
  steps,
2643
2651
  decision: "review",
2644
- blockedByLabel: "Node9 Standard (Inline Execution)"
2652
+ blockedByLabel: "Node9 Standard (Inline Execution)",
2653
+ ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute."
2645
2654
  };
2646
2655
  }
2647
2656
  steps.push({
@@ -2747,7 +2756,8 @@ async function explainPolicy(toolName, args) {
2747
2756
  steps,
2748
2757
  decision: "review",
2749
2758
  blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
2750
- matchedToken: matchedDangerousWord
2759
+ matchedToken: matchedDangerousWord,
2760
+ ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`
2751
2761
  };
2752
2762
  }
2753
2763
  steps.push({
@@ -8306,20 +8316,32 @@ function openBrowserLocal() {
8306
8316
  async function autoStartDaemonAndWait(openBrowser2 = true) {
8307
8317
  if (isTestingMode()) return false;
8308
8318
  if (!import_path16.default.isAbsolute(process.argv[1])) return false;
8319
+ let resolvedArgv1;
8320
+ try {
8321
+ resolvedArgv1 = import_fs13.default.realpathSync(process.argv[1]);
8322
+ } catch {
8323
+ return false;
8324
+ }
8325
+ if (!resolvedArgv1.endsWith(".js")) return false;
8309
8326
  try {
8310
- const child = (0, import_child_process3.spawn)(process.execPath, [process.argv[1], "daemon"], {
8327
+ const child = (0, import_child_process3.spawn)(process.execPath, [resolvedArgv1, "daemon"], {
8311
8328
  detached: true,
8312
8329
  stdio: "ignore",
8313
8330
  // NODE9_BROWSER_OPENED=1 tells the daemon we will open the browser ourselves
8314
8331
  // (openBrowserLocal below), so it must not open a duplicate tab on first approval.
8315
- env: { ...process.env, NODE9_AUTO_STARTED: "1", NODE9_BROWSER_OPENED: "1" }
8332
+ // Only set NODE9_BROWSER_OPENED when we actually intend to open it here.
8333
+ env: {
8334
+ ...process.env,
8335
+ NODE9_AUTO_STARTED: "1",
8336
+ ...openBrowser2 && { NODE9_BROWSER_OPENED: "1" }
8337
+ }
8316
8338
  });
8317
8339
  child.unref();
8318
8340
  for (let i = 0; i < 20; i++) {
8319
8341
  await new Promise((r) => setTimeout(r, 250));
8320
8342
  if (!isDaemonRunning()) continue;
8321
8343
  try {
8322
- const res = await fetch("http://127.0.0.1:7391/settings", {
8344
+ const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/settings`, {
8323
8345
  signal: AbortSignal.timeout(500)
8324
8346
  });
8325
8347
  if (res.ok) {
@@ -8335,12 +8357,13 @@ async function autoStartDaemonAndWait(openBrowser2 = true) {
8335
8357
  }
8336
8358
  return false;
8337
8359
  }
8338
- var import_child_process3, import_path16;
8360
+ var import_child_process3, import_path16, import_fs13;
8339
8361
  var init_daemon_starter = __esm({
8340
8362
  "src/cli/daemon-starter.ts"() {
8341
8363
  "use strict";
8342
8364
  import_child_process3 = require("child_process");
8343
8365
  import_path16 = __toESM(require("path"));
8366
+ import_fs13 = __toESM(require("fs"));
8344
8367
  init_daemon();
8345
8368
  }
8346
8369
  });
@@ -8634,13 +8657,13 @@ function buildRuleSources() {
8634
8657
  function countScanFiles() {
8635
8658
  let total = 0;
8636
8659
  const claudeDir = import_path17.default.join(import_os12.default.homedir(), ".claude", "projects");
8637
- if (import_fs13.default.existsSync(claudeDir)) {
8660
+ if (import_fs14.default.existsSync(claudeDir)) {
8638
8661
  try {
8639
- for (const proj of import_fs13.default.readdirSync(claudeDir)) {
8662
+ for (const proj of import_fs14.default.readdirSync(claudeDir)) {
8640
8663
  const p = import_path17.default.join(claudeDir, proj);
8641
8664
  try {
8642
- if (!import_fs13.default.statSync(p).isDirectory()) continue;
8643
- total += import_fs13.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
8665
+ if (!import_fs14.default.statSync(p).isDirectory()) continue;
8666
+ total += import_fs14.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
8644
8667
  } catch {
8645
8668
  continue;
8646
8669
  }
@@ -8649,16 +8672,16 @@ function countScanFiles() {
8649
8672
  }
8650
8673
  }
8651
8674
  const geminiDir = import_path17.default.join(import_os12.default.homedir(), ".gemini", "tmp");
8652
- if (import_fs13.default.existsSync(geminiDir)) {
8675
+ if (import_fs14.default.existsSync(geminiDir)) {
8653
8676
  try {
8654
- for (const slug of import_fs13.default.readdirSync(geminiDir)) {
8677
+ for (const slug of import_fs14.default.readdirSync(geminiDir)) {
8655
8678
  const p = import_path17.default.join(geminiDir, slug);
8656
8679
  try {
8657
- if (!import_fs13.default.statSync(p).isDirectory()) continue;
8680
+ if (!import_fs14.default.statSync(p).isDirectory()) continue;
8658
8681
  const chatsDir = import_path17.default.join(p, "chats");
8659
- if (import_fs13.default.existsSync(chatsDir)) {
8682
+ if (import_fs14.default.existsSync(chatsDir)) {
8660
8683
  try {
8661
- total += import_fs13.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
8684
+ total += import_fs14.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
8662
8685
  } catch {
8663
8686
  }
8664
8687
  }
@@ -8670,21 +8693,21 @@ function countScanFiles() {
8670
8693
  }
8671
8694
  }
8672
8695
  const codexDir = import_path17.default.join(import_os12.default.homedir(), ".codex", "sessions");
8673
- if (import_fs13.default.existsSync(codexDir)) {
8696
+ if (import_fs14.default.existsSync(codexDir)) {
8674
8697
  try {
8675
- for (const year of import_fs13.default.readdirSync(codexDir)) {
8698
+ for (const year of import_fs14.default.readdirSync(codexDir)) {
8676
8699
  const yp = import_path17.default.join(codexDir, year);
8677
8700
  try {
8678
- if (!import_fs13.default.statSync(yp).isDirectory()) continue;
8679
- for (const month of import_fs13.default.readdirSync(yp)) {
8701
+ if (!import_fs14.default.statSync(yp).isDirectory()) continue;
8702
+ for (const month of import_fs14.default.readdirSync(yp)) {
8680
8703
  const mp = import_path17.default.join(yp, month);
8681
8704
  try {
8682
- if (!import_fs13.default.statSync(mp).isDirectory()) continue;
8683
- for (const day of import_fs13.default.readdirSync(mp)) {
8705
+ if (!import_fs14.default.statSync(mp).isDirectory()) continue;
8706
+ for (const day of import_fs14.default.readdirSync(mp)) {
8684
8707
  const dp = import_path17.default.join(mp, day);
8685
8708
  try {
8686
- if (!import_fs13.default.statSync(dp).isDirectory()) continue;
8687
- total += import_fs13.default.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
8709
+ if (!import_fs14.default.statSync(dp).isDirectory()) continue;
8710
+ total += import_fs14.default.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
8688
8711
  } catch {
8689
8712
  continue;
8690
8713
  }
@@ -8702,17 +8725,18 @@ function countScanFiles() {
8702
8725
  }
8703
8726
  return total;
8704
8727
  }
8705
- function renderProgressBar(done, total) {
8728
+ function renderProgressBar(done, total, lines) {
8706
8729
  const width = 28;
8707
8730
  const pct = total > 0 ? done / total : 0;
8708
8731
  const filled = Math.min(width, Math.round(pct * width));
8709
8732
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
8710
- const label = total > 0 ? `${done}/${total} files` : `${done} files`;
8733
+ const fileLabel = total > 0 ? `${done}/${total} files` : `${done} files`;
8734
+ const lineLabel = lines > 0 ? import_chalk2.default.dim(` ${lines.toLocaleString()} lines`) : "";
8711
8735
  process.stdout.write(
8712
- `\r ${import_chalk2.default.cyan("Scanning")} [${import_chalk2.default.cyan(bar)}] ${import_chalk2.default.dim(label)} `
8736
+ `\r ${import_chalk2.default.cyan("Scanning")} [${import_chalk2.default.cyan(bar)}] ${import_chalk2.default.dim(fileLabel)}${lineLabel} `
8713
8737
  );
8714
8738
  }
8715
- function scanClaudeHistory(startDate, onProgress) {
8739
+ function scanClaudeHistory(startDate, onProgress, onLine) {
8716
8740
  const projectsDir = import_path17.default.join(import_os12.default.homedir(), ".claude", "projects");
8717
8741
  const result = {
8718
8742
  filesScanned: 0,
@@ -8726,10 +8750,10 @@ function scanClaudeHistory(startDate, onProgress) {
8726
8750
  firstDate: null,
8727
8751
  lastDate: null
8728
8752
  };
8729
- if (!import_fs13.default.existsSync(projectsDir)) return result;
8753
+ if (!import_fs14.default.existsSync(projectsDir)) return result;
8730
8754
  let projDirs;
8731
8755
  try {
8732
- projDirs = import_fs13.default.readdirSync(projectsDir);
8756
+ projDirs = import_fs14.default.readdirSync(projectsDir);
8733
8757
  } catch {
8734
8758
  return result;
8735
8759
  }
@@ -8737,14 +8761,14 @@ function scanClaudeHistory(startDate, onProgress) {
8737
8761
  for (const proj of projDirs) {
8738
8762
  const projPath = import_path17.default.join(projectsDir, proj);
8739
8763
  try {
8740
- if (!import_fs13.default.statSync(projPath).isDirectory()) continue;
8764
+ if (!import_fs14.default.statSync(projPath).isDirectory()) continue;
8741
8765
  } catch {
8742
8766
  continue;
8743
8767
  }
8744
8768
  const projLabel = decodeURIComponent(proj).replace(import_os12.default.homedir(), "~").slice(0, 40);
8745
8769
  let files;
8746
8770
  try {
8747
- files = import_fs13.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
8771
+ files = import_fs14.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
8748
8772
  } catch {
8749
8773
  continue;
8750
8774
  }
@@ -8755,13 +8779,14 @@ function scanClaudeHistory(startDate, onProgress) {
8755
8779
  const sessionId = file.replace(/\.jsonl$/, "");
8756
8780
  let raw;
8757
8781
  try {
8758
- raw = import_fs13.default.readFileSync(import_path17.default.join(projPath, file), "utf-8");
8782
+ raw = import_fs14.default.readFileSync(import_path17.default.join(projPath, file), "utf-8");
8759
8783
  } catch {
8760
8784
  continue;
8761
8785
  }
8762
8786
  const sessionCalls = [];
8763
8787
  for (const line of raw.split("\n")) {
8764
8788
  if (!line.trim()) continue;
8789
+ onLine?.();
8765
8790
  let entry;
8766
8791
  try {
8767
8792
  entry = JSON.parse(line);
@@ -8907,7 +8932,7 @@ function scanClaudeHistory(startDate, onProgress) {
8907
8932
  }
8908
8933
  return result;
8909
8934
  }
8910
- function scanGeminiHistory(startDate, onProgress) {
8935
+ function scanGeminiHistory(startDate, onProgress, onLine) {
8911
8936
  const tmpDir = import_path17.default.join(import_os12.default.homedir(), ".gemini", "tmp");
8912
8937
  const result = {
8913
8938
  filesScanned: 0,
@@ -8921,10 +8946,10 @@ function scanGeminiHistory(startDate, onProgress) {
8921
8946
  firstDate: null,
8922
8947
  lastDate: null
8923
8948
  };
8924
- if (!import_fs13.default.existsSync(tmpDir)) return result;
8949
+ if (!import_fs14.default.existsSync(tmpDir)) return result;
8925
8950
  let slugDirs;
8926
8951
  try {
8927
- slugDirs = import_fs13.default.readdirSync(tmpDir);
8952
+ slugDirs = import_fs14.default.readdirSync(tmpDir);
8928
8953
  } catch {
8929
8954
  return result;
8930
8955
  }
@@ -8932,20 +8957,20 @@ function scanGeminiHistory(startDate, onProgress) {
8932
8957
  for (const slug of slugDirs) {
8933
8958
  const slugPath = import_path17.default.join(tmpDir, slug);
8934
8959
  try {
8935
- if (!import_fs13.default.statSync(slugPath).isDirectory()) continue;
8960
+ if (!import_fs14.default.statSync(slugPath).isDirectory()) continue;
8936
8961
  } catch {
8937
8962
  continue;
8938
8963
  }
8939
8964
  let projLabel = slug;
8940
8965
  try {
8941
- projLabel = import_fs13.default.readFileSync(import_path17.default.join(slugPath, ".project_root"), "utf-8").trim().replace(import_os12.default.homedir(), "~").slice(0, 40);
8966
+ projLabel = import_fs14.default.readFileSync(import_path17.default.join(slugPath, ".project_root"), "utf-8").trim().replace(import_os12.default.homedir(), "~").slice(0, 40);
8942
8967
  } catch {
8943
8968
  }
8944
8969
  const chatsDir = import_path17.default.join(slugPath, "chats");
8945
- if (!import_fs13.default.existsSync(chatsDir)) continue;
8970
+ if (!import_fs14.default.existsSync(chatsDir)) continue;
8946
8971
  let chatFiles;
8947
8972
  try {
8948
- chatFiles = import_fs13.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
8973
+ chatFiles = import_fs14.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
8949
8974
  } catch {
8950
8975
  continue;
8951
8976
  }
@@ -8955,7 +8980,7 @@ function scanGeminiHistory(startDate, onProgress) {
8955
8980
  const sessionId = chatFile.replace(/\.json$/, "");
8956
8981
  let raw;
8957
8982
  try {
8958
- raw = import_fs13.default.readFileSync(import_path17.default.join(chatsDir, chatFile), "utf-8");
8983
+ raw = import_fs14.default.readFileSync(import_path17.default.join(chatsDir, chatFile), "utf-8");
8959
8984
  } catch {
8960
8985
  continue;
8961
8986
  }
@@ -8968,6 +8993,7 @@ function scanGeminiHistory(startDate, onProgress) {
8968
8993
  }
8969
8994
  result.sessions++;
8970
8995
  for (const msg of session.messages ?? []) {
8996
+ onLine?.();
8971
8997
  if (msg.type === "user") {
8972
8998
  const content = msg.content;
8973
8999
  const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
@@ -9103,7 +9129,7 @@ function scanGeminiHistory(startDate, onProgress) {
9103
9129
  }
9104
9130
  return result;
9105
9131
  }
9106
- function scanCodexHistory(startDate, onProgress) {
9132
+ function scanCodexHistory(startDate, onProgress, onLine) {
9107
9133
  const sessionsBase = import_path17.default.join(import_os12.default.homedir(), ".codex", "sessions");
9108
9134
  const result = {
9109
9135
  filesScanned: 0,
@@ -9117,31 +9143,31 @@ function scanCodexHistory(startDate, onProgress) {
9117
9143
  firstDate: null,
9118
9144
  lastDate: null
9119
9145
  };
9120
- if (!import_fs13.default.existsSync(sessionsBase)) return result;
9146
+ if (!import_fs14.default.existsSync(sessionsBase)) return result;
9121
9147
  const jsonlFiles = [];
9122
9148
  try {
9123
- for (const year of import_fs13.default.readdirSync(sessionsBase)) {
9149
+ for (const year of import_fs14.default.readdirSync(sessionsBase)) {
9124
9150
  const yearPath = import_path17.default.join(sessionsBase, year);
9125
9151
  try {
9126
- if (!import_fs13.default.statSync(yearPath).isDirectory()) continue;
9152
+ if (!import_fs14.default.statSync(yearPath).isDirectory()) continue;
9127
9153
  } catch {
9128
9154
  continue;
9129
9155
  }
9130
- for (const month of import_fs13.default.readdirSync(yearPath)) {
9156
+ for (const month of import_fs14.default.readdirSync(yearPath)) {
9131
9157
  const monthPath = import_path17.default.join(yearPath, month);
9132
9158
  try {
9133
- if (!import_fs13.default.statSync(monthPath).isDirectory()) continue;
9159
+ if (!import_fs14.default.statSync(monthPath).isDirectory()) continue;
9134
9160
  } catch {
9135
9161
  continue;
9136
9162
  }
9137
- for (const day of import_fs13.default.readdirSync(monthPath)) {
9163
+ for (const day of import_fs14.default.readdirSync(monthPath)) {
9138
9164
  const dayPath = import_path17.default.join(monthPath, day);
9139
9165
  try {
9140
- if (!import_fs13.default.statSync(dayPath).isDirectory()) continue;
9166
+ if (!import_fs14.default.statSync(dayPath).isDirectory()) continue;
9141
9167
  } catch {
9142
9168
  continue;
9143
9169
  }
9144
- for (const file of import_fs13.default.readdirSync(dayPath)) {
9170
+ for (const file of import_fs14.default.readdirSync(dayPath)) {
9145
9171
  if (file.endsWith(".jsonl")) jsonlFiles.push(import_path17.default.join(dayPath, file));
9146
9172
  }
9147
9173
  }
@@ -9156,7 +9182,7 @@ function scanCodexHistory(startDate, onProgress) {
9156
9182
  onProgress?.(result.filesScanned);
9157
9183
  let lines;
9158
9184
  try {
9159
- lines = import_fs13.default.readFileSync(filePath, "utf-8").split("\n");
9185
+ lines = import_fs14.default.readFileSync(filePath, "utf-8").split("\n");
9160
9186
  } catch {
9161
9187
  continue;
9162
9188
  }
@@ -9170,6 +9196,7 @@ function scanCodexHistory(startDate, onProgress) {
9170
9196
  let lastTotalOutput = 0;
9171
9197
  for (const line of lines) {
9172
9198
  if (!line.trim()) continue;
9199
+ onLine?.();
9173
9200
  let entry;
9174
9201
  try {
9175
9202
  entry = JSON.parse(line);
@@ -9374,17 +9401,17 @@ function printRuleGroup(rule, topN, drillDown, previewWidth) {
9374
9401
  }
9375
9402
  }
9376
9403
  function registerScanCommand(program2) {
9377
- 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) => {
9404
+ 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) => {
9378
9405
  const drillDown = options.drillDown ?? false;
9379
9406
  const topN = drillDown ? Infinity : Math.max(1, parseInt(options.top, 10) || 5);
9380
9407
  const previewWidth = 70;
9381
9408
  const startDate = options.all ? null : (() => {
9382
9409
  const d = /* @__PURE__ */ new Date();
9383
- d.setDate(d.getDate() - (parseInt(options.days, 10) || 90));
9410
+ d.setDate(d.getDate() - (parseInt(options.days, 10) || 30));
9384
9411
  d.setHours(0, 0, 0, 0);
9385
9412
  return d;
9386
9413
  })();
9387
- const isInstalled = import_fs13.default.existsSync(import_path17.default.join(import_os12.default.homedir(), ".node9", "audit.log"));
9414
+ const isInstalled = import_fs14.default.existsSync(import_path17.default.join(import_os12.default.homedir(), ".node9", "audit.log"));
9388
9415
  console.log("");
9389
9416
  if (!isInstalled) {
9390
9417
  console.log(
@@ -9401,19 +9428,32 @@ function registerScanCommand(program2) {
9401
9428
  console.log("");
9402
9429
  const totalFiles = countScanFiles();
9403
9430
  let filesScanned = 0;
9431
+ let linesScanned = 0;
9432
+ let lastRender = 0;
9404
9433
  const onProgress = (done) => {
9405
9434
  filesScanned = done;
9406
- renderProgressBar(filesScanned, totalFiles);
9435
+ renderProgressBar(filesScanned, totalFiles, linesScanned);
9436
+ lastRender = Date.now();
9407
9437
  };
9408
- renderProgressBar(0, totalFiles);
9409
- const claudeScan = scanClaudeHistory(startDate, onProgress);
9438
+ const onLine = () => {
9439
+ linesScanned++;
9440
+ const now = Date.now();
9441
+ if (now - lastRender >= 80) {
9442
+ lastRender = now;
9443
+ renderProgressBar(filesScanned, totalFiles, linesScanned);
9444
+ }
9445
+ };
9446
+ renderProgressBar(0, totalFiles, 0);
9447
+ const claudeScan = scanClaudeHistory(startDate, onProgress, onLine);
9410
9448
  const geminiScan = scanGeminiHistory(
9411
9449
  startDate,
9412
- (done) => onProgress(claudeScan.filesScanned + done)
9450
+ (done) => onProgress(claudeScan.filesScanned + done),
9451
+ onLine
9413
9452
  );
9414
9453
  const codexScan = scanCodexHistory(
9415
9454
  startDate,
9416
- (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done)
9455
+ (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
9456
+ onLine
9417
9457
  );
9418
9458
  const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
9419
9459
  const summary = buildScanSummary([
@@ -9431,7 +9471,7 @@ function registerScanCommand(program2) {
9431
9471
  );
9432
9472
  return;
9433
9473
  }
9434
- const rangeLabel = options.all ? import_chalk2.default.dim("all time") : import_chalk2.default.dim(`last ${options.days ?? 90} days`);
9474
+ const rangeLabel = options.all ? import_chalk2.default.dim("all time") : import_chalk2.default.dim(`last ${options.days ?? 30} days`);
9435
9475
  const dateRange = scan.firstDate && scan.lastDate ? import_chalk2.default.dim(` ${fmtTs(scan.firstDate)} \u2013 ${fmtTs(scan.lastDate)}`) : "";
9436
9476
  const breakdownParts = [];
9437
9477
  if (claudeScan.sessions > 0)
@@ -9609,38 +9649,42 @@ function registerScanCommand(program2) {
9609
9649
  console.log(" " + import_chalk2.default.dim("\u2192 ") + import_chalk2.default.underline("https://node9.ai"));
9610
9650
  }
9611
9651
  console.log("");
9612
- if (!isTestingMode() && isDaemonRunning()) {
9613
- const internalToken = getInternalToken();
9614
- if (internalToken) {
9615
- try {
9616
- const summary2 = buildScanSummary([
9617
- { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
9618
- { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
9619
- { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
9620
- ]);
9621
- const payload = { status: "complete", summary: summary2 };
9622
- await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
9623
- method: "POST",
9624
- headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
9625
- body: JSON.stringify(payload),
9626
- signal: AbortSignal.timeout(3e3)
9627
- });
9628
- const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
9629
- console.log(" " + import_chalk2.default.cyan("\u{1F310} View in browser:") + " " + import_chalk2.default.underline(url));
9630
- console.log("");
9631
- openBrowserLocal();
9632
- } catch {
9652
+ const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
9653
+ if (!isTestingMode()) {
9654
+ if (isDaemonRunning()) {
9655
+ const internalToken = getInternalToken();
9656
+ if (internalToken) {
9657
+ try {
9658
+ const summary2 = buildScanSummary([
9659
+ { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
9660
+ { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
9661
+ { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
9662
+ ]);
9663
+ await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
9664
+ method: "POST",
9665
+ headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
9666
+ body: JSON.stringify({ status: "complete", summary: summary2 }),
9667
+ signal: AbortSignal.timeout(3e3)
9668
+ });
9669
+ openBrowserLocal();
9670
+ } catch {
9671
+ }
9633
9672
  }
9634
9673
  }
9674
+ console.log(" " + import_chalk2.default.cyan("\u{1F310} View in browser:") + " " + import_chalk2.default.underline(url));
9675
+ if (!isDaemonRunning()) {
9676
+ console.log(" " + import_chalk2.default.dim(" run node9 daemon --background to activate"));
9677
+ }
9678
+ console.log("");
9635
9679
  }
9636
9680
  });
9637
9681
  }
9638
- var import_chalk2, import_fs13, import_path17, import_os12, CLAUDE_PRICING, GEMINI_PRICING, LOOP_TOOLS, LOOP_THRESHOLD, DEFAULT_RULE_NAMES;
9682
+ var import_chalk2, import_fs14, import_path17, import_os12, CLAUDE_PRICING, GEMINI_PRICING, LOOP_TOOLS, LOOP_THRESHOLD, DEFAULT_RULE_NAMES;
9639
9683
  var init_scan = __esm({
9640
9684
  "src/cli/commands/scan.ts"() {
9641
9685
  "use strict";
9642
9686
  import_chalk2 = __toESM(require("chalk"));
9643
- import_fs13 = __toESM(require("fs"));
9687
+ import_fs14 = __toESM(require("fs"));
9644
9688
  import_path17 = __toESM(require("path"));
9645
9689
  import_os12 = __toESM(require("os"));
9646
9690
  init_shields();
@@ -9778,11 +9822,11 @@ var init_suggestion_tracker = __esm({
9778
9822
  });
9779
9823
 
9780
9824
  // src/daemon/taint-store.ts
9781
- var import_fs14, import_path18, DEFAULT_TTL_MS, TaintStore;
9825
+ var import_fs15, import_path18, DEFAULT_TTL_MS, TaintStore;
9782
9826
  var init_taint_store = __esm({
9783
9827
  "src/daemon/taint-store.ts"() {
9784
9828
  "use strict";
9785
- import_fs14 = __toESM(require("fs"));
9829
+ import_fs15 = __toESM(require("fs"));
9786
9830
  import_path18 = __toESM(require("path"));
9787
9831
  DEFAULT_TTL_MS = 60 * 60 * 1e3;
9788
9832
  TaintStore = class {
@@ -9848,7 +9892,7 @@ var init_taint_store = __esm({
9848
9892
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
9849
9893
  _resolve(filePath) {
9850
9894
  try {
9851
- return import_fs14.default.realpathSync.native(import_path18.default.resolve(filePath));
9895
+ return import_fs15.default.realpathSync.native(import_path18.default.resolve(filePath));
9852
9896
  } catch {
9853
9897
  return import_path18.default.resolve(filePath);
9854
9898
  }
@@ -9968,8 +10012,8 @@ var init_session_history = __esm({
9968
10012
  // src/daemon/state.ts
9969
10013
  function loadInsightCounts() {
9970
10014
  try {
9971
- if (!import_fs15.default.existsSync(INSIGHT_COUNTS_FILE)) return;
9972
- const data = JSON.parse(import_fs15.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
10015
+ if (!import_fs16.default.existsSync(INSIGHT_COUNTS_FILE)) return;
10016
+ const data = JSON.parse(import_fs16.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
9973
10017
  for (const [tool, count] of Object.entries(data)) {
9974
10018
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
9975
10019
  }
@@ -10013,22 +10057,22 @@ function setCachedScanResult(result) {
10013
10057
  }
10014
10058
  function atomicWriteSync2(filePath, data, options) {
10015
10059
  const dir = import_path19.default.dirname(filePath);
10016
- if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
10060
+ if (!import_fs16.default.existsSync(dir)) import_fs16.default.mkdirSync(dir, { recursive: true });
10017
10061
  const tmpPath = `${filePath}.${(0, import_crypto6.randomUUID)()}.tmp`;
10018
10062
  try {
10019
- import_fs15.default.writeFileSync(tmpPath, data, options);
10063
+ import_fs16.default.writeFileSync(tmpPath, data, options);
10020
10064
  } catch (err2) {
10021
10065
  try {
10022
- import_fs15.default.unlinkSync(tmpPath);
10066
+ import_fs16.default.unlinkSync(tmpPath);
10023
10067
  } catch {
10024
10068
  }
10025
10069
  throw err2;
10026
10070
  }
10027
10071
  try {
10028
- import_fs15.default.renameSync(tmpPath, filePath);
10072
+ import_fs16.default.renameSync(tmpPath, filePath);
10029
10073
  } catch (err2) {
10030
10074
  try {
10031
- import_fs15.default.unlinkSync(tmpPath);
10075
+ import_fs16.default.unlinkSync(tmpPath);
10032
10076
  } catch {
10033
10077
  }
10034
10078
  throw err2;
@@ -10053,15 +10097,15 @@ function appendAuditLog(data) {
10053
10097
  source: "daemon"
10054
10098
  };
10055
10099
  const dir = import_path19.default.dirname(AUDIT_LOG_FILE);
10056
- if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
10057
- import_fs15.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10100
+ if (!import_fs16.default.existsSync(dir)) import_fs16.default.mkdirSync(dir, { recursive: true });
10101
+ import_fs16.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10058
10102
  } catch {
10059
10103
  }
10060
10104
  }
10061
10105
  function getAuditHistory(limit = 20) {
10062
10106
  try {
10063
- if (!import_fs15.default.existsSync(AUDIT_LOG_FILE)) return [];
10064
- const lines = import_fs15.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
10107
+ if (!import_fs16.default.existsSync(AUDIT_LOG_FILE)) return [];
10108
+ const lines = import_fs16.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
10065
10109
  if (lines.length === 1 && lines[0] === "") return [];
10066
10110
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
10067
10111
  } catch {
@@ -10070,19 +10114,19 @@ function getAuditHistory(limit = 20) {
10070
10114
  }
10071
10115
  function getOrgName() {
10072
10116
  try {
10073
- if (import_fs15.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
10117
+ if (import_fs16.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
10074
10118
  } catch {
10075
10119
  }
10076
10120
  return null;
10077
10121
  }
10078
10122
  function hasStoredSlackKey() {
10079
- return import_fs15.default.existsSync(CREDENTIALS_FILE);
10123
+ return import_fs16.default.existsSync(CREDENTIALS_FILE);
10080
10124
  }
10081
10125
  function writeGlobalSetting(key, value) {
10082
10126
  let config = {};
10083
10127
  try {
10084
- if (import_fs15.default.existsSync(GLOBAL_CONFIG_FILE)) {
10085
- config = JSON.parse(import_fs15.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
10128
+ if (import_fs16.default.existsSync(GLOBAL_CONFIG_FILE)) {
10129
+ config = JSON.parse(import_fs16.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
10086
10130
  }
10087
10131
  } catch {
10088
10132
  }
@@ -10094,8 +10138,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
10094
10138
  try {
10095
10139
  let trust = { entries: [] };
10096
10140
  try {
10097
- if (import_fs15.default.existsSync(TRUST_FILE2))
10098
- trust = JSON.parse(import_fs15.default.readFileSync(TRUST_FILE2, "utf-8"));
10141
+ if (import_fs16.default.existsSync(TRUST_FILE2))
10142
+ trust = JSON.parse(import_fs16.default.readFileSync(TRUST_FILE2, "utf-8"));
10099
10143
  } catch {
10100
10144
  }
10101
10145
  trust.entries = trust.entries.filter(
@@ -10112,8 +10156,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
10112
10156
  }
10113
10157
  function readPersistentDecisions() {
10114
10158
  try {
10115
- if (import_fs15.default.existsSync(DECISIONS_FILE)) {
10116
- return JSON.parse(import_fs15.default.readFileSync(DECISIONS_FILE, "utf-8"));
10159
+ if (import_fs16.default.existsSync(DECISIONS_FILE)) {
10160
+ return JSON.parse(import_fs16.default.readFileSync(DECISIONS_FILE, "utf-8"));
10117
10161
  }
10118
10162
  } catch {
10119
10163
  }
@@ -10150,7 +10194,7 @@ function estimateToolCost(tool, args) {
10150
10194
  const filePath = a.file_path ?? a.path;
10151
10195
  if (filePath) {
10152
10196
  try {
10153
- const bytes = import_fs15.default.statSync(filePath).size;
10197
+ const bytes = import_fs16.default.statSync(filePath).size;
10154
10198
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
10155
10199
  } catch {
10156
10200
  }
@@ -10208,7 +10252,7 @@ function abandonPending() {
10208
10252
  });
10209
10253
  if (autoStarted) {
10210
10254
  try {
10211
- import_fs15.default.unlinkSync(DAEMON_PID_FILE);
10255
+ import_fs16.default.unlinkSync(DAEMON_PID_FILE);
10212
10256
  } catch {
10213
10257
  }
10214
10258
  setTimeout(() => {
@@ -10219,7 +10263,7 @@ function abandonPending() {
10219
10263
  }
10220
10264
  function startActivitySocket() {
10221
10265
  try {
10222
- import_fs15.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
10266
+ import_fs16.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
10223
10267
  } catch {
10224
10268
  }
10225
10269
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -10301,17 +10345,17 @@ function startActivitySocket() {
10301
10345
  unixServer.listen(ACTIVITY_SOCKET_PATH2);
10302
10346
  process.on("exit", () => {
10303
10347
  try {
10304
- import_fs15.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
10348
+ import_fs16.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
10305
10349
  } catch {
10306
10350
  }
10307
10351
  });
10308
10352
  }
10309
- var import_net2, import_fs15, import_path19, import_os13, import_child_process4, import_crypto6, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, suggestions, taintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, WRITE_TOOL_NAMES;
10353
+ var import_net2, import_fs16, import_path19, import_os13, import_child_process4, import_crypto6, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, suggestions, taintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, WRITE_TOOL_NAMES;
10310
10354
  var init_state2 = __esm({
10311
10355
  "src/daemon/state.ts"() {
10312
10356
  "use strict";
10313
10357
  import_net2 = __toESM(require("net"));
10314
- import_fs15 = __toESM(require("fs"));
10358
+ import_fs16 = __toESM(require("fs"));
10315
10359
  import_path19 = __toESM(require("path"));
10316
10360
  import_os13 = __toESM(require("os"));
10317
10361
  import_child_process4 = require("child_process");
@@ -10376,8 +10420,8 @@ var init_state2 = __esm({
10376
10420
  function patchConfig(configPath, patch) {
10377
10421
  let config = {};
10378
10422
  try {
10379
- if (import_fs16.default.existsSync(configPath)) {
10380
- config = JSON.parse(import_fs16.default.readFileSync(configPath, "utf8"));
10423
+ if (import_fs17.default.existsSync(configPath)) {
10424
+ config = JSON.parse(import_fs17.default.readFileSync(configPath, "utf8"));
10381
10425
  }
10382
10426
  } catch {
10383
10427
  throw new Error(`Cannot read config at ${configPath} \u2014 file may be corrupted`);
@@ -10397,32 +10441,32 @@ function patchConfig(configPath, patch) {
10397
10441
  }
10398
10442
  }
10399
10443
  const dir = import_path20.default.dirname(configPath);
10400
- import_fs16.default.mkdirSync(dir, { recursive: true });
10444
+ import_fs17.default.mkdirSync(dir, { recursive: true });
10401
10445
  const tmp = configPath + ".node9-tmp";
10402
10446
  try {
10403
- import_fs16.default.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
10447
+ import_fs17.default.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
10404
10448
  } catch (err2) {
10405
10449
  try {
10406
- import_fs16.default.unlinkSync(tmp);
10450
+ import_fs17.default.unlinkSync(tmp);
10407
10451
  } catch {
10408
10452
  }
10409
10453
  throw err2;
10410
10454
  }
10411
10455
  try {
10412
- import_fs16.default.renameSync(tmp, configPath);
10456
+ import_fs17.default.renameSync(tmp, configPath);
10413
10457
  } catch (err2) {
10414
10458
  try {
10415
- import_fs16.default.unlinkSync(tmp);
10459
+ import_fs17.default.unlinkSync(tmp);
10416
10460
  } catch {
10417
10461
  }
10418
10462
  throw err2;
10419
10463
  }
10420
10464
  }
10421
- var import_fs16, import_path20, import_os14, GLOBAL_CONFIG_PATH;
10465
+ var import_fs17, import_path20, import_os14, GLOBAL_CONFIG_PATH;
10422
10466
  var init_patch = __esm({
10423
10467
  "src/config/patch.ts"() {
10424
10468
  "use strict";
10425
- import_fs16 = __toESM(require("fs"));
10469
+ import_fs17 = __toESM(require("fs"));
10426
10470
  import_path20 = __toESM(require("path"));
10427
10471
  import_os14 = __toESM(require("os"));
10428
10472
  GLOBAL_CONFIG_PATH = import_path20.default.join(import_os14.default.homedir(), ".node9", "config.json");
@@ -10445,7 +10489,7 @@ function pricingFor(model) {
10445
10489
  function parseJSONLFile(filePath) {
10446
10490
  let content;
10447
10491
  try {
10448
- content = import_fs17.default.readFileSync(filePath, "utf8");
10492
+ content = import_fs18.default.readFileSync(filePath, "utf8");
10449
10493
  } catch {
10450
10494
  return /* @__PURE__ */ new Map();
10451
10495
  }
@@ -10498,24 +10542,24 @@ function parseJSONLFile(filePath) {
10498
10542
  }
10499
10543
  function collectEntries() {
10500
10544
  const projectsDir = import_path21.default.join(import_os15.default.homedir(), ".claude", "projects");
10501
- if (!import_fs17.default.existsSync(projectsDir)) return [];
10545
+ if (!import_fs18.default.existsSync(projectsDir)) return [];
10502
10546
  const combined = /* @__PURE__ */ new Map();
10503
10547
  let dirs;
10504
10548
  try {
10505
- dirs = import_fs17.default.readdirSync(projectsDir);
10549
+ dirs = import_fs18.default.readdirSync(projectsDir);
10506
10550
  } catch {
10507
10551
  return [];
10508
10552
  }
10509
10553
  for (const dir of dirs) {
10510
10554
  const dirPath = import_path21.default.join(projectsDir, dir);
10511
10555
  try {
10512
- if (!import_fs17.default.statSync(dirPath).isDirectory()) continue;
10556
+ if (!import_fs18.default.statSync(dirPath).isDirectory()) continue;
10513
10557
  } catch {
10514
10558
  continue;
10515
10559
  }
10516
10560
  let files;
10517
10561
  try {
10518
- files = import_fs17.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
10562
+ files = import_fs18.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
10519
10563
  } catch {
10520
10564
  continue;
10521
10565
  }
@@ -10556,11 +10600,11 @@ async function syncCost() {
10556
10600
  signal: AbortSignal.timeout(15e3)
10557
10601
  });
10558
10602
  if (!res.ok) {
10559
- import_fs17.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
10603
+ import_fs18.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
10560
10604
  `);
10561
10605
  }
10562
10606
  } catch (err2) {
10563
- import_fs17.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
10607
+ import_fs18.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
10564
10608
  `);
10565
10609
  }
10566
10610
  }
@@ -10573,11 +10617,11 @@ function startCostSync() {
10573
10617
  }, SYNC_INTERVAL_MS);
10574
10618
  timer.unref();
10575
10619
  }
10576
- var import_fs17, import_path21, import_os15, SYNC_INTERVAL_MS, PRICING;
10620
+ var import_fs18, import_path21, import_os15, SYNC_INTERVAL_MS, PRICING;
10577
10621
  var init_costSync = __esm({
10578
10622
  "src/costSync.ts"() {
10579
10623
  "use strict";
10580
- import_fs17 = __toESM(require("fs"));
10624
+ import_fs18 = __toESM(require("fs"));
10581
10625
  import_path21 = __toESM(require("path"));
10582
10626
  import_os15 = __toESM(require("os"));
10583
10627
  init_config();
@@ -10605,7 +10649,7 @@ function readCredentials() {
10605
10649
  }
10606
10650
  try {
10607
10651
  const credPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "credentials.json");
10608
- const creds = JSON.parse(import_fs18.default.readFileSync(credPath, "utf-8"));
10652
+ const creds = JSON.parse(import_fs19.default.readFileSync(credPath, "utf-8"));
10609
10653
  const profileName = process.env.NODE9_PROFILE ?? "default";
10610
10654
  const profile = creds[profileName];
10611
10655
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -10668,8 +10712,8 @@ async function syncOnce() {
10668
10712
  const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
10669
10713
  const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
10670
10714
  const dir = import_path22.default.dirname(rulesCacheFile());
10671
- if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
10672
- import_fs18.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10715
+ if (!import_fs19.default.existsSync(dir)) import_fs19.default.mkdirSync(dir, { recursive: true });
10716
+ import_fs19.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10673
10717
  } catch {
10674
10718
  }
10675
10719
  }
@@ -10682,8 +10726,8 @@ async function runCloudSync() {
10682
10726
  const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
10683
10727
  const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
10684
10728
  const dir = import_path22.default.dirname(rulesCacheFile());
10685
- if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
10686
- import_fs18.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10729
+ if (!import_fs19.default.existsSync(dir)) import_fs19.default.mkdirSync(dir, { recursive: true });
10730
+ import_fs19.default.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10687
10731
  return { ok: true, rules: rules.length, fetchedAt: cache.fetchedAt };
10688
10732
  } catch (err2) {
10689
10733
  return { ok: false, reason: err2 instanceof Error ? err2.message : String(err2) };
@@ -10691,7 +10735,7 @@ async function runCloudSync() {
10691
10735
  }
10692
10736
  function getCloudSyncStatus() {
10693
10737
  try {
10694
- const raw = JSON.parse(import_fs18.default.readFileSync(rulesCacheFile(), "utf-8"));
10738
+ const raw = JSON.parse(import_fs19.default.readFileSync(rulesCacheFile(), "utf-8"));
10695
10739
  if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
10696
10740
  return { cached: true, rules: raw.rules.length, fetchedAt: raw.fetchedAt };
10697
10741
  } catch {
@@ -10700,7 +10744,7 @@ function getCloudSyncStatus() {
10700
10744
  }
10701
10745
  function getCloudRules() {
10702
10746
  try {
10703
- const raw = JSON.parse(import_fs18.default.readFileSync(rulesCacheFile(), "utf-8"));
10747
+ const raw = JSON.parse(import_fs19.default.readFileSync(rulesCacheFile(), "utf-8"));
10704
10748
  return Array.isArray(raw.rules) ? raw.rules : null;
10705
10749
  } catch {
10706
10750
  return null;
@@ -10715,11 +10759,11 @@ function startCloudSync() {
10715
10759
  const recurring = setInterval(() => void syncOnce(), intervalMs);
10716
10760
  recurring.unref();
10717
10761
  }
10718
- var import_fs18, import_https, import_os16, import_path22, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS;
10762
+ var import_fs19, import_https, import_os16, import_path22, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS;
10719
10763
  var init_sync = __esm({
10720
10764
  "src/daemon/sync.ts"() {
10721
10765
  "use strict";
10722
- import_fs18 = __toESM(require("fs"));
10766
+ import_fs19 = __toESM(require("fs"));
10723
10767
  import_https = __toESM(require("https"));
10724
10768
  import_os16 = __toESM(require("os"));
10725
10769
  import_path22 = __toESM(require("path"));
@@ -10734,45 +10778,45 @@ var init_sync = __esm({
10734
10778
  // src/daemon/dlp-scanner.ts
10735
10779
  function loadIndex() {
10736
10780
  try {
10737
- return JSON.parse(import_fs19.default.readFileSync(INDEX_FILE, "utf-8"));
10781
+ return JSON.parse(import_fs20.default.readFileSync(INDEX_FILE, "utf-8"));
10738
10782
  } catch {
10739
10783
  return {};
10740
10784
  }
10741
10785
  }
10742
10786
  function saveIndex(index) {
10743
10787
  try {
10744
- import_fs19.default.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
10788
+ import_fs20.default.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
10745
10789
  } catch {
10746
10790
  }
10747
10791
  }
10748
10792
  function appendAuditEntry(entry) {
10749
10793
  try {
10750
- import_fs19.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10794
+ import_fs20.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10751
10795
  } catch {
10752
10796
  }
10753
10797
  }
10754
10798
  function runDlpScan() {
10755
- if (!import_fs19.default.existsSync(PROJECTS_DIR)) return;
10799
+ if (!import_fs20.default.existsSync(PROJECTS_DIR)) return;
10756
10800
  const index = loadIndex();
10757
10801
  let updated = false;
10758
10802
  let projDirs;
10759
10803
  try {
10760
- projDirs = import_fs19.default.readdirSync(PROJECTS_DIR);
10804
+ projDirs = import_fs20.default.readdirSync(PROJECTS_DIR);
10761
10805
  } catch {
10762
10806
  return;
10763
10807
  }
10764
10808
  for (const proj of projDirs) {
10765
10809
  const projPath = import_path23.default.join(PROJECTS_DIR, proj);
10766
10810
  try {
10767
- if (!import_fs19.default.lstatSync(projPath).isDirectory()) continue;
10768
- const real = import_fs19.default.realpathSync(projPath);
10811
+ if (!import_fs20.default.lstatSync(projPath).isDirectory()) continue;
10812
+ const real = import_fs20.default.realpathSync(projPath);
10769
10813
  if (!real.startsWith(PROJECTS_DIR + import_path23.default.sep) && real !== PROJECTS_DIR) continue;
10770
10814
  } catch {
10771
10815
  continue;
10772
10816
  }
10773
10817
  let files;
10774
10818
  try {
10775
- files = import_fs19.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10819
+ files = import_fs20.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10776
10820
  } catch {
10777
10821
  continue;
10778
10822
  }
@@ -10781,21 +10825,21 @@ function runDlpScan() {
10781
10825
  const lastOffset = index[filePath] ?? 0;
10782
10826
  let size;
10783
10827
  try {
10784
- size = import_fs19.default.statSync(filePath).size;
10828
+ size = import_fs20.default.statSync(filePath).size;
10785
10829
  } catch {
10786
10830
  continue;
10787
10831
  }
10788
10832
  if (size <= lastOffset) continue;
10789
10833
  let fd;
10790
10834
  try {
10791
- fd = import_fs19.default.openSync(filePath, "r");
10835
+ fd = import_fs20.default.openSync(filePath, "r");
10792
10836
  } catch {
10793
10837
  continue;
10794
10838
  }
10795
10839
  try {
10796
10840
  const chunkSize = size - lastOffset;
10797
10841
  const buf = Buffer.alloc(chunkSize);
10798
- import_fs19.default.readSync(fd, buf, 0, chunkSize, lastOffset);
10842
+ import_fs20.default.readSync(fd, buf, 0, chunkSize, lastOffset);
10799
10843
  const chunk = buf.toString("utf-8");
10800
10844
  for (const line of chunk.split("\n")) {
10801
10845
  if (!line.trim()) continue;
@@ -10840,7 +10884,7 @@ Run: node9 report --period 30d`
10840
10884
  updated = true;
10841
10885
  } finally {
10842
10886
  try {
10843
- import_fs19.default.closeSync(fd);
10887
+ import_fs20.default.closeSync(fd);
10844
10888
  } catch {
10845
10889
  }
10846
10890
  }
@@ -10866,11 +10910,11 @@ function startDlpScanner() {
10866
10910
  );
10867
10911
  timer.unref();
10868
10912
  }
10869
- var import_fs19, import_path23, import_os17, INDEX_FILE, PROJECTS_DIR;
10913
+ var import_fs20, import_path23, import_os17, INDEX_FILE, PROJECTS_DIR;
10870
10914
  var init_dlp_scanner = __esm({
10871
10915
  "src/daemon/dlp-scanner.ts"() {
10872
10916
  "use strict";
10873
- import_fs19 = __toESM(require("fs"));
10917
+ import_fs20 = __toESM(require("fs"));
10874
10918
  import_path23 = __toESM(require("path"));
10875
10919
  import_os17 = __toESM(require("os"));
10876
10920
  init_dlp();
@@ -10888,8 +10932,8 @@ function getMcpToolsFile() {
10888
10932
  function readMcpToolsConfig() {
10889
10933
  try {
10890
10934
  const file = getMcpToolsFile();
10891
- if (!import_fs20.default.existsSync(file)) return {};
10892
- const raw = import_fs20.default.readFileSync(file, "utf-8");
10935
+ if (!import_fs21.default.existsSync(file)) return {};
10936
+ const raw = import_fs21.default.readFileSync(file, "utf-8");
10893
10937
  return JSON.parse(raw);
10894
10938
  } catch {
10895
10939
  return {};
@@ -10899,10 +10943,10 @@ function writeMcpToolsConfig(config) {
10899
10943
  try {
10900
10944
  const file = getMcpToolsFile();
10901
10945
  const dir = import_path24.default.dirname(file);
10902
- if (!import_fs20.default.existsSync(dir)) import_fs20.default.mkdirSync(dir, { recursive: true });
10946
+ if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
10903
10947
  const tmpPath = `${file}.${import_os18.default.hostname()}.${process.pid}.tmp`;
10904
- import_fs20.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
10905
- import_fs20.default.renameSync(tmpPath, file);
10948
+ import_fs21.default.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
10949
+ import_fs21.default.renameSync(tmpPath, file);
10906
10950
  } catch (e) {
10907
10951
  console.error("Failed to write mcp-tools.json", e);
10908
10952
  }
@@ -10941,11 +10985,11 @@ function approveServer(serverKey, disabledTools) {
10941
10985
  writeMcpToolsConfig(config);
10942
10986
  }
10943
10987
  }
10944
- var import_fs20, import_path24, import_os18;
10988
+ var import_fs21, import_path24, import_os18;
10945
10989
  var init_mcp_tools = __esm({
10946
10990
  "src/daemon/mcp-tools.ts"() {
10947
10991
  "use strict";
10948
- import_fs20 = __toESM(require("fs"));
10992
+ import_fs21 = __toESM(require("fs"));
10949
10993
  import_path24 = __toESM(require("path"));
10950
10994
  import_os18 = __toESM(require("os"));
10951
10995
  }
@@ -10971,7 +11015,7 @@ function startDaemon() {
10971
11015
  idleTimer = setTimeout(() => {
10972
11016
  if (autoStarted) {
10973
11017
  try {
10974
- import_fs21.default.unlinkSync(DAEMON_PID_FILE);
11018
+ import_fs22.default.unlinkSync(DAEMON_PID_FILE);
10975
11019
  } catch {
10976
11020
  }
10977
11021
  }
@@ -11507,7 +11551,7 @@ data: ${JSON.stringify(item.data)}
11507
11551
  const periodParam = reqUrl.searchParams.get("period") || "7d";
11508
11552
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
11509
11553
  const logPath = import_path25.default.join(import_os19.default.homedir(), ".node9", "audit.log");
11510
- if (!import_fs21.default.existsSync(logPath)) {
11554
+ if (!import_fs22.default.existsSync(logPath)) {
11511
11555
  res.writeHead(200, { "Content-Type": "application/json" });
11512
11556
  return res.end(
11513
11557
  JSON.stringify({
@@ -11520,7 +11564,7 @@ data: ${JSON.stringify(item.data)}
11520
11564
  );
11521
11565
  }
11522
11566
  try {
11523
- const raw = import_fs21.default.readFileSync(logPath, "utf-8");
11567
+ const raw = import_fs22.default.readFileSync(logPath, "utf-8");
11524
11568
  const allEntries = raw.split("\n").flatMap((line) => {
11525
11569
  if (!line.trim()) return [];
11526
11570
  try {
@@ -11624,7 +11668,7 @@ data: ${JSON.stringify(item.data)}
11624
11668
  }
11625
11669
  try {
11626
11670
  const d = /* @__PURE__ */ new Date();
11627
- d.setDate(d.getDate() - 90);
11671
+ d.setDate(d.getDate() - 30);
11628
11672
  d.setHours(0, 0, 0, 0);
11629
11673
  const EMPTY_SCAN = {
11630
11674
  filesScanned: 0,
@@ -11815,7 +11859,7 @@ data: ${JSON.stringify(item.data)}
11815
11859
  if (!validToken(req)) return res.writeHead(403).end();
11816
11860
  try {
11817
11861
  const { serverKey, disabledTools } = JSON.parse(await readBody(req));
11818
- if (typeof serverKey !== "string" || !Array.isArray(disabledTools)) {
11862
+ if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(disabledTools)) {
11819
11863
  res.writeHead(400).end();
11820
11864
  return;
11821
11865
  }
@@ -11837,7 +11881,7 @@ data: ${JSON.stringify(item.data)}
11837
11881
  if (req.headers["x-node9-internal"] !== internalToken) return res.writeHead(403).end();
11838
11882
  try {
11839
11883
  const { serverKey, tools } = JSON.parse(await readBody(req));
11840
- if (typeof serverKey !== "string" || !Array.isArray(tools)) {
11884
+ if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(tools)) {
11841
11885
  res.writeHead(400).end();
11842
11886
  return;
11843
11887
  }
@@ -11943,14 +11987,14 @@ data: ${JSON.stringify(item.data)}
11943
11987
  server.on("error", (e) => {
11944
11988
  if (e.code === "EADDRINUSE") {
11945
11989
  try {
11946
- if (import_fs21.default.existsSync(DAEMON_PID_FILE)) {
11947
- const { pid } = JSON.parse(import_fs21.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
11990
+ if (import_fs22.default.existsSync(DAEMON_PID_FILE)) {
11991
+ const { pid } = JSON.parse(import_fs22.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
11948
11992
  process.kill(pid, 0);
11949
11993
  return process.exit(0);
11950
11994
  }
11951
11995
  } catch {
11952
11996
  try {
11953
- import_fs21.default.unlinkSync(DAEMON_PID_FILE);
11997
+ import_fs22.default.unlinkSync(DAEMON_PID_FILE);
11954
11998
  } catch {
11955
11999
  }
11956
12000
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -12009,12 +12053,12 @@ data: ${JSON.stringify(item.data)}
12009
12053
  }
12010
12054
  startActivitySocket();
12011
12055
  }
12012
- var import_http, import_fs21, import_path25, import_os19, import_crypto7, import_child_process5, import_chalk3;
12056
+ var import_http, import_fs22, import_path25, import_os19, import_crypto7, import_child_process5, import_chalk3;
12013
12057
  var init_server = __esm({
12014
12058
  "src/daemon/server.ts"() {
12015
12059
  "use strict";
12016
12060
  import_http = __toESM(require("http"));
12017
- import_fs21 = __toESM(require("fs"));
12061
+ import_fs22 = __toESM(require("fs"));
12018
12062
  import_path25 = __toESM(require("path"));
12019
12063
  import_os19 = __toESM(require("os"));
12020
12064
  import_crypto7 = require("crypto");
@@ -12040,8 +12084,8 @@ var init_server = __esm({
12040
12084
  function resolveNode9Binary() {
12041
12085
  try {
12042
12086
  const script = process.argv[1];
12043
- if (typeof script === "string" && import_path26.default.isAbsolute(script) && import_fs22.default.existsSync(script)) {
12044
- return import_fs22.default.realpathSync(script);
12087
+ if (typeof script === "string" && import_path26.default.isAbsolute(script) && import_fs23.default.existsSync(script)) {
12088
+ return import_fs23.default.realpathSync(script);
12045
12089
  }
12046
12090
  } catch {
12047
12091
  }
@@ -12099,8 +12143,8 @@ function launchdPlist(binaryPath) {
12099
12143
  }
12100
12144
  function installLaunchd(binaryPath) {
12101
12145
  const dir = import_path26.default.dirname(LAUNCHD_PLIST);
12102
- if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
12103
- import_fs22.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
12146
+ if (!import_fs23.default.existsSync(dir)) import_fs23.default.mkdirSync(dir, { recursive: true });
12147
+ import_fs23.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
12104
12148
  (0, import_child_process6.spawnSync)("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
12105
12149
  const r = (0, import_child_process6.spawnSync)("launchctl", ["load", "-w", LAUNCHD_PLIST], {
12106
12150
  encoding: "utf8",
@@ -12111,13 +12155,13 @@ function installLaunchd(binaryPath) {
12111
12155
  }
12112
12156
  }
12113
12157
  function uninstallLaunchd() {
12114
- if (import_fs22.default.existsSync(LAUNCHD_PLIST)) {
12158
+ if (import_fs23.default.existsSync(LAUNCHD_PLIST)) {
12115
12159
  (0, import_child_process6.spawnSync)("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
12116
- import_fs22.default.unlinkSync(LAUNCHD_PLIST);
12160
+ import_fs23.default.unlinkSync(LAUNCHD_PLIST);
12117
12161
  }
12118
12162
  }
12119
12163
  function isLaunchdInstalled() {
12120
- return import_fs22.default.existsSync(LAUNCHD_PLIST);
12164
+ return import_fs23.default.existsSync(LAUNCHD_PLIST);
12121
12165
  }
12122
12166
  function systemdUnit(binaryPath) {
12123
12167
  return `[Unit]
@@ -12137,10 +12181,10 @@ WantedBy=default.target
12137
12181
  `;
12138
12182
  }
12139
12183
  function installSystemd(binaryPath) {
12140
- if (!import_fs22.default.existsSync(SYSTEMD_UNIT_DIR)) {
12141
- import_fs22.default.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
12184
+ if (!import_fs23.default.existsSync(SYSTEMD_UNIT_DIR)) {
12185
+ import_fs23.default.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
12142
12186
  }
12143
- import_fs22.default.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
12187
+ import_fs23.default.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
12144
12188
  try {
12145
12189
  (0, import_child_process6.execFileSync)("loginctl", ["enable-linger", import_os20.default.userInfo().username], { timeout: 3e3 });
12146
12190
  } catch {
@@ -12162,23 +12206,23 @@ function installSystemd(binaryPath) {
12162
12206
  }
12163
12207
  }
12164
12208
  function uninstallSystemd() {
12165
- if (import_fs22.default.existsSync(SYSTEMD_UNIT)) {
12209
+ if (import_fs23.default.existsSync(SYSTEMD_UNIT)) {
12166
12210
  (0, import_child_process6.spawnSync)("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
12167
12211
  encoding: "utf8",
12168
12212
  timeout: 5e3
12169
12213
  });
12170
12214
  (0, import_child_process6.spawnSync)("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
12171
- import_fs22.default.unlinkSync(SYSTEMD_UNIT);
12215
+ import_fs23.default.unlinkSync(SYSTEMD_UNIT);
12172
12216
  }
12173
12217
  }
12174
12218
  function isSystemdInstalled() {
12175
- return import_fs22.default.existsSync(SYSTEMD_UNIT);
12219
+ return import_fs23.default.existsSync(SYSTEMD_UNIT);
12176
12220
  }
12177
12221
  function stopRunningDaemon() {
12178
12222
  const pidFile = import_path26.default.join(import_os20.default.homedir(), ".node9", "daemon.pid");
12179
- if (!import_fs22.default.existsSync(pidFile)) return;
12223
+ if (!import_fs23.default.existsSync(pidFile)) return;
12180
12224
  try {
12181
- const data = JSON.parse(import_fs22.default.readFileSync(pidFile, "utf-8"));
12225
+ const data = JSON.parse(import_fs23.default.readFileSync(pidFile, "utf-8"));
12182
12226
  const pid = data.pid;
12183
12227
  const MAX_PID2 = 4194304;
12184
12228
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -12198,7 +12242,7 @@ function stopRunningDaemon() {
12198
12242
  }
12199
12243
  }
12200
12244
  try {
12201
- import_fs22.default.unlinkSync(pidFile);
12245
+ import_fs23.default.unlinkSync(pidFile);
12202
12246
  } catch {
12203
12247
  }
12204
12248
  } catch {
@@ -12268,11 +12312,11 @@ function isDaemonServiceInstalled() {
12268
12312
  if (process.platform === "linux") return isSystemdInstalled();
12269
12313
  return false;
12270
12314
  }
12271
- var import_fs22, import_path26, import_os20, import_child_process6, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
12315
+ var import_fs23, import_path26, import_os20, import_child_process6, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
12272
12316
  var init_service = __esm({
12273
12317
  "src/daemon/service.ts"() {
12274
12318
  "use strict";
12275
- import_fs22 = __toESM(require("fs"));
12319
+ import_fs23 = __toESM(require("fs"));
12276
12320
  import_path26 = __toESM(require("path"));
12277
12321
  import_os20 = __toESM(require("os"));
12278
12322
  import_child_process6 = require("child_process");
@@ -12285,9 +12329,9 @@ var init_service = __esm({
12285
12329
 
12286
12330
  // src/daemon/index.ts
12287
12331
  function stopDaemon() {
12288
- if (!import_fs23.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk4.default.yellow("Not running."));
12332
+ if (!import_fs24.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk4.default.yellow("Not running."));
12289
12333
  try {
12290
- const data = JSON.parse(import_fs23.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
12334
+ const data = JSON.parse(import_fs24.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
12291
12335
  const pid = data.pid;
12292
12336
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
12293
12337
  console.log(import_chalk4.default.gray("Cleaned up invalid PID file."));
@@ -12299,7 +12343,7 @@ function stopDaemon() {
12299
12343
  console.log(import_chalk4.default.gray("Cleaned up stale PID file."));
12300
12344
  } finally {
12301
12345
  try {
12302
- import_fs23.default.unlinkSync(DAEMON_PID_FILE);
12346
+ import_fs24.default.unlinkSync(DAEMON_PID_FILE);
12303
12347
  } catch {
12304
12348
  }
12305
12349
  }
@@ -12308,9 +12352,9 @@ function daemonStatus() {
12308
12352
  const serviceInstalled = isDaemonServiceInstalled();
12309
12353
  const serviceLabel = serviceInstalled ? import_chalk4.default.green("installed (starts on login)") : import_chalk4.default.yellow("not installed \u2014 run: node9 daemon install");
12310
12354
  let processStatus;
12311
- if (import_fs23.default.existsSync(DAEMON_PID_FILE)) {
12355
+ if (import_fs24.default.existsSync(DAEMON_PID_FILE)) {
12312
12356
  try {
12313
- const data = JSON.parse(import_fs23.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
12357
+ const data = JSON.parse(import_fs24.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
12314
12358
  const pid = data.pid;
12315
12359
  const port = data.port;
12316
12360
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -12340,11 +12384,11 @@ function daemonStatus() {
12340
12384
  console.log(` Service : ${serviceLabel}
12341
12385
  `);
12342
12386
  }
12343
- var import_fs23, import_chalk4, import_child_process7, MAX_PID;
12387
+ var import_fs24, import_chalk4, import_child_process7, MAX_PID;
12344
12388
  var init_daemon2 = __esm({
12345
12389
  "src/daemon/index.ts"() {
12346
12390
  "use strict";
12347
- import_fs23 = __toESM(require("fs"));
12391
+ import_fs24 = __toESM(require("fs"));
12348
12392
  import_chalk4 = __toESM(require("chalk"));
12349
12393
  import_child_process7 = require("child_process");
12350
12394
  init_server();
@@ -12377,19 +12421,19 @@ function getModelContextLimit(model) {
12377
12421
  }
12378
12422
  function readSessionUsage() {
12379
12423
  const projectsDir = import_path42.default.join(import_os35.default.homedir(), ".claude", "projects");
12380
- if (!import_fs38.default.existsSync(projectsDir)) return null;
12424
+ if (!import_fs39.default.existsSync(projectsDir)) return null;
12381
12425
  let latestFile = null;
12382
12426
  let latestMtime = 0;
12383
12427
  try {
12384
- for (const dir of import_fs38.default.readdirSync(projectsDir)) {
12428
+ for (const dir of import_fs39.default.readdirSync(projectsDir)) {
12385
12429
  const dirPath = import_path42.default.join(projectsDir, dir);
12386
12430
  try {
12387
- if (!import_fs38.default.statSync(dirPath).isDirectory()) continue;
12388
- for (const file of import_fs38.default.readdirSync(dirPath)) {
12431
+ if (!import_fs39.default.statSync(dirPath).isDirectory()) continue;
12432
+ for (const file of import_fs39.default.readdirSync(dirPath)) {
12389
12433
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
12390
12434
  const filePath = import_path42.default.join(dirPath, file);
12391
12435
  try {
12392
- const mtime = import_fs38.default.statSync(filePath).mtimeMs;
12436
+ const mtime = import_fs39.default.statSync(filePath).mtimeMs;
12393
12437
  if (mtime > latestMtime) {
12394
12438
  latestMtime = mtime;
12395
12439
  latestFile = filePath;
@@ -12404,7 +12448,7 @@ function readSessionUsage() {
12404
12448
  }
12405
12449
  if (!latestFile) return null;
12406
12450
  try {
12407
- const lines = import_fs38.default.readFileSync(latestFile, "utf-8").split("\n");
12451
+ const lines = import_fs39.default.readFileSync(latestFile, "utf-8").split("\n");
12408
12452
  let lastModel = "";
12409
12453
  let lastInput = 0;
12410
12454
  let lastOutput = 0;
@@ -12493,9 +12537,9 @@ function renderPending(activity) {
12493
12537
  }
12494
12538
  async function ensureDaemon() {
12495
12539
  let pidPort = null;
12496
- if (import_fs38.default.existsSync(PID_FILE)) {
12540
+ if (import_fs39.default.existsSync(PID_FILE)) {
12497
12541
  try {
12498
- const { port } = JSON.parse(import_fs38.default.readFileSync(PID_FILE, "utf-8"));
12542
+ const { port } = JSON.parse(import_fs39.default.readFileSync(PID_FILE, "utf-8"));
12499
12543
  pidPort = port;
12500
12544
  } catch {
12501
12545
  console.error(import_chalk25.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -12650,7 +12694,7 @@ function buildRecoveryCardLines(req) {
12650
12694
  function readApproversFromDisk() {
12651
12695
  const configPath = import_path42.default.join(import_os35.default.homedir(), ".node9", "config.json");
12652
12696
  try {
12653
- const raw = JSON.parse(import_fs38.default.readFileSync(configPath, "utf-8"));
12697
+ const raw = JSON.parse(import_fs39.default.readFileSync(configPath, "utf-8"));
12654
12698
  const settings = raw.settings ?? {};
12655
12699
  return settings.approvers ?? {};
12656
12700
  } catch {
@@ -12668,13 +12712,13 @@ function approverStatusLine() {
12668
12712
  function toggleApprover(channel) {
12669
12713
  const configPath = import_path42.default.join(import_os35.default.homedir(), ".node9", "config.json");
12670
12714
  try {
12671
- const raw = JSON.parse(import_fs38.default.readFileSync(configPath, "utf-8"));
12715
+ const raw = JSON.parse(import_fs39.default.readFileSync(configPath, "utf-8"));
12672
12716
  const settings = raw.settings ?? {};
12673
12717
  const approvers = settings.approvers ?? {};
12674
12718
  approvers[channel] = approvers[channel] === false;
12675
12719
  settings.approvers = approvers;
12676
12720
  raw.settings = settings;
12677
- import_fs38.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
12721
+ import_fs39.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
12678
12722
  } catch (err2) {
12679
12723
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
12680
12724
  `);
@@ -12844,7 +12888,7 @@ async function startTail(options = {}) {
12844
12888
  }
12845
12889
  postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
12846
12890
  try {
12847
- import_fs38.default.appendFileSync(
12891
+ import_fs39.default.appendFileSync(
12848
12892
  import_path42.default.join(import_os35.default.homedir(), ".node9", "hook-debug.log"),
12849
12893
  `[tail] POST /decision failed: ${String(err2)}
12850
12894
  `
@@ -12928,7 +12972,7 @@ async function startTail(options = {}) {
12928
12972
  }
12929
12973
  const auditLog = import_path42.default.join(import_os35.default.homedir(), ".node9", "audit.log");
12930
12974
  try {
12931
- const unackedDlp = import_fs38.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
12975
+ const unackedDlp = import_fs39.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
12932
12976
  if (unackedDlp > 0) {
12933
12977
  console.log("");
12934
12978
  console.log(
@@ -13113,13 +13157,13 @@ async function startTail(options = {}) {
13113
13157
  process.exit(1);
13114
13158
  });
13115
13159
  }
13116
- var import_http2, import_chalk25, import_fs38, import_os35, import_path42, import_readline5, import_child_process16, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
13160
+ var import_http2, import_chalk25, import_fs39, import_os35, import_path42, import_readline5, import_child_process16, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
13117
13161
  var init_tail = __esm({
13118
13162
  "src/tui/tail.ts"() {
13119
13163
  "use strict";
13120
13164
  import_http2 = __toESM(require("http"));
13121
13165
  import_chalk25 = __toESM(require("chalk"));
13122
- import_fs38 = __toESM(require("fs"));
13166
+ import_fs39 = __toESM(require("fs"));
13123
13167
  import_os35 = __toESM(require("os"));
13124
13168
  import_path42 = __toESM(require("path"));
13125
13169
  import_readline5 = __toESM(require("readline"));
@@ -13249,9 +13293,9 @@ function formatTimeLeft(resetsAt) {
13249
13293
  return ` (${m}m left)`;
13250
13294
  }
13251
13295
  function safeReadJson(filePath) {
13252
- if (!import_fs39.default.existsSync(filePath)) return null;
13296
+ if (!import_fs40.default.existsSync(filePath)) return null;
13253
13297
  try {
13254
- return JSON.parse(import_fs39.default.readFileSync(filePath, "utf-8"));
13298
+ return JSON.parse(import_fs40.default.readFileSync(filePath, "utf-8"));
13255
13299
  } catch {
13256
13300
  return null;
13257
13301
  }
@@ -13272,10 +13316,10 @@ function countHooksInFile(filePath) {
13272
13316
  return Object.keys(cfg.hooks).length;
13273
13317
  }
13274
13318
  function countRulesInDir(rulesDir) {
13275
- if (!import_fs39.default.existsSync(rulesDir)) return 0;
13319
+ if (!import_fs40.default.existsSync(rulesDir)) return 0;
13276
13320
  let count = 0;
13277
13321
  try {
13278
- for (const entry of import_fs39.default.readdirSync(rulesDir, { withFileTypes: true })) {
13322
+ for (const entry of import_fs40.default.readdirSync(rulesDir, { withFileTypes: true })) {
13279
13323
  if (entry.isDirectory()) {
13280
13324
  count += countRulesInDir(import_path43.default.join(rulesDir, entry.name));
13281
13325
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -13301,7 +13345,7 @@ function countConfigs(cwd) {
13301
13345
  let hooksCount = 0;
13302
13346
  const userMcpServers = /* @__PURE__ */ new Set();
13303
13347
  const projectMcpServers = /* @__PURE__ */ new Set();
13304
- if (import_fs39.default.existsSync(import_path43.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
13348
+ if (import_fs40.default.existsSync(import_path43.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
13305
13349
  rulesCount += countRulesInDir(import_path43.default.join(claudeDir, "rules"));
13306
13350
  const userSettings = import_path43.default.join(claudeDir, "settings.json");
13307
13351
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
@@ -13312,18 +13356,18 @@ function countConfigs(cwd) {
13312
13356
  userMcpServers.delete(name);
13313
13357
  }
13314
13358
  if (cwd) {
13315
- if (import_fs39.default.existsSync(import_path43.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
13316
- if (import_fs39.default.existsSync(import_path43.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
13359
+ if (import_fs40.default.existsSync(import_path43.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
13360
+ if (import_fs40.default.existsSync(import_path43.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
13317
13361
  const projectClaudeDir = import_path43.default.join(cwd, ".claude");
13318
13362
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
13319
13363
  if (!overlapsUserScope) {
13320
- if (import_fs39.default.existsSync(import_path43.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
13364
+ if (import_fs40.default.existsSync(import_path43.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
13321
13365
  rulesCount += countRulesInDir(import_path43.default.join(projectClaudeDir, "rules"));
13322
13366
  const projSettings = import_path43.default.join(projectClaudeDir, "settings.json");
13323
13367
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
13324
13368
  hooksCount += countHooksInFile(projSettings);
13325
13369
  }
13326
- if (import_fs39.default.existsSync(import_path43.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
13370
+ if (import_fs40.default.existsSync(import_path43.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
13327
13371
  const localSettings = import_path43.default.join(projectClaudeDir, "settings.local.json");
13328
13372
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
13329
13373
  hooksCount += countHooksInFile(localSettings);
@@ -13361,11 +13405,11 @@ function readActiveShieldsHud() {
13361
13405
  }
13362
13406
  try {
13363
13407
  const shieldsPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "shields.json");
13364
- if (!import_fs39.default.existsSync(shieldsPath)) {
13408
+ if (!import_fs40.default.existsSync(shieldsPath)) {
13365
13409
  shieldsCache = { value: [], ts: now };
13366
13410
  return [];
13367
13411
  }
13368
- const parsed = JSON.parse(import_fs39.default.readFileSync(shieldsPath, "utf-8"));
13412
+ const parsed = JSON.parse(import_fs40.default.readFileSync(shieldsPath, "utf-8"));
13369
13413
  if (!Array.isArray(parsed.active)) {
13370
13414
  shieldsCache = { value: [], ts: now };
13371
13415
  return [];
@@ -13467,17 +13511,17 @@ function renderContextLine(stdin) {
13467
13511
  async function main() {
13468
13512
  try {
13469
13513
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
13470
- if (import_fs39.default.existsSync(import_path43.default.join(import_os36.default.homedir(), ".node9", "hud-debug"))) {
13514
+ if (import_fs40.default.existsSync(import_path43.default.join(import_os36.default.homedir(), ".node9", "hud-debug"))) {
13471
13515
  try {
13472
13516
  const logPath = import_path43.default.join(import_os36.default.homedir(), ".node9", "hud-debug.log");
13473
13517
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
13474
13518
  let size = 0;
13475
13519
  try {
13476
- size = import_fs39.default.statSync(logPath).size;
13520
+ size = import_fs40.default.statSync(logPath).size;
13477
13521
  } catch {
13478
13522
  }
13479
13523
  if (size < MAX_LOG_SIZE) {
13480
- import_fs39.default.appendFileSync(
13524
+ import_fs40.default.appendFileSync(
13481
13525
  logPath,
13482
13526
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
13483
13527
  );
@@ -13501,8 +13545,8 @@ async function main() {
13501
13545
  import_path43.default.join(cwd, "node9.config.json"),
13502
13546
  import_path43.default.join(import_os36.default.homedir(), ".node9", "config.json")
13503
13547
  ]) {
13504
- if (!import_fs39.default.existsSync(configPath)) continue;
13505
- const cfg = JSON.parse(import_fs39.default.readFileSync(configPath, "utf-8"));
13548
+ if (!import_fs40.default.existsSync(configPath)) continue;
13549
+ const cfg = JSON.parse(import_fs40.default.readFileSync(configPath, "utf-8"));
13506
13550
  const hud = cfg.settings?.hud;
13507
13551
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
13508
13552
  }
@@ -13520,11 +13564,11 @@ async function main() {
13520
13564
  renderOffline();
13521
13565
  }
13522
13566
  }
13523
- var import_fs39, import_path43, import_os36, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
13567
+ var import_fs40, import_path43, import_os36, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
13524
13568
  var init_hud = __esm({
13525
13569
  "src/cli/hud.ts"() {
13526
13570
  "use strict";
13527
- import_fs39 = __toESM(require("fs"));
13571
+ import_fs40 = __toESM(require("fs"));
13528
13572
  import_path43 = __toESM(require("path"));
13529
13573
  import_os36 = __toESM(require("os"));
13530
13574
  import_http3 = __toESM(require("http"));
@@ -14574,7 +14618,7 @@ function getAgentsStatus(homeDir2 = import_os11.default.homedir()) {
14574
14618
  // src/cli.ts
14575
14619
  init_daemon2();
14576
14620
  var import_chalk26 = __toESM(require("chalk"));
14577
- var import_fs40 = __toESM(require("fs"));
14621
+ var import_fs41 = __toESM(require("fs"));
14578
14622
  var import_path44 = __toESM(require("path"));
14579
14623
  var import_os37 = __toESM(require("os"));
14580
14624
  var import_prompts2 = require("@inquirer/prompts");
@@ -14761,7 +14805,7 @@ init_daemon_starter();
14761
14805
 
14762
14806
  // src/cli/commands/check.ts
14763
14807
  var import_chalk6 = __toESM(require("chalk"));
14764
- var import_fs26 = __toESM(require("fs"));
14808
+ var import_fs27 = __toESM(require("fs"));
14765
14809
  var import_child_process10 = require("child_process");
14766
14810
  var import_path29 = __toESM(require("path"));
14767
14811
  var import_os23 = __toESM(require("os"));
@@ -14773,7 +14817,7 @@ init_policy();
14773
14817
  // src/undo.ts
14774
14818
  var import_child_process9 = require("child_process");
14775
14819
  var import_crypto8 = __toESM(require("crypto"));
14776
- var import_fs24 = __toESM(require("fs"));
14820
+ var import_fs25 = __toESM(require("fs"));
14777
14821
  var import_net3 = __toESM(require("net"));
14778
14822
  var import_path27 = __toESM(require("path"));
14779
14823
  var import_os21 = __toESM(require("os"));
@@ -14803,16 +14847,16 @@ var MAX_SNAPSHOTS = 10;
14803
14847
  var GIT_TIMEOUT = 15e3;
14804
14848
  function readStack() {
14805
14849
  try {
14806
- if (import_fs24.default.existsSync(SNAPSHOT_STACK_PATH))
14807
- return JSON.parse(import_fs24.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
14850
+ if (import_fs25.default.existsSync(SNAPSHOT_STACK_PATH))
14851
+ return JSON.parse(import_fs25.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
14808
14852
  } catch {
14809
14853
  }
14810
14854
  return [];
14811
14855
  }
14812
14856
  function writeStack(stack) {
14813
14857
  const dir = import_path27.default.dirname(SNAPSHOT_STACK_PATH);
14814
- if (!import_fs24.default.existsSync(dir)) import_fs24.default.mkdirSync(dir, { recursive: true });
14815
- import_fs24.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
14858
+ if (!import_fs25.default.existsSync(dir)) import_fs25.default.mkdirSync(dir, { recursive: true });
14859
+ import_fs25.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
14816
14860
  }
14817
14861
  function extractFilePath(args) {
14818
14862
  if (!args || typeof args !== "object") return null;
@@ -14834,7 +14878,7 @@ function buildArgsSummary(tool, args) {
14834
14878
  function findProjectRoot(filePath) {
14835
14879
  let dir = import_path27.default.dirname(filePath);
14836
14880
  while (true) {
14837
- if (import_fs24.default.existsSync(import_path27.default.join(dir, ".git")) || import_fs24.default.existsSync(import_path27.default.join(dir, "package.json"))) {
14881
+ if (import_fs25.default.existsSync(import_path27.default.join(dir, ".git")) || import_fs25.default.existsSync(import_path27.default.join(dir, "package.json"))) {
14838
14882
  return dir;
14839
14883
  }
14840
14884
  const parent = import_path27.default.dirname(dir);
@@ -14845,7 +14889,7 @@ function findProjectRoot(filePath) {
14845
14889
  function normalizeCwdForHash(cwd) {
14846
14890
  let normalized;
14847
14891
  try {
14848
- normalized = import_fs24.default.realpathSync(cwd);
14892
+ normalized = import_fs25.default.realpathSync(cwd);
14849
14893
  } catch {
14850
14894
  normalized = cwd;
14851
14895
  }
@@ -14860,11 +14904,11 @@ function getShadowRepoDir(cwd) {
14860
14904
  function cleanOrphanedIndexFiles(shadowDir) {
14861
14905
  try {
14862
14906
  const cutoff = Date.now() - 6e4;
14863
- for (const f of import_fs24.default.readdirSync(shadowDir)) {
14907
+ for (const f of import_fs25.default.readdirSync(shadowDir)) {
14864
14908
  if (f.startsWith("index_")) {
14865
14909
  const fp = import_path27.default.join(shadowDir, f);
14866
14910
  try {
14867
- if (import_fs24.default.statSync(fp).mtimeMs < cutoff) import_fs24.default.unlinkSync(fp);
14911
+ if (import_fs25.default.statSync(fp).mtimeMs < cutoff) import_fs25.default.unlinkSync(fp);
14868
14912
  } catch {
14869
14913
  }
14870
14914
  }
@@ -14876,7 +14920,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
14876
14920
  const hardcoded = [".git", ".node9"];
14877
14921
  const lines = [...hardcoded, ...ignorePaths].join("\n");
14878
14922
  try {
14879
- import_fs24.default.writeFileSync(import_path27.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
14923
+ import_fs25.default.writeFileSync(import_path27.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
14880
14924
  } catch {
14881
14925
  }
14882
14926
  }
@@ -14891,23 +14935,23 @@ function ensureShadowRepo(shadowDir, cwd) {
14891
14935
  if (check.status === 0) {
14892
14936
  const ptPath = import_path27.default.join(shadowDir, "project-path.txt");
14893
14937
  try {
14894
- const stored = import_fs24.default.readFileSync(ptPath, "utf8").trim();
14938
+ const stored = import_fs25.default.readFileSync(ptPath, "utf8").trim();
14895
14939
  if (stored === normalizedCwd) return true;
14896
14940
  if (process.env.NODE9_DEBUG === "1")
14897
14941
  console.error(
14898
14942
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
14899
14943
  );
14900
- import_fs24.default.rmSync(shadowDir, { recursive: true, force: true });
14944
+ import_fs25.default.rmSync(shadowDir, { recursive: true, force: true });
14901
14945
  } catch {
14902
14946
  try {
14903
- import_fs24.default.writeFileSync(ptPath, normalizedCwd, "utf8");
14947
+ import_fs25.default.writeFileSync(ptPath, normalizedCwd, "utf8");
14904
14948
  } catch {
14905
14949
  }
14906
14950
  return true;
14907
14951
  }
14908
14952
  }
14909
14953
  try {
14910
- import_fs24.default.mkdirSync(shadowDir, { recursive: true });
14954
+ import_fs25.default.mkdirSync(shadowDir, { recursive: true });
14911
14955
  } catch {
14912
14956
  }
14913
14957
  const init = (0, import_child_process9.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -14924,7 +14968,7 @@ function ensureShadowRepo(shadowDir, cwd) {
14924
14968
  timeout: 3e3
14925
14969
  });
14926
14970
  try {
14927
- import_fs24.default.writeFileSync(import_path27.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
14971
+ import_fs25.default.writeFileSync(import_path27.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
14928
14972
  } catch {
14929
14973
  }
14930
14974
  return true;
@@ -15021,7 +15065,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15021
15065
  writeStack(stack);
15022
15066
  const entry = stack[stack.length - 1];
15023
15067
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
15024
- import_fs24.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
15068
+ import_fs25.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
15025
15069
  if (shouldGc) {
15026
15070
  (0, import_child_process9.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
15027
15071
  }
@@ -15032,7 +15076,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
15032
15076
  } finally {
15033
15077
  if (indexFile) {
15034
15078
  try {
15035
- import_fs24.default.unlinkSync(indexFile);
15079
+ import_fs25.default.unlinkSync(indexFile);
15036
15080
  } catch {
15037
15081
  }
15038
15082
  }
@@ -15109,8 +15153,8 @@ function applyUndo(hash, cwd) {
15109
15153
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
15110
15154
  for (const file of [...tracked, ...untracked]) {
15111
15155
  const fullPath = import_path27.default.join(dir, file);
15112
- if (!snapshotFiles.has(file) && import_fs24.default.existsSync(fullPath)) {
15113
- import_fs24.default.unlinkSync(fullPath);
15156
+ if (!snapshotFiles.has(file) && import_fs25.default.existsSync(fullPath)) {
15157
+ import_fs25.default.unlinkSync(fullPath);
15114
15158
  }
15115
15159
  }
15116
15160
  return true;
@@ -15123,7 +15167,7 @@ function applyUndo(hash, cwd) {
15123
15167
  init_daemon_starter();
15124
15168
 
15125
15169
  // src/skill-pin.ts
15126
- var import_fs25 = __toESM(require("fs"));
15170
+ var import_fs26 = __toESM(require("fs"));
15127
15171
  var import_path28 = __toESM(require("path"));
15128
15172
  var import_os22 = __toESM(require("os"));
15129
15173
  var import_crypto9 = __toESM(require("crypto"));
@@ -15142,7 +15186,7 @@ function walkDir(root) {
15142
15186
  if (out.length >= MAX_FILES) return;
15143
15187
  let entries;
15144
15188
  try {
15145
- entries = import_fs25.default.readdirSync(dir, { withFileTypes: true });
15189
+ entries = import_fs26.default.readdirSync(dir, { withFileTypes: true });
15146
15190
  } catch {
15147
15191
  return;
15148
15192
  }
@@ -15153,7 +15197,7 @@ function walkDir(root) {
15153
15197
  const rel = relDir ? import_path28.default.posix.join(relDir, entry.name) : entry.name;
15154
15198
  let lst;
15155
15199
  try {
15156
- lst = import_fs25.default.lstatSync(full);
15200
+ lst = import_fs26.default.lstatSync(full);
15157
15201
  } catch {
15158
15202
  continue;
15159
15203
  }
@@ -15165,7 +15209,7 @@ function walkDir(root) {
15165
15209
  if (!lst.isFile()) continue;
15166
15210
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
15167
15211
  try {
15168
- const buf = import_fs25.default.readFileSync(full);
15212
+ const buf = import_fs26.default.readFileSync(full);
15169
15213
  totalBytes += buf.length;
15170
15214
  out.push({ rel, hash: sha256Bytes(buf) });
15171
15215
  } catch {
@@ -15179,14 +15223,14 @@ function walkDir(root) {
15179
15223
  function hashSkillRoot(absPath) {
15180
15224
  let lst;
15181
15225
  try {
15182
- lst = import_fs25.default.lstatSync(absPath);
15226
+ lst = import_fs26.default.lstatSync(absPath);
15183
15227
  } catch {
15184
15228
  return { exists: false, contentHash: "", fileCount: 0 };
15185
15229
  }
15186
15230
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
15187
15231
  if (lst.isFile()) {
15188
15232
  try {
15189
- return { exists: true, contentHash: sha256Bytes(import_fs25.default.readFileSync(absPath)), fileCount: 1 };
15233
+ return { exists: true, contentHash: sha256Bytes(import_fs26.default.readFileSync(absPath)), fileCount: 1 };
15190
15234
  } catch {
15191
15235
  return { exists: false, contentHash: "", fileCount: 0 };
15192
15236
  }
@@ -15204,7 +15248,7 @@ function getRootKey(absPath) {
15204
15248
  function readSkillPinsSafe() {
15205
15249
  const filePath = getPinsFilePath();
15206
15250
  try {
15207
- const raw = import_fs25.default.readFileSync(filePath, "utf-8");
15251
+ const raw = import_fs26.default.readFileSync(filePath, "utf-8");
15208
15252
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
15209
15253
  const parsed = JSON.parse(raw);
15210
15254
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -15224,10 +15268,10 @@ function readSkillPins() {
15224
15268
  }
15225
15269
  function writeSkillPins(data) {
15226
15270
  const filePath = getPinsFilePath();
15227
- import_fs25.default.mkdirSync(import_path28.default.dirname(filePath), { recursive: true });
15271
+ import_fs26.default.mkdirSync(import_path28.default.dirname(filePath), { recursive: true });
15228
15272
  const tmp = `${filePath}.${import_crypto9.default.randomBytes(6).toString("hex")}.tmp`;
15229
- import_fs25.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15230
- import_fs25.default.renameSync(tmp, filePath);
15273
+ import_fs26.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15274
+ import_fs26.default.renameSync(tmp, filePath);
15231
15275
  }
15232
15276
  function removePin(rootKey) {
15233
15277
  const pins = readSkillPins();
@@ -15275,7 +15319,7 @@ function defaultSkillRoots(_cwd) {
15275
15319
  const roots = [];
15276
15320
  let registries;
15277
15321
  try {
15278
- registries = import_fs25.default.readdirSync(marketplaces, { withFileTypes: true });
15322
+ registries = import_fs26.default.readdirSync(marketplaces, { withFileTypes: true });
15279
15323
  } catch {
15280
15324
  return [];
15281
15325
  }
@@ -15284,7 +15328,7 @@ function defaultSkillRoots(_cwd) {
15284
15328
  const pluginsDir = import_path28.default.join(marketplaces, registry.name, "plugins");
15285
15329
  let plugins;
15286
15330
  try {
15287
- plugins = import_fs25.default.readdirSync(pluginsDir, { withFileTypes: true });
15331
+ plugins = import_fs26.default.readdirSync(pluginsDir, { withFileTypes: true });
15288
15332
  } catch {
15289
15333
  continue;
15290
15334
  }
@@ -15320,7 +15364,7 @@ function registerCheckCommand(program2) {
15320
15364
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
15321
15365
  const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
15322
15366
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15323
- import_fs26.default.appendFileSync(
15367
+ import_fs27.default.appendFileSync(
15324
15368
  logPath,
15325
15369
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
15326
15370
  RAW: ${raw}
@@ -15335,8 +15379,8 @@ RAW: ${raw}
15335
15379
  const scriptPath = process.argv[1];
15336
15380
  if (typeof scriptPath !== "string" || !import_path29.default.isAbsolute(scriptPath))
15337
15381
  throw new Error("node9: argv[1] is not an absolute path");
15338
- const resolvedScript = import_fs26.default.realpathSync(scriptPath);
15339
- const packageDist = import_fs26.default.realpathSync(import_path29.default.resolve(__dirname, "../.."));
15382
+ const resolvedScript = import_fs27.default.realpathSync(scriptPath);
15383
+ const packageDist = import_fs27.default.realpathSync(import_path29.default.resolve(__dirname, "../.."));
15340
15384
  if (!resolvedScript.startsWith(packageDist + import_path29.default.sep) && resolvedScript !== packageDist)
15341
15385
  throw new Error(
15342
15386
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
@@ -15362,7 +15406,7 @@ RAW: ${raw}
15362
15406
  const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
15363
15407
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
15364
15408
  try {
15365
- import_fs26.default.appendFileSync(
15409
+ import_fs27.default.appendFileSync(
15366
15410
  logPath,
15367
15411
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
15368
15412
  `
@@ -15373,9 +15417,9 @@ RAW: ${raw}
15373
15417
  }
15374
15418
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
15375
15419
  const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
15376
- if (!import_fs26.default.existsSync(import_path29.default.dirname(logPath)))
15377
- import_fs26.default.mkdirSync(import_path29.default.dirname(logPath), { recursive: true });
15378
- import_fs26.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15420
+ if (!import_fs27.default.existsSync(import_path29.default.dirname(logPath)))
15421
+ import_fs27.default.mkdirSync(import_path29.default.dirname(logPath), { recursive: true });
15422
+ import_fs27.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15379
15423
  `);
15380
15424
  }
15381
15425
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -15388,8 +15432,8 @@ RAW: ${raw}
15388
15432
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
15389
15433
  let ttyFd = null;
15390
15434
  try {
15391
- ttyFd = import_fs26.default.openSync("/dev/tty", "w");
15392
- const writeTty = (line) => import_fs26.default.writeSync(ttyFd, line + "\n");
15435
+ ttyFd = import_fs27.default.openSync("/dev/tty", "w");
15436
+ const writeTty = (line) => import_fs27.default.writeSync(ttyFd, line + "\n");
15393
15437
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
15394
15438
  writeTty(import_chalk6.default.bgRed.white.bold(`
15395
15439
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -15408,7 +15452,7 @@ RAW: ${raw}
15408
15452
  } finally {
15409
15453
  if (ttyFd !== null)
15410
15454
  try {
15411
- import_fs26.default.closeSync(ttyFd);
15455
+ import_fs27.default.closeSync(ttyFd);
15412
15456
  } catch {
15413
15457
  }
15414
15458
  }
@@ -15446,13 +15490,13 @@ RAW: ${raw}
15446
15490
  const flagPath = import_path29.default.join(sessionsDir, `${safeSessionId}.json`);
15447
15491
  let flag = null;
15448
15492
  try {
15449
- flag = JSON.parse(import_fs26.default.readFileSync(flagPath, "utf-8"));
15493
+ flag = JSON.parse(import_fs27.default.readFileSync(flagPath, "utf-8"));
15450
15494
  } catch {
15451
15495
  }
15452
15496
  const writeFlag = (data2) => {
15453
15497
  try {
15454
- import_fs26.default.mkdirSync(sessionsDir, { recursive: true });
15455
- import_fs26.default.writeFileSync(
15498
+ import_fs27.default.mkdirSync(sessionsDir, { recursive: true });
15499
+ import_fs27.default.writeFileSync(
15456
15500
  flagPath,
15457
15501
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
15458
15502
  { mode: 384 }
@@ -15463,8 +15507,8 @@ RAW: ${raw}
15463
15507
  const sendSkillWarn = (detail, recoveryCmd) => {
15464
15508
  let ttyFd = null;
15465
15509
  try {
15466
- ttyFd = import_fs26.default.openSync("/dev/tty", "w");
15467
- const w = (line) => import_fs26.default.writeSync(ttyFd, line + "\n");
15510
+ ttyFd = import_fs27.default.openSync("/dev/tty", "w");
15511
+ const w = (line) => import_fs27.default.writeSync(ttyFd, line + "\n");
15468
15512
  w(import_chalk6.default.yellow(`
15469
15513
  \u26A0\uFE0F Node9: installed skill drift detected`));
15470
15514
  w(import_chalk6.default.gray(` ${detail}`));
@@ -15479,7 +15523,7 @@ RAW: ${raw}
15479
15523
  } finally {
15480
15524
  if (ttyFd !== null)
15481
15525
  try {
15482
- import_fs26.default.closeSync(ttyFd);
15526
+ import_fs27.default.closeSync(ttyFd);
15483
15527
  } catch {
15484
15528
  }
15485
15529
  }
@@ -15536,10 +15580,10 @@ RAW: ${raw}
15536
15580
  }
15537
15581
  try {
15538
15582
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
15539
- for (const name of import_fs26.default.readdirSync(sessionsDir)) {
15583
+ for (const name of import_fs27.default.readdirSync(sessionsDir)) {
15540
15584
  const p = import_path29.default.join(sessionsDir, name);
15541
15585
  try {
15542
- if (import_fs26.default.statSync(p).mtimeMs < cutoff) import_fs26.default.unlinkSync(p);
15586
+ if (import_fs27.default.statSync(p).mtimeMs < cutoff) import_fs27.default.unlinkSync(p);
15543
15587
  } catch {
15544
15588
  }
15545
15589
  }
@@ -15551,7 +15595,7 @@ RAW: ${raw}
15551
15595
  try {
15552
15596
  const dbg = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
15553
15597
  const msg = err2 instanceof Error ? err2.message : String(err2);
15554
- import_fs26.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15598
+ import_fs27.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15555
15599
  `);
15556
15600
  } catch {
15557
15601
  }
@@ -15573,12 +15617,12 @@ RAW: ${raw}
15573
15617
  }
15574
15618
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
15575
15619
  try {
15576
- const tty = import_fs26.default.openSync("/dev/tty", "w");
15577
- import_fs26.default.writeSync(
15620
+ const tty = import_fs27.default.openSync("/dev/tty", "w");
15621
+ import_fs27.default.writeSync(
15578
15622
  tty,
15579
15623
  import_chalk6.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
15580
15624
  );
15581
- import_fs26.default.closeSync(tty);
15625
+ import_fs27.default.closeSync(tty);
15582
15626
  } catch {
15583
15627
  }
15584
15628
  const daemonReady = await autoStartDaemonAndWait();
@@ -15607,7 +15651,7 @@ RAW: ${raw}
15607
15651
  if (process.env.NODE9_DEBUG === "1") {
15608
15652
  const logPath = import_path29.default.join(import_os23.default.homedir(), ".node9", "hook-debug.log");
15609
15653
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15610
- import_fs26.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15654
+ import_fs27.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15611
15655
  `);
15612
15656
  }
15613
15657
  process.exit(0);
@@ -15641,7 +15685,7 @@ RAW: ${raw}
15641
15685
  }
15642
15686
 
15643
15687
  // src/cli/commands/log.ts
15644
- var import_fs27 = __toESM(require("fs"));
15688
+ var import_fs28 = __toESM(require("fs"));
15645
15689
  var import_path30 = __toESM(require("path"));
15646
15690
  var import_os24 = __toESM(require("os"));
15647
15691
  init_audit();
@@ -15719,9 +15763,9 @@ function registerLogCommand(program2) {
15719
15763
  source: "post-hook"
15720
15764
  };
15721
15765
  const logPath = import_path30.default.join(import_os24.default.homedir(), ".node9", "audit.log");
15722
- if (!import_fs27.default.existsSync(import_path30.default.dirname(logPath)))
15723
- import_fs27.default.mkdirSync(import_path30.default.dirname(logPath), { recursive: true });
15724
- import_fs27.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
15766
+ if (!import_fs28.default.existsSync(import_path30.default.dirname(logPath)))
15767
+ import_fs28.default.mkdirSync(import_path30.default.dirname(logPath), { recursive: true });
15768
+ import_fs28.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
15725
15769
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
15726
15770
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
15727
15771
  if (command) {
@@ -15777,7 +15821,7 @@ function registerLogCommand(program2) {
15777
15821
  `);
15778
15822
  const debugPath = import_path30.default.join(import_os24.default.homedir(), ".node9", "hook-debug.log");
15779
15823
  try {
15780
- import_fs27.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
15824
+ import_fs28.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
15781
15825
  `);
15782
15826
  } catch {
15783
15827
  }
@@ -16177,7 +16221,7 @@ function registerConfigShowCommand(program2) {
16177
16221
 
16178
16222
  // src/cli/commands/doctor.ts
16179
16223
  var import_chalk8 = __toESM(require("chalk"));
16180
- var import_fs28 = __toESM(require("fs"));
16224
+ var import_fs29 = __toESM(require("fs"));
16181
16225
  var import_path31 = __toESM(require("path"));
16182
16226
  var import_os25 = __toESM(require("os"));
16183
16227
  var import_child_process11 = require("child_process");
@@ -16234,9 +16278,9 @@ function registerDoctorCommand(program2, version2) {
16234
16278
  }
16235
16279
  section("Configuration");
16236
16280
  const globalConfigPath = import_path31.default.join(homeDir2, ".node9", "config.json");
16237
- if (import_fs28.default.existsSync(globalConfigPath)) {
16281
+ if (import_fs29.default.existsSync(globalConfigPath)) {
16238
16282
  try {
16239
- JSON.parse(import_fs28.default.readFileSync(globalConfigPath, "utf-8"));
16283
+ JSON.parse(import_fs29.default.readFileSync(globalConfigPath, "utf-8"));
16240
16284
  pass("~/.node9/config.json found and valid");
16241
16285
  } catch {
16242
16286
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -16245,9 +16289,9 @@ function registerDoctorCommand(program2, version2) {
16245
16289
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
16246
16290
  }
16247
16291
  const projectConfigPath = import_path31.default.join(process.cwd(), "node9.config.json");
16248
- if (import_fs28.default.existsSync(projectConfigPath)) {
16292
+ if (import_fs29.default.existsSync(projectConfigPath)) {
16249
16293
  try {
16250
- JSON.parse(import_fs28.default.readFileSync(projectConfigPath, "utf-8"));
16294
+ JSON.parse(import_fs29.default.readFileSync(projectConfigPath, "utf-8"));
16251
16295
  pass("node9.config.json found and valid (project)");
16252
16296
  } catch {
16253
16297
  fail(
@@ -16257,7 +16301,7 @@ function registerDoctorCommand(program2, version2) {
16257
16301
  }
16258
16302
  }
16259
16303
  const credsPath = import_path31.default.join(homeDir2, ".node9", "credentials.json");
16260
- if (import_fs28.default.existsSync(credsPath)) {
16304
+ if (import_fs29.default.existsSync(credsPath)) {
16261
16305
  pass("Cloud credentials found (~/.node9/credentials.json)");
16262
16306
  } else {
16263
16307
  warn(
@@ -16267,9 +16311,9 @@ function registerDoctorCommand(program2, version2) {
16267
16311
  }
16268
16312
  section("Agent Hooks");
16269
16313
  const claudeSettingsPath = import_path31.default.join(homeDir2, ".claude", "settings.json");
16270
- if (import_fs28.default.existsSync(claudeSettingsPath)) {
16314
+ if (import_fs29.default.existsSync(claudeSettingsPath)) {
16271
16315
  try {
16272
- const cs = JSON.parse(import_fs28.default.readFileSync(claudeSettingsPath, "utf-8"));
16316
+ const cs = JSON.parse(import_fs29.default.readFileSync(claudeSettingsPath, "utf-8"));
16273
16317
  const hasHook = cs.hooks?.PreToolUse?.some(
16274
16318
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16275
16319
  );
@@ -16286,9 +16330,9 @@ function registerDoctorCommand(program2, version2) {
16286
16330
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
16287
16331
  }
16288
16332
  const geminiSettingsPath = import_path31.default.join(homeDir2, ".gemini", "settings.json");
16289
- if (import_fs28.default.existsSync(geminiSettingsPath)) {
16333
+ if (import_fs29.default.existsSync(geminiSettingsPath)) {
16290
16334
  try {
16291
- const gs = JSON.parse(import_fs28.default.readFileSync(geminiSettingsPath, "utf-8"));
16335
+ const gs = JSON.parse(import_fs29.default.readFileSync(geminiSettingsPath, "utf-8"));
16292
16336
  const hasHook = gs.hooks?.BeforeTool?.some(
16293
16337
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16294
16338
  );
@@ -16305,9 +16349,9 @@ function registerDoctorCommand(program2, version2) {
16305
16349
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
16306
16350
  }
16307
16351
  const cursorHooksPath = import_path31.default.join(homeDir2, ".cursor", "hooks.json");
16308
- if (import_fs28.default.existsSync(cursorHooksPath)) {
16352
+ if (import_fs29.default.existsSync(cursorHooksPath)) {
16309
16353
  try {
16310
- const cur = JSON.parse(import_fs28.default.readFileSync(cursorHooksPath, "utf-8"));
16354
+ const cur = JSON.parse(import_fs29.default.readFileSync(cursorHooksPath, "utf-8"));
16311
16355
  const hasHook = cur.hooks?.preToolUse?.some(
16312
16356
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
16313
16357
  );
@@ -16345,7 +16389,7 @@ function registerDoctorCommand(program2, version2) {
16345
16389
 
16346
16390
  // src/cli/commands/audit.ts
16347
16391
  var import_chalk9 = __toESM(require("chalk"));
16348
- var import_fs29 = __toESM(require("fs"));
16392
+ var import_fs30 = __toESM(require("fs"));
16349
16393
  var import_path32 = __toESM(require("path"));
16350
16394
  var import_os26 = __toESM(require("os"));
16351
16395
  function formatRelativeTime(timestamp) {
@@ -16361,13 +16405,13 @@ function formatRelativeTime(timestamp) {
16361
16405
  function registerAuditCommand(program2) {
16362
16406
  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) => {
16363
16407
  const logPath = import_path32.default.join(import_os26.default.homedir(), ".node9", "audit.log");
16364
- if (!import_fs29.default.existsSync(logPath)) {
16408
+ if (!import_fs30.default.existsSync(logPath)) {
16365
16409
  console.log(
16366
16410
  import_chalk9.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
16367
16411
  );
16368
16412
  return;
16369
16413
  }
16370
- const raw = import_fs29.default.readFileSync(logPath, "utf-8");
16414
+ const raw = import_fs30.default.readFileSync(logPath, "utf-8");
16371
16415
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
16372
16416
  let entries = lines.flatMap((line) => {
16373
16417
  try {
@@ -16421,7 +16465,7 @@ function registerAuditCommand(program2) {
16421
16465
 
16422
16466
  // src/cli/commands/report.ts
16423
16467
  var import_chalk10 = __toESM(require("chalk"));
16424
- var import_fs30 = __toESM(require("fs"));
16468
+ var import_fs31 = __toESM(require("fs"));
16425
16469
  var import_path33 = __toESM(require("path"));
16426
16470
  var import_os27 = __toESM(require("os"));
16427
16471
  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;
@@ -16470,8 +16514,8 @@ function getDateRange(period) {
16470
16514
  }
16471
16515
  }
16472
16516
  function parseAuditLog(logPath) {
16473
- if (!import_fs30.default.existsSync(logPath)) return [];
16474
- const raw = import_fs30.default.readFileSync(logPath, "utf-8");
16517
+ if (!import_fs31.default.existsSync(logPath)) return [];
16518
+ const raw = import_fs31.default.readFileSync(logPath, "utf-8");
16475
16519
  return raw.split("\n").flatMap((line) => {
16476
16520
  if (!line.trim()) return [];
16477
16521
  try {
@@ -16551,10 +16595,10 @@ function loadClaudeCost(start, end) {
16551
16595
  cacheReadTokens: 0
16552
16596
  };
16553
16597
  const projectsDir = import_path33.default.join(import_os27.default.homedir(), ".claude", "projects");
16554
- if (!import_fs30.default.existsSync(projectsDir)) return empty;
16598
+ if (!import_fs31.default.existsSync(projectsDir)) return empty;
16555
16599
  let dirs;
16556
16600
  try {
16557
- dirs = import_fs30.default.readdirSync(projectsDir);
16601
+ dirs = import_fs31.default.readdirSync(projectsDir);
16558
16602
  } catch {
16559
16603
  return empty;
16560
16604
  }
@@ -16569,15 +16613,15 @@ function loadClaudeCost(start, end) {
16569
16613
  const projPath = import_path33.default.join(projectsDir, proj);
16570
16614
  let files;
16571
16615
  try {
16572
- const stat = import_fs30.default.statSync(projPath);
16616
+ const stat = import_fs31.default.statSync(projPath);
16573
16617
  if (!stat.isDirectory()) continue;
16574
- files = import_fs30.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16618
+ files = import_fs31.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16575
16619
  } catch {
16576
16620
  continue;
16577
16621
  }
16578
16622
  for (const file of files) {
16579
16623
  try {
16580
- const raw = import_fs30.default.readFileSync(import_path33.default.join(projPath, file), "utf-8");
16624
+ const raw = import_fs31.default.readFileSync(import_path33.default.join(projPath, file), "utf-8");
16581
16625
  for (const line of raw.split("\n")) {
16582
16626
  if (!line.trim()) continue;
16583
16627
  let entry;
@@ -16622,31 +16666,31 @@ function loadCodexCost(start, end) {
16622
16666
  const byDay = /* @__PURE__ */ new Map();
16623
16667
  let total = 0;
16624
16668
  let toolCalls = 0;
16625
- if (!import_fs30.default.existsSync(sessionsBase)) return { total, byDay, toolCalls };
16669
+ if (!import_fs31.default.existsSync(sessionsBase)) return { total, byDay, toolCalls };
16626
16670
  const jsonlFiles = [];
16627
16671
  try {
16628
- for (const year of import_fs30.default.readdirSync(sessionsBase)) {
16672
+ for (const year of import_fs31.default.readdirSync(sessionsBase)) {
16629
16673
  const yearPath = import_path33.default.join(sessionsBase, year);
16630
16674
  try {
16631
- if (!import_fs30.default.statSync(yearPath).isDirectory()) continue;
16675
+ if (!import_fs31.default.statSync(yearPath).isDirectory()) continue;
16632
16676
  } catch {
16633
16677
  continue;
16634
16678
  }
16635
- for (const month of import_fs30.default.readdirSync(yearPath)) {
16679
+ for (const month of import_fs31.default.readdirSync(yearPath)) {
16636
16680
  const monthPath = import_path33.default.join(yearPath, month);
16637
16681
  try {
16638
- if (!import_fs30.default.statSync(monthPath).isDirectory()) continue;
16682
+ if (!import_fs31.default.statSync(monthPath).isDirectory()) continue;
16639
16683
  } catch {
16640
16684
  continue;
16641
16685
  }
16642
- for (const day of import_fs30.default.readdirSync(monthPath)) {
16686
+ for (const day of import_fs31.default.readdirSync(monthPath)) {
16643
16687
  const dayPath = import_path33.default.join(monthPath, day);
16644
16688
  try {
16645
- if (!import_fs30.default.statSync(dayPath).isDirectory()) continue;
16689
+ if (!import_fs31.default.statSync(dayPath).isDirectory()) continue;
16646
16690
  } catch {
16647
16691
  continue;
16648
16692
  }
16649
- for (const file of import_fs30.default.readdirSync(dayPath)) {
16693
+ for (const file of import_fs31.default.readdirSync(dayPath)) {
16650
16694
  if (file.endsWith(".jsonl")) jsonlFiles.push(import_path33.default.join(dayPath, file));
16651
16695
  }
16652
16696
  }
@@ -16658,7 +16702,7 @@ function loadCodexCost(start, end) {
16658
16702
  for (const filePath of jsonlFiles) {
16659
16703
  let lines;
16660
16704
  try {
16661
- lines = import_fs30.default.readFileSync(filePath, "utf-8").split("\n");
16705
+ lines = import_fs31.default.readFileSync(filePath, "utf-8").split("\n");
16662
16706
  } catch {
16663
16707
  continue;
16664
16708
  }
@@ -17213,14 +17257,14 @@ function registerDaemonCommand(program2) {
17213
17257
 
17214
17258
  // src/cli/commands/status.ts
17215
17259
  var import_chalk12 = __toESM(require("chalk"));
17216
- var import_fs31 = __toESM(require("fs"));
17260
+ var import_fs32 = __toESM(require("fs"));
17217
17261
  var import_path34 = __toESM(require("path"));
17218
17262
  var import_os28 = __toESM(require("os"));
17219
17263
  init_core();
17220
17264
  init_daemon();
17221
17265
  function readJson2(filePath) {
17222
17266
  try {
17223
- if (import_fs31.default.existsSync(filePath)) return JSON.parse(import_fs31.default.readFileSync(filePath, "utf-8"));
17267
+ if (import_fs32.default.existsSync(filePath)) return JSON.parse(import_fs32.default.readFileSync(filePath, "utf-8"));
17224
17268
  } catch {
17225
17269
  }
17226
17270
  return null;
@@ -17288,10 +17332,10 @@ function registerStatusCommand(program2) {
17288
17332
  const projectConfig = import_path34.default.join(process.cwd(), "node9.config.json");
17289
17333
  const globalConfig = import_path34.default.join(import_os28.default.homedir(), ".node9", "config.json");
17290
17334
  console.log(
17291
- ` Local: ${import_fs31.default.existsSync(projectConfig) ? import_chalk12.default.green("Active (node9.config.json)") : import_chalk12.default.gray("Not present")}`
17335
+ ` Local: ${import_fs32.default.existsSync(projectConfig) ? import_chalk12.default.green("Active (node9.config.json)") : import_chalk12.default.gray("Not present")}`
17292
17336
  );
17293
17337
  console.log(
17294
- ` Global: ${import_fs31.default.existsSync(globalConfig) ? import_chalk12.default.green("Active (~/.node9/config.json)") : import_chalk12.default.gray("Not present")}`
17338
+ ` Global: ${import_fs32.default.existsSync(globalConfig) ? import_chalk12.default.green("Active (~/.node9/config.json)") : import_chalk12.default.gray("Not present")}`
17295
17339
  );
17296
17340
  if (mergedConfig.policy.sandboxPaths.length > 0) {
17297
17341
  console.log(
@@ -17365,7 +17409,7 @@ function registerStatusCommand(program2) {
17365
17409
 
17366
17410
  // src/cli/commands/init.ts
17367
17411
  var import_chalk13 = __toESM(require("chalk"));
17368
- var import_fs32 = __toESM(require("fs"));
17412
+ var import_fs33 = __toESM(require("fs"));
17369
17413
  var import_path35 = __toESM(require("path"));
17370
17414
  var import_os29 = __toESM(require("os"));
17371
17415
  var import_https3 = __toESM(require("https"));
@@ -17429,14 +17473,14 @@ function registerInitCommand(program2) {
17429
17473
  console.log("");
17430
17474
  }
17431
17475
  const configPath = import_path35.default.join(import_os29.default.homedir(), ".node9", "config.json");
17432
- if (import_fs32.default.existsSync(configPath) && !options.force) {
17476
+ if (import_fs33.default.existsSync(configPath) && !options.force) {
17433
17477
  try {
17434
- const existing = JSON.parse(import_fs32.default.readFileSync(configPath, "utf-8"));
17478
+ const existing = JSON.parse(import_fs33.default.readFileSync(configPath, "utf-8"));
17435
17479
  const settings = existing.settings ?? {};
17436
17480
  if (settings.mode !== chosenMode) {
17437
17481
  settings.mode = chosenMode;
17438
17482
  existing.settings = settings;
17439
- import_fs32.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
17483
+ import_fs33.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
17440
17484
  console.log(import_chalk13.default.green(`\u2705 Mode updated: ${chosenMode}`));
17441
17485
  } else {
17442
17486
  console.log(import_chalk13.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -17450,8 +17494,8 @@ function registerInitCommand(program2) {
17450
17494
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
17451
17495
  };
17452
17496
  const dir = import_path35.default.dirname(configPath);
17453
- if (!import_fs32.default.existsSync(dir)) import_fs32.default.mkdirSync(dir, { recursive: true });
17454
- import_fs32.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
17497
+ if (!import_fs33.default.existsSync(dir)) import_fs33.default.mkdirSync(dir, { recursive: true });
17498
+ import_fs33.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
17455
17499
  console.log(import_chalk13.default.green(`\u2705 Config created: ${configPath}`));
17456
17500
  console.log(import_chalk13.default.gray(` Mode: ${chosenMode}`));
17457
17501
  }
@@ -17891,7 +17935,7 @@ init_orchestrator();
17891
17935
  init_provenance();
17892
17936
 
17893
17937
  // src/mcp-pin.ts
17894
- var import_fs33 = __toESM(require("fs"));
17938
+ var import_fs34 = __toESM(require("fs"));
17895
17939
  var import_path37 = __toESM(require("path"));
17896
17940
  var import_os30 = __toESM(require("os"));
17897
17941
  var import_crypto10 = __toESM(require("crypto"));
@@ -17913,7 +17957,7 @@ function getServerKey(upstreamCommand) {
17913
17957
  function readMcpPinsSafe() {
17914
17958
  const filePath = getPinsFilePath2();
17915
17959
  try {
17916
- const raw = import_fs33.default.readFileSync(filePath, "utf-8");
17960
+ const raw = import_fs34.default.readFileSync(filePath, "utf-8");
17917
17961
  if (!raw.trim()) {
17918
17962
  return { ok: false, reason: "corrupt", detail: "empty file" };
17919
17963
  }
@@ -17937,10 +17981,10 @@ function readMcpPins() {
17937
17981
  }
17938
17982
  function writeMcpPins(data) {
17939
17983
  const filePath = getPinsFilePath2();
17940
- import_fs33.default.mkdirSync(import_path37.default.dirname(filePath), { recursive: true });
17984
+ import_fs34.default.mkdirSync(import_path37.default.dirname(filePath), { recursive: true });
17941
17985
  const tmp = `${filePath}.${import_crypto10.default.randomBytes(6).toString("hex")}.tmp`;
17942
- import_fs33.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17943
- import_fs33.default.renameSync(tmp, filePath);
17986
+ import_fs34.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17987
+ import_fs34.default.renameSync(tmp, filePath);
17944
17988
  }
17945
17989
  function checkPin(serverKey, currentHash) {
17946
17990
  const result = readMcpPinsSafe();
@@ -18388,7 +18432,7 @@ function registerMcpGatewayCommand(program2) {
18388
18432
 
18389
18433
  // src/mcp-server/index.ts
18390
18434
  var import_readline4 = __toESM(require("readline"));
18391
- var import_fs34 = __toESM(require("fs"));
18435
+ var import_fs35 = __toESM(require("fs"));
18392
18436
  var import_os31 = __toESM(require("os"));
18393
18437
  var import_path38 = __toESM(require("path"));
18394
18438
  var import_child_process15 = require("child_process");
@@ -18644,10 +18688,10 @@ function handleStatus() {
18644
18688
  const projectConfig = import_path38.default.join(process.cwd(), "node9.config.json");
18645
18689
  const globalConfig = import_path38.default.join(import_os31.default.homedir(), ".node9", "config.json");
18646
18690
  lines.push(
18647
- `Project config (node9.config.json): ${import_fs34.default.existsSync(projectConfig) ? "present" : "not found"}`
18691
+ `Project config (node9.config.json): ${import_fs35.default.existsSync(projectConfig) ? "present" : "not found"}`
18648
18692
  );
18649
18693
  lines.push(
18650
- `Global config (~/.node9/config.json): ${import_fs34.default.existsSync(globalConfig) ? "present" : "not found"}`
18694
+ `Global config (~/.node9/config.json): ${import_fs35.default.existsSync(globalConfig) ? "present" : "not found"}`
18651
18695
  );
18652
18696
  return lines.join("\n");
18653
18697
  }
@@ -18725,8 +18769,8 @@ var GLOBAL_CONFIG_PATH2 = import_path38.default.join(import_os31.default.homedir
18725
18769
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
18726
18770
  function readGlobalConfigRaw() {
18727
18771
  try {
18728
- if (import_fs34.default.existsSync(GLOBAL_CONFIG_PATH2)) {
18729
- return JSON.parse(import_fs34.default.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
18772
+ if (import_fs35.default.existsSync(GLOBAL_CONFIG_PATH2)) {
18773
+ return JSON.parse(import_fs35.default.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
18730
18774
  }
18731
18775
  } catch {
18732
18776
  }
@@ -18734,8 +18778,8 @@ function readGlobalConfigRaw() {
18734
18778
  }
18735
18779
  function writeGlobalConfigRaw(data) {
18736
18780
  const dir = import_path38.default.dirname(GLOBAL_CONFIG_PATH2);
18737
- if (!import_fs34.default.existsSync(dir)) import_fs34.default.mkdirSync(dir, { recursive: true });
18738
- import_fs34.default.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
18781
+ if (!import_fs35.default.existsSync(dir)) import_fs35.default.mkdirSync(dir, { recursive: true });
18782
+ import_fs35.default.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
18739
18783
  }
18740
18784
  function handleApproverList() {
18741
18785
  const config = getConfig();
@@ -18780,8 +18824,8 @@ function handleAuditGet(args) {
18780
18824
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
18781
18825
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
18782
18826
  const auditPath = import_path38.default.join(import_os31.default.homedir(), ".node9", "audit.log");
18783
- if (!import_fs34.default.existsSync(auditPath)) return "No audit log found.";
18784
- const rawLines = import_fs34.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
18827
+ if (!import_fs35.default.existsSync(auditPath)) return "No audit log found.";
18828
+ const rawLines = import_fs35.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
18785
18829
  const parsed = [];
18786
18830
  for (const line of rawLines) {
18787
18831
  try {
@@ -19335,7 +19379,7 @@ init_scan();
19335
19379
 
19336
19380
  // src/cli/commands/sessions.ts
19337
19381
  var import_chalk22 = __toESM(require("chalk"));
19338
- var import_fs35 = __toESM(require("fs"));
19382
+ var import_fs36 = __toESM(require("fs"));
19339
19383
  var import_path39 = __toESM(require("path"));
19340
19384
  var import_os32 = __toESM(require("os"));
19341
19385
  var CLAUDE_PRICING3 = {
@@ -19453,7 +19497,7 @@ function loadAuditEntries(auditPath) {
19453
19497
  const aPath = auditPath ?? import_path39.default.join(import_os32.default.homedir(), ".node9", "audit.log");
19454
19498
  let raw;
19455
19499
  try {
19456
- raw = import_fs35.default.readFileSync(aPath, "utf-8");
19500
+ raw = import_fs36.default.readFileSync(aPath, "utf-8");
19457
19501
  } catch {
19458
19502
  return [];
19459
19503
  }
@@ -19490,7 +19534,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
19490
19534
  }
19491
19535
  function buildGeminiSessions(days, allAuditEntries) {
19492
19536
  const tmpDir = import_path39.default.join(import_os32.default.homedir(), ".gemini", "tmp");
19493
- if (!import_fs35.default.existsSync(tmpDir)) return [];
19537
+ if (!import_fs36.default.existsSync(tmpDir)) return [];
19494
19538
  const cutoff = days !== null ? (() => {
19495
19539
  const d = /* @__PURE__ */ new Date();
19496
19540
  d.setDate(d.getDate() - days);
@@ -19499,7 +19543,7 @@ function buildGeminiSessions(days, allAuditEntries) {
19499
19543
  })() : null;
19500
19544
  let slugDirs;
19501
19545
  try {
19502
- slugDirs = import_fs35.default.readdirSync(tmpDir);
19546
+ slugDirs = import_fs36.default.readdirSync(tmpDir);
19503
19547
  } catch {
19504
19548
  return [];
19505
19549
  }
@@ -19507,27 +19551,27 @@ function buildGeminiSessions(days, allAuditEntries) {
19507
19551
  for (const slug of slugDirs) {
19508
19552
  const slugPath = import_path39.default.join(tmpDir, slug);
19509
19553
  try {
19510
- if (!import_fs35.default.statSync(slugPath).isDirectory()) continue;
19554
+ if (!import_fs36.default.statSync(slugPath).isDirectory()) continue;
19511
19555
  } catch {
19512
19556
  continue;
19513
19557
  }
19514
19558
  let projectRoot = import_path39.default.join(import_os32.default.homedir(), slug);
19515
19559
  try {
19516
- projectRoot = import_fs35.default.readFileSync(import_path39.default.join(slugPath, ".project_root"), "utf-8").trim();
19560
+ projectRoot = import_fs36.default.readFileSync(import_path39.default.join(slugPath, ".project_root"), "utf-8").trim();
19517
19561
  } catch {
19518
19562
  }
19519
19563
  const chatsDir = import_path39.default.join(slugPath, "chats");
19520
- if (!import_fs35.default.existsSync(chatsDir)) continue;
19564
+ if (!import_fs36.default.existsSync(chatsDir)) continue;
19521
19565
  let chatFiles;
19522
19566
  try {
19523
- chatFiles = import_fs35.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
19567
+ chatFiles = import_fs36.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
19524
19568
  } catch {
19525
19569
  continue;
19526
19570
  }
19527
19571
  for (const chatFile of chatFiles) {
19528
19572
  let raw;
19529
19573
  try {
19530
- raw = import_fs35.default.readFileSync(import_path39.default.join(chatsDir, chatFile), "utf-8");
19574
+ raw = import_fs36.default.readFileSync(import_path39.default.join(chatsDir, chatFile), "utf-8");
19531
19575
  } catch {
19532
19576
  continue;
19533
19577
  }
@@ -19608,7 +19652,7 @@ function buildGeminiSessions(days, allAuditEntries) {
19608
19652
  }
19609
19653
  function buildCodexSessions(days, allAuditEntries) {
19610
19654
  const sessionsBase = import_path39.default.join(import_os32.default.homedir(), ".codex", "sessions");
19611
- if (!import_fs35.default.existsSync(sessionsBase)) return [];
19655
+ if (!import_fs36.default.existsSync(sessionsBase)) return [];
19612
19656
  const cutoff = days !== null ? (() => {
19613
19657
  const d = /* @__PURE__ */ new Date();
19614
19658
  d.setDate(d.getDate() - days);
@@ -19617,28 +19661,28 @@ function buildCodexSessions(days, allAuditEntries) {
19617
19661
  })() : null;
19618
19662
  const jsonlFiles = [];
19619
19663
  try {
19620
- for (const year of import_fs35.default.readdirSync(sessionsBase)) {
19664
+ for (const year of import_fs36.default.readdirSync(sessionsBase)) {
19621
19665
  const yearPath = import_path39.default.join(sessionsBase, year);
19622
19666
  try {
19623
- if (!import_fs35.default.statSync(yearPath).isDirectory()) continue;
19667
+ if (!import_fs36.default.statSync(yearPath).isDirectory()) continue;
19624
19668
  } catch {
19625
19669
  continue;
19626
19670
  }
19627
- for (const month of import_fs35.default.readdirSync(yearPath)) {
19671
+ for (const month of import_fs36.default.readdirSync(yearPath)) {
19628
19672
  const monthPath = import_path39.default.join(yearPath, month);
19629
19673
  try {
19630
- if (!import_fs35.default.statSync(monthPath).isDirectory()) continue;
19674
+ if (!import_fs36.default.statSync(monthPath).isDirectory()) continue;
19631
19675
  } catch {
19632
19676
  continue;
19633
19677
  }
19634
- for (const day of import_fs35.default.readdirSync(monthPath)) {
19678
+ for (const day of import_fs36.default.readdirSync(monthPath)) {
19635
19679
  const dayPath = import_path39.default.join(monthPath, day);
19636
19680
  try {
19637
- if (!import_fs35.default.statSync(dayPath).isDirectory()) continue;
19681
+ if (!import_fs36.default.statSync(dayPath).isDirectory()) continue;
19638
19682
  } catch {
19639
19683
  continue;
19640
19684
  }
19641
- for (const file of import_fs35.default.readdirSync(dayPath)) {
19685
+ for (const file of import_fs36.default.readdirSync(dayPath)) {
19642
19686
  if (file.endsWith(".jsonl")) jsonlFiles.push(import_path39.default.join(dayPath, file));
19643
19687
  }
19644
19688
  }
@@ -19651,7 +19695,7 @@ function buildCodexSessions(days, allAuditEntries) {
19651
19695
  for (const filePath of jsonlFiles) {
19652
19696
  let lines;
19653
19697
  try {
19654
- lines = import_fs35.default.readFileSync(filePath, "utf-8").split("\n");
19698
+ lines = import_fs36.default.readFileSync(filePath, "utf-8").split("\n");
19655
19699
  } catch {
19656
19700
  continue;
19657
19701
  }
@@ -19732,7 +19776,7 @@ function buildSessions(days, historyPath) {
19732
19776
  const hPath = historyPath ?? import_path39.default.join(import_os32.default.homedir(), ".claude", "history.jsonl");
19733
19777
  let historyRaw;
19734
19778
  try {
19735
- historyRaw = import_fs35.default.readFileSync(hPath, "utf-8");
19779
+ historyRaw = import_fs36.default.readFileSync(hPath, "utf-8");
19736
19780
  } catch {
19737
19781
  return [];
19738
19782
  }
@@ -19757,7 +19801,7 @@ function buildSessions(days, historyPath) {
19757
19801
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
19758
19802
  let sessionLines = [];
19759
19803
  try {
19760
- sessionLines = import_fs35.default.readFileSync(jsonlFile, "utf-8").split("\n");
19804
+ sessionLines = import_fs36.default.readFileSync(jsonlFile, "utf-8").split("\n");
19761
19805
  } catch {
19762
19806
  }
19763
19807
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -20024,7 +20068,7 @@ function registerSessionsCommand(program2) {
20024
20068
  console.log(import_chalk22.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk22.default.dim(" \u2014 what your AI agent did"));
20025
20069
  console.log("");
20026
20070
  const historyPath = import_path39.default.join(import_os32.default.homedir(), ".claude", "history.jsonl");
20027
- if (!import_fs35.default.existsSync(historyPath)) {
20071
+ if (!import_fs36.default.existsSync(historyPath)) {
20028
20072
  console.log(import_chalk22.default.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
20029
20073
  console.log(import_chalk22.default.gray(" Install Claude Code, run a few sessions, then try again.\n"));
20030
20074
  return;
@@ -20061,12 +20105,12 @@ function registerSessionsCommand(program2) {
20061
20105
 
20062
20106
  // src/cli/commands/skill-pin.ts
20063
20107
  var import_chalk23 = __toESM(require("chalk"));
20064
- var import_fs36 = __toESM(require("fs"));
20108
+ var import_fs37 = __toESM(require("fs"));
20065
20109
  var import_os33 = __toESM(require("os"));
20066
20110
  var import_path40 = __toESM(require("path"));
20067
20111
  function wipeSkillSessions() {
20068
20112
  try {
20069
- import_fs36.default.rmSync(import_path40.default.join(import_os33.default.homedir(), ".node9", "skill-sessions"), {
20113
+ import_fs37.default.rmSync(import_path40.default.join(import_os33.default.homedir(), ".node9", "skill-sessions"), {
20070
20114
  recursive: true,
20071
20115
  force: true
20072
20116
  });
@@ -20149,7 +20193,7 @@ function registerSkillPinCommand(program2) {
20149
20193
 
20150
20194
  // src/cli/commands/dlp.ts
20151
20195
  var import_chalk24 = __toESM(require("chalk"));
20152
- var import_fs37 = __toESM(require("fs"));
20196
+ var import_fs38 = __toESM(require("fs"));
20153
20197
  var import_path41 = __toESM(require("path"));
20154
20198
  var import_os34 = __toESM(require("os"));
20155
20199
  var AUDIT_LOG = import_path41.default.join(import_os34.default.homedir(), ".node9", "audit.log");
@@ -20160,7 +20204,7 @@ function stripAnsi(s) {
20160
20204
  }
20161
20205
  function loadResolved() {
20162
20206
  try {
20163
- const raw = JSON.parse(import_fs37.default.readFileSync(RESOLVED_FILE, "utf-8"));
20207
+ const raw = JSON.parse(import_fs38.default.readFileSync(RESOLVED_FILE, "utf-8"));
20164
20208
  return new Set(raw);
20165
20209
  } catch {
20166
20210
  return /* @__PURE__ */ new Set();
@@ -20168,13 +20212,13 @@ function loadResolved() {
20168
20212
  }
20169
20213
  function saveResolved(resolved) {
20170
20214
  try {
20171
- import_fs37.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
20215
+ import_fs38.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
20172
20216
  } catch {
20173
20217
  }
20174
20218
  }
20175
20219
  function loadDlpFindings() {
20176
- if (!import_fs37.default.existsSync(AUDIT_LOG)) return [];
20177
- return import_fs37.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
20220
+ if (!import_fs38.default.existsSync(AUDIT_LOG)) return [];
20221
+ return import_fs38.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
20178
20222
  if (!line.trim()) return [];
20179
20223
  try {
20180
20224
  const e = JSON.parse(line);
@@ -20272,20 +20316,20 @@ function registerDlpCommand(program2) {
20272
20316
 
20273
20317
  // src/cli.ts
20274
20318
  var { version } = JSON.parse(
20275
- import_fs40.default.readFileSync(import_path44.default.join(__dirname, "../package.json"), "utf-8")
20319
+ import_fs41.default.readFileSync(import_path44.default.join(__dirname, "../package.json"), "utf-8")
20276
20320
  );
20277
20321
  var program = new import_commander.Command();
20278
20322
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
20279
20323
  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) => {
20280
20324
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
20281
20325
  const credPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "credentials.json");
20282
- if (!import_fs40.default.existsSync(import_path44.default.dirname(credPath)))
20283
- import_fs40.default.mkdirSync(import_path44.default.dirname(credPath), { recursive: true });
20326
+ if (!import_fs41.default.existsSync(import_path44.default.dirname(credPath)))
20327
+ import_fs41.default.mkdirSync(import_path44.default.dirname(credPath), { recursive: true });
20284
20328
  const profileName = options.profile || "default";
20285
20329
  let existingCreds = {};
20286
20330
  try {
20287
- if (import_fs40.default.existsSync(credPath)) {
20288
- const raw = JSON.parse(import_fs40.default.readFileSync(credPath, "utf-8"));
20331
+ if (import_fs41.default.existsSync(credPath)) {
20332
+ const raw = JSON.parse(import_fs41.default.readFileSync(credPath, "utf-8"));
20289
20333
  if (raw.apiKey) {
20290
20334
  existingCreds = {
20291
20335
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -20297,13 +20341,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
20297
20341
  } catch {
20298
20342
  }
20299
20343
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
20300
- import_fs40.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
20344
+ import_fs41.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
20301
20345
  if (profileName === "default") {
20302
20346
  const configPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "config.json");
20303
20347
  let config = {};
20304
20348
  try {
20305
- if (import_fs40.default.existsSync(configPath))
20306
- config = JSON.parse(import_fs40.default.readFileSync(configPath, "utf-8"));
20349
+ if (import_fs41.default.existsSync(configPath))
20350
+ config = JSON.parse(import_fs41.default.readFileSync(configPath, "utf-8"));
20307
20351
  } catch {
20308
20352
  }
20309
20353
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -20318,9 +20362,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
20318
20362
  approvers.cloud = false;
20319
20363
  }
20320
20364
  s.approvers = approvers;
20321
- if (!import_fs40.default.existsSync(import_path44.default.dirname(configPath)))
20322
- import_fs40.default.mkdirSync(import_path44.default.dirname(configPath), { recursive: true });
20323
- import_fs40.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
20365
+ if (!import_fs41.default.existsSync(import_path44.default.dirname(configPath)))
20366
+ import_fs41.default.mkdirSync(import_path44.default.dirname(configPath), { recursive: true });
20367
+ import_fs41.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
20324
20368
  }
20325
20369
  if (options.profile && profileName !== "default") {
20326
20370
  console.log(import_chalk26.default.green(`\u2705 Profile "${profileName}" saved`));
@@ -20458,14 +20502,14 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
20458
20502
  }
20459
20503
  if (options.purge) {
20460
20504
  const node9Dir = import_path44.default.join(import_os37.default.homedir(), ".node9");
20461
- if (import_fs40.default.existsSync(node9Dir)) {
20505
+ if (import_fs41.default.existsSync(node9Dir)) {
20462
20506
  const confirmed = await (0, import_prompts2.confirm)({
20463
20507
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
20464
20508
  default: false
20465
20509
  });
20466
20510
  if (confirmed) {
20467
- import_fs40.default.rmSync(node9Dir, { recursive: true });
20468
- if (import_fs40.default.existsSync(node9Dir)) {
20511
+ import_fs41.default.rmSync(node9Dir, { recursive: true });
20512
+ if (import_fs41.default.existsSync(node9Dir)) {
20469
20513
  console.error(
20470
20514
  import_chalk26.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
20471
20515
  );
@@ -20609,12 +20653,12 @@ Run "node9 addto claude" to register it as the statusLine.`
20609
20653
  if (subcommand === "debug") {
20610
20654
  const flagFile = import_path44.default.join(import_os37.default.homedir(), ".node9", "hud-debug");
20611
20655
  if (state === "on") {
20612
- import_fs40.default.mkdirSync(import_path44.default.dirname(flagFile), { recursive: true });
20613
- import_fs40.default.writeFileSync(flagFile, "");
20656
+ import_fs41.default.mkdirSync(import_path44.default.dirname(flagFile), { recursive: true });
20657
+ import_fs41.default.writeFileSync(flagFile, "");
20614
20658
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
20615
20659
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
20616
20660
  } else if (state === "off") {
20617
- if (import_fs40.default.existsSync(flagFile)) import_fs40.default.unlinkSync(flagFile);
20661
+ if (import_fs41.default.existsSync(flagFile)) import_fs41.default.unlinkSync(flagFile);
20618
20662
  console.log("HUD debug logging disabled.");
20619
20663
  } else {
20620
20664
  console.error("Usage: node9 hud debug on|off");
@@ -20728,7 +20772,7 @@ if (process.argv[2] !== "daemon") {
20728
20772
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
20729
20773
  const logPath = import_path44.default.join(import_os37.default.homedir(), ".node9", "hook-debug.log");
20730
20774
  const msg = reason instanceof Error ? reason.message : String(reason);
20731
- import_fs40.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
20775
+ import_fs41.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
20732
20776
  `);
20733
20777
  }
20734
20778
  process.exit(0);