@node9/proxy 1.36.0 → 1.37.0

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 +1662 -530
  2. package/dist/cli.mjs +1662 -530
  3. package/package.json +3 -2
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 path55 = issue.path.length > 0 ? issue.path.join(".") : "root";
210
- return ` \u2022 ${path55}: ${issue.message}`;
209
+ const path58 = issue.path.length > 0 ? issue.path.join(".") : "root";
210
+ return ` \u2022 ${path58}: ${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, path55) {
1259
+ function getNestedValue(obj, path58) {
1260
1260
  if (!obj || typeof obj !== "object") return null;
1261
- const segments = path55.split(".");
1261
+ const segments = path58.split(".");
1262
1262
  for (const seg of segments) {
1263
1263
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1264
1264
  }
@@ -1771,8 +1771,8 @@ function narrativeRuleLabel(name) {
1771
1771
  "eval-dynamic": "dynamic eval",
1772
1772
  "config-set": "Redis CONFIG SET"
1773
1773
  };
1774
- for (const [key, label] of Object.entries(map)) {
1775
- if (stripped.includes(key)) return label;
1774
+ for (const [key, label2] of Object.entries(map)) {
1775
+ if (stripped.includes(key)) return label2;
1776
1776
  }
1777
1777
  return stripped;
1778
1778
  }
@@ -1784,6 +1784,17 @@ function stripRulePrefixes(name) {
1784
1784
  n = n.replace(/^(block|review|allow)-/, "");
1785
1785
  return n;
1786
1786
  }
1787
+ function computeSecurityScore(opts) {
1788
+ const { critical, high, medium, total } = opts;
1789
+ if (total === 0) return { score: 100, tier: "good" };
1790
+ const criticalRate = critical / total;
1791
+ const highRate = high / total;
1792
+ const mediumRate = medium / total;
1793
+ const deduction = Math.min(criticalRate * 3e3, 60) + Math.min(highRate * 500, 30) + Math.min(mediumRate * 100, 15);
1794
+ const score = Math.max(0, Math.min(100, Math.round(100 - deduction)));
1795
+ const tier = score >= 80 ? "good" : score >= 50 ? "at-risk" : "critical";
1796
+ return { score, tier };
1797
+ }
1787
1798
  function truncateBlastPath(full) {
1788
1799
  if (!full) return "";
1789
1800
  const cleaned = full.replace(/[/\\]+$/, "");
@@ -4941,12 +4952,12 @@ async function explainPolicy(toolName, args) {
4941
4952
  (rule) => matchesPattern(toolName, rule.tool) && evaluateSmartConditions(args, rule)
4942
4953
  );
4943
4954
  if (matchedRule) {
4944
- const label = `Smart Rule: ${matchedRule.name ?? matchedRule.tool}`;
4955
+ const label2 = `Smart Rule: ${matchedRule.name ?? matchedRule.tool}`;
4945
4956
  if (matchedRule.verdict === "allow") {
4946
4957
  steps.push({
4947
4958
  name: "Smart rules",
4948
4959
  outcome: "allow",
4949
- detail: `${label} \u2192 allow`,
4960
+ detail: `${label2} \u2192 allow`,
4950
4961
  isFinal: true
4951
4962
  });
4952
4963
  return { tool: toolName, args, waterfall, steps, decision: "allow" };
@@ -4954,7 +4965,7 @@ async function explainPolicy(toolName, args) {
4954
4965
  steps.push({
4955
4966
  name: "Smart rules",
4956
4967
  outcome: matchedRule.verdict,
4957
- detail: `${label} \u2192 ${matchedRule.verdict}${matchedRule.reason ? `: ${matchedRule.reason}` : ""}`,
4968
+ detail: `${label2} \u2192 ${matchedRule.verdict}${matchedRule.reason ? `: ${matchedRule.reason}` : ""}`,
4958
4969
  isFinal: true
4959
4970
  });
4960
4971
  return {
@@ -4963,7 +4974,7 @@ async function explainPolicy(toolName, args) {
4963
4974
  waterfall,
4964
4975
  steps,
4965
4976
  decision: matchedRule.verdict,
4966
- blockedByLabel: label
4977
+ blockedByLabel: label2
4967
4978
  };
4968
4979
  }
4969
4980
  steps.push({
@@ -5015,7 +5026,7 @@ async function explainPolicy(toolName, args) {
5015
5026
  });
5016
5027
  const evalVerdict = detectDangerousShellExec(shellCommand);
5017
5028
  if (evalVerdict) {
5018
- const label = evalVerdict === "block" ? "Node9: Eval Remote Execution" : "Node9: Eval Dynamic Content";
5029
+ const label2 = evalVerdict === "block" ? "Node9: Eval Remote Execution" : "Node9: Eval Dynamic Content";
5019
5030
  const detail = evalVerdict === "block" ? "eval of remote download (curl/wget) \u2014 near-certain supply-chain attack" : "eval of dynamic content (variable or subshell expansion) \u2014 requires approval";
5020
5031
  steps.push({ name: "AST eval detection", outcome: evalVerdict, detail, isFinal: true });
5021
5032
  return {
@@ -5024,7 +5035,7 @@ async function explainPolicy(toolName, args) {
5024
5035
  waterfall,
5025
5036
  steps,
5026
5037
  decision: evalVerdict,
5027
- blockedByLabel: label
5038
+ blockedByLabel: label2
5028
5039
  };
5029
5040
  }
5030
5041
  steps.push({
@@ -6961,10 +6972,10 @@ function checkPin(serverKey, currentHash, cwd) {
6961
6972
  if (!homeEntry) return "new";
6962
6973
  return homeEntry.toolsHash === currentHash ? "match" : "mismatch";
6963
6974
  }
6964
- function updatePin(serverKey, label, toolsHash, toolNames) {
6975
+ function updatePin(serverKey, label2, toolsHash, toolNames) {
6965
6976
  const pins = readMcpPins();
6966
6977
  pins.servers[serverKey] = {
6967
- label,
6978
+ label: label2,
6968
6979
  toolsHash,
6969
6980
  toolNames,
6970
6981
  toolCount: toolNames.length,
@@ -8293,9 +8304,9 @@ function writeToml(filePath, data) {
8293
8304
  async function setupCodex() {
8294
8305
  seedMcpPinsIfMissing();
8295
8306
  const homeDir2 = import_os12.default.homedir();
8296
- const configPath = import_path15.default.join(homeDir2, ".codex", "config.toml");
8307
+ const configPath2 = import_path15.default.join(homeDir2, ".codex", "config.toml");
8297
8308
  const hooksPath = import_path15.default.join(homeDir2, ".codex", "hooks.json");
8298
- const config = readToml(configPath) ?? {};
8309
+ const config = readToml(configPath2) ?? {};
8299
8310
  const servers = config.mcp_servers ?? {};
8300
8311
  let anythingChanged = false;
8301
8312
  const hooksFile = readJson(hooksPath) ?? {};
@@ -8370,7 +8381,7 @@ async function setupCodex() {
8370
8381
  if (!hasNode9McpServer(servers)) {
8371
8382
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
8372
8383
  config.mcp_servers = servers;
8373
- writeToml(configPath, config);
8384
+ writeToml(configPath2, config);
8374
8385
  console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
8375
8386
  anythingChanged = true;
8376
8387
  }
@@ -8382,7 +8393,7 @@ async function setupCodex() {
8382
8393
  }
8383
8394
  if (serversToWrap.length > 0) {
8384
8395
  console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
8385
- console.log(import_chalk.default.white(` ${configPath}`));
8396
+ console.log(import_chalk.default.white(` ${configPath2}`));
8386
8397
  for (const { name, upstream } of serversToWrap) {
8387
8398
  console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
8388
8399
  }
@@ -8397,7 +8408,7 @@ async function setupCodex() {
8397
8408
  };
8398
8409
  }
8399
8410
  config.mcp_servers = servers;
8400
- writeToml(configPath, config);
8411
+ writeToml(configPath2, config);
8401
8412
  console.log(import_chalk.default.green(`
8402
8413
  \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
8403
8414
  anythingChanged = true;
@@ -8445,7 +8456,7 @@ async function setupCodex() {
8445
8456
  }
8446
8457
  function teardownCodex() {
8447
8458
  const homeDir2 = import_os12.default.homedir();
8448
- const configPath = import_path15.default.join(homeDir2, ".codex", "config.toml");
8459
+ const configPath2 = import_path15.default.join(homeDir2, ".codex", "config.toml");
8449
8460
  const hooksPath = import_path15.default.join(homeDir2, ".codex", "hooks.json");
8450
8461
  const hooksFile = readJson(hooksPath);
8451
8462
  if (hooksFile?.hooks) {
@@ -8463,7 +8474,7 @@ function teardownCodex() {
8463
8474
  console.log(import_chalk.default.green(" \u2705 Removed Node9 hooks from ~/.codex/hooks.json"));
8464
8475
  }
8465
8476
  }
8466
- const config = readToml(configPath);
8477
+ const config = readToml(configPath2);
8467
8478
  if (!config?.mcp_servers) {
8468
8479
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.codex/config.toml not found \u2014 nothing to remove"));
8469
8480
  return;
@@ -8486,7 +8497,7 @@ function teardownCodex() {
8486
8497
  }
8487
8498
  }
8488
8499
  if (changed) {
8489
- writeToml(configPath, config);
8500
+ writeToml(configPath2, config);
8490
8501
  console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in ~/.codex/config.toml"));
8491
8502
  } else {
8492
8503
  console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in ~/.codex/config.toml"));
@@ -8741,18 +8752,18 @@ function teardownVSCode() {
8741
8752
  }
8742
8753
  async function setupClaudeDesktop() {
8743
8754
  seedMcpPinsIfMissing();
8744
- const configPath = claudeDesktopConfigPath();
8745
- if (!configPath) {
8755
+ const configPath2 = claudeDesktopConfigPath();
8756
+ if (!configPath2) {
8746
8757
  console.log(import_chalk.default.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
8747
8758
  return;
8748
8759
  }
8749
- const config = readJson(configPath) ?? {};
8760
+ const config = readJson(configPath2) ?? {};
8750
8761
  const servers = config.mcpServers ?? {};
8751
8762
  let anythingChanged = false;
8752
8763
  if (!hasNode9McpServer(servers)) {
8753
8764
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
8754
8765
  config.mcpServers = servers;
8755
- writeJson(configPath, config);
8766
+ writeJson(configPath2, config);
8756
8767
  console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
8757
8768
  anythingChanged = true;
8758
8769
  }
@@ -8763,7 +8774,7 @@ async function setupClaudeDesktop() {
8763
8774
  }
8764
8775
  if (serversToWrap.length > 0) {
8765
8776
  console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
8766
- console.log(import_chalk.default.white(` ${configPath}`));
8777
+ console.log(import_chalk.default.white(` ${configPath2}`));
8767
8778
  for (const { name, upstream } of serversToWrap) {
8768
8779
  console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
8769
8780
  }
@@ -8778,7 +8789,7 @@ async function setupClaudeDesktop() {
8778
8789
  };
8779
8790
  }
8780
8791
  config.mcpServers = servers;
8781
- writeJson(configPath, config);
8792
+ writeJson(configPath2, config);
8782
8793
  console.log(import_chalk.default.green(`
8783
8794
  \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
8784
8795
  anythingChanged = true;
@@ -8805,12 +8816,12 @@ async function setupClaudeDesktop() {
8805
8816
  }
8806
8817
  }
8807
8818
  function teardownClaudeDesktop() {
8808
- const configPath = claudeDesktopConfigPath();
8809
- if (!configPath) {
8819
+ const configPath2 = claudeDesktopConfigPath();
8820
+ if (!configPath2) {
8810
8821
  console.log(import_chalk.default.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
8811
8822
  return;
8812
8823
  }
8813
- const config = readJson(configPath);
8824
+ const config = readJson(configPath2);
8814
8825
  if (!config?.mcpServers) {
8815
8826
  console.log(import_chalk.default.blue(" \u2139\uFE0F Claude Desktop config not found \u2014 nothing to remove"));
8816
8827
  return;
@@ -8818,7 +8829,7 @@ function teardownClaudeDesktop() {
8818
8829
  let changed = false;
8819
8830
  if (removeNode9McpServer(config.mcpServers)) {
8820
8831
  changed = true;
8821
- console.log(import_chalk.default.green(` \u2705 Removed node9 MCP server entry from ${configPath}`));
8832
+ console.log(import_chalk.default.green(` \u2705 Removed node9 MCP server entry from ${configPath2}`));
8822
8833
  }
8823
8834
  for (const [name, server] of Object.entries(config.mcpServers)) {
8824
8835
  const args = server.args;
@@ -8833,7 +8844,7 @@ function teardownClaudeDesktop() {
8833
8844
  }
8834
8845
  }
8835
8846
  if (changed) {
8836
- writeJson(configPath, config);
8847
+ writeJson(configPath2, config);
8837
8848
  console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in Claude Desktop config"));
8838
8849
  } else {
8839
8850
  console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in Claude Desktop config"));
@@ -8861,7 +8872,7 @@ async function setupOpencode() {
8861
8872
  const homeDir2 = import_os12.default.homedir();
8862
8873
  const configDir = import_path15.default.join(homeDir2, ".config", "opencode");
8863
8874
  const pluginsDir = import_path15.default.join(configDir, "plugins");
8864
- const configPath = import_path15.default.join(configDir, "opencode.json");
8875
+ const configPath2 = import_path15.default.join(configDir, "opencode.json");
8865
8876
  const pluginPath = import_path15.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
8866
8877
  try {
8867
8878
  import_fs13.default.mkdirSync(pluginsDir, { recursive: true });
@@ -8895,7 +8906,7 @@ async function setupOpencode() {
8895
8906
  );
8896
8907
  }
8897
8908
  }
8898
- const config = readJson(configPath) ?? {};
8909
+ const config = readJson(configPath2) ?? {};
8899
8910
  const mcp = config.mcp ?? {};
8900
8911
  let configChanged = false;
8901
8912
  const desiredCommand = [...node9ArgvForShim(), "mcp-server"];
@@ -8916,7 +8927,7 @@ async function setupOpencode() {
8916
8927
  console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
8917
8928
  }
8918
8929
  }
8919
- if (configChanged) writeJson(configPath, config);
8930
+ if (configChanged) writeJson(configPath2, config);
8920
8931
  if (pluginChanged || configChanged) {
8921
8932
  console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Opencode!"));
8922
8933
  console.log(import_chalk.default.gray(" Restart Opencode for changes to take effect."));
@@ -8929,7 +8940,7 @@ function teardownOpencode() {
8929
8940
  const homeDir2 = import_os12.default.homedir();
8930
8941
  const configDir = import_path15.default.join(homeDir2, ".config", "opencode");
8931
8942
  const pluginsDir = import_path15.default.join(configDir, "plugins");
8932
- const configPath = import_path15.default.join(configDir, "opencode.json");
8943
+ const configPath2 = import_path15.default.join(configDir, "opencode.json");
8933
8944
  const pluginPath = import_path15.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
8934
8945
  try {
8935
8946
  if (import_fs13.default.existsSync(pluginPath)) {
@@ -8939,7 +8950,7 @@ function teardownOpencode() {
8939
8950
  } catch (err2) {
8940
8951
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Could not remove ${pluginPath}: ${String(err2)}`));
8941
8952
  }
8942
- const config = readJson(configPath);
8953
+ const config = readJson(configPath2);
8943
8954
  if (!config) {
8944
8955
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.config/opencode/opencode.json not found \u2014 nothing to remove"));
8945
8956
  return;
@@ -8955,7 +8966,7 @@ function teardownOpencode() {
8955
8966
  }
8956
8967
  if (changed) {
8957
8968
  config.mcp = mcp;
8958
- writeJson(configPath, config);
8969
+ writeJson(configPath2, config);
8959
8970
  } else {
8960
8971
  console.log(import_chalk.default.blue(" \u2139\uFE0F No node9 entries found in ~/.config/opencode/opencode.json"));
8961
8972
  }
@@ -9026,15 +9037,15 @@ function hermesAllowlistPath(homeDir2 = import_os12.default.homedir()) {
9026
9037
  }
9027
9038
  function setupHermes() {
9028
9039
  const homeDir2 = import_os12.default.homedir();
9029
- const configPath = hermesConfigPath(homeDir2);
9040
+ const configPath2 = hermesConfigPath(homeDir2);
9030
9041
  const allowlistPath = hermesAllowlistPath(homeDir2);
9031
- if (!import_fs13.default.existsSync(configPath)) {
9032
- console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config not found at ${configPath}`));
9033
- console.log(import_chalk.default.gray(" Run `hermes setup` first, then re-run node9 setup hermes."));
9042
+ if (!import_fs13.default.existsSync(configPath2)) {
9043
+ console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config not found at ${configPath2}`));
9044
+ console.log(import_chalk.default.gray(" Run `hermes setup` first, then re-run node9 agents add hermes."));
9034
9045
  return;
9035
9046
  }
9036
9047
  let anythingChanged = false;
9037
- const raw = import_fs13.default.readFileSync(configPath, "utf-8");
9048
+ const raw = import_fs13.default.readFileSync(configPath2, "utf-8");
9038
9049
  const doc = yaml.parseDocument(raw);
9039
9050
  if (doc.errors.length > 0) {
9040
9051
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config.yaml has YAML parse errors:`));
@@ -9042,7 +9053,9 @@ function setupHermes() {
9042
9053
  console.log(import_chalk.default.gray(` \u2022 ${err2.message}`));
9043
9054
  }
9044
9055
  console.log(
9045
- import_chalk.default.gray(" Fix the file (or run `hermes config edit`), then re-run node9 setup hermes.")
9056
+ import_chalk.default.gray(
9057
+ " Fix the file (or run `hermes config edit`), then re-run node9 agents add hermes."
9058
+ )
9046
9059
  );
9047
9060
  return;
9048
9061
  }
@@ -9072,7 +9085,7 @@ function setupHermes() {
9072
9085
  anythingChanged = true;
9073
9086
  }
9074
9087
  if (anythingChanged) {
9075
- import_fs13.default.writeFileSync(configPath, doc.toString());
9088
+ import_fs13.default.writeFileSync(configPath2, doc.toString());
9076
9089
  }
9077
9090
  let allowlist = {};
9078
9091
  if (import_fs13.default.existsSync(allowlistPath)) {
@@ -9115,24 +9128,24 @@ function setupHermes() {
9115
9128
  }
9116
9129
  function teardownHermes() {
9117
9130
  const homeDir2 = import_os12.default.homedir();
9118
- const configPath = hermesConfigPath(homeDir2);
9131
+ const configPath2 = hermesConfigPath(homeDir2);
9119
9132
  const allowlistPath = hermesAllowlistPath(homeDir2);
9120
- if (!import_fs13.default.existsSync(configPath)) {
9121
- console.log(import_chalk.default.blue(` \u2139\uFE0F ${configPath} not found \u2014 nothing to remove`));
9133
+ if (!import_fs13.default.existsSync(configPath2)) {
9134
+ console.log(import_chalk.default.blue(` \u2139\uFE0F ${configPath2} not found \u2014 nothing to remove`));
9122
9135
  return;
9123
9136
  }
9124
- const raw = import_fs13.default.readFileSync(configPath, "utf-8");
9137
+ const raw = import_fs13.default.readFileSync(configPath2, "utf-8");
9125
9138
  const doc = yaml.parseDocument(raw);
9126
9139
  if (doc.errors.length > 0) {
9127
9140
  console.log(
9128
- import_chalk.default.yellow(` \u26A0\uFE0F Skipping ${configPath} \u2014 file has YAML parse errors, fix it manually.`)
9141
+ import_chalk.default.yellow(` \u26A0\uFE0F Skipping ${configPath2} \u2014 file has YAML parse errors, fix it manually.`)
9129
9142
  );
9130
9143
  } else {
9131
- teardownHermesConfigDoc(doc, configPath);
9144
+ teardownHermesConfigDoc(doc, configPath2);
9132
9145
  }
9133
9146
  teardownHermesAllowlist(allowlistPath);
9134
9147
  }
9135
- function teardownHermesConfigDoc(doc, configPath) {
9148
+ function teardownHermesConfigDoc(doc, configPath2) {
9136
9149
  let anythingChanged = false;
9137
9150
  const current = doc.toJS() ?? {};
9138
9151
  for (const { event } of HERMES_HOOK_PLAN) {
@@ -9154,10 +9167,10 @@ function teardownHermesConfigDoc(doc, configPath) {
9154
9167
  anythingChanged = true;
9155
9168
  }
9156
9169
  if (anythingChanged) {
9157
- import_fs13.default.writeFileSync(configPath, doc.toString());
9158
- console.log(import_chalk.default.green(` \u2705 Removed Node9 hooks from ${configPath}`));
9170
+ import_fs13.default.writeFileSync(configPath2, doc.toString());
9171
+ console.log(import_chalk.default.green(` \u2705 Removed Node9 hooks from ${configPath2}`));
9159
9172
  } else {
9160
- console.log(import_chalk.default.blue(` \u2139\uFE0F No Node9 hooks found in ${configPath}`));
9173
+ console.log(import_chalk.default.blue(` \u2139\uFE0F No Node9 hooks found in ${configPath2}`));
9161
9174
  }
9162
9175
  }
9163
9176
  function teardownHermesAllowlist(allowlistPath) {
@@ -9999,12 +10012,12 @@ function buildScanSummary(agents) {
9999
10012
  }
10000
10013
  function buildSections(findings) {
10001
10014
  const sectionMap = /* @__PURE__ */ new Map();
10002
- function ensureSection(id, label, subtitle, sourceType, shieldKey) {
10015
+ function ensureSection(id, label2, subtitle, sourceType, shieldKey) {
10003
10016
  let s = sectionMap.get(id);
10004
10017
  if (!s) {
10005
10018
  s = {
10006
10019
  id,
10007
- label,
10020
+ label: label2,
10008
10021
  subtitle,
10009
10022
  sourceType,
10010
10023
  shieldKey,
@@ -13097,9 +13110,9 @@ function printRuleGroup(rule, topN, drillDown, previewWidth) {
13097
13110
  }
13098
13111
  }
13099
13112
  function compactRuleLabel(name) {
13100
- let label = name.replace(/^shield:[^:]+:/, "");
13101
- label = label.replace(/^(block|review|allow)-/, "");
13102
- return label.replace(/-+/g, "-");
13113
+ let label2 = name.replace(/^shield:[^:]+:/, "");
13114
+ label2 = label2.replace(/^(block|review|allow)-/, "");
13115
+ return label2.replace(/-+/g, "-");
13103
13116
  }
13104
13117
  function renderCompactScorecard(input) {
13105
13118
  const { scan, summary, blast, blastExposures, blockedCount, reviewCount } = input;
@@ -13200,9 +13213,9 @@ function renderNarrativeScorecard(input) {
13200
13213
  for (const section of summary.sections) {
13201
13214
  for (const rule of section.rules) {
13202
13215
  const sev = classifyRuleSeverity2(rule.name, rule.verdict);
13203
- const label = narrativeRuleLabel2(rule.name);
13216
+ const label2 = narrativeRuleLabel2(rule.name);
13204
13217
  const count = rule.findings.length;
13205
- const display = count > 1 ? `${label} \xD7${count}` : label;
13218
+ const display = count > 1 ? `${label2} \xD7${count}` : label2;
13206
13219
  const entry = { label: display, count };
13207
13220
  if (sev === "critical") critical.push(entry);
13208
13221
  else if (sev === "high") high.push(entry);
@@ -14031,7 +14044,7 @@ function registerScanCommand(program2) {
14031
14044
  console.log(import_chalk5.default.bold(" Enable real-time protection:"));
14032
14045
  console.log("");
14033
14046
  console.log(
14034
- " " + import_chalk5.default.cyan("npm install -g @node9/proxy") + import_chalk5.default.dim(" && ") + import_chalk5.default.cyan("node9 init --recommended")
14047
+ " " + import_chalk5.default.cyan("npm install -g node9-ai") + import_chalk5.default.dim(" && ") + import_chalk5.default.cyan("node9 init --recommended")
14035
14048
  );
14036
14049
  console.log("");
14037
14050
  console.log(
@@ -14337,8 +14350,8 @@ var init_session_counters = __esm({
14337
14350
  if (!isFinite(amount) || amount < 0) return;
14338
14351
  this._estimatedCost += amount;
14339
14352
  }
14340
- recordRuleHit(label) {
14341
- this._lastRuleHit = label;
14353
+ recordRuleHit(label2) {
14354
+ this._lastRuleHit = label2;
14342
14355
  }
14343
14356
  recordBlockedTool(toolName) {
14344
14357
  this._lastBlockedTool = toolName;
@@ -14612,10 +14625,10 @@ function broadcast(event, data) {
14612
14625
  activityRing.push({ event, data });
14613
14626
  if (activityRing.length > ACTIVITY_RING_SIZE) activityRing.shift();
14614
14627
  } else if (event === "activity-result") {
14615
- const { id, status, label, costEstimate } = data;
14628
+ const { id, status, label: label2, costEstimate } = data;
14616
14629
  for (let i = activityRing.length - 1; i >= 0; i--) {
14617
14630
  if (activityRing[i].data.id === id) {
14618
- Object.assign(activityRing[i].data, { status, label, costEstimate });
14631
+ Object.assign(activityRing[i].data, { status, label: label2, costEstimate });
14619
14632
  break;
14620
14633
  }
14621
14634
  }
@@ -16951,20 +16964,20 @@ function getModelContextLimit(model) {
16951
16964
  return 2e5;
16952
16965
  }
16953
16966
  function readSessionUsage() {
16954
- const projectsDir = import_path52.default.join(import_os46.default.homedir(), ".claude", "projects");
16955
- if (!import_fs51.default.existsSync(projectsDir)) return null;
16967
+ const projectsDir = import_path55.default.join(import_os51.default.homedir(), ".claude", "projects");
16968
+ if (!import_fs56.default.existsSync(projectsDir)) return null;
16956
16969
  let latestFile = null;
16957
16970
  let latestMtime = 0;
16958
16971
  try {
16959
- for (const dir of import_fs51.default.readdirSync(projectsDir)) {
16960
- const dirPath = import_path52.default.join(projectsDir, dir);
16972
+ for (const dir of import_fs56.default.readdirSync(projectsDir)) {
16973
+ const dirPath = import_path55.default.join(projectsDir, dir);
16961
16974
  try {
16962
- if (!import_fs51.default.statSync(dirPath).isDirectory()) continue;
16963
- for (const file of import_fs51.default.readdirSync(dirPath)) {
16975
+ if (!import_fs56.default.statSync(dirPath).isDirectory()) continue;
16976
+ for (const file of import_fs56.default.readdirSync(dirPath)) {
16964
16977
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
16965
- const filePath = import_path52.default.join(dirPath, file);
16978
+ const filePath = import_path55.default.join(dirPath, file);
16966
16979
  try {
16967
- const mtime = import_fs51.default.statSync(filePath).mtimeMs;
16980
+ const mtime = import_fs56.default.statSync(filePath).mtimeMs;
16968
16981
  if (mtime > latestMtime) {
16969
16982
  latestMtime = mtime;
16970
16983
  latestFile = filePath;
@@ -16979,7 +16992,7 @@ function readSessionUsage() {
16979
16992
  }
16980
16993
  if (!latestFile) return null;
16981
16994
  try {
16982
- const lines = import_fs51.default.readFileSync(latestFile, "utf-8").split("\n");
16995
+ const lines = import_fs56.default.readFileSync(latestFile, "utf-8").split("\n");
16983
16996
  let lastModel = "";
16984
16997
  let lastInput = 0;
16985
16998
  let lastOutput = 0;
@@ -17004,10 +17017,10 @@ function readSessionUsage() {
17004
17017
  }
17005
17018
  }
17006
17019
  function formatContextStat(stat) {
17007
- const pctColor = stat.fillPct >= 80 ? import_chalk29.default.red : stat.fillPct >= 50 ? import_chalk29.default.yellow : import_chalk29.default.cyan;
17020
+ const pctColor = stat.fillPct >= 80 ? import_chalk32.default.red : stat.fillPct >= 50 ? import_chalk32.default.yellow : import_chalk32.default.cyan;
17008
17021
  const k = (n) => `${Math.round(n / 1e3)}k`;
17009
17022
  const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
17010
- return import_chalk29.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk29.default.dim(
17023
+ return import_chalk32.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk32.default.dim(
17011
17024
  ` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
17012
17025
  );
17013
17026
  }
@@ -17030,32 +17043,32 @@ function agentLabel(agent, mcpServer, sessionId) {
17030
17043
  const tag = sessionTag(sessionId);
17031
17044
  const tagSuffix = tag ? `\xB7${tag}` : "";
17032
17045
  if (!agent || agent === "Terminal") {
17033
- return mcpServer ? import_chalk29.default.dim(`[\u2192 ${mcpServer}] `) : "";
17046
+ return mcpServer ? import_chalk32.default.dim(`[\u2192 ${mcpServer}] `) : "";
17034
17047
  }
17035
17048
  const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
17036
- if (!short) return mcpServer ? import_chalk29.default.dim(`[\u2192 ${mcpServer}] `) : "";
17037
- return mcpServer ? import_chalk29.default.dim(`[${short}${tagSuffix} \u2192 ${mcpServer}] `) : import_chalk29.default.dim(`[${short}${tagSuffix}] `);
17049
+ if (!short) return mcpServer ? import_chalk32.default.dim(`[\u2192 ${mcpServer}] `) : "";
17050
+ return mcpServer ? import_chalk32.default.dim(`[${short}${tagSuffix} \u2192 ${mcpServer}] `) : import_chalk32.default.dim(`[${short}${tagSuffix}] `);
17038
17051
  }
17039
17052
  function formatBase(activity) {
17040
17053
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
17041
17054
  const icon = getIcon(activity.tool);
17042
17055
  const toolName = activity.tool.slice(0, 16).padEnd(16);
17043
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os46.default.homedir(), "~");
17056
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os51.default.homedir(), "~");
17044
17057
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
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)}`;
17058
+ return `${import_chalk32.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk32.default.white.bold(toolName)} ${import_chalk32.default.dim(argsPreview)}`;
17046
17059
  }
17047
17060
  function renderResult(activity, result) {
17048
17061
  const base = formatBase(activity);
17049
17062
  let status;
17050
17063
  if (result.status === "allow") {
17051
- status = import_chalk29.default.green("\u2713 ALLOW");
17064
+ status = import_chalk32.default.green("\u2713 ALLOW");
17052
17065
  } else if (result.status === "dlp") {
17053
- status = import_chalk29.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
17066
+ status = import_chalk32.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
17054
17067
  } else {
17055
- status = import_chalk29.default.red("\u2717 BLOCK");
17068
+ status = import_chalk32.default.red("\u2717 BLOCK");
17056
17069
  }
17057
17070
  const cost = result.costEstimate ?? activity.costEstimate;
17058
- const costSuffix = cost == null ? "" : import_chalk29.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
17071
+ const costSuffix = cost == null ? "" : import_chalk32.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
17059
17072
  if (process.stdout.isTTY) {
17060
17073
  if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
17061
17074
  import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
@@ -17072,19 +17085,19 @@ function renderResult(activity, result) {
17072
17085
  }
17073
17086
  function renderPending(activity) {
17074
17087
  if (!process.stdout.isTTY) return;
17075
- const line = `${formatBase(activity)} ${import_chalk29.default.yellow("\u25CF \u2026")}`;
17088
+ const line = `${formatBase(activity)} ${import_chalk32.default.yellow("\u25CF \u2026")}`;
17076
17089
  pendingShownForId = activity.id;
17077
17090
  pendingWrappedLines = wrappedLineCount(line);
17078
17091
  process.stdout.write(`${line}\r`);
17079
17092
  }
17080
17093
  async function ensureDaemon() {
17081
17094
  let pidPort = null;
17082
- if (import_fs51.default.existsSync(PID_FILE)) {
17095
+ if (import_fs56.default.existsSync(PID_FILE)) {
17083
17096
  try {
17084
- const { port } = JSON.parse(import_fs51.default.readFileSync(PID_FILE, "utf-8"));
17097
+ const { port } = JSON.parse(import_fs56.default.readFileSync(PID_FILE, "utf-8"));
17085
17098
  pidPort = port;
17086
17099
  } catch {
17087
- console.error(import_chalk29.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
17100
+ console.error(import_chalk32.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
17088
17101
  }
17089
17102
  }
17090
17103
  const checkPort = pidPort ?? DAEMON_PORT;
@@ -17095,7 +17108,7 @@ async function ensureDaemon() {
17095
17108
  if (res.ok) return checkPort;
17096
17109
  } catch {
17097
17110
  }
17098
- console.log(import_chalk29.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
17111
+ console.log(import_chalk32.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
17099
17112
  const child = (0, import_child_process12.spawn)(process.execPath, [process.argv[1], "daemon"], {
17100
17113
  detached: true,
17101
17114
  stdio: "ignore",
@@ -17112,7 +17125,7 @@ async function ensureDaemon() {
17112
17125
  } catch {
17113
17126
  }
17114
17127
  }
17115
- console.error(import_chalk29.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
17128
+ console.error(import_chalk32.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
17116
17129
  process.exit(1);
17117
17130
  }
17118
17131
  function postDecisionHttp(id, decision, authToken, port, opts) {
@@ -17122,7 +17135,7 @@ function postDecisionHttp(id, decision, authToken, port, opts) {
17122
17135
  if (opts?.trustDuration) bodyObj.trustDuration = opts.trustDuration;
17123
17136
  if (opts?.reason) bodyObj.reason = opts.reason;
17124
17137
  const body = JSON.stringify(bodyObj);
17125
- const req = import_http2.default.request(
17138
+ const req = import_http3.default.request(
17126
17139
  {
17127
17140
  hostname: "127.0.0.1",
17128
17141
  port,
@@ -17181,7 +17194,7 @@ function buildCardLines(req, localCount = 0) {
17181
17194
  const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
17182
17195
  const rawDesc = req.riskMetadata?.ruleDescription ?? "";
17183
17196
  const description = rawDesc ? cleanReason(rawDesc) : "";
17184
- const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk29.default.dim(`(${req.agent})`)}` : "";
17197
+ const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk32.default.dim(`(${req.agent})`)}` : "";
17185
17198
  const lines = [
17186
17199
  ``,
17187
17200
  `${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
@@ -17237,9 +17250,9 @@ function buildRecoveryCardLines(req) {
17237
17250
  ];
17238
17251
  }
