@node9/proxy 1.64.0 → 1.65.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.
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 path71 = issue.path.length > 0 ? issue.path.join(".") : "root";
252
- return ` \u2022 ${path71}: ${issue.message}`;
251
+ const path72 = issue.path.length > 0 ? issue.path.join(".") : "root";
252
+ return ` \u2022 ${path72}: ${issue.message}`;
253
253
  });
254
254
  return {
255
255
  sanitized,
@@ -1465,9 +1465,9 @@ function matchesPattern(text, patterns) {
1465
1465
  const withoutDotSlash = text.replace(/^\.\//, "");
1466
1466
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1467
1467
  }
1468
- function getNestedValue(obj, path71) {
1468
+ function getNestedValue(obj, path72) {
1469
1469
  if (!obj || typeof obj !== "object") return null;
1470
- const segments = path71.split(".");
1470
+ const segments = path72.split(".");
1471
1471
  for (const seg of segments) {
1472
1472
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1473
1473
  }
@@ -4619,6 +4619,7 @@ function getActiveEnvironment(config) {
4619
4619
  }
4620
4620
  function readRulesCacheResilient(cacheFile) {
4621
4621
  let existed = false;
4622
+ let sawReadError = false;
4622
4623
  for (let attempt = 0; attempt < 3; attempt++) {
4623
4624
  let content;
4624
4625
  try {
@@ -4626,30 +4627,39 @@ function readRulesCacheResilient(cacheFile) {
4626
4627
  existed = true;
4627
4628
  } catch (err2) {
4628
4629
  if (err2.code === "ENOENT") return {};
4630
+ sawReadError = true;
4629
4631
  continue;
4630
4632
  }
4631
4633
  try {
4632
- return JSON.parse(content);
4634
+ const parsed = JSON.parse(content);
4635
+ lastParsedRulesCache = parsed;
4636
+ return parsed;
4633
4637
  } catch {
4634
4638
  }
4635
4639
  }
4636
- if (existed) {
4640
+ if (existed || sawReadError) {
4637
4641
  const backup = path4.join(path4.dirname(cacheFile), "rules-cache.last-good.json");
4638
4642
  if (backup !== cacheFile) {
4639
4643
  try {
4640
4644
  const raw = JSON.parse(fs4.readFileSync(backup, "utf-8"));
4641
4645
  logCacheReadIssue(cacheFile, "RULES_CACHE_CORRUPT_USED_BACKUP");
4646
+ lastParsedRulesCache = raw;
4642
4647
  return raw;
4643
4648
  } catch {
4644
4649
  }
4645
4650
  }
4651
+ if (lastParsedRulesCache) {
4652
+ logCacheReadIssue(cacheFile, "RULES_CACHE_USED_MEMORY");
4653
+ return lastParsedRulesCache;
4654
+ }
4646
4655
  logCacheReadIssue(cacheFile, "RULES_CACHE_UNREADABLE");
4647
4656
  }
4648
4657
  return {};
4649
4658
  }
4650
4659
  function logCacheReadIssue(cacheFile, kind) {
4651
- if (cacheReadFailureLogged) return;
4652
- cacheReadFailureLogged = true;
4660
+ const now = Date.now();
4661
+ if (now - cacheReadLastLoggedAt < CACHE_LOG_REARM_MS) return;
4662
+ cacheReadLastLoggedAt = now;
4653
4663
  try {
4654
4664
  fs4.appendFileSync(
4655
4665
  path4.join(os4.homedir(), ".node9", "hook-debug.log"),
@@ -4900,10 +4910,10 @@ function getConfig(cwd) {
4900
4910
  }
4901
4911
  if (Array.isArray(mc.jailPaths)) {
4902
4912
  for (const jp of mc.jailPaths) {
4903
- const path71 = typeof jp?.path === "string" ? jp.path.trim() : "";
4904
- if (!path71) continue;
4913
+ const path72 = typeof jp?.path === "string" ? jp.path.trim() : "";
4914
+ if (!path72) continue;
4905
4915
  const verdict = jp?.verdict === "review" ? "review" : "block";
4906
- for (const r of pathRules(path71, verdict, "org-managed jail")) {
4916
+ for (const r of pathRules(path72, verdict, "org-managed jail")) {
4907
4917
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
4908
4918
  }
4909
4919
  }
@@ -5056,7 +5066,7 @@ ${error.replace("Invalid config:\n", "")}
5056
5066
  }
5057
5067
  return sanitized;
5058
5068
  }
5059
- var DANGEROUS_WORDS, DEFAULT_CONFIG, ADVISORY_SMART_RULES, cachedConfig, cacheReadFailureLogged;
5069
+ var DANGEROUS_WORDS, DEFAULT_CONFIG, ADVISORY_SMART_RULES, cachedConfig, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt;
5060
5070
  var init_config = __esm({
5061
5071
  "src/config/index.ts"() {
5062
5072
  "use strict";
@@ -5343,7 +5353,9 @@ var init_config = __esm({
5343
5353
  }
5344
5354
  ];
5345
5355
  cachedConfig = null;
5346
- cacheReadFailureLogged = false;
5356
+ lastParsedRulesCache = null;
5357
+ CACHE_LOG_REARM_MS = 5 * 60 * 1e3;
5358
+ cacheReadLastLoggedAt = 0;
5347
5359
  }
5348
5360
  });
5349
5361
 
@@ -6064,6 +6076,18 @@ async function isDaemonReachable(timeoutMs = 500) {
6064
6076
  return false;
6065
6077
  }
6066
6078
  }
6079
+ async function probeDaemonHealth(timeoutMs = 800) {
6080
+ try {
6081
+ const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/health`, {
6082
+ signal: AbortSignal.timeout(timeoutMs)
6083
+ });
6084
+ if (!res.ok) return { kind: "no-health" };
6085
+ const j = await res.json().catch(() => null);
6086
+ return j && typeof j === "object" ? { kind: "health", health: j } : { kind: "no-health" };
6087
+ } catch {
6088
+ return { kind: "unreachable" };
6089
+ }
6090
+ }
6067
6091
  async function daemonHasInteractiveApprover(timeoutMs = 400) {
6068
6092
  try {
6069
6093
  const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/approver`, {
@@ -7035,6 +7059,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
7035
7059
  let riskMetadata;
7036
7060
  let statefulRecoveryCommand;
7037
7061
  let localSmartRuleMatched = false;
7062
+ let hardBlockDowngraded = false;
7038
7063
  let taintWarning = null;
7039
7064
  if (isNetworkTool(toolName, args)) {
7040
7065
  const filePaths = extractFilePaths(toolName, args);
@@ -7276,6 +7301,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
7276
7301
  return { approved: true, checkedBy: "local-policy" };
7277
7302
  }
7278
7303
  if (policyResult.decision === "block") {
7304
+ hardBlockDowngraded = true;
7279
7305
  const daemonUp = isDaemonRunning();
7280
7306
  let humanApproverReachable = false;
7281
7307
  if (!policyResult.dependsOnStatePredicates?.length && daemonUp && !isTestEnv2) {
@@ -7349,7 +7375,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
7349
7375
  explainableLabel = policyResult.blockedByLabel || "Local Config";
7350
7376
  policyMatchedField = policyResult.matchedField;
7351
7377
  policyMatchedWord = policyResult.matchedWord;
7352
- if (policyResult.ruleName || policyResult.tier === 7) localSmartRuleMatched = true;
7378
+ if (policyResult.ruleName || policyResult.tier === 7 || hardBlockDowngraded)
7379
+ localSmartRuleMatched = true;
7353
7380
  if (policyResult.ruleDescription) policyRuleDescription = policyResult.ruleDescription;
7354
7381
  else if (policyResult.reason) policyRuleDescription = policyResult.reason;
7355
7382
  riskMetadata = computeRiskMetadata(
@@ -7361,7 +7388,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
7361
7388
  policyResult.ruleName
7362
7389
  );
7363
7390
  if (policyRuleDescription) riskMetadata.ruleDescription = policyRuleDescription.slice(0, 200);
7364
- const persistent = policyResult.ruleName || policyResult.tier === 7 ? null : getPersistentDecision(toolName);
7391
+ const persistent = policyResult.ruleName || policyResult.tier === 7 || hardBlockDowngraded ? null : getPersistentDecision(toolName);
7365
7392
  if (persistent === "allow" && !appPermReview) {
7366
7393
  if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta, hashAuditArgs);
7367
7394
  return { approved: true, checkedBy: "persistent" };
@@ -7394,7 +7421,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
7394
7421
  return { approved: true };
7395
7422
  }
7396
7423
  }
7397
- if (!taintWarning && !appPermReview && getActiveTrustSession(toolName, args)) {
7424
+ if (!taintWarning && !appPermReview && !hardBlockDowngraded && getActiveTrustSession(toolName, args)) {
7398
7425
  if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
7399
7426
  return { approved: true, checkedBy: "trust" };
7400
7427
  }
@@ -7434,7 +7461,7 @@ ${appPermReview}`
7434
7461
  }
7435
7462
  }
7436
7463
  const cloudEnforcedForDefer = approvers.cloud && !!creds?.apiKey;
7437
- if (options?.deferReview && !taintWarning && !appPermReview && !cloudEnforcedForDefer) {
7464
+ if (options?.deferReview && !hardBlockDowngraded && !taintWarning && !appPermReview && !cloudEnforcedForDefer) {
7438
7465
  return {
7439
7466
  approved: false,
7440
7467
  review: true,
@@ -7458,7 +7485,7 @@ ${appPermReview}`
7458
7485
  forceReview
7459
7486
  );
7460
7487
  if (!initResult.pending) {
7461
- if (initResult.shadowMode && !appPermReview) {
7488
+ if (initResult.shadowMode && !localSmartRuleMatched && !options?.localSmartRuleMatched && !appPermReview) {
7462
7489
  return { approved: true, checkedBy: "cloud" };
7463
7490
  }
7464
7491
  if (!localSmartRuleMatched && !options?.localSmartRuleMatched && !appPermReview) {
@@ -15482,6 +15509,73 @@ var init_scan = __esm({
15482
15509
  }
15483
15510
  });
15484
15511
 
15512
+ // src/daemon/build-id.ts
15513
+ import fs27 from "fs";
15514
+ import path29 from "path";
15515
+ function readOwnVersion() {
15516
+ for (const rel of ["../package.json", "../../package.json"]) {
15517
+ try {
15518
+ const raw = fs27.readFileSync(path29.join(__dirname, rel), "utf-8");
15519
+ const v = JSON.parse(raw).version;
15520
+ if (typeof v === "string" && v.length > 0) return v;
15521
+ } catch {
15522
+ }
15523
+ }
15524
+ return "0.0.0";
15525
+ }
15526
+ function computeBuildId(entry = process.argv[1] ?? "") {
15527
+ let mtimeMs = 0;
15528
+ try {
15529
+ if (entry) mtimeMs = fs27.statSync(entry).mtimeMs;
15530
+ } catch {
15531
+ }
15532
+ return { version: readOwnVersion(), mtimeMs };
15533
+ }
15534
+ function buildIdString(b) {
15535
+ return `${b.version}+${Math.round(b.mtimeMs)}`;
15536
+ }
15537
+ function parseBuildId(s) {
15538
+ if (typeof s !== "string") return null;
15539
+ const at = s.lastIndexOf("+");
15540
+ if (at <= 0) return null;
15541
+ const version2 = s.slice(0, at);
15542
+ const mtimeMs = Number(s.slice(at + 1));
15543
+ if (!/^\d+(\.\d+){2}/.test(version2) || !Number.isFinite(mtimeMs) || mtimeMs < 0) return null;
15544
+ return { version: version2, mtimeMs };
15545
+ }
15546
+ function compareVersion(a, b) {
15547
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
15548
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
15549
+ for (let i = 0; i < 3; i++) {
15550
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
15551
+ if (d !== 0) return d;
15552
+ }
15553
+ return 0;
15554
+ }
15555
+ function compareBuild(a, b) {
15556
+ const v = compareVersion(a.version, b.version);
15557
+ if (v !== 0) return v;
15558
+ return a.mtimeMs - b.mtimeMs;
15559
+ }
15560
+ function describeBuildDrift(running, installed) {
15561
+ const mine = buildIdString(installed);
15562
+ if (running === null) return null;
15563
+ if (running === "no-health") {
15564
+ return `running daemon predates the installed build (no /health \u2014 older than v${installed.version}) \u2014 it is enforcing OLD code`;
15565
+ }
15566
+ const theirs = typeof running.buildId === "string" ? running.buildId : null;
15567
+ if (!theirs || theirs === mine) return null;
15568
+ const theirVersion = typeof running.version === "string" ? running.version : "unknown";
15569
+ return `running daemon is v${theirVersion} (build ${theirs}) but installed is v${installed.version} (build ${mine}) \u2014 it is enforcing a different build`;
15570
+ }
15571
+ var CURRENT_BUILD;
15572
+ var init_build_id = __esm({
15573
+ "src/daemon/build-id.ts"() {
15574
+ "use strict";
15575
+ CURRENT_BUILD = parseBuildId(process.env.NODE9_BUILD_ID_OVERRIDE) ?? computeBuildId();
15576
+ }
15577
+ });
15578
+
15485
15579
  // src/daemon/suggestion-tracker.ts
15486
15580
  import { randomUUID as randomUUID2 } from "crypto";
15487
15581
  function extractPath(args) {
@@ -15573,8 +15667,8 @@ var init_suggestion_tracker = __esm({
15573
15667
  });
15574
15668
 
15575
15669
  // src/daemon/taint-store.ts
15576
- import fs27 from "fs";
15577
- import path29 from "path";
15670
+ import fs28 from "fs";
15671
+ import path30 from "path";
15578
15672
  var DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
15579
15673
  var init_taint_store = __esm({
15580
15674
  "src/daemon/taint-store.ts"() {
@@ -15644,9 +15738,9 @@ var init_taint_store = __esm({
15644
15738
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
15645
15739
  _resolve(filePath) {
15646
15740
  try {
15647
- return fs27.realpathSync.native(path29.resolve(filePath));
15741
+ return fs28.realpathSync.native(path30.resolve(filePath));
15648
15742
  } catch {
15649
- return path29.resolve(filePath);
15743
+ return path30.resolve(filePath);
15650
15744
  }
15651
15745
  }
15652
15746
  };
@@ -15811,14 +15905,14 @@ var init_session_history = __esm({
15811
15905
 
15812
15906
  // src/daemon/state.ts
15813
15907
  import net2 from "net";
15814
- import fs28 from "fs";
15815
- import path30 from "path";
15908
+ import fs29 from "fs";
15909
+ import path31 from "path";
15816
15910
  import os26 from "os";
15817
15911
  import { randomUUID as randomUUID3 } from "crypto";
15818
15912
  function loadInsightCounts() {
15819
15913
  try {
15820
- if (!fs28.existsSync(INSIGHT_COUNTS_FILE)) return;
15821
- const data = JSON.parse(fs28.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
15914
+ if (!fs29.existsSync(INSIGHT_COUNTS_FILE)) return;
15915
+ const data = JSON.parse(fs29.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
15822
15916
  for (const [tool, count] of Object.entries(data)) {
15823
15917
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
15824
15918
  }
@@ -15857,23 +15951,23 @@ function markRejectionHandlerRegistered() {
15857
15951
  daemonRejectionHandlerRegistered = true;
15858
15952
  }
15859
15953
  function atomicWriteSync2(filePath, data, options) {
15860
- const dir = path30.dirname(filePath);
15861
- if (!fs28.existsSync(dir)) fs28.mkdirSync(dir, { recursive: true });
15954
+ const dir = path31.dirname(filePath);
15955
+ if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
15862
15956
  const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
15863
15957
  try {
15864
- fs28.writeFileSync(tmpPath, data, options);
15958
+ fs29.writeFileSync(tmpPath, data, options);
15865
15959
  } catch (err2) {
15866
15960
  try {
15867
- fs28.unlinkSync(tmpPath);
15961
+ fs29.unlinkSync(tmpPath);
15868
15962
  } catch {
15869
15963
  }
15870
15964
  throw err2;
15871
15965
  }
15872
15966
  try {
15873
- fs28.renameSync(tmpPath, filePath);
15967
+ fs29.renameSync(tmpPath, filePath);
15874
15968
  } catch (err2) {
15875
15969
  try {
15876
- fs28.unlinkSync(tmpPath);
15970
+ fs29.unlinkSync(tmpPath);
15877
15971
  } catch {
15878
15972
  }
15879
15973
  throw err2;
@@ -15897,16 +15991,16 @@ function appendAuditLog(data) {
15897
15991
  decision: data.decision,
15898
15992
  source: "daemon"
15899
15993
  };
15900
- const dir = path30.dirname(AUDIT_LOG_FILE);
15901
- if (!fs28.existsSync(dir)) fs28.mkdirSync(dir, { recursive: true });
15902
- fs28.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
15994
+ const dir = path31.dirname(AUDIT_LOG_FILE);
15995
+ if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
15996
+ fs29.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
15903
15997
  } catch {
15904
15998
  }
15905
15999
  }
15906
16000
  function getAuditHistory(limit = 20) {
15907
16001
  try {
15908
- if (!fs28.existsSync(AUDIT_LOG_FILE)) return [];
15909
- const lines = fs28.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
16002
+ if (!fs29.existsSync(AUDIT_LOG_FILE)) return [];
16003
+ const lines = fs29.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
15910
16004
  if (lines.length === 1 && lines[0] === "") return [];
15911
16005
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
15912
16006
  } catch {
@@ -15915,7 +16009,7 @@ function getAuditHistory(limit = 20) {
15915
16009
  }
15916
16010
  function getOrgName() {
15917
16011
  try {
15918
- if (fs28.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
16012
+ if (fs29.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
15919
16013
  } catch {
15920
16014
  }
15921
16015
  return null;
@@ -15923,8 +16017,8 @@ function getOrgName() {
15923
16017
  function writeGlobalSetting(key, value) {
15924
16018
  let config = {};
15925
16019
  try {
15926
- if (fs28.existsSync(GLOBAL_CONFIG_FILE)) {
15927
- config = JSON.parse(fs28.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
16020
+ if (fs29.existsSync(GLOBAL_CONFIG_FILE)) {
16021
+ config = JSON.parse(fs29.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
15928
16022
  }
15929
16023
  } catch {
15930
16024
  }
@@ -15936,8 +16030,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
15936
16030
  try {
15937
16031
  let trust = { entries: [] };
15938
16032
  try {
15939
- if (fs28.existsSync(TRUST_FILE2))
15940
- trust = JSON.parse(fs28.readFileSync(TRUST_FILE2, "utf-8"));
16033
+ if (fs29.existsSync(TRUST_FILE2))
16034
+ trust = JSON.parse(fs29.readFileSync(TRUST_FILE2, "utf-8"));
15941
16035
  } catch {
15942
16036
  }
15943
16037
  trust.entries = trust.entries.filter(
@@ -15954,8 +16048,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
15954
16048
  }
15955
16049
  function readPersistentDecisions() {
15956
16050
  try {
15957
- if (fs28.existsSync(DECISIONS_FILE)) {
15958
- return JSON.parse(fs28.readFileSync(DECISIONS_FILE, "utf-8"));
16051
+ if (fs29.existsSync(DECISIONS_FILE)) {
16052
+ return JSON.parse(fs29.readFileSync(DECISIONS_FILE, "utf-8"));
15959
16053
  }
15960
16054
  } catch {
15961
16055
  }
@@ -15983,7 +16077,7 @@ function estimateToolCost(tool, args) {
15983
16077
  const filePath = a.file_path ?? a.path;
15984
16078
  if (filePath) {
15985
16079
  try {
15986
- const bytes = fs28.statSync(filePath).size;
16080
+ const bytes = fs29.statSync(filePath).size;
15987
16081
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
15988
16082
  } catch {
15989
16083
  }
@@ -16057,7 +16151,7 @@ function abandonPending() {
16057
16151
  });
16058
16152
  if (autoStarted) {
16059
16153
  try {
16060
- fs28.unlinkSync(DAEMON_PID_FILE);
16154
+ fs29.unlinkSync(DAEMON_PID_FILE);
16061
16155
  } catch {
16062
16156
  }
16063
16157
  setTimeout(() => {
@@ -16068,8 +16162,8 @@ function abandonPending() {
16068
16162
  }
16069
16163
  function logActivitySocket(msg) {
16070
16164
  try {
16071
- fs28.appendFileSync(
16072
- path30.join(homeDir, ".node9", "hook-debug.log"),
16165
+ fs29.appendFileSync(
16166
+ path31.join(homeDir, ".node9", "hook-debug.log"),
16073
16167
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
16074
16168
  `
16075
16169
  );
@@ -16091,13 +16185,13 @@ function shouldRebind(now = Date.now()) {
16091
16185
  function startActivitySocket() {
16092
16186
  bindActivitySocket();
16093
16187
  activityHealthInterval = setInterval(() => {
16094
- if (!fs28.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
16188
+ if (!fs29.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
16095
16189
  }, ACTIVITY_HEALTH_PROBE_MS);
16096
16190
  activityHealthInterval.unref();
16097
16191
  process.on("exit", () => {
16098
16192
  if (activityHealthInterval) clearInterval(activityHealthInterval);
16099
16193
  try {
16100
- fs28.unlinkSync(ACTIVITY_SOCKET_PATH2);
16194
+ fs29.unlinkSync(ACTIVITY_SOCKET_PATH2);
16101
16195
  } catch {
16102
16196
  }
16103
16197
  });
@@ -16125,7 +16219,7 @@ function attemptRebind(reason) {
16125
16219
  }
16126
16220
  function bindActivitySocket() {
16127
16221
  try {
16128
- fs28.unlinkSync(ACTIVITY_SOCKET_PATH2);
16222
+ fs29.unlinkSync(ACTIVITY_SOCKET_PATH2);
16129
16223
  } catch {
16130
16224
  }
16131
16225
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -16239,13 +16333,13 @@ var init_state2 = __esm({
16239
16333
  init_session_counters();
16240
16334
  init_session_history();
16241
16335
  homeDir = os26.homedir();
16242
- DAEMON_PID_FILE = path30.join(homeDir, ".node9", "daemon.pid");
16243
- DECISIONS_FILE = path30.join(homeDir, ".node9", "decisions.json");
16244
- AUDIT_LOG_FILE = path30.join(homeDir, ".node9", "audit.log");
16245
- TRUST_FILE2 = path30.join(homeDir, ".node9", "trust.json");
16246
- GLOBAL_CONFIG_FILE = path30.join(homeDir, ".node9", "config.json");
16247
- CREDENTIALS_FILE = path30.join(homeDir, ".node9", "credentials.json");
16248
- INSIGHT_COUNTS_FILE = path30.join(homeDir, ".node9", "insight-counts.json");
16336
+ DAEMON_PID_FILE = path31.join(homeDir, ".node9", "daemon.pid");
16337
+ DECISIONS_FILE = path31.join(homeDir, ".node9", "decisions.json");
16338
+ AUDIT_LOG_FILE = path31.join(homeDir, ".node9", "audit.log");
16339
+ TRUST_FILE2 = path31.join(homeDir, ".node9", "trust.json");
16340
+ GLOBAL_CONFIG_FILE = path31.join(homeDir, ".node9", "config.json");
16341
+ CREDENTIALS_FILE = path31.join(homeDir, ".node9", "credentials.json");
16342
+ INSIGHT_COUNTS_FILE = path31.join(homeDir, ".node9", "insight-counts.json");
16249
16343
  pending = /* @__PURE__ */ new Map();
16250
16344
  sseClients = /* @__PURE__ */ new Set();
16251
16345
  suggestionTracker = new SuggestionTracker(3);
@@ -16263,7 +16357,7 @@ var init_state2 = __esm({
16263
16357
  "2h": 2 * 60 * 6e4
16264
16358
  };
16265
16359
  autoStarted = process.env.NODE9_AUTO_STARTED === "1";
16266
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path30.join(os26.tmpdir(), "node9-activity.sock");
16360
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path31.join(os26.tmpdir(), "node9-activity.sock");
16267
16361
  ACTIVITY_RING_SIZE = 100;
16268
16362
  activityRing = [];
16269
16363
  LARGE_RESPONSE_RING_SIZE = 20;
@@ -16302,20 +16396,20 @@ var init_state2 = __esm({
16302
16396
  });
16303
16397
 
16304
16398
  // src/posture/secrets.ts
16305
- import fs29 from "fs";
16306
- import path31 from "path";
16399
+ import fs30 from "fs";
16400
+ import path32 from "path";
16307
16401
  import os27 from "os";
16308
16402
  function displayPath(p, home) {
16309
16403
  if (p === home) return "~";
16310
- const prefix = home.endsWith(path31.sep) ? home : home + path31.sep;
16311
- if (p.startsWith(prefix)) return "~" + path31.sep + p.slice(prefix.length);
16404
+ const prefix = home.endsWith(path32.sep) ? home : home + path32.sep;
16405
+ if (p.startsWith(prefix)) return "~" + path32.sep + p.slice(prefix.length);
16312
16406
  return p;
16313
16407
  }
16314
16408
  function safeRead(file) {
16315
16409
  try {
16316
- const stat = fs29.statSync(file);
16410
+ const stat = fs30.statSync(file);
16317
16411
  if (!stat.isFile() || stat.size === 0 || stat.size > MAX_FILE_BYTES) return null;
16318
- return fs29.readFileSync(file, "utf8");
16412
+ return fs30.readFileSync(file, "utf8");
16319
16413
  } catch {
16320
16414
  return null;
16321
16415
  }
@@ -16323,8 +16417,8 @@ function safeRead(file) {
16323
16417
  function candidateFiles(home, cwd) {
16324
16418
  const files = /* @__PURE__ */ new Set();
16325
16419
  try {
16326
- for (const name of fs29.readdirSync(cwd)) {
16327
- if (name === ".env" || name.startsWith(".env.")) files.add(path31.join(cwd, name));
16420
+ for (const name of fs30.readdirSync(cwd)) {
16421
+ if (name === ".env" || name.startsWith(".env.")) files.add(path32.join(cwd, name));
16328
16422
  }
16329
16423
  } catch {
16330
16424
  }
@@ -16332,17 +16426,17 @@ function candidateFiles(home, cwd) {
16332
16426
  if (spec.hookFile) files.add(spec.hookFile(home));
16333
16427
  if (spec.mcpFile) files.add(spec.mcpFile(home));
16334
16428
  }
16335
- files.add(path31.join(home, ".env"));
16429
+ files.add(path32.join(home, ".env"));
16336
16430
  return [...files];
16337
16431
  }
16338
16432
  function credentialMaterial(home) {
16339
16433
  return [
16340
- path31.join(home, ".ssh", "id_rsa"),
16341
- path31.join(home, ".ssh", "id_dsa"),
16342
- path31.join(home, ".ssh", "id_ecdsa"),
16343
- path31.join(home, ".ssh", "id_ed25519"),
16344
- path31.join(home, ".aws", "credentials"),
16345
- path31.join(home, ".config", "gcloud", "application_default_credentials.json")
16434
+ path32.join(home, ".ssh", "id_rsa"),
16435
+ path32.join(home, ".ssh", "id_dsa"),
16436
+ path32.join(home, ".ssh", "id_ecdsa"),
16437
+ path32.join(home, ".ssh", "id_ed25519"),
16438
+ path32.join(home, ".aws", "credentials"),
16439
+ path32.join(home, ".config", "gcloud", "application_default_credentials.json")
16346
16440
  ];
16347
16441
  }
16348
16442
  function checkSecrets(ctx) {
@@ -16379,7 +16473,7 @@ function checkSecrets(ctx) {
16379
16473
  const credPaths = [];
16380
16474
  for (const file of credentialMaterial(home)) {
16381
16475
  try {
16382
- if (fs29.statSync(file).isFile()) {
16476
+ if (fs30.statSync(file).isFile()) {
16383
16477
  creds.push(displayPath(file, home));
16384
16478
  credPaths.push(file);
16385
16479
  }
@@ -16535,10 +16629,10 @@ var init_templates = __esm({
16535
16629
  });
16536
16630
 
16537
16631
  // src/posture/egress.ts
16538
- import fs30 from "fs";
16632
+ import fs31 from "fs";
16539
16633
  function sandboxEgressWallActive() {
16540
16634
  try {
16541
- return fs30.existsSync(ALLOWED_DOMAINS_PATH);
16635
+ return fs31.existsSync(ALLOWED_DOMAINS_PATH);
16542
16636
  } catch {
16543
16637
  return false;
16544
16638
  }
@@ -16660,23 +16754,23 @@ var init_gate = __esm({
16660
16754
  });
16661
16755
 
16662
16756
  // src/posture/supply-chain.ts
16663
- import fs31 from "fs";
16757
+ import fs32 from "fs";
16664
16758
  import os28 from "os";
16665
- import path32 from "path";
16759
+ import path33 from "path";
16666
16760
  import { parse as parseToml3 } from "smol-toml";
16667
16761
  function isNode9Managed(command, args = []) {
16668
16762
  if (!command) return false;
16669
- if (path32.basename(command).toLowerCase() === "node9") return true;
16670
- if (PACKAGE_RUNNERS.has(path32.basename(command).toLowerCase())) {
16671
- return args.some((a) => a === "node9" || path32.basename(a).toLowerCase() === "node9");
16763
+ if (path33.basename(command).toLowerCase() === "node9") return true;
16764
+ if (PACKAGE_RUNNERS.has(path33.basename(command).toLowerCase())) {
16765
+ return args.some((a) => a === "node9" || path33.basename(a).toLowerCase() === "node9");
16672
16766
  }
16673
16767
  return false;
16674
16768
  }
16675
16769
  function readServers(file, format, agent) {
16676
16770
  try {
16677
- const stat = fs31.statSync(file);
16771
+ const stat = fs32.statSync(file);
16678
16772
  if (!stat.isFile() || stat.size > MAX_CONFIG_BYTES) return [];
16679
- const text = fs31.readFileSync(file, "utf8");
16773
+ const text = fs32.readFileSync(file, "utf8");
16680
16774
  const map = format === "toml" ? parseToml3(text)?.mcp_servers : JSON.parse(text)?.mcpServers;
16681
16775
  if (!map || typeof map !== "object") return [];
16682
16776
  return Object.entries(map).map(([name, v]) => ({
@@ -16788,11 +16882,11 @@ var init_privilege = __esm({
16788
16882
  });
16789
16883
 
16790
16884
  // src/posture/containment.ts
16791
- import fs32 from "fs";
16885
+ import fs33 from "fs";
16792
16886
  function inContainer() {
16793
- if (fs32.existsSync("/.dockerenv") || fs32.existsSync("/run/.containerenv")) return true;
16887
+ if (fs33.existsSync("/.dockerenv") || fs33.existsSync("/run/.containerenv")) return true;
16794
16888
  try {
16795
- const cgroup = fs32.readFileSync("/proc/1/cgroup", "utf8");
16889
+ const cgroup = fs33.readFileSync("/proc/1/cgroup", "utf8");
16796
16890
  if (/docker|kubepods|containerd|lxc|libpod/.test(cgroup)) return true;
16797
16891
  } catch {
16798
16892
  }
@@ -16838,7 +16932,7 @@ var init_containment = __esm({
16838
16932
  });
16839
16933
 
16840
16934
  // src/posture/inbound.ts
16841
- import fs33 from "fs";
16935
+ import fs34 from "fs";
16842
16936
  function buildNetworkFix(labels) {
16843
16937
  const shielded = [
16844
16938
  ...new Map(
@@ -16896,7 +16990,7 @@ function collectListeners() {
16896
16990
  const byPort = /* @__PURE__ */ new Map();
16897
16991
  for (const file of ["/proc/net/tcp", "/proc/net/tcp6"]) {
16898
16992
  try {
16899
- for (const l of parseListeners(fs33.readFileSync(file, "utf8"))) {
16993
+ for (const l of parseListeners(fs34.readFileSync(file, "utf8"))) {
16900
16994
  if (!byPort.has(l.port)) byPort.set(l.port, l);
16901
16995
  }
16902
16996
  } catch {
@@ -16908,11 +17002,11 @@ function readProc(pid) {
16908
17002
  let comm = "unknown";
16909
17003
  let cmdline = "";
16910
17004
  try {
16911
- comm = fs33.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
17005
+ comm = fs34.readFileSync(`/proc/${pid}/comm`, "utf8").trim().replace(/-MainThread$/, "") || "unknown";
16912
17006
  } catch {
16913
17007
  }
16914
17008
  try {
16915
- cmdline = fs33.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
17009
+ cmdline = fs34.readFileSync(`/proc/${pid}/cmdline`).toString().replace(/\0/g, " ").trim();
16916
17010
  } catch {
16917
17011
  }
16918
17012
  return { comm, cmdline };
@@ -16922,21 +17016,21 @@ function resolveProcesses(inodes) {
16922
17016
  if (inodes.size === 0) return map;
16923
17017
  let pids;
16924
17018
  try {
16925
- pids = fs33.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
17019
+ pids = fs34.readdirSync("/proc").filter((d) => /^\d+$/.test(d));
16926
17020
  } catch {
16927
17021
  return map;
16928
17022
  }
16929
17023
  for (const pid of pids) {
16930
17024
  let fds;
16931
17025
  try {
16932
- fds = fs33.readdirSync(`/proc/${pid}/fd`);
17026
+ fds = fs34.readdirSync(`/proc/${pid}/fd`);
16933
17027
  } catch {
16934
17028
  continue;
16935
17029
  }
16936
17030
  for (const fd of fds) {
16937
17031
  let link;
16938
17032
  try {
16939
- link = fs33.readlinkSync(`/proc/${pid}/fd/${fd}`);
17033
+ link = fs34.readlinkSync(`/proc/${pid}/fd/${fd}`);
16940
17034
  } catch {
16941
17035
  continue;
16942
17036
  }
@@ -17085,7 +17179,7 @@ var init_coverage = __esm({
17085
17179
  });
17086
17180
 
17087
17181
  // src/posture/governance.ts
17088
- import path33 from "path";
17182
+ import path34 from "path";
17089
17183
  function checkData(ctx) {
17090
17184
  const dlp = getConfig(ctx.cwd).policy.dlp;
17091
17185
  if (!dlp?.enabled) {
@@ -17230,7 +17324,7 @@ var init_governance = __esm({
17230
17324
  "src/posture/governance.ts"() {
17231
17325
  "use strict";
17232
17326
  init_config();
17233
- jailProbePath = (home) => path33.join(home, ".aws", "credentials");
17327
+ jailProbePath = (home) => path34.join(home, ".aws", "credentials");
17234
17328
  }
17235
17329
  });
17236
17330
 
@@ -17697,8 +17791,8 @@ var init_mcp_cmd = __esm({
17697
17791
  });
17698
17792
 
17699
17793
  // src/daemon/mcp-tools.ts
17700
- import fs34 from "fs";
17701
- import path34 from "path";
17794
+ import fs35 from "fs";
17795
+ import path35 from "path";
17702
17796
  import os31 from "os";
17703
17797
  function deriveServerName(cmd) {
17704
17798
  if (!cmd || typeof cmd !== "string") return "MCP Server";
@@ -17725,13 +17819,13 @@ function deriveServerName(cmd) {
17725
17819
  return strip(base) || "MCP Server";
17726
17820
  }
17727
17821
  function getMcpToolsFile() {
17728
- return path34.join(os31.homedir(), ".node9", "mcp-tools.json");
17822
+ return path35.join(os31.homedir(), ".node9", "mcp-tools.json");
17729
17823
  }
17730
17824
  function readMcpToolsConfig() {
17731
17825
  try {
17732
17826
  const file = getMcpToolsFile();
17733
- if (!fs34.existsSync(file)) return {};
17734
- const raw = fs34.readFileSync(file, "utf-8");
17827
+ if (!fs35.existsSync(file)) return {};
17828
+ const raw = fs35.readFileSync(file, "utf-8");
17735
17829
  return JSON.parse(raw);
17736
17830
  } catch {
17737
17831
  return {};
@@ -17740,11 +17834,11 @@ function readMcpToolsConfig() {
17740
17834
  function writeMcpToolsConfig(config) {
17741
17835
  try {
17742
17836
  const file = getMcpToolsFile();
17743
- const dir = path34.dirname(file);
17744
- if (!fs34.existsSync(dir)) fs34.mkdirSync(dir, { recursive: true });
17837
+ const dir = path35.dirname(file);
17838
+ if (!fs35.existsSync(dir)) fs35.mkdirSync(dir, { recursive: true });
17745
17839
  const tmpPath = `${file}.${os31.hostname()}.${process.pid}.tmp`;
17746
- fs34.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
17747
- fs34.renameSync(tmpPath, file);
17840
+ fs35.writeFileSync(tmpPath, JSON.stringify(config, null, 2));
17841
+ fs35.renameSync(tmpPath, file);
17748
17842
  } catch (e) {
17749
17843
  console.error("Failed to write mcp-tools.json", e);
17750
17844
  }
@@ -17799,7 +17893,7 @@ var init_mcp_tools = __esm({
17799
17893
  });
17800
17894
 
17801
17895
  // src/mcp-wrap.ts
17802
- import fs35 from "fs";
17896
+ import fs36 from "fs";
17803
17897
  import os32 from "os";
17804
17898
  import { parse as parseToml4, stringify as stringifyToml2 } from "smol-toml";
17805
17899
  function isNode9Command(command) {
@@ -17872,11 +17966,11 @@ function inventoryServerKeys(inv) {
17872
17966
  function writeMcpEntry(mcpFile, format, name, entry) {
17873
17967
  const key = format === "toml" ? "mcp_servers" : "mcpServers";
17874
17968
  let root = {};
17875
- if (fs35.existsSync(mcpFile)) {
17876
- const raw = fs35.readFileSync(mcpFile, "utf-8");
17969
+ if (fs36.existsSync(mcpFile)) {
17970
+ const raw = fs36.readFileSync(mcpFile, "utf-8");
17877
17971
  root = format === "toml" ? parseToml4(raw) : JSON.parse(raw);
17878
17972
  const bak = `${mcpFile}.node9-bak`;
17879
- if (!fs35.existsSync(bak)) fs35.writeFileSync(bak, raw, { mode: 384 });
17973
+ if (!fs36.existsSync(bak)) fs36.writeFileSync(bak, raw, { mode: 384 });
17880
17974
  }
17881
17975
  const existing = root[key];
17882
17976
  const servers = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
@@ -17884,8 +17978,8 @@ function writeMcpEntry(mcpFile, format, name, entry) {
17884
17978
  root[key] = servers;
17885
17979
  const serialized = format === "toml" ? stringifyToml2(root) : JSON.stringify(root, null, 2);
17886
17980
  const tmp = `${mcpFile}.${process.pid}.tmp`;
17887
- fs35.writeFileSync(tmp, serialized, { mode: 384 });
17888
- fs35.renameSync(tmp, mcpFile);
17981
+ fs36.writeFileSync(tmp, serialized, { mode: 384 });
17982
+ fs36.renameSync(tmp, mcpFile);
17889
17983
  }
17890
17984
  var init_mcp_wrap = __esm({
17891
17985
  "src/mcp-wrap.ts"() {
@@ -18012,10 +18106,10 @@ var init_ship2 = __esm({
18012
18106
  });
18013
18107
 
18014
18108
  // src/daemon/sync.ts
18015
- import fs36 from "fs";
18109
+ import fs37 from "fs";
18016
18110
  import https4 from "https";
18017
18111
  import os33 from "os";
18018
- import path35 from "path";
18112
+ import path36 from "path";
18019
18113
  function emptySignals3() {
18020
18114
  return {
18021
18115
  dlpFindings: 0,
@@ -18060,8 +18154,8 @@ function readCredentials() {
18060
18154
  };
18061
18155
  }
18062
18156
  try {
18063
- const credPath = path35.join(os33.homedir(), ".node9", "credentials.json");
18064
- const creds = JSON.parse(fs36.readFileSync(credPath, "utf-8"));
18157
+ const credPath = path36.join(os33.homedir(), ".node9", "credentials.json");
18158
+ const creds = JSON.parse(fs37.readFileSync(credPath, "utf-8"));
18065
18159
  const profileName = process.env.NODE9_PROFILE ?? "default";
18066
18160
  const profile = creds[profileName];
18067
18161
  if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
@@ -18087,7 +18181,7 @@ function readCredentials() {
18087
18181
  }
18088
18182
  function readCachedEtag() {
18089
18183
  try {
18090
- const raw = JSON.parse(fs36.readFileSync(rulesCacheFile(), "utf-8"));
18184
+ const raw = JSON.parse(fs37.readFileSync(rulesCacheFile(), "utf-8"));
18091
18185
  return typeof raw.etag === "string" ? raw.etag : void 0;
18092
18186
  } catch {
18093
18187
  return void 0;
@@ -18095,7 +18189,7 @@ function readCachedEtag() {
18095
18189
  }
18096
18190
  function readCachedSyncIntervalHours() {
18097
18191
  try {
18098
- const raw = JSON.parse(fs36.readFileSync(rulesCacheFile(), "utf-8"));
18192
+ const raw = JSON.parse(fs37.readFileSync(rulesCacheFile(), "utf-8"));
18099
18193
  return typeof raw.syncIntervalHours === "number" ? raw.syncIntervalHours : void 0;
18100
18194
  } catch {
18101
18195
  return void 0;
@@ -18112,7 +18206,7 @@ function effectiveSyncIntervalMs() {
18112
18206
  }
18113
18207
  function readSyncHealth() {
18114
18208
  try {
18115
- const raw = JSON.parse(fs36.readFileSync(syncHealthFile(), "utf-8"));
18209
+ const raw = JSON.parse(fs37.readFileSync(syncHealthFile(), "utf-8"));
18116
18210
  return {
18117
18211
  lastCheckedAt: typeof raw.lastCheckedAt === "string" ? raw.lastCheckedAt : void 0,
18118
18212
  lastChangedAt: typeof raw.lastChangedAt === "string" ? raw.lastChangedAt : void 0,
@@ -18127,17 +18221,17 @@ function readSyncHealth() {
18127
18221
  function writeSyncHealth(h) {
18128
18222
  try {
18129
18223
  const file = syncHealthFile();
18130
- const dir = path35.dirname(file);
18131
- if (!fs36.existsSync(dir)) fs36.mkdirSync(dir, { recursive: true });
18224
+ const dir = path36.dirname(file);
18225
+ if (!fs37.existsSync(dir)) fs37.mkdirSync(dir, { recursive: true });
18132
18226
  const tmp = `${file}.${process.pid}.tmp`;
18133
- fs36.writeFileSync(tmp, JSON.stringify(h, null, 2) + "\n", "utf-8");
18134
- fs36.renameSync(tmp, file);
18227
+ fs37.writeFileSync(tmp, JSON.stringify(h, null, 2) + "\n", "utf-8");
18228
+ fs37.renameSync(tmp, file);
18135
18229
  } catch {
18136
18230
  }
18137
18231
  }
18138
18232
  function readCacheFetchedAt() {
18139
18233
  try {
18140
- const raw = JSON.parse(fs36.readFileSync(rulesCacheFile(), "utf-8"));
18234
+ const raw = JSON.parse(fs37.readFileSync(rulesCacheFile(), "utf-8"));
18141
18235
  return typeof raw.fetchedAt === "string" ? raw.fetchedAt : void 0;
18142
18236
  } catch {
18143
18237
  return void 0;
@@ -18323,8 +18417,24 @@ function extractManagedConfig(body) {
18323
18417
  }
18324
18418
  return out.mode !== void 0 || out.egress !== void 0 || out.dlp !== void 0 || out.approvers !== void 0 || out.reviewChannel !== void 0 || out.approvalTimeoutMs !== void 0 || out.injectionScan !== void 0 || out.loopDetection !== void 0 || out.skillPinning !== void 0 || out.jailPaths !== void 0 || out.trustedHosts !== void 0 || out.appPermissions !== void 0 ? out : void 0;
18325
18419
  }
18420
+ function sweepStaleTmp(target) {
18421
+ try {
18422
+ const dir = path36.dirname(target);
18423
+ const prefix = `${path36.basename(target)}.`;
18424
+ for (const name of fs37.readdirSync(dir)) {
18425
+ if (!name.startsWith(prefix) || !name.endsWith(".tmp")) continue;
18426
+ const full = path36.join(dir, name);
18427
+ try {
18428
+ if (Date.now() - fs37.statSync(full).mtimeMs > 5 * 60 * 1e3) fs37.unlinkSync(full);
18429
+ } catch {
18430
+ }
18431
+ }
18432
+ } catch {
18433
+ }
18434
+ }
18326
18435
  function writeCache2(cache) {
18327
18436
  const data = JSON.stringify(cache, null, 2) + "\n";
18437
+ sweepStaleTmp(rulesCacheFile());
18328
18438
  atomicWriteSync2(rulesCacheFile(), data, "utf-8");
18329
18439
  try {
18330
18440
  atomicWriteSync2(rulesCacheBackupFile(), data, "utf-8");
@@ -18571,7 +18681,7 @@ async function runCloudSync() {
18571
18681
  }
18572
18682
  function getCloudSyncStatus() {
18573
18683
  try {
18574
- const raw = JSON.parse(fs36.readFileSync(rulesCacheFile(), "utf-8"));
18684
+ const raw = JSON.parse(fs37.readFileSync(rulesCacheFile(), "utf-8"));
18575
18685
  if (!Array.isArray(raw.rules) || typeof raw.fetchedAt !== "string") return { cached: false };
18576
18686
  return {
18577
18687
  cached: true,
@@ -18588,7 +18698,7 @@ function getCloudSyncStatus() {
18588
18698
  }
18589
18699
  function getCloudRules() {
18590
18700
  try {
18591
- const raw = JSON.parse(fs36.readFileSync(rulesCacheFile(), "utf-8"));
18701
+ const raw = JSON.parse(fs37.readFileSync(rulesCacheFile(), "utf-8"));
18592
18702
  return Array.isArray(raw.rules) ? raw.rules : null;
18593
18703
  } catch {
18594
18704
  return null;
@@ -18653,13 +18763,13 @@ var init_sync = __esm({
18653
18763
  loop: "loops",
18654
18764
  "long-output-redacted": "longOutputRedactions"
18655
18765
  };
18656
- rulesCacheFile = () => path35.join(os33.homedir(), ".node9", "rules-cache.json");
18657
- rulesCacheBackupFile = () => path35.join(os33.homedir(), ".node9", "rules-cache.last-good.json");
18766
+ rulesCacheFile = () => path36.join(os33.homedir(), ".node9", "rules-cache.json");
18767
+ rulesCacheBackupFile = () => path36.join(os33.homedir(), ".node9", "rules-cache.last-good.json");
18658
18768
  DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept/policies/sync";
18659
18769
  DEFAULT_INTERVAL_HOURS = 5;
18660
18770
  MIN_INTERVAL_SECONDS = 15;
18661
18771
  MAX_INTERVAL_SECONDS = 24 * 60 * 60;
18662
- syncHealthFile = () => path35.join(os33.homedir(), ".node9", "sync-health.json");
18772
+ syncHealthFile = () => path36.join(os33.homedir(), ".node9", "sync-health.json");
18663
18773
  STALE_MIN_MS = 3 * 60 * 60 * 1e3;
18664
18774
  STALE_MAX_MS = 24 * 60 * 60 * 1e3;
18665
18775
  STALE_FACTOR = 3;
@@ -18682,26 +18792,26 @@ __export(audit_shipper_exports, {
18682
18792
  startAuditShipper: () => startAuditShipper,
18683
18793
  writeWatermark: () => writeWatermark
18684
18794
  });
18685
- import fs37 from "fs";
18686
- import path36 from "path";
18795
+ import fs38 from "fs";
18796
+ import path37 from "path";
18687
18797
  import os34 from "os";
18688
18798
  import crypto5 from "crypto";
18689
18799
  function fileSignature(filePath) {
18690
- const fd = fs37.openSync(filePath, "r");
18800
+ const fd = fs38.openSync(filePath, "r");
18691
18801
  try {
18692
18802
  const buf = Buffer.alloc(512);
18693
- const read2 = fs37.readSync(fd, buf, 0, 512, 0);
18803
+ const read2 = fs38.readSync(fd, buf, 0, 512, 0);
18694
18804
  const slice = buf.subarray(0, read2);
18695
18805
  const nl = slice.indexOf(10);
18696
18806
  const firstLine = nl === -1 ? slice : slice.subarray(0, nl);
18697
18807
  return crypto5.createHash("sha256").update(firstLine).digest("hex").slice(0, 16);
18698
18808
  } finally {
18699
- fs37.closeSync(fd);
18809
+ fs38.closeSync(fd);
18700
18810
  }
18701
18811
  }
18702
18812
  function readWatermark(watermarkPath) {
18703
18813
  try {
18704
- const raw = JSON.parse(fs37.readFileSync(watermarkPath, "utf-8"));
18814
+ const raw = JSON.parse(fs38.readFileSync(watermarkPath, "utf-8"));
18705
18815
  if (typeof raw.fileSig === "string" && typeof raw.offset === "number" && raw.offset >= 0)
18706
18816
  return raw;
18707
18817
  } catch {
@@ -18710,8 +18820,8 @@ function readWatermark(watermarkPath) {
18710
18820
  }
18711
18821
  function writeWatermark(watermarkPath, wm) {
18712
18822
  const tmp = `${watermarkPath}.tmp`;
18713
- fs37.writeFileSync(tmp, JSON.stringify(wm));
18714
- fs37.renameSync(tmp, watermarkPath);
18823
+ fs38.writeFileSync(tmp, JSON.stringify(wm));
18824
+ fs38.renameSync(tmp, watermarkPath);
18715
18825
  }
18716
18826
  function buildWireRows(chunk2) {
18717
18827
  const lastNl = chunk2.lastIndexOf(10);
@@ -18789,11 +18899,11 @@ async function shipOnce(deps = {}) {
18789
18899
  if (!creds?.apiKey) return { status: "no-creds", shipped: 0 };
18790
18900
  const endpoint = buildBatchEndpoint(creds.apiUrl);
18791
18901
  if (!endpoint) return { status: "no-creds", shipped: 0 };
18792
- if (!fs37.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
18902
+ if (!fs38.existsSync(auditLogPath)) return { status: "idle", shipped: 0 };
18793
18903
  let shipped = 0;
18794
18904
  try {
18795
18905
  for (let chunkN = 0; chunkN < MAX_CHUNKS_PER_TICK; chunkN++) {
18796
- const size = fs37.statSync(auditLogPath).size;
18906
+ const size = fs38.statSync(auditLogPath).size;
18797
18907
  if (size === 0) break;
18798
18908
  const sig = fileSignature(auditLogPath);
18799
18909
  const wm = readWatermark(watermarkPath);
@@ -18801,12 +18911,12 @@ async function shipOnce(deps = {}) {
18801
18911
  if (offset >= size) break;
18802
18912
  const toRead = Math.min(size - offset, MAX_CHUNK_BYTES);
18803
18913
  const buf = Buffer.alloc(toRead);
18804
- const fd = fs37.openSync(auditLogPath, "r");
18914
+ const fd = fs38.openSync(auditLogPath, "r");
18805
18915
  let read2;
18806
18916
  try {
18807
- read2 = fs37.readSync(fd, buf, 0, toRead, offset);
18917
+ read2 = fs38.readSync(fd, buf, 0, toRead, offset);
18808
18918
  } finally {
18809
- fs37.closeSync(fd);
18919
+ fs38.closeSync(fd);
18810
18920
  }
18811
18921
  const { rows, consumed } = buildWireRows(buf.subarray(0, read2));
18812
18922
  if (consumed === 0) break;
@@ -18853,8 +18963,8 @@ async function shipOnce(deps = {}) {
18853
18963
  }
18854
18964
  function shipLagBytes(auditLogPath = LOCAL_AUDIT_LOG, watermarkPath = AUDIT_SHIP_WATERMARK) {
18855
18965
  try {
18856
- if (!fs37.existsSync(auditLogPath)) return 0;
18857
- const size = fs37.statSync(auditLogPath).size;
18966
+ if (!fs38.existsSync(auditLogPath)) return 0;
18967
+ const size = fs38.statSync(auditLogPath).size;
18858
18968
  const wm = readWatermark(watermarkPath);
18859
18969
  if (!wm) return size;
18860
18970
  if (wm.fileSig !== fileSignature(auditLogPath)) return size;
@@ -18885,7 +18995,7 @@ var init_audit_shipper = __esm({
18885
18995
  init_config();
18886
18996
  init_sync();
18887
18997
  init_cloud();
18888
- AUDIT_SHIP_WATERMARK = path36.join(os34.homedir(), ".node9", "audit-ship.json");
18998
+ AUDIT_SHIP_WATERMARK = path37.join(os34.homedir(), ".node9", "audit-ship.json");
18889
18999
  DEFAULT_INTERVAL_MS = 2e4;
18890
19000
  MAX_BATCH = 500;
18891
19001
  MAX_CHUNK_BYTES = 4 * 1024 * 1024;
@@ -18944,12 +19054,12 @@ var init_decision = __esm({
18944
19054
  });
18945
19055
 
18946
19056
  // src/daemon/dlp-scanner.ts
18947
- import fs38 from "fs";
18948
- import path37 from "path";
19057
+ import fs39 from "fs";
19058
+ import path38 from "path";
18949
19059
  import os35 from "os";
18950
19060
  function loadIndex() {
18951
19061
  try {
18952
- const raw = JSON.parse(fs38.readFileSync(INDEX_FILE, "utf-8"));
19062
+ const raw = JSON.parse(fs39.readFileSync(INDEX_FILE, "utf-8"));
18953
19063
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
18954
19064
  const r = raw;
18955
19065
  if (r.offsets && typeof r.offsets === "object") return r.offsets;
@@ -18961,63 +19071,63 @@ function loadIndex() {
18961
19071
  }
18962
19072
  function saveIndex(index) {
18963
19073
  try {
18964
- fs38.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
19074
+ fs39.writeFileSync(INDEX_FILE, JSON.stringify(index), { encoding: "utf-8", mode: 384 });
18965
19075
  } catch {
18966
19076
  }
18967
19077
  }
18968
19078
  function appendAuditEntry(entry) {
18969
19079
  try {
18970
- fs38.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
19080
+ fs39.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
18971
19081
  } catch {
18972
19082
  }
18973
19083
  }
18974
19084
  function runDlpScan() {
18975
- if (!fs38.existsSync(PROJECTS_DIR2)) return;
19085
+ if (!fs39.existsSync(PROJECTS_DIR2)) return;
18976
19086
  const index = loadIndex();
18977
19087
  const seenThisPass = /* @__PURE__ */ new Set();
18978
19088
  const newFindings = [];
18979
19089
  let updated = false;
18980
19090
  let projDirs;
18981
19091
  try {
18982
- projDirs = fs38.readdirSync(PROJECTS_DIR2);
19092
+ projDirs = fs39.readdirSync(PROJECTS_DIR2);
18983
19093
  } catch {
18984
19094
  return;
18985
19095
  }
18986
19096
  for (const proj of projDirs) {
18987
- const projPath = path37.join(PROJECTS_DIR2, proj);
19097
+ const projPath = path38.join(PROJECTS_DIR2, proj);
18988
19098
  try {
18989
- if (!fs38.lstatSync(projPath).isDirectory()) continue;
18990
- const real = fs38.realpathSync(projPath);
18991
- if (!real.startsWith(PROJECTS_DIR2 + path37.sep) && real !== PROJECTS_DIR2) continue;
19099
+ if (!fs39.lstatSync(projPath).isDirectory()) continue;
19100
+ const real = fs39.realpathSync(projPath);
19101
+ if (!real.startsWith(PROJECTS_DIR2 + path38.sep) && real !== PROJECTS_DIR2) continue;
18992
19102
  } catch {
18993
19103
  continue;
18994
19104
  }
18995
19105
  let files;
18996
19106
  try {
18997
- files = fs38.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
19107
+ files = fs39.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
18998
19108
  } catch {
18999
19109
  continue;
19000
19110
  }
19001
19111
  for (const file of files) {
19002
- const filePath = path37.join(projPath, file);
19112
+ const filePath = path38.join(projPath, file);
19003
19113
  const lastOffset = index[filePath] ?? 0;
19004
19114
  let size;
19005
19115
  try {
19006
- size = fs38.statSync(filePath).size;
19116
+ size = fs39.statSync(filePath).size;
19007
19117
  } catch {
19008
19118
  continue;
19009
19119
  }
19010
19120
  if (size <= lastOffset) continue;
19011
19121
  let fd;
19012
19122
  try {
19013
- fd = fs38.openSync(filePath, "r");
19123
+ fd = fs39.openSync(filePath, "r");
19014
19124
  } catch {
19015
19125
  continue;
19016
19126
  }
19017
19127
  try {
19018
19128
  const chunkSize = size - lastOffset;
19019
19129
  const buf = Buffer.alloc(chunkSize);
19020
- fs38.readSync(fd, buf, 0, chunkSize, lastOffset);
19130
+ fs39.readSync(fd, buf, 0, chunkSize, lastOffset);
19021
19131
  const chunk2 = buf.toString("utf-8");
19022
19132
  for (const line of chunk2.split("\n")) {
19023
19133
  if (!line.trim()) continue;
@@ -19064,7 +19174,7 @@ function runDlpScan() {
19064
19174
  updated = true;
19065
19175
  } finally {
19066
19176
  try {
19067
- fs38.closeSync(fd);
19177
+ fs39.closeSync(fd);
19068
19178
  } catch {
19069
19179
  }
19070
19180
  }
@@ -19112,14 +19222,14 @@ var init_dlp_scanner = __esm({
19112
19222
  init_dlp();
19113
19223
  init_native();
19114
19224
  init_state2();
19115
- INDEX_FILE = path37.join(os35.homedir(), ".node9", "dlp-index.json");
19116
- PROJECTS_DIR2 = path37.join(os35.homedir(), ".claude", "projects");
19225
+ INDEX_FILE = path38.join(os35.homedir(), ".node9", "dlp-index.json");
19226
+ PROJECTS_DIR2 = path38.join(os35.homedir(), ".claude", "projects");
19117
19227
  }
19118
19228
  });
19119
19229
 
19120
19230
  // src/daemon/mcp-reconciler.ts
19121
- import fs39 from "fs";
19122
- import path38 from "path";
19231
+ import fs40 from "fs";
19232
+ import path39 from "path";
19123
19233
  import os36 from "os";
19124
19234
  import crypto6 from "crypto";
19125
19235
  function idKey(e) {
@@ -19128,7 +19238,7 @@ function idKey(e) {
19128
19238
  }
19129
19239
  function loadBaseline() {
19130
19240
  try {
19131
- const raw = JSON.parse(fs39.readFileSync(BASELINE_FILE2, "utf-8"));
19241
+ const raw = JSON.parse(fs40.readFileSync(BASELINE_FILE2, "utf-8"));
19132
19242
  return new Set(Array.isArray(raw) ? raw : []);
19133
19243
  } catch {
19134
19244
  return /* @__PURE__ */ new Set();
@@ -19136,7 +19246,7 @@ function loadBaseline() {
19136
19246
  }
19137
19247
  function saveBaseline(keys) {
19138
19248
  try {
19139
- fs39.writeFileSync(BASELINE_FILE2, JSON.stringify([...keys].slice(-BASELINE_CAP)), {
19249
+ fs40.writeFileSync(BASELINE_FILE2, JSON.stringify([...keys].slice(-BASELINE_CAP)), {
19140
19250
  mode: 384
19141
19251
  });
19142
19252
  } catch {
@@ -19324,7 +19434,7 @@ var init_mcp_reconciler = __esm({
19324
19434
  init_cloud();
19325
19435
  init_audit();
19326
19436
  init_mcp_pin();
19327
- BASELINE_FILE2 = path38.join(os36.homedir(), ".node9", "mcp-baseline.json");
19437
+ BASELINE_FILE2 = path39.join(os36.homedir(), ".node9", "mcp-baseline.json");
19328
19438
  BASELINE_CAP = 500;
19329
19439
  DEFAULT_INTERVAL_MIN = 60;
19330
19440
  DEFAULT_STALE_DAYS = 7;
@@ -19399,22 +19509,22 @@ var init_hook_heal = __esm({
19399
19509
  });
19400
19510
 
19401
19511
  // src/daemon/startup-log.ts
19402
- import fs40 from "fs";
19403
- import path39 from "path";
19512
+ import fs41 from "fs";
19513
+ import path40 from "path";
19404
19514
  import os37 from "os";
19405
19515
  function capStartupLog(file) {
19406
19516
  try {
19407
- if (fs40.statSync(file).size > MAX_STARTUP_LOG_BYTES) fs40.truncateSync(file);
19517
+ if (fs41.statSync(file).size > MAX_STARTUP_LOG_BYTES) fs41.truncateSync(file);
19408
19518
  } catch {
19409
19519
  }
19410
19520
  }
19411
19521
  function openStartupLogFd() {
19412
19522
  try {
19413
19523
  const file = DAEMON_STARTUP_LOG();
19414
- const dir = path39.dirname(file);
19415
- if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19524
+ const dir = path40.dirname(file);
19525
+ if (!fs41.existsSync(dir)) fs41.mkdirSync(dir, { recursive: true });
19416
19526
  capStartupLog(file);
19417
- return fs40.openSync(file, "a");
19527
+ return fs41.openSync(file, "a");
19418
19528
  } catch {
19419
19529
  return void 0;
19420
19530
  }
@@ -19430,18 +19540,18 @@ function recordStartupState(outcome, kind, detail) {
19430
19540
  }
19431
19541
  }
19432
19542
  const file = DAEMON_STARTUP_STATE();
19433
- const dir = path39.dirname(file);
19434
- if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19543
+ const dir = path40.dirname(file);
19544
+ if (!fs41.existsSync(dir)) fs41.mkdirSync(dir, { recursive: true });
19435
19545
  const state = { outcome, at: (/* @__PURE__ */ new Date()).toISOString() };
19436
19546
  if (kind) state.kind = kind;
19437
19547
  if (detail) state.detail = detail.slice(0, MAX_DETAIL);
19438
19548
  const tmp = `${file}.${process.pid}.tmp`;
19439
19549
  try {
19440
- fs40.writeFileSync(tmp, JSON.stringify(state), "utf-8");
19441
- fs40.renameSync(tmp, file);
19550
+ fs41.writeFileSync(tmp, JSON.stringify(state), "utf-8");
19551
+ fs41.renameSync(tmp, file);
19442
19552
  } catch (err2) {
19443
19553
  try {
19444
- fs40.unlinkSync(tmp);
19554
+ fs41.unlinkSync(tmp);
19445
19555
  } catch {
19446
19556
  }
19447
19557
  throw err2;
@@ -19451,7 +19561,7 @@ function recordStartupState(outcome, kind, detail) {
19451
19561
  }
19452
19562
  function readStartupState() {
19453
19563
  try {
19454
- const raw = fs40.readFileSync(DAEMON_STARTUP_STATE(), "utf-8");
19564
+ const raw = fs41.readFileSync(DAEMON_STARTUP_STATE(), "utf-8");
19455
19565
  const s = JSON.parse(raw);
19456
19566
  if (!s || typeof s.outcome !== "string" || typeof s.at !== "string") return null;
19457
19567
  return s;
@@ -19486,11 +19596,11 @@ function readStartupCause(maxAgeMs = 24 * 60 * 60 * 1e3) {
19486
19596
  function logDaemonStartup(kind, detail) {
19487
19597
  try {
19488
19598
  const file = DAEMON_STARTUP_LOG();
19489
- const dir = path39.dirname(file);
19490
- if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19599
+ const dir = path40.dirname(file);
19600
+ if (!fs41.existsSync(dir)) fs41.mkdirSync(dir, { recursive: true });
19491
19601
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-startup:${kind}${detail ? ` ${detail}` : ""}
19492
19602
  `;
19493
- fs40.appendFileSync(file, line, "utf-8");
19603
+ fs41.appendFileSync(file, line, "utf-8");
19494
19604
  } catch {
19495
19605
  }
19496
19606
  }
@@ -19498,9 +19608,9 @@ var DAEMON_STARTUP_LOG, MAX_STARTUP_LOG_BYTES, DAEMON_STARTUP_STATE, MAX_DETAIL,
19498
19608
  var init_startup_log = __esm({
19499
19609
  "src/daemon/startup-log.ts"() {
19500
19610
  "use strict";
19501
- DAEMON_STARTUP_LOG = () => path39.join(os37.homedir(), ".node9", "daemon-startup.log");
19611
+ DAEMON_STARTUP_LOG = () => path40.join(os37.homedir(), ".node9", "daemon-startup.log");
19502
19612
  MAX_STARTUP_LOG_BYTES = 256 * 1024;
19503
- DAEMON_STARTUP_STATE = () => path39.join(os37.homedir(), ".node9", "daemon-startup-state.json");
19613
+ DAEMON_STARTUP_STATE = () => path40.join(os37.homedir(), ".node9", "daemon-startup-state.json");
19504
19614
  MAX_DETAIL = 200;
19505
19615
  STARTING_GRACE_MS = 90 * 1e3;
19506
19616
  }
@@ -19508,8 +19618,8 @@ var init_startup_log = __esm({
19508
19618
 
19509
19619
  // src/daemon/server.ts
19510
19620
  import http3 from "http";
19511
- import fs41 from "fs";
19512
- import path40 from "path";
19621
+ import fs42 from "fs";
19622
+ import path41 from "path";
19513
19623
  import os38 from "os";
19514
19624
  import { randomUUID as randomUUID4 } from "crypto";
19515
19625
  import { spawnSync } from "child_process";
@@ -19603,6 +19713,7 @@ function startDaemon() {
19603
19713
  }
19604
19714
  const internalToken = randomUUID4();
19605
19715
  const validToken = (req) => req.headers["x-node9-internal"] === internalToken || req.headers["x-node9-token"] === internalToken;
19716
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
19606
19717
  const IDLE_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
19607
19718
  const watchMode = process.env.NODE9_WATCH_MODE === "1";
19608
19719
  let idleTimer;
@@ -19612,7 +19723,7 @@ function startDaemon() {
19612
19723
  idleTimer = setTimeout(() => {
19613
19724
  if (autoStarted) {
19614
19725
  try {
19615
- fs41.unlinkSync(DAEMON_PID_FILE);
19726
+ fs42.unlinkSync(DAEMON_PID_FILE);
19616
19727
  } catch {
19617
19728
  }
19618
19729
  }
@@ -19757,7 +19868,7 @@ data: ${JSON.stringify(item.data)}
19757
19868
  mcpServer: entry.mcpServer
19758
19869
  });
19759
19870
  }
19760
- const projectCwd = typeof cwd === "string" && path40.isAbsolute(cwd) ? cwd : void 0;
19871
+ const projectCwd = typeof cwd === "string" && path41.isAbsolute(cwd) ? cwd : void 0;
19761
19872
  const projectConfig = getConfig(projectCwd);
19762
19873
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
19763
19874
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -19975,6 +20086,35 @@ data: ${JSON.stringify(item.data)}
19975
20086
  res.writeHead(200, { "Content-Type": "application/json" });
19976
20087
  return res.end(JSON.stringify({ interactive: hasInteractiveClient() }));
19977
20088
  }
20089
+ if (req.method === "POST" && pathname === "/shutdown") {
20090
+ if (!validToken(req)) {
20091
+ res.writeHead(401, { "Content-Type": "application/json" });
20092
+ return res.end(JSON.stringify({ error: "unauthorized" }));
20093
+ }
20094
+ res.writeHead(200, { "Content-Type": "application/json" });
20095
+ res.end(JSON.stringify({ ok: true }));
20096
+ logDaemonStartup("shutdown", "yielding on authenticated /shutdown (takeover or restart)");
20097
+ setImmediate(() => {
20098
+ try {
20099
+ server.close();
20100
+ } catch {
20101
+ }
20102
+ process.exit(0);
20103
+ });
20104
+ return;
20105
+ }
20106
+ if (req.method === "GET" && pathname === "/health") {
20107
+ res.writeHead(200, { "Content-Type": "application/json" });
20108
+ return res.end(
20109
+ JSON.stringify({
20110
+ version: CURRENT_BUILD.version,
20111
+ buildId: buildIdString(CURRENT_BUILD),
20112
+ pid: process.pid,
20113
+ startedAt,
20114
+ autoStarted
20115
+ })
20116
+ );
20117
+ }
19978
20118
  if (req.method === "GET" && pathname === "/state/check") {
19979
20119
  const predicatesParam = reqUrl.searchParams.get("predicates") ?? "";
19980
20120
  const predicates = predicatesParam.split(",").filter(Boolean);
@@ -20053,8 +20193,8 @@ data: ${JSON.stringify(item.data)}
20053
20193
  if (!validToken(req)) return res.writeHead(403).end();
20054
20194
  const periodParam = reqUrl.searchParams.get("period") || "7d";
20055
20195
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
20056
- const logPath = path40.join(os38.homedir(), ".node9", "audit.log");
20057
- if (!fs41.existsSync(logPath)) {
20196
+ const logPath = path41.join(os38.homedir(), ".node9", "audit.log");
20197
+ if (!fs42.existsSync(logPath)) {
20058
20198
  res.writeHead(200, { "Content-Type": "application/json" });
20059
20199
  return res.end(
20060
20200
  JSON.stringify({
@@ -20067,7 +20207,7 @@ data: ${JSON.stringify(item.data)}
20067
20207
  );
20068
20208
  }
20069
20209
  try {
20070
- const raw = fs41.readFileSync(logPath, "utf-8");
20210
+ const raw = fs42.readFileSync(logPath, "utf-8");
20071
20211
  const allEntries = raw.split("\n").flatMap((line) => {
20072
20212
  if (!line.trim()) return [];
20073
20213
  try {
@@ -20382,34 +20522,114 @@ data: ${JSON.stringify(item.data)}
20382
20522
  setDaemonServer(server);
20383
20523
  let bindAttempts = 0;
20384
20524
  const MAX_BIND_ATTEMPTS = 3;
20525
+ let tookDownHolder = false;
20385
20526
  function retryListen() {
20386
20527
  if (++bindAttempts >= MAX_BIND_ATTEMPTS) {
20528
+ if (tookDownHolder) {
20529
+ logDaemonStartup(
20530
+ "takeover-bind-failed",
20531
+ `shut down the previous daemon but could not bind :${DAEMON_PORT} \u2014 the port was taken during handover`
20532
+ );
20533
+ recordStartupState(
20534
+ "failed",
20535
+ "takeover-bind-failed",
20536
+ `took over :${DAEMON_PORT} but could not rebind (the port was claimed during handover) \u2014 no daemon is serving; run: node9 daemon restart`
20537
+ );
20538
+ } else {
20539
+ logDaemonStartup(
20540
+ "port-unavailable",
20541
+ `:${DAEMON_PORT} is held by something that is not a node9 daemon`
20542
+ );
20543
+ recordStartupState(
20544
+ "failed",
20545
+ "port-unavailable",
20546
+ `:${DAEMON_PORT} is held by another process that is not a node9 daemon \u2014 free the port, then: node9 daemon --background`
20547
+ );
20548
+ }
20549
+ return process.exit(0);
20550
+ }
20551
+ server.listen(DAEMON_PORT, DAEMON_HOST);
20552
+ }
20553
+ async function decideAgainstHolder(holderPid, holderToken) {
20554
+ let holderBuildId = null;
20555
+ try {
20556
+ const res = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/health`, {
20557
+ signal: AbortSignal.timeout(800)
20558
+ });
20559
+ if (res.ok) {
20560
+ const j = await res.json().catch(() => null);
20561
+ if (j && typeof j.buildId === "string") holderBuildId = j.buildId;
20562
+ }
20563
+ } catch {
20564
+ }
20565
+ const holderBuild = holderBuildId ? parseBuildId(holderBuildId) : null;
20566
+ if (holderBuild && compareBuild(CURRENT_BUILD, holderBuild) > 0 && holderToken) {
20567
+ try {
20568
+ const r = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/shutdown`, {
20569
+ method: "POST",
20570
+ headers: { "x-node9-internal": holderToken },
20571
+ signal: AbortSignal.timeout(2e3)
20572
+ });
20573
+ if (r.ok) {
20574
+ for (let i = 0; i < 10; i++) {
20575
+ await new Promise((resolve2) => setTimeout(resolve2, 200));
20576
+ const stillUp = await fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/health`, {
20577
+ signal: AbortSignal.timeout(300)
20578
+ }).then(
20579
+ () => true,
20580
+ () => false
20581
+ );
20582
+ if (!stillUp) break;
20583
+ }
20584
+ logDaemonStartup(
20585
+ "takeover",
20586
+ `took over :${DAEMON_PORT} from older build ${holderBuildId} (pid ${holderPid})`
20587
+ );
20588
+ tookDownHolder = true;
20589
+ retryListen();
20590
+ return;
20591
+ }
20592
+ recordStartupState(
20593
+ "failed",
20594
+ "version-skew-unauthenticated",
20595
+ `an older-build daemon (${holderBuildId}) holds :${DAEMON_PORT} but could not be authenticated for takeover \u2014 run: node9 daemon restart`
20596
+ );
20597
+ logDaemonStartup("port-in-use", `older build on :${DAEMON_PORT}, /shutdown refused`);
20598
+ return process.exit(0);
20599
+ } catch {
20600
+ }
20601
+ }
20602
+ if (holderBuild && compareBuild(holderBuild, CURRENT_BUILD) > 0) {
20387
20603
  logDaemonStartup(
20388
- "port-unavailable",
20389
- `:${DAEMON_PORT} is held by something that is not a node9 daemon`
20390
- );
20391
- recordStartupState(
20392
- "failed",
20393
- "port-unavailable",
20394
- `:${DAEMON_PORT} is held by another process that is not a node9 daemon \u2014 free the port, then: node9 daemon --background`
20604
+ "port-in-use",
20605
+ `a NEWER daemon (${holderBuildId}, pid ${holderPid}) owns :${DAEMON_PORT} \u2014 yielding`
20395
20606
  );
20607
+ recordStartupState("ok-elsewhere", "newer-daemon-running");
20396
20608
  return process.exit(0);
20397
20609
  }
20398
- server.listen(DAEMON_PORT, DAEMON_HOST);
20610
+ logDaemonStartup("port-in-use", `another daemon (pid ${holderPid}) owns :${DAEMON_PORT}`);
20611
+ recordStartupState("ok-elsewhere");
20612
+ return process.exit(0);
20399
20613
  }
20400
20614
  server.on("error", (e) => {
20401
20615
  if (e.code === "EADDRINUSE") {
20402
20616
  try {
20403
- if (fs41.existsSync(DAEMON_PID_FILE)) {
20404
- const { pid } = JSON.parse(fs41.readFileSync(DAEMON_PID_FILE, "utf-8"));
20617
+ if (fs42.existsSync(DAEMON_PID_FILE)) {
20618
+ const parsed = JSON.parse(fs42.readFileSync(DAEMON_PID_FILE, "utf-8"));
20619
+ const pid = parsed.pid;
20620
+ if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > 4194304) {
20621
+ throw new Error("invalid pid in daemon.pid");
20622
+ }
20405
20623
  process.kill(pid, 0);
20406
- logDaemonStartup("port-in-use", `another daemon (pid ${pid}) owns :${DAEMON_PORT}`);
20407
- recordStartupState("ok-elsewhere");
20408
- return process.exit(0);
20624
+ void decideAgainstHolder(
20625
+ pid,
20626
+ typeof parsed.internalToken === "string" ? parsed.internalToken : null
20627
+ );
20628
+ return;
20409
20629
  }
20410
20630
  } catch {
20411
20631
  try {
20412
- fs41.unlinkSync(DAEMON_PID_FILE);
20632
+ fs42.unlinkSync(DAEMON_PID_FILE);
20413
20633
  } catch {
20414
20634
  }
20415
20635
  retryListen();
@@ -20444,7 +20664,12 @@ data: ${JSON.stringify(item.data)}
20444
20664
  process.kill(orphanPid, 0);
20445
20665
  atomicWriteSync2(
20446
20666
  DAEMON_PID_FILE,
20447
- JSON.stringify({ pid: orphanPid, port: DAEMON_PORT, internalToken, autoStarted }),
20667
+ JSON.stringify({
20668
+ pid: orphanPid,
20669
+ port: DAEMON_PORT,
20670
+ internalToken: null,
20671
+ autoStarted
20672
+ }),
20448
20673
  { mode: 384 }
20449
20674
  );
20450
20675
  adopted = true;
@@ -20492,7 +20717,15 @@ data: ${JSON.stringify(item.data)}
20492
20717
  server.listen(DAEMON_PORT, DAEMON_HOST, () => {
20493
20718
  atomicWriteSync2(
20494
20719
  DAEMON_PID_FILE,
20495
- JSON.stringify({ pid: process.pid, port: DAEMON_PORT, internalToken, autoStarted }),
20720
+ JSON.stringify({
20721
+ pid: process.pid,
20722
+ port: DAEMON_PORT,
20723
+ internalToken,
20724
+ autoStarted,
20725
+ version: CURRENT_BUILD.version,
20726
+ buildId: buildIdString(CURRENT_BUILD),
20727
+ startedAt
20728
+ }),
20496
20729
  { mode: 384 }
20497
20730
  );
20498
20731
  console.error(chalk6.green(`\u{1F6E1}\uFE0F Node9 Guard LIVE on 127.0.0.1:${DAEMON_PORT}`));
@@ -20510,6 +20743,7 @@ var init_server = __esm({
20510
20743
  init_core();
20511
20744
  init_scan();
20512
20745
  init_scan_summary();
20746
+ init_build_id();
20513
20747
  init_state2();
20514
20748
  init_state();
20515
20749
  init_costSync();
@@ -20525,15 +20759,15 @@ var init_server = __esm({
20525
20759
  });
20526
20760
 
20527
20761
  // src/daemon/service.ts
20528
- import fs42 from "fs";
20529
- import path41 from "path";
20762
+ import fs43 from "fs";
20763
+ import path42 from "path";
20530
20764
  import os39 from "os";
20531
20765
  import { spawnSync as spawnSync2, execFileSync } from "child_process";
20532
20766
  function resolveNode9Binary() {
20533
20767
  try {
20534
20768
  const script = process.argv[1];
20535
- if (typeof script === "string" && path41.isAbsolute(script) && fs42.existsSync(script)) {
20536
- return fs42.realpathSync(script);
20769
+ if (typeof script === "string" && path42.isAbsolute(script) && fs43.existsSync(script)) {
20770
+ return fs43.realpathSync(script);
20537
20771
  }
20538
20772
  } catch {
20539
20773
  }
@@ -20551,11 +20785,11 @@ function xmlEscape(s) {
20551
20785
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
20552
20786
  }
20553
20787
  function launchdPlist(binaryPath) {
20554
- const logDir = path41.join(os39.homedir(), ".node9");
20788
+ const logDir = path42.join(os39.homedir(), ".node9");
20555
20789
  const nodePath = xmlEscape(process.execPath);
20556
20790
  const scriptPath = xmlEscape(binaryPath);
20557
- const outLog = xmlEscape(path41.join(logDir, "daemon.log"));
20558
- const errLog = xmlEscape(path41.join(logDir, "daemon-error.log"));
20791
+ const outLog = xmlEscape(path42.join(logDir, "daemon.log"));
20792
+ const errLog = xmlEscape(path42.join(logDir, "daemon-error.log"));
20559
20793
  return `<?xml version="1.0" encoding="UTF-8"?>
20560
20794
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
20561
20795
  <plist version="1.0">
@@ -20588,9 +20822,9 @@ function launchdPlist(binaryPath) {
20588
20822
  `;
20589
20823
  }
20590
20824
  function installLaunchd(binaryPath) {
20591
- const dir = path41.dirname(LAUNCHD_PLIST);
20592
- if (!fs42.existsSync(dir)) fs42.mkdirSync(dir, { recursive: true });
20593
- fs42.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
20825
+ const dir = path42.dirname(LAUNCHD_PLIST);
20826
+ if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
20827
+ fs43.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
20594
20828
  spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
20595
20829
  const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
20596
20830
  encoding: "utf8",
@@ -20601,13 +20835,13 @@ function installLaunchd(binaryPath) {
20601
20835
  }
20602
20836
  }
20603
20837
  function uninstallLaunchd() {
20604
- if (fs42.existsSync(LAUNCHD_PLIST)) {
20838
+ if (fs43.existsSync(LAUNCHD_PLIST)) {
20605
20839
  spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
20606
- fs42.unlinkSync(LAUNCHD_PLIST);
20840
+ fs43.unlinkSync(LAUNCHD_PLIST);
20607
20841
  }
20608
20842
  }
20609
20843
  function isLaunchdInstalled() {
20610
- return fs42.existsSync(LAUNCHD_PLIST);
20844
+ return fs43.existsSync(LAUNCHD_PLIST);
20611
20845
  }
20612
20846
  function systemdUnit(binaryPath) {
20613
20847
  return `[Unit]
@@ -20626,10 +20860,10 @@ WantedBy=default.target
20626
20860
  `;
20627
20861
  }
20628
20862
  function installSystemd(binaryPath) {
20629
- if (!fs42.existsSync(SYSTEMD_UNIT_DIR)) {
20630
- fs42.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
20863
+ if (!fs43.existsSync(SYSTEMD_UNIT_DIR)) {
20864
+ fs43.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
20631
20865
  }
20632
- fs42.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
20866
+ fs43.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
20633
20867
  try {
20634
20868
  execFileSync("loginctl", ["enable-linger", os39.userInfo().username], { timeout: 3e3 });
20635
20869
  } catch {
@@ -20651,23 +20885,23 @@ function installSystemd(binaryPath) {
20651
20885
  }
20652
20886
  }
20653
20887
  function uninstallSystemd() {
20654
- if (fs42.existsSync(SYSTEMD_UNIT)) {
20888
+ if (fs43.existsSync(SYSTEMD_UNIT)) {
20655
20889
  spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
20656
20890
  encoding: "utf8",
20657
20891
  timeout: 5e3
20658
20892
  });
20659
20893
  spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
20660
- fs42.unlinkSync(SYSTEMD_UNIT);
20894
+ fs43.unlinkSync(SYSTEMD_UNIT);
20661
20895
  }
20662
20896
  }
20663
20897
  function isSystemdInstalled() {
20664
- return fs42.existsSync(SYSTEMD_UNIT);
20898
+ return fs43.existsSync(SYSTEMD_UNIT);
20665
20899
  }
20666
20900
  function stopRunningDaemon() {
20667
- const pidFile = path41.join(os39.homedir(), ".node9", "daemon.pid");
20668
- if (!fs42.existsSync(pidFile)) return;
20901
+ const pidFile = path42.join(os39.homedir(), ".node9", "daemon.pid");
20902
+ if (!fs43.existsSync(pidFile)) return;
20669
20903
  try {
20670
- const data = JSON.parse(fs42.readFileSync(pidFile, "utf-8"));
20904
+ const data = JSON.parse(fs43.readFileSync(pidFile, "utf-8"));
20671
20905
  const pid = data.pid;
20672
20906
  const MAX_PID2 = 4194304;
20673
20907
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -20687,7 +20921,7 @@ function stopRunningDaemon() {
20687
20921
  }
20688
20922
  }
20689
20923
  try {
20690
- fs42.unlinkSync(pidFile);
20924
+ fs43.unlinkSync(pidFile);
20691
20925
  } catch {
20692
20926
  }
20693
20927
  } catch {
@@ -20831,19 +21065,19 @@ var init_service = __esm({
20831
21065
  "src/daemon/service.ts"() {
20832
21066
  "use strict";
20833
21067
  LAUNCHD_LABEL = "ai.node9.daemon";
20834
- LAUNCHD_PLIST = path41.join(os39.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
20835
- SYSTEMD_UNIT_DIR = path41.join(os39.homedir(), ".config", "systemd", "user");
20836
- SYSTEMD_UNIT = path41.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
21068
+ LAUNCHD_PLIST = path42.join(os39.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
21069
+ SYSTEMD_UNIT_DIR = path42.join(os39.homedir(), ".config", "systemd", "user");
21070
+ SYSTEMD_UNIT = path42.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
20837
21071
  }
20838
21072
  });
20839
21073
 
20840
21074
  // src/daemon/index.ts
20841
- import fs43 from "fs";
21075
+ import fs44 from "fs";
20842
21076
  import chalk7 from "chalk";
20843
21077
  function stopDaemon() {
20844
- if (!fs43.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
21078
+ if (!fs44.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
20845
21079
  try {
20846
- const data = JSON.parse(fs43.readFileSync(DAEMON_PID_FILE, "utf-8"));
21080
+ const data = JSON.parse(fs44.readFileSync(DAEMON_PID_FILE, "utf-8"));
20847
21081
  const pid = data.pid;
20848
21082
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
20849
21083
  console.log(chalk7.gray("Cleaned up invalid PID file."));
@@ -20855,7 +21089,7 @@ function stopDaemon() {
20855
21089
  console.log(chalk7.gray("Cleaned up stale PID file."));
20856
21090
  } finally {
20857
21091
  try {
20858
- fs43.unlinkSync(DAEMON_PID_FILE);
21092
+ fs44.unlinkSync(DAEMON_PID_FILE);
20859
21093
  } catch {
20860
21094
  }
20861
21095
  }
@@ -20864,9 +21098,9 @@ function daemonStatus() {
20864
21098
  const serviceInstalled = isDaemonServiceInstalled();
20865
21099
  const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
20866
21100
  let processStatus;
20867
- if (fs43.existsSync(DAEMON_PID_FILE)) {
21101
+ if (fs44.existsSync(DAEMON_PID_FILE)) {
20868
21102
  try {
20869
- const data = JSON.parse(fs43.readFileSync(DAEMON_PID_FILE, "utf-8"));
21103
+ const data = JSON.parse(fs44.readFileSync(DAEMON_PID_FILE, "utf-8"));
20870
21104
  const pid = data.pid;
20871
21105
  const port = data.port;
20872
21106
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -22009,14 +22243,14 @@ var require_util = __commonJS({
22009
22243
  }
22010
22244
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
22011
22245
  let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
22012
- let path71 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
22246
+ let path72 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
22013
22247
  if (origin[origin.length - 1] === "/") {
22014
22248
  origin = origin.slice(0, origin.length - 1);
22015
22249
  }
22016
- if (path71 && path71[0] !== "/") {
22017
- path71 = `/${path71}`;
22250
+ if (path72 && path72[0] !== "/") {
22251
+ path72 = `/${path72}`;
22018
22252
  }
22019
- return new URL(`${origin}${path71}`);
22253
+ return new URL(`${origin}${path72}`);
22020
22254
  }
22021
22255
  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
22022
22256
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -22837,9 +23071,9 @@ var require_diagnostics = __commonJS({
22837
23071
  "undici:client:sendHeaders",
22838
23072
  (evt) => {
22839
23073
  const {
22840
- request: { method, path: path71, origin }
23074
+ request: { method, path: path72, origin }
22841
23075
  } = evt;
22842
- debugLog("sending request to %s %s%s", method, origin, path71);
23076
+ debugLog("sending request to %s %s%s", method, origin, path72);
22843
23077
  }
22844
23078
  );
22845
23079
  }
@@ -22857,14 +23091,14 @@ var require_diagnostics = __commonJS({
22857
23091
  "undici:request:headers",
22858
23092
  (evt) => {
22859
23093
  const {
22860
- request: { method, path: path71, origin },
23094
+ request: { method, path: path72, origin },
22861
23095
  response: { statusCode }
22862
23096
  } = evt;
22863
23097
  debugLog(
22864
23098
  "received response to %s %s%s - HTTP %d",
22865
23099
  method,
22866
23100
  origin,
22867
- path71,
23101
+ path72,
22868
23102
  statusCode
22869
23103
  );
22870
23104
  }
@@ -22873,23 +23107,23 @@ var require_diagnostics = __commonJS({
22873
23107
  "undici:request:trailers",
22874
23108
  (evt) => {
22875
23109
  const {
22876
- request: { method, path: path71, origin }
23110
+ request: { method, path: path72, origin }
22877
23111
  } = evt;
22878
- debugLog("trailers received from %s %s%s", method, origin, path71);
23112
+ debugLog("trailers received from %s %s%s", method, origin, path72);
22879
23113
  }
22880
23114
  );
22881
23115
  diagnosticsChannel.subscribe(
22882
23116
  "undici:request:error",
22883
23117
  (evt) => {
22884
23118
  const {
22885
- request: { method, path: path71, origin },
23119
+ request: { method, path: path72, origin },
22886
23120
  error
22887
23121
  } = evt;
22888
23122
  debugLog(
22889
23123
  "request to %s %s%s errored - %s",
22890
23124
  method,
22891
23125
  origin,
22892
- path71,
23126
+ path72,
22893
23127
  error.message
22894
23128
  );
22895
23129
  }
@@ -22992,7 +23226,7 @@ var require_request = __commonJS({
22992
23226
  var kHandler = /* @__PURE__ */ Symbol("handler");
22993
23227
  var Request = class {
22994
23228
  constructor(origin, {
22995
- path: path71,
23229
+ path: path72,
22996
23230
  method,
22997
23231
  body,
22998
23232
  headers,
@@ -23009,11 +23243,11 @@ var require_request = __commonJS({
23009
23243
  maxRedirections,
23010
23244
  typeOfService
23011
23245
  }, handler) {
23012
- if (typeof path71 !== "string") {
23246
+ if (typeof path72 !== "string") {
23013
23247
  throw new InvalidArgumentError("path must be a string");
23014
- } else if (path71[0] !== "/" && !(path71.startsWith("http://") || path71.startsWith("https://")) && method !== "CONNECT") {
23248
+ } else if (path72[0] !== "/" && !(path72.startsWith("http://") || path72.startsWith("https://")) && method !== "CONNECT") {
23015
23249
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
23016
- } else if (invalidPathRegex.test(path71)) {
23250
+ } else if (invalidPathRegex.test(path72)) {
23017
23251
  throw new InvalidArgumentError("invalid request path");
23018
23252
  }
23019
23253
  if (typeof method !== "string") {
@@ -23088,7 +23322,7 @@ var require_request = __commonJS({
23088
23322
  this.completed = false;
23089
23323
  this.aborted = false;
23090
23324
  this.upgrade = upgrade || null;
23091
- this.path = query ? serializePathWithQuery(path71, query) : path71;
23325
+ this.path = query ? serializePathWithQuery(path72, query) : path72;
23092
23326
  this.origin = origin;
23093
23327
  this.protocol = getProtocolFromUrlString(origin);
23094
23328
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
@@ -28127,7 +28361,7 @@ var require_client_h1 = __commonJS({
28127
28361
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
28128
28362
  }
28129
28363
  function writeH1(client, request2) {
28130
- const { method, path: path71, host, upgrade, blocking, reset } = request2;
28364
+ const { method, path: path72, host, upgrade, blocking, reset } = request2;
28131
28365
  let { body, headers, contentLength } = request2;
28132
28366
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
28133
28367
  if (util.isFormDataLike(body)) {
@@ -28196,7 +28430,7 @@ var require_client_h1 = __commonJS({
28196
28430
  if (socket.setTypeOfService) {
28197
28431
  socket.setTypeOfService(request2.typeOfService);
28198
28432
  }
28199
- let header = `${method} ${path71} HTTP/1.1\r
28433
+ let header = `${method} ${path72} HTTP/1.1\r
28200
28434
  `;
28201
28435
  if (typeof host === "string") {
28202
28436
  header += `host: ${host}\r
@@ -28849,7 +29083,7 @@ var require_client_h2 = __commonJS({
28849
29083
  function writeH2(client, request2) {
28850
29084
  const requestTimeout = request2.bodyTimeout ?? client[kBodyTimeout];
28851
29085
  const session = client[kHTTP2Session];
28852
- const { method, path: path71, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
29086
+ const { method, path: path72, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
28853
29087
  let { body } = request2;
28854
29088
  if (upgrade != null && upgrade !== "websocket") {
28855
29089
  util.errorRequest(client, request2, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
@@ -28917,7 +29151,7 @@ var require_client_h2 = __commonJS({
28917
29151
  }
28918
29152
  headers[HTTP2_HEADER_METHOD] = "CONNECT";
28919
29153
  headers[HTTP2_HEADER_PROTOCOL] = "websocket";
28920
- headers[HTTP2_HEADER_PATH] = path71;
29154
+ headers[HTTP2_HEADER_PATH] = path72;
28921
29155
  if (protocol === "ws:" || protocol === "wss:") {
28922
29156
  headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
28923
29157
  } else {
@@ -28958,7 +29192,7 @@ var require_client_h2 = __commonJS({
28958
29192
  stream.setTimeout(requestTimeout);
28959
29193
  return true;
28960
29194
  }
28961
- headers[HTTP2_HEADER_PATH] = path71;
29195
+ headers[HTTP2_HEADER_PATH] = path72;
28962
29196
  headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
28963
29197
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
28964
29198
  if (body && typeof body.read === "function") {
@@ -31260,10 +31494,10 @@ var require_proxy_agent = __commonJS({
31260
31494
  };
31261
31495
  const {
31262
31496
  origin,
31263
- path: path71 = "/",
31497
+ path: path72 = "/",
31264
31498
  headers = {}
31265
31499
  } = opts;
31266
- opts.path = origin + path71;
31500
+ opts.path = origin + path72;
31267
31501
  if (!("host" in headers) && !("Host" in headers)) {
31268
31502
  const { host } = new URL(origin);
31269
31503
  headers.host = host;
@@ -33326,20 +33560,20 @@ var require_mock_utils = __commonJS({
33326
33560
  }
33327
33561
  return normalizedQp;
33328
33562
  }
33329
- function safeUrl(path71) {
33330
- if (typeof path71 !== "string") {
33331
- return path71;
33563
+ function safeUrl(path72) {
33564
+ if (typeof path72 !== "string") {
33565
+ return path72;
33332
33566
  }
33333
- const pathSegments = path71.split("?", 3);
33567
+ const pathSegments = path72.split("?", 3);
33334
33568
  if (pathSegments.length !== 2) {
33335
- return path71;
33569
+ return path72;
33336
33570
  }
33337
33571
  const qp = new URLSearchParams(pathSegments.pop());
33338
33572
  qp.sort();
33339
33573
  return [...pathSegments, qp.toString()].join("?");
33340
33574
  }
33341
- function matchKey(mockDispatch2, { path: path71, method, body, headers }) {
33342
- const pathMatch = matchValue(mockDispatch2.path, path71);
33575
+ function matchKey(mockDispatch2, { path: path72, method, body, headers }) {
33576
+ const pathMatch = matchValue(mockDispatch2.path, path72);
33343
33577
  const methodMatch = matchValue(mockDispatch2.method, method);
33344
33578
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
33345
33579
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -33364,8 +33598,8 @@ var require_mock_utils = __commonJS({
33364
33598
  const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path;
33365
33599
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
33366
33600
  const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath);
33367
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path71, ignoreTrailingSlash }) => {
33368
- return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path71)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path71), resolvedPath);
33601
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path72, ignoreTrailingSlash }) => {
33602
+ return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path72)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path72), resolvedPath);
33369
33603
  });
33370
33604
  if (matchedMockDispatches.length === 0) {
33371
33605
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
@@ -33404,19 +33638,19 @@ var require_mock_utils = __commonJS({
33404
33638
  mockDispatches.splice(index, 1);
33405
33639
  }
33406
33640
  }
33407
- function removeTrailingSlash(path71) {
33408
- while (path71.endsWith("/")) {
33409
- path71 = path71.slice(0, -1);
33641
+ function removeTrailingSlash(path72) {
33642
+ while (path72.endsWith("/")) {
33643
+ path72 = path72.slice(0, -1);
33410
33644
  }
33411
- if (path71.length === 0) {
33412
- path71 = "/";
33645
+ if (path72.length === 0) {
33646
+ path72 = "/";
33413
33647
  }
33414
- return path71;
33648
+ return path72;
33415
33649
  }
33416
33650
  function buildKey(opts) {
33417
- const { path: path71, method, body, headers, query } = opts;
33651
+ const { path: path72, method, body, headers, query } = opts;
33418
33652
  return {
33419
- path: path71,
33653
+ path: path72,
33420
33654
  method,
33421
33655
  body,
33422
33656
  headers,
@@ -34106,10 +34340,10 @@ var require_pending_interceptors_formatter = __commonJS({
34106
34340
  }
34107
34341
  format(pendingInterceptors) {
34108
34342
  const withPrettyHeaders = pendingInterceptors.map(
34109
- ({ method, path: path71, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
34343
+ ({ method, path: path72, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
34110
34344
  Method: method,
34111
34345
  Origin: origin,
34112
- Path: path71,
34346
+ Path: path72,
34113
34347
  "Status code": statusCode,
34114
34348
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
34115
34349
  Invocations: timesInvoked,
@@ -34191,9 +34425,9 @@ var require_mock_agent = __commonJS({
34191
34425
  const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
34192
34426
  const dispatchOpts = { ...opts };
34193
34427
  if (acceptNonStandardSearchParameters && dispatchOpts.path) {
34194
- const [path71, searchParams] = dispatchOpts.path.split("?");
34428
+ const [path72, searchParams] = dispatchOpts.path.split("?");
34195
34429
  const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
34196
- dispatchOpts.path = `${path71}?${normalizedSearchParams}`;
34430
+ dispatchOpts.path = `${path72}?${normalizedSearchParams}`;
34197
34431
  }
34198
34432
  return this[kAgent].dispatch(dispatchOpts, handler);
34199
34433
  }
@@ -34594,12 +34828,12 @@ var require_snapshot_recorder = __commonJS({
34594
34828
  * @return {Promise<void>} - Resolves when snapshots are loaded
34595
34829
  */
34596
34830
  async loadSnapshots(filePath) {
34597
- const path71 = filePath || this.#snapshotPath;
34598
- if (!path71) {
34831
+ const path72 = filePath || this.#snapshotPath;
34832
+ if (!path72) {
34599
34833
  throw new InvalidArgumentError("Snapshot path is required");
34600
34834
  }
34601
34835
  try {
34602
- const data = await readFile(resolve2(path71), "utf8");
34836
+ const data = await readFile(resolve2(path72), "utf8");
34603
34837
  const parsed = JSON.parse(data);
34604
34838
  if (Array.isArray(parsed)) {
34605
34839
  this.#snapshots.clear();
@@ -34613,7 +34847,7 @@ var require_snapshot_recorder = __commonJS({
34613
34847
  if (error.code === "ENOENT") {
34614
34848
  this.#snapshots.clear();
34615
34849
  } else {
34616
- throw new UndiciError(`Failed to load snapshots from ${path71}`, { cause: error });
34850
+ throw new UndiciError(`Failed to load snapshots from ${path72}`, { cause: error });
34617
34851
  }
34618
34852
  }
34619
34853
  }
@@ -34624,11 +34858,11 @@ var require_snapshot_recorder = __commonJS({
34624
34858
  * @returns {Promise<void>} - Resolves when snapshots are saved
34625
34859
  */
34626
34860
  async saveSnapshots(filePath) {
34627
- const path71 = filePath || this.#snapshotPath;
34628
- if (!path71) {
34861
+ const path72 = filePath || this.#snapshotPath;
34862
+ if (!path72) {
34629
34863
  throw new InvalidArgumentError("Snapshot path is required");
34630
34864
  }
34631
- const resolvedPath = resolve2(path71);
34865
+ const resolvedPath = resolve2(path72);
34632
34866
  await mkdir(dirname2(resolvedPath), { recursive: true });
34633
34867
  const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
34634
34868
  hash,
@@ -35253,15 +35487,15 @@ var require_redirect_handler = __commonJS({
35253
35487
  return;
35254
35488
  }
35255
35489
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
35256
- const path71 = search ? `${pathname}${search}` : pathname;
35257
- const redirectUrlString = `${origin}${path71}`;
35490
+ const path72 = search ? `${pathname}${search}` : pathname;
35491
+ const redirectUrlString = `${origin}${path72}`;
35258
35492
  for (const historyUrl of this.history) {
35259
35493
  if (historyUrl.toString() === redirectUrlString) {
35260
35494
  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.`);
35261
35495
  }
35262
35496
  }
35263
35497
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
35264
- this.opts.path = path71;
35498
+ this.opts.path = path72;
35265
35499
  this.opts.origin = origin;
35266
35500
  this.opts.query = null;
35267
35501
  }
@@ -41468,11 +41702,11 @@ var require_fetch = __commonJS({
41468
41702
  function dispatch({ body }) {
41469
41703
  const url = requestCurrentURL(request2);
41470
41704
  const agent = fetchParams.controller.dispatcher;
41471
- const path71 = url.pathname + url.search;
41705
+ const path72 = url.pathname + url.search;
41472
41706
  const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === "?";
41473
41707
  return new Promise((resolve2, reject) => agent.dispatch(
41474
41708
  {
41475
- path: hasTrailingQuestionMark ? `${path71}?` : path71,
41709
+ path: hasTrailingQuestionMark ? `${path72}?` : path72,
41476
41710
  origin: url.origin,
41477
41711
  method: request2.method,
41478
41712
  body: agent.isMockActive ? request2.body && (request2.body.source || request2.body.stream) : body,
@@ -42403,9 +42637,9 @@ var require_util4 = __commonJS({
42403
42637
  }
42404
42638
  }
42405
42639
  }
42406
- function validateCookiePath(path71) {
42407
- for (let i = 0; i < path71.length; ++i) {
42408
- const code = path71.charCodeAt(i);
42640
+ function validateCookiePath(path72) {
42641
+ for (let i = 0; i < path72.length; ++i) {
42642
+ const code = path72.charCodeAt(i);
42409
42643
  if (code < 32 || // exclude CTLs (0-31)
42410
42644
  code === 127 || // DEL
42411
42645
  code === 59) {
@@ -45575,11 +45809,11 @@ var require_undici = __commonJS({
45575
45809
  if (typeof opts.path !== "string") {
45576
45810
  throw new InvalidArgumentError("invalid opts.path");
45577
45811
  }
45578
- let path71 = opts.path;
45812
+ let path72 = opts.path;
45579
45813
  if (!opts.path.startsWith("/")) {
45580
- path71 = `/${path71}`;
45814
+ path72 = `/${path72}`;
45581
45815
  }
45582
- url = new URL(util.parseOrigin(url).origin + path71);
45816
+ url = new URL(util.parseOrigin(url).origin + path72);
45583
45817
  } else {
45584
45818
  if (!opts) {
45585
45819
  opts = typeof url === "object" ? url : {};
@@ -45692,17 +45926,21 @@ ${captureLines}` : capture.stack;
45692
45926
  var tail_exports = {};
45693
45927
  __export(tail_exports, {
45694
45928
  agentLabel: () => agentLabel,
45929
+ eventsUrl: () => eventsUrl,
45695
45930
  sessionTag: () => sessionTag,
45696
45931
  shortenPathSummary: () => shortenPathSummary,
45697
45932
  startTail: () => startTail
45698
45933
  });
45699
45934
  import http5 from "http";
45700
45935
  import chalk40 from "chalk";
45701
- import fs72 from "fs";
45936
+ import fs73 from "fs";
45702
45937
  import os61 from "os";
45703
- import path68 from "path";
45938
+ import path69 from "path";
45704
45939
  import readline6 from "readline";
45705
45940
  import { spawn as spawn8 } from "child_process";
45941
+ function eventsUrl(port, canApprove) {
45942
+ return `http://127.0.0.1:${port}/events${canApprove ? "?capabilities=input" : ""}`;
45943
+ }
45706
45944
  function shortenPathSummary(s) {
45707
45945
  if (!s || !s.startsWith("/")) return s;
45708
45946
  const parts = s.split("/").filter(Boolean);
@@ -45724,20 +45962,20 @@ function getModelContextLimit(model) {
45724
45962
  return 2e5;
45725
45963
  }
45726
45964
  function readSessionUsage() {
45727
- const projectsDir = path68.join(os61.homedir(), ".claude", "projects");
45728
- if (!fs72.existsSync(projectsDir)) return null;
45965
+ const projectsDir = path69.join(os61.homedir(), ".claude", "projects");
45966
+ if (!fs73.existsSync(projectsDir)) return null;
45729
45967
  let latestFile = null;
45730
45968
  let latestMtime = 0;
45731
45969
  try {
45732
- for (const dir of fs72.readdirSync(projectsDir)) {
45733
- const dirPath = path68.join(projectsDir, dir);
45970
+ for (const dir of fs73.readdirSync(projectsDir)) {
45971
+ const dirPath = path69.join(projectsDir, dir);
45734
45972
  try {
45735
- if (!fs72.statSync(dirPath).isDirectory()) continue;
45736
- for (const file of fs72.readdirSync(dirPath)) {
45973
+ if (!fs73.statSync(dirPath).isDirectory()) continue;
45974
+ for (const file of fs73.readdirSync(dirPath)) {
45737
45975
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
45738
- const filePath = path68.join(dirPath, file);
45976
+ const filePath = path69.join(dirPath, file);
45739
45977
  try {
45740
- const mtime = fs72.statSync(filePath).mtimeMs;
45978
+ const mtime = fs73.statSync(filePath).mtimeMs;
45741
45979
  if (mtime > latestMtime) {
45742
45980
  latestMtime = mtime;
45743
45981
  latestFile = filePath;
@@ -45752,7 +45990,7 @@ function readSessionUsage() {
45752
45990
  }
45753
45991
  if (!latestFile) return null;
45754
45992
  try {
45755
- const lines = fs72.readFileSync(latestFile, "utf-8").split("\n");
45993
+ const lines = fs73.readFileSync(latestFile, "utf-8").split("\n");
45756
45994
  let lastModel = "";
45757
45995
  let lastInput = 0;
45758
45996
  let lastOutput = 0;
@@ -45852,9 +46090,9 @@ function renderPending(activity) {
45852
46090
  }
45853
46091
  async function ensureDaemon() {
45854
46092
  let pidPort = null;
45855
- if (fs72.existsSync(PID_FILE)) {
46093
+ if (fs73.existsSync(PID_FILE)) {
45856
46094
  try {
45857
- const { port } = JSON.parse(fs72.readFileSync(PID_FILE, "utf-8"));
46095
+ const { port } = JSON.parse(fs73.readFileSync(PID_FILE, "utf-8"));
45858
46096
  pidPort = port;
45859
46097
  } catch {
45860
46098
  console.error(chalk40.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -45880,7 +46118,7 @@ async function ensureDaemon() {
45880
46118
  child.unref();
45881
46119
  if (startupFd !== void 0) {
45882
46120
  try {
45883
- fs72.closeSync(startupFd);
46121
+ fs73.closeSync(startupFd);
45884
46122
  } catch {
45885
46123
  }
45886
46124
  }
@@ -46019,9 +46257,9 @@ function buildRecoveryCardLines(req) {
46019
46257
  ];
46020
46258
  }
46021
46259
  function readApproversFromDisk() {
46022
- const configPath = path68.join(os61.homedir(), ".node9", "config.json");
46260
+ const configPath = path69.join(os61.homedir(), ".node9", "config.json");
46023
46261
  try {
46024
- const raw = JSON.parse(fs72.readFileSync(configPath, "utf-8"));
46262
+ const raw = JSON.parse(fs73.readFileSync(configPath, "utf-8"));
46025
46263
  const settings = raw.settings ?? {};
46026
46264
  return settings.approvers ?? {};
46027
46265
  } catch {
@@ -46037,15 +46275,15 @@ function approverStatusLine() {
46037
46275
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
46038
46276
  }
46039
46277
  function toggleApprover(channel) {
46040
- const configPath = path68.join(os61.homedir(), ".node9", "config.json");
46278
+ const configPath = path69.join(os61.homedir(), ".node9", "config.json");
46041
46279
  try {
46042
- const raw = JSON.parse(fs72.readFileSync(configPath, "utf-8"));
46280
+ const raw = JSON.parse(fs73.readFileSync(configPath, "utf-8"));
46043
46281
  const settings = raw.settings ?? {};
46044
46282
  const approvers = settings.approvers ?? {};
46045
46283
  approvers[channel] = approvers[channel] === false;
46046
46284
  settings.approvers = approvers;
46047
46285
  raw.settings = settings;
46048
- fs72.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
46286
+ fs73.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
46049
46287
  } catch (err2) {
46050
46288
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
46051
46289
  `);
@@ -46217,8 +46455,8 @@ async function startTail(options = {}) {
46217
46455
  }
46218
46456
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
46219
46457
  try {
46220
- fs72.appendFileSync(
46221
- path68.join(os61.homedir(), ".node9", "hook-debug.log"),
46458
+ fs73.appendFileSync(
46459
+ path69.join(os61.homedir(), ".node9", "hook-debug.log"),
46222
46460
  `[tail] POST /decision failed: ${String(err2)}
46223
46461
  `
46224
46462
  );
@@ -46282,9 +46520,9 @@ async function startTail(options = {}) {
46282
46520
  };
46283
46521
  process.stdin.on("keypress", onKeypress);
46284
46522
  }
46285
- const auditLog = path68.join(os61.homedir(), ".node9", "audit.log");
46523
+ const auditLog = path69.join(os61.homedir(), ".node9", "audit.log");
46286
46524
  try {
46287
- const unackedDlp = fs72.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
46525
+ const unackedDlp = fs73.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
46288
46526
  if (unackedDlp > 0) {
46289
46527
  console.log("");
46290
46528
  console.log(
@@ -46324,7 +46562,7 @@ async function startTail(options = {}) {
46324
46562
  if (stallWarned) return;
46325
46563
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
46326
46564
  try {
46327
- const auditMtime = fs72.statSync(auditLog).mtimeMs;
46565
+ const auditMtime = fs73.statSync(auditLog).mtimeMs;
46328
46566
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
46329
46567
  console.log("");
46330
46568
  console.log(
@@ -46337,7 +46575,7 @@ async function startTail(options = {}) {
46337
46575
  }
46338
46576
  }, STALL_THRESHOLD_MS / 2);
46339
46577
  stallWatchdog.unref();
46340
- const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
46578
+ const sseUrl = eventsUrl(port, canApprove);
46341
46579
  const req = http5.get(
46342
46580
  sseUrl,
46343
46581
  {
@@ -46516,7 +46754,7 @@ var init_tail = __esm({
46516
46754
  init_startup_log();
46517
46755
  init_daemon2();
46518
46756
  init_daemon();
46519
- PID_FILE = path68.join(os61.homedir(), ".node9", "daemon.pid");
46757
+ PID_FILE = path69.join(os61.homedir(), ".node9", "daemon.pid");
46520
46758
  ICONS = {
46521
46759
  bash: "\u{1F4BB}",
46522
46760
  shell: "\u{1F4BB}",
@@ -46564,8 +46802,8 @@ __export(hud_exports, {
46564
46802
  main: () => main,
46565
46803
  renderEnvironmentLine: () => renderEnvironmentLine
46566
46804
  });
46567
- import fs73 from "fs";
46568
- import path69 from "path";
46805
+ import fs74 from "fs";
46806
+ import path70 from "path";
46569
46807
  import os62 from "os";
46570
46808
  import http6 from "http";
46571
46809
  async function readStdin() {
@@ -46642,9 +46880,9 @@ function formatTimeLeft(resetsAt) {
46642
46880
  return ` (${m}m left)`;
46643
46881
  }
46644
46882
  function safeReadJson(filePath) {
46645
- if (!fs73.existsSync(filePath)) return null;
46883
+ if (!fs74.existsSync(filePath)) return null;
46646
46884
  try {
46647
- return JSON.parse(fs73.readFileSync(filePath, "utf-8"));
46885
+ return JSON.parse(fs74.readFileSync(filePath, "utf-8"));
46648
46886
  } catch {
46649
46887
  return null;
46650
46888
  }
@@ -46665,12 +46903,12 @@ function countHooksInFile(filePath) {
46665
46903
  return Object.keys(cfg.hooks).length;
46666
46904
  }
46667
46905
  function countRulesInDir(rulesDir) {
46668
- if (!fs73.existsSync(rulesDir)) return 0;
46906
+ if (!fs74.existsSync(rulesDir)) return 0;
46669
46907
  let count = 0;
46670
46908
  try {
46671
- for (const entry of fs73.readdirSync(rulesDir, { withFileTypes: true })) {
46909
+ for (const entry of fs74.readdirSync(rulesDir, { withFileTypes: true })) {
46672
46910
  if (entry.isDirectory()) {
46673
- count += countRulesInDir(path69.join(rulesDir, entry.name));
46911
+ count += countRulesInDir(path70.join(rulesDir, entry.name));
46674
46912
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
46675
46913
  count++;
46676
46914
  }
@@ -46681,46 +46919,46 @@ function countRulesInDir(rulesDir) {
46681
46919
  }
46682
46920
  function isSamePath(a, b) {
46683
46921
  try {
46684
- return path69.resolve(a) === path69.resolve(b);
46922
+ return path70.resolve(a) === path70.resolve(b);
46685
46923
  } catch {
46686
46924
  return false;
46687
46925
  }
46688
46926
  }
46689
46927
  function countConfigs(cwd) {
46690
46928
  const homeDir2 = os62.homedir();
46691
- const claudeDir = path69.join(homeDir2, ".claude");
46929
+ const claudeDir = path70.join(homeDir2, ".claude");
46692
46930
  let claudeMdCount = 0;
46693
46931
  let rulesCount = 0;
46694
46932
  let hooksCount = 0;
46695
46933
  const userMcpServers = /* @__PURE__ */ new Set();
46696
46934
  const projectMcpServers = /* @__PURE__ */ new Set();
46697
- if (fs73.existsSync(path69.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
46698
- rulesCount += countRulesInDir(path69.join(claudeDir, "rules"));
46699
- const userSettings = path69.join(claudeDir, "settings.json");
46935
+ if (fs74.existsSync(path70.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
46936
+ rulesCount += countRulesInDir(path70.join(claudeDir, "rules"));
46937
+ const userSettings = path70.join(claudeDir, "settings.json");
46700
46938
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
46701
46939
  hooksCount += countHooksInFile(userSettings);
46702
- const userClaudeJson = path69.join(homeDir2, ".claude.json");
46940
+ const userClaudeJson = path70.join(homeDir2, ".claude.json");
46703
46941
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
46704
46942
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
46705
46943
  userMcpServers.delete(name);
46706
46944
  }
46707
46945
  if (cwd) {
46708
- if (fs73.existsSync(path69.join(cwd, "CLAUDE.md"))) claudeMdCount++;
46709
- if (fs73.existsSync(path69.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
46710
- const projectClaudeDir = path69.join(cwd, ".claude");
46946
+ if (fs74.existsSync(path70.join(cwd, "CLAUDE.md"))) claudeMdCount++;
46947
+ if (fs74.existsSync(path70.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
46948
+ const projectClaudeDir = path70.join(cwd, ".claude");
46711
46949
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
46712
46950
  if (!overlapsUserScope) {
46713
- if (fs73.existsSync(path69.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
46714
- rulesCount += countRulesInDir(path69.join(projectClaudeDir, "rules"));
46715
- const projSettings = path69.join(projectClaudeDir, "settings.json");
46951
+ if (fs74.existsSync(path70.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
46952
+ rulesCount += countRulesInDir(path70.join(projectClaudeDir, "rules"));
46953
+ const projSettings = path70.join(projectClaudeDir, "settings.json");
46716
46954
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
46717
46955
  hooksCount += countHooksInFile(projSettings);
46718
46956
  }
46719
- if (fs73.existsSync(path69.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
46720
- const localSettings = path69.join(projectClaudeDir, "settings.local.json");
46957
+ if (fs74.existsSync(path70.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
46958
+ const localSettings = path70.join(projectClaudeDir, "settings.local.json");
46721
46959
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
46722
46960
  hooksCount += countHooksInFile(localSettings);
46723
- const mcpJsonServers = getMcpServerNames(path69.join(cwd, ".mcp.json"));
46961
+ const mcpJsonServers = getMcpServerNames(path70.join(cwd, ".mcp.json"));
46724
46962
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
46725
46963
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
46726
46964
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -46753,12 +46991,12 @@ function readActiveShieldsHud() {
46753
46991
  return shieldsCache.value;
46754
46992
  }
46755
46993
  try {
46756
- const shieldsPath = path69.join(os62.homedir(), ".node9", "shields.json");
46757
- if (!fs73.existsSync(shieldsPath)) {
46994
+ const shieldsPath = path70.join(os62.homedir(), ".node9", "shields.json");
46995
+ if (!fs74.existsSync(shieldsPath)) {
46758
46996
  shieldsCache = { value: [], ts: now };
46759
46997
  return [];
46760
46998
  }
46761
- const parsed = JSON.parse(fs73.readFileSync(shieldsPath, "utf-8"));
46999
+ const parsed = JSON.parse(fs74.readFileSync(shieldsPath, "utf-8"));
46762
47000
  if (!Array.isArray(parsed.active)) {
46763
47001
  shieldsCache = { value: [], ts: now };
46764
47002
  return [];
@@ -46860,17 +47098,17 @@ function renderContextLine(stdin) {
46860
47098
  async function main() {
46861
47099
  try {
46862
47100
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
46863
- if (fs73.existsSync(path69.join(os62.homedir(), ".node9", "hud-debug"))) {
47101
+ if (fs74.existsSync(path70.join(os62.homedir(), ".node9", "hud-debug"))) {
46864
47102
  try {
46865
- const logPath = path69.join(os62.homedir(), ".node9", "hud-debug.log");
47103
+ const logPath = path70.join(os62.homedir(), ".node9", "hud-debug.log");
46866
47104
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
46867
47105
  let size = 0;
46868
47106
  try {
46869
- size = fs73.statSync(logPath).size;
47107
+ size = fs74.statSync(logPath).size;
46870
47108
  } catch {
46871
47109
  }
46872
47110
  if (size < MAX_LOG_SIZE) {
46873
- fs73.appendFileSync(
47111
+ fs74.appendFileSync(
46874
47112
  logPath,
46875
47113
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
46876
47114
  );
@@ -46891,11 +47129,11 @@ async function main() {
46891
47129
  try {
46892
47130
  const cwd = stdin.cwd ?? process.cwd();
46893
47131
  for (const configPath of [
46894
- path69.join(cwd, "node9.config.json"),
46895
- path69.join(os62.homedir(), ".node9", "config.json")
47132
+ path70.join(cwd, "node9.config.json"),
47133
+ path70.join(os62.homedir(), ".node9", "config.json")
46896
47134
  ]) {
46897
- if (!fs73.existsSync(configPath)) continue;
46898
- const cfg = JSON.parse(fs73.readFileSync(configPath, "utf-8"));
47135
+ if (!fs74.existsSync(configPath)) continue;
47136
+ const cfg = JSON.parse(fs74.readFileSync(configPath, "utf-8"));
46899
47137
  const hud = cfg.settings?.hud;
46900
47138
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
46901
47139
  }
@@ -47037,8 +47275,8 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
47037
47275
  // src/cli.ts
47038
47276
  init_daemon2();
47039
47277
  import chalk41 from "chalk";
47040
- import fs74 from "fs";
47041
- import path70 from "path";
47278
+ import fs75 from "fs";
47279
+ import path71 from "path";
47042
47280
  import os63 from "os";
47043
47281
  import { spawn as spawn9 } from "child_process";
47044
47282
  import { confirm as confirm2 } from "@inquirer/prompts";
@@ -47228,26 +47466,26 @@ async function runProxy(targetCommand) {
47228
47466
  init_daemon();
47229
47467
  init_startup_log();
47230
47468
  import { spawn as spawn3 } from "child_process";
47231
- import path42 from "path";
47232
- import fs44 from "fs";
47469
+ import path43 from "path";
47470
+ import fs45 from "fs";
47233
47471
  import os40 from "os";
47234
47472
  function isTestingMode() {
47235
47473
  return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
47236
47474
  }
47237
- var SKIP_STAMP = () => path42.join(os40.homedir(), ".node9", ".autostart-skip-stamp");
47475
+ var SKIP_STAMP = () => path43.join(os40.homedir(), ".node9", ".autostart-skip-stamp");
47238
47476
  var SKIP_THROTTLE_MS = 60 * 60 * 1e3;
47239
47477
  function logAutostartSkipThrottled(reason) {
47240
47478
  try {
47241
47479
  const stamp = SKIP_STAMP();
47242
47480
  try {
47243
- if (Date.now() - fs44.statSync(stamp).mtimeMs < SKIP_THROTTLE_MS) return;
47481
+ if (Date.now() - fs45.statSync(stamp).mtimeMs < SKIP_THROTTLE_MS) return;
47244
47482
  } catch {
47245
47483
  }
47246
- const dir = path42.join(os40.homedir(), ".node9");
47247
- if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
47248
- fs44.writeFileSync(stamp, "", "utf-8");
47249
- fs44.appendFileSync(
47250
- path42.join(dir, "hook-debug.log"),
47484
+ const dir = path43.join(os40.homedir(), ".node9");
47485
+ if (!fs45.existsSync(dir)) fs45.mkdirSync(dir, { recursive: true });
47486
+ fs45.writeFileSync(stamp, "", "utf-8");
47487
+ fs45.appendFileSync(
47488
+ path43.join(dir, "hook-debug.log"),
47251
47489
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-skip: ${reason}
47252
47490
  `,
47253
47491
  "utf-8"
@@ -47256,11 +47494,20 @@ function logAutostartSkipThrottled(reason) {
47256
47494
  }
47257
47495
  }
47258
47496
  async function autoStartDaemonAndWait() {
47497
+ let alreadyServing = false;
47498
+ try {
47499
+ alreadyServing = await isDaemonReachable();
47500
+ } catch {
47501
+ }
47502
+ if (alreadyServing) {
47503
+ logAutostartSkipThrottled("already-serving");
47504
+ return true;
47505
+ }
47259
47506
  if (isTestingMode()) return false;
47260
- if (!path42.isAbsolute(process.argv[1])) return false;
47507
+ if (!path43.isAbsolute(process.argv[1])) return false;
47261
47508
  let resolvedArgv1;
47262
47509
  try {
47263
- resolvedArgv1 = fs44.realpathSync(process.argv[1]);
47510
+ resolvedArgv1 = fs45.realpathSync(process.argv[1]);
47264
47511
  } catch {
47265
47512
  return false;
47266
47513
  }
@@ -47298,7 +47545,7 @@ async function autoStartDaemonAndWait() {
47298
47545
  } finally {
47299
47546
  if (startupFd !== void 0) {
47300
47547
  try {
47301
- fs44.closeSync(startupFd);
47548
+ fs45.closeSync(startupFd);
47302
47549
  } catch {
47303
47550
  }
47304
47551
  }
@@ -47317,19 +47564,19 @@ init_startup_log();
47317
47564
  init_config();
47318
47565
  init_policy();
47319
47566
  import chalk9 from "chalk";
47320
- import fs48 from "fs";
47567
+ import fs49 from "fs";
47321
47568
  import { spawn as spawn5 } from "child_process";
47322
- import path46 from "path";
47569
+ import path47 from "path";
47323
47570
  import os44 from "os";
47324
47571
 
47325
47572
  // src/undo.ts
47326
47573
  import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
47327
47574
  import crypto7 from "crypto";
47328
- import fs45 from "fs";
47575
+ import fs46 from "fs";
47329
47576
  import net3 from "net";
47330
- import path43 from "path";
47577
+ import path44 from "path";
47331
47578
  import os41 from "os";
47332
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path43.join(os41.tmpdir(), "node9-activity.sock");
47579
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path44.join(os41.tmpdir(), "node9-activity.sock");
47333
47580
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
47334
47581
  try {
47335
47582
  const payload = JSON.stringify({
@@ -47349,22 +47596,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
47349
47596
  } catch {
47350
47597
  }
47351
47598
  }
47352
- var SNAPSHOT_STACK_PATH = path43.join(os41.homedir(), ".node9", "snapshots.json");
47353
- var UNDO_LATEST_PATH = path43.join(os41.homedir(), ".node9", "undo_latest.txt");
47599
+ var SNAPSHOT_STACK_PATH = path44.join(os41.homedir(), ".node9", "snapshots.json");
47600
+ var UNDO_LATEST_PATH = path44.join(os41.homedir(), ".node9", "undo_latest.txt");
47354
47601
  var MAX_SNAPSHOTS = 10;
47355
47602
  var GIT_TIMEOUT = 15e3;
47356
47603
  function readStack() {
47357
47604
  try {
47358
- if (fs45.existsSync(SNAPSHOT_STACK_PATH))
47359
- return JSON.parse(fs45.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
47605
+ if (fs46.existsSync(SNAPSHOT_STACK_PATH))
47606
+ return JSON.parse(fs46.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
47360
47607
  } catch {
47361
47608
  }
47362
47609
  return [];
47363
47610
  }
47364
47611
  function writeStack(stack) {
47365
- const dir = path43.dirname(SNAPSHOT_STACK_PATH);
47366
- if (!fs45.existsSync(dir)) fs45.mkdirSync(dir, { recursive: true });
47367
- fs45.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
47612
+ const dir = path44.dirname(SNAPSHOT_STACK_PATH);
47613
+ if (!fs46.existsSync(dir)) fs46.mkdirSync(dir, { recursive: true });
47614
+ fs46.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
47368
47615
  }
47369
47616
  function extractFilePath(args) {
47370
47617
  if (!args || typeof args !== "object") return null;
@@ -47384,12 +47631,12 @@ function buildArgsSummary(tool, args) {
47384
47631
  return "";
47385
47632
  }
47386
47633
  function findProjectRoot(filePath) {
47387
- let dir = path43.dirname(filePath);
47634
+ let dir = path44.dirname(filePath);
47388
47635
  while (true) {
47389
- if (fs45.existsSync(path43.join(dir, ".git")) || fs45.existsSync(path43.join(dir, "package.json"))) {
47636
+ if (fs46.existsSync(path44.join(dir, ".git")) || fs46.existsSync(path44.join(dir, "package.json"))) {
47390
47637
  return dir;
47391
47638
  }
47392
- const parent = path43.dirname(dir);
47639
+ const parent = path44.dirname(dir);
47393
47640
  if (parent === dir) return process.cwd();
47394
47641
  dir = parent;
47395
47642
  }
@@ -47397,7 +47644,7 @@ function findProjectRoot(filePath) {
47397
47644
  function normalizeCwdForHash(cwd) {
47398
47645
  let normalized;
47399
47646
  try {
47400
- normalized = fs45.realpathSync(cwd);
47647
+ normalized = fs46.realpathSync(cwd);
47401
47648
  } catch {
47402
47649
  normalized = cwd;
47403
47650
  }
@@ -47407,16 +47654,16 @@ function normalizeCwdForHash(cwd) {
47407
47654
  }
47408
47655
  function getShadowRepoDir(cwd) {
47409
47656
  const hash = crypto7.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
47410
- return path43.join(os41.homedir(), ".node9", "snapshots", hash);
47657
+ return path44.join(os41.homedir(), ".node9", "snapshots", hash);
47411
47658
  }
47412
47659
  function cleanOrphanedIndexFiles(shadowDir) {
47413
47660
  try {
47414
47661
  const cutoff = Date.now() - 6e4;
47415
- for (const f of fs45.readdirSync(shadowDir)) {
47662
+ for (const f of fs46.readdirSync(shadowDir)) {
47416
47663
  if (f.startsWith("index_")) {
47417
- const fp = path43.join(shadowDir, f);
47664
+ const fp = path44.join(shadowDir, f);
47418
47665
  try {
47419
- if (fs45.statSync(fp).mtimeMs < cutoff) fs45.unlinkSync(fp);
47666
+ if (fs46.statSync(fp).mtimeMs < cutoff) fs46.unlinkSync(fp);
47420
47667
  } catch {
47421
47668
  }
47422
47669
  }
@@ -47428,7 +47675,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
47428
47675
  const hardcoded = [".git", ".node9"];
47429
47676
  const lines = [...hardcoded, ...ignorePaths].join("\n");
47430
47677
  try {
47431
- fs45.writeFileSync(path43.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
47678
+ fs46.writeFileSync(path44.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
47432
47679
  } catch {
47433
47680
  }
47434
47681
  }
@@ -47441,25 +47688,25 @@ function ensureShadowRepo(shadowDir, cwd) {
47441
47688
  timeout: 3e3
47442
47689
  });
47443
47690
  if (check.status === 0) {
47444
- const ptPath = path43.join(shadowDir, "project-path.txt");
47691
+ const ptPath = path44.join(shadowDir, "project-path.txt");
47445
47692
  try {
47446
- const stored = fs45.readFileSync(ptPath, "utf8").trim();
47693
+ const stored = fs46.readFileSync(ptPath, "utf8").trim();
47447
47694
  if (stored === normalizedCwd) return true;
47448
47695
  if (process.env.NODE9_DEBUG === "1")
47449
47696
  console.error(
47450
47697
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
47451
47698
  );
47452
- fs45.rmSync(shadowDir, { recursive: true, force: true });
47699
+ fs46.rmSync(shadowDir, { recursive: true, force: true });
47453
47700
  } catch {
47454
47701
  try {
47455
- fs45.writeFileSync(ptPath, normalizedCwd, "utf8");
47702
+ fs46.writeFileSync(ptPath, normalizedCwd, "utf8");
47456
47703
  } catch {
47457
47704
  }
47458
47705
  return true;
47459
47706
  }
47460
47707
  }
47461
47708
  try {
47462
- fs45.mkdirSync(shadowDir, { recursive: true });
47709
+ fs46.mkdirSync(shadowDir, { recursive: true });
47463
47710
  } catch {
47464
47711
  }
47465
47712
  const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -47468,7 +47715,7 @@ function ensureShadowRepo(shadowDir, cwd) {
47468
47715
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
47469
47716
  return false;
47470
47717
  }
47471
- const configFile = path43.join(shadowDir, "config");
47718
+ const configFile = path44.join(shadowDir, "config");
47472
47719
  spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
47473
47720
  timeout: 3e3
47474
47721
  });
@@ -47476,7 +47723,7 @@ function ensureShadowRepo(shadowDir, cwd) {
47476
47723
  timeout: 3e3
47477
47724
  });
47478
47725
  try {
47479
- fs45.writeFileSync(path43.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
47726
+ fs46.writeFileSync(path44.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
47480
47727
  } catch {
47481
47728
  }
47482
47729
  return true;
@@ -47499,12 +47746,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
47499
47746
  let indexFile = null;
47500
47747
  try {
47501
47748
  const rawFilePath = extractFilePath(args);
47502
- const absFilePath = rawFilePath && path43.isAbsolute(rawFilePath) ? rawFilePath : null;
47749
+ const absFilePath = rawFilePath && path44.isAbsolute(rawFilePath) ? rawFilePath : null;
47503
47750
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
47504
47751
  const shadowDir = getShadowRepoDir(cwd);
47505
47752
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
47506
47753
  writeShadowExcludes(shadowDir, ignorePaths);
47507
- indexFile = path43.join(shadowDir, `index_${process.pid}_${Date.now()}`);
47754
+ indexFile = path44.join(shadowDir, `index_${process.pid}_${Date.now()}`);
47508
47755
  const shadowEnv = {
47509
47756
  ...process.env,
47510
47757
  GIT_DIR: shadowDir,
@@ -47576,7 +47823,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
47576
47823
  writeStack(stack);
47577
47824
  const entry = stack[stack.length - 1];
47578
47825
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
47579
- fs45.writeFileSync(UNDO_LATEST_PATH, commitHash);
47826
+ fs46.writeFileSync(UNDO_LATEST_PATH, commitHash);
47580
47827
  if (shouldGc) {
47581
47828
  spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
47582
47829
  }
@@ -47587,7 +47834,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
47587
47834
  } finally {
47588
47835
  if (indexFile) {
47589
47836
  try {
47590
- fs45.unlinkSync(indexFile);
47837
+ fs46.unlinkSync(indexFile);
47591
47838
  } catch {
47592
47839
  }
47593
47840
  }
@@ -47663,9 +47910,9 @@ function applyUndo(hash, cwd) {
47663
47910
  timeout: GIT_TIMEOUT
47664
47911
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
47665
47912
  for (const file of [...tracked, ...untracked]) {
47666
- const fullPath = path43.join(dir, file);
47667
- if (!snapshotFiles.has(file) && fs45.existsSync(fullPath)) {
47668
- fs45.unlinkSync(fullPath);
47913
+ const fullPath = path44.join(dir, file);
47914
+ if (!snapshotFiles.has(file) && fs46.existsSync(fullPath)) {
47915
+ fs46.unlinkSync(fullPath);
47669
47916
  }
47670
47917
  }
47671
47918
  return true;
@@ -47675,12 +47922,12 @@ function applyUndo(hash, cwd) {
47675
47922
  }
47676
47923
 
47677
47924
  // src/skill-pin.ts
47678
- import fs46 from "fs";
47679
- import path44 from "path";
47925
+ import fs47 from "fs";
47926
+ import path45 from "path";
47680
47927
  import os42 from "os";
47681
47928
  import crypto8 from "crypto";
47682
47929
  function getPinsFilePath2() {
47683
- return path44.join(os42.homedir(), ".node9", "skill-pins.json");
47930
+ return path45.join(os42.homedir(), ".node9", "skill-pins.json");
47684
47931
  }
47685
47932
  var MAX_FILES = 5e3;
47686
47933
  var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
@@ -47694,18 +47941,18 @@ function walkDir(root) {
47694
47941
  if (out.length >= MAX_FILES) return;
47695
47942
  let entries;
47696
47943
  try {
47697
- entries = fs46.readdirSync(dir, { withFileTypes: true });
47944
+ entries = fs47.readdirSync(dir, { withFileTypes: true });
47698
47945
  } catch {
47699
47946
  return;
47700
47947
  }
47701
47948
  entries.sort((a, b) => a.name.localeCompare(b.name));
47702
47949
  for (const entry of entries) {
47703
47950
  if (out.length >= MAX_FILES) return;
47704
- const full = path44.join(dir, entry.name);
47705
- const rel = relDir ? path44.posix.join(relDir, entry.name) : entry.name;
47951
+ const full = path45.join(dir, entry.name);
47952
+ const rel = relDir ? path45.posix.join(relDir, entry.name) : entry.name;
47706
47953
  let lst;
47707
47954
  try {
47708
- lst = fs46.lstatSync(full);
47955
+ lst = fs47.lstatSync(full);
47709
47956
  } catch {
47710
47957
  continue;
47711
47958
  }
@@ -47717,7 +47964,7 @@ function walkDir(root) {
47717
47964
  if (!lst.isFile()) continue;
47718
47965
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
47719
47966
  try {
47720
- const buf = fs46.readFileSync(full);
47967
+ const buf = fs47.readFileSync(full);
47721
47968
  totalBytes += buf.length;
47722
47969
  out.push({ rel, hash: sha256Bytes(buf) });
47723
47970
  } catch {
@@ -47731,14 +47978,14 @@ function walkDir(root) {
47731
47978
  function hashSkillRoot(absPath) {
47732
47979
  let lst;
47733
47980
  try {
47734
- lst = fs46.lstatSync(absPath);
47981
+ lst = fs47.lstatSync(absPath);
47735
47982
  } catch {
47736
47983
  return { exists: false, contentHash: "", fileCount: 0 };
47737
47984
  }
47738
47985
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
47739
47986
  if (lst.isFile()) {
47740
47987
  try {
47741
- return { exists: true, contentHash: sha256Bytes(fs46.readFileSync(absPath)), fileCount: 1 };
47988
+ return { exists: true, contentHash: sha256Bytes(fs47.readFileSync(absPath)), fileCount: 1 };
47742
47989
  } catch {
47743
47990
  return { exists: false, contentHash: "", fileCount: 0 };
47744
47991
  }
@@ -47756,7 +48003,7 @@ function getRootKey(absPath) {
47756
48003
  function readSkillPinsSafe() {
47757
48004
  const filePath = getPinsFilePath2();
47758
48005
  try {
47759
- const raw = fs46.readFileSync(filePath, "utf-8");
48006
+ const raw = fs47.readFileSync(filePath, "utf-8");
47760
48007
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
47761
48008
  const parsed = JSON.parse(raw);
47762
48009
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -47776,10 +48023,10 @@ function readSkillPins() {
47776
48023
  }
47777
48024
  function writeSkillPins(data) {
47778
48025
  const filePath = getPinsFilePath2();
47779
- fs46.mkdirSync(path44.dirname(filePath), { recursive: true });
48026
+ fs47.mkdirSync(path45.dirname(filePath), { recursive: true });
47780
48027
  const tmp = `${filePath}.${crypto8.randomBytes(6).toString("hex")}.tmp`;
47781
- fs46.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
47782
- fs46.renameSync(tmp, filePath);
48028
+ fs47.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
48029
+ fs47.renameSync(tmp, filePath);
47783
48030
  }
47784
48031
  function removePin2(rootKey) {
47785
48032
  const pins = readSkillPins();
@@ -47823,36 +48070,36 @@ function verifyAndPinRoots(roots) {
47823
48070
  return { kind: "verified" };
47824
48071
  }
47825
48072
  function defaultSkillRoots(_cwd) {
47826
- const marketplaces = path44.join(os42.homedir(), ".claude", "plugins", "marketplaces");
48073
+ const marketplaces = path45.join(os42.homedir(), ".claude", "plugins", "marketplaces");
47827
48074
  const roots = [];
47828
48075
  let registries;
47829
48076
  try {
47830
- registries = fs46.readdirSync(marketplaces, { withFileTypes: true });
48077
+ registries = fs47.readdirSync(marketplaces, { withFileTypes: true });
47831
48078
  } catch {
47832
48079
  return [];
47833
48080
  }
47834
48081
  for (const registry of registries) {
47835
48082
  if (!registry.isDirectory()) continue;
47836
- const pluginsDir = path44.join(marketplaces, registry.name, "plugins");
48083
+ const pluginsDir = path45.join(marketplaces, registry.name, "plugins");
47837
48084
  let plugins;
47838
48085
  try {
47839
- plugins = fs46.readdirSync(pluginsDir, { withFileTypes: true });
48086
+ plugins = fs47.readdirSync(pluginsDir, { withFileTypes: true });
47840
48087
  } catch {
47841
48088
  continue;
47842
48089
  }
47843
48090
  for (const plugin of plugins) {
47844
48091
  if (!plugin.isDirectory()) continue;
47845
- roots.push(path44.join(pluginsDir, plugin.name));
48092
+ roots.push(path45.join(pluginsDir, plugin.name));
47846
48093
  }
47847
48094
  }
47848
48095
  return roots;
47849
48096
  }
47850
48097
  function resolveUserSkillRoot(entry, cwd) {
47851
48098
  if (!entry) return null;
47852
- if (entry.startsWith("~/") || entry === "~") return path44.join(os42.homedir(), entry.slice(1));
47853
- if (path44.isAbsolute(entry)) return entry;
47854
- if (!cwd || !path44.isAbsolute(cwd)) return null;
47855
- return path44.join(cwd, entry);
48099
+ if (entry.startsWith("~/") || entry === "~") return path45.join(os42.homedir(), entry.slice(1));
48100
+ if (path45.isAbsolute(entry)) return entry;
48101
+ if (!cwd || !path45.isAbsolute(cwd)) return null;
48102
+ return path45.join(cwd, entry);
47856
48103
  }
47857
48104
 
47858
48105
  // src/cli/commands/check.ts
@@ -47861,11 +48108,11 @@ init_audit();
47861
48108
 
47862
48109
  // src/review-pending.ts
47863
48110
  init_hasher();
47864
- import fs47 from "fs";
48111
+ import fs48 from "fs";
47865
48112
  import os43 from "os";
47866
- import path45 from "path";
48113
+ import path46 from "path";
47867
48114
  function storePath() {
47868
- return process.env.NODE9_PENDING_STORE || path45.join(os43.homedir(), ".node9", "pending-reviews.json");
48115
+ return process.env.NODE9_PENDING_STORE || path46.join(os43.homedir(), ".node9", "pending-reviews.json");
47869
48116
  }
47870
48117
  var TTL_MS2 = 6 * 60 * 60 * 1e3;
47871
48118
  var MAX_ENTRIES = 500;
@@ -47882,7 +48129,7 @@ function reviewCorrelationKey(payload) {
47882
48129
  }
47883
48130
  function read() {
47884
48131
  try {
47885
- const parsed = JSON.parse(fs47.readFileSync(storePath(), "utf-8"));
48132
+ const parsed = JSON.parse(fs48.readFileSync(storePath(), "utf-8"));
47886
48133
  if (parsed && Array.isArray(parsed.entries)) return parsed;
47887
48134
  } catch {
47888
48135
  }
@@ -47891,11 +48138,11 @@ function read() {
47891
48138
  function write(store) {
47892
48139
  try {
47893
48140
  const p = storePath();
47894
- const dir = path45.dirname(p);
47895
- if (!fs47.existsSync(dir)) fs47.mkdirSync(dir, { recursive: true });
48141
+ const dir = path46.dirname(p);
48142
+ if (!fs48.existsSync(dir)) fs48.mkdirSync(dir, { recursive: true });
47896
48143
  const tmp = `${p}.${process.pid}.tmp`;
47897
- fs47.writeFileSync(tmp, JSON.stringify(store));
47898
- fs47.renameSync(tmp, p);
48144
+ fs48.writeFileSync(tmp, JSON.stringify(store));
48145
+ fs48.renameSync(tmp, p);
47899
48146
  } catch {
47900
48147
  }
47901
48148
  }
@@ -48008,9 +48255,9 @@ function registerCheckCommand(program2) {
48008
48255
  } catch (err2) {
48009
48256
  const tempConfig = getConfig();
48010
48257
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
48011
- const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
48258
+ const logPath = path47.join(os44.homedir(), ".node9", "hook-debug.log");
48012
48259
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
48013
- fs48.appendFileSync(
48260
+ fs49.appendFileSync(
48014
48261
  logPath,
48015
48262
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
48016
48263
  RAW: ${raw}
@@ -48023,14 +48270,14 @@ RAW: ${raw}
48023
48270
  const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
48024
48271
  if (process.env.NODE9_DEBUG === "1") {
48025
48272
  try {
48026
- const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
48027
- if (!fs48.existsSync(path46.dirname(logPath)))
48028
- fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
48273
+ const logPath = path47.join(os44.homedir(), ".node9", "hook-debug.log");
48274
+ if (!fs49.existsSync(path47.dirname(logPath)))
48275
+ fs49.mkdirSync(path47.dirname(logPath), { recursive: true });
48029
48276
  const sanitized = JSON.stringify({
48030
48277
  ...payload,
48031
48278
  prompt: `<redacted, ${prompt.length} bytes>`
48032
48279
  });
48033
- fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
48280
+ fs49.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
48034
48281
  `);
48035
48282
  } catch {
48036
48283
  }
@@ -48051,8 +48298,8 @@ RAW: ${raw}
48051
48298
  );
48052
48299
  const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
48053
48300
  try {
48054
- const ttyFd = fs48.openSync("/dev/tty", "w");
48055
- fs48.writeSync(
48301
+ const ttyFd = fs49.openSync("/dev/tty", "w");
48302
+ fs49.writeSync(
48056
48303
  ttyFd,
48057
48304
  chalk9.bgRed.white.bold(`
48058
48305
  \u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
@@ -48062,7 +48309,7 @@ RAW: ${raw}
48062
48309
 
48063
48310
  `)
48064
48311
  );
48065
- fs48.closeSync(ttyFd);
48312
+ fs49.closeSync(ttyFd);
48066
48313
  } catch {
48067
48314
  }
48068
48315
  const isCodex = agent2 === "Codex";
@@ -48081,17 +48328,17 @@ RAW: ${raw}
48081
48328
  process.exit(2);
48082
48329
  }
48083
48330
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
48084
- const safeCwdForConfig = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48331
+ const safeCwdForConfig = typeof payloadCwd === "string" && path47.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48085
48332
  const config = getConfig(safeCwdForConfig);
48086
48333
  const daemonDown = !isDaemonRunning();
48087
48334
  if (config.settings.autoStartDaemon && daemonDown && !process.env.NODE9_NO_AUTO_DAEMON) {
48088
48335
  try {
48089
48336
  const scriptPath = process.argv[1];
48090
- if (typeof scriptPath !== "string" || !path46.isAbsolute(scriptPath))
48337
+ if (typeof scriptPath !== "string" || !path47.isAbsolute(scriptPath))
48091
48338
  throw new Error("node9: argv[1] is not an absolute path");
48092
- const resolvedScript = fs48.realpathSync(scriptPath);
48093
- const packageDist = fs48.realpathSync(path46.resolve(__dirname, "../.."));
48094
- if (!resolvedScript.startsWith(packageDist + path46.sep) && resolvedScript !== packageDist)
48339
+ const resolvedScript = fs49.realpathSync(scriptPath);
48340
+ const packageDist = fs49.realpathSync(path47.resolve(__dirname, "../.."));
48341
+ if (!resolvedScript.startsWith(packageDist + path47.sep) && resolvedScript !== packageDist)
48095
48342
  throw new Error(
48096
48343
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
48097
48344
  );
@@ -48121,17 +48368,17 @@ RAW: ${raw}
48121
48368
  } finally {
48122
48369
  if (startupFd !== void 0) {
48123
48370
  try {
48124
- fs48.closeSync(startupFd);
48371
+ fs49.closeSync(startupFd);
48125
48372
  } catch {
48126
48373
  }
48127
48374
  }
48128
48375
  }
48129
48376
  } catch (spawnErr) {
48130
- const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
48377
+ const logPath = path47.join(os44.homedir(), ".node9", "hook-debug.log");
48131
48378
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
48132
48379
  recordStartupState("failed", "spawn-aborted", msg);
48133
48380
  try {
48134
- fs48.appendFileSync(
48381
+ fs49.appendFileSync(
48135
48382
  logPath,
48136
48383
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
48137
48384
  `
@@ -48145,10 +48392,10 @@ RAW: ${raw}
48145
48392
  );
48146
48393
  }
48147
48394
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
48148
- const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
48149
- if (!fs48.existsSync(path46.dirname(logPath)))
48150
- fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
48151
- fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
48395
+ const logPath = path47.join(os44.homedir(), ".node9", "hook-debug.log");
48396
+ if (!fs49.existsSync(path47.dirname(logPath)))
48397
+ fs49.mkdirSync(path47.dirname(logPath), { recursive: true });
48398
+ fs49.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
48152
48399
  `);
48153
48400
  }
48154
48401
  const rawToolName = sanitize2(extractToolName(payload));
@@ -48162,8 +48409,8 @@ RAW: ${raw}
48162
48409
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
48163
48410
  let ttyFd = null;
48164
48411
  try {
48165
- ttyFd = fs48.openSync("/dev/tty", "w");
48166
- const writeTty = (line) => fs48.writeSync(ttyFd, line + "\n");
48412
+ ttyFd = fs49.openSync("/dev/tty", "w");
48413
+ const writeTty = (line) => fs49.writeSync(ttyFd, line + "\n");
48167
48414
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
48168
48415
  writeTty(chalk9.bgRed.white.bold(`
48169
48416
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -48182,7 +48429,7 @@ RAW: ${raw}
48182
48429
  } finally {
48183
48430
  if (ttyFd !== null)
48184
48431
  try {
48185
- fs48.closeSync(ttyFd);
48432
+ fs49.closeSync(ttyFd);
48186
48433
  } catch {
48187
48434
  }
48188
48435
  }
@@ -48239,8 +48486,8 @@ RAW: ${raw}
48239
48486
  } catch {
48240
48487
  }
48241
48488
  try {
48242
- const ttyFd = fs48.openSync("/dev/tty", "w");
48243
- fs48.writeSync(
48489
+ const ttyFd = fs49.openSync("/dev/tty", "w");
48490
+ fs49.writeSync(
48244
48491
  ttyFd,
48245
48492
  chalk9.yellow(
48246
48493
  `
@@ -48248,7 +48495,7 @@ RAW: ${raw}
48248
48495
  `
48249
48496
  )
48250
48497
  );
48251
- fs48.closeSync(ttyFd);
48498
+ fs49.closeSync(ttyFd);
48252
48499
  } catch {
48253
48500
  }
48254
48501
  if (agent === "GitHub Copilot") {
@@ -48280,17 +48527,17 @@ RAW: ${raw}
48280
48527
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
48281
48528
  if (skillPinCfg.enabled && safeSessionId) {
48282
48529
  try {
48283
- const sessionsDir = path46.join(os44.homedir(), ".node9", "skill-sessions");
48284
- const flagPath = path46.join(sessionsDir, `${safeSessionId}.json`);
48530
+ const sessionsDir = path47.join(os44.homedir(), ".node9", "skill-sessions");
48531
+ const flagPath = path47.join(sessionsDir, `${safeSessionId}.json`);
48285
48532
  let flag = null;
48286
48533
  try {
48287
- flag = JSON.parse(fs48.readFileSync(flagPath, "utf-8"));
48534
+ flag = JSON.parse(fs49.readFileSync(flagPath, "utf-8"));
48288
48535
  } catch {
48289
48536
  }
48290
48537
  const writeFlag = (data2) => {
48291
48538
  try {
48292
- fs48.mkdirSync(sessionsDir, { recursive: true });
48293
- fs48.writeFileSync(
48539
+ fs49.mkdirSync(sessionsDir, { recursive: true });
48540
+ fs49.writeFileSync(
48294
48541
  flagPath,
48295
48542
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
48296
48543
  { mode: 384 }
@@ -48301,8 +48548,8 @@ RAW: ${raw}
48301
48548
  const sendSkillWarn = (detail, recoveryCmd) => {
48302
48549
  let ttyFd = null;
48303
48550
  try {
48304
- ttyFd = fs48.openSync("/dev/tty", "w");
48305
- const w = (line) => fs48.writeSync(ttyFd, line + "\n");
48551
+ ttyFd = fs49.openSync("/dev/tty", "w");
48552
+ const w = (line) => fs49.writeSync(ttyFd, line + "\n");
48306
48553
  w(chalk9.yellow(`
48307
48554
  \u26A0\uFE0F Node9: installed skill drift detected`));
48308
48555
  w(chalk9.gray(` ${detail}`));
@@ -48317,7 +48564,7 @@ RAW: ${raw}
48317
48564
  } finally {
48318
48565
  if (ttyFd !== null)
48319
48566
  try {
48320
- fs48.closeSync(ttyFd);
48567
+ fs49.closeSync(ttyFd);
48321
48568
  } catch {
48322
48569
  }
48323
48570
  }
@@ -48333,7 +48580,7 @@ RAW: ${raw}
48333
48580
  return;
48334
48581
  }
48335
48582
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
48336
- const absoluteCwd = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48583
+ const absoluteCwd = typeof payloadCwd === "string" && path47.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48337
48584
  const extraRoots = skillPinCfg.roots;
48338
48585
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
48339
48586
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -48374,10 +48621,10 @@ RAW: ${raw}
48374
48621
  }
48375
48622
  try {
48376
48623
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
48377
- for (const name of fs48.readdirSync(sessionsDir)) {
48378
- const p = path46.join(sessionsDir, name);
48624
+ for (const name of fs49.readdirSync(sessionsDir)) {
48625
+ const p = path47.join(sessionsDir, name);
48379
48626
  try {
48380
- if (fs48.statSync(p).mtimeMs < cutoff) fs48.unlinkSync(p);
48627
+ if (fs49.statSync(p).mtimeMs < cutoff) fs49.unlinkSync(p);
48381
48628
  } catch {
48382
48629
  }
48383
48630
  }
@@ -48387,9 +48634,9 @@ RAW: ${raw}
48387
48634
  } catch (err2) {
48388
48635
  if (process.env.NODE9_DEBUG === "1") {
48389
48636
  try {
48390
- const dbg = path46.join(os44.homedir(), ".node9", "hook-debug.log");
48637
+ const dbg = path47.join(os44.homedir(), ".node9", "hook-debug.log");
48391
48638
  const msg = err2 instanceof Error ? err2.message : String(err2);
48392
- fs48.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
48639
+ fs49.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
48393
48640
  `);
48394
48641
  } catch {
48395
48642
  }
@@ -48399,7 +48646,7 @@ RAW: ${raw}
48399
48646
  if (shouldSnapshot(toolName, toolInput, config)) {
48400
48647
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
48401
48648
  }
48402
- const safeCwdForAuth = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48649
+ const safeCwdForAuth = typeof payloadCwd === "string" && path47.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48403
48650
  const askMode = resolveAskMode(agent, opts, config);
48404
48651
  const result = await authorizeHeadless(toolName, toolInput, meta, {
48405
48652
  cwd: safeCwdForAuth,
@@ -48417,12 +48664,12 @@ RAW: ${raw}
48417
48664
  }
48418
48665
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
48419
48666
  try {
48420
- const tty = fs48.openSync("/dev/tty", "w");
48421
- fs48.writeSync(
48667
+ const tty = fs49.openSync("/dev/tty", "w");
48668
+ fs49.writeSync(
48422
48669
  tty,
48423
48670
  chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
48424
48671
  );
48425
- fs48.closeSync(tty);
48672
+ fs49.closeSync(tty);
48426
48673
  } catch {
48427
48674
  }
48428
48675
  const daemonReady = await autoStartDaemonAndWait();
@@ -48449,9 +48696,9 @@ RAW: ${raw}
48449
48696
  });
48450
48697
  } catch (err2) {
48451
48698
  if (process.env.NODE9_DEBUG === "1") {
48452
- const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
48699
+ const logPath = path47.join(os44.homedir(), ".node9", "hook-debug.log");
48453
48700
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
48454
- fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
48701
+ fs49.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
48455
48702
  `);
48456
48703
  }
48457
48704
  process.exit(0);
@@ -48487,8 +48734,8 @@ RAW: ${raw}
48487
48734
  // src/cli/commands/log.ts
48488
48735
  init_audit();
48489
48736
  init_config();
48490
- import fs49 from "fs";
48491
- import path47 from "path";
48737
+ import fs50 from "fs";
48738
+ import path48 from "path";
48492
48739
  import os45 from "os";
48493
48740
  init_daemon();
48494
48741
  init_dlp();
@@ -48597,10 +48844,10 @@ function registerLogCommand(program2) {
48597
48844
  if (rawToolName !== tool) entry.agentToolName = rawToolName;
48598
48845
  const payloadSessionId = payload.session_id ?? payload.conversationId;
48599
48846
  if (payloadSessionId) entry.sessionId = payloadSessionId;
48600
- const logPath = path47.join(os45.homedir(), ".node9", "audit.log");
48601
- if (!fs49.existsSync(path47.dirname(logPath)))
48602
- fs49.mkdirSync(path47.dirname(logPath), { recursive: true });
48603
- fs49.appendFileSync(logPath, JSON.stringify(entry) + "\n");
48847
+ const logPath = path48.join(os45.homedir(), ".node9", "audit.log");
48848
+ if (!fs50.existsSync(path48.dirname(logPath)))
48849
+ fs50.mkdirSync(path48.dirname(logPath), { recursive: true });
48850
+ fs50.appendFileSync(logPath, JSON.stringify(entry) + "\n");
48604
48851
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
48605
48852
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
48606
48853
  if (command) {
@@ -48634,7 +48881,7 @@ function registerLogCommand(program2) {
48634
48881
  }
48635
48882
  }
48636
48883
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
48637
- const safeCwd = typeof payloadCwd === "string" && path47.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48884
+ const safeCwd = typeof payloadCwd === "string" && path48.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48638
48885
  const config = getConfig(safeCwd);
48639
48886
  {
48640
48887
  const toolOutput = payload.tool_response?.output;
@@ -48711,9 +48958,9 @@ function registerLogCommand(program2) {
48711
48958
  const msg = err2 instanceof Error ? err2.message : String(err2);
48712
48959
  process.stderr.write(`[Node9] audit log error: ${msg}
48713
48960
  `);
48714
- const debugPath = path47.join(os45.homedir(), ".node9", "hook-debug.log");
48961
+ const debugPath = path48.join(os45.homedir(), ".node9", "hook-debug.log");
48715
48962
  try {
48716
- fs49.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
48963
+ fs50.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
48717
48964
  `);
48718
48965
  } catch {
48719
48966
  }
@@ -48740,16 +48987,16 @@ function registerLogCommand(program2) {
48740
48987
  init_shields();
48741
48988
  init_build();
48742
48989
  import chalk10 from "chalk";
48743
- import fs51 from "fs";
48744
- import path49 from "path";
48990
+ import fs52 from "fs";
48991
+ import path50 from "path";
48745
48992
  import os46 from "os";
48746
48993
 
48747
48994
  // src/shields/create.ts
48748
48995
  init_dist();
48749
48996
  init_shields();
48750
48997
  init_audit();
48751
- import fs50 from "fs";
48752
- import path48 from "path";
48998
+ import fs51 from "fs";
48999
+ import path49 from "path";
48753
49000
  function builtinNames() {
48754
49001
  const names = /* @__PURE__ */ new Set();
48755
49002
  for (const def of Object.values(BUILTIN_SHIELDS)) {
@@ -48766,8 +49013,8 @@ function createShield(def, opts = {}) {
48766
49013
  error: `"${name}" is a built-in shield \u2014 choose a different name (a user shield with this name would shadow the built-in).`
48767
49014
  };
48768
49015
  }
48769
- const filePath = path48.join(USER_SHIELDS_DIR_PATH, `${name}.json`);
48770
- if (!opts.overwrite && fs50.existsSync(filePath)) {
49016
+ const filePath = path49.join(USER_SHIELDS_DIR_PATH, `${name}.json`);
49017
+ if (!opts.overwrite && fs51.existsSync(filePath)) {
48771
49018
  return {
48772
49019
  ok: false,
48773
49020
  error: `Shield "${name}" already exists at ${filePath}. Pass --overwrite to replace it.`
@@ -48832,8 +49079,8 @@ var COMMUNITY_INDEX_URL = "https://raw.githubusercontent.com/node9ai/node9-proxy
48832
49079
  function readCloudShields() {
48833
49080
  const out = /* @__PURE__ */ new Set();
48834
49081
  try {
48835
- const file = path49.join(os46.homedir(), ".node9", "rules-cache.json");
48836
- const raw = JSON.parse(fs51.readFileSync(file, "utf-8"));
49082
+ const file = path50.join(os46.homedir(), ".node9", "rules-cache.json");
49083
+ const raw = JSON.parse(fs52.readFileSync(file, "utf-8"));
48837
49084
  for (const r of raw.rules ?? []) {
48838
49085
  const rule = r;
48839
49086
  const fromSource = rule.source?.startsWith("SHIELD:") ? rule.source.slice("SHIELD:".length).toLowerCase() : void 0;
@@ -49150,7 +49397,7 @@ function registerShieldCommand(program2) {
49150
49397
  if (opts.fromFile) {
49151
49398
  let raw;
49152
49399
  try {
49153
- raw = JSON.parse(fs51.readFileSync(opts.fromFile, "utf-8"));
49400
+ raw = JSON.parse(fs52.readFileSync(opts.fromFile, "utf-8"));
49154
49401
  } catch (err2) {
49155
49402
  console.error(
49156
49403
  chalk10.red(`
@@ -49270,13 +49517,14 @@ function registerConfigShowCommand(program2) {
49270
49517
 
49271
49518
  // src/cli/commands/doctor.ts
49272
49519
  init_daemon();
49520
+ init_build_id();
49273
49521
  init_config();
49274
49522
  init_agent_wiring();
49275
49523
  init_sync();
49276
49524
  init_service();
49277
49525
  import chalk11 from "chalk";
49278
- import fs52 from "fs";
49279
- import path50 from "path";
49526
+ import fs53 from "fs";
49527
+ import path51 from "path";
49280
49528
  import os47 from "os";
49281
49529
  import { execSync } from "child_process";
49282
49530
 
@@ -49343,10 +49591,10 @@ function registerDoctorCommand(program2, version2) {
49343
49591
  );
49344
49592
  }
49345
49593
  section("Configuration");
49346
- const globalConfigPath = path50.join(homeDir2, ".node9", "config.json");
49347
- if (fs52.existsSync(globalConfigPath)) {
49594
+ const globalConfigPath = path51.join(homeDir2, ".node9", "config.json");
49595
+ if (fs53.existsSync(globalConfigPath)) {
49348
49596
  try {
49349
- JSON.parse(fs52.readFileSync(globalConfigPath, "utf-8"));
49597
+ JSON.parse(fs53.readFileSync(globalConfigPath, "utf-8"));
49350
49598
  pass("~/.node9/config.json found and valid");
49351
49599
  } catch {
49352
49600
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -49354,10 +49602,10 @@ function registerDoctorCommand(program2, version2) {
49354
49602
  } else {
49355
49603
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
49356
49604
  }
49357
- const projectConfigPath = path50.join(process.cwd(), "node9.config.json");
49358
- if (fs52.existsSync(projectConfigPath)) {
49605
+ const projectConfigPath = path51.join(process.cwd(), "node9.config.json");
49606
+ if (fs53.existsSync(projectConfigPath)) {
49359
49607
  try {
49360
- JSON.parse(fs52.readFileSync(projectConfigPath, "utf-8"));
49608
+ JSON.parse(fs53.readFileSync(projectConfigPath, "utf-8"));
49361
49609
  pass("node9.config.json found and valid (project)");
49362
49610
  } catch {
49363
49611
  fail(
@@ -49366,8 +49614,8 @@ function registerDoctorCommand(program2, version2) {
49366
49614
  );
49367
49615
  }
49368
49616
  }
49369
- const credsPath = path50.join(homeDir2, ".node9", "credentials.json");
49370
- if (fs52.existsSync(credsPath)) {
49617
+ const credsPath = path51.join(homeDir2, ".node9", "credentials.json");
49618
+ if (fs53.existsSync(credsPath)) {
49371
49619
  pass("Cloud credentials found (~/.node9/credentials.json)");
49372
49620
  } else {
49373
49621
  warn(
@@ -49401,6 +49649,17 @@ function registerDoctorCommand(program2, version2) {
49401
49649
  pass(
49402
49650
  `Daemon running on ${DAEMON_HOST}:${DAEMON_PORT} \u2014 terminal & native approvals enabled`
49403
49651
  );
49652
+ const probe = await probeDaemonHealth();
49653
+ const drift = describeBuildDrift(
49654
+ probe.kind === "health" ? probe.health : probe.kind === "no-health" ? "no-health" : null,
49655
+ CURRENT_BUILD
49656
+ );
49657
+ if (drift) {
49658
+ warn(
49659
+ drift,
49660
+ "Stop the running daemon (pid in ~/.node9/daemon.pid), then: node9 daemon --background"
49661
+ );
49662
+ }
49404
49663
  } else {
49405
49664
  warn(
49406
49665
  "Daemon not running \u2014 terminal & native approvals unavailable",
@@ -49422,7 +49681,7 @@ function registerDoctorCommand(program2, version2) {
49422
49681
  cloudEnabled: !!getConfig().settings.approvers?.cloud
49423
49682
  });
49424
49683
  if (autostart) warn(autostart.message, autostart.hint);
49425
- if (fs52.existsSync(path50.join(os47.homedir(), ".node9", "credentials.json")) && getConfig().settings.approvers?.cloud) {
49684
+ if (fs53.existsSync(path51.join(os47.homedir(), ".node9", "credentials.json")) && getConfig().settings.approvers?.cloud) {
49426
49685
  section("Policy sync");
49427
49686
  const health = readSyncHealth();
49428
49687
  if (isPolicyStale(Date.now(), health)) {
@@ -49440,7 +49699,7 @@ function registerDoctorCommand(program2, version2) {
49440
49699
  try {
49441
49700
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
49442
49701
  const cfg = getConfig();
49443
- const creds = fs52.existsSync(path50.join(os47.homedir(), ".node9", "credentials.json"));
49702
+ const creds = fs53.existsSync(path51.join(os47.homedir(), ".node9", "credentials.json"));
49444
49703
  if (!creds) {
49445
49704
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
49446
49705
  } else if (!cfg.settings.approvers.cloud) {
@@ -49491,8 +49750,8 @@ function registerDoctorCommand(program2, version2) {
49491
49750
  // src/cli/commands/audit.ts
49492
49751
  init_decision();
49493
49752
  import chalk12 from "chalk";
49494
- import fs53 from "fs";
49495
- import path51 from "path";
49753
+ import fs54 from "fs";
49754
+ import path52 from "path";
49496
49755
  import os48 from "os";
49497
49756
  function formatRelativeTime(timestamp) {
49498
49757
  const diff = Date.now() - new Date(timestamp).getTime();
@@ -49506,14 +49765,14 @@ function formatRelativeTime(timestamp) {
49506
49765
  }
49507
49766
  function registerAuditCommand(program2) {
49508
49767
  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) => {
49509
- const logPath = path51.join(os48.homedir(), ".node9", "audit.log");
49510
- if (!fs53.existsSync(logPath)) {
49768
+ const logPath = path52.join(os48.homedir(), ".node9", "audit.log");
49769
+ if (!fs54.existsSync(logPath)) {
49511
49770
  console.log(
49512
49771
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
49513
49772
  );
49514
49773
  return;
49515
49774
  }
49516
- const raw = fs53.readFileSync(logPath, "utf-8");
49775
+ const raw = fs54.readFileSync(logPath, "utf-8");
49517
49776
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
49518
49777
  let entries = lines.flatMap((line) => {
49519
49778
  try {
@@ -49579,9 +49838,9 @@ init_costSync();
49579
49838
  init_litellm();
49580
49839
  init_cost_codex();
49581
49840
  init_decision();
49582
- import fs54 from "fs";
49841
+ import fs55 from "fs";
49583
49842
  import os49 from "os";
49584
- import path52 from "path";
49843
+ import path53 from "path";
49585
49844
  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;
49586
49845
  function buildTestTimestamps(allEntries) {
49587
49846
  const testTs = /* @__PURE__ */ new Set();
@@ -49661,8 +49920,8 @@ function getDateRange(period, now) {
49661
49920
  }
49662
49921
  }
49663
49922
  function parseAuditLog(logPath) {
49664
- if (!fs54.existsSync(logPath)) return [];
49665
- const raw = fs54.readFileSync(logPath, "utf-8");
49923
+ if (!fs55.existsSync(logPath)) return [];
49924
+ const raw = fs55.readFileSync(logPath, "utf-8");
49666
49925
  return raw.split("\n").flatMap((line) => {
49667
49926
  if (!line.trim()) return [];
49668
49927
  try {
@@ -49716,25 +49975,25 @@ function freezeClaudeCost(acc) {
49716
49975
  };
49717
49976
  }
49718
49977
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
49719
- const projPath = path52.join(projectsDir, proj);
49978
+ const projPath = path53.join(projectsDir, proj);
49720
49979
  let files;
49721
49980
  try {
49722
- const stat = fs54.statSync(projPath);
49981
+ const stat = fs55.statSync(projPath);
49723
49982
  if (!stat.isDirectory()) return;
49724
- files = fs54.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
49983
+ files = fs55.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
49725
49984
  } catch {
49726
49985
  return;
49727
49986
  }
49728
49987
  const startMs = start.getTime();
49729
49988
  for (const file of files) {
49730
- const filePath = path52.join(projPath, file);
49989
+ const filePath = path53.join(projPath, file);
49731
49990
  try {
49732
- if (fs54.statSync(filePath).mtimeMs < startMs) continue;
49991
+ if (fs55.statSync(filePath).mtimeMs < startMs) continue;
49733
49992
  } catch {
49734
49993
  continue;
49735
49994
  }
49736
49995
  try {
49737
- const raw = fs54.readFileSync(filePath, "utf-8");
49996
+ const raw = fs55.readFileSync(filePath, "utf-8");
49738
49997
  for (const line of raw.split("\n")) {
49739
49998
  if (!line.trim()) continue;
49740
49999
  let entry;
@@ -49784,10 +50043,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
49784
50043
  }
49785
50044
  function loadClaudeCost(start, end, projectsDir) {
49786
50045
  const acc = emptyClaudeCostAccumulator();
49787
- if (!fs54.existsSync(projectsDir)) return freezeClaudeCost(acc);
50046
+ if (!fs55.existsSync(projectsDir)) return freezeClaudeCost(acc);
49788
50047
  let dirs;
49789
50048
  try {
49790
- dirs = fs54.readdirSync(projectsDir);
50049
+ dirs = fs55.readdirSync(projectsDir);
49791
50050
  } catch {
49792
50051
  return freezeClaudeCost(acc);
49793
50052
  }
@@ -49799,7 +50058,7 @@ function loadClaudeCost(start, end, projectsDir) {
49799
50058
  function processCodexCostFile(filePath, start, end, acc) {
49800
50059
  let lines;
49801
50060
  try {
49802
- lines = fs54.readFileSync(filePath, "utf-8").split("\n");
50061
+ lines = fs55.readFileSync(filePath, "utf-8").split("\n");
49803
50062
  } catch {
49804
50063
  return;
49805
50064
  }
@@ -49854,31 +50113,31 @@ function processCodexCostFile(filePath, start, end, acc) {
49854
50113
  }
49855
50114
  function listCodexSessionFiles2(sessionsBase) {
49856
50115
  const jsonlFiles = [];
49857
- if (!fs54.existsSync(sessionsBase)) return jsonlFiles;
50116
+ if (!fs55.existsSync(sessionsBase)) return jsonlFiles;
49858
50117
  try {
49859
- for (const year of fs54.readdirSync(sessionsBase)) {
49860
- const yearPath = path52.join(sessionsBase, year);
50118
+ for (const year of fs55.readdirSync(sessionsBase)) {
50119
+ const yearPath = path53.join(sessionsBase, year);
49861
50120
  try {
49862
- if (!fs54.statSync(yearPath).isDirectory()) continue;
50121
+ if (!fs55.statSync(yearPath).isDirectory()) continue;
49863
50122
  } catch {
49864
50123
  continue;
49865
50124
  }
49866
- for (const month of fs54.readdirSync(yearPath)) {
49867
- const monthPath = path52.join(yearPath, month);
50125
+ for (const month of fs55.readdirSync(yearPath)) {
50126
+ const monthPath = path53.join(yearPath, month);
49868
50127
  try {
49869
- if (!fs54.statSync(monthPath).isDirectory()) continue;
50128
+ if (!fs55.statSync(monthPath).isDirectory()) continue;
49870
50129
  } catch {
49871
50130
  continue;
49872
50131
  }
49873
- for (const day of fs54.readdirSync(monthPath)) {
49874
- const dayPath = path52.join(monthPath, day);
50132
+ for (const day of fs55.readdirSync(monthPath)) {
50133
+ const dayPath = path53.join(monthPath, day);
49875
50134
  try {
49876
- if (!fs54.statSync(dayPath).isDirectory()) continue;
50135
+ if (!fs55.statSync(dayPath).isDirectory()) continue;
49877
50136
  } catch {
49878
50137
  continue;
49879
50138
  }
49880
- for (const file of fs54.readdirSync(dayPath)) {
49881
- if (file.endsWith(".jsonl")) jsonlFiles.push(path52.join(dayPath, file));
50139
+ for (const file of fs55.readdirSync(dayPath)) {
50140
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path53.join(dayPath, file));
49882
50141
  }
49883
50142
  }
49884
50143
  }
@@ -49943,13 +50202,13 @@ function freezeGeminiCost(acc) {
49943
50202
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
49944
50203
  const startMs = start.getTime();
49945
50204
  try {
49946
- if (fs54.statSync(filePath).mtimeMs < startMs) return;
50205
+ if (fs55.statSync(filePath).mtimeMs < startMs) return;
49947
50206
  } catch {
49948
50207
  return;
49949
50208
  }
49950
50209
  let raw;
49951
50210
  try {
49952
- raw = fs54.readFileSync(filePath, "utf-8");
50211
+ raw = fs55.readFileSync(filePath, "utf-8");
49953
50212
  } catch {
49954
50213
  return;
49955
50214
  }
@@ -49998,30 +50257,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
49998
50257
  const out = [];
49999
50258
  let dirs;
50000
50259
  try {
50001
- if (!fs54.statSync(geminiTmpDir2).isDirectory()) return out;
50002
- dirs = fs54.readdirSync(geminiTmpDir2);
50260
+ if (!fs55.statSync(geminiTmpDir2).isDirectory()) return out;
50261
+ dirs = fs55.readdirSync(geminiTmpDir2);
50003
50262
  } catch {
50004
50263
  return out;
50005
50264
  }
50006
50265
  for (const proj of dirs) {
50007
- const chatsDir = path52.join(geminiTmpDir2, proj, "chats");
50266
+ const chatsDir = path53.join(geminiTmpDir2, proj, "chats");
50008
50267
  let files;
50009
50268
  try {
50010
- if (!fs54.statSync(chatsDir).isDirectory()) continue;
50011
- files = fs54.readdirSync(chatsDir);
50269
+ if (!fs55.statSync(chatsDir).isDirectory()) continue;
50270
+ files = fs55.readdirSync(chatsDir);
50012
50271
  } catch {
50013
50272
  continue;
50014
50273
  }
50015
50274
  for (const f of files) {
50016
50275
  if (!f.endsWith(".jsonl")) continue;
50017
- out.push({ projectKey: proj, file: path52.join(chatsDir, f) });
50276
+ out.push({ projectKey: proj, file: path53.join(chatsDir, f) });
50018
50277
  }
50019
50278
  }
50020
50279
  return out;
50021
50280
  }
50022
50281
  function loadGeminiCost(start, end, geminiTmpDir2) {
50023
50282
  const acc = emptyGeminiAccumulator();
50024
- if (!fs54.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
50283
+ if (!fs55.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
50025
50284
  for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
50026
50285
  processGeminiCostFile(file, projectKey, start, end, acc);
50027
50286
  }
@@ -50039,11 +50298,11 @@ function dimensionOfBlock(checkedBy, ruleName) {
50039
50298
  }
50040
50299
  function aggregateReportFromAudit(period, opts = {}) {
50041
50300
  const now = opts.now ?? /* @__PURE__ */ new Date();
50042
- const auditLogPath = opts.auditLogPath ?? path52.join(os49.homedir(), ".node9", "audit.log");
50043
- const claudeProjectsDir = opts.claudeProjectsDir ?? path52.join(os49.homedir(), ".claude", "projects");
50044
- const codexSessionsDir2 = opts.codexSessionsDir ?? path52.join(os49.homedir(), ".codex", "sessions");
50045
- const geminiTmpDir2 = opts.geminiTmpDir ?? path52.join(os49.homedir(), ".gemini", "tmp");
50046
- const hasAuditFile = fs54.existsSync(auditLogPath);
50301
+ const auditLogPath = opts.auditLogPath ?? path53.join(os49.homedir(), ".node9", "audit.log");
50302
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path53.join(os49.homedir(), ".claude", "projects");
50303
+ const codexSessionsDir2 = opts.codexSessionsDir ?? path53.join(os49.homedir(), ".codex", "sessions");
50304
+ const geminiTmpDir2 = opts.geminiTmpDir ?? path53.join(os49.homedir(), ".gemini", "tmp");
50305
+ const hasAuditFile = fs55.existsSync(auditLogPath);
50047
50306
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
50048
50307
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
50049
50308
  const { start, end } = getDateRange(period, now);
@@ -50750,11 +51009,12 @@ function renderTerminalReport(data, responseDlpEntries, excludeTests) {
50750
51009
  }
50751
51010
 
50752
51011
  // src/cli/commands/daemon-cmd.ts
51012
+ init_daemon();
50753
51013
  init_startup_log();
50754
51014
  init_daemon2();
50755
51015
  import chalk14 from "chalk";
50756
51016
  import { spawn as spawn6 } from "child_process";
50757
- import fs55 from "fs";
51017
+ import fs56 from "fs";
50758
51018
  var VALID_ACTIONS = "start | stop | restart | status | install | uninstall";
50759
51019
  function registerDaemonCommand(program2) {
50760
51020
  program2.command("daemon").description("Manage the local approval daemon").argument("[action]", `${VALID_ACTIONS} (default: start)`).option("-b, --background", "Start the daemon in the background (detached)").option(
@@ -50791,7 +51051,10 @@ function registerDaemonCommand(program2) {
50791
51051
  if (cmd === "stop") return stopDaemon();
50792
51052
  if (cmd === "restart") {
50793
51053
  stopDaemon();
50794
- await new Promise((r) => setTimeout(r, 500));
51054
+ for (let i = 0; i < 15; i++) {
51055
+ if (!await isDaemonReachable(300)) break;
51056
+ await new Promise((r) => setTimeout(r, 200));
51057
+ }
50795
51058
  const restartFd = openStartupLogFd();
50796
51059
  recordStartupState("starting");
50797
51060
  const child = spawn6(process.execPath, [process.argv[1], "daemon"], {
@@ -50806,7 +51069,7 @@ function registerDaemonCommand(program2) {
50806
51069
  child.unref();
50807
51070
  if (restartFd !== void 0) {
50808
51071
  try {
50809
- fs55.closeSync(restartFd);
51072
+ fs56.closeSync(restartFd);
50810
51073
  } catch {
50811
51074
  }
50812
51075
  }
@@ -50857,7 +51120,7 @@ function registerDaemonCommand(program2) {
50857
51120
  } finally {
50858
51121
  if (startupFd !== void 0) {
50859
51122
  try {
50860
- fs55.closeSync(startupFd);
51123
+ fs56.closeSync(startupFd);
50861
51124
  } catch {
50862
51125
  }
50863
51126
  }
@@ -50872,12 +51135,13 @@ function registerDaemonCommand(program2) {
50872
51135
  // src/cli/commands/status.ts
50873
51136
  init_core();
50874
51137
  init_daemon();
51138
+ init_build_id();
50875
51139
  init_agent_wiring();
50876
51140
  init_sync();
50877
51141
  init_service();
50878
51142
  import chalk15 from "chalk";
50879
- import fs56 from "fs";
50880
- import path53 from "path";
51143
+ import fs57 from "fs";
51144
+ import path54 from "path";
50881
51145
  import os50 from "os";
50882
51146
  function printAgentSection(label2, hookPairs, wrapped) {
50883
51147
  console.log(chalk15.bold(` ${label2}`));
@@ -50899,7 +51163,7 @@ function printAgentSection(label2, hookPairs, wrapped) {
50899
51163
  }
50900
51164
  }
50901
51165
  function registerStatusCommand(program2) {
50902
- program2.command("status").description("Show current Node9 mode, policy source, and persistent decisions").action(() => {
51166
+ program2.command("status").description("Show current Node9 mode, policy source, and persistent decisions").action(async () => {
50903
51167
  const creds = getCredentials();
50904
51168
  const daemonRunning = isDaemonRunning();
50905
51169
  const mergedConfig = getConfig();
@@ -50930,6 +51194,14 @@ function registerStatusCommand(program2) {
50930
51194
  console.log(
50931
51195
  chalk15.green(" \u25CF Daemon running") + chalk15.gray(` \u2192 http://127.0.0.1:${DAEMON_PORT}/`)
50932
51196
  );
51197
+ const probe = await probeDaemonHealth();
51198
+ const drift = describeBuildDrift(
51199
+ probe.kind === "health" ? probe.health : probe.kind === "no-health" ? "no-health" : null,
51200
+ CURRENT_BUILD
51201
+ );
51202
+ if (drift) {
51203
+ console.log(chalk15.yellow(` \u26A0 ${drift}`) + chalk15.gray(" \u2014 run: node9 doctor"));
51204
+ }
50933
51205
  } else {
50934
51206
  console.log(chalk15.gray(" \u25CB Daemon stopped"));
50935
51207
  }
@@ -50951,13 +51223,13 @@ function registerStatusCommand(program2) {
50951
51223
  console.log("");
50952
51224
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
50953
51225
  console.log(` Mode: ${modeLabel}`);
50954
- const projectConfig = path53.join(process.cwd(), "node9.config.json");
50955
- const globalConfig = path53.join(os50.homedir(), ".node9", "config.json");
51226
+ const projectConfig = path54.join(process.cwd(), "node9.config.json");
51227
+ const globalConfig = path54.join(os50.homedir(), ".node9", "config.json");
50956
51228
  console.log(
50957
- ` Local: ${fs56.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
51229
+ ` Local: ${fs57.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
50958
51230
  );
50959
51231
  console.log(
50960
- ` Global: ${fs56.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
51232
+ ` Global: ${fs57.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
50961
51233
  );
50962
51234
  if (mergedConfig.policy.sandboxPaths.length > 0) {
50963
51235
  console.log(
@@ -51004,8 +51276,8 @@ init_shields();
51004
51276
  init_service();
51005
51277
  init_core();
51006
51278
  import chalk16 from "chalk";
51007
- import fs57 from "fs";
51008
- import path54 from "path";
51279
+ import fs58 from "fs";
51280
+ import path55 from "path";
51009
51281
  import os51 from "os";
51010
51282
  import https6 from "https";
51011
51283
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
@@ -51092,16 +51364,16 @@ function registerInitCommand(program2) {
51092
51364
  }
51093
51365
  console.log("");
51094
51366
  }
51095
- const configPath = path54.join(os51.homedir(), ".node9", "config.json");
51096
- const isFirstInstall = !fs57.existsSync(configPath);
51097
- if (fs57.existsSync(configPath) && !options.force) {
51367
+ const configPath = path55.join(os51.homedir(), ".node9", "config.json");
51368
+ const isFirstInstall = !fs58.existsSync(configPath);
51369
+ if (fs58.existsSync(configPath) && !options.force) {
51098
51370
  try {
51099
- const existing = JSON.parse(fs57.readFileSync(configPath, "utf-8"));
51371
+ const existing = JSON.parse(fs58.readFileSync(configPath, "utf-8"));
51100
51372
  const settings = existing.settings ?? {};
51101
51373
  if (settings.mode !== chosenMode) {
51102
51374
  settings.mode = chosenMode;
51103
51375
  existing.settings = settings;
51104
- fs57.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
51376
+ fs58.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
51105
51377
  console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
51106
51378
  } else {
51107
51379
  console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -51114,9 +51386,9 @@ function registerInitCommand(program2) {
51114
51386
  ...DEFAULT_CONFIG,
51115
51387
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
51116
51388
  };
51117
- const dir = path54.dirname(configPath);
51118
- if (!fs57.existsSync(dir)) fs57.mkdirSync(dir, { recursive: true });
51119
- fs57.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
51389
+ const dir = path55.dirname(configPath);
51390
+ if (!fs58.existsSync(dir)) fs58.mkdirSync(dir, { recursive: true });
51391
+ fs58.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
51120
51392
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
51121
51393
  console.log(chalk16.gray(` Mode: ${chosenMode}`));
51122
51394
  }
@@ -51219,11 +51491,11 @@ init_agent_wiring();
51219
51491
  init_setup();
51220
51492
  init_hook_baseline();
51221
51493
  import chalk17 from "chalk";
51222
- import fs58 from "fs";
51494
+ import fs59 from "fs";
51223
51495
  var hasHookSurface = (a) => a.hooks.length > 0;
51224
51496
  function backupForHeal(file) {
51225
51497
  try {
51226
- if (file && fs58.existsSync(file)) fs58.copyFileSync(file, `${file}.node9-heal-bak`);
51498
+ if (file && fs59.existsSync(file)) fs59.copyFileSync(file, `${file}.node9-heal-bak`);
51227
51499
  } catch {
51228
51500
  }
51229
51501
  }
@@ -51390,7 +51662,7 @@ function registerConnectCommand(program2) {
51390
51662
  }
51391
51663
 
51392
51664
  // src/cli/commands/undo.ts
51393
- import path55 from "path";
51665
+ import path56 from "path";
51394
51666
  import chalk20 from "chalk";
51395
51667
 
51396
51668
  // src/tui/undo-navigator.ts
@@ -51549,7 +51821,7 @@ function findMatchingCwd(startDir, history) {
51549
51821
  let dir = startDir;
51550
51822
  while (true) {
51551
51823
  if (cwds.has(dir)) return dir;
51552
- const parent = path55.dirname(dir);
51824
+ const parent = path56.dirname(dir);
51553
51825
  if (parent === dir) return null;
51554
51826
  dir = parent;
51555
51827
  }
@@ -52183,9 +52455,9 @@ function registerMcpGatewayCommand(program2) {
52183
52455
 
52184
52456
  // src/mcp-server/index.ts
52185
52457
  import readline5 from "readline";
52186
- import fs60 from "fs";
52458
+ import fs61 from "fs";
52187
52459
  import os53 from "os";
52188
- import path57 from "path";
52460
+ import path58 from "path";
52189
52461
  import { spawnSync as spawnSync4 } from "child_process";
52190
52462
  init_decision();
52191
52463
  init_core();
@@ -52193,9 +52465,9 @@ init_daemon();
52193
52465
  init_shields();
52194
52466
 
52195
52467
  // src/auth/egress-config.ts
52196
- import fs59 from "fs";
52468
+ import fs60 from "fs";
52197
52469
  import os52 from "os";
52198
- import path56 from "path";
52470
+ import path57 from "path";
52199
52471
  var DEFAULT_EGRESS = {
52200
52472
  enabled: false,
52201
52473
  mode: "review",
@@ -52204,12 +52476,12 @@ var DEFAULT_EGRESS = {
52204
52476
  allowPrivate: true
52205
52477
  };
52206
52478
  function egressConfigPath() {
52207
- return path56.join(os52.homedir(), ".node9", "config.json");
52479
+ return path57.join(os52.homedir(), ".node9", "config.json");
52208
52480
  }
52209
52481
  function readEgressRawConfig() {
52210
52482
  let text;
52211
52483
  try {
52212
- text = fs59.readFileSync(egressConfigPath(), "utf8");
52484
+ text = fs60.readFileSync(egressConfigPath(), "utf8");
52213
52485
  } catch (err2) {
52214
52486
  if (err2.code === "ENOENT") return {};
52215
52487
  throw err2;
@@ -52224,8 +52496,8 @@ function readEgressRawConfig() {
52224
52496
  }
52225
52497
  function writeEgressRawConfig(config) {
52226
52498
  const p = egressConfigPath();
52227
- fs59.mkdirSync(path56.dirname(p), { recursive: true });
52228
- fs59.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
52499
+ fs60.mkdirSync(path57.dirname(p), { recursive: true });
52500
+ fs60.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
52229
52501
  }
52230
52502
  function applyEgress(config, change) {
52231
52503
  const policy = config.policy = config.policy ?? {};
@@ -52610,13 +52882,13 @@ function handleStatus() {
52610
52882
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
52611
52883
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
52612
52884
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
52613
- const projectConfig = path57.join(process.cwd(), "node9.config.json");
52614
- const globalConfig = path57.join(os53.homedir(), ".node9", "config.json");
52885
+ const projectConfig = path58.join(process.cwd(), "node9.config.json");
52886
+ const globalConfig = path58.join(os53.homedir(), ".node9", "config.json");
52615
52887
  lines.push(
52616
- `Project config (node9.config.json): ${fs60.existsSync(projectConfig) ? "present" : "not found"}`
52888
+ `Project config (node9.config.json): ${fs61.existsSync(projectConfig) ? "present" : "not found"}`
52617
52889
  );
52618
52890
  lines.push(
52619
- `Global config (~/.node9/config.json): ${fs60.existsSync(globalConfig) ? "present" : "not found"}`
52891
+ `Global config (~/.node9/config.json): ${fs61.existsSync(globalConfig) ? "present" : "not found"}`
52620
52892
  );
52621
52893
  return lines.join("\n");
52622
52894
  }
@@ -52722,21 +52994,21 @@ function handleEgressDeny(args) {
52722
52994
  addEgressHost("deny", host);
52723
52995
  return `Denied egress to ${host} (deny always wins over allow).`;
52724
52996
  }
52725
- var GLOBAL_CONFIG_PATH = path57.join(os53.homedir(), ".node9", "config.json");
52997
+ var GLOBAL_CONFIG_PATH = path58.join(os53.homedir(), ".node9", "config.json");
52726
52998
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
52727
52999
  function readGlobalConfigRaw() {
52728
53000
  try {
52729
- if (fs60.existsSync(GLOBAL_CONFIG_PATH)) {
52730
- return JSON.parse(fs60.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
53001
+ if (fs61.existsSync(GLOBAL_CONFIG_PATH)) {
53002
+ return JSON.parse(fs61.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
52731
53003
  }
52732
53004
  } catch {
52733
53005
  }
52734
53006
  return {};
52735
53007
  }
52736
53008
  function writeGlobalConfigRaw(data) {
52737
- const dir = path57.dirname(GLOBAL_CONFIG_PATH);
52738
- if (!fs60.existsSync(dir)) fs60.mkdirSync(dir, { recursive: true });
52739
- fs60.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
53009
+ const dir = path58.dirname(GLOBAL_CONFIG_PATH);
53010
+ if (!fs61.existsSync(dir)) fs61.mkdirSync(dir, { recursive: true });
53011
+ fs61.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
52740
53012
  }
52741
53013
  function handleApproverList() {
52742
53014
  const config = getConfig();
@@ -52780,9 +53052,9 @@ function handleApproverSet(args) {
52780
53052
  function handleAuditGet(args) {
52781
53053
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
52782
53054
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
52783
- const auditPath = path57.join(os53.homedir(), ".node9", "audit.log");
52784
- if (!fs60.existsSync(auditPath)) return "No audit log found.";
52785
- const rawLines = fs60.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
53055
+ const auditPath = path58.join(os53.homedir(), ".node9", "audit.log");
53056
+ if (!fs61.existsSync(auditPath)) return "No audit log found.";
53057
+ const rawLines = fs61.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
52786
53058
  const wanted = filter === "block" ? "deny" : filter;
52787
53059
  const parsed = [];
52788
53060
  for (const line of rawLines) {
@@ -53158,7 +53430,7 @@ function registerTrustCommand(program2) {
53158
53430
  // src/cli/commands/mcp-pin.ts
53159
53431
  init_mcp_pin();
53160
53432
  import chalk24 from "chalk";
53161
- import fs61 from "fs";
53433
+ import fs62 from "fs";
53162
53434
 
53163
53435
  // src/cli/commands/mcp-gateway-cmd.ts
53164
53436
  init_mcp_wrap();
@@ -53366,7 +53638,7 @@ function registerMcpPinCommand(program2) {
53366
53638
  let repoCorrupt = false;
53367
53639
  if (found.source === "repo") {
53368
53640
  try {
53369
- const raw = fs61.readFileSync(found.path, "utf-8");
53641
+ const raw = fs62.readFileSync(found.path, "utf-8");
53370
53642
  const parsed = JSON.parse(raw);
53371
53643
  repoEntries = parsed.servers ?? {};
53372
53644
  } catch {
@@ -53960,8 +54232,8 @@ import chalk30 from "chalk";
53960
54232
 
53961
54233
  // src/ci-check/fetch.ts
53962
54234
  var import_undici = __toESM(require_undici());
53963
- import fs62 from "fs";
53964
- import path58 from "path";
54235
+ import fs63 from "fs";
54236
+ import path59 from "path";
53965
54237
  import { execFileSync as execFileSync2 } from "child_process";
53966
54238
  var cachedGhToken;
53967
54239
  function resolveGitHubToken() {
@@ -54044,7 +54316,7 @@ function parseRepoUrl(input) {
54044
54316
  function isLocalPath(input) {
54045
54317
  if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) return true;
54046
54318
  try {
54047
- return fs62.existsSync(input) && fs62.statSync(input).isDirectory();
54319
+ return fs63.existsSync(input) && fs63.statSync(input).isDirectory();
54048
54320
  } catch {
54049
54321
  return false;
54050
54322
  }
@@ -54159,10 +54431,10 @@ function readLocalTree(dir) {
54159
54431
  const files = [];
54160
54432
  const notes = [];
54161
54433
  const add = (rel) => {
54162
- const abs = path58.join(root, rel);
54434
+ const abs = path59.join(root, rel);
54163
54435
  try {
54164
- if (fs62.existsSync(abs) && fs62.statSync(abs).isFile()) {
54165
- files.push({ path: rel, content: fs62.readFileSync(abs, "utf8") });
54436
+ if (fs63.existsSync(abs) && fs63.statSync(abs).isFile()) {
54437
+ files.push({ path: rel, content: fs63.readFileSync(abs, "utf8") });
54166
54438
  }
54167
54439
  } catch {
54168
54440
  }
@@ -54182,7 +54454,7 @@ function readLocalTree(dir) {
54182
54454
  dirsVisited++;
54183
54455
  let entries;
54184
54456
  try {
54185
- entries = fs62.readdirSync(path58.join(root, relDir), { withFileTypes: true });
54457
+ entries = fs63.readdirSync(path59.join(root, relDir), { withFileTypes: true });
54186
54458
  } catch {
54187
54459
  return;
54188
54460
  }
@@ -54203,11 +54475,11 @@ function readLocalTree(dir) {
54203
54475
  `repo is large \u2014 some agent-surface files may be INCOMPLETE (capped at ${MAX_SURFACE_FILES} files / ${MAX_DIRS} dirs).`
54204
54476
  );
54205
54477
  for (const rel of matches) collect(rel);
54206
- const wfDir = path58.join(root, WORKFLOW_DIR);
54478
+ const wfDir = path59.join(root, WORKFLOW_DIR);
54207
54479
  try {
54208
- if (fs62.existsSync(wfDir)) {
54209
- for (const name of fs62.readdirSync(wfDir)) {
54210
- if (/\.ya?ml$/.test(name)) add(path58.join(WORKFLOW_DIR, name));
54480
+ if (fs63.existsSync(wfDir)) {
54481
+ for (const name of fs63.readdirSync(wfDir)) {
54482
+ if (/\.ya?ml$/.test(name)) add(path59.join(WORKFLOW_DIR, name));
54211
54483
  }
54212
54484
  }
54213
54485
  } catch {
@@ -54512,7 +54784,7 @@ function severityFromScore(score) {
54512
54784
  if (score >= 1) return "advisory";
54513
54785
  return null;
54514
54786
  }
54515
- function analyzeWorkflow(path71, content) {
54787
+ function analyzeWorkflow(path72, content) {
54516
54788
  let raw;
54517
54789
  try {
54518
54790
  raw = parseYaml(content) ?? {};
@@ -54638,7 +54910,7 @@ function analyzeWorkflow(path71, content) {
54638
54910
  dimension: "workflows",
54639
54911
  severity,
54640
54912
  title,
54641
- file: path71,
54913
+ file: path72,
54642
54914
  signals,
54643
54915
  mitigations: mitigations.length ? mitigations : void 0,
54644
54916
  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."
@@ -54714,7 +54986,7 @@ function evalAgentJob(job, wf, raw, untrustedTrigger, reusable) {
54714
54986
  if (reusable && !loadedGun && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
54715
54987
  return { severity, secrets, injectable, canReadEnv };
54716
54988
  }
54717
- function analyzeWorkflowSecrets(path71, content) {
54989
+ function analyzeWorkflowSecrets(path72, content) {
54718
54990
  let raw;
54719
54991
  try {
54720
54992
  raw = parseYaml(content) ?? {};
@@ -54734,7 +55006,7 @@ function analyzeWorkflowSecrets(path71, content) {
54734
55006
  dimension: "data",
54735
55007
  severity: worst.severity,
54736
55008
  title: worst.severity === "advisory" ? "Secrets reachable by the agent \u2014 hardening" : "Exfiltratable secrets reachable by an injectable agent",
54737
- file: path71,
55009
+ file: path72,
54738
55010
  signals: [
54739
55011
  `agent can reach: ${worst.secrets.map((s) => s.name).join(", ")}`,
54740
55012
  worst.injectable ? "the agent is externally triggerable (untrusted trigger, no gate)" : "gated / not externally triggerable \u2014 latent risk only",
@@ -54761,7 +55033,7 @@ function hookCommands(hooks) {
54761
55033
  }
54762
55034
  return out;
54763
55035
  }
54764
- function analyzeAgentConfig(path71, content) {
55036
+ function analyzeAgentConfig(path72, content) {
54765
55037
  let cfg;
54766
55038
  try {
54767
55039
  cfg = JSON.parse(content);
@@ -54780,7 +55052,7 @@ function analyzeAgentConfig(path71, content) {
54780
55052
  dimension: "toolRules",
54781
55053
  severity: high ? "high" : "medium",
54782
55054
  title: high ? "Agent hook runs UNPINNED/remote third-party code on every action" : "Agent hook runs third-party code in the agent hot path",
54783
- file: path71,
55055
+ file: path72,
54784
55056
  signals: [
54785
55057
  `hook command: \`${cmd.slice(0, 120)}\``,
54786
55058
  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"
@@ -54800,7 +55072,7 @@ function analyzeAgentConfig(path71, content) {
54800
55072
  dimension: "toolRules",
54801
55073
  severity: hasBackstop ? "medium" : "high",
54802
55074
  title: hasBackstop ? "Committed agent config pre-authorizes broad tools" : "Committed agent config pre-authorizes broad tools with no deny backstop",
54803
- file: path71,
55075
+ file: path72,
54804
55076
  signals: [
54805
55077
  `broad allow(s): ${broad.slice(0, 5).join(", ")}`,
54806
55078
  hasBackstop ? "a `deny` list backstops the broad allow" : "no `deny` entry covers Bash/Write/Edit \u2014 every contributor is pre-authorized for catastrophic tools"
@@ -54813,16 +55085,16 @@ function analyzeAgentConfig(path71, content) {
54813
55085
 
54814
55086
  // src/ci-check/mcp.ts
54815
55087
  init_dist();
54816
- function analyzeMcp(path71, content) {
55088
+ function analyzeMcp(path72, content) {
54817
55089
  let cfg;
54818
55090
  try {
54819
55091
  cfg = JSON.parse(content);
54820
55092
  } catch {
54821
55093
  return [];
54822
55094
  }
54823
- return analyzeMcpServers(cfg.mcpServers ?? {}, path71);
55095
+ return analyzeMcpServers(cfg.mcpServers ?? {}, path72);
54824
55096
  }
54825
- function analyzeMcpServers(servers, path71) {
55097
+ function analyzeMcpServers(servers, path72) {
54826
55098
  const findings = [];
54827
55099
  for (const [name, srv] of Object.entries(servers ?? {})) {
54828
55100
  if (!srv || srv.disabled) continue;
@@ -54833,7 +55105,7 @@ function analyzeMcpServers(servers, path71) {
54833
55105
  dimension: "mcp",
54834
55106
  severity: "medium",
54835
55107
  title: `MCP server "${name}" runs an unpinned executable`,
54836
- file: path71,
55108
+ file: path72,
54837
55109
  signals: [`\`${argv.slice(0, 120)}\` \u2014 unversioned/@latest npx`],
54838
55110
  fix: "Pin the MCP server package to an exact version so a PR (or a registry compromise) can\u2019t swap the toolchain."
54839
55111
  });
@@ -54847,7 +55119,7 @@ function analyzeMcpServers(servers, path71) {
54847
55119
  dimension: "mcp",
54848
55120
  severity: "high",
54849
55121
  title: `MCP server "${name}" has an inline credential`,
54850
- file: path71,
55122
+ file: path72,
54851
55123
  signals: [
54852
55124
  `env.${k} matches ${hit.patternName} \u2014 agent-reachable secret committed to the repo`
54853
55125
  ],
@@ -54861,7 +55133,7 @@ function analyzeMcpServers(servers, path71) {
54861
55133
 
54862
55134
  // src/ci-check/codex.ts
54863
55135
  import { parse as parseToml5 } from "smol-toml";
54864
- function analyzeCodexConfig(path71, content) {
55136
+ function analyzeCodexConfig(path72, content) {
54865
55137
  let cfg;
54866
55138
  try {
54867
55139
  cfg = parseToml5(content);
@@ -54869,7 +55141,7 @@ function analyzeCodexConfig(path71, content) {
54869
55141
  return [];
54870
55142
  }
54871
55143
  const findings = [];
54872
- findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path71));
55144
+ findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path72));
54873
55145
  const sandbox = typeof cfg.sandbox_mode === "string" ? cfg.sandbox_mode : "";
54874
55146
  const approval = typeof cfg.approval_policy === "string" ? cfg.approval_policy : "";
54875
55147
  const fullAccess = /danger-full-access/i.test(sandbox);
@@ -54884,7 +55156,7 @@ function analyzeCodexConfig(path71, content) {
54884
55156
  dimension: "toolRules",
54885
55157
  severity: fullAccess ? "high" : "medium",
54886
55158
  title: fullAccess ? "Codex config grants a full-access sandbox" : "Codex config never requires approval",
54887
- file: path71,
55159
+ file: path72,
54888
55160
  signals,
54889
55161
  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.'
54890
55162
  });
@@ -54940,10 +55212,10 @@ function decodeSuspiciousBase64(text) {
54940
55212
  }
54941
55213
  return out;
54942
55214
  }
54943
- function mk(severity, title, signals, fix, path71) {
54944
- return { check: "CI-6", dimension: "instructions", severity, title, file: path71, signals, fix };
55215
+ function mk(severity, title, signals, fix, path72) {
55216
+ return { check: "CI-6", dimension: "instructions", severity, title, file: path72, signals, fix };
54945
55217
  }
54946
- function analyzeInstructionFile(path71, content) {
55218
+ function analyzeInstructionFile(path72, content) {
54947
55219
  const findings = [];
54948
55220
  const decoded = decodeSuspiciousBase64(content);
54949
55221
  if (TAG_CHARS.test(content))
@@ -54955,7 +55227,7 @@ function analyzeInstructionFile(path71, content) {
54955
55227
  "contains Unicode tag characters (U+E0000\u2013E007F) \u2014 an invisible instruction-smuggling channel with no legitimate use in text"
54956
55228
  ],
54957
55229
  "Remove the tag characters. Instruction files must be plain, reviewable text.",
54958
- path71
55230
+ path72
54959
55231
  )
54960
55232
  );
54961
55233
  if (BIDI_OVERRIDE.test(content))
@@ -54967,7 +55239,7 @@ function analyzeInstructionFile(path71, content) {
54967
55239
  "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"
54968
55240
  ],
54969
55241
  "Remove the bidi override characters.",
54970
- path71
55242
+ path72
54971
55243
  )
54972
55244
  );
54973
55245
  else if (BIDI_EMBED_ISOLATE.test(content))
@@ -54979,7 +55251,7 @@ function analyzeInstructionFile(path71, content) {
54979
55251
  "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"
54980
55252
  ],
54981
55253
  "Confirm the bidi marks are legitimate RTL formatting; remove otherwise.",
54982
- path71
55254
+ path72
54983
55255
  )
54984
55256
  );
54985
55257
  const zw = suspiciousZeroWidth(content);
@@ -54993,7 +55265,7 @@ function analyzeInstructionFile(path71, content) {
54993
55265
  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)"
54994
55266
  ],
54995
55267
  "Remove the zero-width characters. Instruction files must be plain, reviewable text.",
54996
- path71
55268
+ path72
54997
55269
  )
54998
55270
  );
54999
55271
  }
@@ -55009,7 +55281,7 @@ function analyzeInstructionFile(path71, content) {
55009
55281
  `contains a prompt-override / role-impersonation directive (\`${m[0].slice(0, 60).trim()}\`)${ovEnc ? " \u2014 concealed in a base64 blob" : ""}`
55010
55282
  ],
55011
55283
  "Remove the override text. An instruction file should not tell the agent to ignore its own rules.",
55012
- path71
55284
+ path72
55013
55285
  )
55014
55286
  );
55015
55287
  }
@@ -55021,7 +55293,7 @@ function analyzeInstructionFile(path71, content) {
55021
55293
  "Instruction directs the agent to fetch and run remote code",
55022
55294
  [`\`${fo[0].slice(0, 70).trim()}\` \u2014 fetch-and-obey, outside an install/setup section`],
55023
55295
  "Do not instruct the agent to pipe remote content into a shell; pin and vendor scripts instead.",
55024
- path71
55296
+ path72
55025
55297
  )
55026
55298
  );
55027
55299
  }
@@ -55033,7 +55305,7 @@ function analyzeInstructionFile(path71, content) {
55033
55305
  "Instruction points the agent at credential material",
55034
55306
  [`references \`${sp[0].slice(0, 50).trim()}\` \u2014 directs the agent toward secrets`],
55035
55307
  "Do not reference credential files or paths in agent instructions.",
55036
- path71
55308
+ path72
55037
55309
  )
55038
55310
  );
55039
55311
  }
@@ -55045,7 +55317,7 @@ function analyzeInstructionFile(path71, content) {
55045
55317
  "Instruction directs the agent to send data to an external endpoint",
55046
55318
  [`\`${ex[0].slice(0, 70).trim()}\` \u2014 possible exfiltration directive`],
55047
55319
  "Remove external post/upload directives from agent instructions.",
55048
- path71
55320
+ path72
55049
55321
  )
55050
55322
  );
55051
55323
  }
@@ -55347,17 +55619,17 @@ import chalk32 from "chalk";
55347
55619
  // src/shields/jail.ts
55348
55620
  init_build();
55349
55621
  init_shields();
55350
- import fs63 from "fs";
55622
+ import fs64 from "fs";
55351
55623
  import os54 from "os";
55352
- import path59 from "path";
55624
+ import path60 from "path";
55353
55625
  var USER_JAIL_SHIELD = "user-jail";
55354
55626
  function jailStorePath() {
55355
- return path59.join(os54.homedir(), ".node9", "jail-paths.json");
55627
+ return path60.join(os54.homedir(), ".node9", "jail-paths.json");
55356
55628
  }
55357
55629
  function readJailPaths() {
55358
55630
  let text;
55359
55631
  try {
55360
- text = fs63.readFileSync(jailStorePath(), "utf8");
55632
+ text = fs64.readFileSync(jailStorePath(), "utf8");
55361
55633
  } catch (err2) {
55362
55634
  if (err2.code === "ENOENT") return [];
55363
55635
  throw err2;
@@ -55375,8 +55647,8 @@ function readJailPaths() {
55375
55647
  }
55376
55648
  function writeJailPaths(paths) {
55377
55649
  const p = jailStorePath();
55378
- fs63.mkdirSync(path59.dirname(p), { recursive: true });
55379
- fs63.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
55650
+ fs64.mkdirSync(path60.dirname(p), { recursive: true });
55651
+ fs64.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
55380
55652
  }
55381
55653
  function addJailPath(rawPath, verdict) {
55382
55654
  const norm = rawPath.trim();
@@ -55398,14 +55670,14 @@ function removeJailPath(rawPath) {
55398
55670
  return { removed, paths: after };
55399
55671
  }
55400
55672
  function regenerateUserJail(paths) {
55401
- const file = path59.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
55673
+ const file = path60.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
55402
55674
  if (paths.length === 0) {
55403
55675
  const active2 = readActiveShields();
55404
55676
  if (active2.includes(USER_JAIL_SHIELD)) {
55405
55677
  writeActiveShields(active2.filter((s) => s !== USER_JAIL_SHIELD));
55406
55678
  }
55407
55679
  try {
55408
- fs63.rmSync(file, { force: true });
55680
+ fs64.rmSync(file, { force: true });
55409
55681
  } catch {
55410
55682
  }
55411
55683
  return;
@@ -55520,13 +55792,13 @@ function registerJailCommand(program2) {
55520
55792
  // src/cli/commands/sandbox.ts
55521
55793
  init_config();
55522
55794
  import chalk33 from "chalk";
55523
- import fs66 from "fs";
55524
- import path62 from "path";
55795
+ import fs67 from "fs";
55796
+ import path63 from "path";
55525
55797
  import { spawnSync as spawnSync6 } from "child_process";
55526
55798
 
55527
55799
  // src/sandbox/config.ts
55528
- import fs64 from "fs";
55529
- import path60 from "path";
55800
+ import fs65 from "fs";
55801
+ import path61 from "path";
55530
55802
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
55531
55803
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
55532
55804
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -55599,16 +55871,16 @@ function scaffoldSandboxYaml(agent) {
55599
55871
  return header + stringifyYaml(defaultSandboxConfig(agent));
55600
55872
  }
55601
55873
  function sandboxConfigPath(cwd = process.cwd()) {
55602
- return path60.join(cwd, SANDBOX_CONFIG_FILE);
55874
+ return path61.join(cwd, SANDBOX_CONFIG_FILE);
55603
55875
  }
55604
55876
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
55605
55877
  const p = sandboxConfigPath(cwd);
55606
- if (!fs64.existsSync(p)) {
55878
+ if (!fs65.existsSync(p)) {
55607
55879
  throw new Error(`sandbox: ${SANDBOX_CONFIG_FILE} not found \u2014 run \`node9 sandbox new\` first.`);
55608
55880
  }
55609
55881
  let raw;
55610
55882
  try {
55611
- raw = parseYaml2(fs64.readFileSync(p, "utf-8"));
55883
+ raw = parseYaml2(fs65.readFileSync(p, "utf-8"));
55612
55884
  } catch (err2) {
55613
55885
  throw new Error(
55614
55886
  `sandbox: ${SANDBOX_CONFIG_FILE} is not valid YAML \u2014 ${err2.message}`
@@ -55667,13 +55939,13 @@ init_templates();
55667
55939
 
55668
55940
  // src/sandbox/runtime.ts
55669
55941
  init_templates();
55670
- import fs65 from "fs";
55942
+ import fs66 from "fs";
55671
55943
  import os55 from "os";
55672
- import path61 from "path";
55944
+ import path62 from "path";
55673
55945
  import crypto9 from "crypto";
55674
55946
  import { spawnSync as spawnSync5 } from "child_process";
55675
55947
  function sandboxDataDir(cwd = process.cwd()) {
55676
- return path61.join(cwd, ".node9", "sandbox", "data");
55948
+ return path62.join(cwd, ".node9", "sandbox", "data");
55677
55949
  }
55678
55950
  function detectEngine(engine) {
55679
55951
  const r = spawnSync5(engine, ["--version"], { encoding: "utf-8" });
@@ -55684,7 +55956,7 @@ function detectEngine(engine) {
55684
55956
  }
55685
55957
  function agentCredentialsMount(agent) {
55686
55958
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
55687
- return { hostPath: path61.join(os55.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
55959
+ return { hostPath: path62.join(os55.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
55688
55960
  }
55689
55961
  function buildRunArgs(opts) {
55690
55962
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -55694,7 +55966,7 @@ function buildRunArgs(opts) {
55694
55966
  args.push("-v", `${allowlistHostPath}:${ALLOWED_DOMAINS_PATH}:ro`);
55695
55967
  if (config.node9.mountAgentCredentials) {
55696
55968
  const creds = agentCredentialsMount(config.agent);
55697
- if (fs65.existsSync(creds.hostPath)) {
55969
+ if (fs66.existsSync(creds.hostPath)) {
55698
55970
  args.push("-v", `${creds.hostPath}:${creds.target}`);
55699
55971
  }
55700
55972
  }
@@ -55712,30 +55984,30 @@ function imageContentHash(dockerfile, entrypoint) {
55712
55984
  return crypto9.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
55713
55985
  }
55714
55986
  function sandboxBuildDir(cwd = process.cwd()) {
55715
- return path61.join(cwd, ".node9", "sandbox", "build");
55987
+ return path62.join(cwd, ".node9", "sandbox", "build");
55716
55988
  }
55717
55989
  function writeBuildContext(cwd, dockerfile, entrypoint) {
55718
55990
  const dir = sandboxBuildDir(cwd);
55719
- fs65.mkdirSync(dir, { recursive: true });
55720
- fs65.writeFileSync(path61.join(dir, "Dockerfile"), dockerfile);
55721
- fs65.writeFileSync(path61.join(dir, "entrypoint.sh"), entrypoint);
55991
+ fs66.mkdirSync(dir, { recursive: true });
55992
+ fs66.writeFileSync(path62.join(dir, "Dockerfile"), dockerfile);
55993
+ fs66.writeFileSync(path62.join(dir, "entrypoint.sh"), entrypoint);
55722
55994
  return dir;
55723
55995
  }
55724
55996
  function writeAllowlist(cwd, hosts) {
55725
- const dir = path61.join(cwd, ".node9", "sandbox");
55726
- fs65.mkdirSync(dir, { recursive: true });
55727
- const p = path61.join(dir, "allowed-domains.txt");
55728
- fs65.writeFileSync(p, hosts.join("\n") + "\n");
55997
+ const dir = path62.join(cwd, ".node9", "sandbox");
55998
+ fs66.mkdirSync(dir, { recursive: true });
55999
+ const p = path62.join(dir, "allowed-domains.txt");
56000
+ fs66.writeFileSync(p, hosts.join("\n") + "\n");
55729
56001
  return p;
55730
56002
  }
55731
56003
  function resolveHomePath(p) {
55732
- return p.startsWith("~") ? path61.join(os55.homedir(), p.slice(1)) : path61.resolve(p);
56004
+ return p.startsWith("~") ? path62.join(os55.homedir(), p.slice(1)) : path62.resolve(p);
55733
56005
  }
55734
56006
 
55735
56007
  // src/cli/commands/sandbox.ts
55736
56008
  function seedDataDirConfig(dataDir, sandbox) {
55737
- fs66.mkdirSync(dataDir, { recursive: true });
55738
- const configPath = path62.join(dataDir, "config.json");
56009
+ fs67.mkdirSync(dataDir, { recursive: true });
56010
+ const configPath = path63.join(dataDir, "config.json");
55739
56011
  const seed = {
55740
56012
  settings: {
55741
56013
  approvers: {
@@ -55746,7 +56018,7 @@ function seedDataDirConfig(dataDir, sandbox) {
55746
56018
  }
55747
56019
  }
55748
56020
  };
55749
- fs66.writeFileSync(configPath, JSON.stringify(seed, null, 2), { mode: 384 });
56021
+ fs67.writeFileSync(configPath, JSON.stringify(seed, null, 2), { mode: 384 });
55750
56022
  }
55751
56023
  function registerSandboxCommand(program2, version2) {
55752
56024
  const node9Version2 = pinnedNode9Version(version2);
@@ -55754,13 +56026,13 @@ function registerSandboxCommand(program2, version2) {
55754
56026
  cmd.command("new").description(`Scaffold ${SANDBOX_CONFIG_FILE} in this project`).option("--agent <agent>", "claude (default) or codex", "claude").action((opts) => {
55755
56027
  const agent = opts.agent === "codex" ? "codex" : "claude";
55756
56028
  const p = sandboxConfigPath();
55757
- if (fs66.existsSync(p)) {
56029
+ if (fs67.existsSync(p)) {
55758
56030
  console.log(
55759
56031
  chalk33.yellow(` ${SANDBOX_CONFIG_FILE} already exists \u2014 leaving it untouched.`)
55760
56032
  );
55761
56033
  return;
55762
56034
  }
55763
- fs66.writeFileSync(p, scaffoldSandboxYaml(agent));
56035
+ fs67.writeFileSync(p, scaffoldSandboxYaml(agent));
55764
56036
  console.log(
55765
56037
  chalk33.green(` \u2713 wrote ${SANDBOX_CONFIG_FILE}`) + chalk33.dim(` (agent: ${agent})`)
55766
56038
  );
@@ -55800,8 +56072,8 @@ function registerSandboxCommand(program2, version2) {
55800
56072
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
55801
56073
  const hash = imageContentHash(dockerfile, entrypoint);
55802
56074
  const image = sandbox.runtime.image;
55803
- const hashFile = path62.join(sandboxBuildDir(cwd), ".image-hash");
55804
- const lastHash = fs66.existsSync(hashFile) ? fs66.readFileSync(hashFile, "utf-8").trim() : "";
56075
+ const hashFile = path63.join(sandboxBuildDir(cwd), ".image-hash");
56076
+ const lastHash = fs67.existsSync(hashFile) ? fs67.readFileSync(hashFile, "utf-8").trim() : "";
55805
56077
  const imageExists = spawnSync6(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
55806
56078
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
55807
56079
  if (needBuild) {
@@ -55813,7 +56085,7 @@ function registerSandboxCommand(program2, version2) {
55813
56085
  console.error(chalk33.red(" build failed."));
55814
56086
  process.exit(b.status ?? 1);
55815
56087
  }
55816
- fs66.writeFileSync(hashFile, hash);
56088
+ fs67.writeFileSync(hashFile, hash);
55817
56089
  }
55818
56090
  const dataDir = sandboxDataDir(cwd);
55819
56091
  seedDataDirConfig(dataDir, sandbox);
@@ -55827,7 +56099,7 @@ function registerSandboxCommand(program2, version2) {
55827
56099
  });
55828
56100
  if (sandbox.node9.mountAgentCredentials) {
55829
56101
  const creds = agentCredentialsMount(sandbox.agent);
55830
- if (fs66.existsSync(creds.hostPath)) {
56102
+ if (fs67.existsSync(creds.hostPath)) {
55831
56103
  console.log(chalk33.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
55832
56104
  } else {
55833
56105
  console.log(
@@ -55843,20 +56115,20 @@ function registerSandboxCommand(program2, version2) {
55843
56115
  process.exit(r.status ?? 0);
55844
56116
  });
55845
56117
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
55846
- const auditPath = path62.join(sandboxDataDir(), "audit.log");
55847
- if (!fs66.existsSync(auditPath)) {
56118
+ const auditPath = path63.join(sandboxDataDir(), "audit.log");
56119
+ if (!fs67.existsSync(auditPath)) {
55848
56120
  console.log(chalk33.dim(" no sandbox audit yet."));
55849
56121
  return;
55850
56122
  }
55851
56123
  spawnSync6("tail", ["-f", auditPath], { stdio: "inherit" });
55852
56124
  });
55853
56125
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
55854
- const auditPath = path62.join(sandboxDataDir(), "audit.log");
55855
- if (!fs66.existsSync(auditPath)) {
56126
+ const auditPath = path63.join(sandboxDataDir(), "audit.log");
56127
+ if (!fs67.existsSync(auditPath)) {
55856
56128
  console.log(chalk33.dim(" no sandbox audit yet."));
55857
56129
  return;
55858
56130
  }
55859
- process.stdout.write(fs66.readFileSync(auditPath, "utf-8"));
56131
+ process.stdout.write(fs67.readFileSync(auditPath, "utf-8"));
55860
56132
  });
55861
56133
  cmd.command("clean").description("Remove the sandbox image, build context, and data").action(() => {
55862
56134
  const cwd = process.cwd();
@@ -55870,7 +56142,7 @@ function registerSandboxCommand(program2, version2) {
55870
56142
  stdio: "ignore"
55871
56143
  });
55872
56144
  }
55873
- fs66.rmSync(path62.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
56145
+ fs67.rmSync(path63.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
55874
56146
  console.log(chalk33.green(" \u2713 sandbox image + build + data removed."));
55875
56147
  });
55876
56148
  }
@@ -55882,8 +56154,8 @@ init_litellm();
55882
56154
  init_cost_gemini();
55883
56155
  init_cost_codex();
55884
56156
  import chalk34 from "chalk";
55885
- import fs67 from "fs";
55886
- import path63 from "path";
56157
+ import fs68 from "fs";
56158
+ import path64 from "path";
55887
56159
  import os56 from "os";
55888
56160
  function modelPrice(model) {
55889
56161
  const t = pricingFor(model);
@@ -55901,7 +56173,7 @@ function encodeProjectPath(projectPath) {
55901
56173
  }
55902
56174
  function sessionJsonlPath(projectPath, sessionId) {
55903
56175
  const encoded = encodeProjectPath(projectPath);
55904
- return path63.join(os56.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
56176
+ return path64.join(os56.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
55905
56177
  }
55906
56178
  function projectLabel(projectPath) {
55907
56179
  return projectPath.replace(os56.homedir(), "~");
@@ -55973,10 +56245,10 @@ function parseSessionLines(lines) {
55973
56245
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
55974
56246
  }
55975
56247
  function loadAuditEntries(auditPath) {
55976
- const aPath = auditPath ?? path63.join(os56.homedir(), ".node9", "audit.log");
56248
+ const aPath = auditPath ?? path64.join(os56.homedir(), ".node9", "audit.log");
55977
56249
  let raw;
55978
56250
  try {
55979
- raw = fs67.readFileSync(aPath, "utf-8");
56251
+ raw = fs68.readFileSync(aPath, "utf-8");
55980
56252
  } catch {
55981
56253
  return [];
55982
56254
  }
@@ -56012,8 +56284,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
56012
56284
  return result;
56013
56285
  }
56014
56286
  function buildGeminiSessions(days, allAuditEntries) {
56015
- const tmpDir = path63.join(os56.homedir(), ".gemini", "tmp");
56016
- if (!fs67.existsSync(tmpDir)) return [];
56287
+ const tmpDir = path64.join(os56.homedir(), ".gemini", "tmp");
56288
+ if (!fs68.existsSync(tmpDir)) return [];
56017
56289
  const cutoff = days !== null ? (() => {
56018
56290
  const d = /* @__PURE__ */ new Date();
56019
56291
  d.setDate(d.getDate() - days);
@@ -56022,35 +56294,35 @@ function buildGeminiSessions(days, allAuditEntries) {
56022
56294
  })() : null;
56023
56295
  let slugDirs;
56024
56296
  try {
56025
- slugDirs = fs67.readdirSync(tmpDir);
56297
+ slugDirs = fs68.readdirSync(tmpDir);
56026
56298
  } catch {
56027
56299
  return [];
56028
56300
  }
56029
56301
  const summaries = [];
56030
56302
  for (const slug2 of slugDirs) {
56031
- const slugPath = path63.join(tmpDir, slug2);
56303
+ const slugPath = path64.join(tmpDir, slug2);
56032
56304
  try {
56033
- if (!fs67.statSync(slugPath).isDirectory()) continue;
56305
+ if (!fs68.statSync(slugPath).isDirectory()) continue;
56034
56306
  } catch {
56035
56307
  continue;
56036
56308
  }
56037
- let projectRoot = path63.join(os56.homedir(), slug2);
56309
+ let projectRoot = path64.join(os56.homedir(), slug2);
56038
56310
  try {
56039
- projectRoot = fs67.readFileSync(path63.join(slugPath, ".project_root"), "utf-8").trim();
56311
+ projectRoot = fs68.readFileSync(path64.join(slugPath, ".project_root"), "utf-8").trim();
56040
56312
  } catch {
56041
56313
  }
56042
- const chatsDir = path63.join(slugPath, "chats");
56043
- if (!fs67.existsSync(chatsDir)) continue;
56314
+ const chatsDir = path64.join(slugPath, "chats");
56315
+ if (!fs68.existsSync(chatsDir)) continue;
56044
56316
  let chatFiles;
56045
56317
  try {
56046
- chatFiles = fs67.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
56318
+ chatFiles = fs68.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
56047
56319
  } catch {
56048
56320
  continue;
56049
56321
  }
56050
56322
  for (const chatFile of chatFiles) {
56051
56323
  let raw;
56052
56324
  try {
56053
- raw = fs67.readFileSync(path63.join(chatsDir, chatFile), "utf-8");
56325
+ raw = fs68.readFileSync(path64.join(chatsDir, chatFile), "utf-8");
56054
56326
  } catch {
56055
56327
  continue;
56056
56328
  }
@@ -56130,8 +56402,8 @@ function buildGeminiSessions(days, allAuditEntries) {
56130
56402
  return summaries;
56131
56403
  }
56132
56404
  function buildCodexSessions(days, allAuditEntries) {
56133
- const sessionsBase = path63.join(os56.homedir(), ".codex", "sessions");
56134
- if (!fs67.existsSync(sessionsBase)) return [];
56405
+ const sessionsBase = path64.join(os56.homedir(), ".codex", "sessions");
56406
+ if (!fs68.existsSync(sessionsBase)) return [];
56135
56407
  const cutoff = days !== null ? (() => {
56136
56408
  const d = /* @__PURE__ */ new Date();
56137
56409
  d.setDate(d.getDate() - days);
@@ -56140,29 +56412,29 @@ function buildCodexSessions(days, allAuditEntries) {
56140
56412
  })() : null;
56141
56413
  const jsonlFiles = [];
56142
56414
  try {
56143
- for (const year of fs67.readdirSync(sessionsBase)) {
56144
- const yearPath = path63.join(sessionsBase, year);
56415
+ for (const year of fs68.readdirSync(sessionsBase)) {
56416
+ const yearPath = path64.join(sessionsBase, year);
56145
56417
  try {
56146
- if (!fs67.statSync(yearPath).isDirectory()) continue;
56418
+ if (!fs68.statSync(yearPath).isDirectory()) continue;
56147
56419
  } catch {
56148
56420
  continue;
56149
56421
  }
56150
- for (const month of fs67.readdirSync(yearPath)) {
56151
- const monthPath = path63.join(yearPath, month);
56422
+ for (const month of fs68.readdirSync(yearPath)) {
56423
+ const monthPath = path64.join(yearPath, month);
56152
56424
  try {
56153
- if (!fs67.statSync(monthPath).isDirectory()) continue;
56425
+ if (!fs68.statSync(monthPath).isDirectory()) continue;
56154
56426
  } catch {
56155
56427
  continue;
56156
56428
  }
56157
- for (const day of fs67.readdirSync(monthPath)) {
56158
- const dayPath = path63.join(monthPath, day);
56429
+ for (const day of fs68.readdirSync(monthPath)) {
56430
+ const dayPath = path64.join(monthPath, day);
56159
56431
  try {
56160
- if (!fs67.statSync(dayPath).isDirectory()) continue;
56432
+ if (!fs68.statSync(dayPath).isDirectory()) continue;
56161
56433
  } catch {
56162
56434
  continue;
56163
56435
  }
56164
- for (const file of fs67.readdirSync(dayPath)) {
56165
- if (file.endsWith(".jsonl")) jsonlFiles.push(path63.join(dayPath, file));
56436
+ for (const file of fs68.readdirSync(dayPath)) {
56437
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path64.join(dayPath, file));
56166
56438
  }
56167
56439
  }
56168
56440
  }
@@ -56174,7 +56446,7 @@ function buildCodexSessions(days, allAuditEntries) {
56174
56446
  for (const filePath of jsonlFiles) {
56175
56447
  let lines;
56176
56448
  try {
56177
- lines = fs67.readFileSync(filePath, "utf-8").split("\n");
56449
+ lines = fs68.readFileSync(filePath, "utf-8").split("\n");
56178
56450
  } catch {
56179
56451
  continue;
56180
56452
  }
@@ -56260,10 +56532,10 @@ function buildCodexSessions(days, allAuditEntries) {
56260
56532
  return summaries;
56261
56533
  }
56262
56534
  function buildSessions(days, historyPath) {
56263
- const hPath = historyPath ?? path63.join(os56.homedir(), ".claude", "history.jsonl");
56535
+ const hPath = historyPath ?? path64.join(os56.homedir(), ".claude", "history.jsonl");
56264
56536
  let historyRaw = "";
56265
56537
  try {
56266
- historyRaw = fs67.readFileSync(hPath, "utf-8");
56538
+ historyRaw = fs68.readFileSync(hPath, "utf-8");
56267
56539
  } catch {
56268
56540
  }
56269
56541
  const cutoff = days !== null ? (() => {
@@ -56287,7 +56559,7 @@ function buildSessions(days, historyPath) {
56287
56559
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
56288
56560
  let sessionLines = [];
56289
56561
  try {
56290
- sessionLines = fs67.readFileSync(jsonlFile, "utf-8").split("\n");
56562
+ sessionLines = fs68.readFileSync(jsonlFile, "utf-8").split("\n");
56291
56563
  } catch {
56292
56564
  }
56293
56565
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -56681,12 +56953,12 @@ function registerSessionTaintCommand(program2) {
56681
56953
 
56682
56954
  // src/cli/commands/skill-pin.ts
56683
56955
  import chalk36 from "chalk";
56684
- import fs68 from "fs";
56956
+ import fs69 from "fs";
56685
56957
  import os57 from "os";
56686
- import path64 from "path";
56958
+ import path65 from "path";
56687
56959
  function wipeSkillSessions() {
56688
56960
  try {
56689
- fs68.rmSync(path64.join(os57.homedir(), ".node9", "skill-sessions"), {
56961
+ fs69.rmSync(path65.join(os57.homedir(), ".node9", "skill-sessions"), {
56690
56962
  recursive: true,
56691
56963
  force: true
56692
56964
  });
@@ -56768,15 +57040,15 @@ function registerSkillPinCommand(program2) {
56768
57040
  }
56769
57041
 
56770
57042
  // src/cli/commands/decisions.ts
56771
- import fs69 from "fs";
57043
+ import fs70 from "fs";
56772
57044
  import os58 from "os";
56773
- import path65 from "path";
57045
+ import path66 from "path";
56774
57046
  import chalk37 from "chalk";
56775
- var DECISIONS_FILE2 = path65.join(os58.homedir(), ".node9", "decisions.json");
57047
+ var DECISIONS_FILE2 = path66.join(os58.homedir(), ".node9", "decisions.json");
56776
57048
  function readDecisions() {
56777
57049
  try {
56778
- if (!fs69.existsSync(DECISIONS_FILE2)) return {};
56779
- const raw = fs69.readFileSync(DECISIONS_FILE2, "utf-8");
57050
+ if (!fs70.existsSync(DECISIONS_FILE2)) return {};
57051
+ const raw = fs70.readFileSync(DECISIONS_FILE2, "utf-8");
56780
57052
  const parsed = JSON.parse(raw);
56781
57053
  const out = {};
56782
57054
  for (const [k, v] of Object.entries(parsed)) {
@@ -56788,11 +57060,11 @@ function readDecisions() {
56788
57060
  }
56789
57061
  }
56790
57062
  function writeDecisions(d) {
56791
- const dir = path65.dirname(DECISIONS_FILE2);
56792
- if (!fs69.existsSync(dir)) fs69.mkdirSync(dir, { recursive: true });
57063
+ const dir = path66.dirname(DECISIONS_FILE2);
57064
+ if (!fs70.existsSync(dir)) fs70.mkdirSync(dir, { recursive: true });
56793
57065
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
56794
- fs69.writeFileSync(tmp, JSON.stringify(d, null, 2));
56795
- fs69.renameSync(tmp, DECISIONS_FILE2);
57066
+ fs70.writeFileSync(tmp, JSON.stringify(d, null, 2));
57067
+ fs70.renameSync(tmp, DECISIONS_FILE2);
56796
57068
  }
56797
57069
  function registerDecisionsCommand(program2) {
56798
57070
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -56849,18 +57121,18 @@ Persistent decisions (${entries.length})
56849
57121
 
56850
57122
  // src/cli/commands/dlp.ts
56851
57123
  import chalk38 from "chalk";
56852
- import fs70 from "fs";
56853
- import path66 from "path";
57124
+ import fs71 from "fs";
57125
+ import path67 from "path";
56854
57126
  import os59 from "os";
56855
- var AUDIT_LOG = path66.join(os59.homedir(), ".node9", "audit.log");
56856
- var RESOLVED_FILE = path66.join(os59.homedir(), ".node9", "dlp-resolved.json");
57127
+ var AUDIT_LOG = path67.join(os59.homedir(), ".node9", "audit.log");
57128
+ var RESOLVED_FILE = path67.join(os59.homedir(), ".node9", "dlp-resolved.json");
56857
57129
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
56858
57130
  function stripAnsi(s) {
56859
57131
  return s.replace(ANSI_RE, "");
56860
57132
  }
56861
57133
  function loadResolved() {
56862
57134
  try {
56863
- const raw = JSON.parse(fs70.readFileSync(RESOLVED_FILE, "utf-8"));
57135
+ const raw = JSON.parse(fs71.readFileSync(RESOLVED_FILE, "utf-8"));
56864
57136
  return new Set(raw);
56865
57137
  } catch {
56866
57138
  return /* @__PURE__ */ new Set();
@@ -56868,13 +57140,13 @@ function loadResolved() {
56868
57140
  }
56869
57141
  function saveResolved(resolved) {
56870
57142
  try {
56871
- fs70.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
57143
+ fs71.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
56872
57144
  } catch {
56873
57145
  }
56874
57146
  }
56875
57147
  function loadDlpFindings() {
56876
- if (!fs70.existsSync(AUDIT_LOG)) return [];
56877
- return fs70.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
57148
+ if (!fs71.existsSync(AUDIT_LOG)) return [];
57149
+ return fs71.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
56878
57150
  if (!line.trim()) return [];
56879
57151
  try {
56880
57152
  const e = JSON.parse(line);
@@ -56973,14 +57245,14 @@ function registerDlpCommand(program2) {
56973
57245
  // src/cli/commands/mask.ts
56974
57246
  init_dlp();
56975
57247
  import chalk39 from "chalk";
56976
- import fs71 from "fs";
56977
- import path67 from "path";
57248
+ import fs72 from "fs";
57249
+ import path68 from "path";
56978
57250
  import os60 from "os";
56979
57251
  function findJsonlFiles(dir) {
56980
57252
  const results = [];
56981
- if (!fs71.existsSync(dir)) return results;
56982
- for (const entry of fs71.readdirSync(dir, { withFileTypes: true })) {
56983
- const full = path67.join(dir, entry.name);
57253
+ if (!fs72.existsSync(dir)) return results;
57254
+ for (const entry of fs72.readdirSync(dir, { withFileTypes: true })) {
57255
+ const full = path68.join(dir, entry.name);
56984
57256
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
56985
57257
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
56986
57258
  }
@@ -57023,7 +57295,7 @@ function redactJson(obj) {
57023
57295
  function processFile(filePath, dryRun) {
57024
57296
  let raw;
57025
57297
  try {
57026
- raw = fs71.readFileSync(filePath, "utf-8");
57298
+ raw = fs72.readFileSync(filePath, "utf-8");
57027
57299
  } catch {
57028
57300
  return { redactedLines: 0, patterns: [] };
57029
57301
  }
@@ -57055,14 +57327,14 @@ function processFile(filePath, dryRun) {
57055
57327
  }
57056
57328
  }
57057
57329
  if (!dryRun && redactedLines > 0) {
57058
- fs71.writeFileSync(filePath, newLines.join("\n"), "utf-8");
57330
+ fs72.writeFileSync(filePath, newLines.join("\n"), "utf-8");
57059
57331
  }
57060
57332
  return { redactedLines, patterns };
57061
57333
  }
57062
57334
  function processJsonFile(filePath, dryRun) {
57063
57335
  let raw;
57064
57336
  try {
57065
- raw = fs71.readFileSync(filePath, "utf-8");
57337
+ raw = fs72.readFileSync(filePath, "utf-8");
57066
57338
  } catch {
57067
57339
  return { redactedLines: 0, patterns: [] };
57068
57340
  }
@@ -57075,15 +57347,15 @@ function processJsonFile(filePath, dryRun) {
57075
57347
  const { value, modified, found } = redactJson(parsed);
57076
57348
  if (!modified) return { redactedLines: 0, patterns: [] };
57077
57349
  if (!dryRun) {
57078
- fs71.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
57350
+ fs72.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
57079
57351
  }
57080
57352
  return { redactedLines: 1, patterns: found };
57081
57353
  }
57082
57354
  function findJsonFiles(dir) {
57083
57355
  const results = [];
57084
- if (!fs71.existsSync(dir)) return results;
57085
- for (const entry of fs71.readdirSync(dir, { withFileTypes: true })) {
57086
- const full = path67.join(dir, entry.name);
57356
+ if (!fs72.existsSync(dir)) return results;
57357
+ for (const entry of fs72.readdirSync(dir, { withFileTypes: true })) {
57358
+ const full = path68.join(dir, entry.name);
57087
57359
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
57088
57360
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
57089
57361
  }
@@ -57093,8 +57365,8 @@ function registerMaskCommand(program2) {
57093
57365
  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) => {
57094
57366
  const dryRun = !!options.dryRun;
57095
57367
  const home = os60.homedir();
57096
- const claudeDir = path67.join(home, ".claude", "projects");
57097
- const geminiDir = path67.join(home, ".gemini", "tmp");
57368
+ const claudeDir = path68.join(home, ".claude", "projects");
57369
+ const geminiDir = path68.join(home, ".gemini", "tmp");
57098
57370
  const allFiles = [
57099
57371
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
57100
57372
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -57102,7 +57374,7 @@ function registerMaskCommand(program2) {
57102
57374
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
57103
57375
  const filtered = cutoff ? allFiles.filter((f) => {
57104
57376
  try {
57105
- return fs71.statSync(f.path).mtime >= cutoff;
57377
+ return fs72.statSync(f.path).mtime >= cutoff;
57106
57378
  } catch {
57107
57379
  return false;
57108
57380
  }
@@ -57158,7 +57430,7 @@ function registerMaskCommand(program2) {
57158
57430
  // src/cli.ts
57159
57431
  init_blast();
57160
57432
  var { version } = JSON.parse(
57161
- fs74.readFileSync(path70.join(__dirname, "../package.json"), "utf-8")
57433
+ fs75.readFileSync(path71.join(__dirname, "../package.json"), "utf-8")
57162
57434
  );
57163
57435
  var program = new Command();
57164
57436
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
@@ -57337,15 +57609,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
57337
57609
  } catch {
57338
57610
  }
57339
57611
  if (options.purge) {
57340
- const node9Dir = path70.join(os63.homedir(), ".node9");
57341
- if (fs74.existsSync(node9Dir)) {
57612
+ const node9Dir = path71.join(os63.homedir(), ".node9");
57613
+ if (fs75.existsSync(node9Dir)) {
57342
57614
  const confirmed = await confirm2({
57343
57615
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
57344
57616
  default: false
57345
57617
  });
57346
57618
  if (confirmed) {
57347
- fs74.rmSync(node9Dir, { recursive: true });
57348
- if (fs74.existsSync(node9Dir)) {
57619
+ fs75.rmSync(node9Dir, { recursive: true });
57620
+ if (fs75.existsSync(node9Dir)) {
57349
57621
  console.error(
57350
57622
  chalk41.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
57351
57623
  );
@@ -57470,7 +57742,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
57470
57742
  });
57471
57743
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
57472
57744
  try {
57473
- const dashboardPath = path70.join(__dirname, "dashboard.mjs");
57745
+ const dashboardPath = path71.join(__dirname, "dashboard.mjs");
57474
57746
  const dynamicImport = new Function("id", "return import(id)");
57475
57747
  const mod = await dynamicImport(`file://${dashboardPath}`);
57476
57748
  await mod.startMonitor();
@@ -57508,14 +57780,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
57508
57780
  Run "node9 addto claude" to register it as the statusLine.`
57509
57781
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
57510
57782
  if (subcommand === "debug") {
57511
- const flagFile = path70.join(os63.homedir(), ".node9", "hud-debug");
57783
+ const flagFile = path71.join(os63.homedir(), ".node9", "hud-debug");
57512
57784
  if (state === "on") {
57513
- fs74.mkdirSync(path70.dirname(flagFile), { recursive: true });
57514
- fs74.writeFileSync(flagFile, "");
57785
+ fs75.mkdirSync(path71.dirname(flagFile), { recursive: true });
57786
+ fs75.writeFileSync(flagFile, "");
57515
57787
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
57516
57788
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
57517
57789
  } else if (state === "off") {
57518
- if (fs74.existsSync(flagFile)) fs74.unlinkSync(flagFile);
57790
+ if (fs75.existsSync(flagFile)) fs75.unlinkSync(flagFile);
57519
57791
  console.log("HUD debug logging disabled.");
57520
57792
  } else {
57521
57793
  console.error("Usage: node9 hud debug on|off");
@@ -57638,9 +57910,9 @@ if (process.argv[2] !== "daemon") {
57638
57910
  const isCheckHook = process.argv[2] === "check";
57639
57911
  if (isCheckHook) {
57640
57912
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
57641
- const logPath = path70.join(os63.homedir(), ".node9", "hook-debug.log");
57913
+ const logPath = path71.join(os63.homedir(), ".node9", "hook-debug.log");
57642
57914
  const msg = reason instanceof Error ? reason.message : String(reason);
57643
- fs74.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
57915
+ fs75.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
57644
57916
  `);
57645
57917
  }
57646
57918
  process.exit(0);