@node9/proxy 1.11.13 → 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.mjs CHANGED
@@ -147,8 +147,8 @@ function sanitizeConfig(raw) {
147
147
  }
148
148
  }
149
149
  const lines = result.error.issues.map((issue) => {
150
- const path44 = issue.path.length > 0 ? issue.path.join(".") : "root";
151
- return ` \u2022 ${path44}: ${issue.message}`;
150
+ const path45 = issue.path.length > 0 ? issue.path.join(".") : "root";
151
+ return ` \u2022 ${path45}: ${issue.message}`;
152
152
  });
153
153
  return {
154
154
  sanitized,
@@ -2066,9 +2066,9 @@ function matchesPattern(text, patterns) {
2066
2066
  const withoutDotSlash = text.replace(/^\.\//, "");
2067
2067
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
2068
2068
  }
2069
- function getNestedValue(obj, path44) {
2069
+ function getNestedValue(obj, path45) {
2070
2070
  if (!obj || typeof obj !== "object") return null;
2071
- return path44.split(".").reduce((prev, curr) => prev?.[curr], obj);
2071
+ return path45.split(".").reduce((prev, curr) => prev?.[curr], obj);
2072
2072
  }
2073
2073
  function normalizeCommandForPolicy(command) {
2074
2074
  try {
@@ -2327,7 +2327,12 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2327
2327
  pathTokens = analyzed.paths;
2328
2328
  const INLINE_EXEC_PATTERN = /^(python3?|bash|sh|zsh|perl|ruby|node|php|lua)\s+(-c|-e|-eval)\s/i;
2329
2329
  if (INLINE_EXEC_PATTERN.test(shellCommand.trim())) {
2330
- return { decision: "review", blockedByLabel: "Node9 Standard (Inline Execution)", tier: 3 };
2330
+ return {
2331
+ decision: "review",
2332
+ blockedByLabel: "Node9 Standard (Inline Execution)",
2333
+ ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute.",
2334
+ tier: 3
2335
+ };
2331
2336
  }
2332
2337
  const evalVerdict = detectDangerousShellExec(shellCommand);
2333
2338
  if (evalVerdict === "block") {
@@ -2335,6 +2340,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2335
2340
  decision: "block",
2336
2341
  blockedByLabel: "Node9: Eval Remote Execution",
2337
2342
  reason: "eval of remote download (curl/wget) is a near-certain supply-chain attack",
2343
+ ruleDescription: "The AI is downloading a script from the internet and running it immediately without inspection. This is a common way malware gets installed.",
2338
2344
  tier: 3
2339
2345
  };
2340
2346
  }
@@ -2343,6 +2349,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2343
2349
  decision: "review",
2344
2350
  blockedByLabel: "Node9: Eval Dynamic Content",
2345
2351
  reason: "eval of dynamic content (variable or subshell expansion) requires approval",
2352
+ ruleDescription: "The AI is running a command that includes a variable or subshell expansion. The actual command executed at runtime may differ from what is shown here.",
2346
2353
  tier: 3
2347
2354
  };
2348
2355
  }
@@ -2472,6 +2479,7 @@ async function evaluatePolicy(toolName, args, agent, cwd) {
2472
2479
  blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
2473
2480
  matchedWord: matchedDangerousWord,
2474
2481
  matchedField,
2482
+ ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`,
2475
2483
  tier: 6
2476
2484
  };
2477
2485
  }
@@ -2624,7 +2632,8 @@ async function explainPolicy(toolName, args) {
2624
2632
  waterfall,
2625
2633
  steps,
2626
2634
  decision: "review",
2627
- blockedByLabel: "Node9 Standard (Inline Execution)"
2635
+ blockedByLabel: "Node9 Standard (Inline Execution)",
2636
+ ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute."
2628
2637
  };
2629
2638
  }
2630
2639
  steps.push({
@@ -2730,7 +2739,8 @@ async function explainPolicy(toolName, args) {
2730
2739
  steps,
2731
2740
  decision: "review",
2732
2741
  blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`,
2733
- matchedToken: matchedDangerousWord
2742
+ matchedToken: matchedDangerousWord,
2743
+ ruleDescription: `This command contains a flagged keyword ("${matchedDangerousWord}") from your node9 config. Review it before allowing.`
2734
2744
  };
2735
2745
  }
2736
2746
  steps.push({
@@ -5243,6 +5253,10 @@ var init_ui = __esm({
5243
5253
  border-color: rgba(83, 155, 245, 0.35);
5244
5254
  background: rgba(83, 155, 245, 0.04);
5245
5255
  }
5256
+ .pending-card.discovery-card {
5257
+ border-color: rgba(120, 100, 255, 0.45);
5258
+ background: rgba(100, 80, 255, 0.06);
5259
+ }
5246
5260
  .pending-card-header {
5247
5261
  display: flex;
5248
5262
  align-items: center;
@@ -7429,7 +7443,7 @@ var init_ui = __esm({
7429
7443
  });
7430
7444
 
7431
7445
  try {
7432
- await fetch('/mcp/approve', {
7446
+ const res = await fetch('/mcp/approve', {
7433
7447
  method: 'POST',
7434
7448
  headers: {
7435
7449
  'Content-Type': 'application/json',
@@ -7437,9 +7451,19 @@ var init_ui = __esm({
7437
7451
  },
7438
7452
  body: JSON.stringify({ id, serverKey, disabledTools }),
7439
7453
  });
7454
+ if (!res.ok) {
7455
+ showToast(
7456
+ '\u26A0\uFE0F',
7457
+ 'Approval failed',
7458
+ \`Server responded with \${res.status}\`,
7459
+ 'toast-block'
7460
+ );
7461
+ return;
7462
+ }
7440
7463
  closeMcpReview();
7441
7464
  refreshMcpTools();
7442
7465
  } catch (err) {
7466
+ showToast('\u26A0\uFE0F', 'Approval failed', 'Network error \u2014 check the daemon', 'toast-block');
7443
7467
  console.error('Failed to approve MCP server:', err);
7444
7468
  }
7445
7469
  }
@@ -8254,6 +8278,11 @@ var init_ui2 = __esm({
8254
8278
 
8255
8279
  // src/cli/daemon-starter.ts
8256
8280
  import { spawn as spawn2, execSync } from "child_process";
8281
+ import path16 from "path";
8282
+ import fs13 from "fs";
8283
+ function isTestingMode() {
8284
+ return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
8285
+ }
8257
8286
  function openBrowserLocal() {
8258
8287
  const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
8259
8288
  try {
@@ -8264,26 +8293,41 @@ function openBrowserLocal() {
8264
8293
  } catch {
8265
8294
  }
8266
8295
  }
8267
- async function autoStartDaemonAndWait() {
8268
- if (process.env.NODE9_TESTING === "1") return false;
8296
+ async function autoStartDaemonAndWait(openBrowser2 = true) {
8297
+ if (isTestingMode()) return false;
8298
+ if (!path16.isAbsolute(process.argv[1])) return false;
8299
+ let resolvedArgv1;
8269
8300
  try {
8270
- const child = spawn2(process.execPath, [process.argv[1], "daemon"], {
8301
+ resolvedArgv1 = fs13.realpathSync(process.argv[1]);
8302
+ } catch {
8303
+ return false;
8304
+ }
8305
+ if (!resolvedArgv1.endsWith(".js")) return false;
8306
+ try {
8307
+ const child = spawn2(process.execPath, [resolvedArgv1, "daemon"], {
8271
8308
  detached: true,
8272
8309
  stdio: "ignore",
8273
8310
  // NODE9_BROWSER_OPENED=1 tells the daemon we will open the browser ourselves
8274
8311
  // (openBrowserLocal below), so it must not open a duplicate tab on first approval.
8275
- env: { ...process.env, NODE9_AUTO_STARTED: "1", NODE9_BROWSER_OPENED: "1" }
8312
+ // Only set NODE9_BROWSER_OPENED when we actually intend to open it here.
8313
+ env: {
8314
+ ...process.env,
8315
+ NODE9_AUTO_STARTED: "1",
8316
+ ...openBrowser2 && { NODE9_BROWSER_OPENED: "1" }
8317
+ }
8276
8318
  });
8277
8319
  child.unref();
8278
8320
  for (let i = 0; i < 20; i++) {
8279
8321
  await new Promise((r) => setTimeout(r, 250));
8280
8322
  if (!isDaemonRunning()) continue;
8281
8323
  try {
8282
- const res = await fetch("http://127.0.0.1:7391/settings", {
8324
+ const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/settings`, {
8283
8325
  signal: AbortSignal.timeout(500)
8284
8326
  });
8285
8327
  if (res.ok) {
8286
- openBrowserLocal();
8328
+ if (openBrowser2) {
8329
+ openBrowserLocal();
8330
+ }
8287
8331
  return true;
8288
8332
  }
8289
8333
  } catch {
@@ -8491,8 +8535,8 @@ var init_scan_summary = __esm({
8491
8535
 
8492
8536
  // src/cli/commands/scan.ts
8493
8537
  import chalk2 from "chalk";
8494
- import fs13 from "fs";
8495
- import path16 from "path";
8538
+ import fs14 from "fs";
8539
+ import path17 from "path";
8496
8540
  import os12 from "os";
8497
8541
  function claudeModelPrice(model) {
8498
8542
  const base = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
@@ -8592,14 +8636,14 @@ function buildRuleSources() {
8592
8636
  }
8593
8637
  function countScanFiles() {
8594
8638
  let total = 0;
8595
- const claudeDir = path16.join(os12.homedir(), ".claude", "projects");
8596
- if (fs13.existsSync(claudeDir)) {
8639
+ const claudeDir = path17.join(os12.homedir(), ".claude", "projects");
8640
+ if (fs14.existsSync(claudeDir)) {
8597
8641
  try {
8598
- for (const proj of fs13.readdirSync(claudeDir)) {
8599
- const p = path16.join(claudeDir, proj);
8642
+ for (const proj of fs14.readdirSync(claudeDir)) {
8643
+ const p = path17.join(claudeDir, proj);
8600
8644
  try {
8601
- if (!fs13.statSync(p).isDirectory()) continue;
8602
- total += fs13.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
8645
+ if (!fs14.statSync(p).isDirectory()) continue;
8646
+ total += fs14.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
8603
8647
  } catch {
8604
8648
  continue;
8605
8649
  }
@@ -8607,17 +8651,17 @@ function countScanFiles() {
8607
8651
  } catch {
8608
8652
  }
8609
8653
  }
8610
- const geminiDir = path16.join(os12.homedir(), ".gemini", "tmp");
8611
- if (fs13.existsSync(geminiDir)) {
8654
+ const geminiDir = path17.join(os12.homedir(), ".gemini", "tmp");
8655
+ if (fs14.existsSync(geminiDir)) {
8612
8656
  try {
8613
- for (const slug of fs13.readdirSync(geminiDir)) {
8614
- const p = path16.join(geminiDir, slug);
8657
+ for (const slug of fs14.readdirSync(geminiDir)) {
8658
+ const p = path17.join(geminiDir, slug);
8615
8659
  try {
8616
- if (!fs13.statSync(p).isDirectory()) continue;
8617
- const chatsDir = path16.join(p, "chats");
8618
- if (fs13.existsSync(chatsDir)) {
8660
+ if (!fs14.statSync(p).isDirectory()) continue;
8661
+ const chatsDir = path17.join(p, "chats");
8662
+ if (fs14.existsSync(chatsDir)) {
8619
8663
  try {
8620
- total += fs13.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
8664
+ total += fs14.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
8621
8665
  } catch {
8622
8666
  }
8623
8667
  }
@@ -8628,22 +8672,22 @@ function countScanFiles() {
8628
8672
  } catch {
8629
8673
  }
8630
8674
  }
8631
- const codexDir = path16.join(os12.homedir(), ".codex", "sessions");
8632
- if (fs13.existsSync(codexDir)) {
8675
+ const codexDir = path17.join(os12.homedir(), ".codex", "sessions");
8676
+ if (fs14.existsSync(codexDir)) {
8633
8677
  try {
8634
- for (const year of fs13.readdirSync(codexDir)) {
8635
- const yp = path16.join(codexDir, year);
8678
+ for (const year of fs14.readdirSync(codexDir)) {
8679
+ const yp = path17.join(codexDir, year);
8636
8680
  try {
8637
- if (!fs13.statSync(yp).isDirectory()) continue;
8638
- for (const month of fs13.readdirSync(yp)) {
8639
- const mp = path16.join(yp, month);
8681
+ if (!fs14.statSync(yp).isDirectory()) continue;
8682
+ for (const month of fs14.readdirSync(yp)) {
8683
+ const mp = path17.join(yp, month);
8640
8684
  try {
8641
- if (!fs13.statSync(mp).isDirectory()) continue;
8642
- for (const day of fs13.readdirSync(mp)) {
8643
- const dp = path16.join(mp, day);
8685
+ if (!fs14.statSync(mp).isDirectory()) continue;
8686
+ for (const day of fs14.readdirSync(mp)) {
8687
+ const dp = path17.join(mp, day);
8644
8688
  try {
8645
- if (!fs13.statSync(dp).isDirectory()) continue;
8646
- total += fs13.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
8689
+ if (!fs14.statSync(dp).isDirectory()) continue;
8690
+ total += fs14.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
8647
8691
  } catch {
8648
8692
  continue;
8649
8693
  }
@@ -8661,18 +8705,19 @@ function countScanFiles() {
8661
8705
  }
8662
8706
  return total;
8663
8707
  }
8664
- function renderProgressBar(done, total) {
8708
+ function renderProgressBar(done, total, lines) {
8665
8709
  const width = 28;
8666
8710
  const pct = total > 0 ? done / total : 0;
8667
8711
  const filled = Math.min(width, Math.round(pct * width));
8668
8712
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
8669
- const label = total > 0 ? `${done}/${total} files` : `${done} files`;
8713
+ const fileLabel = total > 0 ? `${done}/${total} files` : `${done} files`;
8714
+ const lineLabel = lines > 0 ? chalk2.dim(` ${lines.toLocaleString()} lines`) : "";
8670
8715
  process.stdout.write(
8671
- `\r ${chalk2.cyan("Scanning")} [${chalk2.cyan(bar)}] ${chalk2.dim(label)} `
8716
+ `\r ${chalk2.cyan("Scanning")} [${chalk2.cyan(bar)}] ${chalk2.dim(fileLabel)}${lineLabel} `
8672
8717
  );
8673
8718
  }
8674
- function scanClaudeHistory(startDate, onProgress) {
8675
- const projectsDir = path16.join(os12.homedir(), ".claude", "projects");
8719
+ function scanClaudeHistory(startDate, onProgress, onLine) {
8720
+ const projectsDir = path17.join(os12.homedir(), ".claude", "projects");
8676
8721
  const result = {
8677
8722
  filesScanned: 0,
8678
8723
  sessions: 0,
@@ -8685,25 +8730,25 @@ function scanClaudeHistory(startDate, onProgress) {
8685
8730
  firstDate: null,
8686
8731
  lastDate: null
8687
8732
  };
8688
- if (!fs13.existsSync(projectsDir)) return result;
8733
+ if (!fs14.existsSync(projectsDir)) return result;
8689
8734
  let projDirs;
8690
8735
  try {
8691
- projDirs = fs13.readdirSync(projectsDir);
8736
+ projDirs = fs14.readdirSync(projectsDir);
8692
8737
  } catch {
8693
8738
  return result;
8694
8739
  }
8695
8740
  const ruleSources = buildRuleSources();
8696
8741
  for (const proj of projDirs) {
8697
- const projPath = path16.join(projectsDir, proj);
8742
+ const projPath = path17.join(projectsDir, proj);
8698
8743
  try {
8699
- if (!fs13.statSync(projPath).isDirectory()) continue;
8744
+ if (!fs14.statSync(projPath).isDirectory()) continue;
8700
8745
  } catch {
8701
8746
  continue;
8702
8747
  }
8703
8748
  const projLabel = decodeURIComponent(proj).replace(os12.homedir(), "~").slice(0, 40);
8704
8749
  let files;
8705
8750
  try {
8706
- files = fs13.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
8751
+ files = fs14.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
8707
8752
  } catch {
8708
8753
  continue;
8709
8754
  }
@@ -8714,13 +8759,14 @@ function scanClaudeHistory(startDate, onProgress) {
8714
8759
  const sessionId = file.replace(/\.jsonl$/, "");
8715
8760
  let raw;
8716
8761
  try {
8717
- raw = fs13.readFileSync(path16.join(projPath, file), "utf-8");
8762
+ raw = fs14.readFileSync(path17.join(projPath, file), "utf-8");
8718
8763
  } catch {
8719
8764
  continue;
8720
8765
  }
8721
8766
  const sessionCalls = [];
8722
8767
  for (const line of raw.split("\n")) {
8723
8768
  if (!line.trim()) continue;
8769
+ onLine?.();
8724
8770
  let entry;
8725
8771
  try {
8726
8772
  entry = JSON.parse(line);
@@ -8866,8 +8912,8 @@ function scanClaudeHistory(startDate, onProgress) {
8866
8912
  }
8867
8913
  return result;
8868
8914
  }
8869
- function scanGeminiHistory(startDate, onProgress) {
8870
- const tmpDir = path16.join(os12.homedir(), ".gemini", "tmp");
8915
+ function scanGeminiHistory(startDate, onProgress, onLine) {
8916
+ const tmpDir = path17.join(os12.homedir(), ".gemini", "tmp");
8871
8917
  const result = {
8872
8918
  filesScanned: 0,
8873
8919
  sessions: 0,
@@ -8880,31 +8926,31 @@ function scanGeminiHistory(startDate, onProgress) {
8880
8926
  firstDate: null,
8881
8927
  lastDate: null
8882
8928
  };
8883
- if (!fs13.existsSync(tmpDir)) return result;
8929
+ if (!fs14.existsSync(tmpDir)) return result;
8884
8930
  let slugDirs;
8885
8931
  try {
8886
- slugDirs = fs13.readdirSync(tmpDir);
8932
+ slugDirs = fs14.readdirSync(tmpDir);
8887
8933
  } catch {
8888
8934
  return result;
8889
8935
  }
8890
8936
  const ruleSources = buildRuleSources();
8891
8937
  for (const slug of slugDirs) {
8892
- const slugPath = path16.join(tmpDir, slug);
8938
+ const slugPath = path17.join(tmpDir, slug);
8893
8939
  try {
8894
- if (!fs13.statSync(slugPath).isDirectory()) continue;
8940
+ if (!fs14.statSync(slugPath).isDirectory()) continue;
8895
8941
  } catch {
8896
8942
  continue;
8897
8943
  }
8898
8944
  let projLabel = slug;
8899
8945
  try {
8900
- projLabel = fs13.readFileSync(path16.join(slugPath, ".project_root"), "utf-8").trim().replace(os12.homedir(), "~").slice(0, 40);
8946
+ projLabel = fs14.readFileSync(path17.join(slugPath, ".project_root"), "utf-8").trim().replace(os12.homedir(), "~").slice(0, 40);
8901
8947
  } catch {
8902
8948
  }
8903
- const chatsDir = path16.join(slugPath, "chats");
8904
- if (!fs13.existsSync(chatsDir)) continue;
8949
+ const chatsDir = path17.join(slugPath, "chats");
8950
+ if (!fs14.existsSync(chatsDir)) continue;
8905
8951
  let chatFiles;
8906
8952
  try {
8907
- chatFiles = fs13.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
8953
+ chatFiles = fs14.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
8908
8954
  } catch {
8909
8955
  continue;
8910
8956
  }
@@ -8914,7 +8960,7 @@ function scanGeminiHistory(startDate, onProgress) {
8914
8960
  const sessionId = chatFile.replace(/\.json$/, "");
8915
8961
  let raw;
8916
8962
  try {
8917
- raw = fs13.readFileSync(path16.join(chatsDir, chatFile), "utf-8");
8963
+ raw = fs14.readFileSync(path17.join(chatsDir, chatFile), "utf-8");
8918
8964
  } catch {
8919
8965
  continue;
8920
8966
  }
@@ -8927,6 +8973,7 @@ function scanGeminiHistory(startDate, onProgress) {
8927
8973
  }
8928
8974
  result.sessions++;
8929
8975
  for (const msg of session.messages ?? []) {
8976
+ onLine?.();
8930
8977
  if (msg.type === "user") {
8931
8978
  const content = msg.content;
8932
8979
  const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
@@ -9062,8 +9109,8 @@ function scanGeminiHistory(startDate, onProgress) {
9062
9109
  }
9063
9110
  return result;
9064
9111
  }
9065
- function scanCodexHistory(startDate, onProgress) {
9066
- const sessionsBase = path16.join(os12.homedir(), ".codex", "sessions");
9112
+ function scanCodexHistory(startDate, onProgress, onLine) {
9113
+ const sessionsBase = path17.join(os12.homedir(), ".codex", "sessions");
9067
9114
  const result = {
9068
9115
  filesScanned: 0,
9069
9116
  sessions: 0,
@@ -9076,32 +9123,32 @@ function scanCodexHistory(startDate, onProgress) {
9076
9123
  firstDate: null,
9077
9124
  lastDate: null
9078
9125
  };
9079
- if (!fs13.existsSync(sessionsBase)) return result;
9126
+ if (!fs14.existsSync(sessionsBase)) return result;
9080
9127
  const jsonlFiles = [];
9081
9128
  try {
9082
- for (const year of fs13.readdirSync(sessionsBase)) {
9083
- const yearPath = path16.join(sessionsBase, year);
9129
+ for (const year of fs14.readdirSync(sessionsBase)) {
9130
+ const yearPath = path17.join(sessionsBase, year);
9084
9131
  try {
9085
- if (!fs13.statSync(yearPath).isDirectory()) continue;
9132
+ if (!fs14.statSync(yearPath).isDirectory()) continue;
9086
9133
  } catch {
9087
9134
  continue;
9088
9135
  }
9089
- for (const month of fs13.readdirSync(yearPath)) {
9090
- const monthPath = path16.join(yearPath, month);
9136
+ for (const month of fs14.readdirSync(yearPath)) {
9137
+ const monthPath = path17.join(yearPath, month);
9091
9138
  try {
9092
- if (!fs13.statSync(monthPath).isDirectory()) continue;
9139
+ if (!fs14.statSync(monthPath).isDirectory()) continue;
9093
9140
  } catch {
9094
9141
  continue;
9095
9142
  }
9096
- for (const day of fs13.readdirSync(monthPath)) {
9097
- const dayPath = path16.join(monthPath, day);
9143
+ for (const day of fs14.readdirSync(monthPath)) {
9144
+ const dayPath = path17.join(monthPath, day);
9098
9145
  try {
9099
- if (!fs13.statSync(dayPath).isDirectory()) continue;
9146
+ if (!fs14.statSync(dayPath).isDirectory()) continue;
9100
9147
  } catch {
9101
9148
  continue;
9102
9149
  }
9103
- for (const file of fs13.readdirSync(dayPath)) {
9104
- if (file.endsWith(".jsonl")) jsonlFiles.push(path16.join(dayPath, file));
9150
+ for (const file of fs14.readdirSync(dayPath)) {
9151
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path17.join(dayPath, file));
9105
9152
  }
9106
9153
  }
9107
9154
  }
@@ -9115,7 +9162,7 @@ function scanCodexHistory(startDate, onProgress) {
9115
9162
  onProgress?.(result.filesScanned);
9116
9163
  let lines;
9117
9164
  try {
9118
- lines = fs13.readFileSync(filePath, "utf-8").split("\n");
9165
+ lines = fs14.readFileSync(filePath, "utf-8").split("\n");
9119
9166
  } catch {
9120
9167
  continue;
9121
9168
  }
@@ -9129,6 +9176,7 @@ function scanCodexHistory(startDate, onProgress) {
9129
9176
  let lastTotalOutput = 0;
9130
9177
  for (const line of lines) {
9131
9178
  if (!line.trim()) continue;
9179
+ onLine?.();
9132
9180
  let entry;
9133
9181
  try {
9134
9182
  entry = JSON.parse(line);
@@ -9333,17 +9381,17 @@ function printRuleGroup(rule, topN, drillDown, previewWidth) {
9333
9381
  }
9334
9382
  }
9335
9383
  function registerScanCommand(program2) {
9336
- program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last 90 days)").option("--days <n>", "Scan last N days of history", "90").option("--top <n>", "Max findings to show per rule (default: 5)", "5").option("--drill-down", "Show all findings with full commands and session IDs").action(async (options) => {
9384
+ program2.command("scan").description("Forecast: scan agent history and show what node9 would catch if installed").option("--all", "Scan all history (default: last 30 days)").option("--days <n>", "Scan last N days of history", "30").option("--top <n>", "Max findings to show per rule (default: 5)", "5").option("--drill-down", "Show all findings with full commands and session IDs").action(async (options) => {
9337
9385
  const drillDown = options.drillDown ?? false;
9338
9386
  const topN = drillDown ? Infinity : Math.max(1, parseInt(options.top, 10) || 5);
9339
9387
  const previewWidth = 70;
9340
9388
  const startDate = options.all ? null : (() => {
9341
9389
  const d = /* @__PURE__ */ new Date();
9342
- d.setDate(d.getDate() - (parseInt(options.days, 10) || 90));
9390
+ d.setDate(d.getDate() - (parseInt(options.days, 10) || 30));
9343
9391
  d.setHours(0, 0, 0, 0);
9344
9392
  return d;
9345
9393
  })();
9346
- const isInstalled = fs13.existsSync(path16.join(os12.homedir(), ".node9", "audit.log"));
9394
+ const isInstalled = fs14.existsSync(path17.join(os12.homedir(), ".node9", "audit.log"));
9347
9395
  console.log("");
9348
9396
  if (!isInstalled) {
9349
9397
  console.log(
@@ -9360,19 +9408,32 @@ function registerScanCommand(program2) {
9360
9408
  console.log("");
9361
9409
  const totalFiles = countScanFiles();
9362
9410
  let filesScanned = 0;
9411
+ let linesScanned = 0;
9412
+ let lastRender = 0;
9363
9413
  const onProgress = (done) => {
9364
9414
  filesScanned = done;
9365
- renderProgressBar(filesScanned, totalFiles);
9415
+ renderProgressBar(filesScanned, totalFiles, linesScanned);
9416
+ lastRender = Date.now();
9366
9417
  };
9367
- renderProgressBar(0, totalFiles);
9368
- const claudeScan = scanClaudeHistory(startDate, onProgress);
9418
+ const onLine = () => {
9419
+ linesScanned++;
9420
+ const now = Date.now();
9421
+ if (now - lastRender >= 80) {
9422
+ lastRender = now;
9423
+ renderProgressBar(filesScanned, totalFiles, linesScanned);
9424
+ }
9425
+ };
9426
+ renderProgressBar(0, totalFiles, 0);
9427
+ const claudeScan = scanClaudeHistory(startDate, onProgress, onLine);
9369
9428
  const geminiScan = scanGeminiHistory(
9370
9429
  startDate,
9371
- (done) => onProgress(claudeScan.filesScanned + done)
9430
+ (done) => onProgress(claudeScan.filesScanned + done),
9431
+ onLine
9372
9432
  );
9373
9433
  const codexScan = scanCodexHistory(
9374
9434
  startDate,
9375
- (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done)
9435
+ (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
9436
+ onLine
9376
9437
  );
9377
9438
  const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
9378
9439
  const summary = buildScanSummary([
@@ -9390,7 +9451,7 @@ function registerScanCommand(program2) {
9390
9451
  );
9391
9452
  return;
9392
9453
  }
9393
- const rangeLabel = options.all ? chalk2.dim("all time") : chalk2.dim(`last ${options.days ?? 90} days`);
9454
+ const rangeLabel = options.all ? chalk2.dim("all time") : chalk2.dim(`last ${options.days ?? 30} days`);
9394
9455
  const dateRange = scan.firstDate && scan.lastDate ? chalk2.dim(` ${fmtTs(scan.firstDate)} \u2013 ${fmtTs(scan.lastDate)}`) : "";
9395
9456
  const breakdownParts = [];
9396
9457
  if (claudeScan.sessions > 0)
@@ -9568,29 +9629,33 @@ function registerScanCommand(program2) {
9568
9629
  console.log(" " + chalk2.dim("\u2192 ") + chalk2.underline("https://node9.ai"));
9569
9630
  }
9570
9631
  console.log("");
9571
- if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
9572
- const internalToken = getInternalToken();
9573
- if (internalToken) {
9574
- try {
9575
- const summary2 = buildScanSummary([
9576
- { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
9577
- { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
9578
- { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
9579
- ]);
9580
- const payload = { status: "complete", summary: summary2 };
9581
- await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
9582
- method: "POST",
9583
- headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
9584
- body: JSON.stringify(payload),
9585
- signal: AbortSignal.timeout(3e3)
9586
- });
9587
- const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
9588
- console.log(" " + chalk2.cyan("\u{1F310} View in browser:") + " " + chalk2.underline(url));
9589
- console.log("");
9590
- openBrowserLocal();
9591
- } catch {
9632
+ const url = `http://${DAEMON_HOST}:${DAEMON_PORT}/`;
9633
+ if (!isTestingMode()) {
9634
+ if (isDaemonRunning()) {
9635
+ const internalToken = getInternalToken();
9636
+ if (internalToken) {
9637
+ try {
9638
+ const summary2 = buildScanSummary([
9639
+ { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
9640
+ { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
9641
+ { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
9642
+ ]);
9643
+ await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/scan/push`, {
9644
+ method: "POST",
9645
+ headers: { "Content-Type": "application/json", "x-node9-internal": internalToken },
9646
+ body: JSON.stringify({ status: "complete", summary: summary2 }),
9647
+ signal: AbortSignal.timeout(3e3)
9648
+ });
9649
+ openBrowserLocal();
9650
+ } catch {
9651
+ }
9592
9652
  }
9593
9653
  }
9654
+ console.log(" " + chalk2.cyan("\u{1F310} View in browser:") + " " + chalk2.underline(url));
9655
+ if (!isDaemonRunning()) {
9656
+ console.log(" " + chalk2.dim(" run node9 daemon --background to activate"));
9657
+ }
9658
+ console.log("");
9594
9659
  }
9595
9660
  });
9596
9661
  }
@@ -9733,8 +9798,8 @@ var init_suggestion_tracker = __esm({
9733
9798
  });
9734
9799
 
9735
9800
  // src/daemon/taint-store.ts
9736
- import fs14 from "fs";
9737
- import path17 from "path";
9801
+ import fs15 from "fs";
9802
+ import path18 from "path";
9738
9803
  var DEFAULT_TTL_MS, TaintStore;
9739
9804
  var init_taint_store = __esm({
9740
9805
  "src/daemon/taint-store.ts"() {
@@ -9803,9 +9868,9 @@ var init_taint_store = __esm({
9803
9868
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
9804
9869
  _resolve(filePath) {
9805
9870
  try {
9806
- return fs14.realpathSync.native(path17.resolve(filePath));
9871
+ return fs15.realpathSync.native(path18.resolve(filePath));
9807
9872
  } catch {
9808
- return path17.resolve(filePath);
9873
+ return path18.resolve(filePath);
9809
9874
  }
9810
9875
  }
9811
9876
  };
@@ -9922,15 +9987,15 @@ var init_session_history = __esm({
9922
9987
 
9923
9988
  // src/daemon/state.ts
9924
9989
  import net2 from "net";
9925
- import fs15 from "fs";
9926
- import path18 from "path";
9990
+ import fs16 from "fs";
9991
+ import path19 from "path";
9927
9992
  import os13 from "os";
9928
9993
  import { spawn as spawn3 } from "child_process";
9929
9994
  import { randomUUID as randomUUID3 } from "crypto";
9930
9995
  function loadInsightCounts() {
9931
9996
  try {
9932
- if (!fs15.existsSync(INSIGHT_COUNTS_FILE)) return;
9933
- const data = JSON.parse(fs15.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
9997
+ if (!fs16.existsSync(INSIGHT_COUNTS_FILE)) return;
9998
+ const data = JSON.parse(fs16.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
9934
9999
  for (const [tool, count] of Object.entries(data)) {
9935
10000
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
9936
10001
  }
@@ -9973,23 +10038,23 @@ function setCachedScanResult(result) {
9973
10038
  cachedScanTs = Date.now();
9974
10039
  }
9975
10040
  function atomicWriteSync2(filePath, data, options) {
9976
- const dir = path18.dirname(filePath);
9977
- if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
10041
+ const dir = path19.dirname(filePath);
10042
+ if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
9978
10043
  const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
9979
10044
  try {
9980
- fs15.writeFileSync(tmpPath, data, options);
10045
+ fs16.writeFileSync(tmpPath, data, options);
9981
10046
  } catch (err2) {
9982
10047
  try {
9983
- fs15.unlinkSync(tmpPath);
10048
+ fs16.unlinkSync(tmpPath);
9984
10049
  } catch {
9985
10050
  }
9986
10051
  throw err2;
9987
10052
  }
9988
10053
  try {
9989
- fs15.renameSync(tmpPath, filePath);
10054
+ fs16.renameSync(tmpPath, filePath);
9990
10055
  } catch (err2) {
9991
10056
  try {
9992
- fs15.unlinkSync(tmpPath);
10057
+ fs16.unlinkSync(tmpPath);
9993
10058
  } catch {
9994
10059
  }
9995
10060
  throw err2;
@@ -10013,16 +10078,16 @@ function appendAuditLog(data) {
10013
10078
  decision: data.decision,
10014
10079
  source: "daemon"
10015
10080
  };
10016
- const dir = path18.dirname(AUDIT_LOG_FILE);
10017
- if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
10018
- fs15.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10081
+ const dir = path19.dirname(AUDIT_LOG_FILE);
10082
+ if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
10083
+ fs16.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10019
10084
  } catch {
10020
10085
  }
10021
10086
  }
10022
10087
  function getAuditHistory(limit = 20) {
10023
10088
  try {
10024
- if (!fs15.existsSync(AUDIT_LOG_FILE)) return [];
10025
- const lines = fs15.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
10089
+ if (!fs16.existsSync(AUDIT_LOG_FILE)) return [];
10090
+ const lines = fs16.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
10026
10091
  if (lines.length === 1 && lines[0] === "") return [];
10027
10092
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
10028
10093
  } catch {
@@ -10031,19 +10096,19 @@ function getAuditHistory(limit = 20) {
10031
10096
  }
10032
10097
  function getOrgName() {
10033
10098
  try {
10034
- if (fs15.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
10099
+ if (fs16.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
10035
10100
  } catch {
10036
10101
  }
10037
10102
  return null;
10038
10103
  }
10039
10104
  function hasStoredSlackKey() {
10040
- return fs15.existsSync(CREDENTIALS_FILE);
10105
+ return fs16.existsSync(CREDENTIALS_FILE);
10041
10106
  }
10042
10107
  function writeGlobalSetting(key, value) {
10043
10108
  let config = {};
10044
10109
  try {
10045
- if (fs15.existsSync(GLOBAL_CONFIG_FILE)) {
10046
- config = JSON.parse(fs15.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
10110
+ if (fs16.existsSync(GLOBAL_CONFIG_FILE)) {
10111
+ config = JSON.parse(fs16.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
10047
10112
  }
10048
10113
  } catch {
10049
10114
  }
@@ -10055,8 +10120,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
10055
10120
  try {
10056
10121
  let trust = { entries: [] };
10057
10122
  try {
10058
- if (fs15.existsSync(TRUST_FILE2))
10059
- trust = JSON.parse(fs15.readFileSync(TRUST_FILE2, "utf-8"));
10123
+ if (fs16.existsSync(TRUST_FILE2))
10124
+ trust = JSON.parse(fs16.readFileSync(TRUST_FILE2, "utf-8"));
10060
10125
  } catch {
10061
10126
  }
10062
10127
  trust.entries = trust.entries.filter(
@@ -10073,8 +10138,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
10073
10138
  }
10074
10139
  function readPersistentDecisions() {
10075
10140
  try {
10076
- if (fs15.existsSync(DECISIONS_FILE)) {
10077
- return JSON.parse(fs15.readFileSync(DECISIONS_FILE, "utf-8"));
10141
+ if (fs16.existsSync(DECISIONS_FILE)) {
10142
+ return JSON.parse(fs16.readFileSync(DECISIONS_FILE, "utf-8"));
10078
10143
  }
10079
10144
  } catch {
10080
10145
  }
@@ -10111,7 +10176,7 @@ function estimateToolCost(tool, args) {
10111
10176
  const filePath = a.file_path ?? a.path;
10112
10177
  if (filePath) {
10113
10178
  try {
10114
- const bytes = fs15.statSync(filePath).size;
10179
+ const bytes = fs16.statSync(filePath).size;
10115
10180
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
10116
10181
  } catch {
10117
10182
  }
@@ -10169,7 +10234,7 @@ function abandonPending() {
10169
10234
  });
10170
10235
  if (autoStarted) {
10171
10236
  try {
10172
- fs15.unlinkSync(DAEMON_PID_FILE);
10237
+ fs16.unlinkSync(DAEMON_PID_FILE);
10173
10238
  } catch {
10174
10239
  }
10175
10240
  setTimeout(() => {
@@ -10180,7 +10245,7 @@ function abandonPending() {
10180
10245
  }
10181
10246
  function startActivitySocket() {
10182
10247
  try {
10183
- fs15.unlinkSync(ACTIVITY_SOCKET_PATH2);
10248
+ fs16.unlinkSync(ACTIVITY_SOCKET_PATH2);
10184
10249
  } catch {
10185
10250
  }
10186
10251
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -10262,7 +10327,7 @@ function startActivitySocket() {
10262
10327
  unixServer.listen(ACTIVITY_SOCKET_PATH2);
10263
10328
  process.on("exit", () => {
10264
10329
  try {
10265
- fs15.unlinkSync(ACTIVITY_SOCKET_PATH2);
10330
+ fs16.unlinkSync(ACTIVITY_SOCKET_PATH2);
10266
10331
  } catch {
10267
10332
  }
10268
10333
  });
@@ -10277,13 +10342,13 @@ var init_state2 = __esm({
10277
10342
  init_session_counters();
10278
10343
  init_session_history();
10279
10344
  homeDir = os13.homedir();
10280
- DAEMON_PID_FILE = path18.join(homeDir, ".node9", "daemon.pid");
10281
- DECISIONS_FILE = path18.join(homeDir, ".node9", "decisions.json");
10282
- AUDIT_LOG_FILE = path18.join(homeDir, ".node9", "audit.log");
10283
- TRUST_FILE2 = path18.join(homeDir, ".node9", "trust.json");
10284
- GLOBAL_CONFIG_FILE = path18.join(homeDir, ".node9", "config.json");
10285
- CREDENTIALS_FILE = path18.join(homeDir, ".node9", "credentials.json");
10286
- INSIGHT_COUNTS_FILE = path18.join(homeDir, ".node9", "insight-counts.json");
10345
+ DAEMON_PID_FILE = path19.join(homeDir, ".node9", "daemon.pid");
10346
+ DECISIONS_FILE = path19.join(homeDir, ".node9", "decisions.json");
10347
+ AUDIT_LOG_FILE = path19.join(homeDir, ".node9", "audit.log");
10348
+ TRUST_FILE2 = path19.join(homeDir, ".node9", "trust.json");
10349
+ GLOBAL_CONFIG_FILE = path19.join(homeDir, ".node9", "config.json");
10350
+ CREDENTIALS_FILE = path19.join(homeDir, ".node9", "credentials.json");
10351
+ INSIGHT_COUNTS_FILE = path19.join(homeDir, ".node9", "insight-counts.json");
10287
10352
  pending = /* @__PURE__ */ new Map();
10288
10353
  sseClients = /* @__PURE__ */ new Set();
10289
10354
  suggestionTracker = new SuggestionTracker(3);
@@ -10301,7 +10366,7 @@ var init_state2 = __esm({
10301
10366
  "2h": 2 * 60 * 6e4
10302
10367
  };
10303
10368
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
10304
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path18.join(os13.tmpdir(), "node9-activity.sock");
10369
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path19.join(os13.tmpdir(), "node9-activity.sock");
10305
10370
  ACTIVITY_RING_SIZE = 100;
10306
10371
  activityRing = [];
10307
10372
  LARGE_RESPONSE_RING_SIZE = 20;
@@ -10328,14 +10393,14 @@ var init_state2 = __esm({
10328
10393
  });
10329
10394
 
10330
10395
  // src/config/patch.ts
10331
- import fs16 from "fs";
10332
- import path19 from "path";
10396
+ import fs17 from "fs";
10397
+ import path20 from "path";
10333
10398
  import os14 from "os";
10334
10399
  function patchConfig(configPath, patch) {
10335
10400
  let config = {};
10336
10401
  try {
10337
- if (fs16.existsSync(configPath)) {
10338
- config = JSON.parse(fs16.readFileSync(configPath, "utf8"));
10402
+ if (fs17.existsSync(configPath)) {
10403
+ config = JSON.parse(fs17.readFileSync(configPath, "utf8"));
10339
10404
  }
10340
10405
  } catch {
10341
10406
  throw new Error(`Cannot read config at ${configPath} \u2014 file may be corrupted`);
@@ -10354,23 +10419,23 @@ function patchConfig(configPath, patch) {
10354
10419
  ignored.push(patch.toolName);
10355
10420
  }
10356
10421
  }
10357
- const dir = path19.dirname(configPath);
10358
- fs16.mkdirSync(dir, { recursive: true });
10422
+ const dir = path20.dirname(configPath);
10423
+ fs17.mkdirSync(dir, { recursive: true });
10359
10424
  const tmp = configPath + ".node9-tmp";
10360
10425
  try {
10361
- fs16.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
10426
+ fs17.writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
10362
10427
  } catch (err2) {
10363
10428
  try {
10364
- fs16.unlinkSync(tmp);
10429
+ fs17.unlinkSync(tmp);
10365
10430
  } catch {
10366
10431
  }
10367
10432
  throw err2;
10368
10433
  }
10369
10434
  try {
10370
- fs16.renameSync(tmp, configPath);
10435
+ fs17.renameSync(tmp, configPath);
10371
10436
  } catch (err2) {
10372
10437
  try {
10373
- fs16.unlinkSync(tmp);
10438
+ fs17.unlinkSync(tmp);
10374
10439
  } catch {
10375
10440
  }
10376
10441
  throw err2;
@@ -10380,13 +10445,13 @@ var GLOBAL_CONFIG_PATH;
10380
10445
  var init_patch = __esm({
10381
10446
  "src/config/patch.ts"() {
10382
10447
  "use strict";
10383
- GLOBAL_CONFIG_PATH = path19.join(os14.homedir(), ".node9", "config.json");
10448
+ GLOBAL_CONFIG_PATH = path20.join(os14.homedir(), ".node9", "config.json");
10384
10449
  }
10385
10450
  });
10386
10451
 
10387
10452
  // src/costSync.ts
10388
- import fs17 from "fs";
10389
- import path20 from "path";
10453
+ import fs18 from "fs";
10454
+ import path21 from "path";
10390
10455
  import os15 from "os";
10391
10456
  function normalizeModel(raw) {
10392
10457
  return raw.replace(/-\d{8}$/, "");
@@ -10403,7 +10468,7 @@ function pricingFor(model) {
10403
10468
  function parseJSONLFile(filePath) {
10404
10469
  let content;
10405
10470
  try {
10406
- content = fs17.readFileSync(filePath, "utf8");
10471
+ content = fs18.readFileSync(filePath, "utf8");
10407
10472
  } catch {
10408
10473
  return /* @__PURE__ */ new Map();
10409
10474
  }
@@ -10455,30 +10520,30 @@ function parseJSONLFile(filePath) {
10455
10520
  return daily;
10456
10521
  }
10457
10522
  function collectEntries() {
10458
- const projectsDir = path20.join(os15.homedir(), ".claude", "projects");
10459
- if (!fs17.existsSync(projectsDir)) return [];
10523
+ const projectsDir = path21.join(os15.homedir(), ".claude", "projects");
10524
+ if (!fs18.existsSync(projectsDir)) return [];
10460
10525
  const combined = /* @__PURE__ */ new Map();
10461
10526
  let dirs;
10462
10527
  try {
10463
- dirs = fs17.readdirSync(projectsDir);
10528
+ dirs = fs18.readdirSync(projectsDir);
10464
10529
  } catch {
10465
10530
  return [];
10466
10531
  }
10467
10532
  for (const dir of dirs) {
10468
- const dirPath = path20.join(projectsDir, dir);
10533
+ const dirPath = path21.join(projectsDir, dir);
10469
10534
  try {
10470
- if (!fs17.statSync(dirPath).isDirectory()) continue;
10535
+ if (!fs18.statSync(dirPath).isDirectory()) continue;
10471
10536
  } catch {
10472
10537
  continue;
10473
10538
  }
10474
10539
  let files;
10475
10540
  try {
10476
- files = fs17.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
10541
+ files = fs18.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
10477
10542
  } catch {
10478
10543
  continue;
10479
10544
  }
10480
10545
  for (const file of files) {
10481
- const entries = parseJSONLFile(path20.join(dirPath, file));
10546
+ const entries = parseJSONLFile(path21.join(dirPath, file));
10482
10547
  for (const [key, e] of entries) {
10483
10548
  const prev = combined.get(key);
10484
10549
  if (prev) {
@@ -10514,11 +10579,11 @@ async function syncCost() {
10514
10579
  signal: AbortSignal.timeout(15e3)
10515
10580
  });
10516
10581
  if (!res.ok) {
10517
- fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
10582
+ fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
10518
10583
  `);
10519
10584
  }
10520
10585
  } catch (err2) {
10521
- fs17.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
10586
+ fs18.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
10522
10587
  `);
10523
10588
  }
10524
10589
  }
@@ -10551,10 +10616,10 @@ var init_costSync = __esm({
10551
10616
  });
10552
10617
 
10553
10618
  // src/daemon/sync.ts
10554
- import fs18 from "fs";
10619
+ import fs19 from "fs";
10555
10620
  import https from "https";
10556
10621
  import os16 from "os";
10557
- import path21 from "path";
10622
+ import path22 from "path";
10558
10623
  function readCredentials() {
10559
10624
  if (process.env.NODE9_API_KEY) {
10560
10625
  return {
@@ -10563,8 +10628,8 @@ function readCredentials() {
10563
10628
  };
10564
10629
  }
10565
10630
  try {
10566
- const credPath = path21.join(os16.homedir(), ".node9", "credentials.json");
10567
- const creds = JSON.parse(fs18.readFileSync(credPath, "utf-8"));
10631
+ const credPath = path22.join(os16.homedir(), ".node9", "credentials.json");
10632
+ const creds = JSON.parse(fs19.readFileSync(credPath, "utf-8"));
10568
10633
  const profileName = process.env.NODE9_PROFILE ?? "default";
10569
10634
  const profile = creds[profileName];
10570
10635
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -10626,9 +10691,9 @@ async function syncOnce() {
10626
10691
  try {
10627
10692
  const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
10628
10693
  const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
10629
- const dir = path21.dirname(rulesCacheFile());
10630
- if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
10631
- fs18.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10694
+ const dir = path22.dirname(rulesCacheFile());
10695
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
10696
+ fs19.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10632
10697
  } catch {
10633
10698
  }
10634
10699
  }
@@ -10640,9 +10705,9 @@ async function runCloudSync() {
10640
10705
  try {
10641
10706
  const rules = await fetchCloudRules(creds.apiKey, creds.apiUrl);
10642
10707
  const cache = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), rules };
10643
- const dir = path21.dirname(rulesCacheFile());
10644
- if (!fs18.existsSync(dir)) fs18.mkdirSync(dir, { recursive: true });
10645
- fs18.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10708
+ const dir = path22.dirname(rulesCacheFile());
10709
+ if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
10710
+ fs19.writeFileSync(rulesCacheFile(), JSON.stringify(cache, null, 2) + "\n", "utf-8");
10646
10711
  return { ok: true, rules: rules.length, fetchedAt: cache.fetchedAt };
10647
10712
  } catch (err2) {
10648
10713
  return { ok: false, reason: err2 instanceof Error ? err2.message : String(err2) };
@@ -10650,7 +10715,7 @@ async function runCloudSync() {
10650
10715
  }
10651
10716
  function getCloudSyncStatus() {
10652
10717
  try {
10653
- const raw = JSON.parse(fs18.readFileSync(rulesCacheFile(), "utf-8"));
10718
+ const raw = JSON.parse(fs19.readFileSync(rulesCacheFile(), "utf-8"));
10654
10719
  if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
10655
10720
  return { cached: true, rules: raw.rules.length, fetchedAt: raw.fetchedAt };
10656
10721
  } catch {
@@ -10659,7 +10724,7 @@ function getCloudSyncStatus() {
10659
10724
  }
10660
10725
  function getCloudRules() {
10661
10726
  try {
10662
- const raw = JSON.parse(fs18.readFileSync(rulesCacheFile(), "utf-8"));
10727
+ const raw = JSON.parse(fs19.readFileSync(rulesCacheFile(), "utf-8"));
10663
10728
  return Array.isArray(raw.rules) ? raw.rules : null;
10664
10729
  } catch {
10665
10730
  return null;
@@ -10679,7 +10744,7 @@ var init_sync = __esm({
10679
10744
  "src/daemon/sync.ts"() {
10680
10745
  "use strict";
10681
10746
  init_config();
10682
- rulesCacheFile = () => path21.join(os16.homedir(), ".node9", "rules-cache.json");
10747
+ rulesCacheFile = () => path22.join(os16.homedir(), ".node9", "rules-cache.json");
10683
10748
  DEFAULT_API_URL = "https://api.node9.ai/api/v1/policy";
10684
10749
  DEFAULT_INTERVAL_HOURS = 5;
10685
10750
  MIN_INTERVAL_HOURS = 1;
@@ -10687,73 +10752,73 @@ var init_sync = __esm({
10687
10752
  });
10688
10753
 
10689
10754
  // src/daemon/dlp-scanner.ts
10690
- import fs19 from "fs";
10691
- import path22 from "path";
10755
+ import fs20 from "fs";
10756
+ import path23 from "path";
10692
10757
  import os17 from "os";
10693
10758
  function loadIndex() {
10694
10759
  try {
10695
- return JSON.parse(fs19.readFileSync(INDEX_FILE, "utf-8"));
10760
+ return JSON.parse(fs20.readFileSync(INDEX_FILE, "utf-8"));
10696
10761
  } catch {
10697
10762
  return {};
10698
10763
  }
10699
10764
  }
10700
10765
  function saveIndex(index) {
10701
10766
  try {
10702
- fs19.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
10767
+ fs20.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
10703
10768
  } catch {
10704
10769
  }
10705
10770
  }
10706
10771
  function appendAuditEntry(entry) {
10707
10772
  try {
10708
- fs19.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10773
+ fs20.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
10709
10774
  } catch {
10710
10775
  }
10711
10776
  }
10712
10777
  function runDlpScan() {
10713
- if (!fs19.existsSync(PROJECTS_DIR)) return;
10778
+ if (!fs20.existsSync(PROJECTS_DIR)) return;
10714
10779
  const index = loadIndex();
10715
10780
  let updated = false;
10716
10781
  let projDirs;
10717
10782
  try {
10718
- projDirs = fs19.readdirSync(PROJECTS_DIR);
10783
+ projDirs = fs20.readdirSync(PROJECTS_DIR);
10719
10784
  } catch {
10720
10785
  return;
10721
10786
  }
10722
10787
  for (const proj of projDirs) {
10723
- const projPath = path22.join(PROJECTS_DIR, proj);
10788
+ const projPath = path23.join(PROJECTS_DIR, proj);
10724
10789
  try {
10725
- if (!fs19.lstatSync(projPath).isDirectory()) continue;
10726
- const real = fs19.realpathSync(projPath);
10727
- if (!real.startsWith(PROJECTS_DIR + path22.sep) && real !== PROJECTS_DIR) continue;
10790
+ if (!fs20.lstatSync(projPath).isDirectory()) continue;
10791
+ const real = fs20.realpathSync(projPath);
10792
+ if (!real.startsWith(PROJECTS_DIR + path23.sep) && real !== PROJECTS_DIR) continue;
10728
10793
  } catch {
10729
10794
  continue;
10730
10795
  }
10731
10796
  let files;
10732
10797
  try {
10733
- files = fs19.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10798
+ files = fs20.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10734
10799
  } catch {
10735
10800
  continue;
10736
10801
  }
10737
10802
  for (const file of files) {
10738
- const filePath = path22.join(projPath, file);
10803
+ const filePath = path23.join(projPath, file);
10739
10804
  const lastOffset = index[filePath] ?? 0;
10740
10805
  let size;
10741
10806
  try {
10742
- size = fs19.statSync(filePath).size;
10807
+ size = fs20.statSync(filePath).size;
10743
10808
  } catch {
10744
10809
  continue;
10745
10810
  }
10746
10811
  if (size <= lastOffset) continue;
10747
10812
  let fd;
10748
10813
  try {
10749
- fd = fs19.openSync(filePath, "r");
10814
+ fd = fs20.openSync(filePath, "r");
10750
10815
  } catch {
10751
10816
  continue;
10752
10817
  }
10753
10818
  try {
10754
10819
  const chunkSize = size - lastOffset;
10755
10820
  const buf = Buffer.alloc(chunkSize);
10756
- fs19.readSync(fd, buf, 0, chunkSize, lastOffset);
10821
+ fs20.readSync(fd, buf, 0, chunkSize, lastOffset);
10757
10822
  const chunk = buf.toString("utf-8");
10758
10823
  for (const line of chunk.split("\n")) {
10759
10824
  if (!line.trim()) continue;
@@ -10798,7 +10863,7 @@ Run: node9 report --period 30d`
10798
10863
  updated = true;
10799
10864
  } finally {
10800
10865
  try {
10801
- fs19.closeSync(fd);
10866
+ fs20.closeSync(fd);
10802
10867
  } catch {
10803
10868
  }
10804
10869
  }
@@ -10831,23 +10896,23 @@ var init_dlp_scanner = __esm({
10831
10896
  init_dlp();
10832
10897
  init_native();
10833
10898
  init_state2();
10834
- INDEX_FILE = path22.join(os17.homedir(), ".node9", "dlp-index.json");
10835
- PROJECTS_DIR = path22.join(os17.homedir(), ".claude", "projects");
10899
+ INDEX_FILE = path23.join(os17.homedir(), ".node9", "dlp-index.json");
10900
+ PROJECTS_DIR = path23.join(os17.homedir(), ".claude", "projects");
10836
10901
  }
10837
10902
  });
10838
10903
 
10839
10904
  // src/daemon/mcp-tools.ts
10840
- import fs20 from "fs";
10841
- import path23 from "path";
10905
+ import fs21 from "fs";
10906
+ import path24 from "path";
10842
10907
  import os18 from "os";
10843
10908
  function getMcpToolsFile() {
10844
- return path23.join(os18.homedir(), ".node9", "mcp-tools.json");
10909
+ return path24.join(os18.homedir(), ".node9", "mcp-tools.json");
10845
10910
  }
10846
10911
  function readMcpToolsConfig() {
10847
10912
  try {
10848
10913
  const file = getMcpToolsFile();
10849
- if (!fs20.existsSync(file)) return {};
10850
- const raw = fs20.readFileSync(file, "utf-8");
10914
+ if (!fs21.existsSync(file)) return {};
10915
+ const raw = fs21.readFileSync(file, "utf-8");
10851
10916
  return JSON.parse(raw);
10852
10917
  } catch {
10853
10918
  return {};
@@ -10856,11 +10921,11 @@ function readMcpToolsConfig() {
10856
10921
  function writeMcpToolsConfig(config) {
10857
10922
  try {
10858
10923
  const file = getMcpToolsFile();
10859
- const dir = path23.dirname(file);
10860
- if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
10924
+ const dir = path24.dirname(file);
10925
+ if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
10861
10926
  const tmpPath = `${file}.${os18.hostname()}.${process.pid}.tmp`;
10862
- fs20.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
10863
- fs20.renameSync(tmpPath, file);
10927
+ fs21.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
10928
+ fs21.renameSync(tmpPath, file);
10864
10929
  } catch (e) {
10865
10930
  console.error("Failed to write mcp-tools.json", e);
10866
10931
  }
@@ -10907,8 +10972,8 @@ var init_mcp_tools = __esm({
10907
10972
 
10908
10973
  // src/daemon/server.ts
10909
10974
  import http from "http";
10910
- import fs21 from "fs";
10911
- import path24 from "path";
10975
+ import fs22 from "fs";
10976
+ import path25 from "path";
10912
10977
  import os19 from "os";
10913
10978
  import { randomUUID as randomUUID4 } from "crypto";
10914
10979
  import { spawnSync as spawnSync2 } from "child_process";
@@ -10932,7 +10997,7 @@ function startDaemon() {
10932
10997
  idleTimer = setTimeout(() => {
10933
10998
  if (autoStarted) {
10934
10999
  try {
10935
- fs21.unlinkSync(DAEMON_PID_FILE);
11000
+ fs22.unlinkSync(DAEMON_PID_FILE);
10936
11001
  } catch {
10937
11002
  }
10938
11003
  }
@@ -11103,7 +11168,7 @@ data: ${JSON.stringify(item.data)}
11103
11168
  status: "pending"
11104
11169
  });
11105
11170
  }
11106
- const projectCwd = typeof cwd === "string" && path24.isAbsolute(cwd) ? cwd : void 0;
11171
+ const projectCwd = typeof cwd === "string" && path25.isAbsolute(cwd) ? cwd : void 0;
11107
11172
  const projectConfig = getConfig(projectCwd);
11108
11173
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
11109
11174
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -11467,8 +11532,8 @@ data: ${JSON.stringify(item.data)}
11467
11532
  if (!validToken(req)) return res.writeHead(403).end();
11468
11533
  const periodParam = reqUrl.searchParams.get("period") || "7d";
11469
11534
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
11470
- const logPath = path24.join(os19.homedir(), ".node9", "audit.log");
11471
- if (!fs21.existsSync(logPath)) {
11535
+ const logPath = path25.join(os19.homedir(), ".node9", "audit.log");
11536
+ if (!fs22.existsSync(logPath)) {
11472
11537
  res.writeHead(200, { "Content-Type": "application/json" });
11473
11538
  return res.end(
11474
11539
  JSON.stringify({
@@ -11481,7 +11546,7 @@ data: ${JSON.stringify(item.data)}
11481
11546
  );
11482
11547
  }
11483
11548
  try {
11484
- const raw = fs21.readFileSync(logPath, "utf-8");
11549
+ const raw = fs22.readFileSync(logPath, "utf-8");
11485
11550
  const allEntries = raw.split("\n").flatMap((line) => {
11486
11551
  if (!line.trim()) return [];
11487
11552
  try {
@@ -11585,7 +11650,7 @@ data: ${JSON.stringify(item.data)}
11585
11650
  }
11586
11651
  try {
11587
11652
  const d = /* @__PURE__ */ new Date();
11588
- d.setDate(d.getDate() - 90);
11653
+ d.setDate(d.getDate() - 30);
11589
11654
  d.setHours(0, 0, 0, 0);
11590
11655
  const EMPTY_SCAN = {
11591
11656
  filesScanned: 0,
@@ -11661,8 +11726,8 @@ data: ${JSON.stringify(item.data)}
11661
11726
  const body = await readBody(req);
11662
11727
  const data = body ? JSON.parse(body) : {};
11663
11728
  const configPath = data.configPath ?? GLOBAL_CONFIG_PATH;
11664
- const node9Dir = path24.dirname(GLOBAL_CONFIG_PATH);
11665
- if (!path24.resolve(configPath).startsWith(node9Dir + path24.sep)) {
11729
+ const node9Dir = path25.dirname(GLOBAL_CONFIG_PATH);
11730
+ if (!path25.resolve(configPath).startsWith(node9Dir + path25.sep)) {
11666
11731
  res.writeHead(400, { "Content-Type": "application/json" });
11667
11732
  return res.end(
11668
11733
  JSON.stringify({ error: "configPath must be within the node9 config directory" })
@@ -11776,11 +11841,16 @@ data: ${JSON.stringify(item.data)}
11776
11841
  if (!validToken(req)) return res.writeHead(403).end();
11777
11842
  try {
11778
11843
  const { serverKey, disabledTools } = JSON.parse(await readBody(req));
11779
- if (typeof serverKey !== "string" || !Array.isArray(disabledTools)) {
11844
+ if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(disabledTools)) {
11780
11845
  res.writeHead(400).end();
11781
11846
  return;
11782
11847
  }
11783
11848
  approveServer(serverKey, disabledTools);
11849
+ appendAuditLog({
11850
+ toolName: `mcp-server:${serverKey}`,
11851
+ args: { disabledTools },
11852
+ decision: "allow"
11853
+ });
11784
11854
  broadcast("mcp-tools-updated", { serverKey, disabledTools });
11785
11855
  res.writeHead(200).end();
11786
11856
  return;
@@ -11793,12 +11863,17 @@ data: ${JSON.stringify(item.data)}
11793
11863
  if (req.headers["x-node9-internal"] !== internalToken) return res.writeHead(403).end();
11794
11864
  try {
11795
11865
  const { serverKey, tools } = JSON.parse(await readBody(req));
11796
- if (typeof serverKey !== "string" || !Array.isArray(tools)) {
11866
+ if (typeof serverKey !== "string" || serverKey.length < 1 || serverKey.length > 256 || !/^[\w.-]+$/.test(serverKey) || !Array.isArray(tools)) {
11797
11867
  res.writeHead(400).end();
11798
11868
  return;
11799
11869
  }
11800
11870
  const status = updateServerDiscovery(serverKey, tools);
11801
11871
  if (status === "new" || status === "drift") {
11872
+ appendAuditLog({
11873
+ toolName: `mcp-server:${serverKey}`,
11874
+ args: { toolCount: tools.length, status },
11875
+ decision: "mcp-discovered"
11876
+ });
11802
11877
  const id = randomUUID4();
11803
11878
  const entry = {
11804
11879
  id,
@@ -11867,6 +11942,11 @@ data: ${JSON.stringify(item.data)}
11867
11942
  }
11868
11943
  clearTimeout(entry.timer);
11869
11944
  approveServer(serverKey, disabledTools);
11945
+ appendAuditLog({
11946
+ toolName: `mcp-server:${serverKey}`,
11947
+ args: { disabledTools },
11948
+ decision: "allow"
11949
+ });
11870
11950
  pending.delete(id);
11871
11951
  broadcast("remove", { id, decision: "allow" });
11872
11952
  res.writeHead(200).end();
@@ -11889,14 +11969,14 @@ data: ${JSON.stringify(item.data)}
11889
11969
  server.on("error", (e) => {
11890
11970
  if (e.code === "EADDRINUSE") {
11891
11971
  try {
11892
- if (fs21.existsSync(DAEMON_PID_FILE)) {
11893
- const { pid } = JSON.parse(fs21.readFileSync(DAEMON_PID_FILE, "utf-8"));
11972
+ if (fs22.existsSync(DAEMON_PID_FILE)) {
11973
+ const { pid } = JSON.parse(fs22.readFileSync(DAEMON_PID_FILE, "utf-8"));
11894
11974
  process.kill(pid, 0);
11895
11975
  return process.exit(0);
11896
11976
  }
11897
11977
  } catch {
11898
11978
  try {
11899
- fs21.unlinkSync(DAEMON_PID_FILE);
11979
+ fs22.unlinkSync(DAEMON_PID_FILE);
11900
11980
  } catch {
11901
11981
  }
11902
11982
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -11975,15 +12055,15 @@ var init_server = __esm({
11975
12055
  });
11976
12056
 
11977
12057
  // src/daemon/service.ts
11978
- import fs22 from "fs";
11979
- import path25 from "path";
12058
+ import fs23 from "fs";
12059
+ import path26 from "path";
11980
12060
  import os20 from "os";
11981
12061
  import { spawnSync as spawnSync3, execFileSync } from "child_process";
11982
12062
  function resolveNode9Binary() {
11983
12063
  try {
11984
12064
  const script = process.argv[1];
11985
- if (typeof script === "string" && path25.isAbsolute(script) && fs22.existsSync(script)) {
11986
- return fs22.realpathSync(script);
12065
+ if (typeof script === "string" && path26.isAbsolute(script) && fs23.existsSync(script)) {
12066
+ return fs23.realpathSync(script);
11987
12067
  }
11988
12068
  } catch {
11989
12069
  }
@@ -12001,11 +12081,11 @@ function xmlEscape(s) {
12001
12081
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
12002
12082
  }
12003
12083
  function launchdPlist(binaryPath) {
12004
- const logDir = path25.join(os20.homedir(), ".node9");
12084
+ const logDir = path26.join(os20.homedir(), ".node9");
12005
12085
  const nodePath = xmlEscape(process.execPath);
12006
12086
  const scriptPath = xmlEscape(binaryPath);
12007
- const outLog = xmlEscape(path25.join(logDir, "daemon.log"));
12008
- const errLog = xmlEscape(path25.join(logDir, "daemon-error.log"));
12087
+ const outLog = xmlEscape(path26.join(logDir, "daemon.log"));
12088
+ const errLog = xmlEscape(path26.join(logDir, "daemon-error.log"));
12009
12089
  return `<?xml version="1.0" encoding="UTF-8"?>
12010
12090
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
12011
12091
  <plist version="1.0">
@@ -12040,9 +12120,9 @@ function launchdPlist(binaryPath) {
12040
12120
  `;
12041
12121
  }
12042
12122
  function installLaunchd(binaryPath) {
12043
- const dir = path25.dirname(LAUNCHD_PLIST);
12044
- if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
12045
- fs22.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
12123
+ const dir = path26.dirname(LAUNCHD_PLIST);
12124
+ if (!fs23.existsSync(dir)) fs23.mkdirSync(dir, { recursive: true });
12125
+ fs23.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
12046
12126
  spawnSync3("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
12047
12127
  const r = spawnSync3("launchctl", ["load", "-w", LAUNCHD_PLIST], {
12048
12128
  encoding: "utf8",
@@ -12053,13 +12133,13 @@ function installLaunchd(binaryPath) {
12053
12133
  }
12054
12134
  }
12055
12135
  function uninstallLaunchd() {
12056
- if (fs22.existsSync(LAUNCHD_PLIST)) {
12136
+ if (fs23.existsSync(LAUNCHD_PLIST)) {
12057
12137
  spawnSync3("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
12058
- fs22.unlinkSync(LAUNCHD_PLIST);
12138
+ fs23.unlinkSync(LAUNCHD_PLIST);
12059
12139
  }
12060
12140
  }
12061
12141
  function isLaunchdInstalled() {
12062
- return fs22.existsSync(LAUNCHD_PLIST);
12142
+ return fs23.existsSync(LAUNCHD_PLIST);
12063
12143
  }
12064
12144
  function systemdUnit(binaryPath) {
12065
12145
  return `[Unit]
@@ -12079,10 +12159,10 @@ WantedBy=default.target
12079
12159
  `;
12080
12160
  }
12081
12161
  function installSystemd(binaryPath) {
12082
- if (!fs22.existsSync(SYSTEMD_UNIT_DIR)) {
12083
- fs22.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
12162
+ if (!fs23.existsSync(SYSTEMD_UNIT_DIR)) {
12163
+ fs23.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
12084
12164
  }
12085
- fs22.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
12165
+ fs23.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
12086
12166
  try {
12087
12167
  execFileSync("loginctl", ["enable-linger", os20.userInfo().username], { timeout: 3e3 });
12088
12168
  } catch {
@@ -12104,23 +12184,23 @@ function installSystemd(binaryPath) {
12104
12184
  }
12105
12185
  }
12106
12186
  function uninstallSystemd() {
12107
- if (fs22.existsSync(SYSTEMD_UNIT)) {
12187
+ if (fs23.existsSync(SYSTEMD_UNIT)) {
12108
12188
  spawnSync3("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
12109
12189
  encoding: "utf8",
12110
12190
  timeout: 5e3
12111
12191
  });
12112
12192
  spawnSync3("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
12113
- fs22.unlinkSync(SYSTEMD_UNIT);
12193
+ fs23.unlinkSync(SYSTEMD_UNIT);
12114
12194
  }
12115
12195
  }
12116
12196
  function isSystemdInstalled() {
12117
- return fs22.existsSync(SYSTEMD_UNIT);
12197
+ return fs23.existsSync(SYSTEMD_UNIT);
12118
12198
  }
12119
12199
  function stopRunningDaemon() {
12120
- const pidFile = path25.join(os20.homedir(), ".node9", "daemon.pid");
12121
- if (!fs22.existsSync(pidFile)) return;
12200
+ const pidFile = path26.join(os20.homedir(), ".node9", "daemon.pid");
12201
+ if (!fs23.existsSync(pidFile)) return;
12122
12202
  try {
12123
- const data = JSON.parse(fs22.readFileSync(pidFile, "utf-8"));
12203
+ const data = JSON.parse(fs23.readFileSync(pidFile, "utf-8"));
12124
12204
  const pid = data.pid;
12125
12205
  const MAX_PID2 = 4194304;
12126
12206
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -12140,7 +12220,7 @@ function stopRunningDaemon() {
12140
12220
  }
12141
12221
  }
12142
12222
  try {
12143
- fs22.unlinkSync(pidFile);
12223
+ fs23.unlinkSync(pidFile);
12144
12224
  } catch {
12145
12225
  }
12146
12226
  } catch {
@@ -12215,20 +12295,20 @@ var init_service = __esm({
12215
12295
  "src/daemon/service.ts"() {
12216
12296
  "use strict";
12217
12297
  LAUNCHD_LABEL = "ai.node9.daemon";
12218
- LAUNCHD_PLIST = path25.join(os20.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
12219
- SYSTEMD_UNIT_DIR = path25.join(os20.homedir(), ".config", "systemd", "user");
12220
- SYSTEMD_UNIT = path25.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
12298
+ LAUNCHD_PLIST = path26.join(os20.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
12299
+ SYSTEMD_UNIT_DIR = path26.join(os20.homedir(), ".config", "systemd", "user");
12300
+ SYSTEMD_UNIT = path26.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
12221
12301
  }
12222
12302
  });
12223
12303
 
12224
12304
  // src/daemon/index.ts
12225
- import fs23 from "fs";
12305
+ import fs24 from "fs";
12226
12306
  import chalk4 from "chalk";
12227
12307
  import { spawnSync as spawnSync4 } from "child_process";
12228
12308
  function stopDaemon() {
12229
- if (!fs23.existsSync(DAEMON_PID_FILE)) return console.log(chalk4.yellow("Not running."));
12309
+ if (!fs24.existsSync(DAEMON_PID_FILE)) return console.log(chalk4.yellow("Not running."));
12230
12310
  try {
12231
- const data = JSON.parse(fs23.readFileSync(DAEMON_PID_FILE, "utf-8"));
12311
+ const data = JSON.parse(fs24.readFileSync(DAEMON_PID_FILE, "utf-8"));
12232
12312
  const pid = data.pid;
12233
12313
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
12234
12314
  console.log(chalk4.gray("Cleaned up invalid PID file."));
@@ -12240,7 +12320,7 @@ function stopDaemon() {
12240
12320
  console.log(chalk4.gray("Cleaned up stale PID file."));
12241
12321
  } finally {
12242
12322
  try {
12243
- fs23.unlinkSync(DAEMON_PID_FILE);
12323
+ fs24.unlinkSync(DAEMON_PID_FILE);
12244
12324
  } catch {
12245
12325
  }
12246
12326
  }
@@ -12249,9 +12329,9 @@ function daemonStatus() {
12249
12329
  const serviceInstalled = isDaemonServiceInstalled();
12250
12330
  const serviceLabel = serviceInstalled ? chalk4.green("installed (starts on login)") : chalk4.yellow("not installed \u2014 run: node9 daemon install");
12251
12331
  let processStatus;
12252
- if (fs23.existsSync(DAEMON_PID_FILE)) {
12332
+ if (fs24.existsSync(DAEMON_PID_FILE)) {
12253
12333
  try {
12254
- const data = JSON.parse(fs23.readFileSync(DAEMON_PID_FILE, "utf-8"));
12334
+ const data = JSON.parse(fs24.readFileSync(DAEMON_PID_FILE, "utf-8"));
12255
12335
  const pid = data.pid;
12256
12336
  const port = data.port;
12257
12337
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -12301,9 +12381,9 @@ __export(tail_exports, {
12301
12381
  });
12302
12382
  import http2 from "http";
12303
12383
  import chalk25 from "chalk";
12304
- import fs38 from "fs";
12384
+ import fs39 from "fs";
12305
12385
  import os35 from "os";
12306
- import path41 from "path";
12386
+ import path42 from "path";
12307
12387
  import readline5 from "readline";
12308
12388
  import { spawn as spawn10, execSync as execSync3 } from "child_process";
12309
12389
  function getIcon(tool) {
@@ -12321,20 +12401,20 @@ function getModelContextLimit(model) {
12321
12401
  return 2e5;
12322
12402
  }
12323
12403
  function readSessionUsage() {
12324
- const projectsDir = path41.join(os35.homedir(), ".claude", "projects");
12325
- if (!fs38.existsSync(projectsDir)) return null;
12404
+ const projectsDir = path42.join(os35.homedir(), ".claude", "projects");
12405
+ if (!fs39.existsSync(projectsDir)) return null;
12326
12406
  let latestFile = null;
12327
12407
  let latestMtime = 0;
12328
12408
  try {
12329
- for (const dir of fs38.readdirSync(projectsDir)) {
12330
- const dirPath = path41.join(projectsDir, dir);
12409
+ for (const dir of fs39.readdirSync(projectsDir)) {
12410
+ const dirPath = path42.join(projectsDir, dir);
12331
12411
  try {
12332
- if (!fs38.statSync(dirPath).isDirectory()) continue;
12333
- for (const file of fs38.readdirSync(dirPath)) {
12412
+ if (!fs39.statSync(dirPath).isDirectory()) continue;
12413
+ for (const file of fs39.readdirSync(dirPath)) {
12334
12414
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
12335
- const filePath = path41.join(dirPath, file);
12415
+ const filePath = path42.join(dirPath, file);
12336
12416
  try {
12337
- const mtime = fs38.statSync(filePath).mtimeMs;
12417
+ const mtime = fs39.statSync(filePath).mtimeMs;
12338
12418
  if (mtime > latestMtime) {
12339
12419
  latestMtime = mtime;
12340
12420
  latestFile = filePath;
@@ -12349,7 +12429,7 @@ function readSessionUsage() {
12349
12429
  }
12350
12430
  if (!latestFile) return null;
12351
12431
  try {
12352
- const lines = fs38.readFileSync(latestFile, "utf-8").split("\n");
12432
+ const lines = fs39.readFileSync(latestFile, "utf-8").split("\n");
12353
12433
  let lastModel = "";
12354
12434
  let lastInput = 0;
12355
12435
  let lastOutput = 0;
@@ -12438,9 +12518,9 @@ function renderPending(activity) {
12438
12518
  }
12439
12519
  async function ensureDaemon() {
12440
12520
  let pidPort = null;
12441
- if (fs38.existsSync(PID_FILE)) {
12521
+ if (fs39.existsSync(PID_FILE)) {
12442
12522
  try {
12443
- const { port } = JSON.parse(fs38.readFileSync(PID_FILE, "utf-8"));
12523
+ const { port } = JSON.parse(fs39.readFileSync(PID_FILE, "utf-8"));
12444
12524
  pidPort = port;
12445
12525
  } catch {
12446
12526
  console.error(chalk25.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -12593,9 +12673,9 @@ function buildRecoveryCardLines(req) {
12593
12673
  ];
12594
12674
  }
12595
12675
  function readApproversFromDisk() {
12596
- const configPath = path41.join(os35.homedir(), ".node9", "config.json");
12676
+ const configPath = path42.join(os35.homedir(), ".node9", "config.json");
12597
12677
  try {
12598
- const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
12678
+ const raw = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
12599
12679
  const settings = raw.settings ?? {};
12600
12680
  return settings.approvers ?? {};
12601
12681
  } catch {
@@ -12611,15 +12691,15 @@ function approverStatusLine() {
12611
12691
  return `${fmt("native", "native")} ${fmt("browser", "browser")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
12612
12692
  }
12613
12693
  function toggleApprover(channel) {
12614
- const configPath = path41.join(os35.homedir(), ".node9", "config.json");
12694
+ const configPath = path42.join(os35.homedir(), ".node9", "config.json");
12615
12695
  try {
12616
- const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
12696
+ const raw = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
12617
12697
  const settings = raw.settings ?? {};
12618
12698
  const approvers = settings.approvers ?? {};
12619
12699
  approvers[channel] = approvers[channel] === false;
12620
12700
  settings.approvers = approvers;
12621
12701
  raw.settings = settings;
12622
- fs38.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
12702
+ fs39.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
12623
12703
  } catch (err2) {
12624
12704
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
12625
12705
  `);
@@ -12789,8 +12869,8 @@ async function startTail(options = {}) {
12789
12869
  }
12790
12870
  postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
12791
12871
  try {
12792
- fs38.appendFileSync(
12793
- path41.join(os35.homedir(), ".node9", "hook-debug.log"),
12872
+ fs39.appendFileSync(
12873
+ path42.join(os35.homedir(), ".node9", "hook-debug.log"),
12794
12874
  `[tail] POST /decision failed: ${String(err2)}
12795
12875
  `
12796
12876
  );
@@ -12871,9 +12951,9 @@ async function startTail(options = {}) {
12871
12951
  }
12872
12952
  } catch {
12873
12953
  }
12874
- const auditLog = path41.join(os35.homedir(), ".node9", "audit.log");
12954
+ const auditLog = path42.join(os35.homedir(), ".node9", "audit.log");
12875
12955
  try {
12876
- const unackedDlp = fs38.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
12956
+ const unackedDlp = fs39.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
12877
12957
  if (unackedDlp > 0) {
12878
12958
  console.log("");
12879
12959
  console.log(
@@ -13065,7 +13145,7 @@ var init_tail = __esm({
13065
13145
  init_daemon2();
13066
13146
  init_daemon();
13067
13147
  init_core();
13068
- PID_FILE = path41.join(os35.homedir(), ".node9", "daemon.pid");
13148
+ PID_FILE = path42.join(os35.homedir(), ".node9", "daemon.pid");
13069
13149
  ICONS = {
13070
13150
  bash: "\u{1F4BB}",
13071
13151
  shell: "\u{1F4BB}",
@@ -13113,8 +13193,8 @@ __export(hud_exports, {
13113
13193
  main: () => main,
13114
13194
  renderEnvironmentLine: () => renderEnvironmentLine
13115
13195
  });
13116
- import fs39 from "fs";
13117
- import path42 from "path";
13196
+ import fs40 from "fs";
13197
+ import path43 from "path";
13118
13198
  import os36 from "os";
13119
13199
  import http3 from "http";
13120
13200
  async function readStdin() {
@@ -13191,9 +13271,9 @@ function formatTimeLeft(resetsAt) {
13191
13271
  return ` (${m}m left)`;
13192
13272
  }
13193
13273
  function safeReadJson(filePath) {
13194
- if (!fs39.existsSync(filePath)) return null;
13274
+ if (!fs40.existsSync(filePath)) return null;
13195
13275
  try {
13196
- return JSON.parse(fs39.readFileSync(filePath, "utf-8"));
13276
+ return JSON.parse(fs40.readFileSync(filePath, "utf-8"));
13197
13277
  } catch {
13198
13278
  return null;
13199
13279
  }
@@ -13214,12 +13294,12 @@ function countHooksInFile(filePath) {
13214
13294
  return Object.keys(cfg.hooks).length;
13215
13295
  }
13216
13296
  function countRulesInDir(rulesDir) {
13217
- if (!fs39.existsSync(rulesDir)) return 0;
13297
+ if (!fs40.existsSync(rulesDir)) return 0;
13218
13298
  let count = 0;
13219
13299
  try {
13220
- for (const entry of fs39.readdirSync(rulesDir, { withFileTypes: true })) {
13300
+ for (const entry of fs40.readdirSync(rulesDir, { withFileTypes: true })) {
13221
13301
  if (entry.isDirectory()) {
13222
- count += countRulesInDir(path42.join(rulesDir, entry.name));
13302
+ count += countRulesInDir(path43.join(rulesDir, entry.name));
13223
13303
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
13224
13304
  count++;
13225
13305
  }
@@ -13230,46 +13310,46 @@ function countRulesInDir(rulesDir) {
13230
13310
  }
13231
13311
  function isSamePath(a, b) {
13232
13312
  try {
13233
- return path42.resolve(a) === path42.resolve(b);
13313
+ return path43.resolve(a) === path43.resolve(b);
13234
13314
  } catch {
13235
13315
  return false;
13236
13316
  }
13237
13317
  }
13238
13318
  function countConfigs(cwd) {
13239
13319
  const homeDir2 = os36.homedir();
13240
- const claudeDir = path42.join(homeDir2, ".claude");
13320
+ const claudeDir = path43.join(homeDir2, ".claude");
13241
13321
  let claudeMdCount = 0;
13242
13322
  let rulesCount = 0;
13243
13323
  let hooksCount = 0;
13244
13324
  const userMcpServers = /* @__PURE__ */ new Set();
13245
13325
  const projectMcpServers = /* @__PURE__ */ new Set();
13246
- if (fs39.existsSync(path42.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
13247
- rulesCount += countRulesInDir(path42.join(claudeDir, "rules"));
13248
- const userSettings = path42.join(claudeDir, "settings.json");
13326
+ if (fs40.existsSync(path43.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
13327
+ rulesCount += countRulesInDir(path43.join(claudeDir, "rules"));
13328
+ const userSettings = path43.join(claudeDir, "settings.json");
13249
13329
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
13250
13330
  hooksCount += countHooksInFile(userSettings);
13251
- const userClaudeJson = path42.join(homeDir2, ".claude.json");
13331
+ const userClaudeJson = path43.join(homeDir2, ".claude.json");
13252
13332
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
13253
13333
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
13254
13334
  userMcpServers.delete(name);
13255
13335
  }
13256
13336
  if (cwd) {
13257
- if (fs39.existsSync(path42.join(cwd, "CLAUDE.md"))) claudeMdCount++;
13258
- if (fs39.existsSync(path42.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
13259
- const projectClaudeDir = path42.join(cwd, ".claude");
13337
+ if (fs40.existsSync(path43.join(cwd, "CLAUDE.md"))) claudeMdCount++;
13338
+ if (fs40.existsSync(path43.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
13339
+ const projectClaudeDir = path43.join(cwd, ".claude");
13260
13340
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
13261
13341
  if (!overlapsUserScope) {
13262
- if (fs39.existsSync(path42.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
13263
- rulesCount += countRulesInDir(path42.join(projectClaudeDir, "rules"));
13264
- const projSettings = path42.join(projectClaudeDir, "settings.json");
13342
+ if (fs40.existsSync(path43.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
13343
+ rulesCount += countRulesInDir(path43.join(projectClaudeDir, "rules"));
13344
+ const projSettings = path43.join(projectClaudeDir, "settings.json");
13265
13345
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
13266
13346
  hooksCount += countHooksInFile(projSettings);
13267
13347
  }
13268
- if (fs39.existsSync(path42.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
13269
- const localSettings = path42.join(projectClaudeDir, "settings.local.json");
13348
+ if (fs40.existsSync(path43.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
13349
+ const localSettings = path43.join(projectClaudeDir, "settings.local.json");
13270
13350
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
13271
13351
  hooksCount += countHooksInFile(localSettings);
13272
- const mcpJsonServers = getMcpServerNames(path42.join(cwd, ".mcp.json"));
13352
+ const mcpJsonServers = getMcpServerNames(path43.join(cwd, ".mcp.json"));
13273
13353
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
13274
13354
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
13275
13355
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -13302,12 +13382,12 @@ function readActiveShieldsHud() {
13302
13382
  return shieldsCache.value;
13303
13383
  }
13304
13384
  try {
13305
- const shieldsPath = path42.join(os36.homedir(), ".node9", "shields.json");
13306
- if (!fs39.existsSync(shieldsPath)) {
13385
+ const shieldsPath = path43.join(os36.homedir(), ".node9", "shields.json");
13386
+ if (!fs40.existsSync(shieldsPath)) {
13307
13387
  shieldsCache = { value: [], ts: now };
13308
13388
  return [];
13309
13389
  }
13310
- const parsed = JSON.parse(fs39.readFileSync(shieldsPath, "utf-8"));
13390
+ const parsed = JSON.parse(fs40.readFileSync(shieldsPath, "utf-8"));
13311
13391
  if (!Array.isArray(parsed.active)) {
13312
13392
  shieldsCache = { value: [], ts: now };
13313
13393
  return [];
@@ -13409,17 +13489,17 @@ function renderContextLine(stdin) {
13409
13489
  async function main() {
13410
13490
  try {
13411
13491
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
13412
- if (fs39.existsSync(path42.join(os36.homedir(), ".node9", "hud-debug"))) {
13492
+ if (fs40.existsSync(path43.join(os36.homedir(), ".node9", "hud-debug"))) {
13413
13493
  try {
13414
- const logPath = path42.join(os36.homedir(), ".node9", "hud-debug.log");
13494
+ const logPath = path43.join(os36.homedir(), ".node9", "hud-debug.log");
13415
13495
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
13416
13496
  let size = 0;
13417
13497
  try {
13418
- size = fs39.statSync(logPath).size;
13498
+ size = fs40.statSync(logPath).size;
13419
13499
  } catch {
13420
13500
  }
13421
13501
  if (size < MAX_LOG_SIZE) {
13422
- fs39.appendFileSync(
13502
+ fs40.appendFileSync(
13423
13503
  logPath,
13424
13504
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
13425
13505
  );
@@ -13440,11 +13520,11 @@ async function main() {
13440
13520
  try {
13441
13521
  const cwd = stdin.cwd ?? process.cwd();
13442
13522
  for (const configPath of [
13443
- path42.join(cwd, "node9.config.json"),
13444
- path42.join(os36.homedir(), ".node9", "config.json")
13523
+ path43.join(cwd, "node9.config.json"),
13524
+ path43.join(os36.homedir(), ".node9", "config.json")
13445
13525
  ]) {
13446
- if (!fs39.existsSync(configPath)) continue;
13447
- const cfg = JSON.parse(fs39.readFileSync(configPath, "utf-8"));
13526
+ if (!fs40.existsSync(configPath)) continue;
13527
+ const cfg = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
13448
13528
  const hud = cfg.settings?.hud;
13449
13529
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
13450
13530
  }
@@ -14512,8 +14592,8 @@ function getAgentsStatus(homeDir2 = os11.homedir()) {
14512
14592
  // src/cli.ts
14513
14593
  init_daemon2();
14514
14594
  import chalk26 from "chalk";
14515
- import fs40 from "fs";
14516
- import path43 from "path";
14595
+ import fs41 from "fs";
14596
+ import path44 from "path";
14517
14597
  import os37 from "os";
14518
14598
  import { confirm as confirm2 } from "@inquirer/prompts";
14519
14599
 
@@ -14703,19 +14783,19 @@ init_daemon();
14703
14783
  init_config();
14704
14784
  init_policy();
14705
14785
  import chalk6 from "chalk";
14706
- import fs26 from "fs";
14786
+ import fs27 from "fs";
14707
14787
  import { spawn as spawn6 } from "child_process";
14708
- import path28 from "path";
14788
+ import path29 from "path";
14709
14789
  import os23 from "os";
14710
14790
 
14711
14791
  // src/undo.ts
14712
14792
  import { spawnSync as spawnSync5, spawn as spawn5 } from "child_process";
14713
14793
  import crypto3 from "crypto";
14714
- import fs24 from "fs";
14794
+ import fs25 from "fs";
14715
14795
  import net3 from "net";
14716
- import path26 from "path";
14796
+ import path27 from "path";
14717
14797
  import os21 from "os";
14718
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path26.join(os21.tmpdir(), "node9-activity.sock");
14798
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path27.join(os21.tmpdir(), "node9-activity.sock");
14719
14799
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
14720
14800
  try {
14721
14801
  const payload = JSON.stringify({
@@ -14735,22 +14815,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
14735
14815
  } catch {
14736
14816
  }
14737
14817
  }
14738
- var SNAPSHOT_STACK_PATH = path26.join(os21.homedir(), ".node9", "snapshots.json");
14739
- var UNDO_LATEST_PATH = path26.join(os21.homedir(), ".node9", "undo_latest.txt");
14818
+ var SNAPSHOT_STACK_PATH = path27.join(os21.homedir(), ".node9", "snapshots.json");
14819
+ var UNDO_LATEST_PATH = path27.join(os21.homedir(), ".node9", "undo_latest.txt");
14740
14820
  var MAX_SNAPSHOTS = 10;
14741
14821
  var GIT_TIMEOUT = 15e3;
14742
14822
  function readStack() {
14743
14823
  try {
14744
- if (fs24.existsSync(SNAPSHOT_STACK_PATH))
14745
- return JSON.parse(fs24.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
14824
+ if (fs25.existsSync(SNAPSHOT_STACK_PATH))
14825
+ return JSON.parse(fs25.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
14746
14826
  } catch {
14747
14827
  }
14748
14828
  return [];
14749
14829
  }
14750
14830
  function writeStack(stack) {
14751
- const dir = path26.dirname(SNAPSHOT_STACK_PATH);
14752
- if (!fs24.existsSync(dir)) fs24.mkdirSync(dir, { recursive: true });
14753
- fs24.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
14831
+ const dir = path27.dirname(SNAPSHOT_STACK_PATH);
14832
+ if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
14833
+ fs25.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
14754
14834
  }
14755
14835
  function extractFilePath(args) {
14756
14836
  if (!args || typeof args !== "object") return null;
@@ -14770,12 +14850,12 @@ function buildArgsSummary(tool, args) {
14770
14850
  return "";
14771
14851
  }
14772
14852
  function findProjectRoot(filePath) {
14773
- let dir = path26.dirname(filePath);
14853
+ let dir = path27.dirname(filePath);
14774
14854
  while (true) {
14775
- if (fs24.existsSync(path26.join(dir, ".git")) || fs24.existsSync(path26.join(dir, "package.json"))) {
14855
+ if (fs25.existsSync(path27.join(dir, ".git")) || fs25.existsSync(path27.join(dir, "package.json"))) {
14776
14856
  return dir;
14777
14857
  }
14778
- const parent = path26.dirname(dir);
14858
+ const parent = path27.dirname(dir);
14779
14859
  if (parent === dir) return process.cwd();
14780
14860
  dir = parent;
14781
14861
  }
@@ -14783,7 +14863,7 @@ function findProjectRoot(filePath) {
14783
14863
  function normalizeCwdForHash(cwd) {
14784
14864
  let normalized;
14785
14865
  try {
14786
- normalized = fs24.realpathSync(cwd);
14866
+ normalized = fs25.realpathSync(cwd);
14787
14867
  } catch {
14788
14868
  normalized = cwd;
14789
14869
  }
@@ -14793,16 +14873,16 @@ function normalizeCwdForHash(cwd) {
14793
14873
  }
14794
14874
  function getShadowRepoDir(cwd) {
14795
14875
  const hash = crypto3.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
14796
- return path26.join(os21.homedir(), ".node9", "snapshots", hash);
14876
+ return path27.join(os21.homedir(), ".node9", "snapshots", hash);
14797
14877
  }
14798
14878
  function cleanOrphanedIndexFiles(shadowDir) {
14799
14879
  try {
14800
14880
  const cutoff = Date.now() - 6e4;
14801
- for (const f of fs24.readdirSync(shadowDir)) {
14881
+ for (const f of fs25.readdirSync(shadowDir)) {
14802
14882
  if (f.startsWith("index_")) {
14803
- const fp = path26.join(shadowDir, f);
14883
+ const fp = path27.join(shadowDir, f);
14804
14884
  try {
14805
- if (fs24.statSync(fp).mtimeMs < cutoff) fs24.unlinkSync(fp);
14885
+ if (fs25.statSync(fp).mtimeMs < cutoff) fs25.unlinkSync(fp);
14806
14886
  } catch {
14807
14887
  }
14808
14888
  }
@@ -14814,7 +14894,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
14814
14894
  const hardcoded = [".git", ".node9"];
14815
14895
  const lines = [...hardcoded, ...ignorePaths].join("\n");
14816
14896
  try {
14817
- fs24.writeFileSync(path26.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
14897
+ fs25.writeFileSync(path27.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
14818
14898
  } catch {
14819
14899
  }
14820
14900
  }
@@ -14827,25 +14907,25 @@ function ensureShadowRepo(shadowDir, cwd) {
14827
14907
  timeout: 3e3
14828
14908
  });
14829
14909
  if (check.status === 0) {
14830
- const ptPath = path26.join(shadowDir, "project-path.txt");
14910
+ const ptPath = path27.join(shadowDir, "project-path.txt");
14831
14911
  try {
14832
- const stored = fs24.readFileSync(ptPath, "utf8").trim();
14912
+ const stored = fs25.readFileSync(ptPath, "utf8").trim();
14833
14913
  if (stored === normalizedCwd) return true;
14834
14914
  if (process.env.NODE9_DEBUG === "1")
14835
14915
  console.error(
14836
14916
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
14837
14917
  );
14838
- fs24.rmSync(shadowDir, { recursive: true, force: true });
14918
+ fs25.rmSync(shadowDir, { recursive: true, force: true });
14839
14919
  } catch {
14840
14920
  try {
14841
- fs24.writeFileSync(ptPath, normalizedCwd, "utf8");
14921
+ fs25.writeFileSync(ptPath, normalizedCwd, "utf8");
14842
14922
  } catch {
14843
14923
  }
14844
14924
  return true;
14845
14925
  }
14846
14926
  }
14847
14927
  try {
14848
- fs24.mkdirSync(shadowDir, { recursive: true });
14928
+ fs25.mkdirSync(shadowDir, { recursive: true });
14849
14929
  } catch {
14850
14930
  }
14851
14931
  const init = spawnSync5("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -14854,7 +14934,7 @@ function ensureShadowRepo(shadowDir, cwd) {
14854
14934
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
14855
14935
  return false;
14856
14936
  }
14857
- const configFile = path26.join(shadowDir, "config");
14937
+ const configFile = path27.join(shadowDir, "config");
14858
14938
  spawnSync5("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
14859
14939
  timeout: 3e3
14860
14940
  });
@@ -14862,7 +14942,7 @@ function ensureShadowRepo(shadowDir, cwd) {
14862
14942
  timeout: 3e3
14863
14943
  });
14864
14944
  try {
14865
- fs24.writeFileSync(path26.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
14945
+ fs25.writeFileSync(path27.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
14866
14946
  } catch {
14867
14947
  }
14868
14948
  return true;
@@ -14882,12 +14962,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14882
14962
  let indexFile = null;
14883
14963
  try {
14884
14964
  const rawFilePath = extractFilePath(args);
14885
- const absFilePath = rawFilePath && path26.isAbsolute(rawFilePath) ? rawFilePath : null;
14965
+ const absFilePath = rawFilePath && path27.isAbsolute(rawFilePath) ? rawFilePath : null;
14886
14966
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
14887
14967
  const shadowDir = getShadowRepoDir(cwd);
14888
14968
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
14889
14969
  writeShadowExcludes(shadowDir, ignorePaths);
14890
- indexFile = path26.join(shadowDir, `index_${process.pid}_${Date.now()}`);
14970
+ indexFile = path27.join(shadowDir, `index_${process.pid}_${Date.now()}`);
14891
14971
  const shadowEnv = {
14892
14972
  ...process.env,
14893
14973
  GIT_DIR: shadowDir,
@@ -14959,7 +15039,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14959
15039
  writeStack(stack);
14960
15040
  const entry = stack[stack.length - 1];
14961
15041
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
14962
- fs24.writeFileSync(UNDO_LATEST_PATH, commitHash);
15042
+ fs25.writeFileSync(UNDO_LATEST_PATH, commitHash);
14963
15043
  if (shouldGc) {
14964
15044
  spawn5("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
14965
15045
  }
@@ -14970,7 +15050,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14970
15050
  } finally {
14971
15051
  if (indexFile) {
14972
15052
  try {
14973
- fs24.unlinkSync(indexFile);
15053
+ fs25.unlinkSync(indexFile);
14974
15054
  } catch {
14975
15055
  }
14976
15056
  }
@@ -15046,9 +15126,9 @@ function applyUndo(hash, cwd) {
15046
15126
  timeout: GIT_TIMEOUT
15047
15127
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
15048
15128
  for (const file of [...tracked, ...untracked]) {
15049
- const fullPath = path26.join(dir, file);
15050
- if (!snapshotFiles.has(file) && fs24.existsSync(fullPath)) {
15051
- fs24.unlinkSync(fullPath);
15129
+ const fullPath = path27.join(dir, file);
15130
+ if (!snapshotFiles.has(file) && fs25.existsSync(fullPath)) {
15131
+ fs25.unlinkSync(fullPath);
15052
15132
  }
15053
15133
  }
15054
15134
  return true;
@@ -15061,12 +15141,12 @@ function applyUndo(hash, cwd) {
15061
15141
  init_daemon_starter();
15062
15142
 
15063
15143
  // src/skill-pin.ts
15064
- import fs25 from "fs";
15065
- import path27 from "path";
15144
+ import fs26 from "fs";
15145
+ import path28 from "path";
15066
15146
  import os22 from "os";
15067
15147
  import crypto4 from "crypto";
15068
15148
  function getPinsFilePath() {
15069
- return path27.join(os22.homedir(), ".node9", "skill-pins.json");
15149
+ return path28.join(os22.homedir(), ".node9", "skill-pins.json");
15070
15150
  }
15071
15151
  var MAX_FILES = 5e3;
15072
15152
  var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
@@ -15080,18 +15160,18 @@ function walkDir(root) {
15080
15160
  if (out.length >= MAX_FILES) return;
15081
15161
  let entries;
15082
15162
  try {
15083
- entries = fs25.readdirSync(dir, { withFileTypes: true });
15163
+ entries = fs26.readdirSync(dir, { withFileTypes: true });
15084
15164
  } catch {
15085
15165
  return;
15086
15166
  }
15087
15167
  entries.sort((a, b) => a.name.localeCompare(b.name));
15088
15168
  for (const entry of entries) {
15089
15169
  if (out.length >= MAX_FILES) return;
15090
- const full = path27.join(dir, entry.name);
15091
- const rel = relDir ? path27.posix.join(relDir, entry.name) : entry.name;
15170
+ const full = path28.join(dir, entry.name);
15171
+ const rel = relDir ? path28.posix.join(relDir, entry.name) : entry.name;
15092
15172
  let lst;
15093
15173
  try {
15094
- lst = fs25.lstatSync(full);
15174
+ lst = fs26.lstatSync(full);
15095
15175
  } catch {
15096
15176
  continue;
15097
15177
  }
@@ -15103,7 +15183,7 @@ function walkDir(root) {
15103
15183
  if (!lst.isFile()) continue;
15104
15184
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
15105
15185
  try {
15106
- const buf = fs25.readFileSync(full);
15186
+ const buf = fs26.readFileSync(full);
15107
15187
  totalBytes += buf.length;
15108
15188
  out.push({ rel, hash: sha256Bytes(buf) });
15109
15189
  } catch {
@@ -15117,14 +15197,14 @@ function walkDir(root) {
15117
15197
  function hashSkillRoot(absPath) {
15118
15198
  let lst;
15119
15199
  try {
15120
- lst = fs25.lstatSync(absPath);
15200
+ lst = fs26.lstatSync(absPath);
15121
15201
  } catch {
15122
15202
  return { exists: false, contentHash: "", fileCount: 0 };
15123
15203
  }
15124
15204
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
15125
15205
  if (lst.isFile()) {
15126
15206
  try {
15127
- return { exists: true, contentHash: sha256Bytes(fs25.readFileSync(absPath)), fileCount: 1 };
15207
+ return { exists: true, contentHash: sha256Bytes(fs26.readFileSync(absPath)), fileCount: 1 };
15128
15208
  } catch {
15129
15209
  return { exists: false, contentHash: "", fileCount: 0 };
15130
15210
  }
@@ -15142,7 +15222,7 @@ function getRootKey(absPath) {
15142
15222
  function readSkillPinsSafe() {
15143
15223
  const filePath = getPinsFilePath();
15144
15224
  try {
15145
- const raw = fs25.readFileSync(filePath, "utf-8");
15225
+ const raw = fs26.readFileSync(filePath, "utf-8");
15146
15226
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
15147
15227
  const parsed = JSON.parse(raw);
15148
15228
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -15162,10 +15242,10 @@ function readSkillPins() {
15162
15242
  }
15163
15243
  function writeSkillPins(data) {
15164
15244
  const filePath = getPinsFilePath();
15165
- fs25.mkdirSync(path27.dirname(filePath), { recursive: true });
15245
+ fs26.mkdirSync(path28.dirname(filePath), { recursive: true });
15166
15246
  const tmp = `${filePath}.${crypto4.randomBytes(6).toString("hex")}.tmp`;
15167
- fs25.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15168
- fs25.renameSync(tmp, filePath);
15247
+ fs26.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
15248
+ fs26.renameSync(tmp, filePath);
15169
15249
  }
15170
15250
  function removePin(rootKey) {
15171
15251
  const pins = readSkillPins();
@@ -15209,36 +15289,36 @@ function verifyAndPinRoots(roots) {
15209
15289
  return { kind: "verified" };
15210
15290
  }
15211
15291
  function defaultSkillRoots(_cwd) {
15212
- const marketplaces = path27.join(os22.homedir(), ".claude", "plugins", "marketplaces");
15292
+ const marketplaces = path28.join(os22.homedir(), ".claude", "plugins", "marketplaces");
15213
15293
  const roots = [];
15214
15294
  let registries;
15215
15295
  try {
15216
- registries = fs25.readdirSync(marketplaces, { withFileTypes: true });
15296
+ registries = fs26.readdirSync(marketplaces, { withFileTypes: true });
15217
15297
  } catch {
15218
15298
  return [];
15219
15299
  }
15220
15300
  for (const registry of registries) {
15221
15301
  if (!registry.isDirectory()) continue;
15222
- const pluginsDir = path27.join(marketplaces, registry.name, "plugins");
15302
+ const pluginsDir = path28.join(marketplaces, registry.name, "plugins");
15223
15303
  let plugins;
15224
15304
  try {
15225
- plugins = fs25.readdirSync(pluginsDir, { withFileTypes: true });
15305
+ plugins = fs26.readdirSync(pluginsDir, { withFileTypes: true });
15226
15306
  } catch {
15227
15307
  continue;
15228
15308
  }
15229
15309
  for (const plugin of plugins) {
15230
15310
  if (!plugin.isDirectory()) continue;
15231
- roots.push(path27.join(pluginsDir, plugin.name));
15311
+ roots.push(path28.join(pluginsDir, plugin.name));
15232
15312
  }
15233
15313
  }
15234
15314
  return roots;
15235
15315
  }
15236
15316
  function resolveUserSkillRoot(entry, cwd) {
15237
15317
  if (!entry) return null;
15238
- if (entry.startsWith("~/") || entry === "~") return path27.join(os22.homedir(), entry.slice(1));
15239
- if (path27.isAbsolute(entry)) return entry;
15240
- if (!cwd || !path27.isAbsolute(cwd)) return null;
15241
- return path27.join(cwd, entry);
15318
+ if (entry.startsWith("~/") || entry === "~") return path28.join(os22.homedir(), entry.slice(1));
15319
+ if (path28.isAbsolute(entry)) return entry;
15320
+ if (!cwd || !path28.isAbsolute(cwd)) return null;
15321
+ return path28.join(cwd, entry);
15242
15322
  }
15243
15323
 
15244
15324
  // src/cli/commands/check.ts
@@ -15256,9 +15336,9 @@ function registerCheckCommand(program2) {
15256
15336
  } catch (err2) {
15257
15337
  const tempConfig = getConfig();
15258
15338
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
15259
- const logPath = path28.join(os23.homedir(), ".node9", "hook-debug.log");
15339
+ const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15260
15340
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15261
- fs26.appendFileSync(
15341
+ fs27.appendFileSync(
15262
15342
  logPath,
15263
15343
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
15264
15344
  RAW: ${raw}
@@ -15271,11 +15351,11 @@ RAW: ${raw}
15271
15351
  if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
15272
15352
  try {
15273
15353
  const scriptPath = process.argv[1];
15274
- if (typeof scriptPath !== "string" || !path28.isAbsolute(scriptPath))
15354
+ if (typeof scriptPath !== "string" || !path29.isAbsolute(scriptPath))
15275
15355
  throw new Error("node9: argv[1] is not an absolute path");
15276
- const resolvedScript = fs26.realpathSync(scriptPath);
15277
- const packageDist = fs26.realpathSync(path28.resolve(__dirname, "../.."));
15278
- if (!resolvedScript.startsWith(packageDist + path28.sep) && resolvedScript !== packageDist)
15356
+ const resolvedScript = fs27.realpathSync(scriptPath);
15357
+ const packageDist = fs27.realpathSync(path29.resolve(__dirname, "../.."));
15358
+ if (!resolvedScript.startsWith(packageDist + path29.sep) && resolvedScript !== packageDist)
15279
15359
  throw new Error(
15280
15360
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
15281
15361
  );
@@ -15297,10 +15377,10 @@ RAW: ${raw}
15297
15377
  });
15298
15378
  d.unref();
15299
15379
  } catch (spawnErr) {
15300
- const logPath = path28.join(os23.homedir(), ".node9", "hook-debug.log");
15380
+ const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15301
15381
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
15302
15382
  try {
15303
- fs26.appendFileSync(
15383
+ fs27.appendFileSync(
15304
15384
  logPath,
15305
15385
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
15306
15386
  `
@@ -15310,10 +15390,10 @@ RAW: ${raw}
15310
15390
  }
15311
15391
  }
15312
15392
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
15313
- const logPath = path28.join(os23.homedir(), ".node9", "hook-debug.log");
15314
- if (!fs26.existsSync(path28.dirname(logPath)))
15315
- fs26.mkdirSync(path28.dirname(logPath), { recursive: true });
15316
- fs26.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15393
+ const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15394
+ if (!fs27.existsSync(path29.dirname(logPath)))
15395
+ fs27.mkdirSync(path29.dirname(logPath), { recursive: true });
15396
+ fs27.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
15317
15397
  `);
15318
15398
  }
15319
15399
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -15326,8 +15406,8 @@ RAW: ${raw}
15326
15406
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
15327
15407
  let ttyFd = null;
15328
15408
  try {
15329
- ttyFd = fs26.openSync("/dev/tty", "w");
15330
- const writeTty = (line) => fs26.writeSync(ttyFd, line + "\n");
15409
+ ttyFd = fs27.openSync("/dev/tty", "w");
15410
+ const writeTty = (line) => fs27.writeSync(ttyFd, line + "\n");
15331
15411
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
15332
15412
  writeTty(chalk6.bgRed.white.bold(`
15333
15413
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -15346,7 +15426,7 @@ RAW: ${raw}
15346
15426
  } finally {
15347
15427
  if (ttyFd !== null)
15348
15428
  try {
15349
- fs26.closeSync(ttyFd);
15429
+ fs27.closeSync(ttyFd);
15350
15430
  } catch {
15351
15431
  }
15352
15432
  }
@@ -15380,17 +15460,17 @@ RAW: ${raw}
15380
15460
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
15381
15461
  if (skillPinCfg.enabled && safeSessionId) {
15382
15462
  try {
15383
- const sessionsDir = path28.join(os23.homedir(), ".node9", "skill-sessions");
15384
- const flagPath = path28.join(sessionsDir, `${safeSessionId}.json`);
15463
+ const sessionsDir = path29.join(os23.homedir(), ".node9", "skill-sessions");
15464
+ const flagPath = path29.join(sessionsDir, `${safeSessionId}.json`);
15385
15465
  let flag = null;
15386
15466
  try {
15387
- flag = JSON.parse(fs26.readFileSync(flagPath, "utf-8"));
15467
+ flag = JSON.parse(fs27.readFileSync(flagPath, "utf-8"));
15388
15468
  } catch {
15389
15469
  }
15390
15470
  const writeFlag = (data2) => {
15391
15471
  try {
15392
- fs26.mkdirSync(sessionsDir, { recursive: true });
15393
- fs26.writeFileSync(
15472
+ fs27.mkdirSync(sessionsDir, { recursive: true });
15473
+ fs27.writeFileSync(
15394
15474
  flagPath,
15395
15475
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
15396
15476
  { mode: 384 }
@@ -15401,8 +15481,8 @@ RAW: ${raw}
15401
15481
  const sendSkillWarn = (detail, recoveryCmd) => {
15402
15482
  let ttyFd = null;
15403
15483
  try {
15404
- ttyFd = fs26.openSync("/dev/tty", "w");
15405
- const w = (line) => fs26.writeSync(ttyFd, line + "\n");
15484
+ ttyFd = fs27.openSync("/dev/tty", "w");
15485
+ const w = (line) => fs27.writeSync(ttyFd, line + "\n");
15406
15486
  w(chalk6.yellow(`
15407
15487
  \u26A0\uFE0F Node9: installed skill drift detected`));
15408
15488
  w(chalk6.gray(` ${detail}`));
@@ -15417,7 +15497,7 @@ RAW: ${raw}
15417
15497
  } finally {
15418
15498
  if (ttyFd !== null)
15419
15499
  try {
15420
- fs26.closeSync(ttyFd);
15500
+ fs27.closeSync(ttyFd);
15421
15501
  } catch {
15422
15502
  }
15423
15503
  }
@@ -15433,7 +15513,7 @@ RAW: ${raw}
15433
15513
  return;
15434
15514
  }
15435
15515
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
15436
- const absoluteCwd = typeof payload.cwd === "string" && path28.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15516
+ const absoluteCwd = typeof payload.cwd === "string" && path29.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15437
15517
  const extraRoots = skillPinCfg.roots;
15438
15518
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
15439
15519
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -15474,10 +15554,10 @@ RAW: ${raw}
15474
15554
  }
15475
15555
  try {
15476
15556
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
15477
- for (const name of fs26.readdirSync(sessionsDir)) {
15478
- const p = path28.join(sessionsDir, name);
15557
+ for (const name of fs27.readdirSync(sessionsDir)) {
15558
+ const p = path29.join(sessionsDir, name);
15479
15559
  try {
15480
- if (fs26.statSync(p).mtimeMs < cutoff) fs26.unlinkSync(p);
15560
+ if (fs27.statSync(p).mtimeMs < cutoff) fs27.unlinkSync(p);
15481
15561
  } catch {
15482
15562
  }
15483
15563
  }
@@ -15487,9 +15567,9 @@ RAW: ${raw}
15487
15567
  } catch (err2) {
15488
15568
  if (process.env.NODE9_DEBUG === "1") {
15489
15569
  try {
15490
- const dbg = path28.join(os23.homedir(), ".node9", "hook-debug.log");
15570
+ const dbg = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15491
15571
  const msg = err2 instanceof Error ? err2.message : String(err2);
15492
- fs26.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15572
+ fs27.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
15493
15573
  `);
15494
15574
  } catch {
15495
15575
  }
@@ -15499,7 +15579,7 @@ RAW: ${raw}
15499
15579
  if (shouldSnapshot(toolName, toolInput, config)) {
15500
15580
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
15501
15581
  }
15502
- const safeCwdForAuth = typeof payload.cwd === "string" && path28.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15582
+ const safeCwdForAuth = typeof payload.cwd === "string" && path29.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15503
15583
  const result = await authorizeHeadless(toolName, toolInput, meta, {
15504
15584
  cwd: safeCwdForAuth
15505
15585
  });
@@ -15511,12 +15591,12 @@ RAW: ${raw}
15511
15591
  }
15512
15592
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
15513
15593
  try {
15514
- const tty = fs26.openSync("/dev/tty", "w");
15515
- fs26.writeSync(
15594
+ const tty = fs27.openSync("/dev/tty", "w");
15595
+ fs27.writeSync(
15516
15596
  tty,
15517
15597
  chalk6.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
15518
15598
  );
15519
- fs26.closeSync(tty);
15599
+ fs27.closeSync(tty);
15520
15600
  } catch {
15521
15601
  }
15522
15602
  const daemonReady = await autoStartDaemonAndWait();
@@ -15543,9 +15623,9 @@ RAW: ${raw}
15543
15623
  });
15544
15624
  } catch (err2) {
15545
15625
  if (process.env.NODE9_DEBUG === "1") {
15546
- const logPath = path28.join(os23.homedir(), ".node9", "hook-debug.log");
15626
+ const logPath = path29.join(os23.homedir(), ".node9", "hook-debug.log");
15547
15627
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
15548
- fs26.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15628
+ fs27.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
15549
15629
  `);
15550
15630
  }
15551
15631
  process.exit(0);
@@ -15581,8 +15661,8 @@ RAW: ${raw}
15581
15661
  // src/cli/commands/log.ts
15582
15662
  init_audit();
15583
15663
  init_config();
15584
- import fs27 from "fs";
15585
- import path29 from "path";
15664
+ import fs28 from "fs";
15665
+ import path30 from "path";
15586
15666
  import os24 from "os";
15587
15667
  init_daemon();
15588
15668
 
@@ -15656,10 +15736,10 @@ function registerLogCommand(program2) {
15656
15736
  decision: "allowed",
15657
15737
  source: "post-hook"
15658
15738
  };
15659
- const logPath = path29.join(os24.homedir(), ".node9", "audit.log");
15660
- if (!fs27.existsSync(path29.dirname(logPath)))
15661
- fs27.mkdirSync(path29.dirname(logPath), { recursive: true });
15662
- fs27.appendFileSync(logPath, JSON.stringify(entry) + "\n");
15739
+ const logPath = path30.join(os24.homedir(), ".node9", "audit.log");
15740
+ if (!fs28.existsSync(path30.dirname(logPath)))
15741
+ fs28.mkdirSync(path30.dirname(logPath), { recursive: true });
15742
+ fs28.appendFileSync(logPath, JSON.stringify(entry) + "\n");
15663
15743
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
15664
15744
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
15665
15745
  if (command) {
@@ -15692,7 +15772,7 @@ function registerLogCommand(program2) {
15692
15772
  }
15693
15773
  }
15694
15774
  }
15695
- const safeCwd = typeof payload.cwd === "string" && path29.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15775
+ const safeCwd = typeof payload.cwd === "string" && path30.isAbsolute(payload.cwd) ? payload.cwd : void 0;
15696
15776
  const config = getConfig(safeCwd);
15697
15777
  if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
15698
15778
  const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
@@ -15713,9 +15793,9 @@ function registerLogCommand(program2) {
15713
15793
  const msg = err2 instanceof Error ? err2.message : String(err2);
15714
15794
  process.stderr.write(`[Node9] audit log error: ${msg}
15715
15795
  `);
15716
- const debugPath = path29.join(os24.homedir(), ".node9", "hook-debug.log");
15796
+ const debugPath = path30.join(os24.homedir(), ".node9", "hook-debug.log");
15717
15797
  try {
15718
- fs27.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
15798
+ fs28.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
15719
15799
  `);
15720
15800
  } catch {
15721
15801
  }
@@ -16116,8 +16196,8 @@ function registerConfigShowCommand(program2) {
16116
16196
  // src/cli/commands/doctor.ts
16117
16197
  init_daemon();
16118
16198
  import chalk8 from "chalk";
16119
- import fs28 from "fs";
16120
- import path30 from "path";
16199
+ import fs29 from "fs";
16200
+ import path31 from "path";
16121
16201
  import os25 from "os";
16122
16202
  import { execSync as execSync2 } from "child_process";
16123
16203
  function registerDoctorCommand(program2, version2) {
@@ -16171,10 +16251,10 @@ function registerDoctorCommand(program2, version2) {
16171
16251
  );
16172
16252
  }
16173
16253
  section("Configuration");
16174
- const globalConfigPath = path30.join(homeDir2, ".node9", "config.json");
16175
- if (fs28.existsSync(globalConfigPath)) {
16254
+ const globalConfigPath = path31.join(homeDir2, ".node9", "config.json");
16255
+ if (fs29.existsSync(globalConfigPath)) {
16176
16256
  try {
16177
- JSON.parse(fs28.readFileSync(globalConfigPath, "utf-8"));
16257
+ JSON.parse(fs29.readFileSync(globalConfigPath, "utf-8"));
16178
16258
  pass("~/.node9/config.json found and valid");
16179
16259
  } catch {
16180
16260
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -16182,10 +16262,10 @@ function registerDoctorCommand(program2, version2) {
16182
16262
  } else {
16183
16263
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
16184
16264
  }
16185
- const projectConfigPath = path30.join(process.cwd(), "node9.config.json");
16186
- if (fs28.existsSync(projectConfigPath)) {
16265
+ const projectConfigPath = path31.join(process.cwd(), "node9.config.json");
16266
+ if (fs29.existsSync(projectConfigPath)) {
16187
16267
  try {
16188
- JSON.parse(fs28.readFileSync(projectConfigPath, "utf-8"));
16268
+ JSON.parse(fs29.readFileSync(projectConfigPath, "utf-8"));
16189
16269
  pass("node9.config.json found and valid (project)");
16190
16270
  } catch {
16191
16271
  fail(
@@ -16194,8 +16274,8 @@ function registerDoctorCommand(program2, version2) {
16194
16274
  );
16195
16275
  }
16196
16276
  }
16197
- const credsPath = path30.join(homeDir2, ".node9", "credentials.json");
16198
- if (fs28.existsSync(credsPath)) {
16277
+ const credsPath = path31.join(homeDir2, ".node9", "credentials.json");
16278
+ if (fs29.existsSync(credsPath)) {
16199
16279
  pass("Cloud credentials found (~/.node9/credentials.json)");
16200
16280
  } else {
16201
16281
  warn(
@@ -16204,10 +16284,10 @@ function registerDoctorCommand(program2, version2) {
16204
16284
  );
16205
16285
  }
16206
16286
  section("Agent Hooks");
16207
- const claudeSettingsPath = path30.join(homeDir2, ".claude", "settings.json");
16208
- if (fs28.existsSync(claudeSettingsPath)) {
16287
+ const claudeSettingsPath = path31.join(homeDir2, ".claude", "settings.json");
16288
+ if (fs29.existsSync(claudeSettingsPath)) {
16209
16289
  try {
16210
- const cs = JSON.parse(fs28.readFileSync(claudeSettingsPath, "utf-8"));
16290
+ const cs = JSON.parse(fs29.readFileSync(claudeSettingsPath, "utf-8"));
16211
16291
  const hasHook = cs.hooks?.PreToolUse?.some(
16212
16292
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16213
16293
  );
@@ -16223,10 +16303,10 @@ function registerDoctorCommand(program2, version2) {
16223
16303
  } else {
16224
16304
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
16225
16305
  }
16226
- const geminiSettingsPath = path30.join(homeDir2, ".gemini", "settings.json");
16227
- if (fs28.existsSync(geminiSettingsPath)) {
16306
+ const geminiSettingsPath = path31.join(homeDir2, ".gemini", "settings.json");
16307
+ if (fs29.existsSync(geminiSettingsPath)) {
16228
16308
  try {
16229
- const gs = JSON.parse(fs28.readFileSync(geminiSettingsPath, "utf-8"));
16309
+ const gs = JSON.parse(fs29.readFileSync(geminiSettingsPath, "utf-8"));
16230
16310
  const hasHook = gs.hooks?.BeforeTool?.some(
16231
16311
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
16232
16312
  );
@@ -16242,10 +16322,10 @@ function registerDoctorCommand(program2, version2) {
16242
16322
  } else {
16243
16323
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
16244
16324
  }
16245
- const cursorHooksPath = path30.join(homeDir2, ".cursor", "hooks.json");
16246
- if (fs28.existsSync(cursorHooksPath)) {
16325
+ const cursorHooksPath = path31.join(homeDir2, ".cursor", "hooks.json");
16326
+ if (fs29.existsSync(cursorHooksPath)) {
16247
16327
  try {
16248
- const cur = JSON.parse(fs28.readFileSync(cursorHooksPath, "utf-8"));
16328
+ const cur = JSON.parse(fs29.readFileSync(cursorHooksPath, "utf-8"));
16249
16329
  const hasHook = cur.hooks?.preToolUse?.some(
16250
16330
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
16251
16331
  );
@@ -16283,8 +16363,8 @@ function registerDoctorCommand(program2, version2) {
16283
16363
 
16284
16364
  // src/cli/commands/audit.ts
16285
16365
  import chalk9 from "chalk";
16286
- import fs29 from "fs";
16287
- import path31 from "path";
16366
+ import fs30 from "fs";
16367
+ import path32 from "path";
16288
16368
  import os26 from "os";
16289
16369
  function formatRelativeTime(timestamp) {
16290
16370
  const diff = Date.now() - new Date(timestamp).getTime();
@@ -16298,14 +16378,14 @@ function formatRelativeTime(timestamp) {
16298
16378
  }
16299
16379
  function registerAuditCommand(program2) {
16300
16380
  program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
16301
- const logPath = path31.join(os26.homedir(), ".node9", "audit.log");
16302
- if (!fs29.existsSync(logPath)) {
16381
+ const logPath = path32.join(os26.homedir(), ".node9", "audit.log");
16382
+ if (!fs30.existsSync(logPath)) {
16303
16383
  console.log(
16304
16384
  chalk9.yellow("No audit logs found. Run node9 with an agent to generate entries.")
16305
16385
  );
16306
16386
  return;
16307
16387
  }
16308
- const raw = fs29.readFileSync(logPath, "utf-8");
16388
+ const raw = fs30.readFileSync(logPath, "utf-8");
16309
16389
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
16310
16390
  let entries = lines.flatMap((line) => {
16311
16391
  try {
@@ -16359,8 +16439,8 @@ function registerAuditCommand(program2) {
16359
16439
 
16360
16440
  // src/cli/commands/report.ts
16361
16441
  import chalk10 from "chalk";
16362
- import fs30 from "fs";
16363
- import path32 from "path";
16442
+ import fs31 from "fs";
16443
+ import path33 from "path";
16364
16444
  import os27 from "os";
16365
16445
  var TEST_COMMAND_RE3 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
16366
16446
  function buildTestTimestamps(allEntries) {
@@ -16408,8 +16488,8 @@ function getDateRange(period) {
16408
16488
  }
16409
16489
  }
16410
16490
  function parseAuditLog(logPath) {
16411
- if (!fs30.existsSync(logPath)) return [];
16412
- const raw = fs30.readFileSync(logPath, "utf-8");
16491
+ if (!fs31.existsSync(logPath)) return [];
16492
+ const raw = fs31.readFileSync(logPath, "utf-8");
16413
16493
  return raw.split("\n").flatMap((line) => {
16414
16494
  if (!line.trim()) return [];
16415
16495
  try {
@@ -16488,11 +16568,11 @@ function loadClaudeCost(start, end) {
16488
16568
  cacheWriteTokens: 0,
16489
16569
  cacheReadTokens: 0
16490
16570
  };
16491
- const projectsDir = path32.join(os27.homedir(), ".claude", "projects");
16492
- if (!fs30.existsSync(projectsDir)) return empty;
16571
+ const projectsDir = path33.join(os27.homedir(), ".claude", "projects");
16572
+ if (!fs31.existsSync(projectsDir)) return empty;
16493
16573
  let dirs;
16494
16574
  try {
16495
- dirs = fs30.readdirSync(projectsDir);
16575
+ dirs = fs31.readdirSync(projectsDir);
16496
16576
  } catch {
16497
16577
  return empty;
16498
16578
  }
@@ -16504,18 +16584,18 @@ function loadClaudeCost(start, end) {
16504
16584
  const byDay = /* @__PURE__ */ new Map();
16505
16585
  const byModel = /* @__PURE__ */ new Map();
16506
16586
  for (const proj of dirs) {
16507
- const projPath = path32.join(projectsDir, proj);
16587
+ const projPath = path33.join(projectsDir, proj);
16508
16588
  let files;
16509
16589
  try {
16510
- const stat = fs30.statSync(projPath);
16590
+ const stat = fs31.statSync(projPath);
16511
16591
  if (!stat.isDirectory()) continue;
16512
- files = fs30.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16592
+ files = fs31.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
16513
16593
  } catch {
16514
16594
  continue;
16515
16595
  }
16516
16596
  for (const file of files) {
16517
16597
  try {
16518
- const raw = fs30.readFileSync(path32.join(projPath, file), "utf-8");
16598
+ const raw = fs31.readFileSync(path33.join(projPath, file), "utf-8");
16519
16599
  for (const line of raw.split("\n")) {
16520
16600
  if (!line.trim()) continue;
16521
16601
  let entry;
@@ -16556,36 +16636,36 @@ function loadClaudeCost(start, end) {
16556
16636
  return { total, byDay, byModel, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens };
16557
16637
  }
16558
16638
  function loadCodexCost(start, end) {
16559
- const sessionsBase = path32.join(os27.homedir(), ".codex", "sessions");
16639
+ const sessionsBase = path33.join(os27.homedir(), ".codex", "sessions");
16560
16640
  const byDay = /* @__PURE__ */ new Map();
16561
16641
  let total = 0;
16562
16642
  let toolCalls = 0;
16563
- if (!fs30.existsSync(sessionsBase)) return { total, byDay, toolCalls };
16643
+ if (!fs31.existsSync(sessionsBase)) return { total, byDay, toolCalls };
16564
16644
  const jsonlFiles = [];
16565
16645
  try {
16566
- for (const year of fs30.readdirSync(sessionsBase)) {
16567
- const yearPath = path32.join(sessionsBase, year);
16646
+ for (const year of fs31.readdirSync(sessionsBase)) {
16647
+ const yearPath = path33.join(sessionsBase, year);
16568
16648
  try {
16569
- if (!fs30.statSync(yearPath).isDirectory()) continue;
16649
+ if (!fs31.statSync(yearPath).isDirectory()) continue;
16570
16650
  } catch {
16571
16651
  continue;
16572
16652
  }
16573
- for (const month of fs30.readdirSync(yearPath)) {
16574
- const monthPath = path32.join(yearPath, month);
16653
+ for (const month of fs31.readdirSync(yearPath)) {
16654
+ const monthPath = path33.join(yearPath, month);
16575
16655
  try {
16576
- if (!fs30.statSync(monthPath).isDirectory()) continue;
16656
+ if (!fs31.statSync(monthPath).isDirectory()) continue;
16577
16657
  } catch {
16578
16658
  continue;
16579
16659
  }
16580
- for (const day of fs30.readdirSync(monthPath)) {
16581
- const dayPath = path32.join(monthPath, day);
16660
+ for (const day of fs31.readdirSync(monthPath)) {
16661
+ const dayPath = path33.join(monthPath, day);
16582
16662
  try {
16583
- if (!fs30.statSync(dayPath).isDirectory()) continue;
16663
+ if (!fs31.statSync(dayPath).isDirectory()) continue;
16584
16664
  } catch {
16585
16665
  continue;
16586
16666
  }
16587
- for (const file of fs30.readdirSync(dayPath)) {
16588
- if (file.endsWith(".jsonl")) jsonlFiles.push(path32.join(dayPath, file));
16667
+ for (const file of fs31.readdirSync(dayPath)) {
16668
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path33.join(dayPath, file));
16589
16669
  }
16590
16670
  }
16591
16671
  }
@@ -16596,7 +16676,7 @@ function loadCodexCost(start, end) {
16596
16676
  for (const filePath of jsonlFiles) {
16597
16677
  let lines;
16598
16678
  try {
16599
- lines = fs30.readFileSync(filePath, "utf-8").split("\n");
16679
+ lines = fs31.readFileSync(filePath, "utf-8").split("\n");
16600
16680
  } catch {
16601
16681
  continue;
16602
16682
  }
@@ -16646,7 +16726,7 @@ function registerReportCommand(program2) {
16646
16726
  const period = ["today", "7d", "30d", "month"].includes(
16647
16727
  options.period
16648
16728
  ) ? options.period : "7d";
16649
- const logPath = path32.join(os27.homedir(), ".node9", "audit.log");
16729
+ const logPath = path33.join(os27.homedir(), ".node9", "audit.log");
16650
16730
  const allEntries = parseAuditLog(logPath);
16651
16731
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
16652
16732
  if (unackedDlp.length > 0) {
@@ -17153,12 +17233,12 @@ function registerDaemonCommand(program2) {
17153
17233
  init_core();
17154
17234
  init_daemon();
17155
17235
  import chalk12 from "chalk";
17156
- import fs31 from "fs";
17157
- import path33 from "path";
17236
+ import fs32 from "fs";
17237
+ import path34 from "path";
17158
17238
  import os28 from "os";
17159
17239
  function readJson2(filePath) {
17160
17240
  try {
17161
- if (fs31.existsSync(filePath)) return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
17241
+ if (fs32.existsSync(filePath)) return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
17162
17242
  } catch {
17163
17243
  }
17164
17244
  return null;
@@ -17223,13 +17303,13 @@ function registerStatusCommand(program2) {
17223
17303
  console.log("");
17224
17304
  const modeLabel = settings.mode === "audit" ? chalk12.blue("audit") : settings.mode === "strict" ? chalk12.red("strict") : chalk12.white("standard");
17225
17305
  console.log(` Mode: ${modeLabel}`);
17226
- const projectConfig = path33.join(process.cwd(), "node9.config.json");
17227
- const globalConfig = path33.join(os28.homedir(), ".node9", "config.json");
17306
+ const projectConfig = path34.join(process.cwd(), "node9.config.json");
17307
+ const globalConfig = path34.join(os28.homedir(), ".node9", "config.json");
17228
17308
  console.log(
17229
- ` Local: ${fs31.existsSync(projectConfig) ? chalk12.green("Active (node9.config.json)") : chalk12.gray("Not present")}`
17309
+ ` Local: ${fs32.existsSync(projectConfig) ? chalk12.green("Active (node9.config.json)") : chalk12.gray("Not present")}`
17230
17310
  );
17231
17311
  console.log(
17232
- ` Global: ${fs31.existsSync(globalConfig) ? chalk12.green("Active (~/.node9/config.json)") : chalk12.gray("Not present")}`
17312
+ ` Global: ${fs32.existsSync(globalConfig) ? chalk12.green("Active (~/.node9/config.json)") : chalk12.gray("Not present")}`
17233
17313
  );
17234
17314
  if (mergedConfig.policy.sandboxPaths.length > 0) {
17235
17315
  console.log(
@@ -17238,13 +17318,13 @@ function registerStatusCommand(program2) {
17238
17318
  }
17239
17319
  const homeDir2 = os28.homedir();
17240
17320
  const claudeSettings = readJson2(
17241
- path33.join(homeDir2, ".claude", "settings.json")
17321
+ path34.join(homeDir2, ".claude", "settings.json")
17242
17322
  );
17243
- const claudeConfig = readJson2(path33.join(homeDir2, ".claude.json"));
17323
+ const claudeConfig = readJson2(path34.join(homeDir2, ".claude.json"));
17244
17324
  const geminiSettings = readJson2(
17245
- path33.join(homeDir2, ".gemini", "settings.json")
17325
+ path34.join(homeDir2, ".gemini", "settings.json")
17246
17326
  );
17247
- const cursorConfig = readJson2(path33.join(homeDir2, ".cursor", "mcp.json"));
17327
+ const cursorConfig = readJson2(path34.join(homeDir2, ".cursor", "mcp.json"));
17248
17328
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
17249
17329
  if (agentFound) {
17250
17330
  console.log("");
@@ -17304,8 +17384,8 @@ function registerStatusCommand(program2) {
17304
17384
  // src/cli/commands/init.ts
17305
17385
  init_core();
17306
17386
  import chalk13 from "chalk";
17307
- import fs32 from "fs";
17308
- import path34 from "path";
17387
+ import fs33 from "fs";
17388
+ import path35 from "path";
17309
17389
  import os29 from "os";
17310
17390
  import https3 from "https";
17311
17391
  init_shields();
@@ -17366,15 +17446,15 @@ function registerInitCommand(program2) {
17366
17446
  }
17367
17447
  console.log("");
17368
17448
  }
17369
- const configPath = path34.join(os29.homedir(), ".node9", "config.json");
17370
- if (fs32.existsSync(configPath) && !options.force) {
17449
+ const configPath = path35.join(os29.homedir(), ".node9", "config.json");
17450
+ if (fs33.existsSync(configPath) && !options.force) {
17371
17451
  try {
17372
- const existing = JSON.parse(fs32.readFileSync(configPath, "utf-8"));
17452
+ const existing = JSON.parse(fs33.readFileSync(configPath, "utf-8"));
17373
17453
  const settings = existing.settings ?? {};
17374
17454
  if (settings.mode !== chosenMode) {
17375
17455
  settings.mode = chosenMode;
17376
17456
  existing.settings = settings;
17377
- fs32.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
17457
+ fs33.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
17378
17458
  console.log(chalk13.green(`\u2705 Mode updated: ${chosenMode}`));
17379
17459
  } else {
17380
17460
  console.log(chalk13.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -17387,9 +17467,9 @@ function registerInitCommand(program2) {
17387
17467
  ...DEFAULT_CONFIG,
17388
17468
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
17389
17469
  };
17390
- const dir = path34.dirname(configPath);
17391
- if (!fs32.existsSync(dir)) fs32.mkdirSync(dir, { recursive: true });
17392
- fs32.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
17470
+ const dir = path35.dirname(configPath);
17471
+ if (!fs33.existsSync(dir)) fs33.mkdirSync(dir, { recursive: true });
17472
+ fs33.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
17393
17473
  console.log(chalk13.green(`\u2705 Config created: ${configPath}`));
17394
17474
  console.log(chalk13.gray(` Mode: ${chosenMode}`));
17395
17475
  }
@@ -17474,7 +17554,7 @@ function registerInitCommand(program2) {
17474
17554
  }
17475
17555
 
17476
17556
  // src/cli/commands/undo.ts
17477
- import path35 from "path";
17557
+ import path36 from "path";
17478
17558
  import chalk15 from "chalk";
17479
17559
 
17480
17560
  // src/tui/undo-navigator.ts
@@ -17633,7 +17713,7 @@ function findMatchingCwd(startDir, history) {
17633
17713
  let dir = startDir;
17634
17714
  while (true) {
17635
17715
  if (cwds.has(dir)) return dir;
17636
- const parent = path35.dirname(dir);
17716
+ const parent = path36.dirname(dir);
17637
17717
  if (parent === dir) return null;
17638
17718
  dir = parent;
17639
17719
  }
@@ -17829,12 +17909,12 @@ import { execa as execa2 } from "execa";
17829
17909
  init_provenance();
17830
17910
 
17831
17911
  // src/mcp-pin.ts
17832
- import fs33 from "fs";
17833
- import path36 from "path";
17912
+ import fs34 from "fs";
17913
+ import path37 from "path";
17834
17914
  import os30 from "os";
17835
17915
  import crypto5 from "crypto";
17836
17916
  function getPinsFilePath2() {
17837
- return path36.join(os30.homedir(), ".node9", "mcp-pins.json");
17917
+ return path37.join(os30.homedir(), ".node9", "mcp-pins.json");
17838
17918
  }
17839
17919
  function hashToolDefinitions(tools) {
17840
17920
  const sorted = [...tools].sort((a, b) => {
@@ -17851,7 +17931,7 @@ function getServerKey(upstreamCommand) {
17851
17931
  function readMcpPinsSafe() {
17852
17932
  const filePath = getPinsFilePath2();
17853
17933
  try {
17854
- const raw = fs33.readFileSync(filePath, "utf-8");
17934
+ const raw = fs34.readFileSync(filePath, "utf-8");
17855
17935
  if (!raw.trim()) {
17856
17936
  return { ok: false, reason: "corrupt", detail: "empty file" };
17857
17937
  }
@@ -17875,10 +17955,10 @@ function readMcpPins() {
17875
17955
  }
17876
17956
  function writeMcpPins(data) {
17877
17957
  const filePath = getPinsFilePath2();
17878
- fs33.mkdirSync(path36.dirname(filePath), { recursive: true });
17958
+ fs34.mkdirSync(path37.dirname(filePath), { recursive: true });
17879
17959
  const tmp = `${filePath}.${crypto5.randomBytes(6).toString("hex")}.tmp`;
17880
- fs33.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17881
- fs33.renameSync(tmp, filePath);
17960
+ fs34.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
17961
+ fs34.renameSync(tmp, filePath);
17882
17962
  }
17883
17963
  function checkPin(serverKey, currentHash) {
17884
17964
  const result = readMcpPinsSafe();
@@ -18326,9 +18406,9 @@ function registerMcpGatewayCommand(program2) {
18326
18406
 
18327
18407
  // src/mcp-server/index.ts
18328
18408
  import readline4 from "readline";
18329
- import fs34 from "fs";
18409
+ import fs35 from "fs";
18330
18410
  import os31 from "os";
18331
- import path37 from "path";
18411
+ import path38 from "path";
18332
18412
  import { spawnSync as spawnSync7 } from "child_process";
18333
18413
  init_core();
18334
18414
  init_daemon();
@@ -18579,13 +18659,13 @@ function handleStatus() {
18579
18659
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
18580
18660
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
18581
18661
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
18582
- const projectConfig = path37.join(process.cwd(), "node9.config.json");
18583
- const globalConfig = path37.join(os31.homedir(), ".node9", "config.json");
18662
+ const projectConfig = path38.join(process.cwd(), "node9.config.json");
18663
+ const globalConfig = path38.join(os31.homedir(), ".node9", "config.json");
18584
18664
  lines.push(
18585
- `Project config (node9.config.json): ${fs34.existsSync(projectConfig) ? "present" : "not found"}`
18665
+ `Project config (node9.config.json): ${fs35.existsSync(projectConfig) ? "present" : "not found"}`
18586
18666
  );
18587
18667
  lines.push(
18588
- `Global config (~/.node9/config.json): ${fs34.existsSync(globalConfig) ? "present" : "not found"}`
18668
+ `Global config (~/.node9/config.json): ${fs35.existsSync(globalConfig) ? "present" : "not found"}`
18589
18669
  );
18590
18670
  return lines.join("\n");
18591
18671
  }
@@ -18659,21 +18739,21 @@ function handleShieldDisable(args) {
18659
18739
  writeActiveShields(active.filter((s) => s !== name));
18660
18740
  return `Shield "${name}" disabled.`;
18661
18741
  }
18662
- var GLOBAL_CONFIG_PATH2 = path37.join(os31.homedir(), ".node9", "config.json");
18742
+ var GLOBAL_CONFIG_PATH2 = path38.join(os31.homedir(), ".node9", "config.json");
18663
18743
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
18664
18744
  function readGlobalConfigRaw() {
18665
18745
  try {
18666
- if (fs34.existsSync(GLOBAL_CONFIG_PATH2)) {
18667
- return JSON.parse(fs34.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
18746
+ if (fs35.existsSync(GLOBAL_CONFIG_PATH2)) {
18747
+ return JSON.parse(fs35.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
18668
18748
  }
18669
18749
  } catch {
18670
18750
  }
18671
18751
  return {};
18672
18752
  }
18673
18753
  function writeGlobalConfigRaw(data) {
18674
- const dir = path37.dirname(GLOBAL_CONFIG_PATH2);
18675
- if (!fs34.existsSync(dir)) fs34.mkdirSync(dir, { recursive: true });
18676
- fs34.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
18754
+ const dir = path38.dirname(GLOBAL_CONFIG_PATH2);
18755
+ if (!fs35.existsSync(dir)) fs35.mkdirSync(dir, { recursive: true });
18756
+ fs35.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
18677
18757
  }
18678
18758
  function handleApproverList() {
18679
18759
  const config = getConfig();
@@ -18717,9 +18797,9 @@ function handleApproverSet(args) {
18717
18797
  function handleAuditGet(args) {
18718
18798
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
18719
18799
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
18720
- const auditPath = path37.join(os31.homedir(), ".node9", "audit.log");
18721
- if (!fs34.existsSync(auditPath)) return "No audit log found.";
18722
- const rawLines = fs34.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
18800
+ const auditPath = path38.join(os31.homedir(), ".node9", "audit.log");
18801
+ if (!fs35.existsSync(auditPath)) return "No audit log found.";
18802
+ const rawLines = fs35.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
18723
18803
  const parsed = [];
18724
18804
  for (const line of rawLines) {
18725
18805
  try {
@@ -19273,8 +19353,8 @@ init_scan();
19273
19353
 
19274
19354
  // src/cli/commands/sessions.ts
19275
19355
  import chalk22 from "chalk";
19276
- import fs35 from "fs";
19277
- import path38 from "path";
19356
+ import fs36 from "fs";
19357
+ import path39 from "path";
19278
19358
  import os32 from "os";
19279
19359
  var CLAUDE_PRICING3 = {
19280
19360
  "claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
@@ -19316,7 +19396,7 @@ function encodeProjectPath(projectPath) {
19316
19396
  }
19317
19397
  function sessionJsonlPath(projectPath, sessionId) {
19318
19398
  const encoded = encodeProjectPath(projectPath);
19319
- return path38.join(os32.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
19399
+ return path39.join(os32.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
19320
19400
  }
19321
19401
  function projectLabel(projectPath) {
19322
19402
  return projectPath.replace(os32.homedir(), "~");
@@ -19388,10 +19468,10 @@ function parseSessionLines(lines) {
19388
19468
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
19389
19469
  }
19390
19470
  function loadAuditEntries(auditPath) {
19391
- const aPath = auditPath ?? path38.join(os32.homedir(), ".node9", "audit.log");
19471
+ const aPath = auditPath ?? path39.join(os32.homedir(), ".node9", "audit.log");
19392
19472
  let raw;
19393
19473
  try {
19394
- raw = fs35.readFileSync(aPath, "utf-8");
19474
+ raw = fs36.readFileSync(aPath, "utf-8");
19395
19475
  } catch {
19396
19476
  return [];
19397
19477
  }
@@ -19427,8 +19507,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
19427
19507
  return result;
19428
19508
  }
19429
19509
  function buildGeminiSessions(days, allAuditEntries) {
19430
- const tmpDir = path38.join(os32.homedir(), ".gemini", "tmp");
19431
- if (!fs35.existsSync(tmpDir)) return [];
19510
+ const tmpDir = path39.join(os32.homedir(), ".gemini", "tmp");
19511
+ if (!fs36.existsSync(tmpDir)) return [];
19432
19512
  const cutoff = days !== null ? (() => {
19433
19513
  const d = /* @__PURE__ */ new Date();
19434
19514
  d.setDate(d.getDate() - days);
@@ -19437,35 +19517,35 @@ function buildGeminiSessions(days, allAuditEntries) {
19437
19517
  })() : null;
19438
19518
  let slugDirs;
19439
19519
  try {
19440
- slugDirs = fs35.readdirSync(tmpDir);
19520
+ slugDirs = fs36.readdirSync(tmpDir);
19441
19521
  } catch {
19442
19522
  return [];
19443
19523
  }
19444
19524
  const summaries = [];
19445
19525
  for (const slug of slugDirs) {
19446
- const slugPath = path38.join(tmpDir, slug);
19526
+ const slugPath = path39.join(tmpDir, slug);
19447
19527
  try {
19448
- if (!fs35.statSync(slugPath).isDirectory()) continue;
19528
+ if (!fs36.statSync(slugPath).isDirectory()) continue;
19449
19529
  } catch {
19450
19530
  continue;
19451
19531
  }
19452
- let projectRoot = path38.join(os32.homedir(), slug);
19532
+ let projectRoot = path39.join(os32.homedir(), slug);
19453
19533
  try {
19454
- projectRoot = fs35.readFileSync(path38.join(slugPath, ".project_root"), "utf-8").trim();
19534
+ projectRoot = fs36.readFileSync(path39.join(slugPath, ".project_root"), "utf-8").trim();
19455
19535
  } catch {
19456
19536
  }
19457
- const chatsDir = path38.join(slugPath, "chats");
19458
- if (!fs35.existsSync(chatsDir)) continue;
19537
+ const chatsDir = path39.join(slugPath, "chats");
19538
+ if (!fs36.existsSync(chatsDir)) continue;
19459
19539
  let chatFiles;
19460
19540
  try {
19461
- chatFiles = fs35.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
19541
+ chatFiles = fs36.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
19462
19542
  } catch {
19463
19543
  continue;
19464
19544
  }
19465
19545
  for (const chatFile of chatFiles) {
19466
19546
  let raw;
19467
19547
  try {
19468
- raw = fs35.readFileSync(path38.join(chatsDir, chatFile), "utf-8");
19548
+ raw = fs36.readFileSync(path39.join(chatsDir, chatFile), "utf-8");
19469
19549
  } catch {
19470
19550
  continue;
19471
19551
  }
@@ -19545,8 +19625,8 @@ function buildGeminiSessions(days, allAuditEntries) {
19545
19625
  return summaries;
19546
19626
  }
19547
19627
  function buildCodexSessions(days, allAuditEntries) {
19548
- const sessionsBase = path38.join(os32.homedir(), ".codex", "sessions");
19549
- if (!fs35.existsSync(sessionsBase)) return [];
19628
+ const sessionsBase = path39.join(os32.homedir(), ".codex", "sessions");
19629
+ if (!fs36.existsSync(sessionsBase)) return [];
19550
19630
  const cutoff = days !== null ? (() => {
19551
19631
  const d = /* @__PURE__ */ new Date();
19552
19632
  d.setDate(d.getDate() - days);
@@ -19555,29 +19635,29 @@ function buildCodexSessions(days, allAuditEntries) {
19555
19635
  })() : null;
19556
19636
  const jsonlFiles = [];
19557
19637
  try {
19558
- for (const year of fs35.readdirSync(sessionsBase)) {
19559
- const yearPath = path38.join(sessionsBase, year);
19638
+ for (const year of fs36.readdirSync(sessionsBase)) {
19639
+ const yearPath = path39.join(sessionsBase, year);
19560
19640
  try {
19561
- if (!fs35.statSync(yearPath).isDirectory()) continue;
19641
+ if (!fs36.statSync(yearPath).isDirectory()) continue;
19562
19642
  } catch {
19563
19643
  continue;
19564
19644
  }
19565
- for (const month of fs35.readdirSync(yearPath)) {
19566
- const monthPath = path38.join(yearPath, month);
19645
+ for (const month of fs36.readdirSync(yearPath)) {
19646
+ const monthPath = path39.join(yearPath, month);
19567
19647
  try {
19568
- if (!fs35.statSync(monthPath).isDirectory()) continue;
19648
+ if (!fs36.statSync(monthPath).isDirectory()) continue;
19569
19649
  } catch {
19570
19650
  continue;
19571
19651
  }
19572
- for (const day of fs35.readdirSync(monthPath)) {
19573
- const dayPath = path38.join(monthPath, day);
19652
+ for (const day of fs36.readdirSync(monthPath)) {
19653
+ const dayPath = path39.join(monthPath, day);
19574
19654
  try {
19575
- if (!fs35.statSync(dayPath).isDirectory()) continue;
19655
+ if (!fs36.statSync(dayPath).isDirectory()) continue;
19576
19656
  } catch {
19577
19657
  continue;
19578
19658
  }
19579
- for (const file of fs35.readdirSync(dayPath)) {
19580
- if (file.endsWith(".jsonl")) jsonlFiles.push(path38.join(dayPath, file));
19659
+ for (const file of fs36.readdirSync(dayPath)) {
19660
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path39.join(dayPath, file));
19581
19661
  }
19582
19662
  }
19583
19663
  }
@@ -19589,7 +19669,7 @@ function buildCodexSessions(days, allAuditEntries) {
19589
19669
  for (const filePath of jsonlFiles) {
19590
19670
  let lines;
19591
19671
  try {
19592
- lines = fs35.readFileSync(filePath, "utf-8").split("\n");
19672
+ lines = fs36.readFileSync(filePath, "utf-8").split("\n");
19593
19673
  } catch {
19594
19674
  continue;
19595
19675
  }
@@ -19667,10 +19747,10 @@ function buildCodexSessions(days, allAuditEntries) {
19667
19747
  return summaries;
19668
19748
  }
19669
19749
  function buildSessions(days, historyPath) {
19670
- const hPath = historyPath ?? path38.join(os32.homedir(), ".claude", "history.jsonl");
19750
+ const hPath = historyPath ?? path39.join(os32.homedir(), ".claude", "history.jsonl");
19671
19751
  let historyRaw;
19672
19752
  try {
19673
- historyRaw = fs35.readFileSync(hPath, "utf-8");
19753
+ historyRaw = fs36.readFileSync(hPath, "utf-8");
19674
19754
  } catch {
19675
19755
  return [];
19676
19756
  }
@@ -19695,7 +19775,7 @@ function buildSessions(days, historyPath) {
19695
19775
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
19696
19776
  let sessionLines = [];
19697
19777
  try {
19698
- sessionLines = fs35.readFileSync(jsonlFile, "utf-8").split("\n");
19778
+ sessionLines = fs36.readFileSync(jsonlFile, "utf-8").split("\n");
19699
19779
  } catch {
19700
19780
  }
19701
19781
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -19961,8 +20041,8 @@ function registerSessionsCommand(program2) {
19961
20041
  console.log("");
19962
20042
  console.log(chalk22.cyan.bold("\u{1F4CB} node9 sessions") + chalk22.dim(" \u2014 what your AI agent did"));
19963
20043
  console.log("");
19964
- const historyPath = path38.join(os32.homedir(), ".claude", "history.jsonl");
19965
- if (!fs35.existsSync(historyPath)) {
20044
+ const historyPath = path39.join(os32.homedir(), ".claude", "history.jsonl");
20045
+ if (!fs36.existsSync(historyPath)) {
19966
20046
  console.log(chalk22.yellow(" No Claude session history found at ~/.claude/history.jsonl"));
19967
20047
  console.log(chalk22.gray(" Install Claude Code, run a few sessions, then try again.\n"));
19968
20048
  return;
@@ -19999,12 +20079,12 @@ function registerSessionsCommand(program2) {
19999
20079
 
20000
20080
  // src/cli/commands/skill-pin.ts
20001
20081
  import chalk23 from "chalk";
20002
- import fs36 from "fs";
20082
+ import fs37 from "fs";
20003
20083
  import os33 from "os";
20004
- import path39 from "path";
20084
+ import path40 from "path";
20005
20085
  function wipeSkillSessions() {
20006
20086
  try {
20007
- fs36.rmSync(path39.join(os33.homedir(), ".node9", "skill-sessions"), {
20087
+ fs37.rmSync(path40.join(os33.homedir(), ".node9", "skill-sessions"), {
20008
20088
  recursive: true,
20009
20089
  force: true
20010
20090
  });
@@ -20087,18 +20167,18 @@ function registerSkillPinCommand(program2) {
20087
20167
 
20088
20168
  // src/cli/commands/dlp.ts
20089
20169
  import chalk24 from "chalk";
20090
- import fs37 from "fs";
20091
- import path40 from "path";
20170
+ import fs38 from "fs";
20171
+ import path41 from "path";
20092
20172
  import os34 from "os";
20093
- var AUDIT_LOG = path40.join(os34.homedir(), ".node9", "audit.log");
20094
- var RESOLVED_FILE = path40.join(os34.homedir(), ".node9", "dlp-resolved.json");
20173
+ var AUDIT_LOG = path41.join(os34.homedir(), ".node9", "audit.log");
20174
+ var RESOLVED_FILE = path41.join(os34.homedir(), ".node9", "dlp-resolved.json");
20095
20175
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
20096
20176
  function stripAnsi(s) {
20097
20177
  return s.replace(ANSI_RE, "");
20098
20178
  }
20099
20179
  function loadResolved() {
20100
20180
  try {
20101
- const raw = JSON.parse(fs37.readFileSync(RESOLVED_FILE, "utf-8"));
20181
+ const raw = JSON.parse(fs38.readFileSync(RESOLVED_FILE, "utf-8"));
20102
20182
  return new Set(raw);
20103
20183
  } catch {
20104
20184
  return /* @__PURE__ */ new Set();
@@ -20106,13 +20186,13 @@ function loadResolved() {
20106
20186
  }
20107
20187
  function saveResolved(resolved) {
20108
20188
  try {
20109
- fs37.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
20189
+ fs38.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
20110
20190
  } catch {
20111
20191
  }
20112
20192
  }
20113
20193
  function loadDlpFindings() {
20114
- if (!fs37.existsSync(AUDIT_LOG)) return [];
20115
- return fs37.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
20194
+ if (!fs38.existsSync(AUDIT_LOG)) return [];
20195
+ return fs38.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
20116
20196
  if (!line.trim()) return [];
20117
20197
  try {
20118
20198
  const e = JSON.parse(line);
@@ -20210,20 +20290,20 @@ function registerDlpCommand(program2) {
20210
20290
 
20211
20291
  // src/cli.ts
20212
20292
  var { version } = JSON.parse(
20213
- fs40.readFileSync(path43.join(__dirname, "../package.json"), "utf-8")
20293
+ fs41.readFileSync(path44.join(__dirname, "../package.json"), "utf-8")
20214
20294
  );
20215
20295
  var program = new Command();
20216
20296
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
20217
20297
  program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
20218
20298
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
20219
- const credPath = path43.join(os37.homedir(), ".node9", "credentials.json");
20220
- if (!fs40.existsSync(path43.dirname(credPath)))
20221
- fs40.mkdirSync(path43.dirname(credPath), { recursive: true });
20299
+ const credPath = path44.join(os37.homedir(), ".node9", "credentials.json");
20300
+ if (!fs41.existsSync(path44.dirname(credPath)))
20301
+ fs41.mkdirSync(path44.dirname(credPath), { recursive: true });
20222
20302
  const profileName = options.profile || "default";
20223
20303
  let existingCreds = {};
20224
20304
  try {
20225
- if (fs40.existsSync(credPath)) {
20226
- const raw = JSON.parse(fs40.readFileSync(credPath, "utf-8"));
20305
+ if (fs41.existsSync(credPath)) {
20306
+ const raw = JSON.parse(fs41.readFileSync(credPath, "utf-8"));
20227
20307
  if (raw.apiKey) {
20228
20308
  existingCreds = {
20229
20309
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -20235,13 +20315,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
20235
20315
  } catch {
20236
20316
  }
20237
20317
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
20238
- fs40.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
20318
+ fs41.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
20239
20319
  if (profileName === "default") {
20240
- const configPath = path43.join(os37.homedir(), ".node9", "config.json");
20320
+ const configPath = path44.join(os37.homedir(), ".node9", "config.json");
20241
20321
  let config = {};
20242
20322
  try {
20243
- if (fs40.existsSync(configPath))
20244
- config = JSON.parse(fs40.readFileSync(configPath, "utf-8"));
20323
+ if (fs41.existsSync(configPath))
20324
+ config = JSON.parse(fs41.readFileSync(configPath, "utf-8"));
20245
20325
  } catch {
20246
20326
  }
20247
20327
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -20256,9 +20336,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
20256
20336
  approvers.cloud = false;
20257
20337
  }
20258
20338
  s.approvers = approvers;
20259
- if (!fs40.existsSync(path43.dirname(configPath)))
20260
- fs40.mkdirSync(path43.dirname(configPath), { recursive: true });
20261
- fs40.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
20339
+ if (!fs41.existsSync(path44.dirname(configPath)))
20340
+ fs41.mkdirSync(path44.dirname(configPath), { recursive: true });
20341
+ fs41.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
20262
20342
  }
20263
20343
  if (options.profile && profileName !== "default") {
20264
20344
  console.log(chalk26.green(`\u2705 Profile "${profileName}" saved`));
@@ -20395,15 +20475,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
20395
20475
  }
20396
20476
  }
20397
20477
  if (options.purge) {
20398
- const node9Dir = path43.join(os37.homedir(), ".node9");
20399
- if (fs40.existsSync(node9Dir)) {
20478
+ const node9Dir = path44.join(os37.homedir(), ".node9");
20479
+ if (fs41.existsSync(node9Dir)) {
20400
20480
  const confirmed = await confirm2({
20401
20481
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
20402
20482
  default: false
20403
20483
  });
20404
20484
  if (confirmed) {
20405
- fs40.rmSync(node9Dir, { recursive: true });
20406
- if (fs40.existsSync(node9Dir)) {
20485
+ fs41.rmSync(node9Dir, { recursive: true });
20486
+ if (fs41.existsSync(node9Dir)) {
20407
20487
  console.error(
20408
20488
  chalk26.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
20409
20489
  );
@@ -20545,14 +20625,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
20545
20625
  Run "node9 addto claude" to register it as the statusLine.`
20546
20626
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
20547
20627
  if (subcommand === "debug") {
20548
- const flagFile = path43.join(os37.homedir(), ".node9", "hud-debug");
20628
+ const flagFile = path44.join(os37.homedir(), ".node9", "hud-debug");
20549
20629
  if (state === "on") {
20550
- fs40.mkdirSync(path43.dirname(flagFile), { recursive: true });
20551
- fs40.writeFileSync(flagFile, "");
20630
+ fs41.mkdirSync(path44.dirname(flagFile), { recursive: true });
20631
+ fs41.writeFileSync(flagFile, "");
20552
20632
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
20553
20633
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
20554
20634
  } else if (state === "off") {
20555
- if (fs40.existsSync(flagFile)) fs40.unlinkSync(flagFile);
20635
+ if (fs41.existsSync(flagFile)) fs41.unlinkSync(flagFile);
20556
20636
  console.log("HUD debug logging disabled.");
20557
20637
  } else {
20558
20638
  console.error("Usage: node9 hud debug on|off");
@@ -20664,9 +20744,9 @@ if (process.argv[2] !== "daemon") {
20664
20744
  const isCheckHook = process.argv[2] === "check";
20665
20745
  if (isCheckHook) {
20666
20746
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
20667
- const logPath = path43.join(os37.homedir(), ".node9", "hook-debug.log");
20747
+ const logPath = path44.join(os37.homedir(), ".node9", "hook-debug.log");
20668
20748
  const msg = reason instanceof Error ? reason.message : String(reason);
20669
- fs40.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
20749
+ fs41.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
20670
20750
  `);
20671
20751
  }
20672
20752
  process.exit(0);