@node9/proxy 1.44.0 → 1.45.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 +1171 -666
  2. package/dist/cli.mjs +1171 -666
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -240,8 +240,8 @@ function sanitizeConfig(raw) {
240
240
  }
241
241
  }
242
242
  const lines = result.error.issues.map((issue) => {
243
- const path62 = issue.path.length > 0 ? issue.path.join(".") : "root";
244
- return ` \u2022 ${path62}: ${issue.message}`;
243
+ const path64 = issue.path.length > 0 ? issue.path.join(".") : "root";
244
+ return ` \u2022 ${path64}: ${issue.message}`;
245
245
  });
246
246
  return {
247
247
  sanitized,
@@ -1357,9 +1357,9 @@ function matchesPattern(text, patterns) {
1357
1357
  const withoutDotSlash = text.replace(/^\.\//, "");
1358
1358
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1359
1359
  }
1360
- function getNestedValue(obj, path62) {
1360
+ function getNestedValue(obj, path64) {
1361
1361
  if (!obj || typeof obj !== "object") return null;
1362
- const segments = path62.split(".");
1362
+ const segments = path64.split(".");
1363
1363
  for (const seg of segments) {
1364
1364
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1365
1365
  }
@@ -4171,7 +4171,7 @@ function installShield(name, shieldJson) {
4171
4171
  import_fs2.default.writeFileSync(tmp, JSON.stringify(shieldJson, null, 2), { mode: 384 });
4172
4172
  import_fs2.default.renameSync(tmp, filePath);
4173
4173
  }
4174
- var import_fs2, import_path2, import_os2, import_crypto4, USER_SHIELDS_DIR, SHIELDS, SHIELDS_STATE_FILE, RULE_KEY_MIGRATIONS;
4174
+ var import_fs2, import_path2, import_os2, import_crypto4, USER_SHIELDS_DIR, SHIELDS, SHIELDS_STATE_FILE, RULE_KEY_MIGRATIONS, USER_SHIELDS_DIR_PATH;
4175
4175
  var init_shields = __esm({
4176
4176
  "src/shields.ts"() {
4177
4177
  "use strict";
@@ -4192,6 +4192,7 @@ var init_shields = __esm({
4192
4192
  // around the review-read-credentials rule.
4193
4193
  ["shield:project-jail:review-read-env-any-tool", "shield:project-jail:block-read-env-any-tool"]
4194
4194
  ];
4195
+ USER_SHIELDS_DIR_PATH = USER_SHIELDS_DIR;
4195
4196
  }
4196
4197
  });
4197
4198
 
@@ -5059,7 +5060,30 @@ function explainIsSqlTool(toolName, toolInspection) {
5059
5060
  const fieldName = toolInspection[matchingPattern];
5060
5061
  return fieldName === "sql" || fieldName === "query";
5061
5062
  }
5062
- async function explainPolicy(toolName, args) {
5063
+ async function explainPolicy(toolName, args, agent = EXPLAIN_AGENT) {
5064
+ const derived = await deriveExplainTrace(toolName, args);
5065
+ const engine = await evaluatePolicy2(toolName, args, agent);
5066
+ if (derived.decision === engine.decision) return derived;
5067
+ if (process.env.NODE9_DEBUG) {
5068
+ console.error(
5069
+ `[node9 explain] decision drift: trace=${derived.decision} engine=${engine.decision} for ${toolName} \u2014 engine wins (${engine.blockedByLabel ?? "engine"}).`
5070
+ );
5071
+ }
5072
+ const engineStep = {
5073
+ name: "Engine verdict (authoritative)",
5074
+ outcome: engine.decision,
5075
+ detail: `${engine.blockedByLabel ?? "policy engine"}${engine.reason ? `: ${engine.reason}` : ""} \u2014 this is the verdict the live hook actually enforces. It differs from the preview trace above, which models the policy tiers separately and can drift; the engine is authoritative.`,
5076
+ isFinal: true
5077
+ };
5078
+ return {
5079
+ ...derived,
5080
+ steps: [...derived.steps, engineStep],
5081
+ decision: engine.decision,
5082
+ blockedByLabel: engine.blockedByLabel ?? derived.blockedByLabel,
5083
+ ruleDescription: engine.ruleDescription ?? derived.ruleDescription
5084
+ };
5085
+ }
5086
+ async function deriveExplainTrace(toolName, args) {
5063
5087
  const steps = [];
5064
5088
  const globalPath = import_path7.default.join(import_os6.default.homedir(), ".node9", "config.json");
5065
5089
  const projectPath = import_path7.default.join(process.cwd(), "node9.config.json");
@@ -5341,7 +5365,7 @@ async function explainPolicy(toolName, args) {
5341
5365
  });
5342
5366
  return { tool: toolName, args, waterfall, steps, decision: "allow" };
5343
5367
  }
5344
- var import_fs7, import_path7, import_os6, import_picomatch2, SQL_DML_KEYWORDS2;
5368
+ var import_fs7, import_path7, import_os6, import_picomatch2, SQL_DML_KEYWORDS2, EXPLAIN_AGENT;
5345
5369
  var init_policy = __esm({
5346
5370
  "src/policy/index.ts"() {
5347
5371
  "use strict";
@@ -5356,6 +5380,7 @@ var init_policy = __esm({
5356
5380
  init_dist();
5357
5381
  init_dist();
5358
5382
  SQL_DML_KEYWORDS2 = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
5383
+ EXPLAIN_AGENT = "agent";
5359
5384
  }
5360
5385
  });
5361
5386
 
@@ -8629,9 +8654,9 @@ function writeToml(filePath, data) {
8629
8654
  async function setupCodex() {
8630
8655
  seedMcpPinsIfMissing();
8631
8656
  const homeDir2 = import_os12.default.homedir();
8632
- const configPath2 = import_path15.default.join(homeDir2, ".codex", "config.toml");
8657
+ const configPath = import_path15.default.join(homeDir2, ".codex", "config.toml");
8633
8658
  const hooksPath = import_path15.default.join(homeDir2, ".codex", "hooks.json");
8634
- const config = readToml(configPath2) ?? {};
8659
+ const config = readToml(configPath) ?? {};
8635
8660
  const servers = config.mcp_servers ?? {};
8636
8661
  let anythingChanged = false;
8637
8662
  const hooksFile = readJson(hooksPath) ?? {};
@@ -8706,7 +8731,7 @@ async function setupCodex() {
8706
8731
  if (!hasNode9McpServer(servers)) {
8707
8732
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
8708
8733
  config.mcp_servers = servers;
8709
- writeToml(configPath2, config);
8734
+ writeToml(configPath, config);
8710
8735
  console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
8711
8736
  anythingChanged = true;
8712
8737
  }
@@ -8718,7 +8743,7 @@ async function setupCodex() {
8718
8743
  }
8719
8744
  if (serversToWrap.length > 0) {
8720
8745
  console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
8721
- console.log(import_chalk.default.white(` ${configPath2}`));
8746
+ console.log(import_chalk.default.white(` ${configPath}`));
8722
8747
  for (const { name, upstream } of serversToWrap) {
8723
8748
  console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
8724
8749
  }
@@ -8733,7 +8758,7 @@ async function setupCodex() {
8733
8758
  };
8734
8759
  }
8735
8760
  config.mcp_servers = servers;
8736
- writeToml(configPath2, config);
8761
+ writeToml(configPath, config);
8737
8762
  console.log(import_chalk.default.green(`
8738
8763
  \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
8739
8764
  anythingChanged = true;
@@ -8781,7 +8806,7 @@ async function setupCodex() {
8781
8806
  }
8782
8807
  function teardownCodex() {
8783
8808
  const homeDir2 = import_os12.default.homedir();
8784
- const configPath2 = import_path15.default.join(homeDir2, ".codex", "config.toml");
8809
+ const configPath = import_path15.default.join(homeDir2, ".codex", "config.toml");
8785
8810
  const hooksPath = import_path15.default.join(homeDir2, ".codex", "hooks.json");
8786
8811
  const hooksFile = readJson(hooksPath);
8787
8812
  if (hooksFile?.hooks) {
@@ -8799,7 +8824,7 @@ function teardownCodex() {
8799
8824
  console.log(import_chalk.default.green(" \u2705 Removed Node9 hooks from ~/.codex/hooks.json"));
8800
8825
  }
8801
8826
  }
8802
- const config = readToml(configPath2);
8827
+ const config = readToml(configPath);
8803
8828
  if (!config?.mcp_servers) {
8804
8829
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.codex/config.toml not found \u2014 nothing to remove"));
8805
8830
  return;
@@ -8822,7 +8847,7 @@ function teardownCodex() {
8822
8847
  }
8823
8848
  }
8824
8849
  if (changed) {
8825
- writeToml(configPath2, config);
8850
+ writeToml(configPath, config);
8826
8851
  console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in ~/.codex/config.toml"));
8827
8852
  } else {
8828
8853
  console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in ~/.codex/config.toml"));
@@ -9077,18 +9102,18 @@ function teardownVSCode() {
9077
9102
  }
9078
9103
  async function setupClaudeDesktop() {
9079
9104
  seedMcpPinsIfMissing();
9080
- const configPath2 = claudeDesktopConfigPath();
9081
- if (!configPath2) {
9105
+ const configPath = claudeDesktopConfigPath();
9106
+ if (!configPath) {
9082
9107
  console.log(import_chalk.default.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
9083
9108
  return;
9084
9109
  }
9085
- const config = readJson(configPath2) ?? {};
9110
+ const config = readJson(configPath) ?? {};
9086
9111
  const servers = config.mcpServers ?? {};
9087
9112
  let anythingChanged = false;
9088
9113
  if (!hasNode9McpServer(servers)) {
9089
9114
  servers["node9"] = NODE9_MCP_SERVER_ENTRY;
9090
9115
  config.mcpServers = servers;
9091
- writeJson(configPath2, config);
9116
+ writeJson(configPath, config);
9092
9117
  console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
9093
9118
  anythingChanged = true;
9094
9119
  }
@@ -9099,7 +9124,7 @@ async function setupClaudeDesktop() {
9099
9124
  }
9100
9125
  if (serversToWrap.length > 0) {
9101
9126
  console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
9102
- console.log(import_chalk.default.white(` ${configPath2}`));
9127
+ console.log(import_chalk.default.white(` ${configPath}`));
9103
9128
  for (const { name, upstream } of serversToWrap) {
9104
9129
  console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
9105
9130
  }
@@ -9114,7 +9139,7 @@ async function setupClaudeDesktop() {
9114
9139
  };
9115
9140
  }
9116
9141
  config.mcpServers = servers;
9117
- writeJson(configPath2, config);
9142
+ writeJson(configPath, config);
9118
9143
  console.log(import_chalk.default.green(`
9119
9144
  \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
9120
9145
  anythingChanged = true;
@@ -9141,12 +9166,12 @@ async function setupClaudeDesktop() {
9141
9166
  }
9142
9167
  }
9143
9168
  function teardownClaudeDesktop() {
9144
- const configPath2 = claudeDesktopConfigPath();
9145
- if (!configPath2) {
9169
+ const configPath = claudeDesktopConfigPath();
9170
+ if (!configPath) {
9146
9171
  console.log(import_chalk.default.yellow(" \u26A0\uFE0F Claude Desktop is not supported on this platform."));
9147
9172
  return;
9148
9173
  }
9149
- const config = readJson(configPath2);
9174
+ const config = readJson(configPath);
9150
9175
  if (!config?.mcpServers) {
9151
9176
  console.log(import_chalk.default.blue(" \u2139\uFE0F Claude Desktop config not found \u2014 nothing to remove"));
9152
9177
  return;
@@ -9154,7 +9179,7 @@ function teardownClaudeDesktop() {
9154
9179
  let changed = false;
9155
9180
  if (removeNode9McpServer(config.mcpServers)) {
9156
9181
  changed = true;
9157
- console.log(import_chalk.default.green(` \u2705 Removed node9 MCP server entry from ${configPath2}`));
9182
+ console.log(import_chalk.default.green(` \u2705 Removed node9 MCP server entry from ${configPath}`));
9158
9183
  }
9159
9184
  for (const [name, server] of Object.entries(config.mcpServers)) {
9160
9185
  const args = server.args;
@@ -9169,7 +9194,7 @@ function teardownClaudeDesktop() {
9169
9194
  }
9170
9195
  }
9171
9196
  if (changed) {
9172
- writeJson(configPath2, config);
9197
+ writeJson(configPath, config);
9173
9198
  console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in Claude Desktop config"));
9174
9199
  } else {
9175
9200
  console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9-wrapped MCP servers found in Claude Desktop config"));
@@ -9197,7 +9222,7 @@ async function setupOpencode() {
9197
9222
  const homeDir2 = import_os12.default.homedir();
9198
9223
  const configDir = import_path15.default.join(homeDir2, ".config", "opencode");
9199
9224
  const pluginsDir = import_path15.default.join(configDir, "plugins");
9200
- const configPath2 = import_path15.default.join(configDir, "opencode.json");
9225
+ const configPath = import_path15.default.join(configDir, "opencode.json");
9201
9226
  const pluginPath = import_path15.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
9202
9227
  try {
9203
9228
  import_fs13.default.mkdirSync(pluginsDir, { recursive: true });
@@ -9231,7 +9256,7 @@ async function setupOpencode() {
9231
9256
  );
9232
9257
  }
9233
9258
  }
9234
- const config = readJson(configPath2) ?? {};
9259
+ const config = readJson(configPath) ?? {};
9235
9260
  const mcp = config.mcp ?? {};
9236
9261
  let configChanged = false;
9237
9262
  const desiredCommand = [...node9ArgvForShim(), "mcp-server"];
@@ -9252,7 +9277,7 @@ async function setupOpencode() {
9252
9277
  console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
9253
9278
  }
9254
9279
  }
9255
- if (configChanged) writeJson(configPath2, config);
9280
+ if (configChanged) writeJson(configPath, config);
9256
9281
  if (pluginChanged || configChanged) {
9257
9282
  console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Opencode!"));
9258
9283
  console.log(import_chalk.default.gray(" Restart Opencode for changes to take effect."));
@@ -9265,7 +9290,7 @@ function teardownOpencode() {
9265
9290
  const homeDir2 = import_os12.default.homedir();
9266
9291
  const configDir = import_path15.default.join(homeDir2, ".config", "opencode");
9267
9292
  const pluginsDir = import_path15.default.join(configDir, "plugins");
9268
- const configPath2 = import_path15.default.join(configDir, "opencode.json");
9293
+ const configPath = import_path15.default.join(configDir, "opencode.json");
9269
9294
  const pluginPath = import_path15.default.join(pluginsDir, OPENCODE_PLUGIN_NAME);
9270
9295
  try {
9271
9296
  if (import_fs13.default.existsSync(pluginPath)) {
@@ -9275,7 +9300,7 @@ function teardownOpencode() {
9275
9300
  } catch (err2) {
9276
9301
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Could not remove ${pluginPath}: ${String(err2)}`));
9277
9302
  }
9278
- const config = readJson(configPath2);
9303
+ const config = readJson(configPath);
9279
9304
  if (!config) {
9280
9305
  console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.config/opencode/opencode.json not found \u2014 nothing to remove"));
9281
9306
  return;
@@ -9291,7 +9316,7 @@ function teardownOpencode() {
9291
9316
  }
9292
9317
  if (changed) {
9293
9318
  config.mcp = mcp;
9294
- writeJson(configPath2, config);
9319
+ writeJson(configPath, config);
9295
9320
  } else {
9296
9321
  console.log(import_chalk.default.blue(" \u2139\uFE0F No node9 entries found in ~/.config/opencode/opencode.json"));
9297
9322
  }
@@ -9362,15 +9387,15 @@ function hermesAllowlistPath(homeDir2 = import_os12.default.homedir()) {
9362
9387
  }
9363
9388
  function setupHermes() {
9364
9389
  const homeDir2 = import_os12.default.homedir();
9365
- const configPath2 = hermesConfigPath(homeDir2);
9390
+ const configPath = hermesConfigPath(homeDir2);
9366
9391
  const allowlistPath = hermesAllowlistPath(homeDir2);
9367
- if (!import_fs13.default.existsSync(configPath2)) {
9368
- console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config not found at ${configPath2}`));
9392
+ if (!import_fs13.default.existsSync(configPath)) {
9393
+ console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config not found at ${configPath}`));
9369
9394
  console.log(import_chalk.default.gray(" Run `hermes setup` first, then re-run node9 agents add hermes."));
9370
9395
  return;
9371
9396
  }
9372
9397
  let anythingChanged = false;
9373
- const raw = import_fs13.default.readFileSync(configPath2, "utf-8");
9398
+ const raw = import_fs13.default.readFileSync(configPath, "utf-8");
9374
9399
  const doc = yaml.parseDocument(raw);
9375
9400
  if (doc.errors.length > 0) {
9376
9401
  console.log(import_chalk.default.yellow(` \u26A0\uFE0F Hermes config.yaml has YAML parse errors:`));
@@ -9410,7 +9435,7 @@ function setupHermes() {
9410
9435
  anythingChanged = true;
9411
9436
  }
9412
9437
  if (anythingChanged) {
9413
- import_fs13.default.writeFileSync(configPath2, doc.toString());
9438
+ import_fs13.default.writeFileSync(configPath, doc.toString());
9414
9439
  }
9415
9440
  let allowlist = {};
9416
9441
  if (import_fs13.default.existsSync(allowlistPath)) {
@@ -9453,24 +9478,24 @@ function setupHermes() {
9453
9478
  }
9454
9479
  function teardownHermes() {
9455
9480
  const homeDir2 = import_os12.default.homedir();
9456
- const configPath2 = hermesConfigPath(homeDir2);
9481
+ const configPath = hermesConfigPath(homeDir2);
9457
9482
  const allowlistPath = hermesAllowlistPath(homeDir2);
9458
- if (!import_fs13.default.existsSync(configPath2)) {
9459
- console.log(import_chalk.default.blue(` \u2139\uFE0F ${configPath2} not found \u2014 nothing to remove`));
9483
+ if (!import_fs13.default.existsSync(configPath)) {
9484
+ console.log(import_chalk.default.blue(` \u2139\uFE0F ${configPath} not found \u2014 nothing to remove`));
9460
9485
  return;
9461
9486
  }
9462
- const raw = import_fs13.default.readFileSync(configPath2, "utf-8");
9487
+ const raw = import_fs13.default.readFileSync(configPath, "utf-8");
9463
9488
  const doc = yaml.parseDocument(raw);
9464
9489
  if (doc.errors.length > 0) {
9465
9490
  console.log(
9466
- import_chalk.default.yellow(` \u26A0\uFE0F Skipping ${configPath2} \u2014 file has YAML parse errors, fix it manually.`)
9491
+ import_chalk.default.yellow(` \u26A0\uFE0F Skipping ${configPath} \u2014 file has YAML parse errors, fix it manually.`)
9467
9492
  );
9468
9493
  } else {
9469
- teardownHermesConfigDoc(doc, configPath2);
9494
+ teardownHermesConfigDoc(doc, configPath);
9470
9495
  }
9471
9496
  teardownHermesAllowlist(allowlistPath);
9472
9497
  }
9473
- function teardownHermesConfigDoc(doc, configPath2) {
9498
+ function teardownHermesConfigDoc(doc, configPath) {
9474
9499
  let anythingChanged = false;
9475
9500
  const current = doc.toJS() ?? {};
9476
9501
  for (const { event } of HERMES_HOOK_PLAN) {
@@ -9492,10 +9517,10 @@ function teardownHermesConfigDoc(doc, configPath2) {
9492
9517
  anythingChanged = true;
9493
9518
  }
9494
9519
  if (anythingChanged) {
9495
- import_fs13.default.writeFileSync(configPath2, doc.toString());
9496
- console.log(import_chalk.default.green(` \u2705 Removed Node9 hooks from ${configPath2}`));
9520
+ import_fs13.default.writeFileSync(configPath, doc.toString());
9521
+ console.log(import_chalk.default.green(` \u2705 Removed Node9 hooks from ${configPath}`));
9497
9522
  } else {
9498
- console.log(import_chalk.default.blue(` \u2139\uFE0F No Node9 hooks found in ${configPath2}`));
9523
+ console.log(import_chalk.default.blue(` \u2139\uFE0F No Node9 hooks found in ${configPath}`));
9499
9524
  }
9500
9525
  }
9501
9526
  function teardownHermesAllowlist(allowlistPath) {
@@ -12110,8 +12135,8 @@ function countScanFiles() {
12110
12135
  const geminiDir = import_path25.default.join(import_os22.default.homedir(), ".gemini", "tmp");
12111
12136
  if (import_fs23.default.existsSync(geminiDir)) {
12112
12137
  try {
12113
- for (const slug of import_fs23.default.readdirSync(geminiDir)) {
12114
- const p = import_path25.default.join(geminiDir, slug);
12138
+ for (const slug2 of import_fs23.default.readdirSync(geminiDir)) {
12139
+ const p = import_path25.default.join(geminiDir, slug2);
12115
12140
  try {
12116
12141
  if (!import_fs23.default.statSync(p).isDirectory()) continue;
12117
12142
  const chatsDir = import_path25.default.join(p, "chats");
@@ -12508,14 +12533,14 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12508
12533
  return result;
12509
12534
  }
12510
12535
  const ruleSources = buildRuleSources();
12511
- for (const slug of slugDirs) {
12512
- const slugPath = import_path25.default.join(tmpDir, slug);
12536
+ for (const slug2 of slugDirs) {
12537
+ const slugPath = import_path25.default.join(tmpDir, slug2);
12513
12538
  try {
12514
12539
  if (!import_fs23.default.statSync(slugPath).isDirectory()) continue;
12515
12540
  } catch {
12516
12541
  continue;
12517
12542
  }
12518
- let projLabel = stripTerminalEscapes(slug).slice(0, 40);
12543
+ let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
12519
12544
  try {
12520
12545
  projLabel = stripTerminalEscapes(
12521
12546
  import_fs23.default.readFileSync(import_path25.default.join(slugPath, ".project_root"), "utf-8").trim()
@@ -13854,7 +13879,7 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
13854
13879
  const hitShieldSet = new Set(
13855
13880
  shieldImpacts.filter((i) => i.totalCatches > 0).map((i) => i.shieldName)
13856
13881
  );
13857
- const zeroHitBuiltins = Object.keys(SHIELDS).filter((name) => !hitShieldSet.has(name)).sort();
13882
+ const zeroHitBuiltins = Object.keys(BUILTIN_SHIELDS).filter((name) => !hitShieldSet.has(name)).sort();
13858
13883
  if (zeroHitBuiltins.length > 0) {
13859
13884
  shieldLines.push(mkLine([""]));
13860
13885
  shieldLines.push(mkLine([zeroHitBuiltins.join(" \xB7 "), import_chalk5.default.dim]));
@@ -18747,20 +18772,20 @@ function getModelContextLimit(model) {
18747
18772
  return 2e5;
18748
18773
  }
18749
18774
  function readSessionUsage() {
18750
- const projectsDir = import_path59.default.join(import_os53.default.homedir(), ".claude", "projects");
18751
- if (!import_fs61.default.existsSync(projectsDir)) return null;
18775
+ const projectsDir = import_path61.default.join(import_os54.default.homedir(), ".claude", "projects");
18776
+ if (!import_fs64.default.existsSync(projectsDir)) return null;
18752
18777
  let latestFile = null;
18753
18778
  let latestMtime = 0;
18754
18779
  try {
18755
- for (const dir of import_fs61.default.readdirSync(projectsDir)) {
18756
- const dirPath = import_path59.default.join(projectsDir, dir);
18780
+ for (const dir of import_fs64.default.readdirSync(projectsDir)) {
18781
+ const dirPath = import_path61.default.join(projectsDir, dir);
18757
18782
  try {
18758
- if (!import_fs61.default.statSync(dirPath).isDirectory()) continue;
18759
- for (const file of import_fs61.default.readdirSync(dirPath)) {
18783
+ if (!import_fs64.default.statSync(dirPath).isDirectory()) continue;
18784
+ for (const file of import_fs64.default.readdirSync(dirPath)) {
18760
18785
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
18761
- const filePath = import_path59.default.join(dirPath, file);
18786
+ const filePath = import_path61.default.join(dirPath, file);
18762
18787
  try {
18763
- const mtime = import_fs61.default.statSync(filePath).mtimeMs;
18788
+ const mtime = import_fs64.default.statSync(filePath).mtimeMs;
18764
18789
  if (mtime > latestMtime) {
18765
18790
  latestMtime = mtime;
18766
18791
  latestFile = filePath;
@@ -18775,7 +18800,7 @@ function readSessionUsage() {
18775
18800
  }
18776
18801
  if (!latestFile) return null;
18777
18802
  try {
18778
- const lines = import_fs61.default.readFileSync(latestFile, "utf-8").split("\n");
18803
+ const lines = import_fs64.default.readFileSync(latestFile, "utf-8").split("\n");
18779
18804
  let lastModel = "";
18780
18805
  let lastInput = 0;
18781
18806
  let lastOutput = 0;
@@ -18800,10 +18825,10 @@ function readSessionUsage() {
18800
18825
  }
18801
18826
  }
18802
18827
  function formatContextStat(stat) {
18803
- const pctColor = stat.fillPct >= 80 ? import_chalk34.default.red : stat.fillPct >= 50 ? import_chalk34.default.yellow : import_chalk34.default.cyan;
18828
+ const pctColor = stat.fillPct >= 80 ? import_chalk35.default.red : stat.fillPct >= 50 ? import_chalk35.default.yellow : import_chalk35.default.cyan;
18804
18829
  const k = (n) => `${Math.round(n / 1e3)}k`;
18805
18830
  const modelShort = stat.model.replace(/@.*$/, "").replace(/-\d{8}$/, "").replace(/^claude-/, "");
18806
- return import_chalk34.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk34.default.dim(
18831
+ return import_chalk35.default.dim("ctx: ") + pctColor(`${stat.fillPct}%`) + import_chalk35.default.dim(
18807
18832
  ` (${k(stat.inputTokens)}/${k(getModelContextLimit(stat.model))} out ${k(stat.outputTokens)} \xB7 ${modelShort})`
18808
18833
  );
18809
18834
  }
@@ -18826,32 +18851,32 @@ function agentLabel(agent, mcpServer, sessionId) {
18826
18851
  const tag = sessionTag(sessionId);
18827
18852
  const tagSuffix = tag ? `\xB7${tag}` : "";
18828
18853
  if (!agent || agent === "Terminal") {
18829
- return mcpServer ? import_chalk34.default.dim(`[\u2192 ${mcpServer}] `) : "";
18854
+ return mcpServer ? import_chalk35.default.dim(`[\u2192 ${mcpServer}] `) : "";
18830
18855
  }
18831
18856
  const short = agent === "Claude Code" ? "Claude" : agent === "Gemini CLI" ? "Gemini" : agent === "Unknown Agent" ? "" : agent.split(" ")[0];
18832
- if (!short) return mcpServer ? import_chalk34.default.dim(`[\u2192 ${mcpServer}] `) : "";
18833
- return mcpServer ? import_chalk34.default.dim(`[${short}${tagSuffix} \u2192 ${mcpServer}] `) : import_chalk34.default.dim(`[${short}${tagSuffix}] `);
18857
+ if (!short) return mcpServer ? import_chalk35.default.dim(`[\u2192 ${mcpServer}] `) : "";
18858
+ return mcpServer ? import_chalk35.default.dim(`[${short}${tagSuffix} \u2192 ${mcpServer}] `) : import_chalk35.default.dim(`[${short}${tagSuffix}] `);
18834
18859
  }
18835
18860
  function formatBase(activity) {
18836
18861
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
18837
18862
  const icon = getIcon(activity.tool);
18838
18863
  const toolName = activity.tool.slice(0, 16).padEnd(16);
18839
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os53.default.homedir(), "~");
18864
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os54.default.homedir(), "~");
18840
18865
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
18841
- return `${import_chalk34.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk34.default.white.bold(toolName)} ${import_chalk34.default.dim(argsPreview)}`;
18866
+ return `${import_chalk35.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk35.default.white.bold(toolName)} ${import_chalk35.default.dim(argsPreview)}`;
18842
18867
  }
18843
18868
  function renderResult(activity, result) {
18844
18869
  const base = formatBase(activity);
18845
18870
  let status;
18846
18871
  if (result.status === "allow") {
18847
- status = import_chalk34.default.green("\u2713 ALLOW");
18872
+ status = import_chalk35.default.green("\u2713 ALLOW");
18848
18873
  } else if (result.status === "dlp") {
18849
- status = import_chalk34.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
18874
+ status = import_chalk35.default.bgRed.white.bold(" \u{1F6E1}\uFE0F DLP ");
18850
18875
  } else {
18851
- status = import_chalk34.default.red("\u2717 BLOCK");
18876
+ status = import_chalk35.default.red("\u2717 BLOCK");
18852
18877
  }
18853
18878
  const cost = result.costEstimate ?? activity.costEstimate;
18854
- const costSuffix = cost == null ? "" : import_chalk34.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
18879
+ const costSuffix = cost == null ? "" : import_chalk35.default.dim(` ~$${cost >= 1e-3 ? cost.toFixed(3) : "0.000"}`);
18855
18880
  if (process.stdout.isTTY) {
18856
18881
  if (pendingShownForId === activity.id && pendingWrappedLines > 1) {
18857
18882
  import_readline6.default.moveCursor(process.stdout, 0, -(pendingWrappedLines - 1));
@@ -18868,19 +18893,19 @@ function renderResult(activity, result) {
18868
18893
  }
18869
18894
  function renderPending(activity) {
18870
18895
  if (!process.stdout.isTTY) return;
18871
- const line = `${formatBase(activity)} ${import_chalk34.default.yellow("\u25CF \u2026")}`;
18896
+ const line = `${formatBase(activity)} ${import_chalk35.default.yellow("\u25CF \u2026")}`;
18872
18897
  pendingShownForId = activity.id;
18873
18898
  pendingWrappedLines = wrappedLineCount(line);
18874
18899
  process.stdout.write(`${line}\r`);
18875
18900
  }
18876
18901
  async function ensureDaemon() {
18877
18902
  let pidPort = null;
18878
- if (import_fs61.default.existsSync(PID_FILE)) {
18903
+ if (import_fs64.default.existsSync(PID_FILE)) {
18879
18904
  try {
18880
- const { port } = JSON.parse(import_fs61.default.readFileSync(PID_FILE, "utf-8"));
18905
+ const { port } = JSON.parse(import_fs64.default.readFileSync(PID_FILE, "utf-8"));
18881
18906
  pidPort = port;
18882
18907
  } catch {
18883
- console.error(import_chalk34.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
18908
+ console.error(import_chalk35.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
18884
18909
  }
18885
18910
  }
18886
18911
  const checkPort = pidPort ?? DAEMON_PORT;
@@ -18891,7 +18916,7 @@ async function ensureDaemon() {
18891
18916
  if (res.ok) return checkPort;
18892
18917
  } catch {
18893
18918
  }
18894
- console.log(import_chalk34.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
18919
+ console.log(import_chalk35.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
18895
18920
  const child = (0, import_child_process14.spawn)(process.execPath, [process.argv[1], "daemon"], {
18896
18921
  detached: true,
18897
18922
  stdio: "ignore",
@@ -18908,7 +18933,7 @@ async function ensureDaemon() {
18908
18933
  } catch {
18909
18934
  }
18910
18935
  }
18911
- console.error(import_chalk34.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
18936
+ console.error(import_chalk35.default.red("\u274C Daemon failed to start. Try: node9 daemon start"));
18912
18937
  process.exit(1);
18913
18938
  }
18914
18939
  function postDecisionHttp(id, decision, authToken, port, opts) {
@@ -18977,7 +19002,7 @@ function buildCardLines(req, localCount = 0) {
18977
19002
  const severityIcon = isBlock ? `${RED}\u{1F6D1}` : `${YELLOW}\u26A0 `;
18978
19003
  const rawDesc = req.riskMetadata?.ruleDescription ?? "";
18979
19004
  const description = rawDesc ? cleanReason(rawDesc) : "";
18980
- const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk34.default.dim(`(${req.agent})`)}` : "";
19005
+ const agentSuffix = req.agent && req.agent !== "Terminal" ? ` ${RESET2}${import_chalk35.default.dim(`(${req.agent})`)}` : "";
18981
19006
  const lines = [
18982
19007
  ``,
18983
19008
  `${BOLD2}${CYAN}\u2554\u2550\u2550 Node9 Approval Required \u2550\u2550\u2557${RESET2}`,
@@ -19033,9 +19058,9 @@ function buildRecoveryCardLines(req) {
19033
19058
  ];
19034
19059
  }
19035
19060
  function readApproversFromDisk() {
19036
- const configPath2 = import_path59.default.join(import_os53.default.homedir(), ".node9", "config.json");
19061
+ const configPath = import_path61.default.join(import_os54.default.homedir(), ".node9", "config.json");
19037
19062
  try {
19038
- const raw = JSON.parse(import_fs61.default.readFileSync(configPath2, "utf-8"));
19063
+ const raw = JSON.parse(import_fs64.default.readFileSync(configPath, "utf-8"));
19039
19064
  const settings = raw.settings ?? {};
19040
19065
  return settings.approvers ?? {};
19041
19066
  } catch {
@@ -19046,20 +19071,20 @@ function approverStatusLine() {
19046
19071
  const a = readApproversFromDisk();
19047
19072
  const fmt = (label2, key) => {
19048
19073
  const on = a[key] !== false;
19049
- return `[${key[0]}]${label2.slice(1)} ${on ? import_chalk34.default.green("\u2713") : import_chalk34.default.dim("\u2717")}`;
19074
+ return `[${key[0]}]${label2.slice(1)} ${on ? import_chalk35.default.green("\u2713") : import_chalk35.default.dim("\u2717")}`;
19050
19075
  };
19051
19076
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
19052
19077
  }
19053
19078
  function toggleApprover(channel) {
19054
- const configPath2 = import_path59.default.join(import_os53.default.homedir(), ".node9", "config.json");
19079
+ const configPath = import_path61.default.join(import_os54.default.homedir(), ".node9", "config.json");
19055
19080
  try {
19056
- const raw = JSON.parse(import_fs61.default.readFileSync(configPath2, "utf-8"));
19081
+ const raw = JSON.parse(import_fs64.default.readFileSync(configPath, "utf-8"));
19057
19082
  const settings = raw.settings ?? {};
19058
19083
  const approvers = settings.approvers ?? {};
19059
19084
  approvers[channel] = approvers[channel] === false;
19060
19085
  settings.approvers = approvers;
19061
19086
  raw.settings = settings;
19062
- import_fs61.default.writeFileSync(configPath2, JSON.stringify(raw, null, 2) + "\n");
19087
+ import_fs64.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
19063
19088
  } catch (err2) {
19064
19089
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
19065
19090
  `);
@@ -19091,7 +19116,7 @@ async function startTail(options = {}) {
19091
19116
  req2.end();
19092
19117
  });
19093
19118
  if (result.ok) {
19094
- console.log(import_chalk34.default.green("\u2713 Flight Recorder buffer cleared."));
19119
+ console.log(import_chalk35.default.green("\u2713 Flight Recorder buffer cleared."));
19095
19120
  } else if (result.code === "ECONNREFUSED") {
19096
19121
  throw new Error("Daemon is not running. Start it with: node9 daemon start");
19097
19122
  } else if (result.code === "ETIMEDOUT") {
@@ -19137,7 +19162,7 @@ async function startTail(options = {}) {
19137
19162
  const channel = name === "n" ? "native" : name === "c" ? "cloud" : name === "t" ? "terminal" : null;
19138
19163
  if (channel) {
19139
19164
  toggleApprover(channel);
19140
- console.log(import_chalk34.default.dim(` Approvers: ${approverStatusLine()}`));
19165
+ console.log(import_chalk35.default.dim(` Approvers: ${approverStatusLine()}`));
19141
19166
  }
19142
19167
  };
19143
19168
  process.stdin.on("keypress", idleKeypressHandler);
@@ -19203,7 +19228,7 @@ async function startTail(options = {}) {
19203
19228
  localAllowCounts.get(req2.toolName) ?? 0
19204
19229
  )
19205
19230
  );
19206
- const decisionStamp = action === "always-allow" ? import_chalk34.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk34.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk34.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk34.default.yellow("\u21A9 REDIRECT AI") : import_chalk34.default.red("\u2717 DENIED");
19231
+ const decisionStamp = action === "always-allow" ? import_chalk35.default.yellow("\u2605 ALWAYS ALLOW") : action === "trust" ? import_chalk35.default.cyan("\u23F1 TRUST 30m") : action === "allow" ? import_chalk35.default.green("\u2713 ALLOWED") : action === "redirect" ? import_chalk35.default.yellow("\u21A9 REDIRECT AI") : import_chalk35.default.red("\u2717 DENIED");
19207
19232
  stampedLines.push(` ${BOLD2}\u2192${RESET2} ${decisionStamp} ${GRAY}(terminal)${RESET2}`, ``);
19208
19233
  for (const line of stampedLines) process.stdout.write(line + "\n");
19209
19234
  process.stdout.write(SHOW_CURSOR);
@@ -19231,8 +19256,8 @@ async function startTail(options = {}) {
19231
19256
  }
19232
19257
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
19233
19258
  try {
19234
- import_fs61.default.appendFileSync(
19235
- import_path59.default.join(import_os53.default.homedir(), ".node9", "hook-debug.log"),
19259
+ import_fs64.default.appendFileSync(
19260
+ import_path61.default.join(import_os54.default.homedir(), ".node9", "hook-debug.log"),
19236
19261
  `[tail] POST /decision failed: ${String(err2)}
19237
19262
  `
19238
19263
  );
@@ -19254,7 +19279,7 @@ async function startTail(options = {}) {
19254
19279
  );
19255
19280
  const stampedLines = buildCardLines(req2, priorCount);
19256
19281
  if (externalDecision) {
19257
- const source = externalDecision === "allow" ? import_chalk34.default.green("\u2713 ALLOWED") : import_chalk34.default.red("\u2717 DENIED");
19282
+ const source = externalDecision === "allow" ? import_chalk35.default.green("\u2713 ALLOWED") : import_chalk35.default.red("\u2717 DENIED");
19258
19283
  stampedLines.push(` ${BOLD2}\u2192${RESET2} ${source} ${GRAY}(external)${RESET2}`, ``);
19259
19284
  }
19260
19285
  for (const line of stampedLines) process.stdout.write(line + "\n");
@@ -19296,31 +19321,31 @@ async function startTail(options = {}) {
19296
19321
  };
19297
19322
  process.stdin.on("keypress", onKeypress);
19298
19323
  }
19299
- const auditLog = import_path59.default.join(import_os53.default.homedir(), ".node9", "audit.log");
19324
+ const auditLog = import_path61.default.join(import_os54.default.homedir(), ".node9", "audit.log");
19300
19325
  try {
19301
- const unackedDlp = import_fs61.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
19326
+ const unackedDlp = import_fs64.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
19302
19327
  if (unackedDlp > 0) {
19303
19328
  console.log("");
19304
19329
  console.log(
19305
- import_chalk34.default.bgRed.white.bold(
19330
+ import_chalk35.default.bgRed.white.bold(
19306
19331
  ` \u26A0\uFE0F DLP ALERT: ${unackedDlp} secret${unackedDlp !== 1 ? "s" : ""} found in Claude response text \u2014 run: node9 dlp `
19307
19332
  )
19308
19333
  );
19309
19334
  }
19310
19335
  } catch {
19311
19336
  }
19312
- console.log(import_chalk34.default.cyan.bold(`
19337
+ console.log(import_chalk35.default.cyan.bold(`
19313
19338
  \u{1F6F0}\uFE0F Node9 tail`));
19314
19339
  if (canApprove) {
19315
- console.log(import_chalk34.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
19316
- console.log(import_chalk34.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
19340
+ console.log(import_chalk35.default.dim("Card: [\u21B5/y] Allow [n] Deny [a] Always [t] Trust 30m"));
19341
+ console.log(import_chalk35.default.dim(`Approvers (toggle): ${approverStatusLine()} [q] quit`));
19317
19342
  }
19318
19343
  const ctxStat = readSessionUsage();
19319
19344
  if (ctxStat) console.log(" " + formatContextStat(ctxStat));
19320
19345
  if (options.history) {
19321
- console.log(import_chalk34.default.dim("Showing history + live events.\n"));
19346
+ console.log(import_chalk35.default.dim("Showing history + live events.\n"));
19322
19347
  } else {
19323
- console.log(import_chalk34.default.dim("Showing live events only. Use --history to include past.\n"));
19348
+ console.log(import_chalk35.default.dim("Showing live events only. Use --history to include past.\n"));
19324
19349
  }
19325
19350
  process.on("SIGINT", () => {
19326
19351
  exitIdleMode();
@@ -19330,7 +19355,7 @@ async function startTail(options = {}) {
19330
19355
  import_readline6.default.clearLine(process.stdout, 0);
19331
19356
  import_readline6.default.cursorTo(process.stdout, 0);
19332
19357
  }
19333
- console.log(import_chalk34.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
19358
+ console.log(import_chalk35.default.dim("\n\u{1F6F0}\uFE0F Disconnected."));
19334
19359
  process.exit(0);
19335
19360
  });
19336
19361
  const STALL_THRESHOLD_MS = 6e4;
@@ -19338,11 +19363,11 @@ async function startTail(options = {}) {
19338
19363
  if (stallWarned) return;
19339
19364
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
19340
19365
  try {
19341
- const auditMtime = import_fs61.default.statSync(auditLog).mtimeMs;
19366
+ const auditMtime = import_fs64.default.statSync(auditLog).mtimeMs;
19342
19367
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
19343
19368
  console.log("");
19344
19369
  console.log(
19345
- import_chalk34.default.yellow(
19370
+ import_chalk35.default.yellow(
19346
19371
  "\u26A0\uFE0F Tail appears stalled \u2014 hooks are firing but no events are arriving. Try: node9 daemon restart"
19347
19372
  )
19348
19373
  );
@@ -19359,7 +19384,7 @@ async function startTail(options = {}) {
19359
19384
  },
19360
19385
  (res) => {
19361
19386
  if (res.statusCode !== 200) {
19362
- console.error(import_chalk34.default.red(`Failed to connect: HTTP ${res.statusCode}`));
19387
+ console.error(import_chalk35.default.red(`Failed to connect: HTTP ${res.statusCode}`));
19363
19388
  process.exit(1);
19364
19389
  }
19365
19390
  if (canApprove) enterIdleMode();
@@ -19390,7 +19415,7 @@ async function startTail(options = {}) {
19390
19415
  import_readline6.default.clearLine(process.stdout, 0);
19391
19416
  import_readline6.default.cursorTo(process.stdout, 0);
19392
19417
  }
19393
- console.log(import_chalk34.default.red("\n\u274C Daemon disconnected."));
19418
+ console.log(import_chalk35.default.red("\n\u274C Daemon disconnected."));
19394
19419
  process.exit(1);
19395
19420
  });
19396
19421
  }
@@ -19403,7 +19428,7 @@ async function startTail(options = {}) {
19403
19428
  const parsed = JSON.parse(rawData);
19404
19429
  const msg = parsed.message ?? "Flight recorder is down \u2014 run: node9 daemon restart";
19405
19430
  console.log("");
19406
- console.log(import_chalk34.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
19431
+ console.log(import_chalk35.default.bgRed.white.bold(` \u26A0\uFE0F ${msg} `));
19407
19432
  } catch {
19408
19433
  }
19409
19434
  return;
@@ -19488,9 +19513,9 @@ async function startTail(options = {}) {
19488
19513
  const rawSummary = data.argsSummary ?? data.tool;
19489
19514
  const summary = shortenPathSummary(rawSummary);
19490
19515
  const fileCount = data.fileCount ?? 0;
19491
- const files = fileCount > 0 ? import_chalk34.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
19516
+ const files = fileCount > 0 ? import_chalk35.default.dim(` \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`) : "";
19492
19517
  process.stdout.write(
19493
- `${import_chalk34.default.dim(time)} ${import_chalk34.default.cyan("\u{1F4F8} snapshot")} ${import_chalk34.default.dim(hash)} ${summary}${files}
19518
+ `${import_chalk35.default.dim(time)} ${import_chalk35.default.cyan("\u{1F4F8} snapshot")} ${import_chalk35.default.dim(hash)} ${summary}${files}
19494
19519
  `
19495
19520
  );
19496
19521
  return;
@@ -19507,36 +19532,36 @@ async function startTail(options = {}) {
19507
19532
  if (event === "execution-result") {
19508
19533
  const exec = data;
19509
19534
  const time = new Date(Date.now()).toLocaleTimeString([], { hour12: false });
19510
- const arrow = exec.isError ? import_chalk34.default.red(" \u21B3 \u2717") : import_chalk34.default.green(" \u21B3 \u2713");
19535
+ const arrow = exec.isError ? import_chalk35.default.red(" \u21B3 \u2717") : import_chalk35.default.green(" \u21B3 \u2713");
19511
19536
  const label2 = agentLabel(exec.agent, exec.mcpServer);
19512
19537
  const tool = (exec.tool ?? "").slice(0, 16);
19513
- const duration = typeof exec.durationMs === "number" ? import_chalk34.default.dim(` (${exec.durationMs}ms)`) : "";
19538
+ const duration = typeof exec.durationMs === "number" ? import_chalk35.default.dim(` (${exec.durationMs}ms)`) : "";
19514
19539
  console.log(
19515
- `${import_chalk34.default.gray(time)} ${arrow} ${label2}${import_chalk34.default.dim(tool)}${import_chalk34.default.dim(" completed")}${duration}`
19540
+ `${import_chalk35.default.gray(time)} ${arrow} ${label2}${import_chalk35.default.dim(tool)}${import_chalk35.default.dim(" completed")}${duration}`
19516
19541
  );
19517
19542
  }
19518
19543
  }
19519
19544
  req.on("error", (err2) => {
19520
19545
  const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
19521
- console.error(import_chalk34.default.red(`
19546
+ console.error(import_chalk35.default.red(`
19522
19547
  \u274C ${msg}`));
19523
19548
  process.exit(1);
19524
19549
  });
19525
19550
  }
19526
- var import_http3, import_chalk34, import_fs61, import_os53, import_path59, import_readline6, import_child_process14, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
19551
+ var import_http3, import_chalk35, import_fs64, import_os54, import_path61, import_readline6, import_child_process14, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
19527
19552
  var init_tail = __esm({
19528
19553
  "src/tui/tail.ts"() {
19529
19554
  "use strict";
19530
19555
  import_http3 = __toESM(require("http"));
19531
- import_chalk34 = __toESM(require("chalk"));
19532
- import_fs61 = __toESM(require("fs"));
19533
- import_os53 = __toESM(require("os"));
19534
- import_path59 = __toESM(require("path"));
19556
+ import_chalk35 = __toESM(require("chalk"));
19557
+ import_fs64 = __toESM(require("fs"));
19558
+ import_os54 = __toESM(require("os"));
19559
+ import_path61 = __toESM(require("path"));
19535
19560
  import_readline6 = __toESM(require("readline"));
19536
19561
  import_child_process14 = require("child_process");
19537
19562
  init_daemon2();
19538
19563
  init_daemon();
19539
- PID_FILE = import_path59.default.join(import_os53.default.homedir(), ".node9", "daemon.pid");
19564
+ PID_FILE = import_path61.default.join(import_os54.default.homedir(), ".node9", "daemon.pid");
19540
19565
  ICONS = {
19541
19566
  bash: "\u{1F4BB}",
19542
19567
  shell: "\u{1F4BB}",
@@ -19658,9 +19683,9 @@ function formatTimeLeft(resetsAt) {
19658
19683
  return ` (${m}m left)`;
19659
19684
  }
19660
19685
  function safeReadJson(filePath) {
19661
- if (!import_fs62.default.existsSync(filePath)) return null;
19686
+ if (!import_fs65.default.existsSync(filePath)) return null;
19662
19687
  try {
19663
- return JSON.parse(import_fs62.default.readFileSync(filePath, "utf-8"));
19688
+ return JSON.parse(import_fs65.default.readFileSync(filePath, "utf-8"));
19664
19689
  } catch {
19665
19690
  return null;
19666
19691
  }
@@ -19681,12 +19706,12 @@ function countHooksInFile(filePath) {
19681
19706
  return Object.keys(cfg.hooks).length;
19682
19707
  }
19683
19708
  function countRulesInDir(rulesDir) {
19684
- if (!import_fs62.default.existsSync(rulesDir)) return 0;
19709
+ if (!import_fs65.default.existsSync(rulesDir)) return 0;
19685
19710
  let count = 0;
19686
19711
  try {
19687
- for (const entry of import_fs62.default.readdirSync(rulesDir, { withFileTypes: true })) {
19712
+ for (const entry of import_fs65.default.readdirSync(rulesDir, { withFileTypes: true })) {
19688
19713
  if (entry.isDirectory()) {
19689
- count += countRulesInDir(import_path60.default.join(rulesDir, entry.name));
19714
+ count += countRulesInDir(import_path62.default.join(rulesDir, entry.name));
19690
19715
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
19691
19716
  count++;
19692
19717
  }
@@ -19697,46 +19722,46 @@ function countRulesInDir(rulesDir) {
19697
19722
  }
19698
19723
  function isSamePath(a, b) {
19699
19724
  try {
19700
- return import_path60.default.resolve(a) === import_path60.default.resolve(b);
19725
+ return import_path62.default.resolve(a) === import_path62.default.resolve(b);
19701
19726
  } catch {
19702
19727
  return false;
19703
19728
  }
19704
19729
  }
19705
19730
  function countConfigs(cwd) {
19706
- const homeDir2 = import_os54.default.homedir();
19707
- const claudeDir = import_path60.default.join(homeDir2, ".claude");
19731
+ const homeDir2 = import_os55.default.homedir();
19732
+ const claudeDir = import_path62.default.join(homeDir2, ".claude");
19708
19733
  let claudeMdCount = 0;
19709
19734
  let rulesCount = 0;
19710
19735
  let hooksCount = 0;
19711
19736
  const userMcpServers = /* @__PURE__ */ new Set();
19712
19737
  const projectMcpServers = /* @__PURE__ */ new Set();
19713
- if (import_fs62.default.existsSync(import_path60.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
19714
- rulesCount += countRulesInDir(import_path60.default.join(claudeDir, "rules"));
19715
- const userSettings = import_path60.default.join(claudeDir, "settings.json");
19738
+ if (import_fs65.default.existsSync(import_path62.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
19739
+ rulesCount += countRulesInDir(import_path62.default.join(claudeDir, "rules"));
19740
+ const userSettings = import_path62.default.join(claudeDir, "settings.json");
19716
19741
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
19717
19742
  hooksCount += countHooksInFile(userSettings);
19718
- const userClaudeJson = import_path60.default.join(homeDir2, ".claude.json");
19743
+ const userClaudeJson = import_path62.default.join(homeDir2, ".claude.json");
19719
19744
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
19720
19745
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
19721
19746
  userMcpServers.delete(name);
19722
19747
  }
19723
19748
  if (cwd) {
19724
- if (import_fs62.default.existsSync(import_path60.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
19725
- if (import_fs62.default.existsSync(import_path60.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
19726
- const projectClaudeDir = import_path60.default.join(cwd, ".claude");
19749
+ if (import_fs65.default.existsSync(import_path62.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
19750
+ if (import_fs65.default.existsSync(import_path62.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
19751
+ const projectClaudeDir = import_path62.default.join(cwd, ".claude");
19727
19752
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
19728
19753
  if (!overlapsUserScope) {
19729
- if (import_fs62.default.existsSync(import_path60.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
19730
- rulesCount += countRulesInDir(import_path60.default.join(projectClaudeDir, "rules"));
19731
- const projSettings = import_path60.default.join(projectClaudeDir, "settings.json");
19754
+ if (import_fs65.default.existsSync(import_path62.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
19755
+ rulesCount += countRulesInDir(import_path62.default.join(projectClaudeDir, "rules"));
19756
+ const projSettings = import_path62.default.join(projectClaudeDir, "settings.json");
19732
19757
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
19733
19758
  hooksCount += countHooksInFile(projSettings);
19734
19759
  }
19735
- if (import_fs62.default.existsSync(import_path60.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19736
- const localSettings = import_path60.default.join(projectClaudeDir, "settings.local.json");
19760
+ if (import_fs65.default.existsSync(import_path62.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19761
+ const localSettings = import_path62.default.join(projectClaudeDir, "settings.local.json");
19737
19762
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
19738
19763
  hooksCount += countHooksInFile(localSettings);
19739
- const mcpJsonServers = getMcpServerNames(import_path60.default.join(cwd, ".mcp.json"));
19764
+ const mcpJsonServers = getMcpServerNames(import_path62.default.join(cwd, ".mcp.json"));
19740
19765
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
19741
19766
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
19742
19767
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -19769,12 +19794,12 @@ function readActiveShieldsHud() {
19769
19794
  return shieldsCache.value;
19770
19795
  }
19771
19796
  try {
19772
- const shieldsPath = import_path60.default.join(import_os54.default.homedir(), ".node9", "shields.json");
19773
- if (!import_fs62.default.existsSync(shieldsPath)) {
19797
+ const shieldsPath = import_path62.default.join(import_os55.default.homedir(), ".node9", "shields.json");
19798
+ if (!import_fs65.default.existsSync(shieldsPath)) {
19774
19799
  shieldsCache = { value: [], ts: now };
19775
19800
  return [];
19776
19801
  }
19777
- const parsed = JSON.parse(import_fs62.default.readFileSync(shieldsPath, "utf-8"));
19802
+ const parsed = JSON.parse(import_fs65.default.readFileSync(shieldsPath, "utf-8"));
19778
19803
  if (!Array.isArray(parsed.active)) {
19779
19804
  shieldsCache = { value: [], ts: now };
19780
19805
  return [];
@@ -19876,17 +19901,17 @@ function renderContextLine(stdin) {
19876
19901
  async function main() {
19877
19902
  try {
19878
19903
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
19879
- if (import_fs62.default.existsSync(import_path60.default.join(import_os54.default.homedir(), ".node9", "hud-debug"))) {
19904
+ if (import_fs65.default.existsSync(import_path62.default.join(import_os55.default.homedir(), ".node9", "hud-debug"))) {
19880
19905
  try {
19881
- const logPath = import_path60.default.join(import_os54.default.homedir(), ".node9", "hud-debug.log");
19906
+ const logPath = import_path62.default.join(import_os55.default.homedir(), ".node9", "hud-debug.log");
19882
19907
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
19883
19908
  let size = 0;
19884
19909
  try {
19885
- size = import_fs62.default.statSync(logPath).size;
19910
+ size = import_fs65.default.statSync(logPath).size;
19886
19911
  } catch {
19887
19912
  }
19888
19913
  if (size < MAX_LOG_SIZE) {
19889
- import_fs62.default.appendFileSync(
19914
+ import_fs65.default.appendFileSync(
19890
19915
  logPath,
19891
19916
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
19892
19917
  );
@@ -19906,12 +19931,12 @@ async function main() {
19906
19931
  const showEnvCounts = (() => {
19907
19932
  try {
19908
19933
  const cwd = stdin.cwd ?? process.cwd();
19909
- for (const configPath2 of [
19910
- import_path60.default.join(cwd, "node9.config.json"),
19911
- import_path60.default.join(import_os54.default.homedir(), ".node9", "config.json")
19934
+ for (const configPath of [
19935
+ import_path62.default.join(cwd, "node9.config.json"),
19936
+ import_path62.default.join(import_os55.default.homedir(), ".node9", "config.json")
19912
19937
  ]) {
19913
- if (!import_fs62.default.existsSync(configPath2)) continue;
19914
- const cfg = JSON.parse(import_fs62.default.readFileSync(configPath2, "utf-8"));
19938
+ if (!import_fs65.default.existsSync(configPath)) continue;
19939
+ const cfg = JSON.parse(import_fs65.default.readFileSync(configPath, "utf-8"));
19915
19940
  const hud = cfg.settings?.hud;
19916
19941
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
19917
19942
  }
@@ -19929,13 +19954,13 @@ async function main() {
19929
19954
  renderOffline();
19930
19955
  }
19931
19956
  }
19932
- var import_fs62, import_path60, import_os54, import_http4, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
19957
+ var import_fs65, import_path62, import_os55, import_http4, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
19933
19958
  var init_hud = __esm({
19934
19959
  "src/cli/hud.ts"() {
19935
19960
  "use strict";
19936
- import_fs62 = __toESM(require("fs"));
19937
- import_path60 = __toESM(require("path"));
19938
- import_os54 = __toESM(require("os"));
19961
+ import_fs65 = __toESM(require("fs"));
19962
+ import_path62 = __toESM(require("path"));
19963
+ import_os55 = __toESM(require("os"));
19939
19964
  import_http4 = __toESM(require("http"));
19940
19965
  init_daemon();
19941
19966
  RESET3 = "\x1B[0m";
@@ -19961,10 +19986,10 @@ var import_commander = require("commander");
19961
19986
  init_core();
19962
19987
  init_setup();
19963
19988
  init_daemon2();
19964
- var import_chalk35 = __toESM(require("chalk"));
19965
- var import_fs63 = __toESM(require("fs"));
19966
- var import_path61 = __toESM(require("path"));
19967
- var import_os55 = __toESM(require("os"));
19989
+ var import_chalk36 = __toESM(require("chalk"));
19990
+ var import_fs66 = __toESM(require("fs"));
19991
+ var import_path63 = __toESM(require("path"));
19992
+ var import_os56 = __toESM(require("os"));
19968
19993
  var import_child_process15 = require("child_process");
19969
19994
  var import_prompts2 = require("@inquirer/prompts");
19970
19995
 
@@ -21593,8 +21618,134 @@ function registerLogCommand(program2) {
21593
21618
 
21594
21619
  // src/cli/commands/shield.ts
21595
21620
  var import_chalk10 = __toESM(require("chalk"));
21621
+ var import_fs46 = __toESM(require("fs"));
21622
+ init_shields();
21623
+
21624
+ // src/shields/build.ts
21625
+ function escapeRegex(s) {
21626
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
21627
+ }
21628
+ function slug(s) {
21629
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "rule";
21630
+ }
21631
+ var B = "[\\s/\\\\]";
21632
+ var SEP = "[/\\\\]";
21633
+ function pathToRegexFragment(rawPath) {
21634
+ const tail = rawPath.trim().replace(/^~[\\/]?/, "").replace(/^\$\{?HOME\}?[\\/]?/, "").replace(/^\/(?:home|Users)\/[^\\/]+[\\/]?/, "").replace(/^[\\/]+/, "").replace(/[\\/]+$/, "");
21635
+ const segments = tail.split(/[\\/]+/).filter(Boolean).map(escapeRegex);
21636
+ if (segments.length === 0) return "";
21637
+ return `(^|${B})${segments.join(SEP)}(${B}|$)`;
21638
+ }
21639
+ function toolRule(tool, verdict, reason) {
21640
+ return {
21641
+ name: `${verdict}-${slug(tool)}`,
21642
+ tool,
21643
+ conditions: [],
21644
+ verdict,
21645
+ reason: reason ?? `${tool} is restricted by this shield`
21646
+ };
21647
+ }
21648
+ function pathRules(rawPath, verdict, reason) {
21649
+ const value = pathToRegexFragment(rawPath);
21650
+ if (!value) return [];
21651
+ const why = reason ?? `Accessing ${rawPath} is restricted by this shield`;
21652
+ const s = slug(rawPath);
21653
+ return [
21654
+ {
21655
+ name: `${verdict}-path-${s}-bash`,
21656
+ tool: "bash",
21657
+ conditions: [{ field: "command", op: "matches", value }],
21658
+ verdict,
21659
+ reason: why
21660
+ },
21661
+ {
21662
+ name: `${verdict}-path-${s}-anytool`,
21663
+ tool: "*",
21664
+ conditions: [{ field: "file_path", op: "matches", value }],
21665
+ verdict,
21666
+ reason: why
21667
+ }
21668
+ ];
21669
+ }
21670
+ function buildShield(input) {
21671
+ const smartRules = [
21672
+ ...(input.blockTools ?? []).map((t) => toolRule(t, "block")),
21673
+ ...(input.reviewTools ?? []).map((t) => toolRule(t, "review")),
21674
+ ...(input.blockPaths ?? []).flatMap((p) => pathRules(p, "block")),
21675
+ ...(input.reviewPaths ?? []).flatMap((p) => pathRules(p, "review"))
21676
+ ];
21677
+ return {
21678
+ name: input.name,
21679
+ description: input.description ?? `Custom shield "${input.name}" created with node9 shield create`,
21680
+ aliases: input.aliases ?? [],
21681
+ smartRules,
21682
+ dangerousWords: []
21683
+ };
21684
+ }
21685
+
21686
+ // src/shields/create.ts
21687
+ var import_fs45 = __toESM(require("fs"));
21688
+ var import_path43 = __toESM(require("path"));
21689
+ init_dist();
21596
21690
  init_shields();
21597
21691
  init_audit();
21692
+ function builtinNames() {
21693
+ const names = /* @__PURE__ */ new Set();
21694
+ for (const def of Object.values(BUILTIN_SHIELDS)) {
21695
+ names.add(def.name.toLowerCase());
21696
+ for (const a of def.aliases ?? []) names.add(a.toLowerCase());
21697
+ }
21698
+ return names;
21699
+ }
21700
+ function createShield(def, opts = {}) {
21701
+ const name = def.name;
21702
+ if (builtinNames().has(name.toLowerCase())) {
21703
+ return {
21704
+ ok: false,
21705
+ error: `"${name}" is a built-in shield \u2014 choose a different name (a user shield with this name would shadow the built-in).`
21706
+ };
21707
+ }
21708
+ const filePath = import_path43.default.join(USER_SHIELDS_DIR_PATH, `${name}.json`);
21709
+ if (!opts.overwrite && import_fs45.default.existsSync(filePath)) {
21710
+ return {
21711
+ ok: false,
21712
+ error: `Shield "${name}" already exists at ${filePath}. Pass --overwrite to replace it.`
21713
+ };
21714
+ }
21715
+ if (def.smartRules.length === 0) {
21716
+ return {
21717
+ ok: false,
21718
+ error: "Shield has no rules \u2014 add at least one --block/--review tool or path."
21719
+ };
21720
+ }
21721
+ if (opts.viaMcp && def.smartRules.some((r) => r.verdict === "allow")) {
21722
+ return {
21723
+ ok: false,
21724
+ error: "allow-verdict rules are not permitted over MCP (they would weaken node9). Use the CLI."
21725
+ };
21726
+ }
21727
+ const v = validateShieldDefinition(def);
21728
+ if ("error" in v) {
21729
+ return { ok: false, error: v.error };
21730
+ }
21731
+ installShield(name, def);
21732
+ let enabled = false;
21733
+ if (opts.enable) {
21734
+ const active = readActiveShields();
21735
+ if (!active.includes(name)) writeActiveShields([...active, name]);
21736
+ enabled = true;
21737
+ }
21738
+ appendConfigAudit({
21739
+ event: "shield-create",
21740
+ shield: name,
21741
+ via: opts.viaMcp ? "mcp" : "cli",
21742
+ enabled
21743
+ });
21744
+ return { ok: true, path: filePath, enabled, ruleCount: def.smartRules.length };
21745
+ }
21746
+
21747
+ // src/cli/commands/shield.ts
21748
+ init_audit();
21598
21749
  init_config();
21599
21750
 
21600
21751
  // src/utils/https-fetch.ts
@@ -21898,6 +22049,73 @@ function registerShieldCommand(program2) {
21898
22049
  process.exit(1);
21899
22050
  });
21900
22051
  });
22052
+ const collect = (val, prev) => [...prev, val];
22053
+ shieldCmd.command("create <name>").description("Author a new shield from block/review tools and paths").option("--desc <text>", "Shield description").option("--block-tool <tool>", "Block a tool entirely (repeatable)", collect, []).option("--review-tool <tool>", "Require approval for a tool (repeatable)", collect, []).option("--block-path <path>", "Block access to a path (repeatable)", collect, []).option("--review-path <path>", "Require approval to access a path (repeatable)", collect, []).option("--from-file <path>", "Build from a shield JSON file (overrides inline flags)").option("--enable", "Activate the shield immediately").option("--overwrite", "Replace an existing user shield of the same name").action(
22054
+ (name, opts) => {
22055
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
22056
+ console.error(
22057
+ import_chalk10.default.red(
22058
+ `
22059
+ \u274C Invalid shield name: only alphanumeric characters, hyphens, and underscores are allowed
22060
+ `
22061
+ )
22062
+ );
22063
+ process.exit(1);
22064
+ }
22065
+ let def;
22066
+ if (opts.fromFile) {
22067
+ let raw;
22068
+ try {
22069
+ raw = JSON.parse(import_fs46.default.readFileSync(opts.fromFile, "utf-8"));
22070
+ } catch (err2) {
22071
+ console.error(
22072
+ import_chalk10.default.red(`
22073
+ \u274C Could not read/parse ${opts.fromFile}: ${String(err2)}
22074
+ `)
22075
+ );
22076
+ process.exit(1);
22077
+ return;
22078
+ }
22079
+ def = { ...raw, name };
22080
+ } else {
22081
+ def = buildShield({
22082
+ name,
22083
+ description: opts.desc,
22084
+ blockTools: opts.blockTool,
22085
+ reviewTools: opts.reviewTool,
22086
+ blockPaths: opts.blockPath,
22087
+ reviewPaths: opts.reviewPath
22088
+ });
22089
+ }
22090
+ const res = createShield(def, {
22091
+ enable: opts.enable,
22092
+ overwrite: opts.overwrite,
22093
+ viaMcp: false
22094
+ });
22095
+ if (!res.ok) {
22096
+ console.error(import_chalk10.default.red(`
22097
+ \u274C ${res.error}
22098
+ `));
22099
+ process.exit(1);
22100
+ return;
22101
+ }
22102
+ console.log(
22103
+ import_chalk10.default.green(`
22104
+ \u2705 Shield "${name}" created`) + import_chalk10.default.gray(` \u2014 ${res.ruleCount} rule(s) \u2192 ${res.path}`)
22105
+ );
22106
+ if (res.enabled) {
22107
+ console.log(import_chalk10.default.gray(` Active now.`));
22108
+ } else {
22109
+ console.log(
22110
+ import_chalk10.default.gray(` Activate it with: ${import_chalk10.default.cyan(`node9 shield enable ${name}`)}`)
22111
+ );
22112
+ }
22113
+ console.log(
22114
+ import_chalk10.default.gray(` Preview a call: ${import_chalk10.default.cyan(`node9 explain bash "<command>"`)}
22115
+ `)
22116
+ );
22117
+ }
22118
+ );
21901
22119
  }
21902
22120
  function registerConfigShowCommand(program2) {
21903
22121
  program2.command("config show").description(
@@ -21968,8 +22186,8 @@ function registerConfigShowCommand(program2) {
21968
22186
 
21969
22187
  // src/cli/commands/doctor.ts
21970
22188
  var import_chalk11 = __toESM(require("chalk"));
21971
- var import_fs45 = __toESM(require("fs"));
21972
- var import_path43 = __toESM(require("path"));
22189
+ var import_fs47 = __toESM(require("fs"));
22190
+ var import_path44 = __toESM(require("path"));
21973
22191
  var import_os40 = __toESM(require("os"));
21974
22192
  var import_child_process8 = require("child_process");
21975
22193
  init_daemon();
@@ -22023,10 +22241,10 @@ function registerDoctorCommand(program2, version2) {
22023
22241
  );
22024
22242
  }
22025
22243
  section("Configuration");
22026
- const globalConfigPath = import_path43.default.join(homeDir2, ".node9", "config.json");
22027
- if (import_fs45.default.existsSync(globalConfigPath)) {
22244
+ const globalConfigPath = import_path44.default.join(homeDir2, ".node9", "config.json");
22245
+ if (import_fs47.default.existsSync(globalConfigPath)) {
22028
22246
  try {
22029
- JSON.parse(import_fs45.default.readFileSync(globalConfigPath, "utf-8"));
22247
+ JSON.parse(import_fs47.default.readFileSync(globalConfigPath, "utf-8"));
22030
22248
  pass("~/.node9/config.json found and valid");
22031
22249
  } catch {
22032
22250
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -22034,10 +22252,10 @@ function registerDoctorCommand(program2, version2) {
22034
22252
  } else {
22035
22253
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
22036
22254
  }
22037
- const projectConfigPath = import_path43.default.join(process.cwd(), "node9.config.json");
22038
- if (import_fs45.default.existsSync(projectConfigPath)) {
22255
+ const projectConfigPath = import_path44.default.join(process.cwd(), "node9.config.json");
22256
+ if (import_fs47.default.existsSync(projectConfigPath)) {
22039
22257
  try {
22040
- JSON.parse(import_fs45.default.readFileSync(projectConfigPath, "utf-8"));
22258
+ JSON.parse(import_fs47.default.readFileSync(projectConfigPath, "utf-8"));
22041
22259
  pass("node9.config.json found and valid (project)");
22042
22260
  } catch {
22043
22261
  fail(
@@ -22046,8 +22264,8 @@ function registerDoctorCommand(program2, version2) {
22046
22264
  );
22047
22265
  }
22048
22266
  }
22049
- const credsPath = import_path43.default.join(homeDir2, ".node9", "credentials.json");
22050
- if (import_fs45.default.existsSync(credsPath)) {
22267
+ const credsPath = import_path44.default.join(homeDir2, ".node9", "credentials.json");
22268
+ if (import_fs47.default.existsSync(credsPath)) {
22051
22269
  pass("Cloud credentials found (~/.node9/credentials.json)");
22052
22270
  } else {
22053
22271
  warn(
@@ -22091,7 +22309,7 @@ function registerDoctorCommand(program2, version2) {
22091
22309
  try {
22092
22310
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
22093
22311
  const cfg = getConfig();
22094
- const creds = import_fs45.default.existsSync(import_path43.default.join(import_os40.default.homedir(), ".node9", "credentials.json"));
22312
+ const creds = import_fs47.default.existsSync(import_path44.default.join(import_os40.default.homedir(), ".node9", "credentials.json"));
22095
22313
  if (!creds) {
22096
22314
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
22097
22315
  } else if (!cfg.settings.approvers.cloud) {
@@ -22141,8 +22359,8 @@ function registerDoctorCommand(program2, version2) {
22141
22359
 
22142
22360
  // src/cli/commands/audit.ts
22143
22361
  var import_chalk12 = __toESM(require("chalk"));
22144
- var import_fs46 = __toESM(require("fs"));
22145
- var import_path44 = __toESM(require("path"));
22362
+ var import_fs48 = __toESM(require("fs"));
22363
+ var import_path45 = __toESM(require("path"));
22146
22364
  var import_os41 = __toESM(require("os"));
22147
22365
  function formatRelativeTime(timestamp) {
22148
22366
  const diff = Date.now() - new Date(timestamp).getTime();
@@ -22156,14 +22374,14 @@ function formatRelativeTime(timestamp) {
22156
22374
  }
22157
22375
  function registerAuditCommand(program2) {
22158
22376
  program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
22159
- const logPath = import_path44.default.join(import_os41.default.homedir(), ".node9", "audit.log");
22160
- if (!import_fs46.default.existsSync(logPath)) {
22377
+ const logPath = import_path45.default.join(import_os41.default.homedir(), ".node9", "audit.log");
22378
+ if (!import_fs48.default.existsSync(logPath)) {
22161
22379
  console.log(
22162
22380
  import_chalk12.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
22163
22381
  );
22164
22382
  return;
22165
22383
  }
22166
- const raw = import_fs46.default.readFileSync(logPath, "utf-8");
22384
+ const raw = import_fs48.default.readFileSync(logPath, "utf-8");
22167
22385
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
22168
22386
  let entries = lines.flatMap((line) => {
22169
22387
  try {
@@ -22219,9 +22437,9 @@ function registerAuditCommand(program2) {
22219
22437
  var import_chalk13 = __toESM(require("chalk"));
22220
22438
 
22221
22439
  // src/cli/aggregate/report-audit.ts
22222
- var import_fs47 = __toESM(require("fs"));
22440
+ var import_fs49 = __toESM(require("fs"));
22223
22441
  var import_os42 = __toESM(require("os"));
22224
- var import_path45 = __toESM(require("path"));
22442
+ var import_path46 = __toESM(require("path"));
22225
22443
  init_costSync();
22226
22444
  init_litellm();
22227
22445
  init_cost_codex();
@@ -22304,8 +22522,8 @@ function getDateRange(period, now) {
22304
22522
  }
22305
22523
  }
22306
22524
  function parseAuditLog(logPath) {
22307
- if (!import_fs47.default.existsSync(logPath)) return [];
22308
- const raw = import_fs47.default.readFileSync(logPath, "utf-8");
22525
+ if (!import_fs49.default.existsSync(logPath)) return [];
22526
+ const raw = import_fs49.default.readFileSync(logPath, "utf-8");
22309
22527
  return raw.split("\n").flatMap((line) => {
22310
22528
  if (!line.trim()) return [];
22311
22529
  try {
@@ -22352,25 +22570,25 @@ function freezeClaudeCost(acc) {
22352
22570
  };
22353
22571
  }
22354
22572
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22355
- const projPath = import_path45.default.join(projectsDir, proj);
22573
+ const projPath = import_path46.default.join(projectsDir, proj);
22356
22574
  let files;
22357
22575
  try {
22358
- const stat = import_fs47.default.statSync(projPath);
22576
+ const stat = import_fs49.default.statSync(projPath);
22359
22577
  if (!stat.isDirectory()) return;
22360
- files = import_fs47.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
22578
+ files = import_fs49.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
22361
22579
  } catch {
22362
22580
  return;
22363
22581
  }
22364
22582
  const startMs = start.getTime();
22365
22583
  for (const file of files) {
22366
- const filePath = import_path45.default.join(projPath, file);
22584
+ const filePath = import_path46.default.join(projPath, file);
22367
22585
  try {
22368
- if (import_fs47.default.statSync(filePath).mtimeMs < startMs) continue;
22586
+ if (import_fs49.default.statSync(filePath).mtimeMs < startMs) continue;
22369
22587
  } catch {
22370
22588
  continue;
22371
22589
  }
22372
22590
  try {
22373
- const raw = import_fs47.default.readFileSync(filePath, "utf-8");
22591
+ const raw = import_fs49.default.readFileSync(filePath, "utf-8");
22374
22592
  for (const line of raw.split("\n")) {
22375
22593
  if (!line.trim()) continue;
22376
22594
  let entry;
@@ -22420,10 +22638,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22420
22638
  }
22421
22639
  function loadClaudeCost(start, end, projectsDir) {
22422
22640
  const acc = emptyClaudeCostAccumulator();
22423
- if (!import_fs47.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
22641
+ if (!import_fs49.default.existsSync(projectsDir)) return freezeClaudeCost(acc);
22424
22642
  let dirs;
22425
22643
  try {
22426
- dirs = import_fs47.default.readdirSync(projectsDir);
22644
+ dirs = import_fs49.default.readdirSync(projectsDir);
22427
22645
  } catch {
22428
22646
  return freezeClaudeCost(acc);
22429
22647
  }
@@ -22435,7 +22653,7 @@ function loadClaudeCost(start, end, projectsDir) {
22435
22653
  function processCodexCostFile(filePath, start, end, acc) {
22436
22654
  let lines;
22437
22655
  try {
22438
- lines = import_fs47.default.readFileSync(filePath, "utf-8").split("\n");
22656
+ lines = import_fs49.default.readFileSync(filePath, "utf-8").split("\n");
22439
22657
  } catch {
22440
22658
  return;
22441
22659
  }
@@ -22490,31 +22708,31 @@ function processCodexCostFile(filePath, start, end, acc) {
22490
22708
  }
22491
22709
  function listCodexSessionFiles2(sessionsBase) {
22492
22710
  const jsonlFiles = [];
22493
- if (!import_fs47.default.existsSync(sessionsBase)) return jsonlFiles;
22711
+ if (!import_fs49.default.existsSync(sessionsBase)) return jsonlFiles;
22494
22712
  try {
22495
- for (const year of import_fs47.default.readdirSync(sessionsBase)) {
22496
- const yearPath = import_path45.default.join(sessionsBase, year);
22713
+ for (const year of import_fs49.default.readdirSync(sessionsBase)) {
22714
+ const yearPath = import_path46.default.join(sessionsBase, year);
22497
22715
  try {
22498
- if (!import_fs47.default.statSync(yearPath).isDirectory()) continue;
22716
+ if (!import_fs49.default.statSync(yearPath).isDirectory()) continue;
22499
22717
  } catch {
22500
22718
  continue;
22501
22719
  }
22502
- for (const month of import_fs47.default.readdirSync(yearPath)) {
22503
- const monthPath = import_path45.default.join(yearPath, month);
22720
+ for (const month of import_fs49.default.readdirSync(yearPath)) {
22721
+ const monthPath = import_path46.default.join(yearPath, month);
22504
22722
  try {
22505
- if (!import_fs47.default.statSync(monthPath).isDirectory()) continue;
22723
+ if (!import_fs49.default.statSync(monthPath).isDirectory()) continue;
22506
22724
  } catch {
22507
22725
  continue;
22508
22726
  }
22509
- for (const day of import_fs47.default.readdirSync(monthPath)) {
22510
- const dayPath = import_path45.default.join(monthPath, day);
22727
+ for (const day of import_fs49.default.readdirSync(monthPath)) {
22728
+ const dayPath = import_path46.default.join(monthPath, day);
22511
22729
  try {
22512
- if (!import_fs47.default.statSync(dayPath).isDirectory()) continue;
22730
+ if (!import_fs49.default.statSync(dayPath).isDirectory()) continue;
22513
22731
  } catch {
22514
22732
  continue;
22515
22733
  }
22516
- for (const file of import_fs47.default.readdirSync(dayPath)) {
22517
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path45.default.join(dayPath, file));
22734
+ for (const file of import_fs49.default.readdirSync(dayPath)) {
22735
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path46.default.join(dayPath, file));
22518
22736
  }
22519
22737
  }
22520
22738
  }
@@ -22579,13 +22797,13 @@ function freezeGeminiCost(acc) {
22579
22797
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
22580
22798
  const startMs = start.getTime();
22581
22799
  try {
22582
- if (import_fs47.default.statSync(filePath).mtimeMs < startMs) return;
22800
+ if (import_fs49.default.statSync(filePath).mtimeMs < startMs) return;
22583
22801
  } catch {
22584
22802
  return;
22585
22803
  }
22586
22804
  let raw;
22587
22805
  try {
22588
- raw = import_fs47.default.readFileSync(filePath, "utf-8");
22806
+ raw = import_fs49.default.readFileSync(filePath, "utf-8");
22589
22807
  } catch {
22590
22808
  return;
22591
22809
  }
@@ -22634,30 +22852,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
22634
22852
  const out = [];
22635
22853
  let dirs;
22636
22854
  try {
22637
- if (!import_fs47.default.statSync(geminiTmpDir2).isDirectory()) return out;
22638
- dirs = import_fs47.default.readdirSync(geminiTmpDir2);
22855
+ if (!import_fs49.default.statSync(geminiTmpDir2).isDirectory()) return out;
22856
+ dirs = import_fs49.default.readdirSync(geminiTmpDir2);
22639
22857
  } catch {
22640
22858
  return out;
22641
22859
  }
22642
22860
  for (const proj of dirs) {
22643
- const chatsDir = import_path45.default.join(geminiTmpDir2, proj, "chats");
22861
+ const chatsDir = import_path46.default.join(geminiTmpDir2, proj, "chats");
22644
22862
  let files;
22645
22863
  try {
22646
- if (!import_fs47.default.statSync(chatsDir).isDirectory()) continue;
22647
- files = import_fs47.default.readdirSync(chatsDir);
22864
+ if (!import_fs49.default.statSync(chatsDir).isDirectory()) continue;
22865
+ files = import_fs49.default.readdirSync(chatsDir);
22648
22866
  } catch {
22649
22867
  continue;
22650
22868
  }
22651
22869
  for (const f of files) {
22652
22870
  if (!f.endsWith(".jsonl")) continue;
22653
- out.push({ projectKey: proj, file: import_path45.default.join(chatsDir, f) });
22871
+ out.push({ projectKey: proj, file: import_path46.default.join(chatsDir, f) });
22654
22872
  }
22655
22873
  }
22656
22874
  return out;
22657
22875
  }
22658
22876
  function loadGeminiCost(start, end, geminiTmpDir2) {
22659
22877
  const acc = emptyGeminiAccumulator();
22660
- if (!import_fs47.default.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
22878
+ if (!import_fs49.default.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
22661
22879
  for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
22662
22880
  processGeminiCostFile(file, projectKey, start, end, acc);
22663
22881
  }
@@ -22665,11 +22883,11 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
22665
22883
  }
22666
22884
  function aggregateReportFromAudit(period, opts = {}) {
22667
22885
  const now = opts.now ?? /* @__PURE__ */ new Date();
22668
- const auditLogPath = opts.auditLogPath ?? import_path45.default.join(import_os42.default.homedir(), ".node9", "audit.log");
22669
- const claudeProjectsDir = opts.claudeProjectsDir ?? import_path45.default.join(import_os42.default.homedir(), ".claude", "projects");
22670
- const codexSessionsDir2 = opts.codexSessionsDir ?? import_path45.default.join(import_os42.default.homedir(), ".codex", "sessions");
22671
- const geminiTmpDir2 = opts.geminiTmpDir ?? import_path45.default.join(import_os42.default.homedir(), ".gemini", "tmp");
22672
- const hasAuditFile = import_fs47.default.existsSync(auditLogPath);
22886
+ const auditLogPath = opts.auditLogPath ?? import_path46.default.join(import_os42.default.homedir(), ".node9", "audit.log");
22887
+ const claudeProjectsDir = opts.claudeProjectsDir ?? import_path46.default.join(import_os42.default.homedir(), ".claude", "projects");
22888
+ const codexSessionsDir2 = opts.codexSessionsDir ?? import_path46.default.join(import_os42.default.homedir(), ".codex", "sessions");
22889
+ const geminiTmpDir2 = opts.geminiTmpDir ?? import_path46.default.join(import_os42.default.homedir(), ".gemini", "tmp");
22890
+ const hasAuditFile = import_fs49.default.existsSync(auditLogPath);
22673
22891
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
22674
22892
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
22675
22893
  const { start, end } = getDateRange(period, now);
@@ -23365,8 +23583,8 @@ function registerDaemonCommand(program2) {
23365
23583
 
23366
23584
  // src/cli/commands/status.ts
23367
23585
  var import_chalk15 = __toESM(require("chalk"));
23368
- var import_fs48 = __toESM(require("fs"));
23369
- var import_path46 = __toESM(require("path"));
23586
+ var import_fs50 = __toESM(require("fs"));
23587
+ var import_path47 = __toESM(require("path"));
23370
23588
  var import_os43 = __toESM(require("os"));
23371
23589
  init_core();
23372
23590
  init_daemon();
@@ -23424,13 +23642,13 @@ function registerStatusCommand(program2) {
23424
23642
  console.log("");
23425
23643
  const modeLabel = settings.mode === "audit" ? import_chalk15.default.blue("audit") : settings.mode === "strict" ? import_chalk15.default.red("strict") : import_chalk15.default.white("standard");
23426
23644
  console.log(` Mode: ${modeLabel}`);
23427
- const projectConfig = import_path46.default.join(process.cwd(), "node9.config.json");
23428
- const globalConfig = import_path46.default.join(import_os43.default.homedir(), ".node9", "config.json");
23645
+ const projectConfig = import_path47.default.join(process.cwd(), "node9.config.json");
23646
+ const globalConfig = import_path47.default.join(import_os43.default.homedir(), ".node9", "config.json");
23429
23647
  console.log(
23430
- ` Local: ${import_fs48.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
23648
+ ` Local: ${import_fs50.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
23431
23649
  );
23432
23650
  console.log(
23433
- ` Global: ${import_fs48.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
23651
+ ` Global: ${import_fs50.default.existsSync(globalConfig) ? import_chalk15.default.green("Active (~/.node9/config.json)") : import_chalk15.default.gray("Not present")}`
23434
23652
  );
23435
23653
  if (mergedConfig.policy.sandboxPaths.length > 0) {
23436
23654
  console.log(
@@ -23472,8 +23690,8 @@ function registerStatusCommand(program2) {
23472
23690
 
23473
23691
  // src/cli/commands/init.ts
23474
23692
  var import_chalk16 = __toESM(require("chalk"));
23475
- var import_fs49 = __toESM(require("fs"));
23476
- var import_path47 = __toESM(require("path"));
23693
+ var import_fs51 = __toESM(require("fs"));
23694
+ var import_path48 = __toESM(require("path"));
23477
23695
  var import_os44 = __toESM(require("os"));
23478
23696
  var import_https5 = __toESM(require("https"));
23479
23697
  init_core();
@@ -23564,32 +23782,32 @@ function registerInitCommand(program2) {
23564
23782
  }
23565
23783
  console.log("");
23566
23784
  }
23567
- const configPath2 = import_path47.default.join(import_os44.default.homedir(), ".node9", "config.json");
23568
- const isFirstInstall = !import_fs49.default.existsSync(configPath2);
23569
- if (import_fs49.default.existsSync(configPath2) && !options.force) {
23785
+ const configPath = import_path48.default.join(import_os44.default.homedir(), ".node9", "config.json");
23786
+ const isFirstInstall = !import_fs51.default.existsSync(configPath);
23787
+ if (import_fs51.default.existsSync(configPath) && !options.force) {
23570
23788
  try {
23571
- const existing = JSON.parse(import_fs49.default.readFileSync(configPath2, "utf-8"));
23789
+ const existing = JSON.parse(import_fs51.default.readFileSync(configPath, "utf-8"));
23572
23790
  const settings = existing.settings ?? {};
23573
23791
  if (settings.mode !== chosenMode) {
23574
23792
  settings.mode = chosenMode;
23575
23793
  existing.settings = settings;
23576
- import_fs49.default.writeFileSync(configPath2, JSON.stringify(existing, null, 2) + "\n");
23794
+ import_fs51.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
23577
23795
  console.log(import_chalk16.default.green(`\u2705 Mode updated: ${chosenMode}`));
23578
23796
  } else {
23579
- console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath2}`));
23797
+ console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
23580
23798
  }
23581
23799
  } catch {
23582
- console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath2}`));
23800
+ console.log(import_chalk16.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
23583
23801
  }
23584
23802
  } else {
23585
23803
  const configToSave = {
23586
23804
  ...DEFAULT_CONFIG,
23587
23805
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
23588
23806
  };
23589
- const dir = import_path47.default.dirname(configPath2);
23590
- if (!import_fs49.default.existsSync(dir)) import_fs49.default.mkdirSync(dir, { recursive: true });
23591
- import_fs49.default.writeFileSync(configPath2, JSON.stringify(configToSave, null, 2) + "\n");
23592
- console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath2}`));
23807
+ const dir = import_path48.default.dirname(configPath);
23808
+ if (!import_fs51.default.existsSync(dir)) import_fs51.default.mkdirSync(dir, { recursive: true });
23809
+ import_fs51.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
23810
+ console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath}`));
23593
23811
  console.log(import_chalk16.default.gray(` Mode: ${chosenMode}`));
23594
23812
  }
23595
23813
  if (options.skipSetup) return;
@@ -23693,7 +23911,7 @@ function registerInitCommand(program2) {
23693
23911
  }
23694
23912
 
23695
23913
  // src/cli/commands/undo.ts
23696
- var import_path48 = __toESM(require("path"));
23914
+ var import_path49 = __toESM(require("path"));
23697
23915
  var import_chalk18 = __toESM(require("chalk"));
23698
23916
 
23699
23917
  // src/tui/undo-navigator.ts
@@ -23852,7 +24070,7 @@ function findMatchingCwd(startDir, history) {
23852
24070
  let dir = startDir;
23853
24071
  while (true) {
23854
24072
  if (cwds.has(dir)) return dir;
23855
- const parent = import_path48.default.dirname(dir);
24073
+ const parent = import_path49.default.dirname(dir);
23856
24074
  if (parent === dir) return null;
23857
24075
  dir = parent;
23858
24076
  }
@@ -24487,13 +24705,83 @@ function registerMcpGatewayCommand(program2) {
24487
24705
 
24488
24706
  // src/mcp-server/index.ts
24489
24707
  var import_readline5 = __toESM(require("readline"));
24490
- var import_fs50 = __toESM(require("fs"));
24491
- var import_os45 = __toESM(require("os"));
24492
- var import_path49 = __toESM(require("path"));
24708
+ var import_fs53 = __toESM(require("fs"));
24709
+ var import_os46 = __toESM(require("os"));
24710
+ var import_path51 = __toESM(require("path"));
24493
24711
  var import_child_process11 = require("child_process");
24494
24712
  init_core();
24495
24713
  init_daemon();
24496
24714
  init_shields();
24715
+
24716
+ // src/auth/egress-config.ts
24717
+ var import_fs52 = __toESM(require("fs"));
24718
+ var import_os45 = __toESM(require("os"));
24719
+ var import_path50 = __toESM(require("path"));
24720
+ var DEFAULT_EGRESS = {
24721
+ enabled: false,
24722
+ mode: "review",
24723
+ allow: [],
24724
+ deny: [],
24725
+ allowPrivate: true
24726
+ };
24727
+ function egressConfigPath() {
24728
+ return import_path50.default.join(import_os45.default.homedir(), ".node9", "config.json");
24729
+ }
24730
+ function readEgressRawConfig() {
24731
+ let text;
24732
+ try {
24733
+ text = import_fs52.default.readFileSync(egressConfigPath(), "utf8");
24734
+ } catch (err2) {
24735
+ if (err2.code === "ENOENT") return {};
24736
+ throw err2;
24737
+ }
24738
+ try {
24739
+ return JSON.parse(text);
24740
+ } catch {
24741
+ throw new Error(
24742
+ `${egressConfigPath()} is not valid JSON \u2014 fix it before changing egress (refusing to overwrite).`
24743
+ );
24744
+ }
24745
+ }
24746
+ function writeEgressRawConfig(config) {
24747
+ const p = egressConfigPath();
24748
+ import_fs52.default.mkdirSync(import_path50.default.dirname(p), { recursive: true });
24749
+ import_fs52.default.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
24750
+ }
24751
+ function applyEgress(config, change) {
24752
+ const policy = config.policy = config.policy ?? {};
24753
+ const existing = policy.egress ?? {};
24754
+ policy.egress = { ...DEFAULT_EGRESS, ...existing, ...change };
24755
+ return config;
24756
+ }
24757
+ function getEgress() {
24758
+ const raw = readEgressRawConfig();
24759
+ const existing = raw.policy?.egress ?? {};
24760
+ return { ...DEFAULT_EGRESS, ...existing };
24761
+ }
24762
+ function setEgress(change) {
24763
+ const config = readEgressRawConfig();
24764
+ applyEgress(config, change);
24765
+ writeEgressRawConfig(config);
24766
+ }
24767
+ function addEgressHost(list, host) {
24768
+ const config = readEgressRawConfig();
24769
+ const existing = config.policy?.egress ?? {};
24770
+ const current = { ...DEFAULT_EGRESS, ...existing };
24771
+ const updated = current[list].includes(host) ? current[list] : [...current[list], host];
24772
+ applyEgress(config, { [list]: updated });
24773
+ writeEgressRawConfig(config);
24774
+ }
24775
+ function normalizeEgressHost(host) {
24776
+ return host.trim().toLowerCase();
24777
+ }
24778
+ var EGRESS_HOST_RE = /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/;
24779
+ function isValidEgressHost(host) {
24780
+ return EGRESS_HOST_RE.test(host);
24781
+ }
24782
+
24783
+ // src/mcp-server/index.ts
24784
+ init_dist();
24497
24785
  function ok(id, result) {
24498
24786
  return JSON.stringify({ jsonrpc: "2.0", id: id ?? null, result });
24499
24787
  }
@@ -24504,12 +24792,20 @@ var TOOL_CAPABILITY = {
24504
24792
  // weaken — gated over MCP
24505
24793
  node9_shield_disable: "weaken",
24506
24794
  node9_approver_set: "weaken",
24795
+ // NOTE: egress LOOSENING (allow a host / turn egress off) is intentionally NOT
24796
+ // exposed over MCP — it has no legitimate agent use case (it's exactly the
24797
+ // exfil-exit the egress gate exists to prevent) and would be attack surface
24798
+ // even gated. A human loosens egress at the CLI: `node9 egress allow|off`.
24507
24799
  // add / restorative — always allowed
24508
24800
  node9_shield_enable: "add",
24509
24801
  node9_rule_add: "add",
24510
24802
  // already block/review-only — handleRuleAdd rejects "allow"
24511
24803
  node9_undo_revert: "add",
24512
24804
  // restorative
24805
+ node9_egress_protect: "add",
24806
+ // enable/strengthen egress (monotonic — never reduces)
24807
+ node9_egress_deny: "add",
24808
+ // add a deny host (deny always wins)
24513
24809
  // readonly
24514
24810
  node9_status: "readonly",
24515
24811
  node9_config_get: "readonly",
@@ -24523,7 +24819,8 @@ var TOOL_CAPABILITY = {
24523
24819
  node9_shield_list: "readonly",
24524
24820
  node9_approver_list: "readonly",
24525
24821
  node9_undo_list: "readonly",
24526
- node9_undo_detail: "readonly"
24822
+ node9_undo_detail: "readonly",
24823
+ node9_egress_status: "readonly"
24527
24824
  };
24528
24825
  function capabilityOf(tool) {
24529
24826
  return TOOL_CAPABILITY[tool] ?? "readonly";
@@ -24779,7 +25076,41 @@ var TOOLS = [
24779
25076
  },
24780
25077
  required: ["name", "tool", "field", "pattern", "verdict", "reason"]
24781
25078
  }
25079
+ },
25080
+ {
25081
+ name: "node9_egress_status",
25082
+ description: "Show egress (outbound network) control: whether it is enabled, the mode (off / review / block), and your allow + deny host lists. Common dev/LLM hosts (github, npm, pypi, anthropic, \u2026) are always allowed by a built-in list. Read-only.",
25083
+ inputSchema: { type: "object", properties: {}, required: [] }
25084
+ },
25085
+ {
25086
+ name: "node9_egress_protect",
25087
+ description: 'Turn on egress control (or strengthen it). mode="review" prompts on unknown hosts; mode="block" hard-blocks them. Monotonic \u2014 it only ever ADDS protection and never reduces it (a request for review will not downgrade an existing block). Only adds protection, so it is always allowed over MCP.',
25088
+ inputSchema: {
25089
+ type: "object",
25090
+ properties: {
25091
+ mode: {
25092
+ type: "string",
25093
+ enum: ["review", "block"],
25094
+ description: 'Enforcement to apply. "review" = prompt, "block" = deny. Defaults to review.'
25095
+ }
25096
+ },
25097
+ required: []
25098
+ }
25099
+ },
25100
+ {
25101
+ name: "node9_egress_deny",
25102
+ description: "Add a host to the egress deny list (deny always wins over allow). Only adds protection, so it is always allowed over MCP. Host is an FQDN or wildcard glob (e.g. evil.com or *.evil.com).",
25103
+ inputSchema: {
25104
+ type: "object",
25105
+ properties: {
25106
+ host: { type: "string", description: "Host to deny (FQDN or *.glob)." }
25107
+ },
25108
+ required: ["host"]
25109
+ }
24782
25110
  }
25111
+ // Egress LOOSENING (allow a host / turn egress off) is deliberately CLI-only —
25112
+ // see the note in TOOL_CAPABILITY. The agent can see and tighten egress over
25113
+ // MCP, but never loosen it.
24783
25114
  ];
24784
25115
  function handleStatus() {
24785
25116
  const config = getConfig();
@@ -24800,13 +25131,13 @@ function handleStatus() {
24800
25131
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
24801
25132
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
24802
25133
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
24803
- const projectConfig = import_path49.default.join(process.cwd(), "node9.config.json");
24804
- const globalConfig = import_path49.default.join(import_os45.default.homedir(), ".node9", "config.json");
25134
+ const projectConfig = import_path51.default.join(process.cwd(), "node9.config.json");
25135
+ const globalConfig = import_path51.default.join(import_os46.default.homedir(), ".node9", "config.json");
24805
25136
  lines.push(
24806
- `Project config (node9.config.json): ${import_fs50.default.existsSync(projectConfig) ? "present" : "not found"}`
25137
+ `Project config (node9.config.json): ${import_fs53.default.existsSync(projectConfig) ? "present" : "not found"}`
24807
25138
  );
24808
25139
  lines.push(
24809
- `Global config (~/.node9/config.json): ${import_fs50.default.existsSync(globalConfig) ? "present" : "not found"}`
25140
+ `Global config (~/.node9/config.json): ${import_fs53.default.existsSync(globalConfig) ? "present" : "not found"}`
24810
25141
  );
24811
25142
  return lines.join("\n");
24812
25143
  }
@@ -24880,21 +25211,53 @@ function handleShieldDisable(args) {
24880
25211
  writeActiveShields(active.filter((s) => s !== name));
24881
25212
  return `Shield "${name}" disabled.`;
24882
25213
  }
24883
- var GLOBAL_CONFIG_PATH = import_path49.default.join(import_os45.default.homedir(), ".node9", "config.json");
25214
+ function handleEgressStatus() {
25215
+ const e = getConfig().policy.egress;
25216
+ const state = !e.enabled ? "OFF \u2014 the agent can reach any host" : e.mode === "block" ? "LOCKED (block) \u2014 unknown hosts are denied" : "WATCHING (review) \u2014 unknown hosts prompt for approval";
25217
+ const lines = [
25218
+ `Egress control: ${state}`,
25219
+ `${DEFAULT_EGRESS_ALLOWLIST.length} common dev/LLM hosts are always allowed (github, npm, pypi, anthropic, \u2026).`,
25220
+ `Your allow list: ${e.allow.length ? e.allow.join(", ") : "(none)"}`,
25221
+ `Your deny list: ${e.deny.length ? e.deny.join(", ") : "(none)"}`
25222
+ ];
25223
+ return lines.join("\n");
25224
+ }
25225
+ function handleEgressProtect(args) {
25226
+ const rawMode = args.mode;
25227
+ if (rawMode !== void 0 && rawMode !== "review" && rawMode !== "block") {
25228
+ throw new Error('mode must be "review" or "block".');
25229
+ }
25230
+ const requested = rawMode ?? "review";
25231
+ const current = getEgress();
25232
+ const mode = current.enabled && current.mode === "block" ? "block" : requested;
25233
+ setEgress({ enabled: true, mode });
25234
+ return mode === "block" ? "Egress is now LOCKED (block) \u2014 unknown hosts are denied. Routine hosts stay allowed." : "Egress is now WATCHED (review) \u2014 unknown hosts prompt for approval.";
25235
+ }
25236
+ function handleEgressDeny(args) {
25237
+ const host = typeof args.host === "string" ? normalizeEgressHost(args.host) : "";
25238
+ if (!isValidEgressHost(host)) {
25239
+ throw new Error(
25240
+ `Invalid host: "${String(args.host)}". Use an FQDN or *.glob (e.g. *.evil.com).`
25241
+ );
25242
+ }
25243
+ addEgressHost("deny", host);
25244
+ return `Denied egress to ${host} (deny always wins over allow).`;
25245
+ }
25246
+ var GLOBAL_CONFIG_PATH = import_path51.default.join(import_os46.default.homedir(), ".node9", "config.json");
24884
25247
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
24885
25248
  function readGlobalConfigRaw() {
24886
25249
  try {
24887
- if (import_fs50.default.existsSync(GLOBAL_CONFIG_PATH)) {
24888
- return JSON.parse(import_fs50.default.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
25250
+ if (import_fs53.default.existsSync(GLOBAL_CONFIG_PATH)) {
25251
+ return JSON.parse(import_fs53.default.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
24889
25252
  }
24890
25253
  } catch {
24891
25254
  }
24892
25255
  return {};
24893
25256
  }
24894
25257
  function writeGlobalConfigRaw(data) {
24895
- const dir = import_path49.default.dirname(GLOBAL_CONFIG_PATH);
24896
- if (!import_fs50.default.existsSync(dir)) import_fs50.default.mkdirSync(dir, { recursive: true });
24897
- import_fs50.default.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
25258
+ const dir = import_path51.default.dirname(GLOBAL_CONFIG_PATH);
25259
+ if (!import_fs53.default.existsSync(dir)) import_fs53.default.mkdirSync(dir, { recursive: true });
25260
+ import_fs53.default.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
24898
25261
  }
24899
25262
  function handleApproverList() {
24900
25263
  const config = getConfig();
@@ -24938,9 +25301,9 @@ function handleApproverSet(args) {
24938
25301
  function handleAuditGet(args) {
24939
25302
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
24940
25303
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
24941
- const auditPath = import_path49.default.join(import_os45.default.homedir(), ".node9", "audit.log");
24942
- if (!import_fs50.default.existsSync(auditPath)) return "No audit log found.";
24943
- const rawLines = import_fs50.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
25304
+ const auditPath = import_path51.default.join(import_os46.default.homedir(), ".node9", "audit.log");
25305
+ if (!import_fs53.default.existsSync(auditPath)) return "No audit log found.";
25306
+ const rawLines = import_fs53.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
24944
25307
  const parsed = [];
24945
25308
  for (const line of rawLines) {
24946
25309
  try {
@@ -25218,6 +25581,12 @@ function runMcpServer() {
25218
25581
  text = handlePostureMcp(toolArgs);
25219
25582
  } else if (toolName === "node9_explain") {
25220
25583
  text = handleExplainMcp(toolArgs);
25584
+ } else if (toolName === "node9_egress_status") {
25585
+ text = handleEgressStatus();
25586
+ } else if (toolName === "node9_egress_protect") {
25587
+ text = handleEgressProtect(toolArgs);
25588
+ } else if (toolName === "node9_egress_deny") {
25589
+ text = handleEgressDeny(toolArgs);
25221
25590
  } else {
25222
25591
  process.stdout.write(err(id, -32601, `Unknown tool: ${toolName}`) + "\n");
25223
25592
  return;
@@ -25308,7 +25677,7 @@ function registerTrustCommand(program2) {
25308
25677
  // src/cli/commands/mcp-pin.ts
25309
25678
  var import_chalk21 = __toESM(require("chalk"));
25310
25679
  init_mcp_pin();
25311
- var import_fs51 = __toESM(require("fs"));
25680
+ var import_fs54 = __toESM(require("fs"));
25312
25681
  function registerMcpPinCommand(program2) {
25313
25682
  const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
25314
25683
  const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
@@ -25319,7 +25688,7 @@ function registerMcpPinCommand(program2) {
25319
25688
  let repoCorrupt = false;
25320
25689
  if (found.source === "repo") {
25321
25690
  try {
25322
- const raw = import_fs51.default.readFileSync(found.path, "utf-8");
25691
+ const raw = import_fs54.default.readFileSync(found.path, "utf-8");
25323
25692
  const parsed = JSON.parse(raw);
25324
25693
  repoEntries = parsed.servers ?? {};
25325
25694
  } catch {
@@ -25806,52 +26175,12 @@ function registerPostureCommand(program2) {
25806
26175
 
25807
26176
  // src/cli/commands/egress.ts
25808
26177
  var import_chalk26 = __toESM(require("chalk"));
25809
- var import_fs52 = __toESM(require("fs"));
25810
- var import_os46 = __toESM(require("os"));
25811
- var import_path50 = __toESM(require("path"));
25812
26178
  init_config();
25813
26179
  init_dist();
25814
- var DEFAULT_EGRESS = {
25815
- enabled: false,
25816
- mode: "review",
25817
- allow: [],
25818
- deny: [],
25819
- allowPrivate: true
25820
- };
25821
- function configPath() {
25822
- return import_path50.default.join(import_os46.default.homedir(), ".node9", "config.json");
25823
- }
25824
- function readRawConfig() {
25825
- let text;
25826
- try {
25827
- text = import_fs52.default.readFileSync(configPath(), "utf8");
25828
- } catch (err2) {
25829
- if (err2.code === "ENOENT") return {};
25830
- throw err2;
25831
- }
25832
- try {
25833
- return JSON.parse(text);
25834
- } catch {
25835
- throw new Error(
25836
- `${configPath()} is not valid JSON \u2014 fix it before changing egress (refusing to overwrite).`
25837
- );
25838
- }
25839
- }
25840
- function writeRawConfig(config) {
25841
- const p = configPath();
25842
- import_fs52.default.mkdirSync(import_path50.default.dirname(p), { recursive: true });
25843
- import_fs52.default.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
25844
- }
25845
- function applyEgress(config, change) {
25846
- const policy = config.policy = config.policy ?? {};
25847
- const existing = policy.egress ?? {};
25848
- policy.egress = { ...DEFAULT_EGRESS, ...existing, ...change };
25849
- return config;
25850
- }
25851
- function withConfig(fn) {
25852
- let config;
26180
+ function guard(fn) {
25853
26181
  try {
25854
- config = readRawConfig();
26182
+ fn();
26183
+ return true;
25855
26184
  } catch (err2) {
25856
26185
  console.error(import_chalk26.default.red(`
25857
26186
  \u2717 ${err2.message}
@@ -25859,20 +26188,12 @@ function withConfig(fn) {
25859
26188
  process.exitCode = 1;
25860
26189
  return false;
25861
26190
  }
25862
- fn(config);
25863
- writeRawConfig(config);
25864
- return true;
25865
26191
  }
25866
26192
  function mutate(change) {
25867
- return withConfig((config) => applyEgress(config, change));
26193
+ return guard(() => setEgress(change));
25868
26194
  }
25869
26195
  function addHost(list, host) {
25870
- return withConfig((config) => {
25871
- const existing = config.policy?.egress ?? {};
25872
- const current = { ...DEFAULT_EGRESS, ...existing };
25873
- const updated = current[list].includes(host) ? current[list] : [...current[list], host];
25874
- applyEgress(config, { [list]: updated });
25875
- });
26196
+ return guard(() => addEgressHost(list, host));
25876
26197
  }
25877
26198
  function showStatus() {
25878
26199
  const e = getConfig().policy.egress;
@@ -25931,16 +26252,191 @@ function registerEgressCommand(program2) {
25931
26252
  egress.action(showStatus);
25932
26253
  }
25933
26254
 
25934
- // src/cli/commands/sandbox.ts
26255
+ // src/cli/commands/jail.ts
25935
26256
  var import_chalk27 = __toESM(require("chalk"));
26257
+
26258
+ // src/shields/jail.ts
25936
26259
  var import_fs55 = __toESM(require("fs"));
25937
- var import_path53 = __toESM(require("path"));
26260
+ var import_os47 = __toESM(require("os"));
26261
+ var import_path52 = __toESM(require("path"));
26262
+ init_shields();
26263
+ var USER_JAIL_SHIELD = "user-jail";
26264
+ function jailStorePath() {
26265
+ return import_path52.default.join(import_os47.default.homedir(), ".node9", "jail-paths.json");
26266
+ }
26267
+ function readJailPaths() {
26268
+ let text;
26269
+ try {
26270
+ text = import_fs55.default.readFileSync(jailStorePath(), "utf8");
26271
+ } catch (err2) {
26272
+ if (err2.code === "ENOENT") return [];
26273
+ throw err2;
26274
+ }
26275
+ let parsed;
26276
+ try {
26277
+ parsed = JSON.parse(text);
26278
+ } catch {
26279
+ throw new Error(`${jailStorePath()} is not valid JSON \u2014 fix it before changing the jail.`);
26280
+ }
26281
+ if (!Array.isArray(parsed.paths)) return [];
26282
+ return parsed.paths.filter(
26283
+ (p) => !!p && typeof p.path === "string" && (p.verdict === "block" || p.verdict === "review")
26284
+ );
26285
+ }
26286
+ function writeJailPaths(paths) {
26287
+ const p = jailStorePath();
26288
+ import_fs55.default.mkdirSync(import_path52.default.dirname(p), { recursive: true });
26289
+ import_fs55.default.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
26290
+ }
26291
+ function addJailPath(rawPath, verdict) {
26292
+ const norm = rawPath.trim();
26293
+ if (!pathToRegexFragment(norm)) {
26294
+ throw new Error(
26295
+ `"${rawPath}" is too broad to jail \u2014 give a specific path (e.g. ~/.gmail-mcp), not a home or root directory.`
26296
+ );
26297
+ }
26298
+ const next = [...readJailPaths().filter((p) => p.path !== norm), { path: norm, verdict }];
26299
+ writeJailPaths(next);
26300
+ return next;
26301
+ }
26302
+ function removeJailPath(rawPath) {
26303
+ const norm = rawPath.trim();
26304
+ const before = readJailPaths();
26305
+ const after = before.filter((p) => p.path !== norm);
26306
+ const removed = after.length !== before.length;
26307
+ if (removed) writeJailPaths(after);
26308
+ return { removed, paths: after };
26309
+ }
26310
+ function regenerateUserJail(paths) {
26311
+ const file = import_path52.default.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26312
+ if (paths.length === 0) {
26313
+ const active2 = readActiveShields();
26314
+ if (active2.includes(USER_JAIL_SHIELD)) {
26315
+ writeActiveShields(active2.filter((s) => s !== USER_JAIL_SHIELD));
26316
+ }
26317
+ try {
26318
+ import_fs55.default.rmSync(file, { force: true });
26319
+ } catch {
26320
+ }
26321
+ return;
26322
+ }
26323
+ const def = buildShield({
26324
+ name: USER_JAIL_SHIELD,
26325
+ description: "User-added credential jail paths (node9 jail add)",
26326
+ blockPaths: paths.filter((p) => p.verdict === "block").map((p) => p.path),
26327
+ reviewPaths: paths.filter((p) => p.verdict === "review").map((p) => p.path)
26328
+ });
26329
+ installShield(USER_JAIL_SHIELD, def);
26330
+ const active = readActiveShields();
26331
+ if (!active.includes(USER_JAIL_SHIELD)) writeActiveShields([...active, USER_JAIL_SHIELD]);
26332
+ }
26333
+
26334
+ // src/cli/commands/jail.ts
26335
+ init_audit();
26336
+ var BUILTIN_JAIL = [
26337
+ "~/.ssh \u2014 SSH private keys",
26338
+ "~/.aws \u2014 AWS credentials",
26339
+ ".env files",
26340
+ "credential files \u2014 credentials.json, .netrc, .npmrc, .docker, .kube, gcloud"
26341
+ ];
26342
+ function registerJailCommand(program2) {
26343
+ const jail = program2.command("jail").description("Grow the credential jail \u2014 block/review reads of your sensitive paths");
26344
+ jail.command("add <path>").description("Add a path to the jail (default: block reads; --review to soften)").option("--review", "Require human approval instead of hard-blocking reads").action((p, opts) => {
26345
+ const verdict = opts.review ? "review" : "block";
26346
+ try {
26347
+ const paths = addJailPath(p, verdict);
26348
+ regenerateUserJail(paths);
26349
+ appendConfigAudit({ event: "jail-add", path: p.trim(), verdict });
26350
+ } catch (err2) {
26351
+ console.error(import_chalk27.default.red(`
26352
+ \u274C ${err2.message}
26353
+ `));
26354
+ process.exit(1);
26355
+ return;
26356
+ }
26357
+ console.log(import_chalk27.default.green(`
26358
+ \u2705 Jailed ${p} (${verdict}).`));
26359
+ console.log(
26360
+ import_chalk27.default.gray(
26361
+ ` AI reads of this path now ${verdict === "block" ? "BLOCK" : "require approval"}.`
26362
+ )
26363
+ );
26364
+ console.log(import_chalk27.default.gray(` Preview: ${import_chalk27.default.cyan(`node9 explain bash "cat ${p}"`)}
26365
+ `));
26366
+ });
26367
+ jail.command("remove <path>").description("Remove a user-added jail path (built-in paths are not removable)").action((p) => {
26368
+ let result;
26369
+ try {
26370
+ result = removeJailPath(p);
26371
+ } catch (err2) {
26372
+ console.error(import_chalk27.default.red(`
26373
+ \u274C ${err2.message}
26374
+ `));
26375
+ process.exit(1);
26376
+ return;
26377
+ }
26378
+ if (!result.removed) {
26379
+ console.error(import_chalk27.default.yellow(`
26380
+ \u2139\uFE0F "${p}" is not a user-added jail path.
26381
+ `));
26382
+ console.error(import_chalk27.default.gray(` Run ${import_chalk27.default.cyan("node9 jail list")} to see your paths.
26383
+ `));
26384
+ process.exit(1);
26385
+ return;
26386
+ }
26387
+ try {
26388
+ regenerateUserJail(result.paths);
26389
+ appendConfigAudit({ event: "jail-remove", path: p.trim() });
26390
+ } catch (err2) {
26391
+ console.error(import_chalk27.default.red(`
26392
+ \u274C ${err2.message}
26393
+ `));
26394
+ process.exit(1);
26395
+ return;
26396
+ }
26397
+ console.log(import_chalk27.default.green(`
26398
+ \u2705 Removed ${p} from the jail.
26399
+ `));
26400
+ });
26401
+ jail.command("list").description("Show built-in + user-added jail paths").action(() => {
26402
+ console.log(import_chalk27.default.bold("\n\u{1F512} Credential Jail\n"));
26403
+ console.log(import_chalk27.default.gray(" Built-in (always on, not removable):"));
26404
+ for (const b of BUILTIN_JAIL) console.log(` ${import_chalk27.default.gray("\u2022")} ${b}`);
26405
+ console.log("");
26406
+ let user;
26407
+ try {
26408
+ user = readJailPaths();
26409
+ } catch (err2) {
26410
+ console.error(import_chalk27.default.red(` \u2717 ${err2.message}
26411
+ `));
26412
+ process.exit(1);
26413
+ return;
26414
+ }
26415
+ if (user.length === 0) {
26416
+ console.log(
26417
+ import_chalk27.default.gray(" Your paths: (none) \u2014 add one with ") + import_chalk27.default.cyan("node9 jail add <path>")
26418
+ );
26419
+ } else {
26420
+ console.log(import_chalk27.default.gray(" Your paths (removable):"));
26421
+ for (const u of user) {
26422
+ const v = u.verdict === "block" ? import_chalk27.default.red("block ") : import_chalk27.default.yellow("review");
26423
+ console.log(` ${v} ${import_chalk27.default.cyan(u.path)}`);
26424
+ }
26425
+ }
26426
+ console.log("");
26427
+ });
26428
+ }
26429
+
26430
+ // src/cli/commands/sandbox.ts
26431
+ var import_chalk28 = __toESM(require("chalk"));
26432
+ var import_fs58 = __toESM(require("fs"));
26433
+ var import_path55 = __toESM(require("path"));
25938
26434
  var import_child_process13 = require("child_process");
25939
26435
  init_config();
25940
26436
 
25941
26437
  // src/sandbox/config.ts
25942
- var import_fs53 = __toESM(require("fs"));
25943
- var import_path51 = __toESM(require("path"));
26438
+ var import_fs56 = __toESM(require("fs"));
26439
+ var import_path53 = __toESM(require("path"));
25944
26440
  var import_yaml = require("yaml");
25945
26441
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
25946
26442
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -26013,16 +26509,16 @@ function scaffoldSandboxYaml(agent) {
26013
26509
  return header + (0, import_yaml.stringify)(defaultSandboxConfig(agent));
26014
26510
  }
26015
26511
  function sandboxConfigPath(cwd = process.cwd()) {
26016
- return import_path51.default.join(cwd, SANDBOX_CONFIG_FILE);
26512
+ return import_path53.default.join(cwd, SANDBOX_CONFIG_FILE);
26017
26513
  }
26018
26514
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
26019
26515
  const p = sandboxConfigPath(cwd);
26020
- if (!import_fs53.default.existsSync(p)) {
26516
+ if (!import_fs56.default.existsSync(p)) {
26021
26517
  throw new Error(`sandbox: ${SANDBOX_CONFIG_FILE} not found \u2014 run \`node9 sandbox new\` first.`);
26022
26518
  }
26023
26519
  let raw;
26024
26520
  try {
26025
- raw = (0, import_yaml.parse)(import_fs53.default.readFileSync(p, "utf-8"));
26521
+ raw = (0, import_yaml.parse)(import_fs56.default.readFileSync(p, "utf-8"));
26026
26522
  } catch (err2) {
26027
26523
  throw new Error(
26028
26524
  `sandbox: ${SANDBOX_CONFIG_FILE} is not valid YAML \u2014 ${err2.message}`
@@ -26080,14 +26576,14 @@ function compileAllowlist(input) {
26080
26576
  init_templates();
26081
26577
 
26082
26578
  // src/sandbox/runtime.ts
26083
- var import_fs54 = __toESM(require("fs"));
26084
- var import_os47 = __toESM(require("os"));
26085
- var import_path52 = __toESM(require("path"));
26579
+ var import_fs57 = __toESM(require("fs"));
26580
+ var import_os48 = __toESM(require("os"));
26581
+ var import_path54 = __toESM(require("path"));
26086
26582
  var import_crypto13 = __toESM(require("crypto"));
26087
26583
  var import_child_process12 = require("child_process");
26088
26584
  init_templates();
26089
26585
  function sandboxDataDir(cwd = process.cwd()) {
26090
- return import_path52.default.join(cwd, ".node9", "sandbox", "data");
26586
+ return import_path54.default.join(cwd, ".node9", "sandbox", "data");
26091
26587
  }
26092
26588
  function detectEngine(engine) {
26093
26589
  const r = (0, import_child_process12.spawnSync)(engine, ["--version"], { encoding: "utf-8" });
@@ -26098,7 +26594,7 @@ function detectEngine(engine) {
26098
26594
  }
26099
26595
  function agentCredentialsMount(agent) {
26100
26596
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
26101
- return { hostPath: import_path52.default.join(import_os47.default.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26597
+ return { hostPath: import_path54.default.join(import_os48.default.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26102
26598
  }
26103
26599
  function buildRunArgs(opts) {
26104
26600
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -26108,7 +26604,7 @@ function buildRunArgs(opts) {
26108
26604
  args.push("-v", `${allowlistHostPath}:${ALLOWED_DOMAINS_PATH}:ro`);
26109
26605
  if (config.node9.mountAgentCredentials) {
26110
26606
  const creds = agentCredentialsMount(config.agent);
26111
- if (import_fs54.default.existsSync(creds.hostPath)) {
26607
+ if (import_fs57.default.existsSync(creds.hostPath)) {
26112
26608
  args.push("-v", `${creds.hostPath}:${creds.target}`);
26113
26609
  }
26114
26610
  }
@@ -26126,30 +26622,30 @@ function imageContentHash(dockerfile, entrypoint) {
26126
26622
  return import_crypto13.default.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
26127
26623
  }
26128
26624
  function sandboxBuildDir(cwd = process.cwd()) {
26129
- return import_path52.default.join(cwd, ".node9", "sandbox", "build");
26625
+ return import_path54.default.join(cwd, ".node9", "sandbox", "build");
26130
26626
  }
26131
26627
  function writeBuildContext(cwd, dockerfile, entrypoint) {
26132
26628
  const dir = sandboxBuildDir(cwd);
26133
- import_fs54.default.mkdirSync(dir, { recursive: true });
26134
- import_fs54.default.writeFileSync(import_path52.default.join(dir, "Dockerfile"), dockerfile);
26135
- import_fs54.default.writeFileSync(import_path52.default.join(dir, "entrypoint.sh"), entrypoint);
26629
+ import_fs57.default.mkdirSync(dir, { recursive: true });
26630
+ import_fs57.default.writeFileSync(import_path54.default.join(dir, "Dockerfile"), dockerfile);
26631
+ import_fs57.default.writeFileSync(import_path54.default.join(dir, "entrypoint.sh"), entrypoint);
26136
26632
  return dir;
26137
26633
  }
26138
26634
  function writeAllowlist(cwd, hosts) {
26139
- const dir = import_path52.default.join(cwd, ".node9", "sandbox");
26140
- import_fs54.default.mkdirSync(dir, { recursive: true });
26141
- const p = import_path52.default.join(dir, "allowed-domains.txt");
26142
- import_fs54.default.writeFileSync(p, hosts.join("\n") + "\n");
26635
+ const dir = import_path54.default.join(cwd, ".node9", "sandbox");
26636
+ import_fs57.default.mkdirSync(dir, { recursive: true });
26637
+ const p = import_path54.default.join(dir, "allowed-domains.txt");
26638
+ import_fs57.default.writeFileSync(p, hosts.join("\n") + "\n");
26143
26639
  return p;
26144
26640
  }
26145
26641
  function resolveHomePath(p) {
26146
- return p.startsWith("~") ? import_path52.default.join(import_os47.default.homedir(), p.slice(1)) : import_path52.default.resolve(p);
26642
+ return p.startsWith("~") ? import_path54.default.join(import_os48.default.homedir(), p.slice(1)) : import_path54.default.resolve(p);
26147
26643
  }
26148
26644
 
26149
26645
  // src/cli/commands/sandbox.ts
26150
26646
  function seedDataDirConfig(dataDir, sandbox) {
26151
- import_fs55.default.mkdirSync(dataDir, { recursive: true });
26152
- const configPath2 = import_path53.default.join(dataDir, "config.json");
26647
+ import_fs58.default.mkdirSync(dataDir, { recursive: true });
26648
+ const configPath = import_path55.default.join(dataDir, "config.json");
26153
26649
  const seed = {
26154
26650
  settings: {
26155
26651
  approvers: {
@@ -26160,7 +26656,7 @@ function seedDataDirConfig(dataDir, sandbox) {
26160
26656
  }
26161
26657
  }
26162
26658
  };
26163
- import_fs55.default.writeFileSync(configPath2, JSON.stringify(seed, null, 2), { mode: 384 });
26659
+ import_fs58.default.writeFileSync(configPath, JSON.stringify(seed, null, 2), { mode: 384 });
26164
26660
  }
26165
26661
  function registerSandboxCommand(program2, version2) {
26166
26662
  const node9Version2 = pinnedNode9Version(version2);
@@ -26168,18 +26664,18 @@ function registerSandboxCommand(program2, version2) {
26168
26664
  cmd.command("new").description(`Scaffold ${SANDBOX_CONFIG_FILE} in this project`).option("--agent <agent>", "claude (default) or codex", "claude").action((opts) => {
26169
26665
  const agent = opts.agent === "codex" ? "codex" : "claude";
26170
26666
  const p = sandboxConfigPath();
26171
- if (import_fs55.default.existsSync(p)) {
26667
+ if (import_fs58.default.existsSync(p)) {
26172
26668
  console.log(
26173
- import_chalk27.default.yellow(` ${SANDBOX_CONFIG_FILE} already exists \u2014 leaving it untouched.`)
26669
+ import_chalk28.default.yellow(` ${SANDBOX_CONFIG_FILE} already exists \u2014 leaving it untouched.`)
26174
26670
  );
26175
26671
  return;
26176
26672
  }
26177
- import_fs55.default.writeFileSync(p, scaffoldSandboxYaml(agent));
26673
+ import_fs58.default.writeFileSync(p, scaffoldSandboxYaml(agent));
26178
26674
  console.log(
26179
- import_chalk27.default.green(` \u2713 wrote ${SANDBOX_CONFIG_FILE}`) + import_chalk27.default.dim(` (agent: ${agent})`)
26675
+ import_chalk28.default.green(` \u2713 wrote ${SANDBOX_CONFIG_FILE}`) + import_chalk28.default.dim(` (agent: ${agent})`)
26180
26676
  );
26181
26677
  console.log(
26182
- import_chalk27.default.dim(" Edit it (mounts / allow / expose), then: ") + import_chalk27.default.cyan("node9 sandbox run")
26678
+ import_chalk28.default.dim(" Edit it (mounts / allow / expose), then: ") + import_chalk28.default.cyan("node9 sandbox run")
26183
26679
  );
26184
26680
  });
26185
26681
  cmd.command("run [agent]").description("Build (if needed) + run the agent jailed. Extra args after -- go to the agent.").allowUnknownOption(true).allowExcessArguments(true).action((agentArg, _opts, command) => {
@@ -26189,7 +26685,7 @@ function registerSandboxCommand(program2, version2) {
26189
26685
  const engine = detectEngine(sandbox.runtime.engine);
26190
26686
  if (!engine.available) {
26191
26687
  console.error(
26192
- import_chalk27.default.red(` ${sandbox.runtime.engine} not found.`) + import_chalk27.default.dim(` Install it first \u2014 node9 sandbox needs a container runtime.`)
26688
+ import_chalk28.default.red(` ${sandbox.runtime.engine} not found.`) + import_chalk28.default.dim(` Install it first \u2014 node9 sandbox needs a container runtime.`)
26193
26689
  );
26194
26690
  process.exit(1);
26195
26691
  }
@@ -26202,11 +26698,11 @@ function registerSandboxCommand(program2, version2) {
26202
26698
  });
26203
26699
  if (compiled.rejected.length) {
26204
26700
  console.log(
26205
- import_chalk27.default.yellow(` \u26A0 ignoring invalid allow hosts: ${compiled.rejected.join(", ")}`)
26701
+ import_chalk28.default.yellow(` \u26A0 ignoring invalid allow hosts: ${compiled.rejected.join(", ")}`)
26206
26702
  );
26207
26703
  }
26208
26704
  if (compiled.denied.length) {
26209
- console.log(import_chalk27.default.dim(` (denied: ${compiled.denied.join(", ")})`));
26705
+ console.log(import_chalk28.default.dim(` (denied: ${compiled.denied.join(", ")})`));
26210
26706
  }
26211
26707
  const allowlistPath = writeAllowlist(cwd, compiled.allow);
26212
26708
  const dockerfile = renderDockerfile(sandbox, node9Version2);
@@ -26214,20 +26710,20 @@ function registerSandboxCommand(program2, version2) {
26214
26710
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
26215
26711
  const hash = imageContentHash(dockerfile, entrypoint);
26216
26712
  const image = sandbox.runtime.image;
26217
- const hashFile = import_path53.default.join(sandboxBuildDir(cwd), ".image-hash");
26218
- const lastHash = import_fs55.default.existsSync(hashFile) ? import_fs55.default.readFileSync(hashFile, "utf-8").trim() : "";
26713
+ const hashFile = import_path55.default.join(sandboxBuildDir(cwd), ".image-hash");
26714
+ const lastHash = import_fs58.default.existsSync(hashFile) ? import_fs58.default.readFileSync(hashFile, "utf-8").trim() : "";
26219
26715
  const imageExists = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
26220
26716
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
26221
26717
  if (needBuild) {
26222
- console.log(import_chalk27.default.dim(` building ${image} \u2026`));
26718
+ console.log(import_chalk28.default.dim(` building ${image} \u2026`));
26223
26719
  const b = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, ["build", "-t", image, buildDir], {
26224
26720
  stdio: "inherit"
26225
26721
  });
26226
26722
  if (b.status !== 0) {
26227
- console.error(import_chalk27.default.red(" build failed."));
26723
+ console.error(import_chalk28.default.red(" build failed."));
26228
26724
  process.exit(b.status ?? 1);
26229
26725
  }
26230
- import_fs55.default.writeFileSync(hashFile, hash);
26726
+ import_fs58.default.writeFileSync(hashFile, hash);
26231
26727
  }
26232
26728
  const dataDir = sandboxDataDir(cwd);
26233
26729
  seedDataDirConfig(dataDir, sandbox);
@@ -26241,36 +26737,36 @@ function registerSandboxCommand(program2, version2) {
26241
26737
  });
26242
26738
  if (sandbox.node9.mountAgentCredentials) {
26243
26739
  const creds = agentCredentialsMount(sandbox.agent);
26244
- if (import_fs55.default.existsSync(creds.hostPath)) {
26245
- console.log(import_chalk27.default.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
26740
+ if (import_fs58.default.existsSync(creds.hostPath)) {
26741
+ console.log(import_chalk28.default.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
26246
26742
  } else {
26247
26743
  console.log(
26248
- import_chalk27.default.yellow(` \u26A0 ${creds.hostPath} not found \u2014 `) + import_chalk27.default.dim(`the agent must auth via an env key in env.pass.`)
26744
+ import_chalk28.default.yellow(` \u26A0 ${creds.hostPath} not found \u2014 `) + import_chalk28.default.dim(`the agent must auth via an env key in env.pass.`)
26249
26745
  );
26250
26746
  }
26251
26747
  }
26252
26748
  console.log(
26253
- import_chalk27.default.green(` \u{1F6E1}\uFE0F ${sandbox.agent} jailed \u2014 ${compiled.allow.length} hosts allowed
26749
+ import_chalk28.default.green(` \u{1F6E1}\uFE0F ${sandbox.agent} jailed \u2014 ${compiled.allow.length} hosts allowed
26254
26750
  `)
26255
26751
  );
26256
26752
  const r = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, runArgs, { stdio: "inherit" });
26257
26753
  process.exit(r.status ?? 0);
26258
26754
  });
26259
26755
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
26260
- const auditPath = import_path53.default.join(sandboxDataDir(), "audit.log");
26261
- if (!import_fs55.default.existsSync(auditPath)) {
26262
- console.log(import_chalk27.default.dim(" no sandbox audit yet."));
26756
+ const auditPath = import_path55.default.join(sandboxDataDir(), "audit.log");
26757
+ if (!import_fs58.default.existsSync(auditPath)) {
26758
+ console.log(import_chalk28.default.dim(" no sandbox audit yet."));
26263
26759
  return;
26264
26760
  }
26265
26761
  (0, import_child_process13.spawnSync)("tail", ["-f", auditPath], { stdio: "inherit" });
26266
26762
  });
26267
26763
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
26268
- const auditPath = import_path53.default.join(sandboxDataDir(), "audit.log");
26269
- if (!import_fs55.default.existsSync(auditPath)) {
26270
- console.log(import_chalk27.default.dim(" no sandbox audit yet."));
26764
+ const auditPath = import_path55.default.join(sandboxDataDir(), "audit.log");
26765
+ if (!import_fs58.default.existsSync(auditPath)) {
26766
+ console.log(import_chalk28.default.dim(" no sandbox audit yet."));
26271
26767
  return;
26272
26768
  }
26273
- process.stdout.write(import_fs55.default.readFileSync(auditPath, "utf-8"));
26769
+ process.stdout.write(import_fs58.default.readFileSync(auditPath, "utf-8"));
26274
26770
  });
26275
26771
  cmd.command("clean").description("Remove the sandbox image, build context, and data").action(() => {
26276
26772
  const cwd = process.cwd();
@@ -26284,16 +26780,16 @@ function registerSandboxCommand(program2, version2) {
26284
26780
  stdio: "ignore"
26285
26781
  });
26286
26782
  }
26287
- import_fs55.default.rmSync(import_path53.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26288
- console.log(import_chalk27.default.green(" \u2713 sandbox image + build + data removed."));
26783
+ import_fs58.default.rmSync(import_path55.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26784
+ console.log(import_chalk28.default.green(" \u2713 sandbox image + build + data removed."));
26289
26785
  });
26290
26786
  }
26291
26787
 
26292
26788
  // src/cli/commands/sessions.ts
26293
- var import_chalk28 = __toESM(require("chalk"));
26294
- var import_fs56 = __toESM(require("fs"));
26295
- var import_path54 = __toESM(require("path"));
26296
- var import_os48 = __toESM(require("os"));
26789
+ var import_chalk29 = __toESM(require("chalk"));
26790
+ var import_fs59 = __toESM(require("fs"));
26791
+ var import_path56 = __toESM(require("path"));
26792
+ var import_os49 = __toESM(require("os"));
26297
26793
  init_scan_summary();
26298
26794
  init_litellm();
26299
26795
  init_cost_gemini();
@@ -26314,10 +26810,10 @@ function encodeProjectPath(projectPath) {
26314
26810
  }
26315
26811
  function sessionJsonlPath(projectPath, sessionId) {
26316
26812
  const encoded = encodeProjectPath(projectPath);
26317
- return import_path54.default.join(import_os48.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
26813
+ return import_path56.default.join(import_os49.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
26318
26814
  }
26319
26815
  function projectLabel(projectPath) {
26320
- return projectPath.replace(import_os48.default.homedir(), "~");
26816
+ return projectPath.replace(import_os49.default.homedir(), "~");
26321
26817
  }
26322
26818
  function parseHistoryLines(lines) {
26323
26819
  const entries = [];
@@ -26386,10 +26882,10 @@ function parseSessionLines(lines) {
26386
26882
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
26387
26883
  }
26388
26884
  function loadAuditEntries(auditPath) {
26389
- const aPath = auditPath ?? import_path54.default.join(import_os48.default.homedir(), ".node9", "audit.log");
26885
+ const aPath = auditPath ?? import_path56.default.join(import_os49.default.homedir(), ".node9", "audit.log");
26390
26886
  let raw;
26391
26887
  try {
26392
- raw = import_fs56.default.readFileSync(aPath, "utf-8");
26888
+ raw = import_fs59.default.readFileSync(aPath, "utf-8");
26393
26889
  } catch {
26394
26890
  return [];
26395
26891
  }
@@ -26425,8 +26921,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
26425
26921
  return result;
26426
26922
  }
26427
26923
  function buildGeminiSessions(days, allAuditEntries) {
26428
- const tmpDir = import_path54.default.join(import_os48.default.homedir(), ".gemini", "tmp");
26429
- if (!import_fs56.default.existsSync(tmpDir)) return [];
26924
+ const tmpDir = import_path56.default.join(import_os49.default.homedir(), ".gemini", "tmp");
26925
+ if (!import_fs59.default.existsSync(tmpDir)) return [];
26430
26926
  const cutoff = days !== null ? (() => {
26431
26927
  const d = /* @__PURE__ */ new Date();
26432
26928
  d.setDate(d.getDate() - days);
@@ -26435,35 +26931,35 @@ function buildGeminiSessions(days, allAuditEntries) {
26435
26931
  })() : null;
26436
26932
  let slugDirs;
26437
26933
  try {
26438
- slugDirs = import_fs56.default.readdirSync(tmpDir);
26934
+ slugDirs = import_fs59.default.readdirSync(tmpDir);
26439
26935
  } catch {
26440
26936
  return [];
26441
26937
  }
26442
26938
  const summaries = [];
26443
- for (const slug of slugDirs) {
26444
- const slugPath = import_path54.default.join(tmpDir, slug);
26939
+ for (const slug2 of slugDirs) {
26940
+ const slugPath = import_path56.default.join(tmpDir, slug2);
26445
26941
  try {
26446
- if (!import_fs56.default.statSync(slugPath).isDirectory()) continue;
26942
+ if (!import_fs59.default.statSync(slugPath).isDirectory()) continue;
26447
26943
  } catch {
26448
26944
  continue;
26449
26945
  }
26450
- let projectRoot = import_path54.default.join(import_os48.default.homedir(), slug);
26946
+ let projectRoot = import_path56.default.join(import_os49.default.homedir(), slug2);
26451
26947
  try {
26452
- projectRoot = import_fs56.default.readFileSync(import_path54.default.join(slugPath, ".project_root"), "utf-8").trim();
26948
+ projectRoot = import_fs59.default.readFileSync(import_path56.default.join(slugPath, ".project_root"), "utf-8").trim();
26453
26949
  } catch {
26454
26950
  }
26455
- const chatsDir = import_path54.default.join(slugPath, "chats");
26456
- if (!import_fs56.default.existsSync(chatsDir)) continue;
26951
+ const chatsDir = import_path56.default.join(slugPath, "chats");
26952
+ if (!import_fs59.default.existsSync(chatsDir)) continue;
26457
26953
  let chatFiles;
26458
26954
  try {
26459
- chatFiles = import_fs56.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
26955
+ chatFiles = import_fs59.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
26460
26956
  } catch {
26461
26957
  continue;
26462
26958
  }
26463
26959
  for (const chatFile of chatFiles) {
26464
26960
  let raw;
26465
26961
  try {
26466
- raw = import_fs56.default.readFileSync(import_path54.default.join(chatsDir, chatFile), "utf-8");
26962
+ raw = import_fs59.default.readFileSync(import_path56.default.join(chatsDir, chatFile), "utf-8");
26467
26963
  } catch {
26468
26964
  continue;
26469
26965
  }
@@ -26543,8 +27039,8 @@ function buildGeminiSessions(days, allAuditEntries) {
26543
27039
  return summaries;
26544
27040
  }
26545
27041
  function buildCodexSessions(days, allAuditEntries) {
26546
- const sessionsBase = import_path54.default.join(import_os48.default.homedir(), ".codex", "sessions");
26547
- if (!import_fs56.default.existsSync(sessionsBase)) return [];
27042
+ const sessionsBase = import_path56.default.join(import_os49.default.homedir(), ".codex", "sessions");
27043
+ if (!import_fs59.default.existsSync(sessionsBase)) return [];
26548
27044
  const cutoff = days !== null ? (() => {
26549
27045
  const d = /* @__PURE__ */ new Date();
26550
27046
  d.setDate(d.getDate() - days);
@@ -26553,29 +27049,29 @@ function buildCodexSessions(days, allAuditEntries) {
26553
27049
  })() : null;
26554
27050
  const jsonlFiles = [];
26555
27051
  try {
26556
- for (const year of import_fs56.default.readdirSync(sessionsBase)) {
26557
- const yearPath = import_path54.default.join(sessionsBase, year);
27052
+ for (const year of import_fs59.default.readdirSync(sessionsBase)) {
27053
+ const yearPath = import_path56.default.join(sessionsBase, year);
26558
27054
  try {
26559
- if (!import_fs56.default.statSync(yearPath).isDirectory()) continue;
27055
+ if (!import_fs59.default.statSync(yearPath).isDirectory()) continue;
26560
27056
  } catch {
26561
27057
  continue;
26562
27058
  }
26563
- for (const month of import_fs56.default.readdirSync(yearPath)) {
26564
- const monthPath = import_path54.default.join(yearPath, month);
27059
+ for (const month of import_fs59.default.readdirSync(yearPath)) {
27060
+ const monthPath = import_path56.default.join(yearPath, month);
26565
27061
  try {
26566
- if (!import_fs56.default.statSync(monthPath).isDirectory()) continue;
27062
+ if (!import_fs59.default.statSync(monthPath).isDirectory()) continue;
26567
27063
  } catch {
26568
27064
  continue;
26569
27065
  }
26570
- for (const day of import_fs56.default.readdirSync(monthPath)) {
26571
- const dayPath = import_path54.default.join(monthPath, day);
27066
+ for (const day of import_fs59.default.readdirSync(monthPath)) {
27067
+ const dayPath = import_path56.default.join(monthPath, day);
26572
27068
  try {
26573
- if (!import_fs56.default.statSync(dayPath).isDirectory()) continue;
27069
+ if (!import_fs59.default.statSync(dayPath).isDirectory()) continue;
26574
27070
  } catch {
26575
27071
  continue;
26576
27072
  }
26577
- for (const file of import_fs56.default.readdirSync(dayPath)) {
26578
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path54.default.join(dayPath, file));
27073
+ for (const file of import_fs59.default.readdirSync(dayPath)) {
27074
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path56.default.join(dayPath, file));
26579
27075
  }
26580
27076
  }
26581
27077
  }
@@ -26587,7 +27083,7 @@ function buildCodexSessions(days, allAuditEntries) {
26587
27083
  for (const filePath of jsonlFiles) {
26588
27084
  let lines;
26589
27085
  try {
26590
- lines = import_fs56.default.readFileSync(filePath, "utf-8").split("\n");
27086
+ lines = import_fs59.default.readFileSync(filePath, "utf-8").split("\n");
26591
27087
  } catch {
26592
27088
  continue;
26593
27089
  }
@@ -26673,10 +27169,10 @@ function buildCodexSessions(days, allAuditEntries) {
26673
27169
  return summaries;
26674
27170
  }
26675
27171
  function buildSessions(days, historyPath) {
26676
- const hPath = historyPath ?? import_path54.default.join(import_os48.default.homedir(), ".claude", "history.jsonl");
27172
+ const hPath = historyPath ?? import_path56.default.join(import_os49.default.homedir(), ".claude", "history.jsonl");
26677
27173
  let historyRaw = "";
26678
27174
  try {
26679
- historyRaw = import_fs56.default.readFileSync(hPath, "utf-8");
27175
+ historyRaw = import_fs59.default.readFileSync(hPath, "utf-8");
26680
27176
  } catch {
26681
27177
  }
26682
27178
  const cutoff = days !== null ? (() => {
@@ -26700,7 +27196,7 @@ function buildSessions(days, historyPath) {
26700
27196
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
26701
27197
  let sessionLines = [];
26702
27198
  try {
26703
- sessionLines = import_fs56.default.readFileSync(jsonlFile, "utf-8").split("\n");
27199
+ sessionLines = import_fs59.default.readFileSync(jsonlFile, "utf-8").split("\n");
26704
27200
  } catch {
26705
27201
  }
26706
27202
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -26786,11 +27282,11 @@ function toolInputSummary(tool, input) {
26786
27282
  }
26787
27283
  function toolColor(tool) {
26788
27284
  const t = tool.toLowerCase();
26789
- if (t === "bash" || t === "execute_bash") return import_chalk28.default.red;
26790
- if (t === "write") return import_chalk28.default.green;
26791
- if (t === "edit" || t === "notebookedit") return import_chalk28.default.yellow;
26792
- if (t === "read") return import_chalk28.default.cyan;
26793
- return import_chalk28.default.gray;
27285
+ if (t === "bash" || t === "execute_bash") return import_chalk29.default.red;
27286
+ if (t === "write") return import_chalk29.default.green;
27287
+ if (t === "edit" || t === "notebookedit") return import_chalk29.default.yellow;
27288
+ if (t === "read") return import_chalk29.default.cyan;
27289
+ return import_chalk29.default.gray;
26794
27290
  }
26795
27291
  function barStr2(value, max, width) {
26796
27292
  if (max === 0 || width <= 0) return "\u2591".repeat(width);
@@ -26800,7 +27296,7 @@ function barStr2(value, max, width) {
26800
27296
  function colorBar2(value, max, width) {
26801
27297
  const s = barStr2(value, max, width);
26802
27298
  const filled = Math.max(1, Math.round(max > 0 ? value / max * width : 0));
26803
- return import_chalk28.default.cyan(s.slice(0, filled)) + import_chalk28.default.dim(s.slice(filled));
27299
+ return import_chalk29.default.cyan(s.slice(0, filled)) + import_chalk29.default.dim(s.slice(filled));
26804
27300
  }
26805
27301
  function renderSummary(summaries) {
26806
27302
  const totalTools = summaries.reduce((n, s) => n + s.toolCalls.length, 0);
@@ -26830,45 +27326,45 @@ function renderSummary(summaries) {
26830
27326
  }
26831
27327
  const topProjects = [...projCosts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
26832
27328
  const W = 20;
26833
- console.log(import_chalk28.default.dim(" " + "\u2500".repeat(70)));
27329
+ console.log(import_chalk29.default.dim(" " + "\u2500".repeat(70)));
26834
27330
  console.log(
26835
- " " + import_chalk28.default.bold.white(String(summaries.length).padEnd(4)) + import_chalk28.default.dim("sessions ") + import_chalk28.default.bold.yellow(fmtCost3(totalCost).padEnd(10)) + import_chalk28.default.dim("total ") + import_chalk28.default.bold.white(String(totalTools).padEnd(6)) + import_chalk28.default.dim("tool calls ") + import_chalk28.default.bold.white(String(totalFiles)) + import_chalk28.default.dim(" files modified") + (totalBlocked > 0 ? import_chalk28.default.dim(" ") + import_chalk28.default.red.bold(String(totalBlocked)) + import_chalk28.default.dim(" blocked by node9") : "")
27331
+ " " + import_chalk29.default.bold.white(String(summaries.length).padEnd(4)) + import_chalk29.default.dim("sessions ") + import_chalk29.default.bold.yellow(fmtCost3(totalCost).padEnd(10)) + import_chalk29.default.dim("total ") + import_chalk29.default.bold.white(String(totalTools).padEnd(6)) + import_chalk29.default.dim("tool calls ") + import_chalk29.default.bold.white(String(totalFiles)) + import_chalk29.default.dim(" files modified") + (totalBlocked > 0 ? import_chalk29.default.dim(" ") + import_chalk29.default.red.bold(String(totalBlocked)) + import_chalk29.default.dim(" blocked by node9") : "")
26836
27332
  );
26837
27333
  console.log(
26838
- " " + import_chalk28.default.dim("avg ") + import_chalk28.default.white(fmtCost3(avgCost).padEnd(10)) + import_chalk28.default.dim("/session ") + import_chalk28.default.green(String(snapshots)) + import_chalk28.default.dim(` of ${summaries.length} sessions had snapshots`)
27334
+ " " + import_chalk29.default.dim("avg ") + import_chalk29.default.white(fmtCost3(avgCost).padEnd(10)) + import_chalk29.default.dim("/session ") + import_chalk29.default.green(String(snapshots)) + import_chalk29.default.dim(` of ${summaries.length} sessions had snapshots`)
26839
27335
  );
26840
27336
  console.log("");
26841
- console.log(" " + import_chalk28.default.dim("Tool breakdown:"));
27337
+ console.log(" " + import_chalk29.default.dim("Tool breakdown:"));
26842
27338
  const maxGroup = Math.max(...Object.values(groups));
26843
27339
  for (const [label2, count] of Object.entries(groups)) {
26844
27340
  if (count === 0) continue;
26845
27341
  const pct = totalTools > 0 ? Math.round(count / totalTools * 100) : 0;
26846
27342
  console.log(
26847
- " " + label2.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " + import_chalk28.default.white(String(count).padStart(4)) + import_chalk28.default.dim(` (${String(pct)}%)`)
27343
+ " " + label2.padEnd(6) + " " + colorBar2(count, maxGroup, W) + " " + import_chalk29.default.white(String(count).padStart(4)) + import_chalk29.default.dim(` (${String(pct)}%)`)
26848
27344
  );
26849
27345
  }
26850
27346
  console.log("");
26851
27347
  if (topProjects.length > 1) {
26852
- console.log(" " + import_chalk28.default.dim("Cost by project:"));
27348
+ console.log(" " + import_chalk29.default.dim("Cost by project:"));
26853
27349
  const maxProjCost = topProjects[0][1];
26854
27350
  for (const [proj, cost] of topProjects) {
26855
27351
  console.log(
26856
- " " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " + import_chalk28.default.yellow(fmtCost3(cost))
27352
+ " " + proj.slice(0, 28).padEnd(28) + " " + colorBar2(cost, maxProjCost, W) + " " + import_chalk29.default.yellow(fmtCost3(cost))
26857
27353
  );
26858
27354
  }
26859
27355
  console.log("");
26860
27356
  }
26861
- console.log(import_chalk28.default.dim(" " + "\u2500".repeat(70)));
27357
+ console.log(import_chalk29.default.dim(" " + "\u2500".repeat(70)));
26862
27358
  console.log("");
26863
27359
  }
26864
27360
  function renderList(summaries, totalCost) {
26865
27361
  if (summaries.length === 0) {
26866
- console.log(import_chalk28.default.yellow(" No sessions found in the requested range.\n"));
27362
+ console.log(import_chalk29.default.yellow(" No sessions found in the requested range.\n"));
26867
27363
  return;
26868
27364
  }
26869
- const totalLabel = totalCost > 0 ? import_chalk28.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
27365
+ const totalLabel = totalCost > 0 ? import_chalk29.default.dim(" ~" + fmtCost3(totalCost) + " total") : "";
26870
27366
  console.log(
26871
- " " + import_chalk28.default.white(String(summaries.length)) + import_chalk28.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
27367
+ " " + import_chalk29.default.white(String(summaries.length)) + import_chalk29.default.dim(` session${summaries.length !== 1 ? "s" : ""}`) + totalLabel
26872
27368
  );
26873
27369
  console.log("");
26874
27370
  let lastGroup = "";
@@ -26876,51 +27372,51 @@ function renderList(summaries, totalCost) {
26876
27372
  const activeDate = fmtDate2(s.lastActiveTime);
26877
27373
  const group = activeDate + " " + s.projectLabel;
26878
27374
  if (group !== lastGroup) {
26879
- console.log(import_chalk28.default.dim(" \u2500\u2500\u2500 ") + import_chalk28.default.bold(activeDate) + import_chalk28.default.dim(" " + s.projectLabel));
27375
+ console.log(import_chalk29.default.dim(" \u2500\u2500\u2500 ") + import_chalk29.default.bold(activeDate) + import_chalk29.default.dim(" " + s.projectLabel));
26880
27376
  lastGroup = group;
26881
27377
  }
26882
27378
  const startDate = fmtDate2(s.startTime);
26883
- const dateRange = startDate !== activeDate ? import_chalk28.default.dim(" (" + startDate + " \u2192 " + activeDate + ")") : "";
26884
- const timeStr = import_chalk28.default.dim(fmtTime(s.startTime));
26885
- const prompt = import_chalk28.default.white(truncate(s.firstPrompt.replace(/\n/g, " "), 50).padEnd(50));
26886
- const tools = s.toolCalls.length > 0 ? import_chalk28.default.dim(String(s.toolCalls.length).padStart(3) + " tools") : import_chalk28.default.dim(" 0 tools");
26887
- const cost = s.costUSD > 0 ? import_chalk28.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
26888
- const blocked = s.blockedCalls.length > 0 ? import_chalk28.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
26889
- const snap = s.hasSnapshot ? import_chalk28.default.green(" \u{1F4F8}") : "";
26890
- const agentBadge = import_chalk28.default[agentColorName(s.agent ?? "claude")](
27379
+ const dateRange = startDate !== activeDate ? import_chalk29.default.dim(" (" + startDate + " \u2192 " + activeDate + ")") : "";
27380
+ const timeStr = import_chalk29.default.dim(fmtTime(s.startTime));
27381
+ const prompt = import_chalk29.default.white(truncate(s.firstPrompt.replace(/\n/g, " "), 50).padEnd(50));
27382
+ const tools = s.toolCalls.length > 0 ? import_chalk29.default.dim(String(s.toolCalls.length).padStart(3) + " tools") : import_chalk29.default.dim(" 0 tools");
27383
+ const cost = s.costUSD > 0 ? import_chalk29.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
27384
+ const blocked = s.blockedCalls.length > 0 ? import_chalk29.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
27385
+ const snap = s.hasSnapshot ? import_chalk29.default.green(" \u{1F4F8}") : "";
27386
+ const agentBadge = import_chalk29.default[agentColorName(s.agent ?? "claude")](
26891
27387
  " " + agentBadgeText(s.agent ?? "claude", 0)
26892
27388
  );
26893
- const sid = import_chalk28.default.dim(" " + s.sessionId.slice(0, 8));
27389
+ const sid = import_chalk29.default.dim(" " + s.sessionId.slice(0, 8));
26894
27390
  console.log(
26895
27391
  ` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
26896
27392
  );
26897
27393
  }
26898
27394
  console.log("");
26899
27395
  console.log(
26900
- import_chalk28.default.dim(" Run") + " " + import_chalk28.default.cyan("node9 sessions --detail <session-id>") + import_chalk28.default.dim(" for full tool trace.")
27396
+ import_chalk29.default.dim(" Run") + " " + import_chalk29.default.cyan("node9 sessions --detail <session-id>") + import_chalk29.default.dim(" for full tool trace.")
26901
27397
  );
26902
27398
  console.log("");
26903
27399
  }
26904
27400
  function renderDetail(s) {
26905
27401
  console.log("");
26906
- console.log(import_chalk28.default.bold(" Session ") + import_chalk28.default.dim(s.sessionId));
27402
+ console.log(import_chalk29.default.bold(" Session ") + import_chalk29.default.dim(s.sessionId));
26907
27403
  console.log(
26908
- import_chalk28.default.bold(" Prompt ") + import_chalk28.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
27404
+ import_chalk29.default.bold(" Prompt ") + import_chalk29.default.white(s.firstPrompt.replace(/\n/g, " ").slice(0, 120))
26909
27405
  );
26910
- console.log(import_chalk28.default.bold(" Project ") + import_chalk28.default.white(s.projectLabel));
27406
+ console.log(import_chalk29.default.bold(" Project ") + import_chalk29.default.white(s.projectLabel));
26911
27407
  if (s.agent) {
26912
- const agentLabel2 = import_chalk28.default[agentColorName(s.agent)](agentDisplayName(s.agent));
26913
- console.log(import_chalk28.default.bold(" Agent ") + agentLabel2);
27408
+ const agentLabel2 = import_chalk29.default[agentColorName(s.agent)](agentDisplayName(s.agent));
27409
+ console.log(import_chalk29.default.bold(" Agent ") + agentLabel2);
26914
27410
  }
26915
- console.log(import_chalk28.default.bold(" When ") + import_chalk28.default.white(fmtDateTime(s.startTime)));
27411
+ console.log(import_chalk29.default.bold(" When ") + import_chalk29.default.white(fmtDateTime(s.startTime)));
26916
27412
  if (s.costUSD > 0)
26917
- console.log(import_chalk28.default.bold(" Cost ") + import_chalk28.default.yellow("~" + fmtCost3(s.costUSD)));
27413
+ console.log(import_chalk29.default.bold(" Cost ") + import_chalk29.default.yellow("~" + fmtCost3(s.costUSD)));
26918
27414
  console.log(
26919
- import_chalk28.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk28.default.green("\u2713 taken") : import_chalk28.default.dim("none"))
27415
+ import_chalk29.default.bold(" Snapshot ") + (s.hasSnapshot ? import_chalk29.default.green("\u2713 taken") : import_chalk29.default.dim("none"))
26920
27416
  );
26921
27417
  console.log("");
26922
27418
  if (s.toolCalls.length === 0 && s.blockedCalls.length === 0) {
26923
- console.log(import_chalk28.default.dim(" No tool calls recorded.\n"));
27419
+ console.log(import_chalk29.default.dim(" No tool calls recorded.\n"));
26924
27420
  return;
26925
27421
  }
26926
27422
  const timeline = [
@@ -26933,32 +27429,32 @@ function renderDetail(s) {
26933
27429
  });
26934
27430
  const headerParts = [`Tool calls (${s.toolCalls.length})`];
26935
27431
  if (s.blockedCalls.length > 0)
26936
- headerParts.push(import_chalk28.default.red(`${s.blockedCalls.length} blocked by node9`));
26937
- console.log(import_chalk28.default.bold(" " + headerParts.join(" \xB7 ")));
27432
+ headerParts.push(import_chalk29.default.red(`${s.blockedCalls.length} blocked by node9`));
27433
+ console.log(import_chalk29.default.bold(" " + headerParts.join(" \xB7 ")));
26938
27434
  console.log("");
26939
27435
  for (const entry of timeline) {
26940
27436
  if (entry.kind === "tool") {
26941
27437
  const tc = entry.tc;
26942
27438
  const colorFn = toolColor(tc.tool);
26943
27439
  const toolPad = colorFn(tc.tool.padEnd(16));
26944
- const detail = import_chalk28.default.gray(truncate(toolInputSummary(tc.tool, tc.input), 70));
26945
- const ts = tc.timestamp ? import_chalk28.default.dim(fmtTime(tc.timestamp) + " ") : " ";
27440
+ const detail = import_chalk29.default.gray(truncate(toolInputSummary(tc.tool, tc.input), 70));
27441
+ const ts = tc.timestamp ? import_chalk29.default.dim(fmtTime(tc.timestamp) + " ") : " ";
26946
27442
  console.log(` ${ts}${toolPad} ${detail}`);
26947
27443
  } else {
26948
27444
  const bc = entry.bc;
26949
- const ts = bc.timestamp ? import_chalk28.default.dim(fmtTime(bc.timestamp) + " ") : " ";
26950
- const label2 = import_chalk28.default.red("\u{1F6D1} BLOCKED".padEnd(16));
26951
- const toolName = import_chalk28.default.red(bc.tool.padEnd(10));
26952
- const argsSummary = bc.args ? import_chalk28.default.gray(truncate(toolInputSummary(bc.tool, bc.args), 40)) : import_chalk28.default.dim("[args not logged]");
26953
- const reason = bc.checkedBy ? import_chalk28.default.dim(" \u2190 " + bc.checkedBy) : "";
27445
+ const ts = bc.timestamp ? import_chalk29.default.dim(fmtTime(bc.timestamp) + " ") : " ";
27446
+ const label2 = import_chalk29.default.red("\u{1F6D1} BLOCKED".padEnd(16));
27447
+ const toolName = import_chalk29.default.red(bc.tool.padEnd(10));
27448
+ const argsSummary = bc.args ? import_chalk29.default.gray(truncate(toolInputSummary(bc.tool, bc.args), 40)) : import_chalk29.default.dim("[args not logged]");
27449
+ const reason = bc.checkedBy ? import_chalk29.default.dim(" \u2190 " + bc.checkedBy) : "";
26954
27450
  console.log(` ${ts}${label2} ${toolName} ${argsSummary}${reason}`);
26955
27451
  }
26956
27452
  }
26957
27453
  console.log("");
26958
27454
  if (s.modifiedFiles.length > 0) {
26959
- console.log(import_chalk28.default.bold(` Files modified (${s.modifiedFiles.length}):`));
27455
+ console.log(import_chalk29.default.bold(` Files modified (${s.modifiedFiles.length}):`));
26960
27456
  for (const f of s.modifiedFiles) {
26961
- console.log(" " + import_chalk28.default.yellow(f));
27457
+ console.log(" " + import_chalk29.default.yellow(f));
26962
27458
  }
26963
27459
  console.log("");
26964
27460
  }
@@ -26966,13 +27462,13 @@ function renderDetail(s) {
26966
27462
  function registerSessionsCommand(program2) {
26967
27463
  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) => {
26968
27464
  console.log("");
26969
- console.log(import_chalk28.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk28.default.dim(" \u2014 what your AI agent did"));
27465
+ console.log(import_chalk29.default.cyan.bold("\u{1F4CB} node9 sessions") + import_chalk29.default.dim(" \u2014 what your AI agent did"));
26970
27466
  console.log("");
26971
27467
  const days = options.detail || options.all ? null : Math.max(1, parseInt(options.days, 10) || 7);
26972
27468
  const rangeLabel = options.detail ? "all time" : options.all ? "all time" : `last ${String(days)} days`;
26973
- console.log(import_chalk28.default.dim(" " + rangeLabel));
27469
+ console.log(import_chalk29.default.dim(" " + rangeLabel));
26974
27470
  console.log("");
26975
- process.stdout.write(import_chalk28.default.dim(" Loading\u2026"));
27471
+ process.stdout.write(import_chalk29.default.dim(" Loading\u2026"));
26976
27472
  const summaries = buildSessions(days);
26977
27473
  if (process.stdout.isTTY) {
26978
27474
  process.stdout.clearLine(0);
@@ -26985,8 +27481,8 @@ function registerSessionsCommand(program2) {
26985
27481
  (s) => s.sessionId === options.detail || s.sessionId.startsWith(options.detail)
26986
27482
  );
26987
27483
  if (!target) {
26988
- console.log(import_chalk28.default.red(` Session not found: ${options.detail}`));
26989
- console.log(import_chalk28.default.dim(" Run `node9 sessions` to list recent sessions.\n"));
27484
+ console.log(import_chalk29.default.red(` Session not found: ${options.detail}`));
27485
+ console.log(import_chalk29.default.dim(" Run `node9 sessions` to list recent sessions.\n"));
26990
27486
  return;
26991
27487
  }
26992
27488
  renderDetail(target);
@@ -26999,7 +27495,7 @@ function registerSessionsCommand(program2) {
26999
27495
  }
27000
27496
 
27001
27497
  // src/cli/commands/session-taint.ts
27002
- var import_chalk29 = __toESM(require("chalk"));
27498
+ var import_chalk30 = __toESM(require("chalk"));
27003
27499
  init_daemon();
27004
27500
  function resolveSessionId(records, query) {
27005
27501
  const exact = records.find((r) => r.sessionId === query);
@@ -27026,22 +27522,22 @@ function registerSessionTaintCommand(program2) {
27026
27522
  const records = await listSessionTaints();
27027
27523
  console.log("");
27028
27524
  if (records.length === 0) {
27029
- console.log(import_chalk29.default.dim(" No tainted sessions."));
27030
- console.log(import_chalk29.default.dim(" (Taint lives in the daemon \u2014 a stopped daemon has none.)") + "\n");
27525
+ console.log(import_chalk30.default.dim(" No tainted sessions."));
27526
+ console.log(import_chalk30.default.dim(" (Taint lives in the daemon \u2014 a stopped daemon has none.)") + "\n");
27031
27527
  return;
27032
27528
  }
27033
27529
  console.log(
27034
- " " + import_chalk29.default.bold(String(records.length)) + import_chalk29.default.dim(` tainted session${records.length !== 1 ? "s" : ""}`)
27530
+ " " + import_chalk30.default.bold(String(records.length)) + import_chalk30.default.dim(` tainted session${records.length !== 1 ? "s" : ""}`)
27035
27531
  );
27036
27532
  console.log("");
27037
27533
  for (const r of records) {
27038
27534
  console.log(
27039
- " " + import_chalk29.default.yellow(r.sessionId.slice(0, 8).padEnd(10)) + import_chalk29.default.red(r.source) + sourceGap(r.source) + import_chalk29.default.dim("clears in " + fmtRemaining(r.expiresAt))
27535
+ " " + import_chalk30.default.yellow(r.sessionId.slice(0, 8).padEnd(10)) + import_chalk30.default.red(r.source) + sourceGap(r.source) + import_chalk30.default.dim("clears in " + fmtRemaining(r.expiresAt))
27040
27536
  );
27041
27537
  }
27042
27538
  console.log("");
27043
27539
  console.log(
27044
- import_chalk29.default.dim(" Run ") + import_chalk29.default.cyan("node9 session-taint clear <id>") + import_chalk29.default.dim(" to release one, or ") + import_chalk29.default.cyan("--all") + import_chalk29.default.dim(" for every session.") + "\n"
27540
+ import_chalk30.default.dim(" Run ") + import_chalk30.default.cyan("node9 session-taint clear <id>") + import_chalk30.default.dim(" to release one, or ") + import_chalk30.default.cyan("--all") + import_chalk30.default.dim(" for every session.") + "\n"
27045
27541
  );
27046
27542
  });
27047
27543
  cmd.command("clear").description("Clear a session's taint so its next network/write action isn't held for review").argument("[sessionId]", "Session id to clear (the 8-char prefix from `list` is accepted)").option("--all", "Clear every session taint").action(async (sessionId, opts) => {
@@ -27049,32 +27545,32 @@ function registerSessionTaintCommand(program2) {
27049
27545
  if (opts.all) {
27050
27546
  const res2 = await clearSessionTaint({ all: true });
27051
27547
  if (res2.daemonUnavailable) {
27052
- console.log(import_chalk29.default.dim(" node9 daemon not running \u2014 no active taints to clear.") + "\n");
27548
+ console.log(import_chalk30.default.dim(" node9 daemon not running \u2014 no active taints to clear.") + "\n");
27053
27549
  return;
27054
27550
  }
27055
27551
  console.log(
27056
- import_chalk29.default.green(" \u2713 ") + `Cleared ${import_chalk29.default.bold(String(res2.cleared))} session taint${res2.cleared !== 1 ? "s" : ""}.
27552
+ import_chalk30.default.green(" \u2713 ") + `Cleared ${import_chalk30.default.bold(String(res2.cleared))} session taint${res2.cleared !== 1 ? "s" : ""}.
27057
27553
  `
27058
27554
  );
27059
27555
  return;
27060
27556
  }
27061
27557
  if (!sessionId) {
27062
- console.log(import_chalk29.default.red(" Provide a session id or --all."));
27063
- console.log(import_chalk29.default.dim(" Run `node9 session-taint list` to see tainted sessions.") + "\n");
27558
+ console.log(import_chalk30.default.red(" Provide a session id or --all."));
27559
+ console.log(import_chalk30.default.dim(" Run `node9 session-taint list` to see tainted sessions.") + "\n");
27064
27560
  return;
27065
27561
  }
27066
27562
  const records = await listSessionTaints();
27067
27563
  if (records.length === 0) {
27068
- console.log(import_chalk29.default.dim(" No tainted sessions \u2014 nothing to clear.") + "\n");
27564
+ console.log(import_chalk30.default.dim(" No tainted sessions \u2014 nothing to clear.") + "\n");
27069
27565
  return;
27070
27566
  }
27071
27567
  const resolved = resolveSessionId(records, sessionId);
27072
27568
  if ("error" in resolved) {
27073
27569
  if (resolved.error === "not-found") {
27074
- console.log(import_chalk29.default.red(` No tainted session matches "${sessionId}".`));
27570
+ console.log(import_chalk30.default.red(` No tainted session matches "${sessionId}".`));
27075
27571
  } else {
27076
- console.log(import_chalk29.default.red(` "${sessionId}" is ambiguous \u2014 matches:`));
27077
- for (const m of resolved.matches) console.log(import_chalk29.default.dim(" " + m));
27572
+ console.log(import_chalk30.default.red(` "${sessionId}" is ambiguous \u2014 matches:`));
27573
+ for (const m of resolved.matches) console.log(import_chalk30.default.dim(" " + m));
27078
27574
  }
27079
27575
  console.log("");
27080
27576
  return;
@@ -27082,24 +27578,24 @@ function registerSessionTaintCommand(program2) {
27082
27578
  const res = await clearSessionTaint({ sessionId: resolved.record.sessionId });
27083
27579
  if (res.cleared > 0) {
27084
27580
  console.log(
27085
- import_chalk29.default.green(" \u2713 ") + `Cleared taint for ${import_chalk29.default.yellow(resolved.record.sessionId.slice(0, 8))} ` + import_chalk29.default.dim(`(was flagged by ${resolved.record.source}).`) + "\n"
27581
+ import_chalk30.default.green(" \u2713 ") + `Cleared taint for ${import_chalk30.default.yellow(resolved.record.sessionId.slice(0, 8))} ` + import_chalk30.default.dim(`(was flagged by ${resolved.record.source}).`) + "\n"
27086
27582
  );
27087
27583
  } else {
27088
27584
  console.log(
27089
- import_chalk29.default.dim(` Session ${resolved.record.sessionId.slice(0, 8)} was already clear.`) + "\n"
27585
+ import_chalk30.default.dim(` Session ${resolved.record.sessionId.slice(0, 8)} was already clear.`) + "\n"
27090
27586
  );
27091
27587
  }
27092
27588
  });
27093
27589
  }
27094
27590
 
27095
27591
  // src/cli/commands/skill-pin.ts
27096
- var import_chalk30 = __toESM(require("chalk"));
27097
- var import_fs57 = __toESM(require("fs"));
27098
- var import_os49 = __toESM(require("os"));
27099
- var import_path55 = __toESM(require("path"));
27592
+ var import_chalk31 = __toESM(require("chalk"));
27593
+ var import_fs60 = __toESM(require("fs"));
27594
+ var import_os50 = __toESM(require("os"));
27595
+ var import_path57 = __toESM(require("path"));
27100
27596
  function wipeSkillSessions() {
27101
27597
  try {
27102
- import_fs57.default.rmSync(import_path55.default.join(import_os49.default.homedir(), ".node9", "skill-sessions"), {
27598
+ import_fs60.default.rmSync(import_path57.default.join(import_os50.default.homedir(), ".node9", "skill-sessions"), {
27103
27599
  recursive: true,
27104
27600
  force: true
27105
27601
  });
@@ -27113,29 +27609,29 @@ function registerSkillPinCommand(program2) {
27113
27609
  const result = readSkillPinsSafe();
27114
27610
  if (!result.ok) {
27115
27611
  if (result.reason === "missing") {
27116
- console.log(import_chalk30.default.gray("\nNo skill roots are pinned yet."));
27612
+ console.log(import_chalk31.default.gray("\nNo skill roots are pinned yet."));
27117
27613
  console.log(
27118
- import_chalk30.default.gray("Pins are created automatically on the first tool call of each session.\n")
27614
+ import_chalk31.default.gray("Pins are created automatically on the first tool call of each session.\n")
27119
27615
  );
27120
27616
  return;
27121
27617
  }
27122
- console.error(import_chalk30.default.red(`
27618
+ console.error(import_chalk31.default.red(`
27123
27619
  \u274C Pin file is corrupt: ${result.detail}`));
27124
- console.error(import_chalk30.default.yellow(" Run: node9 skill pin reset\n"));
27620
+ console.error(import_chalk31.default.yellow(" Run: node9 skill pin reset\n"));
27125
27621
  process.exit(1);
27126
27622
  }
27127
27623
  const entries = Object.entries(result.pins.roots);
27128
27624
  if (entries.length === 0) {
27129
- console.log(import_chalk30.default.gray("\nNo skill roots are pinned yet.\n"));
27625
+ console.log(import_chalk31.default.gray("\nNo skill roots are pinned yet.\n"));
27130
27626
  return;
27131
27627
  }
27132
- console.log(import_chalk30.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
27628
+ console.log(import_chalk31.default.bold("\n\u{1F512} Pinned Skill Roots\n"));
27133
27629
  for (const [key, entry] of entries) {
27134
- const missing = entry.exists ? "" : import_chalk30.default.yellow(" (not present at pin time)");
27135
- console.log(` ${import_chalk30.default.cyan(key)} ${import_chalk30.default.gray(entry.rootPath)}${missing}`);
27630
+ const missing = entry.exists ? "" : import_chalk31.default.yellow(" (not present at pin time)");
27631
+ console.log(` ${import_chalk31.default.cyan(key)} ${import_chalk31.default.gray(entry.rootPath)}${missing}`);
27136
27632
  console.log(` Files (${entry.fileCount})`);
27137
- console.log(` Hash: ${import_chalk30.default.gray(entry.contentHash.slice(0, 16))}...`);
27138
- console.log(` Pinned: ${import_chalk30.default.gray(entry.pinnedAt)}
27633
+ console.log(` Hash: ${import_chalk31.default.gray(entry.contentHash.slice(0, 16))}...`);
27634
+ console.log(` Pinned: ${import_chalk31.default.gray(entry.pinnedAt)}
27139
27635
  `);
27140
27636
  }
27141
27637
  });
@@ -27144,52 +27640,52 @@ function registerSkillPinCommand(program2) {
27144
27640
  try {
27145
27641
  pins = readSkillPins();
27146
27642
  } catch {
27147
- console.error(import_chalk30.default.red("\n\u274C Pin file is corrupt."));
27148
- console.error(import_chalk30.default.yellow(" Run: node9 skill pin reset\n"));
27643
+ console.error(import_chalk31.default.red("\n\u274C Pin file is corrupt."));
27644
+ console.error(import_chalk31.default.yellow(" Run: node9 skill pin reset\n"));
27149
27645
  process.exit(1);
27150
27646
  }
27151
27647
  if (!pins.roots[rootKey]) {
27152
- console.error(import_chalk30.default.red(`
27648
+ console.error(import_chalk31.default.red(`
27153
27649
  \u274C No pin found for root key "${rootKey}"
27154
27650
  `));
27155
- console.error(`Run ${import_chalk30.default.cyan("node9 skill pin list")} to see pinned roots.
27651
+ console.error(`Run ${import_chalk31.default.cyan("node9 skill pin list")} to see pinned roots.
27156
27652
  `);
27157
27653
  process.exit(1);
27158
27654
  }
27159
27655
  const rootPath = pins.roots[rootKey].rootPath;
27160
27656
  removePin2(rootKey);
27161
27657
  wipeSkillSessions();
27162
- console.log(import_chalk30.default.green(`
27163
- \u{1F513} Pin removed for ${import_chalk30.default.cyan(rootKey)}`));
27164
- console.log(import_chalk30.default.gray(` ${rootPath}`));
27165
- console.log(import_chalk30.default.gray(" Next session will re-pin with current state.\n"));
27658
+ console.log(import_chalk31.default.green(`
27659
+ \u{1F513} Pin removed for ${import_chalk31.default.cyan(rootKey)}`));
27660
+ console.log(import_chalk31.default.gray(` ${rootPath}`));
27661
+ console.log(import_chalk31.default.gray(" Next session will re-pin with current state.\n"));
27166
27662
  });
27167
27663
  pinSubCmd.command("reset").description("Clear all skill pins and wipe session verification flags").action(() => {
27168
27664
  const result = readSkillPinsSafe();
27169
27665
  if (!result.ok && result.reason === "missing") {
27170
27666
  wipeSkillSessions();
27171
- console.log(import_chalk30.default.gray("\nNo pins to clear.\n"));
27667
+ console.log(import_chalk31.default.gray("\nNo pins to clear.\n"));
27172
27668
  return;
27173
27669
  }
27174
27670
  const count = result.ok ? Object.keys(result.pins.roots).length : "?";
27175
27671
  clearAllPins2();
27176
27672
  wipeSkillSessions();
27177
- console.log(import_chalk30.default.green(`
27673
+ console.log(import_chalk31.default.green(`
27178
27674
  \u{1F513} Cleared ${count} skill pin(s).`));
27179
- console.log(import_chalk30.default.gray(" Next session will re-pin with current state.\n"));
27675
+ console.log(import_chalk31.default.gray(" Next session will re-pin with current state.\n"));
27180
27676
  });
27181
27677
  }
27182
27678
 
27183
27679
  // src/cli/commands/decisions.ts
27184
- var import_fs58 = __toESM(require("fs"));
27185
- var import_os50 = __toESM(require("os"));
27186
- var import_path56 = __toESM(require("path"));
27187
- var import_chalk31 = __toESM(require("chalk"));
27188
- var DECISIONS_FILE2 = import_path56.default.join(import_os50.default.homedir(), ".node9", "decisions.json");
27680
+ var import_fs61 = __toESM(require("fs"));
27681
+ var import_os51 = __toESM(require("os"));
27682
+ var import_path58 = __toESM(require("path"));
27683
+ var import_chalk32 = __toESM(require("chalk"));
27684
+ var DECISIONS_FILE2 = import_path58.default.join(import_os51.default.homedir(), ".node9", "decisions.json");
27189
27685
  function readDecisions() {
27190
27686
  try {
27191
- if (!import_fs58.default.existsSync(DECISIONS_FILE2)) return {};
27192
- const raw = import_fs58.default.readFileSync(DECISIONS_FILE2, "utf-8");
27687
+ if (!import_fs61.default.existsSync(DECISIONS_FILE2)) return {};
27688
+ const raw = import_fs61.default.readFileSync(DECISIONS_FILE2, "utf-8");
27193
27689
  const parsed = JSON.parse(raw);
27194
27690
  const out = {};
27195
27691
  for (const [k, v] of Object.entries(parsed)) {
@@ -27201,11 +27697,11 @@ function readDecisions() {
27201
27697
  }
27202
27698
  }
27203
27699
  function writeDecisions(d) {
27204
- const dir = import_path56.default.dirname(DECISIONS_FILE2);
27205
- if (!import_fs58.default.existsSync(dir)) import_fs58.default.mkdirSync(dir, { recursive: true });
27700
+ const dir = import_path58.default.dirname(DECISIONS_FILE2);
27701
+ if (!import_fs61.default.existsSync(dir)) import_fs61.default.mkdirSync(dir, { recursive: true });
27206
27702
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
27207
- import_fs58.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
27208
- import_fs58.default.renameSync(tmp, DECISIONS_FILE2);
27703
+ import_fs61.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
27704
+ import_fs61.default.renameSync(tmp, DECISIONS_FILE2);
27209
27705
  }
27210
27706
  function registerDecisionsCommand(program2) {
27211
27707
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -27213,67 +27709,67 @@ function registerDecisionsCommand(program2) {
27213
27709
  const decisions = readDecisions();
27214
27710
  const entries = Object.entries(decisions);
27215
27711
  if (entries.length === 0) {
27216
- console.log(import_chalk31.default.gray(" No persistent decisions stored."));
27712
+ console.log(import_chalk32.default.gray(" No persistent decisions stored."));
27217
27713
  console.log(
27218
- import_chalk31.default.gray(` File: ${DECISIONS_FILE2}
27219
- `) + import_chalk31.default.gray(' Decisions are written when you click "Always Allow" or')
27714
+ import_chalk32.default.gray(` File: ${DECISIONS_FILE2}
27715
+ `) + import_chalk32.default.gray(' Decisions are written when you click "Always Allow" or')
27220
27716
  );
27221
- console.log(import_chalk31.default.gray(' "Always Deny" in node9 tail or the native popup.'));
27717
+ console.log(import_chalk32.default.gray(' "Always Deny" in node9 tail or the native popup.'));
27222
27718
  return;
27223
27719
  }
27224
- console.log(import_chalk31.default.bold(`
27720
+ console.log(import_chalk32.default.bold(`
27225
27721
  Persistent decisions (${entries.length})
27226
27722
  `));
27227
27723
  const w = Math.max(...entries.map(([k]) => k.length));
27228
27724
  for (const [tool, verdict] of entries.sort()) {
27229
- const colored = verdict === "allow" ? import_chalk31.default.green(verdict) : import_chalk31.default.red(verdict);
27725
+ const colored = verdict === "allow" ? import_chalk32.default.green(verdict) : import_chalk32.default.red(verdict);
27230
27726
  console.log(` ${tool.padEnd(w)} ${colored}`);
27231
27727
  }
27232
27728
  console.log(
27233
- import_chalk31.default.gray(`
27729
+ import_chalk32.default.gray(`
27234
27730
  Stored in ${DECISIONS_FILE2}
27235
- `) + import_chalk31.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
27731
+ `) + import_chalk32.default.gray(" Run `node9 decisions clear <tool>` to remove an entry.")
27236
27732
  );
27237
27733
  });
27238
27734
  cmd.command("clear <toolName>").description("Remove a persistent decision for one tool").action((toolName) => {
27239
27735
  const decisions = readDecisions();
27240
27736
  if (!(toolName in decisions)) {
27241
- console.log(import_chalk31.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
27737
+ console.log(import_chalk32.default.yellow(` No persistent decision for "${toolName}". Nothing to clear.`));
27242
27738
  process.exitCode = 1;
27243
27739
  return;
27244
27740
  }
27245
27741
  delete decisions[toolName];
27246
27742
  writeDecisions(decisions);
27247
- console.log(import_chalk31.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
27743
+ console.log(import_chalk32.default.green(` \u2713 Cleared persistent decision for "${toolName}".`));
27248
27744
  });
27249
27745
  cmd.command("clear-all").description("Remove every persistent decision (irreversible)").action(() => {
27250
27746
  const decisions = readDecisions();
27251
27747
  const count = Object.keys(decisions).length;
27252
27748
  if (count === 0) {
27253
- console.log(import_chalk31.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
27749
+ console.log(import_chalk32.default.gray(" Nothing to clear \u2014 no persistent decisions stored."));
27254
27750
  return;
27255
27751
  }
27256
27752
  writeDecisions({});
27257
27753
  console.log(
27258
- import_chalk31.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
27754
+ import_chalk32.default.green(` \u2713 Cleared ${count} persistent decision${count === 1 ? "" : "s"}.`)
27259
27755
  );
27260
27756
  });
27261
27757
  }
27262
27758
 
27263
27759
  // src/cli/commands/dlp.ts
27264
- var import_chalk32 = __toESM(require("chalk"));
27265
- var import_fs59 = __toESM(require("fs"));
27266
- var import_path57 = __toESM(require("path"));
27267
- var import_os51 = __toESM(require("os"));
27268
- var AUDIT_LOG = import_path57.default.join(import_os51.default.homedir(), ".node9", "audit.log");
27269
- var RESOLVED_FILE = import_path57.default.join(import_os51.default.homedir(), ".node9", "dlp-resolved.json");
27760
+ var import_chalk33 = __toESM(require("chalk"));
27761
+ var import_fs62 = __toESM(require("fs"));
27762
+ var import_path59 = __toESM(require("path"));
27763
+ var import_os52 = __toESM(require("os"));
27764
+ var AUDIT_LOG = import_path59.default.join(import_os52.default.homedir(), ".node9", "audit.log");
27765
+ var RESOLVED_FILE = import_path59.default.join(import_os52.default.homedir(), ".node9", "dlp-resolved.json");
27270
27766
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
27271
27767
  function stripAnsi(s) {
27272
27768
  return s.replace(ANSI_RE, "");
27273
27769
  }
27274
27770
  function loadResolved() {
27275
27771
  try {
27276
- const raw = JSON.parse(import_fs59.default.readFileSync(RESOLVED_FILE, "utf-8"));
27772
+ const raw = JSON.parse(import_fs62.default.readFileSync(RESOLVED_FILE, "utf-8"));
27277
27773
  return new Set(raw);
27278
27774
  } catch {
27279
27775
  return /* @__PURE__ */ new Set();
@@ -27281,13 +27777,13 @@ function loadResolved() {
27281
27777
  }
27282
27778
  function saveResolved(resolved) {
27283
27779
  try {
27284
- import_fs59.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
27780
+ import_fs62.default.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
27285
27781
  } catch {
27286
27782
  }
27287
27783
  }
27288
27784
  function loadDlpFindings() {
27289
- if (!import_fs59.default.existsSync(AUDIT_LOG)) return [];
27290
- return import_fs59.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
27785
+ if (!import_fs62.default.existsSync(AUDIT_LOG)) return [];
27786
+ return import_fs62.default.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
27291
27787
  if (!line.trim()) return [];
27292
27788
  try {
27293
27789
  const e = JSON.parse(line);
@@ -27316,14 +27812,14 @@ function registerDlpCommand(program2) {
27316
27812
  cmd.command("resolve").description("Mark all current DLP findings as resolved").action(() => {
27317
27813
  const findings = loadDlpFindings();
27318
27814
  if (findings.length === 0) {
27319
- console.log(import_chalk32.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
27815
+ console.log(import_chalk33.default.green("\n \u2705 No response-DLP findings to resolve.\n"));
27320
27816
  return;
27321
27817
  }
27322
27818
  const resolved = loadResolved();
27323
27819
  for (const e of findings) resolved.add(entryKey2(e));
27324
27820
  saveResolved(resolved);
27325
27821
  console.log(
27326
- import_chalk32.default.green(
27822
+ import_chalk33.default.green(
27327
27823
  `
27328
27824
  \u2705 ${findings.length} finding${findings.length !== 1 ? "s" : ""} marked as resolved.
27329
27825
  `
@@ -27337,63 +27833,63 @@ function registerDlpCommand(program2) {
27337
27833
  const resolvedCount = findings.length - open.length;
27338
27834
  console.log("");
27339
27835
  console.log(
27340
- import_chalk32.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk32.default.dim(" \u2014 secrets found in Claude response text")
27836
+ import_chalk33.default.bold.cyan("\u{1F510} node9 dlp") + import_chalk33.default.dim(" \u2014 secrets found in Claude response text")
27341
27837
  );
27342
27838
  console.log("");
27343
27839
  if (open.length === 0) {
27344
27840
  if (resolvedCount > 0) {
27345
- console.log(import_chalk32.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
27841
+ console.log(import_chalk33.default.green(` \u2705 No open findings \xB7 ${resolvedCount} previously resolved`));
27346
27842
  } else {
27347
27843
  console.log(
27348
- import_chalk32.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
27844
+ import_chalk33.default.green(" \u2705 No findings \u2014 Claude has not leaked secrets in response text")
27349
27845
  );
27350
27846
  }
27351
27847
  console.log("");
27352
27848
  return;
27353
27849
  }
27354
27850
  console.log(
27355
- import_chalk32.default.bgRed.white.bold(` \u26A0\uFE0F ${open.length} open finding${open.length !== 1 ? "s" : ""} `) + import_chalk32.default.dim(resolvedCount > 0 ? ` (${resolvedCount} resolved)` : "")
27851
+ import_chalk33.default.bgRed.white.bold(` \u26A0\uFE0F ${open.length} open finding${open.length !== 1 ? "s" : ""} `) + import_chalk33.default.dim(resolvedCount > 0 ? ` (${resolvedCount} resolved)` : "")
27356
27852
  );
27357
27853
  console.log("");
27358
27854
  console.log(
27359
- import_chalk32.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
27855
+ import_chalk33.default.dim(" These secrets were included in Claude's response text \u2014 NOT blocked.")
27360
27856
  );
27361
- console.log(import_chalk32.default.dim(" Rotate each affected key immediately.\n"));
27857
+ console.log(import_chalk33.default.dim(" Rotate each affected key immediately.\n"));
27362
27858
  for (const e of open) {
27363
27859
  console.log(
27364
- " " + import_chalk32.default.red("\u25CF") + " " + import_chalk32.default.white(e.dlpPattern ?? "Secret") + import_chalk32.default.dim(" " + fmtDate3(e.ts))
27860
+ " " + import_chalk33.default.red("\u25CF") + " " + import_chalk33.default.white(e.dlpPattern ?? "Secret") + import_chalk33.default.dim(" " + fmtDate3(e.ts))
27365
27861
  );
27366
27862
  if (e.dlpSample) {
27367
- console.log(" " + import_chalk32.default.dim("Sample: ") + import_chalk32.default.yellow(stripAnsi(e.dlpSample)));
27863
+ console.log(" " + import_chalk33.default.dim("Sample: ") + import_chalk33.default.yellow(stripAnsi(e.dlpSample)));
27368
27864
  }
27369
27865
  if (e.project) {
27370
- console.log(" " + import_chalk32.default.dim("Project: ") + import_chalk32.default.dim(stripAnsi(e.project)));
27866
+ console.log(" " + import_chalk33.default.dim("Project: ") + import_chalk33.default.dim(stripAnsi(e.project)));
27371
27867
  }
27372
27868
  console.log("");
27373
27869
  }
27374
- console.log(" " + import_chalk32.default.bold("Next steps:"));
27375
- console.log(" " + import_chalk32.default.cyan("1.") + " Rotate any exposed keys shown above");
27870
+ console.log(" " + import_chalk33.default.bold("Next steps:"));
27871
+ console.log(" " + import_chalk33.default.cyan("1.") + " Rotate any exposed keys shown above");
27376
27872
  console.log(
27377
- " " + import_chalk32.default.cyan("2.") + " Run " + import_chalk32.default.white("node9 dlp resolve") + " to acknowledge"
27873
+ " " + import_chalk33.default.cyan("2.") + " Run " + import_chalk33.default.white("node9 dlp resolve") + " to acknowledge"
27378
27874
  );
27379
27875
  console.log(
27380
- " " + import_chalk32.default.cyan("3.") + " Run " + import_chalk32.default.white("node9 report") + " for full audit history"
27876
+ " " + import_chalk33.default.cyan("3.") + " Run " + import_chalk33.default.white("node9 report") + " for full audit history"
27381
27877
  );
27382
27878
  console.log("");
27383
27879
  });
27384
27880
  }
27385
27881
 
27386
27882
  // src/cli/commands/mask.ts
27387
- var import_chalk33 = __toESM(require("chalk"));
27388
- var import_fs60 = __toESM(require("fs"));
27389
- var import_path58 = __toESM(require("path"));
27390
- var import_os52 = __toESM(require("os"));
27883
+ var import_chalk34 = __toESM(require("chalk"));
27884
+ var import_fs63 = __toESM(require("fs"));
27885
+ var import_path60 = __toESM(require("path"));
27886
+ var import_os53 = __toESM(require("os"));
27391
27887
  init_dlp();
27392
27888
  function findJsonlFiles(dir) {
27393
27889
  const results = [];
27394
- if (!import_fs60.default.existsSync(dir)) return results;
27395
- for (const entry of import_fs60.default.readdirSync(dir, { withFileTypes: true })) {
27396
- const full = import_path58.default.join(dir, entry.name);
27890
+ if (!import_fs63.default.existsSync(dir)) return results;
27891
+ for (const entry of import_fs63.default.readdirSync(dir, { withFileTypes: true })) {
27892
+ const full = import_path60.default.join(dir, entry.name);
27397
27893
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
27398
27894
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
27399
27895
  }
@@ -27436,7 +27932,7 @@ function redactJson(obj) {
27436
27932
  function processFile(filePath, dryRun) {
27437
27933
  let raw;
27438
27934
  try {
27439
- raw = import_fs60.default.readFileSync(filePath, "utf-8");
27935
+ raw = import_fs63.default.readFileSync(filePath, "utf-8");
27440
27936
  } catch {
27441
27937
  return { redactedLines: 0, patterns: [] };
27442
27938
  }
@@ -27468,14 +27964,14 @@ function processFile(filePath, dryRun) {
27468
27964
  }
27469
27965
  }
27470
27966
  if (!dryRun && redactedLines > 0) {
27471
- import_fs60.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
27967
+ import_fs63.default.writeFileSync(filePath, newLines.join("\n"), "utf-8");
27472
27968
  }
27473
27969
  return { redactedLines, patterns };
27474
27970
  }
27475
27971
  function processJsonFile(filePath, dryRun) {
27476
27972
  let raw;
27477
27973
  try {
27478
- raw = import_fs60.default.readFileSync(filePath, "utf-8");
27974
+ raw = import_fs63.default.readFileSync(filePath, "utf-8");
27479
27975
  } catch {
27480
27976
  return { redactedLines: 0, patterns: [] };
27481
27977
  }
@@ -27488,15 +27984,15 @@ function processJsonFile(filePath, dryRun) {
27488
27984
  const { value, modified, found } = redactJson(parsed);
27489
27985
  if (!modified) return { redactedLines: 0, patterns: [] };
27490
27986
  if (!dryRun) {
27491
- import_fs60.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
27987
+ import_fs63.default.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
27492
27988
  }
27493
27989
  return { redactedLines: 1, patterns: found };
27494
27990
  }
27495
27991
  function findJsonFiles(dir) {
27496
27992
  const results = [];
27497
- if (!import_fs60.default.existsSync(dir)) return results;
27498
- for (const entry of import_fs60.default.readdirSync(dir, { withFileTypes: true })) {
27499
- const full = import_path58.default.join(dir, entry.name);
27993
+ if (!import_fs63.default.existsSync(dir)) return results;
27994
+ for (const entry of import_fs63.default.readdirSync(dir, { withFileTypes: true })) {
27995
+ const full = import_path60.default.join(dir, entry.name);
27500
27996
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
27501
27997
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
27502
27998
  }
@@ -27505,9 +28001,9 @@ function findJsonFiles(dir) {
27505
28001
  function registerMaskCommand(program2) {
27506
28002
  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) => {
27507
28003
  const dryRun = !!options.dryRun;
27508
- const home = import_os52.default.homedir();
27509
- const claudeDir = import_path58.default.join(home, ".claude", "projects");
27510
- const geminiDir = import_path58.default.join(home, ".gemini", "tmp");
28004
+ const home = import_os53.default.homedir();
28005
+ const claudeDir = import_path60.default.join(home, ".claude", "projects");
28006
+ const geminiDir = import_path60.default.join(home, ".gemini", "tmp");
27511
28007
  const allFiles = [
27512
28008
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
27513
28009
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -27515,18 +28011,18 @@ function registerMaskCommand(program2) {
27515
28011
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
27516
28012
  const filtered = cutoff ? allFiles.filter((f) => {
27517
28013
  try {
27518
- return import_fs60.default.statSync(f.path).mtime >= cutoff;
28014
+ return import_fs63.default.statSync(f.path).mtime >= cutoff;
27519
28015
  } catch {
27520
28016
  return false;
27521
28017
  }
27522
28018
  }) : allFiles;
27523
28019
  if (filtered.length === 0) {
27524
- console.log(import_chalk33.default.yellow(" No session files found."));
28020
+ console.log(import_chalk34.default.yellow(" No session files found."));
27525
28021
  return;
27526
28022
  }
27527
28023
  console.log("");
27528
28024
  if (dryRun) {
27529
- console.log(import_chalk33.default.dim(" Dry run \u2014 no files will be modified.\n"));
28025
+ console.log(import_chalk34.default.dim(" Dry run \u2014 no files will be modified.\n"));
27530
28026
  }
27531
28027
  let totalFiles = 0;
27532
28028
  let totalLines = 0;
@@ -27542,23 +28038,23 @@ function registerMaskCommand(program2) {
27542
28038
  });
27543
28039
  const verb = dryRun ? "Would redact" : "Redacted";
27544
28040
  console.log(
27545
- " " + import_chalk33.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk33.default.red(`${verb}: `) + import_chalk33.default.yellow(patterns.join(", ")) + import_chalk33.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
28041
+ " " + import_chalk34.default.dim(shortPath.slice(0, 60).padEnd(62)) + import_chalk34.default.red(`${verb}: `) + import_chalk34.default.yellow(patterns.join(", ")) + import_chalk34.default.dim(` (${redactedLines} line${redactedLines !== 1 ? "s" : ""})`)
27546
28042
  );
27547
28043
  }
27548
28044
  }
27549
28045
  console.log("");
27550
28046
  if (totalFiles === 0) {
27551
- console.log(import_chalk33.default.green(" No secrets found in session history."));
28047
+ console.log(import_chalk34.default.green(" No secrets found in session history."));
27552
28048
  } else {
27553
28049
  const verb = dryRun ? "would be modified" : "modified";
27554
28050
  console.log(
27555
- import_chalk33.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk33.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
28051
+ import_chalk34.default.bold(` ${totalFiles} file${totalFiles !== 1 ? "s" : ""} ${verb}`) + import_chalk34.default.dim(`, ${totalLines} line${totalLines !== 1 ? "s" : ""} redacted`)
27556
28052
  );
27557
- console.log(" Patterns: " + import_chalk33.default.yellow(totalPatterns.join(", ")));
28053
+ console.log(" Patterns: " + import_chalk34.default.yellow(totalPatterns.join(", ")));
27558
28054
  if (!dryRun) {
27559
28055
  console.log("");
27560
28056
  console.log(
27561
- import_chalk33.default.dim(
28057
+ import_chalk34.default.dim(
27562
28058
  " Note: secrets were already sent to the AI provider during the active session.\n This cleans your local disk only. Rotate any exposed keys."
27563
28059
  )
27564
28060
  );
@@ -27571,20 +28067,20 @@ function registerMaskCommand(program2) {
27571
28067
  // src/cli.ts
27572
28068
  init_blast();
27573
28069
  var { version } = JSON.parse(
27574
- import_fs63.default.readFileSync(import_path61.default.join(__dirname, "../package.json"), "utf-8")
28070
+ import_fs66.default.readFileSync(import_path63.default.join(__dirname, "../package.json"), "utf-8")
27575
28071
  );
27576
28072
  var program = new import_commander.Command();
27577
28073
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
27578
28074
  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) => {
27579
28075
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
27580
- const credPath = import_path61.default.join(import_os55.default.homedir(), ".node9", "credentials.json");
27581
- if (!import_fs63.default.existsSync(import_path61.default.dirname(credPath)))
27582
- import_fs63.default.mkdirSync(import_path61.default.dirname(credPath), { recursive: true });
28076
+ const credPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "credentials.json");
28077
+ if (!import_fs66.default.existsSync(import_path63.default.dirname(credPath)))
28078
+ import_fs66.default.mkdirSync(import_path63.default.dirname(credPath), { recursive: true });
27583
28079
  const profileName = options.profile || "default";
27584
28080
  let existingCreds = {};
27585
28081
  try {
27586
- if (import_fs63.default.existsSync(credPath)) {
27587
- const raw = JSON.parse(import_fs63.default.readFileSync(credPath, "utf-8"));
28082
+ if (import_fs66.default.existsSync(credPath)) {
28083
+ const raw = JSON.parse(import_fs66.default.readFileSync(credPath, "utf-8"));
27588
28084
  if (raw.apiKey) {
27589
28085
  existingCreds = {
27590
28086
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -27596,14 +28092,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
27596
28092
  } catch {
27597
28093
  }
27598
28094
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
27599
- import_fs63.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
28095
+ import_fs66.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
27600
28096
  let effectiveCloud = null;
27601
28097
  if (profileName === "default") {
27602
- const configPath2 = import_path61.default.join(import_os55.default.homedir(), ".node9", "config.json");
28098
+ const configPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "config.json");
27603
28099
  let config = {};
27604
28100
  try {
27605
- if (import_fs63.default.existsSync(configPath2))
27606
- config = JSON.parse(import_fs63.default.readFileSync(configPath2, "utf-8"));
28101
+ if (import_fs66.default.existsSync(configPath))
28102
+ config = JSON.parse(import_fs66.default.readFileSync(configPath, "utf-8"));
27607
28103
  } catch {
27608
28104
  }
27609
28105
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -27618,35 +28114,35 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
27618
28114
  approvers.cloud = false;
27619
28115
  }
27620
28116
  s.approvers = approvers;
27621
- if (!import_fs63.default.existsSync(import_path61.default.dirname(configPath2)))
27622
- import_fs63.default.mkdirSync(import_path61.default.dirname(configPath2), { recursive: true });
27623
- import_fs63.default.writeFileSync(configPath2, JSON.stringify(config, null, 2), { mode: 384 });
28117
+ if (!import_fs66.default.existsSync(import_path63.default.dirname(configPath)))
28118
+ import_fs66.default.mkdirSync(import_path63.default.dirname(configPath), { recursive: true });
28119
+ import_fs66.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
27624
28120
  effectiveCloud = approvers.cloud === true;
27625
28121
  }
27626
28122
  if (options.profile && profileName !== "default") {
27627
- console.log(import_chalk35.default.green(`\u2705 Profile "${profileName}" saved`));
27628
- console.log(import_chalk35.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
28123
+ console.log(import_chalk36.default.green(`\u2705 Profile "${profileName}" saved`));
28124
+ console.log(import_chalk36.default.gray(` Switch to it per-session: NODE9_PROFILE=${profileName} claude`));
27629
28125
  } else if (options.local || effectiveCloud === false) {
27630
- console.log(import_chalk35.default.green(`\u2705 Key saved \u2014 Privacy mode \u{1F6E1}\uFE0F`));
27631
- console.log(import_chalk35.default.gray(` All decisions stay on this machine. Nothing syncs to the cloud.`));
28126
+ console.log(import_chalk36.default.green(`\u2705 Key saved \u2014 Privacy mode \u{1F6E1}\uFE0F`));
28127
+ console.log(import_chalk36.default.gray(` All decisions stay on this machine. Nothing syncs to the cloud.`));
27632
28128
  if (!options.local) {
27633
28129
  console.log(
27634
- import_chalk35.default.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
28130
+ import_chalk36.default.yellow(` Your config has cloud approvals OFF (settings.approvers.cloud).`)
27635
28131
  );
27636
28132
  console.log(
27637
- import_chalk35.default.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
28133
+ import_chalk36.default.gray(` To enable team policy + dashboard sync: set it to true, or re-init.`)
27638
28134
  );
27639
28135
  }
27640
28136
  } else {
27641
- console.log(import_chalk35.default.green(`\u2705 Logged in \u2014 agent mode`));
27642
- console.log(import_chalk35.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
28137
+ console.log(import_chalk36.default.green(`\u2705 Logged in \u2014 agent mode`));
28138
+ console.log(import_chalk36.default.gray(` Team policy enforced for all calls via Node9 cloud.`));
27643
28139
  }
27644
28140
  });
27645
28141
  program.command("signup").description("Create your node9 account / open the dashboard in your browser").option("--login", "Open the login page instead of signup").action((options) => {
27646
28142
  const route = options.login ? "auth/login" : "auth/signup";
27647
28143
  const url = `https://node9.ai/${route}?ref=cli_cmd`;
27648
28144
  console.log("");
27649
- console.log(" " + import_chalk35.default.dim("Opening ") + import_chalk35.default.cyan.underline(url));
28145
+ console.log(" " + import_chalk36.default.dim("Opening ") + import_chalk36.default.cyan.underline(url));
27650
28146
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
27651
28147
  try {
27652
28148
  const child = (0, import_child_process15.spawn)(opener, [url], {
@@ -27679,7 +28175,7 @@ program.command("addto", { hidden: true }).description("Integrate Node9 with an
27679
28175
  if (target === "hermes") return setupHermes();
27680
28176
  if (target === "hud") return setupHud();
27681
28177
  console.error(
27682
- import_chalk35.default.red(
28178
+ import_chalk36.default.red(
27683
28179
  `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
27684
28180
  )
27685
28181
  );
@@ -27693,20 +28189,20 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
27693
28189
  "The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
27694
28190
  ).action(async (target) => {
27695
28191
  if (!target) {
27696
- console.log(import_chalk35.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
27697
- console.log(" Usage: " + import_chalk35.default.white("node9 setup <target>") + "\n");
28192
+ console.log(import_chalk36.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
28193
+ console.log(" Usage: " + import_chalk36.default.white("node9 setup <target>") + "\n");
27698
28194
  console.log(" Targets:");
27699
- console.log(" " + import_chalk35.default.green("claude") + " \u2014 Claude Code (hook mode)");
27700
- console.log(" " + import_chalk35.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
27701
- console.log(" " + import_chalk35.default.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
27702
- console.log(" " + import_chalk35.default.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
27703
- console.log(" " + import_chalk35.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
27704
- console.log(" " + import_chalk35.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
27705
- console.log(" " + import_chalk35.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
27706
- console.log(" " + import_chalk35.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
27707
- console.log(" " + import_chalk35.default.green("hermes") + " \u2014 Hermes Agent (hook mode)");
28195
+ console.log(" " + import_chalk36.default.green("claude") + " \u2014 Claude Code (hook mode)");
28196
+ console.log(" " + import_chalk36.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
28197
+ console.log(" " + import_chalk36.default.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
28198
+ console.log(" " + import_chalk36.default.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
28199
+ console.log(" " + import_chalk36.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
28200
+ console.log(" " + import_chalk36.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
28201
+ console.log(" " + import_chalk36.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
28202
+ console.log(" " + import_chalk36.default.green("vscode") + " \u2014 VSCode / Copilot (MCP proxy)");
28203
+ console.log(" " + import_chalk36.default.green("hermes") + " \u2014 Hermes Agent (hook mode)");
27708
28204
  process.stdout.write(
27709
- " " + import_chalk35.default.green("hud") + " \u2014 Claude Code security statusline\n"
28205
+ " " + import_chalk36.default.green("hud") + " \u2014 Claude Code security statusline\n"
27710
28206
  );
27711
28207
  console.log("");
27712
28208
  return;
@@ -27723,7 +28219,7 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
27723
28219
  if (t === "hermes") return setupHermes();
27724
28220
  if (t === "hud") return setupHud();
27725
28221
  console.error(
27726
- import_chalk35.default.red(
28222
+ import_chalk36.default.red(
27727
28223
  `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
27728
28224
  )
27729
28225
  );
@@ -27749,33 +28245,33 @@ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks
27749
28245
  else if (target === "hud") fn = teardownHud;
27750
28246
  else {
27751
28247
  console.error(
27752
- import_chalk35.default.red(
28248
+ import_chalk36.default.red(
27753
28249
  `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
27754
28250
  )
27755
28251
  );
27756
28252
  process.exit(1);
27757
28253
  }
27758
- console.log(import_chalk35.default.cyan(`
28254
+ console.log(import_chalk36.default.cyan(`
27759
28255
  \u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
27760
28256
  `));
27761
28257
  try {
27762
28258
  fn();
27763
28259
  } catch (err2) {
27764
- console.error(import_chalk35.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
28260
+ console.error(import_chalk36.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
27765
28261
  process.exit(1);
27766
28262
  }
27767
- console.log(import_chalk35.default.gray("\n Restart the agent for changes to take effect."));
28263
+ console.log(import_chalk36.default.gray("\n Restart the agent for changes to take effect."));
27768
28264
  });
27769
28265
  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) => {
27770
- console.log(import_chalk35.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
27771
- console.log(import_chalk35.default.bold("Stopping daemon..."));
28266
+ console.log(import_chalk36.default.cyan("\n\u{1F6E1}\uFE0F Node9 Uninstall\n"));
28267
+ console.log(import_chalk36.default.bold("Stopping daemon..."));
27772
28268
  try {
27773
28269
  stopDaemon();
27774
- console.log(import_chalk35.default.green(" \u2705 Daemon stopped"));
28270
+ console.log(import_chalk36.default.green(" \u2705 Daemon stopped"));
27775
28271
  } catch {
27776
- console.log(import_chalk35.default.blue(" \u2139\uFE0F Daemon was not running"));
28272
+ console.log(import_chalk36.default.blue(" \u2139\uFE0F Daemon was not running"));
27777
28273
  }
27778
- console.log(import_chalk35.default.bold("\nRemoving hooks..."));
28274
+ console.log(import_chalk36.default.bold("\nRemoving hooks..."));
27779
28275
  let teardownFailed = false;
27780
28276
  for (const [label2, fn] of [
27781
28277
  ["Claude", teardownClaude],
@@ -27791,45 +28287,45 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
27791
28287
  } catch (err2) {
27792
28288
  teardownFailed = true;
27793
28289
  console.error(
27794
- import_chalk35.default.red(
28290
+ import_chalk36.default.red(
27795
28291
  ` \u26A0\uFE0F Failed to remove ${label2} hooks: ${err2 instanceof Error ? err2.message : String(err2)}`
27796
28292
  )
27797
28293
  );
27798
28294
  }
27799
28295
  }
27800
28296
  if (options.purge) {
27801
- const node9Dir = import_path61.default.join(import_os55.default.homedir(), ".node9");
27802
- if (import_fs63.default.existsSync(node9Dir)) {
28297
+ const node9Dir = import_path63.default.join(import_os56.default.homedir(), ".node9");
28298
+ if (import_fs66.default.existsSync(node9Dir)) {
27803
28299
  const confirmed = await (0, import_prompts2.confirm)({
27804
28300
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
27805
28301
  default: false
27806
28302
  });
27807
28303
  if (confirmed) {
27808
- import_fs63.default.rmSync(node9Dir, { recursive: true });
27809
- if (import_fs63.default.existsSync(node9Dir)) {
28304
+ import_fs66.default.rmSync(node9Dir, { recursive: true });
28305
+ if (import_fs66.default.existsSync(node9Dir)) {
27810
28306
  console.error(
27811
- import_chalk35.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
28307
+ import_chalk36.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
27812
28308
  );
27813
28309
  } else {
27814
- console.log(import_chalk35.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
28310
+ console.log(import_chalk36.default.green("\n \u2705 Deleted ~/.node9/ (config, audit log, credentials)"));
27815
28311
  }
27816
28312
  } else {
27817
- console.log(import_chalk35.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
28313
+ console.log(import_chalk36.default.yellow("\n Skipped \u2014 ~/.node9/ was not deleted."));
27818
28314
  }
27819
28315
  } else {
27820
- console.log(import_chalk35.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
28316
+ console.log(import_chalk36.default.blue("\n \u2139\uFE0F ~/.node9/ not found \u2014 nothing to delete"));
27821
28317
  }
27822
28318
  } else {
27823
28319
  console.log(
27824
- import_chalk35.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
28320
+ import_chalk36.default.gray("\n ~/.node9/ kept \u2014 run with --purge to delete config and audit log")
27825
28321
  );
27826
28322
  }
27827
28323
  if (teardownFailed) {
27828
- console.error(import_chalk35.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
28324
+ console.error(import_chalk36.default.red("\n \u26A0\uFE0F Some hooks could not be removed \u2014 see errors above."));
27829
28325
  process.exit(1);
27830
28326
  }
27831
- console.log(import_chalk35.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g node9-ai"));
27832
- console.log(import_chalk35.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
28327
+ console.log(import_chalk36.default.green.bold("\n\u{1F6E1}\uFE0F Node9 removed. Run: npm uninstall -g node9-ai"));
28328
+ console.log(import_chalk36.default.gray(" Restart any open AI agent sessions for changes to take effect.\n"));
27833
28329
  });
27834
28330
  registerDoctorCommand(program, version);
27835
28331
  program.command("explain").description(
@@ -27842,7 +28338,7 @@ program.command("explain").description(
27842
28338
  try {
27843
28339
  args = JSON.parse(trimmed);
27844
28340
  } catch {
27845
- console.error(import_chalk35.default.red(`
28341
+ console.error(import_chalk36.default.red(`
27846
28342
  \u274C Invalid JSON: ${trimmed}
27847
28343
  `));
27848
28344
  process.exit(1);
@@ -27853,54 +28349,62 @@ program.command("explain").description(
27853
28349
  }
27854
28350
  const result = await explainPolicy(tool, args);
27855
28351
  console.log("");
27856
- console.log(import_chalk35.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
28352
+ console.log(import_chalk36.default.cyan.bold("\u{1F6E1}\uFE0F Node9 Explain"));
27857
28353
  console.log("");
27858
- console.log(` ${import_chalk35.default.bold("Tool:")} ${import_chalk35.default.white(result.tool)}`);
28354
+ console.log(` ${import_chalk36.default.bold("Tool:")} ${import_chalk36.default.white(result.tool)}`);
27859
28355
  if (argsRaw) {
27860
28356
  const preview2 = argsRaw.length > 80 ? argsRaw.slice(0, 77) + "\u2026" : argsRaw;
27861
- console.log(` ${import_chalk35.default.bold("Input:")} ${import_chalk35.default.gray(preview2)}`);
28357
+ console.log(` ${import_chalk36.default.bold("Input:")} ${import_chalk36.default.gray(preview2)}`);
27862
28358
  }
27863
28359
  console.log("");
27864
- console.log(import_chalk35.default.bold("Config Sources (Waterfall):"));
28360
+ console.log(import_chalk36.default.bold("Config Sources (Waterfall):"));
27865
28361
  for (const tier of result.waterfall) {
27866
- const num3 = import_chalk35.default.gray(` ${tier.tier}.`);
28362
+ const num3 = import_chalk36.default.gray(` ${tier.tier}.`);
27867
28363
  const label2 = tier.label.padEnd(16);
27868
28364
  let statusStr;
27869
28365
  if (tier.tier === 1) {
27870
- statusStr = import_chalk35.default.gray(tier.note ?? "");
28366
+ statusStr = import_chalk36.default.gray(tier.note ?? "");
27871
28367
  } else if (tier.status === "active") {
27872
- const loc = tier.path ? import_chalk35.default.gray(tier.path) : "";
27873
- const note = tier.note ? import_chalk35.default.gray(`(${tier.note})`) : "";
27874
- statusStr = import_chalk35.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
28368
+ const loc = tier.path ? import_chalk36.default.gray(tier.path) : "";
28369
+ const note = tier.note ? import_chalk36.default.gray(`(${tier.note})`) : "";
28370
+ statusStr = import_chalk36.default.green("\u2713 active") + (loc ? " " + loc : "") + (note ? " " + note : "");
27875
28371
  } else {
27876
- statusStr = import_chalk35.default.gray("\u25CB " + (tier.note ?? "not found"));
28372
+ statusStr = import_chalk36.default.gray("\u25CB " + (tier.note ?? "not found"));
27877
28373
  }
27878
- console.log(`${num3} ${import_chalk35.default.white(label2)} ${statusStr}`);
28374
+ console.log(`${num3} ${import_chalk36.default.white(label2)} ${statusStr}`);
27879
28375
  }
27880
28376
  console.log("");
27881
- console.log(import_chalk35.default.bold("Policy Evaluation:"));
28377
+ console.log(import_chalk36.default.bold("Policy Evaluation:"));
27882
28378
  for (const step of result.steps) {
27883
28379
  const isFinal = step.isFinal;
27884
28380
  let icon;
27885
- if (step.outcome === "allow") icon = import_chalk35.default.green(" \u2705");
27886
- else if (step.outcome === "review") icon = import_chalk35.default.red(" \u{1F534}");
27887
- else if (step.outcome === "skip") icon = import_chalk35.default.gray(" \u2500 ");
27888
- else icon = import_chalk35.default.gray(" \u25CB ");
28381
+ if (step.outcome === "allow") icon = import_chalk36.default.green(" \u2705");
28382
+ else if (step.outcome === "block") icon = import_chalk36.default.red(" \u{1F6D1}");
28383
+ else if (step.outcome === "review") icon = import_chalk36.default.red(" \u{1F534}");
28384
+ else if (step.outcome === "skip") icon = import_chalk36.default.gray(" \u2500 ");
28385
+ else icon = import_chalk36.default.gray(" \u25CB ");
27889
28386
  const name = step.name.padEnd(18);
27890
- const nameStr = isFinal ? import_chalk35.default.white.bold(name) : import_chalk35.default.white(name);
27891
- const detail = isFinal ? import_chalk35.default.white(step.detail) : import_chalk35.default.gray(step.detail);
27892
- const arrow = isFinal ? import_chalk35.default.yellow(" \u2190 STOP") : "";
28387
+ const nameStr = isFinal ? import_chalk36.default.white.bold(name) : import_chalk36.default.white(name);
28388
+ const detail = isFinal ? import_chalk36.default.white(step.detail) : import_chalk36.default.gray(step.detail);
28389
+ const arrow = isFinal ? import_chalk36.default.yellow(" \u2190 STOP") : "";
27893
28390
  console.log(`${icon} ${nameStr} ${detail}${arrow}`);
27894
28391
  }
27895
28392
  console.log("");
27896
28393
  if (result.decision === "allow") {
27897
- console.log(import_chalk35.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk35.default.gray(" \u2014 no approval needed"));
28394
+ console.log(import_chalk36.default.green.bold(" Decision: \u2705 ALLOW") + import_chalk36.default.gray(" \u2014 no approval needed"));
28395
+ } else if (result.decision === "block") {
28396
+ console.log(
28397
+ import_chalk36.default.red.bold(" Decision: \u{1F6D1} BLOCK") + import_chalk36.default.gray(" \u2014 this action is blocked")
28398
+ );
28399
+ if (result.blockedByLabel) {
28400
+ console.log(import_chalk36.default.gray(` Reason: ${result.blockedByLabel}`));
28401
+ }
27898
28402
  } else {
27899
28403
  console.log(
27900
- import_chalk35.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk35.default.gray(" \u2014 human approval required")
28404
+ import_chalk36.default.red.bold(" Decision: \u{1F534} REVIEW") + import_chalk36.default.gray(" \u2014 human approval required")
27901
28405
  );
27902
28406
  if (result.blockedByLabel) {
27903
- console.log(import_chalk35.default.gray(` Reason: ${result.blockedByLabel}`));
28407
+ console.log(import_chalk36.default.gray(` Reason: ${result.blockedByLabel}`));
27904
28408
  }
27905
28409
  }
27906
28410
  console.log("");
@@ -27915,18 +28419,18 @@ program.command("tail").description("Stream live agent activity to the terminal"
27915
28419
  try {
27916
28420
  await startTail2(options);
27917
28421
  } catch (err2) {
27918
- console.error(import_chalk35.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
28422
+ console.error(import_chalk36.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
27919
28423
  process.exit(1);
27920
28424
  }
27921
28425
  });
27922
28426
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
27923
28427
  try {
27924
- const dashboardPath = import_path61.default.join(__dirname, "dashboard.mjs");
28428
+ const dashboardPath = import_path63.default.join(__dirname, "dashboard.mjs");
27925
28429
  const dynamicImport = new Function("id", "return import(id)");
27926
28430
  const mod = await dynamicImport(`file://${dashboardPath}`);
27927
28431
  await mod.startMonitor();
27928
28432
  } catch (err2) {
27929
- console.error(import_chalk35.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
28433
+ console.error(import_chalk36.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
27930
28434
  process.exit(1);
27931
28435
  }
27932
28436
  });
@@ -27959,14 +28463,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
27959
28463
  Run "node9 addto claude" to register it as the statusLine.`
27960
28464
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
27961
28465
  if (subcommand === "debug") {
27962
- const flagFile = import_path61.default.join(import_os55.default.homedir(), ".node9", "hud-debug");
28466
+ const flagFile = import_path63.default.join(import_os56.default.homedir(), ".node9", "hud-debug");
27963
28467
  if (state === "on") {
27964
- import_fs63.default.mkdirSync(import_path61.default.dirname(flagFile), { recursive: true });
27965
- import_fs63.default.writeFileSync(flagFile, "");
28468
+ import_fs66.default.mkdirSync(import_path63.default.dirname(flagFile), { recursive: true });
28469
+ import_fs66.default.writeFileSync(flagFile, "");
27966
28470
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
27967
28471
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
27968
28472
  } else if (state === "off") {
27969
- if (import_fs63.default.existsSync(flagFile)) import_fs63.default.unlinkSync(flagFile);
28473
+ if (import_fs66.default.existsSync(flagFile)) import_fs66.default.unlinkSync(flagFile);
27970
28474
  console.log("HUD debug logging disabled.");
27971
28475
  } else {
27972
28476
  console.error("Usage: node9 hud debug on|off");
@@ -27981,7 +28485,7 @@ program.command("pause").description("Temporarily disable Node9 protection for a
27981
28485
  const ms = parseDuration(options.duration);
27982
28486
  if (ms === null) {
27983
28487
  console.error(
27984
- import_chalk35.default.red(`
28488
+ import_chalk36.default.red(`
27985
28489
  \u274C Invalid duration: "${options.duration}". Use format like 15m, 1h, 30s.
27986
28490
  `)
27987
28491
  );
@@ -27989,20 +28493,20 @@ program.command("pause").description("Temporarily disable Node9 protection for a
27989
28493
  }
27990
28494
  pauseNode9(ms, options.duration);
27991
28495
  const expiresAt = new Date(Date.now() + ms).toLocaleTimeString();
27992
- console.log(import_chalk35.default.yellow(`
28496
+ console.log(import_chalk36.default.yellow(`
27993
28497
  \u23F8 Node9 paused until ${expiresAt}`));
27994
- console.log(import_chalk35.default.gray(` All tool calls will be allowed without review.`));
27995
- console.log(import_chalk35.default.gray(` Run "node9 resume" to re-enable early.
28498
+ console.log(import_chalk36.default.gray(` All tool calls will be allowed without review.`));
28499
+ console.log(import_chalk36.default.gray(` Run "node9 resume" to re-enable early.
27996
28500
  `));
27997
28501
  });
27998
28502
  program.command("resume").description("Re-enable Node9 protection immediately").action(() => {
27999
28503
  const { paused } = checkPause();
28000
28504
  if (!paused) {
28001
- console.log(import_chalk35.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
28505
+ console.log(import_chalk36.default.gray("\nNode9 is already active \u2014 nothing to resume.\n"));
28002
28506
  return;
28003
28507
  }
28004
28508
  resumeNode9();
28005
- console.log(import_chalk35.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
28509
+ console.log(import_chalk36.default.green("\n\u25B6 Node9 resumed \u2014 protection is active.\n"));
28006
28510
  });
28007
28511
  var HOOK_BASED_AGENTS = {
28008
28512
  claude: "claude",
@@ -28018,15 +28522,15 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
28018
28522
  if (HOOK_BASED_AGENTS[firstArg2] !== void 0) {
28019
28523
  const target = HOOK_BASED_AGENTS[firstArg2];
28020
28524
  console.error(
28021
- import_chalk35.default.yellow(`
28525
+ import_chalk36.default.yellow(`
28022
28526
  \u26A0\uFE0F Node9 proxy mode does not support "${target}" directly.`)
28023
28527
  );
28024
- console.error(import_chalk35.default.white(`
28528
+ console.error(import_chalk36.default.white(`
28025
28529
  "${target}" uses its own hook system. Use:`));
28026
28530
  console.error(
28027
- import_chalk35.default.green(` node9 addto ${target} `) + import_chalk35.default.gray("# one-time setup")
28531
+ import_chalk36.default.green(` node9 addto ${target} `) + import_chalk36.default.gray("# one-time setup")
28028
28532
  );
28029
- console.error(import_chalk35.default.green(` ${target} `) + import_chalk35.default.gray("# run normally"));
28533
+ console.error(import_chalk36.default.green(` ${target} `) + import_chalk36.default.gray("# run normally"));
28030
28534
  process.exit(1);
28031
28535
  }
28032
28536
  const runArgs = firstArg2 === "shell" ? commandArgs.slice(1) : commandArgs;
@@ -28043,7 +28547,7 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
28043
28547
  }
28044
28548
  );
28045
28549
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && getConfig().settings.autoStartDaemon) {
28046
- console.error(import_chalk35.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
28550
+ console.error(import_chalk36.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically..."));
28047
28551
  const daemonReady = await autoStartDaemonAndWait();
28048
28552
  if (daemonReady) result = await authorizeHeadless("shell", { command: fullCommand });
28049
28553
  }
@@ -28056,12 +28560,12 @@ program.argument("[command...]", "The agent command to run (e.g., gemini)").acti
28056
28560
  }
28057
28561
  if (!result.approved) {
28058
28562
  console.error(
28059
- import_chalk35.default.red(`
28563
+ import_chalk36.default.red(`
28060
28564
  \u274C Node9 Blocked: ${result.reason || "Dangerous command detected."}`)
28061
28565
  );
28062
28566
  process.exit(1);
28063
28567
  }
28064
- console.error(import_chalk35.default.green("\n\u2705 Approved \u2014 running command...\n"));
28568
+ console.error(import_chalk36.default.green("\n\u2705 Approved \u2014 running command...\n"));
28065
28569
  await runProxy(fullCommand);
28066
28570
  } else {
28067
28571
  program.help();
@@ -28076,6 +28580,7 @@ registerAgentsCommand(program);
28076
28580
  registerScanCommand(program);
28077
28581
  registerPostureCommand(program);
28078
28582
  registerEgressCommand(program);
28583
+ registerJailCommand(program);
28079
28584
  registerSandboxCommand(program, version);
28080
28585
  registerSessionsCommand(program);
28081
28586
  registerSessionTaintCommand(program);
@@ -28087,9 +28592,9 @@ if (process.argv[2] !== "daemon") {
28087
28592
  const isCheckHook = process.argv[2] === "check";
28088
28593
  if (isCheckHook) {
28089
28594
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
28090
- const logPath = import_path61.default.join(import_os55.default.homedir(), ".node9", "hook-debug.log");
28595
+ const logPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "hook-debug.log");
28091
28596
  const msg = reason instanceof Error ? reason.message : String(reason);
28092
- import_fs63.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
28597
+ import_fs66.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
28093
28598
  `);
28094
28599
  }
28095
28600
  process.exit(0);