@node9/proxy 1.19.2 → 1.19.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1708,7 +1708,11 @@ function extractCanonicalFindings(call, ctx) {
1708
1708
  })
1709
1709
  );
1710
1710
  }
1711
- if (PRIVILEGE_ESCALATION_RE.test(command)) {
1711
+ const ast = analyzeShellCommand(command);
1712
+ const sudoVariant = ast.actions.includes("sudo") || ast.actions.includes("su");
1713
+ const chmodVariant = ast.actions.includes("chmod") && (ast.allTokens.includes("777") || ast.allTokens.includes("0777") || ast.allTokens.includes("+x"));
1714
+ const chownVariant = ast.actions.includes("chown") && ast.allTokens.includes("root");
1715
+ if (sudoVariant || chmodVariant || chownVariant) {
1712
1716
  out.push(
1713
1717
  makeFinding({
1714
1718
  type: "privilege-escalation",
@@ -1840,7 +1844,7 @@ function* stringValues(obj, depth = 0) {
1840
1844
  }
1841
1845
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
1842
1846
  }
1843
- var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto2, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, FS_OP_CACHE_MAX, fsOpCache, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, PRIVILEGE_ESCALATION_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE;
1847
+ var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto2, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, FS_OP_CACHE_MAX, fsOpCache, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE;
1844
1848
  var init_dist = __esm({
1845
1849
  "packages/policy-engine/dist/index.mjs"() {
1846
1850
  "use strict";
@@ -3246,7 +3250,6 @@ var init_dist = __esm({
3246
3250
  LOOP_THRESHOLD_FOR_WASTE = 3;
3247
3251
  COST_PER_LOOP_ITER_USD = 6e-3;
3248
3252
  DESTRUCTIVE_OP_RE = /\brm\s+-[rRf]+\b|\bDROP\s+(TABLE|DATABASE|COLLECTION|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bgit\s+push\s+(--force|-f)\b|\bFLUSHALL\b|\bFLUSHDB\b|\bkubectl\s+delete\b|\bhelm\s+uninstall\b/i;
3249
- PRIVILEGE_ESCALATION_RE = /\b(sudo|su)\b\s+[a-z]|\bchmod\s+(0?777|\+x)\b|\bchown\s+root\b/i;
3250
3253
  SENSITIVE_PATH_RE = /\.aws\/(credentials|config)\b|\.ssh\/(id_rsa|id_ed25519|id_ecdsa|id_dsa)\b|\.env(\.|$|\b)|\.config\/gcloud\/credentials\.db\b|\.docker\/config\.json\b|\.netrc\b|\.npmrc\b|\.node9\/credentials\.json\b/i;
3251
3254
  FILE_TOOLS = /* @__PURE__ */ new Set([
3252
3255
  "read",
@@ -3266,7 +3269,7 @@ var init_dist = __esm({
3266
3269
  PII_PHONE_RE = /\b(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}\b/;
3267
3270
  PII_CC_RE = /\b(?:4\d{3}|5[1-5]\d{2}|3[47]\d{2}|6\d{3})[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/;
3268
3271
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
3269
- CANONICAL_EXTRACTOR_VERSION = "canonical-v1";
3272
+ CANONICAL_EXTRACTOR_VERSION = "canonical-v4";
3270
3273
  DEDUPE_PREVIEW_LEN = 120;
3271
3274
  TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
3272
3275
  /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
@@ -4739,19 +4742,16 @@ function isDaemonRunning() {
4739
4742
  }
4740
4743
  return false;
4741
4744
  }
4742
- const r = (0, import_child_process.spawnSync)("ss", ["-Htnp", `sport = :${DAEMON_PORT}`], {
4743
- encoding: "utf8",
4744
- timeout: 300
4745
- });
4746
- if (r.status === 0 && (r.stdout ?? "").includes(`:${DAEMON_PORT}`)) return true;
4747
- return false;
4745
+ return true;
4748
4746
  }
4747
+ return false;
4748
+ }
4749
+ async function isDaemonReachable(timeoutMs = 500) {
4749
4750
  try {
4750
- const r = (0, import_child_process.spawnSync)("ss", ["-Htnp", `sport = :${DAEMON_PORT}`], {
4751
- encoding: "utf8",
4752
- timeout: 300
4751
+ const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/settings`, {
4752
+ signal: AbortSignal.timeout(timeoutMs)
4753
4753
  });
4754
- return r.status === 0 && (r.stdout ?? "").includes(`:${DAEMON_PORT}`);
4754
+ return res.ok;
4755
4755
  } catch {
4756
4756
  return false;
4757
4757
  }
@@ -4810,7 +4810,7 @@ async function waitForDaemonDecision(id, signal) {
4810
4810
  if (signal) signal.removeEventListener("abort", onAbort);
4811
4811
  }
4812
4812
  }
4813
- async function notifyDaemonViewer(toolName, args, meta, riskMetadata) {
4813
+ async function notifyDaemonViewer(toolName, args, meta, riskMetadata, activityId, socketActivitySent) {
4814
4814
  const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
4815
4815
  const res = await fetch(`${base}/check`, {
4816
4816
  method: "POST",
@@ -4821,7 +4821,12 @@ async function notifyDaemonViewer(toolName, args, meta, riskMetadata) {
4821
4821
  slackDelegated: true,
4822
4822
  agent: meta?.agent,
4823
4823
  mcpServer: meta?.mcpServer,
4824
- ...riskMetadata && { riskMetadata }
4824
+ ...riskMetadata && { riskMetadata },
4825
+ // fromCLI=true tells the daemon the CLI already sent the activity
4826
+ // event via socket. Same contract as registerDaemonEntry — without
4827
+ // it the daemon double-emits 'activity' for cloud-enforced flows.
4828
+ fromCLI: socketActivitySent !== false,
4829
+ activityId
4825
4830
  }),
4826
4831
  signal: AbortSignal.timeout(3e3)
4827
4832
  });
@@ -4890,7 +4895,7 @@ async function resolveViaDaemon(id, decision, internalToken, source) {
4890
4895
  signal: AbortSignal.timeout(3e3)
4891
4896
  });
4892
4897
  }
4893
- var import_fs9, import_net, import_path9, import_os8, import_child_process, ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
4898
+ var import_fs9, import_net, import_path9, import_os8, ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
4894
4899
  var init_daemon = __esm({
4895
4900
  "src/auth/daemon.ts"() {
4896
4901
  "use strict";
@@ -4898,7 +4903,6 @@ var init_daemon = __esm({
4898
4903
  import_net = __toESM(require("net"));
4899
4904
  import_path9 = __toESM(require("path"));
4900
4905
  import_os8 = __toESM(require("os"));
4901
- import_child_process = require("child_process");
4902
4906
  ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path9.default.join(import_os8.default.tmpdir(), "node9-activity.sock");
4903
4907
  DAEMON_PORT = 7391;
4904
4908
  DAEMON_HOST = "127.0.0.1";
@@ -5098,9 +5102,9 @@ function sendDesktopNotification(title, body) {
5098
5102
  if (process.platform === "darwin") {
5099
5103
  const esc = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
5100
5104
  const script = `display notification "${esc(body)}" with title "${esc(title)}"`;
5101
- (0, import_child_process2.spawn)("osascript", ["-e", script], { detached: true, stdio: "ignore" }).unref();
5105
+ (0, import_child_process.spawn)("osascript", ["-e", script], { detached: true, stdio: "ignore" }).unref();
5102
5106
  } else if (process.platform === "linux") {
5103
- (0, import_child_process2.spawn)("notify-send", [title, body, "--icon=dialog-warning"], {
5107
+ (0, import_child_process.spawn)("notify-send", [title, body, "--icon=dialog-warning"], {
5104
5108
  detached: true,
5105
5109
  stdio: "ignore"
5106
5110
  }).unref();
@@ -5195,7 +5199,7 @@ activate
5195
5199
  display dialog (item 1 of argv) with title (item 2 of argv) ${buttons}
5196
5200
  end tell
5197
5201
  end run`;
5198
- childProcess = (0, import_child_process2.spawn)("osascript", ["-e", script, "--", message, title]);
5202
+ childProcess = (0, import_child_process.spawn)("osascript", ["-e", script, "--", message, title]);
5199
5203
  } else if (process.platform === "linux") {
5200
5204
  const pangoMessage = buildPangoMessage(
5201
5205
  toolName,
@@ -5221,12 +5225,12 @@ end run`;
5221
5225
  argsList.push("--cancel-label", "Block \u238B");
5222
5226
  argsList.push("--extra-button", "Always Allow");
5223
5227
  }
5224
- childProcess = (0, import_child_process2.spawn)("zenity", argsList);
5228
+ childProcess = (0, import_child_process.spawn)("zenity", argsList);
5225
5229
  } else if (process.platform === "win32") {
5226
5230
  const b64Msg = Buffer.from(message).toString("base64");
5227
5231
  const b64Title = Buffer.from(title).toString("base64");
5228
5232
  const ps = `Add-Type -AssemblyName PresentationFramework; $msg = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("${b64Msg}")); $title = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("${b64Title}")); $res = [System.Windows.MessageBox]::Show($msg, $title, "${locked ? "OK" : "YesNo"}", "Warning", "Button2", "DefaultDesktopOnly"); if ($res -eq "Yes") { exit 0 } else { exit 1 }`;
5229
- childProcess = (0, import_child_process2.spawn)("powershell", ["-Command", ps]);
5233
+ childProcess = (0, import_child_process.spawn)("powershell", ["-Command", ps]);
5230
5234
  }
5231
5235
  let output = "";
5232
5236
  childProcess?.stdout?.on("data", (d) => output += d.toString());
@@ -5242,11 +5246,11 @@ end run`;
5242
5246
  }
5243
5247
  });
5244
5248
  }
5245
- var import_child_process2, import_path11, isTestEnv;
5249
+ var import_child_process, import_path11, isTestEnv;
5246
5250
  var init_native = __esm({
5247
5251
  "src/ui/native.ts"() {
5248
5252
  "use strict";
5249
- import_child_process2 = require("child_process");
5253
+ import_child_process = require("child_process");
5250
5254
  import_path11 = __toESM(require("path"));
5251
5255
  init_context_sniper();
5252
5256
  isTestEnv = () => {
@@ -5967,7 +5971,14 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
5967
5971
  let daemonAllowCount = 1;
5968
5972
  if (approvers.terminal && isDaemonRunning() && !options?.calledFromDaemon) {
5969
5973
  if (cloudEnforced && cloudRequestId) {
5970
- const viewer = await notifyDaemonViewer(toolName, args, meta, riskMetadata).catch(() => null);
5974
+ const viewer = await notifyDaemonViewer(
5975
+ toolName,
5976
+ args,
5977
+ meta,
5978
+ riskMetadata,
5979
+ options?.activityId,
5980
+ options?.socketActivitySent
5981
+ ).catch(() => null);
5971
5982
  viewerId = viewer?.id ?? null;
5972
5983
  daemonEntryId = viewerId;
5973
5984
  if (viewer) daemonAllowCount = viewer.allowCount;
@@ -8060,6 +8071,7 @@ __export(scan_watermark_exports, {
8060
8071
  markUploadComplete: () => markUploadComplete,
8061
8072
  saveWatermark: () => saveWatermark,
8062
8073
  scanDelta: () => scanDelta,
8074
+ tickForensicBroadcast: () => tickForensicBroadcast,
8063
8075
  tickScanWatcher: () => tickScanWatcher
8064
8076
  });
8065
8077
  function freshWatermark() {
@@ -8263,6 +8275,25 @@ function extractFindingsFromLine(line, sessionId, lineIndex) {
8263
8275
  }
8264
8276
  return findings;
8265
8277
  }
8278
+ async function tickForensicBroadcast(offsets) {
8279
+ const out = [];
8280
+ const files = listJsonlFiles();
8281
+ for (const file of files) {
8282
+ const size = fileSize(file);
8283
+ const offset = offsets.get(file);
8284
+ if (offset === void 0) {
8285
+ offsets.set(file, size);
8286
+ continue;
8287
+ }
8288
+ if (size <= offset) continue;
8289
+ const sessionId = import_path19.default.basename(file, ".jsonl");
8290
+ const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
8291
+ out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
8292
+ });
8293
+ offsets.set(file, newOffset);
8294
+ }
8295
+ return out;
8296
+ }
8266
8297
  function markUploadComplete() {
8267
8298
  const state = loadWatermark();
8268
8299
  if (state.status === "schema-future") return;
@@ -11031,6 +11062,19 @@ data: ${JSON.stringify(data)}
11031
11062
  }
11032
11063
  });
11033
11064
  }
11065
+ function broadcastForensic(finding) {
11066
+ const severity = CRITICAL_FORENSIC_CATEGORIES.has(finding.type) ? "critical" : "warning";
11067
+ const event = {
11068
+ type: "forensic",
11069
+ id: `fnd_${(0, import_crypto6.randomUUID)()}`,
11070
+ ts: Date.now(),
11071
+ sessionId: finding.sessionId,
11072
+ category: finding.type,
11073
+ severity
11074
+ };
11075
+ if (finding.patternName !== void 0) event.patternName = finding.patternName;
11076
+ broadcast("forensic", event);
11077
+ }
11034
11078
  function abandonPending() {
11035
11079
  setAbandonTimer(null);
11036
11080
  pending.forEach((entry, id) => {
@@ -11214,7 +11258,7 @@ function bindActivitySocket() {
11214
11258
  });
11215
11259
  activitySocketServer = unixServer;
11216
11260
  }
11217
- var import_net2, import_fs21, import_path23, import_os19, import_crypto6, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
11261
+ var import_net2, import_fs21, import_path23, import_os19, import_crypto6, homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
11218
11262
  var init_state2 = __esm({
11219
11263
  "src/daemon/state.ts"() {
11220
11264
  "use strict";
@@ -11264,6 +11308,11 @@ var init_state2 = __esm({
11264
11308
  INPUT_PRICE_PER_1M = 3;
11265
11309
  OUTPUT_PRICE_PER_1M = 15;
11266
11310
  BYTES_PER_TOKEN = 4;
11311
+ CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
11312
+ "privilege-escalation",
11313
+ "destructive-op",
11314
+ "eval-of-remote"
11315
+ ]);
11267
11316
  WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
11268
11317
  "write",
11269
11318
  "write_file",
@@ -11604,7 +11653,26 @@ function startCloudSync() {
11604
11653
  const recurring = setInterval(() => void syncOnce(), intervalMs);
11605
11654
  recurring.unref();
11606
11655
  }
11607
- var import_fs22, import_https2, import_os20, import_path24, FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS;
11656
+ function startForensicBroadcast() {
11657
+ const tick = async () => {
11658
+ try {
11659
+ const findings = await tickForensicBroadcast(forensicBroadcastOffsets);
11660
+ for (const f of findings) broadcastForensic(f);
11661
+ } catch (err2) {
11662
+ const msg = err2 instanceof Error ? err2.message : String(err2);
11663
+ appendToLog(HOOK_DEBUG_LOG, {
11664
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
11665
+ kind: "forensic-broadcast-error",
11666
+ error: msg
11667
+ });
11668
+ }
11669
+ };
11670
+ const initial = setTimeout(() => void tick(), FORENSIC_INITIAL_DELAY_MS);
11671
+ initial.unref();
11672
+ const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
11673
+ recurring.unref();
11674
+ }
11675
+ var import_fs22, import_https2, import_os20, import_path24, FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
11608
11676
  var init_sync = __esm({
11609
11677
  "src/daemon/sync.ts"() {
11610
11678
  "use strict";
@@ -11616,6 +11684,8 @@ var init_sync = __esm({
11616
11684
  init_blast();
11617
11685
  init_dist();
11618
11686
  init_scan_watermark();
11687
+ init_state2();
11688
+ init_audit();
11619
11689
  FINDING_TO_SIGNAL3 = {
11620
11690
  dlp: "dlpFindings",
11621
11691
  pii: "piiFindings",
@@ -11632,6 +11702,9 @@ var init_sync = __esm({
11632
11702
  DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
11633
11703
  DEFAULT_INTERVAL_HOURS = 5;
11634
11704
  MIN_INTERVAL_HOURS = 1;
11705
+ FORENSIC_BROADCAST_INTERVAL_MS = 3e4;
11706
+ FORENSIC_INITIAL_DELAY_MS = 5e3;
11707
+ forensicBroadcastOffsets = /* @__PURE__ */ new Map();
11635
11708
  }
11636
11709
  });
11637
11710
 
@@ -11859,6 +11932,7 @@ var init_mcp_tools = __esm({
11859
11932
  function startDaemon() {
11860
11933
  startCostSync();
11861
11934
  startCloudSync();
11935
+ startForensicBroadcast();
11862
11936
  startDlpScanner();
11863
11937
  loadInsightCounts();
11864
11938
  const internalToken = (0, import_crypto7.randomUUID)();
@@ -12664,13 +12738,26 @@ data: ${JSON.stringify(item.data)}
12664
12738
  }).then((res) => {
12665
12739
  if (res.ok) {
12666
12740
  try {
12667
- const r = (0, import_child_process3.spawnSync)("ss", ["-Htnp", `sport = :${DAEMON_PORT}`], {
12741
+ let orphanPid = null;
12742
+ const ss = (0, import_child_process2.spawnSync)("ss", ["-Htnp", `sport = :${DAEMON_PORT}`], {
12668
12743
  encoding: "utf8",
12669
12744
  timeout: 1e3
12670
12745
  });
12671
- const match = r.stdout?.match(/pid=(\d+)/);
12672
- if (match) {
12673
- const orphanPid = parseInt(match[1], 10);
12746
+ if (!ss.error && ss.status === 0) {
12747
+ const m = ss.stdout?.match(/pid=(\d+)/);
12748
+ if (m) orphanPid = parseInt(m[1], 10);
12749
+ } else if (ss.error?.code === "ENOENT" || ss.status === null) {
12750
+ const lsof = (0, import_child_process2.spawnSync)(
12751
+ "lsof",
12752
+ ["-nP", `-iTCP:${DAEMON_PORT}`, "-sTCP:LISTEN", "-t"],
12753
+ { encoding: "utf8", timeout: 1e3 }
12754
+ );
12755
+ if (!lsof.error && lsof.status === 0) {
12756
+ const first = (lsof.stdout ?? "").split("\n")[0].trim();
12757
+ if (/^\d+$/.test(first)) orphanPid = parseInt(first, 10);
12758
+ }
12759
+ }
12760
+ if (orphanPid !== null) {
12674
12761
  process.kill(orphanPid, 0);
12675
12762
  atomicWriteSync2(
12676
12763
  DAEMON_PID_FILE,
@@ -12705,14 +12792,14 @@ data: ${JSON.stringify(item.data)}
12705
12792
  JSON.stringify({ pid: process.pid, port: DAEMON_PORT, internalToken, autoStarted }),
12706
12793
  { mode: 384 }
12707
12794
  );
12708
- console.error(import_chalk6.default.green(`\u{1F6E1}\uFE0F Node9 Guard LIVE: http://127.0.0.1:${DAEMON_PORT}`));
12795
+ console.error(import_chalk6.default.green(`\u{1F6E1}\uFE0F Node9 Guard LIVE on 127.0.0.1:${DAEMON_PORT}`));
12709
12796
  });
12710
12797
  if (watchMode) {
12711
12798
  console.error(import_chalk6.default.cyan("\u{1F6F0}\uFE0F Flight Recorder active \u2014 daemon will not idle-timeout"));
12712
12799
  }
12713
12800
  startActivitySocket();
12714
12801
  }
12715
- var import_http, import_fs25, import_path27, import_os23, import_crypto7, import_child_process3, import_chalk6;
12802
+ var import_http, import_fs25, import_path27, import_os23, import_crypto7, import_child_process2, import_chalk6;
12716
12803
  var init_server = __esm({
12717
12804
  "src/daemon/server.ts"() {
12718
12805
  "use strict";
@@ -12721,7 +12808,7 @@ var init_server = __esm({
12721
12808
  import_path27 = __toESM(require("path"));
12722
12809
  import_os23 = __toESM(require("os"));
12723
12810
  import_crypto7 = require("crypto");
12724
- import_child_process3 = require("child_process");
12811
+ import_child_process2 = require("child_process");
12725
12812
  import_chalk6 = __toESM(require("chalk"));
12726
12813
  init_core();
12727
12814
  init_scan();
@@ -12746,7 +12833,7 @@ function resolveNode9Binary() {
12746
12833
  }
12747
12834
  try {
12748
12835
  const cmd = process.platform === "win32" ? "where" : "which";
12749
- const r = (0, import_child_process4.spawnSync)(cmd, ["node9"], { encoding: "utf8", timeout: 3e3 });
12836
+ const r = (0, import_child_process3.spawnSync)(cmd, ["node9"], { encoding: "utf8", timeout: 3e3 });
12750
12837
  if (r.status === 0 && r.stdout.trim()) {
12751
12838
  return r.stdout.trim().split("\n")[0].trim();
12752
12839
  }
@@ -12798,8 +12885,8 @@ function installLaunchd(binaryPath) {
12798
12885
  const dir = import_path28.default.dirname(LAUNCHD_PLIST);
12799
12886
  if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
12800
12887
  import_fs26.default.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
12801
- (0, import_child_process4.spawnSync)("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
12802
- const r = (0, import_child_process4.spawnSync)("launchctl", ["load", "-w", LAUNCHD_PLIST], {
12888
+ (0, import_child_process3.spawnSync)("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
12889
+ const r = (0, import_child_process3.spawnSync)("launchctl", ["load", "-w", LAUNCHD_PLIST], {
12803
12890
  encoding: "utf8",
12804
12891
  timeout: 5e3
12805
12892
  });
@@ -12809,7 +12896,7 @@ function installLaunchd(binaryPath) {
12809
12896
  }
12810
12897
  function uninstallLaunchd() {
12811
12898
  if (import_fs26.default.existsSync(LAUNCHD_PLIST)) {
12812
- (0, import_child_process4.spawnSync)("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
12899
+ (0, import_child_process3.spawnSync)("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
12813
12900
  import_fs26.default.unlinkSync(LAUNCHD_PLIST);
12814
12901
  }
12815
12902
  }
@@ -12838,18 +12925,18 @@ function installSystemd(binaryPath) {
12838
12925
  }
12839
12926
  import_fs26.default.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
12840
12927
  try {
12841
- (0, import_child_process4.execFileSync)("loginctl", ["enable-linger", import_os24.default.userInfo().username], { timeout: 3e3 });
12928
+ (0, import_child_process3.execFileSync)("loginctl", ["enable-linger", import_os24.default.userInfo().username], { timeout: 3e3 });
12842
12929
  } catch {
12843
12930
  }
12844
- const reload = (0, import_child_process4.spawnSync)("systemctl", ["--user", "daemon-reload"], {
12931
+ const reload = (0, import_child_process3.spawnSync)("systemctl", ["--user", "daemon-reload"], {
12845
12932
  encoding: "utf8",
12846
12933
  timeout: 5e3
12847
12934
  });
12848
12935
  if (reload.status !== 0) {
12849
12936
  throw new Error(`systemctl daemon-reload failed: ${reload.stderr}`);
12850
12937
  }
12851
- (0, import_child_process4.spawnSync)("systemctl", ["--user", "stop", "node9-daemon"], { encoding: "utf8", timeout: 3e3 });
12852
- const enable = (0, import_child_process4.spawnSync)("systemctl", ["--user", "enable", "--now", "node9-daemon"], {
12938
+ (0, import_child_process3.spawnSync)("systemctl", ["--user", "stop", "node9-daemon"], { encoding: "utf8", timeout: 3e3 });
12939
+ const enable = (0, import_child_process3.spawnSync)("systemctl", ["--user", "enable", "--now", "node9-daemon"], {
12853
12940
  encoding: "utf8",
12854
12941
  timeout: 5e3
12855
12942
  });
@@ -12859,11 +12946,11 @@ function installSystemd(binaryPath) {
12859
12946
  }
12860
12947
  function uninstallSystemd() {
12861
12948
  if (import_fs26.default.existsSync(SYSTEMD_UNIT)) {
12862
- (0, import_child_process4.spawnSync)("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
12949
+ (0, import_child_process3.spawnSync)("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
12863
12950
  encoding: "utf8",
12864
12951
  timeout: 5e3
12865
12952
  });
12866
- (0, import_child_process4.spawnSync)("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
12953
+ (0, import_child_process3.spawnSync)("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
12867
12954
  import_fs26.default.unlinkSync(SYSTEMD_UNIT);
12868
12955
  }
12869
12956
  }
@@ -12881,7 +12968,7 @@ function stopRunningDaemon() {
12881
12968
  try {
12882
12969
  process.kill(pid, "SIGTERM");
12883
12970
  const deadline = Date.now() + 3e3;
12884
- const pollStop = (0, import_child_process4.spawnSync)(
12971
+ const pollStop = (0, import_child_process3.spawnSync)(
12885
12972
  "sh",
12886
12973
  ["-c", `while kill -0 ${pid} 2>/dev/null; do sleep 0.1; done`],
12887
12974
  {
@@ -12913,7 +13000,7 @@ function installDaemonService() {
12913
13000
  return { ok: true, platform: "launchd", alreadyInstalled };
12914
13001
  }
12915
13002
  if (process.platform === "linux") {
12916
- const check = (0, import_child_process4.spawnSync)("systemctl", ["--user", "--version"], {
13003
+ const check = (0, import_child_process3.spawnSync)("systemctl", ["--user", "--version"], {
12917
13004
  encoding: "utf8",
12918
13005
  timeout: 2e3
12919
13006
  });
@@ -12964,14 +13051,14 @@ function isDaemonServiceInstalled() {
12964
13051
  if (process.platform === "linux") return isSystemdInstalled();
12965
13052
  return false;
12966
13053
  }
12967
- var import_fs26, import_path28, import_os24, import_child_process4, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
13054
+ var import_fs26, import_path28, import_os24, import_child_process3, LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
12968
13055
  var init_service = __esm({
12969
13056
  "src/daemon/service.ts"() {
12970
13057
  "use strict";
12971
13058
  import_fs26 = __toESM(require("fs"));
12972
13059
  import_path28 = __toESM(require("path"));
12973
13060
  import_os24 = __toESM(require("os"));
12974
- import_child_process4 = require("child_process");
13061
+ import_child_process3 = require("child_process");
12975
13062
  LAUNCHD_LABEL = "ai.node9.daemon";
12976
13063
  LAUNCHD_PLIST = import_path28.default.join(import_os24.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
12977
13064
  SYSTEMD_UNIT_DIR = import_path28.default.join(import_os24.default.homedir(), ".config", "systemd", "user");
@@ -13021,28 +13108,19 @@ function daemonStatus() {
13021
13108
  processStatus = import_chalk7.default.yellow("not running (stale PID file)");
13022
13109
  }
13023
13110
  } else {
13024
- const r = (0, import_child_process5.spawnSync)("ss", ["-Htnp", `sport = :${DAEMON_PORT}`], {
13025
- encoding: "utf8",
13026
- timeout: 500
13027
- });
13028
- if (r.status === 0 && (r.stdout ?? "").includes(`:${DAEMON_PORT}`)) {
13029
- processStatus = import_chalk7.default.yellow(`running (orphaned \u2014 no PID file)`);
13030
- } else {
13031
- processStatus = import_chalk7.default.yellow("not running");
13032
- }
13111
+ processStatus = import_chalk7.default.yellow("not running");
13033
13112
  }
13034
13113
  console.log(`
13035
13114
  Process : ${processStatus}`);
13036
13115
  console.log(` Service : ${serviceLabel}
13037
13116
  `);
13038
13117
  }
13039
- var import_fs27, import_chalk7, import_child_process5, MAX_PID;
13118
+ var import_fs27, import_chalk7, MAX_PID;
13040
13119
  var init_daemon2 = __esm({
13041
13120
  "src/daemon/index.ts"() {
13042
13121
  "use strict";
13043
13122
  import_fs27 = __toESM(require("fs"));
13044
13123
  import_chalk7 = __toESM(require("chalk"));
13045
- import_child_process5 = require("child_process");
13046
13124
  init_server();
13047
13125
  init_state2();
13048
13126
  init_service();
@@ -13219,7 +13297,7 @@ async function ensureDaemon() {
13219
13297
  } catch {
13220
13298
  }
13221
13299
  console.log(import_chalk30.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon..."));
13222
- const child = (0, import_child_process15.spawn)(process.execPath, [process.argv[1], "daemon"], {
13300
+ const child = (0, import_child_process13.spawn)(process.execPath, [process.argv[1], "daemon"], {
13223
13301
  detached: true,
13224
13302
  stdio: "ignore",
13225
13303
  env: { ...process.env, NODE9_AUTO_STARTED: "1" }
@@ -13849,7 +13927,7 @@ async function startTail(options = {}) {
13849
13927
  process.exit(1);
13850
13928
  });
13851
13929
  }
13852
- var import_http2, import_chalk30, import_fs45, import_os41, import_path47, import_readline6, import_child_process15, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
13930
+ var import_http2, import_chalk30, import_fs45, import_os41, import_path47, import_readline6, import_child_process13, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
13853
13931
  var init_tail = __esm({
13854
13932
  "src/tui/tail.ts"() {
13855
13933
  "use strict";
@@ -13859,7 +13937,7 @@ var init_tail = __esm({
13859
13937
  import_os41 = __toESM(require("os"));
13860
13938
  import_path47 = __toESM(require("path"));
13861
13939
  import_readline6 = __toESM(require("readline"));
13862
- import_child_process15 = require("child_process");
13940
+ import_child_process13 = require("child_process");
13863
13941
  init_daemon2();
13864
13942
  init_daemon();
13865
13943
  PID_FILE = import_path47.default.join(import_os41.default.homedir(), ".node9", "daemon.pid");
@@ -14315,7 +14393,7 @@ function parseDuration(str) {
14315
14393
  // src/proxy/index.ts
14316
14394
  var import_readline2 = __toESM(require("readline"));
14317
14395
  var import_chalk8 = __toESM(require("chalk"));
14318
- var import_child_process6 = require("child_process");
14396
+ var import_child_process4 = require("child_process");
14319
14397
  var import_execa = require("execa");
14320
14398
  var import_execa2 = require("execa");
14321
14399
  init_orchestrator();
@@ -14403,11 +14481,11 @@ async function runProxy(targetCommand) {
14403
14481
  }
14404
14482
  console.error(import_chalk8.default.green(`\u{1F680} Node9 Proxy Active: Monitoring [${targetCommand}]`));
14405
14483
  const spawnEnv = { ...process.env, FORCE_COLOR: "1" };
14406
- const child = useShell ? (0, import_child_process6.spawn)("/bin/bash", ["-c", targetCommand], {
14484
+ const child = useShell ? (0, import_child_process4.spawn)("/bin/bash", ["-c", targetCommand], {
14407
14485
  stdio: ["pipe", "pipe", "inherit"],
14408
14486
  shell: false,
14409
14487
  env: spawnEnv
14410
- }) : (0, import_child_process6.spawn)(executable, args, { stdio: ["pipe", "pipe", "inherit"], shell: false, env: spawnEnv });
14488
+ }) : (0, import_child_process4.spawn)(executable, args, { stdio: ["pipe", "pipe", "inherit"], shell: false, env: spawnEnv });
14411
14489
  const agentIn = import_readline2.default.createInterface({ input: process.stdin, terminal: false });
14412
14490
  agentIn.on("line", async (line) => {
14413
14491
  let message;
@@ -14471,7 +14549,7 @@ async function runProxy(targetCommand) {
14471
14549
  }
14472
14550
 
14473
14551
  // src/cli/daemon-starter.ts
14474
- var import_child_process7 = require("child_process");
14552
+ var import_child_process5 = require("child_process");
14475
14553
  var import_path29 = __toESM(require("path"));
14476
14554
  var import_fs28 = __toESM(require("fs"));
14477
14555
  init_daemon();
@@ -14489,7 +14567,7 @@ async function autoStartDaemonAndWait() {
14489
14567
  }
14490
14568
  if (!resolvedArgv1.endsWith(".js")) return false;
14491
14569
  try {
14492
- const child = (0, import_child_process7.spawn)(process.execPath, [resolvedArgv1, "daemon"], {
14570
+ const child = (0, import_child_process5.spawn)(process.execPath, [resolvedArgv1, "daemon"], {
14493
14571
  detached: true,
14494
14572
  stdio: "ignore",
14495
14573
  env: {
@@ -14501,13 +14579,7 @@ async function autoStartDaemonAndWait() {
14501
14579
  for (let i = 0; i < 20; i++) {
14502
14580
  await new Promise((r) => setTimeout(r, 250));
14503
14581
  if (!isDaemonRunning()) continue;
14504
- try {
14505
- const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/settings`, {
14506
- signal: AbortSignal.timeout(500)
14507
- });
14508
- if (res.ok) return true;
14509
- } catch {
14510
- }
14582
+ if (await isDaemonReachable()) return true;
14511
14583
  }
14512
14584
  } catch {
14513
14585
  }
@@ -14517,7 +14589,7 @@ async function autoStartDaemonAndWait() {
14517
14589
  // src/cli/commands/check.ts
14518
14590
  var import_chalk9 = __toESM(require("chalk"));
14519
14591
  var import_fs31 = __toESM(require("fs"));
14520
- var import_child_process9 = require("child_process");
14592
+ var import_child_process7 = require("child_process");
14521
14593
  var import_path32 = __toESM(require("path"));
14522
14594
  var import_os27 = __toESM(require("os"));
14523
14595
  init_orchestrator();
@@ -14526,7 +14598,7 @@ init_config();
14526
14598
  init_policy();
14527
14599
 
14528
14600
  // src/undo.ts
14529
- var import_child_process8 = require("child_process");
14601
+ var import_child_process6 = require("child_process");
14530
14602
  var import_crypto8 = __toESM(require("crypto"));
14531
14603
  var import_fs29 = __toESM(require("fs"));
14532
14604
  var import_net3 = __toESM(require("net"));
@@ -14639,7 +14711,7 @@ function ensureShadowRepo(shadowDir, cwd) {
14639
14711
  cleanOrphanedIndexFiles(shadowDir);
14640
14712
  const normalizedCwd = normalizeCwdForHash(cwd);
14641
14713
  const shadowEnvBase = { ...process.env, GIT_DIR: shadowDir, GIT_WORK_TREE: cwd };
14642
- const check = (0, import_child_process8.spawnSync)("git", ["rev-parse", "--git-dir"], {
14714
+ const check = (0, import_child_process6.spawnSync)("git", ["rev-parse", "--git-dir"], {
14643
14715
  env: shadowEnvBase,
14644
14716
  timeout: 3e3
14645
14717
  });
@@ -14665,17 +14737,17 @@ function ensureShadowRepo(shadowDir, cwd) {
14665
14737
  import_fs29.default.mkdirSync(shadowDir, { recursive: true });
14666
14738
  } catch {
14667
14739
  }
14668
- const init = (0, import_child_process8.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
14740
+ const init = (0, import_child_process6.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
14669
14741
  if (init.status !== 0 || init.error) {
14670
14742
  const reason = init.error ? init.error.message : init.stderr?.toString();
14671
14743
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
14672
14744
  return false;
14673
14745
  }
14674
14746
  const configFile = import_path30.default.join(shadowDir, "config");
14675
- (0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
14747
+ (0, import_child_process6.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
14676
14748
  timeout: 3e3
14677
14749
  });
14678
- (0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.fsmonitor", "true"], {
14750
+ (0, import_child_process6.spawnSync)("git", ["config", "--file", configFile, "core.fsmonitor", "true"], {
14679
14751
  timeout: 3e3
14680
14752
  });
14681
14753
  try {
@@ -14686,14 +14758,17 @@ function ensureShadowRepo(shadowDir, cwd) {
14686
14758
  }
14687
14759
  function buildGitEnv(cwd) {
14688
14760
  const shadowDir = getShadowRepoDir(cwd);
14689
- const check = (0, import_child_process8.spawnSync)("git", ["rev-parse", "--git-dir"], {
14761
+ const check = (0, import_child_process6.spawnSync)("git", ["rev-parse", "--git-dir"], {
14690
14762
  env: { ...process.env, GIT_DIR: shadowDir, GIT_WORK_TREE: cwd },
14691
14763
  timeout: 2e3
14692
14764
  });
14693
14765
  if (check.status === 0) {
14694
14766
  return { ...process.env, GIT_DIR: shadowDir, GIT_WORK_TREE: cwd };
14695
14767
  }
14696
- return { ...process.env };
14768
+ const env = { ...process.env };
14769
+ delete env.GIT_DIR;
14770
+ delete env.GIT_WORK_TREE;
14771
+ return env;
14697
14772
  }
14698
14773
  async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = []) {
14699
14774
  let indexFile = null;
@@ -14711,11 +14786,11 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14711
14786
  GIT_WORK_TREE: cwd,
14712
14787
  GIT_INDEX_FILE: indexFile
14713
14788
  };
14714
- (0, import_child_process8.spawnSync)("git", ["add", "-A"], { env: shadowEnv, timeout: GIT_TIMEOUT });
14715
- const treeRes = (0, import_child_process8.spawnSync)("git", ["write-tree"], { env: shadowEnv, timeout: GIT_TIMEOUT });
14789
+ (0, import_child_process6.spawnSync)("git", ["add", "-A"], { env: shadowEnv, timeout: GIT_TIMEOUT });
14790
+ const treeRes = (0, import_child_process6.spawnSync)("git", ["write-tree"], { env: shadowEnv, timeout: GIT_TIMEOUT });
14716
14791
  const treeHash = treeRes.stdout?.toString().trim();
14717
14792
  if (!treeHash || treeRes.status !== 0) return null;
14718
- const commitRes = (0, import_child_process8.spawnSync)(
14793
+ const commitRes = (0, import_child_process6.spawnSync)(
14719
14794
  "git",
14720
14795
  ["commit-tree", treeHash, "-m", `Node9 AI Snapshot: ${(/* @__PURE__ */ new Date()).toISOString()}`],
14721
14796
  { env: shadowEnv, timeout: GIT_TIMEOUT }
@@ -14727,7 +14802,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14727
14802
  let capturedFiles = [];
14728
14803
  let capturedDiff = null;
14729
14804
  if (prevEntry) {
14730
- const filesRes = (0, import_child_process8.spawnSync)("git", ["diff", "--name-only", prevEntry.hash, commitHash], {
14805
+ const filesRes = (0, import_child_process6.spawnSync)("git", ["diff", "--name-only", prevEntry.hash, commitHash], {
14731
14806
  env: shadowEnv,
14732
14807
  timeout: GIT_TIMEOUT
14733
14808
  });
@@ -14737,7 +14812,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14737
14812
  if (capturedFiles.length === 0) {
14738
14813
  return prevEntry.hash;
14739
14814
  }
14740
- const diffRes = (0, import_child_process8.spawnSync)("git", ["diff", prevEntry.hash, commitHash], {
14815
+ const diffRes = (0, import_child_process6.spawnSync)("git", ["diff", prevEntry.hash, commitHash], {
14741
14816
  env: shadowEnv,
14742
14817
  timeout: GIT_TIMEOUT
14743
14818
  });
@@ -14745,7 +14820,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14745
14820
  capturedDiff = diffRes.stdout?.toString() || null;
14746
14821
  }
14747
14822
  } else {
14748
- const filesRes = (0, import_child_process8.spawnSync)("git", ["ls-tree", "-r", "--name-only", commitHash], {
14823
+ const filesRes = (0, import_child_process6.spawnSync)("git", ["ls-tree", "-r", "--name-only", commitHash], {
14749
14824
  env: shadowEnv,
14750
14825
  timeout: GIT_TIMEOUT
14751
14826
  });
@@ -14778,7 +14853,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
14778
14853
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
14779
14854
  import_fs29.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
14780
14855
  if (shouldGc) {
14781
- (0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
14856
+ (0, import_child_process6.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
14782
14857
  }
14783
14858
  return commitHash;
14784
14859
  } catch (err2) {
@@ -14799,14 +14874,14 @@ function getSnapshotHistory() {
14799
14874
  function computeUndoDiff(hash, cwd) {
14800
14875
  try {
14801
14876
  const env = buildGitEnv(cwd);
14802
- const statRes = (0, import_child_process8.spawnSync)("git", ["diff", hash, "--stat", "--", "."], {
14877
+ const statRes = (0, import_child_process6.spawnSync)("git", ["diff", hash, "--stat", "--", "."], {
14803
14878
  cwd,
14804
14879
  env,
14805
14880
  timeout: GIT_TIMEOUT
14806
14881
  });
14807
14882
  const stat = statRes.stdout?.toString().trim();
14808
14883
  if (!stat || statRes.status !== 0) return null;
14809
- const diffRes = (0, import_child_process8.spawnSync)("git", ["diff", hash, "--", "."], {
14884
+ const diffRes = (0, import_child_process6.spawnSync)("git", ["diff", hash, "--", "."], {
14810
14885
  cwd,
14811
14886
  env,
14812
14887
  timeout: GIT_TIMEOUT
@@ -14825,7 +14900,7 @@ function applyUndo(hash, cwd) {
14825
14900
  try {
14826
14901
  const dir = cwd ?? process.cwd();
14827
14902
  const env = buildGitEnv(dir);
14828
- const restore = (0, import_child_process8.spawnSync)("git", ["restore", "--source", hash, "--staged", "--worktree", "."], {
14903
+ const restore = (0, import_child_process6.spawnSync)("git", ["restore", "--source", hash, "--staged", "--worktree", "."], {
14829
14904
  cwd: dir,
14830
14905
  env,
14831
14906
  timeout: GIT_TIMEOUT
@@ -14837,7 +14912,7 @@ function applyUndo(hash, cwd) {
14837
14912
  }
14838
14913
  return false;
14839
14914
  }
14840
- const lsTree = (0, import_child_process8.spawnSync)("git", ["ls-tree", "-r", "--name-only", hash], {
14915
+ const lsTree = (0, import_child_process6.spawnSync)("git", ["ls-tree", "-r", "--name-only", hash], {
14841
14916
  cwd: dir,
14842
14917
  env,
14843
14918
  timeout: GIT_TIMEOUT
@@ -14856,8 +14931,8 @@ function applyUndo(hash, cwd) {
14856
14931
  `);
14857
14932
  return false;
14858
14933
  }
14859
- const tracked = (0, import_child_process8.spawnSync)("git", ["ls-files"], { cwd: dir, env, timeout: GIT_TIMEOUT }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
14860
- const untracked = (0, import_child_process8.spawnSync)("git", ["ls-files", "--others", "--exclude-standard"], {
14934
+ const tracked = (0, import_child_process6.spawnSync)("git", ["ls-files"], { cwd: dir, env, timeout: GIT_TIMEOUT }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
14935
+ const untracked = (0, import_child_process6.spawnSync)("git", ["ls-files", "--others", "--exclude-standard"], {
14861
14936
  cwd: dir,
14862
14937
  env,
14863
14938
  timeout: GIT_TIMEOUT
@@ -15128,7 +15203,7 @@ RAW: ${raw}
15128
15203
  ]) {
15129
15204
  delete safeEnv[key];
15130
15205
  }
15131
- const d = (0, import_child_process9.spawn)(process.execPath, [scriptPath, "daemon"], {
15206
+ const d = (0, import_child_process7.spawn)(process.execPath, [scriptPath, "daemon"], {
15132
15207
  detached: true,
15133
15208
  stdio: "ignore",
15134
15209
  env: { ...safeEnv, NODE9_AUTO_STARTED: "1" }
@@ -15961,7 +16036,7 @@ var import_chalk11 = __toESM(require("chalk"));
15961
16036
  var import_fs33 = __toESM(require("fs"));
15962
16037
  var import_path34 = __toESM(require("path"));
15963
16038
  var import_os29 = __toESM(require("os"));
15964
- var import_child_process10 = require("child_process");
16039
+ var import_child_process8 = require("child_process");
15965
16040
  init_daemon();
15966
16041
  function registerDoctorCommand(program2, version2) {
15967
16042
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
@@ -15987,7 +16062,7 @@ function registerDoctorCommand(program2, version2) {
15987
16062
  `));
15988
16063
  section("Binary");
15989
16064
  try {
15990
- const which = (0, import_child_process10.execSync)("which node9", { encoding: "utf-8", timeout: 3e3 }).trim();
16065
+ const which = (0, import_child_process8.execSync)("which node9", { encoding: "utf-8", timeout: 3e3 }).trim();
15991
16066
  pass(`node9 found at ${which}`);
15992
16067
  } catch {
15993
16068
  warn(
@@ -16005,7 +16080,7 @@ function registerDoctorCommand(program2, version2) {
16005
16080
  );
16006
16081
  }
16007
16082
  try {
16008
- const gitVersion = (0, import_child_process10.execSync)("git --version", { encoding: "utf-8", timeout: 3e3 }).trim();
16083
+ const gitVersion = (0, import_child_process8.execSync)("git --version", { encoding: "utf-8", timeout: 3e3 }).trim();
16009
16084
  pass(gitVersion);
16010
16085
  } catch {
16011
16086
  warn(
@@ -16106,10 +16181,12 @@ function registerDoctorCommand(program2, version2) {
16106
16181
  }
16107
16182
  section("Daemon (optional)");
16108
16183
  if (isDaemonRunning()) {
16109
- pass(`Browser dashboard running \u2192 http://${DAEMON_HOST}:${DAEMON_PORT}/`);
16184
+ pass(
16185
+ `Daemon running on ${DAEMON_HOST}:${DAEMON_PORT} \u2014 terminal & native approvals enabled`
16186
+ );
16110
16187
  } else {
16111
16188
  warn(
16112
- "Daemon not running \u2014 browser approvals unavailable",
16189
+ "Daemon not running \u2014 terminal & native approvals unavailable",
16113
16190
  "Run: node9 daemon --background"
16114
16191
  );
16115
16192
  }
@@ -16981,7 +17058,7 @@ function registerReportCommand(program2) {
16981
17058
 
16982
17059
  // src/cli/commands/daemon-cmd.ts
16983
17060
  var import_chalk14 = __toESM(require("chalk"));
16984
- var import_child_process11 = require("child_process");
17061
+ var import_child_process9 = require("child_process");
16985
17062
  init_daemon2();
16986
17063
  var VALID_ACTIONS = "start | stop | restart | status | install | uninstall";
16987
17064
  function registerDaemonCommand(program2) {
@@ -17020,7 +17097,7 @@ function registerDaemonCommand(program2) {
17020
17097
  if (cmd === "restart") {
17021
17098
  stopDaemon();
17022
17099
  await new Promise((r) => setTimeout(r, 500));
17023
- const child = (0, import_child_process11.spawn)(process.execPath, [process.argv[1], "daemon"], {
17100
+ const child = (0, import_child_process9.spawn)(process.execPath, [process.argv[1], "daemon"], {
17024
17101
  detached: true,
17025
17102
  stdio: "ignore",
17026
17103
  env: { ...process.env, NODE9_AUTO_STARTED: "1" }
@@ -17050,7 +17127,7 @@ function registerDaemonCommand(program2) {
17050
17127
  return;
17051
17128
  }
17052
17129
  if (options.background) {
17053
- const child = (0, import_child_process11.spawn)(process.execPath, [process.argv[1], "daemon"], {
17130
+ const child = (0, import_child_process9.spawn)(process.execPath, [process.argv[1], "daemon"], {
17054
17131
  detached: true,
17055
17132
  stdio: "ignore"
17056
17133
  });
@@ -17708,7 +17785,7 @@ function registerUndoCommand(program2) {
17708
17785
 
17709
17786
  // src/cli/commands/watch.ts
17710
17787
  var import_chalk19 = __toESM(require("chalk"));
17711
- var import_child_process12 = require("child_process");
17788
+ var import_child_process10 = require("child_process");
17712
17789
  init_daemon();
17713
17790
  function registerWatchCommand(program2) {
17714
17791
  program2.command("watch").description("Run a command under Node9 watch mode (daemon stays alive for the session)").argument("<command>", "Command to run").argument("[args...]", "Arguments for the command").action(async (cmd, args) => {
@@ -17725,7 +17802,7 @@ function registerWatchCommand(program2) {
17725
17802
  }
17726
17803
  } catch {
17727
17804
  console.error(import_chalk19.default.dim("\u{1F6E1}\uFE0F Starting Node9 daemon (watch mode)..."));
17728
- const child = (0, import_child_process12.spawn)(process.execPath, [process.argv[1], "daemon"], {
17805
+ const child = (0, import_child_process10.spawn)(process.execPath, [process.argv[1], "daemon"], {
17729
17806
  detached: true,
17730
17807
  stdio: "ignore",
17731
17808
  env: { ...process.env, NODE9_AUTO_STARTED: "1", NODE9_WATCH_MODE: "1" }
@@ -17755,7 +17832,7 @@ function registerWatchCommand(program2) {
17755
17832
  "\n Tip: run `node9 tail` in another terminal to review and approve AI actions.\n"
17756
17833
  )
17757
17834
  );
17758
- const result = (0, import_child_process12.spawnSync)(cmd, args, {
17835
+ const result = (0, import_child_process10.spawnSync)(cmd, args, {
17759
17836
  stdio: "inherit",
17760
17837
  env: { ...process.env, NODE9_WATCH_MODE: "1" }
17761
17838
  });
@@ -17770,7 +17847,7 @@ function registerWatchCommand(program2) {
17770
17847
  // src/mcp-gateway/index.ts
17771
17848
  var import_readline4 = __toESM(require("readline"));
17772
17849
  var import_chalk20 = __toESM(require("chalk"));
17773
- var import_child_process13 = require("child_process");
17850
+ var import_child_process11 = require("child_process");
17774
17851
  var import_execa3 = require("execa");
17775
17852
  init_orchestrator();
17776
17853
  init_provenance();
@@ -17956,7 +18033,7 @@ async function runMcpGateway(upstreamCommand) {
17956
18033
  const safeEnv = Object.fromEntries(
17957
18034
  Object.entries(process.env).filter(([k]) => !UPSTREAM_INJECTOR_VARS.has(k))
17958
18035
  );
17959
- const child = (0, import_child_process13.spawn)(executable, cmdArgs, {
18036
+ const child = (0, import_child_process11.spawn)(executable, cmdArgs, {
17960
18037
  stdio: ["pipe", "pipe", "inherit"],
17961
18038
  // control stdin/stdout; inherit stderr
17962
18039
  shell: false,
@@ -18302,7 +18379,7 @@ var import_readline5 = __toESM(require("readline"));
18302
18379
  var import_fs39 = __toESM(require("fs"));
18303
18380
  var import_os35 = __toESM(require("os"));
18304
18381
  var import_path41 = __toESM(require("path"));
18305
- var import_child_process14 = require("child_process");
18382
+ var import_child_process12 = require("child_process");
18306
18383
  init_core();
18307
18384
  init_daemon();
18308
18385
  init_shields();
@@ -18776,7 +18853,7 @@ function handleRuleAdd(args) {
18776
18853
  return `Rule "${name}" added to ~/.node9/config.json \u2014 verdict: ${verdict} when ${field} matches "${pattern}"`;
18777
18854
  }
18778
18855
  function runCliCommand(subArgs) {
18779
- const result = (0, import_child_process14.spawnSync)(process.execPath, [process.argv[1], ...subArgs], {
18856
+ const result = (0, import_child_process12.spawnSync)(process.execPath, [process.argv[1], ...subArgs], {
18780
18857
  encoding: "utf-8",
18781
18858
  timeout: 6e4,
18782
18859
  // Disable colors — stdout is piped (not a TTY), chalk auto-detects, but be explicit
@@ -20786,6 +20863,17 @@ program.command("tail").description("Stream live agent activity to the terminal"
20786
20863
  process.exit(1);
20787
20864
  }
20788
20865
  });
20866
+ program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
20867
+ try {
20868
+ const dashboardPath = import_path49.default.join(__dirname, "dashboard.mjs");
20869
+ const dynamicImport = new Function("id", "return import(id)");
20870
+ const mod = await dynamicImport(`file://${dashboardPath}`);
20871
+ await mod.startMonitor();
20872
+ } catch (err2) {
20873
+ console.error(import_chalk31.default.red(`\u274C ${err2 instanceof Error ? err2.message : String(err2)}`));
20874
+ process.exit(1);
20875
+ }
20876
+ });
20789
20877
  registerWatchCommand(program);
20790
20878
  registerMcpGatewayCommand(program);
20791
20879
  registerMcpServerCommand(program);