@node9/proxy 1.34.0 → 1.35.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.
Files changed (3) hide show
  1. package/dist/cli.js +596 -355
  2. package/dist/cli.mjs +592 -350
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -185,8 +185,8 @@ function sanitizeConfig(raw) {
185
185
  }
186
186
  }
187
187
  const lines = result.error.issues.map((issue) => {
188
- const path54 = issue.path.length > 0 ? issue.path.join(".") : "root";
189
- return ` \u2022 ${path54}: ${issue.message}`;
188
+ const path55 = issue.path.length > 0 ? issue.path.join(".") : "root";
189
+ return ` \u2022 ${path55}: ${issue.message}`;
190
190
  });
191
191
  return {
192
192
  sanitized,
@@ -1240,9 +1240,9 @@ function matchesPattern(text, patterns) {
1240
1240
  const withoutDotSlash = text.replace(/^\.\//, "");
1241
1241
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1242
1242
  }
1243
- function getNestedValue(obj, path54) {
1243
+ function getNestedValue(obj, path55) {
1244
1244
  if (!obj || typeof obj !== "object") return null;
1245
- const segments = path54.split(".");
1245
+ const segments = path55.split(".");
1246
1246
  for (const seg of segments) {
1247
1247
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1248
1248
  }
@@ -5869,6 +5869,57 @@ function validateApiUrl(raw) {
5869
5869
  }
5870
5870
  return null;
5871
5871
  }
5872
+ function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
5873
+ const validated = validateApiUrl(creds.apiUrl);
5874
+ if (!validated) {
5875
+ try {
5876
+ fs10.appendFileSync(
5877
+ HOOK_DEBUG_LOG,
5878
+ `[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
5879
+ `
5880
+ );
5881
+ } catch {
5882
+ }
5883
+ return Promise.resolve();
5884
+ }
5885
+ const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
5886
+ const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
5887
+ const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
5888
+ const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
5889
+ const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
5890
+ Object.entries(riskMetadata).filter(
5891
+ ([, v]) => typeof v === "string" && v.length > 0 || typeof v === "number" && Number.isFinite(v)
5892
+ )
5893
+ ) : void 0;
5894
+ const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
5895
+ return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
5896
+ method: "POST",
5897
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
5898
+ body: JSON.stringify({
5899
+ toolName,
5900
+ args: safeArgs,
5901
+ checkedBy: safeCheckedBy,
5902
+ ...dlpInfo && { dlpPattern, dlpSample },
5903
+ ...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
5904
+ // session_id (Claude Code + Gemini CLI) groups all audit rows from one
5905
+ // agent run; transcript_path is the authoritative pointer to the
5906
+ // session log (survives Gemini resume drift). Both optional —
5907
+ // unsupported agents (MCP-mediated) leave them undefined.
5908
+ ...meta?.sessionId && { runId: meta.sessionId },
5909
+ ...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
5910
+ context: {
5911
+ agent: meta?.agent,
5912
+ mcpServer: meta?.mcpServer,
5913
+ hostname: os9.hostname(),
5914
+ cwd: process.cwd(),
5915
+ platform: os9.platform()
5916
+ }
5917
+ }),
5918
+ signal: AbortSignal.timeout(5e3)
5919
+ }).then(() => {
5920
+ }).catch(() => {
5921
+ });
5922
+ }
5872
5923
  async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
5873
5924
  const controller = new AbortController();
5874
5925
  const timeout = setTimeout(() => controller.abort(), 1e4);
@@ -5992,10 +6043,44 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
5992
6043
  );
5993
6044
  }
5994
6045
  }
6046
+ var DLP_SAMPLE_MAX_LEN, DLP_PATTERN_MAX_LEN, KNOWN_CHECKED_BY;
5995
6047
  var init_cloud = __esm({
5996
6048
  "src/auth/cloud.ts"() {
5997
6049
  "use strict";
5998
6050
  init_audit();
6051
+ DLP_SAMPLE_MAX_LEN = 200;
6052
+ DLP_PATTERN_MAX_LEN = 100;
6053
+ KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
6054
+ "dlp-block",
6055
+ "observe-mode-dlp-would-block",
6056
+ "dlp-review-flagged",
6057
+ "loop-detected",
6058
+ "audit-mode",
6059
+ "local-policy",
6060
+ "smart-rule-block",
6061
+ // Smart-rule block was downgraded to review because the daemon was
6062
+ // running and we're not in CI. The block attempt is still recorded;
6063
+ // the user got a popup. Distinct from 'smart-rule-block' so the
6064
+ // dashboard can show "block rule overridden" separately from a hard
6065
+ // block that fired with no human in the loop.
6066
+ "smart-rule-block-override",
6067
+ "persistent",
6068
+ "trust",
6069
+ "observe-mode",
6070
+ "observe-mode-would-block",
6071
+ // MCP supply-chain: the gateway pinned a server's tool definitions and they
6072
+ // changed since (possible tool poisoning / rug pull). Emitted as a synthetic
6073
+ // audit row so the SaaS surfaces it as a blocked event. The firewall maps
6074
+ // this checkedBy to AUTO_BLOCKED. See doc/roadmap/active/saas-value-first.md
6075
+ // (workstream B-Tier2).
6076
+ "mcp-pin-mismatch",
6077
+ // MCP visibility (B-Tier2, informational — NOT blocks): the gateway
6078
+ // discovered a server's tool inventory (mcp-discovered) or saw an oversized
6079
+ // tool response that bloats the context window (mcp-large-response). Stored
6080
+ // AUTO_ALLOWED; carries mcpToolCount / mcpResponseBytes in riskMetadata.
6081
+ "mcp-discovered",
6082
+ "mcp-large-response"
6083
+ ]);
5999
6084
  }
6000
6085
  });
6001
6086
 
@@ -16819,9 +16904,9 @@ __export(tail_exports, {
16819
16904
  });
16820
16905
  import http2 from "http";
16821
16906
  import chalk29 from "chalk";
16822
- import fs50 from "fs";
16823
- import os45 from "os";
16824
- import path51 from "path";
16907
+ import fs51 from "fs";
16908
+ import os46 from "os";
16909
+ import path52 from "path";
16825
16910
  import readline6 from "readline";
16826
16911
  import { spawn as spawn8 } from "child_process";