17239
17252
  function readApproversFromDisk() {
17240
- const configPath = import_path52.default.join(import_os46.default.homedir(), ".node9", "config.json");
17253
+ const configPath2 = import_path55.default.join(import_os51.default.homedir(), ".node9", "config.json");
17241
17254
  try {
17242
- const raw = JSON.parse(import_fs51.default.readFileSync(configPath, "utf-8"));
17255
+ const raw = JSON.parse(import_fs56.default.readFileSync(configPath2, "utf-8"));
17243
17256
  const settings = raw.settings ?? {};
17244
17257
  return settings.approvers ?? {};
17245
17258
  } catch {
@@ -17248,22 +17261,22 @@ function readApproversFromDisk() {
17248
17261
  }
17249
17262
  function approverStatusLine() {
17250
17263
  const a = readApproversFromDisk();
17251
- const fmt = (label, key) => {
17264
+ const fmt = (label2, key) => {
17252
17265
  const on = a[key] !== false;
17253
- return `[${key[0]}]${label.slice(1)} ${on ? import_chalk29.default.green("\u2713") : import_chalk29.default.dim("\u2717")}`;
17266
+ return `[${key[0]}]${label2.slice(1)} ${on ? import_chalk32.default.green("\u2713") : import_chalk32.default.dim("\u2717")}`;
17254
17267
  };
17255
17268
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
17256
17269
  }
17257
17270
  function toggleApprover(channel) {
17258
- const configPath = import_path52.default.join(import_os46.default.homedir(), ".node9", "config.json");
17271
+ const configPath2 = import_path55.default.join(import_os51.default.homedir(), ".node9", "config.json");
17259
17272
  try {
17260
- const raw = JSON.parse(import_fs51.default.readFileSync(configPath, "utf-8"));
17273
+ const raw = JSON.parse(import_fs56.default.readFileSync(configPath2, "utf-8"));
17261
17274
  const settings = raw.settings ?? {};
17262
17275
  const approvers = settings.approvers ?? {};
17263
17276
  approvers[channel] = approvers[channel] === false;
17264
17277
  settings.approvers = approvers;
17265
17278
  raw.settings = settings;
17266
- import_fs51.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
17279
+ import_fs56.default.writeFileSync(configPath2, JSON.stringify(raw, null, 2) + "\n");
17267
17280
  } catch (err2) {
17268
17281
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
17269
17282
  `);
@@ -17273,7 +17286,7 @@ async function startTail(options = {}) {
17273
17286
  const port = await ensureDaemon();
17274
17287
  if (options.clear) {
17275
17288
  const result = await new Promise((resolve) => {
17276
- const req2 = import_http2.default.request(
17289
+ const req2 = import_http3.default.request(
17277
17290
  { method: "POST", hostname: "127.0.0.1", port, path: "/events/clear" },
17278
17291
  (res) => {
17279
17292
  const status = res.statusCode ?? 0;
@@ -17295,7 +17308,7 @@ async function startTail(options = {}) {
17295
17308
  req2.end();
17296
17309
  });
17297
17310
  if (result.ok) {
17298
- console.log(import_chalk29.default.green("\u2713 Flight Recorder buffer cleared."));
17311
+ console.log(import_chalk32.default.green("\u2713 Flight Recorder buffer cleared."));
17299
17312
  } else if (result.code === "ECONNREFUSED") {
17300
17313
  throw new Error("Daemon is not running. Start it with: node9 daemon start");
17301
17314
  } else if (result.code === "ETIMEDOUT") {
@@ -17341,7 +17354,7 @@ async function startTail(options = {}) {
17341
17354
  const channel = name === "n" ? "native" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
17342
17355
  if (channel) {
17343
17356
  toggleApprover(channel);
17344
- console.log(import_chalk29.default.dim(` Approvers: ${approverStatusLine()}`));
17357
+ console.log(import_chalk32.default.dim(` Approvers: ${approverStatusLine()}`));
17345
17358
  }
17346
17359
  };
17347
17360
  process.stdin.on("keypress", idleKeypressHandler);
@@ -17407,7 +17420,7 @@ async function startTail(options = {}) {
17407
17420
  localAllowCounts.get(req2.toolName) ?? 0
17408
17421
  )
17409
17422
  );
17410
- const decisionStamp = action === "always-allow" ? import_chalk29.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk29.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk29.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk29.default.yellow("\u21A9 REDIRECT AI") : import_chalk29.default.red("\u2717 DENIED");
17423
+ const decisionStamp = action === "always-allow" ? import_chalk32.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk32.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk32.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk32.default.yellow("\u21A9 REDIRECT AI") : import_chalk32.default.red("\u2717 DENIED");
17411
17424
  stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
17412
17425
  for (const line of stampedLines) process.stdout.write(line + "\n");
17413
17426
  process.stdout.write(SHOW_CURSOR);
@@ -17435,8 +17448,8 @@ async function startTail(options = {}) {
17435
17448
  }
17436
17449
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
17437
17450
  try {
17438
- import_fs51.default.appendFileSync(
17439
- import_path52.default.join(import_os46.default.homedir(), ".node9", "hook-debug.log"),
17451
+ import_fs56.default.appendFileSync(
17452
+ import_path55.default.join(import_os51.default.homedir(), ".node9", "hook-debug.log"),
17440
17453
  `[tail] POST /decision failed: ${String(err2)}
17441
17454
  `
17442
17455
  );
@@ -17458,7 +17471,7 @@ async function startTail(options = {}) {
17458
17471
  );
17459
17472
  const stampedLines = buildCardLines(req2, priorCount);
17460
17473
  if (externalDecision) {
17461
- const source = externalDecision === "allow" ? import_chalk29.default.green("\u2713 ALLOWED") : import_chalk29.default.red("\u2717 DENIED");
17474
+ const source = externalDecision === "allow" ? import_chalk32.default.green("\u2713 ALLOWED") : import_chalk32.default.red("\u2717 DENIED");
17462
17475
  stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
17463
17476
  }
17464
17477
  for (const line of stampedLines) process.stdout.write(line + "\n");
@@ -17500,31 +17513,31 @@ async function startTail(options = {}) {
17500
17513
  };
17501
17514
  process.stdin.on("keypress", onKeypress);
17502
17515
  }
17503
- const auditLog = import_path52.default.join(import_os46.default.homedir(), ".node9", "audit.log");
17516
+ const auditLog = import_path55.default.join(import_os51.default.homedir(), ".node9", "audit.log");
17504
17517
  try {
17505
- const unackedDlp = import_fs51.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17518
+ const unackedDlp = import_fs56.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17506
17519
  if (unackedDlp > 0) {
17507
17520
  console.log("");
17508
17521
  console.log(
17509
- import_chalk29.default.bgRed.white.bold(
17522
+ import_chalk32.default.bgRed.white.bold(
17510
17523
  ` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
17511
17524
  )
17512
17525
  );
17513
17526
  }
17514
17527
  } catch {
17515
17528
  }
17516
- console.log(import_chalk29.default.cyan.bold(`
17529
+ console.log(import_chalk32.default.cyan.bold(`
17517
17530
  \u{1F6F0}\uFE0F Node9 tail`));
17518
17531
  if (canApprove) {
17519
- console.log(import_chalk29.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
17520
- console.log(import_chalk29.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
17532
+ console.log(import_chalk32.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
17533
+ console.log(import_chalk32.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
17521
17534
  }
17522
17535
  const ctxStat = readSessionUsage();
17523
17536
  if (ctxStat) console.log(" " + formatContextStat(ctxStat));
17524
17537
  if (options.history) {
17525
- console.log(import_chalk29.default.dim("Showing history + live events.\n"));
17538
+ console.log(import_chalk32.default.dim("Showing history + live events.\n"));
17526
17539
  } else {
17527
- console.log(import_chalk29.default.dim("Showing live events only. Use --history to include past.\n"));
17540
+ console.log(import_chalk32.default.dim("Showing live events only. Use --history to include past.\n"));
17528
17541
  }
17529
17542
  process.on("SIGINT", () => {
17530
17543
  exitIdleMode();
@@ -17534,7 +17547,7 @@ async function startTail(options = {}) {
17534
17547
  import_readline6.default.clearLine(process.stdout, 0);
17535
17548
  import_readline6.default.cursorTo(process.stdout, 0);
17536
17549
  }
17537
- console.log(import_chalk29.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
17550
+ console.log(import_chalk32.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
17538
17551
  process.exit(0);
17539
17552
  });
17540
17553
  const STALL_THRESHOLD_MS = 6e4;
@@ -17542,11 +17555,11 @@ async function startTail(options = {}) {
17542
17555
  if (stallWarned) return;
17543
17556
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
17544
17557
  try {
17545
- const auditMtime = import_fs51.default.statSync(auditLog).mtimeMs;
17558
+ const auditMtime = import_fs56.default.statSync(auditLog).mtimeMs;
17546
17559
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
17547
17560
  console.log("");
17548
17561
  console.log(
17549
- import_chalk29.default.yellow(
17562
+ import_chalk32.default.yellow(
17550
17563
  "\u26A0\uFE0F Tail appears stalled \u2014 hooks are firing but no events are arriving. Try: node9 daemon restart"
17551
17564
  )
17552
17565
  );
@@ -17556,14 +17569,14 @@ async function startTail(options = {}) {
17556
17569
  }, STALL_THRESHOLD_MS / 2);
17557
17570
  stallWatchdog.unref();
17558
17571
  const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
17559
- const req = import_http2.default.get(
17572
+ const req = import_http3.default.get(
17560
17573
  sseUrl,
17561
17574
  {
17562
17575
  headers: authToken ? { "X-Node9-Internal": authToken } : {}
17563
17576
  },
17564
17577
  (res) => {
17565
17578
  if (res.statusCode !== 200) {
17566
- console.error(import_chalk29.default.red(`Failed to connect: HTTP ${res.statusCode}`));
17579
+ console.error(import_chalk32.default.red(`Failed to connect: HTTP ${res.statusCode}`));
17567
17580
  process.exit(1);
17568
17581
  }
17569
17582
  if (canApprove) enterIdleMode();
@@ -17594,7 +17607,7 @@ async function startTail(options = {}) {
17594
17607
  import_readline6.default.clearLine(process.stdout, 0);
17595
17608
  import_readline6.default.cursorTo(process.stdout, 0);
17596
17609
  }
17597
- console.log(import_chalk29.default.red("\n\u274C Daemon disconnected."));
17610
+ console.log(import_chalk32.default.red("\n\u274C Daemon disconnected."));
17598
17611
  process.exit(1);
17599
17612
  });
17600
17613
  }
@@ -17607,7 +17620,7 @@ async function startTail(options = {}) {
17607
17620
  const parsed = JSON.parse(rawData);
17608
17621
  const msg = parsed.message ?? "Flight recorder is down \u2014 run: node9 daemon restart";
17609
17622
  console.log("");
17610
- console.log(import_chalk29.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
17623
+ console.log(import_chalk32.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
17611
17624
  } catch {
17612
17625
  }
17613
17626
  return;
@@ -17692,9 +17705,9 @@ async function startTail(options = {}) {
17692
17705
  const rawSummary = data.argsSummary ?? data.tool;
17693
17706
  const summary = shortenPathSummary(rawSummary);
17694
17707
  const fileCount = data.fileCount ?? 0;
17695
- const files = fileCount > 0 ? import_chalk29.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
17708
+ const files = fileCount > 0 ? import_chalk32.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
17696
17709
  process.stdout.write(
17697
- `${import_chalk29.default.dim(time)} ${import_chalk29.default.cyan("\u{1F4F8} snapshot")} ${import_chalk29.default.dim(hash)} ${summary}${files}
17710
+ `${import_chalk32.default.dim(time)} ${import_chalk32.default.cyan("\u{1F4F8} snapshot")} ${import_chalk32.default.dim(hash)} ${summary}${files}
17698
17711
  `
17699
17712
  );
17700
17713
  return;
@@ -17711,36 +17724,36 @@ async function startTail(options = {}) {
17711
17724
  if (event === "execution-result") {
17712
17725
  const exec = data;
17713
17726
  const time = new Date(Date.now()).toLocaleTimeString([], { hour12: false });
17714
- const arrow = exec.isError ? import_chalk29.default.red(" \u21B3 \u2717") : import_chalk29.default.green(" \u21B3 \u2713");
17715
- const label = agentLabel(exec.agent, exec.mcpServer);
17727
+ const arrow = exec.isError ? import_chalk32.default.red(" \u21B3 \u2717") : import_chalk32.default.green(" \u21B3 \u2713");
17728
+ const label2 = agentLabel(exec.agent, exec.mcpServer);
17716
17729
  const tool = (exec.tool ?? "").slice(0, 16);
17717
- const duration = typeof exec.durationMs === "number" ? import_chalk29.default.dim(` (${exec.durationMs}ms)`) : "";
17730
+ const duration = typeof exec.durationMs === "number" ? import_chalk32.default.dim(` (${exec.durationMs}ms)`) : "";
17718
17731
  console.log(
17719
- `${import_chalk29.default.gray(time)} ${arrow} ${label}${import_chalk29.default.dim(tool)}${import_chalk29.default.dim(" completed")}${duration}`
17732
+ `${import_chalk32.default.gray(time)} ${arrow} ${label2}${import_chalk32.default.dim(tool)}${import_chalk32.default.dim(" completed")}${duration}`
17720
17733
  );
17721
17734
  }
17722
17735
  }
17723
17736
  req.on("error", (err2) => {
17724
17737
  const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
17725
- console.error(import_chalk29.default.red(`
17738
+ console.error(import_chalk32.default.red(`
17726
17739
  \u274C ${msg}`));
17727
17740
  process.exit(1);
17728
17741
  });
17729
17742
  }
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;
17743
+ var import_http3, import_chalk32, import_fs56, import_os51, import_path55, 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;
17731
17744
  var init_tail = __esm({
17732
17745
  "src/tui/tail.ts"() {
17733
17746
  "use strict";
17734
- import_http2 = __toESM(require("http"));
17735
- import_chalk29 = __toESM(require("chalk"));
17736
- import_fs51 = __toESM(require("fs"));
17737
- import_os46 = __toESM(require("os"));
17738
- import_path52 = __toESM(require("path"));
17747
+ import_http3 = __toESM(require("http"));
17748
+ import_chalk32 = __toESM(require("chalk"));
17749
+ import_fs56 = __toESM(require("fs"));
17750
+ import_os51 = __toESM(require("os"));
17751
+ import_path55 = __toESM(require("path"));
17739
17752
  import_readline6 = __toESM(require("readline"));
17740
17753
  import_child_process12 = require("child_process");
17741
17754
  init_daemon2();
17742
17755
  init_daemon();
17743
- PID_FILE = import_path52.default.join(import_os46.default.homedir(), ".node9", "daemon.pid");
17756
+ PID_FILE = import_path55.default.join(import_os51.default.homedir(), ".node9", "daemon.pid");
17744
17757
  ICONS = {
17745
17758
  bash: "\u{1F4BB}",
17746
17759
  shell: "\u{1F4BB}",
@@ -17805,7 +17818,7 @@ function queryDaemon() {
17805
17818
  return new Promise((resolve) => {
17806
17819
  const timeout = setTimeout(() => resolve(null), 50);
17807
17820
  try {
17808
- const req = import_http3.default.get(
17821
+ const req = import_http4.default.get(
17809
17822
  `http://${DAEMON_HOST}:${DAEMON_PORT}/status`,
17810
17823
  { timeout: 50 },
17811
17824
  (res) => {
@@ -17862,9 +17875,9 @@ function formatTimeLeft(resetsAt) {
17862
17875
  return ` (${m}m left)`;
17863
17876
  }
17864
17877
  function safeReadJson(filePath) {
17865
- if (!import_fs52.default.existsSync(filePath)) return null;
17878
+ if (!import_fs57.default.existsSync(filePath)) return null;
17866
17879
  try {
17867
- return JSON.parse(import_fs52.default.readFileSync(filePath, "utf-8"));
17880
+ return JSON.parse(import_fs57.default.readFileSync(filePath, "utf-8"));
17868
17881
  } catch {
17869
17882
  return null;
17870
17883
  }
@@ -17885,12 +17898,12 @@ function countHooksInFile(filePath) {
17885
17898
  return Object.keys(cfg.hooks).length;
17886
17899
  }
17887
17900
  function countRulesInDir(rulesDir) {
17888
- if (!import_fs52.default.existsSync(rulesDir)) return 0;
17901
+ if (!import_fs57.default.existsSync(rulesDir)) return 0;
17889
17902
  let count = 0;
17890
17903
  try {
17891
- for (const entry of import_fs52.default.readdirSync(rulesDir, { withFileTypes: true })) {
17904
+ for (const entry of import_fs57.default.readdirSync(rulesDir, { withFileTypes: true })) {
17892
17905
  if (entry.isDirectory()) {
17893
- count += countRulesInDir(import_path53.default.join(rulesDir, entry.name));
17906
+ count += countRulesInDir(import_path56.default.join(rulesDir, entry.name));
17894
17907
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
17895
17908
  count++;
17896
17909
  }
@@ -17901,46 +17914,46 @@ function countRulesInDir(rulesDir) {
17901
17914
  }
17902
17915
  function isSamePath(a, b) {
17903
17916
  try {
17904
- return import_path53.default.resolve(a) === import_path53.default.resolve(b);
17917
+ return import_path56.default.resolve(a) === import_path56.default.resolve(b);
17905
17918
  } catch {
17906
17919
  return false;
17907
17920
  }
17908
17921
  }
17909
17922
  function countConfigs(cwd) {
17910
- const homeDir2 = import_os47.default.homedir();
17911
- const claudeDir = import_path53.default.join(homeDir2, ".claude");
17923
+ const homeDir2 = import_os52.default.homedir();
17924
+ const claudeDir = import_path56.default.join(homeDir2, ".claude");
17912
17925
  let claudeMdCount = 0;
17913
17926
  let rulesCount = 0;
17914
17927
  let hooksCount = 0;
17915
17928
  const userMcpServers = /* @__PURE__ */ new Set();
17916
17929
  const projectMcpServers = /* @__PURE__ */ new Set();
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");
17930
+ if (import_fs57.default.existsSync(import_path56.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
17931
+ rulesCount += countRulesInDir(import_path56.default.join(claudeDir, "rules"));
17932
+ const userSettings = import_path56.default.join(claudeDir, "settings.json");
17920
17933
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
17921
17934
  hooksCount += countHooksInFile(userSettings);
17922
- const userClaudeJson = import_path53.default.join(homeDir2, ".claude.json");
17935
+ const userClaudeJson = import_path56.default.join(homeDir2, ".claude.json");
17923
17936
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
17924
17937
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
17925
17938
  userMcpServers.delete(name);
17926
17939
  }
17927
17940
  if (cwd) {
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");
17941
+ if (import_fs57.default.existsSync(import_path56.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
17942
+ if (import_fs57.default.existsSync(import_path56.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
17943
+ const projectClaudeDir = import_path56.default.join(cwd, ".claude");
17931
17944
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
17932
17945
  if (!overlapsUserScope) {
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");
17946
+ if (import_fs57.default.existsSync(import_path56.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
17947
+ rulesCount += countRulesInDir(import_path56.default.join(projectClaudeDir, "rules"));
17948
+ const projSettings = import_path56.default.join(projectClaudeDir, "settings.json");
17936
17949
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
17937
17950
  hooksCount += countHooksInFile(projSettings);
17938
17951
  }
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");
17952
+ if (import_fs57.default.existsSync(import_path56.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
17953
+ const localSettings = import_path56.default.join(projectClaudeDir, "settings.local.json");
17941
17954
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
17942
17955
  hooksCount += countHooksInFile(localSettings);
17943
- const mcpJsonServers = getMcpServerNames(import_path53.default.join(cwd, ".mcp.json"));
17956
+ const mcpJsonServers = getMcpServerNames(import_path56.default.join(cwd, ".mcp.json"));
17944
17957
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
17945
17958
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
17946
17959
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -17973,12 +17986,12 @@ function readActiveShieldsHud() {
17973
17986
  return shieldsCache.value;
17974
17987
  }
17975
17988
  try {
17976
- const shieldsPath = import_path53.default.join(import_os47.default.homedir(), ".node9", "shields.json");
17977
- if (!import_fs52.default.existsSync(shieldsPath)) {
17989
+ const shieldsPath = import_path56.default.join(import_os52.default.homedir(), ".node9", "shields.json");
17990
+ if (!import_fs57.default.existsSync(shieldsPath)) {
17978
17991
  shieldsCache = { value: [], ts: now };
17979
17992
  return [];
17980
17993
  }
17981
- const parsed = JSON.parse(import_fs52.default.readFileSync(shieldsPath, "utf-8"));
17994
+ const parsed = JSON.parse(import_fs57.default.readFileSync(shieldsPath, "utf-8"));
17982
17995
  if (!Array.isArray(parsed.active)) {
17983
17996
  shieldsCache = { value: [], ts: now };
17984
17997
  return [];
@@ -18080,17 +18093,17 @@ function renderContextLine(stdin) {
18080
18093
  async function main() {
18081
18094
  try {
18082
18095
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
18083
- if (import_fs52.default.existsSync(import_path53.default.join(import_os47.default.homedir(), ".node9", "hud-debug"))) {
18096
+ if (import_fs57.default.existsSync(import_path56.default.join(import_os52.default.homedir(), ".node9", "hud-debug"))) {
18084
18097
  try {
18085
- const logPath = import_path53.default.join(import_os47.default.homedir(), ".node9", "hud-debug.log");
18098
+ const logPath = import_path56.default.join(import_os52.default.homedir(), ".node9", "hud-debug.log");
18086
18099
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
18087
18100
  let size = 0;
18088
18101
  try {
18089
- size = import_fs52.default.statSync(logPath).size;
18102
+ size = import_fs57.default.statSync(logPath).size;
18090
18103
  } catch {
18091
18104
  }
18092
18105
  if (size < MAX_LOG_SIZE) {
18093
- import_fs52.default.appendFileSync(
18106
+ import_fs57.default.appendFileSync(
18094
18107
  logPath,
18095
18108
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
18096
18109
  );
@@ -18110,12 +18123,12 @@ async function main() {
18110
18123
  const showEnvCounts = (() => {
18111
18124
  try {
18112
18125
  const cwd = stdin.cwd ?? process.cwd();
18113
- for (const configPath of [
18114
- import_path53.default.join(cwd, "node9.config.json"),
18115
- import_path53.default.join(import_os47.default.homedir(), ".node9", "config.json")
18126
+ for (const configPath2 of [
18127
+ import_path56.default.join(cwd, "node9.config.json"),
18128
+ import_path56.default.join(import_os52.default.homedir(), ".node9", "config.json")
18116
18129
  ]) {
18117
- if (!import_fs52.default.existsSync(configPath)) continue;
18118
- const cfg = JSON.parse(import_fs52.default.readFileSync(configPath, "utf-8"));
18130
+ if (!import_fs57.default.existsSync(configPath2)) continue;
18131
+ const cfg = JSON.parse(import_fs57.default.readFileSync(configPath2, "utf-8"));
18119
18132
  const hud = cfg.settings?.hud;
18120
18133
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
18121
18134
  }
@@ -18133,14 +18146,14 @@ async function main() {
18133
18146
  renderOffline();
18134
18147
  }
18135
18148
  }
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;
18149
+ var import_fs57, import_path56, import_os52, import_http4, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
18137
18150
  var init_hud = __esm({
18138
18151
  "src/cli/hud.ts"() {
18139
18152
  "use strict";
18140
- import_fs52 = __toESM(require("fs"));
18141
- import_path53 = __toESM(require("path"));
18142
- import_os47 = __toESM(require("os"));
18143
- import_http3 = __toESM(require("http"));
18153
+ import_fs57 = __toESM(require("fs"));
18154
+ import_path56 = __toESM(require("path"));
18155
+ import_os52 = __toESM(require("os"));
18156
+ import_http4 = __toESM(require("http"));
18144
18157
  init_daemon();
18145
18158
  RESET3 = "\x1B[0m";
18146
18159
  BOLD3 = "\x1B[1m";
@@ -18165,10 +18178,10 @@ var import_commander = require("commander");
18165
18178
  init_core();
18166
18179
  init_setup();
18167
18180
  init_daemon2();
18168
- var import_chalk30 = __toESM(require("chalk"));
18169
- var import_fs53 = __toESM(require("fs"));
18170
- var import_path54 = __toESM(require("path"));
18171
- var import_os48 = __toESM(require("os"));
18181
+ var import_chalk33 = __toESM(require("chalk"));
18182
+ var import_fs58 = __toESM(require("fs"));
18183
+ var import_path57 = __toESM(require("path"));
18184
+ var import_os53 = __toESM(require("os"));
18172
18185
  var import_prompts2 = require("@inquirer/prompts");
18173
18186
 
18174
18187
  // src/utils/duration.ts
@@ -18208,8 +18221,8 @@ INSTRUCTIONS:
18208
18221
  - Acknowledge the block to the user and ask if there is an alternative approach.
18209
18222
  - If you believe this action is critical, explain your reasoning and ask them to run "node9 pause 15m" to proceed.`;
18210
18223
  }
18211
- const label = blockedByLabel.toLowerCase();
18212
- if (label.includes("dlp") || label.includes("secret detected") || label.includes("credential review")) {
18224
+ const label2 = blockedByLabel.toLowerCase();
18225
+ if (label2.includes("dlp") || label2.includes("secret detected") || label2.includes("credential review")) {
18213
18226
  return `NODE9 SECURITY ALERT: A sensitive credential (API key, token, or private key) was found in your tool call arguments.
18214
18227
  CRITICAL INSTRUCTION: Do NOT retry this action.
18215
18228
  REQUIRED ACTIONS:
@@ -18218,37 +18231,37 @@ REQUIRED ACTIONS:
18218
18231
  3. Treat the leaked credential as compromised and rotate it immediately.
18219
18232
  Do NOT attempt to bypass this check or pass the credential through another tool.`;
18220
18233
  }
18221
- if (label.includes("sql safety") && label.includes("delete without where")) {
18234
+ if (label2.includes("sql safety") && label2.includes("delete without where")) {
18222
18235
  return `NODE9: Blocked \u2014 DELETE without WHERE clause would wipe the entire table.
18223
18236
  INSTRUCTION: Add a WHERE clause to scope the deletion (e.g. WHERE id = <value>).
18224
18237
  Do NOT retry without a WHERE clause.`;
18225
18238
  }
18226
- if (label.includes("sql safety") && label.includes("update without where")) {
18239
+ if (label2.includes("sql safety") && label2.includes("update without where")) {
18227
18240
  return `NODE9: Blocked \u2014 UPDATE without WHERE clause would update every row.
18228
18241
  INSTRUCTION: Add a WHERE clause to scope the update (e.g. WHERE id = <value>).
18229
18242
  Do NOT retry without a WHERE clause.`;
18230
18243
  }
18231
- if (label.includes("dangerous word")) {
18244
+ if (label2.includes("dangerous word")) {
18232
18245
  const match = blockedByLabel.match(/dangerous word: "([^"]+)"/i);
18233
18246
  const word = match?.[1] ?? "a dangerous keyword";
18234
18247
  return `NODE9: Blocked \u2014 command contains forbidden keyword "${word}".
18235
18248
  INSTRUCTION: Do NOT use "${word}". Use a non-destructive alternative.
18236
18249
  Do NOT attempt to bypass this with shell tricks or aliases \u2014 it will be blocked again.`;
18237
18250
  }
18238
- if (label.includes("path blocked") || label.includes("sandbox")) {
18251
+ if (label2.includes("path blocked") || label2.includes("sandbox")) {
18239
18252
  return `NODE9: Blocked \u2014 operation targets a path outside the allowed sandbox.
18240
18253
  INSTRUCTION: Move your output to an allowed directory such as /tmp/ or the project directory.
18241
18254
  Do NOT retry on the same path.`;
18242
18255
  }
18243
- if (label.includes("inline execution")) {
18256
+ if (label2.includes("inline execution")) {
18244
18257
  return `NODE9: Blocked \u2014 inline code execution (e.g. bash -c "...") is not allowed.
18245
18258
  INSTRUCTION: Use individual tool calls instead of embedding code in a shell string.`;
18246
18259
  }
18247
- if (label.includes("strict mode")) {
18260
+ if (label2.includes("strict mode")) {
18248
18261
  return `NODE9: Blocked \u2014 strict mode is active. All tool calls require explicit human approval.
18249
18262
  INSTRUCTION: Inform the user this action is pending approval. Wait for them to approve via the dashboard or run "node9 pause".`;
18250
18263
  }
18251
- if (label.includes("rule") && label.includes("default block")) {
18264
+ if (label2.includes("rule") && label2.includes("default block")) {
18252
18265
  const match = blockedByLabel.match(/rule "([^"]+)"/i);
18253
18266
  const rule = match?.[1] ?? "a policy rule";
18254
18267
  return `NODE9: Blocked \u2014 action "${rule}" is forbidden by security policy.
@@ -20039,7 +20052,7 @@ var AGENT_SPECS = [
20039
20052
  {
20040
20053
  id: "claude",
20041
20054
  label: "Claude Code",
20042
- setupCommand: "node9 setup claude",
20055
+ setupCommand: "node9 agents add claude",
20043
20056
  hookFile: (h) => import_path39.default.join(h, ".claude", "settings.json"),
20044
20057
  hookFormat: "matcher",
20045
20058
  hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
@@ -20049,7 +20062,7 @@ var AGENT_SPECS = [
20049
20062
  {
20050
20063
  id: "gemini",
20051
20064
  label: "Gemini CLI",
20052
- setupCommand: "node9 setup gemini",
20065
+ setupCommand: "node9 agents add gemini",
20053
20066
  hookFile: (h) => import_path39.default.join(h, ".gemini", "settings.json"),
20054
20067
  hookFormat: "matcher",
20055
20068
  hookEvents: [ck("BeforeTool"), lg("AfterTool")],
@@ -20059,7 +20072,7 @@ var AGENT_SPECS = [
20059
20072
  {
20060
20073
  id: "codex",
20061
20074
  label: "Codex",
20062
- setupCommand: "node9 setup codex",
20075
+ setupCommand: "node9 agents add codex",
20063
20076
  hookFile: (h) => import_path39.default.join(h, ".codex", "hooks.json"),
20064
20077
  hookFormat: "matcher",
20065
20078
  hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
@@ -20070,7 +20083,7 @@ var AGENT_SPECS = [
20070
20083
  {
20071
20084
  id: "antigravity",
20072
20085
  label: "Antigravity",
20073
- setupCommand: "node9 setup antigravity",
20086
+ setupCommand: "node9 agents add antigravity",
20074
20087
  hookFile: (h) => import_path39.default.join(h, ".gemini", "config", "hooks.json"),
20075
20088
  hookFormat: "matcher",
20076
20089
  hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
@@ -20080,7 +20093,7 @@ var AGENT_SPECS = [
20080
20093
  {
20081
20094
  id: "copilot",
20082
20095
  label: "GitHub Copilot",
20083
- setupCommand: "node9 setup copilot",
20096
+ setupCommand: "node9 agents add copilot",
20084
20097
  hookFile: (h) => import_path39.default.join(h, ".copilot", "hooks", "node9.json"),
20085
20098
  hookFormat: "flat",
20086
20099
  hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
@@ -20090,7 +20103,7 @@ var AGENT_SPECS = [
20090
20103
  {
20091
20104
  id: "cursor",
20092
20105
  label: "Cursor",
20093
- setupCommand: "node9 setup cursor",
20106
+ setupCommand: "node9 agents add cursor",
20094
20107
  // MCP-only — no hook file (see note above).
20095
20108
  hookFormat: "flat",
20096
20109
  hookEvents: [],
@@ -20100,7 +20113,7 @@ var AGENT_SPECS = [
20100
20113
  {
20101
20114
  id: "hermes",
20102
20115
  label: "Hermes Agent",
20103
- setupCommand: "node9 setup hermes",
20116
+ setupCommand: "node9 agents add hermes",
20104
20117
  hookFile: (h) => hermesConfigPath(h),
20105
20118
  hookFormat: "yaml",
20106
20119
  hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
@@ -20113,7 +20126,7 @@ var AGENT_SPECS = [
20113
20126
  // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
20114
20127
  id: "opencode",
20115
20128
  label: "OpenCode",
20116
- setupCommand: "node9 setup opencode",
20129
+ setupCommand: "node9 agents add opencode",
20117
20130
  hookFormat: "flat",
20118
20131
  hookEvents: [],
20119
20132
  shimFile: (h) => import_path39.default.join(h, ".config", "opencode", "plugins", "node9.js"),
@@ -20122,7 +20135,7 @@ var AGENT_SPECS = [
20122
20135
  {
20123
20136
  id: "pi",
20124
20137
  label: "Pi",
20125
- setupCommand: "node9 setup pi",
20138
+ setupCommand: "node9 agents add pi",
20126
20139
  hookFormat: "flat",
20127
20140
  hookEvents: [],
20128
20141
  shimFile: (h) => import_path39.default.join(h, ".pi", "agent", "extensions", "node9.js"),
@@ -20206,10 +20219,7 @@ function registerDoctorCommand(program2, version2) {
20206
20219
  const which = (0, import_child_process8.execSync)("which node9", { encoding: "utf-8", timeout: 3e3 }).trim();
20207
20220
  pass(`node9 found at ${which}`);
20208
20221
  } catch {
20209
- warn(
20210
- "node9 not found in $PATH \u2014 hooks may not find it",
20211
- "Run: npm install -g @node9/proxy"
20212
- );
20222
+ warn("node9 not found in $PATH \u2014 hooks may not find it", "Run: npm install -g node9-ai");
20213
20223
  }
20214
20224
  const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
20215
20225
  if (nodeMajor >= 18) {
@@ -20279,7 +20289,7 @@ function registerDoctorCommand(program2, version2) {
20279
20289
  if (notConfigured.length > 0) {
20280
20290
  console.log(
20281
20291
  import_chalk11.default.gray(
20282
- ` \xB7 Not configured: ${notConfigured.join(", ")} \u2014 run \`node9 setup <agent>\` if you use one`
20292
+ ` \xB7 Not configured: ${notConfigured.join(", ")} \u2014 run \`node9 agents add <agent>\` if you use one`
20283
20293
  )
20284
20294
  );
20285
20295
  }
@@ -21260,10 +21270,10 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
21260
21270
  );
21261
21271
  console.log("");
21262
21272
  const COL1 = 18;
21263
- const summaryRow = (icon, label, count, note, colorFn = (s) => s) => {
21273
+ const summaryRow = (icon, label2, count, note, colorFn = (s) => s) => {
21264
21274
  const countStr = colorFn(num2(count));
21265
21275
  const noteStr = note ? import_chalk13.default.dim(" " + note) : "";
21266
- console.log(" " + icon + " " + import_chalk13.default.white(label.padEnd(COL1)) + countStr + noteStr);
21276
+ console.log(" " + icon + " " + import_chalk13.default.white(label2.padEnd(COL1)) + countStr + noteStr);
21267
21277
  };
21268
21278
  summaryRow(
21269
21279
  userApproved > 0 ? import_chalk13.default.green("\u2705") : import_chalk13.default.dim("\u2705"),
@@ -21330,21 +21340,21 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
21330
21340
  let leftStyled = " ".repeat(COL);
21331
21341
  if (i < topTools.length) {
21332
21342
  const [tool, { calls }] = topTools[i];
21333
- const label = tool.length > LABEL - 1 ? tool.slice(0, LABEL - 2) + "\u2026" : tool;
21343
+ const label2 = tool.length > LABEL - 1 ? tool.slice(0, LABEL - 2) + "\u2026" : tool;
21334
21344
  const countStr = num2(calls).padStart(TOOL_COUNT_W);
21335
21345
  const b = colorBar(calls, maxTool, BAR);
21336
21346
  const rawLen = LABEL + BAR + 1 + TOOL_COUNT_W;
21337
21347
  const pad = Math.max(0, COL - rawLen);
21338
- leftStyled = import_chalk13.default.white(label.padEnd(LABEL)) + b + " " + import_chalk13.default.white(countStr) + " ".repeat(pad);
21348
+ leftStyled = import_chalk13.default.white(label2.padEnd(LABEL)) + b + " " + import_chalk13.default.white(countStr) + " ".repeat(pad);
21339
21349
  }
21340
21350
  let rightStyled = "";
21341
21351
  if (i < topBlocks.length) {
21342
21352
  const [reason, count] = topBlocks[i];
21343
21353
  const readable = humanBlockReason(reason);
21344
- const label = readable.length > LABEL - 1 ? readable.slice(0, LABEL - 2) + "\u2026" : readable;
21354
+ const label2 = readable.length > LABEL - 1 ? readable.slice(0, LABEL - 2) + "\u2026" : readable;
21345
21355
  const countStr = num2(count).padStart(BLOCK_COUNT_W);
21346
21356
  const b = colorBar(count, maxBlock, BAR);
21347
- rightStyled = import_chalk13.default.white(label.padEnd(LABEL)) + b + " " + import_chalk13.default.red(countStr);
21357
+ rightStyled = import_chalk13.default.white(label2.padEnd(LABEL)) + b + " " + import_chalk13.default.red(countStr);
21348
21358
  }
21349
21359
  console.log(" " + leftStyled + " " + rightStyled);
21350
21360
  }
@@ -21357,9 +21367,9 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
21357
21367
  console.log(" " + import_chalk13.default.dim("\u2500".repeat(Math.min(50, W - 4))));
21358
21368
  const maxAgent = Math.max(...agentMap.values(), 1);
21359
21369
  for (const [agent, count] of [...agentMap.entries()].sort((a, b) => b[1] - a[1])) {
21360
- const label = agent.slice(0, LABEL - 1);
21370
+ const label2 = agent.slice(0, LABEL - 1);
21361
21371
  const b = colorBar(count, maxAgent, BAR);
21362
- console.log(" " + import_chalk13.default.white(label.padEnd(LABEL)) + b + " " + import_chalk13.default.white(num2(count)));
21372
+ console.log(" " + import_chalk13.default.white(label2.padEnd(LABEL)) + b + " " + import_chalk13.default.white(num2(count)));
21363
21373
  }
21364
21374
  }
21365
21375
  if (mcpMap.size > 0) {
@@ -21368,9 +21378,9 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
21368
21378
  console.log(" " + import_chalk13.default.dim("\u2500".repeat(Math.min(50, W - 4))));
21369
21379
  const maxMcp = Math.max(...mcpMap.values(), 1);
21370
21380
  for (const [server, count] of [...mcpMap.entries()].sort((a, b) => b[1] - a[1])) {
21371
- const label = server.slice(0, LABEL - 1).padEnd(LABEL);
21381
+ const label2 = server.slice(0, LABEL - 1).padEnd(LABEL);
21372
21382
  const b = colorBar(count, maxMcp, BAR);
21373
- console.log(" " + import_chalk13.default.white(label) + b + " " + import_chalk13.default.white(num2(count)));
21383
+ console.log(" " + import_chalk13.default.white(label2) + b + " " + import_chalk13.default.white(num2(count)));
21374
21384
  }
21375
21385
  }
21376
21386
  if (hourMap.size > 0) {
@@ -21391,13 +21401,13 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
21391
21401
  console.log(" " + import_chalk13.default.dim("\u2500".repeat(W - 2)));
21392
21402
  const DAY_BAR = Math.max(8, Math.min(30, W - 36));
21393
21403
  for (const [dateKey, { calls, blocked: db }] of dailyList) {
21394
- const label = fmtDate(dateKey).padEnd(10);
21404
+ const label2 = fmtDate(dateKey).padEnd(10);
21395
21405
  const b = colorBar(calls, maxDaily, DAY_BAR);
21396
21406
  const dayCost = costByDay.get(dateKey);
21397
21407
  const costNote = dayCost ? import_chalk13.default.magenta(` ${fmtCost2(dayCost)}`) : "";
21398
21408
  const blockNote = db > 0 ? import_chalk13.default.red(` ${db} blocked`) : "";
21399
21409
  console.log(
21400
- " " + import_chalk13.default.dim(label) + " " + b + " " + import_chalk13.default.white(num2(calls)) + blockNote + costNote
21410
+ " " + import_chalk13.default.dim(label2) + " " + b + " " + import_chalk13.default.white(num2(calls)) + blockNote + costNote
21401
21411
  );
21402
21412
  }
21403
21413
  }
@@ -21415,10 +21425,10 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
21415
21425
  ["Output", costOutputTokens, import_chalk13.default.white(num2(costOutputTokens))],
21416
21426
  ["Cache write", costCacheWrite, import_chalk13.default.yellow(num2(costCacheWrite))]
21417
21427
  ];
21418
- for (const [label, count, colored] of nonCacheRows) {
21428
+ for (const [label2, count, colored] of nonCacheRows) {
21419
21429
  if (count === 0) continue;
21420
21430
  const b = colorBar(count, maxNonCache, TOK_BAR);
21421
- console.log(" " + import_chalk13.default.white(label.padEnd(TOK_LABEL)) + b + " " + colored);
21431
+ console.log(" " + import_chalk13.default.white(label2.padEnd(TOK_LABEL)) + b + " " + colored);
21422
21432
  }
21423
21433
  if (costCacheRead > 0) {
21424
21434
  const cacheBar = colorBar(costCacheRead, costCacheRead, TOK_BAR);
@@ -21449,10 +21459,10 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
21449
21459
  const MODEL_LABEL = 22;
21450
21460
  const MODEL_BAR = Math.max(6, Math.min(20, W - MODEL_LABEL - 12));
21451
21461
  for (const [model, cost] of modelList) {
21452
- const label = model.length > MODEL_LABEL - 1 ? model.slice(0, MODEL_LABEL - 2) + "\u2026" : model;
21462
+ const label2 = model.length > MODEL_LABEL - 1 ? model.slice(0, MODEL_LABEL - 2) + "\u2026" : model;
21453
21463
  const b = colorBar(cost, maxModelCost, MODEL_BAR);
21454
21464
  console.log(
21455
- " " + import_chalk13.default.white(label.padEnd(MODEL_LABEL)) + b + " " + import_chalk13.default.yellow(fmtCost2(cost))
21465
+ " " + import_chalk13.default.white(label2.padEnd(MODEL_LABEL)) + b + " " + import_chalk13.default.yellow(fmtCost2(cost))
21456
21466
  );
21457
21467
  }
21458
21468
  }
@@ -21577,8 +21587,8 @@ var import_path43 = __toESM(require("path"));
21577
21587
  var import_os38 = __toESM(require("os"));
21578
21588
  init_core();
21579
21589
  init_daemon();
21580
- function printAgentSection(label, hookPairs, wrapped) {
21581
- console.log(import_chalk15.default.bold(` ${label}`));
21590
+ function printAgentSection(label2, hookPairs, wrapped) {
21591
+ console.log(import_chalk15.default.bold(` ${label2}`));
21582
21592
  for (const { name, present } of hookPairs) {
21583
21593
  if (present) {
21584
21594
  console.log(import_chalk15.default.green(` \u2713 ${name}`));
@@ -21770,32 +21780,32 @@ function registerInitCommand(program2) {
21770
21780
  }
21771
21781
  console.log("");
21772
21782
  }
21773
- const configPath = import_path44.default.join(import_os39.default.homedir(), ".node9", "config.json");
21774
- const isFirstInstall = !import_fs43.default.existsSync(configPath);
21775
- if (import_fs43.default.existsSync(configPath) && !options.force) {
21783
+ const configPath2 = import_path44.default.join(import_os39.default.homedir(), ".node9", "config.json");
21784
+ const isFirstInstall = !import_fs43.default.existsSync(configPath2);
21785
+ if (import_fs43.default.existsSync(configPath2) && !options.force) {
21776
21786
  try {
21777
- const existing = JSON.parse(import_fs43.default.readFileSync(configPath, "utf-8"));
21787
+ const existing = JSON.parse(import_fs43.default.readFileSync(configPath2, "utf-8"));
21778
21788
  const settings = existing.settings ?? {};
21779
21789
  if (settings.mode !== chosenMode) {
21780
21790
  settings.mode = chosenMode;
21781
21791
  existing.settings = settings;
21782
- import_fs43.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
21792
+ import_fs43.default.writeFileSync(configPath2, JSON.stringify(existing, null, 2) + "\n");
21783
21793
  console.log(import_chalk16.default.green(`\u2705 Mode updated: ${chosenMode}`));
21784
21794
  } else {
21785
- console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
21795
+ console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath2}`));
21786
21796
  }
21787
21797
  } catch {
21788
- console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
21798
+ console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath2}`));
21789
21799
  }
21790
21800
  } else {
21791
21801
  const configToSave = {
21792
21802
  ...DEFAULT_CONFIG,
21793
21803
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
21794
21804
  };
21795
- const dir = import_path44.default.dirname(configPath);
21805
+ const dir = import_path44.default.dirname(configPath2);
21796
21806
  if (!import_fs43.default.existsSync(dir)) import_fs43.default.mkdirSync(dir, { recursive: true });
21797
- import_fs43.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
21798
- console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath}`));
21807
+ import_fs43.default.writeFileSync(configPath2, JSON.stringify(configToSave, null, 2) + "\n");
21808
+ console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath2}`));
21799
21809
  console.log(import_chalk16.default.gray(` Mode: ${chosenMode}`));
21800
21810
  }
21801
21811
  if (options.skipSetup) return;
@@ -22106,13 +22116,13 @@ function registerUndoCommand(program2) {
22106
22116
  const e = display[i];
22107
22117
  const isGap = prevTs !== null && prevTs - e.timestamp > 6e4;
22108
22118
  if (isGap) console.log(import_chalk18.default.gray(" \u2500\u2500 earlier \u2500\u2500"));
22109
- const label = (e.argsSummary || e.files?.[0] || "\u2014").slice(0, 30).padEnd(30);
22119
+ const label2 = (e.argsSummary || e.files?.[0] || "\u2014").slice(0, 30).padEnd(30);
22110
22120
  const tool = e.tool.slice(0, 8).padEnd(8);
22111
22121
  const when = formatAge2(e.timestamp).padEnd(10);
22112
22122
  const dir = e.cwd.length > 30 ? "\u2026" + e.cwd.slice(-29) : e.cwd;
22113
22123
  console.log(
22114
22124
  import_chalk18.default.white(
22115
- ` ${String(i + 1).padEnd(3)} ${label} ${import_chalk18.default.cyan(tool)} ${import_chalk18.default.gray(when)} ${import_chalk18.default.gray(dir)}`
22125
+ ` ${String(i + 1).padEnd(3)} ${label2} ${import_chalk18.default.cyan(tool)} ${import_chalk18.default.gray(when)} ${import_chalk18.default.gray(dir)}`
22116
22126
  )
22117
22127
  );
22118
22128
  prevTs = e.timestamp;
@@ -23527,11 +23537,11 @@ function registerMcpPinCommand(program2) {
23527
23537
  `);
23528
23538
  process.exit(1);
23529
23539
  }
23530
- const label = pins.servers[serverKey].label;
23540
+ const label2 = pins.servers[serverKey].label;
23531
23541
  removePin(serverKey);
23532
23542
  console.log(import_chalk21.default.green(`
23533
23543
  \u{1F513} Pin removed for ${import_chalk21.default.cyan(serverKey)}`));
23534
- console.log(import_chalk21.default.gray(` Server: ${label}`));
23544
+ console.log(import_chalk21.default.gray(` Server: ${label2}`));
23535
23545
  console.log(import_chalk21.default.gray(" Next connection will re-pin with current tool definitions.\n"));
23536
23546
  });
23537
23547
  pinSubCmd.command("reset").description("Clear all MCP pins (next connection to each server will re-pin)").action(() => {
@@ -23743,11 +23753,1131 @@ function registerAgentsCommand(program2) {
23743
23753
  // src/cli.ts
23744
23754
  init_scan();
23745
23755
 
23746
- // src/cli/commands/sessions.ts
23747
- var import_chalk24 = __toESM(require("chalk"));
23756
+ // src/cli/commands/posture.ts
23757
+ var import_chalk25 = __toESM(require("chalk"));
23758
+
23759
+ // src/posture/index.ts
23760
+ var import_os44 = __toESM(require("os"));
23761
+
23762
+ // src/posture/secrets.ts
23748
23763
  var import_fs46 = __toESM(require("fs"));
23749
23764
  var import_path47 = __toESM(require("path"));
23750
23765
  var import_os41 = __toESM(require("os"));
23766
+ init_dist();
23767
+ var MAX_FILE_BYTES = 256 * 1024;
23768
+ function displayPath(p, home) {
23769
+ if (p === home) return "~";
23770
+ const prefix = home.endsWith(import_path47.default.sep) ? home : home + import_path47.default.sep;
23771
+ if (p.startsWith(prefix)) return "~" + import_path47.default.sep + p.slice(prefix.length);
23772
+ return p;
23773
+ }
23774
+ function safeRead(file) {
23775
+ try {
23776
+ const stat = import_fs46.default.statSync(file);
23777
+ if (!stat.isFile() || stat.size === 0 || stat.size > MAX_FILE_BYTES) return null;
23778
+ return import_fs46.default.readFileSync(file, "utf8");
23779
+ } catch {
23780
+ return null;
23781
+ }
23782
+ }
23783
+ function candidateFiles(home, cwd) {
23784
+ const files = /* @__PURE__ */ new Set();
23785
+ try {
23786
+ for (const name of import_fs46.default.readdirSync(cwd)) {
23787
+ if (name === ".env" || name.startsWith(".env.")) files.add(import_path47.default.join(cwd, name));
23788
+ }
23789
+ } catch {
23790
+ }
23791
+ for (const spec of AGENT_SPECS) {
23792
+ if (spec.hookFile) files.add(spec.hookFile(home));
23793
+ if (spec.mcpFile) files.add(spec.mcpFile(home));
23794
+ }
23795
+ files.add(import_path47.default.join(home, ".env"));
23796
+ return [...files];
23797
+ }
23798
+ function credentialMaterial(home) {
23799
+ return [
23800
+ import_path47.default.join(home, ".ssh", "id_rsa"),
23801
+ import_path47.default.join(home, ".ssh", "id_dsa"),
23802
+ import_path47.default.join(home, ".ssh", "id_ecdsa"),
23803
+ import_path47.default.join(home, ".ssh", "id_ed25519"),
23804
+ import_path47.default.join(home, ".aws", "credentials"),
23805
+ import_path47.default.join(home, ".config", "gcloud", "application_default_credentials.json")
23806
+ ];
23807
+ }
23808
+ function checkSecrets(ctx) {
23809
+ const home = ctx.home || import_os41.default.homedir();
23810
+ const findings = [];
23811
+ const plaintext = [];
23812
+ const plaintextPaths = [];
23813
+ for (const file of candidateFiles(home, ctx.cwd)) {
23814
+ const text = safeRead(file);
23815
+ if (!text) continue;
23816
+ const match = scanText(text);
23817
+ if (match) {
23818
+ plaintext.push(`${match.patternName} in ${displayPath(file, home)}`);
23819
+ plaintextPaths.push(file);
23820
+ }
23821
+ }
23822
+ if (plaintext.length > 0) {
23823
+ findings.push({
23824
+ category: "Secrets",
23825
+ severity: "critical",
23826
+ title: `${plaintext.length} plaintext secret${plaintext.length === 1 ? "" : "s"} on disk`,
23827
+ what: "API keys/tokens are sitting unencrypted in files on disk.",
23828
+ why: "They were saved in plaintext config / .env files.",
23829
+ who: "A tricked agent (or any program you run) could read and leak them.",
23830
+ detail: plaintext,
23831
+ fix: "Fix it now: run `node9 shield enable project-jail` (blocks credential-file reads in-path).",
23832
+ // Coverage is decided at the DLP layer — does node9 block the agent
23833
+ // reading these? (See enforcement.ts.)
23834
+ owner: "node9",
23835
+ coverageProbe: { kind: "fileRead", paths: plaintextPaths }
23836
+ });
23837
+ }
23838
+ const creds = [];
23839
+ const credPaths = [];
23840
+ for (const file of credentialMaterial(home)) {
23841
+ try {
23842
+ if (import_fs46.default.statSync(file).isFile()) {
23843
+ creds.push(displayPath(file, home));
23844
+ credPaths.push(file);
23845
+ }
23846
+ } catch {
23847
+ }
23848
+ }
23849
+ if (creds.length > 0) {
23850
+ findings.push({
23851
+ category: "Secrets",
23852
+ severity: "high",
23853
+ title: `${creds.length} credential file${creds.length === 1 ? "" : "s"} readable by the agent`,
23854
+ what: "Your SSH keys / cloud login files can be read by programs you run.",
23855
+ why: "They sit unlocked in your home folder.",
23856
+ who: "An unsandboxed agent could read them and use them to reach your servers / cloud.",
23857
+ detail: creds,
23858
+ fix: "Fix it now: run `node9 shield enable project-jail` (blocks ~/.ssh, ~/.aws, .env reads in-path).",
23859
+ owner: "node9",
23860
+ coverageProbe: { kind: "fileRead", paths: credPaths }
23861
+ });
23862
+ }
23863
+ return findings;
23864
+ }
23865
+
23866
+ // src/posture/egress.ts
23867
+ init_config();
23868
+ function evaluateEgressConfig(egress) {
23869
+ if (egress.enabled && egress.mode === "block") {
23870
+ return {
23871
+ category: "Egress",
23872
+ severity: "high",
23873
+ title: "Egress is locked, but node9 is not enforcing it",
23874
+ what: "Egress is set to block, but node9 is not applying the policy.",
23875
+ why: "node9 isn't wired in (or is in observe mode), so the lock has no effect.",
23876
+ who: "The lock protects nothing until node9 is enforcing in-path.",
23877
+ owner: "node9",
23878
+ detail: [],
23879
+ fix: "Run `node9 init` and ensure node9 is in enforcing mode.",
23880
+ coverageProbe: { kind: "egress" },
23881
+ // Open here means only "node9 isn't enforcing" — Coverage already says
23882
+ // that, so drop this row when open to avoid double-surfacing.
23883
+ redundantWhenOpen: true
23884
+ };
23885
+ }
23886
+ if (egress.enabled && egress.mode === "review") {
23887
+ return {
23888
+ category: "Egress",
23889
+ severity: "medium",
23890
+ title: "Egress is in review, but node9 is not enforcing it",
23891
+ what: "Egress is set to review (approval-gate), but node9 is not applying the policy.",
23892
+ why: "node9 isn't wired in (or is in observe mode), so the gate has no effect.",
23893
+ who: "Nothing gates outbound until node9 is enforcing in-path.",
23894
+ owner: "node9",
23895
+ detail: [],
23896
+ fix: "Run `node9 init` and ensure node9 is in enforcing mode.",
23897
+ coverageProbe: { kind: "egress" },
23898
+ // Open here means only "node9 isn't enforcing" — Coverage already says
23899
+ // that, so drop this row when open to avoid double-surfacing.
23900
+ redundantWhenOpen: true
23901
+ };
23902
+ }
23903
+ return {
23904
+ category: "Egress",
23905
+ severity: "high",
23906
+ title: "Egress is open",
23907
+ what: "Your agent can connect to any server on the internet.",
23908
+ why: "node9 isn't restricting where its network tools (curl, wget, ssh) can reach.",
23909
+ who: "If the agent is ever tricked, nothing stops it sending your data out.",
23910
+ owner: "node9",
23911
+ detail: [],
23912
+ fix: "Fix it now: run `node9 egress watch` (or `node9 egress lock` to hard-block).",
23913
+ coverageProbe: { kind: "egress" }
23914
+ };
23915
+ }
23916
+ function checkEgress(ctx) {
23917
+ const config = getConfig(ctx.cwd);
23918
+ const egress = config.policy.egress;
23919
+ return [evaluateEgressConfig({ enabled: egress.enabled, mode: egress.mode })];
23920
+ }
23921
+
23922
+ // src/posture/gate.ts
23923
+ init_policy();
23924
+ var BASELINE = ["rm", "-rf", "/"].join(" ");
23925
+ async function checkGate(ctx) {
23926
+ const verdict = await evaluatePolicy2("Bash", { command: BASELINE }, ctx.agent, ctx.cwd);
23927
+ if (verdict.decision !== "block") {
23928
+ return [
23929
+ {
23930
+ category: "Approval gate",
23931
+ severity: "critical",
23932
+ title: "No approval gate is active \u2014 destructive commands run unchecked",
23933
+ what: "Dangerous shell commands aren't gated \u2014 even `rm -rf /` would run.",
23934
+ why: "No enforcing shield or smart rule is gating Bash.",
23935
+ who: "A confused or tricked agent could damage the machine with one command.",
23936
+ detail: [],
23937
+ owner: "node9",
23938
+ fix: "Turn on the gate: run `node9 shield enable bash-safe` (or add a smart rule). node9 then blocks dangerous commands and the negotiation loop tells the agent what's allowed."
23939
+ }
23940
+ ];
23941
+ }
23942
+ return [
23943
+ {
23944
+ category: "Approval gate",
23945
+ severity: "advisory",
23946
+ title: "node9 is your approval gate \u2014 destructive commands are blocked",
23947
+ what: "Dangerous shell commands are blocked in-path by your shields and smart rules; when node9 blocks, the negotiation loop tells the agent what is allowed.",
23948
+ detail: [],
23949
+ owner: "node9",
23950
+ coverageProbe: { kind: "command", command: BASELINE },
23951
+ redundantWhenOpen: true
23952
+ }
23953
+ ];
23954
+ }
23955
+
23956
+ // src/posture/supply-chain.ts
23957
+ var import_fs47 = __toESM(require("fs"));
23958
+ var import_os42 = __toESM(require("os"));
23959
+ var import_path48 = __toESM(require("path"));
23960
+ var import_smol_toml3 = require("smol-toml");
23961
+ init_provenance();
23962
+ var PACKAGE_RUNNERS = /* @__PURE__ */ new Set(["npx", "pnpx", "bunx", "dlx", "yarn", "pnpm", "bun"]);
23963
+ function isNode9Managed(command, args = []) {
23964
+ if (!command) return false;
23965
+ if (import_path48.default.basename(command).toLowerCase() === "node9") return true;
23966
+ if (PACKAGE_RUNNERS.has(import_path48.default.basename(command).toLowerCase())) {
23967
+ return args.some((a) => a === "node9" || import_path48.default.basename(a).toLowerCase() === "node9");
23968
+ }
23969
+ return false;
23970
+ }
23971
+ var MAX_CONFIG_BYTES = 10 * 1024 * 1024;
23972
+ function readServers(file, format, agent) {
23973
+ try {
23974
+ const stat = import_fs47.default.statSync(file);
23975
+ if (!stat.isFile() || stat.size > MAX_CONFIG_BYTES) return [];
23976
+ const text = import_fs47.default.readFileSync(file, "utf8");
23977
+ const map = format === "toml" ? (0, import_smol_toml3.parse)(text)?.mcp_servers : JSON.parse(text)?.mcpServers;
23978
+ if (!map || typeof map !== "object") return [];
23979
+ return Object.entries(map).map(([name, v]) => ({
23980
+ name,
23981
+ command: v?.command,
23982
+ args: Array.isArray(v?.args) ? v.args : void 0,
23983
+ agent
23984
+ }));
23985
+ } catch {
23986
+ return [];
23987
+ }
23988
+ }
23989
+ function checkSupplyChain(ctx) {
23990
+ const home = ctx.home || import_os42.default.homedir();
23991
+ const servers = [];
23992
+ for (const spec of AGENT_SPECS) {
23993
+ if (!spec.mcpFile) continue;
23994
+ servers.push(...readServers(spec.mcpFile(home), spec.mcpFormat ?? "json", spec.label));
23995
+ }
23996
+ if (servers.length === 0) return [];
23997
+ const findings = [];
23998
+ const unmanaged = servers.filter((s) => s.command && !isNode9Managed(s.command, s.args));
23999
+ const suspect = unmanaged.filter(
24000
+ (s) => checkProvenance(s.command, ctx.cwd).trustLevel === "suspect"
24001
+ );
24002
+ if (suspect.length > 0) {
24003
+ findings.push({
24004
+ category: "Supply chain",
24005
+ severity: "high",
24006
+ title: `${suspect.length} MCP server${suspect.length === 1 ? "" : "s"} launched from an untrusted path`,
24007
+ what: "An MCP tool-server runs from an untrusted location.",
24008
+ why: "Its binary lives in /tmp or a world-writable directory.",
24009
+ who: "Anything on the machine could swap that binary for malware the agent then runs.",
24010
+ detail: suspect.map((s) => `${s.name} \u2192 ${s.command} (${s.agent})`),
24011
+ owner: "node9",
24012
+ fix: "node9 can pin + provenance-check MCP servers before they run."
24013
+ });
24014
+ }
24015
+ if (unmanaged.length > 0) {
24016
+ findings.push({
24017
+ category: "Supply chain",
24018
+ severity: "medium",
24019
+ title: `${unmanaged.length} of ${servers.length} MCP server${servers.length === 1 ? "" : "s"} run outside node9`,
24020
+ what: "Some MCP tool-servers run without node9 watching their tool calls.",
24021
+ why: "They're launched directly, not wrapped by node9.",
24022
+ who: "A poisoned or silently-updated server could act freely (tool-poisoning / rug-pull).",
24023
+ detail: unmanaged.slice(0, 5).map((s) => `${s.name} (${s.agent})`),
24024
+ owner: "node9",
24025
+ fix: "node9 can wrap MCP servers so every tool call is gated + pinned."
24026
+ });
24027
+ }
24028
+ return findings;
24029
+ }
24030
+
24031
+ // src/posture/privilege.ts
24032
+ init_policy();
24033
+ var SUDO_PROBE = "sudo chmod 777 /etc/passwd";
24034
+ async function checkPrivilege(ctx) {
24035
+ const findings = [];
24036
+ const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
24037
+ const isRoot = uid === 0;
24038
+ if (isRoot) {
24039
+ findings.push({
24040
+ category: "Privilege",
24041
+ severity: "high",
24042
+ title: "Running as root",
24043
+ what: "The agent process is running as root (full system rights).",
24044
+ why: "It was started as uid 0.",
24045
+ who: "One bad command can change any file, user, or service on the machine.",
24046
+ detail: [],
24047
+ owner: "node9",
24048
+ fix: "node9 can block privileged commands (sudo, system-path writes) in-path."
24049
+ });
24050
+ }
24051
+ const verdict = await evaluatePolicy2("Bash", { command: SUDO_PROBE }, ctx.agent, ctx.cwd);
24052
+ if (verdict.decision !== "block") {
24053
+ findings.push({
24054
+ category: "Privilege",
24055
+ severity: isRoot ? "high" : "medium",
24056
+ title: "Privilege escalation is not gated",
24057
+ what: "node9 isn't gating `sudo`.",
24058
+ why: "No sudo rule is active in the current policy.",
24059
+ // Calibrated: don't claim the agent CAN become root — it depends on sudo config.
24060
+ who: "If `sudo` is passwordless (NOPASSWD), an agent could become root; with a password prompt the risk is lower.",
24061
+ detail: [],
24062
+ fix: "node9 can gate sudo / privilege-escalation in-path.",
24063
+ // Coverage probes the real policy: block OR review = gated (covered).
24064
+ owner: "node9",
24065
+ coverageProbe: { kind: "command", command: SUDO_PROBE }
24066
+ });
24067
+ }
24068
+ return findings;
24069
+ }
24070
+
24071
+ // src/posture/containment.ts
24072
+ var import_fs48 = __toESM(require("fs"));
24073
+ function inContainer() {
24074
+ if (import_fs48.default.existsSync("/.dockerenv") || import_fs48.default.existsSync("/run/.containerenv")) return true;
24075
+ try {
24076
+ const cgroup = import_fs48.default.readFileSync("/proc/1/cgroup", "utf8");
24077
+ if (/docker|kubepods|containerd|lxc|libpod/.test(cgroup)) return true;
24078
+ } catch {
24079
+ }
24080
+ return false;
24081
+ }
24082
+ function checkContainment(_ctx) {
24083
+ if (inContainer()) return [];
24084
+ return [
24085
+ {
24086
+ category: "Isolation",
24087
+ severity: "advisory",
24088
+ title: "Running directly on the host \u2014 no container",
24089
+ what: "The agent runs loose on your whole machine, not in a sandbox.",
24090
+ why: "It's started on the bare host, not inside a container or VM.",
24091
+ who: "If it gets tricked, the damage reaches every file and program \u2014 not one room.",
24092
+ detail: [],
24093
+ owner: "os",
24094
+ node9Reduces: true,
24095
+ fix: "node9 can shrink the blast radius without a container \u2014 you keep every tool:\n \u2022 node9 shield enable project-jail \u2014 block credential reads\n \u2022 node9 egress lock \u2014 block data exfil\nA container/VM adds full isolation, but you lose host access.",
24096
+ coverageProbe: { kind: "cantFix" }
24097
+ }
24098
+ ];
24099
+ }
24100
+
24101
+ // src/posture/inbound.ts
24102
+ var import_fs49 = __toESM(require("fs"));
24103
+ var KNOWN_SERVICE_PORTS = {
24104
+ 5432: "PostgreSQL",
24105
+ 6379: "Redis",
24106
+ 3306: "MySQL/MariaDB",
24107
+ 27017: "MongoDB",
24108
+ 9200: "Elasticsearch",
24109
+ 11211: "Memcached",
24110
+ 5672: "RabbitMQ",
24111
+ 9092: "Kafka",
24112
+ 2379: "etcd",
24113
+ 8086: "InfluxDB"
24114
+ };
24115
+ var KNOWN_SERVICE_COMMS = {
24116
+ postgres: "PostgreSQL",
24117
+ "redis-server": "Redis",
24118
+ mysqld: "MySQL",
24119
+ mariadbd: "MariaDB",
24120
+ mongod: "MongoDB"
24121
+ };
24122
+ var DB_LABEL = /PostgreSQL|Redis|MySQL|MariaDB|MongoDB/;
24123
+ var SHIELD_FOR_SERVICE = {
24124
+ PostgreSQL: {
24125
+ shield: "postgres",
24126
+ blocks: "DROP TABLE / TRUNCATE",
24127
+ rebind: "PostgreSQL \u2192 listen_addresses='localhost'"
24128
+ },
24129
+ Redis: { shield: "redis", blocks: "FLUSHALL / FLUSHDB", rebind: "Redis \u2192 bind 127.0.0.1" }
24130
+ };
24131
+ function buildNetworkFix(labels) {
24132
+ const shielded = [
24133
+ ...new Map(
24134
+ labels.map((label2) => {
24135
+ const key = Object.keys(SHIELD_FOR_SERVICE).find((k) => label2.includes(k));
24136
+ return key ? SHIELD_FOR_SERVICE[key] : null;
24137
+ }).filter((s) => s !== null).map((s) => [s.shield, s])
24138
+ ).values()
24139
+ ];
24140
+ if (shielded.length === 0) {
24141
+ return {
24142
+ fix: "Bind to 127.0.0.1 or firewall the port; node9 gates the agent, not the socket.",
24143
+ reduces: false
24144
+ };
24145
+ }
24146
+ const protectLines = shielded.map((s) => ` \u2022 node9 shield enable ${s.shield} \u2014 blocks ${s.blocks}`).join("\n");
24147
+ const rebindLines = shielded.map((s) => ` \u2022 ${s.rebind}`).join("\n");
24148
+ return {
24149
+ fix: "Protect the agent now \u2014 node9 blocks destructive DB ops:\n" + protectLines + "\nClose the port to other machines (your part):\n" + rebindLines + "\n \u2022 or firewall the port",
24150
+ reduces: true
24151
+ };
24152
+ }
24153
+ function parseListeners(procText) {
24154
+ const out = [];
24155
+ for (const line of procText.split("\n").slice(1)) {
24156
+ const cols = line.trim().split(/\s+/);
24157
+ if (cols.length < 10) continue;
24158
+ if (cols[3] !== "0A") continue;
24159
+ const local = cols[1];
24160
+ const sep = local.lastIndexOf(":");
24161
+ if (sep < 0) continue;
24162
+ const addrHex = local.slice(0, sep);
24163
+ const port = parseInt(local.slice(sep + 1), 16);
24164
+ if (!/^0+$/.test(addrHex) || !Number.isFinite(port)) continue;
24165
+ out.push({ port, inode: cols[9] });
24166
+ }
24167
+ return out;
24168
+ }
24169
+ function tiesToAgent(proc, agentName) {
24170
+ const needle = agentName.trim().toLowerCase();
24171
+ if (needle.length < 4) return false;
24172
+ const escaped = needle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24173
+ const boundary = new RegExp(`(^|[^a-z0-9])${escaped}([^a-z0-9]|$)`);
24174
+ return boundary.test(proc.comm.toLowerCase()) || boundary.test(proc.cmdline.toLowerCase());
24175
+ }
24176
+ function classifyListener(port, proc, agentName) {
24177
+ if (agentName && proc && tiesToAgent(proc, agentName)) {
24178
+ return { kind: "agent", label: `${proc.comm} on :${port}` };
24179
+ }
24180
+ const service = KNOWN_SERVICE_PORTS[port] ?? (proc ? KNOWN_SERVICE_COMMS[proc.comm] : void 0);
24181
+ if (service) return { kind: "service", label: `${service} on :${port}` };
24182
+ return { kind: "unknown", label: `${proc?.comm || "unknown process"} on :${port}` };
24183
+ }
24184
+ function collectListeners() {
24185
+ const byPort = /* @__PURE__ */ new Map();
24186
+ for (const file of ["/proc/net/tcp", "/proc/net/tcp6"]) {
24187
+ try {
24188
+ for (const l of parseListeners(import_fs49.default.readFileSync(file, "utf8"))) {
24189
+ if (!byPort.has(l.port)) byPort.set(l.port, l);
24190
+ }
24191
+ } catch {
24192
+ }
24193
+ }
24194
+ return [...byPort.values()].sort((a, b) => a.port - b.port);
24195
+ }
24196
+ function readProc(pid) {
24197
+ let comm = "unknown";
24198
+ let cmdline = "";
24199
+ try {
24200
+ comm = import_fs49.default.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
24201
+ } catch {
24202
+ }
24203
+ try {
24204
+ cmdline = import_fs49.default.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
24205
+ } catch {
24206
+ }
24207
+ return { comm, cmdline };
24208
+ }
24209
+ function resolveProcesses(inodes) {
24210
+ const map = /* @__PURE__ */ new Map();
24211
+ if (inodes.size === 0) return map;
24212
+ let pids;
24213
+ try {
24214
+ pids = import_fs49.default.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
24215
+ } catch {
24216
+ return map;
24217
+ }
24218
+ for (const pid of pids) {
24219
+ let fds;
24220
+ try {
24221
+ fds = import_fs49.default.readdirSync(`/proc/${pid}/fd`);
24222
+ } catch {
24223
+ continue;
24224
+ }
24225
+ for (const fd of fds) {
24226
+ let link;
24227
+ try {
24228
+ link = import_fs49.default.readlinkSync(`/proc/${pid}/fd/${fd}`);
24229
+ } catch {
24230
+ continue;
24231
+ }
24232
+ const m = /^socket:\[(\d+)\]$/.exec(link);
24233
+ if (m && inodes.has(m[1]) && !map.has(m[1])) {
24234
+ map.set(m[1], readProc(pid));
24235
+ }
24236
+ }
24237
+ if (map.size === inodes.size) break;
24238
+ }
24239
+ return map;
24240
+ }
24241
+ function checkInbound(ctx) {
24242
+ const listeners = collectListeners();
24243
+ if (listeners.length === 0) return [];
24244
+ const procByInode = resolveProcesses(new Set(listeners.map((l) => l.inode)));
24245
+ const classified = listeners.map((l) => ({
24246
+ port: l.port,
24247
+ ...classifyListener(l.port, procByInode.get(l.inode) ?? null, ctx.agent)
24248
+ }));
24249
+ const findings = [];
24250
+ const agentPorts = classified.filter((c) => c.kind === "agent");
24251
+ if (agentPorts.length > 0) {
24252
+ findings.push({
24253
+ category: "Agent inbound",
24254
+ severity: "advisory",
24255
+ title: `Your agent is reachable on 0.0.0.0 (port${agentPorts.length === 1 ? "" : "s"} ${agentPorts.map((a) => a.port).join(", ")})`,
24256
+ what: "Your agent itself is listening for incoming network connections.",
24257
+ why: "It's bound to 0.0.0.0, so other devices on the network can reach it.",
24258
+ who: "Anyone who can reach the port could send it instructions (pilot it). Confirm it requires an auth token.",
24259
+ detail: agentPorts.map((a) => a.label),
24260
+ owner: "os",
24261
+ fix: "Bind the agent port to 127.0.0.1, or require an auth token on inbound requests.",
24262
+ coverageProbe: { kind: "cantFix" }
24263
+ });
24264
+ }
24265
+ const exposed = classified.filter((c) => c.kind !== "agent");
24266
+ if (exposed.length > 0) {
24267
+ const hasDb = exposed.some((e) => DB_LABEL.test(e.label));
24268
+ const { fix, reduces } = buildNetworkFix(exposed.map((e) => e.label));
24269
+ findings.push({
24270
+ category: "Network exposure",
24271
+ severity: "advisory",
24272
+ title: `${exposed.length} service${exposed.length === 1 ? "" : "s"} reachable on 0.0.0.0`,
24273
+ what: "These services accept connections from your whole network, not just this laptop.",
24274
+ why: "They listen on 0.0.0.0 (all interfaces) instead of 127.0.0.1 (this machine only).",
24275
+ // Calibrated: 0.0.0.0 = your local network (WiFi), not the public internet
24276
+ // unless the box has a public IP.
24277
+ who: "Other devices on your network (e.g. your WiFi) can connect \u2014 usually not the whole internet unless this box has a public IP." + (hasDb ? " An open, unauthenticated database is a direct data-theft path." : ""),
24278
+ detail: exposed.map((e) => e.label),
24279
+ owner: "os",
24280
+ // Only when node9 actually has a shield for an exposed service — otherwise
24281
+ // (bare dev servers) it stays purely the user's to rebind.
24282
+ node9Reduces: reduces,
24283
+ fix,
24284
+ coverageProbe: { kind: "cantFix" }
24285
+ });
24286
+ }
24287
+ return findings;
24288
+ }
24289
+
24290
+ // src/posture/coverage.ts
24291
+ var import_os43 = __toESM(require("os"));
24292
+ init_config();
24293
+ function checkCoverage(ctx) {
24294
+ const home = ctx.home || import_os43.default.homedir();
24295
+ const findings = [];
24296
+ const protectedAgents = getAgentWiring(home).filter((r) => r.isProtected);
24297
+ if (protectedAgents.length === 0) {
24298
+ findings.push({
24299
+ category: "Coverage",
24300
+ severity: "critical",
24301
+ title: "node9 is not in-path for any agent",
24302
+ what: "node9 isn't actually in the loop for any agent on this machine.",
24303
+ why: "No agent has node9 hooks or MCP wired in.",
24304
+ who: "Everything else here is unenforced \u2014 node9 can only report, not block.",
24305
+ detail: [],
24306
+ owner: "node9",
24307
+ fix: "Run `node9 init` to put node9 in-path for your agents."
24308
+ });
24309
+ return findings;
24310
+ }
24311
+ const mode = getConfig(ctx.cwd).settings.mode;
24312
+ if (mode === "observe" || mode === "audit") {
24313
+ findings.push({
24314
+ category: "Coverage",
24315
+ severity: "high",
24316
+ title: `node9 is in ${mode} mode \u2014 watching, not blocking`,
24317
+ what: "node9 is watching but not actually blocking anything.",
24318
+ why: `It's in ${mode} mode, which logs risky actions but lets them through.`,
24319
+ who: "The guardrails above are observed, not enforced.",
24320
+ detail: [],
24321
+ owner: "node9",
24322
+ fix: "Set mode to `standard` (or `strict`) to enforce in-path."
24323
+ });
24324
+ }
24325
+ return findings;
24326
+ }
24327
+
24328
+ // src/posture/score.ts
24329
+ init_dist();
24330
+ function scorePosture(findings, checksRun) {
24331
+ const open = findings.filter(
24332
+ (f) => f.coverage?.state !== "covered" && f.coverage?.state !== "cant-fix"
24333
+ );
24334
+ const count = (sev) => open.filter((f) => f.severity === sev).length;
24335
+ return computeSecurityScore({
24336
+ critical: count("critical"),
24337
+ high: count("high"),
24338
+ medium: count("medium"),
24339
+ // Denominator = number of checks evaluated. With computeSecurityScore's
24340
+ // caps this makes any critical → critical tier, any high → at-risk, and a
24341
+ // fully clean run (0 findings, checksRun > 0) → 100/good.
24342
+ total: Math.max(checksRun, 1)
24343
+ });
24344
+ }
24345
+
24346
+ // src/posture/headline.ts
24347
+ var SEVERITY_RANK = {
24348
+ critical: 0,
24349
+ high: 1,
24350
+ medium: 2,
24351
+ advisory: 3
24352
+ };
24353
+ function worstFinding(findings) {
24354
+ return [...findings].sort((a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity])[0];
24355
+ }
24356
+ function deriveHeadline(allFindings) {
24357
+ const findings = allFindings.filter(
24358
+ (f) => f.coverage?.state !== "covered" && f.coverage?.state !== "cant-fix"
24359
+ );
24360
+ if (findings.length === 0 || findings.every((f) => f.severity === "advisory")) return null;
24361
+ const has = (category) => findings.some((f) => f.category === category);
24362
+ const secrets = has("Secrets");
24363
+ const egressOpen = has("Egress");
24364
+ const noIsolation = has("Isolation");
24365
+ const gateWeak = has("Approval gate");
24366
+ const notWired = findings.some((f) => f.category === "Coverage" && f.severity === "critical");
24367
+ const observeOnly = findings.some((f) => f.category === "Coverage" && f.severity === "high");
24368
+ let risk;
24369
+ if (secrets && egressOpen) {
24370
+ risk = "An agent on this host can read the credentials on this box and send them to any host" + (noIsolation ? ", and there is no container around it" : "") + ". One poisoned input \u2014 a malicious file, or a prompt-injection in a page it reads \u2014 is all it takes.";
24371
+ } else if (secrets) {
24372
+ risk = "An agent on this host can read the credentials on this box" + (noIsolation ? " with no sandbox around it" : "") + ". A single poisoned instruction would expose those keys.";
24373
+ } else if (egressOpen && gateWeak) {
24374
+ risk = "An agent here can run unrestricted commands and reach any host \u2014 an open path for a poisoned instruction to exfiltrate data or damage the box.";
24375
+ } else if (egressOpen) {
24376
+ risk = "An agent here can reach any host on the internet \u2014 an open exfiltration path the moment it is compromised.";
24377
+ } else if (gateWeak) {
24378
+ risk = "Destructive commands are not reliably blocked here \u2014 an agent given a bad instruction could damage the box.";
24379
+ } else {
24380
+ risk = worstFinding(findings)?.title ?? "Review the findings below.";
24381
+ }
24382
+ let action;
24383
+ if (notWired) {
24384
+ action = "Run `node9 init` \u2014 node9 is not in-path yet, so nothing here is enforced.";
24385
+ } else if (observeOnly) {
24386
+ action = "Switch node9 to enforcing mode \u2014 right now it is only watching, not blocking.";
24387
+ } else if (egressOpen) {
24388
+ action = "lock egress to an allowlist (node9 can enforce it) \u2014 it closes the exit the exfiltration needs.";
24389
+ } else if (secrets) {
24390
+ action = "node9 can block reads of sensitive paths (~/.ssh, ~/.aws) in-path.";
24391
+ } else if (gateWeak) {
24392
+ action = "node9 can enforce destructive-command blocking in-path.";
24393
+ } else {
24394
+ action = worstFinding(findings)?.fix ?? "Review the findings below.";
24395
+ }
24396
+ return { risk, action };
24397
+ }
24398
+
24399
+ // src/posture/enforcement.ts
24400
+ init_dlp();
24401
+ init_policy();
24402
+ init_config();
24403
+ function egressCoverage(env) {
24404
+ if (env.enforcing && env.egressBlocking) {
24405
+ return { state: "covered", level: "block", via: "node9 egress" };
24406
+ }
24407
+ if (env.enforcing && env.egressReviewing) {
24408
+ return { state: "covered", level: "review", via: "node9 egress" };
24409
+ }
24410
+ return { state: "open" };
24411
+ }
24412
+ function coverageFromVerdict(verdict, env, via) {
24413
+ if (!env.enforcing) return { state: "open" };
24414
+ if (verdict === "block") return { state: "covered", level: "block", via };
24415
+ if (verdict === "review") return { state: "covered", level: "review", via };
24416
+ return { state: "open" };
24417
+ }
24418
+ function viaFromRule(ruleName) {
24419
+ if (!ruleName) return void 0;
24420
+ const m = /^shield:([^:]+):/.exec(ruleName);
24421
+ return m ? `${m[1]} shield` : void 0;
24422
+ }
24423
+ async function annotateCoverage(findings, ctx) {
24424
+ const config = getConfig(ctx.cwd);
24425
+ const mode = config.settings.mode;
24426
+ const wired = getAgentWiring(ctx.home).some((r) => r.isProtected);
24427
+ const env = {
24428
+ enforcing: wired && mode !== "observe" && mode !== "audit",
24429
+ egressBlocking: config.policy.egress.enabled && config.policy.egress.mode === "block",
24430
+ egressReviewing: config.policy.egress.enabled && config.policy.egress.mode === "review"
24431
+ };
24432
+ for (const f of findings) {
24433
+ const probe = f.coverageProbe;
24434
+ if (!probe) continue;
24435
+ if (probe.kind === "cantFix") {
24436
+ f.coverage = { state: "cant-fix" };
24437
+ continue;
24438
+ }
24439
+ if (probe.kind === "egress") {
24440
+ f.coverage = egressCoverage(env);
24441
+ continue;
24442
+ }
24443
+ if (probe.kind === "fileRead") {
24444
+ const verdicts = probe.paths.map((p) => scanFilePath(p)?.severity ?? null);
24445
+ if (verdicts.length === 0 || verdicts.some((v) => v === null)) {
24446
+ f.coverage = coverageFromVerdict("allow", env);
24447
+ } else {
24448
+ const worst = verdicts.some((v) => v === "review") ? "review" : "block";
24449
+ f.coverage = coverageFromVerdict(worst, env, "node9 DLP");
24450
+ }
24451
+ continue;
24452
+ }
24453
+ const verdict = await evaluatePolicy2("Bash", { command: probe.command }, ctx.agent, ctx.cwd);
24454
+ f.coverage = coverageFromVerdict(
24455
+ verdict.decision,
24456
+ env,
24457
+ viaFromRule(verdict.ruleName)
24458
+ );
24459
+ }
24460
+ }
24461
+
24462
+ // src/posture/index.ts
24463
+ var POSTURE_CHECKS = [
24464
+ { category: "Secrets", run: checkSecrets },
24465
+ { category: "Egress", run: checkEgress },
24466
+ { category: "Approval gate", run: checkGate },
24467
+ { category: "Supply chain", run: checkSupplyChain },
24468
+ { category: "Privilege", run: checkPrivilege },
24469
+ { category: "Isolation", run: checkContainment },
24470
+ { category: "Inbound", run: checkInbound },
24471
+ { category: "Coverage", run: checkCoverage }
24472
+ ];
24473
+ function dropEnforcementRedundant(findings) {
24474
+ const coveragePresent = findings.some((f) => f.category === "Coverage");
24475
+ if (!coveragePresent) return findings;
24476
+ return findings.filter((f) => !(f.redundantWhenOpen && f.coverage?.state === "open"));
24477
+ }
24478
+ async function runChecks(checks, ctx) {
24479
+ const findings = [];
24480
+ const passedCategories = [];
24481
+ const erroredCategories = [];
24482
+ for (const check of checks) {
24483
+ try {
24484
+ const result = await check.run(ctx);
24485
+ if (result.length === 0) passedCategories.push(check.category);
24486
+ else findings.push(...result);
24487
+ } catch (err2) {
24488
+ erroredCategories.push(check.category);
24489
+ if (process.env.NODE9_DEBUG) {
24490
+ console.error(`[posture] check "${check.category}" failed:`, err2?.message);
24491
+ }
24492
+ }
24493
+ }
24494
+ return { findings, passedCategories, erroredCategories };
24495
+ }
24496
+ async function runPosture(opts = {}) {
24497
+ const ctx = {
24498
+ home: opts.home ?? import_os44.default.homedir(),
24499
+ cwd: opts.cwd ?? process.cwd(),
24500
+ agent: opts.agent
24501
+ };
24502
+ const {
24503
+ findings: rawFindings,
24504
+ passedCategories,
24505
+ erroredCategories
24506
+ } = await runChecks(POSTURE_CHECKS, ctx);
24507
+ await annotateCoverage(rawFindings, ctx);
24508
+ const findings = dropEnforcementRedundant(rawFindings);
24509
+ const { score, tier } = scorePosture(findings, POSTURE_CHECKS.length);
24510
+ return {
24511
+ agent: opts.agent ? `${opts.agent} on this host` : "agent on this host",
24512
+ findings,
24513
+ passedCategories,
24514
+ erroredCategories,
24515
+ headline: deriveHeadline(findings),
24516
+ score,
24517
+ tier,
24518
+ checksRun: POSTURE_CHECKS.length
24519
+ };
24520
+ }
24521
+
24522
+ // src/posture/render.ts
24523
+ var import_chalk24 = __toESM(require("chalk"));
24524
+ var ICON = {
24525
+ critical: import_chalk24.default.red("\u274C"),
24526
+ high: import_chalk24.default.red("\u274C"),
24527
+ medium: import_chalk24.default.yellow("\u26A0\uFE0F "),
24528
+ advisory: import_chalk24.default.gray("\u26A0\uFE0F ")
24529
+ };
24530
+ var TIER_LABEL = {
24531
+ good: import_chalk24.default.green("Good"),
24532
+ "at-risk": import_chalk24.default.yellow("At risk"),
24533
+ critical: import_chalk24.default.red("Critical")
24534
+ };
24535
+ function wrap(text, width) {
24536
+ const out = [];
24537
+ let cur = "";
24538
+ for (const word of text.split(" ")) {
24539
+ if (cur && (cur + " " + word).length > width) {
24540
+ out.push(cur);
24541
+ cur = word;
24542
+ } else {
24543
+ cur = cur ? cur + " " + word : word;
24544
+ }
24545
+ }
24546
+ if (cur) out.push(cur);
24547
+ return out;
24548
+ }
24549
+ var LABEL_WIDTH = 14;
24550
+ function label(category) {
24551
+ return import_chalk24.default.bold(category.padEnd(Math.max(LABEL_WIDTH, category.length + 1)));
24552
+ }
24553
+ function renderFinding(f) {
24554
+ const lines = [];
24555
+ lines.push(` ${ICON[f.severity]} ${label(f.category)}${f.title}`);
24556
+ const indent = " ".repeat(2 + 3 + LABEL_WIDTH);
24557
+ const width = 80 - indent.length;
24558
+ for (const s of [f.what, f.why, f.who]) {
24559
+ if (s) for (const l of wrap(s, width)) lines.push(indent + import_chalk24.default.gray(l));
24560
+ }
24561
+ for (const d of f.detail) lines.push(indent + import_chalk24.default.gray(d));
24562
+ if (f.fix) {
24563
+ let first = true;
24564
+ for (const seg of f.fix.split("\n")) {
24565
+ for (const l of wrap(seg, width - 2)) {
24566
+ lines.push(indent + import_chalk24.default.cyan(first ? "\u2192 " + l : " " + l));
24567
+ first = false;
24568
+ }
24569
+ }
24570
+ }
24571
+ return lines;
24572
+ }
24573
+ function renderPosture(result) {
24574
+ const lines = [];
24575
+ const tier = TIER_LABEL[result.tier];
24576
+ lines.push("");
24577
+ lines.push(
24578
+ import_chalk24.default.cyan.bold(`\u{1F6E1}\uFE0F Node9 Posture`) + import_chalk24.default.gray(` \u2014 ${result.agent}`) + ` ${import_chalk24.default.bold(`Score: ${result.score}/100`)} (${tier})`
24579
+ );
24580
+ const advisories = result.findings.filter(
24581
+ (f) => f.severity === "advisory" && f.coverage?.state !== "covered"
24582
+ ).length;
24583
+ if (advisories > 0) {
24584
+ const word = advisories === 1 ? "advisory" : "advisories";
24585
+ const verb = advisories === 1 ? "doesn't" : "don't";
24586
+ lines.push(
24587
+ " " + import_chalk24.default.gray(
24588
+ `${advisories} ${word} below ${verb} affect the score \u2014 OS-level exposure node9 can't enforce, yours to weigh.`
24589
+ )
24590
+ );
24591
+ }
24592
+ lines.push("");
24593
+ if (result.headline) {
24594
+ const indent = " ";
24595
+ lines.push(` ${import_chalk24.default.red.bold("\u{1F525} Biggest risk")}`);
24596
+ for (const l of wrap(result.headline.risk, 74)) lines.push(indent + import_chalk24.default.white(l));
24597
+ const action = wrap(`Do this first: ${result.headline.action}`, 72);
24598
+ action.forEach((l, i) => lines.push(indent + import_chalk24.default.cyan(i === 0 ? "\u2192 " + l : " " + l)));
24599
+ lines.push("");
24600
+ }
24601
+ const covered = result.findings.filter((f) => f.coverage?.state === "covered");
24602
+ const open = result.findings.filter((f) => f.coverage?.state !== "covered");
24603
+ if (covered.length > 0) {
24604
+ lines.push(" " + import_chalk24.default.green("\u{1F7E2} node9 is already protecting you"));
24605
+ for (const f of covered) {
24606
+ const gated = f.coverage?.level === "review" ? "approval-gating" : "blocking";
24607
+ const via = f.coverage?.via ?? "node9";
24608
+ lines.push(
24609
+ ` ${import_chalk24.default.green("\u2705")} ${label(f.category)}${import_chalk24.default.gray(`${via} is ${gated} this`)}`
24610
+ );
24611
+ }
24612
+ lines.push("");
24613
+ }
24614
+ const node9Open = open.filter((f) => f.owner === "node9");
24615
+ const reduceOpen = open.filter((f) => f.owner !== "node9" && f.node9Reduces);
24616
+ const osOpen = open.filter((f) => f.owner !== "node9" && !f.node9Reduces);
24617
+ if (node9Open.length > 0) {
24618
+ lines.push(" " + import_chalk24.default.cyan.bold("\u{1F527} node9 can fix these \u2014 run the command"));
24619
+ for (const f of node9Open) lines.push(...renderFinding(f));
24620
+ }
24621
+ if (reduceOpen.length > 0) {
24622
+ if (node9Open.length > 0) lines.push("");
24623
+ lines.push(
24624
+ " " + import_chalk24.default.yellow.bold("\u{1F512} node9 reduces these \u2014 run the command, the rest is yours")
24625
+ );
24626
+ for (const f of reduceOpen) lines.push(...renderFinding(f));
24627
+ }
24628
+ if (osOpen.length > 0) {
24629
+ if (node9Open.length > 0 || reduceOpen.length > 0) lines.push("");
24630
+ lines.push(" " + import_chalk24.default.bold("\u{1F9F1} Only you can fix these \u2014 node9 can't"));
24631
+ for (const f of osOpen) lines.push(...renderFinding(f));
24632
+ }
24633
+ for (const cat of result.passedCategories) {
24634
+ lines.push(` ${import_chalk24.default.green("\u2705")} ${label(cat)}${import_chalk24.default.gray("no issues found")}`);
24635
+ }
24636
+ for (const cat of result.erroredCategories) {
24637
+ lines.push(` ${import_chalk24.default.gray("\u2022")} ${label(cat)}${import_chalk24.default.gray("could not be checked")}`);
24638
+ }
24639
+ lines.push("");
24640
+ const crit = open.filter((f) => f.severity === "critical").length;
24641
+ const high = open.filter((f) => f.severity === "high").length;
24642
+ const med = open.filter((f) => f.severity === "medium").length;
24643
+ const adv = open.filter((f) => f.severity === "advisory").length;
24644
+ const parts = [];
24645
+ if (crit) parts.push(import_chalk24.default.red(`${crit} critical`));
24646
+ if (high) parts.push(import_chalk24.default.red(`${high} high`));
24647
+ if (med) parts.push(import_chalk24.default.yellow(`${med} medium`));
24648
+ if (adv) parts.push(import_chalk24.default.gray(`${adv} advisory`));
24649
+ const summary = parts.length ? parts.join(" \xB7 ") : import_chalk24.default.green("no findings");
24650
+ lines.push(` ${summary} \xB7 ${import_chalk24.default.gray("track your fleet at app.node9.ai/posture")}`);
24651
+ lines.push("");
24652
+ return lines.join("\n");
24653
+ }
24654
+
24655
+ // src/posture/ship.ts
24656
+ var import_http2 = __toESM(require("http"));
24657
+ var import_https5 = __toESM(require("https"));
24658
+ var import_url = require("url");
24659
+ function buildShipBody(result) {
24660
+ return {
24661
+ score: result.score,
24662
+ tier: result.tier,
24663
+ agent: result.agent,
24664
+ headline: result.headline,
24665
+ // { risk, action } | null — both safe strings
24666
+ findings: result.findings.map((f) => ({
24667
+ category: f.category,
24668
+ severity: f.severity,
24669
+ title: f.title,
24670
+ // Coverage state so the SaaS counts OPEN-only (matching the local score).
24671
+ // A non-sensitive enum — no values or paths. Default 'open' if unannotated.
24672
+ coverage: f.coverage?.state ?? "open",
24673
+ // Plain-language parity with the CLI report. Prose only, no paths.
24674
+ what: f.what,
24675
+ why: f.why,
24676
+ who: f.who,
24677
+ // The runnable fix / OS action — commands + advice, never a path.
24678
+ fix: f.fix,
24679
+ // Whose job it is. Default 'os' so the SaaS never falsely claims node9 can fix it.
24680
+ owner: f.owner ?? "os"
24681
+ }))
24682
+ };
24683
+ }
24684
+ function postureUrlFrom(apiUrl) {
24685
+ return apiUrl.endsWith("/policies/sync") ? apiUrl.replace(/\/policies\/sync$/, "/posture/report") : null;
24686
+ }
24687
+ async function shipPosture(result, creds) {
24688
+ const url = postureUrlFrom(creds.apiUrl);
24689
+ if (!url) return false;
24690
+ const body = JSON.stringify(buildShipBody(result));
24691
+ const parsed = new import_url.URL(url);
24692
+ const transport = parsed.protocol === "http:" ? import_http2.default : import_https5.default;
24693
+ return new Promise((resolve) => {
24694
+ const req = transport.request(
24695
+ {
24696
+ hostname: parsed.hostname,
24697
+ port: parsed.port ? parseInt(parsed.port, 10) : void 0,
24698
+ path: parsed.pathname + parsed.search,
24699
+ method: "POST",
24700
+ headers: {
24701
+ "Content-Type": "application/json",
24702
+ "Content-Length": Buffer.byteLength(body),
24703
+ Authorization: `Bearer ${creds.apiKey}`
24704
+ },
24705
+ timeout: 1e4
24706
+ },
24707
+ (res) => {
24708
+ const ok2 = !!res.statusCode && res.statusCode >= 200 && res.statusCode < 300;
24709
+ res.resume();
24710
+ res.on("end", () => resolve(ok2));
24711
+ res.on("error", () => resolve(false));
24712
+ }
24713
+ );
24714
+ req.on("error", () => resolve(false));
24715
+ req.on("timeout", () => {
24716
+ req.destroy();
24717
+ resolve(false);
24718
+ });
24719
+ req.write(body);
24720
+ req.end();
24721
+ });
24722
+ }
24723
+
24724
+ // src/cli/commands/posture.ts
24725
+ init_sync();
24726
+ function registerPostureCommand(program2) {
24727
+ program2.command("posture").description("Security scorecard for the agent on this host (secrets, egress, gate)").option("--agent <name>", "label / policy scope for the agent being graded").option("--json", "emit the raw result as JSON instead of the scorecard").option("--ship", "send a redacted snapshot to your node9 dashboard").action(async (opts) => {
24728
+ const result = await runPosture({ agent: opts.agent });
24729
+ if (opts.json) {
24730
+ console.log(JSON.stringify(result, null, 2));
24731
+ } else {
24732
+ console.log(renderPosture(result));
24733
+ }
24734
+ if (opts.ship) {
24735
+ const creds = readCredentials();
24736
+ if (!creds) {
24737
+ console.error(import_chalk25.default.gray(" Run `node9 login` to ship this to your dashboard."));
24738
+ } else {
24739
+ const ok2 = await shipPosture(result, creds);
24740
+ console.error(
24741
+ ok2 ? import_chalk25.default.gray(" \u2713 Shipped to your node9 dashboard.") : import_chalk25.default.gray(" Could not reach the dashboard \u2014 saved locally only.")
24742
+ );
24743
+ }
24744
+ }
24745
+ if (result.tier === "critical") process.exitCode = 2;
24746
+ });
24747
+ }
24748
+
24749
+ // src/cli/commands/egress.ts
24750
+ var import_chalk26 = __toESM(require("chalk"));
24751
+ var import_fs50 = __toESM(require("fs"));
24752
+ var import_os45 = __toESM(require("os"));
24753
+ var import_path49 = __toESM(require("path"));
24754
+ init_config();
24755
+ init_dist();
24756
+ var DEFAULT_EGRESS = {
24757
+ enabled: false,
24758
+ mode: "review",
24759
+ allow: [],
24760
+ deny: [],
24761
+ allowPrivate: true
24762
+ };
24763
+ function configPath() {
24764
+ return import_path49.default.join(import_os45.default.homedir(), ".node9", "config.json");
24765
+ }
24766
+ function readRawConfig() {
24767
+ let text;
24768
+ try {
24769
+ text = import_fs50.default.readFileSync(configPath(), "utf8");
24770
+ } catch (err2) {
24771
+ if (err2.code === "ENOENT") return {};
24772
+ throw err2;
24773
+ }
24774
+ try {
24775
+ return JSON.parse(text);
24776
+ } catch {
24777
+ throw new Error(
24778
+ `${configPath()} is not valid JSON \u2014 fix it before changing egress (refusing to overwrite).`
24779
+ );
24780
+ }
24781
+ }
24782
+ function writeRawConfig(config) {
24783
+ const p = configPath();
24784
+ import_fs50.default.mkdirSync(import_path49.default.dirname(p), { recursive: true });
24785
+ import_fs50.default.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
24786
+ }
24787
+ function applyEgress(config, change) {
24788
+ const policy = config.policy = config.policy ?? {};
24789
+ const existing = policy.egress ?? {};
24790
+ policy.egress = { ...DEFAULT_EGRESS, ...existing, ...change };
24791
+ return config;
24792
+ }
24793
+ function withConfig(fn) {
24794
+ let config;
24795
+ try {
24796
+ config = readRawConfig();
24797
+ } catch (err2) {
24798
+ console.error(import_chalk26.default.red(`
24799
+ \u2717 ${err2.message}
24800
+ `));
24801
+ process.exitCode = 1;
24802
+ return false;
24803
+ }
24804
+ fn(config);
24805
+ writeRawConfig(config);
24806
+ return true;
24807
+ }
24808
+ function mutate(change) {
24809
+ return withConfig((config) => applyEgress(config, change));
24810
+ }
24811
+ function addHost(list, host) {
24812
+ return withConfig((config) => {
24813
+ const existing = config.policy?.egress ?? {};
24814
+ const current = { ...DEFAULT_EGRESS, ...existing };
24815
+ const updated = current[list].includes(host) ? current[list] : [...current[list], host];
24816
+ applyEgress(config, { [list]: updated });
24817
+ });
24818
+ }
24819
+ function showStatus() {
24820
+ const e = getConfig().policy.egress;
24821
+ const state = !e.enabled ? import_chalk26.default.red("OFF \u2014 your agent can reach any host") : e.mode === "block" ? import_chalk26.default.green("LOCKED (block) \u2014 unknown hosts are denied") : import_chalk26.default.yellow("WATCHING (review) \u2014 unknown hosts prompt you");
24822
+ console.log(import_chalk26.default.cyan.bold("\n\u{1F310} Egress control"));
24823
+ console.log(" State: " + state);
24824
+ console.log(
24825
+ import_chalk26.default.gray(
24826
+ ` ${DEFAULT_EGRESS_ALLOWLIST.length} common dev/LLM hosts are always allowed (github, npm, pypi, anthropic, \u2026).`
24827
+ )
24828
+ );
24829
+ if (e.allow.length) console.log(" Your allow: " + e.allow.join(", "));
24830
+ if (e.deny.length) console.log(" Your deny: " + e.deny.join(", "));
24831
+ if (!e.enabled) {
24832
+ console.log(import_chalk26.default.gray("\n Turn it on: node9 egress watch (prompt on unknown hosts)"));
24833
+ console.log(import_chalk26.default.gray(" node9 egress lock (hard-block unknown hosts)"));
24834
+ }
24835
+ console.log("");
24836
+ }
24837
+ function registerEgressCommand(program2) {
24838
+ const egress = program2.command("egress").description("Control where your agent can send data (egress allowlist)");
24839
+ egress.command("watch").description("Prompt before the agent reaches an unknown host (review mode)").action(() => {
24840
+ if (!mutate({ enabled: true, mode: "review" })) return;
24841
+ console.log(import_chalk26.default.green("\n\u2713 Egress is now watched (review mode)."));
24842
+ console.log(
24843
+ import_chalk26.default.gray(" Routine hosts (LLM APIs, package registries, localhost) are allowed.")
24844
+ );
24845
+ console.log(
24846
+ import_chalk26.default.gray(" An unknown host will prompt you \u2014 run `node9 egress lock` to hard-block.\n")
24847
+ );
24848
+ });
24849
+ egress.command("lock").description("Block the agent from reaching unknown hosts (block mode)").action(() => {
24850
+ if (!mutate({ enabled: true, mode: "block" })) return;
24851
+ console.log(import_chalk26.default.green("\n\u2713 Egress is now locked (block mode)."));
24852
+ console.log(import_chalk26.default.gray(" Routine hosts are still allowed; unknown hosts are denied."));
24853
+ console.log(import_chalk26.default.gray(" Allow a specific host with `node9 egress allow <host>`.\n"));
24854
+ });
24855
+ egress.command("allow <host>").description("Allow an extra host (glob, e.g. *.mycorp.com)").action((host) => {
24856
+ if (!addHost("allow", host)) return;
24857
+ console.log(import_chalk26.default.green(`
24858
+ \u2713 Allowed egress to ${host}.
24859
+ `));
24860
+ });
24861
+ egress.command("deny <host>").description("Block an extra host (deny always wins)").action((host) => {
24862
+ if (!addHost("deny", host)) return;
24863
+ console.log(import_chalk26.default.green(`
24864
+ \u2713 Denied egress to ${host}.
24865
+ `));
24866
+ });
24867
+ egress.command("off").description("Turn egress control off").action(() => {
24868
+ if (!mutate({ enabled: false })) return;
24869
+ console.log(
24870
+ import_chalk26.default.yellow("\n\u2713 Egress control is off \u2014 the agent can reach any host again.\n")
24871
+ );
24872
+ });
24873
+ egress.action(showStatus);
24874
+ }
24875
+
24876
+ // src/cli/commands/sessions.ts
24877
+ var import_chalk27 = __toESM(require("chalk"));
24878
+ var import_fs51 = __toESM(require("fs"));
24879
+ var import_path50 = __toESM(require("path"));
24880
+ var import_os46 = __toESM(require("os"));
23751
24881
  init_scan_summary();
23752
24882
  init_litellm();
23753
24883
  init_cost_gemini();
@@ -23768,10 +24898,10 @@ function encodeProjectPath(projectPath) {
23768
24898
  }
23769
24899
  function sessionJsonlPath(projectPath, sessionId) {
23770
24900
  const encoded = encodeProjectPath(projectPath);
23771
- return import_path47.default.join(import_os41.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
24901
+ return import_path50.default.join(import_os46.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
23772
24902
  }
23773
24903
  function projectLabel(projectPath) {
23774
- return projectPath.replace(import_os41.default.homedir(), "~");
24904
+ return projectPath.replace(import_os46.default.homedir(), "~");
23775
24905
  }
23776
24906
  function parseHistoryLines(lines) {
23777
24907
  const entries = [];
@@ -23840,10 +24970,10 @@ function parseSessionLines(lines) {
23840
24970
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
23841
24971
  }
23842
24972
  function loadAuditEntries(auditPath) {
23843
- const aPath = auditPath ?? import_path47.default.join(import_os41.default.homedir(), ".node9", "audit.log");
24973
+ const aPath = auditPath ?? import_path50.default.join(import_os46.default.homedir(), ".node9", "audit.log");
23844
24974
  let raw;
23845
24975
  try {
23846
- raw = import_fs46.default.readFileSync(aPath, "utf-8");
24976
+ raw = import_fs51.default.readFileSync(aPath, "utf-8");
23847
24977
  } catch {
23848
24978
  return [];
23849
24979
  }
@@ -23879,8 +25009,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
23879
25009
  return result;
23880
25010
  }
23881
25011
  function buildGeminiSessions(days, allAuditEntries) {
23882
- const tmpDir = import_path47.default.join(import_os41.default.homedir(), ".gemini", "tmp");
23883
- if (!import_fs46.default.existsSync(tmpDir)) return [];
25012
+ const tmpDir = import_path50.default.join(import_os46.default.homedir(), ".gemini", "tmp");
25013
+ if (!import_fs51.default.existsSync(tmpDir)) return [];
23884
25014
  const cutoff = days !== null ? (() => {
23885
25015
  const d = /* @__PURE__ */ new Date();
23886
25016
  d.setDate(d.getDate() - days);
@@ -23889,35 +25019,35 @@ function buildGeminiSessions(days, allAuditEntries) {
23889
25019
  })() : null;
23890
25020
  let slugDirs;
23891
25021
  try {
23892
- slugDirs = import_fs46.default.readdirSync(tmpDir);
25022
+ slugDirs = import_fs51.default.readdirSync(tmpDir);
23893
25023
  } catch {
23894
25024
  return [];
23895
25025
  }
23896
25026
  const summaries = [];
23897
25027
  for (const slug of slugDirs) {
23898
- const slugPath = import_path47.default.join(tmpDir, slug);
25028
+ const slugPath = import_path50.default.join(tmpDir, slug);
23899
25029
  try {
23900
- if (!import_fs46.default.statSync(slugPath).isDirectory()) continue;
25030
+ if (!import_fs51.default.statSync(slugPath).isDirectory()) continue;
23901
25031
  } catch {
23902
25032
  continue;
23903
25033
  }
23904
- let projectRoot = import_path47.default.join(import_os41.default.homedir(), slug);
25034
+ let projectRoot = import_path50.default.join(import_os46.default.homedir(), slug);
23905
25035
  try {
23906
- projectRoot = import_fs46.default.readFileSync(import_path47.default.join(slugPath, ".project_root"), "utf-8").trim();
25036
+ projectRoot = import_fs51.default.readFileSync(import_path50.default.join(slugPath, ".project_root"), "utf-8").trim();
23907
25037
  } catch {
23908
25038
  }
23909
- const chatsDir = import_path47.default.join(slugPath, "chats");
23910
- if (!import_fs46.default.existsSync(chatsDir)) continue;
25039
+ const chatsDir = import_path50.default.join(slugPath, "chats");
25040
+ if (!import_fs51.default.existsSync(chatsDir)) continue;
23911
25041
  let chatFiles;
23912
25042
  try {
23913
- chatFiles = import_fs46.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
25043
+ chatFiles = import_fs51.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
23914
25044
  } catch {
23915
25045
  continue;
23916
25046
  }
23917
25047
  for (const chatFile of chatFiles) {
23918
25048
  let raw;
23919
25049
  try {
23920
- raw = import_fs46.default.readFileSync(import_path47.default.join(chatsDir, chatFile), "utf-8");
25050
+ raw = import_fs51.default.readFileSync(import_path50.default.join(chatsDir, chatFile), "utf-8");
23921
25051
  } catch {
23922
25052
  continue;
23923
25053
  }
@@ -23997,8 +25127,8 @@ function buildGeminiSessions(days, allAuditEntries) {
23997
25127
  return summaries;
23998
25128
  }
23999
25129
  function buildCodexSessions(days, allAuditEntries) {
24000
- const sessionsBase = import_path47.default.join(import_os41.default.homedir(), ".codex", "sessions");
24001
- if (!import_fs46.default.existsSync(sessionsBase)) return [];
25130
+ const sessionsBase = import_path50.default.join(import_os46.default.homedir(), ".codex", "sessions");
25131
+ if (!import_fs51.default.existsSync(sessionsBase)) return [];
24002
25132
  const cutoff = days !== null ? (() => {
24003
25133
  const d = /* @__PURE__ */ new Date();
24004
25134
  d.setDate(d.getDate() - days);
@@ -24007,29 +25137,29 @@ function buildCodexSessions(days, allAuditEntries) {
24007
25137
  })() : null;
24008
25138
  const jsonlFiles = [];
24009
25139
  try {
24010
- for (const year of import_fs46.default.readdirSync(sessionsBase)) {
24011
- const yearPath = import_path47.default.join(sessionsBase, year);
25140
+ for (const year of import_fs51.default.readdirSync(sessionsBase)) {
25141
+ const yearPath = import_path50.default.join(sessionsBase, year);
24012
25142
  try {
24013
- if (!import_fs46.default.statSync(yearPath).isDirectory()) continue;
25143
+ if (!import_fs51.default.statSync(yearPath).isDirectory()) continue;
24014
25144
  } catch {
24015
25145
  continue;
24016
25146
  }
24017
- for (const month of import_fs46.default.readdirSync(yearPath)) {
24018
- const monthPath = import_path47.default.join(yearPath, month);
25147
+ for (const month of import_fs51.default.readdirSync(yearPath)) {
25148
+ const monthPath = import_path50.default.join(yearPath, month);
24019
25149
  try {
24020
- if (!import_fs46.default.statSync(monthPath).isDirectory()) continue;
25150
+ if (!import_fs51.default.statSync(monthPath).isDirectory()) continue;
24021
25151
  } catch {
24022
25152
  continue;
24023
25153
  }
24024
- for (const day of import_fs46.default.readdirSync(monthPath)) {
24025
- const dayPath = import_path47.default.join(monthPath, day);
25154
+ for (const day of import_fs51.default.readdirSync(monthPath)) {
25155
+ const dayPath = import_path50.default.join(monthPath, day);
24026
25156
  try {
24027
- if (!import_fs46.default.statSync(dayPath).isDirectory()) continue;
25157
+ if (!import_fs51.default.statSync(dayPath).isDirectory()) continue;
24028
25158
  } catch {
24029
25159
  continue;
24030
25160
  }
24031
- for (const file of import_fs46.default.readdirSync(dayPath)) {
24032
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path47.default.join(dayPath, file));
25161
+ for (const file of import_fs51.default.readdirSync(dayPath)) {
25162
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path50.default.join(dayPath, file));
24033
25163
  }
24034
25164
  }
24035
25165
  }
@@ -24041,7 +25171,7 @@ function buildCodexSessions(days, allAuditEntries) {
24041
25171
  for (const filePath of jsonlFiles) {
24042
25172
  let lines;
24043
25173
  try {
24044
- lines = import_fs46.default.readFileSync(filePath, "utf-8").split("\n");
25174
+ lines = import_fs51.default.readFileSync(filePath, "utf-8").split("\n");
24045
25175
  } catch {
24046
25176
  continue;
24047
25177
  }
@@ -24127,10 +25257,10 @@ function buildCodexSessions(days, allAuditEntries) {
24127
25257
  return summaries;
24128
25258
  }
24129
25259
  function buildSessions(days, historyPath) {
24130
- const hPath = historyPath ?? import_path47.default.join(import_os41.default.homedir(), ".claude", "history.jsonl");
25260
+ const hPath = historyPath ?? import_path50.default.join(import_os46.default.homedir(), ".claude", "history.jsonl");
24131
25261
  let historyRaw = "";
24132
25262
  try {
24133
- historyRaw = import_fs46.default.readFileSync(hPath, "utf-8");
25263
+ historyRaw = import_fs51.default.readFileSync(hPath, "utf-8");
24134
25264
  } catch {
24135
25265
  }
24136
25266
  const cutoff = days !== null ? (() => {
@@ -24154,7 +25284,7 @@ function buildSessions(days, historyPath) {
24154
25284
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
24155
25285
  let sessionLines = [];
24156
25286
  try {
24157
- sessionLines = import_fs46.default.readFileSync(jsonlFile, "utf-8").split("\n");
25287
+ sessionLines = import_fs51.default.readFileSync(jsonlFile, "utf-8").split("\n");
24158
25288
  } catch {
24159
25289
  }
24160
25290
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -24240,11 +25370,11 @@ function toolInputSummary(tool, input) {
24240
25370
  }
24241
25371
  function toolColor(tool) {
24242
25372
  const t = tool.toLowerCase();
24243
- if (t === "bash" || t === "execute_bash") return import_chalk24.default.red;
24244
- if (t === "write") return import_chalk24.default.green;
24245
- if (t === "edit" || t === "notebookedit") return import_chalk24.default.yellow;
24246
- if (t === "read") return import_chalk24.default.cyan;
24247
- return import_chalk24.default.gray;
25373
+ if (t === "bash" || t === "execute_bash") return import_chalk27.default.red;
25374
+ if (t === "write") return import_chalk27.default.green;
25375
+ if (t === "edit" || t === "notebookedit") return import_chalk27.default.yellow;
25376
+ if (t === "read") return import_chalk27.default.cyan;
25377
+ return import_chalk27.default.gray;
24248
25378
  }
24249
25379
  function barStr2(value, max, width) {
24250
25380
  if (max === 0 || width <= 0) return "\u2591".repeat(width);
@@ -24254,7 +25384,7 @@ function barStr2(value, max, width) {
24254
25384
  function colorBar2(value, max, width) {
24255
25385
  const s = barStr2(value, max, width);
24256
25386
  const filled = Math.max(1, Math.round(max > 0 ? value / max * width : 0));
24257
- return import_chalk24.default.cyan(s.slice(0, filled)) + import_chalk24.default.dim(s.slice(filled));
25387
+ return import_chalk27.default.cyan(s.slice(0, filled)) + import_chalk27.default.dim(s.slice(filled));
24258
25388
  }
24259
25389
  function renderSummary(summaries) {
24260
25390
  const totalTools = summaries.reduce((n, s) => n + s.toolCalls.length, 0);
@@ -24284,45 +25414,45 @@ function renderSummary(summaries) {
24284
25414
  }
24285
25415
  const topProjects = [...projCosts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
24286
25416
  const W = 20;
24287
- console.log(import_chalk24.default.dim(" " + "\u2500".repeat(70)));
25417
+ console.log(import_chalk27.default.dim(" " + "\u2500".repeat(70)));
24288
25418
  console.log(
24289
- " " + import_chalk24.default.bold.white(String(summaries.length).padEnd(4)) + import_chalk24.default.dim("sessions ") + import_chalk24.default.bold.yellow(fmtCost3(totalCost).padEnd(10)) + import_chalk24.default.dim("total ") + import_chalk24.default.bold.white(String(totalTools).padEnd(6)) + import_chalk24.default.dim("tool calls ") + import_chalk24.default.bold.white(String(totalFiles)) + import_chalk24.default.dim(" files modified") + (totalBlocked > 0 ? import_chalk24.default.dim(" ") + import_chalk24.default.red.bold(String(totalBlocked)) + import_chalk24.default.dim(" blocked by node9") : "")
25419
+ " " + import_chalk27.default.bold.white(String(summaries.length).padEnd(4)) + import_chalk27.default.dim("sessions ") + import_chalk27.default.bold.yellow(fmtCost3(totalCost).padEnd(10)) + import_chalk27.default.dim("total ") + import_chalk27.default.bold.white(String(totalTools).padEnd(6)) + import_chalk27.default.dim("tool calls ") + import_chalk27.default.bold.white(String(totalFiles)) + import_chalk27.default.dim(" files modified") + (totalBlocked > 0 ? import_chalk27.default.dim(" ") + import_chalk27.default.red.bold(String(totalBlocked)) + import_chalk27.default.dim(" blocked by node9") : "")
24290
25420
  );
24291
25421
  console.log(
24292
- " " + import_chalk24.default.dim("avg ") + import_chalk24.default.white(fmtCost3(avgCost).padEnd(10)) + import_chalk24.default.dim("/session ") + import_chalk24.default.green(String(snapshots)) + import_chalk24.default.dim(` of ${summaries.length} sessions had snapshots`)
25422
+ " " + import_chalk27.default.dim("avg ") + import_chalk27.default.white(fmtCost3(avgCost).padEnd(10)) + import_chalk27.default.dim("/session ") + import_chalk27.default.green(String(snapshots)) + import_chalk27.default.dim(` of ${summaries.length} sessions had snapshots`)
24293
25423
  );
24294
25424
  console.log("");
24295
- console.log(" " + import_chalk24.default.dim("Tool breakdown:"));
25425
+ console.log(" " + import_chalk27.default.dim("Tool breakdown:"));
24296
25426
  const maxGroup = Math.max(...Object.values(groups));
24297
- for (const [label, count] of Object.entries(groups)) {
25427
+ for (const [label2, count] of Object.entries(groups)) {
24298
25428
  if (count === 0) continue;
24299
25429
  const pct = totalTools > 0 ? Math.round(count / totalTools * 100) : 0;
24300
25430
  console.log(
24301
- " " + label.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " + import_chalk24.default.white(String(count).padStart(4)) + import_chalk24.default.dim(` (${String(pct)}%)`)
25431
+ " " + label2.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " + import_chalk27.default.white(String(count).padStart(4)) + import_chalk27.default.dim(` (${String(pct)}%)`)
24302
25432
  );
24303
25433
  }
24304
25434
  console.log("");
24305
25435
  if (topProjects.length > 1) {
24306
- console.log(" " + import_chalk24.default.dim("Cost by project:"));
25436
+ console.log(" " + import_chalk27.default.dim("Cost by project:"));
24307
25437
  const maxProjCost = topProjects[0][1];
24308
25438
  for (const [proj, cost] of topProjects) {
24309
25439
  console.log(
24310
- " " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " + import_chalk24.default.yellow(fmtCost3(cost))
25440
+ " " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " + import_chalk27.default.yellow(fmtCost3(cost))
24311
25441
  );
24312
25442
  }
24313
25443
  console.log("");
24314
25444
  }
24315
- console.log(import_chalk24.default.dim(" " + "\u2500".repeat(70)));
25445
+ console.log(import_chalk27.default.dim(" " + "\u2500".repeat(70)));
24316
25446
  console.log("");
24317
25447
  }
24318
25448
  function renderList(summaries, totalCost) {
24319
25449
  if (summaries.length === 0) {
24320
- console.log(import_chalk24.default.yellow(" No sessions found in the requested range.\n"));
25450
+ console.log(import_chalk27.default.yellow(" No sessions found in the requested range.\n"));
24321
25451
  return;
24322
25452
  }
24323
- const totalLabel = totalCost > 0 ? import_chalk24.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
25453
+ const totalLabel = totalCost > 0 ? import_chalk27.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
24324
25454
  console.log(
24325
- " " + import_chalk24.default.white(String(summaries.length)) + import_chalk24.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
25455
+ " " + import_chalk27.default.white(String(summaries.length)) + import_chalk27.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
24326
25456
  );
24327
25457
  console.log("");
24328
25458
  let lastGroup = "";
@@ -24330,51 +25460,51 @@ function renderList(summaries, totalCost) {
24330
25460
  const activeDate = fmtDate2(s.lastActiveTime);
24331
25461
  const group = activeDate + " " + s.projectLabel;
24332
25462
  if (group !== lastGroup) {
24333
- console.log(import_chalk24.default.dim(" \u2500\u2500\u2500 ") + import_chalk24.default.bold(activeDate) + import_chalk24.default.dim(" " + s.projectLabel));
25463
+ console.log(import_chalk27.default.dim(" \u2500\u2500\u2500 ") + import_chalk27.default.bold(activeDate) + import_chalk27.default.dim(" " + s.projectLabel));
24334
25464
  lastGroup = group;
24335
25465
  }
24336
25466
  const startDate = fmtDate2(s.startTime);
24337
- const dateRange = startDate !== activeDate ? import_chalk24.default.dim(" (" + startDate + " \u2192 " + activeDate + ")") : "";
24338
- const timeStr = import_chalk24.default.dim(fmtTime(s.startTime));
24339
- const prompt = import_chalk24.default.white(truncate(s.firstPrompt.replace(/\n/g, " "), 50).padEnd(50));
24340
- const tools = s.toolCalls.length > 0 ? import_chalk24.default.dim(String(s.toolCalls.length).padStart(3) + " tools") : import_chalk24.default.dim(" 0 tools");
24341
- const cost = s.costUSD > 0 ? import_chalk24.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
24342
- const blocked = s.blockedCalls.length > 0 ? import_chalk24.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
24343
- const snap = s.hasSnapshot ? import_chalk24.default.green(" \u{1F4F8}") : "";
24344
- const agentBadge = import_chalk24.default[agentColorName(s.agent ?? "claude")](
25467
+ const dateRange = startDate !== activeDate ? import_chalk27.default.dim(" (" + startDate + " \u2192 " + activeDate + ")") : "";
25468
+ const timeStr = import_chalk27.default.dim(fmtTime(s.startTime));
25469
+ const prompt = import_chalk27.default.white(truncate(s.firstPrompt.replace(/\n/g, " "), 50).padEnd(50));
25470
+ const tools = s.toolCalls.length > 0 ? import_chalk27.default.dim(String(s.toolCalls.length).padStart(3) + " tools") : import_chalk27.default.dim(" 0 tools");
25471
+ const cost = s.costUSD > 0 ? import_chalk27.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
25472
+ const blocked = s.blockedCalls.length > 0 ? import_chalk27.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
25473
+ const snap = s.hasSnapshot ? import_chalk27.default.green(" \u{1F4F8}") : "";
25474
+ const agentBadge = import_chalk27.default[agentColorName(s.agent ?? "claude")](
24345
25475
  " " + agentBadgeText(s.agent ?? "claude", 0)
24346
25476
  );
24347
- const sid = import_chalk24.default.dim(" " + s.sessionId.slice(0, 8));
25477
+ const sid = import_chalk27.default.dim(" " + s.sessionId.slice(0, 8));
24348
25478
  console.log(
24349
25479
  ` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
24350
25480
  );
24351
25481
  }
24352
25482
  console.log("");
24353
25483
  console.log(
24354
- import_chalk24.default.dim(" Run") + " " + import_chalk24.default.cyan("node9 sessions --detail <session-id>") + import_chalk24.default.dim(" for full tool trace.")
25484
+ import_chalk27.default.dim(" Run") + " " + import_chalk27.default.cyan("node9 sessions --detail <session-id>") + import_chalk27.default.dim(" for full tool trace.")
24355
25485
  );
24356
25486
  console.log("");
24357
25487
  }
24358
25488
  function renderDetail(s) {
24359
25489
  console.log("");
24360
- console.log(import_chalk24.default.bold(" Session ") + import_chalk24.default.dim(s.sessionId));
25490
+ console.log(import_chalk27.default.bold(" Session ") + import_chalk27.default.dim(s.sessionId));
24361
25491
  console.log(
24362
- import_chalk24.default.bold(" Prompt ") + import_chalk24.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
25492
+ import_chalk27.default.bold(" Prompt ") + import_chalk27.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
24363
25493
  );
24364
- console.log(import_chalk24.default.bold(" Project ") + import_chalk24.default.white(s.projectLabel));
25494
+ console.log(import_chalk27.default.bold(" Project ") + import_chalk27.default.white(s.projectLabel));
24365
25495
  if (s.agent) {
24366
- const agentLabel2 = import_chalk24.default[agentColorName(s.agent)](agentDisplayName(s.agent));
24367
- console.log(import_chalk24.default.bold(" Agent ") + agentLabel2);
25496
+ const agentLabel2 = import_chalk27.default[agentColorName(s.agent)](agentDisplayName(s.agent));
25497
+ console.log(import_chalk27.default.bold(" Agent ") + agentLabel2);
24368
25498
  }
24369
- console.log(import_chalk24.default.bold(" When ") + import_chalk24.default.white(fmtDateTime(s.startTime)));
25499
+ console.log(import_chalk27.default.bold(" When ") + import_chalk27.default.white(fmtDateTime(s.startTime)));
24370
25500
  if (s.costUSD > 0)
24371
- console.log(import_chalk24.default.bold(" Cost ") + import_chalk24.default.yellow("~" + fmtCost3(s.costUSD)));
25501
+ console.log(import_chalk27.default.bold(" Cost ") + import_chalk27.default.yellow("~" + fmtCost3(s.costUSD)));
24372
25502
  console.log(
24373
- import_chalk24.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk24.default.green("\u2713 taken") : import_chalk24.default.dim("none"))
25503
+ import_chalk27.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk27.default.green("\u2713 taken") : import_chalk27.default.dim("none"))
24374
25504
  );
24375
25505
  console.log("");
24376
25506
  if (s.toolCalls.length === 0 && s.blockedCalls.length === 0) {
24377
- console.log(import_chalk24.default.dim(" No tool calls recorded.\n"));
25507
+ console.log(import_chalk27.default.dim(" No tool calls recorded.\n"));
24378
25508
  return;
24379
25509
  }
24380
25510
  const timeline = [
@@ -24387,32 +25517,32 @@ function renderDetail(s) {
24387
25517
  });
24388
25518
  const headerParts = [`Tool calls (${s.toolCalls.length})`];
24389
25519
  if (s.blockedCalls.length > 0)
24390
- headerParts.push(import_chalk24.default.red(`${s.blockedCalls.length} blocked by node9`));
24391
- console.log(import_chalk24.default.bold(" " + headerParts.join(" \xB7 ")));
25520
+ headerParts.push(import_chalk27.default.red(`${s.blockedCalls.length} blocked by node9`));
25521
+ console.log(import_chalk27.default.bold(" " + headerParts.join(" \xB7 ")));
24392
25522
  console.log("");
24393
25523
  for (const entry of timeline) {
24394
25524
  if (entry.kind === "tool") {
24395
25525
  const tc = entry.tc;
24396
25526
  const colorFn = toolColor(tc.tool);
24397
25527
  const toolPad = colorFn(tc.tool.padEnd(16));
24398
- const detail = import_chalk24.default.gray(truncate(toolInputSummary(tc.tool, tc.input), 70));
24399
- const ts = tc.timestamp ? import_chalk24.default.dim(fmtTime(tc.timestamp) + " ") : " ";
25528
+ const detail = import_chalk27.default.gray(truncate(toolInputSummary(tc.tool, tc.input), 70));
25529
+ const ts = tc.timestamp ? import_chalk27.default.dim(fmtTime(tc.timestamp) + " ") : " ";
24400
25530
  console.log(` ${ts}${toolPad} ${detail}`);
24401
25531
  } else {
24402
25532
  const bc = entry.bc;
24403
- const ts = bc.timestamp ? import_chalk24.default.dim(fmtTime(bc.timestamp) + " ") : " ";
24404
- const label = import_chalk24.default.red("\u{1F6D1} BLOCKED".padEnd(16));
24405
- const toolName = import_chalk24.default.red(bc.tool.padEnd(10));
24406
- const argsSummary = bc.args ? import_chalk24.default.gray(truncate(toolInputSummary(bc.tool, bc.args), 40)) : import_chalk24.default.dim("[args not logged]");
24407
- const reason = bc.checkedBy ? import_chalk24.default.dim(" \u2190 " + bc.checkedBy) : "";
24408
- console.log(` ${ts}${label} ${toolName} ${argsSummary}${reason}`);
25533
+ const ts = bc.timestamp ? import_chalk27.default.dim(fmtTime(bc.timestamp) + " ") : " ";
25534
+ const label2 = import_chalk27.default.red("\u{1F6D1} BLOCKED".padEnd(16));
25535
+ const toolName = import_chalk27.default.red(bc.tool.padEnd(10));
25536
+ const argsSummary = bc.args ? import_chalk27.default.gray(truncate(toolInputSummary(bc.tool, bc.args), 40)) : import_chalk27.default.dim("[args not logged]");
25537
+ const reason = bc.checkedBy ? import_chalk27.default.dim(" \u2190 " + bc.checkedBy) : "";
25538
+ console.log(` ${ts}${label2} ${toolName} ${argsSummary}${reason}`);
24409
25539
  }
24410
25540
  }
24411
25541
  console.log("");
24412
25542
  if (s.modifiedFiles.length > 0) {
24413
- console.log(import_chalk24.default.bold(` Files modified (${s.modifiedFiles.length}):`));
25543
+ console.log(import_chalk27.default.bold(` Files modified (${s.modifiedFiles.length}):`));
24414
25544
  for (const f of s.modifiedFiles) {
24415
- console.log(" " + import_chalk24.default.yellow(f));
25545
+ console.log(" " + import_chalk27.default.yellow(f));
24416
25546
  }
24417
25547
  console.log("");
24418
25548
  }
@@ -24420,13 +25550,13 @@ function renderDetail(s) {
24420
25550
  function registerSessionsCommand(program2) {
24421
25551
  program2.command("sessions").description("Show what your AI agent did \u2014 sessions, tool calls, cost, and file changes").option("--all", "Show all sessions (default: last 7 days)").option("--days <n>", "Show last N days of sessions", "7").option("--detail <sessionId>", "Show full tool trace for a session").action((options) => {
24422
25552
  console.log("");
24423
- console.log(import_chalk24.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk24.default.dim(" \u2014 what your AI agent did"));
25553
+ console.log(import_chalk27.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk27.default.dim(" \u2014 what your AI agent did"));
24424
25554
  console.log("");
24425
25555
  const days = options.detail || options.all ? null : Math.max(1, parseInt(options.days, 10) || 7);
24426
25556
  const rangeLabel = options.detail ? "all time" : options.all ? "all time" : `last ${String(days)} days`;
24427
- console.log(import_chalk24.default.dim(" " + rangeLabel));
25557
+ console.log(import_chalk27.default.dim(" " + rangeLabel));
24428
25558
  console.log("");
24429
- process.stdout.write(import_chalk24.default.dim(" Loading\u2026"));
25559
+ process.stdout.write(import_chalk27.default.dim(" Loading\u2026"));
24430
25560
  const summaries = buildSessions(days);
24431
25561
  if (process.stdout.isTTY) {
24432
25562
  process.stdout.clearLine(0);
@@ -24439,8 +25569,8 @@ function registerSessionsCommand(program2) {
24439
25569
  (s) => s.sessionId === options.detail || s.sessionId.startsWith(options.detail)
24440
25570
  );
24441
25571
  if (!target) {
24442
- console.log(import_chalk24.default.red(` Session not found: ${options.detail}`));
24443
- console.log(import_chalk24.default.dim(" Run `node9 sessions` to list recent sessions.\n"));
25572
+ console.log(import_chalk27.default.red(` Session not found: ${options.detail}`));
25573
+ console.log(import_chalk27.default.dim(" Run `node9 sessions` to list recent sessions.\n"));
24444
25574
  return;
24445
25575
  }
24446
25576
  renderDetail(target);
@@ -24453,13 +25583,13 @@ function registerSessionsCommand(program2) {
24453
25583
  }
24454
25584
 
24455
25585
  // src/cli/commands/skill-pin.ts
24456
- var import_chalk25 = __toESM(require("chalk"));
24457
- var import_fs47 = __toESM(require("fs"));
24458
- var import_os42 = __toESM(require("os"));
24459
- var import_path48 = __toESM(require("path"));
25586
+ var import_chalk28 = __toESM(require("chalk"));
25587
+ var import_fs52 = __toESM(require("fs"));
25588
+ var import_os47 = __toESM(require("os"));
25589
+ var import_path51 = __toESM(require("path"));
24460
25590
  function wipeSkillSessions() {
24461
25591
  try {
24462
- import_fs47.default.rmSync(import_path48.default.join(import_os42.default.homedir(), ".node9", "skill-sessions"), {
25592
+ import_fs52.default.rmSync(import_path51.default.join(import_os47.default.homedir(), ".node9", "skill-sessions"), {
24463
25593
  recursive: true,
24464
25594
  force: true
24465
25595
  });
@@ -24473,29 +25603,29 @@ function registerSkillPinCommand(program2) {
24473
25603
  const result = readSkillPinsSafe();
24474
25604
  if (!result.ok) {
24475
25605
  if (result.reason === "missing") {
24476
- console.log(import_chalk25.default.gray("\nNo skill roots are pinned yet."));
25606
+ console.log(import_chalk28.default.gray("\nNo skill roots are pinned yet."));
24477
25607
  console.log(
24478
- import_chalk25.default.gray("Pins are created automatically on the first tool call of each session.\n")
25608
+ import_chalk28.default.gray("Pins are created automatically on the first tool call of each session.\n")
24479
25609
  );
24480
25610
  return;
24481
25611
  }
24482
- console.error(import_chalk25.default.red(`
25612
+ console.error(import_chalk28.default.red(`
24483
25613
  \u274C Pin file is corrupt: ${result.detail}`));
24484
- console.error(import_chalk25.default.yellow(" Run: node9 skill pin reset\n"));
25614
+ console.error(import_chalk28.default.yellow(" Run: node9 skill pin reset\n"));
24485
25615
  process.exit(1);
24486
25616
  }
24487
25617
  const entries = Object.entries(result.pins.roots);
24488
25618
  if (entries.length === 0) {
24489
- console.log(import_chalk25.default.gray("\nNo skill roots are pinned yet.\n"));
25619
+ console.log(import_chalk28.default.gray("\nNo skill roots are pinned yet.\n"));
24490
25620
  return;
24491
25621
  }
24492
- console.log(import_chalk25.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
25622
+ console.log(import_chalk28.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
24493
25623
  for (const [key, entry] of entries) {
24494
- const missing = entry.exists ? "" : import_chalk25.default.yellow(" (not present at pin time)");
24495
- console.log(` ${import_chalk25.default.cyan(key)} ${import_chalk25.default.gray(entry.rootPath)}${missing}`);
25624
+ const missing = entry.exists ? "" : import_chalk28.default.yellow(" (not present at pin time)");
25625
+ console.log(` ${import_chalk28.default.cyan(key)} ${import_chalk28.default.gray(entry.rootPath)}${missing}`);
24496
25626
  console.log(` Files (${entry.fileCount})`);
24497
- console.log(` Hash: ${import_chalk25.default.gray(entry.contentHash.slice(0, 16))}...`);
24498
- console.log(` Pinned: ${import_chalk25.default.gray(entry.pinnedAt)}
25627
+ console.log(` Hash: ${import_chalk28.default.gray(entry.contentHash.slice(0, 16))}...`);
25628
+ console.log(` Pinned: ${import_chalk28.default.gray(entry.pinnedAt)}
24499
25629
  `);
24500
25630
  }
24501
25631
  });
@@ -24504,52 +25634,52 @@ function registerSkillPinCommand(program2) {
24504
25634
  try {
24505
25635
  pins = readSkillPins();
24506
25636
  } catch {
24507
- console.error(import_chalk25.default.red("\n\u274C Pin file is corrupt."));
24508
- console.error(import_chalk25.default.yellow(" Run: node9 skill pin reset\n"));
25637
+ console.error(import_chalk28.default.red("\n\u274C Pin file is corrupt."));
25638
+ console.error(import_chalk28.default.yellow(" Run: node9 skill pin reset\n"));
24509
25639
  process.exit(1);
24510
25640
  }
24511
25641
  if (!pins.roots[rootKey]) {
24512
- console.error(import_chalk25.default.red(`
25642
+ console.error(import_chalk28.default.red(`
24513
25643
  \u274C No pin found for root key "${rootKey}"
24514
25644
  `));
24515
- console.error(`Run ${import_chalk25.default.cyan("node9 skill pin list")} to see pinned roots.
25645
+ console.error(`Run ${import_chalk28.default.cyan("node9 skill pin list")} to see pinned roots.
24516
25646
  `);
24517
25647
  process.exit(1);
24518
25648
  }
24519
25649
  const rootPath = pins.roots[rootKey].rootPath;
24520
25650
  removePin2(rootKey);
24521
25651
  wipeSkillSessions();
24522
- console.log(import_chalk25.default.green(`
24523
- \u{1F513} Pin removed for ${import_chalk25.default.cyan(rootKey)}`));
24524
- console.log(import_chalk25.default.gray(` ${rootPath}`));
24525
- console.log(import_chalk25.default.gray(" Next session will re-pin with current state.\n"));
25652
+ console.log(import_chalk28.default.green(`
25653
+ \u{1F513} Pin removed for ${import_chalk28.default.cyan(rootKey)}`));
25654
+ console.log(import_chalk28.default.gray(` ${rootPath}`));
25655
+ console.log(import_chalk28.default.gray(" Next session will re-pin with current state.\n"));
24526
25656
  });
24527
25657
  pinSubCmd.command("reset").description("Clear all skill pins and wipe session verification flags").action(() => {
24528
25658
  const result = readSkillPinsSafe();
24529
25659
  if (!result.ok && result.reason === "missing") {
24530
25660
  wipeSkillSessions();
24531
- console.log(import_chalk25.default.gray("\nNo pins to clear.\n"));
25661
+ console.log(import_chalk28.default.gray("\nNo pins to clear.\n"));
24532
25662
  return;
24533
25663
  }
24534
25664
  const count = result.ok ? Object.keys(result.pins.roots).length : "?";
24535
25665
  clearAllPins2();
24536
25666
  wipeSkillSessions();
24537
- console.log(import_chalk25.default.green(`
25667
+ console.log(import_chalk28.default.green(`
24538
25668
  \u{1F513} Cleared ${count} skill pin(s).`));
24539
- console.log(import_chalk25.default.gray(" Next session will re-pin with current state.\n"));
25669
+ console.log(import_chalk28.default.gray(" Next session will re-pin with current state.\n"));
24540
25670
  });
24541
25671
  }
24542
25672
 
24543
25673
  // src/cli/commands/decisions.ts
24544
- var import_fs48 = __toESM(require("fs"));
24545
- var import_os43 = __toESM(require("os"));
24546
- var import_path49 = __toESM(require("path"));
24547
- var import_chalk26 = __toESM(require("chalk"));
24548
- var DECISIONS_FILE2 = import_path49.default.join(import_os43.default.homedir(), ".node9", "decisions.json");
25674
+ var import_fs53 = __toESM(require("fs"));
25675
+ var import_os48 = __toESM(require("os"));
25676
+ var import_path52 = __toESM(require("path"));
25677
+ var import_chalk29 = __toESM(require("chalk"));
25678
+ var DECISIONS_FILE2 = import_path52.default.join(import_os48.default.homedir(), ".node9", "decisions.json");
24549
25679
  function readDecisions() {
24550
25680
  try {
24551
- if (!import_fs48.default.existsSync(DECISIONS_FILE2)) return {};
24552
- const raw = import_fs48.default.readFileSync(DECISIONS_FILE2, "utf-8");
25681
+ if (!import_fs53.default.existsSync(DECISIONS_FILE2)) return {};
25682
+ const raw = import_fs53.default.readFileSync(DECISIONS_FILE2, "utf-8");
24553
25683
  const parsed = JSON.parse(raw);
24554
25684
  const out = {};
24555
25685
  for (const [k, v] of Object.entries(parsed)) {
@@ -24561,11 +25691,11 @@ function readDecisions() {
24561
25691
  }
24562
25692
  }
24563
25693
  function writeDecisions(d) {
24564
- const dir = import_path49.default.dirname(DECISIONS_FILE2);
24565
- if (!import_fs48.default.existsSync(dir)) import_fs48.default.mkdirSync(dir, { recursive: true });
25694
+ const dir = import_path52.default.dirname(DECISIONS_FILE2);
25695
+ if (!import_fs53.default.existsSync(dir)) import_fs53.default.mkdirSync(dir, { recursive: true });
24566
25696
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
24567
- import_fs48.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
24568
- import_fs48.default.renameSync(tmp, DECISIONS_FILE2);
25697
+ import_fs53.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
25698
+ import_fs53.default.renameSync(tmp, DECISIONS_FILE2);
24569
25699
  }
24570
25700
  function registerDecisionsCommand(program2) {
24571
25701
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -24573,67 +25703,67 @@ function registerDecisionsCommand(program2) {
24573
25703
  const decisions = readDecisions();
24574
25704
  const entries = Object.entries(decisions);
24575
25705
  if (entries.length === 0) {
24576
- console.log(import_chalk26.default.gray(" No persistent decisions stored."));
25706
+ console.log(import_chalk29.default.gray(" No persistent decisions stored."));
24577
25707
  console.log(
24578
- import_chalk26.default.gray(` File: ${DECISIONS_FILE2}
24579
- `) + import_chalk26.default.gray(' Decisions are written when you click "Always Allow" or')
25708
+ import_chalk29.default.gray(` File: ${DECISIONS_FILE2}
25709
+ `) + import_chalk29.default.gray(' Decisions are written when you click "Always Allow" or')
24580
25710
  );
24581
- console.log(import_chalk26.default.gray(' "Always Deny" in node9 tail or the native popup.'));
25711
+ console.log(import_chalk29.default.gray(' "Always Deny" in node9 tail or the native popup.'));
24582
25712
  return;
24583
25713
  }
24584
- console.log(import_chalk26.default.bold(`
25714
+ console.log(import_chalk29.default.bold(`
24585
25715
  Persistent decisions (${entries.length})
24586
25716
  `));
24587
25717
  const w = Math.max(...entries.map(([k]) => k.length));
24588
25718
  for (const [tool, verdict] of entries.sort()) {
24589
- const colored = verdict === "allow" ? import_chalk26.default.green(verdict) : import_chalk26.default.red(verdict);
25719
+ const colored = verdict === "allow" ? import_chalk29.default.green(verdict) : import_chalk29.default.red(verdict);
24590
25720
  console.log(` ${tool.padEnd(w)} ${colored}`);
24591
25721
  }
24592
25722
  console.log(
24593
- import_chalk26.default.gray(`
25723
+ import_chalk29.default.gray(`
24594
25724
  Stored in ${DECISIONS_FILE2}
24595
- `) + import_chalk26.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
25725
+ `) + import_chalk29.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
24596
25726
  );
24597
25727
  });
24598
25728
  cmd.command("clear <toolName>").description("Remove a persistent decision for one tool").action((toolName) => {
24599
25729
  const decisions = readDecisions();
24600
25730
  if (!(toolName in decisions)) {
24601
- console.log(import_chalk26.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
25731
+ console.log(import_chalk29.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
24602
25732
  process.exitCode = 1;
24603
25733
  return;
24604
25734
  }
24605
25735
  delete decisions[toolName];
24606
25736
  writeDecisions(decisions);
24607
- console.log(import_chalk26.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
25737
+ console.log(import_chalk29.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
24608
25738
  });
24609
25739
  cmd.command("clear-all").description("Remove every persistent decision (irreversible)").action(() => {
24610
25740
  const decisions = readDecisions();
24611
25741
  const count = Object.keys(decisions).length;
24612
25742
  if (count === 0) {
24613
- console.log(import_chalk26.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
25743
+ console.log(import_chalk29.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
24614
25744
  return;
24615
25745
  }
24616
25746
  writeDecisions({});
24617
25747
  console.log(
24618
- import_chalk26.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
25748
+ import_chalk29.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
24619
25749
  );
24620
25750
  });
24621
25751
  }
24622
25752
 
24623
25753
  // src/cli/commands/dlp.ts
24624
- var import_chalk27 = __toESM(require("chalk"));
24625
- var import_fs49 = __toESM(require("fs"));
24626
- var import_path50 = __toESM(require("path"));
24627
- var import_os44 = __toESM(require("os"));
24628
- var AUDIT_LOG = import_path50.default.join(import_os44.default.homedir(), ".node9", "audit.log");
24629
- var RESOLVED_FILE = import_path50.default.join(import_os44.default.homedir(), ".node9", "dlp-resolved.json");
25754
+ var import_chalk30 = __toESM(require("chalk"));
25755
+ var import_fs54 = __toESM(require("fs"));
25756
+ var import_path53 = __toESM(require("path"));
25757
+ var import_os49 = __toESM(require("os"));
25758
+ var AUDIT_LOG = import_path53.default.join(import_os49.default.homedir(), ".node9", "audit.log");
25759
+ var RESOLVED_FILE = import_path53.default.join(import_os49.default.homedir(), ".node9", "dlp-resolved.json");
24630
25760
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
24631
25761
  function stripAnsi(s) {
24632
25762
  return s.replace(ANSI_RE, "");
24633
25763
  }
24634
25764
  function loadResolved() {
24635
25765
  try {
24636
- const raw = JSON.parse(import_fs49.default.readFileSync(RESOLVED_FILE, "utf-8"));
25766
+ const raw = JSON.parse(import_fs54.default.readFileSync(RESOLVED_FILE, "utf-8"));
24637
25767
  return new Set(raw);
24638
25768
  } catch {
24639
25769
  return /* @__PURE__ */ new Set();
@@ -24641,13 +25771,13 @@ function loadResolved() {
24641
25771
  }
24642
25772
  function saveResolved(resolved) {
24643
25773
  try {
24644
- import_fs49.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
25774
+ import_fs54.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
24645
25775
  } catch {
24646
25776
  }
24647
25777
  }
24648
25778
  function loadDlpFindings() {
24649
- if (!import_fs49.default.existsSync(AUDIT_LOG)) return [];
24650
- return import_fs49.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
25779
+ if (!import_fs54.default.existsSync(AUDIT_LOG)) return [];
25780
+ return import_fs54.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
24651
25781
  if (!line.trim()) return [];
24652
25782
  try {
24653
25783
  const e = JSON.parse(line);
@@ -24676,14 +25806,14 @@ function registerDlpCommand(program2) {
24676
25806
  cmd.command("resolve").description("Mark all current DLP findings as resolved").action(() => {
24677
25807
  const findings = loadDlpFindings();
24678
25808
  if (findings.length === 0) {
24679
- console.log(import_chalk27.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
25809
+ console.log(import_chalk30.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
24680
25810
  return;
24681
25811
  }
24682
25812
  const resolved = loadResolved();
24683
25813
  for (const e of findings) resolved.add(entryKey2(e));
24684
25814
  saveResolved(resolved);
24685
25815
  console.log(
24686
- import_chalk27.default.green(
25816
+ import_chalk30.default.green(
24687
25817
  `
24688
25818
  \u2705 ${findings.length} finding${findings.length !== 1 ? "s" : ""} marked as resolved.
24689
25819
  `
@@ -24697,63 +25827,63 @@ function registerDlpCommand(program2) {
24697
25827
  const resolvedCount = findings.length - open.length;
24698
25828
  console.log("");
24699
25829
  console.log(
24700
- import_chalk27.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk27.default.dim(" \u2014 secrets found in Claude response text")
25830
+ import_chalk30.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk30.default.dim(" \u2014 secrets found in Claude response text")
24701
25831
  );
24702
25832
  console.log("");
24703
25833
  if (open.length === 0) {
24704
25834
  if (resolvedCount > 0) {
24705
- console.log(import_chalk27.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
25835
+ console.log(import_chalk30.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
24706
25836
  } else {
24707
25837
  console.log(
24708
- import_chalk27.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
25838
+ import_chalk30.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
24709
25839
  );
24710
25840
  }
24711
25841
  console.log("");
24712
25842
  return;
24713
25843
  }
24714
25844
  console.log(
24715
- import_chalk27.default.bgRed.white.bold(` \u26A0\uFE0F ${open.length} open finding${open.length !== 1 ? "s" : ""} `) + import_chalk27.default.dim(resolvedCount > 0 ? ` (${resolvedCount} resolved)` : "")
25845
+ import_chalk30.default.bgRed.white.bold(` \u26A0\uFE0F ${open.length} open finding${open.length !== 1 ? "s" : ""} `) + import_chalk30.default.dim(resolvedCount > 0 ? ` (${resolvedCount} resolved)` : "")
24716
25846
  );
24717
25847
  console.log("");
24718
25848
  console.log(
24719
- import_chalk27.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
25849
+ import_chalk30.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
24720
25850
  );
24721
- console.log(import_chalk27.default.dim(" Rotate each affected key immediately.\n"));
25851
+ console.log(import_chalk30.default.dim(" Rotate each affected key immediately.\n"));
24722
25852
  for (const e of open) {
24723
25853
  console.log(
24724
- " " + import_chalk27.default.red("\u25CF") + " " + import_chalk27.default.white(e.dlpPattern ?? "Secret") + import_chalk27.default.dim(" " + fmtDate3(e.ts))
25854
+ " " + import_chalk30.default.red("\u25CF") + " " + import_chalk30.default.white(e.dlpPattern ?? "Secret") + import_chalk30.default.dim(" " + fmtDate3(e.ts))
24725
25855
  );
24726
25856
  if (e.dlpSample) {
24727
- console.log(" " + import_chalk27.default.dim("Sample: ") + import_chalk27.default.yellow(stripAnsi(e.dlpSample)));
25857
+ console.log(" " + import_chalk30.default.dim("Sample: ") + import_chalk30.default.yellow(stripAnsi(e.dlpSample)));
24728
25858
  }
24729
25859
  if (e.project) {
24730
- console.log(" " + import_chalk27.default.dim("Project: ") + import_chalk27.default.dim(stripAnsi(e.project)));
25860
+ console.log(" " + import_chalk30.default.dim("Project: ") + import_chalk30.default.dim(stripAnsi(e.project)));
24731
25861
  }
24732
25862
  console.log("");
24733
25863
  }
24734
- console.log(" " + import_chalk27.default.bold("Next steps:"));
24735
- console.log(" " + import_chalk27.default.cyan("1.") + " Rotate any exposed keys shown above");
25864
+ console.log(" " + import_chalk30.default.bold("Next steps:"));
25865
+ console.log(" " + import_chalk30.default.cyan("1.") + " Rotate any exposed keys shown above");
24736
25866
  console.log(
24737
- " " + import_chalk27.default.cyan("2.") + " Run " + import_chalk27.default.white("node9 dlp resolve") + " to acknowledge"
25867
+ " " + import_chalk30.default.cyan("2.") + " Run " + import_chalk30.default.white("node9 dlp resolve") + " to acknowledge"
24738
25868
  );
24739
25869
  console.log(
24740
- " " + import_chalk27.default.cyan("3.") + " Run " + import_chalk27.default.white("node9 report") + " for full audit history"
25870
+ " " + import_chalk30.default.cyan("3.") + " Run " + import_chalk30.default.white("node9 report") + " for full audit history"
24741
25871
  );
24742
25872
  console.log("");
24743
25873
  });
24744
25874
  }
24745
25875
 
24746
25876
  // src/cli/commands/mask.ts
24747
- var import_chalk28 = __toESM(require("chalk"));
24748
- var import_fs50 = __toESM(require("fs"));
24749
- var import_path51 = __toESM(require("path"));
24750
- var import_os45 = __toESM(require("os"));
25877
+ var import_chalk31 = __toESM(require("chalk"));
25878
+ var import_fs55 = __toESM(require("fs"));
25879
+ var import_path54 = __toESM(require("path"));
25880
+ var import_os50 = __toESM(require("os"));
24751
25881
  init_dlp();
24752
25882
  function findJsonlFiles(dir) {
24753
25883
  const results = [];
24754
- if (!import_fs50.default.existsSync(dir)) return results;
24755
- for (const entry of import_fs50.default.readdirSync(dir, { withFileTypes: true })) {
24756
- const full = import_path51.default.join(dir, entry.name);
25884
+ if (!import_fs55.default.existsSync(dir)) return results;
25885
+ for (const entry of import_fs55.default.readdirSync(dir, { withFileTypes: true })) {
25886
+ const full = import_path54.default.join(dir, entry.name);
24757
25887
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
24758
25888
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
24759
25889
  }
@@ -24796,7 +25926,7 @@ function redactJson(obj) {
24796
25926
  function processFile(filePath, dryRun) {
24797
25927
  let raw;
24798
25928
  try {
24799
- raw = import_fs50.default.readFileSync(filePath, "utf-8");
25929
+ raw = import_fs55.default.readFileSync(filePath, "utf-8");
24800
25930
  } catch {
24801
25931
  return { redactedLines: 0, patterns: [] };
24802
25932
  }
@@ -24828,14 +25958,14 @@ function processFile(filePath, dryRun) {
24828
25958
  }
24829
25959
  }
24830
25960
  if (!dryRun && redactedLines > 0) {
24831
- import_fs50.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
25961
+ import_fs55.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
24832
25962
  }
24833
25963
  return { redactedLines, patterns };
24834
25964
  }
24835
25965
  function processJsonFile(filePath, dryRun) {
24836
25966
  let raw;
24837
25967
  try {
24838
- raw = import_fs50.default.readFileSync(filePath, "utf-8");
25968
+ raw = import_fs55.default.readFileSync(filePath, "utf-8");
24839
25969
  } catch {
24840
25970
  return { redactedLines: 0, patterns: [] };
24841
25971
  }
@@ -24848,15 +25978,15 @@ function processJsonFile(filePath, dryRun) {
24848
25978
  const { value, modified, found } = redactJson(parsed);
24849
25979
  if (!modified) return { redactedLines: 0, patterns: [] };
24850
25980
  if (!dryRun) {
24851
- import_fs50.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
25981
+ import_fs55.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
24852
25982
  }
24853
25983
  return { redactedLines: 1, patterns: found };
24854
25984
  }
24855
25985
  function findJsonFiles(dir) {
24856
25986
  const results = [];
24857
- if (!import_fs50.default.existsSync(dir)) return results;
24858
- for (const entry of import_fs50.default.readdirSync(dir, { withFileTypes: true })) {
24859
- const full = import_path51.default.join(dir, entry.name);
25987
+ if (!import_fs55.default.existsSync(dir)) return results;
25988
+ for (const entry of import_fs55.default.readdirSync(dir, { withFileTypes: true })) {
25989
+ const full = import_path54.default.join(dir, entry.name);
24860
25990
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
24861
25991
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
24862
25992
  }
@@ -24865,9 +25995,9 @@ function findJsonFiles(dir) {
24865
25995
  function registerMaskCommand(program2) {
24866
25996
  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) => {
24867
25997
  const dryRun = !!options.dryRun;
24868
- const home = import_os45.default.homedir();
24869
- const claudeDir = import_path51.default.join(home, ".claude", "projects");
24870
- const geminiDir = import_path51.default.join(home, ".gemini", "tmp");
25998
+ const home = import_os50.default.homedir();
25999
+ const claudeDir = import_path54.default.join(home, ".claude", "projects");
26000
+ const geminiDir = import_path54.default.join(home, ".gemini", "tmp");
24871
26001
  const allFiles = [
24872
26002
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
24873
26003
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -24875,18 +26005,18 @@ function registerMaskCommand(program2) {
24875
26005
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
24876
26006
  const filtered = cutoff ? allFiles.filter((f) => {
24877
26007
  try {
24878
- return import_fs50.default.statSync(f.path).mtime >= cutoff;
26008
+ return import_fs55.default.statSync(f.path).mtime >= cutoff;
24879
26009
  } catch {
24880
26010
  return false;
24881
26011
  }
24882
26012
  }) : allFiles;
24883
26013
  if (filtered.length === 0) {
24884
- console.log(import_chalk28.default.yellow(" No session files found."));
26014
+ console.log(import_chalk31.default.yellow(" No session files found."));
24885
26015
  return;
24886
26016
  }
24887
26017
  console.log("");
24888
26018
  if (dryRun) {
24889
- console.log(import_chalk28.default.dim(" Dry run \u2014 no files will be modified.\n"));
26019
+ console.log(import_chalk31.default.dim(" Dry run \u2014 no files will be modified.\n"));
24890
26020
  }
24891
26021
  let totalFiles = 0;
24892
26022
  let totalLines = 0;
@@ -24902,23 +26032,23 @@ function registerMaskCommand(program2) {
24902
26032
  });
24903
26033
  const verb = dryRun ? "Would redact" : "Redacted";
24904
26034
  console.log(
24905
- " " + import_chalk28.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk28.default.red(`${verb}: `) + import_chalk28.default.yellow(patterns.join(", ")) + import_chalk28.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
26035
+ " " + import_chalk31.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk31.default.red(`${verb}: `) + import_chalk31.default.yellow(patterns.join(", ")) + import_chalk31.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
24906
26036
  );
24907
26037
  }
24908
26038
  }
24909
26039
  console.log("");
24910
26040
  if (totalFiles === 0) {
24911
- console.log(import_chalk28.default.green(" No secrets found in session history."));
26041
+ console.log(import_chalk31.default.green(" No secrets found in session history."));
24912
26042
  } else {
24913
26043
  const verb = dryRun ? "would be modified" : "modified";
24914
26044
  console.log(
24915
- import_chalk28.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk28.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
26045
+ import_chalk31.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk31.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
24916
26046
  );
24917
- console.log(" Patterns: " + import_chalk28.default.yellow(totalPatterns.join(", ")));
26047
+ console.log(" Patterns: " + import_chalk31.default.yellow(totalPatterns.join(", ")));
24918
26048
  if (!dryRun) {
24919
26049
  console.log("");
24920
26050
  console.log(
24921
- import_chalk28.default.dim(
26051
+ import_chalk31.default.dim(
24922
26052
  " Note: secrets were already sent to the AI provider during the active session.\n This cleans your local disk only. Rotate any exposed keys."
24923
26053
  )
24924
26054
  );
@@ -24931,20 +26061,20 @@ function registerMaskCommand(program2) {
24931
26061
  // src/cli.ts
24932
26062
  init_blast();
24933
26063
  var { version } = JSON.parse(
24934
- import_fs53.default.readFileSync(import_path54.default.join(__dirname, "../package.json"), "utf-8")
26064
+ import_fs58.default.readFileSync(import_path57.default.join(__dirname, "../package.json"), "utf-8")
24935
26065
  );
24936
26066
  var program = new import_commander.Command();
24937
26067
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
24938
26068
  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) => {
24939
26069
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
24940
- const credPath = import_path54.default.join(import_os48.default.homedir(), ".node9", "credentials.json");
24941
- if (!import_fs53.default.existsSync(import_path54.default.dirname(credPath)))
24942
- import_fs53.default.mkdirSync(import_path54.default.dirname(credPath), { recursive: true });
26070
+ const credPath = import_path57.default.join(import_os53.default.homedir(), ".node9", "credentials.json");
26071
+ if (!import_fs58.default.existsSync(import_path57.default.dirname(credPath)))
26072
+ import_fs58.default.mkdirSync(import_path57.default.dirname(credPath), { recursive: true });
24943
26073
  const profileName = options.profile || "default";
24944
26074
  let existingCreds = {};
24945
26075
  try {
24946
- if (import_fs53.default.existsSync(credPath)) {
24947
- const raw = JSON.parse(import_fs53.default.readFileSync(credPath, "utf-8"));
26076
+ if (import_fs58.default.existsSync(credPath)) {
26077
+ const raw = JSON.parse(import_fs58.default.readFileSync(credPath, "utf-8"));
24948
26078
  if (raw.apiKey) {
24949
26079
  existingCreds = {
24950
26080
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -24956,14 +26086,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24956
26086
  } catch {
24957
26087
  }
24958
26088
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
24959
- import_fs53.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
26089
+ import_fs58.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
24960
26090
  let effectiveCloud = null;
24961
26091
  if (profileName === "default") {
24962
- const configPath = import_path54.default.join(import_os48.default.homedir(), ".node9", "config.json");
26092
+ const configPath2 = import_path57.default.join(import_os53.default.homedir(), ".node9", "config.json");
24963
26093
  let config = {};
24964
26094
  try {
24965
- if (import_fs53.default.existsSync(configPath))
24966
- config = JSON.parse(import_fs53.default.readFileSync(configPath, "utf-8"));
26095
+ if (import_fs58.default.existsSync(configPath2))
26096
+ config = JSON.parse(import_fs58.default.readFileSync(configPath2, "utf-8"));
24967
26097
  } catch {
24968
26098
  }
24969
26099
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -24978,28 +26108,28 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24978
26108
  approvers.cloud = false;
24979
26109
  }
24980
26110
  s.approvers = approvers;
24981
- if (!import_fs53.default.existsSync(import_path54.default.dirname(configPath)))
24982
- import_fs53.default.mkdirSync(import_path54.default.dirname(configPath), { recursive: true });
24983
- import_fs53.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
26111
+ if (!import_fs58.default.existsSync(import_path57.default.dirname(configPath2)))
26112
+ import_fs58.default.mkdirSync(import_path57.default.dirname(configPath2), { recursive: true });
26113
+ import_fs58.default.writeFileSync(configPath2, JSON.stringify(config, null, 2), { mode: 384 });
24984
26114
  effectiveCloud = approvers.cloud === true;
24985
26115
  }
24986
26116
  if (options.profile && profileName !== "default") {
24987
- console.log(import_chalk30.default.green(`\u2705 Profile "${profileName}" saved`));
24988
- console.log(import_chalk30.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
26117
+ console.log(import_chalk33.default.green(`\u2705 Profile "${profileName}" saved`));
26118
+ console.log(import_chalk33.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
24989
26119
  } else if (options.local || effectiveCloud === false) {
24990
- console.log(import_chalk30.default.green(`\u2705 Key saved \u2014 Privacy mode \u{1F6E1}\uFE0F`));
24991
- console.log(import_chalk30.default.gray(` All decisions stay on this machine. Nothing syncs to the cloud.`));
26120
+ console.log(import_chalk33.default.green(`\u2705 Key saved \u2014 Privacy mode \u{1F6E1}\uFE0F`));
26121
+ console.log(import_chalk33.default.gray(` All decisions stay on this machine. Nothing syncs to the cloud.`));
24992
26122
  if (!options.local) {
24993
26123
  console.log(
24994
- import_chalk30.default.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
26124
+ import_chalk33.default.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
24995
26125
  );
24996
26126
  console.log(
24997
- import_chalk30.default.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
26127
+ import_chalk33.default.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
24998
26128
  );
24999
26129
  }
25000
26130
  } else {
25001
- console.log(import_chalk30.default.green(`\u2705 Logged in \u2014 agent mode`));
25002
- console.log(import_chalk30.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
26131
+ console.log(import_chalk33.default.green(`\u2705 Logged in \u2014 agent mode`));
26132
+ console.log(import_chalk33.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
25003
26133
  }
25004
26134
  });
25005
26135
  program.command("addto", { hidden: true }).description("Integrate Node9 with an AI agent").addHelpText(
@@ -25020,7 +26150,7 @@ program.command("addto", { hidden: true }).description("Integrate Node9 with an
25020
26150
  if (target === "hermes") return setupHermes();
25021
26151
  if (target === "hud") return setupHud();
25022
26152
  console.error(
25023
- import_chalk30.default.red(
26153
+ import_chalk33.default.red(
25024
26154
  `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
25025
26155
  )
25026
26156
  );
@@ -25034,20 +26164,20 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
25034
26164
  "The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
25035
26165
  ).action(async (target) => {
25036
26166
  if (!target) {
25037
- console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
25038
- console.log(" Usage: " + import_chalk30.default.white("node9 setup <target>") + "\n");
26167
+ console.log(import_chalk33.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
26168
+ console.log(" Usage: " + import_chalk33.default.white("node9 setup <target>") + "\n");
25039
26169
  console.log(" Targets:");
25040
- console.log(" " + import_chalk30.default.green("claude") + " \u2014 Claude Code (hook mode)");
25041
- console.log(" " + import_chalk30.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
25042
- console.log(" " + import_chalk30.default.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
25043
- console.log(" " + import_chalk30.default.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
25044
- console.log(" " + import_chalk30.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
25045
- console.log(" " + import_chalk30.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
25046
- console.log(" " + import_chalk30.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
25047
- console.log(" " + import_chalk30.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
25048
- console.log(" " + import_chalk30.default.green("hermes") + " \u2014 Hermes Agent (hook mode)");
26170
+ console.log(" " + import_chalk33.default.green("claude") + " \u2014 Claude Code (hook mode)");
26171
+ console.log(" " + import_chalk33.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
26172
+ console.log(" " + import_chalk33.default.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
26173
+ console.log(" " + import_chalk33.default.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
26174
+ console.log(" " + import_chalk33.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
26175
+ console.log(" " + import_chalk33.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
26176
+ console.log(" " + import_chalk33.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
26177
+ console.log(" " + import_chalk33.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
26178
+ console.log(" " + import_chalk33.default.green("hermes") + " \u2014 Hermes Agent (hook mode)");
25049
26179
  process.stdout.write(
25050
- " " + import_chalk30.default.green("hud") + " \u2014 Claude Code security statusline\n"
26180
+ " " + import_chalk33.default.green("hud") + " \u2014 Claude Code security statusline\n"
25051
26181
  );
25052
26182
  console.log("");
25053
26183
  return;
@@ -25064,7 +26194,7 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
25064
26194
  if (t === "hermes") return setupHermes();
25065
26195
  if (t === "hud") return setupHud();
25066
26196
  console.error(
25067
- import_chalk30.default.red(
26197
+ import_chalk33.default.red(
25068
26198
  `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
25069
26199
  )
25070
26200
  );
@@ -25090,35 +26220,35 @@ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks
25090
26220
  else if (target === "hud") fn = teardownHud;
25091
26221
  else {
25092
26222
  console.error(
25093
- import_chalk30.default.red(
26223
+ import_chalk33.default.red(
25094
26224
  `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
25095
26225
  )
25096
26226
  );
25097
26227
  process.exit(1);
25098
26228
  }
25099
- console.log(import_chalk30.default.cyan(`
26229
+ console.log(import_chalk33.default.cyan(`
25100
26230
  \u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
25101
26231
  `));
25102
26232
  try {
25103
26233
  fn();
25104
26234
  } catch (err2) {
25105
- console.error(import_chalk30.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
26235
+ console.error(import_chalk33.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
25106
26236
  process.exit(1);
25107
26237
  }
25108
- console.log(import_chalk30.default.gray("\n Restart the agent for changes to take effect."));
26238
+ console.log(import_chalk33.default.gray("\n Restart the agent for changes to take effect."));
25109
26239
  });
25110
26240
  program.command("uninstall").description("Remove all Node9 hooks and optionally delete config files").option("--purge", "Also delete ~/.node9/ directory (config, audit log, credentials)").action(async (options) => {
25111
- console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
25112
- console.log(import_chalk30.default.bold("Stopping daemon..."));
26241
+ console.log(import_chalk33.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
26242
+ console.log(import_chalk33.default.bold("Stopping daemon..."));
25113
26243
  try {
25114
26244
  stopDaemon();
25115
- console.log(import_chalk30.default.green(" \u2705 Daemon stopped"));
26245
+ console.log(import_chalk33.default.green(" \u2705 Daemon stopped"));
25116
26246
  } catch {
25117
- console.log(import_chalk30.default.blue(" \u2139\uFE0F Daemon was not running"));
26247
+ console.log(import_chalk33.default.blue(" \u2139\uFE0F Daemon was not running"));
25118
26248
  }
25119
- console.log(import_chalk30.default.bold("\nRemoving hooks..."));
26249
+ console.log(import_chalk33.default.bold("\nRemoving hooks..."));
25120
26250
  let teardownFailed = false;
25121
- for (const [label, fn] of [
26251
+ for (const [label2, fn] of [
25122
26252
  ["Claude", teardownClaude],
25123
26253
  ["Gemini", teardownGemini],
25124
26254
  ["Cursor", teardownCursor],
@@ -25132,45 +26262,45 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
25132
26262
  } catch (err2) {
25133
26263
  teardownFailed = true;
25134
26264
  console.error(
25135
- import_chalk30.default.red(
25136
- ` \u26A0\uFE0F Failed to remove ${label} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
26265
+ import_chalk33.default.red(
26266
+ ` \u26A0\uFE0F Failed to remove ${label2} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
25137
26267
  )
25138
26268
  );
25139
26269
  }
25140
26270
  }
25141
26271
  if (options.purge) {
25142
- const node9Dir = import_path54.default.join(import_os48.default.homedir(), ".node9");
25143
- if (import_fs53.default.existsSync(node9Dir)) {
26272
+ const node9Dir = import_path57.default.join(import_os53.default.homedir(), ".node9");
26273
+ if (import_fs58.default.existsSync(node9Dir)) {
25144
26274
  const confirmed = await (0, import_prompts2.confirm)({
25145
26275
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
25146
26276
  default: false
25147
26277
  });
25148
26278
  if (confirmed) {
25149
- import_fs53.default.rmSync(node9Dir, { recursive: true });
25150
- if (import_fs53.default.existsSync(node9Dir)) {
26279
+ import_fs58.default.rmSync(node9Dir, { recursive: true });
26280
+ if (import_fs58.default.existsSync(node9Dir)) {
25151
26281
  console.error(
25152
- import_chalk30.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
26282
+ import_chalk33.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
25153
26283
  );
25154
26284
  } else {
25155
- console.log(import_chalk30.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
26285
+ console.log(import_chalk33.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
25156
26286
  }
25157
26287
  } else {
25158
- console.log(import_chalk30.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
26288
+ console.log(import_chalk33.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
25159
26289
  }
25160
26290
  } else {
25161
- console.log(import_chalk30.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
26291
+ console.log(import_chalk33.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
25162
26292
  }
25163
26293
  } else {
25164
26294
  console.log(
25165
- import_chalk30.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
26295
+ import_chalk33.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
25166
26296
  );
25167
26297
  }
25168
26298
  if (teardownFailed) {
25169
- console.error(import_chalk30.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
26299
+ console.error(import_chalk33.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
25170
26300
  process.exit(1);
25171
26301
  }
25172
- console.log(import_chalk30.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g @node9/proxy"));
25173
- console.log(import_chalk30.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
26302
+ console.log(import_chalk33.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g node9-ai"));
26303
+ console.log(import_chalk33.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
25174
26304
  });
25175
26305
  registerDoctorCommand(program, version);
25176
26306
  program.command("explain").description(
@@ -25183,7 +26313,7 @@ program.command("explain").description(
25183
26313
  try {
25184
26314
  args = JSON.parse(trimmed);
25185
26315
  } catch {
25186
- console.error(import_chalk30.default.red(`
26316
+ console.error(import_chalk33.default.red(`
25187
26317
  \u274C Invalid JSON: ${trimmed}
25188
26318
  `));
25189
26319
  process.exit(1);
@@ -25194,54 +26324,54 @@ program.command("explain").description(
25194
26324
  }
25195
26325
  const result = await explainPolicy(tool, args);
25196
26326
  console.log("");
25197
- console.log(import_chalk30.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
26327
+ console.log(import_chalk33.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
25198
26328
  console.log("");
25199
- console.log(` ${import_chalk30.default.bold("Tool:")} ${import_chalk30.default.white(result.tool)}`);
26329
+ console.log(` ${import_chalk33.default.bold("Tool:")} ${import_chalk33.default.white(result.tool)}`);
25200
26330
  if (argsRaw) {
25201
26331
  const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
25202
- console.log(` ${import_chalk30.default.bold("Input:")} ${import_chalk30.default.gray(preview2)}`);
26332
+ console.log(` ${import_chalk33.default.bold("Input:")} ${import_chalk33.default.gray(preview2)}`);
25203
26333
  }
25204
26334
  console.log("");
25205
- console.log(import_chalk30.default.bold("Config Sources (Waterfall):"));
26335
+ console.log(import_chalk33.default.bold("Config Sources (Waterfall):"));
25206
26336
  for (const tier of result.waterfall) {
25207
- const num3 = import_chalk30.default.gray(` ${tier.tier}.`);
25208
- const label = tier.label.padEnd(16);
26337
+ const num3 = import_chalk33.default.gray(` ${tier.tier}.`);
26338
+ const label2 = tier.label.padEnd(16);
25209
26339
  let statusStr;
25210
26340
  if (tier.tier === 1) {
25211
- statusStr = import_chalk30.default.gray(tier.note ?? "");
26341
+ statusStr = import_chalk33.default.gray(tier.note ?? "");
25212
26342
  } else if (tier.status === "active") {
25213
- const loc = tier.path ? import_chalk30.default.gray(tier.path) : "";
25214
- const note = tier.note ? import_chalk30.default.gray(`(${tier.note})`) : "";
25215
- statusStr = import_chalk30.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
26343
+ const loc = tier.path ? import_chalk33.default.gray(tier.path) : "";
26344
+ const note = tier.note ? import_chalk33.default.gray(`(${tier.note})`) : "";
26345
+ statusStr = import_chalk33.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
25216
26346
  } else {
25217
- statusStr = import_chalk30.default.gray("\u25CB " + (tier.note ?? "not found"));
26347
+ statusStr = import_chalk33.default.gray("\u25CB " + (tier.note ?? "not found"));
25218
26348
  }
25219
- console.log(`${num3} ${import_chalk30.default.white(label)} ${statusStr}`);
26349
+ console.log(`${num3} ${import_chalk33.default.white(label2)} ${statusStr}`);
25220
26350
  }
25221
26351
  console.log("");
25222
- console.log(import_chalk30.default.bold("Policy Evaluation:"));
26352
+ console.log(import_chalk33.default.bold("Policy Evaluation:"));
25223
26353
  for (const step of result.steps) {
25224
26354
  const isFinal = step.isFinal;
25225
26355
  let icon;
25226
- if (step.outcome === "allow") icon = import_chalk30.default.green(" \u2705");
25227
- else if (step.outcome === "review") icon = import_chalk30.default.red(" \u{1F534}");
25228
- else if (step.outcome === "skip") icon = import_chalk30.default.gray(" \u2500 ");
25229
- else icon = import_chalk30.default.gray(" \u25CB ");
26356
+ if (step.outcome === "allow") icon = import_chalk33.default.green(" \u2705");
26357
+ else if (step.outcome === "review") icon = import_chalk33.default.red(" \u{1F534}");
26358
+ else if (step.outcome === "skip") icon = import_chalk33.default.gray(" \u2500 ");
26359
+ else icon = import_chalk33.default.gray(" \u25CB ");
25230
26360
  const name = step.name.padEnd(18);
25231
- const nameStr = isFinal ? import_chalk30.default.white.bold(name) : import_chalk30.default.white(name);
25232
- const detail = isFinal ? import_chalk30.default.white(step.detail) : import_chalk30.default.gray(step.detail);
25233
- const arrow = isFinal ? import_chalk30.default.yellow(" \u2190 STOP") : "";
26361
+ const nameStr = isFinal ? import_chalk33.default.white.bold(name) : import_chalk33.default.white(name);
26362
+ const detail = isFinal ? import_chalk33.default.white(step.detail) : import_chalk33.default.gray(step.detail);
26363
+ const arrow = isFinal ? import_chalk33.default.yellow(" \u2190 STOP") : "";
25234
26364
  console.log(`${icon} ${nameStr} ${detail}${arrow}`);
25235
26365
  }
25236
26366
  console.log("");
25237
26367
  if (result.decision === "allow") {
25238
- console.log(import_chalk30.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk30.default.gray(" \u2014 no approval needed"));
26368
+ console.log(import_chalk33.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk33.default.gray(" \u2014 no approval needed"));
25239
26369
  } else {
25240
26370
  console.log(
25241
- import_chalk30.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk30.default.gray(" \u2014 human approval required")
26371
+ import_chalk33.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk33.default.gray(" \u2014 human approval required")
25242
26372
  );
25243
26373
  if (result.blockedByLabel) {
25244
- console.log(import_chalk30.default.gray(` Reason: ${result.blockedByLabel}`));
26374
+ console.log(import_chalk33.default.gray(` Reason: ${result.blockedByLabel}`));
25245
26375
  }
25246
26376
  }
25247
26377
  console.log("");
@@ -25256,18 +26386,18 @@ program.command("tail").description("Stream live agent activity to the terminal"
25256
26386
  try {
25257
26387
  await startTail2(options);
25258
26388
  } catch (err2) {
25259
- console.error(import_chalk30.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
26389
+ console.error(import_chalk33.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
25260
26390
  process.exit(1);
25261
26391
  }
25262
26392
  });
25263
26393
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
25264
26394
  try {
25265
- const dashboardPath = import_path54.default.join(__dirname, "dashboard.mjs");
26395
+ const dashboardPath = import_path57.default.join(__dirname, "dashboard.mjs");
25266
26396
  const dynamicImport = new Function("id", "return import(id)");
25267
26397
  const mod = await dynamicImport(`file://${dashboardPath}`);
25268
26398
  await mod.startMonitor();
25269
26399
  } catch (err2) {
25270
- console.error(import_chalk30.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
26400
+ console.error(import_chalk33.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
25271
26401
  process.exit(1);
25272
26402
  }
25273
26403
  });
@@ -25300,14 +26430,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
25300
26430
  Run "node9 addto claude" to register it as the statusLine.`
25301
26431
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
25302
26432
  if (subcommand === "debug") {
25303
- const flagFile = import_path54.default.join(import_os48.default.homedir(), ".node9", "hud-debug");
26433
+ const flagFile = import_path57.default.join(import_os53.default.homedir(), ".node9", "hud-debug");
25304
26434
  if (state === "on") {
25305
- import_fs53.default.mkdirSync(import_path54.default.dirname(flagFile), { recursive: true });
25306
- import_fs53.default.writeFileSync(flagFile, "");
26435
+ import_fs58.default.mkdirSync(import_path57.default.dirname(flagFile), { recursive: true });
26436
+ import_fs58.default.writeFileSync(flagFile, "");
25307
26437
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
25308
26438
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
25309
26439
  } else if (state === "off") {
25310
- if (import_fs53.default.existsSync(flagFile)) import_fs53.default.unlinkSync(flagFile);
26440
+ if (import_fs58.default.existsSync(flagFile)) import_fs58.default.unlinkSync(flagFile);
25311
26441
  console.log("HUD debug logging disabled.");
25312
26442
  } else {
25313
26443
  console.error("Usage: node9 hud debug on|off");
@@ -25322,7 +26452,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
25322
26452
  const ms = parseDuration(options.duration);
25323
26453
  if (ms === null) {
25324
26454
  console.error(
25325
- import_chalk30.default.red(`
26455
+ import_chalk33.default.red(`
25326
26456
  \u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
25327
26457
  `)
25328
26458
  );
@@ -25330,20 +26460,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
25330
26460
  }
25331
26461
  pauseNode9(ms, options.duration);
25332
26462
  const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
25333
- console.log(import_chalk30.default.yellow(`
26463
+ console.log(import_chalk33.default.yellow(`
25334
26464
  \u23F8 Node9 paused until ${expiresAt}`));
25335
- console.log(import_chalk30.default.gray(` All tool calls will be allowed without review.`));
25336
- console.log(import_chalk30.default.gray(` Run "node9 resume" to re-enable early.
26465
+ console.log(import_chalk33.default.gray(` All tool calls will be allowed without review.`));
26466
+ console.log(import_chalk33.default.gray(` Run "node9 resume" to re-enable early.
25337
26467
  `));
25338
26468
  });
25339
26469
  program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
25340
26470
  const { paused } = checkPause();
25341
26471
  if (!paused) {
25342
- console.log(import_chalk30.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
26472
+ console.log(import_chalk33.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
25343
26473
  return;
25344
26474
  }
25345
26475
  resumeNode9();
25346
- console.log(import_chalk30.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
26476
+ console.log(import_chalk33.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
25347
26477
  });
25348
26478
  var HOOK_BASED_AGENTS = {
25349
26479
  claude: "claude",
@@ -25359,15 +26489,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
25359
26489
  if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
25360
26490
  const target = HOOK_BASED_AGENTS[firstArg2];
25361
26491
  console.error(
25362
- import_chalk30.default.yellow(`
26492
+ import_chalk33.default.yellow(`
25363
26493
  \u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
25364
26494
  );
25365
- console.error(import_chalk30.default.white(`
26495
+ console.error(import_chalk33.default.white(`
25366
26496
  "${target}" uses its own hook system. Use:`));
25367
26497
  console.error(
25368
- import_chalk30.default.green(` node9 addto ${target} `) + import_chalk30.default.gray("# one-time setup")
26498
+ import_chalk33.default.green(` node9 addto ${target} `) + import_chalk33.default.gray("# one-time setup")
25369
26499
  );
25370
- console.error(import_chalk30.default.green(` ${target} `) + import_chalk30.default.gray("# run normally"));
26500
+ console.error(import_chalk33.default.green(` ${target} `) + import_chalk33.default.gray("# run normally"));
25371
26501
  process.exit(1);
25372
26502
  }
25373
26503
  const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
@@ -25384,7 +26514,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
25384
26514
  }
25385
26515
  );
25386
26516
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
25387
- console.error(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
26517
+ console.error(import_chalk33.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
25388
26518
  const daemonReady = await autoStartDaemonAndWait();
25389
26519
  if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
25390
26520
  }
@@ -25397,12 +26527,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
25397
26527
  }
25398
26528
  if (!result.approved) {
25399
26529
  console.error(
25400
- import_chalk30.default.red(`
26530
+ import_chalk33.default.red(`
25401
26531
  \u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
25402
26532
  );
25403
26533
  process.exit(1);
25404
26534
  }
25405
- console.error(import_chalk30.default.green("\n\u2705 Approved \u2014 running command...\n"));
26535
+ console.error(import_chalk33.default.green("\n\u2705 Approved \u2014 running command...\n"));
25406
26536
  await runProxy(fullCommand);
25407
26537
  } else {
25408
26538
  program.help();
@@ -25415,6 +26545,8 @@ registerTrustCommand(program);
25415
26545
  registerSyncCommand(program);
25416
26546
  registerAgentsCommand(program);
25417
26547
  registerScanCommand(program);
26548
+ registerPostureCommand(program);
26549
+ registerEgressCommand(program);
25418
26550
  registerSessionsCommand(program);
25419
26551
  registerDlpCommand(program);
25420
26552
  registerMaskCommand(program);
@@ -25424,9 +26556,9 @@ if (process.argv[2] !== "daemon") {
25424
26556
  const isCheckHook = process.argv[2] === "check";
25425
26557
  if (isCheckHook) {
25426
26558
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
25427
- const logPath = import_path54.default.join(import_os48.default.homedir(), ".node9", "hook-debug.log");
26559
+ const logPath = import_path57.default.join(import_os53.default.homedir(), ".node9", "hook-debug.log");
25428
26560
  const msg = reason instanceof Error ? reason.message : String(reason);
25429
- import_fs53.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
26561
+ import_fs58.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
25430
26562
  `);
25431
26563
  }
25432
26564
  process.exit(0);