@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.js CHANGED
@@ -206,8 +206,8 @@ function sanitizeConfig(raw) {
206
206
  }
207
207
  }
208
208
  const lines = result.error.issues.map((issue) => {
209
- const path54 = issue.path.length > 0 ? issue.path.join(".") : "root";
210
- return ` \u2022 ${path54}: ${issue.message}`;
209
+ const path55 = issue.path.length > 0 ? issue.path.join(".") : "root";
210
+ return ` \u2022 ${path55}: ${issue.message}`;
211
211
  });
212
212
  return {
213
213
  sanitized,
@@ -1256,9 +1256,9 @@ function matchesPattern(text, patterns) {
1256
1256
  const withoutDotSlash = text.replace(/^\.\//, "");
1257
1257
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1258
1258
  }
1259
- function getNestedValue(obj, path54) {
1259
+ function getNestedValue(obj, path55) {
1260
1260
  if (!obj || typeof obj !== "object") return null;
1261
- const segments = path54.split(".");
1261
+ const segments = path55.split(".");
1262
1262
  for (const seg of segments) {
1263
1263
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1264
1264
  }
@@ -5889,6 +5889,57 @@ function validateApiUrl(raw) {
5889
5889
  }
5890
5890
  return null;
5891
5891
  }
5892
+ function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
5893
+ const validated = validateApiUrl(creds.apiUrl);
5894
+ if (!validated) {
5895
+ try {
5896
+ import_fs10.default.appendFileSync(
5897
+ HOOK_DEBUG_LOG,
5898
+ `[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
5899
+ `
5900
+ );
5901
+ } catch {
5902
+ }
5903
+ return Promise.resolve();
5904
+ }
5905
+ const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
5906
+ const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
5907
+ const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
5908
+ const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
5909
+ const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
5910
+ Object.entries(riskMetadata).filter(
5911
+ ([, v]) => typeof v === "string" && v.length > 0 || typeof v === "number" && Number.isFinite(v)
5912
+ )
5913
+ ) : void 0;
5914
+ const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
5915
+ return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
5916
+ method: "POST",
5917
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
5918
+ body: JSON.stringify({
5919
+ toolName,
5920
+ args: safeArgs,
5921
+ checkedBy: safeCheckedBy,
5922
+ ...dlpInfo && { dlpPattern, dlpSample },
5923
+ ...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
5924
+ // session_id (Claude Code + Gemini CLI) groups all audit rows from one
5925
+ // agent run; transcript_path is the authoritative pointer to the
5926
+ // session log (survives Gemini resume drift). Both optional —
5927
+ // unsupported agents (MCP-mediated) leave them undefined.
5928
+ ...meta?.sessionId && { runId: meta.sessionId },
5929
+ ...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
5930
+ context: {
5931
+ agent: meta?.agent,
5932
+ mcpServer: meta?.mcpServer,
5933
+ hostname: import_os9.default.hostname(),
5934
+ cwd: process.cwd(),
5935
+ platform: import_os9.default.platform()
5936
+ }
5937
+ }),
5938
+ signal: AbortSignal.timeout(5e3)
5939
+ }).then(() => {
5940
+ }).catch(() => {
5941
+ });
5942
+ }
5892
5943
  async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
5893
5944
  const controller = new AbortController();
5894
5945
  const timeout = setTimeout(() => controller.abort(), 1e4);
@@ -6012,7 +6063,7 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
6012
6063
  );
6013
6064
  }
6014
6065
  }
6015
- var import_fs10, import_os9, import_path12;
6066
+ var import_fs10, import_os9, import_path12, DLP_SAMPLE_MAX_LEN, DLP_PATTERN_MAX_LEN, KNOWN_CHECKED_BY;
6016
6067
  var init_cloud = __esm({
6017
6068
  "src/auth/cloud.ts"() {
6018
6069
  "use strict";
@@ -6020,6 +6071,39 @@ var init_cloud = __esm({
6020
6071
  import_os9 = __toESM(require("os"));
6021
6072
  import_path12 = __toESM(require("path"));
6022
6073
  init_audit();
6074
+ DLP_SAMPLE_MAX_LEN = 200;
6075
+ DLP_PATTERN_MAX_LEN = 100;
6076
+ KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
6077
+ "dlp-block",
6078
+ "observe-mode-dlp-would-block",
6079
+ "dlp-review-flagged",
6080
+ "loop-detected",
6081
+ "audit-mode",
6082
+ "local-policy",
6083
+ "smart-rule-block",
6084
+ // Smart-rule block was downgraded to review because the daemon was
6085
+ // running and we're not in CI. The block attempt is still recorded;
6086
+ // the user got a popup. Distinct from 'smart-rule-block' so the
6087
+ // dashboard can show "block rule overridden" separately from a hard
6088
+ // block that fired with no human in the loop.
6089
+ "smart-rule-block-override",
6090
+ "persistent",
6091
+ "trust",
6092
+ "observe-mode",
6093
+ "observe-mode-would-block",
6094
+ // MCP supply-chain: the gateway pinned a server's tool definitions and they
6095
+ // changed since (possible tool poisoning / rug pull). Emitted as a synthetic
6096
+ // audit row so the SaaS surfaces it as a blocked event. The firewall maps
6097
+ // this checkedBy to AUTO_BLOCKED. See doc/roadmap/active/saas-value-first.md
6098
+ // (workstream B-Tier2).
6099
+ "mcp-pin-mismatch",
6100
+ // MCP visibility (B-Tier2, informational — NOT blocks): the gateway
6101
+ // discovered a server's tool inventory (mcp-discovered) or saw an oversized
6102
+ // tool response that bloats the context window (mcp-large-response). Stored
6103
+ // AUTO_ALLOWED; carries mcpToolCount / mcpResponseBytes in riskMetadata.
6104
+ "mcp-discovered",
6105
+ "mcp-large-response"
6106
+ ]);
6023
6107
  }
6024
6108
  });
6025
6109
 
@@ -16867,20 +16951,20 @@ function getModelContextLimit(model) {
16867
16951
  return 2e5;
16868
16952
  }
