@node9/proxy 2.43.1 → 2.44.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.
package/dist/cli.mjs CHANGED
@@ -32029,16 +32029,124 @@ var init_credentials = __esm({
32029
32029
  }
32030
32030
  });
32031
32031
 
32032
- // src/daemon/service.ts
32032
+ // src/daemon/startup-log.ts
32033
32033
  import fs38 from "fs";
32034
32034
  import path39 from "path";
32035
32035
  import os32 from "os";
32036
+ function capStartupLog(file2) {
32037
+ try {
32038
+ if (fs38.statSync(file2).size > MAX_STARTUP_LOG_BYTES) fs38.truncateSync(file2);
32039
+ } catch {
32040
+ }
32041
+ }
32042
+ function openStartupLogFd() {
32043
+ try {
32044
+ const file2 = DAEMON_STARTUP_LOG();
32045
+ const dir2 = path39.dirname(file2);
32046
+ if (!fs38.existsSync(dir2)) fs38.mkdirSync(dir2, { recursive: true });
32047
+ capStartupLog(file2);
32048
+ return fs38.openSync(file2, "a");
32049
+ } catch {
32050
+ return void 0;
32051
+ }
32052
+ }
32053
+ function recordStartupState(outcome, kind, detail) {
32054
+ try {
32055
+ if (outcome === "starting") {
32056
+ const prev = readStartupState();
32057
+ if (prev?.outcome === "starting") {
32058
+ const prevAt = new Date(prev.at).getTime();
32059
+ const age = Math.abs(Date.now() - prevAt);
32060
+ if (!isNaN(prevAt) && age < 24 * 60 * 60 * 1e3) return;
32061
+ }
32062
+ }
32063
+ const file2 = DAEMON_STARTUP_STATE();
32064
+ const dir2 = path39.dirname(file2);
32065
+ if (!fs38.existsSync(dir2)) fs38.mkdirSync(dir2, { recursive: true });
32066
+ const state = { outcome, at: (/* @__PURE__ */ new Date()).toISOString() };
32067
+ if (kind) state.kind = kind;
32068
+ if (detail) state.detail = detail.slice(0, MAX_DETAIL);
32069
+ const tmp = `${file2}.${process.pid}.tmp`;
32070
+ try {
32071
+ fs38.writeFileSync(tmp, JSON.stringify(state), "utf-8");
32072
+ fs38.renameSync(tmp, file2);
32073
+ } catch (err2) {
32074
+ try {
32075
+ fs38.unlinkSync(tmp);
32076
+ } catch {
32077
+ }
32078
+ throw err2;
32079
+ }
32080
+ } catch {
32081
+ }
32082
+ }
32083
+ function readStartupState() {
32084
+ try {
32085
+ const raw = fs38.readFileSync(DAEMON_STARTUP_STATE(), "utf-8");
32086
+ const s = JSON.parse(raw);
32087
+ if (!s || typeof s.outcome !== "string" || typeof s.at !== "string") return null;
32088
+ return s;
32089
+ } catch {
32090
+ return null;
32091
+ }
32092
+ }
32093
+ function readStartupCause(maxAgeMs = 24 * 60 * 60 * 1e3) {
32094
+ const s = readStartupState();
32095
+ if (!s) return null;
32096
+ const at = new Date(s.at);
32097
+ if (isNaN(at.getTime()) || Date.now() - at.getTime() > maxAgeMs) return null;
32098
+ switch (s.outcome) {
32099
+ case "ok":
32100
+ case "ok-elsewhere":
32101
+ return null;
32102
+ case "starting":
32103
+ if (Date.now() - at.getTime() < STARTING_GRACE_MS) return null;
32104
+ return {
32105
+ kind: "did-not-start",
32106
+ detail: "the daemon did not come up \u2014 see ~/.node9/daemon-startup.log and hook-debug.log",
32107
+ at,
32108
+ // `at` is the FIRST attempt of the streak, not the most recent one.
32109
+ label: "start attempts failing since"
32110
+ };
32111
+ case "failed":
32112
+ return { kind: s.kind || "failed", detail: s.detail || "", at, label: "last start attempt" };
32113
+ default:
32114
+ return null;
32115
+ }
32116
+ }
32117
+ function logDaemonStartup(kind, detail) {
32118
+ try {
32119
+ const file2 = DAEMON_STARTUP_LOG();
32120
+ const dir2 = path39.dirname(file2);
32121
+ if (!fs38.existsSync(dir2)) fs38.mkdirSync(dir2, { recursive: true });
32122
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-startup:${kind}${detail ? ` ${detail}` : ""}
32123
+ `;
32124
+ fs38.appendFileSync(file2, line, "utf-8");
32125
+ } catch {
32126
+ }
32127
+ }
32128
+ var DAEMON_STARTUP_LOG, MAX_STARTUP_LOG_BYTES, DAEMON_STARTUP_STATE, MAX_DETAIL, STARTING_GRACE_MS;
32129
+ var init_startup_log = __esm({
32130
+ "src/daemon/startup-log.ts"() {
32131
+ "use strict";
32132
+ DAEMON_STARTUP_LOG = () => path39.join(os32.homedir(), ".node9", "daemon-startup.log");
32133
+ MAX_STARTUP_LOG_BYTES = 256 * 1024;
32134
+ DAEMON_STARTUP_STATE = () => path39.join(os32.homedir(), ".node9", "daemon-startup-state.json");
32135
+ MAX_DETAIL = 200;
32136
+ STARTING_GRACE_MS = 90 * 1e3;
32137
+ }
32138
+ });
32139
+
32140
+ // src/daemon/service.ts
32141
+ import fs39 from "fs";
32142
+ import path40 from "path";
32143
+ import os33 from "os";
32036
32144
  import { spawnSync as spawnSync2, execFileSync } from "child_process";
32037
32145
  function resolveNode9Binary() {
32038
32146
  try {
32039
32147
  const script = process.argv[1];
32040
- if (typeof script === "string" && path39.isAbsolute(script) && fs38.existsSync(script)) {
32041
- return fs38.realpathSync(script);
32148
+ if (typeof script === "string" && path40.isAbsolute(script) && fs39.existsSync(script)) {
32149
+ return fs39.realpathSync(script);
32042
32150
  }
32043
32151
  } catch {
32044
32152
  }
@@ -32062,11 +32170,11 @@ function daemonServiceEnv() {
32062
32170
  return env;
32063
32171
  }
32064
32172
  function launchdPlist(binaryPath) {
32065
- const logDir = path39.join(os32.homedir(), ".node9");
32173
+ const logDir = path40.join(os33.homedir(), ".node9");
32066
32174
  const nodePath = xmlEscape(process.execPath);
32067
32175
  const scriptPath = xmlEscape(binaryPath);
32068
- const outLog = xmlEscape(path39.join(logDir, "daemon.log"));
32069
- const errLog = xmlEscape(path39.join(logDir, "daemon-error.log"));
32176
+ const outLog = xmlEscape(path40.join(logDir, "daemon.log"));
32177
+ const errLog = xmlEscape(path40.join(logDir, "daemon-error.log"));
32070
32178
  const envEntries = Object.entries(daemonServiceEnv()).map(([k, v]) => ` <key>${xmlEscape(k)}</key>
32071
32179
  <string>${xmlEscape(v)}</string>`).join("\n");
32072
32180
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -32100,9 +32208,9 @@ ${envEntries}
32100
32208
  `;
32101
32209
  }
32102
32210
  function installLaunchd(binaryPath) {
32103
- const dir2 = path39.dirname(LAUNCHD_PLIST);
32104
- if (!fs38.existsSync(dir2)) fs38.mkdirSync(dir2, { recursive: true });
32105
- fs38.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
32211
+ const dir2 = path40.dirname(LAUNCHD_PLIST);
32212
+ if (!fs39.existsSync(dir2)) fs39.mkdirSync(dir2, { recursive: true });
32213
+ fs39.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
32106
32214
  spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
32107
32215
  const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
32108
32216
  encoding: "utf8",
@@ -32113,13 +32221,13 @@ function installLaunchd(binaryPath) {
32113
32221
  }
32114
32222
  }
32115
32223
  function uninstallLaunchd() {
32116
- if (fs38.existsSync(LAUNCHD_PLIST)) {
32224
+ if (fs39.existsSync(LAUNCHD_PLIST)) {
32117
32225
  spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
32118
- fs38.unlinkSync(LAUNCHD_PLIST);
32226
+ fs39.unlinkSync(LAUNCHD_PLIST);
32119
32227
  }
32120
32228
  }
32121
32229
  function isLaunchdInstalled() {
32122
- return fs38.existsSync(LAUNCHD_PLIST);
32230
+ return fs39.existsSync(LAUNCHD_PLIST);
32123
32231
  }
32124
32232
  function systemdUnit(binaryPath) {
32125
32233
  const envLines = Object.entries(daemonServiceEnv()).map(([k, v]) => `Environment="${k}=${v}"`).join("\n");
@@ -32139,12 +32247,12 @@ WantedBy=default.target
32139
32247
  `;
32140
32248
  }
32141
32249
  function installSystemd(binaryPath) {
32142
- if (!fs38.existsSync(SYSTEMD_UNIT_DIR)) {
32143
- fs38.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
32250
+ if (!fs39.existsSync(SYSTEMD_UNIT_DIR)) {
32251
+ fs39.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
32144
32252
  }
32145
- fs38.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
32253
+ fs39.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
32146
32254
  try {
32147
- execFileSync("loginctl", ["enable-linger", os32.userInfo().username], { timeout: 3e3 });
32255
+ execFileSync("loginctl", ["enable-linger", os33.userInfo().username], { timeout: 3e3 });
32148
32256
  } catch {
32149
32257
  }
32150
32258
  const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
@@ -32164,21 +32272,21 @@ function installSystemd(binaryPath) {
32164
32272
  }
32165
32273
  }
32166
32274
  function uninstallSystemd() {
32167
- if (fs38.existsSync(SYSTEMD_UNIT)) {
32275
+ if (fs39.existsSync(SYSTEMD_UNIT)) {
32168
32276
  spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
32169
32277
  encoding: "utf8",
32170
32278
  timeout: 5e3
32171
32279
  });
32172
32280
  spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
32173
- fs38.unlinkSync(SYSTEMD_UNIT);
32281
+ fs39.unlinkSync(SYSTEMD_UNIT);
32174
32282
  }
32175
32283
  }
32176
32284
  function isSystemdInstalled() {
32177
- return fs38.existsSync(SYSTEMD_UNIT);
32285
+ return fs39.existsSync(SYSTEMD_UNIT);
32178
32286
  }
32179
- function windowsStartupDir(homeDir2 = os32.homedir()) {
32180
- const appData = process.env.APPDATA || path39.join(homeDir2, "AppData", "Roaming");
32181
- return path39.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
32287
+ function windowsStartupDir(homeDir2 = os33.homedir()) {
32288
+ const appData = process.env.APPDATA || path40.join(homeDir2, "AppData", "Roaming");
32289
+ return path40.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
32182
32290
  }
32183
32291
  function windowsLauncherVbs(nodePath, scriptPath) {
32184
32292
  for (const p of [nodePath, scriptPath]) {
@@ -32198,23 +32306,23 @@ function windowsLauncherVbs(nodePath, scriptPath) {
32198
32306
  }
32199
32307
  function installWindowsStartup(binaryPath) {
32200
32308
  const target = WIN_LAUNCHER();
32201
- const dir2 = path39.dirname(target);
32202
- if (!fs38.existsSync(dir2)) fs38.mkdirSync(dir2, { recursive: true });
32203
- fs38.writeFileSync(target, windowsLauncherVbs(process.execPath, binaryPath), "utf-8");
32309
+ const dir2 = path40.dirname(target);
32310
+ if (!fs39.existsSync(dir2)) fs39.mkdirSync(dir2, { recursive: true });
32311
+ fs39.writeFileSync(target, windowsLauncherVbs(process.execPath, binaryPath), "utf-8");
32204
32312
  spawnSync2("wscript.exe", ["//B", target], { encoding: "utf8", timeout: 1e4 });
32205
32313
  }
32206
32314
  function uninstallWindowsStartup() {
32207
32315
  const target = WIN_LAUNCHER();
32208
- if (fs38.existsSync(target)) fs38.unlinkSync(target);
32316
+ if (fs39.existsSync(target)) fs39.unlinkSync(target);
32209
32317
  }
32210
32318
  function isWindowsStartupInstalled() {
32211
- return fs38.existsSync(WIN_LAUNCHER());
32319
+ return fs39.existsSync(WIN_LAUNCHER());
32212
32320
  }
32213
32321
  function stopRunningDaemon() {
32214
- const pidFile = path39.join(os32.homedir(), ".node9", "daemon.pid");
32215
- if (!fs38.existsSync(pidFile)) return;
32322
+ const pidFile = path40.join(os33.homedir(), ".node9", "daemon.pid");
32323
+ if (!fs39.existsSync(pidFile)) return;
32216
32324
  try {
32217
- const data = JSON.parse(fs38.readFileSync(pidFile, "utf-8"));
32325
+ const data = JSON.parse(fs39.readFileSync(pidFile, "utf-8"));
32218
32326
  const pid = data.pid;
32219
32327
  const MAX_PID2 = 4194304;
32220
32328
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -32234,7 +32342,7 @@ function stopRunningDaemon() {
32234
32342
  }
32235
32343
  }
32236
32344
  try {
32237
- fs38.unlinkSync(pidFile);
32345
+ fs39.unlinkSync(pidFile);
32238
32346
  } catch {
32239
32347
  }
32240
32348
  } catch {
@@ -32327,7 +32435,7 @@ function enableDaemonServiceQuiet() {
32327
32435
  if (process.platform === "linux") {
32328
32436
  const r = spawnSync2("systemctl", ["--user", "enable", "node9-daemon"], {
32329
32437
  encoding: "utf8",
32330
- timeout: 3e3
32438
+ timeout: 1e4
32331
32439
  });
32332
32440
  return r.status === 0;
32333
32441
  }
@@ -32345,19 +32453,35 @@ function ensureAutostartHealthy(autoStartDaemon) {
32345
32453
  enabled: isDaemonServiceEnabled(),
32346
32454
  autoStartDaemon
32347
32455
  });
32348
- if (decision === "repair") return enableDaemonServiceQuiet() ? "repaired" : "skipped";
32456
+ if (decision === "repair") return enableDaemonServiceQuiet() ? "repaired" : "failed";
32349
32457
  return decision === "ok" ? "ok" : decision === "unsupported" ? "unsupported" : "skipped";
32350
32458
  }
32351
- function scheduleAutostartSelfHeal(autoStartDaemon) {
32459
+ function logAutostartHeal(result) {
32460
+ if (result !== "repaired" && result !== "failed") return;
32461
+ try {
32462
+ const file2 = DAEMON_STARTUP_LOG();
32463
+ fs39.mkdirSync(path40.dirname(file2), { recursive: true });
32464
+ capStartupLog(file2);
32465
+ fs39.appendFileSync(file2, `[${(/* @__PURE__ */ new Date()).toISOString()}] [autostart-heal] ${result}
32466
+ `);
32467
+ } catch {
32468
+ }
32469
+ }
32470
+ function scheduleAutostartSelfHeal(autoStartDaemon, heal = ensureAutostartHealthy) {
32352
32471
  if (process.env.NODE9_TESTING === "1") return;
32472
+ let failures = 0;
32353
32473
  const run = () => {
32474
+ let result = "skipped";
32354
32475
  try {
32355
- ensureAutostartHealthy(autoStartDaemon());
32476
+ result = heal(autoStartDaemon());
32356
32477
  } catch {
32357
32478
  }
32479
+ logAutostartHeal(result);
32480
+ failures = result === "failed" ? failures + 1 : 0;
32481
+ const next = failures > 0 && failures <= AUTOSTART_HEAL_RETRY_MS.length ? AUTOSTART_HEAL_RETRY_MS[failures - 1] : AUTOSTART_HEAL_INTERVAL_MS;
32482
+ setTimeout(run, next).unref();
32358
32483
  };
32359
32484
  setTimeout(run, 6e4).unref();
32360
- setInterval(run, AUTOSTART_HEAL_INTERVAL_MS).unref();
32361
32485
  }
32362
32486
  function restartDaemonService() {
32363
32487
  try {
@@ -32432,39 +32556,41 @@ function autostartState() {
32432
32556
  if (!isDaemonServiceInstalled()) return "absent";
32433
32557
  return isDaemonServiceEnabled() ? "enabled" : "disabled";
32434
32558
  }
32435
- var LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT, STARTUP_FILE, WIN_LAUNCHER, AUTOSTART_HEAL_INTERVAL_MS;
32559
+ var LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT, STARTUP_FILE, WIN_LAUNCHER, AUTOSTART_HEAL_INTERVAL_MS, AUTOSTART_HEAL_RETRY_MS;
32436
32560
  var init_service = __esm({
32437
32561
  "src/daemon/service.ts"() {
32438
32562
  "use strict";
32563
+ init_startup_log();
32439
32564
  init_api_url();
32440
32565
  LAUNCHD_LABEL = "ai.node9.daemon";
32441
- LAUNCHD_PLIST = path39.join(os32.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
32442
- SYSTEMD_UNIT_DIR = path39.join(os32.homedir(), ".config", "systemd", "user");
32443
- SYSTEMD_UNIT = path39.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
32566
+ LAUNCHD_PLIST = path40.join(os33.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
32567
+ SYSTEMD_UNIT_DIR = path40.join(os33.homedir(), ".config", "systemd", "user");
32568
+ SYSTEMD_UNIT = path40.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
32444
32569
  STARTUP_FILE = "node9-daemon.vbs";
32445
- WIN_LAUNCHER = () => path39.join(windowsStartupDir(), STARTUP_FILE);
32570
+ WIN_LAUNCHER = () => path40.join(windowsStartupDir(), STARTUP_FILE);
32446
32571
  AUTOSTART_HEAL_INTERVAL_MS = 24 * 60 * 60 * 1e3;
32572
+ AUTOSTART_HEAL_RETRY_MS = [10 * 60 * 1e3, 60 * 60 * 1e3];
32447
32573
  }
32448
32574
  });
32449
32575
 
32450
32576
  // src/apps/detect.ts
32451
- import fs39 from "fs";
32452
- import os33 from "os";
32453
- import path40 from "path";
32577
+ import fs40 from "fs";
32578
+ import os34 from "os";
32579
+ import path41 from "path";
32454
32580
  function readTail(file2, bytes) {
32455
32581
  let fd;
32456
32582
  try {
32457
- fd = fs39.openSync(file2, "r");
32458
- const size = fs39.fstatSync(fd).size;
32583
+ fd = fs40.openSync(file2, "r");
32584
+ const size = fs40.fstatSync(fd).size;
32459
32585
  const start = Math.max(0, size - bytes);
32460
32586
  const buf = Buffer.alloc(size - start);
32461
- fs39.readSync(fd, buf, 0, buf.length, start);
32587
+ fs40.readSync(fd, buf, 0, buf.length, start);
32462
32588
  const text = buf.toString("utf8");
32463
32589
  return start > 0 ? text.slice(text.indexOf("\n") + 1) : text;
32464
32590
  } catch {
32465
32591
  return "";
32466
32592
  } finally {
32467
- if (fd !== void 0) fs39.closeSync(fd);
32593
+ if (fd !== void 0) fs40.closeSync(fd);
32468
32594
  }
32469
32595
  }
32470
32596
  function programsOf(command) {
@@ -32478,7 +32604,7 @@ function programsOf(command) {
32478
32604
  i++;
32479
32605
  continue;
32480
32606
  }
32481
- const base2 = path40.basename(w.replace(/^["']|["']$/g, ""));
32607
+ const base2 = path41.basename(w.replace(/^["']|["']$/g, ""));
32482
32608
  if (WRAPPERS.has(base2)) {
32483
32609
  i++;
32484
32610
  continue;
@@ -32513,7 +32639,7 @@ function observeAudit(auditFile) {
32513
32639
  for (const p of programsOf(row.argsPreview)) commands.add(p);
32514
32640
  }
32515
32641
  const dir2 = typeof row.workingDir === "string" ? row.workingDir : "";
32516
- if (dir2 && path40.isAbsolute(dir2) && !seenDirs.has(dir2) && dirs.length < MAX_PROJECTS) {
32642
+ if (dir2 && path41.isAbsolute(dir2) && !seenDirs.has(dir2) && dirs.length < MAX_PROJECTS) {
32517
32643
  seenDirs.add(dir2);
32518
32644
  dirs.push(dir2);
32519
32645
  }
@@ -32523,21 +32649,21 @@ function observeAudit(auditFile) {
32523
32649
  function readSmall(file2) {
32524
32650
  let fd;
32525
32651
  try {
32526
- fd = fs39.openSync(file2, READ_FLAGS);
32527
- const st = fs39.fstatSync(fd);
32652
+ fd = fs40.openSync(file2, READ_FLAGS);
32653
+ const st = fs40.fstatSync(fd);
32528
32654
  if (!st.isFile() || st.size > MAX_FILE_BYTES) return null;
32529
32655
  const buf = Buffer.alloc(st.size);
32530
- const n = fs39.readSync(fd, buf, 0, st.size, 0);
32656
+ const n = fs40.readSync(fd, buf, 0, st.size, 0);
32531
32657
  return buf.toString("utf8", 0, n);
32532
32658
  } catch {
32533
32659
  return null;
32534
32660
  } finally {
32535
- if (fd !== void 0) fs39.closeSync(fd);
32661
+ if (fd !== void 0) fs40.closeSync(fd);
32536
32662
  }
32537
32663
  }
32538
32664
  function projectPackages(dir2) {
32539
32665
  const out = /* @__PURE__ */ new Set();
32540
- const pkg = readSmall(path40.join(dir2, "package.json"));
32666
+ const pkg = readSmall(path41.join(dir2, "package.json"));
32541
32667
  if (pkg) {
32542
32668
  try {
32543
32669
  const j = JSON.parse(pkg);
@@ -32554,7 +32680,7 @@ function projectPackages(dir2) {
32554
32680
  } catch {
32555
32681
  }
32556
32682
  }
32557
- const req = readSmall(path40.join(dir2, "requirements.txt"));
32683
+ const req = readSmall(path41.join(dir2, "requirements.txt"));
32558
32684
  if (req) {
32559
32685
  for (const line of req.split("\n")) {
32560
32686
  const m = /^\s*([A-Za-z0-9][A-Za-z0-9._-]*)/.exec(line);
@@ -32562,7 +32688,7 @@ function projectPackages(dir2) {
32562
32688
  out.add(m[1].toLowerCase());
32563
32689
  }
32564
32690
  }
32565
- const py = readSmall(path40.join(dir2, "pyproject.toml"));
32691
+ const py = readSmall(path41.join(dir2, "pyproject.toml"));
32566
32692
  if (py) {
32567
32693
  for (const m of py.matchAll(/["']([A-Za-z0-9][A-Za-z0-9._-]*)\s*(?:[<>=!~;[\]]|["'])/g))
32568
32694
  out.add(m[1].toLowerCase());
@@ -32582,7 +32708,7 @@ function projectPackages(dir2) {
32582
32708
  function projectEnvNames(dir2) {
32583
32709
  const out = /* @__PURE__ */ new Set();
32584
32710
  for (const f of [".env", ".env.local", ".env.development"]) {
32585
- const text = readSmall(path40.join(dir2, f));
32711
+ const text = readSmall(path41.join(dir2, f));
32586
32712
  if (!text) continue;
32587
32713
  for (const line of text.split("\n")) {
32588
32714
  const m = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/.exec(line);
@@ -32618,14 +32744,14 @@ function matchApps(obs, projectFacts, catalog = APP_CATALOG) {
32618
32744
  }
32619
32745
  return found;
32620
32746
  }
32621
- function detectApps(home = os33.homedir()) {
32622
- const obs = observeAudit(path40.join(home, ".node9", "audit.log"));
32747
+ function detectApps(home = os34.homedir()) {
32748
+ const obs = observeAudit(path41.join(home, ".node9", "audit.log"));
32623
32749
  for (const cfg of Object.values(readMcpToolsConfig())) {
32624
32750
  if (cfg?.name) obs.servers.add(cfg.name.toLowerCase());
32625
32751
  }
32626
32752
  const facts = obs.projects.filter((dir2) => {
32627
32753
  try {
32628
- return fs39.statSync(dir2).isDirectory();
32754
+ return fs40.statSync(dir2).isDirectory();
32629
32755
  } catch {
32630
32756
  return false;
32631
32757
  }
@@ -32660,7 +32786,7 @@ var init_detect = __esm({
32660
32786
  "pnpx",
32661
32787
  "bunx"
32662
32788
  ]);
32663
- READ_FLAGS = typeof fs39.constants.O_NONBLOCK === "number" ? fs39.constants.O_RDONLY | fs39.constants.O_NONBLOCK : fs39.constants.O_RDONLY;
32789
+ READ_FLAGS = typeof fs40.constants.O_NONBLOCK === "number" ? fs40.constants.O_RDONLY | fs40.constants.O_NONBLOCK : fs40.constants.O_RDONLY;
32664
32790
  }
32665
32791
  });
32666
32792
 
@@ -32720,9 +32846,9 @@ var init_daemon2 = __esm({
32720
32846
  });
32721
32847
 
32722
32848
  // src/config/migrate.ts
32723
- import fs40 from "fs";
32724
- import path41 from "path";
32725
- import os34 from "os";
32849
+ import fs41 from "fs";
32850
+ import path42 from "path";
32851
+ import os35 from "os";
32726
32852
  function backupPath(filePath) {
32727
32853
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
32728
32854
  return `${filePath}.bak-${stamp}`;
@@ -32732,7 +32858,7 @@ function migrateConfigFile(options = {}) {
32732
32858
  if (options.dryRun) {
32733
32859
  let raw;
32734
32860
  try {
32735
- raw = JSON.parse(fs40.readFileSync(filePath, "utf8"));
32861
+ raw = JSON.parse(fs41.readFileSync(filePath, "utf8"));
32736
32862
  } catch (err2) {
32737
32863
  if (err2.code === "ENOENT") return { status: "no-file" };
32738
32864
  return { status: "failed", error: `cannot read ${filePath}: ${err2.message}` };
@@ -32755,14 +32881,14 @@ function migrateConfigFile(options = {}) {
32755
32881
  }
32756
32882
  }
32757
32883
  function undoMigration(filePath = globalConfigPath()) {
32758
- const dir2 = path41.dirname(filePath);
32759
- const base2 = path41.basename(filePath);
32760
- const backups = fs40.readdirSync(dir2).filter((f) => f.startsWith(`${base2}.bak-`)).sort();
32884
+ const dir2 = path42.dirname(filePath);
32885
+ const base2 = path42.basename(filePath);
32886
+ const backups = fs41.readdirSync(dir2).filter((f) => f.startsWith(`${base2}.bak-`)).sort();
32761
32887
  const newest = backups.at(-1);
32762
32888
  if (!newest) return null;
32763
- const from = path41.join(dir2, newest);
32764
- atomicWriteSync(filePath, fs40.readFileSync(from, "utf8"), { mode: 384 });
32765
- fs40.unlinkSync(from);
32889
+ const from = path42.join(dir2, newest);
32890
+ atomicWriteSync(filePath, fs41.readFileSync(from, "utf8"), { mode: 384 });
32891
+ fs41.unlinkSync(from);
32766
32892
  return { restored: from };
32767
32893
  }
32768
32894
  function autoMigrateLocalConfig() {
@@ -32780,8 +32906,8 @@ function autoMigrateLocalConfig() {
32780
32906
  );
32781
32907
  } else if (outcome.status === "failed") {
32782
32908
  try {
32783
- fs40.appendFileSync(
32784
- path41.join(os34.homedir(), ".node9", "hook-debug.log"),
32909
+ fs41.appendFileSync(
32910
+ path42.join(os35.homedir(), ".node9", "hook-debug.log"),
32785
32911
  `[${(/* @__PURE__ */ new Date()).toISOString()}] config migrate failed: ${outcome.error}
32786
32912
  `
32787
32913
  );
@@ -33022,18 +33148,18 @@ var init_forecast = __esm({
33022
33148
  });
33023
33149
 
33024
33150
  // src/version.ts
33025
- import fs41 from "fs";
33026
- import path42 from "path";
33151
+ import fs42 from "fs";
33152
+ import path43 from "path";
33027
33153
  function readVersion() {
33028
33154
  let dir2 = __dirname;
33029
33155
  for (let i = 0; i < 5; i++) {
33030
- const p = path42.join(dir2, "package.json");
33156
+ const p = path43.join(dir2, "package.json");
33031
33157
  try {
33032
- const pkg = JSON.parse(fs41.readFileSync(p, "utf-8"));
33158
+ const pkg = JSON.parse(fs42.readFileSync(p, "utf-8"));
33033
33159
  if (pkg.name === "@node9/proxy" && typeof pkg.version === "string") return pkg.version;
33034
33160
  } catch {
33035
33161
  }
33036
- const parent = path42.dirname(dir2);
33162
+ const parent = path43.dirname(dir2);
33037
33163
  if (parent === dir2) break;
33038
33164
  dir2 = parent;
33039
33165
  }
@@ -33048,15 +33174,15 @@ var init_version = __esm({
33048
33174
  });
33049
33175
 
33050
33176
  // src/cli/commands/scan-cache.ts
33051
- import fs42 from "fs";
33052
- import os35 from "os";
33053
- import path43 from "path";
33177
+ import fs43 from "fs";
33178
+ import os36 from "os";
33179
+ import path44 from "path";
33054
33180
  import { createHash as createHash6 } from "crypto";
33055
33181
  function flushScanCaches() {
33056
33182
  for (const c of activeCaches) c.flush();
33057
33183
  }
33058
33184
  function scanCacheRoot() {
33059
- return path43.join(os35.homedir(), ".node9", "scan-cache");
33185
+ return path44.join(os36.homedir(), ".node9", "scan-cache");
33060
33186
  }
33061
33187
  function configHashOf(config) {
33062
33188
  const h = createHash6("sha256");
@@ -33064,14 +33190,14 @@ function configHashOf(config) {
33064
33190
  h.update(JSON.stringify(config.policySource));
33065
33191
  h.update(JSON.stringify(config.policy));
33066
33192
  for (const name of SIDECAR_FILES) {
33067
- const st = fileStamp(path43.join(os35.homedir(), ".node9", name));
33193
+ const st = fileStamp(path44.join(os36.homedir(), ".node9", name));
33068
33194
  h.update(`|${name}:${st ? `${st.size}:${st.mtimeMs}` : "absent"}`);
33069
33195
  }
33070
33196
  return h.digest("hex").slice(0, 16);
33071
33197
  }
33072
33198
  function fileStamp(filePath) {
33073
33199
  try {
33074
- const st = fs42.statSync(filePath);
33200
+ const st = fs43.statSync(filePath);
33075
33201
  return { size: st.size, mtimeMs: st.mtimeMs };
33076
33202
  } catch {
33077
33203
  return null;
@@ -33095,8 +33221,8 @@ var init_scan_cache = __esm({
33095
33221
  "trusted-hosts.json",
33096
33222
  "trusted-recipients.json",
33097
33223
  "rules-cache.json",
33098
- path43.join("osv", "npm", "meta.json"),
33099
- path43.join("osv", "PyPI", "meta.json")
33224
+ path44.join("osv", "npm", "meta.json"),
33225
+ path44.join("osv", "PyPI", "meta.json")
33100
33226
  ];
33101
33227
  ScanCache = class {
33102
33228
  root;
@@ -33125,7 +33251,7 @@ var init_scan_cache = __esm({
33125
33251
  if (!this.enabled) return fresh;
33126
33252
  try {
33127
33253
  const raw = JSON.parse(
33128
- fs42.readFileSync(path43.join(this.root, "index.json"), "utf8")
33254
+ fs43.readFileSync(path44.join(this.root, "index.json"), "utf8")
33129
33255
  );
33130
33256
  if (raw.cacheVersion !== SCAN_CACHE_VERSION || raw.engineVersion !== CANONICAL_EXTRACTOR_VERSION || raw.proxyVersion !== PROXY_VERSION || raw.configHash !== configHash || typeof raw.files !== "object") {
33131
33257
  this.dropAll();
@@ -33138,7 +33264,7 @@ var init_scan_cache = __esm({
33138
33264
  }
33139
33265
  dropAll() {
33140
33266
  try {
33141
- fs42.rmSync(this.root, { recursive: true, force: true });
33267
+ fs43.rmSync(this.root, { recursive: true, force: true });
33142
33268
  } catch {
33143
33269
  }
33144
33270
  }
@@ -33162,7 +33288,7 @@ var init_scan_cache = __esm({
33162
33288
  }
33163
33289
  }
33164
33290
  try {
33165
- const stored = JSON.parse(fs42.readFileSync(path43.join(this.root, e.result), "utf8"));
33291
+ const stored = JSON.parse(fs43.readFileSync(path44.join(this.root, e.result), "utf8"));
33166
33292
  if ((stored.judgedFrom ?? null) !== (e.judgedFrom ?? null) || !stored.result) {
33167
33293
  this.misses++;
33168
33294
  return null;
@@ -33177,14 +33303,14 @@ var init_scan_cache = __esm({
33177
33303
  put(filePath, stamp, agent, result, hasher, judgeFrom = null) {
33178
33304
  if (!this.enabled) return;
33179
33305
  const name = createHash6("sha256").update(`${agent}|${filePath}`).digest("hex").slice(0, 40);
33180
- const rel = path43.join("files", name.slice(0, 2), `${name}.json`);
33306
+ const rel = path44.join("files", name.slice(0, 2), `${name}.json`);
33181
33307
  const configHashes = {};
33182
33308
  if (hasher)
33183
33309
  for (const cwd of result.configCwds ?? []) configHashes[cwd] = hasher.configHashFor(cwd);
33184
33310
  try {
33185
- const abs = path43.join(this.root, rel);
33186
- fs42.mkdirSync(path43.dirname(abs), { recursive: true, mode: 448 });
33187
- fs42.writeFileSync(
33311
+ const abs = path44.join(this.root, rel);
33312
+ fs43.mkdirSync(path44.dirname(abs), { recursive: true, mode: 448 });
33313
+ fs43.writeFileSync(
33188
33314
  abs,
33189
33315
  JSON.stringify({ judgedFrom: judgeFrom ? judgeFrom.toISOString() : null, result }),
33190
33316
  { mode: 384 }
@@ -33207,9 +33333,9 @@ var init_scan_cache = __esm({
33207
33333
  /** Drop the entries of transcripts that no longer exist, and their files. */
33208
33334
  prune() {
33209
33335
  for (const [key, e] of Object.entries(this.index.files)) {
33210
- if (fs42.existsSync(key)) continue;
33336
+ if (fs43.existsSync(key)) continue;
33211
33337
  try {
33212
- fs42.rmSync(path43.join(this.root, e.result), { force: true });
33338
+ fs43.rmSync(path44.join(this.root, e.result), { force: true });
33213
33339
  } catch {
33214
33340
  }
33215
33341
  delete this.index.files[key];
@@ -33230,10 +33356,10 @@ var init_scan_cache = __esm({
33230
33356
  this.lastFlush = Date.now();
33231
33357
  if (!this.dirty) return;
33232
33358
  try {
33233
- fs42.mkdirSync(this.root, { recursive: true, mode: 448 });
33234
- const tmp = path43.join(this.root, `index.${process.pid}.tmp`);
33235
- fs42.writeFileSync(tmp, JSON.stringify(this.index), { mode: 384 });
33236
- fs42.renameSync(tmp, path43.join(this.root, "index.json"));
33359
+ fs43.mkdirSync(this.root, { recursive: true, mode: 448 });
33360
+ const tmp = path44.join(this.root, `index.${process.pid}.tmp`);
33361
+ fs43.writeFileSync(tmp, JSON.stringify(this.index), { mode: 384 });
33362
+ fs43.renameSync(tmp, path44.join(this.root, "index.json"));
33237
33363
  this.dirty = false;
33238
33364
  } catch {
33239
33365
  }
@@ -33243,9 +33369,9 @@ var init_scan_cache = __esm({
33243
33369
  });
33244
33370
 
33245
33371
  // src/cli/commands/scan-judge.ts
33246
- import fs43 from "fs";
33247
- import os36 from "os";
33248
- import path44 from "path";
33372
+ import fs44 from "fs";
33373
+ import os37 from "os";
33374
+ import path45 from "path";
33249
33375
  function findingDedupKey(f) {
33250
33376
  return `${f.source.rule.name ?? "<unnamed>"}|${preview(f.input)}|${f.project}|${f.repo ?? ""}`;
33251
33377
  }
@@ -33344,24 +33470,24 @@ var init_scan_judge = __esm({
33344
33470
  * file, so worktrees count), home-relative; undefined when none. Cached per
33345
33471
  * directory: history repeats the same few cwds thousands of times. */
33346
33472
  repoOf(cwd) {
33347
- if (!cwd || !path44.isAbsolute(cwd)) return void 0;
33473
+ if (!cwd || !path45.isAbsolute(cwd)) return void 0;
33348
33474
  if (this.repos.has(cwd)) return this.repos.get(cwd);
33349
33475
  let dir2 = cwd;
33350
33476
  let found;
33351
33477
  for (; ; ) {
33352
33478
  try {
33353
- if (fs43.existsSync(path44.join(dir2, ".git"))) {
33479
+ if (fs44.existsSync(path45.join(dir2, ".git"))) {
33354
33480
  found = dir2;
33355
33481
  break;
33356
33482
  }
33357
33483
  } catch {
33358
33484
  break;
33359
33485
  }
33360
- const parent = path44.dirname(dir2);
33486
+ const parent = path45.dirname(dir2);
33361
33487
  if (parent === dir2) break;
33362
33488
  dir2 = parent;
33363
33489
  }
33364
- const home = os36.homedir();
33490
+ const home = os37.homedir();
33365
33491
  const label3 = found && found.startsWith(home) ? "~" + found.slice(home.length) : found;
33366
33492
  this.repos.set(cwd, label3);
33367
33493
  return label3;
@@ -33506,15 +33632,15 @@ var init_scan_judge = __esm({
33506
33632
  });
33507
33633
 
33508
33634
  // src/pricing/litellm.ts
33509
- import fs44 from "fs";
33510
- import path45 from "path";
33511
- import os37 from "os";
33635
+ import fs45 from "fs";
33636
+ import path46 from "path";
33637
+ import os38 from "os";
33512
33638
  function normalizeModel(raw) {
33513
33639
  return raw.replace(/-\d{8}$/, "").toLowerCase();
33514
33640
  }
33515
33641
  function readCache(opts) {
33516
33642
  try {
33517
- const raw = JSON.parse(fs44.readFileSync(CACHE_FILE(), "utf-8"));
33643
+ const raw = JSON.parse(fs45.readFileSync(CACHE_FILE(), "utf-8"));
33518
33644
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
33519
33645
  return null;
33520
33646
  }
@@ -33529,18 +33655,18 @@ function readCache(opts) {
33529
33655
  function writeCache(prices) {
33530
33656
  try {
33531
33657
  const target = CACHE_FILE();
33532
- const dir2 = path45.dirname(target);
33533
- if (!fs44.existsSync(dir2)) fs44.mkdirSync(dir2, { recursive: true });
33658
+ const dir2 = path46.dirname(target);
33659
+ if (!fs45.existsSync(dir2)) fs45.mkdirSync(dir2, { recursive: true });
33534
33660
  const tmp = target + ".tmp";
33535
33661
  const body = {
33536
33662
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
33537
33663
  prices
33538
33664
  };
33539
- fs44.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
33540
- fs44.renameSync(tmp, target);
33665
+ fs45.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
33666
+ fs45.renameSync(tmp, target);
33541
33667
  } catch (err2) {
33542
33668
  try {
33543
- fs44.appendFileSync(
33669
+ fs45.appendFileSync(
33544
33670
  HOOK_DEBUG_LOG,
33545
33671
  `[pricing] cache write failed: ${err2.message}
33546
33672
  `
@@ -33698,7 +33824,7 @@ var init_litellm = __esm({
33698
33824
  "gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
33699
33825
  "gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
33700
33826
  };
33701
- CACHE_FILE = () => path45.join(os37.homedir(), ".node9", "model-pricing.json");
33827
+ CACHE_FILE = () => path46.join(os38.homedir(), ".node9", "model-pricing.json");
33702
33828
  TTL_MS2 = 24 * 60 * 60 * 1e3;
33703
33829
  memCache = null;
33704
33830
  memCacheAt = 0;
@@ -33708,11 +33834,11 @@ var init_litellm = __esm({
33708
33834
  });
33709
33835
 
33710
33836
  // src/cost-codex.ts
33711
- import fs45 from "fs";
33712
- import os38 from "os";
33713
- import path46 from "path";
33837
+ import fs46 from "fs";
33838
+ import os39 from "os";
33839
+ import path47 from "path";
33714
33840
  function codexSessionsDir() {
33715
- return path46.join(process.env.CODEX_HOME?.trim() || path46.join(os38.homedir(), ".codex"), "sessions");
33841
+ return path47.join(process.env.CODEX_HOME?.trim() || path47.join(os39.homedir(), ".codex"), "sessions");
33716
33842
  }
33717
33843
  function codexPriceFor(model) {
33718
33844
  return pricingFor(codexModel(model), { exact: true }) ?? CODEX_FALLBACK;
@@ -33744,14 +33870,14 @@ function codexSessionCost(model, tokens, request2) {
33744
33870
  function statAndFirstLine(file2) {
33745
33871
  const CAP = 4 * 1024 * 1024;
33746
33872
  const CHUNK = 64 * 1024;
33747
- const fd = fs45.openSync(file2, "r");
33873
+ const fd = fs46.openSync(file2, "r");
33748
33874
  try {
33749
- const stat = fs45.fstatSync(fd);
33875
+ const stat = fs46.fstatSync(fd);
33750
33876
  const limit = Math.min(stat.size, CAP);
33751
33877
  const parts = [];
33752
33878
  for (let pos = 0; pos < limit; pos += CHUNK) {
33753
33879
  const buf = Buffer.alloc(Math.min(CHUNK, limit - pos));
33754
- const read3 = fs45.readSync(fd, buf, 0, buf.length, pos);
33880
+ const read3 = fs46.readSync(fd, buf, 0, buf.length, pos);
33755
33881
  if (read3 <= 0) break;
33756
33882
  const slice = buf.subarray(0, read3);
33757
33883
  const nl = slice.indexOf(10);
@@ -33760,15 +33886,15 @@ function statAndFirstLine(file2) {
33760
33886
  }
33761
33887
  return { stat, first: Buffer.concat(parts).toString("utf8") };
33762
33888
  } finally {
33763
- fs45.closeSync(fd);
33889
+ fs46.closeSync(fd);
33764
33890
  }
33765
33891
  }
33766
33892
  function listCodexSessionFiles(base2 = codexSessionsDir()) {
33767
33893
  const files = [];
33768
33894
  const walk2 = (dir2) => {
33769
33895
  try {
33770
- for (const entry of fs45.readdirSync(dir2, { withFileTypes: true })) {
33771
- const file2 = path46.join(dir2, entry.name);
33896
+ for (const entry of fs46.readdirSync(dir2, { withFileTypes: true })) {
33897
+ const file2 = path47.join(dir2, entry.name);
33772
33898
  if (entry.isDirectory()) walk2(file2);
33773
33899
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(file2);
33774
33900
  }
@@ -33776,7 +33902,7 @@ function listCodexSessionFiles(base2 = codexSessionsDir()) {
33776
33902
  }
33777
33903
  };
33778
33904
  walk2(base2);
33779
- if (path46.basename(base2) === "sessions") walk2(path46.join(path46.dirname(base2), "archived_sessions"));
33905
+ if (path47.basename(base2) === "sessions") walk2(path47.join(path47.dirname(base2), "archived_sessions"));
33780
33906
  const sessions = /* @__PURE__ */ new Map();
33781
33907
  for (const file2 of files.sort()) {
33782
33908
  try {
@@ -33974,13 +34100,13 @@ var init_cost_codex = __esm({
33974
34100
  CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
33975
34101
  codexSource = {
33976
34102
  id: "codex",
33977
- available: () => fs45.existsSync(codexSessionsDir()) || fs45.existsSync(path46.join(path46.dirname(codexSessionsDir()), "archived_sessions")),
34103
+ available: () => fs46.existsSync(codexSessionsDir()) || fs46.existsSync(path47.join(path47.dirname(codexSessionsDir()), "archived_sessions")),
33978
34104
  collect(sinceMs) {
33979
34105
  const entries = [];
33980
34106
  for (const file2 of listCodexSessionFiles()) {
33981
34107
  try {
33982
- if (sinceMs !== void 0 && fs45.statSync(file2).mtimeMs < sinceMs) continue;
33983
- entries.push(...parseCodexSession(fs45.readFileSync(file2, "utf8").split("\n")));
34108
+ if (sinceMs !== void 0 && fs46.statSync(file2).mtimeMs < sinceMs) continue;
34109
+ entries.push(...parseCodexSession(fs46.readFileSync(file2, "utf8").split("\n")));
33984
34110
  } catch {
33985
34111
  }
33986
34112
  }
@@ -33991,11 +34117,11 @@ var init_cost_codex = __esm({
33991
34117
  });
33992
34118
 
33993
34119
  // src/cost-gemini.ts
33994
- import fs46 from "fs";
33995
- import os39 from "os";
33996
- import path47 from "path";
34120
+ import fs47 from "fs";
34121
+ import os40 from "os";
34122
+ import path48 from "path";
33997
34123
  function geminiTmpDir() {
33998
- return path47.join(os39.homedir(), ".gemini", "tmp");
34124
+ return path48.join(os40.homedir(), ".gemini", "tmp");
33999
34125
  }
34000
34126
  function geminiPriceFor(model) {
34001
34127
  let tuple = pricingFor(model);
@@ -34010,14 +34136,14 @@ function geminiPriceFor(model) {
34010
34136
  }
34011
34137
  function safeReaddir(dir2) {
34012
34138
  try {
34013
- return fs46.readdirSync(dir2);
34139
+ return fs47.readdirSync(dir2);
34014
34140
  } catch {
34015
34141
  return [];
34016
34142
  }
34017
34143
  }
34018
34144
  function isDir(p) {
34019
34145
  try {
34020
- return fs46.statSync(p).isDirectory();
34146
+ return fs47.statSync(p).isDirectory();
34021
34147
  } catch {
34022
34148
  return false;
34023
34149
  }
@@ -34025,11 +34151,11 @@ function isDir(p) {
34025
34151
  function listGeminiSessionFiles(base2) {
34026
34152
  const out = [];
34027
34153
  for (const project of safeReaddir(base2)) {
34028
- const chats = path47.join(base2, project, "chats");
34154
+ const chats = path48.join(base2, project, "chats");
34029
34155
  if (!isDir(chats)) continue;
34030
34156
  for (const f of safeReaddir(chats)) {
34031
34157
  if (f.startsWith("session-") && f.endsWith(".jsonl")) {
34032
- out.push({ file: path47.join(chats, f), project });
34158
+ out.push({ file: path48.join(chats, f), project });
34033
34159
  }
34034
34160
  }
34035
34161
  }
@@ -34096,7 +34222,7 @@ var init_cost_gemini = __esm({
34096
34222
  id: "gemini",
34097
34223
  available() {
34098
34224
  try {
34099
- return fs46.existsSync(geminiTmpDir());
34225
+ return fs47.existsSync(geminiTmpDir());
34100
34226
  } catch {
34101
34227
  return false;
34102
34228
  }
@@ -34105,13 +34231,13 @@ var init_cost_gemini = __esm({
34105
34231
  const combined2 = /* @__PURE__ */ new Map();
34106
34232
  for (const { file: file2, project } of listGeminiSessionFiles(geminiTmpDir())) {
34107
34233
  try {
34108
- if (sinceMs !== void 0 && fs46.statSync(file2).mtimeMs < sinceMs) continue;
34234
+ if (sinceMs !== void 0 && fs47.statSync(file2).mtimeMs < sinceMs) continue;
34109
34235
  } catch {
34110
34236
  continue;
34111
34237
  }
34112
34238
  let content;
34113
34239
  try {
34114
- content = fs46.readFileSync(file2, "utf8");
34240
+ content = fs47.readFileSync(file2, "utf8");
34115
34241
  } catch {
34116
34242
  continue;
34117
34243
  }
@@ -34136,15 +34262,15 @@ var init_cost_gemini = __esm({
34136
34262
  });
34137
34263
 
34138
34264
  // src/cost-copilot.ts
34139
- import fs47 from "fs";
34140
- import os40 from "os";
34141
- import path48 from "path";
34265
+ import fs48 from "fs";
34266
+ import os41 from "os";
34267
+ import path49 from "path";
34142
34268
  function copilotSessionsDir() {
34143
- return path48.join(os40.homedir(), ".copilot", "session-state");
34269
+ return path49.join(os41.homedir(), ".copilot", "session-state");
34144
34270
  }
34145
34271
  function safeReaddir2(dir2) {
34146
34272
  try {
34147
- return fs47.readdirSync(dir2);
34273
+ return fs48.readdirSync(dir2);
34148
34274
  } catch {
34149
34275
  return [];
34150
34276
  }
@@ -34220,7 +34346,7 @@ var init_cost_copilot = __esm({
34220
34346
  id: "copilot",
34221
34347
  available() {
34222
34348
  try {
34223
- return fs47.existsSync(copilotSessionsDir());
34349
+ return fs48.existsSync(copilotSessionsDir());
34224
34350
  } catch {
34225
34351
  return false;
34226
34352
  }
@@ -34229,15 +34355,15 @@ var init_cost_copilot = __esm({
34229
34355
  const base2 = copilotSessionsDir();
34230
34356
  const combined2 = /* @__PURE__ */ new Map();
34231
34357
  for (const sid of safeReaddir2(base2)) {
34232
- const file2 = path48.join(base2, sid, "events.jsonl");
34358
+ const file2 = path49.join(base2, sid, "events.jsonl");
34233
34359
  try {
34234
- if (sinceMs !== void 0 && fs47.statSync(file2).mtimeMs < sinceMs) continue;
34360
+ if (sinceMs !== void 0 && fs48.statSync(file2).mtimeMs < sinceMs) continue;
34235
34361
  } catch {
34236
34362
  continue;
34237
34363
  }
34238
34364
  let content;
34239
34365
  try {
34240
- content = fs47.readFileSync(file2, "utf8");
34366
+ content = fs48.readFileSync(file2, "utf8");
34241
34367
  } catch {
34242
34368
  continue;
34243
34369
  }
@@ -34262,21 +34388,21 @@ var init_cost_copilot = __esm({
34262
34388
  });
34263
34389
 
34264
34390
  // src/session-files.ts
34265
- import * as fs48 from "fs";
34266
- import * as path49 from "path";
34391
+ import * as fs49 from "fs";
34392
+ import * as path50 from "path";
34267
34393
  function listSessionFiles(dir2, maxDepth = 6) {
34268
34394
  const out = [];
34269
34395
  const walk2 = (d, rel, depth) => {
34270
34396
  if (depth > maxDepth) return;
34271
34397
  let entries;
34272
34398
  try {
34273
- entries = fs48.readdirSync(d, { withFileTypes: true });
34399
+ entries = fs49.readdirSync(d, { withFileTypes: true });
34274
34400
  } catch {
34275
34401
  return;
34276
34402
  }
34277
34403
  for (const e of entries) {
34278
- const childRel = rel ? path49.join(rel, e.name) : e.name;
34279
- if (e.isDirectory()) walk2(path49.join(d, e.name), childRel, depth + 1);
34404
+ const childRel = rel ? path50.join(rel, e.name) : e.name;
34405
+ if (e.isDirectory()) walk2(path50.join(d, e.name), childRel, depth + 1);
34280
34406
  else if (e.name.endsWith(".jsonl")) out.push(childRel);
34281
34407
  }
34282
34408
  };
@@ -34284,7 +34410,7 @@ function listSessionFiles(dir2, maxDepth = 6) {
34284
34410
  return out;
34285
34411
  }
34286
34412
  function sessionIdOf(relPath) {
34287
- return path49.basename(relPath).replace(/\.jsonl$/, "");
34413
+ return path50.basename(relPath).replace(/\.jsonl$/, "");
34288
34414
  }
34289
34415
  var init_session_files = __esm({
34290
34416
  "src/session-files.ts"() {
@@ -34293,17 +34419,17 @@ var init_session_files = __esm({
34293
34419
  });
34294
34420
 
34295
34421
  // src/costSync.ts
34296
- import fs49 from "fs";
34297
- import path50 from "path";
34298
- import os41 from "os";
34422
+ import fs50 from "fs";
34423
+ import path51 from "path";
34424
+ import os42 from "os";
34299
34425
  function decodeProjectDirName(dirName) {
34300
34426
  return dirName.replace(/-/g, "/");
34301
34427
  }
34302
34428
  function parseJSONLFile(filePath, fallbackWorkingDir) {
34303
- const runId = path50.basename(filePath, ".jsonl");
34429
+ const runId = path51.basename(filePath, ".jsonl");
34304
34430
  let content;
34305
34431
  try {
34306
- content = fs49.readFileSync(filePath, "utf8");
34432
+ content = fs50.readFileSync(filePath, "utf8");
34307
34433
  } catch {
34308
34434
  return /* @__PURE__ */ new Map();
34309
34435
  }
@@ -34403,7 +34529,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
34403
34529
  signal: AbortSignal.timeout(15e3)
34404
34530
  });
34405
34531
  if (!res.ok) {
34406
- fs49.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
34532
+ fs50.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
34407
34533
  `);
34408
34534
  } else {
34409
34535
  let stored;
@@ -34425,8 +34551,8 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
34425
34551
  }
34426
34552
  function debugLog(line) {
34427
34553
  try {
34428
- fs49.mkdirSync(path50.dirname(HOOK_DEBUG_LOG), { recursive: true });
34429
- fs49.appendFileSync(HOOK_DEBUG_LOG, line);
34554
+ fs50.mkdirSync(path51.dirname(HOOK_DEBUG_LOG), { recursive: true });
34555
+ fs50.appendFileSync(HOOK_DEBUG_LOG, line);
34430
34556
  } catch (err2) {
34431
34557
  console.error(`[cost-sync] could not write ${HOOK_DEBUG_LOG}: ${safeMessage(err2)}`);
34432
34558
  }
@@ -34439,10 +34565,10 @@ async function syncCost() {
34439
34565
  if (entries.length === 0) return;
34440
34566
  let username = "unknown";
34441
34567
  try {
34442
- username = os41.userInfo().username;
34568
+ username = os42.userInfo().username;
34443
34569
  } catch {
34444
34570
  }
34445
- const machineId = `${os41.hostname()}:${username}`;
34571
+ const machineId = `${os42.hostname()}:${username}`;
34446
34572
  await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
34447
34573
  }
34448
34574
  function startCostSync() {
@@ -34470,22 +34596,22 @@ var init_costSync = __esm({
34470
34596
  claudeSource = {
34471
34597
  id: "claude",
34472
34598
  available() {
34473
- return fs49.existsSync(path50.join(os41.homedir(), ".claude", "projects"));
34599
+ return fs50.existsSync(path51.join(os42.homedir(), ".claude", "projects"));
34474
34600
  },
34475
34601
  collect(sinceMs) {
34476
- const projectsDir = path50.join(os41.homedir(), ".claude", "projects");
34477
- if (!fs49.existsSync(projectsDir)) return [];
34602
+ const projectsDir = path51.join(os42.homedir(), ".claude", "projects");
34603
+ if (!fs50.existsSync(projectsDir)) return [];
34478
34604
  const combined2 = /* @__PURE__ */ new Map();
34479
34605
  let dirs;
34480
34606
  try {
34481
- dirs = fs49.readdirSync(projectsDir);
34607
+ dirs = fs50.readdirSync(projectsDir);
34482
34608
  } catch {
34483
34609
  return [];
34484
34610
  }
34485
34611
  for (const dir2 of dirs) {
34486
- const dirPath = path50.join(projectsDir, dir2);
34612
+ const dirPath = path51.join(projectsDir, dir2);
34487
34613
  try {
34488
- if (!fs49.statSync(dirPath).isDirectory()) continue;
34614
+ if (!fs50.statSync(dirPath).isDirectory()) continue;
34489
34615
  } catch {
34490
34616
  continue;
34491
34617
  }
@@ -34497,10 +34623,10 @@ var init_costSync = __esm({
34497
34623
  }
34498
34624
  const fallbackWorkingDir = decodeProjectDirName(dir2);
34499
34625
  for (const file2 of files) {
34500
- const filePath = path50.join(dirPath, file2);
34626
+ const filePath = path51.join(dirPath, file2);
34501
34627
  if (sinceMs !== void 0) {
34502
34628
  try {
34503
- if (fs49.statSync(filePath).mtimeMs < sinceMs) continue;
34629
+ if (fs50.statSync(filePath).mtimeMs < sinceMs) continue;
34504
34630
  } catch {
34505
34631
  continue;
34506
34632
  }
@@ -34736,7 +34862,7 @@ function buildScanSummary(agents) {
34736
34862
  };
34737
34863
  }
34738
34864
  function wouldHaveVerb(summary, past = "would have blocked") {
34739
- return summary.policy?.recordOnly ? `logged only (${summary.policy.mode} mode, nothing enforced)` : past;
34865
+ return summary.policy?.recordOnly ? `logged only (${levelOf(summary.policy.mode)} mode, nothing enforced)` : past;
34740
34866
  }
34741
34867
  function buildSections(findings) {
34742
34868
  const sectionMap = /* @__PURE__ */ new Map();
@@ -34872,10 +34998,10 @@ var init_scan_summary = __esm({
34872
34998
  });
34873
34999
 
34874
35000
  // src/credential-files.ts
34875
- import path51 from "path";
35001
+ import path52 from "path";
34876
35002
  function homeCredentialFiles(home) {
34877
35003
  const f = (parts, description, score, kind = "credential") => ({
34878
- full: path51.join(home, ...parts),
35004
+ full: path52.join(home, ...parts),
34879
35005
  label: "~/" + parts.join("/"),
34880
35006
  description,
34881
35007
  score,
@@ -34896,8 +35022,7 @@ function homeCredentialFiles(home) {
34896
35022
  ),
34897
35023
  f([".docker", "config.json"], "Docker registry auth tokens", 10),
34898
35024
  f([".netrc"], "FTP/HTTP credentials in plain text", 15),
34899
- f([".npmrc"], "npm auth token: can publish packages as you", 10),
34900
- f([".node9", "credentials.json"], "Node9 cloud API key", 10)
35025
+ f([".npmrc"], "npm auth token: can publish packages as you", 10)
34901
35026
  ];
34902
35027
  }
34903
35028
  var init_credential_files = __esm({
@@ -34908,9 +35033,9 @@ var init_credential_files = __esm({
34908
35033
 
34909
35034
  // src/cli/commands/blast.ts
34910
35035
  import chalk3 from "chalk";
34911
- import fs50 from "fs";
34912
- import path52 from "path";
34913
- import os42 from "os";
35036
+ import fs51 from "fs";
35037
+ import path53 from "path";
35038
+ import os43 from "os";
34914
35039
  function buildSensitivePaths(home, cwd) {
34915
35040
  const homeFiles = homeCredentialFiles(home).map(({ full, label: label3, description, score }) => ({
34916
35041
  full,
@@ -34921,19 +35046,19 @@ function buildSensitivePaths(home, cwd) {
34921
35046
  return [
34922
35047
  ...homeFiles,
34923
35048
  {
34924
- full: path52.join(cwd, ".env"),
35049
+ full: path53.join(cwd, ".env"),
34925
35050
  label: ".env (current folder)",
34926
35051
  description: "App secrets: database passwords, API keys",
34927
35052
  score: 20
34928
35053
  },
34929
35054
  {
34930
- full: path52.join(cwd, ".env.local"),
35055
+ full: path53.join(cwd, ".env.local"),
34931
35056
  label: ".env.local (current folder)",
34932
35057
  description: "Local overrides: often contains real credentials",
34933
35058
  score: 15
34934
35059
  },
34935
35060
  {
34936
- full: path52.join(cwd, ".env.production"),
35061
+ full: path53.join(cwd, ".env.production"),
34937
35062
  label: ".env.production (current folder)",
34938
35063
  description: "Production secrets",
34939
35064
  score: 20
@@ -34942,7 +35067,7 @@ function buildSensitivePaths(home, cwd) {
34942
35067
  }
34943
35068
  function isReadable(filePath) {
34944
35069
  try {
34945
- fs50.accessSync(filePath, fs50.constants.R_OK);
35070
+ fs51.accessSync(filePath, fs51.constants.R_OK);
34946
35071
  return true;
34947
35072
  } catch {
34948
35073
  return false;
@@ -34955,13 +35080,13 @@ function scoreLabel(score) {
34955
35080
  return chalk3.red.bold(`${score}/100 Critical`);
34956
35081
  }
34957
35082
  function runBlast() {
34958
- const home = os42.homedir();
35083
+ const home = os43.homedir();
34959
35084
  const cwd = process.cwd();
34960
35085
  const paths = buildSensitivePaths(home, cwd);
34961
35086
  let scoreDeduction = 0;
34962
35087
  const reachable = [];
34963
35088
  for (const p of paths) {
34964
- if (fs50.existsSync(p.full) && isReadable(p.full)) {
35089
+ if (fs51.existsSync(p.full) && isReadable(p.full)) {
34965
35090
  reachable.push(p);
34966
35091
  scoreDeduction += p.score;
34967
35092
  }
@@ -34977,11 +35102,18 @@ function runBlast() {
34977
35102
  }
34978
35103
  return { reachable, envFindings, score: Math.max(0, 100 - scoreDeduction) };
34979
35104
  }
35105
+ function labelColumnWidth(labels) {
35106
+ return Math.max(38, ...labels.map((l) => l.length + 2));
35107
+ }
34980
35108
  function registerBlastCommand(program2) {
34981
35109
  program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
34982
- const home = os42.homedir();
35110
+ const home = os43.homedir();
34983
35111
  const cwd = process.cwd();
34984
35112
  const { reachable, envFindings, score } = runBlast();
35113
+ const width = labelColumnWidth([
35114
+ ...reachable.map((p) => p.label),
35115
+ ...envFindings.map((f) => f.key)
35116
+ ]);
34985
35117
  console.log("");
34986
35118
  console.log(
34987
35119
  chalk3.bold(" \u{1F52D} Node9 Blast Radius") + chalk3.dim(" \xB7 what an AI agent can reach from here")
@@ -34992,7 +35124,7 @@ function registerBlastCommand(program2) {
34992
35124
  console.log(" " + chalk3.red.bold("Sensitive files reachable:"));
34993
35125
  for (const p of reachable) {
34994
35126
  console.log(
34995
- " " + chalk3.red("\u2717 ") + chalk3.yellow(p.label.padEnd(38)) + chalk3.dim(p.description)
35127
+ " " + chalk3.red("\u2717 ") + chalk3.yellow(p.label.padEnd(width)) + chalk3.dim(p.description)
34996
35128
  );
34997
35129
  }
34998
35130
  console.log("");
@@ -35001,7 +35133,7 @@ function registerBlastCommand(program2) {
35001
35133
  console.log(" " + chalk3.red.bold("Secrets in active environment:"));
35002
35134
  for (const f of envFindings) {
35003
35135
  console.log(
35004
- " " + chalk3.red("\u2717 ") + chalk3.yellow(f.key.padEnd(38)) + chalk3.dim(f.patternName)
35136
+ " " + chalk3.red("\u2717 ") + chalk3.yellow(f.key.padEnd(width)) + chalk3.dim(f.patternName)
35005
35137
  );
35006
35138
  }
35007
35139
  console.log("");
@@ -35116,6 +35248,12 @@ function relativeDate(timestamp2, now = /* @__PURE__ */ new Date()) {
35116
35248
  if (days > 90) return "90d+";
35117
35249
  return `${days}d`;
35118
35250
  }
35251
+ function relativeAgo(timestamp2, now = /* @__PURE__ */ new Date()) {
35252
+ const rel = relativeDate(timestamp2, now);
35253
+ if (rel === "?" || rel === "today") return rel;
35254
+ if (rel === "1d") return "yesterday";
35255
+ return `${rel} ago`;
35256
+ }
35119
35257
  var PANEL_WIDTH;
35120
35258
  var init_scan_derive = __esm({
35121
35259
  "src/cli/render/scan-derive.ts"() {
@@ -35169,17 +35307,17 @@ var init_scan_json = __esm({
35169
35307
  });
35170
35308
 
35171
35309
  // src/cli/render/scan-history.ts
35172
- import fs51 from "fs";
35173
- import path53 from "path";
35174
- import os43 from "os";
35310
+ import fs52 from "fs";
35311
+ import path54 from "path";
35312
+ import os44 from "os";
35175
35313
  function defaultHistoryPath() {
35176
- return path53.join(os43.homedir(), ".node9", "scan-history.json");
35314
+ return path54.join(os44.homedir(), ".node9", "scan-history.json");
35177
35315
  }
35178
35316
  function readPreviousScan(opts = {}) {
35179
35317
  const filePath = opts.path ?? defaultHistoryPath();
35180
35318
  try {
35181
- if (!fs51.existsSync(filePath)) return null;
35182
- const raw = fs51.readFileSync(filePath, "utf8");
35319
+ if (!fs52.existsSync(filePath)) return null;
35320
+ const raw = fs52.readFileSync(filePath, "utf8");
35183
35321
  const parsed = JSON.parse(raw);
35184
35322
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
35185
35323
  const last = parsed[parsed.length - 1];
@@ -35193,11 +35331,11 @@ function appendScanHistory(record2, opts = {}) {
35193
35331
  const filePath = opts.path ?? defaultHistoryPath();
35194
35332
  const cap = opts.cap ?? SCAN_HISTORY_CAP;
35195
35333
  try {
35196
- fs51.mkdirSync(path53.dirname(filePath), { recursive: true });
35334
+ fs52.mkdirSync(path54.dirname(filePath), { recursive: true });
35197
35335
  let history = [];
35198
- if (fs51.existsSync(filePath)) {
35336
+ if (fs52.existsSync(filePath)) {
35199
35337
  try {
35200
- const parsed = JSON.parse(fs51.readFileSync(filePath, "utf8"));
35338
+ const parsed = JSON.parse(fs52.readFileSync(filePath, "utf8"));
35201
35339
  if (Array.isArray(parsed)) {
35202
35340
  history = parsed.filter(isValidRecord);
35203
35341
  }
@@ -35252,9 +35390,9 @@ __export(scan_watermark_exports, {
35252
35390
  tickForensicBroadcast: () => tickForensicBroadcast,
35253
35391
  tickScanWatcher: () => tickScanWatcher
35254
35392
  });
35255
- import fs52 from "fs";
35256
- import os44 from "os";
35257
- import path54 from "path";
35393
+ import fs53 from "fs";
35394
+ import os45 from "os";
35395
+ import path55 from "path";
35258
35396
  import readline from "readline";
35259
35397
  function freshWatermark() {
35260
35398
  return {
@@ -35267,7 +35405,7 @@ function freshWatermark() {
35267
35405
  function loadWatermark() {
35268
35406
  let raw;
35269
35407
  try {
35270
- raw = fs52.readFileSync(WATERMARK_FILE(), "utf-8");
35408
+ raw = fs53.readFileSync(WATERMARK_FILE(), "utf-8");
35271
35409
  } catch {
35272
35410
  return { status: "fresh", wm: freshWatermark() };
35273
35411
  }
@@ -35319,28 +35457,28 @@ function loadWatermark() {
35319
35457
  function saveWatermark(wm) {
35320
35458
  if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
35321
35459
  const target = WATERMARK_FILE();
35322
- const dir2 = path54.dirname(target);
35323
- if (!fs52.existsSync(dir2)) fs52.mkdirSync(dir2, { recursive: true });
35460
+ const dir2 = path55.dirname(target);
35461
+ if (!fs53.existsSync(dir2)) fs53.mkdirSync(dir2, { recursive: true });
35324
35462
  const tmp = target + ".tmp";
35325
- fs52.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
35326
- fs52.renameSync(tmp, target);
35463
+ fs53.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
35464
+ fs53.renameSync(tmp, target);
35327
35465
  }
35328
35466
  function listJsonlFiles() {
35329
35467
  const root = PROJECTS_DIR();
35330
- if (!fs52.existsSync(root)) return [];
35468
+ if (!fs53.existsSync(root)) return [];
35331
35469
  const out = [];
35332
- for (const entry of fs52.readdirSync(root, { withFileTypes: true })) {
35470
+ for (const entry of fs53.readdirSync(root, { withFileTypes: true })) {
35333
35471
  if (!entry.isDirectory()) continue;
35334
- const projectDir = path54.join(root, entry.name);
35472
+ const projectDir = path55.join(root, entry.name);
35335
35473
  let inner;
35336
35474
  try {
35337
- inner = fs52.readdirSync(projectDir, { withFileTypes: true });
35475
+ inner = fs53.readdirSync(projectDir, { withFileTypes: true });
35338
35476
  } catch {
35339
35477
  continue;
35340
35478
  }
35341
35479
  for (const file2 of inner) {
35342
35480
  if (file2.isFile() && file2.name.endsWith(".jsonl")) {
35343
- out.push(path54.join(projectDir, file2.name));
35481
+ out.push(path55.join(projectDir, file2.name));
35344
35482
  }
35345
35483
  }
35346
35484
  }
@@ -35348,7 +35486,7 @@ function listJsonlFiles() {
35348
35486
  }
35349
35487
  function fileSize(p) {
35350
35488
  try {
35351
- return fs52.statSync(p).size;
35489
+ return fs53.statSync(p).size;
35352
35490
  } catch {
35353
35491
  return 0;
35354
35492
  }
@@ -35358,7 +35496,7 @@ async function scanDelta(filePath, fromByte, onLine) {
35358
35496
  if (size <= fromByte) return fromByte;
35359
35497
  const lastNl = findLastNewline(filePath, fromByte, size);
35360
35498
  const endsWithNewline = lastNl === size - 1;
35361
- const stream = fs52.createReadStream(filePath, {
35499
+ const stream = fs53.createReadStream(filePath, {
35362
35500
  start: fromByte,
35363
35501
  end: size - 1,
35364
35502
  highWaterMark: 64 * 1024
@@ -35387,7 +35525,7 @@ function findLastNewline(filePath, from, size) {
35387
35525
  const CHUNK = 64 * 1024;
35388
35526
  let fd;
35389
35527
  try {
35390
- fd = fs52.openSync(filePath, "r");
35528
+ fd = fs53.openSync(filePath, "r");
35391
35529
  } catch {
35392
35530
  return -1;
35393
35531
  }
@@ -35396,7 +35534,7 @@ function findLastNewline(filePath, from, size) {
35396
35534
  let end = size;
35397
35535
  while (end > from) {
35398
35536
  const start = Math.max(from, end - CHUNK);
35399
- const n = fs52.readSync(fd, buf, 0, end - start, start);
35537
+ const n = fs53.readSync(fd, buf, 0, end - start, start);
35400
35538
  const idx = buf.subarray(0, n).lastIndexOf(10);
35401
35539
  if (idx !== -1) return start + idx;
35402
35540
  end = start;
@@ -35405,7 +35543,7 @@ function findLastNewline(filePath, from, size) {
35405
35543
  } catch {
35406
35544
  return -1;
35407
35545
  } finally {
35408
- fs52.closeSync(fd);
35546
+ fs53.closeSync(fd);
35409
35547
  }
35410
35548
  }
35411
35549
  function safeCanaryCtxValues() {
@@ -35416,7 +35554,7 @@ function safeCanaryCtxValues() {
35416
35554
  }
35417
35555
  }
35418
35556
  function canonicalContextForLine(obj, sessionId, lineIndex, canaryVals = []) {
35419
- const cwd = typeof obj.cwd === "string" && path54.isAbsolute(obj.cwd) ? obj.cwd : void 0;
35557
+ const cwd = typeof obj.cwd === "string" && path55.isAbsolute(obj.cwd) ? obj.cwd : void 0;
35420
35558
  return {
35421
35559
  sessionId,
35422
35560
  lineIndex,
@@ -35432,7 +35570,7 @@ function canonicalContextForLine(obj, sessionId, lineIndex, canaryVals = []) {
35432
35570
  // line-level DLP runs above already
35433
35571
  canaryValues: canaryVals,
35434
35572
  ...cwd && { cwd },
35435
- homeDirs: [os44.homedir()]
35573
+ homeDirs: [os45.homedir()]
35436
35574
  };
35437
35575
  }
35438
35576
  function extractFindingsFromLine(line, sessionId, lineIndex, canaryVals = []) {
@@ -35520,7 +35658,7 @@ async function tickForensicBroadcast(offsets) {
35520
35658
  continue;
35521
35659
  }
35522
35660
  if (size <= offset) continue;
35523
- const sessionId = path54.basename(file2, ".jsonl");
35661
+ const sessionId = path55.basename(file2, ".jsonl");
35524
35662
  const newOffset = await scanDelta(file2, offset, (obj, lineIndex) => {
35525
35663
  out.push(...extractFindingsFromLine(obj, sessionId, lineIndex, canaryVals));
35526
35664
  });
@@ -35576,7 +35714,7 @@ function emptyTick(uploadAs) {
35576
35714
  function readRawWatermarkPreservingOffsets() {
35577
35715
  let raw;
35578
35716
  try {
35579
- raw = fs52.readFileSync(WATERMARK_FILE(), "utf-8");
35717
+ raw = fs53.readFileSync(WATERMARK_FILE(), "utf-8");
35580
35718
  } catch {
35581
35719
  return null;
35582
35720
  }
@@ -35611,13 +35749,13 @@ async function runActualTick(wm) {
35611
35749
  if (!known) {
35612
35750
  let mtimeMs = 0;
35613
35751
  try {
35614
- mtimeMs = fs52.statSync(filePath).mtime.getTime();
35752
+ mtimeMs = fs53.statSync(filePath).mtime.getTime();
35615
35753
  } catch {
35616
35754
  continue;
35617
35755
  }
35618
35756
  if (mtimeMs >= watermarkCreatedAt) {
35619
35757
  filesNew++;
35620
- const sessionId2 = path54.basename(filePath, ".jsonl");
35758
+ const sessionId2 = path55.basename(filePath, ".jsonl");
35621
35759
  const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
35622
35760
  totalToolCalls++;
35623
35761
  toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
@@ -35635,7 +35773,7 @@ async function runActualTick(wm) {
35635
35773
  filesSkipped++;
35636
35774
  continue;
35637
35775
  }
35638
- const sessionId = path54.basename(filePath, ".jsonl");
35776
+ const sessionId = path55.basename(filePath, ".jsonl");
35639
35777
  const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
35640
35778
  totalToolCalls++;
35641
35779
  toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
@@ -35670,8 +35808,8 @@ var init_scan_watermark = __esm({
35670
35808
  init_registry2();
35671
35809
  init_config();
35672
35810
  init_dist();
35673
- PROJECTS_DIR = () => path54.join(os44.homedir(), ".claude", "projects");
35674
- WATERMARK_FILE = () => path54.join(os44.homedir(), ".node9", "scan-watermark.json");
35811
+ PROJECTS_DIR = () => path55.join(os45.homedir(), ".claude", "projects");
35812
+ WATERMARK_FILE = () => path55.join(os45.homedir(), ".node9", "scan-watermark.json");
35675
35813
  MAX_LINE_BYTES = 2 * 1024 * 1024;
35676
35814
  WATERMARK_SCHEMA_VERSION = 2;
35677
35815
  LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
@@ -35687,10 +35825,10 @@ __export(scan_upload_history_exports, {
35687
35825
  parseSinceCutoff: () => parseSinceCutoff,
35688
35826
  runUploadHistory: () => runUploadHistory
35689
35827
  });
35690
- import fs53 from "fs";
35828
+ import fs54 from "fs";
35691
35829
  import https from "https";
35692
- import os45 from "os";
35693
- import path55 from "path";
35830
+ import os46 from "os";
35831
+ import path56 from "path";
35694
35832
  import chalk5 from "chalk";
35695
35833
  function emptySignals2() {
35696
35834
  return {
@@ -35725,40 +35863,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
35725
35863
  return now.getTime() - 90 * 864e5;
35726
35864
  }
35727
35865
  function* iterateJsonlFiles(cutoffMs) {
35728
- const projectsDir = path55.join(os45.homedir(), ".claude", "projects");
35866
+ const projectsDir = path56.join(os46.homedir(), ".claude", "projects");
35729
35867
  let dirs;
35730
35868
  try {
35731
- dirs = fs53.readdirSync(projectsDir);
35869
+ dirs = fs54.readdirSync(projectsDir);
35732
35870
  } catch {
35733
35871
  return;
35734
35872
  }
35735
35873
  for (const dir2 of dirs) {
35736
- const dirPath = path55.join(projectsDir, dir2);
35874
+ const dirPath = path56.join(projectsDir, dir2);
35737
35875
  let stats;
35738
35876
  try {
35739
- stats = fs53.statSync(dirPath);
35877
+ stats = fs54.statSync(dirPath);
35740
35878
  } catch {
35741
35879
  continue;
35742
35880
  }
35743
35881
  if (!stats.isDirectory()) continue;
35744
35882
  let files;
35745
35883
  try {
35746
- files = fs53.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
35884
+ files = fs54.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
35747
35885
  } catch {
35748
35886
  continue;
35749
35887
  }
35750
35888
  for (const file2 of files) {
35751
- const filePath = path55.join(dirPath, file2);
35889
+ const filePath = path56.join(dirPath, file2);
35752
35890
  let mtime = 0;
35753
35891
  try {
35754
- mtime = fs53.statSync(filePath).mtimeMs;
35892
+ mtime = fs54.statSync(filePath).mtimeMs;
35755
35893
  } catch {
35756
35894
  continue;
35757
35895
  }
35758
35896
  if (mtime < cutoffMs) continue;
35759
35897
  yield {
35760
35898
  filePath,
35761
- sessionId: path55.basename(file2, ".jsonl"),
35899
+ sessionId: path56.basename(file2, ".jsonl"),
35762
35900
  projectDir: dir2
35763
35901
  };
35764
35902
  }
@@ -35826,7 +35964,7 @@ async function runUploadHistory(opts) {
35826
35964
  filesScanned++;
35827
35965
  let content;
35828
35966
  try {
35829
- content = fs53.readFileSync(filePath, "utf8");
35967
+ content = fs54.readFileSync(filePath, "utf8");
35830
35968
  } catch {
35831
35969
  continue;
35832
35970
  }
@@ -35900,10 +36038,10 @@ async function runUploadHistory(opts) {
35900
36038
  const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
35901
36039
  let username = "unknown";
35902
36040
  try {
35903
- username = os45.userInfo().username;
36041
+ username = os46.userInfo().username;
35904
36042
  } catch {
35905
36043
  }
35906
- const machineId = `${os45.hostname()}:${username}`;
36044
+ const machineId = `${os46.hostname()}:${username}`;
35907
36045
  await postJson(costUrl, creds.apiKey, {
35908
36046
  machineId,
35909
36047
  entries: dailyEntries
@@ -35977,9 +36115,9 @@ var init_scan_upload_history = __esm({
35977
36115
 
35978
36116
  // src/cli/commands/scan.ts
35979
36117
  import chalk6 from "chalk";
35980
- import fs54 from "fs";
35981
- import path56 from "path";
35982
- import os46 from "os";
36118
+ import fs55 from "fs";
36119
+ import path57 from "path";
36120
+ import os47 from "os";
35983
36121
  import stringWidth2 from "string-width";
35984
36122
  function dayKey(ts) {
35985
36123
  if (!ts) return "unknown";
@@ -36122,7 +36260,7 @@ function emptyScanDedup() {
36122
36260
  return { findingsKeys: /* @__PURE__ */ new Set(), dlpKeys: /* @__PURE__ */ new Set(), canaryIndex: /* @__PURE__ */ new Map() };
36123
36261
  }
36124
36262
  function displayHome(p) {
36125
- const home = os46.homedir();
36263
+ const home = os47.homedir();
36126
36264
  return p.startsWith(home) ? "~" + p.slice(home.length) : p;
36127
36265
  }
36128
36266
  function dlpKey(patternName, redactedSample, projLabel, timestamp2) {
@@ -36261,7 +36399,7 @@ function detectLoops(calls, project, sessionId, agent) {
36261
36399
  return findings.sort((a, b) => b.count - a.count);
36262
36400
  }
36263
36401
  function geminiChatFiles(chatsDir) {
36264
- const files = fs54.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
36402
+ const files = fs55.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
36265
36403
  const seen = /* @__PURE__ */ new Set();
36266
36404
  return files.filter((f) => {
36267
36405
  const id = f.replace(/\.jsonl?$/, "");
@@ -36273,14 +36411,14 @@ function geminiChatFiles(chatsDir) {
36273
36411
  function countScanFiles(startDate = null) {
36274
36412
  let total = 0;
36275
36413
  const counts = (p) => fileMayHoldWindow(fileStamp(p), startDate);
36276
- const claudeDir = path56.join(os46.homedir(), ".claude", "projects");
36277
- if (fs54.existsSync(claudeDir)) {
36414
+ const claudeDir = path57.join(os47.homedir(), ".claude", "projects");
36415
+ if (fs55.existsSync(claudeDir)) {
36278
36416
  try {
36279
- for (const proj of fs54.readdirSync(claudeDir)) {
36280
- const p = path56.join(claudeDir, proj);
36417
+ for (const proj of fs55.readdirSync(claudeDir)) {
36418
+ const p = path57.join(claudeDir, proj);
36281
36419
  try {
36282
- if (!fs54.statSync(p).isDirectory()) continue;
36283
- total += listSessionFiles(p).filter((f) => counts(path56.join(p, f))).length;
36420
+ if (!fs55.statSync(p).isDirectory()) continue;
36421
+ total += listSessionFiles(p).filter((f) => counts(path57.join(p, f))).length;
36284
36422
  } catch {
36285
36423
  continue;
36286
36424
  }
@@ -36288,18 +36426,18 @@ function countScanFiles(startDate = null) {
36288
36426
  } catch {
36289
36427
  }
36290
36428
  }
36291
- const geminiDir = path56.join(os46.homedir(), ".gemini", "tmp");
36292
- if (fs54.existsSync(geminiDir)) {
36429
+ const geminiDir = path57.join(os47.homedir(), ".gemini", "tmp");
36430
+ if (fs55.existsSync(geminiDir)) {
36293
36431
  try {
36294
- for (const slug2 of fs54.readdirSync(geminiDir)) {
36295
- const p = path56.join(geminiDir, slug2);
36432
+ for (const slug2 of fs55.readdirSync(geminiDir)) {
36433
+ const p = path57.join(geminiDir, slug2);
36296
36434
  try {
36297
- if (!fs54.statSync(p).isDirectory()) continue;
36298
- const chatsDir = path56.join(p, "chats");
36299
- if (fs54.existsSync(chatsDir)) {
36435
+ if (!fs55.statSync(p).isDirectory()) continue;
36436
+ const chatsDir = path57.join(p, "chats");
36437
+ if (fs55.existsSync(chatsDir)) {
36300
36438
  try {
36301
36439
  total += geminiChatFiles(chatsDir).filter(
36302
- (f) => counts(path56.join(chatsDir, f))
36440
+ (f) => counts(path57.join(chatsDir, f))
36303
36441
  ).length;
36304
36442
  } catch {
36305
36443
  }
@@ -36312,13 +36450,13 @@ function countScanFiles(startDate = null) {
36312
36450
  }
36313
36451
  }
36314
36452
  for (const surface of ["antigravity-cli", "antigravity-ide"]) {
36315
- const brainDir = path56.join(os46.homedir(), ".gemini", surface, "brain");
36316
- if (!fs54.existsSync(brainDir)) continue;
36453
+ const brainDir = path57.join(os47.homedir(), ".gemini", surface, "brain");
36454
+ if (!fs55.existsSync(brainDir)) continue;
36317
36455
  try {
36318
- for (const conv of fs54.readdirSync(brainDir)) {
36319
- const convPath = path56.join(brainDir, conv);
36456
+ for (const conv of fs55.readdirSync(brainDir)) {
36457
+ const convPath = path57.join(brainDir, conv);
36320
36458
  try {
36321
- if (!fs54.statSync(convPath).isDirectory()) continue;
36459
+ if (!fs55.statSync(convPath).isDirectory()) continue;
36322
36460
  const transcript = antigravityTranscriptPath(convPath);
36323
36461
  if (transcript && counts(transcript)) total += 1;
36324
36462
  } catch {
@@ -36328,12 +36466,12 @@ function countScanFiles(startDate = null) {
36328
36466
  } catch {
36329
36467
  }
36330
36468
  }
36331
- const copilotDir = path56.join(os46.homedir(), ".copilot", "session-state");
36332
- if (fs54.existsSync(copilotDir)) {
36469
+ const copilotDir = path57.join(os47.homedir(), ".copilot", "session-state");
36470
+ if (fs55.existsSync(copilotDir)) {
36333
36471
  try {
36334
- for (const sid of fs54.readdirSync(copilotDir)) {
36335
- const events = path56.join(copilotDir, sid, "events.jsonl");
36336
- if (fs54.existsSync(events) && counts(events)) total += 1;
36472
+ for (const sid of fs55.readdirSync(copilotDir)) {
36473
+ const events = path57.join(copilotDir, sid, "events.jsonl");
36474
+ if (fs55.existsSync(events) && counts(events)) total += 1;
36337
36475
  }
36338
36476
  } catch {
36339
36477
  }
@@ -36357,7 +36495,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
36357
36495
  const session = { sessionId, costUSD: 0, toolCalls: 0 };
36358
36496
  let raw;
36359
36497
  try {
36360
- raw = fs54.readFileSync(path56.join(projPath, file2), "utf-8");
36498
+ raw = fs55.readFileSync(path57.join(projPath, file2), "utf-8");
36361
36499
  } catch {
36362
36500
  return false;
36363
36501
  }
@@ -36426,7 +36564,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
36426
36564
  if (block.type !== "tool_result") continue;
36427
36565
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
36428
36566
  if (filePath) {
36429
- const ext = path56.extname(filePath).toLowerCase();
36567
+ const ext = path57.extname(filePath).toLowerCase();
36430
36568
  if (CODE_EXTENSIONS.has(ext)) continue;
36431
36569
  }
36432
36570
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -36546,9 +36684,9 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
36546
36684
  return true;
36547
36685
  }
36548
36686
  async function processClaudeProject(proj, projectsDir, judge, cache7, startDate, result, dedup, canaryVals, onProgress, onLine) {
36549
- const projPath = path56.join(projectsDir, proj);
36687
+ const projPath = path57.join(projectsDir, proj);
36550
36688
  try {
36551
- if (!fs54.statSync(projPath).isDirectory()) return;
36689
+ if (!fs55.statSync(projPath).isDirectory()) return;
36552
36690
  } catch {
36553
36691
  return;
36554
36692
  }
@@ -36560,7 +36698,7 @@ async function processClaudeProject(proj, projectsDir, judge, cache7, startDate,
36560
36698
  return;
36561
36699
  }
36562
36700
  for (const file2 of files) {
36563
- const filePath = path56.join(projPath, file2);
36701
+ const filePath = path57.join(projPath, file2);
36564
36702
  const stamp = fileStamp(filePath);
36565
36703
  if (!fileMayHoldWindow(stamp, startDate)) continue;
36566
36704
  result.filesScanned++;
@@ -36591,7 +36729,7 @@ async function processClaudeProject(proj, projectsDir, judge, cache7, startDate,
36591
36729
  }
36592
36730
  function claudeProjectLabel(dirName) {
36593
36731
  const name = decodeURIComponent(dirName);
36594
- const homeDashed = os46.homedir().replace(/\//g, "-");
36732
+ const homeDashed = os47.homedir().replace(/\//g, "-");
36595
36733
  const label3 = name.startsWith(homeDashed) ? "~" + decodeProjectDirName(name.slice(homeDashed.length)) : decodeProjectDirName(name);
36596
36734
  return stripTerminalEscapes(label3).slice(0, 40);
36597
36735
  }
@@ -36702,12 +36840,12 @@ function mergeFileScan(result, file2, dedup, startDate = null) {
36702
36840
  }
36703
36841
  }
36704
36842
  async function scanClaudeHistory(startDate, onProgress, onLine, opts = {}) {
36705
- const projectsDir = path56.join(os46.homedir(), ".claude", "projects");
36843
+ const projectsDir = path57.join(os47.homedir(), ".claude", "projects");
36706
36844
  const result = emptyClaudeScan();
36707
- if (!fs54.existsSync(projectsDir)) return result;
36845
+ if (!fs55.existsSync(projectsDir)) return result;
36708
36846
  let projDirs;
36709
36847
  try {
36710
- projDirs = fs54.readdirSync(projectsDir);
36848
+ projDirs = fs55.readdirSync(projectsDir);
36711
36849
  } catch {
36712
36850
  return result;
36713
36851
  }
@@ -36735,7 +36873,7 @@ async function scanClaudeHistory(startDate, onProgress, onLine, opts = {}) {
36735
36873
  }
36736
36874
  async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
36737
36875
  const canaryVals = safeCanaryScanValues();
36738
- const tmpDir = path56.join(os46.homedir(), ".gemini", "tmp");
36876
+ const tmpDir = path57.join(os47.homedir(), ".gemini", "tmp");
36739
36877
  const result = {
36740
36878
  filesScanned: 0,
36741
36879
  sessions: 0,
@@ -36752,31 +36890,31 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
36752
36890
  perSession: []
36753
36891
  };
36754
36892
  const dedup = emptyScanDedup();
36755
- if (!fs54.existsSync(tmpDir)) return result;
36893
+ if (!fs55.existsSync(tmpDir)) return result;
36756
36894
  let slugDirs;
36757
36895
  try {
36758
- slugDirs = fs54.readdirSync(tmpDir);
36896
+ slugDirs = fs55.readdirSync(tmpDir);
36759
36897
  } catch {
36760
36898
  return result;
36761
36899
  }
36762
36900
  const judge = new HistoryJudge();
36763
36901
  const cache7 = openScanCache(judge, opts);
36764
36902
  for (const slug2 of slugDirs) {
36765
- const slugPath = path56.join(tmpDir, slug2);
36903
+ const slugPath = path57.join(tmpDir, slug2);
36766
36904
  try {
36767
- if (!fs54.statSync(slugPath).isDirectory()) continue;
36905
+ if (!fs55.statSync(slugPath).isDirectory()) continue;
36768
36906
  } catch {
36769
36907
  continue;
36770
36908
  }
36771
36909
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
36772
36910
  try {
36773
36911
  projLabel = stripTerminalEscapes(
36774
- fs54.readFileSync(path56.join(slugPath, ".project_root"), "utf-8").trim()
36775
- ).replace(os46.homedir(), "~").slice(0, 40);
36912
+ fs55.readFileSync(path57.join(slugPath, ".project_root"), "utf-8").trim()
36913
+ ).replace(os47.homedir(), "~").slice(0, 40);
36776
36914
  } catch {
36777
36915
  }
36778
- const chatsDir = path56.join(slugPath, "chats");
36779
- if (!fs54.existsSync(chatsDir)) continue;
36916
+ const chatsDir = path57.join(slugPath, "chats");
36917
+ if (!fs55.existsSync(chatsDir)) continue;
36780
36918
  let chatFiles;
36781
36919
  try {
36782
36920
  chatFiles = geminiChatFiles(chatsDir);
@@ -36785,7 +36923,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
36785
36923
  }
36786
36924
  for (const chatFile of chatFiles) {
36787
36925
  const sessionId = chatFile.replace(/\.jsonl?$/, "");
36788
- const filePath = path56.join(chatsDir, chatFile);
36926
+ const filePath = path57.join(chatsDir, chatFile);
36789
36927
  const stamp = fileStamp(filePath);
36790
36928
  if (!fileMayHoldWindow(stamp, startDate)) continue;
36791
36929
  result.filesScanned++;
@@ -36799,7 +36937,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
36799
36937
  const fileDedup = emptyScanDedup();
36800
36938
  let raw;
36801
36939
  try {
36802
- raw = fs54.readFileSync(path56.join(chatsDir, chatFile), "utf-8");
36940
+ raw = fs55.readFileSync(path57.join(chatsDir, chatFile), "utf-8");
36803
36941
  } catch {
36804
36942
  continue;
36805
36943
  }
@@ -36936,13 +37074,13 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
36936
37074
  return result;
36937
37075
  }
36938
37076
  function antigravityBrainDirs() {
36939
- return ["antigravity-cli", "antigravity-ide"].map((surface) => path56.join(os46.homedir(), ".gemini", surface, "brain")).filter((p) => fs54.existsSync(p));
37077
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => path57.join(os47.homedir(), ".gemini", surface, "brain")).filter((p) => fs55.existsSync(p));
36940
37078
  }
36941
37079
  function antigravityTranscriptPath(convPath) {
36942
- const logsDir = path56.join(convPath, ".system_generated", "logs");
37080
+ const logsDir = path57.join(convPath, ".system_generated", "logs");
36943
37081
  for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
36944
- const p = path56.join(logsDir, name);
36945
- if (fs54.existsSync(p)) return p;
37082
+ const p = path57.join(logsDir, name);
37083
+ if (fs55.existsSync(p)) return p;
36946
37084
  }
36947
37085
  return null;
36948
37086
  }
@@ -36972,14 +37110,14 @@ async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {})
36972
37110
  for (const brainDir of brainDirs) {
36973
37111
  let convDirs;
36974
37112
  try {
36975
- convDirs = fs54.readdirSync(brainDir);
37113
+ convDirs = fs55.readdirSync(brainDir);
36976
37114
  } catch {
36977
37115
  continue;
36978
37116
  }
36979
37117
  for (const conv of convDirs) {
36980
- const convPath = path56.join(brainDir, conv);
37118
+ const convPath = path57.join(brainDir, conv);
36981
37119
  try {
36982
- if (!fs54.statSync(convPath).isDirectory()) continue;
37120
+ if (!fs55.statSync(convPath).isDirectory()) continue;
36983
37121
  } catch {
36984
37122
  continue;
36985
37123
  }
@@ -36998,7 +37136,7 @@ async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {})
36998
37136
  const fileDedup = emptyScanDedup();
36999
37137
  let raw;
37000
37138
  try {
37001
- raw = fs54.readFileSync(transcriptFile, "utf-8");
37139
+ raw = fs55.readFileSync(transcriptFile, "utf-8");
37002
37140
  } catch {
37003
37141
  continue;
37004
37142
  }
@@ -37073,7 +37211,7 @@ async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {})
37073
37211
  day.bashCalls++;
37074
37212
  const cwd = String(input.cwd ?? "");
37075
37213
  if (cwd && projLabel === conv.slice(0, 8)) {
37076
- projLabel = stripTerminalEscapes(cwd).replace(os46.homedir(), "~").slice(0, 40);
37214
+ projLabel = stripTerminalEscapes(cwd).replace(os47.homedir(), "~").slice(0, 40);
37077
37215
  }
37078
37216
  }
37079
37217
  const rawCmd = String(input.command ?? "").trimStart();
@@ -37099,7 +37237,7 @@ async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {})
37099
37237
  timestamp: timestamp2,
37100
37238
  sessionId,
37101
37239
  project: projLabel,
37102
- cwd: typeof input.cwd === "string" && path56.isAbsolute(input.cwd) ? input.cwd : void 0
37240
+ cwd: typeof input.cwd === "string" && path57.isAbsolute(input.cwd) ? input.cwd : void 0
37103
37241
  },
37104
37242
  fileResult,
37105
37243
  fileDedup
@@ -37122,7 +37260,7 @@ async function scanAntigravityHistory(startDate, onProgress, onLine, opts = {})
37122
37260
  }
37123
37261
  async function scanCopilotHistory(startDate, onProgress, onLine, opts = {}) {
37124
37262
  const canaryVals = safeCanaryScanValues();
37125
- const sessionDir = path56.join(os46.homedir(), ".copilot", "session-state");
37263
+ const sessionDir = path57.join(os47.homedir(), ".copilot", "session-state");
37126
37264
  const result = {
37127
37265
  filesScanned: 0,
37128
37266
  sessions: 0,
@@ -37140,18 +37278,18 @@ async function scanCopilotHistory(startDate, onProgress, onLine, opts = {}) {
37140
37278
  perSession: []
37141
37279
  };
37142
37280
  const dedup = emptyScanDedup();
37143
- if (!fs54.existsSync(sessionDir)) return result;
37281
+ if (!fs55.existsSync(sessionDir)) return result;
37144
37282
  let sessionIds;
37145
37283
  try {
37146
- sessionIds = fs54.readdirSync(sessionDir);
37284
+ sessionIds = fs55.readdirSync(sessionDir);
37147
37285
  } catch {
37148
37286
  return result;
37149
37287
  }
37150
37288
  const judge = new HistoryJudge();
37151
37289
  const cache7 = openScanCache(judge, opts);
37152
37290
  for (const sessionId of sessionIds) {
37153
- const eventsPath = path56.join(sessionDir, sessionId, "events.jsonl");
37154
- if (!fs54.existsSync(eventsPath)) continue;
37291
+ const eventsPath = path57.join(sessionDir, sessionId, "events.jsonl");
37292
+ if (!fs55.existsSync(eventsPath)) continue;
37155
37293
  const stamp = fileStamp(eventsPath);
37156
37294
  if (!fileMayHoldWindow(stamp, startDate)) continue;
37157
37295
  result.filesScanned++;
@@ -37165,7 +37303,7 @@ async function scanCopilotHistory(startDate, onProgress, onLine, opts = {}) {
37165
37303
  const fileDedup = emptyScanDedup();
37166
37304
  let raw;
37167
37305
  try {
37168
- raw = fs54.readFileSync(eventsPath, "utf-8");
37306
+ raw = fs55.readFileSync(eventsPath, "utf-8");
37169
37307
  } catch {
37170
37308
  continue;
37171
37309
  }
@@ -37190,8 +37328,8 @@ async function scanCopilotHistory(startDate, onProgress, onLine, opts = {}) {
37190
37328
  if (ev.type === "session.start") {
37191
37329
  const cwd = ev.data?.context?.cwd;
37192
37330
  if (typeof cwd === "string" && cwd) {
37193
- projLabel = stripTerminalEscapes(cwd).replace(os46.homedir(), "~").slice(0, 40);
37194
- if (path56.isAbsolute(cwd)) sessionCwd = cwd;
37331
+ projLabel = stripTerminalEscapes(cwd).replace(os47.homedir(), "~").slice(0, 40);
37332
+ if (path57.isAbsolute(cwd)) sessionCwd = cwd;
37195
37333
  }
37196
37334
  continue;
37197
37335
  }
@@ -37324,7 +37462,7 @@ async function scanCodexHistory(startDate, onProgress, onLine, opts = {}) {
37324
37462
  const fileDedup = emptyScanDedup();
37325
37463
  let lines;
37326
37464
  try {
37327
- lines = fs54.readFileSync(filePath, "utf-8").split("\n");
37465
+ lines = fs55.readFileSync(filePath, "utf-8").split("\n");
37328
37466
  } catch {
37329
37467
  continue;
37330
37468
  }
@@ -37347,8 +37485,8 @@ async function scanCodexHistory(startDate, onProgress, onLine, opts = {}) {
37347
37485
  sessionId = String(payload["id"] ?? filePath);
37348
37486
  startTime = String(payload["timestamp"] ?? "");
37349
37487
  const cwd = String(payload["cwd"] ?? "");
37350
- projLabel = stripTerminalEscapes(cwd.replace(os46.homedir(), "~")).slice(0, 40);
37351
- if (path56.isAbsolute(cwd)) sessionCwd = cwd;
37488
+ projLabel = stripTerminalEscapes(cwd.replace(os47.homedir(), "~")).slice(0, 40);
37489
+ if (path57.isAbsolute(cwd)) sessionCwd = cwd;
37352
37490
  continue;
37353
37491
  }
37354
37492
  if (entry.type === "event_msg" && payload["type"] === "user_message") {
@@ -37465,17 +37603,17 @@ async function scanCodexHistory(startDate, onProgress, onLine, opts = {}) {
37465
37603
  return result;
37466
37604
  }
37467
37605
  function scanShellConfig() {
37468
- const home = os46.homedir();
37606
+ const home = os47.homedir();
37469
37607
  const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
37470
- (f) => path56.join(home, f)
37608
+ (f) => path57.join(home, f)
37471
37609
  );
37472
37610
  const findings = [];
37473
37611
  const seen = /* @__PURE__ */ new Set();
37474
37612
  for (const filePath of configFiles) {
37475
- if (!fs54.existsSync(filePath)) continue;
37613
+ if (!fs55.existsSync(filePath)) continue;
37476
37614
  let lines;
37477
37615
  try {
37478
- lines = fs54.readFileSync(filePath, "utf-8").split("\n");
37616
+ lines = fs55.readFileSync(filePath, "utf-8").split("\n");
37479
37617
  } catch {
37480
37618
  continue;
37481
37619
  }
@@ -37805,25 +37943,25 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
37805
37943
  const topLines = [];
37806
37944
  if (scan.canaryFindings.length > 0) {
37807
37945
  const latest = scan.canaryFindings[0];
37808
- const rel = relativeDate(latest.timestamp, now);
37946
+ const rel = relativeAgo(latest.timestamp, now);
37809
37947
  const n = scan.canaryFindings.length;
37810
37948
  topLines.push(
37811
37949
  mkLine(
37812
37950
  ["\u{1FAA4} ", chalk6.red],
37813
37951
  [`${n} decoy credential${n !== 1 ? "s" : ""} tripped `, chalk6.bold],
37814
- [`(latest: ${rel} ago, ${latest.kind} at ${displayHome(latest.path)})`, chalk6.dim]
37952
+ [`(latest: ${rel}, ${latest.kind} at ${displayHome(latest.path)})`, chalk6.dim]
37815
37953
  )
37816
37954
  );
37817
37955
  }
37818
37956
  if (scan.dlpFindings.length > 0) {
37819
37957
  const latest = scan.dlpFindings[0];
37820
- const rel = relativeDate(latest.timestamp, now);
37958
+ const rel = relativeAgo(latest.timestamp, now);
37821
37959
  const noun = `credential leak${scan.dlpFindings.length !== 1 ? "s" : ""}`;
37822
37960
  topLines.push(
37823
37961
  mkLine(
37824
37962
  ["\u{1F6A8} ", chalk6.red],
37825
37963
  [`${scan.dlpFindings.length} ${noun} in tool input `, chalk6.bold],
37826
- [`(latest: ${rel} ago, ${latest.patternName})`, chalk6.dim]
37964
+ [`(latest: ${rel}, ${latest.patternName})`, chalk6.dim]
37827
37965
  )
37828
37966
  );
37829
37967
  }
@@ -38392,7 +38530,7 @@ function registerScanCommand(program2) {
38392
38530
  if (!drillDown) {
38393
38531
  const useInk2 = !options.classic;
38394
38532
  if (useInk2) {
38395
- const scanInkPath = path56.join(__dirname, "scan-ink.mjs");
38533
+ const scanInkPath = path57.join(__dirname, "scan-ink.mjs");
38396
38534
  const dynamicImport = new Function("id", "return import(id)");
38397
38535
  const mod = await dynamicImport(`file://${scanInkPath}`);
38398
38536
  const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
@@ -38749,12 +38887,12 @@ var init_scan = __esm({
38749
38887
  });
38750
38888
 
38751
38889
  // src/daemon/build-id.ts
38752
- import fs55 from "fs";
38753
- import path57 from "path";
38890
+ import fs56 from "fs";
38891
+ import path58 from "path";
38754
38892
  function readOwnVersion() {
38755
38893
  for (const rel of ["../package.json", "../../package.json"]) {
38756
38894
  try {
38757
- const raw = fs55.readFileSync(path57.join(__dirname, rel), "utf-8");
38895
+ const raw = fs56.readFileSync(path58.join(__dirname, rel), "utf-8");
38758
38896
  const v = JSON.parse(raw).version;
38759
38897
  if (typeof v === "string" && v.length > 0) return v;
38760
38898
  } catch {
@@ -38765,7 +38903,7 @@ function readOwnVersion() {
38765
38903
  function computeBuildId(entry = process.argv[1] ?? "") {
38766
38904
  let mtimeMs = 0;
38767
38905
  try {
38768
- if (entry) mtimeMs = fs55.statSync(entry).mtimeMs;
38906
+ if (entry) mtimeMs = fs56.statSync(entry).mtimeMs;
38769
38907
  } catch {
38770
38908
  }
38771
38909
  return { version: readOwnVersion(), mtimeMs };
@@ -38906,8 +39044,8 @@ var init_suggestion_tracker = __esm({
38906
39044
  });
38907
39045
 
38908
39046
  // src/daemon/taint-store.ts
38909
- import fs56 from "fs";
38910
- import path58 from "path";
39047
+ import fs57 from "fs";
39048
+ import path59 from "path";
38911
39049
  var DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS2, SessionTaintStore;
38912
39050
  var init_taint_store = __esm({
38913
39051
  "src/daemon/taint-store.ts"() {
@@ -38977,9 +39115,9 @@ var init_taint_store = __esm({
38977
39115
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
38978
39116
  _resolve(filePath) {
38979
39117
  try {
38980
- return fs56.realpathSync.native(path58.resolve(filePath));
39118
+ return fs57.realpathSync.native(path59.resolve(filePath));
38981
39119
  } catch {
38982
- return path58.resolve(filePath);
39120
+ return path59.resolve(filePath);
38983
39121
  }
38984
39122
  }
38985
39123
  };
@@ -39035,7 +39173,7 @@ var init_taint_store = __esm({
39035
39173
  });
39036
39174
 
39037
39175
  // src/daemon/written-store.ts
39038
- import path59 from "path";
39176
+ import path60 from "path";
39039
39177
  var SESSION_WRITE_TTL_MS, MAX_WRITTEN, MAX_PATH, SessionWriteStore;
39040
39178
  var init_written_store = __esm({
39041
39179
  "src/daemon/written-store.ts"() {
@@ -39047,7 +39185,7 @@ var init_written_store = __esm({
39047
39185
  records = /* @__PURE__ */ new Map();
39048
39186
  key(p) {
39049
39187
  if (typeof p !== "string" || !p || p.length > MAX_PATH) return null;
39050
- return path59.resolve(p);
39188
+ return path60.resolve(p);
39051
39189
  }
39052
39190
  /** Record that these absolute paths were (or are about to be) written. */
39053
39191
  record(paths, ttlMs = SESSION_WRITE_TTL_MS) {
@@ -39378,9 +39516,9 @@ var init_cards = __esm({
39378
39516
 
39379
39517
  // src/daemon/state.ts
39380
39518
  import net2 from "net";
39381
- import fs57 from "fs";
39382
- import path60 from "path";
39383
- import os47 from "os";
39519
+ import fs58 from "fs";
39520
+ import path61 from "path";
39521
+ import os48 from "os";
39384
39522
  import { randomUUID as randomUUID6 } from "crypto";
39385
39523
  function onCardDecided(card) {
39386
39524
  const r = card.record;
@@ -39406,8 +39544,8 @@ function onCardDecided(card) {
39406
39544
  }
39407
39545
  function loadInsightCounts() {
39408
39546
  try {
39409
- if (!fs57.existsSync(INSIGHT_COUNTS_FILE)) return;
39410
- const data = JSON.parse(fs57.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
39547
+ if (!fs58.existsSync(INSIGHT_COUNTS_FILE)) return;
39548
+ const data = JSON.parse(fs58.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
39411
39549
  for (const [tool, count] of Object.entries(data)) {
39412
39550
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
39413
39551
  }
@@ -39475,16 +39613,16 @@ function appendAuditLog(data) {
39475
39613
  ...data.channel && { channel: data.channel },
39476
39614
  ...data.cardId && { cardId: data.cardId }
39477
39615
  };
39478
- const dir2 = path60.dirname(AUDIT_LOG_FILE);
39479
- if (!fs57.existsSync(dir2)) fs57.mkdirSync(dir2, { recursive: true });
39480
- fs57.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
39616
+ const dir2 = path61.dirname(AUDIT_LOG_FILE);
39617
+ if (!fs58.existsSync(dir2)) fs58.mkdirSync(dir2, { recursive: true });
39618
+ fs58.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
39481
39619
  } catch {
39482
39620
  }
39483
39621
  }
39484
39622
  function getAuditHistory(limit = 20) {
39485
39623
  try {
39486
- if (!fs57.existsSync(AUDIT_LOG_FILE)) return [];
39487
- const lines = fs57.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
39624
+ if (!fs58.existsSync(AUDIT_LOG_FILE)) return [];
39625
+ const lines = fs58.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
39488
39626
  if (lines.length === 1 && lines[0] === "") return [];
39489
39627
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
39490
39628
  } catch {
@@ -39493,7 +39631,7 @@ function getAuditHistory(limit = 20) {
39493
39631
  }
39494
39632
  function getOrgName() {
39495
39633
  try {
39496
- if (fs57.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
39634
+ if (fs58.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
39497
39635
  } catch {
39498
39636
  }
39499
39637
  return null;
@@ -39505,8 +39643,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
39505
39643
  try {
39506
39644
  let trust = { entries: [] };
39507
39645
  try {
39508
- if (fs57.existsSync(TRUST_FILE2))
39509
- trust = JSON.parse(fs57.readFileSync(TRUST_FILE2, "utf-8"));
39646
+ if (fs58.existsSync(TRUST_FILE2))
39647
+ trust = JSON.parse(fs58.readFileSync(TRUST_FILE2, "utf-8"));
39510
39648
  } catch {
39511
39649
  }
39512
39650
  trust.entries = trust.entries.filter(
@@ -39523,8 +39661,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
39523
39661
  }
39524
39662
  function readPersistentDecisions() {
39525
39663
  try {
39526
- if (fs57.existsSync(DECISIONS_FILE)) {
39527
- return JSON.parse(fs57.readFileSync(DECISIONS_FILE, "utf-8"));
39664
+ if (fs58.existsSync(DECISIONS_FILE)) {
39665
+ return JSON.parse(fs58.readFileSync(DECISIONS_FILE, "utf-8"));
39528
39666
  }
39529
39667
  } catch {
39530
39668
  }
@@ -39552,7 +39690,7 @@ function estimateToolCost(tool, args) {
39552
39690
  const filePath = a.file_path ?? a.path;
39553
39691
  if (filePath) {
39554
39692
  try {
39555
- const bytes = fs57.statSync(filePath).size;
39693
+ const bytes = fs58.statSync(filePath).size;
39556
39694
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
39557
39695
  } catch {
39558
39696
  }
@@ -39620,7 +39758,7 @@ function abandonPending() {
39620
39758
  cards.abandonLocalOnly();
39621
39759
  if (autoStarted && cards.open().length === 0) {
39622
39760
  try {
39623
- fs57.unlinkSync(DAEMON_PID_FILE);
39761
+ fs58.unlinkSync(DAEMON_PID_FILE);
39624
39762
  } catch {
39625
39763
  }
39626
39764
  setTimeout(() => {
@@ -39631,8 +39769,8 @@ function abandonPending() {
39631
39769
  }
39632
39770
  function logActivitySocket(msg) {
39633
39771
  try {
39634
- fs57.appendFileSync(
39635
- path60.join(homeDir, ".node9", "hook-debug.log"),
39772
+ fs58.appendFileSync(
39773
+ path61.join(homeDir, ".node9", "hook-debug.log"),
39636
39774
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
39637
39775
  `
39638
39776
  );
@@ -39654,13 +39792,13 @@ function shouldRebind(now = Date.now()) {
39654
39792
  function startActivitySocket() {
39655
39793
  bindActivitySocket();
39656
39794
  activityHealthInterval = setInterval(() => {
39657
- if (!fs57.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
39795
+ if (!fs58.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
39658
39796
  }, ACTIVITY_HEALTH_PROBE_MS);
39659
39797
  activityHealthInterval.unref();
39660
39798
  process.on("exit", () => {
39661
39799
  if (activityHealthInterval) clearInterval(activityHealthInterval);
39662
39800
  try {
39663
- fs57.unlinkSync(ACTIVITY_SOCKET_PATH2);
39801
+ fs58.unlinkSync(ACTIVITY_SOCKET_PATH2);
39664
39802
  } catch {
39665
39803
  }
39666
39804
  });
@@ -39688,7 +39826,7 @@ function attemptRebind(reason) {
39688
39826
  }
39689
39827
  function bindActivitySocket() {
39690
39828
  try {
39691
- fs57.unlinkSync(ACTIVITY_SOCKET_PATH2);
39829
+ fs58.unlinkSync(ACTIVITY_SOCKET_PATH2);
39692
39830
  } catch {
39693
39831
  }
39694
39832
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -39797,14 +39935,14 @@ var init_state3 = __esm({
39797
39935
  init_session_history();
39798
39936
  init_atomic_write();
39799
39937
  init_cards();
39800
- homeDir = os47.homedir();
39801
- DAEMON_PID_FILE = path60.join(homeDir, ".node9", "daemon.pid");
39802
- DECISIONS_FILE = path60.join(homeDir, ".node9", "decisions.json");
39803
- AUDIT_LOG_FILE = path60.join(homeDir, ".node9", "audit.log");
39804
- TRUST_FILE2 = path60.join(homeDir, ".node9", "trust.json");
39805
- GLOBAL_CONFIG_FILE = path60.join(homeDir, ".node9", "config.json");
39806
- CREDENTIALS_FILE = path60.join(homeDir, ".node9", "credentials.json");
39807
- INSIGHT_COUNTS_FILE = path60.join(homeDir, ".node9", "insight-counts.json");
39938
+ homeDir = os48.homedir();
39939
+ DAEMON_PID_FILE = path61.join(homeDir, ".node9", "daemon.pid");
39940
+ DECISIONS_FILE = path61.join(homeDir, ".node9", "decisions.json");
39941
+ AUDIT_LOG_FILE = path61.join(homeDir, ".node9", "audit.log");
39942
+ TRUST_FILE2 = path61.join(homeDir, ".node9", "trust.json");
39943
+ GLOBAL_CONFIG_FILE = path61.join(homeDir, ".node9", "config.json");
39944
+ CREDENTIALS_FILE = path61.join(homeDir, ".node9", "credentials.json");
39945
+ INSIGHT_COUNTS_FILE = path61.join(homeDir, ".node9", "insight-counts.json");
39808
39946
  sseClients = /* @__PURE__ */ new Set();
39809
39947
  cards = new CardStore({
39810
39948
  onDecided: onCardDecided,
@@ -39827,7 +39965,7 @@ var init_state3 = __esm({
39827
39965
  "2h": 2 * 60 * 6e4
39828
39966
  };
39829
39967
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
39830
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path60.join(os47.tmpdir(), "node9-activity.sock");
39968
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path61.join(os48.tmpdir(), "node9-activity.sock");
39831
39969
  ACTIVITY_RING_SIZE = 100;
39832
39970
  activityRing = [];
39833
39971
  LARGE_RESPONSE_RING_SIZE = 20;
@@ -39866,13 +40004,13 @@ var init_state3 = __esm({
39866
40004
  });
39867
40005
 
39868
40006
  // src/posture/secrets.ts
39869
- import fs58 from "fs";
39870
- import path61 from "path";
39871
- import os48 from "os";
40007
+ import fs59 from "fs";
40008
+ import path62 from "path";
40009
+ import os49 from "os";
39872
40010
  function displayPath(p, home) {
39873
40011
  if (p === home) return "~";
39874
- const prefix = home.endsWith(path61.sep) ? home : home + path61.sep;
39875
- if (p.startsWith(prefix)) return "~" + path61.sep + p.slice(prefix.length);
40012
+ const prefix = home.endsWith(path62.sep) ? home : home + path62.sep;
40013
+ if (p.startsWith(prefix)) return "~" + path62.sep + p.slice(prefix.length);
39876
40014
  return p;
39877
40015
  }
39878
40016
  function safeRead(file2) {
@@ -39883,8 +40021,8 @@ function safeRead(file2) {
39883
40021
  function candidateFiles(home, cwd) {
39884
40022
  const files = /* @__PURE__ */ new Set();
39885
40023
  try {
39886
- for (const name of fs58.readdirSync(cwd)) {
39887
- if (name === ".env" || name.startsWith(".env.")) files.add(path61.join(cwd, name));
40024
+ for (const name of fs59.readdirSync(cwd)) {
40025
+ if (name === ".env" || name.startsWith(".env.")) files.add(path62.join(cwd, name));
39888
40026
  }
39889
40027
  } catch {
39890
40028
  }
@@ -39892,7 +40030,7 @@ function candidateFiles(home, cwd) {
39892
40030
  if (spec.hookFile) files.add(spec.hookFile(home));
39893
40031
  if (spec.mcpFile) files.add(spec.mcpFile(home));
39894
40032
  }
39895
- files.add(path61.join(home, ".env"));
40033
+ files.add(path62.join(home, ".env"));
39896
40034
  return [...files];
39897
40035
  }
39898
40036
  function credentialMaterial(home) {
@@ -39906,7 +40044,7 @@ function plantedDecoyPaths() {
39906
40044
  }
39907
40045
  }
39908
40046
  function checkSecrets(ctx) {
39909
- const home = ctx.home || os48.homedir();
40047
+ const home = ctx.home || os49.homedir();
39910
40048
  const findings = [];
39911
40049
  const planted = plantedDecoyPaths();
39912
40050
  const plaintext = [];
@@ -39942,7 +40080,7 @@ function checkSecrets(ctx) {
39942
40080
  for (const file2 of credentialMaterial(home)) {
39943
40081
  if (planted.has(file2)) continue;
39944
40082
  try {
39945
- if (fs58.statSync(file2).isFile()) {
40083
+ if (fs59.statSync(file2).isFile()) {
39946
40084
  creds.push(displayPath(file2, home));
39947
40085
  credPaths.push(file2);
39948
40086
  }
@@ -40112,10 +40250,10 @@ var init_templates = __esm({
40112
40250
  });
40113
40251
 
40114
40252
  // src/posture/egress.ts
40115
- import fs59 from "fs";
40253
+ import fs60 from "fs";
40116
40254
  function sandboxEgressWallActive() {
40117
40255
  try {
40118
- return fs59.existsSync(ALLOWED_DOMAINS_PATH);
40256
+ return fs60.existsSync(ALLOWED_DOMAINS_PATH);
40119
40257
  } catch {
40120
40258
  return false;
40121
40259
  }
@@ -40127,7 +40265,7 @@ function evaluateEgressConfig(egress) {
40127
40265
  severity: "high",
40128
40266
  title: "Egress is locked, but node9 is not enforcing it",
40129
40267
  what: "Egress is set to block, but node9 is not applying the policy.",
40130
- why: "node9 isn't wired in (or is in observe mode), so the lock has no effect.",
40268
+ why: "node9 isn't wired in (or is in audit mode), so the lock has no effect.",
40131
40269
  who: "The lock protects nothing until node9 is enforcing in-path.",
40132
40270
  owner: "node9",
40133
40271
  detail: [],
@@ -40144,7 +40282,7 @@ function evaluateEgressConfig(egress) {
40144
40282
  severity: "medium",
40145
40283
  title: "Egress is in review, but node9 is not enforcing it",
40146
40284
  what: "Egress is set to review (approval-gate), but node9 is not applying the policy.",
40147
- why: "node9 isn't wired in (or is in observe mode), so the gate has no effect.",
40285
+ why: "node9 isn't wired in (or is in audit mode), so the gate has no effect.",
40148
40286
  who: "Nothing gates outbound until node9 is enforcing in-path.",
40149
40287
  owner: "node9",
40150
40288
  detail: [],
@@ -40285,14 +40423,14 @@ var init_gate = __esm({
40285
40423
  });
40286
40424
 
40287
40425
  // src/posture/supply-chain.ts
40288
- import os49 from "os";
40289
- import path62 from "path";
40426
+ import os50 from "os";
40427
+ import path63 from "path";
40290
40428
  import { parse as parseToml6 } from "smol-toml";
40291
40429
  function isNode9Managed(command, args = []) {
40292
40430
  if (!command) return false;
40293
- if (path62.basename(command).toLowerCase() === "node9") return true;
40294
- if (PACKAGE_RUNNERS.has(path62.basename(command).toLowerCase())) {
40295
- return args.some((a) => a === "node9" || path62.basename(a).toLowerCase() === "node9");
40431
+ if (path63.basename(command).toLowerCase() === "node9") return true;
40432
+ if (PACKAGE_RUNNERS.has(path63.basename(command).toLowerCase())) {
40433
+ return args.some((a) => a === "node9" || path63.basename(a).toLowerCase() === "node9");
40296
40434
  }
40297
40435
  return false;
40298
40436
  }
@@ -40314,7 +40452,7 @@ function readServers(file2, format, agent) {
40314
40452
  }
40315
40453
  }
40316
40454
  function checkSupplyChain(ctx) {
40317
- const home = ctx.home || os49.homedir();
40455
+ const home = ctx.home || os50.homedir();
40318
40456
  const servers = [];
40319
40457
  for (const spec of AGENT_SPECS) {
40320
40458
  if (!spec.mcpFile) continue;
@@ -40413,11 +40551,11 @@ var init_privilege = __esm({
40413
40551
  });
40414
40552
 
40415
40553
  // src/posture/containment.ts
40416
- import fs60 from "fs";
40554
+ import fs61 from "fs";
40417
40555
  function inContainer() {
40418
- if (fs60.existsSync("/.dockerenv") || fs60.existsSync("/run/.containerenv")) return true;
40556
+ if (fs61.existsSync("/.dockerenv") || fs61.existsSync("/run/.containerenv")) return true;
40419
40557
  try {
40420
- const cgroup = fs60.readFileSync("/proc/1/cgroup", "utf8");
40558
+ const cgroup = fs61.readFileSync("/proc/1/cgroup", "utf8");
40421
40559
  if (/docker|kubepods|containerd|lxc|libpod/.test(cgroup)) return true;
40422
40560
  } catch {
40423
40561
  }
@@ -40463,7 +40601,7 @@ var init_containment = __esm({
40463
40601
  });
40464
40602
 
40465
40603
  // src/posture/inbound.ts
40466
- import fs61 from "fs";
40604
+ import fs62 from "fs";
40467
40605
  function buildNetworkFix(labels) {
40468
40606
  const shielded = [
40469
40607
  ...new Map(
@@ -40521,7 +40659,7 @@ function collectListeners() {
40521
40659
  const byPort = /* @__PURE__ */ new Map();
40522
40660
  for (const file2 of ["/proc/net/tcp", "/proc/net/tcp6"]) {
40523
40661
  try {
40524
- for (const l of parseListeners(fs61.readFileSync(file2, "utf8"))) {
40662
+ for (const l of parseListeners(fs62.readFileSync(file2, "utf8"))) {
40525
40663
  if (!byPort.has(l.port)) byPort.set(l.port, l);
40526
40664
  }
40527
40665
  } catch {
@@ -40533,11 +40671,11 @@ function readProc(pid) {
40533
40671
  let comm = "unknown";
40534
40672
  let cmdline = "";
40535
40673
  try {
40536
- comm = fs61.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
40674
+ comm = fs62.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
40537
40675
  } catch {
40538
40676
  }
40539
40677
  try {
40540
- cmdline = fs61.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
40678
+ cmdline = fs62.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
40541
40679
  } catch {
40542
40680
  }
40543
40681
  return { comm, cmdline };
@@ -40547,21 +40685,21 @@ function resolveProcesses(inodes) {
40547
40685
  if (inodes.size === 0) return map;
40548
40686
  let pids;
40549
40687
  try {
40550
- pids = fs61.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
40688
+ pids = fs62.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
40551
40689
  } catch {
40552
40690
  return map;
40553
40691
  }
40554
40692
  for (const pid of pids) {
40555
40693
  let fds;
40556
40694
  try {
40557
- fds = fs61.readdirSync(`/proc/${pid}/fd`);
40695
+ fds = fs62.readdirSync(`/proc/${pid}/fd`);
40558
40696
  } catch {
40559
40697
  continue;
40560
40698
  }
40561
40699
  for (const fd of fds) {
40562
40700
  let link;
40563
40701
  try {
40564
- link = fs61.readlinkSync(`/proc/${pid}/fd/${fd}`);
40702
+ link = fs62.readlinkSync(`/proc/${pid}/fd/${fd}`);
40565
40703
  } catch {
40566
40704
  continue;
40567
40705
  }
@@ -40666,9 +40804,9 @@ var init_inbound = __esm({
40666
40804
  });
40667
40805
 
40668
40806
  // src/posture/coverage.ts
40669
- import os50 from "os";
40807
+ import os51 from "os";
40670
40808
  function checkCoverage(ctx) {
40671
- const home = ctx.home || os50.homedir();
40809
+ const home = ctx.home || os51.homedir();
40672
40810
  const findings = [];
40673
40811
  const protectedAgents = getAgentWiring(home).filter((r) => r.isProtected);
40674
40812
  if (protectedAgents.length === 0) {
@@ -40686,13 +40824,14 @@ function checkCoverage(ctx) {
40686
40824
  return findings;
40687
40825
  }
40688
40826
  const mode = getConfig(ctx.cwd).settings.mode;
40689
- if (mode === "observe" || mode === "audit") {
40827
+ if (isRecordOnly(mode)) {
40828
+ const level = levelOf(mode);
40690
40829
  findings.push({
40691
40830
  category: "Coverage",
40692
40831
  severity: "high",
40693
- title: `node9 is in ${mode} mode \u2014 watching, not blocking`,
40832
+ title: `node9 is in ${level} mode \u2014 watching, not blocking`,
40694
40833
  what: "node9 is watching but not actually blocking anything.",
40695
- why: `It's in ${mode} mode, which logs risky actions but lets them through.`,
40834
+ why: `It's in ${level} mode, which logs risky actions but lets them through.`,
40696
40835
  who: "The guardrails above are observed, not enforced.",
40697
40836
  detail: [],
40698
40837
  owner: "node9",
@@ -40705,12 +40844,13 @@ var init_coverage = __esm({
40705
40844
  "src/posture/coverage.ts"() {
40706
40845
  "use strict";
40707
40846
  init_config();
40847
+ init_dist();
40708
40848
  init_agent_wiring();
40709
40849
  }
40710
40850
  });
40711
40851
 
40712
40852
  // src/posture/governance.ts
40713
- import path63 from "path";
40853
+ import path64 from "path";
40714
40854
  function checkData(ctx) {
40715
40855
  const dlp = getConfig(ctx.cwd).policy.dlp;
40716
40856
  if (!dlp?.enabled) {
@@ -40854,7 +40994,7 @@ var init_governance = __esm({
40854
40994
  "src/posture/governance.ts"() {
40855
40995
  "use strict";
40856
40996
  init_config();
40857
- jailProbePath = (home) => path63.join(home, ".aws", "credentials");
40997
+ jailProbePath = (home) => path64.join(home, ".aws", "credentials");
40858
40998
  }
40859
40999
  });
40860
41000
 
@@ -41008,7 +41148,7 @@ async function annotateCoverage(findings, ctx) {
41008
41148
  const worst = verdicts.some((v) => v === "review") ? "review" : "block";
41009
41149
  f.coverage = coverageFromVerdict(worst, env, "node9 DLP");
41010
41150
  if (isRecordOnly(mode) && f.fix?.includes("shield enable project-jail")) {
41011
- f.fix = `node9 already recognises reads of these files, but it is in ${mode ?? "audit"} mode, which logs them and lets them through. Fix it now: set mode to \`standard\` (or \`strict\`) to stop them.`;
41151
+ f.fix = `node9 already recognises reads of these files, but it is in ${levelOf(mode)} mode, which logs them and lets them through. Fix it now: set mode to \`standard\` (or \`strict\`) to stop them.`;
41012
41152
  }
41013
41153
  }
41014
41154
  continue;
@@ -41033,7 +41173,7 @@ var init_enforcement = __esm({
41033
41173
  });
41034
41174
 
41035
41175
  // src/posture/index.ts
41036
- import os51 from "os";
41176
+ import os52 from "os";
41037
41177
  function dropEnforcementRedundant(findings) {
41038
41178
  const coveragePresent = findings.some((f) => f.category === "Coverage");
41039
41179
  if (!coveragePresent) return findings;
@@ -41059,7 +41199,7 @@ async function runChecks(checks, ctx) {
41059
41199
  }
41060
41200
  async function runPosture(opts = {}) {
41061
41201
  const ctx = {
41062
- home: opts.home ?? os51.homedir(),
41202
+ home: opts.home ?? os52.homedir(),
41063
41203
  cwd: opts.cwd ?? process.cwd(),
41064
41204
  agent: opts.agent
41065
41205
  };
@@ -41436,20 +41576,20 @@ var init_ship2 = __esm({
41436
41576
 
41437
41577
  // src/machine-id.ts
41438
41578
  import * as crypto7 from "crypto";
41439
- import * as fs62 from "fs";
41440
- import * as os52 from "os";
41441
- import * as path64 from "path";
41442
- function getMachineId(homeDir2 = os52.homedir()) {
41443
- const file2 = path64.join(homeDir2, ".node9", "machine-id");
41579
+ import * as fs63 from "fs";
41580
+ import * as os53 from "os";
41581
+ import * as path65 from "path";
41582
+ function getMachineId(homeDir2 = os53.homedir()) {
41583
+ const file2 = path65.join(homeDir2, ".node9", "machine-id");
41444
41584
  try {
41445
- const existing = fs62.readFileSync(file2, "utf-8").trim();
41585
+ const existing = fs63.readFileSync(file2, "utf-8").trim();
41446
41586
  if (UUID_RE.test(existing)) return existing;
41447
41587
  } catch {
41448
41588
  }
41449
41589
  const id = crypto7.randomUUID();
41450
41590
  try {
41451
- fs62.mkdirSync(path64.dirname(file2), { recursive: true });
41452
- fs62.writeFileSync(file2, id + "\n", { mode: 384 });
41591
+ fs63.mkdirSync(path65.dirname(file2), { recursive: true });
41592
+ fs63.writeFileSync(file2, id + "\n", { mode: 384 });
41453
41593
  } catch {
41454
41594
  }
41455
41595
  return id;
@@ -41489,10 +41629,10 @@ var init_sync_trigger = __esm({
41489
41629
  });
41490
41630
 
41491
41631
  // src/daemon/sync.ts
41492
- import fs63 from "fs";
41632
+ import fs64 from "fs";
41493
41633
  import https4 from "https";
41494
- import os53 from "os";
41495
- import path65 from "path";
41634
+ import os54 from "os";
41635
+ import path66 from "path";
41496
41636
  function emptySignals3() {
41497
41637
  return {
41498
41638
  dlpFindings: 0,
@@ -41540,7 +41680,7 @@ function readCredentials() {
41540
41680
  }
41541
41681
  function readCachedEtag() {
41542
41682
  try {
41543
- const raw = JSON.parse(fs63.readFileSync(rulesCacheFile(), "utf-8"));
41683
+ const raw = JSON.parse(fs64.readFileSync(rulesCacheFile(), "utf-8"));
41544
41684
  return typeof raw.etag === "string" ? raw.etag : void 0;
41545
41685
  } catch {
41546
41686
  return void 0;
@@ -41548,7 +41688,7 @@ function readCachedEtag() {
41548
41688
  }
41549
41689
  function readCachedSyncIntervalHours() {
41550
41690
  try {
41551
- const raw = JSON.parse(fs63.readFileSync(rulesCacheFile(), "utf-8"));
41691
+ const raw = JSON.parse(fs64.readFileSync(rulesCacheFile(), "utf-8"));
41552
41692
  return typeof raw.syncIntervalHours === "number" ? raw.syncIntervalHours : void 0;
41553
41693
  } catch {
41554
41694
  return void 0;
@@ -41565,7 +41705,7 @@ function effectiveSyncIntervalMs() {
41565
41705
  }
41566
41706
  function readSyncHealth() {
41567
41707
  try {
41568
- const raw = JSON.parse(fs63.readFileSync(syncHealthFile(), "utf-8"));
41708
+ const raw = JSON.parse(fs64.readFileSync(syncHealthFile(), "utf-8"));
41569
41709
  return {
41570
41710
  lastCheckedAt: typeof raw.lastCheckedAt === "string" ? raw.lastCheckedAt : void 0,
41571
41711
  lastChangedAt: typeof raw.lastChangedAt === "string" ? raw.lastChangedAt : void 0,
@@ -41580,17 +41720,17 @@ function readSyncHealth() {
41580
41720
  function writeSyncHealth(h) {
41581
41721
  try {
41582
41722
  const file2 = syncHealthFile();
41583
- const dir2 = path65.dirname(file2);
41584
- if (!fs63.existsSync(dir2)) fs63.mkdirSync(dir2, { recursive: true });
41723
+ const dir2 = path66.dirname(file2);
41724
+ if (!fs64.existsSync(dir2)) fs64.mkdirSync(dir2, { recursive: true });
41585
41725
  const tmp = `${file2}.${process.pid}.tmp`;
41586
- fs63.writeFileSync(tmp, JSON.stringify(h, null, 2) + "\n", "utf-8");
41587
- fs63.renameSync(tmp, file2);
41726
+ fs64.writeFileSync(tmp, JSON.stringify(h, null, 2) + "\n", "utf-8");
41727
+ fs64.renameSync(tmp, file2);
41588
41728
  } catch {
41589
41729
  }
41590
41730
  }
41591
41731
  function readCacheFetchedAt() {
41592
41732
  try {
41593
- const raw = JSON.parse(fs63.readFileSync(rulesCacheFile(), "utf-8"));
41733
+ const raw = JSON.parse(fs64.readFileSync(rulesCacheFile(), "utf-8"));
41594
41734
  return typeof raw.fetchedAt === "string" ? raw.fetchedAt : void 0;
41595
41735
  } catch {
41596
41736
  return void 0;
@@ -41627,13 +41767,13 @@ function safeNode9Version() {
41627
41767
  let dir2 = __dirname;
41628
41768
  for (let up = 0; up < 5; up++) {
41629
41769
  try {
41630
- const pkg = JSON.parse(fs63.readFileSync(path65.join(dir2, "package.json"), "utf-8"));
41770
+ const pkg = JSON.parse(fs64.readFileSync(path66.join(dir2, "package.json"), "utf-8"));
41631
41771
  if (pkg.name === "@node9/proxy" || pkg.name === "node9-ai") {
41632
41772
  return pkg.version;
41633
41773
  }
41634
41774
  } catch {
41635
41775
  }
41636
- const parent = path65.dirname(dir2);
41776
+ const parent = path66.dirname(dir2);
41637
41777
  if (parent === dir2) break;
41638
41778
  dir2 = parent;
41639
41779
  }
@@ -41886,13 +42026,13 @@ function extractManagedConfig(body) {
41886
42026
  }
41887
42027
  function sweepStaleTmp(target) {
41888
42028
  try {
41889
- const dir2 = path65.dirname(target);
41890
- const prefix = `${path65.basename(target)}.`;
41891
- for (const name of fs63.readdirSync(dir2)) {
42029
+ const dir2 = path66.dirname(target);
42030
+ const prefix = `${path66.basename(target)}.`;
42031
+ for (const name of fs64.readdirSync(dir2)) {
41892
42032
  if (!name.startsWith(prefix) || !name.endsWith(".tmp")) continue;
41893
- const full = path65.join(dir2, name);
42033
+ const full = path66.join(dir2, name);
41894
42034
  try {
41895
- if (Date.now() - fs63.statSync(full).mtimeMs > 5 * 60 * 1e3) fs63.unlinkSync(full);
42035
+ if (Date.now() - fs64.statSync(full).mtimeMs > 5 * 60 * 1e3) fs64.unlinkSync(full);
41896
42036
  } catch {
41897
42037
  }
41898
42038
  }
@@ -41995,7 +42135,7 @@ async function pushBlastSnapshot(creds) {
41995
42135
  }
41996
42136
  async function pushPostureSnapshot(creds) {
41997
42137
  try {
41998
- const home = os53.homedir();
42138
+ const home = os54.homedir();
41999
42139
  const result = await runPosture({ home, cwd: home });
42000
42140
  await shipPosture(result, creds);
42001
42141
  } catch {
@@ -42169,7 +42309,7 @@ async function runCloudSync() {
42169
42309
  }
42170
42310
  function getCloudSyncStatus() {
42171
42311
  try {
42172
- const raw = JSON.parse(fs63.readFileSync(rulesCacheFile(), "utf-8"));
42312
+ const raw = JSON.parse(fs64.readFileSync(rulesCacheFile(), "utf-8"));
42173
42313
  if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
42174
42314
  return {
42175
42315
  cached: true,
@@ -42186,7 +42326,7 @@ function getCloudSyncStatus() {
42186
42326
  }
42187
42327
  function getCloudRules() {
42188
42328
  try {
42189
- const raw = JSON.parse(fs63.readFileSync(rulesCacheFile(), "utf-8"));
42329
+ const raw = JSON.parse(fs64.readFileSync(rulesCacheFile(), "utf-8"));
42190
42330
  return Array.isArray(raw.rules) ? raw.rules : null;
42191
42331
  } catch {
42192
42332
  return null;
@@ -42256,12 +42396,12 @@ var init_sync = __esm({
42256
42396
  loop: "loops",
42257
42397
  "long-output-redacted": "longOutputRedactions"
42258
42398
  };
42259
- rulesCacheFile = () => path65.join(os53.homedir(), ".node9", "rules-cache.json");
42260
- rulesCacheBackupFile = () => path65.join(os53.homedir(), ".node9", "rules-cache.last-good.json");
42399
+ rulesCacheFile = () => path66.join(os54.homedir(), ".node9", "rules-cache.json");
42400
+ rulesCacheBackupFile = () => path66.join(os54.homedir(), ".node9", "rules-cache.last-good.json");
42261
42401
  DEFAULT_INTERVAL_HOURS = 5;
42262
42402
  MIN_INTERVAL_SECONDS = 15;
42263
42403
  MAX_INTERVAL_SECONDS = 24 * 60 * 60;
42264
- syncHealthFile = () => path65.join(os53.homedir(), ".node9", "sync-health.json");
42404
+ syncHealthFile = () => path66.join(os54.homedir(), ".node9", "sync-health.json");
42265
42405
  STALE_MIN_MS = 3 * 60 * 60 * 1e3;
42266
42406
  STALE_MAX_MS = 24 * 60 * 60 * 1e3;
42267
42407
  STALE_FACTOR = 3;
@@ -42286,9 +42426,9 @@ __export(audit_shipper_exports, {
42286
42426
  startAuditShipper: () => startAuditShipper,
42287
42427
  writeWatermark: () => writeWatermark
42288
42428
  });
42289
- import fs64 from "fs";
42290
- import path66 from "path";
42291
- import os54 from "os";
42429
+ import fs65 from "fs";
42430
+ import path67 from "path";
42431
+ import os55 from "os";
42292
42432
  import crypto8 from "crypto";
42293
42433
  function exposureOf(v) {
42294
42434
  if (!v || typeof v !== "object") return null;
@@ -42298,21 +42438,21 @@ function exposureOf(v) {
42298
42438
  return { kind: o.kind, destinations: Math.max(0, Math.min(1e3, Math.floor(n))) };
42299
42439
  }
42300
42440
  function fileSignature(filePath) {
42301
- const fd = fs64.openSync(filePath, "r");
42441
+ const fd = fs65.openSync(filePath, "r");
42302
42442
  try {
42303
42443
  const buf = Buffer.alloc(512);
42304
- const read3 = fs64.readSync(fd, buf, 0, 512, 0);
42444
+ const read3 = fs65.readSync(fd, buf, 0, 512, 0);
42305
42445
  const slice = buf.subarray(0, read3);
42306
42446
  const nl = slice.indexOf(10);
42307
42447
  const firstLine = nl === -1 ? slice : slice.subarray(0, nl);
42308
42448
  return crypto8.createHash("sha256").update(firstLine).digest("hex").slice(0, 16);
42309
42449
  } finally {
42310
- fs64.closeSync(fd);
42450
+ fs65.closeSync(fd);
42311
42451
  }
42312
42452
  }
42313
42453
  function readWatermark(watermarkPath) {
42314
42454
  try {
42315
- const raw = JSON.parse(fs64.readFileSync(watermarkPath, "utf-8"));
42455
+ const raw = JSON.parse(fs65.readFileSync(watermarkPath, "utf-8"));
42316
42456
  if (typeof raw.fileSig === "string" && typeof raw.offset === "number" && raw.offset >= 0)
42317
42457
  return raw;
42318
42458
  } catch {
@@ -42321,8 +42461,8 @@ function readWatermark(watermarkPath) {
42321
42461
  }
42322
42462
  function writeWatermark(watermarkPath, wm) {
42323
42463
  const tmp = `${watermarkPath}.tmp`;
42324
- fs64.writeFileSync(tmp, JSON.stringify(wm));
42325
- fs64.renameSync(tmp, watermarkPath);
42464
+ fs65.writeFileSync(tmp, JSON.stringify(wm));
42465
+ fs65.renameSync(tmp, watermarkPath);
42326
42466
  }
42327
42467
  function buildWireRows(chunk2) {
42328
42468
  const lastNl = chunk2.lastIndexOf(10);
@@ -42409,11 +42549,11 @@ async function shipOnce(deps = {}) {
42409
42549
  if (!creds?.apiKey) return { status: "no-creds", shipped: 0 };
42410
42550
  const endpoint = buildBatchEndpoint(creds.apiUrl);
42411
42551
  if (!endpoint) return { status: "no-creds", shipped: 0 };
42412
- if (!fs64.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
42552
+ if (!fs65.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
42413
42553
  let shipped = 0;
42414
42554
  try {
42415
42555
  for (let chunkN = 0; chunkN < MAX_CHUNKS_PER_TICK; chunkN++) {
42416
- const size = fs64.statSync(auditLogPath).size;
42556
+ const size = fs65.statSync(auditLogPath).size;
42417
42557
  if (size === 0) break;
42418
42558
  const sig = fileSignature(auditLogPath);
42419
42559
  const wm = readWatermark(watermarkPath);
@@ -42421,12 +42561,12 @@ async function shipOnce(deps = {}) {
42421
42561
  if (offset >= size) break;
42422
42562
  const toRead = Math.min(size - offset, MAX_CHUNK_BYTES);
42423
42563
  const buf = Buffer.alloc(toRead);
42424
- const fd = fs64.openSync(auditLogPath, "r");
42564
+ const fd = fs65.openSync(auditLogPath, "r");
42425
42565
  let read3;
42426
42566
  try {
42427
- read3 = fs64.readSync(fd, buf, 0, toRead, offset);
42567
+ read3 = fs65.readSync(fd, buf, 0, toRead, offset);
42428
42568
  } finally {
42429
- fs64.closeSync(fd);
42569
+ fs65.closeSync(fd);
42430
42570
  }
42431
42571
  const { rows, consumed } = buildWireRows(buf.subarray(0, read3));
42432
42572
  if (consumed === 0) break;
@@ -42473,8 +42613,8 @@ async function shipOnce(deps = {}) {
42473
42613
  }
42474
42614
  function shipLagBytes(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHIP_WATERMARK) {
42475
42615
  try {
42476
- if (!fs64.existsSync(auditLogPath)) return 0;
42477
- const size = fs64.statSync(auditLogPath).size;
42616
+ if (!fs65.existsSync(auditLogPath)) return 0;
42617
+ const size = fs65.statSync(auditLogPath).size;
42478
42618
  const wm = readWatermark(watermarkPath);
42479
42619
  if (!wm) return size;
42480
42620
  if (wm.fileSig !== fileSignature(auditLogPath)) return size;
@@ -42499,20 +42639,20 @@ function startAuditShipper() {
42499
42639
  }
42500
42640
  function outboxBacklog(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHIP_WATERMARK, maxBytes = MAX_CHUNK_BYTES) {
42501
42641
  try {
42502
- const fd = fs64.openSync(auditLogPath, "r");
42642
+ const fd = fs65.openSync(auditLogPath, "r");
42503
42643
  let chunk2;
42504
42644
  let truncated;
42505
42645
  try {
42506
- const size = fs64.fstatSync(fd).size;
42646
+ const size = fs65.fstatSync(fd).size;
42507
42647
  const wm = readWatermark(watermarkPath);
42508
42648
  const offset = wm && wm.fileSig === fileSignature(auditLogPath) && wm.offset <= size ? wm.offset : 0;
42509
42649
  const length = Math.min(size - offset, maxBytes);
42510
42650
  const buffer = Buffer.alloc(length);
42511
- const read3 = fs64.readSync(fd, buffer, 0, length, offset);
42651
+ const read3 = fs65.readSync(fd, buffer, 0, length, offset);
42512
42652
  chunk2 = buffer.subarray(0, read3);
42513
42653
  truncated = size - offset > length;
42514
42654
  } finally {
42515
- fs64.closeSync(fd);
42655
+ fs65.closeSync(fd);
42516
42656
  }
42517
42657
  const { rows } = buildWireRows(chunk2);
42518
42658
  const oldest = rows.reduce((old, row) => {
@@ -42549,7 +42689,7 @@ var init_audit_shipper = __esm({
42549
42689
  "policy",
42550
42690
  "abandoned"
42551
42691
  ]);
42552
- AUDIT_SHIP_WATERMARK = path66.join(os54.homedir(), ".node9", "audit-ship.json");
42692
+ AUDIT_SHIP_WATERMARK = path67.join(os55.homedir(), ".node9", "audit-ship.json");
42553
42693
  DEFAULT_INTERVAL_MS = 2e4;
42554
42694
  MAX_BATCH = 500;
42555
42695
  MAX_CHUNK_BYTES = 4 * 1024 * 1024;
@@ -42631,12 +42771,12 @@ var init_decision = __esm({
42631
42771
  });
42632
42772
 
42633
42773
  // src/daemon/dlp-scanner.ts
42634
- import fs65 from "fs";
42635
- import path67 from "path";
42636
- import os55 from "os";
42774
+ import fs66 from "fs";
42775
+ import path68 from "path";
42776
+ import os56 from "os";
42637
42777
  function loadIndex() {
42638
42778
  try {
42639
- const raw = JSON.parse(fs65.readFileSync(INDEX_FILE, "utf-8"));
42779
+ const raw = JSON.parse(fs66.readFileSync(INDEX_FILE, "utf-8"));
42640
42780
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
42641
42781
  const r = raw;
42642
42782
  if (r.offsets && typeof r.offsets === "object") return r.offsets;
@@ -42648,13 +42788,13 @@ function loadIndex() {
42648
42788
  }
42649
42789
  function saveIndex(index) {
42650
42790
  try {
42651
- fs65.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
42791
+ fs66.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
42652
42792
  } catch {
42653
42793
  }
42654
42794
  }
42655
42795
  function appendAuditEntry(entry) {
42656
42796
  try {
42657
- fs65.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
42797
+ fs66.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
42658
42798
  } catch {
42659
42799
  }
42660
42800
  }
@@ -42668,52 +42808,52 @@ function runDlpScan() {
42668
42808
  canaryVals = [];
42669
42809
  }
42670
42810
  const canaryFindings = [];
42671
- if (!fs65.existsSync(PROJECTS_DIR2)) return;
42811
+ if (!fs66.existsSync(PROJECTS_DIR2)) return;
42672
42812
  const index = loadIndex();
42673
42813
  const seenThisPass = /* @__PURE__ */ new Set();
42674
42814
  const newFindings = [];
42675
42815
  let updated = false;
42676
42816
  let projDirs;
42677
42817
  try {
42678
- projDirs = fs65.readdirSync(PROJECTS_DIR2);
42818
+ projDirs = fs66.readdirSync(PROJECTS_DIR2);
42679
42819
  } catch {
42680
42820
  return;
42681
42821
  }
42682
42822
  for (const proj of projDirs) {
42683
- const projPath = path67.join(PROJECTS_DIR2, proj);
42823
+ const projPath = path68.join(PROJECTS_DIR2, proj);
42684
42824
  try {
42685
- if (!fs65.lstatSync(projPath).isDirectory()) continue;
42686
- const real = fs65.realpathSync(projPath);
42687
- if (!real.startsWith(PROJECTS_DIR2 + path67.sep) && real !== PROJECTS_DIR2) continue;
42825
+ if (!fs66.lstatSync(projPath).isDirectory()) continue;
42826
+ const real = fs66.realpathSync(projPath);
42827
+ if (!real.startsWith(PROJECTS_DIR2 + path68.sep) && real !== PROJECTS_DIR2) continue;
42688
42828
  } catch {
42689
42829
  continue;
42690
42830
  }
42691
42831
  let files;
42692
42832
  try {
42693
- files = fs65.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
42833
+ files = fs66.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
42694
42834
  } catch {
42695
42835
  continue;
42696
42836
  }
42697
42837
  for (const file2 of files) {
42698
- const filePath = path67.join(projPath, file2);
42838
+ const filePath = path68.join(projPath, file2);
42699
42839
  const lastOffset = index[filePath] ?? 0;
42700
42840
  let size;
42701
42841
  try {
42702
- size = fs65.statSync(filePath).size;
42842
+ size = fs66.statSync(filePath).size;
42703
42843
  } catch {
42704
42844
  continue;
42705
42845
  }
42706
42846
  if (size <= lastOffset) continue;
42707
42847
  let fd;
42708
42848
  try {
42709
- fd = fs65.openSync(filePath, "r");
42849
+ fd = fs66.openSync(filePath, "r");
42710
42850
  } catch {
42711
42851
  continue;
42712
42852
  }
42713
42853
  try {
42714
42854
  const chunkSize = size - lastOffset;
42715
42855
  const buf = Buffer.alloc(chunkSize);
42716
- fs65.readSync(fd, buf, 0, chunkSize, lastOffset);
42856
+ fs66.readSync(fd, buf, 0, chunkSize, lastOffset);
42717
42857
  const chunk2 = buf.toString("utf-8");
42718
42858
  for (const line of chunk2.split("\n")) {
42719
42859
  if (!line.trim()) continue;
@@ -42734,7 +42874,7 @@ function runDlpScan() {
42734
42874
  const match = scanText(text);
42735
42875
  const canary = canaryVals.length > 0 ? matchCanary(text, canaryVals) : null;
42736
42876
  if (!match && !canary) continue;
42737
- const projLabel = decodeURIComponent(proj).replace(os55.homedir(), "~").slice(0, 40);
42877
+ const projLabel = decodeURIComponent(proj).replace(os56.homedir(), "~").slice(0, 40);
42738
42878
  const ts = entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
42739
42879
  if (canary) {
42740
42880
  const rec = canaryById.get(canary.id);
@@ -42784,7 +42924,7 @@ function runDlpScan() {
42784
42924
  updated = true;
42785
42925
  } finally {
42786
42926
  try {
42787
- fs65.closeSync(fd);
42927
+ fs66.closeSync(fd);
42788
42928
  } catch {
42789
42929
  }
42790
42930
  }
@@ -42843,15 +42983,15 @@ var init_dlp_scanner = __esm({
42843
42983
  init_registry2();
42844
42984
  init_native();
42845
42985
  init_state3();
42846
- INDEX_FILE = path67.join(os55.homedir(), ".node9", "dlp-index.json");
42847
- PROJECTS_DIR2 = path67.join(os55.homedir(), ".claude", "projects");
42986
+ INDEX_FILE = path68.join(os56.homedir(), ".node9", "dlp-index.json");
42987
+ PROJECTS_DIR2 = path68.join(os56.homedir(), ".claude", "projects");
42848
42988
  }
42849
42989
  });
42850
42990
 
42851
42991
  // src/daemon/mcp-reconciler.ts
42852
- import fs66 from "fs";
42853
- import path68 from "path";
42854
- import os56 from "os";
42992
+ import fs67 from "fs";
42993
+ import path69 from "path";
42994
+ import os57 from "os";
42855
42995
  import crypto9 from "crypto";
42856
42996
  function idKey(e) {
42857
42997
  const cmdHash = crypto9.createHash("sha256").update(`${e.command} ${e.args.join(" ")}`).digest("hex").slice(0, 16);
@@ -42859,7 +42999,7 @@ function idKey(e) {
42859
42999
  }
42860
43000
  function loadBaseline() {
42861
43001
  try {
42862
- const raw = JSON.parse(fs66.readFileSync(BASELINE_FILE2, "utf-8"));
43002
+ const raw = JSON.parse(fs67.readFileSync(BASELINE_FILE2, "utf-8"));
42863
43003
  return new Set(Array.isArray(raw) ? raw : []);
42864
43004
  } catch {
42865
43005
  return /* @__PURE__ */ new Set();
@@ -42867,7 +43007,7 @@ function loadBaseline() {
42867
43007
  }
42868
43008
  function saveBaseline(keys) {
42869
43009
  try {
42870
- fs66.writeFileSync(BASELINE_FILE2, JSON.stringify([...keys].slice(-BASELINE_CAP)), {
43010
+ fs67.writeFileSync(BASELINE_FILE2, JSON.stringify([...keys].slice(-BASELINE_CAP)), {
42871
43011
  mode: 384
42872
43012
  });
42873
43013
  } catch {
@@ -43055,7 +43195,7 @@ var init_mcp_reconciler = __esm({
43055
43195
  init_cloud();
43056
43196
  init_audit();
43057
43197
  init_mcp_pin();
43058
- BASELINE_FILE2 = path68.join(os56.homedir(), ".node9", "mcp-baseline.json");
43198
+ BASELINE_FILE2 = path69.join(os57.homedir(), ".node9", "mcp-baseline.json");
43059
43199
  BASELINE_CAP = 500;
43060
43200
  DEFAULT_INTERVAL_MIN = 60;
43061
43201
  DEFAULT_STALE_DAYS = 7;
@@ -43129,114 +43269,6 @@ var init_hook_heal = __esm({
43129
43269
  }
43130
43270
  });
43131
43271
 
43132
- // src/daemon/startup-log.ts
43133
- import fs67 from "fs";
43134
- import path69 from "path";
43135
- import os57 from "os";
43136
- function capStartupLog(file2) {
43137
- try {
43138
- if (fs67.statSync(file2).size > MAX_STARTUP_LOG_BYTES) fs67.truncateSync(file2);
43139
- } catch {
43140
- }
43141
- }
43142
- function openStartupLogFd() {
43143
- try {
43144
- const file2 = DAEMON_STARTUP_LOG();
43145
- const dir2 = path69.dirname(file2);
43146
- if (!fs67.existsSync(dir2)) fs67.mkdirSync(dir2, { recursive: true });
43147
- capStartupLog(file2);
43148
- return fs67.openSync(file2, "a");
43149
- } catch {
43150
- return void 0;
43151
- }
43152
- }
43153
- function recordStartupState(outcome, kind, detail) {
43154
- try {
43155
- if (outcome === "starting") {
43156
- const prev = readStartupState();
43157
- if (prev?.outcome === "starting") {
43158
- const prevAt = new Date(prev.at).getTime();
43159
- const age = Math.abs(Date.now() - prevAt);
43160
- if (!isNaN(prevAt) && age < 24 * 60 * 60 * 1e3) return;
43161
- }
43162
- }
43163
- const file2 = DAEMON_STARTUP_STATE();
43164
- const dir2 = path69.dirname(file2);
43165
- if (!fs67.existsSync(dir2)) fs67.mkdirSync(dir2, { recursive: true });
43166
- const state = { outcome, at: (/* @__PURE__ */ new Date()).toISOString() };
43167
- if (kind) state.kind = kind;
43168
- if (detail) state.detail = detail.slice(0, MAX_DETAIL);
43169
- const tmp = `${file2}.${process.pid}.tmp`;
43170
- try {
43171
- fs67.writeFileSync(tmp, JSON.stringify(state), "utf-8");
43172
- fs67.renameSync(tmp, file2);
43173
- } catch (err2) {
43174
- try {
43175
- fs67.unlinkSync(tmp);
43176
- } catch {
43177
- }
43178
- throw err2;
43179
- }
43180
- } catch {
43181
- }
43182
- }
43183
- function readStartupState() {
43184
- try {
43185
- const raw = fs67.readFileSync(DAEMON_STARTUP_STATE(), "utf-8");
43186
- const s = JSON.parse(raw);
43187
- if (!s || typeof s.outcome !== "string" || typeof s.at !== "string") return null;
43188
- return s;
43189
- } catch {
43190
- return null;
43191
- }
43192
- }
43193
- function readStartupCause(maxAgeMs = 24 * 60 * 60 * 1e3) {
43194
- const s = readStartupState();
43195
- if (!s) return null;
43196
- const at = new Date(s.at);
43197
- if (isNaN(at.getTime()) || Date.now() - at.getTime() > maxAgeMs) return null;
43198
- switch (s.outcome) {
43199
- case "ok":
43200
- case "ok-elsewhere":
43201
- return null;
43202
- case "starting":
43203
- if (Date.now() - at.getTime() < STARTING_GRACE_MS) return null;
43204
- return {
43205
- kind: "did-not-start",
43206
- detail: "the daemon did not come up \u2014 see ~/.node9/daemon-startup.log and hook-debug.log",
43207
- at,
43208
- // `at` is the FIRST attempt of the streak, not the most recent one.
43209
- label: "start attempts failing since"
43210
- };
43211
- case "failed":
43212
- return { kind: s.kind || "failed", detail: s.detail || "", at, label: "last start attempt" };
43213
- default:
43214
- return null;
43215
- }
43216
- }
43217
- function logDaemonStartup(kind, detail) {
43218
- try {
43219
- const file2 = DAEMON_STARTUP_LOG();
43220
- const dir2 = path69.dirname(file2);
43221
- if (!fs67.existsSync(dir2)) fs67.mkdirSync(dir2, { recursive: true });
43222
- const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-startup:${kind}${detail ? ` ${detail}` : ""}
43223
- `;
43224
- fs67.appendFileSync(file2, line, "utf-8");
43225
- } catch {
43226
- }
43227
- }
43228
- var DAEMON_STARTUP_LOG, MAX_STARTUP_LOG_BYTES, DAEMON_STARTUP_STATE, MAX_DETAIL, STARTING_GRACE_MS;
43229
- var init_startup_log = __esm({
43230
- "src/daemon/startup-log.ts"() {
43231
- "use strict";
43232
- DAEMON_STARTUP_LOG = () => path69.join(os57.homedir(), ".node9", "daemon-startup.log");
43233
- MAX_STARTUP_LOG_BYTES = 256 * 1024;
43234
- DAEMON_STARTUP_STATE = () => path69.join(os57.homedir(), ".node9", "daemon-startup-state.json");
43235
- MAX_DETAIL = 200;
43236
- STARTING_GRACE_MS = 90 * 1e3;
43237
- }
43238
- });
43239
-
43240
43272
  // src/supply-chain/zip.ts
43241
43273
  import fs68 from "fs";
43242
43274
  import zlib from "zlib";
@@ -77568,7 +77600,7 @@ function buildReportJson(input) {
77568
77600
  var BLOCK_REASON_LABELS = {
77569
77601
  timeout: "Approval timeout",
77570
77602
  "smart-rule-block": "Smart rule",
77571
- "observe-mode-dlp-would-block": "DLP (observe)",
77603
+ "observe-mode-dlp-would-block": "DLP (audit)",
77572
77604
  "persistent-deny": "Persistent deny",
77573
77605
  "local-decision": "User denied",
77574
77606
  "dlp-block": "DLP block",
@@ -77608,10 +77640,10 @@ function fmtCost2(usd) {
77608
77640
  return "$" + usd.toFixed(2);
77609
77641
  }
77610
77642
  function registerReportCommand(program2) {
77611
- program2.command("report").description("Activity and security report \u2014 what Claude did, what was blocked").option("--period <period>", "today | 7d | 30d | month", "7d").option("--no-tests", "exclude test runner calls (npm test, vitest, pytest\u2026) from stats").option("--json", "Emit machine-readable JSON to stdout (suppresses renderer)").action((options) => {
77643
+ program2.command("report").description("Activity and security report \u2014 what Claude did, what was blocked").option("--period <period>", "today | 7d | 30d | 90d | month", "30d").option("--no-tests", "exclude test runner calls (npm test, vitest, pytest\u2026) from stats").option("--json", "Emit machine-readable JSON to stdout (suppresses renderer)").action((options) => {
77612
77644
  const period = ["today", "7d", "30d", "90d", "month"].includes(
77613
77645
  options.period
77614
- ) ? options.period : "7d";
77646
+ ) ? options.period : "30d";
77615
77647
  const excludeTests = options.tests === false;
77616
77648
  const { data, hasAuditFile, responseDlpEntries } = aggregateReportFromAudit(period, {
77617
77649
  excludeTests
@@ -77809,7 +77841,7 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
77809
77841
  );
77810
77842
  summaryRow(
77811
77843
  observeDlp > 0 ? chalk20.blue("\u{1F441}") : chalk20.dim("\u{1F441}"),
77812
- "DLP (observe)",
77844
+ "DLP (audit)",
77813
77845
  observeDlp,
77814
77846
  observeDlp > 0 ? "would-block in strict mode" : void 0,
77815
77847
  observeDlp > 0 ? (s) => chalk20.blue(s) : (s) => chalk20.dim(s)
@@ -79317,6 +79349,235 @@ import fs92 from "fs";
79317
79349
  import os78 from "os";
79318
79350
  import path91 from "path";
79319
79351
  import { spawnSync as spawnSync5 } from "child_process";
79352
+
79353
+ // src/cli/commands/egress.ts
79354
+ init_config();
79355
+ init_keyed_guard();
79356
+ init_dist();
79357
+ init_egress_config();
79358
+ init_egress_config();
79359
+ import chalk27 from "chalk";
79360
+ function guard(fn) {
79361
+ try {
79362
+ fn();
79363
+ return true;
79364
+ } catch (err2) {
79365
+ console.error(chalk27.red(`
79366
+ \u2717 ${err2.message}
79367
+ `));
79368
+ process.exitCode = 1;
79369
+ return false;
79370
+ }
79371
+ }
79372
+ function mutate(action, change) {
79373
+ if (!cliGuardPolicyWrite(action)) return false;
79374
+ return guard(() => setEgress(change));
79375
+ }
79376
+ function addHost(list2, host2) {
79377
+ if (!cliGuardPolicyWrite(`egress ${list2} ${host2}`)) return false;
79378
+ return guard(() => addEgressHost(list2, host2));
79379
+ }
79380
+ function exempt2(address) {
79381
+ if (!cliGuardPolicyWrite(`egress exempt ${address}`)) return false;
79382
+ return guard(() => addSsrfExemption(address));
79383
+ }
79384
+ var INTERNAL_FIELDS = {
79385
+ allowed: { ssrfStrict: false, allowPrivate: true },
79386
+ listed: { ssrfStrict: false, allowPrivate: false },
79387
+ blocked: { ssrfStrict: true, allowPrivate: false }
79388
+ };
79389
+ var INTERNAL_SAID = {
79390
+ allowed: "reachable without listing them",
79391
+ listed: "reachable only if they are on your allowlist",
79392
+ blocked: "blocked"
79393
+ };
79394
+ function describeInternalState(state) {
79395
+ return INTERNAL_SAID[state];
79396
+ }
79397
+ function readInternalState(e) {
79398
+ if (e.ssrfStrict === true) return "blocked";
79399
+ return e.allowPrivate === false ? "listed" : "allowed";
79400
+ }
79401
+ function setInternal(value) {
79402
+ const state = value.trim().toLowerCase();
79403
+ if (!(state in INTERNAL_FIELDS)) {
79404
+ console.error(
79405
+ chalk27.red(`
79406
+ \u2717 Expected "allowed", "listed" or "blocked", got "${value}".`) + chalk27.gray(
79407
+ "\n allowed loopback, 10/172.16/192.168 and CGNAT are reachable (default)\n listed they are reachable only if you allowlist them\n blocked they are blocked at the floor\n"
79408
+ )
79409
+ );
79410
+ process.exitCode = 1;
79411
+ return;
79412
+ }
79413
+ if (!mutate(`egress internal ${state}`, INTERNAL_FIELDS[state])) return;
79414
+ _resetConfigCache();
79415
+ const effective = readInternalState(getConfig().policy.egress);
79416
+ if (effective !== state) {
79417
+ console.log(
79418
+ chalk27.yellow(
79419
+ `
79420
+ \u26A0 Saved, but not in effect: your workspace sets internal addresses to ${effective.toUpperCase()} and that governs this machine.
79421
+ Change it in the dashboard, Enforcement \u2192 Network.
79422
+ `
79423
+ )
79424
+ );
79425
+ return;
79426
+ }
79427
+ const line = `
79428
+ \u2713 Internal addresses: ${state} \u2014 loopback, the private ranges and CGNAT are ${INTERNAL_SAID[state]}.
79429
+ `;
79430
+ console.log(state === "allowed" ? chalk27.yellow(line) : chalk27.green(line));
79431
+ }
79432
+ function showFloor(e, ssrfStrictSource) {
79433
+ console.log(
79434
+ chalk27.gray("\n Protected addresses") + chalk27.gray(" \u2014 in shell commands and declared URLs")
79435
+ );
79436
+ console.log(
79437
+ chalk27.gray(
79438
+ " always blocked: cloud metadata, link-local, multicast\n no setting releases these, though `node9 pause` suspends all enforcement"
79439
+ )
79440
+ );
79441
+ const state = readInternalState(e);
79442
+ const by = ssrfStrictSource === "workspace" ? "workspace (app.node9.ai)" : ssrfStrictSource === "local" ? "this machine (config.json)" : "the shipped default";
79443
+ const STATE_LABEL = {
79444
+ allowed: "allowed",
79445
+ listed: "allowlist only",
79446
+ blocked: "blocked"
79447
+ };
79448
+ console.log(
79449
+ ` Internal addresses: ${state === "blocked" ? chalk27.green(STATE_LABEL[state]) : chalk27.yellow(STATE_LABEL[state])}` + chalk27.gray(` loopback, 10/172.16/192.168 and CGNAT are ${INTERNAL_SAID[state]}`)
79450
+ );
79451
+ console.log(chalk27.gray(` set by: ${by}`));
79452
+ const exemptions = e.ssrfAllow ?? [];
79453
+ console.log(chalk27.gray(` Exemptions: ${exemptions.length ? exemptions.join(", ") : "none"}`));
79454
+ console.log(
79455
+ chalk27.gray(
79456
+ " Covered: shell commands, and tools that declare a URL (WebFetch, an MCP\n fetch tool, browser navigate).\n Not covered: an interpreter one-liner (node -e, python3 -c) hides its\n destination inside a program and does not reach this gate.\n CGNAT (100.64/10) is NOT in the always-blocked set: it is reachable\n until Internal addresses is on, and an exemption can release it."
79457
+ )
79458
+ );
79459
+ }
79460
+ function showStatus() {
79461
+ const cfg = getConfig();
79462
+ const e = cfg.policy.egress;
79463
+ const state = !e.enabled ? chalk27.red("OFF \u2014 your agent can reach any host, except the protected ones below") : e.mode === "block" ? chalk27.green("LOCKED (block) \u2014 unknown hosts are denied") : chalk27.yellow("WATCHING (review) \u2014 unknown hosts prompt you");
79464
+ console.log(chalk27.cyan.bold("\n\u{1F310} Egress control"));
79465
+ console.log(" State: " + state);
79466
+ if (cfg.policySource === "workspace") {
79467
+ console.log(
79468
+ chalk27.gray(" Source: workspace config (app.node9.ai) \u2014 local egress settings are ignored")
79469
+ );
79470
+ }
79471
+ console.log(
79472
+ chalk27.gray(
79473
+ ` ${DEFAULT_EGRESS_ALLOWLIST.length} common dev/LLM hosts are always allowed (github, npm, pypi, anthropic, \u2026).`
79474
+ )
79475
+ );
79476
+ showFloor(e, cfg.ssrfStrictSource);
79477
+ if (e.allow.length) console.log("\n Your allow: " + e.allow.join(", "));
79478
+ if (e.deny.length) console.log(" Your deny: " + e.deny.join(", "));
79479
+ if (!e.enabled) {
79480
+ console.log(chalk27.gray("\n Turn it on: node9 egress watch (prompt on unknown hosts)"));
79481
+ console.log(chalk27.gray(" node9 egress lock (hard-block unknown hosts)"));
79482
+ }
79483
+ console.log("");
79484
+ }
79485
+ function registerEgressCommand(program2) {
79486
+ const egress = program2.command("egress").description("Control where your agent can send data (egress allowlist)");
79487
+ egress.command("watch").description("Prompt before the agent reaches an unknown host (review mode)").action(() => {
79488
+ if (!mutate("egress watch", { enabled: true, mode: "review" })) return;
79489
+ console.log(chalk27.green("\n\u2713 Egress is now watched (review mode)."));
79490
+ console.log(
79491
+ chalk27.gray(" Routine hosts (LLM APIs, package registries, localhost) are allowed.")
79492
+ );
79493
+ console.log(
79494
+ chalk27.gray(" An unknown host will prompt you \u2014 run `node9 egress lock` to hard-block.\n")
79495
+ );
79496
+ });
79497
+ egress.command("lock").description("Block the agent from reaching unknown hosts (block mode)").action(() => {
79498
+ if (!mutate("egress lock", { enabled: true, mode: "block" })) return;
79499
+ console.log(chalk27.green("\n\u2713 Egress is now locked (block mode)."));
79500
+ console.log(chalk27.gray(" Routine hosts are still allowed; unknown hosts are denied."));
79501
+ console.log(chalk27.gray(" Allow a specific host with `node9 egress allow <host>`.\n"));
79502
+ });
79503
+ egress.command("allow <host>").description("Allow an extra host (glob, e.g. *.mycorp.com)").action((host2) => {
79504
+ if (!addHost("allow", host2)) return;
79505
+ console.log(chalk27.green(`
79506
+ \u2713 Allowed egress to ${host2}.
79507
+ `));
79508
+ });
79509
+ egress.command("deny <host>").description("Block an extra host (deny always wins)").action((host2) => {
79510
+ if (!addHost("deny", host2)) return;
79511
+ console.log(chalk27.green(`
79512
+ \u2713 Denied egress to ${host2}.
79513
+ `));
79514
+ });
79515
+ egress.command("off").description("Turn egress control off").action(() => {
79516
+ if (!mutate("egress off", { enabled: false })) return;
79517
+ console.log(
79518
+ chalk27.yellow("\n\u2713 Egress control is off \u2014 the agent can reach any host again.\n")
79519
+ );
79520
+ });
79521
+ egress.command("internal <allowed|listed|blocked>").description(
79522
+ "How loopback, private ranges and CGNAT are treated: allowed (default), listed (must be on the allowlist), or blocked"
79523
+ ).action((value) => setInternal(value));
79524
+ egress.command("strict <on|off>").description("Deprecated alias for `egress internal blocked|allowed`").action((value) => {
79525
+ const v = value.trim().toLowerCase();
79526
+ if (v !== "on" && v !== "off") {
79527
+ console.error(chalk27.red(`
79528
+ \u2717 Expected "on" or "off", got "${value}".
79529
+ `));
79530
+ process.exitCode = 1;
79531
+ return;
79532
+ }
79533
+ const state = v === "on" ? "blocked" : getConfig().policy.egress.allowPrivate === false ? "listed" : "allowed";
79534
+ console.log(chalk27.gray(`
79535
+ (\`egress strict ${v}\` is now \`egress internal ${state}\`)`));
79536
+ setInternal(state);
79537
+ });
79538
+ egress.command("exempt <address>").description("Let an address or a CIDR range through the floor (e.g. 100.64.0.0/10)").action((address) => {
79539
+ const a = normalizeEgressHost(address);
79540
+ const slash = a.indexOf("/");
79541
+ const base2 = slash === -1 ? a : a.slice(0, slash);
79542
+ const prefixText = slash === -1 ? null : a.slice(slash + 1);
79543
+ if (!normalizeIpLiteral(base2) || prefixText !== null && !/^\d{1,3}$/.test(prefixText)) {
79544
+ console.error(
79545
+ chalk27.red(`
79546
+ \u2717 "${address}" is not an address or a range.`) + chalk27.gray(
79547
+ "\n Exemptions are matched as an address (10.0.0.5) or a CIDR\n range (100.64.0.0/10), never a name.\n"
79548
+ )
79549
+ );
79550
+ process.exitCode = 1;
79551
+ return;
79552
+ }
79553
+ const m = classifySsrf(base2);
79554
+ if (m && !m.overridable) {
79555
+ console.error(
79556
+ chalk27.red(`
79557
+ \u2717 ${a} cannot be exempted.`) + chalk27.gray(
79558
+ `
79559
+ ${slash === -1 ? "That address is" : "That range covers only"} protected addresses (${m.tier}), which no setting releases.
79560
+ `
79561
+ )
79562
+ );
79563
+ process.exitCode = 1;
79564
+ return;
79565
+ }
79566
+ if (!exempt2(a)) return;
79567
+ console.log(chalk27.green(`
79568
+ \u2713 ${a} is exempt from the floor.`));
79569
+ if (!m)
79570
+ console.log(
79571
+ chalk27.gray(" Note: this address is not on the floor anyway \u2014 nothing changes.\n")
79572
+ );
79573
+ else console.log("");
79574
+ });
79575
+ egress.command("status").description("Show the current egress state").action(showStatus);
79576
+ egress.action(showStatus);
79577
+ }
79578
+
79579
+ // src/mcp-server/index.ts
79580
+ init_agent_wiring();
79320
79581
  function ok(id, result) {
79321
79582
  return JSON.stringify({ jsonrpc: "2.0", id: id ?? null, result });
79322
79583
  }
@@ -79450,7 +79711,7 @@ var TOOLS = [
79450
79711
  },
79451
79712
  {
79452
79713
  name: "node9_scan",
79453
- description: "Scan all AI agent history (Claude + Gemini) and report what node9 would have blocked or flagged. Shows blocked operations, reviewed commands, credential leaks, and agent spend. Use this to audit past activity and find security gaps before they become incidents.",
79714
+ description: "Scan the history of all supported coding agents (Claude Code, Gemini, Codex, Copilot, Antigravity) and report what node9 would have blocked or flagged. Shows blocked operations, reviewed commands, credential leaks, and agent spend. Use this to audit past activity and find security gaps before they become incidents.",
79454
79715
  inputSchema: {
79455
79716
  type: "object",
79456
79717
  properties: {
@@ -79470,8 +79731,8 @@ var TOOLS = [
79470
79731
  properties: {
79471
79732
  period: {
79472
79733
  type: "string",
79473
- enum: ["today", "7d", "30d", "month"],
79474
- description: "Time period for the report (default: 7d)."
79734
+ enum: ["today", "7d", "30d", "90d", "month"],
79735
+ description: "Time period for the report (default: 30d)."
79475
79736
  },
79476
79737
  no_tests: {
79477
79738
  type: "boolean",
@@ -79605,7 +79866,7 @@ function handleStatus() {
79605
79866
  const wsGoverned = config.policySource === "workspace";
79606
79867
  const activeShields = wsGoverned ? config.policy.appliedShields ?? [] : readActiveShields();
79607
79868
  const lines = [];
79608
- lines.push(`Mode: ${settings.mode}`);
79869
+ lines.push(`Mode: ${levelOf(settings.mode)}`);
79609
79870
  if (wsGoverned) {
79610
79871
  lines.push(
79611
79872
  "Policy source: workspace config (app.node9.ai) \u2014 local config files are ignored for policy"
@@ -79621,6 +79882,12 @@ function handleStatus() {
79621
79882
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
79622
79883
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
79623
79884
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
79885
+ lines.push("Agent hooks:");
79886
+ for (const a of getAgentWiring()) {
79887
+ const hooked = a.hooks.some((h) => h.wired);
79888
+ const state = !a.installed ? "not installed" : hooked ? "wired" : a.isProtected ? "protected through MCP" : "not wired";
79889
+ lines.push(` ${a.label}: ${state}`);
79890
+ }
79624
79891
  const projectConfig = path91.join(process.cwd(), "node9.config.json");
79625
79892
  const globalConfig = path91.join(os78.homedir(), ".node9", "config.json");
79626
79893
  const presentLabel = wsGoverned ? "present \u2014 ignored (workspace config governs)" : "present";
@@ -79713,6 +79980,11 @@ function handleShieldDisable(args) {
79713
79980
  if (disabledApp) recordUserDecision(disabledApp.slug, "off");
79714
79981
  return `Shield "${name}" disabled.`;
79715
79982
  }
79983
+ function internalAddressLine(e) {
79984
+ const configured = readInternalState(e);
79985
+ const state = !e.enabled && configured === "listed" ? "allowed" : configured;
79986
+ return `Internal addresses: ${state} \u2014 loopback and the private ranges are ${describeInternalState(state)}.`;
79987
+ }
79716
79988
  function handleEgressStatus() {
79717
79989
  const cfgEgress = getConfig();
79718
79990
  const e = cfgEgress.policy.egress;
@@ -79734,7 +80006,7 @@ function handleEgressStatus() {
79734
80006
  // actually covers is measured in egress.integration.test.ts.
79735
80007
  "Protected addresses: cloud metadata, link-local and multicast are blocked before any of the above is consulted, in shell commands AND in tools that declare a URL (WebFetch, an MCP fetch tool, browser navigate). No setting releases them, though `node9 pause` suspends all enforcement.",
79736
80008
  "NOT covered by that: an interpreter one-liner (node -e, python3 -c) carries its destination inside a program and does not reach this gate. CGNAT (100.64/10) is NOT in the always-blocked set \u2014 it is reachable until the strict tier is on, and an exemption can release it.",
79737
- `Internal addresses: ${e.ssrfStrict ? "on" : "off"} \u2014 loopback and the private ranges are ${e.ssrfStrict ? "blocked too" : "reachable"}.`,
80009
+ internalAddressLine(e),
79738
80010
  `Floor exemptions: ${e.ssrfAllow?.length ? e.ssrfAllow.join(", ") : "(none)"}`
79739
80011
  ];
79740
80012
  return lines.join("\n");
@@ -80057,7 +80329,7 @@ function registerMcpServerCommand(program2) {
80057
80329
  init_trusted_hosts();
80058
80330
  init_keyed_guard();
80059
80331
  init_trusted_recipients();
80060
- import chalk27 from "chalk";
80332
+ import chalk28 from "chalk";
80061
80333
  function isValidHost(host2) {
80062
80334
  return /^(\*\.)?[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/.test(host2);
80063
80335
  }
@@ -80068,17 +80340,17 @@ function registerTrustCommand(program2) {
80068
80340
  const normalized = normalizeHost2(host2.trim());
80069
80341
  if (!isValidHost(normalized)) {
80070
80342
  console.error(
80071
- chalk27.red(`
80343
+ chalk28.red(`
80072
80344
  \u274C Invalid host: "${host2}"
80073
- `) + chalk27.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
80345
+ `) + chalk28.gray(" Use an FQDN like api.mycompany.com or *.mycompany.com\n")
80074
80346
  );
80075
80347
  process.exit(1);
80076
80348
  }
80077
80349
  addTrustedHost(normalized);
80078
- console.log(chalk27.green(`
80350
+ console.log(chalk28.green(`
80079
80351
  \u2705 ${normalized} added to trusted hosts.`));
80080
80352
  console.log(
80081
- chalk27.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
80353
+ chalk28.gray(" Pipe-chain blocks to this host: critical \u2192 review, high \u2192 allow\n")
80082
80354
  );
80083
80355
  });
80084
80356
  trustCmd.command("remove <host>").description("Remove a trusted host").action((host2) => {
@@ -80086,27 +80358,27 @@ function registerTrustCommand(program2) {
80086
80358
  const normalized = normalizeHost2(host2.trim());
80087
80359
  const removed = removeTrustedHost(normalized);
80088
80360
  if (!removed) {
80089
- console.error(chalk27.yellow(`
80361
+ console.error(chalk28.yellow(`
80090
80362
  \u26A0\uFE0F "${normalized}" is not in the trusted hosts list.
80091
80363
  `));
80092
80364
  process.exit(1);
80093
80365
  }
80094
- console.log(chalk27.green(`
80366
+ console.log(chalk28.green(`
80095
80367
  \u2705 ${normalized} removed from trusted hosts.
80096
80368
  `));
80097
80369
  });
80098
80370
  trustCmd.command("list").description("Show all trusted hosts").action(() => {
80099
80371
  const hosts = readTrustedHosts();
80100
80372
  if (hosts.length === 0) {
80101
- console.log(chalk27.gray("\n No trusted hosts configured.\n"));
80102
- console.log(` Add one: ${chalk27.cyan("node9 trust add api.mycompany.com")}
80373
+ console.log(chalk28.gray("\n No trusted hosts configured.\n"));
80374
+ console.log(` Add one: ${chalk28.cyan("node9 trust add api.mycompany.com")}
80103
80375
  `);
80104
80376
  return;
80105
80377
  }
80106
- console.log(chalk27.bold("\n\u{1F513} Trusted Hosts\n"));
80378
+ console.log(chalk28.bold("\n\u{1F513} Trusted Hosts\n"));
80107
80379
  for (const entry of hosts) {
80108
80380
  const date = new Date(entry.addedAt).toLocaleDateString();
80109
- console.log(` ${chalk27.cyan(entry.host.padEnd(40))} ${chalk27.gray(`added ${date}`)}`);
80381
+ console.log(` ${chalk28.cyan(entry.host.padEnd(40))} ${chalk28.gray(`added ${date}`)}`);
80110
80382
  }
80111
80383
  console.log("");
80112
80384
  });
@@ -80118,41 +80390,41 @@ function registerTrustCommand(program2) {
80118
80390
  const stored = addTrustedRecipient(value, "user");
80119
80391
  if (!stored) {
80120
80392
  console.error(
80121
- chalk27.red(`
80393
+ chalk28.red(`
80122
80394
  \u274C Not a destination: "${value}"
80123
- `) + chalk27.gray(" Use an email address, a hostname or URL, or an IBAN.\n")
80395
+ `) + chalk28.gray(" Use an email address, a hostname or URL, or an IBAN.\n")
80124
80396
  );
80125
80397
  process.exit(1);
80126
80398
  }
80127
- console.log(chalk27.green(`
80399
+ console.log(chalk28.green(`
80128
80400
  \u2705 ${stored} added to trusted recipients.
80129
80401
  `));
80130
80402
  });
80131
80403
  rec.command("remove <value>").description("Remove a trusted recipient").action((value) => {
80132
80404
  if (!cliGuardPolicyWrite(`trust recipient remove ${value}`)) return;
80133
80405
  if (!removeTrustedRecipient(value)) {
80134
- console.error(chalk27.yellow(`
80406
+ console.error(chalk28.yellow(`
80135
80407
  \u26A0\uFE0F "${value}" is not in the trusted recipients list.
80136
80408
  `));
80137
80409
  process.exit(1);
80138
80410
  }
80139
- console.log(chalk27.green(`
80411
+ console.log(chalk28.green(`
80140
80412
  \u2705 ${value} removed from trusted recipients.
80141
80413
  `));
80142
80414
  });
80143
80415
  rec.command("list").description("Show all trusted recipients").action(() => {
80144
80416
  const entries = readTrustedRecipients();
80145
80417
  if (entries.length === 0) {
80146
- console.log(chalk27.gray("\n No trusted recipients.\n"));
80147
- console.log(` Add one: ${chalk27.cyan("node9 trust recipient add partner@example.com")}
80418
+ console.log(chalk28.gray("\n No trusted recipients.\n"));
80419
+ console.log(` Add one: ${chalk28.cyan("node9 trust recipient add partner@example.com")}
80148
80420
  `);
80149
80421
  return;
80150
80422
  }
80151
- console.log(chalk27.bold("\n\u{1F513} Trusted Recipients\n"));
80423
+ console.log(chalk28.bold("\n\u{1F513} Trusted Recipients\n"));
80152
80424
  for (const e of entries) {
80153
80425
  const date = new Date(e.addedAt).toLocaleDateString();
80154
80426
  console.log(
80155
- ` ${chalk27.cyan(e.value.padEnd(44))} ${chalk27.gray(`added ${date} (${e.addedBy})`)}`
80427
+ ` ${chalk28.cyan(e.value.padEnd(44))} ${chalk28.gray(`added ${date} (${e.addedBy})`)}`
80156
80428
  );
80157
80429
  }
80158
80430
  console.log("");
@@ -80161,13 +80433,13 @@ function registerTrustCommand(program2) {
80161
80433
 
80162
80434
  // src/cli/commands/mcp-pin.ts
80163
80435
  init_mcp_pin();
80164
- import chalk29 from "chalk";
80436
+ import chalk30 from "chalk";
80165
80437
  import fs93 from "fs";
80166
80438
 
80167
80439
  // src/cli/commands/mcp-gateway-cmd.ts
80168
80440
  init_mcp_wrap();
80169
80441
  init_mcp_status();
80170
- import chalk28 from "chalk";
80442
+ import chalk29 from "chalk";
80171
80443
  var STATE_WORD = {
80172
80444
  gatewayed: "governed",
80173
80445
  "node9-self": "node9",
@@ -80176,10 +80448,10 @@ var STATE_WORD = {
80176
80448
  };
80177
80449
  function stateChip(state) {
80178
80450
  const w = STATE_WORD[state].padEnd(12);
80179
- if (state === "gatewayed") return chalk28.green(w);
80180
- if (state === "node9-self") return chalk28.gray(w);
80181
- if (state === "remote") return chalk28.gray(w);
80182
- return chalk28.yellow(w);
80451
+ if (state === "gatewayed") return chalk29.green(w);
80452
+ if (state === "node9-self") return chalk29.gray(w);
80453
+ if (state === "remote") return chalk29.gray(w);
80454
+ return chalk29.yellow(w);
80183
80455
  }
80184
80456
  function relativeAge(ms, now) {
80185
80457
  const s = Math.max(0, Math.round((now - ms) / 1e3));
@@ -80193,15 +80465,15 @@ function relativeAge(ms, now) {
80193
80465
  function connectionChip(e, now) {
80194
80466
  switch (e.connection) {
80195
80467
  case "connected":
80196
- return chalk28.green(
80468
+ return chalk29.green(
80197
80469
  `\u2713 connected${e.lastSeenAt ? ` (${relativeAge(e.lastSeenAt, now)})` : ""}`
80198
80470
  );
80199
80471
  case "stale":
80200
- return chalk28.yellow(`\u26A0 stale${e.lastSeenAt ? ` (${relativeAge(e.lastSeenAt, now)})` : ""}`);
80472
+ return chalk29.yellow(`\u26A0 stale${e.lastSeenAt ? ` (${relativeAge(e.lastSeenAt, now)})` : ""}`);
80201
80473
  case "pending-launch":
80202
- return chalk28.cyan(`\u2026 pending \u2014 restart ${e.agentLabel} to connect`);
80474
+ return chalk29.cyan(`\u2026 pending \u2014 restart ${e.agentLabel} to connect`);
80203
80475
  case "unlaunchable":
80204
- return chalk28.red(
80476
+ return chalk29.red(
80205
80477
  `\u2717 can't launch \u2014 ${(e.missingEnv ?? []).map((v) => `$${v}`).join(", ")} not set`
80206
80478
  );
80207
80479
  default:
@@ -80226,7 +80498,7 @@ function registerMcpGatewayCommand2(mcp) {
80226
80498
  const now = Date.now();
80227
80499
  const rows = resolveMcpStatus(void 0, process.env, now);
80228
80500
  if (rows.length === 0) {
80229
- console.error(chalk28.gray("No MCP servers found in any agent config."));
80501
+ console.error(chalk29.gray("No MCP servers found in any agent config."));
80230
80502
  return;
80231
80503
  }
80232
80504
  for (const e of rows) {
@@ -80240,20 +80512,20 @@ function registerMcpGatewayCommand2(mcp) {
80240
80512
  const pending = rows.filter((e) => e.connection === "pending-launch").length;
80241
80513
  if (ungoverned > 0) {
80242
80514
  console.error(
80243
- chalk28.yellow(
80515
+ chalk29.yellow(
80244
80516
  `
80245
- ${ungoverned} ungoverned \u2014 run ` + chalk28.bold("node9 mcp gateway --all") + " to govern."
80517
+ ${ungoverned} ungoverned \u2014 run ` + chalk29.bold("node9 mcp gateway --all") + " to govern."
80246
80518
  )
80247
80519
  );
80248
80520
  }
80249
80521
  if (pending > 0) {
80250
80522
  console.error(
80251
- chalk28.cyan(`${pending} governed but not yet connected \u2014 restart the agent to activate.`)
80523
+ chalk29.cyan(`${pending} governed but not yet connected \u2014 restart the agent to activate.`)
80252
80524
  );
80253
80525
  }
80254
80526
  if (unlaunchable > 0) {
80255
80527
  console.error(
80256
- chalk28.red(
80528
+ chalk29.red(
80257
80529
  `${unlaunchable} can't launch \u2014 set the missing env var(s) where the agent starts (a restart alone won't fix it).`
80258
80530
  )
80259
80531
  );
@@ -80272,27 +80544,27 @@ ${ungoverned} ungoverned \u2014 run ` + chalk28.bold("node9 mcp gateway --all")
80272
80544
  } else if (name) {
80273
80545
  const matches2 = resolve(name, opts.agent);
80274
80546
  if (matches2.length === 0) {
80275
- console.error(chalk28.red(`No MCP server named "${name}" found.`));
80547
+ console.error(chalk29.red(`No MCP server named "${name}" found.`));
80276
80548
  process.exitCode = 1;
80277
80549
  return;
80278
80550
  }
80279
80551
  if (matches2.length > 1) {
80280
80552
  console.error(
80281
- chalk28.red(`"${name}" exists in ${matches2.length} agents \u2014 pass --agent <id> (`) + matches2.map((m) => m.agent).join(", ") + ")."
80553
+ chalk29.red(`"${name}" exists in ${matches2.length} agents \u2014 pass --agent <id> (`) + matches2.map((m) => m.agent).join(", ") + ")."
80282
80554
  );
80283
80555
  process.exitCode = 1;
80284
80556
  return;
80285
80557
  }
80286
80558
  targets = matches2;
80287
80559
  } else {
80288
- console.error(chalk28.red("Pass a server name or --all. See `node9 mcp status`."));
80560
+ console.error(chalk29.red("Pass a server name or --all. See `node9 mcp status`."));
80289
80561
  process.exitCode = 1;
80290
80562
  return;
80291
80563
  }
80292
80564
  const doable = targets.filter((e) => e.state === targetState);
80293
80565
  if (doable.length === 0) {
80294
80566
  console.error(
80295
- chalk28.gray(
80567
+ chalk29.gray(
80296
80568
  opts.rewrap ? "Nothing to do \u2014 no governed servers to refresh." : "Nothing to do \u2014 already governed (or node9-self)."
80297
80569
  )
80298
80570
  );
@@ -80306,20 +80578,20 @@ ${ungoverned} ungoverned \u2014 run ` + chalk28.bold("node9 mcp gateway --all")
80306
80578
  else wrapEntry(e);
80307
80579
  agents.add(e.agentLabel);
80308
80580
  const verb = opts.rewrap ? "refreshed" : "governed";
80309
- console.error(chalk28.green(`\u2713 ${verb} ${e.name}`) + chalk28.gray(` (${e.agentLabel})`));
80581
+ console.error(chalk29.green(`\u2713 ${verb} ${e.name}`) + chalk29.gray(` (${e.agentLabel})`));
80310
80582
  } catch (err2) {
80311
80583
  failed++;
80312
80584
  const msg = err2 instanceof Error ? err2.message : String(err2);
80313
80585
  console.error(
80314
- chalk28.red(`\u2717 failed ${e.name}`) + chalk28.gray(` (${e.agentLabel}): ${msg}`)
80586
+ chalk29.red(`\u2717 failed ${e.name}`) + chalk29.gray(` (${e.agentLabel}): ${msg}`)
80315
80587
  );
80316
80588
  }
80317
80589
  }
80318
80590
  if (failed > 0) process.exitCode = 1;
80319
80591
  if (agents.size > 0) {
80320
80592
  console.error(
80321
- chalk28.bold(`
80322
- Restart ${[...agents].join(", ")}`) + chalk28.gray(" to activate. Undo any server with ") + chalk28.cyan("node9 mcp ungateway <name>") + chalk28.gray(".")
80593
+ chalk29.bold(`
80594
+ Restart ${[...agents].join(", ")}`) + chalk29.gray(" to activate. Undo any server with ") + chalk29.cyan("node9 mcp ungateway <name>") + chalk29.gray(".")
80323
80595
  );
80324
80596
  }
80325
80597
  }
@@ -80327,19 +80599,19 @@ Restart ${[...agents].join(", ")}`) + chalk28.gray(" to activate. Undo any serve
80327
80599
  mcp.command("ungateway <name>").description("Restore an MCP server to its original (un-route it from node9)").option("--agent <id>", "disambiguate a name in more than one agent").action((name, opts) => {
80328
80600
  const matches2 = resolve(name, opts.agent).filter((e2) => e2.state === "gatewayed");
80329
80601
  if (matches2.length === 0) {
80330
- console.error(chalk28.red(`No governed MCP server named "${name}" found.`));
80602
+ console.error(chalk29.red(`No governed MCP server named "${name}" found.`));
80331
80603
  process.exitCode = 1;
80332
80604
  return;
80333
80605
  }
80334
80606
  if (matches2.length > 1) {
80335
- console.error(chalk28.red(`"${name}" is governed in multiple agents \u2014 pass --agent <id>.`));
80607
+ console.error(chalk29.red(`"${name}" is governed in multiple agents \u2014 pass --agent <id>.`));
80336
80608
  process.exitCode = 1;
80337
80609
  return;
80338
80610
  }
80339
80611
  const e = matches2[0];
80340
80612
  const orig = fromGateway(e.raw);
80341
80613
  if (!orig) {
80342
- console.error(chalk28.red(`Couldn't parse the original command for "${name}".`));
80614
+ console.error(chalk29.red(`Couldn't parse the original command for "${name}".`));
80343
80615
  process.exitCode = 1;
80344
80616
  return;
80345
80617
  }
@@ -80347,12 +80619,12 @@ Restart ${[...agents].join(", ")}`) + chalk28.gray(" to activate. Undo any serve
80347
80619
  writeMcpEntry(e.mcpFile, e.format, e.name, orig);
80348
80620
  } catch (err2) {
80349
80621
  const msg = err2 instanceof Error ? err2.message : String(err2);
80350
- console.error(chalk28.red(`Failed to restore "${name}": ${msg}`));
80622
+ console.error(chalk29.red(`Failed to restore "${name}": ${msg}`));
80351
80623
  process.exitCode = 1;
80352
80624
  return;
80353
80625
  }
80354
80626
  console.error(
80355
- chalk28.green(`\u2713 restored ${e.name}`) + chalk28.gray(` (${e.agentLabel}) \u2014 restart ${e.agentLabel} to apply.`)
80627
+ chalk29.green(`\u2713 restored ${e.name}`) + chalk29.gray(` (${e.agentLabel}) \u2014 restart ${e.agentLabel} to apply.`)
80356
80628
  );
80357
80629
  });
80358
80630
  }
@@ -80378,14 +80650,14 @@ function registerMcpPinCommand(program2) {
80378
80650
  }
80379
80651
  }
80380
80652
  if (repoCorrupt) {
80381
- console.error(chalk29.red(`
80653
+ console.error(chalk30.red(`
80382
80654
  \u274C Repo pin file at ${found.path} is corrupt or unreadable.`));
80383
80655
  process.exit(1);
80384
80656
  }
80385
80657
  if (!homeResult.ok && homeResult.reason === "corrupt") {
80386
- console.error(chalk29.red(`
80658
+ console.error(chalk30.red(`
80387
80659
  \u274C Home pin file is corrupt: ${homeResult.detail}`));
80388
- console.error(chalk29.yellow(" Run: node9 mcp pin reset\n"));
80660
+ console.error(chalk30.yellow(" Run: node9 mcp pin reset\n"));
80389
80661
  process.exit(1);
80390
80662
  }
80391
80663
  const homeEntries = homeResult.ok ? homeResult.pins.servers : {};
@@ -80397,25 +80669,25 @@ function registerMcpPinCommand(program2) {
80397
80669
  merged.set(key, { entry, source: "repo" });
80398
80670
  }
80399
80671
  if (merged.size === 0) {
80400
- console.log(chalk29.gray("\nNo MCP servers are pinned yet."));
80672
+ console.log(chalk30.gray("\nNo MCP servers are pinned yet."));
80401
80673
  console.log(
80402
- chalk29.gray("Pins are created automatically when the MCP gateway first connects.\n")
80674
+ chalk30.gray("Pins are created automatically when the MCP gateway first connects.\n")
80403
80675
  );
80404
80676
  return;
80405
80677
  }
80406
- console.log(chalk29.bold("\n\u{1F512} Pinned MCP Servers\n"));
80678
+ console.log(chalk30.bold("\n\u{1F512} Pinned MCP Servers\n"));
80407
80679
  const showSource = found.source === "repo";
80408
80680
  for (const [key, { entry, source }] of merged) {
80409
- const tag = showSource ? ` ${chalk29.yellow(`[${source}]`)}` : "";
80410
- console.log(` ${chalk29.cyan(key)}${tag} ${chalk29.gray(entry.label)}`);
80411
- console.log(` Tools (${entry.toolCount}): ${chalk29.white(entry.toolNames.join(", "))}`);
80412
- console.log(` Hash: ${chalk29.gray(entry.toolsHash.slice(0, 16))}...`);
80413
- console.log(` Pinned: ${chalk29.gray(entry.pinnedAt)}`);
80681
+ const tag = showSource ? ` ${chalk30.yellow(`[${source}]`)}` : "";
80682
+ console.log(` ${chalk30.cyan(key)}${tag} ${chalk30.gray(entry.label)}`);
80683
+ console.log(` Tools (${entry.toolCount}): ${chalk30.white(entry.toolNames.join(", "))}`);
80684
+ console.log(` Hash: ${chalk30.gray(entry.toolsHash.slice(0, 16))}...`);
80685
+ console.log(` Pinned: ${chalk30.gray(entry.pinnedAt)}`);
80414
80686
  console.log("");
80415
80687
  }
80416
80688
  if (showSource) {
80417
- console.log(chalk29.gray(` [repo] entries come from ${found.path}`));
80418
- console.log(chalk29.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
80689
+ console.log(chalk30.gray(` [repo] entries come from ${found.path}`));
80690
+ console.log(chalk30.gray(" [home] entries come from ~/.node9/mcp-pins.json\n"));
80419
80691
  }
80420
80692
  });
80421
80693
  pinSubCmd.command("promote <serverKey>").description(
@@ -80425,22 +80697,22 @@ function registerMcpPinCommand(program2) {
80425
80697
  const { repoPath, created } = promotePin(serverKey, process.cwd());
80426
80698
  if (created) {
80427
80699
  console.log(
80428
- chalk29.green(
80700
+ chalk30.green(
80429
80701
  `
80430
- \u2705 Created ${repoPath} with the promoted pin for ${chalk29.cyan(serverKey)}.`
80702
+ \u2705 Created ${repoPath} with the promoted pin for ${chalk30.cyan(serverKey)}.`
80431
80703
  )
80432
80704
  );
80433
80705
  } else {
80434
- console.log(chalk29.green(`
80435
- \u2705 Promoted ${chalk29.cyan(serverKey)} into ${repoPath}.`));
80706
+ console.log(chalk30.green(`
80707
+ \u2705 Promoted ${chalk30.cyan(serverKey)} into ${repoPath}.`));
80436
80708
  }
80437
- console.log(chalk29.gray(" Review the change and commit it:"));
80438
- console.log(chalk29.cyan(` git add ${repoPath}`));
80439
- console.log(chalk29.cyan(` git commit -m "pin ${serverKey} (node9)"`));
80709
+ console.log(chalk30.gray(" Review the change and commit it:"));
80710
+ console.log(chalk30.cyan(` git add ${repoPath}`));
80711
+ console.log(chalk30.cyan(` git commit -m "pin ${serverKey} (node9)"`));
80440
80712
  console.log("");
80441
80713
  } catch (err2) {
80442
80714
  const msg = err2 instanceof Error ? err2.message : String(err2);
80443
- console.error(chalk29.red(`
80715
+ console.error(chalk30.red(`
80444
80716
  \u274C ${msg}
80445
80717
  `));
80446
80718
  process.exit(1);
@@ -80453,36 +80725,36 @@ function registerMcpPinCommand(program2) {
80453
80725
  try {
80454
80726
  pins = readMcpPins();
80455
80727
  } catch {
80456
- console.error(chalk29.red("\n\u274C Pin file is corrupt."));
80457
- console.error(chalk29.yellow(" Run: node9 mcp pin reset\n"));
80728
+ console.error(chalk30.red("\n\u274C Pin file is corrupt."));
80729
+ console.error(chalk30.yellow(" Run: node9 mcp pin reset\n"));
80458
80730
  process.exit(1);
80459
80731
  }
80460
80732
  if (!pins.servers[serverKey]) {
80461
- console.error(chalk29.red(`
80733
+ console.error(chalk30.red(`
80462
80734
  \u274C No pin found for server key "${serverKey}"
80463
80735
  `));
80464
- console.error(`Run ${chalk29.cyan("node9 mcp pin list")} to see pinned servers.
80736
+ console.error(`Run ${chalk30.cyan("node9 mcp pin list")} to see pinned servers.
80465
80737
  `);
80466
80738
  process.exit(1);
80467
80739
  }
80468
80740
  const label3 = pins.servers[serverKey].label;
80469
80741
  removePin(serverKey);
80470
- console.log(chalk29.green(`
80471
- \u{1F513} Pin removed for ${chalk29.cyan(serverKey)}`));
80472
- console.log(chalk29.gray(` Server: ${label3}`));
80473
- console.log(chalk29.gray(" Next connection will re-pin with current tool definitions.\n"));
80742
+ console.log(chalk30.green(`
80743
+ \u{1F513} Pin removed for ${chalk30.cyan(serverKey)}`));
80744
+ console.log(chalk30.gray(` Server: ${label3}`));
80745
+ console.log(chalk30.gray(" Next connection will re-pin with current tool definitions.\n"));
80474
80746
  });
80475
80747
  pinSubCmd.command("reset").description("Clear all MCP pins (next connection to each server will re-pin)").action(() => {
80476
80748
  const result = readMcpPinsSafe();
80477
80749
  if (!result.ok && result.reason === "missing") {
80478
- console.log(chalk29.gray("\nNo pins to clear.\n"));
80750
+ console.log(chalk30.gray("\nNo pins to clear.\n"));
80479
80751
  return;
80480
80752
  }
80481
80753
  const count = result.ok ? Object.keys(result.pins.servers).length : "?";
80482
80754
  clearAllPins();
80483
- console.log(chalk29.green(`
80755
+ console.log(chalk30.green(`
80484
80756
  \u{1F513} Cleared ${count} MCP pin(s).`));
80485
- console.log(chalk29.gray(" Next connection to each server will re-pin.\n"));
80757
+ console.log(chalk30.gray(" Next connection to each server will re-pin.\n"));
80486
80758
  });
80487
80759
  pinCmd.command("forget [serverKey]").option("--stale", "Remove all stale (orphaned) servers at once").description("Remove a server pin that is no longer configured in any agent").action((serverKey, opts) => {
80488
80760
  if (opts.stale) {
@@ -80491,7 +80763,7 @@ function registerMcpPinCommand(program2) {
80491
80763
  }
80492
80764
  if (!serverKey) {
80493
80765
  console.error(
80494
- chalk29.red("\n\u274C Please provide a server key, or use --stale to remove all orphans.\n")
80766
+ chalk30.red("\n\u274C Please provide a server key, or use --stale to remove all orphans.\n")
80495
80767
  );
80496
80768
  process.exit(1);
80497
80769
  }
@@ -80499,15 +80771,15 @@ function registerMcpPinCommand(program2) {
80499
80771
  try {
80500
80772
  pins = readMcpPins();
80501
80773
  } catch {
80502
- console.error(chalk29.red("\n\u274C Pin file is corrupt."));
80503
- console.error(chalk29.yellow(" Run: node9 mcp pin reset\n"));
80774
+ console.error(chalk30.red("\n\u274C Pin file is corrupt."));
80775
+ console.error(chalk30.yellow(" Run: node9 mcp pin reset\n"));
80504
80776
  process.exit(1);
80505
80777
  }
80506
80778
  if (!pins.servers[serverKey]) {
80507
- console.error(chalk29.red(`
80779
+ console.error(chalk30.red(`
80508
80780
  \u274C No pin found for server key "${serverKey}"
80509
80781
  `));
80510
- console.error(`Run ${chalk29.cyan("node9 mcp pin list")} to see pinned servers.
80782
+ console.error(`Run ${chalk30.cyan("node9 mcp pin list")} to see pinned servers.
80511
80783
  `);
80512
80784
  process.exit(1);
80513
80785
  }
@@ -80515,10 +80787,10 @@ function registerMcpPinCommand(program2) {
80515
80787
  const liveKeys = inventoryServerKeys(inv);
80516
80788
  if (liveKeys.has(serverKey)) {
80517
80789
  const agent = "an agent config";
80518
- console.error(chalk29.red(`
80790
+ console.error(chalk30.red(`
80519
80791
  \u274C Server "${serverKey}" is still configured in ${agent}.`));
80520
80792
  console.error(
80521
- chalk29.yellow(
80793
+ chalk30.yellow(
80522
80794
  ` Remove it from the agent config first, then run: node9 mcp forget ${serverKey}
80523
80795
  `
80524
80796
  )
@@ -80527,10 +80799,10 @@ function registerMcpPinCommand(program2) {
80527
80799
  }
80528
80800
  const label3 = pins.servers[serverKey].label;
80529
80801
  removePin(serverKey);
80530
- console.log(chalk29.green(`
80531
- \u2713 Forgot server ${chalk29.cyan(serverKey)}`));
80532
- console.log(chalk29.gray(` Was: ${label3}`));
80533
- console.log(chalk29.gray(" Pin removed \u2014 server will no longer appear in the dashboard.\n"));
80802
+ console.log(chalk30.green(`
80803
+ \u2713 Forgot server ${chalk30.cyan(serverKey)}`));
80804
+ console.log(chalk30.gray(` Was: ${label3}`));
80805
+ console.log(chalk30.gray(" Pin removed \u2014 server will no longer appear in the dashboard.\n"));
80534
80806
  });
80535
80807
  }
80536
80808
  function forgetAllStale() {
@@ -80538,16 +80810,16 @@ function forgetAllStale() {
80538
80810
  try {
80539
80811
  pins = readMcpPins();
80540
80812
  } catch {
80541
- console.error(chalk29.red("\n\u274C Pin file is corrupt."));
80542
- console.error(chalk29.yellow(" Run: node9 mcp pin reset\n"));
80813
+ console.error(chalk30.red("\n\u274C Pin file is corrupt."));
80814
+ console.error(chalk30.yellow(" Run: node9 mcp pin reset\n"));
80543
80815
  process.exit(1);
80544
80816
  }
80545
80817
  const inv = inventoryMcp();
80546
80818
  const liveKeys = inventoryServerKeys(inv);
80547
80819
  if (liveKeys.size === 0) {
80548
- console.error(chalk29.red("\n\u274C No live MCP servers detected \u2014 refusing to remove every pin."));
80820
+ console.error(chalk30.red("\n\u274C No live MCP servers detected \u2014 refusing to remove every pin."));
80549
80821
  console.error(
80550
- chalk29.yellow(
80822
+ chalk30.yellow(
80551
80823
  " This usually means an agent config is missing or unreadable, not that\n every server is gone. Check your agent configs, or remove one at a\n time: node9 mcp forget <key>\n"
80552
80824
  )
80553
80825
  );
@@ -80555,118 +80827,118 @@ function forgetAllStale() {
80555
80827
  }
80556
80828
  const stale = Object.entries(pins.servers).filter(([sk]) => !liveKeys.has(sk));
80557
80829
  if (stale.length === 0) {
80558
- console.log(chalk29.gray("\nNo stale servers to remove.\n"));
80830
+ console.log(chalk30.gray("\nNo stale servers to remove.\n"));
80559
80831
  return;
80560
80832
  }
80561
80833
  for (const [sk, pin] of stale) {
80562
80834
  delete pins.servers[sk];
80563
- console.log(chalk29.green(` \u2713 ${chalk29.cyan(sk)} ${chalk29.gray(pin.label)}`));
80835
+ console.log(chalk30.green(` \u2713 ${chalk30.cyan(sk)} ${chalk30.gray(pin.label)}`));
80564
80836
  }
80565
80837
  writeMcpPins(pins);
80566
- console.log(chalk29.green(`
80838
+ console.log(chalk30.green(`
80567
80839
  Removed ${stale.length} stale server(s).
80568
80840
  `));
80569
80841
  }
80570
80842
 
80571
80843
  // src/cli/commands/sync.ts
80572
80844
  init_sync();
80573
- import chalk30 from "chalk";
80845
+ import chalk31 from "chalk";
80574
80846
  function registerSyncCommand(program2) {
80575
80847
  const policy = program2.command("policy").description("Manage cloud policy rules");
80576
80848
  policy.command("push").description("Push this machine's effective policy to the node9 dashboard").action(async () => {
80577
- process.stdout.write(chalk30.cyan("Pushing policy to the dashboard\u2026"));
80849
+ process.stdout.write(chalk31.cyan("Pushing policy to the dashboard\u2026"));
80578
80850
  const result = await runPolicyPush();
80579
80851
  process.stdout.write("\n");
80580
80852
  if (!result.ok) {
80581
- console.error(chalk30.red(`\u2717 ${result.reason}`));
80853
+ console.error(chalk31.red(`\u2717 ${result.reason}`));
80582
80854
  process.exit(1);
80583
80855
  }
80584
- console.log(chalk30.green("\u2713 Policy mirrored to the dashboard"));
80585
- console.log(chalk30.gray(" See it under Security Policy \u2192 Machines"));
80856
+ console.log(chalk31.green("\u2713 Policy mirrored to the dashboard"));
80857
+ console.log(chalk31.gray(" See it under Security Policy \u2192 Machines"));
80586
80858
  });
80587
80859
  policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
80588
- process.stdout.write(chalk30.cyan("Syncing cloud policy rules\u2026"));
80860
+ process.stdout.write(chalk31.cyan("Syncing cloud policy rules\u2026"));
80589
80861
  const result = await runCloudSync();
80590
80862
  process.stdout.write("\n");
80591
80863
  if (!result.ok) {
80592
- console.error(chalk30.red(`\u2717 ${result.reason}`));
80864
+ console.error(chalk31.red(`\u2717 ${result.reason}`));
80593
80865
  process.exit(1);
80594
80866
  }
80595
80867
  if (result.unchanged) {
80596
80868
  console.log(
80597
- chalk30.green(
80869
+ chalk31.green(
80598
80870
  `\u2713 Already up to date \u2014 ${result.rules} rule${result.rules === 1 ? "" : "s"} cached`
80599
80871
  )
80600
80872
  );
80601
- console.log(chalk30.gray(` Cached at: ${result.fetchedAt}`));
80602
- console.log(chalk30.gray(` Server returned 304 (no changes since last sync)`));
80873
+ console.log(chalk31.gray(` Cached at: ${result.fetchedAt}`));
80874
+ console.log(chalk31.gray(` Server returned 304 (no changes since last sync)`));
80603
80875
  } else {
80604
80876
  console.log(
80605
- chalk30.green(`\u2713 Synced ${result.rules} rule${result.rules === 1 ? "" : "s"} from cloud`)
80877
+ chalk31.green(`\u2713 Synced ${result.rules} rule${result.rules === 1 ? "" : "s"} from cloud`)
80606
80878
  );
80607
- console.log(chalk30.gray(` Cached at: ${result.fetchedAt}`));
80608
- console.log(chalk30.gray(` File: ~/.node9/rules-cache.json`));
80879
+ console.log(chalk31.gray(` Cached at: ${result.fetchedAt}`));
80880
+ console.log(chalk31.gray(` File: ~/.node9/rules-cache.json`));
80609
80881
  }
80610
80882
  });
80611
80883
  policy.command("show").description("List all cloud policy rules in the local cache").action(() => {
80612
80884
  const status = getCloudSyncStatus();
80613
80885
  if (!status.cached) {
80614
- console.log(chalk30.yellow("\n No cloud rules cached \u2014 run: node9 policy sync\n"));
80886
+ console.log(chalk31.yellow("\n No cloud rules cached \u2014 run: node9 policy sync\n"));
80615
80887
  return;
80616
80888
  }
80617
80889
  const rules = getCloudRules() ?? [];
80618
80890
  const age = Math.round((Date.now() - new Date(status.fetchedAt).getTime()) / 6e4);
80619
80891
  console.log(
80620
- chalk30.bold(`
80621
- Cloud policy rules`) + chalk30.gray(
80892
+ chalk31.bold(`
80893
+ Cloud policy rules`) + chalk31.gray(
80622
80894
  ` (${rules.length} rule${rules.length === 1 ? "" : "s"}, synced ${age}m ago)
80623
80895
  `
80624
80896
  )
80625
80897
  );
80626
80898
  if (rules.length === 0) {
80627
- console.log(chalk30.gray(" No rules defined in cloud policy.\n"));
80899
+ console.log(chalk31.gray(" No rules defined in cloud policy.\n"));
80628
80900
  return;
80629
80901
  }
80630
80902
  for (const rule of rules) {
80631
80903
  const r = rule;
80632
- const verdictColor = r.verdict === "block" ? chalk30.red : r.verdict === "allow" ? chalk30.green : chalk30.yellow;
80904
+ const verdictColor = r.verdict === "block" ? chalk31.red : r.verdict === "allow" ? chalk31.green : chalk31.yellow;
80633
80905
  console.log(
80634
80906
  ` ${verdictColor(
80635
80907
  String(r.verdict ?? "unknown").toUpperCase().padEnd(6)
80636
- )} ${chalk30.white(String(r.name ?? "(unnamed)"))}`
80908
+ )} ${chalk31.white(String(r.name ?? "(unnamed)"))}`
80637
80909
  );
80638
- if (r.reason) console.log(chalk30.gray(` ${String(r.reason)}`));
80910
+ if (r.reason) console.log(chalk31.gray(` ${String(r.reason)}`));
80639
80911
  }
80640
80912
  console.log("");
80641
80913
  });
80642
80914
  policy.command("status").description("Show current cloud policy cache status").action(() => {
80643
80915
  const s = getCloudSyncStatus();
80644
80916
  if (!s.cached) {
80645
- console.log(chalk30.yellow("\n No cache yet \u2014 run: node9 policy sync\n"));
80917
+ console.log(chalk31.yellow("\n No cache yet \u2014 run: node9 policy sync\n"));
80646
80918
  return;
80647
80919
  }
80648
80920
  const age = Math.round((Date.now() - new Date(s.fetchedAt).getTime()) / 6e4);
80649
80921
  console.log(`
80650
- Rules : ${chalk30.green(String(s.rules))} cloud rules loaded`);
80922
+ Rules : ${chalk31.green(String(s.rules))} cloud rules loaded`);
80651
80923
  console.log(
80652
- ` Synced : ${chalk30.gray(`${age} minute${age === 1 ? "" : "s"} ago`)} (${s.fetchedAt})`
80924
+ ` Synced : ${chalk31.gray(`${age} minute${age === 1 ? "" : "s"} ago`)} (${s.fetchedAt})`
80653
80925
  );
80654
80926
  if (s.workspaceId) {
80655
- console.log(` Workspace: ${chalk30.gray(s.workspaceId)}`);
80927
+ console.log(` Workspace: ${chalk31.gray(s.workspaceId)}`);
80656
80928
  }
80657
80929
  if (s.panicMode) {
80658
80930
  console.log(
80659
- ` ${chalk30.red.bold("\u{1F6A8} Panic mode : ON")} ` + chalk30.dim("(every review-verdict becomes block)")
80931
+ ` ${chalk31.red.bold("\u{1F6A8} Panic mode : ON")} ` + chalk31.dim("(every review-verdict becomes block)")
80660
80932
  );
80661
80933
  }
80662
80934
  if (s.shadowMode) {
80663
80935
  console.log(
80664
- ` ${chalk30.yellow.bold("\u{1F441} Shadow mode : ON")} ` + chalk30.dim("(blocks become would-block log entries)")
80936
+ ` ${chalk31.yellow.bold("\u{1F441} Shadow mode : ON")} ` + chalk31.dim("(blocks become would-block log entries)")
80665
80937
  );
80666
80938
  }
80667
80939
  if (s.syncIntervalHours) {
80668
80940
  console.log(
80669
- chalk30.gray(
80941
+ chalk31.gray(
80670
80942
  ` Polling : every ${s.syncIntervalHours} hour${s.syncIntervalHours === 1 ? "" : "s"}`
80671
80943
  )
80672
80944
  );
@@ -80677,7 +80949,7 @@ function registerSyncCommand(program2) {
80677
80949
 
80678
80950
  // src/cli/commands/agents.ts
80679
80951
  init_setup();
80680
- import chalk31 from "chalk";
80952
+ import chalk32 from "chalk";
80681
80953
  var SETUP_FN = {
80682
80954
  claude: setupClaude,
80683
80955
  gemini: setupGemini,
@@ -80720,30 +80992,30 @@ function registerAgentsCommand(program2) {
80720
80992
  console.log(` ${"Agent".padEnd(14)}${"Installed".padEnd(11)}${"Wired".padEnd(8)}Mode`);
80721
80993
  console.log(" " + "\u2500".repeat(44));
80722
80994
  for (const s of statuses) {
80723
- const installed = s.installed ? chalk31.green("\u2713") : chalk31.gray("\u2717");
80724
- const wired = !s.installed ? chalk31.gray("\u2014") : s.wired ? chalk31.green("\u2713") : chalk31.yellow("\u2717");
80725
- const mode = s.mode ? chalk31.gray(s.mode) : chalk31.gray("\u2014");
80726
- const hint = s.installed && !s.wired ? chalk31.gray(` \u2190 node9 agents add ${s.name}`) : "";
80995
+ const installed = s.installed ? chalk32.green("\u2713") : chalk32.gray("\u2717");
80996
+ const wired = !s.installed ? chalk32.gray("\u2014") : s.wired ? chalk32.green("\u2713") : chalk32.yellow("\u2717");
80997
+ const mode = s.mode ? chalk32.gray(s.mode) : chalk32.gray("\u2014");
80998
+ const hint = s.installed && !s.wired ? chalk32.gray(` \u2190 node9 agents add ${s.name}`) : "";
80727
80999
  console.log(` ${s.label.padEnd(14)}${installed} ${wired} ${mode}${hint}`);
80728
81000
  }
80729
81001
  console.log("");
80730
81002
  if (!anyInstalled) {
80731
81003
  console.log(
80732
- chalk31.gray(" No AI agents detected. Install Claude Code, Gemini CLI, Cursor,\n") + chalk31.gray(" Windsurf, VSCode, or Codex then run: node9 agents list\n")
81004
+ chalk32.gray(" No AI agents detected. Install Claude Code, Gemini CLI, Cursor,\n") + chalk32.gray(" Windsurf, VSCode, or Codex then run: node9 agents list\n")
80733
81005
  );
80734
81006
  return;
80735
81007
  }
80736
81008
  const unwired = statuses.filter((s) => s.installed && !s.wired);
80737
81009
  if (unwired.length > 0) {
80738
81010
  console.log(
80739
- chalk31.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + chalk31.white(`node9 agents add ${unwired[0].name}`) + "\n"
81011
+ chalk32.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + chalk32.white(`node9 agents add ${unwired[0].name}`) + "\n"
80740
81012
  );
80741
81013
  }
80742
81014
  if (statuses.some((s) => s.name === "gemini" && s.installed)) {
80743
81015
  console.log(
80744
- chalk31.yellow(
81016
+ chalk32.yellow(
80745
81017
  " \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n"
80746
- ) + chalk31.gray(" Migrate to Antigravity: ") + chalk31.white("node9 agents add antigravity") + "\n"
81018
+ ) + chalk32.gray(" Migrate to Antigravity: ") + chalk32.white("node9 agents add antigravity") + "\n"
80747
81019
  );
80748
81020
  }
80749
81021
  });
@@ -80751,7 +81023,7 @@ function registerAgentsCommand(program2) {
80751
81023
  const name = resolveAgentName(agent);
80752
81024
  const fn = SETUP_FN[name];
80753
81025
  if (!fn) {
80754
- console.error(chalk31.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
81026
+ console.error(chalk32.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
80755
81027
  process.exit(1);
80756
81028
  }
80757
81029
  await fn();
@@ -80760,14 +81032,14 @@ function registerAgentsCommand(program2) {
80760
81032
  const name = resolveAgentName(agent);
80761
81033
  const fn = TEARDOWN_FN[name];
80762
81034
  if (!fn) {
80763
- console.error(chalk31.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
81035
+ console.error(chalk32.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
80764
81036
  process.exit(1);
80765
81037
  }
80766
- console.log(chalk31.cyan(`
81038
+ console.log(chalk32.cyan(`
80767
81039
  \u{1F6E1}\uFE0F Node9: removing from ${name}...
80768
81040
  `));
80769
81041
  fn();
80770
- console.log(chalk31.gray("\n Restart the agent for changes to take effect."));
81042
+ console.log(chalk32.gray("\n Restart the agent for changes to take effect."));
80771
81043
  });
80772
81044
  }
80773
81045
 
@@ -80776,21 +81048,21 @@ init_scan();
80776
81048
 
80777
81049
  // src/cli/commands/posture.ts
80778
81050
  init_posture();
80779
- import chalk33 from "chalk";
81051
+ import chalk34 from "chalk";
80780
81052
 
80781
81053
  // src/posture/render.ts
80782
81054
  init_score();
80783
- import chalk32 from "chalk";
81055
+ import chalk33 from "chalk";
80784
81056
  var ICON = {
80785
- critical: chalk32.red("\u274C"),
80786
- high: chalk32.red("\u274C"),
80787
- medium: chalk32.yellow("\u26A0\uFE0F "),
80788
- advisory: chalk32.gray("\u26A0\uFE0F ")
81057
+ critical: chalk33.red("\u274C"),
81058
+ high: chalk33.red("\u274C"),
81059
+ medium: chalk33.yellow("\u26A0\uFE0F "),
81060
+ advisory: chalk33.gray("\u26A0\uFE0F ")
80789
81061
  };
80790
81062
  var TIER_LABEL = {
80791
- good: chalk32.green("Good"),
80792
- "at-risk": chalk32.yellow("At risk"),
80793
- critical: chalk32.red("Critical")
81063
+ good: chalk33.green("Good"),
81064
+ "at-risk": chalk33.yellow("At risk"),
81065
+ critical: chalk33.red("Critical")
80794
81066
  };
80795
81067
  function wrap(text, width) {
80796
81068
  const out = [];
@@ -80808,7 +81080,7 @@ function wrap(text, width) {
80808
81080
  }
80809
81081
  var LABEL_WIDTH = 14;
80810
81082
  function label2(category) {
80811
- return chalk32.bold(category.padEnd(Math.max(LABEL_WIDTH, category.length + 1)));
81083
+ return chalk33.bold(category.padEnd(Math.max(LABEL_WIDTH, category.length + 1)));
80812
81084
  }
80813
81085
  function guardedWhat(f) {
80814
81086
  if (f.detail.length === 0) return "this";
@@ -80818,31 +81090,31 @@ function guardedWhat(f) {
80818
81090
  }
80819
81091
  function renderFinding(f, showWeight = false, displayLabel = f.category) {
80820
81092
  const lines = [];
80821
- const wt = showWeight && f.scoreWeight ? chalk32.cyan.bold(`+${f.scoreWeight} `) : "";
81093
+ const wt = showWeight && f.scoreWeight ? chalk33.cyan.bold(`+${f.scoreWeight} `) : "";
80822
81094
  lines.push(` ${ICON[f.severity]} ${label2(displayLabel)}${wt}${f.title}`);
80823
81095
  const indent = " ".repeat(2 + 3 + LABEL_WIDTH);
80824
81096
  const width = 80 - indent.length;
80825
81097
  for (const s of [f.what, f.why, f.who]) {
80826
- if (s) for (const l of wrap(s, width)) lines.push(indent + chalk32.gray(l));
81098
+ if (s) for (const l of wrap(s, width)) lines.push(indent + chalk33.gray(l));
80827
81099
  }
80828
- for (const d of f.detail) lines.push(indent + chalk32.gray(d));
81100
+ for (const d of f.detail) lines.push(indent + chalk33.gray(d));
80829
81101
  if (f.fix) {
80830
81102
  let first = true;
80831
81103
  for (const seg of f.fix.split("\n")) {
80832
81104
  for (const l of wrap(seg, width - 2)) {
80833
- lines.push(indent + chalk32.cyan(first ? "\u2192 " + l : " " + l));
81105
+ lines.push(indent + chalk33.cyan(first ? "\u2192 " + l : " " + l));
80834
81106
  first = false;
80835
81107
  }
80836
81108
  }
80837
81109
  }
80838
81110
  const tradeoff = [
80839
- [f.gain, "gain: ", chalk32.green],
80840
- [f.cost, "cost: ", chalk32.yellow]
81111
+ [f.gain, "gain: ", chalk33.green],
81112
+ [f.cost, "cost: ", chalk33.yellow]
80841
81113
  ];
80842
81114
  for (const [text, lbl, color2] of tradeoff) {
80843
81115
  if (!text) continue;
80844
81116
  wrap(text, width - 6).forEach((l, i) => {
80845
- lines.push(indent + (i === 0 ? color2(lbl) : " ") + chalk32.gray(l));
81117
+ lines.push(indent + (i === 0 ? color2(lbl) : " ") + chalk33.gray(l));
80846
81118
  });
80847
81119
  }
80848
81120
  return lines;
@@ -80852,7 +81124,7 @@ function renderPosture(result) {
80852
81124
  const tier = TIER_LABEL[result.tier];
80853
81125
  lines.push("");
80854
81126
  lines.push(
80855
- chalk32.cyan.bold(`\u{1F6E1}\uFE0F Node9 Posture`) + chalk32.gray(` \u2014 ${result.agent}`) + ` ${chalk32.bold(`Score: ${result.score}/100`)} (${tier})`
81127
+ chalk33.cyan.bold(`\u{1F6E1}\uFE0F Node9 Posture`) + chalk33.gray(` \u2014 ${result.agent}`) + ` ${chalk33.bold(`Score: ${result.score}/100`)} (${tier})`
80856
81128
  );
80857
81129
  const headroom = openHeadroom(result.findings);
80858
81130
  if (headroom > 0) {
@@ -80860,15 +81132,15 @@ function renderPosture(result) {
80860
81132
  (f) => f.coverage?.state !== "covered" && f.coverage?.state !== "cant-fix" && !f.scoreWeight && f.severity !== "advisory"
80861
81133
  ).length;
80862
81134
  const note = openExposures > 0 ? `Of the gap to 100: ${headroom} pts is optional hardening you can choose to turn on (the \u{1F512} tier below, each at some cost to flexibility); the rest is the open findings \u2014 the flagged rows below. Fix those first.` : `${headroom} pts of headroom \u2014 optional hardening you can choose to turn on (the \u{1F512} tier below), each at some cost to flexibility.`;
80863
- for (const l of wrap(note, 76)) lines.push(" " + chalk32.gray(l));
81135
+ for (const l of wrap(note, 76)) lines.push(" " + chalk33.gray(l));
80864
81136
  }
80865
81137
  lines.push("");
80866
81138
  if (result.headline) {
80867
81139
  const indent = " ";
80868
- lines.push(` ${chalk32.red.bold("\u{1F525} Biggest risk")}`);
80869
- for (const l of wrap(result.headline.risk, 74)) lines.push(indent + chalk32.white(l));
81140
+ lines.push(` ${chalk33.red.bold("\u{1F525} Biggest risk")}`);
81141
+ for (const l of wrap(result.headline.risk, 74)) lines.push(indent + chalk33.white(l));
80870
81142
  const action = wrap(`Do this first: ${result.headline.action}`, 72);
80871
- action.forEach((l, i) => lines.push(indent + chalk32.cyan(i === 0 ? "\u2192 " + l : " " + l)));
81143
+ action.forEach((l, i) => lines.push(indent + chalk33.cyan(i === 0 ? "\u2192 " + l : " " + l)));
80872
81144
  lines.push("");
80873
81145
  }
80874
81146
  const covered = result.findings.filter((f) => f.coverage?.state === "covered");
@@ -80878,13 +81150,13 @@ function renderPosture(result) {
80878
81150
  );
80879
81151
  const openLabel = (f) => collision.has(f.category) ? `${f.category} (exposed)` : f.category;
80880
81152
  if (covered.length > 0) {
80881
- lines.push(" " + chalk32.green("\u{1F7E2} ON NOW \u2014 node9 is enforcing these (your floor)"));
81153
+ lines.push(" " + chalk33.green("\u{1F7E2} ON NOW \u2014 node9 is enforcing these (your floor)"));
80882
81154
  for (const f of covered) {
80883
81155
  const gated = f.coverage?.level === "review" ? "approval-gating" : "blocking";
80884
81156
  const via = f.coverage?.via ?? "node9";
80885
81157
  const lbl = collision.has(f.category) ? `${f.category} (guarded)` : f.category;
80886
81158
  lines.push(
80887
- ` ${chalk32.green("\u2705")} ${label2(lbl)}${chalk32.gray(`${via} is ${gated} ${guardedWhat(f)}`)}`
81159
+ ` ${chalk33.green("\u2705")} ${label2(lbl)}${chalk33.gray(`${via} is ${gated} ${guardedWhat(f)}`)}`
80888
81160
  );
80889
81161
  }
80890
81162
  lines.push("");
@@ -80893,24 +81165,24 @@ function renderPosture(result) {
80893
81165
  const reduceOpen = open.filter((f) => f.owner !== "node9" && f.node9Reduces);
80894
81166
  const osOpen = open.filter((f) => f.owner !== "node9" && !f.node9Reduces);
80895
81167
  if (node9Open.length > 0) {
80896
- lines.push(" " + chalk32.cyan.bold("\u{1F527} node9 can fix these \u2014 run the command"));
81168
+ lines.push(" " + chalk33.cyan.bold("\u{1F527} node9 can fix these \u2014 run the command"));
80897
81169
  for (const f of node9Open) lines.push(...renderFinding(f, true, openLabel(f)));
80898
81170
  }
80899
81171
  if (reduceOpen.length > 0) {
80900
81172
  if (node9Open.length > 0) lines.push("");
80901
- lines.push(" " + chalk32.yellow.bold("\u{1F512} AVAILABLE \u2014 turn on to harden (each has a tradeoff)"));
81173
+ lines.push(" " + chalk33.yellow.bold("\u{1F512} AVAILABLE \u2014 turn on to harden (each has a tradeoff)"));
80902
81174
  for (const f of reduceOpen) lines.push(...renderFinding(f, true, openLabel(f)));
80903
81175
  }
80904
81176
  if (osOpen.length > 0) {
80905
81177
  if (node9Open.length > 0 || reduceOpen.length > 0) lines.push("");
80906
- lines.push(" " + chalk32.bold("\u{1F9F1} YOUR PART \u2014 node9 can't fix these (OS-level)"));
81178
+ lines.push(" " + chalk33.bold("\u{1F9F1} YOUR PART \u2014 node9 can't fix these (OS-level)"));
80907
81179
  for (const f of osOpen) lines.push(...renderFinding(f, false, openLabel(f)));
80908
81180
  }
80909
81181
  for (const cat of result.passedCategories) {
80910
- lines.push(` ${chalk32.green("\u2705")} ${label2(cat)}${chalk32.gray("no issues found")}`);
81182
+ lines.push(` ${chalk33.green("\u2705")} ${label2(cat)}${chalk33.gray("no issues found")}`);
80911
81183
  }
80912
81184
  for (const cat of result.erroredCategories) {
80913
- lines.push(` ${chalk32.gray("\u2022")} ${label2(cat)}${chalk32.gray("could not be checked")}`);
81185
+ lines.push(` ${chalk33.gray("\u2022")} ${label2(cat)}${chalk33.gray("could not be checked")}`);
80914
81186
  }
80915
81187
  lines.push("");
80916
81188
  const crit = open.filter((f) => f.severity === "critical").length;
@@ -80918,16 +81190,16 @@ function renderPosture(result) {
80918
81190
  const med = open.filter((f) => f.severity === "medium").length;
80919
81191
  const adv = open.filter((f) => f.severity === "advisory").length;
80920
81192
  const parts = [];
80921
- if (crit) parts.push(chalk32.red(`${crit} critical`));
80922
- if (high) parts.push(chalk32.red(`${high} high`));
80923
- if (med) parts.push(chalk32.yellow(`${med} medium`));
80924
- if (adv) parts.push(chalk32.gray(`${adv} advisory`));
80925
- const summary = parts.length ? parts.join(" \xB7 ") : chalk32.green("no findings");
81193
+ if (crit) parts.push(chalk33.red(`${crit} critical`));
81194
+ if (high) parts.push(chalk33.red(`${high} high`));
81195
+ if (med) parts.push(chalk33.yellow(`${med} medium`));
81196
+ if (adv) parts.push(chalk33.gray(`${adv} advisory`));
81197
+ const summary = parts.length ? parts.join(" \xB7 ") : chalk33.green("no findings");
80926
81198
  lines.push(` ${summary}`);
80927
81199
  lines.push("");
80928
- lines.push(" " + chalk32.bold("Track this across your fleet & keep it green:"));
81200
+ lines.push(" " + chalk33.bold("Track this across your fleet & keep it green:"));
80929
81201
  lines.push(
80930
- " " + chalk32.dim("\u2192 ") + chalk32.cyan.underline("https://node9.ai/auth/signup?ref=cli_posture")
81202
+ " " + chalk33.dim("\u2192 ") + chalk33.cyan.underline("https://node9.ai/auth/signup?ref=cli_posture")
80931
81203
  );
80932
81204
  lines.push("");
80933
81205
  return lines.join("\n");
@@ -80947,11 +81219,11 @@ function registerPostureCommand(program2) {
80947
81219
  if (opts.ship) {
80948
81220
  const creds = readCredentials();
80949
81221
  if (!creds) {
80950
- console.error(chalk33.gray(" Run `node9 login` to ship this to your dashboard."));
81222
+ console.error(chalk34.gray(" Run `node9 login` to ship this to your dashboard."));
80951
81223
  } else {
80952
81224
  const ok2 = await shipPosture(result, creds);
80953
81225
  console.error(
80954
- ok2 ? chalk33.gray(" \u2713 Shipped to your node9 dashboard.") : chalk33.gray(" Could not reach the dashboard \u2014 saved locally only.")
81226
+ ok2 ? chalk34.gray(" \u2713 Shipped to your node9 dashboard.") : chalk34.gray(" Could not reach the dashboard \u2014 saved locally only.")
80955
81227
  );
80956
81228
  }
80957
81229
  }
@@ -80960,7 +81232,7 @@ function registerPostureCommand(program2) {
80960
81232
  }
80961
81233
 
80962
81234
  // src/cli/commands/scan-repo.ts
80963
- import chalk35 from "chalk";
81235
+ import chalk36 from "chalk";
80964
81236
 
80965
81237
  // src/ci-check/fetch.ts
80966
81238
  var import_undici = __toESM(require_undici());
@@ -86472,7 +86744,7 @@ async function scanRepo(input, onProgress) {
86472
86744
  }
86473
86745
 
86474
86746
  // src/ci-check/render.ts
86475
- import chalk34 from "chalk";
86747
+ import chalk35 from "chalk";
86476
86748
  var listed = (fs109) => fs109.filter((f) => f.tier !== "review");
86477
86749
  function excerpt(f) {
86478
86750
  const m = /`([^`]+)`/.exec(f.signals[0] ?? "");
@@ -86517,23 +86789,23 @@ function reviewTerminal(findings) {
86517
86789
  const L = [];
86518
86790
  if (items) {
86519
86791
  L.push(
86520
- chalk34.bold(`\u{1F50D} ${items} ${items === 1 ? "item" : "items"} worth a look (not counted)`) + chalk34.gray(": risky-looking text in a context that is usually harmless. Please confirm.")
86792
+ chalk35.bold(`\u{1F50D} ${items} ${items === 1 ? "item" : "items"} worth a look (not counted)`) + chalk35.gray(": risky-looking text in a context that is usually harmless. Please confirm.")
86521
86793
  );
86522
86794
  const files = [...byFile.keys()];
86523
86795
  for (const file2 of files.slice(0, REVIEW_FILES)) {
86524
86796
  const fs109 = byFile.get(file2);
86525
- L.push(chalk34.gray(` ${file2}`));
86797
+ L.push(chalk35.gray(` ${file2}`));
86526
86798
  for (const f of fs109.slice(0, REVIEW_LINES_PER_FILE))
86527
86799
  L.push(` \u2022 ${f.line ? `line ${f.line}: ` : ""}${excerpt(f)}`);
86528
86800
  if (fs109.length > REVIEW_LINES_PER_FILE)
86529
- L.push(chalk34.gray(` and ${fs109.length - REVIEW_LINES_PER_FILE} similar lines`));
86801
+ L.push(chalk35.gray(` and ${fs109.length - REVIEW_LINES_PER_FILE} similar lines`));
86530
86802
  }
86531
86803
  const more = files.length - REVIEW_FILES;
86532
- if (more > 0) L.push(chalk34.gray(` and ${more} more file${more === 1 ? "" : "s"}`));
86804
+ if (more > 0) L.push(chalk35.gray(` and ${more} more file${more === 1 ? "" : "s"}`));
86533
86805
  }
86534
86806
  if (grants)
86535
86807
  L.push(
86536
- chalk34.gray(
86808
+ chalk35.gray(
86537
86809
  ` ${grants} skill${grants === 1 ? " is" : "s are"} granted broad tools (Bash, Write or Edit); usually what the skill needs.`
86538
86810
  )
86539
86811
  );
@@ -86547,45 +86819,45 @@ var ICON2 = {
86547
86819
  advisory: "\u{1F7E2}"
86548
86820
  };
86549
86821
  var COLOR = {
86550
- critical: chalk34.red.bold,
86551
- high: chalk34.red,
86552
- medium: chalk34.yellow,
86553
- advisory: chalk34.gray
86822
+ critical: chalk35.red.bold,
86823
+ high: chalk35.red,
86824
+ medium: chalk35.yellow,
86825
+ advisory: chalk35.gray
86554
86826
  };
86555
86827
  var ACTION_URL = "https://github.com/marketplace/actions/node9-agent-security?ref=cli_scan_repo";
86556
86828
  function renderCta(res) {
86557
86829
  const L = [];
86558
- L.push(chalk34.dim(" " + "\u2500".repeat(63)));
86830
+ L.push(chalk35.dim(" " + "\u2500".repeat(63)));
86559
86831
  if (res.worst === "critical" || res.worst === "high") {
86560
86832
  const n = res.findings.filter(
86561
86833
  (f) => isAlert(f) && !f.suppressed && (f.severity === "critical" || f.severity === "high")
86562
86834
  ).length;
86563
86835
  L.push(
86564
- " " + chalk34.red.bold(
86836
+ " " + chalk35.red.bold(
86565
86837
  `\u{1F534} ${n} ${n === 1 ? "issue" : "issues"} to fix, then stop the next at the PR.`
86566
86838
  )
86567
86839
  );
86568
86840
  L.push("");
86569
- L.push(" " + chalk34.bold("Catch this class of issue on every PR, automatically:"));
86841
+ L.push(" " + chalk35.bold("Catch this class of issue on every PR, automatically:"));
86570
86842
  } else if (res.worst) {
86571
- L.push(" " + chalk34.yellow("\u{1F7E1} Review the findings above, then keep it covered:"));
86843
+ L.push(" " + chalk35.yellow("\u{1F7E1} Review the findings above, then keep it covered:"));
86572
86844
  L.push("");
86573
- L.push(" " + chalk34.bold("Check every PR for agent-CI risk:"));
86845
+ L.push(" " + chalk35.bold("Check every PR for agent-CI risk:"));
86574
86846
  } else if (res.incomplete) {
86575
- L.push(" " + chalk34.yellow.bold("\u26A0\uFE0F Incomplete: not a clean bill of health."));
86847
+ L.push(" " + chalk35.yellow.bold("\u26A0\uFE0F Incomplete: not a clean bill of health."));
86576
86848
  L.push("");
86577
- L.push(" " + chalk34.bold("Get a complete check on every PR (CI reads the tree directly):"));
86849
+ L.push(" " + chalk35.bold("Get a complete check on every PR (CI reads the tree directly):"));
86578
86850
  } else {
86579
86851
  L.push(
86580
- " " + chalk34.green(
86852
+ " " + chalk35.green(
86581
86853
  groupReview(res.findings).byFile.size ? "\u2705 No alerts. Check the items worth a look above." : "\u2705 Agent CI is well-configured: 0 unmitigated issues."
86582
86854
  )
86583
86855
  );
86584
86856
  L.push("");
86585
- L.push(" " + chalk34.bold("Keep it green as you add agent workflows. Check every PR:"));
86857
+ L.push(" " + chalk35.bold("Keep it green as you add agent workflows. Check every PR:"));
86586
86858
  }
86587
- L.push(" " + chalk34.dim("\u2192 ") + chalk34.cyan.underline(ACTION_URL));
86588
- L.push(" " + chalk34.gray(" zero setup \xB7 no token \xB7 runs in your CI"));
86859
+ L.push(" " + chalk35.dim("\u2192 ") + chalk35.cyan.underline(ACTION_URL));
86860
+ L.push(" " + chalk35.gray(" zero setup \xB7 no token \xB7 runs in your CI"));
86589
86861
  return L;
86590
86862
  }
86591
86863
  function ownedHint(source) {
@@ -86625,21 +86897,21 @@ function renderScan(res, diff) {
86625
86897
  const L = [];
86626
86898
  const shown = listed(res.findings);
86627
86899
  const n = shown.length;
86628
- const head = res.worst === "critical" || res.worst === "high" ? chalk34.red.bold("\u26A0\uFE0F agent-security risk found") : res.worst ? chalk34.yellow("agent-security notes") : res.incomplete ? chalk34.yellow.bold("\u26A0\uFE0F INCOMPLETE: could not read all files") : groupReview(res.findings).byFile.size ? chalk34.green("\u2705 agent-security: no alerts (items worth a look below)") : chalk34.green("\u2705 agent-security: clean");
86629
- L.push(`\u{1F6E1}\uFE0F ${chalk34.bold("node9 scan-repo")} \xB7 ${res.source} \xB7 ${head}`);
86900
+ const head = res.worst === "critical" || res.worst === "high" ? chalk35.red.bold("\u26A0\uFE0F agent-security risk found") : res.worst ? chalk35.yellow("agent-security notes") : res.incomplete ? chalk35.yellow.bold("\u26A0\uFE0F INCOMPLETE: could not read all files") : groupReview(res.findings).byFile.size ? chalk35.green("\u2705 agent-security: no alerts (items worth a look below)") : chalk35.green("\u2705 agent-security: clean");
86901
+ L.push(`\u{1F6E1}\uFE0F ${chalk35.bold("node9 scan-repo")} \xB7 ${res.source} \xB7 ${head}`);
86630
86902
  L.push(
86631
- chalk34.gray(
86903
+ chalk35.gray(
86632
86904
  ` inspected ${res.inspected.length} config file(s), ${n} finding(s)` + (res.suppressedCount ? ` \xB7 ${res.suppressedCount} suppressed by .node9-ignore.json` : "")
86633
86905
  )
86634
86906
  );
86635
86907
  if (diff) {
86636
86908
  const introduced = listed(diff.added).length + diff.escalated.length;
86637
86909
  L.push(
86638
- diff.base !== "ok" ? chalk34.yellow.bold(
86910
+ diff.base !== "ok" ? chalk35.yellow.bold(
86639
86911
  ` \u26A0\uFE0F base ${diff.base === "did-not-run" ? "could not be read" : "scan was incomplete"}, so it cannot say what is new; showing everything`
86640
- ) : introduced > 0 ? chalk34.red.bold(
86912
+ ) : introduced > 0 ? chalk35.red.bold(
86641
86913
  ` \u26A0\uFE0F this change introduced ${introduced} finding(s)` + (diff.escalated.length ? ` (${diff.escalated.length} by widening an existing one)` : "")
86642
- ) : chalk34.green(
86914
+ ) : chalk35.green(
86643
86915
  ` \u2705 this change introduced nothing` + (diff.unchanged.length ? ` (${diff.unchanged.length} pre-existing)` : "") + (diff.removed.length ? `, and fixed ${diff.removed.length}` : "")
86644
86916
  )
86645
86917
  );
@@ -86647,7 +86919,7 @@ function renderScan(res, diff) {
86647
86919
  if (res.incomplete) {
86648
86920
  const rateLimited = res.notes.some((nt) => /rate limit/i.test(nt));
86649
86921
  L.push(
86650
- chalk34.yellow.bold(
86922
+ chalk35.yellow.bold(
86651
86923
  rateLimited ? " \u26A0\uFE0F Rate-limited: some files were unread. NOT a clean bill of health; set GITHUB_TOKEN (or run `gh auth login`) and re-run." : " \u26A0\uFE0F A network error left some files unread. NOT a clean bill of health; re-run."
86652
86924
  )
86653
86925
  );
@@ -86656,36 +86928,36 @@ function renderScan(res, diff) {
86656
86928
  for (const f of shown) {
86657
86929
  const ex = f.explain;
86658
86930
  L.push(
86659
- `${ICON2[f.severity]} ${COLOR[f.severity](f.severity.toUpperCase())} ${chalk34.bold(ex ? ex.headline : f.title)}`
86931
+ `${ICON2[f.severity]} ${COLOR[f.severity](f.severity.toUpperCase())} ${chalk35.bold(ex ? ex.headline : f.title)}`
86660
86932
  );
86661
- L.push(chalk34.gray(` ${f.file}${f.line ? ":" + f.line : ""} \xB7 ${f.check}`));
86933
+ L.push(chalk35.gray(` ${f.file}${f.line ? ":" + f.line : ""} \xB7 ${f.check}`));
86662
86934
  if (ex) {
86663
86935
  L.push(` ${ex.happens}`);
86664
86936
  for (const s of ex.saw) L.push(` \u2022 ${s}`);
86665
- ex.fix.forEach((x, i) => L.push(chalk34.cyan(` ${i + 1}. ${x}`)));
86937
+ ex.fix.forEach((x, i) => L.push(chalk35.cyan(` ${i + 1}. ${x}`)));
86666
86938
  } else {
86667
86939
  for (const s of f.signals) L.push(` \u2022 ${s}`);
86668
86940
  if (f.mitigations?.length)
86669
- L.push(chalk34.gray(` \u2713 mitigated: ${f.mitigations.join("; ")}`));
86670
- L.push(chalk34.cyan(` \u2192 ${f.fix}`));
86941
+ L.push(chalk35.gray(` \u2713 mitigated: ${f.mitigations.join("; ")}`));
86942
+ L.push(chalk35.cyan(` \u2192 ${f.fix}`));
86671
86943
  }
86672
86944
  L.push("");
86673
86945
  }
86674
86946
  if (n === 0 && !res.incomplete) {
86675
86947
  L.push(
86676
- chalk34.gray(" No committed agent hooks, injectable workflows, or unpinned MCP servers.")
86948
+ chalk35.gray(" No committed agent hooks, injectable workflows, or unpinned MCP servers.")
86677
86949
  );
86678
86950
  L.push("");
86679
86951
  }
86680
86952
  L.push(...reviewTerminal(res.findings));
86681
- for (const note of res.notes) L.push(chalk34.gray(` note: ${note}`));
86953
+ for (const note of res.notes) L.push(chalk35.gray(` note: ${note}`));
86682
86954
  const hasHigh = shown.some(
86683
86955
  (f) => isAlert(f) && !f.suppressed && (f.severity === "critical" || f.severity === "high")
86684
86956
  );
86685
86957
  if (hasHigh && !ownedHint(res.source)) {
86686
86958
  L.push("");
86687
86959
  L.push(
86688
- chalk34.yellow(
86960
+ chalk35.yellow(
86689
86961
  " \u26A0\uFE0F This looks like a live issue on a repo you may not own. Disclose it privately\n to the maintainers; do not publish it. (node9 never weaponizes findings.)"
86690
86962
  )
86691
86963
  );
@@ -86879,7 +87151,7 @@ function makeProgress(target, quiet) {
86879
87151
  let frame = 0;
86880
87152
  let last = `scanning ${target}\u2026`;
86881
87153
  const timer = setInterval(() => {
86882
- process.stderr.write(`\r${chalk35.cyan(SPIN[frame++ % SPIN.length])} ${last} `);
87154
+ process.stderr.write(`\r${chalk36.cyan(SPIN[frame++ % SPIN.length])} ${last} `);
86883
87155
  }, 80);
86884
87156
  return {
86885
87157
  onProgress: (p) => {
@@ -86942,236 +87214,13 @@ function registerScanRepoCommand(program2) {
86942
87214
  if (!telemetryDisabled()) {
86943
87215
  await sendScanRepoPing(buildScanRepoPing(res, target));
86944
87216
  if (!opts.json && !opts.markdown && process.stderr.isTTY) {
86945
- process.stderr.write(chalk35.dim(SCAN_REPO_TELEMETRY_NOTICE) + "\n");
87217
+ process.stderr.write(chalk36.dim(SCAN_REPO_TELEMETRY_NOTICE) + "\n");
86946
87218
  }
86947
87219
  }
86948
87220
  }
86949
87221
  );
86950
87222
  }
86951
87223
 
86952
- // src/cli/commands/egress.ts
86953
- init_config();
86954
- init_keyed_guard();
86955
- init_dist();
86956
- init_egress_config();
86957
- init_egress_config();
86958
- import chalk36 from "chalk";
86959
- function guard(fn) {
86960
- try {
86961
- fn();
86962
- return true;
86963
- } catch (err2) {
86964
- console.error(chalk36.red(`
86965
- \u2717 ${err2.message}
86966
- `));
86967
- process.exitCode = 1;
86968
- return false;
86969
- }
86970
- }
86971
- function mutate(action, change) {
86972
- if (!cliGuardPolicyWrite(action)) return false;
86973
- return guard(() => setEgress(change));
86974
- }
86975
- function addHost(list2, host2) {
86976
- if (!cliGuardPolicyWrite(`egress ${list2} ${host2}`)) return false;
86977
- return guard(() => addEgressHost(list2, host2));
86978
- }
86979
- function exempt2(address) {
86980
- if (!cliGuardPolicyWrite(`egress exempt ${address}`)) return false;
86981
- return guard(() => addSsrfExemption(address));
86982
- }
86983
- var INTERNAL_FIELDS = {
86984
- allowed: { ssrfStrict: false, allowPrivate: true },
86985
- listed: { ssrfStrict: false, allowPrivate: false },
86986
- blocked: { ssrfStrict: true, allowPrivate: false }
86987
- };
86988
- var INTERNAL_SAID = {
86989
- allowed: "reachable without listing them",
86990
- listed: "reachable only if they are on your allowlist",
86991
- blocked: "blocked"
86992
- };
86993
- function readInternalState(e) {
86994
- if (e.ssrfStrict === true) return "blocked";
86995
- return e.allowPrivate === false ? "listed" : "allowed";
86996
- }
86997
- function setInternal(value) {
86998
- const state = value.trim().toLowerCase();
86999
- if (!(state in INTERNAL_FIELDS)) {
87000
- console.error(
87001
- chalk36.red(`
87002
- \u2717 Expected "allowed", "listed" or "blocked", got "${value}".`) + chalk36.gray(
87003
- "\n allowed loopback, 10/172.16/192.168 and CGNAT are reachable (default)\n listed they are reachable only if you allowlist them\n blocked they are blocked at the floor\n"
87004
- )
87005
- );
87006
- process.exitCode = 1;
87007
- return;
87008
- }
87009
- if (!mutate(`egress internal ${state}`, INTERNAL_FIELDS[state])) return;
87010
- _resetConfigCache();
87011
- const effective = readInternalState(getConfig().policy.egress);
87012
- if (effective !== state) {
87013
- console.log(
87014
- chalk36.yellow(
87015
- `
87016
- \u26A0 Saved, but not in effect: your workspace sets internal addresses to ${effective.toUpperCase()} and that governs this machine.
87017
- Change it in the dashboard, Enforcement \u2192 Network.
87018
- `
87019
- )
87020
- );
87021
- return;
87022
- }
87023
- const line = `
87024
- \u2713 Internal addresses: ${state} \u2014 loopback, the private ranges and CGNAT are ${INTERNAL_SAID[state]}.
87025
- `;
87026
- console.log(state === "allowed" ? chalk36.yellow(line) : chalk36.green(line));
87027
- }
87028
- function showFloor(e, ssrfStrictSource) {
87029
- console.log(
87030
- chalk36.gray("\n Protected addresses") + chalk36.gray(" \u2014 in shell commands and declared URLs")
87031
- );
87032
- console.log(
87033
- chalk36.gray(
87034
- " always blocked: cloud metadata, link-local, multicast\n no setting releases these, though `node9 pause` suspends all enforcement"
87035
- )
87036
- );
87037
- const state = readInternalState(e);
87038
- const by = ssrfStrictSource === "workspace" ? "workspace (app.node9.ai)" : ssrfStrictSource === "local" ? "this machine (config.json)" : "the shipped default";
87039
- const STATE_LABEL = {
87040
- allowed: "allowed",
87041
- listed: "allowlist only",
87042
- blocked: "blocked"
87043
- };
87044
- console.log(
87045
- ` Internal addresses: ${state === "blocked" ? chalk36.green(STATE_LABEL[state]) : chalk36.yellow(STATE_LABEL[state])}` + chalk36.gray(` loopback, 10/172.16/192.168 and CGNAT are ${INTERNAL_SAID[state]}`)
87046
- );
87047
- console.log(chalk36.gray(` set by: ${by}`));
87048
- const exemptions = e.ssrfAllow ?? [];
87049
- console.log(chalk36.gray(` Exemptions: ${exemptions.length ? exemptions.join(", ") : "none"}`));
87050
- console.log(
87051
- chalk36.gray(
87052
- " Covered: shell commands, and tools that declare a URL (WebFetch, an MCP\n fetch tool, browser navigate).\n Not covered: an interpreter one-liner (node -e, python3 -c) hides its\n destination inside a program and does not reach this gate.\n CGNAT (100.64/10) is NOT in the always-blocked set: it is reachable\n until Internal addresses is on, and an exemption can release it."
87053
- )
87054
- );
87055
- }
87056
- function showStatus() {
87057
- const cfg = getConfig();
87058
- const e = cfg.policy.egress;
87059
- const state = !e.enabled ? chalk36.red("OFF \u2014 your agent can reach any host, except the protected ones below") : e.mode === "block" ? chalk36.green("LOCKED (block) \u2014 unknown hosts are denied") : chalk36.yellow("WATCHING (review) \u2014 unknown hosts prompt you");
87060
- console.log(chalk36.cyan.bold("\n\u{1F310} Egress control"));
87061
- console.log(" State: " + state);
87062
- if (cfg.policySource === "workspace") {
87063
- console.log(
87064
- chalk36.gray(" Source: workspace config (app.node9.ai) \u2014 local egress settings are ignored")
87065
- );
87066
- }
87067
- console.log(
87068
- chalk36.gray(
87069
- ` ${DEFAULT_EGRESS_ALLOWLIST.length} common dev/LLM hosts are always allowed (github, npm, pypi, anthropic, \u2026).`
87070
- )
87071
- );
87072
- showFloor(e, cfg.ssrfStrictSource);
87073
- if (e.allow.length) console.log("\n Your allow: " + e.allow.join(", "));
87074
- if (e.deny.length) console.log(" Your deny: " + e.deny.join(", "));
87075
- if (!e.enabled) {
87076
- console.log(chalk36.gray("\n Turn it on: node9 egress watch (prompt on unknown hosts)"));
87077
- console.log(chalk36.gray(" node9 egress lock (hard-block unknown hosts)"));
87078
- }
87079
- console.log("");
87080
- }
87081
- function registerEgressCommand(program2) {
87082
- const egress = program2.command("egress").description("Control where your agent can send data (egress allowlist)");
87083
- egress.command("watch").description("Prompt before the agent reaches an unknown host (review mode)").action(() => {
87084
- if (!mutate("egress watch", { enabled: true, mode: "review" })) return;
87085
- console.log(chalk36.green("\n\u2713 Egress is now watched (review mode)."));
87086
- console.log(
87087
- chalk36.gray(" Routine hosts (LLM APIs, package registries, localhost) are allowed.")
87088
- );
87089
- console.log(
87090
- chalk36.gray(" An unknown host will prompt you \u2014 run `node9 egress lock` to hard-block.\n")
87091
- );
87092
- });
87093
- egress.command("lock").description("Block the agent from reaching unknown hosts (block mode)").action(() => {
87094
- if (!mutate("egress lock", { enabled: true, mode: "block" })) return;
87095
- console.log(chalk36.green("\n\u2713 Egress is now locked (block mode)."));
87096
- console.log(chalk36.gray(" Routine hosts are still allowed; unknown hosts are denied."));
87097
- console.log(chalk36.gray(" Allow a specific host with `node9 egress allow <host>`.\n"));
87098
- });
87099
- egress.command("allow <host>").description("Allow an extra host (glob, e.g. *.mycorp.com)").action((host2) => {
87100
- if (!addHost("allow", host2)) return;
87101
- console.log(chalk36.green(`
87102
- \u2713 Allowed egress to ${host2}.
87103
- `));
87104
- });
87105
- egress.command("deny <host>").description("Block an extra host (deny always wins)").action((host2) => {
87106
- if (!addHost("deny", host2)) return;
87107
- console.log(chalk36.green(`
87108
- \u2713 Denied egress to ${host2}.
87109
- `));
87110
- });
87111
- egress.command("off").description("Turn egress control off").action(() => {
87112
- if (!mutate("egress off", { enabled: false })) return;
87113
- console.log(
87114
- chalk36.yellow("\n\u2713 Egress control is off \u2014 the agent can reach any host again.\n")
87115
- );
87116
- });
87117
- egress.command("internal <allowed|listed|blocked>").description(
87118
- "How loopback, private ranges and CGNAT are treated: allowed (default), listed (must be on the allowlist), or blocked"
87119
- ).action((value) => setInternal(value));
87120
- egress.command("strict <on|off>").description("Deprecated alias for `egress internal blocked|allowed`").action((value) => {
87121
- const v = value.trim().toLowerCase();
87122
- if (v !== "on" && v !== "off") {
87123
- console.error(chalk36.red(`
87124
- \u2717 Expected "on" or "off", got "${value}".
87125
- `));
87126
- process.exitCode = 1;
87127
- return;
87128
- }
87129
- const state = v === "on" ? "blocked" : getConfig().policy.egress.allowPrivate === false ? "listed" : "allowed";
87130
- console.log(chalk36.gray(`
87131
- (\`egress strict ${v}\` is now \`egress internal ${state}\`)`));
87132
- setInternal(state);
87133
- });
87134
- egress.command("exempt <address>").description("Let an address or a CIDR range through the floor (e.g. 100.64.0.0/10)").action((address) => {
87135
- const a = normalizeEgressHost(address);
87136
- const slash = a.indexOf("/");
87137
- const base2 = slash === -1 ? a : a.slice(0, slash);
87138
- const prefixText = slash === -1 ? null : a.slice(slash + 1);
87139
- if (!normalizeIpLiteral(base2) || prefixText !== null && !/^\d{1,3}$/.test(prefixText)) {
87140
- console.error(
87141
- chalk36.red(`
87142
- \u2717 "${address}" is not an address or a range.`) + chalk36.gray(
87143
- "\n Exemptions are matched as an address (10.0.0.5) or a CIDR\n range (100.64.0.0/10), never a name.\n"
87144
- )
87145
- );
87146
- process.exitCode = 1;
87147
- return;
87148
- }
87149
- const m = classifySsrf(base2);
87150
- if (m && !m.overridable) {
87151
- console.error(
87152
- chalk36.red(`
87153
- \u2717 ${a} cannot be exempted.`) + chalk36.gray(
87154
- `
87155
- ${slash === -1 ? "That address is" : "That range covers only"} protected addresses (${m.tier}), which no setting releases.
87156
- `
87157
- )
87158
- );
87159
- process.exitCode = 1;
87160
- return;
87161
- }
87162
- if (!exempt2(a)) return;
87163
- console.log(chalk36.green(`
87164
- \u2713 ${a} is exempt from the floor.`));
87165
- if (!m)
87166
- console.log(
87167
- chalk36.gray(" Note: this address is not on the floor anyway \u2014 nothing changes.\n")
87168
- );
87169
- else console.log("");
87170
- });
87171
- egress.command("status").description("Show the current egress state").action(showStatus);
87172
- egress.action(showStatus);
87173
- }
87174
-
87175
87224
  // src/cli/commands/checks.ts
87176
87225
  init_dist();
87177
87226
  init_config();
@@ -87212,7 +87261,9 @@ function renderChecks(report) {
87212
87261
  const lines = [];
87213
87262
  const where = report.policySource === "workspace" ? "workspace (app.node9.ai)" : "local (~/.node9/config.json)";
87214
87263
  lines.push("");
87215
- lines.push(`${chalk37.cyan.bold("node9 checks")} policy: ${where} mode: ${report.mode}`);
87264
+ lines.push(
87265
+ `${chalk37.cyan.bold("node9 checks")} policy: ${where} mode: ${levelOf(report.mode)}`
87266
+ );
87216
87267
  lines.push("");
87217
87268
  const row = (c) => {
87218
87269
  const value = (VALUE_COLOR[c.value] ?? chalk37.white)(c.value.padEnd(7));