16827
16912
  function shortenPathSummary(s) {
@@ -16845,20 +16930,20 @@ function getModelContextLimit(model) {
16845
16930
  return 2e5;
16846
16931
  }
16847
16932
  function readSessionUsage() {
16848
- const projectsDir = path51.join(os45.homedir(), ".claude", "projects");
16849
- if (!fs50.existsSync(projectsDir)) return null;
16933
+ const projectsDir = path52.join(os46.homedir(), ".claude", "projects");
16934
+ if (!fs51.existsSync(projectsDir)) return null;
16850
16935
  let latestFile = null;
16851
16936
  let latestMtime = 0;
16852
16937
  try {
16853
- for (const dir of fs50.readdirSync(projectsDir)) {
16854
- const dirPath = path51.join(projectsDir, dir);
16938
+ for (const dir of fs51.readdirSync(projectsDir)) {
16939
+ const dirPath = path52.join(projectsDir, dir);
16855
16940
  try {
16856
- if (!fs50.statSync(dirPath).isDirectory()) continue;
16857
- for (const file of fs50.readdirSync(dirPath)) {
16941
+ if (!fs51.statSync(dirPath).isDirectory()) continue;
16942
+ for (const file of fs51.readdirSync(dirPath)) {
16858
16943
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
16859
- const filePath = path51.join(dirPath, file);
16944
+ const filePath = path52.join(dirPath, file);
16860
16945
  try {
16861
- const mtime = fs50.statSync(filePath).mtimeMs;
16946
+ const mtime = fs51.statSync(filePath).mtimeMs;
16862
16947
  if (mtime > latestMtime) {
16863
16948
  latestMtime = mtime;
16864
16949
  latestFile = filePath;
@@ -16873,7 +16958,7 @@ function readSessionUsage() {
16873
16958
  }
16874
16959
  if (!latestFile) return null;
16875
16960
  try {
16876
- const lines = fs50.readFileSync(latestFile, "utf-8").split("\n");
16961
+ const lines = fs51.readFileSync(latestFile, "utf-8").split("\n");
16877
16962
  let lastModel = "";
16878
16963
  let lastInput = 0;
16879
16964
  let lastOutput = 0;
@@ -16934,7 +17019,7 @@ function formatBase(activity) {
16934
17019
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
16935
17020
  const icon = getIcon(activity.tool);
16936
17021
  const toolName = activity.tool.slice(0, 16).padEnd(16);
16937
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os45.homedir(), "~");
17022
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os46.homedir(), "~");
16938
17023
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
16939
17024
  return `${chalk29.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk29.white.bold(toolName)} ${chalk29.dim(argsPreview)}`;
16940
17025
  }
@@ -16973,9 +17058,9 @@ function renderPending(activity) {
16973
17058
  }
16974
17059
  async function ensureDaemon() {
16975
17060
  let pidPort = null;
16976
- if (fs50.existsSync(PID_FILE)) {
17061
+ if (fs51.existsSync(PID_FILE)) {
16977
17062
  try {
16978
- const { port } = JSON.parse(fs50.readFileSync(PID_FILE, "utf-8"));
17063
+ const { port } = JSON.parse(fs51.readFileSync(PID_FILE, "utf-8"));
16979
17064
  pidPort = port;
16980
17065
  } catch {
16981
17066
  console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -17131,9 +17216,9 @@ function buildRecoveryCardLines(req) {
17131
17216
  ];
17132
17217
  }
17133
17218
  function readApproversFromDisk() {
17134
- const configPath = path51.join(os45.homedir(), ".node9", "config.json");
17219
+ const configPath = path52.join(os46.homedir(), ".node9", "config.json");
17135
17220
  try {
17136
- const raw = JSON.parse(fs50.readFileSync(configPath, "utf-8"));
17221
+ const raw = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
17137
17222
  const settings = raw.settings ?? {};
17138
17223
  return settings.approvers ?? {};
17139
17224
  } catch {
@@ -17149,15 +17234,15 @@ function approverStatusLine() {
17149
17234
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
17150
17235
  }
17151
17236
  function toggleApprover(channel) {
17152
- const configPath = path51.join(os45.homedir(), ".node9", "config.json");
17237
+ const configPath = path52.join(os46.homedir(), ".node9", "config.json");
17153
17238
  try {
17154
- const raw = JSON.parse(fs50.readFileSync(configPath, "utf-8"));
17239
+ const raw = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
17155
17240
  const settings = raw.settings ?? {};
17156
17241
  const approvers = settings.approvers ?? {};
17157
17242
  approvers[channel] = approvers[channel] === false;
17158
17243
  settings.approvers = approvers;
17159
17244
  raw.settings = settings;
17160
- fs50.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
17245
+ fs51.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
17161
17246
  } catch (err2) {
17162
17247
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
17163
17248
  `);
@@ -17329,8 +17414,8 @@ async function startTail(options = {}) {
17329
17414
  }
17330
17415
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
17331
17416
  try {
17332
- fs50.appendFileSync(
17333
- path51.join(os45.homedir(), ".node9", "hook-debug.log"),
17417
+ fs51.appendFileSync(
17418
+ path52.join(os46.homedir(), ".node9", "hook-debug.log"),
17334
17419
  `[tail] POST /decision failed: ${String(err2)}
17335
17420
  `
17336
17421
  );
@@ -17394,9 +17479,9 @@ async function startTail(options = {}) {
17394
17479
  };
17395
17480
  process.stdin.on("keypress", onKeypress);
17396
17481
  }
17397
- const auditLog = path51.join(os45.homedir(), ".node9", "audit.log");
17482
+ const auditLog = path52.join(os46.homedir(), ".node9", "audit.log");
17398
17483
  try {
17399
- const unackedDlp = fs50.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17484
+ const unackedDlp = fs51.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17400
17485
  if (unackedDlp > 0) {
17401
17486
  console.log("");
17402
17487
  console.log(
@@ -17436,7 +17521,7 @@ async function startTail(options = {}) {
17436
17521
  if (stallWarned) return;
17437
17522
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
17438
17523
  try {
17439
- const auditMtime = fs50.statSync(auditLog).mtimeMs;
17524
+ const auditMtime = fs51.statSync(auditLog).mtimeMs;
17440
17525
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
17441
17526
  console.log("");
17442
17527
  console.log(
@@ -17627,7 +17712,7 @@ var init_tail = __esm({
17627
17712
  "use strict";
17628
17713
  init_daemon2();
17629
17714
  init_daemon();
17630
- PID_FILE = path51.join(os45.homedir(), ".node9", "daemon.pid");
17715
+ PID_FILE = path52.join(os46.homedir(), ".node9", "daemon.pid");
17631
17716
  ICONS = {
17632
17717
  bash: "\u{1F4BB}",
17633
17718
  shell: "\u{1F4BB}",
@@ -17675,9 +17760,9 @@ __export(hud_exports, {
17675
17760
  main: () => main,
17676
17761
  renderEnvironmentLine: () => renderEnvironmentLine
17677
17762
  });
17678
- import fs51 from "fs";
17679
- import path52 from "path";
17680
- import os46 from "os";
17763
+ import fs52 from "fs";
17764
+ import path53 from "path";
17765
+ import os47 from "os";
17681
17766
  import http3 from "http";
17682
17767
  async function readStdin() {
17683
17768
  const chunks = [];
@@ -17753,9 +17838,9 @@ function formatTimeLeft(resetsAt) {
17753
17838
  return ` (${m}m left)`;
17754
17839
  }
17755
17840
  function safeReadJson(filePath) {
17756
- if (!fs51.existsSync(filePath)) return null;
17841
+ if (!fs52.existsSync(filePath)) return null;
17757
17842
  try {
17758
- return JSON.parse(fs51.readFileSync(filePath, "utf-8"));
17843
+ return JSON.parse(fs52.readFileSync(filePath, "utf-8"));
17759
17844
  } catch {
17760
17845
  return null;
17761
17846
  }
@@ -17776,12 +17861,12 @@ function countHooksInFile(filePath) {
17776
17861
  return Object.keys(cfg.hooks).length;
17777
17862
  }
17778
17863
  function countRulesInDir(rulesDir) {
17779
- if (!fs51.existsSync(rulesDir)) return 0;
17864
+ if (!fs52.existsSync(rulesDir)) return 0;
17780
17865
  let count = 0;
17781
17866
  try {
17782
- for (const entry of fs51.readdirSync(rulesDir, { withFileTypes: true })) {
17867
+ for (const entry of fs52.readdirSync(rulesDir, { withFileTypes: true })) {
17783
17868
  if (entry.isDirectory()) {
17784
- count += countRulesInDir(path52.join(rulesDir, entry.name));
17869
+ count += countRulesInDir(path53.join(rulesDir, entry.name));
17785
17870
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
17786
17871
  count++;
17787
17872
  }
@@ -17792,46 +17877,46 @@ function countRulesInDir(rulesDir) {
17792
17877
  }
17793
17878
  function isSamePath(a, b) {
17794
17879
  try {
17795
- return path52.resolve(a) === path52.resolve(b);
17880
+ return path53.resolve(a) === path53.resolve(b);
17796
17881
  } catch {
17797
17882
  return false;
17798
17883
  }
17799
17884
  }
17800
17885
  function countConfigs(cwd) {
17801
- const homeDir2 = os46.homedir();
17802
- const claudeDir = path52.join(homeDir2, ".claude");
17886
+ const homeDir2 = os47.homedir();
17887
+ const claudeDir = path53.join(homeDir2, ".claude");
17803
17888
  let claudeMdCount = 0;
17804
17889
  let rulesCount = 0;
17805
17890
  let hooksCount = 0;
17806
17891
  const userMcpServers = /* @__PURE__ */ new Set();
17807
17892
  const projectMcpServers = /* @__PURE__ */ new Set();
17808
- if (fs51.existsSync(path52.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
17809
- rulesCount += countRulesInDir(path52.join(claudeDir, "rules"));
17810
- const userSettings = path52.join(claudeDir, "settings.json");
17893
+ if (fs52.existsSync(path53.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
17894
+ rulesCount += countRulesInDir(path53.join(claudeDir, "rules"));
17895
+ const userSettings = path53.join(claudeDir, "settings.json");
17811
17896
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
17812
17897
  hooksCount += countHooksInFile(userSettings);
17813
- const userClaudeJson = path52.join(homeDir2, ".claude.json");
17898
+ const userClaudeJson = path53.join(homeDir2, ".claude.json");
17814
17899
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
17815
17900
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
17816
17901
  userMcpServers.delete(name);
17817
17902
  }
17818
17903
  if (cwd) {
17819
- if (fs51.existsSync(path52.join(cwd, "CLAUDE.md"))) claudeMdCount++;
17820
- if (fs51.existsSync(path52.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
17821
- const projectClaudeDir = path52.join(cwd, ".claude");
17904
+ if (fs52.existsSync(path53.join(cwd, "CLAUDE.md"))) claudeMdCount++;
17905
+ if (fs52.existsSync(path53.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
17906
+ const projectClaudeDir = path53.join(cwd, ".claude");
17822
17907
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
17823
17908
  if (!overlapsUserScope) {
17824
- if (fs51.existsSync(path52.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
17825
- rulesCount += countRulesInDir(path52.join(projectClaudeDir, "rules"));
17826
- const projSettings = path52.join(projectClaudeDir, "settings.json");
17909
+ if (fs52.existsSync(path53.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
17910
+ rulesCount += countRulesInDir(path53.join(projectClaudeDir, "rules"));
17911
+ const projSettings = path53.join(projectClaudeDir, "settings.json");
17827
17912
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
17828
17913
  hooksCount += countHooksInFile(projSettings);
17829
17914
  }
17830
- if (fs51.existsSync(path52.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
17831
- const localSettings = path52.join(projectClaudeDir, "settings.local.json");
17915
+ if (fs52.existsSync(path53.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
17916
+ const localSettings = path53.join(projectClaudeDir, "settings.local.json");
17832
17917
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
17833
17918
  hooksCount += countHooksInFile(localSettings);
17834
- const mcpJsonServers = getMcpServerNames(path52.join(cwd, ".mcp.json"));
17919
+ const mcpJsonServers = getMcpServerNames(path53.join(cwd, ".mcp.json"));
17835
17920
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
17836
17921
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
17837
17922
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -17864,12 +17949,12 @@ function readActiveShieldsHud() {
17864
17949
  return shieldsCache.value;
17865
17950
  }
17866
17951
  try {
17867
- const shieldsPath = path52.join(os46.homedir(), ".node9", "shields.json");
17868
- if (!fs51.existsSync(shieldsPath)) {
17952
+ const shieldsPath = path53.join(os47.homedir(), ".node9", "shields.json");
17953
+ if (!fs52.existsSync(shieldsPath)) {
17869
17954
  shieldsCache = { value: [], ts: now };
17870
17955
  return [];
17871
17956
  }
17872
- const parsed = JSON.parse(fs51.readFileSync(shieldsPath, "utf-8"));
17957
+ const parsed = JSON.parse(fs52.readFileSync(shieldsPath, "utf-8"));
17873
17958
  if (!Array.isArray(parsed.active)) {
17874
17959
  shieldsCache = { value: [], ts: now };
17875
17960
  return [];
@@ -17971,17 +18056,17 @@ function renderContextLine(stdin) {
17971
18056
  async function main() {
17972
18057
  try {
17973
18058
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
17974
- if (fs51.existsSync(path52.join(os46.homedir(), ".node9", "hud-debug"))) {
18059
+ if (fs52.existsSync(path53.join(os47.homedir(), ".node9", "hud-debug"))) {
17975
18060
  try {
17976
- const logPath = path52.join(os46.homedir(), ".node9", "hud-debug.log");
18061
+ const logPath = path53.join(os47.homedir(), ".node9", "hud-debug.log");
17977
18062
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
17978
18063
  let size = 0;
17979
18064
  try {
17980
- size = fs51.statSync(logPath).size;
18065
+ size = fs52.statSync(logPath).size;
17981
18066
  } catch {
17982
18067
  }
17983
18068
  if (size < MAX_LOG_SIZE) {
17984
- fs51.appendFileSync(
18069
+ fs52.appendFileSync(
17985
18070
  logPath,
17986
18071
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
17987
18072
  );
@@ -18002,11 +18087,11 @@ async function main() {
18002
18087
  try {
18003
18088
  const cwd = stdin.cwd ?? process.cwd();
18004
18089
  for (const configPath of [
18005
- path52.join(cwd, "node9.config.json"),
18006
- path52.join(os46.homedir(), ".node9", "config.json")
18090
+ path53.join(cwd, "node9.config.json"),
18091
+ path53.join(os47.homedir(), ".node9", "config.json")
18007
18092
  ]) {
18008
- if (!fs51.existsSync(configPath)) continue;
18009
- const cfg = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
18093
+ if (!fs52.existsSync(configPath)) continue;
18094
+ const cfg = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
18010
18095
  const hud = cfg.settings?.hud;
18011
18096
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
18012
18097
  }
@@ -18053,9 +18138,9 @@ init_setup();
18053
18138
  init_daemon2();
18054
18139
  import { Command } from "commander";
18055
18140
  import chalk30 from "chalk";
18056
- import fs52 from "fs";
18057
- import path53 from "path";
18058
- import os47 from "os";
18141
+ import fs53 from "fs";
18142
+ import path54 from "path";
18143
+ import os48 from "os";
18059
18144
  import { confirm as confirm2 } from "@inquirer/prompts";
18060
18145
 
18061
18146
  // src/utils/duration.ts
@@ -19845,13 +19930,149 @@ function registerConfigShowCommand(program2) {
19845
19930
  init_daemon();
19846
19931
  init_config();
19847
19932
  import chalk11 from "chalk";
19933
+ import fs39 from "fs";
19934
+ import path40 from "path";
19935
+ import os35 from "os";
19936
+ import { execSync } from "child_process";
19937
+
19938
+ // src/agent-wiring.ts
19939
+ init_setup();
19848
19940
  import fs38 from "fs";
19849
19941
  import path39 from "path";
19850
19942
  import os34 from "os";
19851
- import { execSync } from "child_process";
19943
+ import * as yaml2 from "yaml";
19944
+ function readJson2(filePath) {
19945
+ if (!fs38.existsSync(filePath)) return null;
19946
+ try {
19947
+ return JSON.parse(fs38.readFileSync(filePath, "utf-8"));
19948
+ } catch {
19949
+ return "invalid";
19950
+ }
19951
+ }
19952
+ function matchersHaveNode9Hook(matchers) {
19953
+ return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
19954
+ }
19955
+ function flatHaveNode9Hook(entries) {
19956
+ return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
19957
+ }
19958
+ function jsonWire(filePath, read) {
19959
+ const parsed = readJson2(filePath);
19960
+ if (parsed === null) return "absent";
19961
+ if (parsed === "invalid") return "invalid";
19962
+ return read(parsed) ? "wired" : "unwired";
19963
+ }
19964
+ function hermesWire(home) {
19965
+ const configPath = hermesConfigPath(home);
19966
+ if (!fs38.existsSync(configPath)) return "absent";
19967
+ let raw;
19968
+ try {
19969
+ raw = fs38.readFileSync(configPath, "utf-8");
19970
+ } catch {
19971
+ return "absent";
19972
+ }
19973
+ try {
19974
+ const cfg = yaml2.parse(raw);
19975
+ const pre = (cfg?.hooks?.pre_tool_call ?? []).some(
19976
+ (e) => typeof e?.command === "string" && isNode9Hook(e.command)
19977
+ );
19978
+ return pre ? "wired" : "unwired";
19979
+ } catch {
19980
+ return "invalid";
19981
+ }
19982
+ }
19983
+ var AGENT_SPECS = [
19984
+ {
19985
+ id: "claude",
19986
+ label: "Claude Code",
19987
+ hookLabel: "PreToolUse hook",
19988
+ setupCommand: "node9 setup claude",
19989
+ settingsPath: (h) => path39.join(h, ".claude", "settings.json"),
19990
+ wireState: (h) => jsonWire(
19991
+ path39.join(h, ".claude", "settings.json"),
19992
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
19993
+ )
19994
+ },
19995
+ {
19996
+ id: "gemini",
19997
+ label: "Gemini CLI",
19998
+ hookLabel: "BeforeTool hook",
19999
+ setupCommand: "node9 setup gemini",
20000
+ settingsPath: (h) => path39.join(h, ".gemini", "settings.json"),
20001
+ wireState: (h) => jsonWire(
20002
+ path39.join(h, ".gemini", "settings.json"),
20003
+ (p) => matchersHaveNode9Hook(p.hooks?.BeforeTool)
20004
+ )
20005
+ },
20006
+ {
20007
+ id: "codex",
20008
+ label: "Codex",
20009
+ hookLabel: "PreToolUse hook",
20010
+ setupCommand: "node9 setup codex",
20011
+ settingsPath: (h) => path39.join(h, ".codex", "hooks.json"),
20012
+ wireState: (h) => jsonWire(
20013
+ path39.join(h, ".codex", "hooks.json"),
20014
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
20015
+ )
20016
+ },
20017
+ {
20018
+ id: "antigravity",
20019
+ label: "Antigravity",
20020
+ hookLabel: "PreToolUse hook",
20021
+ setupCommand: "node9 setup antigravity",
20022
+ settingsPath: (h) => path39.join(h, ".gemini", "config", "hooks.json"),
20023
+ wireState: (h) => jsonWire(
20024
+ path39.join(h, ".gemini", "config", "hooks.json"),
20025
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
20026
+ )
20027
+ },
20028
+ {
20029
+ id: "copilot",
20030
+ label: "GitHub Copilot",
20031
+ hookLabel: "PreToolUse hook",
20032
+ setupCommand: "node9 setup copilot",
20033
+ settingsPath: (h) => path39.join(h, ".copilot", "hooks", "node9.json"),
20034
+ wireState: (h) => jsonWire(
20035
+ path39.join(h, ".copilot", "hooks", "node9.json"),
20036
+ (p) => flatHaveNode9Hook(p.hooks?.PreToolUse)
20037
+ )
20038
+ },
20039
+ {
20040
+ id: "cursor",
20041
+ label: "Cursor",
20042
+ hookLabel: "preToolUse hook",
20043
+ setupCommand: "node9 setup cursor",
20044
+ settingsPath: (h) => path39.join(h, ".cursor", "hooks.json"),
20045
+ wireState: (h) => jsonWire(
20046
+ path39.join(h, ".cursor", "hooks.json"),
20047
+ (p) => flatHaveNode9Hook(p.hooks?.preToolUse)
20048
+ )
20049
+ },
20050
+ {
20051
+ id: "hermes",
20052
+ label: "Hermes Agent",
20053
+ hookLabel: "pre_tool_call hook",
20054
+ setupCommand: "node9 setup hermes",
20055
+ settingsPath: (h) => hermesConfigPath(h),
20056
+ wireState: (h) => hermesWire(h)
20057
+ }
20058
+ ];
20059
+ function getAgentWiring(home = os34.homedir()) {
20060
+ const detected = detectAgents(home);
20061
+ return AGENT_SPECS.map((spec) => ({
20062
+ id: spec.id,
20063
+ label: spec.label,
20064
+ hookLabel: spec.hookLabel,
20065
+ setupCommand: spec.setupCommand,
20066
+ settingsPath: spec.settingsPath(home),
20067
+ installed: detected[spec.id],
20068
+ wireState: spec.wireState(home)
20069
+ }));
20070
+ }
20071
+
20072
+ // src/cli/commands/doctor.ts
19852
20073
  function registerDoctorCommand(program2, version2) {
19853
20074
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
19854
- const homeDir2 = os34.homedir();
20075
+ const homeDir2 = os35.homedir();
19855
20076
  let failures = 0;
19856
20077
  function pass(msg) {
19857
20078
  console.log(chalk11.green(" \u2705 ") + msg);
@@ -19900,10 +20121,10 @@ function registerDoctorCommand(program2, version2) {
19900
20121
  );
19901
20122
  }
19902
20123
  section("Configuration");
19903
- const globalConfigPath = path39.join(homeDir2, ".node9", "config.json");
19904
- if (fs38.existsSync(globalConfigPath)) {
20124
+ const globalConfigPath = path40.join(homeDir2, ".node9", "config.json");
20125
+ if (fs39.existsSync(globalConfigPath)) {
19905
20126
  try {
19906
- JSON.parse(fs38.readFileSync(globalConfigPath, "utf-8"));
20127
+ JSON.parse(fs39.readFileSync(globalConfigPath, "utf-8"));
19907
20128
  pass("~/.node9/config.json found and valid");
19908
20129
  } catch {
19909
20130
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -19911,10 +20132,10 @@ function registerDoctorCommand(program2, version2) {
19911
20132
  } else {
19912
20133
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
19913
20134
  }
19914
- const projectConfigPath = path39.join(process.cwd(), "node9.config.json");
19915
- if (fs38.existsSync(projectConfigPath)) {
20135
+ const projectConfigPath = path40.join(process.cwd(), "node9.config.json");
20136
+ if (fs39.existsSync(projectConfigPath)) {
19916
20137
  try {
19917
- JSON.parse(fs38.readFileSync(projectConfigPath, "utf-8"));
20138
+ JSON.parse(fs39.readFileSync(projectConfigPath, "utf-8"));
19918
20139
  pass("node9.config.json found and valid (project)");
19919
20140
  } catch {
19920
20141
  fail(
@@ -19923,8 +20144,8 @@ function registerDoctorCommand(program2, version2) {
19923
20144
  );
19924
20145
  }
19925
20146
  }
19926
- const credsPath = path39.join(homeDir2, ".node9", "credentials.json");
19927
- if (fs38.existsSync(credsPath)) {
20147
+ const credsPath = path40.join(homeDir2, ".node9", "credentials.json");
20148
+ if (fs39.existsSync(credsPath)) {
19928
20149
  pass("Cloud credentials found (~/.node9/credentials.json)");
19929
20150
  } else {
19930
20151
  warn(
@@ -19933,62 +20154,24 @@ function registerDoctorCommand(program2, version2) {
19933
20154
  );
19934
20155
  }
19935
20156
  section("Agent Hooks");
19936
- const claudeSettingsPath = path39.join(homeDir2, ".claude", "settings.json");
19937
- if (fs38.existsSync(claudeSettingsPath)) {
19938
- try {
19939
- const cs = JSON.parse(fs38.readFileSync(claudeSettingsPath, "utf-8"));
19940
- const hasHook = cs.hooks?.PreToolUse?.some(
19941
- (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
19942
- );
19943
- if (hasHook) pass("Claude Code \u2014 PreToolUse hook active");
19944
- else
19945
- fail(
19946
- "Claude Code \u2014 hooks file found but node9 hook missing",
19947
- "Run: node9 setup claude"
19948
- );
19949
- } catch {
19950
- fail("Claude Code \u2014 ~/.claude/settings.json is invalid JSON");
19951
- }
19952
- } else {
19953
- warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
19954
- }
19955
- const geminiSettingsPath = path39.join(homeDir2, ".gemini", "settings.json");
19956
- if (fs38.existsSync(geminiSettingsPath)) {
19957
- try {
19958
- const gs = JSON.parse(fs38.readFileSync(geminiSettingsPath, "utf-8"));
19959
- const hasHook = gs.hooks?.BeforeTool?.some(
19960
- (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
19961
- );
19962
- if (hasHook) pass("Gemini CLI \u2014 BeforeTool hook active");
19963
- else
19964
- fail(
19965
- "Gemini CLI \u2014 hooks file found but node9 hook missing",
19966
- "Run: node9 setup gemini"
19967
- );
19968
- } catch {
19969
- fail("Gemini CLI \u2014 ~/.gemini/settings.json is invalid JSON");
20157
+ const notConfigured = [];
20158
+ for (const a of getAgentWiring(homeDir2)) {
20159
+ if (a.wireState === "wired") {
20160
+ pass(`${a.label} \u2014 ${a.hookLabel} active`);
20161
+ } else if (a.wireState === "unwired") {
20162
+ fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
20163
+ } else if (a.wireState === "invalid") {
20164
+ fail(`${a.label} \u2014 settings file is invalid JSON`, a.settingsPath);
20165
+ } else {
20166
+ notConfigured.push(a.label);
19970
20167
  }
19971
- } else {
19972
- warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
19973
20168
  }
19974
- const cursorHooksPath = path39.join(homeDir2, ".cursor", "hooks.json");
19975
- if (fs38.existsSync(cursorHooksPath)) {
19976
- try {
19977
- const cur = JSON.parse(fs38.readFileSync(cursorHooksPath, "utf-8"));
19978
- const hasHook = cur.hooks?.preToolUse?.some(
19979
- (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
19980
- );
19981
- if (hasHook) pass("Cursor \u2014 preToolUse hook active");
19982
- else
19983
- fail(
19984
- "Cursor \u2014 hooks file found but node9 hook missing",
19985
- "Run: node9 setup cursor"
19986
- );
19987
- } catch {
19988
- fail("Cursor \u2014 ~/.cursor/hooks.json is invalid JSON");
19989
- }
19990
- } else {
19991
- warn("Cursor \u2014 not configured", "Run: node9 setup cursor (skip if not using Cursor)");
20169
+ if (notConfigured.length > 0) {
20170
+ console.log(
20171
+ chalk11.gray(
20172
+ ` \xB7 Not configured: ${notConfigured.join(", ")} \u2014 run \`node9 setup <agent>\` if you use one`
20173
+ )
20174
+ );
19992
20175
  }
19993
20176
  section("Daemon (optional)");
19994
20177
  if (isDaemonRunning()) {
@@ -20005,7 +20188,7 @@ function registerDoctorCommand(program2, version2) {
20005
20188
  try {
20006
20189
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
20007
20190
  const cfg = getConfig();
20008
- const creds = fs38.existsSync(path39.join(os34.homedir(), ".node9", "credentials.json"));
20191
+ const creds = fs39.existsSync(path40.join(os35.homedir(), ".node9", "credentials.json"));
20009
20192
  if (!creds) {
20010
20193
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
20011
20194
  } else if (!cfg.settings.approvers.cloud) {
@@ -20055,9 +20238,9 @@ function registerDoctorCommand(program2, version2) {
20055
20238
 
20056
20239
  // src/cli/commands/audit.ts
20057
20240
  import chalk12 from "chalk";
20058
- import fs39 from "fs";
20059
- import path40 from "path";
20060
- import os35 from "os";
20241
+ import fs40 from "fs";
20242
+ import path41 from "path";
20243
+ import os36 from "os";
20061
20244
  function formatRelativeTime(timestamp) {
20062
20245
  const diff = Date.now() - new Date(timestamp).getTime();
20063
20246
  const sec = Math.floor(diff / 1e3);
@@ -20070,14 +20253,14 @@ function formatRelativeTime(timestamp) {
20070
20253
  }
20071
20254
  function registerAuditCommand(program2) {
20072
20255
  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) => {
20073
- const logPath = path40.join(os35.homedir(), ".node9", "audit.log");
20074
- if (!fs39.existsSync(logPath)) {
20256
+ const logPath = path41.join(os36.homedir(), ".node9", "audit.log");
20257
+ if (!fs40.existsSync(logPath)) {
20075
20258
  console.log(
20076
20259
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
20077
20260
  );
20078
20261
  return;
20079
20262
  }
20080
- const raw = fs39.readFileSync(logPath, "utf-8");
20263
+ const raw = fs40.readFileSync(logPath, "utf-8");
20081
20264
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
20082
20265
  let entries = lines.flatMap((line) => {
20083
20266
  try {
@@ -20136,9 +20319,9 @@ import chalk13 from "chalk";
20136
20319
  init_costSync();
20137
20320
  init_litellm();
20138
20321
  init_cost_codex();
20139
- import fs40 from "fs";
20140
- import os36 from "os";
20141
- import path41 from "path";
20322
+ import fs41 from "fs";
20323
+ import os37 from "os";
20324
+ import path42 from "path";
20142
20325
  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;
20143
20326
  function buildTestTimestamps(allEntries) {
20144
20327
  const testTs = /* @__PURE__ */ new Set();
@@ -20218,8 +20401,8 @@ function getDateRange(period, now) {
20218
20401
  }
20219
20402
  }
20220
20403
  function parseAuditLog(logPath) {
20221
- if (!fs40.existsSync(logPath)) return [];
20222
- const raw = fs40.readFileSync(logPath, "utf-8");
20404
+ if (!fs41.existsSync(logPath)) return [];
20405
+ const raw = fs41.readFileSync(logPath, "utf-8");
20223
20406
  return raw.split("\n").flatMap((line) => {
20224
20407
  if (!line.trim()) return [];
20225
20408
  try {
@@ -20266,25 +20449,25 @@ function freezeClaudeCost(acc) {
20266
20449
  };
20267
20450
  }
20268
20451
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
20269
- const projPath = path41.join(projectsDir, proj);
20452
+ const projPath = path42.join(projectsDir, proj);
20270
20453
  let files;
20271
20454
  try {
20272
- const stat = fs40.statSync(projPath);
20455
+ const stat = fs41.statSync(projPath);
20273
20456
  if (!stat.isDirectory()) return;
20274
- files = fs40.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
20457
+ files = fs41.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
20275
20458
  } catch {
20276
20459
  return;
20277
20460
  }
20278
20461
  const startMs = start.getTime();
20279
20462
  for (const file of files) {
20280
- const filePath = path41.join(projPath, file);
20463
+ const filePath = path42.join(projPath, file);
20281
20464
  try {
20282
- if (fs40.statSync(filePath).mtimeMs < startMs) continue;
20465
+ if (fs41.statSync(filePath).mtimeMs < startMs) continue;
20283
20466
  } catch {
20284
20467
  continue;
20285
20468
  }
20286
20469
  try {
20287
- const raw = fs40.readFileSync(filePath, "utf-8");
20470
+ const raw = fs41.readFileSync(filePath, "utf-8");
20288
20471
  for (const line of raw.split("\n")) {
20289
20472
  if (!line.trim()) continue;
20290
20473
  let entry;
@@ -20334,10 +20517,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
20334
20517
  }
20335
20518
  function loadClaudeCost(start, end, projectsDir) {
20336
20519
  const acc = emptyClaudeCostAccumulator();
20337
- if (!fs40.existsSync(projectsDir)) return freezeClaudeCost(acc);
20520
+ if (!fs41.existsSync(projectsDir)) return freezeClaudeCost(acc);
20338
20521
  let dirs;
20339
20522
  try {
20340
- dirs = fs40.readdirSync(projectsDir);
20523
+ dirs = fs41.readdirSync(projectsDir);
20341
20524
  } catch {
20342
20525
  return freezeClaudeCost(acc);
20343
20526
  }
@@ -20349,7 +20532,7 @@ function loadClaudeCost(start, end, projectsDir) {
20349
20532
  function processCodexCostFile(filePath, start, end, acc) {
20350
20533
  let lines;
20351
20534
  try {
20352
- lines = fs40.readFileSync(filePath, "utf-8").split("\n");
20535
+ lines = fs41.readFileSync(filePath, "utf-8").split("\n");
20353
20536
  } catch {
20354
20537
  return;
20355
20538
  }
@@ -20404,31 +20587,31 @@ function processCodexCostFile(filePath, start, end, acc) {
20404
20587
  }
20405
20588
  function listCodexSessionFiles2(sessionsBase) {
20406
20589
  const jsonlFiles = [];
20407
- if (!fs40.existsSync(sessionsBase)) return jsonlFiles;
20590
+ if (!fs41.existsSync(sessionsBase)) return jsonlFiles;
20408
20591
  try {
20409
- for (const year of fs40.readdirSync(sessionsBase)) {
20410
- const yearPath = path41.join(sessionsBase, year);
20592
+ for (const year of fs41.readdirSync(sessionsBase)) {
20593
+ const yearPath = path42.join(sessionsBase, year);
20411
20594
  try {
20412
- if (!fs40.statSync(yearPath).isDirectory()) continue;
20595
+ if (!fs41.statSync(yearPath).isDirectory()) continue;
20413
20596
  } catch {
20414
20597
  continue;
20415
20598
  }
20416
- for (const month of fs40.readdirSync(yearPath)) {
20417
- const monthPath = path41.join(yearPath, month);
20599
+ for (const month of fs41.readdirSync(yearPath)) {
20600
+ const monthPath = path42.join(yearPath, month);
20418
20601
  try {
20419
- if (!fs40.statSync(monthPath).isDirectory()) continue;
20602
+ if (!fs41.statSync(monthPath).isDirectory()) continue;
20420
20603
  } catch {
20421
20604
  continue;
20422
20605
  }
20423
- for (const day of fs40.readdirSync(monthPath)) {
20424
- const dayPath = path41.join(monthPath, day);
20606
+ for (const day of fs41.readdirSync(monthPath)) {
20607
+ const dayPath = path42.join(monthPath, day);
20425
20608
  try {
20426
- if (!fs40.statSync(dayPath).isDirectory()) continue;
20609
+ if (!fs41.statSync(dayPath).isDirectory()) continue;
20427
20610
  } catch {
20428
20611
  continue;
20429
20612
  }
20430
- for (const file of fs40.readdirSync(dayPath)) {
20431
- if (file.endsWith(".jsonl")) jsonlFiles.push(path41.join(dayPath, file));
20613
+ for (const file of fs41.readdirSync(dayPath)) {
20614
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path42.join(dayPath, file));
20432
20615
  }
20433
20616
  }
20434
20617
  }
@@ -20493,13 +20676,13 @@ function freezeGeminiCost(acc) {
20493
20676
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
20494
20677
  const startMs = start.getTime();
20495
20678
  try {
20496
- if (fs40.statSync(filePath).mtimeMs < startMs) return;
20679
+ if (fs41.statSync(filePath).mtimeMs < startMs) return;
20497
20680
  } catch {
20498
20681
  return;
20499
20682
  }
20500
20683
  let raw;
20501
20684
  try {
20502
- raw = fs40.readFileSync(filePath, "utf-8");
20685
+ raw = fs41.readFileSync(filePath, "utf-8");
20503
20686
  } catch {
20504
20687
  return;
20505
20688
  }
@@ -20548,30 +20731,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
20548
20731
  const out = [];
20549
20732
  let dirs;
20550
20733
  try {
20551
- if (!fs40.statSync(geminiTmpDir2).isDirectory()) return out;
20552
- dirs = fs40.readdirSync(geminiTmpDir2);
20734
+ if (!fs41.statSync(geminiTmpDir2).isDirectory()) return out;
20735
+ dirs = fs41.readdirSync(geminiTmpDir2);
20553
20736
  } catch {
20554
20737
  return out;
20555
20738
  }
20556
20739
  for (const proj of dirs) {
20557
- const chatsDir = path41.join(geminiTmpDir2, proj, "chats");
20740
+ const chatsDir = path42.join(geminiTmpDir2, proj, "chats");
20558
20741
  let files;
20559
20742
  try {
20560
- if (!fs40.statSync(chatsDir).isDirectory()) continue;
20561
- files = fs40.readdirSync(chatsDir);
20743
+ if (!fs41.statSync(chatsDir).isDirectory()) continue;
20744
+ files = fs41.readdirSync(chatsDir);
20562
20745
  } catch {
20563
20746
  continue;
20564
20747
  }
20565
20748
  for (const f of files) {
20566
20749
  if (!f.endsWith(".jsonl")) continue;
20567
- out.push({ projectKey: proj, file: path41.join(chatsDir, f) });
20750
+ out.push({ projectKey: proj, file: path42.join(chatsDir, f) });
20568
20751
  }
20569
20752
  }
20570
20753
  return out;
20571
20754
  }
20572
20755
  function loadGeminiCost(start, end, geminiTmpDir2) {
20573
20756
  const acc = emptyGeminiAccumulator();
20574
- if (!fs40.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
20757
+ if (!fs41.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
20575
20758
  for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
20576
20759
  processGeminiCostFile(file, projectKey, start, end, acc);
20577
20760
  }
@@ -20579,11 +20762,11 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
20579
20762
  }
20580
20763
  function aggregateReportFromAudit(period, opts = {}) {
20581
20764
  const now = opts.now ?? /* @__PURE__ */ new Date();
20582
- const auditLogPath = opts.auditLogPath ?? path41.join(os36.homedir(), ".node9", "audit.log");
20583
- const claudeProjectsDir = opts.claudeProjectsDir ?? path41.join(os36.homedir(), ".claude", "projects");
20584
- const codexSessionsDir2 = opts.codexSessionsDir ?? path41.join(os36.homedir(), ".codex", "sessions");
20585
- const geminiTmpDir2 = opts.geminiTmpDir ?? path41.join(os36.homedir(), ".gemini", "tmp");
20586
- const hasAuditFile = fs40.existsSync(auditLogPath);
20765
+ const auditLogPath = opts.auditLogPath ?? path42.join(os37.homedir(), ".node9", "audit.log");
20766
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path42.join(os37.homedir(), ".claude", "projects");
20767
+ const codexSessionsDir2 = opts.codexSessionsDir ?? path42.join(os37.homedir(), ".codex", "sessions");
20768
+ const geminiTmpDir2 = opts.geminiTmpDir ?? path42.join(os37.homedir(), ".gemini", "tmp");
20769
+ const hasAuditFile = fs41.existsSync(auditLogPath);
20587
20770
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
20588
20771
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
20589
20772
  const { start, end } = getDateRange(period, now);
@@ -21282,20 +21465,20 @@ init_core();
21282
21465
  init_daemon();
21283
21466
  init_setup();
21284
21467
  import chalk15 from "chalk";
21285
- import fs41 from "fs";
21286
- import path42 from "path";
21287
- import os37 from "os";
21288
- import * as yaml2 from "yaml";
21468
+ import fs42 from "fs";
21469
+ import path43 from "path";
21470
+ import os38 from "os";
21471
+ import * as yaml3 from "yaml";
21289
21472
  function readHermesHooks(configPath) {
21290
- if (!fs41.existsSync(configPath)) return null;
21473
+ if (!fs42.existsSync(configPath)) return null;
21291
21474
  let raw;
21292
21475
  try {
21293
- raw = fs41.readFileSync(configPath, "utf-8");
21476
+ raw = fs42.readFileSync(configPath, "utf-8");
21294
21477
  } catch {
21295
21478
  return null;
21296
21479
  }
21297
21480
  try {
21298
- const cfg = yaml2.parse(raw);
21481
+ const cfg = yaml3.parse(raw);
21299
21482
  const has = (event) => (cfg?.hooks?.[event] ?? []).some(
21300
21483
  (e) => typeof e?.command === "string" && isNode9Hook(e.command)
21301
21484
  );
@@ -21309,17 +21492,17 @@ function readHermesHooks(configPath) {
21309
21492
  return { pre: false, post: false };
21310
21493
  }
21311
21494
  }
21312
- function readJson2(filePath) {
21495
+ function readJson3(filePath) {
21313
21496
  try {
21314
- if (fs41.existsSync(filePath)) return JSON.parse(fs41.readFileSync(filePath, "utf-8"));
21497
+ if (fs42.existsSync(filePath)) return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
21315
21498
  } catch {
21316
21499
  }
21317
21500
  return null;
21318
21501
  }
21319
- function matchersHaveNode9Hook(matchers) {
21502
+ function matchersHaveNode9Hook2(matchers) {
21320
21503
  return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
21321
21504
  }
21322
- function flatHaveNode9Hook(entries) {
21505
+ function flatHaveNode9Hook2(entries) {
21323
21506
  return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
21324
21507
  }
21325
21508
  function wrappedMcpServers(servers) {
@@ -21379,42 +21562,42 @@ function registerStatusCommand(program2) {
21379
21562
  console.log("");
21380
21563
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
21381
21564
  console.log(` Mode: ${modeLabel}`);
21382
- const projectConfig = path42.join(process.cwd(), "node9.config.json");
21383
- const globalConfig = path42.join(os37.homedir(), ".node9", "config.json");
21565
+ const projectConfig = path43.join(process.cwd(), "node9.config.json");
21566
+ const globalConfig = path43.join(os38.homedir(), ".node9", "config.json");
21384
21567
  console.log(
21385
- ` Local: ${fs41.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
21568
+ ` Local: ${fs42.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
21386
21569
  );
21387
21570
  console.log(
21388
- ` Global: ${fs41.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
21571
+ ` Global: ${fs42.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
21389
21572
  );
21390
21573
  if (mergedConfig.policy.sandboxPaths.length > 0) {
21391
21574
  console.log(
21392
21575
  ` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
21393
21576
  );
21394
21577
  }
21395
- const homeDir2 = os37.homedir();
21396
- const claudeSettings = readJson2(
21397
- path42.join(homeDir2, ".claude", "settings.json")
21578
+ const homeDir2 = os38.homedir();
21579
+ const claudeSettings = readJson3(
21580
+ path43.join(homeDir2, ".claude", "settings.json")
21398
21581
  );
21399
- const claudeConfig = readJson2(path42.join(homeDir2, ".claude.json"));
21400
- const geminiSettings = readJson2(
21401
- path42.join(homeDir2, ".gemini", "settings.json")
21582
+ const claudeConfig = readJson3(path43.join(homeDir2, ".claude.json"));
21583
+ const geminiSettings = readJson3(
21584
+ path43.join(homeDir2, ".gemini", "settings.json")
21402
21585
  );
21403
- const cursorConfig = readJson2(path42.join(homeDir2, ".cursor", "mcp.json"));
21404
- const antigravityHooks = readJson2(
21405
- path42.join(homeDir2, ".gemini", "config", "hooks.json")
21586
+ const cursorConfig = readJson3(path43.join(homeDir2, ".cursor", "mcp.json"));
21587
+ const antigravityHooks = readJson3(
21588
+ path43.join(homeDir2, ".gemini", "config", "hooks.json")
21406
21589
  );
21407
- const antigravityMcp = readJson2(
21408
- path42.join(homeDir2, ".gemini", "config", "mcp_config.json")
21590
+ const antigravityMcp = readJson3(
21591
+ path43.join(homeDir2, ".gemini", "config", "mcp_config.json")
21409
21592
  );
21410
- const antigravityPresent = antigravityHooks !== null || fs41.existsSync(path42.join(homeDir2, ".gemini", "antigravity-cli")) || fs41.existsSync(path42.join(homeDir2, ".gemini", "antigravity-ide"));
21411
- const copilotHooks = readJson2(
21412
- path42.join(homeDir2, ".copilot", "hooks", "node9.json")
21593
+ const antigravityPresent = antigravityHooks !== null || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-cli")) || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-ide"));
21594
+ const copilotHooks = readJson3(
21595
+ path43.join(homeDir2, ".copilot", "hooks", "node9.json")
21413
21596
  );
21414
- const copilotMcp = readJson2(
21415
- path42.join(homeDir2, ".copilot", "mcp-config.json")
21597
+ const copilotMcp = readJson3(
21598
+ path43.join(homeDir2, ".copilot", "mcp-config.json")
21416
21599
  );
21417
- const copilotPresent = fs41.existsSync(path42.join(homeDir2, ".copilot"));
21600
+ const copilotPresent = fs42.existsSync(path43.join(homeDir2, ".copilot"));
21418
21601
  const hermesHooks = readHermesHooks(hermesConfigPath(homeDir2));
21419
21602
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig || antigravityPresent || copilotPresent || hermesHooks;
21420
21603
  if (agentFound) {
@@ -21422,8 +21605,8 @@ function registerStatusCommand(program2) {
21422
21605
  console.log(chalk15.bold(" Agent Wiring:"));
21423
21606
  console.log("");
21424
21607
  if (claudeSettings || claudeConfig) {
21425
- const preHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PreToolUse);
21426
- const postHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PostToolUse);
21608
+ const preHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PreToolUse);
21609
+ const postHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PostToolUse);
21427
21610
  printAgentSection(
21428
21611
  "Claude Code",
21429
21612
  [
@@ -21435,8 +21618,8 @@ function registerStatusCommand(program2) {
21435
21618
  console.log("");
21436
21619
  }
21437
21620
  if (geminiSettings) {
21438
- const beforeHook = matchersHaveNode9Hook(geminiSettings.hooks?.BeforeTool);
21439
- const afterHook = matchersHaveNode9Hook(geminiSettings.hooks?.AfterTool);
21621
+ const beforeHook = matchersHaveNode9Hook2(geminiSettings.hooks?.BeforeTool);
21622
+ const afterHook = matchersHaveNode9Hook2(geminiSettings.hooks?.AfterTool);
21440
21623
  printAgentSection(
21441
21624
  "Gemini CLI",
21442
21625
  [
@@ -21448,8 +21631,8 @@ function registerStatusCommand(program2) {
21448
21631
  console.log("");
21449
21632
  }
21450
21633
  if (antigravityPresent) {
21451
- const preHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PreToolUse);
21452
- const postHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PostToolUse);
21634
+ const preHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PreToolUse);
21635
+ const postHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PostToolUse);
21453
21636
  printAgentSection(
21454
21637
  "Antigravity",
21455
21638
  [
@@ -21461,9 +21644,9 @@ function registerStatusCommand(program2) {
21461
21644
  console.log("");
21462
21645
  }
21463
21646
  if (copilotPresent) {
21464
- const preHook = flatHaveNode9Hook(copilotHooks?.hooks?.PreToolUse);
21465
- const postHook = flatHaveNode9Hook(copilotHooks?.hooks?.PostToolUse);
21466
- const promptHook = flatHaveNode9Hook(copilotHooks?.hooks?.UserPromptSubmit);
21647
+ const preHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PreToolUse);
21648
+ const postHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PostToolUse);
21649
+ const promptHook = flatHaveNode9Hook2(copilotHooks?.hooks?.UserPromptSubmit);
21467
21650
  printAgentSection(
21468
21651
  "GitHub Copilot",
21469
21652
  [
@@ -21510,9 +21693,9 @@ init_setup();
21510
21693
  init_shields();
21511
21694
  init_service();
21512
21695
  import chalk16 from "chalk";
21513
- import fs42 from "fs";
21514
- import path43 from "path";
21515
- import os38 from "os";
21696
+ import fs43 from "fs";
21697
+ import path44 from "path";
21698
+ import os39 from "os";
21516
21699
  import https4 from "https";
21517
21700
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
21518
21701
  function buildTelemetryPayload(agents, firstInstall) {
@@ -21598,16 +21781,16 @@ function registerInitCommand(program2) {
21598
21781
  }
21599
21782
  console.log("");
21600
21783
  }
21601
- const configPath = path43.join(os38.homedir(), ".node9", "config.json");
21602
- const isFirstInstall = !fs42.existsSync(configPath);
21603
- if (fs42.existsSync(configPath) && !options.force) {
21784
+ const configPath = path44.join(os39.homedir(), ".node9", "config.json");
21785
+ const isFirstInstall = !fs43.existsSync(configPath);
21786
+ if (fs43.existsSync(configPath) && !options.force) {
21604
21787
  try {
21605
- const existing = JSON.parse(fs42.readFileSync(configPath, "utf-8"));
21788
+ const existing = JSON.parse(fs43.readFileSync(configPath, "utf-8"));
21606
21789
  const settings = existing.settings ?? {};
21607
21790
  if (settings.mode !== chosenMode) {
21608
21791
  settings.mode = chosenMode;
21609
21792
  existing.settings = settings;
21610
- fs42.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
21793
+ fs43.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
21611
21794
  console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
21612
21795
  } else {
21613
21796
  console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -21620,9 +21803,9 @@ function registerInitCommand(program2) {
21620
21803
  ...DEFAULT_CONFIG,
21621
21804
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
21622
21805
  };
21623
- const dir = path43.dirname(configPath);
21624
- if (!fs42.existsSync(dir)) fs42.mkdirSync(dir, { recursive: true });
21625
- fs42.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
21806
+ const dir = path44.dirname(configPath);
21807
+ if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
21808
+ fs43.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
21626
21809
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
21627
21810
  console.log(chalk16.gray(` Mode: ${chosenMode}`));
21628
21811
  }
@@ -21727,7 +21910,7 @@ function registerInitCommand(program2) {
21727
21910
  }
21728
21911
 
21729
21912
  // src/cli/commands/undo.ts
21730
- import path44 from "path";
21913
+ import path45 from "path";
21731
21914
  import chalk18 from "chalk";
21732
21915
 
21733
21916
  // src/tui/undo-navigator.ts
@@ -21886,7 +22069,7 @@ function findMatchingCwd(startDir, history) {
21886
22069
  let dir = startDir;
21887
22070
  while (true) {
21888
22071
  if (cwds.has(dir)) return dir;
21889
- const parent = path44.dirname(dir);
22072
+ const parent = path45.dirname(dir);
21890
22073
  if (parent === dir) return null;
21891
22074
  dir = parent;
21892
22075
  }
@@ -22014,6 +22197,8 @@ function registerUndoCommand(program2) {
22014
22197
 
22015
22198
  // src/mcp-gateway/index.ts
22016
22199
  init_orchestrator();
22200
+ init_cloud();
22201
+ init_config();
22017
22202
  import readline4 from "readline";
22018
22203
  import chalk19 from "chalk";
22019
22204
  import { spawn as spawn7 } from "child_process";
@@ -22046,6 +22231,60 @@ function normalizeClientName(name) {
22046
22231
  const sanitized = sanitize4(name).slice(0, 40);
22047
22232
  return sanitized.length > 0 ? sanitized : void 0;
22048
22233
  }
22234
+ function reportPinMismatchToCloud(serverKey, agent) {
22235
+ try {
22236
+ const creds = getCredentials();
22237
+ if (!creds) return;
22238
+ void auditLocalAllow(
22239
+ `mcp-server:${serverKey}`,
22240
+ { serverKey, reason: "tool-pin-mismatch" },
22241
+ "mcp-pin-mismatch",
22242
+ creds,
22243
+ { mcpServer: serverKey, agent },
22244
+ void 0,
22245
+ false,
22246
+ {
22247
+ ruleName: "MCP tool definitions changed (possible rug pull)",
22248
+ ruleDescription: `The MCP server "${serverKey}" changed its tool definitions since they were pinned. This can indicate a supply-chain attack (tool poisoning). The session was quarantined. Review with: node9 mcp pin update ${serverKey}`
22249
+ }
22250
+ );
22251
+ } catch {
22252
+ }
22253
+ }
22254
+ function reportInventoryToCloud(serverKey, toolCount, agent) {
22255
+ try {
22256
+ const creds = getCredentials();
22257
+ if (!creds) return;
22258
+ void auditLocalAllow(
22259
+ `mcp-server:${serverKey}`,
22260
+ { serverKey, toolCount },
22261
+ "mcp-discovered",
22262
+ creds,
22263
+ { mcpServer: serverKey, agent },
22264
+ void 0,
22265
+ false,
22266
+ { mcpToolCount: toolCount }
22267
+ );
22268
+ } catch {
22269
+ }
22270
+ }
22271
+ function reportLargeResponseToCloud(serverKey, responseBytes, agent) {
22272
+ try {
22273
+ const creds = getCredentials();
22274
+ if (!creds) return;
22275
+ void auditLocalAllow(
22276
+ `mcp-server:${serverKey}`,
22277
+ { serverKey, responseBytes },
22278
+ "mcp-large-response",
22279
+ creds,
22280
+ { mcpServer: serverKey, agent },
22281
+ void 0,
22282
+ false,
22283
+ { mcpResponseBytes: responseBytes }
22284
+ );
22285
+ } catch {
22286
+ }
22287
+ }
22049
22288
  function tokenize4(cmd) {
22050
22289
  const tokens = [];
22051
22290
  let current = "";
@@ -22306,6 +22545,7 @@ async function runMcpGateway(upstreamCommand) {
22306
22545
  const currentHash = hashToolDefinitions(tools);
22307
22546
  const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
22308
22547
  const token = getInternalToken();
22548
+ reportInventoryToCloud(serverKey, tools.length, clientName);
22309
22549
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
22310
22550
  const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
22311
22551
  fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/discovered`, {
@@ -22370,6 +22610,7 @@ async function runMcpGateway(upstreamCommand) {
22370
22610
  console.error(chalk19.red(" Session quarantined \u2014 all tool calls blocked."));
22371
22611
  console.error(chalk19.yellow(` Run: node9 mcp pin update ${serverKey}
22372
22612
  `));
22613
+ reportPinMismatchToCloud(serverKey, clientName);
22373
22614
  const errorResponse = {
22374
22615
  jsonrpc: "2.0",
22375
22616
  id: parsed.id,
@@ -22415,6 +22656,7 @@ async function runMcpGateway(upstreamCommand) {
22415
22656
  `\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
22416
22657
  )
22417
22658
  );
22659
+ reportLargeResponseToCloud(serverKey, line.length, clientName);
22418
22660
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
22419
22661
  const token = getInternalToken();
22420
22662
  fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/large-response`, {
@@ -22462,9 +22704,9 @@ function registerMcpGatewayCommand(program2) {
22462
22704
 
22463
22705
  // src/mcp-server/index.ts
22464
22706
  import readline5 from "readline";
22465
- import fs43 from "fs";
22466
- import os39 from "os";
22467
- import path45 from "path";
22707
+ import fs44 from "fs";
22708
+ import os40 from "os";
22709
+ import path46 from "path";
22468
22710
  import { spawnSync as spawnSync4 } from "child_process";
22469
22711
  init_core();
22470
22712
  init_daemon();
@@ -22715,13 +22957,13 @@ function handleStatus() {
22715
22957
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
22716
22958
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
22717
22959
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
22718
- const projectConfig = path45.join(process.cwd(), "node9.config.json");
22719
- const globalConfig = path45.join(os39.homedir(), ".node9", "config.json");
22960
+ const projectConfig = path46.join(process.cwd(), "node9.config.json");
22961
+ const globalConfig = path46.join(os40.homedir(), ".node9", "config.json");
22720
22962
  lines.push(
22721
- `Project config (node9.config.json): ${fs43.existsSync(projectConfig) ? "present" : "not found"}`
22963
+ `Project config (node9.config.json): ${fs44.existsSync(projectConfig) ? "present" : "not found"}`
22722
22964
  );
22723
22965
  lines.push(
22724
- `Global config (~/.node9/config.json): ${fs43.existsSync(globalConfig) ? "present" : "not found"}`
22966
+ `Global config (~/.node9/config.json): ${fs44.existsSync(globalConfig) ? "present" : "not found"}`
22725
22967
  );
22726
22968
  return lines.join("\n");
22727
22969
  }
@@ -22795,21 +23037,21 @@ function handleShieldDisable(args) {
22795
23037
  writeActiveShields(active.filter((s) => s !== name));
22796
23038
  return `Shield "${name}" disabled.`;
22797
23039
  }
22798
- var GLOBAL_CONFIG_PATH = path45.join(os39.homedir(), ".node9", "config.json");
23040
+ var GLOBAL_CONFIG_PATH = path46.join(os40.homedir(), ".node9", "config.json");
22799
23041
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
22800
23042
  function readGlobalConfigRaw() {
22801
23043
  try {
22802
- if (fs43.existsSync(GLOBAL_CONFIG_PATH)) {
22803
- return JSON.parse(fs43.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
23044
+ if (fs44.existsSync(GLOBAL_CONFIG_PATH)) {
23045
+ return JSON.parse(fs44.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
22804
23046
  }
22805
23047
  } catch {
22806
23048
  }
22807
23049
  return {};
22808
23050
  }
22809
23051
  function writeGlobalConfigRaw(data) {
22810
- const dir = path45.dirname(GLOBAL_CONFIG_PATH);
22811
- if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
22812
- fs43.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
23052
+ const dir = path46.dirname(GLOBAL_CONFIG_PATH);
23053
+ if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
23054
+ fs44.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
22813
23055
  }
22814
23056
  function handleApproverList() {
22815
23057
  const config = getConfig();
@@ -22853,9 +23095,9 @@ function handleApproverSet(args) {
22853
23095
  function handleAuditGet(args) {
22854
23096
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
22855
23097
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
22856
- const auditPath = path45.join(os39.homedir(), ".node9", "audit.log");
22857
- if (!fs43.existsSync(auditPath)) return "No audit log found.";
22858
- const rawLines = fs43.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
23098
+ const auditPath = path46.join(os40.homedir(), ".node9", "audit.log");
23099
+ if (!fs44.existsSync(auditPath)) return "No audit log found.";
23100
+ const rawLines = fs44.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
22859
23101
  const parsed = [];
22860
23102
  for (const line of rawLines) {
22861
23103
  try {
@@ -23190,7 +23432,7 @@ function registerTrustCommand(program2) {
23190
23432
  // src/cli/commands/mcp-pin.ts
23191
23433
  init_mcp_pin();
23192
23434
  import chalk21 from "chalk";
23193
- import fs44 from "fs";
23435
+ import fs45 from "fs";
23194
23436
  function registerMcpPinCommand(program2) {
23195
23437
  const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
23196
23438
  const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
@@ -23201,7 +23443,7 @@ function registerMcpPinCommand(program2) {
23201
23443
  let repoCorrupt = false;
23202
23444
  if (found.source === "repo") {
23203
23445
  try {
23204
- const raw = fs44.readFileSync(found.path, "utf-8");
23446
+ const raw = fs45.readFileSync(found.path, "utf-8");
23205
23447
  const parsed = JSON.parse(raw);
23206
23448
  repoEntries = parsed.servers ?? {};
23207
23449
  } catch {
@@ -23518,9 +23760,9 @@ init_litellm();
23518
23760
  init_cost_gemini();
23519
23761
  init_cost_codex();
23520
23762
  import chalk24 from "chalk";
23521
- import fs45 from "fs";
23522
- import path46 from "path";
23523
- import os40 from "os";
23763
+ import fs46 from "fs";
23764
+ import path47 from "path";
23765
+ import os41 from "os";
23524
23766
  function modelPrice(model) {
23525
23767
  const t = pricingFor(model);
23526
23768
  if (!t) return null;
@@ -23537,10 +23779,10 @@ function encodeProjectPath(projectPath) {
23537
23779
  }
23538
23780
  function sessionJsonlPath(projectPath, sessionId) {
23539
23781
  const encoded = encodeProjectPath(projectPath);
23540
- return path46.join(os40.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
23782
+ return path47.join(os41.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
23541
23783
  }
23542
23784
  function projectLabel(projectPath) {
23543
- return projectPath.replace(os40.homedir(), "~");
23785
+ return projectPath.replace(os41.homedir(), "~");
23544
23786
  }
23545
23787
  function parseHistoryLines(lines) {
23546
23788
  const entries = [];
@@ -23609,10 +23851,10 @@ function parseSessionLines(lines) {
23609
23851
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
23610
23852
  }
23611
23853
  function loadAuditEntries(auditPath) {
23612
- const aPath = auditPath ?? path46.join(os40.homedir(), ".node9", "audit.log");
23854
+ const aPath = auditPath ?? path47.join(os41.homedir(), ".node9", "audit.log");
23613
23855
  let raw;
23614
23856
  try {
23615
- raw = fs45.readFileSync(aPath, "utf-8");
23857
+ raw = fs46.readFileSync(aPath, "utf-8");
23616
23858
  } catch {
23617
23859
  return [];
23618
23860
  }
@@ -23648,8 +23890,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
23648
23890
  return result;
23649
23891
  }
23650
23892
  function buildGeminiSessions(days, allAuditEntries) {
23651
- const tmpDir = path46.join(os40.homedir(), ".gemini", "tmp");
23652
- if (!fs45.existsSync(tmpDir)) return [];
23893
+ const tmpDir = path47.join(os41.homedir(), ".gemini", "tmp");
23894
+ if (!fs46.existsSync(tmpDir)) return [];
23653
23895
  const cutoff = days !== null ? (() => {
23654
23896
  const d = /* @__PURE__ */ new Date();
23655
23897
  d.setDate(d.getDate() - days);
@@ -23658,35 +23900,35 @@ function buildGeminiSessions(days, allAuditEntries) {
23658
23900
  })() : null;
23659
23901
  let slugDirs;
23660
23902
  try {
23661
- slugDirs = fs45.readdirSync(tmpDir);
23903
+ slugDirs = fs46.readdirSync(tmpDir);
23662
23904
  } catch {
23663
23905
  return [];
23664
23906
  }
23665
23907
  const summaries = [];
23666
23908
  for (const slug of slugDirs) {
23667
- const slugPath = path46.join(tmpDir, slug);
23909
+ const slugPath = path47.join(tmpDir, slug);
23668
23910
  try {
23669
- if (!fs45.statSync(slugPath).isDirectory()) continue;
23911
+ if (!fs46.statSync(slugPath).isDirectory()) continue;
23670
23912
  } catch {
23671
23913
  continue;
23672
23914
  }
23673
- let projectRoot = path46.join(os40.homedir(), slug);
23915
+ let projectRoot = path47.join(os41.homedir(), slug);
23674
23916
  try {
23675
- projectRoot = fs45.readFileSync(path46.join(slugPath, ".project_root"), "utf-8").trim();
23917
+ projectRoot = fs46.readFileSync(path47.join(slugPath, ".project_root"), "utf-8").trim();
23676
23918
  } catch {
23677
23919
  }
23678
- const chatsDir = path46.join(slugPath, "chats");
23679
- if (!fs45.existsSync(chatsDir)) continue;
23920
+ const chatsDir = path47.join(slugPath, "chats");
23921
+ if (!fs46.existsSync(chatsDir)) continue;
23680
23922
  let chatFiles;
23681
23923
  try {
23682
- chatFiles = fs45.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
23924
+ chatFiles = fs46.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
23683
23925
  } catch {
23684
23926
  continue;
23685
23927
  }
23686
23928
  for (const chatFile of chatFiles) {
23687
23929
  let raw;
23688
23930
  try {
23689
- raw = fs45.readFileSync(path46.join(chatsDir, chatFile), "utf-8");
23931
+ raw = fs46.readFileSync(path47.join(chatsDir, chatFile), "utf-8");
23690
23932
  } catch {
23691
23933
  continue;
23692
23934
  }
@@ -23766,8 +24008,8 @@ function buildGeminiSessions(days, allAuditEntries) {
23766
24008
  return summaries;
23767
24009
  }
23768
24010
  function buildCodexSessions(days, allAuditEntries) {
23769
- const sessionsBase = path46.join(os40.homedir(), ".codex", "sessions");
23770
- if (!fs45.existsSync(sessionsBase)) return [];
24011
+ const sessionsBase = path47.join(os41.homedir(), ".codex", "sessions");
24012
+ if (!fs46.existsSync(sessionsBase)) return [];
23771
24013
  const cutoff = days !== null ? (() => {
23772
24014
  const d = /* @__PURE__ */ new Date();
23773
24015
  d.setDate(d.getDate() - days);
@@ -23776,29 +24018,29 @@ function buildCodexSessions(days, allAuditEntries) {
23776
24018
  })() : null;
23777
24019
  const jsonlFiles = [];
23778
24020
  try {
23779
- for (const year of fs45.readdirSync(sessionsBase)) {
23780
- const yearPath = path46.join(sessionsBase, year);
24021
+ for (const year of fs46.readdirSync(sessionsBase)) {
24022
+ const yearPath = path47.join(sessionsBase, year);
23781
24023
  try {
23782
- if (!fs45.statSync(yearPath).isDirectory()) continue;
24024
+ if (!fs46.statSync(yearPath).isDirectory()) continue;
23783
24025
  } catch {
23784
24026
  continue;
23785
24027
  }
23786
- for (const month of fs45.readdirSync(yearPath)) {
23787
- const monthPath = path46.join(yearPath, month);
24028
+ for (const month of fs46.readdirSync(yearPath)) {
24029
+ const monthPath = path47.join(yearPath, month);
23788
24030
  try {
23789
- if (!fs45.statSync(monthPath).isDirectory()) continue;
24031
+ if (!fs46.statSync(monthPath).isDirectory()) continue;
23790
24032
  } catch {
23791
24033
  continue;
23792
24034
  }
23793
- for (const day of fs45.readdirSync(monthPath)) {
23794
- const dayPath = path46.join(monthPath, day);
24035
+ for (const day of fs46.readdirSync(monthPath)) {
24036
+ const dayPath = path47.join(monthPath, day);
23795
24037
  try {
23796
- if (!fs45.statSync(dayPath).isDirectory()) continue;
24038
+ if (!fs46.statSync(dayPath).isDirectory()) continue;
23797
24039
  } catch {
23798
24040
  continue;
23799
24041
  }
23800
- for (const file of fs45.readdirSync(dayPath)) {
23801
- if (file.endsWith(".jsonl")) jsonlFiles.push(path46.join(dayPath, file));
24042
+ for (const file of fs46.readdirSync(dayPath)) {
24043
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path47.join(dayPath, file));
23802
24044
  }
23803
24045
  }
23804
24046
  }
@@ -23810,7 +24052,7 @@ function buildCodexSessions(days, allAuditEntries) {
23810
24052
  for (const filePath of jsonlFiles) {
23811
24053
  let lines;
23812
24054
  try {
23813
- lines = fs45.readFileSync(filePath, "utf-8").split("\n");
24055
+ lines = fs46.readFileSync(filePath, "utf-8").split("\n");
23814
24056
  } catch {
23815
24057
  continue;
23816
24058
  }
@@ -23896,10 +24138,10 @@ function buildCodexSessions(days, allAuditEntries) {
23896
24138
  return summaries;
23897
24139
  }
23898
24140
  function buildSessions(days, historyPath) {
23899
- const hPath = historyPath ?? path46.join(os40.homedir(), ".claude", "history.jsonl");
24141
+ const hPath = historyPath ?? path47.join(os41.homedir(), ".claude", "history.jsonl");
23900
24142
  let historyRaw = "";
23901
24143
  try {
23902
- historyRaw = fs45.readFileSync(hPath, "utf-8");
24144
+ historyRaw = fs46.readFileSync(hPath, "utf-8");
23903
24145
  } catch {
23904
24146
  }
23905
24147
  const cutoff = days !== null ? (() => {
@@ -23923,7 +24165,7 @@ function buildSessions(days, historyPath) {
23923
24165
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
23924
24166
  let sessionLines = [];
23925
24167
  try {
23926
- sessionLines = fs45.readFileSync(jsonlFile, "utf-8").split("\n");
24168
+ sessionLines = fs46.readFileSync(jsonlFile, "utf-8").split("\n");
23927
24169
  } catch {
23928
24170
  }
23929
24171
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -24223,12 +24465,12 @@ function registerSessionsCommand(program2) {
24223
24465
 
24224
24466
  // src/cli/commands/skill-pin.ts
24225
24467
  import chalk25 from "chalk";
24226
- import fs46 from "fs";
24227
- import os41 from "os";
24228
- import path47 from "path";
24468
+ import fs47 from "fs";
24469
+ import os42 from "os";
24470
+ import path48 from "path";
24229
24471
  function wipeSkillSessions() {
24230
24472
  try {
24231
- fs46.rmSync(path47.join(os41.homedir(), ".node9", "skill-sessions"), {
24473
+ fs47.rmSync(path48.join(os42.homedir(), ".node9", "skill-sessions"), {
24232
24474
  recursive: true,
24233
24475
  force: true
24234
24476
  });
@@ -24310,15 +24552,15 @@ function registerSkillPinCommand(program2) {
24310
24552
  }
24311
24553
 
24312
24554
  // src/cli/commands/decisions.ts
24313
- import fs47 from "fs";
24314
- import os42 from "os";
24315
- import path48 from "path";
24555
+ import fs48 from "fs";
24556
+ import os43 from "os";
24557
+ import path49 from "path";
24316
24558
  import chalk26 from "chalk";
24317
- var DECISIONS_FILE2 = path48.join(os42.homedir(), ".node9", "decisions.json");
24559
+ var DECISIONS_FILE2 = path49.join(os43.homedir(), ".node9", "decisions.json");
24318
24560
  function readDecisions() {
24319
24561
  try {
24320
- if (!fs47.existsSync(DECISIONS_FILE2)) return {};
24321
- const raw = fs47.readFileSync(DECISIONS_FILE2, "utf-8");
24562
+ if (!fs48.existsSync(DECISIONS_FILE2)) return {};
24563
+ const raw = fs48.readFileSync(DECISIONS_FILE2, "utf-8");
24322
24564
  const parsed = JSON.parse(raw);
24323
24565
  const out = {};
24324
24566
  for (const [k, v] of Object.entries(parsed)) {
@@ -24330,11 +24572,11 @@ function readDecisions() {
24330
24572
  }
24331
24573
  }
24332
24574
  function writeDecisions(d) {
24333
- const dir = path48.dirname(DECISIONS_FILE2);
24334
- if (!fs47.existsSync(dir)) fs47.mkdirSync(dir, { recursive: true });
24575
+ const dir = path49.dirname(DECISIONS_FILE2);
24576
+ if (!fs48.existsSync(dir)) fs48.mkdirSync(dir, { recursive: true });
24335
24577
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
24336
- fs47.writeFileSync(tmp, JSON.stringify(d, null, 2));
24337
- fs47.renameSync(tmp, DECISIONS_FILE2);
24578
+ fs48.writeFileSync(tmp, JSON.stringify(d, null, 2));
24579
+ fs48.renameSync(tmp, DECISIONS_FILE2);
24338
24580
  }
24339
24581
  function registerDecisionsCommand(program2) {
24340
24582
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -24391,18 +24633,18 @@ Persistent decisions (${entries.length})
24391
24633
 
24392
24634
  // src/cli/commands/dlp.ts
24393
24635
  import chalk27 from "chalk";
24394
- import fs48 from "fs";
24395
- import path49 from "path";
24396
- import os43 from "os";
24397
- var AUDIT_LOG = path49.join(os43.homedir(), ".node9", "audit.log");
24398
- var RESOLVED_FILE = path49.join(os43.homedir(), ".node9", "dlp-resolved.json");
24636
+ import fs49 from "fs";
24637
+ import path50 from "path";
24638
+ import os44 from "os";
24639
+ var AUDIT_LOG = path50.join(os44.homedir(), ".node9", "audit.log");
24640
+ var RESOLVED_FILE = path50.join(os44.homedir(), ".node9", "dlp-resolved.json");
24399
24641
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
24400
24642
  function stripAnsi(s) {
24401
24643
  return s.replace(ANSI_RE, "");
24402
24644
  }
24403
24645
  function loadResolved() {
24404
24646
  try {
24405
- const raw = JSON.parse(fs48.readFileSync(RESOLVED_FILE, "utf-8"));
24647
+ const raw = JSON.parse(fs49.readFileSync(RESOLVED_FILE, "utf-8"));
24406
24648
  return new Set(raw);
24407
24649
  } catch {
24408
24650
  return /* @__PURE__ */ new Set();
@@ -24410,13 +24652,13 @@ function loadResolved() {
24410
24652
  }
24411
24653
  function saveResolved(resolved) {
24412
24654
  try {
24413
- fs48.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
24655
+ fs49.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
24414
24656
  } catch {
24415
24657
  }
24416
24658
  }
24417
24659
  function loadDlpFindings() {
24418
- if (!fs48.existsSync(AUDIT_LOG)) return [];
24419
- return fs48.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
24660
+ if (!fs49.existsSync(AUDIT_LOG)) return [];
24661
+ return fs49.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
24420
24662
  if (!line.trim()) return [];
24421
24663
  try {
24422
24664
  const e = JSON.parse(line);
@@ -24515,14 +24757,14 @@ function registerDlpCommand(program2) {
24515
24757
  // src/cli/commands/mask.ts
24516
24758
  init_dlp();
24517
24759
  import chalk28 from "chalk";
24518
- import fs49 from "fs";
24519
- import path50 from "path";
24520
- import os44 from "os";
24760
+ import fs50 from "fs";
24761
+ import path51 from "path";
24762
+ import os45 from "os";
24521
24763
  function findJsonlFiles(dir) {
24522
24764
  const results = [];
24523
- if (!fs49.existsSync(dir)) return results;
24524
- for (const entry of fs49.readdirSync(dir, { withFileTypes: true })) {
24525
- const full = path50.join(dir, entry.name);
24765
+ if (!fs50.existsSync(dir)) return results;
24766
+ for (const entry of fs50.readdirSync(dir, { withFileTypes: true })) {
24767
+ const full = path51.join(dir, entry.name);
24526
24768
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
24527
24769
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
24528
24770
  }
@@ -24565,7 +24807,7 @@ function redactJson(obj) {
24565
24807
  function processFile(filePath, dryRun) {
24566
24808
  let raw;
24567
24809
  try {
24568
- raw = fs49.readFileSync(filePath, "utf-8");
24810
+ raw = fs50.readFileSync(filePath, "utf-8");
24569
24811
  } catch {
24570
24812
  return { redactedLines: 0, patterns: [] };
24571
24813
  }
@@ -24597,14 +24839,14 @@ function processFile(filePath, dryRun) {
24597
24839
  }
24598
24840
  }
24599
24841
  if (!dryRun && redactedLines > 0) {
24600
- fs49.writeFileSync(filePath, newLines.join("\n"), "utf-8");
24842
+ fs50.writeFileSync(filePath, newLines.join("\n"), "utf-8");
24601
24843
  }
24602
24844
  return { redactedLines, patterns };
24603
24845
  }
24604
24846
  function processJsonFile(filePath, dryRun) {
24605
24847
  let raw;
24606
24848
  try {
24607
- raw = fs49.readFileSync(filePath, "utf-8");
24849
+ raw = fs50.readFileSync(filePath, "utf-8");
24608
24850
  } catch {
24609
24851
  return { redactedLines: 0, patterns: [] };
24610
24852
  }
@@ -24617,15 +24859,15 @@ function processJsonFile(filePath, dryRun) {
24617
24859
  const { value, modified, found } = redactJson(parsed);
24618
24860
  if (!modified) return { redactedLines: 0, patterns: [] };
24619
24861
  if (!dryRun) {
24620
- fs49.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
24862
+ fs50.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
24621
24863
  }
24622
24864
  return { redactedLines: 1, patterns: found };
24623
24865
  }
24624
24866
  function findJsonFiles(dir) {
24625
24867
  const results = [];
24626
- if (!fs49.existsSync(dir)) return results;
24627
- for (const entry of fs49.readdirSync(dir, { withFileTypes: true })) {
24628
- const full = path50.join(dir, entry.name);
24868
+ if (!fs50.existsSync(dir)) return results;
24869
+ for (const entry of fs50.readdirSync(dir, { withFileTypes: true })) {
24870
+ const full = path51.join(dir, entry.name);
24629
24871
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
24630
24872
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
24631
24873
  }
@@ -24634,9 +24876,9 @@ function findJsonFiles(dir) {
24634
24876
  function registerMaskCommand(program2) {
24635
24877
  program2.command("mask").description("Redact plaintext secrets from local AI session history files").option("--dry-run", "show what would be redacted without making changes").option("--all", "scan all history (default: last 30 days)").action(async (options) => {
24636
24878
  const dryRun = !!options.dryRun;
24637
- const home = os44.homedir();
24638
- const claudeDir = path50.join(home, ".claude", "projects");
24639
- const geminiDir = path50.join(home, ".gemini", "tmp");
24879
+ const home = os45.homedir();
24880
+ const claudeDir = path51.join(home, ".claude", "projects");
24881
+ const geminiDir = path51.join(home, ".gemini", "tmp");
24640
24882
  const allFiles = [
24641
24883
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
24642
24884
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -24644,7 +24886,7 @@ function registerMaskCommand(program2) {
24644
24886
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
24645
24887
  const filtered = cutoff ? allFiles.filter((f) => {
24646
24888
  try {
24647
- return fs49.statSync(f.path).mtime >= cutoff;
24889
+ return fs50.statSync(f.path).mtime >= cutoff;
24648
24890
  } catch {
24649
24891
  return false;
24650
24892
  }
@@ -24700,20 +24942,20 @@ function registerMaskCommand(program2) {
24700
24942
  // src/cli.ts
24701
24943
  init_blast();
24702
24944
  var { version } = JSON.parse(
24703
- fs52.readFileSync(path53.join(__dirname, "../package.json"), "utf-8")
24945
+ fs53.readFileSync(path54.join(__dirname, "../package.json"), "utf-8")
24704
24946
  );
24705
24947
  var program = new Command();
24706
24948
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
24707
24949
  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) => {
24708
24950
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
24709
- const credPath = path53.join(os47.homedir(), ".node9", "credentials.json");
24710
- if (!fs52.existsSync(path53.dirname(credPath)))
24711
- fs52.mkdirSync(path53.dirname(credPath), { recursive: true });
24951
+ const credPath = path54.join(os48.homedir(), ".node9", "credentials.json");
24952
+ if (!fs53.existsSync(path54.dirname(credPath)))
24953
+ fs53.mkdirSync(path54.dirname(credPath), { recursive: true });
24712
24954
  const profileName = options.profile || "default";
24713
24955
  let existingCreds = {};
24714
24956
  try {
24715
- if (fs52.existsSync(credPath)) {
24716
- const raw = JSON.parse(fs52.readFileSync(credPath, "utf-8"));
24957
+ if (fs53.existsSync(credPath)) {
24958
+ const raw = JSON.parse(fs53.readFileSync(credPath, "utf-8"));
24717
24959
  if (raw.apiKey) {
24718
24960
  existingCreds = {
24719
24961
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -24725,14 +24967,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24725
24967
  } catch {
24726
24968
  }
24727
24969
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
24728
- fs52.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
24970
+ fs53.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
24729
24971
  let effectiveCloud = null;
24730
24972
  if (profileName === "default") {
24731
- const configPath = path53.join(os47.homedir(), ".node9", "config.json");
24973
+ const configPath = path54.join(os48.homedir(), ".node9", "config.json");
24732
24974
  let config = {};
24733
24975
  try {
24734
- if (fs52.existsSync(configPath))
24735
- config = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
24976
+ if (fs53.existsSync(configPath))
24977
+ config = JSON.parse(fs53.readFileSync(configPath, "utf-8"));
24736
24978
  } catch {
24737
24979
  }
24738
24980
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -24747,9 +24989,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24747
24989
  approvers.cloud = false;
24748
24990
  }
24749
24991
  s.approvers = approvers;
24750
- if (!fs52.existsSync(path53.dirname(configPath)))
24751
- fs52.mkdirSync(path53.dirname(configPath), { recursive: true });
24752
- fs52.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
24992
+ if (!fs53.existsSync(path54.dirname(configPath)))
24993
+ fs53.mkdirSync(path54.dirname(configPath), { recursive: true });
24994
+ fs53.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
24753
24995
  effectiveCloud = approvers.cloud === true;
24754
24996
  }
24755
24997
  if (options.profile && profileName !== "default") {
@@ -24908,15 +25150,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
24908
25150
  }
24909
25151
  }
24910
25152
  if (options.purge) {
24911
- const node9Dir = path53.join(os47.homedir(), ".node9");
24912
- if (fs52.existsSync(node9Dir)) {
25153
+ const node9Dir = path54.join(os48.homedir(), ".node9");
25154
+ if (fs53.existsSync(node9Dir)) {
24913
25155
  const confirmed = await confirm2({
24914
25156
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
24915
25157
  default: false
24916
25158
  });
24917
25159
  if (confirmed) {
24918
- fs52.rmSync(node9Dir, { recursive: true });
24919
- if (fs52.existsSync(node9Dir)) {
25160
+ fs53.rmSync(node9Dir, { recursive: true });
25161
+ if (fs53.existsSync(node9Dir)) {
24920
25162
  console.error(
24921
25163
  chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
24922
25164
  );
@@ -25031,7 +25273,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
25031
25273
  });
25032
25274
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
25033
25275
  try {
25034
- const dashboardPath = path53.join(__dirname, "dashboard.mjs");
25276
+ const dashboardPath = path54.join(__dirname, "dashboard.mjs");
25035
25277
  const dynamicImport = new Function("id", "return import(id)");
25036
25278
  const mod = await dynamicImport(`file://${dashboardPath}`);
25037
25279
  await mod.startMonitor();
@@ -25069,14 +25311,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
25069
25311
  Run "node9 addto claude" to register it as the statusLine.`
25070
25312
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
25071
25313
  if (subcommand === "debug") {
25072
- const flagFile = path53.join(os47.homedir(), ".node9", "hud-debug");
25314
+ const flagFile = path54.join(os48.homedir(), ".node9", "hud-debug");
25073
25315
  if (state === "on") {
25074
- fs52.mkdirSync(path53.dirname(flagFile), { recursive: true });
25075
- fs52.writeFileSync(flagFile, "");
25316
+ fs53.mkdirSync(path54.dirname(flagFile), { recursive: true });
25317
+ fs53.writeFileSync(flagFile, "");
25076
25318
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
25077
25319
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
25078
25320
  } else if (state === "off") {
25079
- if (fs52.existsSync(flagFile)) fs52.unlinkSync(flagFile);
25321
+ if (fs53.existsSync(flagFile)) fs53.unlinkSync(flagFile);
25080
25322
  console.log("HUD debug logging disabled.");
25081
25323
  } else {
25082
25324
  console.error("Usage: node9 hud debug on|off");
@@ -25193,9 +25435,9 @@ if (process.argv[2] !== "daemon") {
25193
25435
  const isCheckHook = process.argv[2] === "check";
25194
25436
  if (isCheckHook) {
25195
25437
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
25196
- const logPath = path53.join(os47.homedir(), ".node9", "hook-debug.log");
25438
+ const logPath = path54.join(os48.homedir(), ".node9", "hook-debug.log");
25197
25439
  const msg = reason instanceof Error ? reason.message : String(reason);
25198
- fs52.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
25440
+ fs53.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
25199
25441
  `);
25200
25442
  }
25201
25443
  process.exit(0);