16869
16953
  function readSessionUsage() {
16870
- const projectsDir = import_path51.default.join(import_os45.default.homedir(), ".claude", "projects");
16871
- if (!import_fs50.default.existsSync(projectsDir)) return null;
16954
+ const projectsDir = import_path52.default.join(import_os46.default.homedir(), ".claude", "projects");
16955
+ if (!import_fs51.default.existsSync(projectsDir)) return null;
16872
16956
  let latestFile = null;
16873
16957
  let latestMtime = 0;
16874
16958
  try {
16875
- for (const dir of import_fs50.default.readdirSync(projectsDir)) {
16876
- const dirPath = import_path51.default.join(projectsDir, dir);
16959
+ for (const dir of import_fs51.default.readdirSync(projectsDir)) {
16960
+ const dirPath = import_path52.default.join(projectsDir, dir);
16877
16961
  try {
16878
- if (!import_fs50.default.statSync(dirPath).isDirectory()) continue;
16879
- for (const file of import_fs50.default.readdirSync(dirPath)) {
16962
+ if (!import_fs51.default.statSync(dirPath).isDirectory()) continue;
16963
+ for (const file of import_fs51.default.readdirSync(dirPath)) {
16880
16964
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
16881
- const filePath = import_path51.default.join(dirPath, file);
16965
+ const filePath = import_path52.default.join(dirPath, file);
16882
16966
  try {
16883
- const mtime = import_fs50.default.statSync(filePath).mtimeMs;
16967
+ const mtime = import_fs51.default.statSync(filePath).mtimeMs;
16884
16968
  if (mtime > latestMtime) {
16885
16969
  latestMtime = mtime;
16886
16970
  latestFile = filePath;
@@ -16895,7 +16979,7 @@ function readSessionUsage() {
16895
16979
  }
16896
16980
  if (!latestFile) return null;
16897
16981
  try {
16898
- const lines = import_fs50.default.readFileSync(latestFile, "utf-8").split("\n");
16982
+ const lines = import_fs51.default.readFileSync(latestFile, "utf-8").split("\n");
16899
16983
  let lastModel = "";
16900
16984
  let lastInput = 0;
16901
16985
  let lastOutput = 0;
@@ -16956,7 +17040,7 @@ function formatBase(activity) {
16956
17040
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
16957
17041
  const icon = getIcon(activity.tool);
16958
17042
  const toolName = activity.tool.slice(0, 16).padEnd(16);
16959
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os45.default.homedir(), "~");
17043
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os46.default.homedir(), "~");
16960
17044
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
16961
17045
  return `${import_chalk29.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk29.default.white.bold(toolName)} ${import_chalk29.default.dim(argsPreview)}`;
16962
17046
  }
@@ -16995,9 +17079,9 @@ function renderPending(activity) {
16995
17079
  }
16996
17080
  async function ensureDaemon() {
16997
17081
  let pidPort = null;
16998
- if (import_fs50.default.existsSync(PID_FILE)) {
17082
+ if (import_fs51.default.existsSync(PID_FILE)) {
16999
17083
  try {
17000
- const { port } = JSON.parse(import_fs50.default.readFileSync(PID_FILE, "utf-8"));
17084
+ const { port } = JSON.parse(import_fs51.default.readFileSync(PID_FILE, "utf-8"));
17001
17085
  pidPort = port;
17002
17086
  } catch {
17003
17087
  console.error(import_chalk29.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -17153,9 +17237,9 @@ function buildRecoveryCardLines(req) {
17153
17237
  ];
17154
17238
  }
17155
17239
  function readApproversFromDisk() {
17156
- const configPath = import_path51.default.join(import_os45.default.homedir(), ".node9", "config.json");
17240
+ const configPath = import_path52.default.join(import_os46.default.homedir(), ".node9", "config.json");
17157
17241
  try {
17158
- const raw = JSON.parse(import_fs50.default.readFileSync(configPath, "utf-8"));
17242
+ const raw = JSON.parse(import_fs51.default.readFileSync(configPath, "utf-8"));
17159
17243
  const settings = raw.settings ?? {};
17160
17244
  return settings.approvers ?? {};
17161
17245
  } catch {
@@ -17171,15 +17255,15 @@ function approverStatusLine() {
17171
17255
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
17172
17256
  }
17173
17257
  function toggleApprover(channel) {
17174
- const configPath = import_path51.default.join(import_os45.default.homedir(), ".node9", "config.json");
17258
+ const configPath = import_path52.default.join(import_os46.default.homedir(), ".node9", "config.json");
17175
17259
  try {
17176
- const raw = JSON.parse(import_fs50.default.readFileSync(configPath, "utf-8"));
17260
+ const raw = JSON.parse(import_fs51.default.readFileSync(configPath, "utf-8"));
17177
17261
  const settings = raw.settings ?? {};
17178
17262
  const approvers = settings.approvers ?? {};
17179
17263
  approvers[channel] = approvers[channel] === false;
17180
17264
  settings.approvers = approvers;
17181
17265
  raw.settings = settings;
17182
- import_fs50.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
17266
+ import_fs51.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
17183
17267
  } catch (err2) {
17184
17268
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
17185
17269
  `);
@@ -17351,8 +17435,8 @@ async function startTail(options = {}) {
17351
17435
  }
17352
17436
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
17353
17437
  try {
17354
- import_fs50.default.appendFileSync(
17355
- import_path51.default.join(import_os45.default.homedir(), ".node9", "hook-debug.log"),
17438
+ import_fs51.default.appendFileSync(
17439
+ import_path52.default.join(import_os46.default.homedir(), ".node9", "hook-debug.log"),
17356
17440
  `[tail] POST /decision failed: ${String(err2)}
17357
17441
  `
17358
17442
  );
@@ -17416,9 +17500,9 @@ async function startTail(options = {}) {
17416
17500
  };
17417
17501
  process.stdin.on("keypress", onKeypress);
17418
17502
  }
17419
- const auditLog = import_path51.default.join(import_os45.default.homedir(), ".node9", "audit.log");
17503
+ const auditLog = import_path52.default.join(import_os46.default.homedir(), ".node9", "audit.log");
17420
17504
  try {
17421
- const unackedDlp = import_fs50.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17505
+ const unackedDlp = import_fs51.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17422
17506
  if (unackedDlp > 0) {
17423
17507
  console.log("");
17424
17508
  console.log(
@@ -17458,7 +17542,7 @@ async function startTail(options = {}) {
17458
17542
  if (stallWarned) return;
17459
17543
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
17460
17544
  try {
17461
- const auditMtime = import_fs50.default.statSync(auditLog).mtimeMs;
17545
+ const auditMtime = import_fs51.default.statSync(auditLog).mtimeMs;
17462
17546
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
17463
17547
  console.log("");
17464
17548
  console.log(
@@ -17643,20 +17727,20 @@ async function startTail(options = {}) {
17643
17727
  process.exit(1);
17644
17728
  });
17645
17729
  }
17646
- var import_http2, import_chalk29, import_fs50, import_os45, import_path51, import_readline6, import_child_process12, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
17730
+ var import_http2, import_chalk29, import_fs51, import_os46, import_path52, import_readline6, import_child_process12, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
17647
17731
  var init_tail = __esm({
17648
17732
  "src/tui/tail.ts"() {
17649
17733
  "use strict";
17650
17734
  import_http2 = __toESM(require("http"));
17651
17735
  import_chalk29 = __toESM(require("chalk"));
17652
- import_fs50 = __toESM(require("fs"));
17653
- import_os45 = __toESM(require("os"));
17654
- import_path51 = __toESM(require("path"));
17736
+ import_fs51 = __toESM(require("fs"));
17737
+ import_os46 = __toESM(require("os"));
17738
+ import_path52 = __toESM(require("path"));
17655
17739
  import_readline6 = __toESM(require("readline"));
17656
17740
  import_child_process12 = require("child_process");
17657
17741
  init_daemon2();
17658
17742
  init_daemon();
17659
- PID_FILE = import_path51.default.join(import_os45.default.homedir(), ".node9", "daemon.pid");
17743
+ PID_FILE = import_path52.default.join(import_os46.default.homedir(), ".node9", "daemon.pid");
17660
17744
  ICONS = {
17661
17745
  bash: "\u{1F4BB}",
17662
17746
  shell: "\u{1F4BB}",
@@ -17778,9 +17862,9 @@ function formatTimeLeft(resetsAt) {
17778
17862
  return ` (${m}m left)`;
17779
17863
  }
17780
17864
  function safeReadJson(filePath) {
17781
- if (!import_fs51.default.existsSync(filePath)) return null;
17865
+ if (!import_fs52.default.existsSync(filePath)) return null;
17782
17866
  try {
17783
- return JSON.parse(import_fs51.default.readFileSync(filePath, "utf-8"));
17867
+ return JSON.parse(import_fs52.default.readFileSync(filePath, "utf-8"));
17784
17868
  } catch {
17785
17869
  return null;
17786
17870
  }
@@ -17801,12 +17885,12 @@ function countHooksInFile(filePath) {
17801
17885
  return Object.keys(cfg.hooks).length;
17802
17886
  }
17803
17887
  function countRulesInDir(rulesDir) {
17804
- if (!import_fs51.default.existsSync(rulesDir)) return 0;
17888
+ if (!import_fs52.default.existsSync(rulesDir)) return 0;
17805
17889
  let count = 0;
17806
17890
  try {
17807
- for (const entry of import_fs51.default.readdirSync(rulesDir, { withFileTypes: true })) {
17891
+ for (const entry of import_fs52.default.readdirSync(rulesDir, { withFileTypes: true })) {
17808
17892
  if (entry.isDirectory()) {
17809
- count += countRulesInDir(import_path52.default.join(rulesDir, entry.name));
17893
+ count += countRulesInDir(import_path53.default.join(rulesDir, entry.name));
17810
17894
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
17811
17895
  count++;
17812
17896
  }
@@ -17817,46 +17901,46 @@ function countRulesInDir(rulesDir) {
17817
17901
  }
17818
17902
  function isSamePath(a, b) {
17819
17903
  try {
17820
- return import_path52.default.resolve(a) === import_path52.default.resolve(b);
17904
+ return import_path53.default.resolve(a) === import_path53.default.resolve(b);
17821
17905
  } catch {
17822
17906
  return false;
17823
17907
  }
17824
17908
  }
17825
17909
  function countConfigs(cwd) {
17826
- const homeDir2 = import_os46.default.homedir();
17827
- const claudeDir = import_path52.default.join(homeDir2, ".claude");
17910
+ const homeDir2 = import_os47.default.homedir();
17911
+ const claudeDir = import_path53.default.join(homeDir2, ".claude");
17828
17912
  let claudeMdCount = 0;
17829
17913
  let rulesCount = 0;
17830
17914
  let hooksCount = 0;
17831
17915
  const userMcpServers = /* @__PURE__ */ new Set();
17832
17916
  const projectMcpServers = /* @__PURE__ */ new Set();
17833
- if (import_fs51.default.existsSync(import_path52.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
17834
- rulesCount += countRulesInDir(import_path52.default.join(claudeDir, "rules"));
17835
- const userSettings = import_path52.default.join(claudeDir, "settings.json");
17917
+ if (import_fs52.default.existsSync(import_path53.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
17918
+ rulesCount += countRulesInDir(import_path53.default.join(claudeDir, "rules"));
17919
+ const userSettings = import_path53.default.join(claudeDir, "settings.json");
17836
17920
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
17837
17921
  hooksCount += countHooksInFile(userSettings);
17838
- const userClaudeJson = import_path52.default.join(homeDir2, ".claude.json");
17922
+ const userClaudeJson = import_path53.default.join(homeDir2, ".claude.json");
17839
17923
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
17840
17924
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
17841
17925
  userMcpServers.delete(name);
17842
17926
  }
17843
17927
  if (cwd) {
17844
- if (import_fs51.default.existsSync(import_path52.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
17845
- if (import_fs51.default.existsSync(import_path52.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
17846
- const projectClaudeDir = import_path52.default.join(cwd, ".claude");
17928
+ if (import_fs52.default.existsSync(import_path53.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
17929
+ if (import_fs52.default.existsSync(import_path53.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
17930
+ const projectClaudeDir = import_path53.default.join(cwd, ".claude");
17847
17931
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
17848
17932
  if (!overlapsUserScope) {
17849
- if (import_fs51.default.existsSync(import_path52.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
17850
- rulesCount += countRulesInDir(import_path52.default.join(projectClaudeDir, "rules"));
17851
- const projSettings = import_path52.default.join(projectClaudeDir, "settings.json");
17933
+ if (import_fs52.default.existsSync(import_path53.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
17934
+ rulesCount += countRulesInDir(import_path53.default.join(projectClaudeDir, "rules"));
17935
+ const projSettings = import_path53.default.join(projectClaudeDir, "settings.json");
17852
17936
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
17853
17937
  hooksCount += countHooksInFile(projSettings);
17854
17938
  }
17855
- if (import_fs51.default.existsSync(import_path52.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
17856
- const localSettings = import_path52.default.join(projectClaudeDir, "settings.local.json");
17939
+ if (import_fs52.default.existsSync(import_path53.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
17940
+ const localSettings = import_path53.default.join(projectClaudeDir, "settings.local.json");
17857
17941
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
17858
17942
  hooksCount += countHooksInFile(localSettings);
17859
- const mcpJsonServers = getMcpServerNames(import_path52.default.join(cwd, ".mcp.json"));
17943
+ const mcpJsonServers = getMcpServerNames(import_path53.default.join(cwd, ".mcp.json"));
17860
17944
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
17861
17945
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
17862
17946
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -17889,12 +17973,12 @@ function readActiveShieldsHud() {
17889
17973
  return shieldsCache.value;
17890
17974
  }
17891
17975
  try {
17892
- const shieldsPath = import_path52.default.join(import_os46.default.homedir(), ".node9", "shields.json");
17893
- if (!import_fs51.default.existsSync(shieldsPath)) {
17976
+ const shieldsPath = import_path53.default.join(import_os47.default.homedir(), ".node9", "shields.json");
17977
+ if (!import_fs52.default.existsSync(shieldsPath)) {
17894
17978
  shieldsCache = { value: [], ts: now };
17895
17979
  return [];
17896
17980
  }
17897
- const parsed = JSON.parse(import_fs51.default.readFileSync(shieldsPath, "utf-8"));
17981
+ const parsed = JSON.parse(import_fs52.default.readFileSync(shieldsPath, "utf-8"));
17898
17982
  if (!Array.isArray(parsed.active)) {
17899
17983
  shieldsCache = { value: [], ts: now };
17900
17984
  return [];
@@ -17996,17 +18080,17 @@ function renderContextLine(stdin) {
17996
18080
  async function main() {
17997
18081
  try {
17998
18082
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
17999
- if (import_fs51.default.existsSync(import_path52.default.join(import_os46.default.homedir(), ".node9", "hud-debug"))) {
18083
+ if (import_fs52.default.existsSync(import_path53.default.join(import_os47.default.homedir(), ".node9", "hud-debug"))) {
18000
18084
  try {
18001
- const logPath = import_path52.default.join(import_os46.default.homedir(), ".node9", "hud-debug.log");
18085
+ const logPath = import_path53.default.join(import_os47.default.homedir(), ".node9", "hud-debug.log");
18002
18086
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
18003
18087
  let size = 0;
18004
18088
  try {
18005
- size = import_fs51.default.statSync(logPath).size;
18089
+ size = import_fs52.default.statSync(logPath).size;
18006
18090
  } catch {
18007
18091
  }
18008
18092
  if (size < MAX_LOG_SIZE) {
18009
- import_fs51.default.appendFileSync(
18093
+ import_fs52.default.appendFileSync(
18010
18094
  logPath,
18011
18095
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
18012
18096
  );
@@ -18027,11 +18111,11 @@ async function main() {
18027
18111
  try {
18028
18112
  const cwd = stdin.cwd ?? process.cwd();
18029
18113
  for (const configPath of [
18030
- import_path52.default.join(cwd, "node9.config.json"),
18031
- import_path52.default.join(import_os46.default.homedir(), ".node9", "config.json")
18114
+ import_path53.default.join(cwd, "node9.config.json"),
18115
+ import_path53.default.join(import_os47.default.homedir(), ".node9", "config.json")
18032
18116
  ]) {
18033
- if (!import_fs51.default.existsSync(configPath)) continue;
18034
- const cfg = JSON.parse(import_fs51.default.readFileSync(configPath, "utf-8"));
18117
+ if (!import_fs52.default.existsSync(configPath)) continue;
18118
+ const cfg = JSON.parse(import_fs52.default.readFileSync(configPath, "utf-8"));
18035
18119
  const hud = cfg.settings?.hud;
18036
18120
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
18037
18121
  }
@@ -18049,13 +18133,13 @@ async function main() {
18049
18133
  renderOffline();
18050
18134
  }
18051
18135
  }
18052
- var import_fs51, import_path52, import_os46, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
18136
+ var import_fs52, import_path53, import_os47, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
18053
18137
  var init_hud = __esm({
18054
18138
  "src/cli/hud.ts"() {
18055
18139
  "use strict";
18056
- import_fs51 = __toESM(require("fs"));
18057
- import_path52 = __toESM(require("path"));
18058
- import_os46 = __toESM(require("os"));
18140
+ import_fs52 = __toESM(require("fs"));
18141
+ import_path53 = __toESM(require("path"));
18142
+ import_os47 = __toESM(require("os"));
18059
18143
  import_http3 = __toESM(require("http"));
18060
18144
  init_daemon();
18061
18145
  RESET3 = "\x1B[0m";
@@ -18082,9 +18166,9 @@ init_core();
18082
18166
  init_setup();
18083
18167
  init_daemon2();
18084
18168
  var import_chalk30 = __toESM(require("chalk"));
18085
- var import_fs52 = __toESM(require("fs"));
18086
- var import_path53 = __toESM(require("path"));
18087
- var import_os47 = __toESM(require("os"));
18169
+ var import_fs53 = __toESM(require("fs"));
18170
+ var import_path54 = __toESM(require("path"));
18171
+ var import_os48 = __toESM(require("os"));
18088
18172
  var import_prompts2 = require("@inquirer/prompts");
18089
18173
 
18090
18174
  // src/utils/duration.ts
@@ -19872,15 +19956,151 @@ function registerConfigShowCommand(program2) {
19872
19956
 
19873
19957
  // src/cli/commands/doctor.ts
19874
19958
  var import_chalk11 = __toESM(require("chalk"));
19875
- var import_fs38 = __toESM(require("fs"));
19876
- var import_path39 = __toESM(require("path"));
19877
- var import_os34 = __toESM(require("os"));
19959
+ var import_fs39 = __toESM(require("fs"));
19960
+ var import_path40 = __toESM(require("path"));
19961
+ var import_os35 = __toESM(require("os"));
19878
19962
  var import_child_process8 = require("child_process");
19879
19963
  init_daemon();
19880
19964
  init_config();
19965
+
19966
+ // src/agent-wiring.ts
19967
+ var import_fs38 = __toESM(require("fs"));
19968
+ var import_path39 = __toESM(require("path"));
19969
+ var import_os34 = __toESM(require("os"));
19970
+ var yaml2 = __toESM(require("yaml"));
19971
+ init_setup();
19972
+ function readJson2(filePath) {
19973
+ if (!import_fs38.default.existsSync(filePath)) return null;
19974
+ try {
19975
+ return JSON.parse(import_fs38.default.readFileSync(filePath, "utf-8"));
19976
+ } catch {
19977
+ return "invalid";
19978
+ }
19979
+ }
19980
+ function matchersHaveNode9Hook(matchers) {
19981
+ return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
19982
+ }
19983
+ function flatHaveNode9Hook(entries) {
19984
+ return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
19985
+ }
19986
+ function jsonWire(filePath, read) {
19987
+ const parsed = readJson2(filePath);
19988
+ if (parsed === null) return "absent";
19989
+ if (parsed === "invalid") return "invalid";
19990
+ return read(parsed) ? "wired" : "unwired";
19991
+ }
19992
+ function hermesWire(home) {
19993
+ const configPath = hermesConfigPath(home);
19994
+ if (!import_fs38.default.existsSync(configPath)) return "absent";
19995
+ let raw;
19996
+ try {
19997
+ raw = import_fs38.default.readFileSync(configPath, "utf-8");
19998
+ } catch {
19999
+ return "absent";
20000
+ }
20001
+ try {
20002
+ const cfg = yaml2.parse(raw);
20003
+ const pre = (cfg?.hooks?.pre_tool_call ?? []).some(
20004
+ (e) => typeof e?.command === "string" && isNode9Hook(e.command)
20005
+ );
20006
+ return pre ? "wired" : "unwired";
20007
+ } catch {
20008
+ return "invalid";
20009
+ }
20010
+ }
20011
+ var AGENT_SPECS = [
20012
+ {
20013
+ id: "claude",
20014
+ label: "Claude Code",
20015
+ hookLabel: "PreToolUse hook",
20016
+ setupCommand: "node9 setup claude",
20017
+ settingsPath: (h) => import_path39.default.join(h, ".claude", "settings.json"),
20018
+ wireState: (h) => jsonWire(
20019
+ import_path39.default.join(h, ".claude", "settings.json"),
20020
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
20021
+ )
20022
+ },
20023
+ {
20024
+ id: "gemini",
20025
+ label: "Gemini CLI",
20026
+ hookLabel: "BeforeTool hook",
20027
+ setupCommand: "node9 setup gemini",
20028
+ settingsPath: (h) => import_path39.default.join(h, ".gemini", "settings.json"),
20029
+ wireState: (h) => jsonWire(
20030
+ import_path39.default.join(h, ".gemini", "settings.json"),
20031
+ (p) => matchersHaveNode9Hook(p.hooks?.BeforeTool)
20032
+ )
20033
+ },
20034
+ {
20035
+ id: "codex",
20036
+ label: "Codex",
20037
+ hookLabel: "PreToolUse hook",
20038
+ setupCommand: "node9 setup codex",
20039
+ settingsPath: (h) => import_path39.default.join(h, ".codex", "hooks.json"),
20040
+ wireState: (h) => jsonWire(
20041
+ import_path39.default.join(h, ".codex", "hooks.json"),
20042
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
20043
+ )
20044
+ },
20045
+ {
20046
+ id: "antigravity",
20047
+ label: "Antigravity",
20048
+ hookLabel: "PreToolUse hook",
20049
+ setupCommand: "node9 setup antigravity",
20050
+ settingsPath: (h) => import_path39.default.join(h, ".gemini", "config", "hooks.json"),
20051
+ wireState: (h) => jsonWire(
20052
+ import_path39.default.join(h, ".gemini", "config", "hooks.json"),
20053
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
20054
+ )
20055
+ },
20056
+ {
20057
+ id: "copilot",
20058
+ label: "GitHub Copilot",
20059
+ hookLabel: "PreToolUse hook",
20060
+ setupCommand: "node9 setup copilot",
20061
+ settingsPath: (h) => import_path39.default.join(h, ".copilot", "hooks", "node9.json"),
20062
+ wireState: (h) => jsonWire(
20063
+ import_path39.default.join(h, ".copilot", "hooks", "node9.json"),
20064
+ (p) => flatHaveNode9Hook(p.hooks?.PreToolUse)
20065
+ )
20066
+ },
20067
+ {
20068
+ id: "cursor",
20069
+ label: "Cursor",
20070
+ hookLabel: "preToolUse hook",
20071
+ setupCommand: "node9 setup cursor",
20072
+ settingsPath: (h) => import_path39.default.join(h, ".cursor", "hooks.json"),
20073
+ wireState: (h) => jsonWire(
20074
+ import_path39.default.join(h, ".cursor", "hooks.json"),
20075
+ (p) => flatHaveNode9Hook(p.hooks?.preToolUse)
20076
+ )
20077
+ },
20078
+ {
20079
+ id: "hermes",
20080
+ label: "Hermes Agent",
20081
+ hookLabel: "pre_tool_call hook",
20082
+ setupCommand: "node9 setup hermes",
20083
+ settingsPath: (h) => hermesConfigPath(h),
20084
+ wireState: (h) => hermesWire(h)
20085
+ }
20086
+ ];
20087
+ function getAgentWiring(home = import_os34.default.homedir()) {
20088
+ const detected = detectAgents(home);
20089
+ return AGENT_SPECS.map((spec) => ({
20090
+ id: spec.id,
20091
+ label: spec.label,
20092
+ hookLabel: spec.hookLabel,
20093
+ setupCommand: spec.setupCommand,
20094
+ settingsPath: spec.settingsPath(home),
20095
+ installed: detected[spec.id],
20096
+ wireState: spec.wireState(home)
20097
+ }));
20098
+ }
20099
+
20100
+ // src/cli/commands/doctor.ts
19881
20101
  function registerDoctorCommand(program2, version2) {
19882
20102
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
19883
- const homeDir2 = import_os34.default.homedir();
20103
+ const homeDir2 = import_os35.default.homedir();
19884
20104
  let failures = 0;
19885
20105
  function pass(msg) {
19886
20106
  console.log(import_chalk11.default.green(" \u2705 ") + msg);
@@ -19929,10 +20149,10 @@ function registerDoctorCommand(program2, version2) {
19929
20149
  );
19930
20150
  }
19931
20151
  section("Configuration");
19932
- const globalConfigPath = import_path39.default.join(homeDir2, ".node9", "config.json");
19933
- if (import_fs38.default.existsSync(globalConfigPath)) {
20152
+ const globalConfigPath = import_path40.default.join(homeDir2, ".node9", "config.json");
20153
+ if (import_fs39.default.existsSync(globalConfigPath)) {
19934
20154
  try {
19935
- JSON.parse(import_fs38.default.readFileSync(globalConfigPath, "utf-8"));
20155
+ JSON.parse(import_fs39.default.readFileSync(globalConfigPath, "utf-8"));
19936
20156
  pass("~/.node9/config.json found and valid");
19937
20157
  } catch {
19938
20158
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -19940,10 +20160,10 @@ function registerDoctorCommand(program2, version2) {
19940
20160
  } else {
19941
20161
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
19942
20162
  }
19943
- const projectConfigPath = import_path39.default.join(process.cwd(), "node9.config.json");
19944
- if (import_fs38.default.existsSync(projectConfigPath)) {
20163
+ const projectConfigPath = import_path40.default.join(process.cwd(), "node9.config.json");
20164
+ if (import_fs39.default.existsSync(projectConfigPath)) {
19945
20165
  try {
19946
- JSON.parse(import_fs38.default.readFileSync(projectConfigPath, "utf-8"));
20166
+ JSON.parse(import_fs39.default.readFileSync(projectConfigPath, "utf-8"));
19947
20167
  pass("node9.config.json found and valid (project)");
19948
20168
  } catch {
19949
20169
  fail(
@@ -19952,8 +20172,8 @@ function registerDoctorCommand(program2, version2) {
19952
20172
  );
19953
20173
  }
19954
20174
  }
19955
- const credsPath = import_path39.default.join(homeDir2, ".node9", "credentials.json");
19956
- if (import_fs38.default.existsSync(credsPath)) {
20175
+ const credsPath = import_path40.default.join(homeDir2, ".node9", "credentials.json");
20176
+ if (import_fs39.default.existsSync(credsPath)) {
19957
20177
  pass("Cloud credentials found (~/.node9/credentials.json)");
19958
20178
  } else {
19959
20179
  warn(
@@ -19962,62 +20182,24 @@ function registerDoctorCommand(program2, version2) {
19962
20182
  );
19963
20183
  }
19964
20184
  section("Agent Hooks");
19965
- const claudeSettingsPath = import_path39.default.join(homeDir2, ".claude", "settings.json");
19966
- if (import_fs38.default.existsSync(claudeSettingsPath)) {
19967
- try {
19968
- const cs = JSON.parse(import_fs38.default.readFileSync(claudeSettingsPath, "utf-8"));
19969
- const hasHook = cs.hooks?.PreToolUse?.some(
19970
- (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
19971
- );
19972
- if (hasHook) pass("Claude Code \u2014 PreToolUse hook active");
19973
- else
19974
- fail(
19975
- "Claude Code \u2014 hooks file found but node9 hook missing",
19976
- "Run: node9 setup claude"
19977
- );
19978
- } catch {
19979
- fail("Claude Code \u2014 ~/.claude/settings.json is invalid JSON");
19980
- }
19981
- } else {
19982
- warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
19983
- }
19984
- const geminiSettingsPath = import_path39.default.join(homeDir2, ".gemini", "settings.json");
19985
- if (import_fs38.default.existsSync(geminiSettingsPath)) {
19986
- try {
19987
- const gs = JSON.parse(import_fs38.default.readFileSync(geminiSettingsPath, "utf-8"));
19988
- const hasHook = gs.hooks?.BeforeTool?.some(
19989
- (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
19990
- );
19991
- if (hasHook) pass("Gemini CLI \u2014 BeforeTool hook active");
19992
- else
19993
- fail(
19994
- "Gemini CLI \u2014 hooks file found but node9 hook missing",
19995
- "Run: node9 setup gemini"
19996
- );
19997
- } catch {
19998
- fail("Gemini CLI \u2014 ~/.gemini/settings.json is invalid JSON");
20185
+ const notConfigured = [];
20186
+ for (const a of getAgentWiring(homeDir2)) {
20187
+ if (a.wireState === "wired") {
20188
+ pass(`${a.label} \u2014 ${a.hookLabel} active`);
20189
+ } else if (a.wireState === "unwired") {
20190
+ fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
20191
+ } else if (a.wireState === "invalid") {
20192
+ fail(`${a.label} \u2014 settings file is invalid JSON`, a.settingsPath);
20193
+ } else {
20194
+ notConfigured.push(a.label);
19999
20195
  }
20000
- } else {
20001
- warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
20002
20196
  }
20003
- const cursorHooksPath = import_path39.default.join(homeDir2, ".cursor", "hooks.json");
20004
- if (import_fs38.default.existsSync(cursorHooksPath)) {
20005
- try {
20006
- const cur = JSON.parse(import_fs38.default.readFileSync(cursorHooksPath, "utf-8"));
20007
- const hasHook = cur.hooks?.preToolUse?.some(
20008
- (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
20009
- );
20010
- if (hasHook) pass("Cursor \u2014 preToolUse hook active");
20011
- else
20012
- fail(
20013
- "Cursor \u2014 hooks file found but node9 hook missing",
20014
- "Run: node9 setup cursor"
20015
- );
20016
- } catch {
20017
- fail("Cursor \u2014 ~/.cursor/hooks.json is invalid JSON");
20018
- }
20019
- } else {
20020
- warn("Cursor \u2014 not configured", "Run: node9 setup cursor (skip if not using Cursor)");
20197
+ if (notConfigured.length > 0) {
20198
+ console.log(
20199
+ import_chalk11.default.gray(
20200
+ ` \xB7 Not configured: ${notConfigured.join(", ")} \u2014 run \`node9 setup <agent>\` if you use one`
20201
+ )
20202
+ );
20021
20203
  }
20022
20204
  section("Daemon (optional)");
20023
20205
  if (isDaemonRunning()) {
@@ -20034,7 +20216,7 @@ function registerDoctorCommand(program2, version2) {
20034
20216
  try {
20035
20217
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
20036
20218
  const cfg = getConfig();
20037
- const creds = import_fs38.default.existsSync(import_path39.default.join(import_os34.default.homedir(), ".node9", "credentials.json"));
20219
+ const creds = import_fs39.default.existsSync(import_path40.default.join(import_os35.default.homedir(), ".node9", "credentials.json"));
20038
20220
  if (!creds) {
20039
20221
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
20040
20222
  } else if (!cfg.settings.approvers.cloud) {
@@ -20084,9 +20266,9 @@ function registerDoctorCommand(program2, version2) {
20084
20266
 
20085
20267
  // src/cli/commands/audit.ts
20086
20268
  var import_chalk12 = __toESM(require("chalk"));
20087
- var import_fs39 = __toESM(require("fs"));
20088
- var import_path40 = __toESM(require("path"));
20089
- var import_os35 = __toESM(require("os"));
20269
+ var import_fs40 = __toESM(require("fs"));
20270
+ var import_path41 = __toESM(require("path"));
20271
+ var import_os36 = __toESM(require("os"));
20090
20272
  function formatRelativeTime(timestamp) {
20091
20273
  const diff = Date.now() - new Date(timestamp).getTime();
20092
20274
  const sec = Math.floor(diff / 1e3);
@@ -20099,14 +20281,14 @@ function formatRelativeTime(timestamp) {
20099
20281
  }
20100
20282
  function registerAuditCommand(program2) {
20101
20283
  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) => {
20102
- const logPath = import_path40.default.join(import_os35.default.homedir(), ".node9", "audit.log");
20103
- if (!import_fs39.default.existsSync(logPath)) {
20284
+ const logPath = import_path41.default.join(import_os36.default.homedir(), ".node9", "audit.log");
20285
+ if (!import_fs40.default.existsSync(logPath)) {
20104
20286
  console.log(
20105
20287
  import_chalk12.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
20106
20288
  );
20107
20289
  return;
20108
20290
  }
20109
- const raw = import_fs39.default.readFileSync(logPath, "utf-8");
20291
+ const raw = import_fs40.default.readFileSync(logPath, "utf-8");
20110
20292
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
20111
20293
  let entries = lines.flatMap((line) => {
20112
20294
  try {
@@ -20162,9 +20344,9 @@ function registerAuditCommand(program2) {
20162
20344
  var import_chalk13 = __toESM(require("chalk"));
20163
20345
 
20164
20346
  // src/cli/aggregate/report-audit.ts
20165
- var import_fs40 = __toESM(require("fs"));
20166
- var import_os36 = __toESM(require("os"));
20167
- var import_path41 = __toESM(require("path"));
20347
+ var import_fs41 = __toESM(require("fs"));
20348
+ var import_os37 = __toESM(require("os"));
20349
+ var import_path42 = __toESM(require("path"));
20168
20350
  init_costSync();
20169
20351
  init_litellm();
20170
20352
  init_cost_codex();
@@ -20247,8 +20429,8 @@ function getDateRange(period, now) {
20247
20429
  }
20248
20430
  }
20249
20431
  function parseAuditLog(logPath) {
20250
- if (!import_fs40.default.existsSync(logPath)) return [];
20251
- const raw = import_fs40.default.readFileSync(logPath, "utf-8");
20432
+ if (!import_fs41.default.existsSync(logPath)) return [];
20433
+ const raw = import_fs41.default.readFileSync(logPath, "utf-8");
20252
20434
  return raw.split("\n").flatMap((line) => {
20253
20435
  if (!line.trim()) return [];
20254
20436
  try {
@@ -20295,25 +20477,25 @@ function freezeClaudeCost(acc) {
20295
20477
  };
20296
20478
  }
20297
20479
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
20298
- const projPath = import_path41.default.join(projectsDir, proj);
20480
+ const projPath = import_path42.default.join(projectsDir, proj);
20299
20481
  let files;
20300
20482
  try {
20301
- const stat = import_fs40.default.statSync(projPath);
20483
+ const stat = import_fs41.default.statSync(projPath);
20302
20484
  if (!stat.isDirectory()) return;
20303
- files = import_fs40.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
20485
+ files = import_fs41.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
20304
20486
  } catch {
20305
20487
  return;
20306
20488
  }
20307
20489
  const startMs = start.getTime();
20308
20490
  for (const file of files) {
20309
- const filePath = import_path41.default.join(projPath, file);
20491
+ const filePath = import_path42.default.join(projPath, file);
20310
20492
  try {
20311
- if (import_fs40.default.statSync(filePath).mtimeMs < startMs) continue;
20493
+ if (import_fs41.default.statSync(filePath).mtimeMs < startMs) continue;
20312
20494
  } catch {
20313
20495
  continue;
20314
20496
  }
20315
20497
  try {
20316
- const raw = import_fs40.default.readFileSync(filePath, "utf-8");
20498
+ const raw = import_fs41.default.readFileSync(filePath, "utf-8");
20317
20499
  for (const line of raw.split("\n")) {
20318
20500
  if (!line.trim()) continue;
20319
20501
  let entry;
@@ -20363,10 +20545,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
20363
20545
  }
20364
20546
  function loadClaudeCost(start, end, projectsDir) {
20365
20547
  const acc = emptyClaudeCostAccumulator();
20366
- if (!import_fs40.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
20548
+ if (!import_fs41.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
20367
20549
  let dirs;
20368
20550
  try {
20369
- dirs = import_fs40.default.readdirSync(projectsDir);
20551
+ dirs = import_fs41.default.readdirSync(projectsDir);
20370
20552
  } catch {
20371
20553
  return freezeClaudeCost(acc);
20372
20554
  }
@@ -20378,7 +20560,7 @@ function loadClaudeCost(start, end, projectsDir) {
20378
20560
  function processCodexCostFile(filePath, start, end, acc) {
20379
20561
  let lines;
20380
20562
  try {
20381
- lines = import_fs40.default.readFileSync(filePath, "utf-8").split("\n");
20563
+ lines = import_fs41.default.readFileSync(filePath, "utf-8").split("\n");
20382
20564
  } catch {
20383
20565
  return;
20384
20566
  }
@@ -20433,31 +20615,31 @@ function processCodexCostFile(filePath, start, end, acc) {
20433
20615
  }
20434
20616
  function listCodexSessionFiles2(sessionsBase) {
20435
20617
  const jsonlFiles = [];
20436
- if (!import_fs40.default.existsSync(sessionsBase)) return jsonlFiles;
20618
+ if (!import_fs41.default.existsSync(sessionsBase)) return jsonlFiles;
20437
20619
  try {
20438
- for (const year of import_fs40.default.readdirSync(sessionsBase)) {
20439
- const yearPath = import_path41.default.join(sessionsBase, year);
20620
+ for (const year of import_fs41.default.readdirSync(sessionsBase)) {
20621
+ const yearPath = import_path42.default.join(sessionsBase, year);
20440
20622
  try {
20441
- if (!import_fs40.default.statSync(yearPath).isDirectory()) continue;
20623
+ if (!import_fs41.default.statSync(yearPath).isDirectory()) continue;
20442
20624
  } catch {
20443
20625
  continue;
20444
20626
  }
20445
- for (const month of import_fs40.default.readdirSync(yearPath)) {
20446
- const monthPath = import_path41.default.join(yearPath, month);
20627
+ for (const month of import_fs41.default.readdirSync(yearPath)) {
20628
+ const monthPath = import_path42.default.join(yearPath, month);
20447
20629
  try {
20448
- if (!import_fs40.default.statSync(monthPath).isDirectory()) continue;
20630
+ if (!import_fs41.default.statSync(monthPath).isDirectory()) continue;
20449
20631
  } catch {
20450
20632
  continue;
20451
20633
  }
20452
- for (const day of import_fs40.default.readdirSync(monthPath)) {
20453
- const dayPath = import_path41.default.join(monthPath, day);
20634
+ for (const day of import_fs41.default.readdirSync(monthPath)) {
20635
+ const dayPath = import_path42.default.join(monthPath, day);
20454
20636
  try {
20455
- if (!import_fs40.default.statSync(dayPath).isDirectory()) continue;
20637
+ if (!import_fs41.default.statSync(dayPath).isDirectory()) continue;
20456
20638
  } catch {
20457
20639
  continue;
20458
20640
  }
20459
- for (const file of import_fs40.default.readdirSync(dayPath)) {
20460
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path41.default.join(dayPath, file));
20641
+ for (const file of import_fs41.default.readdirSync(dayPath)) {
20642
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path42.default.join(dayPath, file));
20461
20643
  }
20462
20644
  }
20463
20645
  }
@@ -20522,13 +20704,13 @@ function freezeGeminiCost(acc) {
20522
20704
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
20523
20705
  const startMs = start.getTime();
20524
20706
  try {
20525
- if (import_fs40.default.statSync(filePath).mtimeMs < startMs) return;
20707
+ if (import_fs41.default.statSync(filePath).mtimeMs < startMs) return;
20526
20708
  } catch {
20527
20709
  return;
20528
20710
  }
20529
20711
  let raw;
20530
20712
  try {
20531
- raw = import_fs40.default.readFileSync(filePath, "utf-8");
20713
+ raw = import_fs41.default.readFileSync(filePath, "utf-8");
20532
20714
  } catch {
20533
20715
  return;
20534
20716
  }
@@ -20577,30 +20759,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
20577
20759
  const out = [];
20578
20760
  let dirs;
20579
20761
  try {
20580
- if (!import_fs40.default.statSync(geminiTmpDir2).isDirectory()) return out;
20581
- dirs = import_fs40.default.readdirSync(geminiTmpDir2);
20762
+ if (!import_fs41.default.statSync(geminiTmpDir2).isDirectory()) return out;
20763
+ dirs = import_fs41.default.readdirSync(geminiTmpDir2);
20582
20764
  } catch {
20583
20765
  return out;
20584
20766
  }
20585
20767
  for (const proj of dirs) {
20586
- const chatsDir = import_path41.default.join(geminiTmpDir2, proj, "chats");
20768
+ const chatsDir = import_path42.default.join(geminiTmpDir2, proj, "chats");
20587
20769
  let files;
20588
20770
  try {
20589
- if (!import_fs40.default.statSync(chatsDir).isDirectory()) continue;
20590
- files = import_fs40.default.readdirSync(chatsDir);
20771
+ if (!import_fs41.default.statSync(chatsDir).isDirectory()) continue;
20772
+ files = import_fs41.default.readdirSync(chatsDir);
20591
20773
  } catch {
20592
20774
  continue;
20593
20775
  }
20594
20776
  for (const f of files) {
20595
20777
  if (!f.endsWith(".jsonl")) continue;
20596
- out.push({ projectKey: proj, file: import_path41.default.join(chatsDir, f) });
20778
+ out.push({ projectKey: proj, file: import_path42.default.join(chatsDir, f) });
20597
20779
  }
20598
20780
  }
20599
20781
  return out;
20600
20782
  }
20601
20783
  function loadGeminiCost(start, end, geminiTmpDir2) {
20602
20784
  const acc = emptyGeminiAccumulator();
20603
- if (!import_fs40.default.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
20785
+ if (!import_fs41.default.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
20604
20786
  for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
20605
20787
  processGeminiCostFile(file, projectKey, start, end, acc);
20606
20788
  }
@@ -20608,11 +20790,11 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
20608
20790
  }
20609
20791
  function aggregateReportFromAudit(period, opts = {}) {
20610
20792
  const now = opts.now ?? /* @__PURE__ */ new Date();
20611
- const auditLogPath = opts.auditLogPath ?? import_path41.default.join(import_os36.default.homedir(), ".node9", "audit.log");
20612
- const claudeProjectsDir = opts.claudeProjectsDir ?? import_path41.default.join(import_os36.default.homedir(), ".claude", "projects");
20613
- const codexSessionsDir2 = opts.codexSessionsDir ?? import_path41.default.join(import_os36.default.homedir(), ".codex", "sessions");
20614
- const geminiTmpDir2 = opts.geminiTmpDir ?? import_path41.default.join(import_os36.default.homedir(), ".gemini", "tmp");
20615
- const hasAuditFile = import_fs40.default.existsSync(auditLogPath);
20793
+ const auditLogPath = opts.auditLogPath ?? import_path42.default.join(import_os37.default.homedir(), ".node9", "audit.log");
20794
+ const claudeProjectsDir = opts.claudeProjectsDir ?? import_path42.default.join(import_os37.default.homedir(), ".claude", "projects");
20795
+ const codexSessionsDir2 = opts.codexSessionsDir ?? import_path42.default.join(import_os37.default.homedir(), ".codex", "sessions");
20796
+ const geminiTmpDir2 = opts.geminiTmpDir ?? import_path42.default.join(import_os37.default.homedir(), ".gemini", "tmp");
20797
+ const hasAuditFile = import_fs41.default.existsSync(auditLogPath);
20616
20798
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
20617
20799
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
20618
20800
  const { start, end } = getDateRange(period, now);
@@ -21308,23 +21490,23 @@ function registerDaemonCommand(program2) {
21308
21490
 
21309
21491
  // src/cli/commands/status.ts
21310
21492
  var import_chalk15 = __toESM(require("chalk"));
21311
- var import_fs41 = __toESM(require("fs"));
21312
- var import_path42 = __toESM(require("path"));
21313
- var import_os37 = __toESM(require("os"));
21314
- var yaml2 = __toESM(require("yaml"));
21493
+ var import_fs42 = __toESM(require("fs"));
21494
+ var import_path43 = __toESM(require("path"));
21495
+ var import_os38 = __toESM(require("os"));
21496
+ var yaml3 = __toESM(require("yaml"));
21315
21497
  init_core();
21316
21498
  init_daemon();
21317
21499
  init_setup();
21318
21500
  function readHermesHooks(configPath) {
21319
- if (!import_fs41.default.existsSync(configPath)) return null;
21501
+ if (!import_fs42.default.existsSync(configPath)) return null;
21320
21502
  let raw;
21321
21503
  try {
21322
- raw = import_fs41.default.readFileSync(configPath, "utf-8");
21504
+ raw = import_fs42.default.readFileSync(configPath, "utf-8");
21323
21505
  } catch {
21324
21506
  return null;
21325
21507
  }
21326
21508
  try {
21327
- const cfg = yaml2.parse(raw);
21509
+ const cfg = yaml3.parse(raw);
21328
21510
  const has = (event) => (cfg?.hooks?.[event] ?? []).some(
21329
21511
  (e) => typeof e?.command === "string" && isNode9Hook(e.command)
21330
21512
  );
@@ -21338,17 +21520,17 @@ function readHermesHooks(configPath) {
21338
21520
  return { pre: false, post: false };
21339
21521
  }
21340
21522
  }
21341
- function readJson2(filePath) {
21523
+ function readJson3(filePath) {
21342
21524
  try {
21343
- if (import_fs41.default.existsSync(filePath)) return JSON.parse(import_fs41.default.readFileSync(filePath, "utf-8"));
21525
+ if (import_fs42.default.existsSync(filePath)) return JSON.parse(import_fs42.default.readFileSync(filePath, "utf-8"));
21344
21526
  } catch {
21345
21527
  }
21346
21528
  return null;
21347
21529
  }
21348
- function matchersHaveNode9Hook(matchers) {
21530
+ function matchersHaveNode9Hook2(matchers) {
21349
21531
  return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
21350
21532
  }
21351
- function flatHaveNode9Hook(entries) {
21533
+ function flatHaveNode9Hook2(entries) {
21352
21534
  return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
21353
21535
  }
21354
21536
  function wrappedMcpServers(servers) {
@@ -21408,42 +21590,42 @@ function registerStatusCommand(program2) {
21408
21590
  console.log("");
21409
21591
  const modeLabel = settings.mode === "audit" ? import_chalk15.default.blue("audit") : settings.mode === "strict" ? import_chalk15.default.red("strict") : import_chalk15.default.white("standard");
21410
21592
  console.log(` Mode: ${modeLabel}`);
21411
- const projectConfig = import_path42.default.join(process.cwd(), "node9.config.json");
21412
- const globalConfig = import_path42.default.join(import_os37.default.homedir(), ".node9", "config.json");
21593
+ const projectConfig = import_path43.default.join(process.cwd(), "node9.config.json");
21594
+ const globalConfig = import_path43.default.join(import_os38.default.homedir(), ".node9", "config.json");
21413
21595
  console.log(
21414
- ` Local: ${import_fs41.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
21596
+ ` Local: ${import_fs42.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
21415
21597
  );
21416
21598
  console.log(
21417
- ` Global: ${import_fs41.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
21599
+ ` Global: ${import_fs42.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
21418
21600
  );
21419
21601
  if (mergedConfig.policy.sandboxPaths.length > 0) {
21420
21602
  console.log(
21421
21603
  ` Sandbox: ${import_chalk15.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
21422
21604
  );
21423
21605
  }
21424
- const homeDir2 = import_os37.default.homedir();
21425
- const claudeSettings = readJson2(
21426
- import_path42.default.join(homeDir2, ".claude", "settings.json")
21606
+ const homeDir2 = import_os38.default.homedir();
21607
+ const claudeSettings = readJson3(
21608
+ import_path43.default.join(homeDir2, ".claude", "settings.json")
21427
21609
  );
21428
- const claudeConfig = readJson2(import_path42.default.join(homeDir2, ".claude.json"));
21429
- const geminiSettings = readJson2(
21430
- import_path42.default.join(homeDir2, ".gemini", "settings.json")
21610
+ const claudeConfig = readJson3(import_path43.default.join(homeDir2, ".claude.json"));
21611
+ const geminiSettings = readJson3(
21612
+ import_path43.default.join(homeDir2, ".gemini", "settings.json")
21431
21613
  );
21432
- const cursorConfig = readJson2(import_path42.default.join(homeDir2, ".cursor", "mcp.json"));
21433
- const antigravityHooks = readJson2(
21434
- import_path42.default.join(homeDir2, ".gemini", "config", "hooks.json")
21614
+ const cursorConfig = readJson3(import_path43.default.join(homeDir2, ".cursor", "mcp.json"));
21615
+ const antigravityHooks = readJson3(
21616
+ import_path43.default.join(homeDir2, ".gemini", "config", "hooks.json")
21435
21617
  );
21436
- const antigravityMcp = readJson2(
21437
- import_path42.default.join(homeDir2, ".gemini", "config", "mcp_config.json")
21618
+ const antigravityMcp = readJson3(
21619
+ import_path43.default.join(homeDir2, ".gemini", "config", "mcp_config.json")
21438
21620
  );
21439
- const antigravityPresent = antigravityHooks !== null || import_fs41.default.existsSync(import_path42.default.join(homeDir2, ".gemini", "antigravity-cli")) || import_fs41.default.existsSync(import_path42.default.join(homeDir2, ".gemini", "antigravity-ide"));
21440
- const copilotHooks = readJson2(
21441
- import_path42.default.join(homeDir2, ".copilot", "hooks", "node9.json")
21621
+ const antigravityPresent = antigravityHooks !== null || import_fs42.default.existsSync(import_path43.default.join(homeDir2, ".gemini", "antigravity-cli")) || import_fs42.default.existsSync(import_path43.default.join(homeDir2, ".gemini", "antigravity-ide"));
21622
+ const copilotHooks = readJson3(
21623
+ import_path43.default.join(homeDir2, ".copilot", "hooks", "node9.json")
21442
21624
  );
21443
- const copilotMcp = readJson2(
21444
- import_path42.default.join(homeDir2, ".copilot", "mcp-config.json")
21625
+ const copilotMcp = readJson3(
21626
+ import_path43.default.join(homeDir2, ".copilot", "mcp-config.json")
21445
21627
  );
21446
- const copilotPresent = import_fs41.default.existsSync(import_path42.default.join(homeDir2, ".copilot"));
21628
+ const copilotPresent = import_fs42.default.existsSync(import_path43.default.join(homeDir2, ".copilot"));
21447
21629
  const hermesHooks = readHermesHooks(hermesConfigPath(homeDir2));
21448
21630
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig || antigravityPresent || copilotPresent || hermesHooks;
21449
21631
  if (agentFound) {
@@ -21451,8 +21633,8 @@ function registerStatusCommand(program2) {
21451
21633
  console.log(import_chalk15.default.bold(" Agent Wiring:"));
21452
21634
  console.log("");
21453
21635
  if (claudeSettings || claudeConfig) {
21454
- const preHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PreToolUse);
21455
- const postHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PostToolUse);
21636
+ const preHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PreToolUse);
21637
+ const postHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PostToolUse);
21456
21638
  printAgentSection(
21457
21639
  "Claude Code",
21458
21640
  [
@@ -21464,8 +21646,8 @@ function registerStatusCommand(program2) {
21464
21646
  console.log("");
21465
21647
  }
21466
21648
  if (geminiSettings) {
21467
- const beforeHook = matchersHaveNode9Hook(geminiSettings.hooks?.BeforeTool);
21468
- const afterHook = matchersHaveNode9Hook(geminiSettings.hooks?.AfterTool);
21649
+ const beforeHook = matchersHaveNode9Hook2(geminiSettings.hooks?.BeforeTool);
21650
+ const afterHook = matchersHaveNode9Hook2(geminiSettings.hooks?.AfterTool);
21469
21651
  printAgentSection(
21470
21652
  "Gemini CLI",
21471
21653
  [
@@ -21477,8 +21659,8 @@ function registerStatusCommand(program2) {
21477
21659
  console.log("");
21478
21660
  }
21479
21661
  if (antigravityPresent) {
21480
- const preHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PreToolUse);
21481
- const postHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PostToolUse);
21662
+ const preHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PreToolUse);
21663
+ const postHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PostToolUse);
21482
21664
  printAgentSection(
21483
21665
  "Antigravity",
21484
21666
  [
@@ -21490,9 +21672,9 @@ function registerStatusCommand(program2) {
21490
21672
  console.log("");
21491
21673
  }
21492
21674
  if (copilotPresent) {
21493
- const preHook = flatHaveNode9Hook(copilotHooks?.hooks?.PreToolUse);
21494
- const postHook = flatHaveNode9Hook(copilotHooks?.hooks?.PostToolUse);
21495
- const promptHook = flatHaveNode9Hook(copilotHooks?.hooks?.UserPromptSubmit);
21675
+ const preHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PreToolUse);
21676
+ const postHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PostToolUse);
21677
+ const promptHook = flatHaveNode9Hook2(copilotHooks?.hooks?.UserPromptSubmit);
21496
21678
  printAgentSection(
21497
21679
  "GitHub Copilot",
21498
21680
  [
@@ -21535,9 +21717,9 @@ function registerStatusCommand(program2) {
21535
21717
 
21536
21718
  // src/cli/commands/init.ts
21537
21719
  var import_chalk16 = __toESM(require("chalk"));
21538
- var import_fs42 = __toESM(require("fs"));
21539
- var import_path43 = __toESM(require("path"));
21540
- var import_os38 = __toESM(require("os"));
21720
+ var import_fs43 = __toESM(require("fs"));
21721
+ var import_path44 = __toESM(require("path"));
21722
+ var import_os39 = __toESM(require("os"));
21541
21723
  var import_https4 = __toESM(require("https"));
21542
21724
  init_core();
21543
21725
  init_setup();
@@ -21627,16 +21809,16 @@ function registerInitCommand(program2) {
21627
21809
  }
21628
21810
  console.log("");
21629
21811
  }
21630
- const configPath = import_path43.default.join(import_os38.default.homedir(), ".node9", "config.json");
21631
- const isFirstInstall = !import_fs42.default.existsSync(configPath);
21632
- if (import_fs42.default.existsSync(configPath) && !options.force) {
21812
+ const configPath = import_path44.default.join(import_os39.default.homedir(), ".node9", "config.json");
21813
+ const isFirstInstall = !import_fs43.default.existsSync(configPath);
21814
+ if (import_fs43.default.existsSync(configPath) && !options.force) {
21633
21815
  try {
21634
- const existing = JSON.parse(import_fs42.default.readFileSync(configPath, "utf-8"));
21816
+ const existing = JSON.parse(import_fs43.default.readFileSync(configPath, "utf-8"));
21635
21817
  const settings = existing.settings ?? {};
21636
21818
  if (settings.mode !== chosenMode) {
21637
21819
  settings.mode = chosenMode;
21638
21820
  existing.settings = settings;
21639
- import_fs42.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
21821
+ import_fs43.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
21640
21822
  console.log(import_chalk16.default.green(`\u2705 Mode updated: ${chosenMode}`));
21641
21823
  } else {
21642
21824
  console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -21649,9 +21831,9 @@ function registerInitCommand(program2) {
21649
21831
  ...DEFAULT_CONFIG,
21650
21832
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
21651
21833
  };
21652
- const dir = import_path43.default.dirname(configPath);
21653
- if (!import_fs42.default.existsSync(dir)) import_fs42.default.mkdirSync(dir, { recursive: true });
21654
- import_fs42.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
21834
+ const dir = import_path44.default.dirname(configPath);
21835
+ if (!import_fs43.default.existsSync(dir)) import_fs43.default.mkdirSync(dir, { recursive: true });
21836
+ import_fs43.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
21655
21837
  console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath}`));
21656
21838
  console.log(import_chalk16.default.gray(` Mode: ${chosenMode}`));
21657
21839
  }
@@ -21756,7 +21938,7 @@ function registerInitCommand(program2) {
21756
21938
  }
21757
21939
 
21758
21940
  // src/cli/commands/undo.ts
21759
- var import_path44 = __toESM(require("path"));
21941
+ var import_path45 = __toESM(require("path"));
21760
21942
  var import_chalk18 = __toESM(require("chalk"));
21761
21943
 
21762
21944
  // src/tui/undo-navigator.ts
@@ -21915,7 +22097,7 @@ function findMatchingCwd(startDir, history) {
21915
22097
  let dir = startDir;
21916
22098
  while (true) {
21917
22099
  if (cwds.has(dir)) return dir;
21918
- const parent = import_path44.default.dirname(dir);
22100
+ const parent = import_path45.default.dirname(dir);
21919
22101
  if (parent === dir) return null;
21920
22102
  dir = parent;
21921
22103
  }
@@ -22047,6 +22229,8 @@ var import_chalk19 = __toESM(require("chalk"));
22047
22229
  var import_child_process10 = require("child_process");
22048
22230
  var import_execa3 = require("execa");
22049
22231
  init_orchestrator();
22232
+ init_cloud();
22233
+ init_config();
22050
22234
  init_provenance();
22051
22235
  init_mcp_pin();
22052
22236
  init_mcp_tools();
@@ -22075,6 +22259,60 @@ function normalizeClientName(name) {
22075
22259
  const sanitized = sanitize4(name).slice(0, 40);
22076
22260
  return sanitized.length > 0 ? sanitized : void 0;
22077
22261
  }
22262
+ function reportPinMismatchToCloud(serverKey, agent) {
22263
+ try {
22264
+ const creds = getCredentials();
22265
+ if (!creds) return;
22266
+ void auditLocalAllow(
22267
+ `mcp-server:${serverKey}`,
22268
+ { serverKey, reason: "tool-pin-mismatch" },
22269
+ "mcp-pin-mismatch",
22270
+ creds,
22271
+ { mcpServer: serverKey, agent },
22272
+ void 0,
22273
+ false,
22274
+ {
22275
+ ruleName: "MCP tool definitions changed (possible rug pull)",
22276
+ 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}`
22277
+ }
22278
+ );
22279
+ } catch {
22280
+ }
22281
+ }
22282
+ function reportInventoryToCloud(serverKey, toolCount, agent) {
22283
+ try {
22284
+ const creds = getCredentials();
22285
+ if (!creds) return;
22286
+ void auditLocalAllow(
22287
+ `mcp-server:${serverKey}`,
22288
+ { serverKey, toolCount },
22289
+ "mcp-discovered",
22290
+ creds,
22291
+ { mcpServer: serverKey, agent },
22292
+ void 0,
22293
+ false,
22294
+ { mcpToolCount: toolCount }
22295
+ );
22296
+ } catch {
22297
+ }
22298
+ }
22299
+ function reportLargeResponseToCloud(serverKey, responseBytes, agent) {
22300
+ try {
22301
+ const creds = getCredentials();
22302
+ if (!creds) return;
22303
+ void auditLocalAllow(
22304
+ `mcp-server:${serverKey}`,
22305
+ { serverKey, responseBytes },
22306
+ "mcp-large-response",
22307
+ creds,
22308
+ { mcpServer: serverKey, agent },
22309
+ void 0,
22310
+ false,
22311
+ { mcpResponseBytes: responseBytes }
22312
+ );
22313
+ } catch {
22314
+ }
22315
+ }
22078
22316
  function tokenize4(cmd) {
22079
22317
  const tokens = [];
22080
22318
  let current = "";
@@ -22335,6 +22573,7 @@ async function runMcpGateway(upstreamCommand) {
22335
22573
  const currentHash = hashToolDefinitions(tools);
22336
22574
  const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
22337
22575
  const token = getInternalToken();
22576
+ reportInventoryToCloud(serverKey, tools.length, clientName);
22338
22577
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
22339
22578
  const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
22340
22579
  fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/discovered`, {
@@ -22399,6 +22638,7 @@ async function runMcpGateway(upstreamCommand) {
22399
22638
  console.error(import_chalk19.default.red(" Session quarantined \u2014 all tool calls blocked."));
22400
22639
  console.error(import_chalk19.default.yellow(` Run: node9 mcp pin update ${serverKey}
22401
22640
  `));
22641
+ reportPinMismatchToCloud(serverKey, clientName);
22402
22642
  const errorResponse = {
22403
22643
  jsonrpc: "2.0",
22404
22644
  id: parsed.id,
@@ -22444,6 +22684,7 @@ async function runMcpGateway(upstreamCommand) {
22444
22684
  `\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
22445
22685
  )
22446
22686
  );
22687
+ reportLargeResponseToCloud(serverKey, line.length, clientName);
22447
22688
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
22448
22689
  const token = getInternalToken();
22449
22690
  fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/large-response`, {
@@ -22491,9 +22732,9 @@ function registerMcpGatewayCommand(program2) {
22491
22732
 
22492
22733
  // src/mcp-server/index.ts
22493
22734
  var import_readline5 = __toESM(require("readline"));
22494
- var import_fs43 = __toESM(require("fs"));
22495
- var import_os39 = __toESM(require("os"));
22496
- var import_path45 = __toESM(require("path"));
22735
+ var import_fs44 = __toESM(require("fs"));
22736
+ var import_os40 = __toESM(require("os"));
22737
+ var import_path46 = __toESM(require("path"));
22497
22738
  var import_child_process11 = require("child_process");
22498
22739
  init_core();
22499
22740
  init_daemon();
@@ -22744,13 +22985,13 @@ function handleStatus() {
22744
22985
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
22745
22986
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
22746
22987
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
22747
- const projectConfig = import_path45.default.join(process.cwd(), "node9.config.json");
22748
- const globalConfig = import_path45.default.join(import_os39.default.homedir(), ".node9", "config.json");
22988
+ const projectConfig = import_path46.default.join(process.cwd(), "node9.config.json");
22989
+ const globalConfig = import_path46.default.join(import_os40.default.homedir(), ".node9", "config.json");
22749
22990
  lines.push(
22750
- `Project config (node9.config.json): ${import_fs43.default.existsSync(projectConfig) ? "present" : "not found"}`
22991
+ `Project config (node9.config.json): ${import_fs44.default.existsSync(projectConfig) ? "present" : "not found"}`
22751
22992
  );
22752
22993
  lines.push(
22753
- `Global config (~/.node9/config.json): ${import_fs43.default.existsSync(globalConfig) ? "present" : "not found"}`
22994
+ `Global config (~/.node9/config.json): ${import_fs44.default.existsSync(globalConfig) ? "present" : "not found"}`
22754
22995
  );
22755
22996
  return lines.join("\n");
22756
22997
  }
@@ -22824,21 +23065,21 @@ function handleShieldDisable(args) {
22824
23065
  writeActiveShields(active.filter((s) => s !== name));
22825
23066
  return `Shield "${name}" disabled.`;
22826
23067
  }
22827
- var GLOBAL_CONFIG_PATH = import_path45.default.join(import_os39.default.homedir(), ".node9", "config.json");
23068
+ var GLOBAL_CONFIG_PATH = import_path46.default.join(import_os40.default.homedir(), ".node9", "config.json");
22828
23069
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
22829
23070
  function readGlobalConfigRaw() {
22830
23071
  try {
22831
- if (import_fs43.default.existsSync(GLOBAL_CONFIG_PATH)) {
22832
- return JSON.parse(import_fs43.default.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
23072
+ if (import_fs44.default.existsSync(GLOBAL_CONFIG_PATH)) {
23073
+ return JSON.parse(import_fs44.default.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
22833
23074
  }
22834
23075
  } catch {
22835
23076
  }
22836
23077
  return {};
22837
23078
  }
22838
23079
  function writeGlobalConfigRaw(data) {
22839
- const dir = import_path45.default.dirname(GLOBAL_CONFIG_PATH);
22840
- if (!import_fs43.default.existsSync(dir)) import_fs43.default.mkdirSync(dir, { recursive: true });
22841
- import_fs43.default.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
23080
+ const dir = import_path46.default.dirname(GLOBAL_CONFIG_PATH);
23081
+ if (!import_fs44.default.existsSync(dir)) import_fs44.default.mkdirSync(dir, { recursive: true });
23082
+ import_fs44.default.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
22842
23083
  }
22843
23084
  function handleApproverList() {
22844
23085
  const config = getConfig();
@@ -22882,9 +23123,9 @@ function handleApproverSet(args) {
22882
23123
  function handleAuditGet(args) {
22883
23124
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
22884
23125
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
22885
- const auditPath = import_path45.default.join(import_os39.default.homedir(), ".node9", "audit.log");
22886
- if (!import_fs43.default.existsSync(auditPath)) return "No audit log found.";
22887
- const rawLines = import_fs43.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
23126
+ const auditPath = import_path46.default.join(import_os40.default.homedir(), ".node9", "audit.log");
23127
+ if (!import_fs44.default.existsSync(auditPath)) return "No audit log found.";
23128
+ const rawLines = import_fs44.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
22888
23129
  const parsed = [];
22889
23130
  for (const line of rawLines) {
22890
23131
  try {
@@ -23219,7 +23460,7 @@ function registerTrustCommand(program2) {
23219
23460
  // src/cli/commands/mcp-pin.ts
23220
23461
  var import_chalk21 = __toESM(require("chalk"));
23221
23462
  init_mcp_pin();
23222
- var import_fs44 = __toESM(require("fs"));
23463
+ var import_fs45 = __toESM(require("fs"));
23223
23464
  function registerMcpPinCommand(program2) {
23224
23465
  const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
23225
23466
  const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
@@ -23230,7 +23471,7 @@ function registerMcpPinCommand(program2) {
23230
23471
  let repoCorrupt = false;
23231
23472
  if (found.source === "repo") {
23232
23473
  try {
23233
- const raw = import_fs44.default.readFileSync(found.path, "utf-8");
23474
+ const raw = import_fs45.default.readFileSync(found.path, "utf-8");
23234
23475
  const parsed = JSON.parse(raw);
23235
23476
  repoEntries = parsed.servers ?? {};
23236
23477
  } catch {
@@ -23543,9 +23784,9 @@ init_scan();
23543
23784
 
23544
23785
  // src/cli/commands/sessions.ts
23545
23786
  var import_chalk24 = __toESM(require("chalk"));
23546
- var import_fs45 = __toESM(require("fs"));
23547
- var import_path46 = __toESM(require("path"));
23548
- var import_os40 = __toESM(require("os"));
23787
+ var import_fs46 = __toESM(require("fs"));
23788
+ var import_path47 = __toESM(require("path"));
23789
+ var import_os41 = __toESM(require("os"));
23549
23790
  init_scan_summary();
23550
23791
  init_litellm();
23551
23792
  init_cost_gemini();
@@ -23566,10 +23807,10 @@ function encodeProjectPath(projectPath) {
23566
23807
  }
23567
23808
  function sessionJsonlPath(projectPath, sessionId) {
23568
23809
  const encoded = encodeProjectPath(projectPath);
23569
- return import_path46.default.join(import_os40.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
23810
+ return import_path47.default.join(import_os41.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
23570
23811
  }
23571
23812
  function projectLabel(projectPath) {
23572
- return projectPath.replace(import_os40.default.homedir(), "~");
23813
+ return projectPath.replace(import_os41.default.homedir(), "~");
23573
23814
  }
23574
23815
  function parseHistoryLines(lines) {
23575
23816
  const entries = [];
@@ -23638,10 +23879,10 @@ function parseSessionLines(lines) {
23638
23879
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
23639
23880
  }
23640
23881
  function loadAuditEntries(auditPath) {
23641
- const aPath = auditPath ?? import_path46.default.join(import_os40.default.homedir(), ".node9", "audit.log");
23882
+ const aPath = auditPath ?? import_path47.default.join(import_os41.default.homedir(), ".node9", "audit.log");
23642
23883
  let raw;
23643
23884
  try {
23644
- raw = import_fs45.default.readFileSync(aPath, "utf-8");
23885
+ raw = import_fs46.default.readFileSync(aPath, "utf-8");
23645
23886
  } catch {
23646
23887
  return [];
23647
23888
  }
@@ -23677,8 +23918,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
23677
23918
  return result;
23678
23919
  }
23679
23920
  function buildGeminiSessions(days, allAuditEntries) {
23680
- const tmpDir = import_path46.default.join(import_os40.default.homedir(), ".gemini", "tmp");
23681
- if (!import_fs45.default.existsSync(tmpDir)) return [];
23921
+ const tmpDir = import_path47.default.join(import_os41.default.homedir(), ".gemini", "tmp");
23922
+ if (!import_fs46.default.existsSync(tmpDir)) return [];
23682
23923
  const cutoff = days !== null ? (() => {
23683
23924
  const d = /* @__PURE__ */ new Date();
23684
23925
  d.setDate(d.getDate() - days);
@@ -23687,35 +23928,35 @@ function buildGeminiSessions(days, allAuditEntries) {
23687
23928
  })() : null;
23688
23929
  let slugDirs;
23689
23930
  try {
23690
- slugDirs = import_fs45.default.readdirSync(tmpDir);
23931
+ slugDirs = import_fs46.default.readdirSync(tmpDir);
23691
23932
  } catch {
23692
23933
  return [];
23693
23934
  }
23694
23935
  const summaries = [];
23695
23936
  for (const slug of slugDirs) {
23696
- const slugPath = import_path46.default.join(tmpDir, slug);
23937
+ const slugPath = import_path47.default.join(tmpDir, slug);
23697
23938
  try {
23698
- if (!import_fs45.default.statSync(slugPath).isDirectory()) continue;
23939
+ if (!import_fs46.default.statSync(slugPath).isDirectory()) continue;
23699
23940
  } catch {
23700
23941
  continue;
23701
23942
  }
23702
- let projectRoot = import_path46.default.join(import_os40.default.homedir(), slug);
23943
+ let projectRoot = import_path47.default.join(import_os41.default.homedir(), slug);
23703
23944
  try {
23704
- projectRoot = import_fs45.default.readFileSync(import_path46.default.join(slugPath, ".project_root"), "utf-8").trim();
23945
+ projectRoot = import_fs46.default.readFileSync(import_path47.default.join(slugPath, ".project_root"), "utf-8").trim();
23705
23946
  } catch {
23706
23947
  }
23707
- const chatsDir = import_path46.default.join(slugPath, "chats");
23708
- if (!import_fs45.default.existsSync(chatsDir)) continue;
23948
+ const chatsDir = import_path47.default.join(slugPath, "chats");
23949
+ if (!import_fs46.default.existsSync(chatsDir)) continue;
23709
23950
  let chatFiles;
23710
23951
  try {
23711
- chatFiles = import_fs45.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
23952
+ chatFiles = import_fs46.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
23712
23953
  } catch {
23713
23954
  continue;
23714
23955
  }
23715
23956
  for (const chatFile of chatFiles) {
23716
23957
  let raw;
23717
23958
  try {
23718
- raw = import_fs45.default.readFileSync(import_path46.default.join(chatsDir, chatFile), "utf-8");
23959
+ raw = import_fs46.default.readFileSync(import_path47.default.join(chatsDir, chatFile), "utf-8");
23719
23960
  } catch {
23720
23961
  continue;
23721
23962
  }
@@ -23795,8 +24036,8 @@ function buildGeminiSessions(days, allAuditEntries) {
23795
24036
  return summaries;
23796
24037
  }
23797
24038
  function buildCodexSessions(days, allAuditEntries) {
23798
- const sessionsBase = import_path46.default.join(import_os40.default.homedir(), ".codex", "sessions");
23799
- if (!import_fs45.default.existsSync(sessionsBase)) return [];
24039
+ const sessionsBase = import_path47.default.join(import_os41.default.homedir(), ".codex", "sessions");
24040
+ if (!import_fs46.default.existsSync(sessionsBase)) return [];
23800
24041
  const cutoff = days !== null ? (() => {
23801
24042
  const d = /* @__PURE__ */ new Date();
23802
24043
  d.setDate(d.getDate() - days);
@@ -23805,29 +24046,29 @@ function buildCodexSessions(days, allAuditEntries) {
23805
24046
  })() : null;
23806
24047
  const jsonlFiles = [];
23807
24048
  try {
23808
- for (const year of import_fs45.default.readdirSync(sessionsBase)) {
23809
- const yearPath = import_path46.default.join(sessionsBase, year);
24049
+ for (const year of import_fs46.default.readdirSync(sessionsBase)) {
24050
+ const yearPath = import_path47.default.join(sessionsBase, year);
23810
24051
  try {
23811
- if (!import_fs45.default.statSync(yearPath).isDirectory()) continue;
24052
+ if (!import_fs46.default.statSync(yearPath).isDirectory()) continue;
23812
24053
  } catch {
23813
24054
  continue;
23814
24055
  }
23815
- for (const month of import_fs45.default.readdirSync(yearPath)) {
23816
- const monthPath = import_path46.default.join(yearPath, month);
24056
+ for (const month of import_fs46.default.readdirSync(yearPath)) {
24057
+ const monthPath = import_path47.default.join(yearPath, month);
23817
24058
  try {
23818
- if (!import_fs45.default.statSync(monthPath).isDirectory()) continue;
24059
+ if (!import_fs46.default.statSync(monthPath).isDirectory()) continue;
23819
24060
  } catch {
23820
24061
  continue;
23821
24062
  }
23822
- for (const day of import_fs45.default.readdirSync(monthPath)) {
23823
- const dayPath = import_path46.default.join(monthPath, day);
24063
+ for (const day of import_fs46.default.readdirSync(monthPath)) {
24064
+ const dayPath = import_path47.default.join(monthPath, day);
23824
24065
  try {
23825
- if (!import_fs45.default.statSync(dayPath).isDirectory()) continue;
24066
+ if (!import_fs46.default.statSync(dayPath).isDirectory()) continue;
23826
24067
  } catch {
23827
24068
  continue;
23828
24069
  }
23829
- for (const file of import_fs45.default.readdirSync(dayPath)) {
23830
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path46.default.join(dayPath, file));
24070
+ for (const file of import_fs46.default.readdirSync(dayPath)) {
24071
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path47.default.join(dayPath, file));
23831
24072
  }
23832
24073
  }
23833
24074
  }
@@ -23839,7 +24080,7 @@ function buildCodexSessions(days, allAuditEntries) {
23839
24080
  for (const filePath of jsonlFiles) {
23840
24081
  let lines;
23841
24082
  try {
23842
- lines = import_fs45.default.readFileSync(filePath, "utf-8").split("\n");
24083
+ lines = import_fs46.default.readFileSync(filePath, "utf-8").split("\n");
23843
24084
  } catch {
23844
24085
  continue;
23845
24086
  }
@@ -23925,10 +24166,10 @@ function buildCodexSessions(days, allAuditEntries) {
23925
24166
  return summaries;
23926
24167
  }
23927
24168
  function buildSessions(days, historyPath) {
23928
- const hPath = historyPath ?? import_path46.default.join(import_os40.default.homedir(), ".claude", "history.jsonl");
24169
+ const hPath = historyPath ?? import_path47.default.join(import_os41.default.homedir(), ".claude", "history.jsonl");
23929
24170
  let historyRaw = "";
23930
24171
  try {
23931
- historyRaw = import_fs45.default.readFileSync(hPath, "utf-8");
24172
+ historyRaw = import_fs46.default.readFileSync(hPath, "utf-8");
23932
24173
  } catch {
23933
24174
  }
23934
24175
  const cutoff = days !== null ? (() => {
@@ -23952,7 +24193,7 @@ function buildSessions(days, historyPath) {
23952
24193
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
23953
24194
  let sessionLines = [];
23954
24195
  try {
23955
- sessionLines = import_fs45.default.readFileSync(jsonlFile, "utf-8").split("\n");
24196
+ sessionLines = import_fs46.default.readFileSync(jsonlFile, "utf-8").split("\n");
23956
24197
  } catch {
23957
24198
  }
23958
24199
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -24252,12 +24493,12 @@ function registerSessionsCommand(program2) {
24252
24493
 
24253
24494
  // src/cli/commands/skill-pin.ts
24254
24495
  var import_chalk25 = __toESM(require("chalk"));
24255
- var import_fs46 = __toESM(require("fs"));
24256
- var import_os41 = __toESM(require("os"));
24257
- var import_path47 = __toESM(require("path"));
24496
+ var import_fs47 = __toESM(require("fs"));
24497
+ var import_os42 = __toESM(require("os"));
24498
+ var import_path48 = __toESM(require("path"));
24258
24499
  function wipeSkillSessions() {
24259
24500
  try {
24260
- import_fs46.default.rmSync(import_path47.default.join(import_os41.default.homedir(), ".node9", "skill-sessions"), {
24501
+ import_fs47.default.rmSync(import_path48.default.join(import_os42.default.homedir(), ".node9", "skill-sessions"), {
24261
24502
  recursive: true,
24262
24503
  force: true
24263
24504
  });
@@ -24339,15 +24580,15 @@ function registerSkillPinCommand(program2) {
24339
24580
  }
24340
24581
 
24341
24582
  // src/cli/commands/decisions.ts
24342
- var import_fs47 = __toESM(require("fs"));
24343
- var import_os42 = __toESM(require("os"));
24344
- var import_path48 = __toESM(require("path"));
24583
+ var import_fs48 = __toESM(require("fs"));
24584
+ var import_os43 = __toESM(require("os"));
24585
+ var import_path49 = __toESM(require("path"));
24345
24586
  var import_chalk26 = __toESM(require("chalk"));
24346
- var DECISIONS_FILE2 = import_path48.default.join(import_os42.default.homedir(), ".node9", "decisions.json");
24587
+ var DECISIONS_FILE2 = import_path49.default.join(import_os43.default.homedir(), ".node9", "decisions.json");
24347
24588
  function readDecisions() {
24348
24589
  try {
24349
- if (!import_fs47.default.existsSync(DECISIONS_FILE2)) return {};
24350
- const raw = import_fs47.default.readFileSync(DECISIONS_FILE2, "utf-8");
24590
+ if (!import_fs48.default.existsSync(DECISIONS_FILE2)) return {};
24591
+ const raw = import_fs48.default.readFileSync(DECISIONS_FILE2, "utf-8");
24351
24592
  const parsed = JSON.parse(raw);
24352
24593
  const out = {};
24353
24594
  for (const [k, v] of Object.entries(parsed)) {
@@ -24359,11 +24600,11 @@ function readDecisions() {
24359
24600
  }
24360
24601
  }
24361
24602
  function writeDecisions(d) {
24362
- const dir = import_path48.default.dirname(DECISIONS_FILE2);
24363
- if (!import_fs47.default.existsSync(dir)) import_fs47.default.mkdirSync(dir, { recursive: true });
24603
+ const dir = import_path49.default.dirname(DECISIONS_FILE2);
24604
+ if (!import_fs48.default.existsSync(dir)) import_fs48.default.mkdirSync(dir, { recursive: true });
24364
24605
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
24365
- import_fs47.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
24366
- import_fs47.default.renameSync(tmp, DECISIONS_FILE2);
24606
+ import_fs48.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
24607
+ import_fs48.default.renameSync(tmp, DECISIONS_FILE2);
24367
24608
  }
24368
24609
  function registerDecisionsCommand(program2) {
24369
24610
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -24420,18 +24661,18 @@ Persistent decisions (${entries.length})
24420
24661
 
24421
24662
  // src/cli/commands/dlp.ts
24422
24663
  var import_chalk27 = __toESM(require("chalk"));
24423
- var import_fs48 = __toESM(require("fs"));
24424
- var import_path49 = __toESM(require("path"));
24425
- var import_os43 = __toESM(require("os"));
24426
- var AUDIT_LOG = import_path49.default.join(import_os43.default.homedir(), ".node9", "audit.log");
24427
- var RESOLVED_FILE = import_path49.default.join(import_os43.default.homedir(), ".node9", "dlp-resolved.json");
24664
+ var import_fs49 = __toESM(require("fs"));
24665
+ var import_path50 = __toESM(require("path"));
24666
+ var import_os44 = __toESM(require("os"));
24667
+ var AUDIT_LOG = import_path50.default.join(import_os44.default.homedir(), ".node9", "audit.log");
24668
+ var RESOLVED_FILE = import_path50.default.join(import_os44.default.homedir(), ".node9", "dlp-resolved.json");
24428
24669
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
24429
24670
  function stripAnsi(s) {
24430
24671
  return s.replace(ANSI_RE, "");
24431
24672
  }
24432
24673
  function loadResolved() {
24433
24674
  try {
24434
- const raw = JSON.parse(import_fs48.default.readFileSync(RESOLVED_FILE, "utf-8"));
24675
+ const raw = JSON.parse(import_fs49.default.readFileSync(RESOLVED_FILE, "utf-8"));
24435
24676
  return new Set(raw);
24436
24677
  } catch {
24437
24678
  return /* @__PURE__ */ new Set();
@@ -24439,13 +24680,13 @@ function loadResolved() {
24439
24680
  }
24440
24681
  function saveResolved(resolved) {
24441
24682
  try {
24442
- import_fs48.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
24683
+ import_fs49.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
24443
24684
  } catch {
24444
24685
  }
24445
24686
  }
24446
24687
  function loadDlpFindings() {
24447
- if (!import_fs48.default.existsSync(AUDIT_LOG)) return [];
24448
- return import_fs48.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
24688
+ if (!import_fs49.default.existsSync(AUDIT_LOG)) return [];
24689
+ return import_fs49.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
24449
24690
  if (!line.trim()) return [];
24450
24691
  try {
24451
24692
  const e = JSON.parse(line);
@@ -24543,15 +24784,15 @@ function registerDlpCommand(program2) {
24543
24784
 
24544
24785
  // src/cli/commands/mask.ts
24545
24786
  var import_chalk28 = __toESM(require("chalk"));
24546
- var import_fs49 = __toESM(require("fs"));
24547
- var import_path50 = __toESM(require("path"));
24548
- var import_os44 = __toESM(require("os"));
24787
+ var import_fs50 = __toESM(require("fs"));
24788
+ var import_path51 = __toESM(require("path"));
24789
+ var import_os45 = __toESM(require("os"));
24549
24790
  init_dlp();
24550
24791
  function findJsonlFiles(dir) {
24551
24792
  const results = [];
24552
- if (!import_fs49.default.existsSync(dir)) return results;
24553
- for (const entry of import_fs49.default.readdirSync(dir, { withFileTypes: true })) {
24554
- const full = import_path50.default.join(dir, entry.name);
24793
+ if (!import_fs50.default.existsSync(dir)) return results;
24794
+ for (const entry of import_fs50.default.readdirSync(dir, { withFileTypes: true })) {
24795
+ const full = import_path51.default.join(dir, entry.name);
24555
24796
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
24556
24797
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
24557
24798
  }
@@ -24594,7 +24835,7 @@ function redactJson(obj) {
24594
24835
  function processFile(filePath, dryRun) {
24595
24836
  let raw;
24596
24837
  try {
24597
- raw = import_fs49.default.readFileSync(filePath, "utf-8");
24838
+ raw = import_fs50.default.readFileSync(filePath, "utf-8");
24598
24839
  } catch {
24599
24840
  return { redactedLines: 0, patterns: [] };
24600
24841
  }
@@ -24626,14 +24867,14 @@ function processFile(filePath, dryRun) {
24626
24867
  }
24627
24868
  }
24628
24869
  if (!dryRun && redactedLines > 0) {
24629
- import_fs49.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
24870
+ import_fs50.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
24630
24871
  }
24631
24872
  return { redactedLines, patterns };
24632
24873
  }
24633
24874
  function processJsonFile(filePath, dryRun) {
24634
24875
  let raw;
24635
24876
  try {
24636
- raw = import_fs49.default.readFileSync(filePath, "utf-8");
24877
+ raw = import_fs50.default.readFileSync(filePath, "utf-8");
24637
24878
  } catch {
24638
24879
  return { redactedLines: 0, patterns: [] };
24639
24880
  }
@@ -24646,15 +24887,15 @@ function processJsonFile(filePath, dryRun) {
24646
24887
  const { value, modified, found } = redactJson(parsed);
24647
24888
  if (!modified) return { redactedLines: 0, patterns: [] };
24648
24889
  if (!dryRun) {
24649
- import_fs49.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
24890
+ import_fs50.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
24650
24891
  }
24651
24892
  return { redactedLines: 1, patterns: found };
24652
24893
  }
24653
24894
  function findJsonFiles(dir) {
24654
24895
  const results = [];
24655
- if (!import_fs49.default.existsSync(dir)) return results;
24656
- for (const entry of import_fs49.default.readdirSync(dir, { withFileTypes: true })) {
24657
- const full = import_path50.default.join(dir, entry.name);
24896
+ if (!import_fs50.default.existsSync(dir)) return results;
24897
+ for (const entry of import_fs50.default.readdirSync(dir, { withFileTypes: true })) {
24898
+ const full = import_path51.default.join(dir, entry.name);
24658
24899
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
24659
24900
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
24660
24901
  }
@@ -24663,9 +24904,9 @@ function findJsonFiles(dir) {
24663
24904
  function registerMaskCommand(program2) {
24664
24905
  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) => {
24665
24906
  const dryRun = !!options.dryRun;
24666
- const home = import_os44.default.homedir();
24667
- const claudeDir = import_path50.default.join(home, ".claude", "projects");
24668
- const geminiDir = import_path50.default.join(home, ".gemini", "tmp");
24907
+ const home = import_os45.default.homedir();
24908
+ const claudeDir = import_path51.default.join(home, ".claude", "projects");
24909
+ const geminiDir = import_path51.default.join(home, ".gemini", "tmp");
24669
24910
  const allFiles = [
24670
24911
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
24671
24912
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -24673,7 +24914,7 @@ function registerMaskCommand(program2) {
24673
24914
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
24674
24915
  const filtered = cutoff ? allFiles.filter((f) => {
24675
24916
  try {
24676
- return import_fs49.default.statSync(f.path).mtime >= cutoff;
24917
+ return import_fs50.default.statSync(f.path).mtime >= cutoff;
24677
24918
  } catch {
24678
24919
  return false;
24679
24920
  }
@@ -24729,20 +24970,20 @@ function registerMaskCommand(program2) {
24729
24970
  // src/cli.ts
24730
24971
  init_blast();
24731
24972
  var { version } = JSON.parse(
24732
- import_fs52.default.readFileSync(import_path53.default.join(__dirname, "../package.json"), "utf-8")
24973
+ import_fs53.default.readFileSync(import_path54.default.join(__dirname, "../package.json"), "utf-8")
24733
24974
  );
24734
24975
  var program = new import_commander.Command();
24735
24976
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
24736
24977
  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) => {
24737
24978
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
24738
- const credPath = import_path53.default.join(import_os47.default.homedir(), ".node9", "credentials.json");
24739
- if (!import_fs52.default.existsSync(import_path53.default.dirname(credPath)))
24740
- import_fs52.default.mkdirSync(import_path53.default.dirname(credPath), { recursive: true });
24979
+ const credPath = import_path54.default.join(import_os48.default.homedir(), ".node9", "credentials.json");
24980
+ if (!import_fs53.default.existsSync(import_path54.default.dirname(credPath)))
24981
+ import_fs53.default.mkdirSync(import_path54.default.dirname(credPath), { recursive: true });
24741
24982
  const profileName = options.profile || "default";
24742
24983
  let existingCreds = {};
24743
24984
  try {
24744
- if (import_fs52.default.existsSync(credPath)) {
24745
- const raw = JSON.parse(import_fs52.default.readFileSync(credPath, "utf-8"));
24985
+ if (import_fs53.default.existsSync(credPath)) {
24986
+ const raw = JSON.parse(import_fs53.default.readFileSync(credPath, "utf-8"));
24746
24987
  if (raw.apiKey) {
24747
24988
  existingCreds = {
24748
24989
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -24754,14 +24995,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24754
24995
  } catch {
24755
24996
  }
24756
24997
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
24757
- import_fs52.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
24998
+ import_fs53.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
24758
24999
  let effectiveCloud = null;
24759
25000
  if (profileName === "default") {
24760
- const configPath = import_path53.default.join(import_os47.default.homedir(), ".node9", "config.json");
25001
+ const configPath = import_path54.default.join(import_os48.default.homedir(), ".node9", "config.json");
24761
25002
  let config = {};
24762
25003
  try {
24763
- if (import_fs52.default.existsSync(configPath))
24764
- config = JSON.parse(import_fs52.default.readFileSync(configPath, "utf-8"));
25004
+ if (import_fs53.default.existsSync(configPath))
25005
+ config = JSON.parse(import_fs53.default.readFileSync(configPath, "utf-8"));
24765
25006
  } catch {
24766
25007
  }
24767
25008
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -24776,9 +25017,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24776
25017
  approvers.cloud = false;
24777
25018
  }
24778
25019
  s.approvers = approvers;
24779
- if (!import_fs52.default.existsSync(import_path53.default.dirname(configPath)))
24780
- import_fs52.default.mkdirSync(import_path53.default.dirname(configPath), { recursive: true });
24781
- import_fs52.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
25020
+ if (!import_fs53.default.existsSync(import_path54.default.dirname(configPath)))
25021
+ import_fs53.default.mkdirSync(import_path54.default.dirname(configPath), { recursive: true });
25022
+ import_fs53.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
24782
25023
  effectiveCloud = approvers.cloud === true;
24783
25024
  }
24784
25025
  if (options.profile && profileName !== "default") {
@@ -24937,15 +25178,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
24937
25178
  }
24938
25179
  }
24939
25180
  if (options.purge) {
24940
- const node9Dir = import_path53.default.join(import_os47.default.homedir(), ".node9");
24941
- if (import_fs52.default.existsSync(node9Dir)) {
25181
+ const node9Dir = import_path54.default.join(import_os48.default.homedir(), ".node9");
25182
+ if (import_fs53.default.existsSync(node9Dir)) {
24942
25183
  const confirmed = await (0, import_prompts2.confirm)({
24943
25184
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
24944
25185
  default: false
24945
25186
  });
24946
25187
  if (confirmed) {
24947
- import_fs52.default.rmSync(node9Dir, { recursive: true });
24948
- if (import_fs52.default.existsSync(node9Dir)) {
25188
+ import_fs53.default.rmSync(node9Dir, { recursive: true });
25189
+ if (import_fs53.default.existsSync(node9Dir)) {
24949
25190
  console.error(
24950
25191
  import_chalk30.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
24951
25192
  );
@@ -25060,7 +25301,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
25060
25301
  });
25061
25302
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
25062
25303
  try {
25063
- const dashboardPath = import_path53.default.join(__dirname, "dashboard.mjs");
25304
+ const dashboardPath = import_path54.default.join(__dirname, "dashboard.mjs");
25064
25305
  const dynamicImport = new Function("id", "return import(id)");
25065
25306
  const mod = await dynamicImport(`file://${dashboardPath}`);
25066
25307
  await mod.startMonitor();
@@ -25098,14 +25339,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
25098
25339
  Run "node9 addto claude" to register it as the statusLine.`
25099
25340
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
25100
25341
  if (subcommand === "debug") {
25101
- const flagFile = import_path53.default.join(import_os47.default.homedir(), ".node9", "hud-debug");
25342
+ const flagFile = import_path54.default.join(import_os48.default.homedir(), ".node9", "hud-debug");
25102
25343
  if (state === "on") {
25103
- import_fs52.default.mkdirSync(import_path53.default.dirname(flagFile), { recursive: true });
25104
- import_fs52.default.writeFileSync(flagFile, "");
25344
+ import_fs53.default.mkdirSync(import_path54.default.dirname(flagFile), { recursive: true });
25345
+ import_fs53.default.writeFileSync(flagFile, "");
25105
25346
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
25106
25347
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
25107
25348
  } else if (state === "off") {
25108
- if (import_fs52.default.existsSync(flagFile)) import_fs52.default.unlinkSync(flagFile);
25349
+ if (import_fs53.default.existsSync(flagFile)) import_fs53.default.unlinkSync(flagFile);
25109
25350
  console.log("HUD debug logging disabled.");
25110
25351
  } else {
25111
25352
  console.error("Usage: node9 hud debug on|off");
@@ -25222,9 +25463,9 @@ if (process.argv[2] !== "daemon") {
25222
25463
  const isCheckHook = process.argv[2] === "check";
25223
25464
  if (isCheckHook) {
25224
25465
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
25225
- const logPath = import_path53.default.join(import_os47.default.homedir(), ".node9", "hook-debug.log");
25466
+ const logPath = import_path54.default.join(import_os48.default.homedir(), ".node9", "hook-debug.log");
25226
25467
  const msg = reason instanceof Error ? reason.message : String(reason);
25227
- import_fs52.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
25468
+ import_fs53.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
25228
25469
  `);
25229
25470
  }
25230
25471
  process.exit(0);