@node9/proxy 1.61.1 → 1.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +973 -622
  2. package/dist/cli.mjs +967 -616
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -248,8 +248,8 @@ function sanitizeConfig(raw) {
248
248
  }
249
249
  }
250
250
  const lines = result.error.issues.map((issue) => {
251
- const path70 = issue.path.length > 0 ? issue.path.join(".") : "root";
252
- return ` \u2022 ${path70}: ${issue.message}`;
251
+ const path71 = issue.path.length > 0 ? issue.path.join(".") : "root";
252
+ return ` \u2022 ${path71}: ${issue.message}`;
253
253
  });
254
254
  return {
255
255
  sanitized,
@@ -1464,9 +1464,9 @@ function matchesPattern(text, patterns) {
1464
1464
  const withoutDotSlash = text.replace(/^\.\//, "");
1465
1465
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1466
1466
  }
1467
- function getNestedValue(obj, path70) {
1467
+ function getNestedValue(obj, path71) {
1468
1468
  if (!obj || typeof obj !== "object") return null;
1469
- const segments = path70.split(".");
1469
+ const segments = path71.split(".");
1470
1470
  for (const seg of segments) {
1471
1471
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1472
1472
  }
@@ -4849,10 +4849,10 @@ function getConfig(cwd) {
4849
4849
  }
4850
4850
  if (Array.isArray(mc.jailPaths)) {
4851
4851
  for (const jp of mc.jailPaths) {
4852
- const path70 = typeof jp?.path === "string" ? jp.path.trim() : "";
4853
- if (!path70) continue;
4852
+ const path71 = typeof jp?.path === "string" ? jp.path.trim() : "";
4853
+ if (!path71) continue;
4854
4854
  const verdict = jp?.verdict === "review" ? "review" : "block";
4855
- for (const r of pathRules(path70, verdict, "org-managed jail")) {
4855
+ for (const r of pathRules(path71, verdict, "org-managed jail")) {
4856
4856
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
4857
4857
  }
4858
4858
  }
@@ -17963,6 +17963,66 @@ function pickSyncIntervalMs(cloudHours, localSettings) {
17963
17963
  function effectiveSyncIntervalMs() {
17964
17964
  return pickSyncIntervalMs(readCachedSyncIntervalHours(), getConfig().settings);
17965
17965
  }
17966
+ function readSyncHealth() {
17967
+ try {
17968
+ const raw = JSON.parse(fs36.readFileSync(syncHealthFile(), "utf-8"));
17969
+ return {
17970
+ lastCheckedAt: typeof raw.lastCheckedAt === "string" ? raw.lastCheckedAt : void 0,
17971
+ lastChangedAt: typeof raw.lastChangedAt === "string" ? raw.lastChangedAt : void 0,
17972
+ lastError: typeof raw.lastError === "string" ? raw.lastError : void 0,
17973
+ lastErrorAt: typeof raw.lastErrorAt === "string" ? raw.lastErrorAt : void 0,
17974
+ consecutiveFailures: typeof raw.consecutiveFailures === "number" && raw.consecutiveFailures >= 0 ? raw.consecutiveFailures : 0
17975
+ };
17976
+ } catch {
17977
+ return { consecutiveFailures: 0 };
17978
+ }
17979
+ }
17980
+ function writeSyncHealth(h) {
17981
+ try {
17982
+ const file = syncHealthFile();
17983
+ const dir = path35.dirname(file);
17984
+ if (!fs36.existsSync(dir)) fs36.mkdirSync(dir, { recursive: true });
17985
+ const tmp = `${file}.${process.pid}.tmp`;
17986
+ fs36.writeFileSync(tmp, JSON.stringify(h, null, 2) + "\n", "utf-8");
17987
+ fs36.renameSync(tmp, file);
17988
+ } catch {
17989
+ }
17990
+ }
17991
+ function readCacheFetchedAt() {
17992
+ try {
17993
+ const raw = JSON.parse(fs36.readFileSync(rulesCacheFile(), "utf-8"));
17994
+ return typeof raw.fetchedAt === "string" ? raw.fetchedAt : void 0;
17995
+ } catch {
17996
+ return void 0;
17997
+ }
17998
+ }
17999
+ function recordSyncHealth(result) {
18000
+ const h = readSyncHealth();
18001
+ const now = (/* @__PURE__ */ new Date()).toISOString();
18002
+ if (result.ok) {
18003
+ h.lastCheckedAt = now;
18004
+ if (result.changed) h.lastChangedAt = now;
18005
+ h.consecutiveFailures = 0;
18006
+ h.lastError = void 0;
18007
+ h.lastErrorAt = void 0;
18008
+ } else {
18009
+ h.consecutiveFailures += 1;
18010
+ h.lastError = result.error;
18011
+ h.lastErrorAt = now;
18012
+ }
18013
+ writeSyncHealth(h);
18014
+ }
18015
+ function stalenessThresholdMs(intervalMs) {
18016
+ return Math.min(STALE_MAX_MS, Math.max(STALE_MIN_MS, intervalMs * STALE_FACTOR));
18017
+ }
18018
+ function isPolicyStale(nowMs = Date.now(), health) {
18019
+ const h = health ?? readSyncHealth();
18020
+ const lastKnownGood = h.lastCheckedAt ?? readCacheFetchedAt();
18021
+ if (!lastKnownGood) return false;
18022
+ const last = Date.parse(lastKnownGood);
18023
+ if (Number.isNaN(last)) return false;
18024
+ return nowMs - last > stalenessThresholdMs(effectiveSyncIntervalMs());
18025
+ }
17966
18026
  function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
17967
18027
  const parsed = new URL(apiUrl);
17968
18028
  const headers = {
@@ -18127,6 +18187,7 @@ async function syncOnce() {
18127
18187
  try {
18128
18188
  const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
18129
18189
  if (result.kind === "unchanged") {
18190
+ recordSyncHealth({ ok: true });
18130
18191
  } else {
18131
18192
  const cache = {
18132
18193
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -18140,8 +18201,19 @@ async function syncOnce() {
18140
18201
  managedConfig: extractManagedConfig(result.body)
18141
18202
  };
18142
18203
  writeCache2(cache);
18204
+ recordSyncHealth({ ok: true, changed: true });
18205
+ }
18206
+ } catch (err2) {
18207
+ const msg = err2 instanceof Error ? err2.message : String(err2);
18208
+ recordSyncHealth({ ok: false, error: msg });
18209
+ try {
18210
+ appendToLog(HOOK_DEBUG_LOG, {
18211
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
18212
+ kind: "policy-sync-error",
18213
+ error: msg
18214
+ });
18215
+ } catch {
18143
18216
  }
18144
- } catch {
18145
18217
  }
18146
18218
  if (process.env.NODE9_BLAST_DISABLE !== "1") {
18147
18219
  void pushBlastSnapshot(creds);
@@ -18317,6 +18389,7 @@ async function runCloudSync() {
18317
18389
  const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
18318
18390
  if (result.kind === "unchanged") {
18319
18391
  const status = getCloudSyncStatus();
18392
+ recordSyncHealth({ ok: true });
18320
18393
  maybePushBlast();
18321
18394
  return status.cached ? { ok: true, rules: status.rules, fetchedAt: status.fetchedAt, unchanged: true } : { ok: true, rules: 0, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), unchanged: true };
18322
18395
  }
@@ -18332,11 +18405,14 @@ async function runCloudSync() {
18332
18405
  managedConfig: extractManagedConfig(result.body)
18333
18406
  };
18334
18407
  writeCache2(cache);
18408
+ recordSyncHealth({ ok: true, changed: true });
18335
18409
  maybePushBlast();
18336
18410
  return { ok: true, rules: cache.rules.length, fetchedAt: cache.fetchedAt };
18337
18411
  } catch (err2) {
18412
+ const msg = err2 instanceof Error ? err2.message : String(err2);
18413
+ recordSyncHealth({ ok: false, error: msg });
18338
18414
  maybePushBlast();
18339
- return { ok: false, reason: err2 instanceof Error ? err2.message : String(err2) };
18415
+ return { ok: false, reason: msg };
18340
18416
  }
18341
18417
  }
18342
18418
  function getCloudSyncStatus() {
@@ -18393,7 +18469,7 @@ function startForensicBroadcast() {
18393
18469
  const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
18394
18470
  recurring.unref();
18395
18471
  }
18396
- var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL2, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_SECONDS, MAX_INTERVAL_SECONDS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
18472
+ var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL2, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_SECONDS, MAX_INTERVAL_SECONDS, syncHealthFile, STALE_MIN_MS, STALE_MAX_MS, STALE_FACTOR, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
18397
18473
  var init_sync = __esm({
18398
18474
  "src/daemon/sync.ts"() {
18399
18475
  "use strict";
@@ -18427,6 +18503,10 @@ var init_sync = __esm({
18427
18503
  DEFAULT_INTERVAL_HOURS = 5;
18428
18504
  MIN_INTERVAL_SECONDS = 15;
18429
18505
  MAX_INTERVAL_SECONDS = 24 * 60 * 60;
18506
+ syncHealthFile = () => path35.join(os33.homedir(), ".node9", "sync-health.json");
18507
+ STALE_MIN_MS = 3 * 60 * 60 * 1e3;
18508
+ STALE_MAX_MS = 24 * 60 * 60 * 1e3;
18509
+ STALE_FACTOR = 3;
18430
18510
  FORENSIC_BROADCAST_INTERVAL_MS = 3e4;
18431
18511
  FORENSIC_INITIAL_DELAY_MS = 5e3;
18432
18512
  forensicBroadcastOffsets = /* @__PURE__ */ new Map();
@@ -19041,23 +19121,68 @@ var init_hook_heal = __esm({
19041
19121
  }
19042
19122
  });
19043
19123
 
19044
- // src/daemon/server.ts
19045
- import http3 from "http";
19124
+ // src/daemon/startup-log.ts
19046
19125
  import fs40 from "fs";
19047
19126
  import path39 from "path";
19048
19127
  import os37 from "os";
19128
+ function openStartupLogFd() {
19129
+ try {
19130
+ const file = DAEMON_STARTUP_LOG();
19131
+ const dir = path39.dirname(file);
19132
+ if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19133
+ try {
19134
+ if (fs40.statSync(file).size > MAX_STARTUP_LOG_BYTES) fs40.truncateSync(file);
19135
+ } catch {
19136
+ }
19137
+ return fs40.openSync(file, "a");
19138
+ } catch {
19139
+ return void 0;
19140
+ }
19141
+ }
19142
+ function logDaemonStartup(kind, detail) {
19143
+ try {
19144
+ const file = DAEMON_STARTUP_LOG();
19145
+ const dir = path39.dirname(file);
19146
+ if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19147
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-startup:${kind}${detail ? ` ${detail}` : ""}
19148
+ `;
19149
+ fs40.appendFileSync(file, line, "utf-8");
19150
+ } catch {
19151
+ }
19152
+ }
19153
+ var DAEMON_STARTUP_LOG, MAX_STARTUP_LOG_BYTES;
19154
+ var init_startup_log = __esm({
19155
+ "src/daemon/startup-log.ts"() {
19156
+ "use strict";
19157
+ DAEMON_STARTUP_LOG = () => path39.join(os37.homedir(), ".node9", "daemon-startup.log");
19158
+ MAX_STARTUP_LOG_BYTES = 256 * 1024;
19159
+ }
19160
+ });
19161
+
19162
+ // src/daemon/server.ts
19163
+ import http3 from "http";
19164
+ import fs41 from "fs";
19165
+ import path40 from "path";
19166
+ import os38 from "os";
19049
19167
  import { randomUUID as randomUUID4 } from "crypto";
19050
19168
  import { spawnSync } from "child_process";
19051
19169
  import chalk6 from "chalk";
19052
19170
  function startDaemon() {
19053
- startCostSync();
19054
- startCloudSync();
19055
- startForensicBroadcast();
19056
- startAuditShipper();
19057
- startDlpScanner();
19058
- startMcpReconciler();
19059
- startHookHeal();
19060
- loadInsightCounts();
19171
+ try {
19172
+ startCostSync();
19173
+ startCloudSync();
19174
+ startForensicBroadcast();
19175
+ startAuditShipper();
19176
+ startDlpScanner();
19177
+ startMcpReconciler();
19178
+ startHookHeal();
19179
+ loadInsightCounts();
19180
+ } catch (err2) {
19181
+ const stack = err2 instanceof Error ? err2.stack ?? err2.message : String(err2);
19182
+ console.error("\n\u{1F6D1} Node9 daemon startup failed:\n" + stack);
19183
+ logDaemonStartup("startup-throw", err2 instanceof Error ? err2.message : String(err2));
19184
+ process.exit(1);
19185
+ }
19061
19186
  const internalToken = randomUUID4();
19062
19187
  const validToken = (req) => req.headers["x-node9-internal"] === internalToken || req.headers["x-node9-token"] === internalToken;
19063
19188
  const IDLE_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
@@ -19069,7 +19194,7 @@ function startDaemon() {
19069
19194
  idleTimer = setTimeout(() => {
19070
19195
  if (autoStarted) {
19071
19196
  try {
19072
- fs40.unlinkSync(DAEMON_PID_FILE);
19197
+ fs41.unlinkSync(DAEMON_PID_FILE);
19073
19198
  } catch {
19074
19199
  }
19075
19200
  }
@@ -19214,7 +19339,7 @@ data: ${JSON.stringify(item.data)}
19214
19339
  mcpServer: entry.mcpServer
19215
19340
  });
19216
19341
  }
19217
- const projectCwd = typeof cwd === "string" && path39.isAbsolute(cwd) ? cwd : void 0;
19342
+ const projectCwd = typeof cwd === "string" && path40.isAbsolute(cwd) ? cwd : void 0;
19218
19343
  const projectConfig = getConfig(projectCwd);
19219
19344
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
19220
19345
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -19506,8 +19631,8 @@ data: ${JSON.stringify(item.data)}
19506
19631
  if (!validToken(req)) return res.writeHead(403).end();
19507
19632
  const periodParam = reqUrl.searchParams.get("period") || "7d";
19508
19633
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
19509
- const logPath = path39.join(os37.homedir(), ".node9", "audit.log");
19510
- if (!fs40.existsSync(logPath)) {
19634
+ const logPath = path40.join(os38.homedir(), ".node9", "audit.log");
19635
+ if (!fs41.existsSync(logPath)) {
19511
19636
  res.writeHead(200, { "Content-Type": "application/json" });
19512
19637
  return res.end(
19513
19638
  JSON.stringify({
@@ -19520,7 +19645,7 @@ data: ${JSON.stringify(item.data)}
19520
19645
  );
19521
19646
  }
19522
19647
  try {
19523
- const raw = fs40.readFileSync(logPath, "utf-8");
19648
+ const raw = fs41.readFileSync(logPath, "utf-8");
19524
19649
  const allEntries = raw.split("\n").flatMap((line) => {
19525
19650
  if (!line.trim()) return [];
19526
19651
  try {
@@ -19903,14 +20028,15 @@ data: ${JSON.stringify(item.data)}
19903
20028
  server.on("error", (e) => {
19904
20029
  if (e.code === "EADDRINUSE") {
19905
20030
  try {
19906
- if (fs40.existsSync(DAEMON_PID_FILE)) {
19907
- const { pid } = JSON.parse(fs40.readFileSync(DAEMON_PID_FILE, "utf-8"));
20031
+ if (fs41.existsSync(DAEMON_PID_FILE)) {
20032
+ const { pid } = JSON.parse(fs41.readFileSync(DAEMON_PID_FILE, "utf-8"));
19908
20033
  process.kill(pid, 0);
20034
+ logDaemonStartup("port-in-use", `another daemon (pid ${pid}) owns :${DAEMON_PORT}`);
19909
20035
  return process.exit(0);
19910
20036
  }
19911
20037
  } catch {
19912
20038
  try {
19913
- fs40.unlinkSync(DAEMON_PID_FILE);
20039
+ fs41.unlinkSync(DAEMON_PID_FILE);
19914
20040
  } catch {
19915
20041
  }
19916
20042
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -19959,6 +20085,7 @@ data: ${JSON.stringify(item.data)}
19959
20085
  });
19960
20086
  return;
19961
20087
  }
20088
+ logDaemonStartup("bind-failed", e.message);
19962
20089
  console.error(chalk6.red("\n\u{1F6D1} Node9 Daemon Error:"), e.message);
19963
20090
  process.exit(1);
19964
20091
  });
@@ -19996,20 +20123,21 @@ var init_server = __esm({
19996
20123
  init_dlp_scanner();
19997
20124
  init_mcp_reconciler();
19998
20125
  init_hook_heal();
20126
+ init_startup_log();
19999
20127
  init_mcp_tools();
20000
20128
  }
20001
20129
  });
20002
20130
 
20003
20131
  // src/daemon/service.ts
20004
- import fs41 from "fs";
20005
- import path40 from "path";
20006
- import os38 from "os";
20132
+ import fs42 from "fs";
20133
+ import path41 from "path";
20134
+ import os39 from "os";
20007
20135
  import { spawnSync as spawnSync2, execFileSync } from "child_process";
20008
20136
  function resolveNode9Binary() {
20009
20137
  try {
20010
20138
  const script = process.argv[1];
20011
- if (typeof script === "string" && path40.isAbsolute(script) && fs41.existsSync(script)) {
20012
- return fs41.realpathSync(script);
20139
+ if (typeof script === "string" && path41.isAbsolute(script) && fs42.existsSync(script)) {
20140
+ return fs42.realpathSync(script);
20013
20141
  }
20014
20142
  } catch {
20015
20143
  }
@@ -20027,11 +20155,11 @@ function xmlEscape(s) {
20027
20155
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
20028
20156
  }
20029
20157
  function launchdPlist(binaryPath) {
20030
- const logDir = path40.join(os38.homedir(), ".node9");
20158
+ const logDir = path41.join(os39.homedir(), ".node9");
20031
20159
  const nodePath = xmlEscape(process.execPath);
20032
20160
  const scriptPath = xmlEscape(binaryPath);
20033
- const outLog = xmlEscape(path40.join(logDir, "daemon.log"));
20034
- const errLog = xmlEscape(path40.join(logDir, "daemon-error.log"));
20161
+ const outLog = xmlEscape(path41.join(logDir, "daemon.log"));
20162
+ const errLog = xmlEscape(path41.join(logDir, "daemon-error.log"));
20035
20163
  return `<?xml version="1.0" encoding="UTF-8"?>
20036
20164
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
20037
20165
  <plist version="1.0">
@@ -20064,9 +20192,9 @@ function launchdPlist(binaryPath) {
20064
20192
  `;
20065
20193
  }
20066
20194
  function installLaunchd(binaryPath) {
20067
- const dir = path40.dirname(LAUNCHD_PLIST);
20068
- if (!fs41.existsSync(dir)) fs41.mkdirSync(dir, { recursive: true });
20069
- fs41.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
20195
+ const dir = path41.dirname(LAUNCHD_PLIST);
20196
+ if (!fs42.existsSync(dir)) fs42.mkdirSync(dir, { recursive: true });
20197
+ fs42.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
20070
20198
  spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
20071
20199
  const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
20072
20200
  encoding: "utf8",
@@ -20077,13 +20205,13 @@ function installLaunchd(binaryPath) {
20077
20205
  }
20078
20206
  }
20079
20207
  function uninstallLaunchd() {
20080
- if (fs41.existsSync(LAUNCHD_PLIST)) {
20208
+ if (fs42.existsSync(LAUNCHD_PLIST)) {
20081
20209
  spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
20082
- fs41.unlinkSync(LAUNCHD_PLIST);
20210
+ fs42.unlinkSync(LAUNCHD_PLIST);
20083
20211
  }
20084
20212
  }
20085
20213
  function isLaunchdInstalled() {
20086
- return fs41.existsSync(LAUNCHD_PLIST);
20214
+ return fs42.existsSync(LAUNCHD_PLIST);
20087
20215
  }
20088
20216
  function systemdUnit(binaryPath) {
20089
20217
  return `[Unit]
@@ -20102,12 +20230,12 @@ WantedBy=default.target
20102
20230
  `;
20103
20231
  }
20104
20232
  function installSystemd(binaryPath) {
20105
- if (!fs41.existsSync(SYSTEMD_UNIT_DIR)) {
20106
- fs41.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
20233
+ if (!fs42.existsSync(SYSTEMD_UNIT_DIR)) {
20234
+ fs42.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
20107
20235
  }
20108
- fs41.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
20236
+ fs42.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
20109
20237
  try {
20110
- execFileSync("loginctl", ["enable-linger", os38.userInfo().username], { timeout: 3e3 });
20238
+ execFileSync("loginctl", ["enable-linger", os39.userInfo().username], { timeout: 3e3 });
20111
20239
  } catch {
20112
20240
  }
20113
20241
  const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
@@ -20127,23 +20255,23 @@ function installSystemd(binaryPath) {
20127
20255
  }
20128
20256
  }
20129
20257
  function uninstallSystemd() {
20130
- if (fs41.existsSync(SYSTEMD_UNIT)) {
20258
+ if (fs42.existsSync(SYSTEMD_UNIT)) {
20131
20259
  spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
20132
20260
  encoding: "utf8",
20133
20261
  timeout: 5e3
20134
20262
  });
20135
20263
  spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
20136
- fs41.unlinkSync(SYSTEMD_UNIT);
20264
+ fs42.unlinkSync(SYSTEMD_UNIT);
20137
20265
  }
20138
20266
  }
20139
20267
  function isSystemdInstalled() {
20140
- return fs41.existsSync(SYSTEMD_UNIT);
20268
+ return fs42.existsSync(SYSTEMD_UNIT);
20141
20269
  }
20142
20270
  function stopRunningDaemon() {
20143
- const pidFile = path40.join(os38.homedir(), ".node9", "daemon.pid");
20144
- if (!fs41.existsSync(pidFile)) return;
20271
+ const pidFile = path41.join(os39.homedir(), ".node9", "daemon.pid");
20272
+ if (!fs42.existsSync(pidFile)) return;
20145
20273
  try {
20146
- const data = JSON.parse(fs41.readFileSync(pidFile, "utf-8"));
20274
+ const data = JSON.parse(fs42.readFileSync(pidFile, "utf-8"));
20147
20275
  const pid = data.pid;
20148
20276
  const MAX_PID2 = 4194304;
20149
20277
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -20163,7 +20291,7 @@ function stopRunningDaemon() {
20163
20291
  }
20164
20292
  }
20165
20293
  try {
20166
- fs41.unlinkSync(pidFile);
20294
+ fs42.unlinkSync(pidFile);
20167
20295
  } catch {
20168
20296
  }
20169
20297
  } catch {
@@ -20233,24 +20361,93 @@ function isDaemonServiceInstalled() {
20233
20361
  if (process.platform === "linux") return isSystemdInstalled();
20234
20362
  return false;
20235
20363
  }
20364
+ function autostartRepairDecision(opts) {
20365
+ if (!opts.autoStartDaemon) return "skip";
20366
+ if (process.platform !== "linux" && process.platform !== "darwin") return "unsupported";
20367
+ if (!opts.installed) return "skip";
20368
+ return opts.enabled ? "ok" : "repair";
20369
+ }
20370
+ function enableDaemonServiceQuiet() {
20371
+ try {
20372
+ if (process.platform === "linux") {
20373
+ const r = spawnSync2("systemctl", ["--user", "enable", "node9-daemon"], {
20374
+ encoding: "utf8",
20375
+ timeout: 3e3
20376
+ });
20377
+ return r.status === 0;
20378
+ }
20379
+ return process.platform === "darwin";
20380
+ } catch {
20381
+ return false;
20382
+ }
20383
+ }
20384
+ function ensureAutostartHealthy(autoStartDaemon) {
20385
+ const decision = autostartRepairDecision({
20386
+ installed: isDaemonServiceInstalled(),
20387
+ enabled: isDaemonServiceEnabled(),
20388
+ autoStartDaemon
20389
+ });
20390
+ if (decision === "repair") return enableDaemonServiceQuiet() ? "repaired" : "skipped";
20391
+ return decision === "ok" ? "ok" : decision === "unsupported" ? "unsupported" : "skipped";
20392
+ }
20393
+ function autostartAdvice(opts) {
20394
+ const installable = process.platform === "linux" || process.platform === "darwin";
20395
+ if (!opts.cloudEnabled || !installable) return null;
20396
+ const installHint = process.platform === "linux" ? "Run: systemctl --user enable --now node9-daemon (or: node9 daemon install)" : "Run: node9 daemon install";
20397
+ if (opts.installed && !opts.enabled) {
20398
+ return {
20399
+ level: "warn",
20400
+ message: "Daemon autostart is INSTALLED but DISABLED \u2014 it will NOT survive a reboot, so cloud policy can silently go stale.",
20401
+ hint: installHint
20402
+ };
20403
+ }
20404
+ if (!opts.installed) {
20405
+ return {
20406
+ level: "warn",
20407
+ message: "No daemon autostart installed \u2014 the daemon only runs when an agent happens to spawn it; cloud policy may lag.",
20408
+ hint: installHint
20409
+ };
20410
+ }
20411
+ return null;
20412
+ }
20413
+ function isDaemonServiceEnabled() {
20414
+ try {
20415
+ if (process.platform === "linux") {
20416
+ const r = spawnSync2("systemctl", ["--user", "is-enabled", "node9-daemon"], {
20417
+ encoding: "utf8",
20418
+ timeout: 3e3
20419
+ });
20420
+ return r.status === 0 && (r.stdout ?? "").trim() === "enabled";
20421
+ }
20422
+ if (process.platform === "darwin") {
20423
+ const r = spawnSync2("launchctl", ["list", LAUNCHD_LABEL], {
20424
+ encoding: "utf8",
20425
+ timeout: 3e3
20426
+ });
20427
+ return r.status === 0;
20428
+ }
20429
+ } catch {
20430
+ }
20431
+ return false;
20432
+ }
20236
20433
  var LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
20237
20434
  var init_service = __esm({
20238
20435
  "src/daemon/service.ts"() {
20239
20436
  "use strict";
20240
20437
  LAUNCHD_LABEL = "ai.node9.daemon";
20241
- LAUNCHD_PLIST = path40.join(os38.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
20242
- SYSTEMD_UNIT_DIR = path40.join(os38.homedir(), ".config", "systemd", "user");
20243
- SYSTEMD_UNIT = path40.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
20438
+ LAUNCHD_PLIST = path41.join(os39.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
20439
+ SYSTEMD_UNIT_DIR = path41.join(os39.homedir(), ".config", "systemd", "user");
20440
+ SYSTEMD_UNIT = path41.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
20244
20441
  }
20245
20442
  });
20246
20443
 
20247
20444
  // src/daemon/index.ts
20248
- import fs42 from "fs";
20445
+ import fs43 from "fs";
20249
20446
  import chalk7 from "chalk";
20250
20447
  function stopDaemon() {
20251
- if (!fs42.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
20448
+ if (!fs43.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
20252
20449
  try {
20253
- const data = JSON.parse(fs42.readFileSync(DAEMON_PID_FILE, "utf-8"));
20450
+ const data = JSON.parse(fs43.readFileSync(DAEMON_PID_FILE, "utf-8"));
20254
20451
  const pid = data.pid;
20255
20452
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
20256
20453
  console.log(chalk7.gray("Cleaned up invalid PID file."));
@@ -20262,7 +20459,7 @@ function stopDaemon() {
20262
20459
  console.log(chalk7.gray("Cleaned up stale PID file."));
20263
20460
  } finally {
20264
20461
  try {
20265
- fs42.unlinkSync(DAEMON_PID_FILE);
20462
+ fs43.unlinkSync(DAEMON_PID_FILE);
20266
20463
  } catch {
20267
20464
  }
20268
20465
  }
@@ -20271,9 +20468,9 @@ function daemonStatus() {
20271
20468
  const serviceInstalled = isDaemonServiceInstalled();
20272
20469
  const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
20273
20470
  let processStatus;
20274
- if (fs42.existsSync(DAEMON_PID_FILE)) {
20471
+ if (fs43.existsSync(DAEMON_PID_FILE)) {
20275
20472
  try {
20276
- const data = JSON.parse(fs42.readFileSync(DAEMON_PID_FILE, "utf-8"));
20473
+ const data = JSON.parse(fs43.readFileSync(DAEMON_PID_FILE, "utf-8"));
20277
20474
  const pid = data.pid;
20278
20475
  const port = data.port;
20279
20476
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -21416,14 +21613,14 @@ var require_util = __commonJS({
21416
21613
  }
21417
21614
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
21418
21615
  let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
21419
- let path70 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
21616
+ let path71 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
21420
21617
  if (origin[origin.length - 1] === "/") {
21421
21618
  origin = origin.slice(0, origin.length - 1);
21422
21619
  }
21423
- if (path70 && path70[0] !== "/") {
21424
- path70 = `/${path70}`;
21620
+ if (path71 && path71[0] !== "/") {
21621
+ path71 = `/${path71}`;
21425
21622
  }
21426
- return new URL(`${origin}${path70}`);
21623
+ return new URL(`${origin}${path71}`);
21427
21624
  }
21428
21625
  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
21429
21626
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -22244,9 +22441,9 @@ var require_diagnostics = __commonJS({
22244
22441
  "undici:client:sendHeaders",
22245
22442
  (evt) => {
22246
22443
  const {
22247
- request: { method, path: path70, origin }
22444
+ request: { method, path: path71, origin }
22248
22445
  } = evt;
22249
- debugLog("sending request to %s %s%s", method, origin, path70);
22446
+ debugLog("sending request to %s %s%s", method, origin, path71);
22250
22447
  }
22251
22448
  );
22252
22449
  }
@@ -22264,14 +22461,14 @@ var require_diagnostics = __commonJS({
22264
22461
  "undici:request:headers",
22265
22462
  (evt) => {
22266
22463
  const {
22267
- request: { method, path: path70, origin },
22464
+ request: { method, path: path71, origin },
22268
22465
  response: { statusCode }
22269
22466
  } = evt;
22270
22467
  debugLog(
22271
22468
  "received response to %s %s%s - HTTP %d",
22272
22469
  method,
22273
22470
  origin,
22274
- path70,
22471
+ path71,
22275
22472
  statusCode
22276
22473
  );
22277
22474
  }
@@ -22280,23 +22477,23 @@ var require_diagnostics = __commonJS({
22280
22477
  "undici:request:trailers",
22281
22478
  (evt) => {
22282
22479
  const {
22283
- request: { method, path: path70, origin }
22480
+ request: { method, path: path71, origin }
22284
22481
  } = evt;
22285
- debugLog("trailers received from %s %s%s", method, origin, path70);
22482
+ debugLog("trailers received from %s %s%s", method, origin, path71);
22286
22483
  }
22287
22484
  );
22288
22485
  diagnosticsChannel.subscribe(
22289
22486
  "undici:request:error",
22290
22487
  (evt) => {
22291
22488
  const {
22292
- request: { method, path: path70, origin },
22489
+ request: { method, path: path71, origin },
22293
22490
  error
22294
22491
  } = evt;
22295
22492
  debugLog(
22296
22493
  "request to %s %s%s errored - %s",
22297
22494
  method,
22298
22495
  origin,
22299
- path70,
22496
+ path71,
22300
22497
  error.message
22301
22498
  );
22302
22499
  }
@@ -22399,7 +22596,7 @@ var require_request = __commonJS({
22399
22596
  var kHandler = /* @__PURE__ */ Symbol("handler");
22400
22597
  var Request = class {
22401
22598
  constructor(origin, {
22402
- path: path70,
22599
+ path: path71,
22403
22600
  method,
22404
22601
  body,
22405
22602
  headers,
@@ -22416,11 +22613,11 @@ var require_request = __commonJS({
22416
22613
  maxRedirections,
22417
22614
  typeOfService
22418
22615
  }, handler) {
22419
- if (typeof path70 !== "string") {
22616
+ if (typeof path71 !== "string") {
22420
22617
  throw new InvalidArgumentError("path must be a string");
22421
- } else if (path70[0] !== "/" && !(path70.startsWith("http://") || path70.startsWith("https://")) && method !== "CONNECT") {
22618
+ } else if (path71[0] !== "/" && !(path71.startsWith("http://") || path71.startsWith("https://")) && method !== "CONNECT") {
22422
22619
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
22423
- } else if (invalidPathRegex.test(path70)) {
22620
+ } else if (invalidPathRegex.test(path71)) {
22424
22621
  throw new InvalidArgumentError("invalid request path");
22425
22622
  }
22426
22623
  if (typeof method !== "string") {
@@ -22495,7 +22692,7 @@ var require_request = __commonJS({
22495
22692
  this.completed = false;
22496
22693
  this.aborted = false;
22497
22694
  this.upgrade = upgrade || null;
22498
- this.path = query ? serializePathWithQuery(path70, query) : path70;
22695
+ this.path = query ? serializePathWithQuery(path71, query) : path71;
22499
22696
  this.origin = origin;
22500
22697
  this.protocol = getProtocolFromUrlString(origin);
22501
22698
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
@@ -27534,7 +27731,7 @@ var require_client_h1 = __commonJS({
27534
27731
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
27535
27732
  }
27536
27733
  function writeH1(client, request2) {
27537
- const { method, path: path70, host, upgrade, blocking, reset } = request2;
27734
+ const { method, path: path71, host, upgrade, blocking, reset } = request2;
27538
27735
  let { body, headers, contentLength } = request2;
27539
27736
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
27540
27737
  if (util.isFormDataLike(body)) {
@@ -27603,7 +27800,7 @@ var require_client_h1 = __commonJS({
27603
27800
  if (socket.setTypeOfService) {
27604
27801
  socket.setTypeOfService(request2.typeOfService);
27605
27802
  }
27606
- let header = `${method} ${path70} HTTP/1.1\r
27803
+ let header = `${method} ${path71} HTTP/1.1\r
27607
27804
  `;
27608
27805
  if (typeof host === "string") {
27609
27806
  header += `host: ${host}\r
@@ -28256,7 +28453,7 @@ var require_client_h2 = __commonJS({
28256
28453
  function writeH2(client, request2) {
28257
28454
  const requestTimeout = request2.bodyTimeout ?? client[kBodyTimeout];
28258
28455
  const session = client[kHTTP2Session];
28259
- const { method, path: path70, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
28456
+ const { method, path: path71, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
28260
28457
  let { body } = request2;
28261
28458
  if (upgrade != null && upgrade !== "websocket") {
28262
28459
  util.errorRequest(client, request2, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
@@ -28324,7 +28521,7 @@ var require_client_h2 = __commonJS({
28324
28521
  }
28325
28522
  headers[HTTP2_HEADER_METHOD] = "CONNECT";
28326
28523
  headers[HTTP2_HEADER_PROTOCOL] = "websocket";
28327
- headers[HTTP2_HEADER_PATH] = path70;
28524
+ headers[HTTP2_HEADER_PATH] = path71;
28328
28525
  if (protocol === "ws:" || protocol === "wss:") {
28329
28526
  headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
28330
28527
  } else {
@@ -28365,7 +28562,7 @@ var require_client_h2 = __commonJS({
28365
28562
  stream.setTimeout(requestTimeout);
28366
28563
  return true;
28367
28564
  }
28368
- headers[HTTP2_HEADER_PATH] = path70;
28565
+ headers[HTTP2_HEADER_PATH] = path71;
28369
28566
  headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
28370
28567
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
28371
28568
  if (body && typeof body.read === "function") {
@@ -30667,10 +30864,10 @@ var require_proxy_agent = __commonJS({
30667
30864
  };
30668
30865
  const {
30669
30866
  origin,
30670
- path: path70 = "/",
30867
+ path: path71 = "/",
30671
30868
  headers = {}
30672
30869
  } = opts;
30673
- opts.path = origin + path70;
30870
+ opts.path = origin + path71;
30674
30871
  if (!("host" in headers) && !("Host" in headers)) {
30675
30872
  const { host } = new URL(origin);
30676
30873
  headers.host = host;
@@ -32733,20 +32930,20 @@ var require_mock_utils = __commonJS({
32733
32930
  }
32734
32931
  return normalizedQp;
32735
32932
  }
32736
- function safeUrl(path70) {
32737
- if (typeof path70 !== "string") {
32738
- return path70;
32933
+ function safeUrl(path71) {
32934
+ if (typeof path71 !== "string") {
32935
+ return path71;
32739
32936
  }
32740
- const pathSegments = path70.split("?", 3);
32937
+ const pathSegments = path71.split("?", 3);
32741
32938
  if (pathSegments.length !== 2) {
32742
- return path70;
32939
+ return path71;
32743
32940
  }
32744
32941
  const qp = new URLSearchParams(pathSegments.pop());
32745
32942
  qp.sort();
32746
32943
  return [...pathSegments, qp.toString()].join("?");
32747
32944
  }
32748
- function matchKey(mockDispatch2, { path: path70, method, body, headers }) {
32749
- const pathMatch = matchValue(mockDispatch2.path, path70);
32945
+ function matchKey(mockDispatch2, { path: path71, method, body, headers }) {
32946
+ const pathMatch = matchValue(mockDispatch2.path, path71);
32750
32947
  const methodMatch = matchValue(mockDispatch2.method, method);
32751
32948
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
32752
32949
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -32771,8 +32968,8 @@ var require_mock_utils = __commonJS({
32771
32968
  const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path;
32772
32969
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
32773
32970
  const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath);
32774
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path70, ignoreTrailingSlash }) => {
32775
- return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path70)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path70), resolvedPath);
32971
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path71, ignoreTrailingSlash }) => {
32972
+ return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path71)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path71), resolvedPath);
32776
32973
  });
32777
32974
  if (matchedMockDispatches.length === 0) {
32778
32975
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
@@ -32811,19 +33008,19 @@ var require_mock_utils = __commonJS({
32811
33008
  mockDispatches.splice(index, 1);
32812
33009
  }
32813
33010
  }
32814
- function removeTrailingSlash(path70) {
32815
- while (path70.endsWith("/")) {
32816
- path70 = path70.slice(0, -1);
33011
+ function removeTrailingSlash(path71) {
33012
+ while (path71.endsWith("/")) {
33013
+ path71 = path71.slice(0, -1);
32817
33014
  }
32818
- if (path70.length === 0) {
32819
- path70 = "/";
33015
+ if (path71.length === 0) {
33016
+ path71 = "/";
32820
33017
  }
32821
- return path70;
33018
+ return path71;
32822
33019
  }
32823
33020
  function buildKey(opts) {
32824
- const { path: path70, method, body, headers, query } = opts;
33021
+ const { path: path71, method, body, headers, query } = opts;
32825
33022
  return {
32826
- path: path70,
33023
+ path: path71,
32827
33024
  method,
32828
33025
  body,
32829
33026
  headers,
@@ -33513,10 +33710,10 @@ var require_pending_interceptors_formatter = __commonJS({
33513
33710
  }
33514
33711
  format(pendingInterceptors) {
33515
33712
  const withPrettyHeaders = pendingInterceptors.map(
33516
- ({ method, path: path70, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
33713
+ ({ method, path: path71, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
33517
33714
  Method: method,
33518
33715
  Origin: origin,
33519
- Path: path70,
33716
+ Path: path71,
33520
33717
  "Status code": statusCode,
33521
33718
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
33522
33719
  Invocations: timesInvoked,
@@ -33598,9 +33795,9 @@ var require_mock_agent = __commonJS({
33598
33795
  const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
33599
33796
  const dispatchOpts = { ...opts };
33600
33797
  if (acceptNonStandardSearchParameters && dispatchOpts.path) {
33601
- const [path70, searchParams] = dispatchOpts.path.split("?");
33798
+ const [path71, searchParams] = dispatchOpts.path.split("?");
33602
33799
  const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
33603
- dispatchOpts.path = `${path70}?${normalizedSearchParams}`;
33800
+ dispatchOpts.path = `${path71}?${normalizedSearchParams}`;
33604
33801
  }
33605
33802
  return this[kAgent].dispatch(dispatchOpts, handler);
33606
33803
  }
@@ -34001,12 +34198,12 @@ var require_snapshot_recorder = __commonJS({
34001
34198
  * @return {Promise<void>} - Resolves when snapshots are loaded
34002
34199
  */
34003
34200
  async loadSnapshots(filePath) {
34004
- const path70 = filePath || this.#snapshotPath;
34005
- if (!path70) {
34201
+ const path71 = filePath || this.#snapshotPath;
34202
+ if (!path71) {
34006
34203
  throw new InvalidArgumentError("Snapshot path is required");
34007
34204
  }
34008
34205
  try {
34009
- const data = await readFile(resolve2(path70), "utf8");
34206
+ const data = await readFile(resolve2(path71), "utf8");
34010
34207
  const parsed = JSON.parse(data);
34011
34208
  if (Array.isArray(parsed)) {
34012
34209
  this.#snapshots.clear();
@@ -34020,7 +34217,7 @@ var require_snapshot_recorder = __commonJS({
34020
34217
  if (error.code === "ENOENT") {
34021
34218
  this.#snapshots.clear();
34022
34219
  } else {
34023
- throw new UndiciError(`Failed to load snapshots from ${path70}`, { cause: error });
34220
+ throw new UndiciError(`Failed to load snapshots from ${path71}`, { cause: error });
34024
34221
  }
34025
34222
  }
34026
34223
  }
@@ -34031,11 +34228,11 @@ var require_snapshot_recorder = __commonJS({
34031
34228
  * @returns {Promise<void>} - Resolves when snapshots are saved
34032
34229
  */
34033
34230
  async saveSnapshots(filePath) {
34034
- const path70 = filePath || this.#snapshotPath;
34035
- if (!path70) {
34231
+ const path71 = filePath || this.#snapshotPath;
34232
+ if (!path71) {
34036
34233
  throw new InvalidArgumentError("Snapshot path is required");
34037
34234
  }
34038
- const resolvedPath = resolve2(path70);
34235
+ const resolvedPath = resolve2(path71);
34039
34236
  await mkdir(dirname2(resolvedPath), { recursive: true });
34040
34237
  const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
34041
34238
  hash,
@@ -34660,15 +34857,15 @@ var require_redirect_handler = __commonJS({
34660
34857
  return;
34661
34858
  }
34662
34859
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
34663
- const path70 = search ? `${pathname}${search}` : pathname;
34664
- const redirectUrlString = `${origin}${path70}`;
34860
+ const path71 = search ? `${pathname}${search}` : pathname;
34861
+ const redirectUrlString = `${origin}${path71}`;
34665
34862
  for (const historyUrl of this.history) {
34666
34863
  if (historyUrl.toString() === redirectUrlString) {
34667
34864
  throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`);
34668
34865
  }
34669
34866
  }
34670
34867
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
34671
- this.opts.path = path70;
34868
+ this.opts.path = path71;
34672
34869
  this.opts.origin = origin;
34673
34870
  this.opts.query = null;
34674
34871
  }
@@ -40875,11 +41072,11 @@ var require_fetch = __commonJS({
40875
41072
  function dispatch({ body }) {
40876
41073
  const url = requestCurrentURL(request2);
40877
41074
  const agent = fetchParams.controller.dispatcher;
40878
- const path70 = url.pathname + url.search;
41075
+ const path71 = url.pathname + url.search;
40879
41076
  const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === "?";
40880
41077
  return new Promise((resolve2, reject) => agent.dispatch(
40881
41078
  {
40882
- path: hasTrailingQuestionMark ? `${path70}?` : path70,
41079
+ path: hasTrailingQuestionMark ? `${path71}?` : path71,
40883
41080
  origin: url.origin,
40884
41081
  method: request2.method,
40885
41082
  body: agent.isMockActive ? request2.body && (request2.body.source || request2.body.stream) : body,
@@ -41810,9 +42007,9 @@ var require_util4 = __commonJS({
41810
42007
  }
41811
42008
  }
41812
42009
  }
41813
- function validateCookiePath(path70) {
41814
- for (let i = 0; i < path70.length; ++i) {
41815
- const code = path70.charCodeAt(i);
42010
+ function validateCookiePath(path71) {
42011
+ for (let i = 0; i < path71.length; ++i) {
42012
+ const code = path71.charCodeAt(i);
41816
42013
  if (code < 32 || // exclude CTLs (0-31)
41817
42014
  code === 127 || // DEL
41818
42015
  code === 59) {
@@ -44982,11 +45179,11 @@ var require_undici = __commonJS({
44982
45179
  if (typeof opts.path !== "string") {
44983
45180
  throw new InvalidArgumentError("invalid opts.path");
44984
45181
  }
44985
- let path70 = opts.path;
45182
+ let path71 = opts.path;
44986
45183
  if (!opts.path.startsWith("/")) {
44987
- path70 = `/${path70}`;
45184
+ path71 = `/${path71}`;
44988
45185
  }
44989
- url = new URL(util.parseOrigin(url).origin + path70);
45186
+ url = new URL(util.parseOrigin(url).origin + path71);
44990
45187
  } else {
44991
45188
  if (!opts) {
44992
45189
  opts = typeof url === "object" ? url : {};
@@ -45105,9 +45302,9 @@ __export(tail_exports, {
45105
45302
  });
45106
45303
  import http5 from "http";
45107
45304
  import chalk40 from "chalk";
45108
- import fs70 from "fs";
45109
- import os59 from "os";
45110
- import path67 from "path";
45305
+ import fs71 from "fs";
45306
+ import os61 from "os";
45307
+ import path68 from "path";
45111
45308
  import readline6 from "readline";
45112
45309
  import { spawn as spawn8 } from "child_process";
45113
45310
  function shortenPathSummary(s) {
@@ -45131,20 +45328,20 @@ function getModelContextLimit(model) {
45131
45328
  return 2e5;
45132
45329
  }
45133
45330
  function readSessionUsage() {
45134
- const projectsDir = path67.join(os59.homedir(), ".claude", "projects");
45135
- if (!fs70.existsSync(projectsDir)) return null;
45331
+ const projectsDir = path68.join(os61.homedir(), ".claude", "projects");
45332
+ if (!fs71.existsSync(projectsDir)) return null;
45136
45333
  let latestFile = null;
45137
45334
  let latestMtime = 0;
45138
45335
  try {
45139
- for (const dir of fs70.readdirSync(projectsDir)) {
45140
- const dirPath = path67.join(projectsDir, dir);
45336
+ for (const dir of fs71.readdirSync(projectsDir)) {
45337
+ const dirPath = path68.join(projectsDir, dir);
45141
45338
  try {
45142
- if (!fs70.statSync(dirPath).isDirectory()) continue;
45143
- for (const file of fs70.readdirSync(dirPath)) {
45339
+ if (!fs71.statSync(dirPath).isDirectory()) continue;
45340
+ for (const file of fs71.readdirSync(dirPath)) {
45144
45341
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
45145
- const filePath = path67.join(dirPath, file);
45342
+ const filePath = path68.join(dirPath, file);
45146
45343
  try {
45147
- const mtime = fs70.statSync(filePath).mtimeMs;
45344
+ const mtime = fs71.statSync(filePath).mtimeMs;
45148
45345
  if (mtime > latestMtime) {
45149
45346
  latestMtime = mtime;
45150
45347
  latestFile = filePath;
@@ -45159,7 +45356,7 @@ function readSessionUsage() {
45159
45356
  }
45160
45357
  if (!latestFile) return null;
45161
45358
  try {
45162
- const lines = fs70.readFileSync(latestFile, "utf-8").split("\n");
45359
+ const lines = fs71.readFileSync(latestFile, "utf-8").split("\n");
45163
45360
  let lastModel = "";
45164
45361
  let lastInput = 0;
45165
45362
  let lastOutput = 0;
@@ -45220,7 +45417,7 @@ function formatBase(activity) {
45220
45417
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
45221
45418
  const icon = getIcon(activity.tool);
45222
45419
  const toolName = activity.tool.slice(0, 16).padEnd(16);
45223
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os59.homedir(), "~");
45420
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os61.homedir(), "~");
45224
45421
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
45225
45422
  return `${chalk40.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk40.white.bold(toolName)} ${chalk40.dim(argsPreview)}`;
45226
45423
  }
@@ -45259,9 +45456,9 @@ function renderPending(activity) {
45259
45456
  }
45260
45457
  async function ensureDaemon() {
45261
45458
  let pidPort = null;
45262
- if (fs70.existsSync(PID_FILE)) {
45459
+ if (fs71.existsSync(PID_FILE)) {
45263
45460
  try {
45264
- const { port } = JSON.parse(fs70.readFileSync(PID_FILE, "utf-8"));
45461
+ const { port } = JSON.parse(fs71.readFileSync(PID_FILE, "utf-8"));
45265
45462
  pidPort = port;
45266
45463
  } catch {
45267
45464
  console.error(chalk40.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -45417,9 +45614,9 @@ function buildRecoveryCardLines(req) {
45417
45614
  ];
45418
45615
  }
45419
45616
  function readApproversFromDisk() {
45420
- const configPath = path67.join(os59.homedir(), ".node9", "config.json");
45617
+ const configPath = path68.join(os61.homedir(), ".node9", "config.json");
45421
45618
  try {
45422
- const raw = JSON.parse(fs70.readFileSync(configPath, "utf-8"));
45619
+ const raw = JSON.parse(fs71.readFileSync(configPath, "utf-8"));
45423
45620
  const settings = raw.settings ?? {};
45424
45621
  return settings.approvers ?? {};
45425
45622
  } catch {
@@ -45435,15 +45632,15 @@ function approverStatusLine() {
45435
45632
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
45436
45633
  }
45437
45634
  function toggleApprover(channel) {
45438
- const configPath = path67.join(os59.homedir(), ".node9", "config.json");
45635
+ const configPath = path68.join(os61.homedir(), ".node9", "config.json");
45439
45636
  try {
45440
- const raw = JSON.parse(fs70.readFileSync(configPath, "utf-8"));
45637
+ const raw = JSON.parse(fs71.readFileSync(configPath, "utf-8"));
45441
45638
  const settings = raw.settings ?? {};
45442
45639
  const approvers = settings.approvers ?? {};
45443
45640
  approvers[channel] = approvers[channel] === false;
45444
45641
  settings.approvers = approvers;
45445
45642
  raw.settings = settings;
45446
- fs70.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
45643
+ fs71.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
45447
45644
  } catch (err2) {
45448
45645
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
45449
45646
  `);
@@ -45615,8 +45812,8 @@ async function startTail(options = {}) {
45615
45812
  }
45616
45813
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
45617
45814
  try {
45618
- fs70.appendFileSync(
45619
- path67.join(os59.homedir(), ".node9", "hook-debug.log"),
45815
+ fs71.appendFileSync(
45816
+ path68.join(os61.homedir(), ".node9", "hook-debug.log"),
45620
45817
  `[tail] POST /decision failed: ${String(err2)}
45621
45818
  `
45622
45819
  );
@@ -45680,9 +45877,9 @@ async function startTail(options = {}) {
45680
45877
  };
45681
45878
  process.stdin.on("keypress", onKeypress);
45682
45879
  }
45683
- const auditLog = path67.join(os59.homedir(), ".node9", "audit.log");
45880
+ const auditLog = path68.join(os61.homedir(), ".node9", "audit.log");
45684
45881
  try {
45685
- const unackedDlp = fs70.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
45882
+ const unackedDlp = fs71.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
45686
45883
  if (unackedDlp > 0) {
45687
45884
  console.log("");
45688
45885
  console.log(
@@ -45722,7 +45919,7 @@ async function startTail(options = {}) {
45722
45919
  if (stallWarned) return;
45723
45920
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
45724
45921
  try {
45725
- const auditMtime = fs70.statSync(auditLog).mtimeMs;
45922
+ const auditMtime = fs71.statSync(auditLog).mtimeMs;
45726
45923
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
45727
45924
  console.log("");
45728
45925
  console.log(
@@ -45913,7 +46110,7 @@ var init_tail = __esm({
45913
46110
  "use strict";
45914
46111
  init_daemon2();
45915
46112
  init_daemon();
45916
- PID_FILE = path67.join(os59.homedir(), ".node9", "daemon.pid");
46113
+ PID_FILE = path68.join(os61.homedir(), ".node9", "daemon.pid");
45917
46114
  ICONS = {
45918
46115
  bash: "\u{1F4BB}",
45919
46116
  shell: "\u{1F4BB}",
@@ -45961,9 +46158,9 @@ __export(hud_exports, {
45961
46158
  main: () => main,
45962
46159
  renderEnvironmentLine: () => renderEnvironmentLine
45963
46160
  });
45964
- import fs71 from "fs";
45965
- import path68 from "path";
45966
- import os60 from "os";
46161
+ import fs72 from "fs";
46162
+ import path69 from "path";
46163
+ import os62 from "os";
45967
46164
  import http6 from "http";
45968
46165
  async function readStdin() {
45969
46166
  const chunks = [];
@@ -46039,9 +46236,9 @@ function formatTimeLeft(resetsAt) {
46039
46236
  return ` (${m}m left)`;
46040
46237
  }
46041
46238
  function safeReadJson(filePath) {
46042
- if (!fs71.existsSync(filePath)) return null;
46239
+ if (!fs72.existsSync(filePath)) return null;
46043
46240
  try {
46044
- return JSON.parse(fs71.readFileSync(filePath, "utf-8"));
46241
+ return JSON.parse(fs72.readFileSync(filePath, "utf-8"));
46045
46242
  } catch {
46046
46243
  return null;
46047
46244
  }
@@ -46062,12 +46259,12 @@ function countHooksInFile(filePath) {
46062
46259
  return Object.keys(cfg.hooks).length;
46063
46260
  }
46064
46261
  function countRulesInDir(rulesDir) {
46065
- if (!fs71.existsSync(rulesDir)) return 0;
46262
+ if (!fs72.existsSync(rulesDir)) return 0;
46066
46263
  let count = 0;
46067
46264
  try {
46068
- for (const entry of fs71.readdirSync(rulesDir, { withFileTypes: true })) {
46265
+ for (const entry of fs72.readdirSync(rulesDir, { withFileTypes: true })) {
46069
46266
  if (entry.isDirectory()) {
46070
- count += countRulesInDir(path68.join(rulesDir, entry.name));
46267
+ count += countRulesInDir(path69.join(rulesDir, entry.name));
46071
46268
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
46072
46269
  count++;
46073
46270
  }
@@ -46078,46 +46275,46 @@ function countRulesInDir(rulesDir) {
46078
46275
  }
46079
46276
  function isSamePath(a, b) {
46080
46277
  try {
46081
- return path68.resolve(a) === path68.resolve(b);
46278
+ return path69.resolve(a) === path69.resolve(b);
46082
46279
  } catch {
46083
46280
  return false;
46084
46281
  }
46085
46282
  }
46086
46283
  function countConfigs(cwd) {
46087
- const homeDir2 = os60.homedir();
46088
- const claudeDir = path68.join(homeDir2, ".claude");
46284
+ const homeDir2 = os62.homedir();
46285
+ const claudeDir = path69.join(homeDir2, ".claude");
46089
46286
  let claudeMdCount = 0;
46090
46287
  let rulesCount = 0;
46091
46288
  let hooksCount = 0;
46092
46289
  const userMcpServers = /* @__PURE__ */ new Set();
46093
46290
  const projectMcpServers = /* @__PURE__ */ new Set();
46094
- if (fs71.existsSync(path68.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
46095
- rulesCount += countRulesInDir(path68.join(claudeDir, "rules"));
46096
- const userSettings = path68.join(claudeDir, "settings.json");
46291
+ if (fs72.existsSync(path69.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
46292
+ rulesCount += countRulesInDir(path69.join(claudeDir, "rules"));
46293
+ const userSettings = path69.join(claudeDir, "settings.json");
46097
46294
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
46098
46295
  hooksCount += countHooksInFile(userSettings);
46099
- const userClaudeJson = path68.join(homeDir2, ".claude.json");
46296
+ const userClaudeJson = path69.join(homeDir2, ".claude.json");
46100
46297
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
46101
46298
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
46102
46299
  userMcpServers.delete(name);
46103
46300
  }
46104
46301
  if (cwd) {
46105
- if (fs71.existsSync(path68.join(cwd, "CLAUDE.md"))) claudeMdCount++;
46106
- if (fs71.existsSync(path68.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
46107
- const projectClaudeDir = path68.join(cwd, ".claude");
46302
+ if (fs72.existsSync(path69.join(cwd, "CLAUDE.md"))) claudeMdCount++;
46303
+ if (fs72.existsSync(path69.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
46304
+ const projectClaudeDir = path69.join(cwd, ".claude");
46108
46305
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
46109
46306
  if (!overlapsUserScope) {
46110
- if (fs71.existsSync(path68.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
46111
- rulesCount += countRulesInDir(path68.join(projectClaudeDir, "rules"));
46112
- const projSettings = path68.join(projectClaudeDir, "settings.json");
46307
+ if (fs72.existsSync(path69.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
46308
+ rulesCount += countRulesInDir(path69.join(projectClaudeDir, "rules"));
46309
+ const projSettings = path69.join(projectClaudeDir, "settings.json");
46113
46310
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
46114
46311
  hooksCount += countHooksInFile(projSettings);
46115
46312
  }
46116
- if (fs71.existsSync(path68.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
46117
- const localSettings = path68.join(projectClaudeDir, "settings.local.json");
46313
+ if (fs72.existsSync(path69.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
46314
+ const localSettings = path69.join(projectClaudeDir, "settings.local.json");
46118
46315
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
46119
46316
  hooksCount += countHooksInFile(localSettings);
46120
- const mcpJsonServers = getMcpServerNames(path68.join(cwd, ".mcp.json"));
46317
+ const mcpJsonServers = getMcpServerNames(path69.join(cwd, ".mcp.json"));
46121
46318
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
46122
46319
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
46123
46320
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -46150,12 +46347,12 @@ function readActiveShieldsHud() {
46150
46347
  return shieldsCache.value;
46151
46348
  }
46152
46349
  try {
46153
- const shieldsPath = path68.join(os60.homedir(), ".node9", "shields.json");
46154
- if (!fs71.existsSync(shieldsPath)) {
46350
+ const shieldsPath = path69.join(os62.homedir(), ".node9", "shields.json");
46351
+ if (!fs72.existsSync(shieldsPath)) {
46155
46352
  shieldsCache = { value: [], ts: now };
46156
46353
  return [];
46157
46354
  }
46158
- const parsed = JSON.parse(fs71.readFileSync(shieldsPath, "utf-8"));
46355
+ const parsed = JSON.parse(fs72.readFileSync(shieldsPath, "utf-8"));
46159
46356
  if (!Array.isArray(parsed.active)) {
46160
46357
  shieldsCache = { value: [], ts: now };
46161
46358
  return [];
@@ -46257,17 +46454,17 @@ function renderContextLine(stdin) {
46257
46454
  async function main() {
46258
46455
  try {
46259
46456
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
46260
- if (fs71.existsSync(path68.join(os60.homedir(), ".node9", "hud-debug"))) {
46457
+ if (fs72.existsSync(path69.join(os62.homedir(), ".node9", "hud-debug"))) {
46261
46458
  try {
46262
- const logPath = path68.join(os60.homedir(), ".node9", "hud-debug.log");
46459
+ const logPath = path69.join(os62.homedir(), ".node9", "hud-debug.log");
46263
46460
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
46264
46461
  let size = 0;
46265
46462
  try {
46266
- size = fs71.statSync(logPath).size;
46463
+ size = fs72.statSync(logPath).size;
46267
46464
  } catch {
46268
46465
  }
46269
46466
  if (size < MAX_LOG_SIZE) {
46270
- fs71.appendFileSync(
46467
+ fs72.appendFileSync(
46271
46468
  logPath,
46272
46469
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
46273
46470
  );
@@ -46288,11 +46485,11 @@ async function main() {
46288
46485
  try {
46289
46486
  const cwd = stdin.cwd ?? process.cwd();
46290
46487
  for (const configPath of [
46291
- path68.join(cwd, "node9.config.json"),
46292
- path68.join(os60.homedir(), ".node9", "config.json")
46488
+ path69.join(cwd, "node9.config.json"),
46489
+ path69.join(os62.homedir(), ".node9", "config.json")
46293
46490
  ]) {
46294
- if (!fs71.existsSync(configPath)) continue;
46295
- const cfg = JSON.parse(fs71.readFileSync(configPath, "utf-8"));
46491
+ if (!fs72.existsSync(configPath)) continue;
46492
+ const cfg = JSON.parse(fs72.readFileSync(configPath, "utf-8"));
46296
46493
  const hud = cfg.settings?.hud;
46297
46494
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
46298
46495
  }
@@ -46434,9 +46631,9 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
46434
46631
  // src/cli.ts
46435
46632
  init_daemon2();
46436
46633
  import chalk41 from "chalk";
46437
- import fs72 from "fs";
46438
- import path69 from "path";
46439
- import os61 from "os";
46634
+ import fs73 from "fs";
46635
+ import path70 from "path";
46636
+ import os63 from "os";
46440
46637
  import { spawn as spawn9 } from "child_process";
46441
46638
  import { confirm as confirm2 } from "@inquirer/prompts";
46442
46639
 
@@ -46623,26 +46820,48 @@ async function runProxy(targetCommand) {
46623
46820
 
46624
46821
  // src/cli/daemon-starter.ts
46625
46822
  init_daemon();
46823
+ init_startup_log();
46626
46824
  import { spawn as spawn3 } from "child_process";
46627
- import path41 from "path";
46628
- import fs43 from "fs";
46825
+ import path42 from "path";
46826
+ import fs44 from "fs";
46827
+ import os40 from "os";
46629
46828
  function isTestingMode() {
46630
46829
  return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
46631
46830
  }
46831
+ var SKIP_STAMP = () => path42.join(os40.homedir(), ".node9", ".autostart-skip-stamp");
46832
+ var SKIP_THROTTLE_MS = 60 * 60 * 1e3;
46833
+ function logAutostartSkipThrottled(reason) {
46834
+ try {
46835
+ const stamp = SKIP_STAMP();
46836
+ try {
46837
+ if (Date.now() - fs44.statSync(stamp).mtimeMs < SKIP_THROTTLE_MS) return;
46838
+ } catch {
46839
+ }
46840
+ fs44.writeFileSync(stamp, "", "utf-8");
46841
+ fs44.appendFileSync(
46842
+ path42.join(os40.homedir(), ".node9", "hook-debug.log"),
46843
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-skip: ${reason}
46844
+ `,
46845
+ "utf-8"
46846
+ );
46847
+ } catch {
46848
+ }
46849
+ }
46632
46850
  async function autoStartDaemonAndWait() {
46633
46851
  if (isTestingMode()) return false;
46634
- if (!path41.isAbsolute(process.argv[1])) return false;
46852
+ if (!path42.isAbsolute(process.argv[1])) return false;
46635
46853
  let resolvedArgv1;
46636
46854
  try {
46637
- resolvedArgv1 = fs43.realpathSync(process.argv[1]);
46855
+ resolvedArgv1 = fs44.realpathSync(process.argv[1]);
46638
46856
  } catch {
46639
46857
  return false;
46640
46858
  }
46641
46859
  if (!resolvedArgv1.endsWith(".js")) return false;
46860
+ const startupFd = openStartupLogFd();
46642
46861
  try {
46643
46862
  const child = spawn3(process.execPath, [resolvedArgv1, "daemon"], {
46644
46863
  detached: true,
46645
- stdio: "ignore",
46864
+ stdio: ["ignore", "ignore", startupFd ?? "ignore"],
46646
46865
  env: {
46647
46866
  ...process.env,
46648
46867
  NODE9_AUTO_STARTED: "1"
@@ -46655,30 +46874,41 @@ async function autoStartDaemonAndWait() {
46655
46874
  if (await isDaemonReachable()) return true;
46656
46875
  }
46657
46876
  } catch {
46877
+ } finally {
46878
+ if (startupFd !== void 0) {
46879
+ try {
46880
+ fs44.closeSync(startupFd);
46881
+ } catch {
46882
+ }
46883
+ }
46658
46884
  }
46659
46885
  return false;
46660
46886
  }
46661
46887
 
46888
+ // src/cli.ts
46889
+ init_service();
46890
+
46662
46891
  // src/cli/commands/check.ts
46663
46892
  init_orchestrator();
46664
46893
  init_state();
46665
46894
  init_daemon();
46895
+ init_startup_log();
46666
46896
  init_config();
46667
46897
  init_policy();
46668
46898
  import chalk9 from "chalk";
46669
- import fs47 from "fs";
46899
+ import fs48 from "fs";
46670
46900
  import { spawn as spawn5 } from "child_process";
46671
- import path45 from "path";
46672
- import os42 from "os";
46901
+ import path46 from "path";
46902
+ import os44 from "os";
46673
46903
 
46674
46904
  // src/undo.ts
46675
46905
  import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
46676
46906
  import crypto7 from "crypto";
46677
- import fs44 from "fs";
46907
+ import fs45 from "fs";
46678
46908
  import net3 from "net";
46679
- import path42 from "path";
46680
- import os39 from "os";
46681
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path42.join(os39.tmpdir(), "node9-activity.sock");
46909
+ import path43 from "path";
46910
+ import os41 from "os";
46911
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path43.join(os41.tmpdir(), "node9-activity.sock");
46682
46912
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
46683
46913
  try {
46684
46914
  const payload = JSON.stringify({
@@ -46698,22 +46928,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
46698
46928
  } catch {
46699
46929
  }
46700
46930
  }
46701
- var SNAPSHOT_STACK_PATH = path42.join(os39.homedir(), ".node9", "snapshots.json");
46702
- var UNDO_LATEST_PATH = path42.join(os39.homedir(), ".node9", "undo_latest.txt");
46931
+ var SNAPSHOT_STACK_PATH = path43.join(os41.homedir(), ".node9", "snapshots.json");
46932
+ var UNDO_LATEST_PATH = path43.join(os41.homedir(), ".node9", "undo_latest.txt");
46703
46933
  var MAX_SNAPSHOTS = 10;
46704
46934
  var GIT_TIMEOUT = 15e3;
46705
46935
  function readStack() {
46706
46936
  try {
46707
- if (fs44.existsSync(SNAPSHOT_STACK_PATH))
46708
- return JSON.parse(fs44.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
46937
+ if (fs45.existsSync(SNAPSHOT_STACK_PATH))
46938
+ return JSON.parse(fs45.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
46709
46939
  } catch {
46710
46940
  }
46711
46941
  return [];
46712
46942
  }
46713
46943
  function writeStack(stack) {
46714
- const dir = path42.dirname(SNAPSHOT_STACK_PATH);
46715
- if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
46716
- fs44.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
46944
+ const dir = path43.dirname(SNAPSHOT_STACK_PATH);
46945
+ if (!fs45.existsSync(dir)) fs45.mkdirSync(dir, { recursive: true });
46946
+ fs45.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
46717
46947
  }
46718
46948
  function extractFilePath(args) {
46719
46949
  if (!args || typeof args !== "object") return null;
@@ -46733,12 +46963,12 @@ function buildArgsSummary(tool, args) {
46733
46963
  return "";
46734
46964
  }
46735
46965
  function findProjectRoot(filePath) {
46736
- let dir = path42.dirname(filePath);
46966
+ let dir = path43.dirname(filePath);
46737
46967
  while (true) {
46738
- if (fs44.existsSync(path42.join(dir, ".git")) || fs44.existsSync(path42.join(dir, "package.json"))) {
46968
+ if (fs45.existsSync(path43.join(dir, ".git")) || fs45.existsSync(path43.join(dir, "package.json"))) {
46739
46969
  return dir;
46740
46970
  }
46741
- const parent = path42.dirname(dir);
46971
+ const parent = path43.dirname(dir);
46742
46972
  if (parent === dir) return process.cwd();
46743
46973
  dir = parent;
46744
46974
  }
@@ -46746,7 +46976,7 @@ function findProjectRoot(filePath) {
46746
46976
  function normalizeCwdForHash(cwd) {
46747
46977
  let normalized;
46748
46978
  try {
46749
- normalized = fs44.realpathSync(cwd);
46979
+ normalized = fs45.realpathSync(cwd);
46750
46980
  } catch {
46751
46981
  normalized = cwd;
46752
46982
  }
@@ -46756,16 +46986,16 @@ function normalizeCwdForHash(cwd) {
46756
46986
  }
46757
46987
  function getShadowRepoDir(cwd) {
46758
46988
  const hash = crypto7.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
46759
- return path42.join(os39.homedir(), ".node9", "snapshots", hash);
46989
+ return path43.join(os41.homedir(), ".node9", "snapshots", hash);
46760
46990
  }
46761
46991
  function cleanOrphanedIndexFiles(shadowDir) {
46762
46992
  try {
46763
46993
  const cutoff = Date.now() - 6e4;
46764
- for (const f of fs44.readdirSync(shadowDir)) {
46994
+ for (const f of fs45.readdirSync(shadowDir)) {
46765
46995
  if (f.startsWith("index_")) {
46766
- const fp = path42.join(shadowDir, f);
46996
+ const fp = path43.join(shadowDir, f);
46767
46997
  try {
46768
- if (fs44.statSync(fp).mtimeMs < cutoff) fs44.unlinkSync(fp);
46998
+ if (fs45.statSync(fp).mtimeMs < cutoff) fs45.unlinkSync(fp);
46769
46999
  } catch {
46770
47000
  }
46771
47001
  }
@@ -46777,7 +47007,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
46777
47007
  const hardcoded = [".git", ".node9"];
46778
47008
  const lines = [...hardcoded, ...ignorePaths].join("\n");
46779
47009
  try {
46780
- fs44.writeFileSync(path42.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
47010
+ fs45.writeFileSync(path43.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
46781
47011
  } catch {
46782
47012
  }
46783
47013
  }
@@ -46790,25 +47020,25 @@ function ensureShadowRepo(shadowDir, cwd) {
46790
47020
  timeout: 3e3
46791
47021
  });
46792
47022
  if (check.status === 0) {
46793
- const ptPath = path42.join(shadowDir, "project-path.txt");
47023
+ const ptPath = path43.join(shadowDir, "project-path.txt");
46794
47024
  try {
46795
- const stored = fs44.readFileSync(ptPath, "utf8").trim();
47025
+ const stored = fs45.readFileSync(ptPath, "utf8").trim();
46796
47026
  if (stored === normalizedCwd) return true;
46797
47027
  if (process.env.NODE9_DEBUG === "1")
46798
47028
  console.error(
46799
47029
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
46800
47030
  );
46801
- fs44.rmSync(shadowDir, { recursive: true, force: true });
47031
+ fs45.rmSync(shadowDir, { recursive: true, force: true });
46802
47032
  } catch {
46803
47033
  try {
46804
- fs44.writeFileSync(ptPath, normalizedCwd, "utf8");
47034
+ fs45.writeFileSync(ptPath, normalizedCwd, "utf8");
46805
47035
  } catch {
46806
47036
  }
46807
47037
  return true;
46808
47038
  }
46809
47039
  }
46810
47040
  try {
46811
- fs44.mkdirSync(shadowDir, { recursive: true });
47041
+ fs45.mkdirSync(shadowDir, { recursive: true });
46812
47042
  } catch {
46813
47043
  }
46814
47044
  const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -46817,7 +47047,7 @@ function ensureShadowRepo(shadowDir, cwd) {
46817
47047
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
46818
47048
  return false;
46819
47049
  }
46820
- const configFile = path42.join(shadowDir, "config");
47050
+ const configFile = path43.join(shadowDir, "config");
46821
47051
  spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
46822
47052
  timeout: 3e3
46823
47053
  });
@@ -46825,7 +47055,7 @@ function ensureShadowRepo(shadowDir, cwd) {
46825
47055
  timeout: 3e3
46826
47056
  });
46827
47057
  try {
46828
- fs44.writeFileSync(path42.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
47058
+ fs45.writeFileSync(path43.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
46829
47059
  } catch {
46830
47060
  }
46831
47061
  return true;
@@ -46848,12 +47078,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
46848
47078
  let indexFile = null;
46849
47079
  try {
46850
47080
  const rawFilePath = extractFilePath(args);
46851
- const absFilePath = rawFilePath && path42.isAbsolute(rawFilePath) ? rawFilePath : null;
47081
+ const absFilePath = rawFilePath && path43.isAbsolute(rawFilePath) ? rawFilePath : null;
46852
47082
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
46853
47083
  const shadowDir = getShadowRepoDir(cwd);
46854
47084
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
46855
47085
  writeShadowExcludes(shadowDir, ignorePaths);
46856
- indexFile = path42.join(shadowDir, `index_${process.pid}_${Date.now()}`);
47086
+ indexFile = path43.join(shadowDir, `index_${process.pid}_${Date.now()}`);
46857
47087
  const shadowEnv = {
46858
47088
  ...process.env,
46859
47089
  GIT_DIR: shadowDir,
@@ -46925,7 +47155,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
46925
47155
  writeStack(stack);
46926
47156
  const entry = stack[stack.length - 1];
46927
47157
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
46928
- fs44.writeFileSync(UNDO_LATEST_PATH, commitHash);
47158
+ fs45.writeFileSync(UNDO_LATEST_PATH, commitHash);
46929
47159
  if (shouldGc) {
46930
47160
  spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
46931
47161
  }
@@ -46936,7 +47166,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
46936
47166
  } finally {
46937
47167
  if (indexFile) {
46938
47168
  try {
46939
- fs44.unlinkSync(indexFile);
47169
+ fs45.unlinkSync(indexFile);
46940
47170
  } catch {
46941
47171
  }
46942
47172
  }
@@ -47012,9 +47242,9 @@ function applyUndo(hash, cwd) {
47012
47242
  timeout: GIT_TIMEOUT
47013
47243
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
47014
47244
  for (const file of [...tracked, ...untracked]) {
47015
- const fullPath = path42.join(dir, file);
47016
- if (!snapshotFiles.has(file) && fs44.existsSync(fullPath)) {
47017
- fs44.unlinkSync(fullPath);
47245
+ const fullPath = path43.join(dir, file);
47246
+ if (!snapshotFiles.has(file) && fs45.existsSync(fullPath)) {
47247
+ fs45.unlinkSync(fullPath);
47018
47248
  }
47019
47249
  }
47020
47250
  return true;
@@ -47024,12 +47254,12 @@ function applyUndo(hash, cwd) {
47024
47254
  }
47025
47255
 
47026
47256
  // src/skill-pin.ts
47027
- import fs45 from "fs";
47028
- import path43 from "path";
47029
- import os40 from "os";
47257
+ import fs46 from "fs";
47258
+ import path44 from "path";
47259
+ import os42 from "os";
47030
47260
  import crypto8 from "crypto";
47031
47261
  function getPinsFilePath2() {
47032
- return path43.join(os40.homedir(), ".node9", "skill-pins.json");
47262
+ return path44.join(os42.homedir(), ".node9", "skill-pins.json");
47033
47263
  }
47034
47264
  var MAX_FILES = 5e3;
47035
47265
  var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
@@ -47043,18 +47273,18 @@ function walkDir(root) {
47043
47273
  if (out.length >= MAX_FILES) return;
47044
47274
  let entries;
47045
47275
  try {
47046
- entries = fs45.readdirSync(dir, { withFileTypes: true });
47276
+ entries = fs46.readdirSync(dir, { withFileTypes: true });
47047
47277
  } catch {
47048
47278
  return;
47049
47279
  }
47050
47280
  entries.sort((a, b) => a.name.localeCompare(b.name));
47051
47281
  for (const entry of entries) {
47052
47282
  if (out.length >= MAX_FILES) return;
47053
- const full = path43.join(dir, entry.name);
47054
- const rel = relDir ? path43.posix.join(relDir, entry.name) : entry.name;
47283
+ const full = path44.join(dir, entry.name);
47284
+ const rel = relDir ? path44.posix.join(relDir, entry.name) : entry.name;
47055
47285
  let lst;
47056
47286
  try {
47057
- lst = fs45.lstatSync(full);
47287
+ lst = fs46.lstatSync(full);
47058
47288
  } catch {
47059
47289
  continue;
47060
47290
  }
@@ -47066,7 +47296,7 @@ function walkDir(root) {
47066
47296
  if (!lst.isFile()) continue;
47067
47297
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
47068
47298
  try {
47069
- const buf = fs45.readFileSync(full);
47299
+ const buf = fs46.readFileSync(full);
47070
47300
  totalBytes += buf.length;
47071
47301
  out.push({ rel, hash: sha256Bytes(buf) });
47072
47302
  } catch {
@@ -47080,14 +47310,14 @@ function walkDir(root) {
47080
47310
  function hashSkillRoot(absPath) {
47081
47311
  let lst;
47082
47312
  try {
47083
- lst = fs45.lstatSync(absPath);
47313
+ lst = fs46.lstatSync(absPath);
47084
47314
  } catch {
47085
47315
  return { exists: false, contentHash: "", fileCount: 0 };
47086
47316
  }
47087
47317
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
47088
47318
  if (lst.isFile()) {
47089
47319
  try {
47090
- return { exists: true, contentHash: sha256Bytes(fs45.readFileSync(absPath)), fileCount: 1 };
47320
+ return { exists: true, contentHash: sha256Bytes(fs46.readFileSync(absPath)), fileCount: 1 };
47091
47321
  } catch {
47092
47322
  return { exists: false, contentHash: "", fileCount: 0 };
47093
47323
  }
@@ -47105,7 +47335,7 @@ function getRootKey(absPath) {
47105
47335
  function readSkillPinsSafe() {
47106
47336
  const filePath = getPinsFilePath2();
47107
47337
  try {
47108
- const raw = fs45.readFileSync(filePath, "utf-8");
47338
+ const raw = fs46.readFileSync(filePath, "utf-8");
47109
47339
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
47110
47340
  const parsed = JSON.parse(raw);
47111
47341
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -47125,10 +47355,10 @@ function readSkillPins() {
47125
47355
  }
47126
47356
  function writeSkillPins(data) {
47127
47357
  const filePath = getPinsFilePath2();
47128
- fs45.mkdirSync(path43.dirname(filePath), { recursive: true });
47358
+ fs46.mkdirSync(path44.dirname(filePath), { recursive: true });
47129
47359
  const tmp = `${filePath}.${crypto8.randomBytes(6).toString("hex")}.tmp`;
47130
- fs45.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
47131
- fs45.renameSync(tmp, filePath);
47360
+ fs46.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
47361
+ fs46.renameSync(tmp, filePath);
47132
47362
  }
47133
47363
  function removePin2(rootKey) {
47134
47364
  const pins = readSkillPins();
@@ -47172,36 +47402,36 @@ function verifyAndPinRoots(roots) {
47172
47402
  return { kind: "verified" };
47173
47403
  }
47174
47404
  function defaultSkillRoots(_cwd) {
47175
- const marketplaces = path43.join(os40.homedir(), ".claude", "plugins", "marketplaces");
47405
+ const marketplaces = path44.join(os42.homedir(), ".claude", "plugins", "marketplaces");
47176
47406
  const roots = [];
47177
47407
  let registries;
47178
47408
  try {
47179
- registries = fs45.readdirSync(marketplaces, { withFileTypes: true });
47409
+ registries = fs46.readdirSync(marketplaces, { withFileTypes: true });
47180
47410
  } catch {
47181
47411
  return [];
47182
47412
  }
47183
47413
  for (const registry of registries) {
47184
47414
  if (!registry.isDirectory()) continue;
47185
- const pluginsDir = path43.join(marketplaces, registry.name, "plugins");
47415
+ const pluginsDir = path44.join(marketplaces, registry.name, "plugins");
47186
47416
  let plugins;
47187
47417
  try {
47188
- plugins = fs45.readdirSync(pluginsDir, { withFileTypes: true });
47418
+ plugins = fs46.readdirSync(pluginsDir, { withFileTypes: true });
47189
47419
  } catch {
47190
47420
  continue;
47191
47421
  }
47192
47422
  for (const plugin of plugins) {
47193
47423
  if (!plugin.isDirectory()) continue;
47194
- roots.push(path43.join(pluginsDir, plugin.name));
47424
+ roots.push(path44.join(pluginsDir, plugin.name));
47195
47425
  }
47196
47426
  }
47197
47427
  return roots;
47198
47428
  }
47199
47429
  function resolveUserSkillRoot(entry, cwd) {
47200
47430
  if (!entry) return null;
47201
- if (entry.startsWith("~/") || entry === "~") return path43.join(os40.homedir(), entry.slice(1));
47202
- if (path43.isAbsolute(entry)) return entry;
47203
- if (!cwd || !path43.isAbsolute(cwd)) return null;
47204
- return path43.join(cwd, entry);
47431
+ if (entry.startsWith("~/") || entry === "~") return path44.join(os42.homedir(), entry.slice(1));
47432
+ if (path44.isAbsolute(entry)) return entry;
47433
+ if (!cwd || !path44.isAbsolute(cwd)) return null;
47434
+ return path44.join(cwd, entry);
47205
47435
  }
47206
47436
 
47207
47437
  // src/cli/commands/check.ts
@@ -47210,11 +47440,11 @@ init_audit();
47210
47440
 
47211
47441
  // src/review-pending.ts
47212
47442
  init_hasher();
47213
- import fs46 from "fs";
47214
- import os41 from "os";
47215
- import path44 from "path";
47443
+ import fs47 from "fs";
47444
+ import os43 from "os";
47445
+ import path45 from "path";
47216
47446
  function storePath() {
47217
- return process.env.NODE9_PENDING_STORE || path44.join(os41.homedir(), ".node9", "pending-reviews.json");
47447
+ return process.env.NODE9_PENDING_STORE || path45.join(os43.homedir(), ".node9", "pending-reviews.json");
47218
47448
  }
47219
47449
  var TTL_MS2 = 6 * 60 * 60 * 1e3;
47220
47450
  var MAX_ENTRIES = 500;
@@ -47231,7 +47461,7 @@ function reviewCorrelationKey(payload) {
47231
47461
  }
47232
47462
  function read() {
47233
47463
  try {
47234
- const parsed = JSON.parse(fs46.readFileSync(storePath(), "utf-8"));
47464
+ const parsed = JSON.parse(fs47.readFileSync(storePath(), "utf-8"));
47235
47465
  if (parsed && Array.isArray(parsed.entries)) return parsed;
47236
47466
  } catch {
47237
47467
  }
@@ -47240,11 +47470,11 @@ function read() {
47240
47470
  function write(store) {
47241
47471
  try {
47242
47472
  const p = storePath();
47243
- const dir = path44.dirname(p);
47244
- if (!fs46.existsSync(dir)) fs46.mkdirSync(dir, { recursive: true });
47473
+ const dir = path45.dirname(p);
47474
+ if (!fs47.existsSync(dir)) fs47.mkdirSync(dir, { recursive: true });
47245
47475
  const tmp = `${p}.${process.pid}.tmp`;
47246
- fs46.writeFileSync(tmp, JSON.stringify(store));
47247
- fs46.renameSync(tmp, p);
47476
+ fs47.writeFileSync(tmp, JSON.stringify(store));
47477
+ fs47.renameSync(tmp, p);
47248
47478
  } catch {
47249
47479
  }
47250
47480
  }
@@ -47357,9 +47587,9 @@ function registerCheckCommand(program2) {
47357
47587
  } catch (err2) {
47358
47588
  const tempConfig = getConfig();
47359
47589
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
47360
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47590
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47361
47591
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
47362
- fs47.appendFileSync(
47592
+ fs48.appendFileSync(
47363
47593
  logPath,
47364
47594
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
47365
47595
  RAW: ${raw}
@@ -47372,14 +47602,14 @@ RAW: ${raw}
47372
47602
  const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
47373
47603
  if (process.env.NODE9_DEBUG === "1") {
47374
47604
  try {
47375
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47376
- if (!fs47.existsSync(path45.dirname(logPath)))
47377
- fs47.mkdirSync(path45.dirname(logPath), { recursive: true });
47605
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47606
+ if (!fs48.existsSync(path46.dirname(logPath)))
47607
+ fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
47378
47608
  const sanitized = JSON.stringify({
47379
47609
  ...payload,
47380
47610
  prompt: `<redacted, ${prompt.length} bytes>`
47381
47611
  });
47382
- fs47.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
47612
+ fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
47383
47613
  `);
47384
47614
  } catch {
47385
47615
  }
@@ -47400,8 +47630,8 @@ RAW: ${raw}
47400
47630
  );
47401
47631
  const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
47402
47632
  try {
47403
- const ttyFd = fs47.openSync("/dev/tty", "w");
47404
- fs47.writeSync(
47633
+ const ttyFd = fs48.openSync("/dev/tty", "w");
47634
+ fs48.writeSync(
47405
47635
  ttyFd,
47406
47636
  chalk9.bgRed.white.bold(`
47407
47637
  \u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
@@ -47411,7 +47641,7 @@ RAW: ${raw}
47411
47641
 
47412
47642
  `)
47413
47643
  );
47414
- fs47.closeSync(ttyFd);
47644
+ fs48.closeSync(ttyFd);
47415
47645
  } catch {
47416
47646
  }
47417
47647
  const isCodex = agent2 === "Codex";
@@ -47430,16 +47660,17 @@ RAW: ${raw}
47430
47660
  process.exit(2);
47431
47661
  }
47432
47662
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
47433
- const safeCwdForConfig = typeof payloadCwd === "string" && path45.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47663
+ const safeCwdForConfig = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47434
47664
  const config = getConfig(safeCwdForConfig);
47435
- if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
47665
+ const daemonDown = !isDaemonRunning();
47666
+ if (config.settings.autoStartDaemon && daemonDown && !process.env.NODE9_NO_AUTO_DAEMON) {
47436
47667
  try {
47437
47668
  const scriptPath = process.argv[1];
47438
- if (typeof scriptPath !== "string" || !path45.isAbsolute(scriptPath))
47669
+ if (typeof scriptPath !== "string" || !path46.isAbsolute(scriptPath))
47439
47670
  throw new Error("node9: argv[1] is not an absolute path");
47440
- const resolvedScript = fs47.realpathSync(scriptPath);
47441
- const packageDist = fs47.realpathSync(path45.resolve(__dirname, "../.."));
47442
- if (!resolvedScript.startsWith(packageDist + path45.sep) && resolvedScript !== packageDist)
47671
+ const resolvedScript = fs48.realpathSync(scriptPath);
47672
+ const packageDist = fs48.realpathSync(path46.resolve(__dirname, "../.."));
47673
+ if (!resolvedScript.startsWith(packageDist + path46.sep) && resolvedScript !== packageDist)
47443
47674
  throw new Error(
47444
47675
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
47445
47676
  );
@@ -47454,17 +47685,27 @@ RAW: ${raw}
47454
47685
  ]) {
47455
47686
  delete safeEnv[key];
47456
47687
  }
47457
- const d = spawn5(process.execPath, [scriptPath, "daemon"], {
47458
- detached: true,
47459
- stdio: "ignore",
47460
- env: { ...safeEnv, NODE9_AUTO_STARTED: "1" }
47461
- });
47462
- d.unref();
47688
+ const startupFd = openStartupLogFd();
47689
+ try {
47690
+ const d = spawn5(process.execPath, [scriptPath, "daemon"], {
47691
+ detached: true,
47692
+ stdio: ["ignore", "ignore", startupFd ?? "ignore"],
47693
+ env: { ...safeEnv, NODE9_AUTO_STARTED: "1" }
47694
+ });
47695
+ d.unref();
47696
+ } finally {
47697
+ if (startupFd !== void 0) {
47698
+ try {
47699
+ fs48.closeSync(startupFd);
47700
+ } catch {
47701
+ }
47702
+ }
47703
+ }
47463
47704
  } catch (spawnErr) {
47464
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47705
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47465
47706
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
47466
47707
  try {
47467
- fs47.appendFileSync(
47708
+ fs48.appendFileSync(
47468
47709
  logPath,
47469
47710
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
47470
47711
  `
@@ -47472,12 +47713,16 @@ RAW: ${raw}
47472
47713
  } catch {
47473
47714
  }
47474
47715
  }
47716
+ } else if (daemonDown && !isTestingMode()) {
47717
+ logAutostartSkipThrottled(
47718
+ !config.settings.autoStartDaemon ? "autoStartDaemon=false" : process.env.NODE9_NO_AUTO_DAEMON ? "NODE9_NO_AUTO_DAEMON" : "unknown"
47719
+ );
47475
47720
  }
47476
47721
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
47477
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47478
- if (!fs47.existsSync(path45.dirname(logPath)))
47479
- fs47.mkdirSync(path45.dirname(logPath), { recursive: true });
47480
- fs47.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
47722
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47723
+ if (!fs48.existsSync(path46.dirname(logPath)))
47724
+ fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
47725
+ fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
47481
47726
  `);
47482
47727
  }
47483
47728
  const rawToolName = sanitize2(extractToolName(payload));
@@ -47491,8 +47736,8 @@ RAW: ${raw}
47491
47736
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
47492
47737
  let ttyFd = null;
47493
47738
  try {
47494
- ttyFd = fs47.openSync("/dev/tty", "w");
47495
- const writeTty = (line) => fs47.writeSync(ttyFd, line + "\n");
47739
+ ttyFd = fs48.openSync("/dev/tty", "w");
47740
+ const writeTty = (line) => fs48.writeSync(ttyFd, line + "\n");
47496
47741
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
47497
47742
  writeTty(chalk9.bgRed.white.bold(`
47498
47743
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -47511,7 +47756,7 @@ RAW: ${raw}
47511
47756
  } finally {
47512
47757
  if (ttyFd !== null)
47513
47758
  try {
47514
- fs47.closeSync(ttyFd);
47759
+ fs48.closeSync(ttyFd);
47515
47760
  } catch {
47516
47761
  }
47517
47762
  }
@@ -47568,8 +47813,8 @@ RAW: ${raw}
47568
47813
  } catch {
47569
47814
  }
47570
47815
  try {
47571
- const ttyFd = fs47.openSync("/dev/tty", "w");
47572
- fs47.writeSync(
47816
+ const ttyFd = fs48.openSync("/dev/tty", "w");
47817
+ fs48.writeSync(
47573
47818
  ttyFd,
47574
47819
  chalk9.yellow(
47575
47820
  `
@@ -47577,7 +47822,7 @@ RAW: ${raw}
47577
47822
  `
47578
47823
  )
47579
47824
  );
47580
- fs47.closeSync(ttyFd);
47825
+ fs48.closeSync(ttyFd);
47581
47826
  } catch {
47582
47827
  }
47583
47828
  if (agent === "GitHub Copilot") {
@@ -47609,17 +47854,17 @@ RAW: ${raw}
47609
47854
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
47610
47855
  if (skillPinCfg.enabled && safeSessionId) {
47611
47856
  try {
47612
- const sessionsDir = path45.join(os42.homedir(), ".node9", "skill-sessions");
47613
- const flagPath = path45.join(sessionsDir, `${safeSessionId}.json`);
47857
+ const sessionsDir = path46.join(os44.homedir(), ".node9", "skill-sessions");
47858
+ const flagPath = path46.join(sessionsDir, `${safeSessionId}.json`);
47614
47859
  let flag = null;
47615
47860
  try {
47616
- flag = JSON.parse(fs47.readFileSync(flagPath, "utf-8"));
47861
+ flag = JSON.parse(fs48.readFileSync(flagPath, "utf-8"));
47617
47862
  } catch {
47618
47863
  }
47619
47864
  const writeFlag = (data2) => {
47620
47865
  try {
47621
- fs47.mkdirSync(sessionsDir, { recursive: true });
47622
- fs47.writeFileSync(
47866
+ fs48.mkdirSync(sessionsDir, { recursive: true });
47867
+ fs48.writeFileSync(
47623
47868
  flagPath,
47624
47869
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
47625
47870
  { mode: 384 }
@@ -47630,8 +47875,8 @@ RAW: ${raw}
47630
47875
  const sendSkillWarn = (detail, recoveryCmd) => {
47631
47876
  let ttyFd = null;
47632
47877
  try {
47633
- ttyFd = fs47.openSync("/dev/tty", "w");
47634
- const w = (line) => fs47.writeSync(ttyFd, line + "\n");
47878
+ ttyFd = fs48.openSync("/dev/tty", "w");
47879
+ const w = (line) => fs48.writeSync(ttyFd, line + "\n");
47635
47880
  w(chalk9.yellow(`
47636
47881
  \u26A0\uFE0F Node9: installed skill drift detected`));
47637
47882
  w(chalk9.gray(` ${detail}`));
@@ -47646,7 +47891,7 @@ RAW: ${raw}
47646
47891
  } finally {
47647
47892
  if (ttyFd !== null)
47648
47893
  try {
47649
- fs47.closeSync(ttyFd);
47894
+ fs48.closeSync(ttyFd);
47650
47895
  } catch {
47651
47896
  }
47652
47897
  }
@@ -47662,7 +47907,7 @@ RAW: ${raw}
47662
47907
  return;
47663
47908
  }
47664
47909
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
47665
- const absoluteCwd = typeof payloadCwd === "string" && path45.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47910
+ const absoluteCwd = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47666
47911
  const extraRoots = skillPinCfg.roots;
47667
47912
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
47668
47913
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -47703,10 +47948,10 @@ RAW: ${raw}
47703
47948
  }
47704
47949
  try {
47705
47950
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
47706
- for (const name of fs47.readdirSync(sessionsDir)) {
47707
- const p = path45.join(sessionsDir, name);
47951
+ for (const name of fs48.readdirSync(sessionsDir)) {
47952
+ const p = path46.join(sessionsDir, name);
47708
47953
  try {
47709
- if (fs47.statSync(p).mtimeMs < cutoff) fs47.unlinkSync(p);
47954
+ if (fs48.statSync(p).mtimeMs < cutoff) fs48.unlinkSync(p);
47710
47955
  } catch {
47711
47956
  }
47712
47957
  }
@@ -47716,9 +47961,9 @@ RAW: ${raw}
47716
47961
  } catch (err2) {
47717
47962
  if (process.env.NODE9_DEBUG === "1") {
47718
47963
  try {
47719
- const dbg = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47964
+ const dbg = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47720
47965
  const msg = err2 instanceof Error ? err2.message : String(err2);
47721
- fs47.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
47966
+ fs48.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
47722
47967
  `);
47723
47968
  } catch {
47724
47969
  }
@@ -47728,7 +47973,7 @@ RAW: ${raw}
47728
47973
  if (shouldSnapshot(toolName, toolInput, config)) {
47729
47974
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
47730
47975
  }
47731
- const safeCwdForAuth = typeof payloadCwd === "string" && path45.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47976
+ const safeCwdForAuth = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47732
47977
  const askMode = resolveAskMode(agent, opts, config);
47733
47978
  const result = await authorizeHeadless(toolName, toolInput, meta, {
47734
47979
  cwd: safeCwdForAuth,
@@ -47746,12 +47991,12 @@ RAW: ${raw}
47746
47991
  }
47747
47992
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
47748
47993
  try {
47749
- const tty = fs47.openSync("/dev/tty", "w");
47750
- fs47.writeSync(
47994
+ const tty = fs48.openSync("/dev/tty", "w");
47995
+ fs48.writeSync(
47751
47996
  tty,
47752
47997
  chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
47753
47998
  );
47754
- fs47.closeSync(tty);
47999
+ fs48.closeSync(tty);
47755
48000
  } catch {
47756
48001
  }
47757
48002
  const daemonReady = await autoStartDaemonAndWait();
@@ -47778,9 +48023,9 @@ RAW: ${raw}
47778
48023
  });
47779
48024
  } catch (err2) {
47780
48025
  if (process.env.NODE9_DEBUG === "1") {
47781
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
48026
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47782
48027
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
47783
- fs47.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
48028
+ fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
47784
48029
  `);
47785
48030
  }
47786
48031
  process.exit(0);
@@ -47816,9 +48061,9 @@ RAW: ${raw}
47816
48061
  // src/cli/commands/log.ts
47817
48062
  init_audit();
47818
48063
  init_config();
47819
- import fs48 from "fs";
47820
- import path46 from "path";
47821
- import os43 from "os";
48064
+ import fs49 from "fs";
48065
+ import path47 from "path";
48066
+ import os45 from "os";
47822
48067
  init_daemon();
47823
48068
  init_dlp();
47824
48069
 
@@ -47926,10 +48171,10 @@ function registerLogCommand(program2) {
47926
48171
  if (rawToolName !== tool) entry.agentToolName = rawToolName;
47927
48172
  const payloadSessionId = payload.session_id ?? payload.conversationId;
47928
48173
  if (payloadSessionId) entry.sessionId = payloadSessionId;
47929
- const logPath = path46.join(os43.homedir(), ".node9", "audit.log");
47930
- if (!fs48.existsSync(path46.dirname(logPath)))
47931
- fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
47932
- fs48.appendFileSync(logPath, JSON.stringify(entry) + "\n");
48174
+ const logPath = path47.join(os45.homedir(), ".node9", "audit.log");
48175
+ if (!fs49.existsSync(path47.dirname(logPath)))
48176
+ fs49.mkdirSync(path47.dirname(logPath), { recursive: true });
48177
+ fs49.appendFileSync(logPath, JSON.stringify(entry) + "\n");
47933
48178
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
47934
48179
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
47935
48180
  if (command) {
@@ -47963,7 +48208,7 @@ function registerLogCommand(program2) {
47963
48208
  }
47964
48209
  }
47965
48210
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
47966
- const safeCwd = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48211
+ const safeCwd = typeof payloadCwd === "string" && path47.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47967
48212
  const config = getConfig(safeCwd);
47968
48213
  {
47969
48214
  const toolOutput = payload.tool_response?.output;
@@ -48040,9 +48285,9 @@ function registerLogCommand(program2) {
48040
48285
  const msg = err2 instanceof Error ? err2.message : String(err2);
48041
48286
  process.stderr.write(`[Node9] audit log error: ${msg}
48042
48287
  `);
48043
- const debugPath = path46.join(os43.homedir(), ".node9", "hook-debug.log");
48288
+ const debugPath = path47.join(os45.homedir(), ".node9", "hook-debug.log");
48044
48289
  try {
48045
- fs48.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
48290
+ fs49.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
48046
48291
  `);
48047
48292
  } catch {
48048
48293
  }
@@ -48069,16 +48314,16 @@ function registerLogCommand(program2) {
48069
48314
  init_shields();
48070
48315
  init_build();
48071
48316
  import chalk10 from "chalk";
48072
- import fs50 from "fs";
48073
- import path48 from "path";
48074
- import os44 from "os";
48317
+ import fs51 from "fs";
48318
+ import path49 from "path";
48319
+ import os46 from "os";
48075
48320
 
48076
48321
  // src/shields/create.ts
48077
48322
  init_dist();
48078
48323
  init_shields();
48079
48324
  init_audit();
48080
- import fs49 from "fs";
48081
- import path47 from "path";
48325
+ import fs50 from "fs";
48326
+ import path48 from "path";
48082
48327
  function builtinNames() {
48083
48328
  const names = /* @__PURE__ */ new Set();
48084
48329
  for (const def of Object.values(BUILTIN_SHIELDS)) {
@@ -48095,8 +48340,8 @@ function createShield(def, opts = {}) {
48095
48340
  error: `"${name}" is a built-in shield \u2014 choose a different name (a user shield with this name would shadow the built-in).`
48096
48341
  };
48097
48342
  }
48098
- const filePath = path47.join(USER_SHIELDS_DIR_PATH, `${name}.json`);
48099
- if (!opts.overwrite && fs49.existsSync(filePath)) {
48343
+ const filePath = path48.join(USER_SHIELDS_DIR_PATH, `${name}.json`);
48344
+ if (!opts.overwrite && fs50.existsSync(filePath)) {
48100
48345
  return {
48101
48346
  ok: false,
48102
48347
  error: `Shield "${name}" already exists at ${filePath}. Pass --overwrite to replace it.`
@@ -48161,8 +48406,8 @@ var COMMUNITY_INDEX_URL = "https://raw.githubusercontent.com/node9ai/node9-proxy
48161
48406
  function readCloudShields() {
48162
48407
  const out = /* @__PURE__ */ new Set();
48163
48408
  try {
48164
- const file = path48.join(os44.homedir(), ".node9", "rules-cache.json");
48165
- const raw = JSON.parse(fs50.readFileSync(file, "utf-8"));
48409
+ const file = path49.join(os46.homedir(), ".node9", "rules-cache.json");
48410
+ const raw = JSON.parse(fs51.readFileSync(file, "utf-8"));
48166
48411
  for (const r of raw.rules ?? []) {
48167
48412
  const rule = r;
48168
48413
  const fromSource = rule.source?.startsWith("SHIELD:") ? rule.source.slice("SHIELD:".length).toLowerCase() : void 0;
@@ -48479,7 +48724,7 @@ function registerShieldCommand(program2) {
48479
48724
  if (opts.fromFile) {
48480
48725
  let raw;
48481
48726
  try {
48482
- raw = JSON.parse(fs50.readFileSync(opts.fromFile, "utf-8"));
48727
+ raw = JSON.parse(fs51.readFileSync(opts.fromFile, "utf-8"));
48483
48728
  } catch (err2) {
48484
48729
  console.error(
48485
48730
  chalk10.red(`
@@ -48601,14 +48846,31 @@ function registerConfigShowCommand(program2) {
48601
48846
  init_daemon();
48602
48847
  init_config();
48603
48848
  init_agent_wiring();
48849
+ init_sync();
48850
+ init_service();
48604
48851
  import chalk11 from "chalk";
48605
- import fs51 from "fs";
48606
- import path49 from "path";
48607
- import os45 from "os";
48852
+ import fs52 from "fs";
48853
+ import path50 from "path";
48854
+ import os47 from "os";
48608
48855
  import { execSync } from "child_process";
48856
+
48857
+ // src/lib/relative-time.ts
48858
+ function agoLabel(iso, now = Date.now()) {
48859
+ const ms = now - new Date(iso).getTime();
48860
+ if (!Number.isFinite(ms) || ms < 0) return "just now";
48861
+ const min = Math.floor(ms / 6e4);
48862
+ if (min < 1) return "just now";
48863
+ if (min < 60) return `${min} min ago`;
48864
+ const hr = Math.floor(min / 60);
48865
+ if (hr < 24) return `${hr} hour${hr === 1 ? "" : "s"} ago`;
48866
+ const d = Math.floor(hr / 24);
48867
+ return `${d} day${d === 1 ? "" : "s"} ago`;
48868
+ }
48869
+
48870
+ // src/cli/commands/doctor.ts
48609
48871
  function registerDoctorCommand(program2, version2) {
48610
48872
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
48611
- const homeDir2 = os45.homedir();
48873
+ const homeDir2 = os47.homedir();
48612
48874
  let failures = 0;
48613
48875
  function pass(msg) {
48614
48876
  console.log(chalk11.green(" \u2705 ") + msg);
@@ -48654,10 +48916,10 @@ function registerDoctorCommand(program2, version2) {
48654
48916
  );
48655
48917
  }
48656
48918
  section("Configuration");
48657
- const globalConfigPath = path49.join(homeDir2, ".node9", "config.json");
48658
- if (fs51.existsSync(globalConfigPath)) {
48919
+ const globalConfigPath = path50.join(homeDir2, ".node9", "config.json");
48920
+ if (fs52.existsSync(globalConfigPath)) {
48659
48921
  try {
48660
- JSON.parse(fs51.readFileSync(globalConfigPath, "utf-8"));
48922
+ JSON.parse(fs52.readFileSync(globalConfigPath, "utf-8"));
48661
48923
  pass("~/.node9/config.json found and valid");
48662
48924
  } catch {
48663
48925
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -48665,10 +48927,10 @@ function registerDoctorCommand(program2, version2) {
48665
48927
  } else {
48666
48928
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
48667
48929
  }
48668
- const projectConfigPath = path49.join(process.cwd(), "node9.config.json");
48669
- if (fs51.existsSync(projectConfigPath)) {
48930
+ const projectConfigPath = path50.join(process.cwd(), "node9.config.json");
48931
+ if (fs52.existsSync(projectConfigPath)) {
48670
48932
  try {
48671
- JSON.parse(fs51.readFileSync(projectConfigPath, "utf-8"));
48933
+ JSON.parse(fs52.readFileSync(projectConfigPath, "utf-8"));
48672
48934
  pass("node9.config.json found and valid (project)");
48673
48935
  } catch {
48674
48936
  fail(
@@ -48677,8 +48939,8 @@ function registerDoctorCommand(program2, version2) {
48677
48939
  );
48678
48940
  }
48679
48941
  }
48680
- const credsPath = path49.join(homeDir2, ".node9", "credentials.json");
48681
- if (fs51.existsSync(credsPath)) {
48942
+ const credsPath = path50.join(homeDir2, ".node9", "credentials.json");
48943
+ if (fs52.existsSync(credsPath)) {
48682
48944
  pass("Cloud credentials found (~/.node9/credentials.json)");
48683
48945
  } else {
48684
48946
  warn(
@@ -48718,11 +48980,31 @@ function registerDoctorCommand(program2, version2) {
48718
48980
  "Run: node9 daemon --background"
48719
48981
  );
48720
48982
  }
48983
+ const autostart = autostartAdvice({
48984
+ installed: isDaemonServiceInstalled(),
48985
+ enabled: isDaemonServiceEnabled(),
48986
+ cloudEnabled: !!getConfig().settings.approvers?.cloud
48987
+ });
48988
+ if (autostart) warn(autostart.message, autostart.hint);
48989
+ if (fs52.existsSync(path50.join(os47.homedir(), ".node9", "credentials.json")) && getConfig().settings.approvers?.cloud) {
48990
+ section("Policy sync");
48991
+ const health = readSyncHealth();
48992
+ if (isPolicyStale(Date.now(), health)) {
48993
+ const when = health.lastCheckedAt ? `last reached the cloud ${agoLabel(health.lastCheckedAt)}` : "never reached the cloud";
48994
+ const fails = health.consecutiveFailures > 0 ? ` (${health.consecutiveFailures} consecutive failure${health.consecutiveFailures === 1 ? "" : "s"}${health.lastError ? `: ${health.lastError}` : ""})` : "";
48995
+ warn(
48996
+ `Cloud policy is STALE \u2014 ${when}${fails}. The cached policy is still enforced, but changes from the dashboard are not reaching this machine.`,
48997
+ "Run: node9 policy sync (and ensure the daemon autostarts: systemctl --user enable --now node9-daemon)"
48998
+ );
48999
+ } else if (health.lastCheckedAt) {
49000
+ pass(`Cloud policy fresh \u2014 last synced ${agoLabel(health.lastCheckedAt)}`);
49001
+ }
49002
+ }
48721
49003
  section("Cloud audit shipping");
48722
49004
  try {
48723
49005
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
48724
49006
  const cfg = getConfig();
48725
- const creds = fs51.existsSync(path49.join(os45.homedir(), ".node9", "credentials.json"));
49007
+ const creds = fs52.existsSync(path50.join(os47.homedir(), ".node9", "credentials.json"));
48726
49008
  if (!creds) {
48727
49009
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
48728
49010
  } else if (!cfg.settings.approvers.cloud) {
@@ -48772,9 +49054,9 @@ function registerDoctorCommand(program2, version2) {
48772
49054
 
48773
49055
  // src/cli/commands/audit.ts
48774
49056
  import chalk12 from "chalk";
48775
- import fs52 from "fs";
48776
- import path50 from "path";
48777
- import os46 from "os";
49057
+ import fs53 from "fs";
49058
+ import path51 from "path";
49059
+ import os48 from "os";
48778
49060
  function formatRelativeTime(timestamp) {
48779
49061
  const diff = Date.now() - new Date(timestamp).getTime();
48780
49062
  const sec = Math.floor(diff / 1e3);
@@ -48787,14 +49069,14 @@ function formatRelativeTime(timestamp) {
48787
49069
  }
48788
49070
  function registerAuditCommand(program2) {
48789
49071
  program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
48790
- const logPath = path50.join(os46.homedir(), ".node9", "audit.log");
48791
- if (!fs52.existsSync(logPath)) {
49072
+ const logPath = path51.join(os48.homedir(), ".node9", "audit.log");
49073
+ if (!fs53.existsSync(logPath)) {
48792
49074
  console.log(
48793
49075
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
48794
49076
  );
48795
49077
  return;
48796
49078
  }
48797
- const raw = fs52.readFileSync(logPath, "utf-8");
49079
+ const raw = fs53.readFileSync(logPath, "utf-8");
48798
49080
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
48799
49081
  let entries = lines.flatMap((line) => {
48800
49082
  try {
@@ -48853,9 +49135,9 @@ import chalk13 from "chalk";
48853
49135
  init_costSync();
48854
49136
  init_litellm();
48855
49137
  init_cost_codex();
48856
- import fs53 from "fs";
48857
- import os47 from "os";
48858
- import path51 from "path";
49138
+ import fs54 from "fs";
49139
+ import os49 from "os";
49140
+ import path52 from "path";
48859
49141
  var TEST_COMMAND_RE3 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
48860
49142
  function buildTestTimestamps(allEntries) {
48861
49143
  const testTs = /* @__PURE__ */ new Set();
@@ -48935,8 +49217,8 @@ function getDateRange(period, now) {
48935
49217
  }
48936
49218
  }
48937
49219
  function parseAuditLog(logPath) {
48938
- if (!fs53.existsSync(logPath)) return [];
48939
- const raw = fs53.readFileSync(logPath, "utf-8");
49220
+ if (!fs54.existsSync(logPath)) return [];
49221
+ const raw = fs54.readFileSync(logPath, "utf-8");
48940
49222
  return raw.split("\n").flatMap((line) => {
48941
49223
  if (!line.trim()) return [];
48942
49224
  try {
@@ -48983,25 +49265,25 @@ function freezeClaudeCost(acc) {
48983
49265
  };
48984
49266
  }
48985
49267
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
48986
- const projPath = path51.join(projectsDir, proj);
49268
+ const projPath = path52.join(projectsDir, proj);
48987
49269
  let files;
48988
49270
  try {
48989
- const stat = fs53.statSync(projPath);
49271
+ const stat = fs54.statSync(projPath);
48990
49272
  if (!stat.isDirectory()) return;
48991
- files = fs53.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
49273
+ files = fs54.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
48992
49274
  } catch {
48993
49275
  return;
48994
49276
  }
48995
49277
  const startMs = start.getTime();
48996
49278
  for (const file of files) {
48997
- const filePath = path51.join(projPath, file);
49279
+ const filePath = path52.join(projPath, file);
48998
49280
  try {
48999
- if (fs53.statSync(filePath).mtimeMs < startMs) continue;
49281
+ if (fs54.statSync(filePath).mtimeMs < startMs) continue;
49000
49282
  } catch {
49001
49283
  continue;
49002
49284
  }
49003
49285
  try {
49004
- const raw = fs53.readFileSync(filePath, "utf-8");
49286
+ const raw = fs54.readFileSync(filePath, "utf-8");
49005
49287
  for (const line of raw.split("\n")) {
49006
49288
  if (!line.trim()) continue;
49007
49289
  let entry;
@@ -49051,10 +49333,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
49051
49333
  }
49052
49334
  function loadClaudeCost(start, end, projectsDir) {
49053
49335
  const acc = emptyClaudeCostAccumulator();
49054
- if (!fs53.existsSync(projectsDir)) return freezeClaudeCost(acc);
49336
+ if (!fs54.existsSync(projectsDir)) return freezeClaudeCost(acc);
49055
49337
  let dirs;
49056
49338
  try {
49057
- dirs = fs53.readdirSync(projectsDir);
49339
+ dirs = fs54.readdirSync(projectsDir);
49058
49340
  } catch {
49059
49341
  return freezeClaudeCost(acc);
49060
49342
  }
@@ -49066,7 +49348,7 @@ function loadClaudeCost(start, end, projectsDir) {
49066
49348
  function processCodexCostFile(filePath, start, end, acc) {
49067
49349
  let lines;
49068
49350
  try {
49069
- lines = fs53.readFileSync(filePath, "utf-8").split("\n");
49351
+ lines = fs54.readFileSync(filePath, "utf-8").split("\n");
49070
49352
  } catch {
49071
49353
  return;
49072
49354
  }
@@ -49121,31 +49403,31 @@ function processCodexCostFile(filePath, start, end, acc) {
49121
49403
  }
49122
49404
  function listCodexSessionFiles2(sessionsBase) {
49123
49405
  const jsonlFiles = [];
49124
- if (!fs53.existsSync(sessionsBase)) return jsonlFiles;
49406
+ if (!fs54.existsSync(sessionsBase)) return jsonlFiles;
49125
49407
  try {
49126
- for (const year of fs53.readdirSync(sessionsBase)) {
49127
- const yearPath = path51.join(sessionsBase, year);
49408
+ for (const year of fs54.readdirSync(sessionsBase)) {
49409
+ const yearPath = path52.join(sessionsBase, year);
49128
49410
  try {
49129
- if (!fs53.statSync(yearPath).isDirectory()) continue;
49411
+ if (!fs54.statSync(yearPath).isDirectory()) continue;
49130
49412
  } catch {
49131
49413
  continue;
49132
49414
  }
49133
- for (const month of fs53.readdirSync(yearPath)) {
49134
- const monthPath = path51.join(yearPath, month);
49415
+ for (const month of fs54.readdirSync(yearPath)) {
49416
+ const monthPath = path52.join(yearPath, month);
49135
49417
  try {
49136
- if (!fs53.statSync(monthPath).isDirectory()) continue;
49418
+ if (!fs54.statSync(monthPath).isDirectory()) continue;
49137
49419
  } catch {
49138
49420
  continue;
49139
49421
  }
49140
- for (const day of fs53.readdirSync(monthPath)) {
49141
- const dayPath = path51.join(monthPath, day);
49422
+ for (const day of fs54.readdirSync(monthPath)) {
49423
+ const dayPath = path52.join(monthPath, day);
49142
49424
  try {
49143
- if (!fs53.statSync(dayPath).isDirectory()) continue;
49425
+ if (!fs54.statSync(dayPath).isDirectory()) continue;
49144
49426
  } catch {
49145
49427
  continue;
49146
49428
  }
49147
- for (const file of fs53.readdirSync(dayPath)) {
49148
- if (file.endsWith(".jsonl")) jsonlFiles.push(path51.join(dayPath, file));
49429
+ for (const file of fs54.readdirSync(dayPath)) {
49430
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path52.join(dayPath, file));
49149
49431
  }
49150
49432
  }
49151
49433
  }
@@ -49210,13 +49492,13 @@ function freezeGeminiCost(acc) {
49210
49492
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
49211
49493
  const startMs = start.getTime();
49212
49494
  try {
49213
- if (fs53.statSync(filePath).mtimeMs < startMs) return;
49495
+ if (fs54.statSync(filePath).mtimeMs < startMs) return;
49214
49496
  } catch {
49215
49497
  return;
49216
49498
  }
49217
49499
  let raw;
49218
49500
  try {
49219
- raw = fs53.readFileSync(filePath, "utf-8");
49501
+ raw = fs54.readFileSync(filePath, "utf-8");
49220
49502
  } catch {
49221
49503
  return;
49222
49504
  }
@@ -49265,30 +49547,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
49265
49547
  const out = [];
49266
49548
  let dirs;
49267
49549
  try {
49268
- if (!fs53.statSync(geminiTmpDir2).isDirectory()) return out;
49269
- dirs = fs53.readdirSync(geminiTmpDir2);
49550
+ if (!fs54.statSync(geminiTmpDir2).isDirectory()) return out;
49551
+ dirs = fs54.readdirSync(geminiTmpDir2);
49270
49552
  } catch {
49271
49553
  return out;
49272
49554
  }
49273
49555
  for (const proj of dirs) {
49274
- const chatsDir = path51.join(geminiTmpDir2, proj, "chats");
49556
+ const chatsDir = path52.join(geminiTmpDir2, proj, "chats");
49275
49557
  let files;
49276
49558
  try {
49277
- if (!fs53.statSync(chatsDir).isDirectory()) continue;
49278
- files = fs53.readdirSync(chatsDir);
49559
+ if (!fs54.statSync(chatsDir).isDirectory()) continue;
49560
+ files = fs54.readdirSync(chatsDir);
49279
49561
  } catch {
49280
49562
  continue;
49281
49563
  }
49282
49564
  for (const f of files) {
49283
49565
  if (!f.endsWith(".jsonl")) continue;
49284
- out.push({ projectKey: proj, file: path51.join(chatsDir, f) });
49566
+ out.push({ projectKey: proj, file: path52.join(chatsDir, f) });
49285
49567
  }
49286
49568
  }
49287
49569
  return out;
49288
49570
  }
49289
49571
  function loadGeminiCost(start, end, geminiTmpDir2) {
49290
49572
  const acc = emptyGeminiAccumulator();
49291
- if (!fs53.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
49573
+ if (!fs54.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
49292
49574
  for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
49293
49575
  processGeminiCostFile(file, projectKey, start, end, acc);
49294
49576
  }
@@ -49306,11 +49588,11 @@ function dimensionOfBlock(checkedBy, ruleName) {
49306
49588
  }
49307
49589
  function aggregateReportFromAudit(period, opts = {}) {
49308
49590
  const now = opts.now ?? /* @__PURE__ */ new Date();
49309
- const auditLogPath = opts.auditLogPath ?? path51.join(os47.homedir(), ".node9", "audit.log");
49310
- const claudeProjectsDir = opts.claudeProjectsDir ?? path51.join(os47.homedir(), ".claude", "projects");
49311
- const codexSessionsDir2 = opts.codexSessionsDir ?? path51.join(os47.homedir(), ".codex", "sessions");
49312
- const geminiTmpDir2 = opts.geminiTmpDir ?? path51.join(os47.homedir(), ".gemini", "tmp");
49313
- const hasAuditFile = fs53.existsSync(auditLogPath);
49591
+ const auditLogPath = opts.auditLogPath ?? path52.join(os49.homedir(), ".node9", "audit.log");
49592
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path52.join(os49.homedir(), ".claude", "projects");
49593
+ const codexSessionsDir2 = opts.codexSessionsDir ?? path52.join(os49.homedir(), ".codex", "sessions");
49594
+ const geminiTmpDir2 = opts.geminiTmpDir ?? path52.join(os49.homedir(), ".gemini", "tmp");
49595
+ const hasAuditFile = fs54.existsSync(auditLogPath);
49314
49596
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
49315
49597
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
49316
49598
  const { start, end } = getDateRange(period, now);
@@ -50103,10 +50385,12 @@ function registerDaemonCommand(program2) {
50103
50385
  init_core();
50104
50386
  init_daemon();
50105
50387
  init_agent_wiring();
50388
+ init_sync();
50389
+ init_service();
50106
50390
  import chalk15 from "chalk";
50107
- import fs54 from "fs";
50108
- import path52 from "path";
50109
- import os48 from "os";
50391
+ import fs55 from "fs";
50392
+ import path53 from "path";
50393
+ import os50 from "os";
50110
50394
  function printAgentSection(label2, hookPairs, wrapped) {
50111
50395
  console.log(chalk15.bold(` ${label2}`));
50112
50396
  for (const { name, present } of hookPairs) {
@@ -50135,6 +50419,15 @@ function registerStatusCommand(program2) {
50135
50419
  console.log("");
50136
50420
  if (creds && settings.approvers.cloud) {
50137
50421
  console.log(chalk15.green(" \u25CF Agent mode") + chalk15.gray(" \u2014 cloud team policy enforced"));
50422
+ const health = readSyncHealth();
50423
+ if (isPolicyStale(Date.now(), health)) {
50424
+ const when = health.lastCheckedAt ? `last synced ${agoLabel(health.lastCheckedAt)}` : "never synced";
50425
+ const fails = health.consecutiveFailures > 0 ? ` \xB7 ${health.consecutiveFailures} failed attempt${health.consecutiveFailures === 1 ? "" : "s"}${health.lastError ? ` (${health.lastError})` : ""}` : "";
50426
+ console.log(chalk15.yellow(" \u26A0 Policy sync STALE") + chalk15.gray(` \u2014 ${when}${fails}`));
50427
+ console.log(chalk15.gray(" the cached policy is still enforced \u2014 run: node9 doctor"));
50428
+ } else if (health.lastCheckedAt) {
50429
+ console.log(chalk15.gray(` \u21B3 policy synced ${agoLabel(health.lastCheckedAt)}`));
50430
+ }
50138
50431
  } else if (creds && !settings.approvers.cloud) {
50139
50432
  console.log(
50140
50433
  chalk15.blue(" \u25CF Privacy mode \u{1F6E1}\uFE0F") + chalk15.gray(" \u2014 all decisions stay on this machine")
@@ -50152,6 +50445,16 @@ function registerStatusCommand(program2) {
50152
50445
  } else {
50153
50446
  console.log(chalk15.gray(" \u25CB Daemon stopped"));
50154
50447
  }
50448
+ const autostart = autostartAdvice({
50449
+ installed: isDaemonServiceInstalled(),
50450
+ enabled: isDaemonServiceEnabled(),
50451
+ cloudEnabled: !!(creds && settings.approvers.cloud)
50452
+ });
50453
+ if (autostart) {
50454
+ console.log(
50455
+ chalk15.yellow(" \u26A0 daemon autostart not active") + chalk15.gray(" \u2014 won't survive reboot; run: node9 doctor")
50456
+ );
50457
+ }
50155
50458
  if (settings.enableUndo) {
50156
50459
  console.log(
50157
50460
  chalk15.magenta(" \u25CF Undo Engine") + chalk15.gray(` \u2192 Auto-snapshotting Git repos on AI change`)
@@ -50160,20 +50463,20 @@ function registerStatusCommand(program2) {
50160
50463
  console.log("");
50161
50464
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
50162
50465
  console.log(` Mode: ${modeLabel}`);
50163
- const projectConfig = path52.join(process.cwd(), "node9.config.json");
50164
- const globalConfig = path52.join(os48.homedir(), ".node9", "config.json");
50466
+ const projectConfig = path53.join(process.cwd(), "node9.config.json");
50467
+ const globalConfig = path53.join(os50.homedir(), ".node9", "config.json");
50165
50468
  console.log(
50166
- ` Local: ${fs54.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
50469
+ ` Local: ${fs55.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
50167
50470
  );
50168
50471
  console.log(
50169
- ` Global: ${fs54.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
50472
+ ` Global: ${fs55.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
50170
50473
  );
50171
50474
  if (mergedConfig.policy.sandboxPaths.length > 0) {
50172
50475
  console.log(
50173
50476
  ` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
50174
50477
  );
50175
50478
  }
50176
- const wiring = getAgentWiring(os48.homedir()).filter((a) => a.present);
50479
+ const wiring = getAgentWiring(os50.homedir()).filter((a) => a.present);
50177
50480
  if (wiring.length > 0) {
50178
50481
  console.log("");
50179
50482
  console.log(chalk15.bold(" Agent Wiring:"));
@@ -50211,10 +50514,11 @@ init_core();
50211
50514
  init_setup();
50212
50515
  init_shields();
50213
50516
  init_service();
50517
+ init_core();
50214
50518
  import chalk16 from "chalk";
50215
- import fs55 from "fs";
50216
- import path53 from "path";
50217
- import os49 from "os";
50519
+ import fs56 from "fs";
50520
+ import path54 from "path";
50521
+ import os51 from "os";
50218
50522
  import https6 from "https";
50219
50523
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
50220
50524
  function buildTelemetryPayload(agents, firstInstall) {
@@ -50300,16 +50604,16 @@ function registerInitCommand(program2) {
50300
50604
  }
50301
50605
  console.log("");
50302
50606
  }
50303
- const configPath = path53.join(os49.homedir(), ".node9", "config.json");
50304
- const isFirstInstall = !fs55.existsSync(configPath);
50305
- if (fs55.existsSync(configPath) && !options.force) {
50607
+ const configPath = path54.join(os51.homedir(), ".node9", "config.json");
50608
+ const isFirstInstall = !fs56.existsSync(configPath);
50609
+ if (fs56.existsSync(configPath) && !options.force) {
50306
50610
  try {
50307
- const existing = JSON.parse(fs55.readFileSync(configPath, "utf-8"));
50611
+ const existing = JSON.parse(fs56.readFileSync(configPath, "utf-8"));
50308
50612
  const settings = existing.settings ?? {};
50309
50613
  if (settings.mode !== chosenMode) {
50310
50614
  settings.mode = chosenMode;
50311
50615
  existing.settings = settings;
50312
- fs55.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
50616
+ fs56.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
50313
50617
  console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
50314
50618
  } else {
50315
50619
  console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -50322,9 +50626,9 @@ function registerInitCommand(program2) {
50322
50626
  ...DEFAULT_CONFIG,
50323
50627
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
50324
50628
  };
50325
- const dir = path53.dirname(configPath);
50326
- if (!fs55.existsSync(dir)) fs55.mkdirSync(dir, { recursive: true });
50327
- fs55.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
50629
+ const dir = path54.dirname(configPath);
50630
+ if (!fs56.existsSync(dir)) fs56.mkdirSync(dir, { recursive: true });
50631
+ fs56.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
50328
50632
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
50329
50633
  console.log(chalk16.gray(` Mode: ${chosenMode}`));
50330
50634
  }
@@ -50376,8 +50680,13 @@ function registerInitCommand(program2) {
50376
50680
  console.log(chalk16.gray(" You can try again later with: node9 daemon install"));
50377
50681
  }
50378
50682
  }
50683
+ } else if (isDaemonServiceEnabled()) {
50684
+ console.log(chalk16.green(" \u2713 Daemon login service already installed & enabled"));
50379
50685
  } else {
50380
- console.log(chalk16.green(" \u2713 Daemon login service already installed"));
50686
+ const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
50687
+ console.log(
50688
+ healed === "repaired" ? chalk16.green(" \u2713 Re-enabled daemon login service (was installed but disabled)") : chalk16.gray(" \xB7 Daemon login service is disabled (autostart off) \u2014 left as-is")
50689
+ );
50381
50690
  }
50382
50691
  if (!isTestingMode()) {
50383
50692
  process.stdout.write(chalk16.dim(" Starting daemon..."));
@@ -50422,11 +50731,11 @@ init_agent_wiring();
50422
50731
  init_setup();
50423
50732
  init_hook_baseline();
50424
50733
  import chalk17 from "chalk";
50425
- import fs56 from "fs";
50734
+ import fs57 from "fs";
50426
50735
  var hasHookSurface = (a) => a.hooks.length > 0;
50427
50736
  function backupForHeal(file) {
50428
50737
  try {
50429
- if (file && fs56.existsSync(file)) fs56.copyFileSync(file, `${file}.node9-heal-bak`);
50738
+ if (file && fs57.existsSync(file)) fs57.copyFileSync(file, `${file}.node9-heal-bak`);
50430
50739
  } catch {
50431
50740
  }
50432
50741
  }
@@ -50593,7 +50902,7 @@ function registerConnectCommand(program2) {
50593
50902
  }
50594
50903
 
50595
50904
  // src/cli/commands/undo.ts
50596
- import path54 from "path";
50905
+ import path55 from "path";
50597
50906
  import chalk20 from "chalk";
50598
50907
 
50599
50908
  // src/tui/undo-navigator.ts
@@ -50752,7 +51061,7 @@ function findMatchingCwd(startDir, history) {
50752
51061
  let dir = startDir;
50753
51062
  while (true) {
50754
51063
  if (cwds.has(dir)) return dir;
50755
- const parent = path54.dirname(dir);
51064
+ const parent = path55.dirname(dir);
50756
51065
  if (parent === dir) return null;
50757
51066
  dir = parent;
50758
51067
  }
@@ -51386,18 +51695,18 @@ function registerMcpGatewayCommand(program2) {
51386
51695
 
51387
51696
  // src/mcp-server/index.ts
51388
51697
  import readline5 from "readline";
51389
- import fs58 from "fs";
51390
- import os51 from "os";
51391
- import path56 from "path";
51698
+ import fs59 from "fs";
51699
+ import os53 from "os";
51700
+ import path57 from "path";
51392
51701
  import { spawnSync as spawnSync4 } from "child_process";
51393
51702
  init_core();
51394
51703
  init_daemon();
51395
51704
  init_shields();
51396
51705
 
51397
51706
  // src/auth/egress-config.ts
51398
- import fs57 from "fs";
51399
- import os50 from "os";
51400
- import path55 from "path";
51707
+ import fs58 from "fs";
51708
+ import os52 from "os";
51709
+ import path56 from "path";
51401
51710
  var DEFAULT_EGRESS = {
51402
51711
  enabled: false,
51403
51712
  mode: "review",
@@ -51406,12 +51715,12 @@ var DEFAULT_EGRESS = {
51406
51715
  allowPrivate: true
51407
51716
  };
51408
51717
  function egressConfigPath() {
51409
- return path55.join(os50.homedir(), ".node9", "config.json");
51718
+ return path56.join(os52.homedir(), ".node9", "config.json");
51410
51719
  }
51411
51720
  function readEgressRawConfig() {
51412
51721
  let text;
51413
51722
  try {
51414
- text = fs57.readFileSync(egressConfigPath(), "utf8");
51723
+ text = fs58.readFileSync(egressConfigPath(), "utf8");
51415
51724
  } catch (err2) {
51416
51725
  if (err2.code === "ENOENT") return {};
51417
51726
  throw err2;
@@ -51426,8 +51735,8 @@ function readEgressRawConfig() {
51426
51735
  }
51427
51736
  function writeEgressRawConfig(config) {
51428
51737
  const p = egressConfigPath();
51429
- fs57.mkdirSync(path55.dirname(p), { recursive: true });
51430
- fs57.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
51738
+ fs58.mkdirSync(path56.dirname(p), { recursive: true });
51739
+ fs58.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
51431
51740
  }
51432
51741
  function applyEgress(config, change) {
51433
51742
  const policy = config.policy = config.policy ?? {};
@@ -51812,13 +52121,13 @@ function handleStatus() {
51812
52121
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
51813
52122
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
51814
52123
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
51815
- const projectConfig = path56.join(process.cwd(), "node9.config.json");
51816
- const globalConfig = path56.join(os51.homedir(), ".node9", "config.json");
52124
+ const projectConfig = path57.join(process.cwd(), "node9.config.json");
52125
+ const globalConfig = path57.join(os53.homedir(), ".node9", "config.json");
51817
52126
  lines.push(
51818
- `Project config (node9.config.json): ${fs58.existsSync(projectConfig) ? "present" : "not found"}`
52127
+ `Project config (node9.config.json): ${fs59.existsSync(projectConfig) ? "present" : "not found"}`
51819
52128
  );
51820
52129
  lines.push(
51821
- `Global config (~/.node9/config.json): ${fs58.existsSync(globalConfig) ? "present" : "not found"}`
52130
+ `Global config (~/.node9/config.json): ${fs59.existsSync(globalConfig) ? "present" : "not found"}`
51822
52131
  );
51823
52132
  return lines.join("\n");
51824
52133
  }
@@ -51924,21 +52233,21 @@ function handleEgressDeny(args) {
51924
52233
  addEgressHost("deny", host);
51925
52234
  return `Denied egress to ${host} (deny always wins over allow).`;
51926
52235
  }
51927
- var GLOBAL_CONFIG_PATH = path56.join(os51.homedir(), ".node9", "config.json");
52236
+ var GLOBAL_CONFIG_PATH = path57.join(os53.homedir(), ".node9", "config.json");
51928
52237
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
51929
52238
  function readGlobalConfigRaw() {
51930
52239
  try {
51931
- if (fs58.existsSync(GLOBAL_CONFIG_PATH)) {
51932
- return JSON.parse(fs58.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
52240
+ if (fs59.existsSync(GLOBAL_CONFIG_PATH)) {
52241
+ return JSON.parse(fs59.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
51933
52242
  }
51934
52243
  } catch {
51935
52244
  }
51936
52245
  return {};
51937
52246
  }
51938
52247
  function writeGlobalConfigRaw(data) {
51939
- const dir = path56.dirname(GLOBAL_CONFIG_PATH);
51940
- if (!fs58.existsSync(dir)) fs58.mkdirSync(dir, { recursive: true });
51941
- fs58.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
52248
+ const dir = path57.dirname(GLOBAL_CONFIG_PATH);
52249
+ if (!fs59.existsSync(dir)) fs59.mkdirSync(dir, { recursive: true });
52250
+ fs59.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
51942
52251
  }
51943
52252
  function handleApproverList() {
51944
52253
  const config = getConfig();
@@ -51982,9 +52291,9 @@ function handleApproverSet(args) {
51982
52291
  function handleAuditGet(args) {
51983
52292
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
51984
52293
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
51985
- const auditPath = path56.join(os51.homedir(), ".node9", "audit.log");
51986
- if (!fs58.existsSync(auditPath)) return "No audit log found.";
51987
- const rawLines = fs58.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
52294
+ const auditPath = path57.join(os53.homedir(), ".node9", "audit.log");
52295
+ if (!fs59.existsSync(auditPath)) return "No audit log found.";
52296
+ const rawLines = fs59.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
51988
52297
  const parsed = [];
51989
52298
  for (const line of rawLines) {
51990
52299
  try {
@@ -52358,7 +52667,7 @@ function registerTrustCommand(program2) {
52358
52667
  // src/cli/commands/mcp-pin.ts
52359
52668
  init_mcp_pin();
52360
52669
  import chalk24 from "chalk";
52361
- import fs59 from "fs";
52670
+ import fs60 from "fs";
52362
52671
 
52363
52672
  // src/cli/commands/mcp-gateway-cmd.ts
52364
52673
  init_mcp_wrap();
@@ -52565,7 +52874,7 @@ function registerMcpPinCommand(program2) {
52565
52874
  let repoCorrupt = false;
52566
52875
  if (found.source === "repo") {
52567
52876
  try {
52568
- const raw = fs59.readFileSync(found.path, "utf-8");
52877
+ const raw = fs60.readFileSync(found.path, "utf-8");
52569
52878
  const parsed = JSON.parse(raw);
52570
52879
  repoEntries = parsed.servers ?? {};
52571
52880
  } catch {
@@ -53066,8 +53375,8 @@ import chalk30 from "chalk";
53066
53375
 
53067
53376
  // src/ci-check/fetch.ts
53068
53377
  var import_undici = __toESM(require_undici());
53069
- import fs60 from "fs";
53070
- import path57 from "path";
53378
+ import fs61 from "fs";
53379
+ import path58 from "path";
53071
53380
  import { execFileSync as execFileSync2 } from "child_process";
53072
53381
  var cachedGhToken;
53073
53382
  function resolveGitHubToken() {
@@ -53150,7 +53459,7 @@ function parseRepoUrl(input) {
53150
53459
  function isLocalPath(input) {
53151
53460
  if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) return true;
53152
53461
  try {
53153
- return fs60.existsSync(input) && fs60.statSync(input).isDirectory();
53462
+ return fs61.existsSync(input) && fs61.statSync(input).isDirectory();
53154
53463
  } catch {
53155
53464
  return false;
53156
53465
  }
@@ -53265,10 +53574,10 @@ function readLocalTree(dir) {
53265
53574
  const files = [];
53266
53575
  const notes = [];
53267
53576
  const add = (rel) => {
53268
- const abs = path57.join(root, rel);
53577
+ const abs = path58.join(root, rel);
53269
53578
  try {
53270
- if (fs60.existsSync(abs) && fs60.statSync(abs).isFile()) {
53271
- files.push({ path: rel, content: fs60.readFileSync(abs, "utf8") });
53579
+ if (fs61.existsSync(abs) && fs61.statSync(abs).isFile()) {
53580
+ files.push({ path: rel, content: fs61.readFileSync(abs, "utf8") });
53272
53581
  }
53273
53582
  } catch {
53274
53583
  }
@@ -53288,7 +53597,7 @@ function readLocalTree(dir) {
53288
53597
  dirsVisited++;
53289
53598
  let entries;
53290
53599
  try {
53291
- entries = fs60.readdirSync(path57.join(root, relDir), { withFileTypes: true });
53600
+ entries = fs61.readdirSync(path58.join(root, relDir), { withFileTypes: true });
53292
53601
  } catch {
53293
53602
  return;
53294
53603
  }
@@ -53309,11 +53618,11 @@ function readLocalTree(dir) {
53309
53618
  `repo is large \u2014 some agent-surface files may be INCOMPLETE (capped at ${MAX_SURFACE_FILES} files / ${MAX_DIRS} dirs).`
53310
53619
  );
53311
53620
  for (const rel of matches) collect(rel);
53312
- const wfDir = path57.join(root, WORKFLOW_DIR);
53621
+ const wfDir = path58.join(root, WORKFLOW_DIR);
53313
53622
  try {
53314
- if (fs60.existsSync(wfDir)) {
53315
- for (const name of fs60.readdirSync(wfDir)) {
53316
- if (/\.ya?ml$/.test(name)) add(path57.join(WORKFLOW_DIR, name));
53623
+ if (fs61.existsSync(wfDir)) {
53624
+ for (const name of fs61.readdirSync(wfDir)) {
53625
+ if (/\.ya?ml$/.test(name)) add(path58.join(WORKFLOW_DIR, name));
53317
53626
  }
53318
53627
  }
53319
53628
  } catch {
@@ -53456,6 +53765,38 @@ function collectTools(steps) {
53456
53765
  }
53457
53766
  return s;
53458
53767
  }
53768
+ function toolTokens(blob) {
53769
+ const out = [];
53770
+ let buf = "";
53771
+ let inParen = false;
53772
+ const flush = () => {
53773
+ const t = buf.replace(/[[\]"'`\r\n]/g, "").trim();
53774
+ if (t) out.push(t);
53775
+ buf = "";
53776
+ };
53777
+ for (const ch of blob) {
53778
+ if (ch === "(") inParen = true;
53779
+ else if (ch === ")") inParen = false;
53780
+ if (!inParen && (ch === "," || /\s/.test(ch))) {
53781
+ flush();
53782
+ continue;
53783
+ }
53784
+ buf += ch;
53785
+ }
53786
+ flush();
53787
+ return out;
53788
+ }
53789
+ function matchedBroadTools(blob) {
53790
+ const seen = /* @__PURE__ */ new Set();
53791
+ const out = [];
53792
+ for (const tok of toolTokens(blob)) {
53793
+ if (!BROAD_TOOL_RE.test(`,${tok},`)) continue;
53794
+ if (seen.has(tok)) continue;
53795
+ seen.add(tok);
53796
+ out.push(tok.length > 40 ? tok.slice(0, 40) + "\u2026" : tok);
53797
+ }
53798
+ return out;
53799
+ }
53459
53800
  function untrustedHeadCheckout(steps) {
53460
53801
  for (const step of steps) {
53461
53802
  if (!step.uses || !/actions\/checkout/.test(step.uses)) continue;
@@ -53586,7 +53927,7 @@ function severityFromScore(score) {
53586
53927
  if (score >= 1) return "advisory";
53587
53928
  return null;
53588
53929
  }
53589
- function analyzeWorkflow(path70, content) {
53930
+ function analyzeWorkflow(path71, content) {
53590
53931
  let raw;
53591
53932
  try {
53592
53933
  raw = parseYaml(content) ?? {};
@@ -53682,7 +54023,12 @@ function analyzeWorkflow(path70, content) {
53682
54023
  if (head === "root") signals.push("checks out the untrusted PR head into the workspace root");
53683
54024
  if (head === "subdir") signals.push("checks out the untrusted PR head into an isolated subdir");
53684
54025
  if (promptUntrusted) signals.push("feeds untrusted PR/issue text to the agent");
53685
- if (broadTools) signals.push("agent has broad/write-capable tools (Bash/Write/curl/git push)");
54026
+ if (broadTools) {
54027
+ const names = matchedBroadTools(toolsBlob);
54028
+ signals.push(
54029
+ names.length ? `agent has broad/write-capable tools: ${names.map((n) => `\`${n}\``).join(", ")}` : "agent has broad/write-capable tool grants"
54030
+ );
54031
+ }
53686
54032
  if (bypassActive) signals.push('allowed_non_write_users: "*" \u2014 any user can trigger the agent');
53687
54033
  if (elevated) signals.push("elevated permissions (contents/id-token: write)");
53688
54034
  if (pat) signals.push("a static PAT is exposed to the agent (recoverable via injection)");
@@ -53707,7 +54053,7 @@ function analyzeWorkflow(path70, content) {
53707
54053
  dimension: "workflows",
53708
54054
  severity,
53709
54055
  title,
53710
- file: path70,
54056
+ file: path71,
53711
54057
  signals,
53712
54058
  mitigations: mitigations.length ? mitigations : void 0,
53713
54059
  fix: head === "root" && privileged ? "Do not check out the untrusted PR head into the workspace root under a privileged trigger (pull_request_target/workflow_run) \u2014 check out the base ref, or isolate the head in a subdir (--add-dir). Add an actor gate and scope the agent tools." : head === "root" ? "This runs under `pull_request` (fork PRs get a read-only token), so the head checkout is low-risk today \u2014 keep it on `pull_request` (not `pull_request_target`) and keep the actor gate + scoped tools." : "Add/verify an actor gate, scope the agent tools to read-only, and env-deny secrets. See Anthropic\u2019s claude-code-action security doc."
@@ -53783,7 +54129,7 @@ function evalAgentJob(job, wf, raw, untrustedTrigger, reusable) {
53783
54129
  if (reusable && !loadedGun && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
53784
54130
  return { severity, secrets, injectable, canReadEnv };
53785
54131
  }
53786
- function analyzeWorkflowSecrets(path70, content) {
54132
+ function analyzeWorkflowSecrets(path71, content) {
53787
54133
  let raw;
53788
54134
  try {
53789
54135
  raw = parseYaml(content) ?? {};
@@ -53803,7 +54149,7 @@ function analyzeWorkflowSecrets(path70, content) {
53803
54149
  dimension: "data",
53804
54150
  severity: worst.severity,
53805
54151
  title: worst.severity === "advisory" ? "Secrets reachable by the agent \u2014 hardening" : "Exfiltratable secrets reachable by an injectable agent",
53806
- file: path70,
54152
+ file: path71,
53807
54153
  signals: [
53808
54154
  `agent can reach: ${worst.secrets.map((s) => s.name).join(", ")}`,
53809
54155
  worst.injectable ? "the agent is externally triggerable (untrusted trigger, no gate)" : "gated / not externally triggerable \u2014 latent risk only",
@@ -53830,7 +54176,7 @@ function hookCommands(hooks) {
53830
54176
  }
53831
54177
  return out;
53832
54178
  }
53833
- function analyzeAgentConfig(path70, content) {
54179
+ function analyzeAgentConfig(path71, content) {
53834
54180
  let cfg;
53835
54181
  try {
53836
54182
  cfg = JSON.parse(content);
@@ -53849,7 +54195,7 @@ function analyzeAgentConfig(path70, content) {
53849
54195
  dimension: "toolRules",
53850
54196
  severity: high ? "high" : "medium",
53851
54197
  title: high ? "Agent hook runs UNPINNED/remote third-party code on every action" : "Agent hook runs third-party code in the agent hot path",
53852
- file: path70,
54198
+ file: path71,
53853
54199
  signals: [
53854
54200
  `hook command: \`${cmd.slice(0, 120)}\``,
53855
54201
  remoteExec ? "fetch-and-run (curl|wget / pipe-to-shell) \u2014 unpinnable remote code execution on every contributor" : unpinned ? "unpinned \u2014 a compromised/yanked package = code execution on every contributor" : "pinned, but still a standing supply-chain dependency in the agent hot path"
@@ -53869,7 +54215,7 @@ function analyzeAgentConfig(path70, content) {
53869
54215
  dimension: "toolRules",
53870
54216
  severity: hasBackstop ? "medium" : "high",
53871
54217
  title: hasBackstop ? "Committed agent config pre-authorizes broad tools" : "Committed agent config pre-authorizes broad tools with no deny backstop",
53872
- file: path70,
54218
+ file: path71,
53873
54219
  signals: [
53874
54220
  `broad allow(s): ${broad.slice(0, 5).join(", ")}`,
53875
54221
  hasBackstop ? "a `deny` list backstops the broad allow" : "no `deny` entry covers Bash/Write/Edit \u2014 every contributor is pre-authorized for catastrophic tools"
@@ -53882,16 +54228,16 @@ function analyzeAgentConfig(path70, content) {
53882
54228
 
53883
54229
  // src/ci-check/mcp.ts
53884
54230
  init_dist();
53885
- function analyzeMcp(path70, content) {
54231
+ function analyzeMcp(path71, content) {
53886
54232
  let cfg;
53887
54233
  try {
53888
54234
  cfg = JSON.parse(content);
53889
54235
  } catch {
53890
54236
  return [];
53891
54237
  }
53892
- return analyzeMcpServers(cfg.mcpServers ?? {}, path70);
54238
+ return analyzeMcpServers(cfg.mcpServers ?? {}, path71);
53893
54239
  }
53894
- function analyzeMcpServers(servers, path70) {
54240
+ function analyzeMcpServers(servers, path71) {
53895
54241
  const findings = [];
53896
54242
  for (const [name, srv] of Object.entries(servers ?? {})) {
53897
54243
  if (!srv || srv.disabled) continue;
@@ -53902,7 +54248,7 @@ function analyzeMcpServers(servers, path70) {
53902
54248
  dimension: "mcp",
53903
54249
  severity: "medium",
53904
54250
  title: `MCP server "${name}" runs an unpinned executable`,
53905
- file: path70,
54251
+ file: path71,
53906
54252
  signals: [`\`${argv.slice(0, 120)}\` \u2014 unversioned/@latest npx`],
53907
54253
  fix: "Pin the MCP server package to an exact version so a PR (or a registry compromise) can\u2019t swap the toolchain."
53908
54254
  });
@@ -53916,7 +54262,7 @@ function analyzeMcpServers(servers, path70) {
53916
54262
  dimension: "mcp",
53917
54263
  severity: "high",
53918
54264
  title: `MCP server "${name}" has an inline credential`,
53919
- file: path70,
54265
+ file: path71,
53920
54266
  signals: [
53921
54267
  `env.${k} matches ${hit.patternName} \u2014 agent-reachable secret committed to the repo`
53922
54268
  ],
@@ -53930,7 +54276,7 @@ function analyzeMcpServers(servers, path70) {
53930
54276
 
53931
54277
  // src/ci-check/codex.ts
53932
54278
  import { parse as parseToml5 } from "smol-toml";
53933
- function analyzeCodexConfig(path70, content) {
54279
+ function analyzeCodexConfig(path71, content) {
53934
54280
  let cfg;
53935
54281
  try {
53936
54282
  cfg = parseToml5(content);
@@ -53938,7 +54284,7 @@ function analyzeCodexConfig(path70, content) {
53938
54284
  return [];
53939
54285
  }
53940
54286
  const findings = [];
53941
- findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path70));
54287
+ findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path71));
53942
54288
  const sandbox = typeof cfg.sandbox_mode === "string" ? cfg.sandbox_mode : "";
53943
54289
  const approval = typeof cfg.approval_policy === "string" ? cfg.approval_policy : "";
53944
54290
  const fullAccess = /danger-full-access/i.test(sandbox);
@@ -53953,7 +54299,7 @@ function analyzeCodexConfig(path70, content) {
53953
54299
  dimension: "toolRules",
53954
54300
  severity: fullAccess ? "high" : "medium",
53955
54301
  title: fullAccess ? "Codex config grants a full-access sandbox" : "Codex config never requires approval",
53956
- file: path70,
54302
+ file: path71,
53957
54303
  signals,
53958
54304
  fix: 'Commit a least-privilege Codex config: prefer `sandbox_mode = "read-only"` (or `"workspace-write"`) and `approval_policy = "on-request"`/`"on-failure"`. A repo-committed config applies to every contributor who runs Codex here.'
53959
54305
  });
@@ -54009,10 +54355,10 @@ function decodeSuspiciousBase64(text) {
54009
54355
  }
54010
54356
  return out;
54011
54357
  }
54012
- function mk(severity, title, signals, fix, path70) {
54013
- return { check: "CI-6", dimension: "instructions", severity, title, file: path70, signals, fix };
54358
+ function mk(severity, title, signals, fix, path71) {
54359
+ return { check: "CI-6", dimension: "instructions", severity, title, file: path71, signals, fix };
54014
54360
  }
54015
- function analyzeInstructionFile(path70, content) {
54361
+ function analyzeInstructionFile(path71, content) {
54016
54362
  const findings = [];
54017
54363
  const decoded = decodeSuspiciousBase64(content);
54018
54364
  if (TAG_CHARS.test(content))
@@ -54024,7 +54370,7 @@ function analyzeInstructionFile(path70, content) {
54024
54370
  "contains Unicode tag characters (U+E0000\u2013E007F) \u2014 an invisible instruction-smuggling channel with no legitimate use in text"
54025
54371
  ],
54026
54372
  "Remove the tag characters. Instruction files must be plain, reviewable text.",
54027
- path70
54373
+ path71
54028
54374
  )
54029
54375
  );
54030
54376
  if (BIDI_OVERRIDE.test(content))
@@ -54036,7 +54382,7 @@ function analyzeInstructionFile(path70, content) {
54036
54382
  "contains a bidi override (U+202D/U+202E) \u2014 a Trojan-Source technique that visually reorders text so a human reads something different from what the agent parses"
54037
54383
  ],
54038
54384
  "Remove the bidi override characters.",
54039
- path70
54385
+ path71
54040
54386
  )
54041
54387
  );
54042
54388
  else if (BIDI_EMBED_ISOLATE.test(content))
@@ -54048,7 +54394,7 @@ function analyzeInstructionFile(path70, content) {
54048
54394
  "contains bidi embed/isolate characters (U+202A\u2013202C / U+2066\u20132069) \u2014 legitimate in right-to-left text, but confirm they are not being used to hide or reorder instructions"
54049
54395
  ],
54050
54396
  "Confirm the bidi marks are legitimate RTL formatting; remove otherwise.",
54051
- path70
54397
+ path71
54052
54398
  )
54053
54399
  );
54054
54400
  const zw = suspiciousZeroWidth(content);
@@ -54062,7 +54408,7 @@ function analyzeInstructionFile(path70, content) {
54062
54408
  revealed ? "a zero-width character conceals a prompt-override directive that only appears once the hidden characters are stripped" : "a zero-width character splits a visible Latin word \u2014 a concealment technique (hides text from human review while the agent reads it as contiguous)"
54063
54409
  ],
54064
54410
  "Remove the zero-width characters. Instruction files must be plain, reviewable text.",
54065
- path70
54411
+ path71
54066
54412
  )
54067
54413
  );
54068
54414
  }
@@ -54078,7 +54424,7 @@ function analyzeInstructionFile(path70, content) {
54078
54424
  `contains a prompt-override / role-impersonation directive (\`${m[0].slice(0, 60).trim()}\`)${ovEnc ? " \u2014 concealed in a base64 blob" : ""}`
54079
54425
  ],
54080
54426
  "Remove the override text. An instruction file should not tell the agent to ignore its own rules.",
54081
- path70
54427
+ path71
54082
54428
  )
54083
54429
  );
54084
54430
  }
@@ -54090,7 +54436,7 @@ function analyzeInstructionFile(path70, content) {
54090
54436
  "Instruction directs the agent to fetch and run remote code",
54091
54437
  [`\`${fo[0].slice(0, 70).trim()}\` \u2014 fetch-and-obey, outside an install/setup section`],
54092
54438
  "Do not instruct the agent to pipe remote content into a shell; pin and vendor scripts instead.",
54093
- path70
54439
+ path71
54094
54440
  )
54095
54441
  );
54096
54442
  }
@@ -54102,7 +54448,7 @@ function analyzeInstructionFile(path70, content) {
54102
54448
  "Instruction points the agent at credential material",
54103
54449
  [`references \`${sp[0].slice(0, 50).trim()}\` \u2014 directs the agent toward secrets`],
54104
54450
  "Do not reference credential files or paths in agent instructions.",
54105
- path70
54451
+ path71
54106
54452
  )
54107
54453
  );
54108
54454
  }
@@ -54114,7 +54460,7 @@ function analyzeInstructionFile(path70, content) {
54114
54460
  "Instruction directs the agent to send data to an external endpoint",
54115
54461
  [`\`${ex[0].slice(0, 70).trim()}\` \u2014 possible exfiltration directive`],
54116
54462
  "Remove external post/upload directives from agent instructions.",
54117
- path70
54463
+ path71
54118
54464
  )
54119
54465
  );
54120
54466
  }
@@ -54416,17 +54762,17 @@ import chalk32 from "chalk";
54416
54762
  // src/shields/jail.ts
54417
54763
  init_build();
54418
54764
  init_shields();
54419
- import fs61 from "fs";
54420
- import os52 from "os";
54421
- import path58 from "path";
54765
+ import fs62 from "fs";
54766
+ import os54 from "os";
54767
+ import path59 from "path";
54422
54768
  var USER_JAIL_SHIELD = "user-jail";
54423
54769
  function jailStorePath() {
54424
- return path58.join(os52.homedir(), ".node9", "jail-paths.json");
54770
+ return path59.join(os54.homedir(), ".node9", "jail-paths.json");
54425
54771
  }
54426
54772
  function readJailPaths() {
54427
54773
  let text;
54428
54774
  try {
54429
- text = fs61.readFileSync(jailStorePath(), "utf8");
54775
+ text = fs62.readFileSync(jailStorePath(), "utf8");
54430
54776
  } catch (err2) {
54431
54777
  if (err2.code === "ENOENT") return [];
54432
54778
  throw err2;
@@ -54444,8 +54790,8 @@ function readJailPaths() {
54444
54790
  }
54445
54791
  function writeJailPaths(paths) {
54446
54792
  const p = jailStorePath();
54447
- fs61.mkdirSync(path58.dirname(p), { recursive: true });
54448
- fs61.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
54793
+ fs62.mkdirSync(path59.dirname(p), { recursive: true });
54794
+ fs62.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
54449
54795
  }
54450
54796
  function addJailPath(rawPath, verdict) {
54451
54797
  const norm = rawPath.trim();
@@ -54467,14 +54813,14 @@ function removeJailPath(rawPath) {
54467
54813
  return { removed, paths: after };
54468
54814
  }
54469
54815
  function regenerateUserJail(paths) {
54470
- const file = path58.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
54816
+ const file = path59.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
54471
54817
  if (paths.length === 0) {
54472
54818
  const active2 = readActiveShields();
54473
54819
  if (active2.includes(USER_JAIL_SHIELD)) {
54474
54820
  writeActiveShields(active2.filter((s) => s !== USER_JAIL_SHIELD));
54475
54821
  }
54476
54822
  try {
54477
- fs61.rmSync(file, { force: true });
54823
+ fs62.rmSync(file, { force: true });
54478
54824
  } catch {
54479
54825
  }
54480
54826
  return;
@@ -54589,13 +54935,13 @@ function registerJailCommand(program2) {
54589
54935
  // src/cli/commands/sandbox.ts
54590
54936
  init_config();
54591
54937
  import chalk33 from "chalk";
54592
- import fs64 from "fs";
54593
- import path61 from "path";
54938
+ import fs65 from "fs";
54939
+ import path62 from "path";
54594
54940
  import { spawnSync as spawnSync6 } from "child_process";
54595
54941
 
54596
54942
  // src/sandbox/config.ts
54597
- import fs62 from "fs";
54598
- import path59 from "path";
54943
+ import fs63 from "fs";
54944
+ import path60 from "path";
54599
54945
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
54600
54946
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
54601
54947
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -54668,16 +55014,16 @@ function scaffoldSandboxYaml(agent) {
54668
55014
  return header + stringifyYaml(defaultSandboxConfig(agent));
54669
55015
  }
54670
55016
  function sandboxConfigPath(cwd = process.cwd()) {
54671
- return path59.join(cwd, SANDBOX_CONFIG_FILE);
55017
+ return path60.join(cwd, SANDBOX_CONFIG_FILE);
54672
55018
  }
54673
55019
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
54674
55020
  const p = sandboxConfigPath(cwd);
54675
- if (!fs62.existsSync(p)) {
55021
+ if (!fs63.existsSync(p)) {
54676
55022
  throw new Error(`sandbox: ${SANDBOX_CONFIG_FILE} not found \u2014 run \`node9 sandbox new\` first.`);
54677
55023
  }
54678
55024
  let raw;
54679
55025
  try {
54680
- raw = parseYaml2(fs62.readFileSync(p, "utf-8"));
55026
+ raw = parseYaml2(fs63.readFileSync(p, "utf-8"));
54681
55027
  } catch (err2) {
54682
55028
  throw new Error(
54683
55029
  `sandbox: ${SANDBOX_CONFIG_FILE} is not valid YAML \u2014 ${err2.message}`
@@ -54736,13 +55082,13 @@ init_templates();
54736
55082
 
54737
55083
  // src/sandbox/runtime.ts
54738
55084
  init_templates();
54739
- import fs63 from "fs";
54740
- import os53 from "os";
54741
- import path60 from "path";
55085
+ import fs64 from "fs";
55086
+ import os55 from "os";
55087
+ import path61 from "path";
54742
55088
  import crypto9 from "crypto";
54743
55089
  import { spawnSync as spawnSync5 } from "child_process";
54744
55090
  function sandboxDataDir(cwd = process.cwd()) {
54745
- return path60.join(cwd, ".node9", "sandbox", "data");
55091
+ return path61.join(cwd, ".node9", "sandbox", "data");
54746
55092
  }
54747
55093
  function detectEngine(engine) {
54748
55094
  const r = spawnSync5(engine, ["--version"], { encoding: "utf-8" });
@@ -54753,7 +55099,7 @@ function detectEngine(engine) {
54753
55099
  }
54754
55100
  function agentCredentialsMount(agent) {
54755
55101
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
54756
- return { hostPath: path60.join(os53.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
55102
+ return { hostPath: path61.join(os55.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
54757
55103
  }
54758
55104
  function buildRunArgs(opts) {
54759
55105
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -54763,7 +55109,7 @@ function buildRunArgs(opts) {
54763
55109
  args.push("-v", `${allowlistHostPath}:${ALLOWED_DOMAINS_PATH}:ro`);
54764
55110
  if (config.node9.mountAgentCredentials) {
54765
55111
  const creds = agentCredentialsMount(config.agent);
54766
- if (fs63.existsSync(creds.hostPath)) {
55112
+ if (fs64.existsSync(creds.hostPath)) {
54767
55113
  args.push("-v", `${creds.hostPath}:${creds.target}`);
54768
55114
  }
54769
55115
  }
@@ -54781,30 +55127,30 @@ function imageContentHash(dockerfile, entrypoint) {
54781
55127
  return crypto9.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
54782
55128
  }
54783
55129
  function sandboxBuildDir(cwd = process.cwd()) {
54784
- return path60.join(cwd, ".node9", "sandbox", "build");
55130
+ return path61.join(cwd, ".node9", "sandbox", "build");
54785
55131
  }
54786
55132
  function writeBuildContext(cwd, dockerfile, entrypoint) {
54787
55133
  const dir = sandboxBuildDir(cwd);
54788
- fs63.mkdirSync(dir, { recursive: true });
54789
- fs63.writeFileSync(path60.join(dir, "Dockerfile"), dockerfile);
54790
- fs63.writeFileSync(path60.join(dir, "entrypoint.sh"), entrypoint);
55134
+ fs64.mkdirSync(dir, { recursive: true });
55135
+ fs64.writeFileSync(path61.join(dir, "Dockerfile"), dockerfile);
55136
+ fs64.writeFileSync(path61.join(dir, "entrypoint.sh"), entrypoint);
54791
55137
  return dir;
54792
55138
  }
54793
55139
  function writeAllowlist(cwd, hosts) {
54794
- const dir = path60.join(cwd, ".node9", "sandbox");
54795
- fs63.mkdirSync(dir, { recursive: true });
54796
- const p = path60.join(dir, "allowed-domains.txt");
54797
- fs63.writeFileSync(p, hosts.join("\n") + "\n");
55140
+ const dir = path61.join(cwd, ".node9", "sandbox");
55141
+ fs64.mkdirSync(dir, { recursive: true });
55142
+ const p = path61.join(dir, "allowed-domains.txt");
55143
+ fs64.writeFileSync(p, hosts.join("\n") + "\n");
54798
55144
  return p;
54799
55145
  }
54800
55146
  function resolveHomePath(p) {
54801
- return p.startsWith("~") ? path60.join(os53.homedir(), p.slice(1)) : path60.resolve(p);
55147
+ return p.startsWith("~") ? path61.join(os55.homedir(), p.slice(1)) : path61.resolve(p);
54802
55148
  }
54803
55149
 
54804
55150
  // src/cli/commands/sandbox.ts
54805
55151
  function seedDataDirConfig(dataDir, sandbox) {
54806
- fs64.mkdirSync(dataDir, { recursive: true });
54807
- const configPath = path61.join(dataDir, "config.json");
55152
+ fs65.mkdirSync(dataDir, { recursive: true });
55153
+ const configPath = path62.join(dataDir, "config.json");
54808
55154
  const seed = {
54809
55155
  settings: {
54810
55156
  approvers: {
@@ -54815,7 +55161,7 @@ function seedDataDirConfig(dataDir, sandbox) {
54815
55161
  }
54816
55162
  }
54817
55163
  };
54818
- fs64.writeFileSync(configPath, JSON.stringify(seed, null, 2), { mode: 384 });
55164
+ fs65.writeFileSync(configPath, JSON.stringify(seed, null, 2), { mode: 384 });
54819
55165
  }
54820
55166
  function registerSandboxCommand(program2, version2) {
54821
55167
  const node9Version2 = pinnedNode9Version(version2);
@@ -54823,13 +55169,13 @@ function registerSandboxCommand(program2, version2) {
54823
55169
  cmd.command("new").description(`Scaffold ${SANDBOX_CONFIG_FILE} in this project`).option("--agent <agent>", "claude (default) or codex", "claude").action((opts) => {
54824
55170
  const agent = opts.agent === "codex" ? "codex" : "claude";
54825
55171
  const p = sandboxConfigPath();
54826
- if (fs64.existsSync(p)) {
55172
+ if (fs65.existsSync(p)) {
54827
55173
  console.log(
54828
55174
  chalk33.yellow(` ${SANDBOX_CONFIG_FILE} already exists \u2014 leaving it untouched.`)
54829
55175
  );
54830
55176
  return;
54831
55177
  }
54832
- fs64.writeFileSync(p, scaffoldSandboxYaml(agent));
55178
+ fs65.writeFileSync(p, scaffoldSandboxYaml(agent));
54833
55179
  console.log(
54834
55180
  chalk33.green(` \u2713 wrote ${SANDBOX_CONFIG_FILE}`) + chalk33.dim(` (agent: ${agent})`)
54835
55181
  );
@@ -54869,8 +55215,8 @@ function registerSandboxCommand(program2, version2) {
54869
55215
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
54870
55216
  const hash = imageContentHash(dockerfile, entrypoint);
54871
55217
  const image = sandbox.runtime.image;
54872
- const hashFile = path61.join(sandboxBuildDir(cwd), ".image-hash");
54873
- const lastHash = fs64.existsSync(hashFile) ? fs64.readFileSync(hashFile, "utf-8").trim() : "";
55218
+ const hashFile = path62.join(sandboxBuildDir(cwd), ".image-hash");
55219
+ const lastHash = fs65.existsSync(hashFile) ? fs65.readFileSync(hashFile, "utf-8").trim() : "";
54874
55220
  const imageExists = spawnSync6(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
54875
55221
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
54876
55222
  if (needBuild) {
@@ -54882,7 +55228,7 @@ function registerSandboxCommand(program2, version2) {
54882
55228
  console.error(chalk33.red(" build failed."));
54883
55229
  process.exit(b.status ?? 1);
54884
55230
  }
54885
- fs64.writeFileSync(hashFile, hash);
55231
+ fs65.writeFileSync(hashFile, hash);
54886
55232
  }
54887
55233
  const dataDir = sandboxDataDir(cwd);
54888
55234
  seedDataDirConfig(dataDir, sandbox);
@@ -54896,7 +55242,7 @@ function registerSandboxCommand(program2, version2) {
54896
55242
  });
54897
55243
  if (sandbox.node9.mountAgentCredentials) {
54898
55244
  const creds = agentCredentialsMount(sandbox.agent);
54899
- if (fs64.existsSync(creds.hostPath)) {
55245
+ if (fs65.existsSync(creds.hostPath)) {
54900
55246
  console.log(chalk33.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
54901
55247
  } else {
54902
55248
  console.log(
@@ -54912,20 +55258,20 @@ function registerSandboxCommand(program2, version2) {
54912
55258
  process.exit(r.status ?? 0);
54913
55259
  });
54914
55260
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
54915
- const auditPath = path61.join(sandboxDataDir(), "audit.log");
54916
- if (!fs64.existsSync(auditPath)) {
55261
+ const auditPath = path62.join(sandboxDataDir(), "audit.log");
55262
+ if (!fs65.existsSync(auditPath)) {
54917
55263
  console.log(chalk33.dim(" no sandbox audit yet."));
54918
55264
  return;
54919
55265
  }
54920
55266
  spawnSync6("tail", ["-f", auditPath], { stdio: "inherit" });
54921
55267
  });
54922
55268
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
54923
- const auditPath = path61.join(sandboxDataDir(), "audit.log");
54924
- if (!fs64.existsSync(auditPath)) {
55269
+ const auditPath = path62.join(sandboxDataDir(), "audit.log");
55270
+ if (!fs65.existsSync(auditPath)) {
54925
55271
  console.log(chalk33.dim(" no sandbox audit yet."));
54926
55272
  return;
54927
55273
  }
54928
- process.stdout.write(fs64.readFileSync(auditPath, "utf-8"));
55274
+ process.stdout.write(fs65.readFileSync(auditPath, "utf-8"));
54929
55275
  });
54930
55276
  cmd.command("clean").description("Remove the sandbox image, build context, and data").action(() => {
54931
55277
  const cwd = process.cwd();
@@ -54939,7 +55285,7 @@ function registerSandboxCommand(program2, version2) {
54939
55285
  stdio: "ignore"
54940
55286
  });
54941
55287
  }
54942
- fs64.rmSync(path61.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
55288
+ fs65.rmSync(path62.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
54943
55289
  console.log(chalk33.green(" \u2713 sandbox image + build + data removed."));
54944
55290
  });
54945
55291
  }
@@ -54950,9 +55296,9 @@ init_litellm();
54950
55296
  init_cost_gemini();
54951
55297
  init_cost_codex();
54952
55298
  import chalk34 from "chalk";
54953
- import fs65 from "fs";
54954
- import path62 from "path";
54955
- import os54 from "os";
55299
+ import fs66 from "fs";
55300
+ import path63 from "path";
55301
+ import os56 from "os";
54956
55302
  function modelPrice(model) {
54957
55303
  const t = pricingFor(model);
54958
55304
  if (!t) return null;
@@ -54969,10 +55315,10 @@ function encodeProjectPath(projectPath) {
54969
55315
  }
54970
55316
  function sessionJsonlPath(projectPath, sessionId) {
54971
55317
  const encoded = encodeProjectPath(projectPath);
54972
- return path62.join(os54.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
55318
+ return path63.join(os56.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
54973
55319
  }
54974
55320
  function projectLabel(projectPath) {
54975
- return projectPath.replace(os54.homedir(), "~");
55321
+ return projectPath.replace(os56.homedir(), "~");
54976
55322
  }
54977
55323
  function parseHistoryLines(lines) {
54978
55324
  const entries = [];
@@ -55041,10 +55387,10 @@ function parseSessionLines(lines) {
55041
55387
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
55042
55388
  }
55043
55389
  function loadAuditEntries(auditPath) {
55044
- const aPath = auditPath ?? path62.join(os54.homedir(), ".node9", "audit.log");
55390
+ const aPath = auditPath ?? path63.join(os56.homedir(), ".node9", "audit.log");
55045
55391
  let raw;
55046
55392
  try {
55047
- raw = fs65.readFileSync(aPath, "utf-8");
55393
+ raw = fs66.readFileSync(aPath, "utf-8");
55048
55394
  } catch {
55049
55395
  return [];
55050
55396
  }
@@ -55080,8 +55426,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
55080
55426
  return result;
55081
55427
  }
55082
55428
  function buildGeminiSessions(days, allAuditEntries) {
55083
- const tmpDir = path62.join(os54.homedir(), ".gemini", "tmp");
55084
- if (!fs65.existsSync(tmpDir)) return [];
55429
+ const tmpDir = path63.join(os56.homedir(), ".gemini", "tmp");
55430
+ if (!fs66.existsSync(tmpDir)) return [];
55085
55431
  const cutoff = days !== null ? (() => {
55086
55432
  const d = /* @__PURE__ */ new Date();
55087
55433
  d.setDate(d.getDate() - days);
@@ -55090,35 +55436,35 @@ function buildGeminiSessions(days, allAuditEntries) {
55090
55436
  })() : null;
55091
55437
  let slugDirs;
55092
55438
  try {
55093
- slugDirs = fs65.readdirSync(tmpDir);
55439
+ slugDirs = fs66.readdirSync(tmpDir);
55094
55440
  } catch {
55095
55441
  return [];
55096
55442
  }
55097
55443
  const summaries = [];
55098
55444
  for (const slug2 of slugDirs) {
55099
- const slugPath = path62.join(tmpDir, slug2);
55445
+ const slugPath = path63.join(tmpDir, slug2);
55100
55446
  try {
55101
- if (!fs65.statSync(slugPath).isDirectory()) continue;
55447
+ if (!fs66.statSync(slugPath).isDirectory()) continue;
55102
55448
  } catch {
55103
55449
  continue;
55104
55450
  }
55105
- let projectRoot = path62.join(os54.homedir(), slug2);
55451
+ let projectRoot = path63.join(os56.homedir(), slug2);
55106
55452
  try {
55107
- projectRoot = fs65.readFileSync(path62.join(slugPath, ".project_root"), "utf-8").trim();
55453
+ projectRoot = fs66.readFileSync(path63.join(slugPath, ".project_root"), "utf-8").trim();
55108
55454
  } catch {
55109
55455
  }
55110
- const chatsDir = path62.join(slugPath, "chats");
55111
- if (!fs65.existsSync(chatsDir)) continue;
55456
+ const chatsDir = path63.join(slugPath, "chats");
55457
+ if (!fs66.existsSync(chatsDir)) continue;
55112
55458
  let chatFiles;
55113
55459
  try {
55114
- chatFiles = fs65.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
55460
+ chatFiles = fs66.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
55115
55461
  } catch {
55116
55462
  continue;
55117
55463
  }
55118
55464
  for (const chatFile of chatFiles) {
55119
55465
  let raw;
55120
55466
  try {
55121
- raw = fs65.readFileSync(path62.join(chatsDir, chatFile), "utf-8");
55467
+ raw = fs66.readFileSync(path63.join(chatsDir, chatFile), "utf-8");
55122
55468
  } catch {
55123
55469
  continue;
55124
55470
  }
@@ -55198,8 +55544,8 @@ function buildGeminiSessions(days, allAuditEntries) {
55198
55544
  return summaries;
55199
55545
  }
55200
55546
  function buildCodexSessions(days, allAuditEntries) {
55201
- const sessionsBase = path62.join(os54.homedir(), ".codex", "sessions");
55202
- if (!fs65.existsSync(sessionsBase)) return [];
55547
+ const sessionsBase = path63.join(os56.homedir(), ".codex", "sessions");
55548
+ if (!fs66.existsSync(sessionsBase)) return [];
55203
55549
  const cutoff = days !== null ? (() => {
55204
55550
  const d = /* @__PURE__ */ new Date();
55205
55551
  d.setDate(d.getDate() - days);
@@ -55208,29 +55554,29 @@ function buildCodexSessions(days, allAuditEntries) {
55208
55554
  })() : null;
55209
55555
  const jsonlFiles = [];
55210
55556
  try {
55211
- for (const year of fs65.readdirSync(sessionsBase)) {
55212
- const yearPath = path62.join(sessionsBase, year);
55557
+ for (const year of fs66.readdirSync(sessionsBase)) {
55558
+ const yearPath = path63.join(sessionsBase, year);
55213
55559
  try {
55214
- if (!fs65.statSync(yearPath).isDirectory()) continue;
55560
+ if (!fs66.statSync(yearPath).isDirectory()) continue;
55215
55561
  } catch {
55216
55562
  continue;
55217
55563
  }
55218
- for (const month of fs65.readdirSync(yearPath)) {
55219
- const monthPath = path62.join(yearPath, month);
55564
+ for (const month of fs66.readdirSync(yearPath)) {
55565
+ const monthPath = path63.join(yearPath, month);
55220
55566
  try {
55221
- if (!fs65.statSync(monthPath).isDirectory()) continue;
55567
+ if (!fs66.statSync(monthPath).isDirectory()) continue;
55222
55568
  } catch {
55223
55569
  continue;
55224
55570
  }
55225
- for (const day of fs65.readdirSync(monthPath)) {
55226
- const dayPath = path62.join(monthPath, day);
55571
+ for (const day of fs66.readdirSync(monthPath)) {
55572
+ const dayPath = path63.join(monthPath, day);
55227
55573
  try {
55228
- if (!fs65.statSync(dayPath).isDirectory()) continue;
55574
+ if (!fs66.statSync(dayPath).isDirectory()) continue;
55229
55575
  } catch {
55230
55576
  continue;
55231
55577
  }
55232
- for (const file of fs65.readdirSync(dayPath)) {
55233
- if (file.endsWith(".jsonl")) jsonlFiles.push(path62.join(dayPath, file));
55578
+ for (const file of fs66.readdirSync(dayPath)) {
55579
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path63.join(dayPath, file));
55234
55580
  }
55235
55581
  }
55236
55582
  }
@@ -55242,7 +55588,7 @@ function buildCodexSessions(days, allAuditEntries) {
55242
55588
  for (const filePath of jsonlFiles) {
55243
55589
  let lines;
55244
55590
  try {
55245
- lines = fs65.readFileSync(filePath, "utf-8").split("\n");
55591
+ lines = fs66.readFileSync(filePath, "utf-8").split("\n");
55246
55592
  } catch {
55247
55593
  continue;
55248
55594
  }
@@ -55328,10 +55674,10 @@ function buildCodexSessions(days, allAuditEntries) {
55328
55674
  return summaries;
55329
55675
  }
55330
55676
  function buildSessions(days, historyPath) {
55331
- const hPath = historyPath ?? path62.join(os54.homedir(), ".claude", "history.jsonl");
55677
+ const hPath = historyPath ?? path63.join(os56.homedir(), ".claude", "history.jsonl");
55332
55678
  let historyRaw = "";
55333
55679
  try {
55334
- historyRaw = fs65.readFileSync(hPath, "utf-8");
55680
+ historyRaw = fs66.readFileSync(hPath, "utf-8");
55335
55681
  } catch {
55336
55682
  }
55337
55683
  const cutoff = days !== null ? (() => {
@@ -55355,7 +55701,7 @@ function buildSessions(days, historyPath) {
55355
55701
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
55356
55702
  let sessionLines = [];
55357
55703
  try {
55358
- sessionLines = fs65.readFileSync(jsonlFile, "utf-8").split("\n");
55704
+ sessionLines = fs66.readFileSync(jsonlFile, "utf-8").split("\n");
55359
55705
  } catch {
55360
55706
  }
55361
55707
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -55749,12 +56095,12 @@ function registerSessionTaintCommand(program2) {
55749
56095
 
55750
56096
  // src/cli/commands/skill-pin.ts
55751
56097
  import chalk36 from "chalk";
55752
- import fs66 from "fs";
55753
- import os55 from "os";
55754
- import path63 from "path";
56098
+ import fs67 from "fs";
56099
+ import os57 from "os";
56100
+ import path64 from "path";
55755
56101
  function wipeSkillSessions() {
55756
56102
  try {
55757
- fs66.rmSync(path63.join(os55.homedir(), ".node9", "skill-sessions"), {
56103
+ fs67.rmSync(path64.join(os57.homedir(), ".node9", "skill-sessions"), {
55758
56104
  recursive: true,
55759
56105
  force: true
55760
56106
  });
@@ -55836,15 +56182,15 @@ function registerSkillPinCommand(program2) {
55836
56182
  }
55837
56183
 
55838
56184
  // src/cli/commands/decisions.ts
55839
- import fs67 from "fs";
55840
- import os56 from "os";
55841
- import path64 from "path";
56185
+ import fs68 from "fs";
56186
+ import os58 from "os";
56187
+ import path65 from "path";
55842
56188
  import chalk37 from "chalk";
55843
- var DECISIONS_FILE2 = path64.join(os56.homedir(), ".node9", "decisions.json");
56189
+ var DECISIONS_FILE2 = path65.join(os58.homedir(), ".node9", "decisions.json");
55844
56190
  function readDecisions() {
55845
56191
  try {
55846
- if (!fs67.existsSync(DECISIONS_FILE2)) return {};
55847
- const raw = fs67.readFileSync(DECISIONS_FILE2, "utf-8");
56192
+ if (!fs68.existsSync(DECISIONS_FILE2)) return {};
56193
+ const raw = fs68.readFileSync(DECISIONS_FILE2, "utf-8");
55848
56194
  const parsed = JSON.parse(raw);
55849
56195
  const out = {};
55850
56196
  for (const [k, v] of Object.entries(parsed)) {
@@ -55856,11 +56202,11 @@ function readDecisions() {
55856
56202
  }
55857
56203
  }
55858
56204
  function writeDecisions(d) {
55859
- const dir = path64.dirname(DECISIONS_FILE2);
55860
- if (!fs67.existsSync(dir)) fs67.mkdirSync(dir, { recursive: true });
56205
+ const dir = path65.dirname(DECISIONS_FILE2);
56206
+ if (!fs68.existsSync(dir)) fs68.mkdirSync(dir, { recursive: true });
55861
56207
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
55862
- fs67.writeFileSync(tmp, JSON.stringify(d, null, 2));
55863
- fs67.renameSync(tmp, DECISIONS_FILE2);
56208
+ fs68.writeFileSync(tmp, JSON.stringify(d, null, 2));
56209
+ fs68.renameSync(tmp, DECISIONS_FILE2);
55864
56210
  }
55865
56211
  function registerDecisionsCommand(program2) {
55866
56212
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -55917,18 +56263,18 @@ Persistent decisions (${entries.length})
55917
56263
 
55918
56264
  // src/cli/commands/dlp.ts
55919
56265
  import chalk38 from "chalk";
55920
- import fs68 from "fs";
55921
- import path65 from "path";
55922
- import os57 from "os";
55923
- var AUDIT_LOG = path65.join(os57.homedir(), ".node9", "audit.log");
55924
- var RESOLVED_FILE = path65.join(os57.homedir(), ".node9", "dlp-resolved.json");
56266
+ import fs69 from "fs";
56267
+ import path66 from "path";
56268
+ import os59 from "os";
56269
+ var AUDIT_LOG = path66.join(os59.homedir(), ".node9", "audit.log");
56270
+ var RESOLVED_FILE = path66.join(os59.homedir(), ".node9", "dlp-resolved.json");
55925
56271
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
55926
56272
  function stripAnsi(s) {
55927
56273
  return s.replace(ANSI_RE, "");
55928
56274
  }
55929
56275
  function loadResolved() {
55930
56276
  try {
55931
- const raw = JSON.parse(fs68.readFileSync(RESOLVED_FILE, "utf-8"));
56277
+ const raw = JSON.parse(fs69.readFileSync(RESOLVED_FILE, "utf-8"));
55932
56278
  return new Set(raw);
55933
56279
  } catch {
55934
56280
  return /* @__PURE__ */ new Set();
@@ -55936,13 +56282,13 @@ function loadResolved() {
55936
56282
  }
55937
56283
  function saveResolved(resolved) {
55938
56284
  try {
55939
- fs68.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
56285
+ fs69.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
55940
56286
  } catch {
55941
56287
  }
55942
56288
  }
55943
56289
  function loadDlpFindings() {
55944
- if (!fs68.existsSync(AUDIT_LOG)) return [];
55945
- return fs68.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
56290
+ if (!fs69.existsSync(AUDIT_LOG)) return [];
56291
+ return fs69.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
55946
56292
  if (!line.trim()) return [];
55947
56293
  try {
55948
56294
  const e = JSON.parse(line);
@@ -56041,14 +56387,14 @@ function registerDlpCommand(program2) {
56041
56387
  // src/cli/commands/mask.ts
56042
56388
  init_dlp();
56043
56389
  import chalk39 from "chalk";
56044
- import fs69 from "fs";
56045
- import path66 from "path";
56046
- import os58 from "os";
56390
+ import fs70 from "fs";
56391
+ import path67 from "path";
56392
+ import os60 from "os";
56047
56393
  function findJsonlFiles(dir) {
56048
56394
  const results = [];
56049
- if (!fs69.existsSync(dir)) return results;
56050
- for (const entry of fs69.readdirSync(dir, { withFileTypes: true })) {
56051
- const full = path66.join(dir, entry.name);
56395
+ if (!fs70.existsSync(dir)) return results;
56396
+ for (const entry of fs70.readdirSync(dir, { withFileTypes: true })) {
56397
+ const full = path67.join(dir, entry.name);
56052
56398
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
56053
56399
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
56054
56400
  }
@@ -56091,7 +56437,7 @@ function redactJson(obj) {
56091
56437
  function processFile(filePath, dryRun) {
56092
56438
  let raw;
56093
56439
  try {
56094
- raw = fs69.readFileSync(filePath, "utf-8");
56440
+ raw = fs70.readFileSync(filePath, "utf-8");
56095
56441
  } catch {
56096
56442
  return { redactedLines: 0, patterns: [] };
56097
56443
  }
@@ -56123,14 +56469,14 @@ function processFile(filePath, dryRun) {
56123
56469
  }
56124
56470
  }
56125
56471
  if (!dryRun && redactedLines > 0) {
56126
- fs69.writeFileSync(filePath, newLines.join("\n"), "utf-8");
56472
+ fs70.writeFileSync(filePath, newLines.join("\n"), "utf-8");
56127
56473
  }
56128
56474
  return { redactedLines, patterns };
56129
56475
  }
56130
56476
  function processJsonFile(filePath, dryRun) {
56131
56477
  let raw;
56132
56478
  try {
56133
- raw = fs69.readFileSync(filePath, "utf-8");
56479
+ raw = fs70.readFileSync(filePath, "utf-8");
56134
56480
  } catch {
56135
56481
  return { redactedLines: 0, patterns: [] };
56136
56482
  }
@@ -56143,15 +56489,15 @@ function processJsonFile(filePath, dryRun) {
56143
56489
  const { value, modified, found } = redactJson(parsed);
56144
56490
  if (!modified) return { redactedLines: 0, patterns: [] };
56145
56491
  if (!dryRun) {
56146
- fs69.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
56492
+ fs70.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
56147
56493
  }
56148
56494
  return { redactedLines: 1, patterns: found };
56149
56495
  }
56150
56496
  function findJsonFiles(dir) {
56151
56497
  const results = [];
56152
- if (!fs69.existsSync(dir)) return results;
56153
- for (const entry of fs69.readdirSync(dir, { withFileTypes: true })) {
56154
- const full = path66.join(dir, entry.name);
56498
+ if (!fs70.existsSync(dir)) return results;
56499
+ for (const entry of fs70.readdirSync(dir, { withFileTypes: true })) {
56500
+ const full = path67.join(dir, entry.name);
56155
56501
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
56156
56502
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
56157
56503
  }
@@ -56160,9 +56506,9 @@ function findJsonFiles(dir) {
56160
56506
  function registerMaskCommand(program2) {
56161
56507
  program2.command("mask").description("Redact plaintext secrets from local AI session history files").option("--dry-run", "show what would be redacted without making changes").option("--all", "scan all history (default: last 30 days)").action(async (options) => {
56162
56508
  const dryRun = !!options.dryRun;
56163
- const home = os58.homedir();
56164
- const claudeDir = path66.join(home, ".claude", "projects");
56165
- const geminiDir = path66.join(home, ".gemini", "tmp");
56509
+ const home = os60.homedir();
56510
+ const claudeDir = path67.join(home, ".claude", "projects");
56511
+ const geminiDir = path67.join(home, ".gemini", "tmp");
56166
56512
  const allFiles = [
56167
56513
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
56168
56514
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -56170,7 +56516,7 @@ function registerMaskCommand(program2) {
56170
56516
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
56171
56517
  const filtered = cutoff ? allFiles.filter((f) => {
56172
56518
  try {
56173
- return fs69.statSync(f.path).mtime >= cutoff;
56519
+ return fs70.statSync(f.path).mtime >= cutoff;
56174
56520
  } catch {
56175
56521
  return false;
56176
56522
  }
@@ -56226,7 +56572,7 @@ function registerMaskCommand(program2) {
56226
56572
  // src/cli.ts
56227
56573
  init_blast();
56228
56574
  var { version } = JSON.parse(
56229
- fs72.readFileSync(path69.join(__dirname, "../package.json"), "utf-8")
56575
+ fs73.readFileSync(path70.join(__dirname, "../package.json"), "utf-8")
56230
56576
  );
56231
56577
  var program = new Command();
56232
56578
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
@@ -56252,6 +56598,11 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
56252
56598
  } else {
56253
56599
  console.log(chalk41.green(`\u2705 Logged in \u2014 agent mode`));
56254
56600
  console.log(chalk41.gray(` Team policy enforced for all calls via Node9 cloud.`));
56601
+ if (!isTestingMode()) {
56602
+ const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
56603
+ if (healed === "repaired")
56604
+ console.log(chalk41.green(` \u2713 Re-enabled daemon autostart (survives reboot)`));
56605
+ }
56255
56606
  }
56256
56607
  });
56257
56608
  program.command("signup").description("Create your node9 account / open the dashboard in your browser").option("--login", "Open the login page instead of signup").action((options) => {
@@ -56400,15 +56751,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
56400
56751
  } catch {
56401
56752
  }
56402
56753
  if (options.purge) {
56403
- const node9Dir = path69.join(os61.homedir(), ".node9");
56404
- if (fs72.existsSync(node9Dir)) {
56754
+ const node9Dir = path70.join(os63.homedir(), ".node9");
56755
+ if (fs73.existsSync(node9Dir)) {
56405
56756
  const confirmed = await confirm2({
56406
56757
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
56407
56758
  default: false
56408
56759
  });
56409
56760
  if (confirmed) {
56410
- fs72.rmSync(node9Dir, { recursive: true });
56411
- if (fs72.existsSync(node9Dir)) {
56761
+ fs73.rmSync(node9Dir, { recursive: true });
56762
+ if (fs73.existsSync(node9Dir)) {
56412
56763
  console.error(
56413
56764
  chalk41.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
56414
56765
  );
@@ -56533,7 +56884,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
56533
56884
  });
56534
56885
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
56535
56886
  try {
56536
- const dashboardPath = path69.join(__dirname, "dashboard.mjs");
56887
+ const dashboardPath = path70.join(__dirname, "dashboard.mjs");
56537
56888
  const dynamicImport = new Function("id", "return import(id)");
56538
56889
  const mod = await dynamicImport(`file://${dashboardPath}`);
56539
56890
  await mod.startMonitor();
@@ -56571,14 +56922,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
56571
56922
  Run "node9 addto claude" to register it as the statusLine.`
56572
56923
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
56573
56924
  if (subcommand === "debug") {
56574
- const flagFile = path69.join(os61.homedir(), ".node9", "hud-debug");
56925
+ const flagFile = path70.join(os63.homedir(), ".node9", "hud-debug");
56575
56926
  if (state === "on") {
56576
- fs72.mkdirSync(path69.dirname(flagFile), { recursive: true });
56577
- fs72.writeFileSync(flagFile, "");
56927
+ fs73.mkdirSync(path70.dirname(flagFile), { recursive: true });
56928
+ fs73.writeFileSync(flagFile, "");
56578
56929
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
56579
56930
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
56580
56931
  } else if (state === "off") {
56581
- if (fs72.existsSync(flagFile)) fs72.unlinkSync(flagFile);
56932
+ if (fs73.existsSync(flagFile)) fs73.unlinkSync(flagFile);
56582
56933
  console.log("HUD debug logging disabled.");
56583
56934
  } else {
56584
56935
  console.error("Usage: node9 hud debug on|off");
@@ -56701,9 +57052,9 @@ if (process.argv[2] !== "daemon") {
56701
57052
  const isCheckHook = process.argv[2] === "check";
56702
57053
  if (isCheckHook) {
56703
57054
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
56704
- const logPath = path69.join(os61.homedir(), ".node9", "hook-debug.log");
57055
+ const logPath = path70.join(os63.homedir(), ".node9", "hook-debug.log");
56705
57056
  const msg = reason instanceof Error ? reason.message : String(reason);
56706
- fs72.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
57057
+ fs73.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
56707
57058
  `);
56708
57059
  }
56709
57060
  process.exit(0);