@node9/proxy 1.61.0 → 1.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -248,8 +248,8 @@ function sanitizeConfig(raw) {
248
248
  }
249
249
  }
250
250
  const lines = result.error.issues.map((issue) => {
251
- const path70 = issue.path.length > 0 ? issue.path.join(".") : "root";
252
- return ` \u2022 ${path70}: ${issue.message}`;
251
+ const path71 = issue.path.length > 0 ? issue.path.join(".") : "root";
252
+ return ` \u2022 ${path71}: ${issue.message}`;
253
253
  });
254
254
  return {
255
255
  sanitized,
@@ -983,6 +983,91 @@ function analyzeFsOperation(command) {
983
983
  fsOpCache.set(normalized, computed);
984
984
  return computed;
985
985
  }
986
+ function isSensitiveCleanupName(p) {
987
+ const base = p.replace(/^.*[\\/]/, "");
988
+ return /^\.env(\.|$)/i.test(base) || /(?:^|[\\/])\.(?:ssh|aws|gnupg|git)(?:[\\/]|$)/i.test(p) || /\.(?:pem|key|p12|pfx|crt)$/i.test(base) || /^\.?(?:netrc|npmrc|pgpass|htpasswd)$/i.test(base) || /^id_(?:rsa|dsa|ecdsa|ed25519)/i.test(base) || /credential/i.test(p) || /secret/i.test(base);
989
+ }
990
+ function isWaivableCleanupTarget(p) {
991
+ if (/^[/~]/.test(p) || /^\$/.test(p)) return false;
992
+ if (/(?:^|[\\/])\.\.(?:[\\/]|$)/.test(p)) return false;
993
+ if (/[*?[{]/.test(p)) return false;
994
+ if (isSensitiveCleanupName(p)) return false;
995
+ return true;
996
+ }
997
+ function deriveRedirOp(sample) {
998
+ try {
999
+ const f = sharedParser.Parse(sample, "cmd");
1000
+ let op = -1;
1001
+ syntax.Walk(f, (node) => {
1002
+ const n = node;
1003
+ if (n && syntax.NodeType(n) === "Redirect" && op < 0) op = n.Op;
1004
+ return true;
1005
+ });
1006
+ return op;
1007
+ } catch {
1008
+ return -1;
1009
+ }
1010
+ }
1011
+ function collectSameCommandCreations(f) {
1012
+ const created = /* @__PURE__ */ new Set();
1013
+ try {
1014
+ const stmts = Array.isArray(f?.Stmts) ? f.Stmts : [];
1015
+ for (const stmt of stmts) {
1016
+ if (!stmt || !stmt.Cmd || syntax.NodeType(stmt.Cmd) !== "CallExpr") continue;
1017
+ const redirs = stmt.Redirs || [];
1018
+ if (!redirs.some((r) => r && REDIR_HEREDOC_OPS.has(r.Op))) continue;
1019
+ for (const r of redirs) {
1020
+ if (r && REDIR_TRUNCATE_OPS.has(r.Op) && r.N == null) {
1021
+ const w = resolveWordLiteral(r.Word);
1022
+ if (w) created.add(stripDotSlash(w));
1023
+ }
1024
+ }
1025
+ }
1026
+ } catch {
1027
+ return created;
1028
+ }
1029
+ return created;
1030
+ }
1031
+ function isRmCreatedInCommandCleanup(command) {
1032
+ if (!/\brm\b/.test(command)) return false;
1033
+ const f = parseShared(command);
1034
+ if (f === PARSE_FAIL) return false;
1035
+ const created = collectSameCommandCreations(f);
1036
+ if (created.size === 0) return false;
1037
+ let sawRm = false;
1038
+ let ok2 = true;
1039
+ try {
1040
+ syntax.Walk(f, (node) => {
1041
+ if (!node || !ok2) return false;
1042
+ const n = node;
1043
+ if (syntax.NodeType(n) !== "CallExpr") return true;
1044
+ const args = n.Args || [];
1045
+ const name = (resolveWordLiteral(args[0]) ?? "").toLowerCase();
1046
+ if (name !== "rm") return true;
1047
+ sawRm = true;
1048
+ const { flags, paths } = extractLiteralArgs(n);
1049
+ if (args.length - 1 > flags.length + paths.length) {
1050
+ ok2 = false;
1051
+ return false;
1052
+ }
1053
+ if (paths.length === 0) {
1054
+ ok2 = false;
1055
+ return false;
1056
+ }
1057
+ for (const p of paths) {
1058
+ const np = stripDotSlash(p);
1059
+ if (!created.has(np) || !isWaivableCleanupTarget(np)) {
1060
+ ok2 = false;
1061
+ return false;
1062
+ }
1063
+ }
1064
+ return true;
1065
+ });
1066
+ } catch {
1067
+ return false;
1068
+ }
1069
+ return sawRm && ok2;
1070
+ }
986
1071
  function analyzeFsOperationImpl(command) {
987
1072
  const f = parseShared(command);
988
1073
  if (f === PARSE_FAIL) return null;
@@ -1379,9 +1464,9 @@ function matchesPattern(text, patterns) {
1379
1464
  const withoutDotSlash = text.replace(/^\.\//, "");
1380
1465
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1381
1466
  }
1382
- function getNestedValue(obj, path70) {
1467
+ function getNestedValue(obj, path71) {
1383
1468
  if (!obj || typeof obj !== "object") return null;
1384
- const segments = path70.split(".");
1469
+ const segments = path71.split(".");
1385
1470
  for (const seg of segments) {
1386
1471
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1387
1472
  }
@@ -1553,8 +1638,9 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
1553
1638
  }
1554
1639
  }
1555
1640
  if (config.policy.smartRules.length > 0) {
1641
+ const rmCleanupWaiver = bashCommand !== null && isRmCreatedInCommandCleanup(bashCommand);
1556
1642
  const matches = config.policy.smartRules.filter(
1557
- (rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
1643
+ (rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && !(rmCleanupWaiver && rule.name === "review-rm" && rule.verdict === "review") && evaluateSmartConditions(args, rule)
1558
1644
  );
1559
1645
  const matchedRule = resolvePinned(matches);
1560
1646
  if (matchedRule) {
@@ -2295,7 +2381,7 @@ function* stringValues(obj, depth = 0) {
2295
2381
  }
2296
2382
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
2297
2383
  }
2298
- var MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
2384
+ var MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
2299
2385
  var init_dist = __esm({
2300
2386
  "packages/policy-engine/dist/index.mjs"() {
2301
2387
  "use strict";
@@ -2966,7 +3052,7 @@ var init_dist = __esm({
2966
3052
  "mongosh"
2967
3053
  ]);
2968
3054
  SQL_DDL_RE = /\b(DROP|TRUNCATE)\s+(TABLE|DATABASE|SCHEMA|INDEX)\b/i;
2969
- CHMOD_OPEN_PERM_TOKENS = /* @__PURE__ */ new Set(["777", "0777", "a+rwx", "+x"]);
3055
+ CHMOD_OPEN_PERM_TOKENS = /* @__PURE__ */ new Set(["777", "0777", "a+rwx"]);
2970
3056
  COMMAND_WRAPPERS = /* @__PURE__ */ new Set([
2971
3057
  "sudo",
2972
3058
  "doas",
@@ -3069,6 +3155,12 @@ var init_dist = __esm({
3069
3155
  };
3070
3156
  FS_OP_CACHE_MAX = 5e3;
3071
3157
  fsOpCache = /* @__PURE__ */ new Map();
3158
+ stripDotSlash = (p) => p.replace(/^\.\//, "");
3159
+ REDIR_TRUNCATE_OPS = /* @__PURE__ */ new Set([deriveRedirOp(">_f")]);
3160
+ REDIR_HEREDOC_OPS = /* @__PURE__ */ new Set([
3161
+ deriveRedirOp("cat <<X\nX"),
3162
+ deriveRedirOp("cat <<-X\nX")
3163
+ ]);
3072
3164
  DEFAULT_EGRESS_ALLOWLIST = [
3073
3165
  "*.github.com",
3074
3166
  "*.githubusercontent.com",
@@ -4757,10 +4849,10 @@ function getConfig(cwd) {
4757
4849
  }
4758
4850
  if (Array.isArray(mc.jailPaths)) {
4759
4851
  for (const jp of mc.jailPaths) {
4760
- const path70 = typeof jp?.path === "string" ? jp.path.trim() : "";
4761
- if (!path70) continue;
4852
+ const path71 = typeof jp?.path === "string" ? jp.path.trim() : "";
4853
+ if (!path71) continue;
4762
4854
  const verdict = jp?.verdict === "review" ? "review" : "block";
4763
- for (const r of pathRules(path70, verdict, "org-managed jail")) {
4855
+ for (const r of pathRules(path71, verdict, "org-managed jail")) {
4764
4856
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
4765
4857
  }
4766
4858
  }
@@ -17871,6 +17963,66 @@ function pickSyncIntervalMs(cloudHours, localSettings) {
17871
17963
  function effectiveSyncIntervalMs() {
17872
17964
  return pickSyncIntervalMs(readCachedSyncIntervalHours(), getConfig().settings);
17873
17965
  }
17966
+ function readSyncHealth() {
17967
+ try {
17968
+ const raw = JSON.parse(fs36.readFileSync(syncHealthFile(), "utf-8"));
17969
+ return {
17970
+ lastCheckedAt: typeof raw.lastCheckedAt === "string" ? raw.lastCheckedAt : void 0,
17971
+ lastChangedAt: typeof raw.lastChangedAt === "string" ? raw.lastChangedAt : void 0,
17972
+ lastError: typeof raw.lastError === "string" ? raw.lastError : void 0,
17973
+ lastErrorAt: typeof raw.lastErrorAt === "string" ? raw.lastErrorAt : void 0,
17974
+ consecutiveFailures: typeof raw.consecutiveFailures === "number" && raw.consecutiveFailures >= 0 ? raw.consecutiveFailures : 0
17975
+ };
17976
+ } catch {
17977
+ return { consecutiveFailures: 0 };
17978
+ }
17979
+ }
17980
+ function writeSyncHealth(h) {
17981
+ try {
17982
+ const file = syncHealthFile();
17983
+ const dir = path35.dirname(file);
17984
+ if (!fs36.existsSync(dir)) fs36.mkdirSync(dir, { recursive: true });
17985
+ const tmp = `${file}.${process.pid}.tmp`;
17986
+ fs36.writeFileSync(tmp, JSON.stringify(h, null, 2) + "\n", "utf-8");
17987
+ fs36.renameSync(tmp, file);
17988
+ } catch {
17989
+ }
17990
+ }
17991
+ function readCacheFetchedAt() {
17992
+ try {
17993
+ const raw = JSON.parse(fs36.readFileSync(rulesCacheFile(), "utf-8"));
17994
+ return typeof raw.fetchedAt === "string" ? raw.fetchedAt : void 0;
17995
+ } catch {
17996
+ return void 0;
17997
+ }
17998
+ }
17999
+ function recordSyncHealth(result) {
18000
+ const h = readSyncHealth();
18001
+ const now = (/* @__PURE__ */ new Date()).toISOString();
18002
+ if (result.ok) {
18003
+ h.lastCheckedAt = now;
18004
+ if (result.changed) h.lastChangedAt = now;
18005
+ h.consecutiveFailures = 0;
18006
+ h.lastError = void 0;
18007
+ h.lastErrorAt = void 0;
18008
+ } else {
18009
+ h.consecutiveFailures += 1;
18010
+ h.lastError = result.error;
18011
+ h.lastErrorAt = now;
18012
+ }
18013
+ writeSyncHealth(h);
18014
+ }
18015
+ function stalenessThresholdMs(intervalMs) {
18016
+ return Math.min(STALE_MAX_MS, Math.max(STALE_MIN_MS, intervalMs * STALE_FACTOR));
18017
+ }
18018
+ function isPolicyStale(nowMs = Date.now(), health) {
18019
+ const h = health ?? readSyncHealth();
18020
+ const lastKnownGood = h.lastCheckedAt ?? readCacheFetchedAt();
18021
+ if (!lastKnownGood) return false;
18022
+ const last = Date.parse(lastKnownGood);
18023
+ if (Number.isNaN(last)) return false;
18024
+ return nowMs - last > stalenessThresholdMs(effectiveSyncIntervalMs());
18025
+ }
17874
18026
  function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
17875
18027
  const parsed = new URL(apiUrl);
17876
18028
  const headers = {
@@ -18035,6 +18187,7 @@ async function syncOnce() {
18035
18187
  try {
18036
18188
  const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
18037
18189
  if (result.kind === "unchanged") {
18190
+ recordSyncHealth({ ok: true });
18038
18191
  } else {
18039
18192
  const cache = {
18040
18193
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -18048,8 +18201,19 @@ async function syncOnce() {
18048
18201
  managedConfig: extractManagedConfig(result.body)
18049
18202
  };
18050
18203
  writeCache2(cache);
18204
+ recordSyncHealth({ ok: true, changed: true });
18205
+ }
18206
+ } catch (err2) {
18207
+ const msg = err2 instanceof Error ? err2.message : String(err2);
18208
+ recordSyncHealth({ ok: false, error: msg });
18209
+ try {
18210
+ appendToLog(HOOK_DEBUG_LOG, {
18211
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
18212
+ kind: "policy-sync-error",
18213
+ error: msg
18214
+ });
18215
+ } catch {
18051
18216
  }
18052
- } catch {
18053
18217
  }
18054
18218
  if (process.env.NODE9_BLAST_DISABLE !== "1") {
18055
18219
  void pushBlastSnapshot(creds);
@@ -18225,6 +18389,7 @@ async function runCloudSync() {
18225
18389
  const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
18226
18390
  if (result.kind === "unchanged") {
18227
18391
  const status = getCloudSyncStatus();
18392
+ recordSyncHealth({ ok: true });
18228
18393
  maybePushBlast();
18229
18394
  return status.cached ? { ok: true, rules: status.rules, fetchedAt: status.fetchedAt, unchanged: true } : { ok: true, rules: 0, fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), unchanged: true };
18230
18395
  }
@@ -18240,11 +18405,14 @@ async function runCloudSync() {
18240
18405
  managedConfig: extractManagedConfig(result.body)
18241
18406
  };
18242
18407
  writeCache2(cache);
18408
+ recordSyncHealth({ ok: true, changed: true });
18243
18409
  maybePushBlast();
18244
18410
  return { ok: true, rules: cache.rules.length, fetchedAt: cache.fetchedAt };
18245
18411
  } catch (err2) {
18412
+ const msg = err2 instanceof Error ? err2.message : String(err2);
18413
+ recordSyncHealth({ ok: false, error: msg });
18246
18414
  maybePushBlast();
18247
- return { ok: false, reason: err2 instanceof Error ? err2.message : String(err2) };
18415
+ return { ok: false, reason: msg };
18248
18416
  }
18249
18417
  }
18250
18418
  function getCloudSyncStatus() {
@@ -18301,7 +18469,7 @@ function startForensicBroadcast() {
18301
18469
  const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
18302
18470
  recurring.unref();
18303
18471
  }
18304
- var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL2, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_SECONDS, MAX_INTERVAL_SECONDS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
18472
+ var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL2, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_SECONDS, MAX_INTERVAL_SECONDS, syncHealthFile, STALE_MIN_MS, STALE_MAX_MS, STALE_FACTOR, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
18305
18473
  var init_sync = __esm({
18306
18474
  "src/daemon/sync.ts"() {
18307
18475
  "use strict";
@@ -18335,6 +18503,10 @@ var init_sync = __esm({
18335
18503
  DEFAULT_INTERVAL_HOURS = 5;
18336
18504
  MIN_INTERVAL_SECONDS = 15;
18337
18505
  MAX_INTERVAL_SECONDS = 24 * 60 * 60;
18506
+ syncHealthFile = () => path35.join(os33.homedir(), ".node9", "sync-health.json");
18507
+ STALE_MIN_MS = 3 * 60 * 60 * 1e3;
18508
+ STALE_MAX_MS = 24 * 60 * 60 * 1e3;
18509
+ STALE_FACTOR = 3;
18338
18510
  FORENSIC_BROADCAST_INTERVAL_MS = 3e4;
18339
18511
  FORENSIC_INITIAL_DELAY_MS = 5e3;
18340
18512
  forensicBroadcastOffsets = /* @__PURE__ */ new Map();
@@ -18949,23 +19121,68 @@ var init_hook_heal = __esm({
18949
19121
  }
18950
19122
  });
18951
19123
 
18952
- // src/daemon/server.ts
18953
- import http3 from "http";
19124
+ // src/daemon/startup-log.ts
18954
19125
  import fs40 from "fs";
18955
19126
  import path39 from "path";
18956
19127
  import os37 from "os";
19128
+ function openStartupLogFd() {
19129
+ try {
19130
+ const file = DAEMON_STARTUP_LOG();
19131
+ const dir = path39.dirname(file);
19132
+ if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19133
+ try {
19134
+ if (fs40.statSync(file).size > MAX_STARTUP_LOG_BYTES) fs40.truncateSync(file);
19135
+ } catch {
19136
+ }
19137
+ return fs40.openSync(file, "a");
19138
+ } catch {
19139
+ return void 0;
19140
+ }
19141
+ }
19142
+ function logDaemonStartup(kind, detail) {
19143
+ try {
19144
+ const file = DAEMON_STARTUP_LOG();
19145
+ const dir = path39.dirname(file);
19146
+ if (!fs40.existsSync(dir)) fs40.mkdirSync(dir, { recursive: true });
19147
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-startup:${kind}${detail ? ` ${detail}` : ""}
19148
+ `;
19149
+ fs40.appendFileSync(file, line, "utf-8");
19150
+ } catch {
19151
+ }
19152
+ }
19153
+ var DAEMON_STARTUP_LOG, MAX_STARTUP_LOG_BYTES;
19154
+ var init_startup_log = __esm({
19155
+ "src/daemon/startup-log.ts"() {
19156
+ "use strict";
19157
+ DAEMON_STARTUP_LOG = () => path39.join(os37.homedir(), ".node9", "daemon-startup.log");
19158
+ MAX_STARTUP_LOG_BYTES = 256 * 1024;
19159
+ }
19160
+ });
19161
+
19162
+ // src/daemon/server.ts
19163
+ import http3 from "http";
19164
+ import fs41 from "fs";
19165
+ import path40 from "path";
19166
+ import os38 from "os";
18957
19167
  import { randomUUID as randomUUID4 } from "crypto";
18958
19168
  import { spawnSync } from "child_process";
18959
19169
  import chalk6 from "chalk";
18960
19170
  function startDaemon() {
18961
- startCostSync();
18962
- startCloudSync();
18963
- startForensicBroadcast();
18964
- startAuditShipper();
18965
- startDlpScanner();
18966
- startMcpReconciler();
18967
- startHookHeal();
18968
- loadInsightCounts();
19171
+ try {
19172
+ startCostSync();
19173
+ startCloudSync();
19174
+ startForensicBroadcast();
19175
+ startAuditShipper();
19176
+ startDlpScanner();
19177
+ startMcpReconciler();
19178
+ startHookHeal();
19179
+ loadInsightCounts();
19180
+ } catch (err2) {
19181
+ const stack = err2 instanceof Error ? err2.stack ?? err2.message : String(err2);
19182
+ console.error("\n\u{1F6D1} Node9 daemon startup failed:\n" + stack);
19183
+ logDaemonStartup("startup-throw", err2 instanceof Error ? err2.message : String(err2));
19184
+ process.exit(1);
19185
+ }
18969
19186
  const internalToken = randomUUID4();
18970
19187
  const validToken = (req) => req.headers["x-node9-internal"] === internalToken || req.headers["x-node9-token"] === internalToken;
18971
19188
  const IDLE_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
@@ -18977,7 +19194,7 @@ function startDaemon() {
18977
19194
  idleTimer = setTimeout(() => {
18978
19195
  if (autoStarted) {
18979
19196
  try {
18980
- fs40.unlinkSync(DAEMON_PID_FILE);
19197
+ fs41.unlinkSync(DAEMON_PID_FILE);
18981
19198
  } catch {
18982
19199
  }
18983
19200
  }
@@ -19122,7 +19339,7 @@ data: ${JSON.stringify(item.data)}
19122
19339
  mcpServer: entry.mcpServer
19123
19340
  });
19124
19341
  }
19125
- const projectCwd = typeof cwd === "string" && path39.isAbsolute(cwd) ? cwd : void 0;
19342
+ const projectCwd = typeof cwd === "string" && path40.isAbsolute(cwd) ? cwd : void 0;
19126
19343
  const projectConfig = getConfig(projectCwd);
19127
19344
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
19128
19345
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -19414,8 +19631,8 @@ data: ${JSON.stringify(item.data)}
19414
19631
  if (!validToken(req)) return res.writeHead(403).end();
19415
19632
  const periodParam = reqUrl.searchParams.get("period") || "7d";
19416
19633
  const period = ["today", "7d", "30d", "month"].includes(periodParam) ? periodParam : "7d";
19417
- const logPath = path39.join(os37.homedir(), ".node9", "audit.log");
19418
- if (!fs40.existsSync(logPath)) {
19634
+ const logPath = path40.join(os38.homedir(), ".node9", "audit.log");
19635
+ if (!fs41.existsSync(logPath)) {
19419
19636
  res.writeHead(200, { "Content-Type": "application/json" });
19420
19637
  return res.end(
19421
19638
  JSON.stringify({
@@ -19428,7 +19645,7 @@ data: ${JSON.stringify(item.data)}
19428
19645
  );
19429
19646
  }
19430
19647
  try {
19431
- const raw = fs40.readFileSync(logPath, "utf-8");
19648
+ const raw = fs41.readFileSync(logPath, "utf-8");
19432
19649
  const allEntries = raw.split("\n").flatMap((line) => {
19433
19650
  if (!line.trim()) return [];
19434
19651
  try {
@@ -19811,14 +20028,15 @@ data: ${JSON.stringify(item.data)}
19811
20028
  server.on("error", (e) => {
19812
20029
  if (e.code === "EADDRINUSE") {
19813
20030
  try {
19814
- if (fs40.existsSync(DAEMON_PID_FILE)) {
19815
- const { pid } = JSON.parse(fs40.readFileSync(DAEMON_PID_FILE, "utf-8"));
20031
+ if (fs41.existsSync(DAEMON_PID_FILE)) {
20032
+ const { pid } = JSON.parse(fs41.readFileSync(DAEMON_PID_FILE, "utf-8"));
19816
20033
  process.kill(pid, 0);
20034
+ logDaemonStartup("port-in-use", `another daemon (pid ${pid}) owns :${DAEMON_PORT}`);
19817
20035
  return process.exit(0);
19818
20036
  }
19819
20037
  } catch {
19820
20038
  try {
19821
- fs40.unlinkSync(DAEMON_PID_FILE);
20039
+ fs41.unlinkSync(DAEMON_PID_FILE);
19822
20040
  } catch {
19823
20041
  }
19824
20042
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -19867,6 +20085,7 @@ data: ${JSON.stringify(item.data)}
19867
20085
  });
19868
20086
  return;
19869
20087
  }
20088
+ logDaemonStartup("bind-failed", e.message);
19870
20089
  console.error(chalk6.red("\n\u{1F6D1} Node9 Daemon Error:"), e.message);
19871
20090
  process.exit(1);
19872
20091
  });
@@ -19904,20 +20123,21 @@ var init_server = __esm({
19904
20123
  init_dlp_scanner();
19905
20124
  init_mcp_reconciler();
19906
20125
  init_hook_heal();
20126
+ init_startup_log();
19907
20127
  init_mcp_tools();
19908
20128
  }
19909
20129
  });
19910
20130
 
19911
20131
  // src/daemon/service.ts
19912
- import fs41 from "fs";
19913
- import path40 from "path";
19914
- import os38 from "os";
20132
+ import fs42 from "fs";
20133
+ import path41 from "path";
20134
+ import os39 from "os";
19915
20135
  import { spawnSync as spawnSync2, execFileSync } from "child_process";
19916
20136
  function resolveNode9Binary() {
19917
20137
  try {
19918
20138
  const script = process.argv[1];
19919
- if (typeof script === "string" && path40.isAbsolute(script) && fs41.existsSync(script)) {
19920
- return fs41.realpathSync(script);
20139
+ if (typeof script === "string" && path41.isAbsolute(script) && fs42.existsSync(script)) {
20140
+ return fs42.realpathSync(script);
19921
20141
  }
19922
20142
  } catch {
19923
20143
  }
@@ -19935,11 +20155,11 @@ function xmlEscape(s) {
19935
20155
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
19936
20156
  }
19937
20157
  function launchdPlist(binaryPath) {
19938
- const logDir = path40.join(os38.homedir(), ".node9");
20158
+ const logDir = path41.join(os39.homedir(), ".node9");
19939
20159
  const nodePath = xmlEscape(process.execPath);
19940
20160
  const scriptPath = xmlEscape(binaryPath);
19941
- const outLog = xmlEscape(path40.join(logDir, "daemon.log"));
19942
- const errLog = xmlEscape(path40.join(logDir, "daemon-error.log"));
20161
+ const outLog = xmlEscape(path41.join(logDir, "daemon.log"));
20162
+ const errLog = xmlEscape(path41.join(logDir, "daemon-error.log"));
19943
20163
  return `<?xml version="1.0" encoding="UTF-8"?>
19944
20164
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
19945
20165
  <plist version="1.0">
@@ -19972,9 +20192,9 @@ function launchdPlist(binaryPath) {
19972
20192
  `;
19973
20193
  }
19974
20194
  function installLaunchd(binaryPath) {
19975
- const dir = path40.dirname(LAUNCHD_PLIST);
19976
- if (!fs41.existsSync(dir)) fs41.mkdirSync(dir, { recursive: true });
19977
- fs41.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
20195
+ const dir = path41.dirname(LAUNCHD_PLIST);
20196
+ if (!fs42.existsSync(dir)) fs42.mkdirSync(dir, { recursive: true });
20197
+ fs42.writeFileSync(LAUNCHD_PLIST, launchdPlist(binaryPath), "utf-8");
19978
20198
  spawnSync2("launchctl", ["unload", LAUNCHD_PLIST], { encoding: "utf8" });
19979
20199
  const r = spawnSync2("launchctl", ["load", "-w", LAUNCHD_PLIST], {
19980
20200
  encoding: "utf8",
@@ -19985,13 +20205,13 @@ function installLaunchd(binaryPath) {
19985
20205
  }
19986
20206
  }
19987
20207
  function uninstallLaunchd() {
19988
- if (fs41.existsSync(LAUNCHD_PLIST)) {
20208
+ if (fs42.existsSync(LAUNCHD_PLIST)) {
19989
20209
  spawnSync2("launchctl", ["unload", "-w", LAUNCHD_PLIST], { encoding: "utf8", timeout: 5e3 });
19990
- fs41.unlinkSync(LAUNCHD_PLIST);
20210
+ fs42.unlinkSync(LAUNCHD_PLIST);
19991
20211
  }
19992
20212
  }
19993
20213
  function isLaunchdInstalled() {
19994
- return fs41.existsSync(LAUNCHD_PLIST);
20214
+ return fs42.existsSync(LAUNCHD_PLIST);
19995
20215
  }
19996
20216
  function systemdUnit(binaryPath) {
19997
20217
  return `[Unit]
@@ -20010,12 +20230,12 @@ WantedBy=default.target
20010
20230
  `;
20011
20231
  }
20012
20232
  function installSystemd(binaryPath) {
20013
- if (!fs41.existsSync(SYSTEMD_UNIT_DIR)) {
20014
- fs41.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
20233
+ if (!fs42.existsSync(SYSTEMD_UNIT_DIR)) {
20234
+ fs42.mkdirSync(SYSTEMD_UNIT_DIR, { recursive: true });
20015
20235
  }
20016
- fs41.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
20236
+ fs42.writeFileSync(SYSTEMD_UNIT, systemdUnit(binaryPath), "utf-8");
20017
20237
  try {
20018
- execFileSync("loginctl", ["enable-linger", os38.userInfo().username], { timeout: 3e3 });
20238
+ execFileSync("loginctl", ["enable-linger", os39.userInfo().username], { timeout: 3e3 });
20019
20239
  } catch {
20020
20240
  }
20021
20241
  const reload = spawnSync2("systemctl", ["--user", "daemon-reload"], {
@@ -20035,23 +20255,23 @@ function installSystemd(binaryPath) {
20035
20255
  }
20036
20256
  }
20037
20257
  function uninstallSystemd() {
20038
- if (fs41.existsSync(SYSTEMD_UNIT)) {
20258
+ if (fs42.existsSync(SYSTEMD_UNIT)) {
20039
20259
  spawnSync2("systemctl", ["--user", "disable", "--now", "node9-daemon"], {
20040
20260
  encoding: "utf8",
20041
20261
  timeout: 5e3
20042
20262
  });
20043
20263
  spawnSync2("systemctl", ["--user", "daemon-reload"], { encoding: "utf8", timeout: 5e3 });
20044
- fs41.unlinkSync(SYSTEMD_UNIT);
20264
+ fs42.unlinkSync(SYSTEMD_UNIT);
20045
20265
  }
20046
20266
  }
20047
20267
  function isSystemdInstalled() {
20048
- return fs41.existsSync(SYSTEMD_UNIT);
20268
+ return fs42.existsSync(SYSTEMD_UNIT);
20049
20269
  }
20050
20270
  function stopRunningDaemon() {
20051
- const pidFile = path40.join(os38.homedir(), ".node9", "daemon.pid");
20052
- if (!fs41.existsSync(pidFile)) return;
20271
+ const pidFile = path41.join(os39.homedir(), ".node9", "daemon.pid");
20272
+ if (!fs42.existsSync(pidFile)) return;
20053
20273
  try {
20054
- const data = JSON.parse(fs41.readFileSync(pidFile, "utf-8"));
20274
+ const data = JSON.parse(fs42.readFileSync(pidFile, "utf-8"));
20055
20275
  const pid = data.pid;
20056
20276
  const MAX_PID2 = 4194304;
20057
20277
  if (typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid <= MAX_PID2) {
@@ -20071,7 +20291,7 @@ function stopRunningDaemon() {
20071
20291
  }
20072
20292
  }
20073
20293
  try {
20074
- fs41.unlinkSync(pidFile);
20294
+ fs42.unlinkSync(pidFile);
20075
20295
  } catch {
20076
20296
  }
20077
20297
  } catch {
@@ -20141,24 +20361,93 @@ function isDaemonServiceInstalled() {
20141
20361
  if (process.platform === "linux") return isSystemdInstalled();
20142
20362
  return false;
20143
20363
  }
20364
+ function autostartRepairDecision(opts) {
20365
+ if (!opts.autoStartDaemon) return "skip";
20366
+ if (process.platform !== "linux" && process.platform !== "darwin") return "unsupported";
20367
+ if (!opts.installed) return "skip";
20368
+ return opts.enabled ? "ok" : "repair";
20369
+ }
20370
+ function enableDaemonServiceQuiet() {
20371
+ try {
20372
+ if (process.platform === "linux") {
20373
+ const r = spawnSync2("systemctl", ["--user", "enable", "node9-daemon"], {
20374
+ encoding: "utf8",
20375
+ timeout: 3e3
20376
+ });
20377
+ return r.status === 0;
20378
+ }
20379
+ return process.platform === "darwin";
20380
+ } catch {
20381
+ return false;
20382
+ }
20383
+ }
20384
+ function ensureAutostartHealthy(autoStartDaemon) {
20385
+ const decision = autostartRepairDecision({
20386
+ installed: isDaemonServiceInstalled(),
20387
+ enabled: isDaemonServiceEnabled(),
20388
+ autoStartDaemon
20389
+ });
20390
+ if (decision === "repair") return enableDaemonServiceQuiet() ? "repaired" : "skipped";
20391
+ return decision === "ok" ? "ok" : decision === "unsupported" ? "unsupported" : "skipped";
20392
+ }
20393
+ function autostartAdvice(opts) {
20394
+ const installable = process.platform === "linux" || process.platform === "darwin";
20395
+ if (!opts.cloudEnabled || !installable) return null;
20396
+ const installHint = process.platform === "linux" ? "Run: systemctl --user enable --now node9-daemon (or: node9 daemon install)" : "Run: node9 daemon install";
20397
+ if (opts.installed && !opts.enabled) {
20398
+ return {
20399
+ level: "warn",
20400
+ message: "Daemon autostart is INSTALLED but DISABLED \u2014 it will NOT survive a reboot, so cloud policy can silently go stale.",
20401
+ hint: installHint
20402
+ };
20403
+ }
20404
+ if (!opts.installed) {
20405
+ return {
20406
+ level: "warn",
20407
+ message: "No daemon autostart installed \u2014 the daemon only runs when an agent happens to spawn it; cloud policy may lag.",
20408
+ hint: installHint
20409
+ };
20410
+ }
20411
+ return null;
20412
+ }
20413
+ function isDaemonServiceEnabled() {
20414
+ try {
20415
+ if (process.platform === "linux") {
20416
+ const r = spawnSync2("systemctl", ["--user", "is-enabled", "node9-daemon"], {
20417
+ encoding: "utf8",
20418
+ timeout: 3e3
20419
+ });
20420
+ return r.status === 0 && (r.stdout ?? "").trim() === "enabled";
20421
+ }
20422
+ if (process.platform === "darwin") {
20423
+ const r = spawnSync2("launchctl", ["list", LAUNCHD_LABEL], {
20424
+ encoding: "utf8",
20425
+ timeout: 3e3
20426
+ });
20427
+ return r.status === 0;
20428
+ }
20429
+ } catch {
20430
+ }
20431
+ return false;
20432
+ }
20144
20433
  var LAUNCHD_LABEL, LAUNCHD_PLIST, SYSTEMD_UNIT_DIR, SYSTEMD_UNIT;
20145
20434
  var init_service = __esm({
20146
20435
  "src/daemon/service.ts"() {
20147
20436
  "use strict";
20148
20437
  LAUNCHD_LABEL = "ai.node9.daemon";
20149
- LAUNCHD_PLIST = path40.join(os38.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
20150
- SYSTEMD_UNIT_DIR = path40.join(os38.homedir(), ".config", "systemd", "user");
20151
- SYSTEMD_UNIT = path40.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
20438
+ LAUNCHD_PLIST = path41.join(os39.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
20439
+ SYSTEMD_UNIT_DIR = path41.join(os39.homedir(), ".config", "systemd", "user");
20440
+ SYSTEMD_UNIT = path41.join(SYSTEMD_UNIT_DIR, "node9-daemon.service");
20152
20441
  }
20153
20442
  });
20154
20443
 
20155
20444
  // src/daemon/index.ts
20156
- import fs42 from "fs";
20445
+ import fs43 from "fs";
20157
20446
  import chalk7 from "chalk";
20158
20447
  function stopDaemon() {
20159
- if (!fs42.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
20448
+ if (!fs43.existsSync(DAEMON_PID_FILE)) return console.log(chalk7.yellow("Not running."));
20160
20449
  try {
20161
- const data = JSON.parse(fs42.readFileSync(DAEMON_PID_FILE, "utf-8"));
20450
+ const data = JSON.parse(fs43.readFileSync(DAEMON_PID_FILE, "utf-8"));
20162
20451
  const pid = data.pid;
20163
20452
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
20164
20453
  console.log(chalk7.gray("Cleaned up invalid PID file."));
@@ -20170,7 +20459,7 @@ function stopDaemon() {
20170
20459
  console.log(chalk7.gray("Cleaned up stale PID file."));
20171
20460
  } finally {
20172
20461
  try {
20173
- fs42.unlinkSync(DAEMON_PID_FILE);
20462
+ fs43.unlinkSync(DAEMON_PID_FILE);
20174
20463
  } catch {
20175
20464
  }
20176
20465
  }
@@ -20179,9 +20468,9 @@ function daemonStatus() {
20179
20468
  const serviceInstalled = isDaemonServiceInstalled();
20180
20469
  const serviceLabel = serviceInstalled ? chalk7.green("installed (starts on login)") : chalk7.yellow("not installed \u2014 run: node9 daemon install");
20181
20470
  let processStatus;
20182
- if (fs42.existsSync(DAEMON_PID_FILE)) {
20471
+ if (fs43.existsSync(DAEMON_PID_FILE)) {
20183
20472
  try {
20184
- const data = JSON.parse(fs42.readFileSync(DAEMON_PID_FILE, "utf-8"));
20473
+ const data = JSON.parse(fs43.readFileSync(DAEMON_PID_FILE, "utf-8"));
20185
20474
  const pid = data.pid;
20186
20475
  const port = data.port;
20187
20476
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0 || pid > MAX_PID) {
@@ -21324,14 +21613,14 @@ var require_util = __commonJS({
21324
21613
  }
21325
21614
  const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
21326
21615
  let origin = url.origin != null ? url.origin : `${url.protocol || ""}//${url.hostname || ""}:${port}`;
21327
- let path70 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
21616
+ let path71 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
21328
21617
  if (origin[origin.length - 1] === "/") {
21329
21618
  origin = origin.slice(0, origin.length - 1);
21330
21619
  }
21331
- if (path70 && path70[0] !== "/") {
21332
- path70 = `/${path70}`;
21620
+ if (path71 && path71[0] !== "/") {
21621
+ path71 = `/${path71}`;
21333
21622
  }
21334
- return new URL(`${origin}${path70}`);
21623
+ return new URL(`${origin}${path71}`);
21335
21624
  }
21336
21625
  if (!isHttpOrHttpsPrefixed(url.origin || url.protocol)) {
21337
21626
  throw new InvalidArgumentError("Invalid URL protocol: the URL must start with `http:` or `https:`.");
@@ -22152,9 +22441,9 @@ var require_diagnostics = __commonJS({
22152
22441
  "undici:client:sendHeaders",
22153
22442
  (evt) => {
22154
22443
  const {
22155
- request: { method, path: path70, origin }
22444
+ request: { method, path: path71, origin }
22156
22445
  } = evt;
22157
- debugLog("sending request to %s %s%s", method, origin, path70);
22446
+ debugLog("sending request to %s %s%s", method, origin, path71);
22158
22447
  }
22159
22448
  );
22160
22449
  }
@@ -22172,14 +22461,14 @@ var require_diagnostics = __commonJS({
22172
22461
  "undici:request:headers",
22173
22462
  (evt) => {
22174
22463
  const {
22175
- request: { method, path: path70, origin },
22464
+ request: { method, path: path71, origin },
22176
22465
  response: { statusCode }
22177
22466
  } = evt;
22178
22467
  debugLog(
22179
22468
  "received response to %s %s%s - HTTP %d",
22180
22469
  method,
22181
22470
  origin,
22182
- path70,
22471
+ path71,
22183
22472
  statusCode
22184
22473
  );
22185
22474
  }
@@ -22188,23 +22477,23 @@ var require_diagnostics = __commonJS({
22188
22477
  "undici:request:trailers",
22189
22478
  (evt) => {
22190
22479
  const {
22191
- request: { method, path: path70, origin }
22480
+ request: { method, path: path71, origin }
22192
22481
  } = evt;
22193
- debugLog("trailers received from %s %s%s", method, origin, path70);
22482
+ debugLog("trailers received from %s %s%s", method, origin, path71);
22194
22483
  }
22195
22484
  );
22196
22485
  diagnosticsChannel.subscribe(
22197
22486
  "undici:request:error",
22198
22487
  (evt) => {
22199
22488
  const {
22200
- request: { method, path: path70, origin },
22489
+ request: { method, path: path71, origin },
22201
22490
  error
22202
22491
  } = evt;
22203
22492
  debugLog(
22204
22493
  "request to %s %s%s errored - %s",
22205
22494
  method,
22206
22495
  origin,
22207
- path70,
22496
+ path71,
22208
22497
  error.message
22209
22498
  );
22210
22499
  }
@@ -22307,7 +22596,7 @@ var require_request = __commonJS({
22307
22596
  var kHandler = /* @__PURE__ */ Symbol("handler");
22308
22597
  var Request = class {
22309
22598
  constructor(origin, {
22310
- path: path70,
22599
+ path: path71,
22311
22600
  method,
22312
22601
  body,
22313
22602
  headers,
@@ -22324,11 +22613,11 @@ var require_request = __commonJS({
22324
22613
  maxRedirections,
22325
22614
  typeOfService
22326
22615
  }, handler) {
22327
- if (typeof path70 !== "string") {
22616
+ if (typeof path71 !== "string") {
22328
22617
  throw new InvalidArgumentError("path must be a string");
22329
- } else if (path70[0] !== "/" && !(path70.startsWith("http://") || path70.startsWith("https://")) && method !== "CONNECT") {
22618
+ } else if (path71[0] !== "/" && !(path71.startsWith("http://") || path71.startsWith("https://")) && method !== "CONNECT") {
22330
22619
  throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
22331
- } else if (invalidPathRegex.test(path70)) {
22620
+ } else if (invalidPathRegex.test(path71)) {
22332
22621
  throw new InvalidArgumentError("invalid request path");
22333
22622
  }
22334
22623
  if (typeof method !== "string") {
@@ -22403,7 +22692,7 @@ var require_request = __commonJS({
22403
22692
  this.completed = false;
22404
22693
  this.aborted = false;
22405
22694
  this.upgrade = upgrade || null;
22406
- this.path = query ? serializePathWithQuery(path70, query) : path70;
22695
+ this.path = query ? serializePathWithQuery(path71, query) : path71;
22407
22696
  this.origin = origin;
22408
22697
  this.protocol = getProtocolFromUrlString(origin);
22409
22698
  this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
@@ -27442,7 +27731,7 @@ var require_client_h1 = __commonJS({
27442
27731
  return method !== "GET" && method !== "HEAD" && method !== "OPTIONS" && method !== "TRACE" && method !== "CONNECT";
27443
27732
  }
27444
27733
  function writeH1(client, request2) {
27445
- const { method, path: path70, host, upgrade, blocking, reset } = request2;
27734
+ const { method, path: path71, host, upgrade, blocking, reset } = request2;
27446
27735
  let { body, headers, contentLength } = request2;
27447
27736
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH" || method === "QUERY" || method === "PROPFIND" || method === "PROPPATCH";
27448
27737
  if (util.isFormDataLike(body)) {
@@ -27511,7 +27800,7 @@ var require_client_h1 = __commonJS({
27511
27800
  if (socket.setTypeOfService) {
27512
27801
  socket.setTypeOfService(request2.typeOfService);
27513
27802
  }
27514
- let header = `${method} ${path70} HTTP/1.1\r
27803
+ let header = `${method} ${path71} HTTP/1.1\r
27515
27804
  `;
27516
27805
  if (typeof host === "string") {
27517
27806
  header += `host: ${host}\r
@@ -28164,7 +28453,7 @@ var require_client_h2 = __commonJS({
28164
28453
  function writeH2(client, request2) {
28165
28454
  const requestTimeout = request2.bodyTimeout ?? client[kBodyTimeout];
28166
28455
  const session = client[kHTTP2Session];
28167
- const { method, path: path70, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
28456
+ const { method, path: path71, host, upgrade, expectContinue, signal, protocol, headers: reqHeaders } = request2;
28168
28457
  let { body } = request2;
28169
28458
  if (upgrade != null && upgrade !== "websocket") {
28170
28459
  util.errorRequest(client, request2, new InvalidArgumentError(`Custom upgrade "${upgrade}" not supported over HTTP/2`));
@@ -28232,7 +28521,7 @@ var require_client_h2 = __commonJS({
28232
28521
  }
28233
28522
  headers[HTTP2_HEADER_METHOD] = "CONNECT";
28234
28523
  headers[HTTP2_HEADER_PROTOCOL] = "websocket";
28235
- headers[HTTP2_HEADER_PATH] = path70;
28524
+ headers[HTTP2_HEADER_PATH] = path71;
28236
28525
  if (protocol === "ws:" || protocol === "wss:") {
28237
28526
  headers[HTTP2_HEADER_SCHEME] = protocol === "ws:" ? "http" : "https";
28238
28527
  } else {
@@ -28273,7 +28562,7 @@ var require_client_h2 = __commonJS({
28273
28562
  stream.setTimeout(requestTimeout);
28274
28563
  return true;
28275
28564
  }
28276
- headers[HTTP2_HEADER_PATH] = path70;
28565
+ headers[HTTP2_HEADER_PATH] = path71;
28277
28566
  headers[HTTP2_HEADER_SCHEME] = protocol === "http:" ? "http" : "https";
28278
28567
  const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
28279
28568
  if (body && typeof body.read === "function") {
@@ -30575,10 +30864,10 @@ var require_proxy_agent = __commonJS({
30575
30864
  };
30576
30865
  const {
30577
30866
  origin,
30578
- path: path70 = "/",
30867
+ path: path71 = "/",
30579
30868
  headers = {}
30580
30869
  } = opts;
30581
- opts.path = origin + path70;
30870
+ opts.path = origin + path71;
30582
30871
  if (!("host" in headers) && !("Host" in headers)) {
30583
30872
  const { host } = new URL(origin);
30584
30873
  headers.host = host;
@@ -32641,20 +32930,20 @@ var require_mock_utils = __commonJS({
32641
32930
  }
32642
32931
  return normalizedQp;
32643
32932
  }
32644
- function safeUrl(path70) {
32645
- if (typeof path70 !== "string") {
32646
- return path70;
32933
+ function safeUrl(path71) {
32934
+ if (typeof path71 !== "string") {
32935
+ return path71;
32647
32936
  }
32648
- const pathSegments = path70.split("?", 3);
32937
+ const pathSegments = path71.split("?", 3);
32649
32938
  if (pathSegments.length !== 2) {
32650
- return path70;
32939
+ return path71;
32651
32940
  }
32652
32941
  const qp = new URLSearchParams(pathSegments.pop());
32653
32942
  qp.sort();
32654
32943
  return [...pathSegments, qp.toString()].join("?");
32655
32944
  }
32656
- function matchKey(mockDispatch2, { path: path70, method, body, headers }) {
32657
- const pathMatch = matchValue(mockDispatch2.path, path70);
32945
+ function matchKey(mockDispatch2, { path: path71, method, body, headers }) {
32946
+ const pathMatch = matchValue(mockDispatch2.path, path71);
32658
32947
  const methodMatch = matchValue(mockDispatch2.method, method);
32659
32948
  const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
32660
32949
  const headersMatch = matchHeaders(mockDispatch2, headers);
@@ -32679,8 +32968,8 @@ var require_mock_utils = __commonJS({
32679
32968
  const basePath = key.query ? serializePathWithQuery(key.path, key.query) : key.path;
32680
32969
  const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
32681
32970
  const resolvedPathWithoutTrailingSlash = removeTrailingSlash(resolvedPath);
32682
- let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path70, ignoreTrailingSlash }) => {
32683
- return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path70)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path70), resolvedPath);
32971
+ let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path71, ignoreTrailingSlash }) => {
32972
+ return ignoreTrailingSlash ? matchValue(removeTrailingSlash(safeUrl(path71)), resolvedPathWithoutTrailingSlash) : matchValue(safeUrl(path71), resolvedPath);
32684
32973
  });
32685
32974
  if (matchedMockDispatches.length === 0) {
32686
32975
  throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
@@ -32719,19 +33008,19 @@ var require_mock_utils = __commonJS({
32719
33008
  mockDispatches.splice(index, 1);
32720
33009
  }
32721
33010
  }
32722
- function removeTrailingSlash(path70) {
32723
- while (path70.endsWith("/")) {
32724
- path70 = path70.slice(0, -1);
33011
+ function removeTrailingSlash(path71) {
33012
+ while (path71.endsWith("/")) {
33013
+ path71 = path71.slice(0, -1);
32725
33014
  }
32726
- if (path70.length === 0) {
32727
- path70 = "/";
33015
+ if (path71.length === 0) {
33016
+ path71 = "/";
32728
33017
  }
32729
- return path70;
33018
+ return path71;
32730
33019
  }
32731
33020
  function buildKey(opts) {
32732
- const { path: path70, method, body, headers, query } = opts;
33021
+ const { path: path71, method, body, headers, query } = opts;
32733
33022
  return {
32734
- path: path70,
33023
+ path: path71,
32735
33024
  method,
32736
33025
  body,
32737
33026
  headers,
@@ -33421,10 +33710,10 @@ var require_pending_interceptors_formatter = __commonJS({
33421
33710
  }
33422
33711
  format(pendingInterceptors) {
33423
33712
  const withPrettyHeaders = pendingInterceptors.map(
33424
- ({ method, path: path70, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
33713
+ ({ method, path: path71, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
33425
33714
  Method: method,
33426
33715
  Origin: origin,
33427
- Path: path70,
33716
+ Path: path71,
33428
33717
  "Status code": statusCode,
33429
33718
  Persistent: persist ? PERSISTENT : NOT_PERSISTENT,
33430
33719
  Invocations: timesInvoked,
@@ -33506,9 +33795,9 @@ var require_mock_agent = __commonJS({
33506
33795
  const acceptNonStandardSearchParameters = this[kMockAgentAcceptsNonStandardSearchParameters];
33507
33796
  const dispatchOpts = { ...opts };
33508
33797
  if (acceptNonStandardSearchParameters && dispatchOpts.path) {
33509
- const [path70, searchParams] = dispatchOpts.path.split("?");
33798
+ const [path71, searchParams] = dispatchOpts.path.split("?");
33510
33799
  const normalizedSearchParams = normalizeSearchParams(searchParams, acceptNonStandardSearchParameters);
33511
- dispatchOpts.path = `${path70}?${normalizedSearchParams}`;
33800
+ dispatchOpts.path = `${path71}?${normalizedSearchParams}`;
33512
33801
  }
33513
33802
  return this[kAgent].dispatch(dispatchOpts, handler);
33514
33803
  }
@@ -33909,12 +34198,12 @@ var require_snapshot_recorder = __commonJS({
33909
34198
  * @return {Promise<void>} - Resolves when snapshots are loaded
33910
34199
  */
33911
34200
  async loadSnapshots(filePath) {
33912
- const path70 = filePath || this.#snapshotPath;
33913
- if (!path70) {
34201
+ const path71 = filePath || this.#snapshotPath;
34202
+ if (!path71) {
33914
34203
  throw new InvalidArgumentError("Snapshot path is required");
33915
34204
  }
33916
34205
  try {
33917
- const data = await readFile(resolve2(path70), "utf8");
34206
+ const data = await readFile(resolve2(path71), "utf8");
33918
34207
  const parsed = JSON.parse(data);
33919
34208
  if (Array.isArray(parsed)) {
33920
34209
  this.#snapshots.clear();
@@ -33928,7 +34217,7 @@ var require_snapshot_recorder = __commonJS({
33928
34217
  if (error.code === "ENOENT") {
33929
34218
  this.#snapshots.clear();
33930
34219
  } else {
33931
- throw new UndiciError(`Failed to load snapshots from ${path70}`, { cause: error });
34220
+ throw new UndiciError(`Failed to load snapshots from ${path71}`, { cause: error });
33932
34221
  }
33933
34222
  }
33934
34223
  }
@@ -33939,11 +34228,11 @@ var require_snapshot_recorder = __commonJS({
33939
34228
  * @returns {Promise<void>} - Resolves when snapshots are saved
33940
34229
  */
33941
34230
  async saveSnapshots(filePath) {
33942
- const path70 = filePath || this.#snapshotPath;
33943
- if (!path70) {
34231
+ const path71 = filePath || this.#snapshotPath;
34232
+ if (!path71) {
33944
34233
  throw new InvalidArgumentError("Snapshot path is required");
33945
34234
  }
33946
- const resolvedPath = resolve2(path70);
34235
+ const resolvedPath = resolve2(path71);
33947
34236
  await mkdir(dirname2(resolvedPath), { recursive: true });
33948
34237
  const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
33949
34238
  hash,
@@ -34568,15 +34857,15 @@ var require_redirect_handler = __commonJS({
34568
34857
  return;
34569
34858
  }
34570
34859
  const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
34571
- const path70 = search ? `${pathname}${search}` : pathname;
34572
- const redirectUrlString = `${origin}${path70}`;
34860
+ const path71 = search ? `${pathname}${search}` : pathname;
34861
+ const redirectUrlString = `${origin}${path71}`;
34573
34862
  for (const historyUrl of this.history) {
34574
34863
  if (historyUrl.toString() === redirectUrlString) {
34575
34864
  throw new InvalidArgumentError(`Redirect loop detected. Cannot redirect to ${origin}. This typically happens when using a Client or Pool with cross-origin redirects. Use an Agent for cross-origin redirects.`);
34576
34865
  }
34577
34866
  }
34578
34867
  this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
34579
- this.opts.path = path70;
34868
+ this.opts.path = path71;
34580
34869
  this.opts.origin = origin;
34581
34870
  this.opts.query = null;
34582
34871
  }
@@ -40783,11 +41072,11 @@ var require_fetch = __commonJS({
40783
41072
  function dispatch({ body }) {
40784
41073
  const url = requestCurrentURL(request2);
40785
41074
  const agent = fetchParams.controller.dispatcher;
40786
- const path70 = url.pathname + url.search;
41075
+ const path71 = url.pathname + url.search;
40787
41076
  const hasTrailingQuestionMark = url.search.length === 0 && url.href[url.href.length - url.hash.length - 1] === "?";
40788
41077
  return new Promise((resolve2, reject) => agent.dispatch(
40789
41078
  {
40790
- path: hasTrailingQuestionMark ? `${path70}?` : path70,
41079
+ path: hasTrailingQuestionMark ? `${path71}?` : path71,
40791
41080
  origin: url.origin,
40792
41081
  method: request2.method,
40793
41082
  body: agent.isMockActive ? request2.body && (request2.body.source || request2.body.stream) : body,
@@ -41718,9 +42007,9 @@ var require_util4 = __commonJS({
41718
42007
  }
41719
42008
  }
41720
42009
  }
41721
- function validateCookiePath(path70) {
41722
- for (let i = 0; i < path70.length; ++i) {
41723
- const code = path70.charCodeAt(i);
42010
+ function validateCookiePath(path71) {
42011
+ for (let i = 0; i < path71.length; ++i) {
42012
+ const code = path71.charCodeAt(i);
41724
42013
  if (code < 32 || // exclude CTLs (0-31)
41725
42014
  code === 127 || // DEL
41726
42015
  code === 59) {
@@ -44890,11 +45179,11 @@ var require_undici = __commonJS({
44890
45179
  if (typeof opts.path !== "string") {
44891
45180
  throw new InvalidArgumentError("invalid opts.path");
44892
45181
  }
44893
- let path70 = opts.path;
45182
+ let path71 = opts.path;
44894
45183
  if (!opts.path.startsWith("/")) {
44895
- path70 = `/${path70}`;
45184
+ path71 = `/${path71}`;
44896
45185
  }
44897
- url = new URL(util.parseOrigin(url).origin + path70);
45186
+ url = new URL(util.parseOrigin(url).origin + path71);
44898
45187
  } else {
44899
45188
  if (!opts) {
44900
45189
  opts = typeof url === "object" ? url : {};
@@ -45013,9 +45302,9 @@ __export(tail_exports, {
45013
45302
  });
45014
45303
  import http5 from "http";
45015
45304
  import chalk40 from "chalk";
45016
- import fs70 from "fs";
45017
- import os59 from "os";
45018
- import path67 from "path";
45305
+ import fs71 from "fs";
45306
+ import os61 from "os";
45307
+ import path68 from "path";
45019
45308
  import readline6 from "readline";
45020
45309
  import { spawn as spawn8 } from "child_process";
45021
45310
  function shortenPathSummary(s) {
@@ -45039,20 +45328,20 @@ function getModelContextLimit(model) {
45039
45328
  return 2e5;
45040
45329
  }
45041
45330
  function readSessionUsage() {
45042
- const projectsDir = path67.join(os59.homedir(), ".claude", "projects");
45043
- if (!fs70.existsSync(projectsDir)) return null;
45331
+ const projectsDir = path68.join(os61.homedir(), ".claude", "projects");
45332
+ if (!fs71.existsSync(projectsDir)) return null;
45044
45333
  let latestFile = null;
45045
45334
  let latestMtime = 0;
45046
45335
  try {
45047
- for (const dir of fs70.readdirSync(projectsDir)) {
45048
- const dirPath = path67.join(projectsDir, dir);
45336
+ for (const dir of fs71.readdirSync(projectsDir)) {
45337
+ const dirPath = path68.join(projectsDir, dir);
45049
45338
  try {
45050
- if (!fs70.statSync(dirPath).isDirectory()) continue;
45051
- for (const file of fs70.readdirSync(dirPath)) {
45339
+ if (!fs71.statSync(dirPath).isDirectory()) continue;
45340
+ for (const file of fs71.readdirSync(dirPath)) {
45052
45341
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
45053
- const filePath = path67.join(dirPath, file);
45342
+ const filePath = path68.join(dirPath, file);
45054
45343
  try {
45055
- const mtime = fs70.statSync(filePath).mtimeMs;
45344
+ const mtime = fs71.statSync(filePath).mtimeMs;
45056
45345
  if (mtime > latestMtime) {
45057
45346
  latestMtime = mtime;
45058
45347
  latestFile = filePath;
@@ -45067,7 +45356,7 @@ function readSessionUsage() {
45067
45356
  }
45068
45357
  if (!latestFile) return null;
45069
45358
  try {
45070
- const lines = fs70.readFileSync(latestFile, "utf-8").split("\n");
45359
+ const lines = fs71.readFileSync(latestFile, "utf-8").split("\n");
45071
45360
  let lastModel = "";
45072
45361
  let lastInput = 0;
45073
45362
  let lastOutput = 0;
@@ -45128,7 +45417,7 @@ function formatBase(activity) {
45128
45417
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
45129
45418
  const icon = getIcon(activity.tool);
45130
45419
  const toolName = activity.tool.slice(0, 16).padEnd(16);
45131
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os59.homedir(), "~");
45420
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os61.homedir(), "~");
45132
45421
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
45133
45422
  return `${chalk40.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk40.white.bold(toolName)} ${chalk40.dim(argsPreview)}`;
45134
45423
  }
@@ -45167,9 +45456,9 @@ function renderPending(activity) {
45167
45456
  }
45168
45457
  async function ensureDaemon() {
45169
45458
  let pidPort = null;
45170
- if (fs70.existsSync(PID_FILE)) {
45459
+ if (fs71.existsSync(PID_FILE)) {
45171
45460
  try {
45172
- const { port } = JSON.parse(fs70.readFileSync(PID_FILE, "utf-8"));
45461
+ const { port } = JSON.parse(fs71.readFileSync(PID_FILE, "utf-8"));
45173
45462
  pidPort = port;
45174
45463
  } catch {
45175
45464
  console.error(chalk40.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -45325,9 +45614,9 @@ function buildRecoveryCardLines(req) {
45325
45614
  ];
45326
45615
  }
45327
45616
  function readApproversFromDisk() {
45328
- const configPath = path67.join(os59.homedir(), ".node9", "config.json");
45617
+ const configPath = path68.join(os61.homedir(), ".node9", "config.json");
45329
45618
  try {
45330
- const raw = JSON.parse(fs70.readFileSync(configPath, "utf-8"));
45619
+ const raw = JSON.parse(fs71.readFileSync(configPath, "utf-8"));
45331
45620
  const settings = raw.settings ?? {};
45332
45621
  return settings.approvers ?? {};
45333
45622
  } catch {
@@ -45343,15 +45632,15 @@ function approverStatusLine() {
45343
45632
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
45344
45633
  }
45345
45634
  function toggleApprover(channel) {
45346
- const configPath = path67.join(os59.homedir(), ".node9", "config.json");
45635
+ const configPath = path68.join(os61.homedir(), ".node9", "config.json");
45347
45636
  try {
45348
- const raw = JSON.parse(fs70.readFileSync(configPath, "utf-8"));
45637
+ const raw = JSON.parse(fs71.readFileSync(configPath, "utf-8"));
45349
45638
  const settings = raw.settings ?? {};
45350
45639
  const approvers = settings.approvers ?? {};
45351
45640
  approvers[channel] = approvers[channel] === false;
45352
45641
  settings.approvers = approvers;
45353
45642
  raw.settings = settings;
45354
- fs70.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
45643
+ fs71.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
45355
45644
  } catch (err2) {
45356
45645
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
45357
45646
  `);
@@ -45523,8 +45812,8 @@ async function startTail(options = {}) {
45523
45812
  }
45524
45813
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
45525
45814
  try {
45526
- fs70.appendFileSync(
45527
- path67.join(os59.homedir(), ".node9", "hook-debug.log"),
45815
+ fs71.appendFileSync(
45816
+ path68.join(os61.homedir(), ".node9", "hook-debug.log"),
45528
45817
  `[tail] POST /decision failed: ${String(err2)}
45529
45818
  `
45530
45819
  );
@@ -45588,9 +45877,9 @@ async function startTail(options = {}) {
45588
45877
  };
45589
45878
  process.stdin.on("keypress", onKeypress);
45590
45879
  }
45591
- const auditLog = path67.join(os59.homedir(), ".node9", "audit.log");
45880
+ const auditLog = path68.join(os61.homedir(), ".node9", "audit.log");
45592
45881
  try {
45593
- const unackedDlp = fs70.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
45882
+ const unackedDlp = fs71.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
45594
45883
  if (unackedDlp > 0) {
45595
45884
  console.log("");
45596
45885
  console.log(
@@ -45630,7 +45919,7 @@ async function startTail(options = {}) {
45630
45919
  if (stallWarned) return;
45631
45920
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
45632
45921
  try {
45633
- const auditMtime = fs70.statSync(auditLog).mtimeMs;
45922
+ const auditMtime = fs71.statSync(auditLog).mtimeMs;
45634
45923
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
45635
45924
  console.log("");
45636
45925
  console.log(
@@ -45821,7 +46110,7 @@ var init_tail = __esm({
45821
46110
  "use strict";
45822
46111
  init_daemon2();
45823
46112
  init_daemon();
45824
- PID_FILE = path67.join(os59.homedir(), ".node9", "daemon.pid");
46113
+ PID_FILE = path68.join(os61.homedir(), ".node9", "daemon.pid");
45825
46114
  ICONS = {
45826
46115
  bash: "\u{1F4BB}",
45827
46116
  shell: "\u{1F4BB}",
@@ -45869,9 +46158,9 @@ __export(hud_exports, {
45869
46158
  main: () => main,
45870
46159
  renderEnvironmentLine: () => renderEnvironmentLine
45871
46160
  });
45872
- import fs71 from "fs";
45873
- import path68 from "path";
45874
- import os60 from "os";
46161
+ import fs72 from "fs";
46162
+ import path69 from "path";
46163
+ import os62 from "os";
45875
46164
  import http6 from "http";
45876
46165
  async function readStdin() {
45877
46166
  const chunks = [];
@@ -45947,9 +46236,9 @@ function formatTimeLeft(resetsAt) {
45947
46236
  return ` (${m}m left)`;
45948
46237
  }
45949
46238
  function safeReadJson(filePath) {
45950
- if (!fs71.existsSync(filePath)) return null;
46239
+ if (!fs72.existsSync(filePath)) return null;
45951
46240
  try {
45952
- return JSON.parse(fs71.readFileSync(filePath, "utf-8"));
46241
+ return JSON.parse(fs72.readFileSync(filePath, "utf-8"));
45953
46242
  } catch {
45954
46243
  return null;
45955
46244
  }
@@ -45970,12 +46259,12 @@ function countHooksInFile(filePath) {
45970
46259
  return Object.keys(cfg.hooks).length;
45971
46260
  }
45972
46261
  function countRulesInDir(rulesDir) {
45973
- if (!fs71.existsSync(rulesDir)) return 0;
46262
+ if (!fs72.existsSync(rulesDir)) return 0;
45974
46263
  let count = 0;
45975
46264
  try {
45976
- for (const entry of fs71.readdirSync(rulesDir, { withFileTypes: true })) {
46265
+ for (const entry of fs72.readdirSync(rulesDir, { withFileTypes: true })) {
45977
46266
  if (entry.isDirectory()) {
45978
- count += countRulesInDir(path68.join(rulesDir, entry.name));
46267
+ count += countRulesInDir(path69.join(rulesDir, entry.name));
45979
46268
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
45980
46269
  count++;
45981
46270
  }
@@ -45986,46 +46275,46 @@ function countRulesInDir(rulesDir) {
45986
46275
  }
45987
46276
  function isSamePath(a, b) {
45988
46277
  try {
45989
- return path68.resolve(a) === path68.resolve(b);
46278
+ return path69.resolve(a) === path69.resolve(b);
45990
46279
  } catch {
45991
46280
  return false;
45992
46281
  }
45993
46282
  }
45994
46283
  function countConfigs(cwd) {
45995
- const homeDir2 = os60.homedir();
45996
- const claudeDir = path68.join(homeDir2, ".claude");
46284
+ const homeDir2 = os62.homedir();
46285
+ const claudeDir = path69.join(homeDir2, ".claude");
45997
46286
  let claudeMdCount = 0;
45998
46287
  let rulesCount = 0;
45999
46288
  let hooksCount = 0;
46000
46289
  const userMcpServers = /* @__PURE__ */ new Set();
46001
46290
  const projectMcpServers = /* @__PURE__ */ new Set();
46002
- if (fs71.existsSync(path68.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
46003
- rulesCount += countRulesInDir(path68.join(claudeDir, "rules"));
46004
- const userSettings = path68.join(claudeDir, "settings.json");
46291
+ if (fs72.existsSync(path69.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
46292
+ rulesCount += countRulesInDir(path69.join(claudeDir, "rules"));
46293
+ const userSettings = path69.join(claudeDir, "settings.json");
46005
46294
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
46006
46295
  hooksCount += countHooksInFile(userSettings);
46007
- const userClaudeJson = path68.join(homeDir2, ".claude.json");
46296
+ const userClaudeJson = path69.join(homeDir2, ".claude.json");
46008
46297
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
46009
46298
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
46010
46299
  userMcpServers.delete(name);
46011
46300
  }
46012
46301
  if (cwd) {
46013
- if (fs71.existsSync(path68.join(cwd, "CLAUDE.md"))) claudeMdCount++;
46014
- if (fs71.existsSync(path68.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
46015
- const projectClaudeDir = path68.join(cwd, ".claude");
46302
+ if (fs72.existsSync(path69.join(cwd, "CLAUDE.md"))) claudeMdCount++;
46303
+ if (fs72.existsSync(path69.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
46304
+ const projectClaudeDir = path69.join(cwd, ".claude");
46016
46305
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
46017
46306
  if (!overlapsUserScope) {
46018
- if (fs71.existsSync(path68.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
46019
- rulesCount += countRulesInDir(path68.join(projectClaudeDir, "rules"));
46020
- const projSettings = path68.join(projectClaudeDir, "settings.json");
46307
+ if (fs72.existsSync(path69.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
46308
+ rulesCount += countRulesInDir(path69.join(projectClaudeDir, "rules"));
46309
+ const projSettings = path69.join(projectClaudeDir, "settings.json");
46021
46310
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
46022
46311
  hooksCount += countHooksInFile(projSettings);
46023
46312
  }
46024
- if (fs71.existsSync(path68.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
46025
- const localSettings = path68.join(projectClaudeDir, "settings.local.json");
46313
+ if (fs72.existsSync(path69.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
46314
+ const localSettings = path69.join(projectClaudeDir, "settings.local.json");
46026
46315
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
46027
46316
  hooksCount += countHooksInFile(localSettings);
46028
- const mcpJsonServers = getMcpServerNames(path68.join(cwd, ".mcp.json"));
46317
+ const mcpJsonServers = getMcpServerNames(path69.join(cwd, ".mcp.json"));
46029
46318
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
46030
46319
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
46031
46320
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -46058,12 +46347,12 @@ function readActiveShieldsHud() {
46058
46347
  return shieldsCache.value;
46059
46348
  }
46060
46349
  try {
46061
- const shieldsPath = path68.join(os60.homedir(), ".node9", "shields.json");
46062
- if (!fs71.existsSync(shieldsPath)) {
46350
+ const shieldsPath = path69.join(os62.homedir(), ".node9", "shields.json");
46351
+ if (!fs72.existsSync(shieldsPath)) {
46063
46352
  shieldsCache = { value: [], ts: now };
46064
46353
  return [];
46065
46354
  }
46066
- const parsed = JSON.parse(fs71.readFileSync(shieldsPath, "utf-8"));
46355
+ const parsed = JSON.parse(fs72.readFileSync(shieldsPath, "utf-8"));
46067
46356
  if (!Array.isArray(parsed.active)) {
46068
46357
  shieldsCache = { value: [], ts: now };
46069
46358
  return [];
@@ -46165,17 +46454,17 @@ function renderContextLine(stdin) {
46165
46454
  async function main() {
46166
46455
  try {
46167
46456
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
46168
- if (fs71.existsSync(path68.join(os60.homedir(), ".node9", "hud-debug"))) {
46457
+ if (fs72.existsSync(path69.join(os62.homedir(), ".node9", "hud-debug"))) {
46169
46458
  try {
46170
- const logPath = path68.join(os60.homedir(), ".node9", "hud-debug.log");
46459
+ const logPath = path69.join(os62.homedir(), ".node9", "hud-debug.log");
46171
46460
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
46172
46461
  let size = 0;
46173
46462
  try {
46174
- size = fs71.statSync(logPath).size;
46463
+ size = fs72.statSync(logPath).size;
46175
46464
  } catch {
46176
46465
  }
46177
46466
  if (size < MAX_LOG_SIZE) {
46178
- fs71.appendFileSync(
46467
+ fs72.appendFileSync(
46179
46468
  logPath,
46180
46469
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
46181
46470
  );
@@ -46196,11 +46485,11 @@ async function main() {
46196
46485
  try {
46197
46486
  const cwd = stdin.cwd ?? process.cwd();
46198
46487
  for (const configPath of [
46199
- path68.join(cwd, "node9.config.json"),
46200
- path68.join(os60.homedir(), ".node9", "config.json")
46488
+ path69.join(cwd, "node9.config.json"),
46489
+ path69.join(os62.homedir(), ".node9", "config.json")
46201
46490
  ]) {
46202
- if (!fs71.existsSync(configPath)) continue;
46203
- const cfg = JSON.parse(fs71.readFileSync(configPath, "utf-8"));
46491
+ if (!fs72.existsSync(configPath)) continue;
46492
+ const cfg = JSON.parse(fs72.readFileSync(configPath, "utf-8"));
46204
46493
  const hud = cfg.settings?.hud;
46205
46494
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
46206
46495
  }
@@ -46342,9 +46631,9 @@ function writeCredentialsAndConfig(apiKey, opts = {}) {
46342
46631
  // src/cli.ts
46343
46632
  init_daemon2();
46344
46633
  import chalk41 from "chalk";
46345
- import fs72 from "fs";
46346
- import path69 from "path";
46347
- import os61 from "os";
46634
+ import fs73 from "fs";
46635
+ import path70 from "path";
46636
+ import os63 from "os";
46348
46637
  import { spawn as spawn9 } from "child_process";
46349
46638
  import { confirm as confirm2 } from "@inquirer/prompts";
46350
46639
 
@@ -46531,26 +46820,48 @@ async function runProxy(targetCommand) {
46531
46820
 
46532
46821
  // src/cli/daemon-starter.ts
46533
46822
  init_daemon();
46823
+ init_startup_log();
46534
46824
  import { spawn as spawn3 } from "child_process";
46535
- import path41 from "path";
46536
- import fs43 from "fs";
46825
+ import path42 from "path";
46826
+ import fs44 from "fs";
46827
+ import os40 from "os";
46537
46828
  function isTestingMode() {
46538
46829
  return /^(1|true|yes)$/i.test(process.env.NODE9_TESTING ?? "");
46539
46830
  }
46831
+ var SKIP_STAMP = () => path42.join(os40.homedir(), ".node9", ".autostart-skip-stamp");
46832
+ var SKIP_THROTTLE_MS = 60 * 60 * 1e3;
46833
+ function logAutostartSkipThrottled(reason) {
46834
+ try {
46835
+ const stamp = SKIP_STAMP();
46836
+ try {
46837
+ if (Date.now() - fs44.statSync(stamp).mtimeMs < SKIP_THROTTLE_MS) return;
46838
+ } catch {
46839
+ }
46840
+ fs44.writeFileSync(stamp, "", "utf-8");
46841
+ fs44.appendFileSync(
46842
+ path42.join(os40.homedir(), ".node9", "hook-debug.log"),
46843
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-skip: ${reason}
46844
+ `,
46845
+ "utf-8"
46846
+ );
46847
+ } catch {
46848
+ }
46849
+ }
46540
46850
  async function autoStartDaemonAndWait() {
46541
46851
  if (isTestingMode()) return false;
46542
- if (!path41.isAbsolute(process.argv[1])) return false;
46852
+ if (!path42.isAbsolute(process.argv[1])) return false;
46543
46853
  let resolvedArgv1;
46544
46854
  try {
46545
- resolvedArgv1 = fs43.realpathSync(process.argv[1]);
46855
+ resolvedArgv1 = fs44.realpathSync(process.argv[1]);
46546
46856
  } catch {
46547
46857
  return false;
46548
46858
  }
46549
46859
  if (!resolvedArgv1.endsWith(".js")) return false;
46860
+ const startupFd = openStartupLogFd();
46550
46861
  try {
46551
46862
  const child = spawn3(process.execPath, [resolvedArgv1, "daemon"], {
46552
46863
  detached: true,
46553
- stdio: "ignore",
46864
+ stdio: ["ignore", "ignore", startupFd ?? "ignore"],
46554
46865
  env: {
46555
46866
  ...process.env,
46556
46867
  NODE9_AUTO_STARTED: "1"
@@ -46563,30 +46874,41 @@ async function autoStartDaemonAndWait() {
46563
46874
  if (await isDaemonReachable()) return true;
46564
46875
  }
46565
46876
  } catch {
46877
+ } finally {
46878
+ if (startupFd !== void 0) {
46879
+ try {
46880
+ fs44.closeSync(startupFd);
46881
+ } catch {
46882
+ }
46883
+ }
46566
46884
  }
46567
46885
  return false;
46568
46886
  }
46569
46887
 
46888
+ // src/cli.ts
46889
+ init_service();
46890
+
46570
46891
  // src/cli/commands/check.ts
46571
46892
  init_orchestrator();
46572
46893
  init_state();
46573
46894
  init_daemon();
46895
+ init_startup_log();
46574
46896
  init_config();
46575
46897
  init_policy();
46576
46898
  import chalk9 from "chalk";
46577
- import fs47 from "fs";
46899
+ import fs48 from "fs";
46578
46900
  import { spawn as spawn5 } from "child_process";
46579
- import path45 from "path";
46580
- import os42 from "os";
46901
+ import path46 from "path";
46902
+ import os44 from "os";
46581
46903
 
46582
46904
  // src/undo.ts
46583
46905
  import { spawnSync as spawnSync3, spawn as spawn4 } from "child_process";
46584
46906
  import crypto7 from "crypto";
46585
- import fs44 from "fs";
46907
+ import fs45 from "fs";
46586
46908
  import net3 from "net";
46587
- import path42 from "path";
46588
- import os39 from "os";
46589
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path42.join(os39.tmpdir(), "node9-activity.sock");
46909
+ import path43 from "path";
46910
+ import os41 from "os";
46911
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path43.join(os41.tmpdir(), "node9-activity.sock");
46590
46912
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
46591
46913
  try {
46592
46914
  const payload = JSON.stringify({
@@ -46606,22 +46928,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
46606
46928
  } catch {
46607
46929
  }
46608
46930
  }
46609
- var SNAPSHOT_STACK_PATH = path42.join(os39.homedir(), ".node9", "snapshots.json");
46610
- var UNDO_LATEST_PATH = path42.join(os39.homedir(), ".node9", "undo_latest.txt");
46931
+ var SNAPSHOT_STACK_PATH = path43.join(os41.homedir(), ".node9", "snapshots.json");
46932
+ var UNDO_LATEST_PATH = path43.join(os41.homedir(), ".node9", "undo_latest.txt");
46611
46933
  var MAX_SNAPSHOTS = 10;
46612
46934
  var GIT_TIMEOUT = 15e3;
46613
46935
  function readStack() {
46614
46936
  try {
46615
- if (fs44.existsSync(SNAPSHOT_STACK_PATH))
46616
- return JSON.parse(fs44.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
46937
+ if (fs45.existsSync(SNAPSHOT_STACK_PATH))
46938
+ return JSON.parse(fs45.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
46617
46939
  } catch {
46618
46940
  }
46619
46941
  return [];
46620
46942
  }
46621
46943
  function writeStack(stack) {
46622
- const dir = path42.dirname(SNAPSHOT_STACK_PATH);
46623
- if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
46624
- fs44.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
46944
+ const dir = path43.dirname(SNAPSHOT_STACK_PATH);
46945
+ if (!fs45.existsSync(dir)) fs45.mkdirSync(dir, { recursive: true });
46946
+ fs45.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
46625
46947
  }
46626
46948
  function extractFilePath(args) {
46627
46949
  if (!args || typeof args !== "object") return null;
@@ -46641,12 +46963,12 @@ function buildArgsSummary(tool, args) {
46641
46963
  return "";
46642
46964
  }
46643
46965
  function findProjectRoot(filePath) {
46644
- let dir = path42.dirname(filePath);
46966
+ let dir = path43.dirname(filePath);
46645
46967
  while (true) {
46646
- if (fs44.existsSync(path42.join(dir, ".git")) || fs44.existsSync(path42.join(dir, "package.json"))) {
46968
+ if (fs45.existsSync(path43.join(dir, ".git")) || fs45.existsSync(path43.join(dir, "package.json"))) {
46647
46969
  return dir;
46648
46970
  }
46649
- const parent = path42.dirname(dir);
46971
+ const parent = path43.dirname(dir);
46650
46972
  if (parent === dir) return process.cwd();
46651
46973
  dir = parent;
46652
46974
  }
@@ -46654,7 +46976,7 @@ function findProjectRoot(filePath) {
46654
46976
  function normalizeCwdForHash(cwd) {
46655
46977
  let normalized;
46656
46978
  try {
46657
- normalized = fs44.realpathSync(cwd);
46979
+ normalized = fs45.realpathSync(cwd);
46658
46980
  } catch {
46659
46981
  normalized = cwd;
46660
46982
  }
@@ -46664,16 +46986,16 @@ function normalizeCwdForHash(cwd) {
46664
46986
  }
46665
46987
  function getShadowRepoDir(cwd) {
46666
46988
  const hash = crypto7.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
46667
- return path42.join(os39.homedir(), ".node9", "snapshots", hash);
46989
+ return path43.join(os41.homedir(), ".node9", "snapshots", hash);
46668
46990
  }
46669
46991
  function cleanOrphanedIndexFiles(shadowDir) {
46670
46992
  try {
46671
46993
  const cutoff = Date.now() - 6e4;
46672
- for (const f of fs44.readdirSync(shadowDir)) {
46994
+ for (const f of fs45.readdirSync(shadowDir)) {
46673
46995
  if (f.startsWith("index_")) {
46674
- const fp = path42.join(shadowDir, f);
46996
+ const fp = path43.join(shadowDir, f);
46675
46997
  try {
46676
- if (fs44.statSync(fp).mtimeMs < cutoff) fs44.unlinkSync(fp);
46998
+ if (fs45.statSync(fp).mtimeMs < cutoff) fs45.unlinkSync(fp);
46677
46999
  } catch {
46678
47000
  }
46679
47001
  }
@@ -46685,7 +47007,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
46685
47007
  const hardcoded = [".git", ".node9"];
46686
47008
  const lines = [...hardcoded, ...ignorePaths].join("\n");
46687
47009
  try {
46688
- fs44.writeFileSync(path42.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
47010
+ fs45.writeFileSync(path43.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
46689
47011
  } catch {
46690
47012
  }
46691
47013
  }
@@ -46698,25 +47020,25 @@ function ensureShadowRepo(shadowDir, cwd) {
46698
47020
  timeout: 3e3
46699
47021
  });
46700
47022
  if (check.status === 0) {
46701
- const ptPath = path42.join(shadowDir, "project-path.txt");
47023
+ const ptPath = path43.join(shadowDir, "project-path.txt");
46702
47024
  try {
46703
- const stored = fs44.readFileSync(ptPath, "utf8").trim();
47025
+ const stored = fs45.readFileSync(ptPath, "utf8").trim();
46704
47026
  if (stored === normalizedCwd) return true;
46705
47027
  if (process.env.NODE9_DEBUG === "1")
46706
47028
  console.error(
46707
47029
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
46708
47030
  );
46709
- fs44.rmSync(shadowDir, { recursive: true, force: true });
47031
+ fs45.rmSync(shadowDir, { recursive: true, force: true });
46710
47032
  } catch {
46711
47033
  try {
46712
- fs44.writeFileSync(ptPath, normalizedCwd, "utf8");
47034
+ fs45.writeFileSync(ptPath, normalizedCwd, "utf8");
46713
47035
  } catch {
46714
47036
  }
46715
47037
  return true;
46716
47038
  }
46717
47039
  }
46718
47040
  try {
46719
- fs44.mkdirSync(shadowDir, { recursive: true });
47041
+ fs45.mkdirSync(shadowDir, { recursive: true });
46720
47042
  } catch {
46721
47043
  }
46722
47044
  const init = spawnSync3("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -46725,7 +47047,7 @@ function ensureShadowRepo(shadowDir, cwd) {
46725
47047
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
46726
47048
  return false;
46727
47049
  }
46728
- const configFile = path42.join(shadowDir, "config");
47050
+ const configFile = path43.join(shadowDir, "config");
46729
47051
  spawnSync3("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
46730
47052
  timeout: 3e3
46731
47053
  });
@@ -46733,7 +47055,7 @@ function ensureShadowRepo(shadowDir, cwd) {
46733
47055
  timeout: 3e3
46734
47056
  });
46735
47057
  try {
46736
- fs44.writeFileSync(path42.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
47058
+ fs45.writeFileSync(path43.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
46737
47059
  } catch {
46738
47060
  }
46739
47061
  return true;
@@ -46756,12 +47078,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
46756
47078
  let indexFile = null;
46757
47079
  try {
46758
47080
  const rawFilePath = extractFilePath(args);
46759
- const absFilePath = rawFilePath && path42.isAbsolute(rawFilePath) ? rawFilePath : null;
47081
+ const absFilePath = rawFilePath && path43.isAbsolute(rawFilePath) ? rawFilePath : null;
46760
47082
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
46761
47083
  const shadowDir = getShadowRepoDir(cwd);
46762
47084
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
46763
47085
  writeShadowExcludes(shadowDir, ignorePaths);
46764
- indexFile = path42.join(shadowDir, `index_${process.pid}_${Date.now()}`);
47086
+ indexFile = path43.join(shadowDir, `index_${process.pid}_${Date.now()}`);
46765
47087
  const shadowEnv = {
46766
47088
  ...process.env,
46767
47089
  GIT_DIR: shadowDir,
@@ -46833,7 +47155,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
46833
47155
  writeStack(stack);
46834
47156
  const entry = stack[stack.length - 1];
46835
47157
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
46836
- fs44.writeFileSync(UNDO_LATEST_PATH, commitHash);
47158
+ fs45.writeFileSync(UNDO_LATEST_PATH, commitHash);
46837
47159
  if (shouldGc) {
46838
47160
  spawn4("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
46839
47161
  }
@@ -46844,7 +47166,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
46844
47166
  } finally {
46845
47167
  if (indexFile) {
46846
47168
  try {
46847
- fs44.unlinkSync(indexFile);
47169
+ fs45.unlinkSync(indexFile);
46848
47170
  } catch {
46849
47171
  }
46850
47172
  }
@@ -46920,9 +47242,9 @@ function applyUndo(hash, cwd) {
46920
47242
  timeout: GIT_TIMEOUT
46921
47243
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
46922
47244
  for (const file of [...tracked, ...untracked]) {
46923
- const fullPath = path42.join(dir, file);
46924
- if (!snapshotFiles.has(file) && fs44.existsSync(fullPath)) {
46925
- fs44.unlinkSync(fullPath);
47245
+ const fullPath = path43.join(dir, file);
47246
+ if (!snapshotFiles.has(file) && fs45.existsSync(fullPath)) {
47247
+ fs45.unlinkSync(fullPath);
46926
47248
  }
46927
47249
  }
46928
47250
  return true;
@@ -46932,12 +47254,12 @@ function applyUndo(hash, cwd) {
46932
47254
  }
46933
47255
 
46934
47256
  // src/skill-pin.ts
46935
- import fs45 from "fs";
46936
- import path43 from "path";
46937
- import os40 from "os";
47257
+ import fs46 from "fs";
47258
+ import path44 from "path";
47259
+ import os42 from "os";
46938
47260
  import crypto8 from "crypto";
46939
47261
  function getPinsFilePath2() {
46940
- return path43.join(os40.homedir(), ".node9", "skill-pins.json");
47262
+ return path44.join(os42.homedir(), ".node9", "skill-pins.json");
46941
47263
  }
46942
47264
  var MAX_FILES = 5e3;
46943
47265
  var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
@@ -46951,18 +47273,18 @@ function walkDir(root) {
46951
47273
  if (out.length >= MAX_FILES) return;
46952
47274
  let entries;
46953
47275
  try {
46954
- entries = fs45.readdirSync(dir, { withFileTypes: true });
47276
+ entries = fs46.readdirSync(dir, { withFileTypes: true });
46955
47277
  } catch {
46956
47278
  return;
46957
47279
  }
46958
47280
  entries.sort((a, b) => a.name.localeCompare(b.name));
46959
47281
  for (const entry of entries) {
46960
47282
  if (out.length >= MAX_FILES) return;
46961
- const full = path43.join(dir, entry.name);
46962
- const rel = relDir ? path43.posix.join(relDir, entry.name) : entry.name;
47283
+ const full = path44.join(dir, entry.name);
47284
+ const rel = relDir ? path44.posix.join(relDir, entry.name) : entry.name;
46963
47285
  let lst;
46964
47286
  try {
46965
- lst = fs45.lstatSync(full);
47287
+ lst = fs46.lstatSync(full);
46966
47288
  } catch {
46967
47289
  continue;
46968
47290
  }
@@ -46974,7 +47296,7 @@ function walkDir(root) {
46974
47296
  if (!lst.isFile()) continue;
46975
47297
  if (totalBytes + lst.size > MAX_TOTAL_BYTES) continue;
46976
47298
  try {
46977
- const buf = fs45.readFileSync(full);
47299
+ const buf = fs46.readFileSync(full);
46978
47300
  totalBytes += buf.length;
46979
47301
  out.push({ rel, hash: sha256Bytes(buf) });
46980
47302
  } catch {
@@ -46988,14 +47310,14 @@ function walkDir(root) {
46988
47310
  function hashSkillRoot(absPath) {
46989
47311
  let lst;
46990
47312
  try {
46991
- lst = fs45.lstatSync(absPath);
47313
+ lst = fs46.lstatSync(absPath);
46992
47314
  } catch {
46993
47315
  return { exists: false, contentHash: "", fileCount: 0 };
46994
47316
  }
46995
47317
  if (lst.isSymbolicLink()) return { exists: false, contentHash: "", fileCount: 0 };
46996
47318
  if (lst.isFile()) {
46997
47319
  try {
46998
- return { exists: true, contentHash: sha256Bytes(fs45.readFileSync(absPath)), fileCount: 1 };
47320
+ return { exists: true, contentHash: sha256Bytes(fs46.readFileSync(absPath)), fileCount: 1 };
46999
47321
  } catch {
47000
47322
  return { exists: false, contentHash: "", fileCount: 0 };
47001
47323
  }
@@ -47013,7 +47335,7 @@ function getRootKey(absPath) {
47013
47335
  function readSkillPinsSafe() {
47014
47336
  const filePath = getPinsFilePath2();
47015
47337
  try {
47016
- const raw = fs45.readFileSync(filePath, "utf-8");
47338
+ const raw = fs46.readFileSync(filePath, "utf-8");
47017
47339
  if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
47018
47340
  const parsed = JSON.parse(raw);
47019
47341
  if (!parsed.roots || typeof parsed.roots !== "object" || Array.isArray(parsed.roots)) {
@@ -47033,10 +47355,10 @@ function readSkillPins() {
47033
47355
  }
47034
47356
  function writeSkillPins(data) {
47035
47357
  const filePath = getPinsFilePath2();
47036
- fs45.mkdirSync(path43.dirname(filePath), { recursive: true });
47358
+ fs46.mkdirSync(path44.dirname(filePath), { recursive: true });
47037
47359
  const tmp = `${filePath}.${crypto8.randomBytes(6).toString("hex")}.tmp`;
47038
- fs45.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
47039
- fs45.renameSync(tmp, filePath);
47360
+ fs46.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
47361
+ fs46.renameSync(tmp, filePath);
47040
47362
  }
47041
47363
  function removePin2(rootKey) {
47042
47364
  const pins = readSkillPins();
@@ -47080,36 +47402,36 @@ function verifyAndPinRoots(roots) {
47080
47402
  return { kind: "verified" };
47081
47403
  }
47082
47404
  function defaultSkillRoots(_cwd) {
47083
- const marketplaces = path43.join(os40.homedir(), ".claude", "plugins", "marketplaces");
47405
+ const marketplaces = path44.join(os42.homedir(), ".claude", "plugins", "marketplaces");
47084
47406
  const roots = [];
47085
47407
  let registries;
47086
47408
  try {
47087
- registries = fs45.readdirSync(marketplaces, { withFileTypes: true });
47409
+ registries = fs46.readdirSync(marketplaces, { withFileTypes: true });
47088
47410
  } catch {
47089
47411
  return [];
47090
47412
  }
47091
47413
  for (const registry of registries) {
47092
47414
  if (!registry.isDirectory()) continue;
47093
- const pluginsDir = path43.join(marketplaces, registry.name, "plugins");
47415
+ const pluginsDir = path44.join(marketplaces, registry.name, "plugins");
47094
47416
  let plugins;
47095
47417
  try {
47096
- plugins = fs45.readdirSync(pluginsDir, { withFileTypes: true });
47418
+ plugins = fs46.readdirSync(pluginsDir, { withFileTypes: true });
47097
47419
  } catch {
47098
47420
  continue;
47099
47421
  }
47100
47422
  for (const plugin of plugins) {
47101
47423
  if (!plugin.isDirectory()) continue;
47102
- roots.push(path43.join(pluginsDir, plugin.name));
47424
+ roots.push(path44.join(pluginsDir, plugin.name));
47103
47425
  }
47104
47426
  }
47105
47427
  return roots;
47106
47428
  }
47107
47429
  function resolveUserSkillRoot(entry, cwd) {
47108
47430
  if (!entry) return null;
47109
- if (entry.startsWith("~/") || entry === "~") return path43.join(os40.homedir(), entry.slice(1));
47110
- if (path43.isAbsolute(entry)) return entry;
47111
- if (!cwd || !path43.isAbsolute(cwd)) return null;
47112
- return path43.join(cwd, entry);
47431
+ if (entry.startsWith("~/") || entry === "~") return path44.join(os42.homedir(), entry.slice(1));
47432
+ if (path44.isAbsolute(entry)) return entry;
47433
+ if (!cwd || !path44.isAbsolute(cwd)) return null;
47434
+ return path44.join(cwd, entry);
47113
47435
  }
47114
47436
 
47115
47437
  // src/cli/commands/check.ts
@@ -47118,11 +47440,11 @@ init_audit();
47118
47440
 
47119
47441
  // src/review-pending.ts
47120
47442
  init_hasher();
47121
- import fs46 from "fs";
47122
- import os41 from "os";
47123
- import path44 from "path";
47443
+ import fs47 from "fs";
47444
+ import os43 from "os";
47445
+ import path45 from "path";
47124
47446
  function storePath() {
47125
- return process.env.NODE9_PENDING_STORE || path44.join(os41.homedir(), ".node9", "pending-reviews.json");
47447
+ return process.env.NODE9_PENDING_STORE || path45.join(os43.homedir(), ".node9", "pending-reviews.json");
47126
47448
  }
47127
47449
  var TTL_MS2 = 6 * 60 * 60 * 1e3;
47128
47450
  var MAX_ENTRIES = 500;
@@ -47139,7 +47461,7 @@ function reviewCorrelationKey(payload) {
47139
47461
  }
47140
47462
  function read() {
47141
47463
  try {
47142
- const parsed = JSON.parse(fs46.readFileSync(storePath(), "utf-8"));
47464
+ const parsed = JSON.parse(fs47.readFileSync(storePath(), "utf-8"));
47143
47465
  if (parsed && Array.isArray(parsed.entries)) return parsed;
47144
47466
  } catch {
47145
47467
  }
@@ -47148,11 +47470,11 @@ function read() {
47148
47470
  function write(store) {
47149
47471
  try {
47150
47472
  const p = storePath();
47151
- const dir = path44.dirname(p);
47152
- if (!fs46.existsSync(dir)) fs46.mkdirSync(dir, { recursive: true });
47473
+ const dir = path45.dirname(p);
47474
+ if (!fs47.existsSync(dir)) fs47.mkdirSync(dir, { recursive: true });
47153
47475
  const tmp = `${p}.${process.pid}.tmp`;
47154
- fs46.writeFileSync(tmp, JSON.stringify(store));
47155
- fs46.renameSync(tmp, p);
47476
+ fs47.writeFileSync(tmp, JSON.stringify(store));
47477
+ fs47.renameSync(tmp, p);
47156
47478
  } catch {
47157
47479
  }
47158
47480
  }
@@ -47265,9 +47587,9 @@ function registerCheckCommand(program2) {
47265
47587
  } catch (err2) {
47266
47588
  const tempConfig = getConfig();
47267
47589
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
47268
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47590
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47269
47591
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
47270
- fs47.appendFileSync(
47592
+ fs48.appendFileSync(
47271
47593
  logPath,
47272
47594
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
47273
47595
  RAW: ${raw}
@@ -47280,14 +47602,14 @@ RAW: ${raw}
47280
47602
  const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
47281
47603
  if (process.env.NODE9_DEBUG === "1") {
47282
47604
  try {
47283
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47284
- if (!fs47.existsSync(path45.dirname(logPath)))
47285
- fs47.mkdirSync(path45.dirname(logPath), { recursive: true });
47605
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47606
+ if (!fs48.existsSync(path46.dirname(logPath)))
47607
+ fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
47286
47608
  const sanitized = JSON.stringify({
47287
47609
  ...payload,
47288
47610
  prompt: `<redacted, ${prompt.length} bytes>`
47289
47611
  });
47290
- fs47.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
47612
+ fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
47291
47613
  `);
47292
47614
  } catch {
47293
47615
  }
@@ -47308,8 +47630,8 @@ RAW: ${raw}
47308
47630
  );
47309
47631
  const reason = `\u{1F6A8} Node9 DLP: ${dlpMatch.patternName} detected in prompt (${dlpMatch.redactedSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
47310
47632
  try {
47311
- const ttyFd = fs47.openSync("/dev/tty", "w");
47312
- fs47.writeSync(
47633
+ const ttyFd = fs48.openSync("/dev/tty", "w");
47634
+ fs48.writeSync(
47313
47635
  ttyFd,
47314
47636
  chalk9.bgRed.white.bold(`
47315
47637
  \u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
@@ -47319,7 +47641,7 @@ RAW: ${raw}
47319
47641
 
47320
47642
  `)
47321
47643
  );
47322
- fs47.closeSync(ttyFd);
47644
+ fs48.closeSync(ttyFd);
47323
47645
  } catch {
47324
47646
  }
47325
47647
  const isCodex = agent2 === "Codex";
@@ -47338,16 +47660,17 @@ RAW: ${raw}
47338
47660
  process.exit(2);
47339
47661
  }
47340
47662
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
47341
- const safeCwdForConfig = typeof payloadCwd === "string" && path45.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47663
+ const safeCwdForConfig = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47342
47664
  const config = getConfig(safeCwdForConfig);
47343
- if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
47665
+ const daemonDown = !isDaemonRunning();
47666
+ if (config.settings.autoStartDaemon && daemonDown && !process.env.NODE9_NO_AUTO_DAEMON) {
47344
47667
  try {
47345
47668
  const scriptPath = process.argv[1];
47346
- if (typeof scriptPath !== "string" || !path45.isAbsolute(scriptPath))
47669
+ if (typeof scriptPath !== "string" || !path46.isAbsolute(scriptPath))
47347
47670
  throw new Error("node9: argv[1] is not an absolute path");
47348
- const resolvedScript = fs47.realpathSync(scriptPath);
47349
- const packageDist = fs47.realpathSync(path45.resolve(__dirname, "../.."));
47350
- if (!resolvedScript.startsWith(packageDist + path45.sep) && resolvedScript !== packageDist)
47671
+ const resolvedScript = fs48.realpathSync(scriptPath);
47672
+ const packageDist = fs48.realpathSync(path46.resolve(__dirname, "../.."));
47673
+ if (!resolvedScript.startsWith(packageDist + path46.sep) && resolvedScript !== packageDist)
47351
47674
  throw new Error(
47352
47675
  `node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
47353
47676
  );
@@ -47362,17 +47685,27 @@ RAW: ${raw}
47362
47685
  ]) {
47363
47686
  delete safeEnv[key];
47364
47687
  }
47365
- const d = spawn5(process.execPath, [scriptPath, "daemon"], {
47366
- detached: true,
47367
- stdio: "ignore",
47368
- env: { ...safeEnv, NODE9_AUTO_STARTED: "1" }
47369
- });
47370
- d.unref();
47688
+ const startupFd = openStartupLogFd();
47689
+ try {
47690
+ const d = spawn5(process.execPath, [scriptPath, "daemon"], {
47691
+ detached: true,
47692
+ stdio: ["ignore", "ignore", startupFd ?? "ignore"],
47693
+ env: { ...safeEnv, NODE9_AUTO_STARTED: "1" }
47694
+ });
47695
+ d.unref();
47696
+ } finally {
47697
+ if (startupFd !== void 0) {
47698
+ try {
47699
+ fs48.closeSync(startupFd);
47700
+ } catch {
47701
+ }
47702
+ }
47703
+ }
47371
47704
  } catch (spawnErr) {
47372
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47705
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47373
47706
  const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
47374
47707
  try {
47375
- fs47.appendFileSync(
47708
+ fs48.appendFileSync(
47376
47709
  logPath,
47377
47710
  `[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
47378
47711
  `
@@ -47380,12 +47713,16 @@ RAW: ${raw}
47380
47713
  } catch {
47381
47714
  }
47382
47715
  }
47716
+ } else if (daemonDown && !isTestingMode()) {
47717
+ logAutostartSkipThrottled(
47718
+ !config.settings.autoStartDaemon ? "autoStartDaemon=false" : process.env.NODE9_NO_AUTO_DAEMON ? "NODE9_NO_AUTO_DAEMON" : "unknown"
47719
+ );
47383
47720
  }
47384
47721
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
47385
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47386
- if (!fs47.existsSync(path45.dirname(logPath)))
47387
- fs47.mkdirSync(path45.dirname(logPath), { recursive: true });
47388
- fs47.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
47722
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47723
+ if (!fs48.existsSync(path46.dirname(logPath)))
47724
+ fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
47725
+ fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
47389
47726
  `);
47390
47727
  }
47391
47728
  const rawToolName = sanitize2(extractToolName(payload));
@@ -47399,8 +47736,8 @@ RAW: ${raw}
47399
47736
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
47400
47737
  let ttyFd = null;
47401
47738
  try {
47402
- ttyFd = fs47.openSync("/dev/tty", "w");
47403
- const writeTty = (line) => fs47.writeSync(ttyFd, line + "\n");
47739
+ ttyFd = fs48.openSync("/dev/tty", "w");
47740
+ const writeTty = (line) => fs48.writeSync(ttyFd, line + "\n");
47404
47741
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
47405
47742
  writeTty(chalk9.bgRed.white.bold(`
47406
47743
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -47419,7 +47756,7 @@ RAW: ${raw}
47419
47756
  } finally {
47420
47757
  if (ttyFd !== null)
47421
47758
  try {
47422
- fs47.closeSync(ttyFd);
47759
+ fs48.closeSync(ttyFd);
47423
47760
  } catch {
47424
47761
  }
47425
47762
  }
@@ -47476,8 +47813,8 @@ RAW: ${raw}
47476
47813
  } catch {
47477
47814
  }
47478
47815
  try {
47479
- const ttyFd = fs47.openSync("/dev/tty", "w");
47480
- fs47.writeSync(
47816
+ const ttyFd = fs48.openSync("/dev/tty", "w");
47817
+ fs48.writeSync(
47481
47818
  ttyFd,
47482
47819
  chalk9.yellow(
47483
47820
  `
@@ -47485,7 +47822,7 @@ RAW: ${raw}
47485
47822
  `
47486
47823
  )
47487
47824
  );
47488
- fs47.closeSync(ttyFd);
47825
+ fs48.closeSync(ttyFd);
47489
47826
  } catch {
47490
47827
  }
47491
47828
  if (agent === "GitHub Copilot") {
@@ -47517,17 +47854,17 @@ RAW: ${raw}
47517
47854
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
47518
47855
  if (skillPinCfg.enabled && safeSessionId) {
47519
47856
  try {
47520
- const sessionsDir = path45.join(os42.homedir(), ".node9", "skill-sessions");
47521
- const flagPath = path45.join(sessionsDir, `${safeSessionId}.json`);
47857
+ const sessionsDir = path46.join(os44.homedir(), ".node9", "skill-sessions");
47858
+ const flagPath = path46.join(sessionsDir, `${safeSessionId}.json`);
47522
47859
  let flag = null;
47523
47860
  try {
47524
- flag = JSON.parse(fs47.readFileSync(flagPath, "utf-8"));
47861
+ flag = JSON.parse(fs48.readFileSync(flagPath, "utf-8"));
47525
47862
  } catch {
47526
47863
  }
47527
47864
  const writeFlag = (data2) => {
47528
47865
  try {
47529
- fs47.mkdirSync(sessionsDir, { recursive: true });
47530
- fs47.writeFileSync(
47866
+ fs48.mkdirSync(sessionsDir, { recursive: true });
47867
+ fs48.writeFileSync(
47531
47868
  flagPath,
47532
47869
  JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
47533
47870
  { mode: 384 }
@@ -47538,8 +47875,8 @@ RAW: ${raw}
47538
47875
  const sendSkillWarn = (detail, recoveryCmd) => {
47539
47876
  let ttyFd = null;
47540
47877
  try {
47541
- ttyFd = fs47.openSync("/dev/tty", "w");
47542
- const w = (line) => fs47.writeSync(ttyFd, line + "\n");
47878
+ ttyFd = fs48.openSync("/dev/tty", "w");
47879
+ const w = (line) => fs48.writeSync(ttyFd, line + "\n");
47543
47880
  w(chalk9.yellow(`
47544
47881
  \u26A0\uFE0F Node9: installed skill drift detected`));
47545
47882
  w(chalk9.gray(` ${detail}`));
@@ -47554,7 +47891,7 @@ RAW: ${raw}
47554
47891
  } finally {
47555
47892
  if (ttyFd !== null)
47556
47893
  try {
47557
- fs47.closeSync(ttyFd);
47894
+ fs48.closeSync(ttyFd);
47558
47895
  } catch {
47559
47896
  }
47560
47897
  }
@@ -47570,7 +47907,7 @@ RAW: ${raw}
47570
47907
  return;
47571
47908
  }
47572
47909
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
47573
- const absoluteCwd = typeof payloadCwd === "string" && path45.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47910
+ const absoluteCwd = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47574
47911
  const extraRoots = skillPinCfg.roots;
47575
47912
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
47576
47913
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -47611,10 +47948,10 @@ RAW: ${raw}
47611
47948
  }
47612
47949
  try {
47613
47950
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
47614
- for (const name of fs47.readdirSync(sessionsDir)) {
47615
- const p = path45.join(sessionsDir, name);
47951
+ for (const name of fs48.readdirSync(sessionsDir)) {
47952
+ const p = path46.join(sessionsDir, name);
47616
47953
  try {
47617
- if (fs47.statSync(p).mtimeMs < cutoff) fs47.unlinkSync(p);
47954
+ if (fs48.statSync(p).mtimeMs < cutoff) fs48.unlinkSync(p);
47618
47955
  } catch {
47619
47956
  }
47620
47957
  }
@@ -47624,9 +47961,9 @@ RAW: ${raw}
47624
47961
  } catch (err2) {
47625
47962
  if (process.env.NODE9_DEBUG === "1") {
47626
47963
  try {
47627
- const dbg = path45.join(os42.homedir(), ".node9", "hook-debug.log");
47964
+ const dbg = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47628
47965
  const msg = err2 instanceof Error ? err2.message : String(err2);
47629
- fs47.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
47966
+ fs48.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
47630
47967
  `);
47631
47968
  } catch {
47632
47969
  }
@@ -47636,7 +47973,7 @@ RAW: ${raw}
47636
47973
  if (shouldSnapshot(toolName, toolInput, config)) {
47637
47974
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
47638
47975
  }
47639
- const safeCwdForAuth = typeof payloadCwd === "string" && path45.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47976
+ const safeCwdForAuth = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47640
47977
  const askMode = resolveAskMode(agent, opts, config);
47641
47978
  const result = await authorizeHeadless(toolName, toolInput, meta, {
47642
47979
  cwd: safeCwdForAuth,
@@ -47654,12 +47991,12 @@ RAW: ${raw}
47654
47991
  }
47655
47992
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
47656
47993
  try {
47657
- const tty = fs47.openSync("/dev/tty", "w");
47658
- fs47.writeSync(
47994
+ const tty = fs48.openSync("/dev/tty", "w");
47995
+ fs48.writeSync(
47659
47996
  tty,
47660
47997
  chalk9.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
47661
47998
  );
47662
- fs47.closeSync(tty);
47999
+ fs48.closeSync(tty);
47663
48000
  } catch {
47664
48001
  }
47665
48002
  const daemonReady = await autoStartDaemonAndWait();
@@ -47686,9 +48023,9 @@ RAW: ${raw}
47686
48023
  });
47687
48024
  } catch (err2) {
47688
48025
  if (process.env.NODE9_DEBUG === "1") {
47689
- const logPath = path45.join(os42.homedir(), ".node9", "hook-debug.log");
48026
+ const logPath = path46.join(os44.homedir(), ".node9", "hook-debug.log");
47690
48027
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
47691
- fs47.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
48028
+ fs48.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
47692
48029
  `);
47693
48030
  }
47694
48031
  process.exit(0);
@@ -47724,9 +48061,9 @@ RAW: ${raw}
47724
48061
  // src/cli/commands/log.ts
47725
48062
  init_audit();
47726
48063
  init_config();
47727
- import fs48 from "fs";
47728
- import path46 from "path";
47729
- import os43 from "os";
48064
+ import fs49 from "fs";
48065
+ import path47 from "path";
48066
+ import os45 from "os";
47730
48067
  init_daemon();
47731
48068
  init_dlp();
47732
48069
 
@@ -47834,10 +48171,10 @@ function registerLogCommand(program2) {
47834
48171
  if (rawToolName !== tool) entry.agentToolName = rawToolName;
47835
48172
  const payloadSessionId = payload.session_id ?? payload.conversationId;
47836
48173
  if (payloadSessionId) entry.sessionId = payloadSessionId;
47837
- const logPath = path46.join(os43.homedir(), ".node9", "audit.log");
47838
- if (!fs48.existsSync(path46.dirname(logPath)))
47839
- fs48.mkdirSync(path46.dirname(logPath), { recursive: true });
47840
- fs48.appendFileSync(logPath, JSON.stringify(entry) + "\n");
48174
+ const logPath = path47.join(os45.homedir(), ".node9", "audit.log");
48175
+ if (!fs49.existsSync(path47.dirname(logPath)))
48176
+ fs49.mkdirSync(path47.dirname(logPath), { recursive: true });
48177
+ fs49.appendFileSync(logPath, JSON.stringify(entry) + "\n");
47841
48178
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
47842
48179
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
47843
48180
  if (command) {
@@ -47871,7 +48208,7 @@ function registerLogCommand(program2) {
47871
48208
  }
47872
48209
  }
47873
48210
  const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
47874
- const safeCwd = typeof payloadCwd === "string" && path46.isAbsolute(payloadCwd) ? payloadCwd : void 0;
48211
+ const safeCwd = typeof payloadCwd === "string" && path47.isAbsolute(payloadCwd) ? payloadCwd : void 0;
47875
48212
  const config = getConfig(safeCwd);
47876
48213
  {
47877
48214
  const toolOutput = payload.tool_response?.output;
@@ -47948,9 +48285,9 @@ function registerLogCommand(program2) {
47948
48285
  const msg = err2 instanceof Error ? err2.message : String(err2);
47949
48286
  process.stderr.write(`[Node9] audit log error: ${msg}
47950
48287
  `);
47951
- const debugPath = path46.join(os43.homedir(), ".node9", "hook-debug.log");
48288
+ const debugPath = path47.join(os45.homedir(), ".node9", "hook-debug.log");
47952
48289
  try {
47953
- fs48.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
48290
+ fs49.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
47954
48291
  `);
47955
48292
  } catch {
47956
48293
  }
@@ -47977,16 +48314,16 @@ function registerLogCommand(program2) {
47977
48314
  init_shields();
47978
48315
  init_build();
47979
48316
  import chalk10 from "chalk";
47980
- import fs50 from "fs";
47981
- import path48 from "path";
47982
- import os44 from "os";
48317
+ import fs51 from "fs";
48318
+ import path49 from "path";
48319
+ import os46 from "os";
47983
48320
 
47984
48321
  // src/shields/create.ts
47985
48322
  init_dist();
47986
48323
  init_shields();
47987
48324
  init_audit();
47988
- import fs49 from "fs";
47989
- import path47 from "path";
48325
+ import fs50 from "fs";
48326
+ import path48 from "path";
47990
48327
  function builtinNames() {
47991
48328
  const names = /* @__PURE__ */ new Set();
47992
48329
  for (const def of Object.values(BUILTIN_SHIELDS)) {
@@ -48003,8 +48340,8 @@ function createShield(def, opts = {}) {
48003
48340
  error: `"${name}" is a built-in shield \u2014 choose a different name (a user shield with this name would shadow the built-in).`
48004
48341
  };
48005
48342
  }
48006
- const filePath = path47.join(USER_SHIELDS_DIR_PATH, `${name}.json`);
48007
- if (!opts.overwrite && fs49.existsSync(filePath)) {
48343
+ const filePath = path48.join(USER_SHIELDS_DIR_PATH, `${name}.json`);
48344
+ if (!opts.overwrite && fs50.existsSync(filePath)) {
48008
48345
  return {
48009
48346
  ok: false,
48010
48347
  error: `Shield "${name}" already exists at ${filePath}. Pass --overwrite to replace it.`
@@ -48069,8 +48406,8 @@ var COMMUNITY_INDEX_URL = "https://raw.githubusercontent.com/node9ai/node9-proxy
48069
48406
  function readCloudShields() {
48070
48407
  const out = /* @__PURE__ */ new Set();
48071
48408
  try {
48072
- const file = path48.join(os44.homedir(), ".node9", "rules-cache.json");
48073
- const raw = JSON.parse(fs50.readFileSync(file, "utf-8"));
48409
+ const file = path49.join(os46.homedir(), ".node9", "rules-cache.json");
48410
+ const raw = JSON.parse(fs51.readFileSync(file, "utf-8"));
48074
48411
  for (const r of raw.rules ?? []) {
48075
48412
  const rule = r;
48076
48413
  const fromSource = rule.source?.startsWith("SHIELD:") ? rule.source.slice("SHIELD:".length).toLowerCase() : void 0;
@@ -48387,7 +48724,7 @@ function registerShieldCommand(program2) {
48387
48724
  if (opts.fromFile) {
48388
48725
  let raw;
48389
48726
  try {
48390
- raw = JSON.parse(fs50.readFileSync(opts.fromFile, "utf-8"));
48727
+ raw = JSON.parse(fs51.readFileSync(opts.fromFile, "utf-8"));
48391
48728
  } catch (err2) {
48392
48729
  console.error(
48393
48730
  chalk10.red(`
@@ -48509,14 +48846,31 @@ function registerConfigShowCommand(program2) {
48509
48846
  init_daemon();
48510
48847
  init_config();
48511
48848
  init_agent_wiring();
48849
+ init_sync();
48850
+ init_service();
48512
48851
  import chalk11 from "chalk";
48513
- import fs51 from "fs";
48514
- import path49 from "path";
48515
- import os45 from "os";
48852
+ import fs52 from "fs";
48853
+ import path50 from "path";
48854
+ import os47 from "os";
48516
48855
  import { execSync } from "child_process";
48856
+
48857
+ // src/lib/relative-time.ts
48858
+ function agoLabel(iso, now = Date.now()) {
48859
+ const ms = now - new Date(iso).getTime();
48860
+ if (!Number.isFinite(ms) || ms < 0) return "just now";
48861
+ const min = Math.floor(ms / 6e4);
48862
+ if (min < 1) return "just now";
48863
+ if (min < 60) return `${min} min ago`;
48864
+ const hr = Math.floor(min / 60);
48865
+ if (hr < 24) return `${hr} hour${hr === 1 ? "" : "s"} ago`;
48866
+ const d = Math.floor(hr / 24);
48867
+ return `${d} day${d === 1 ? "" : "s"} ago`;
48868
+ }
48869
+
48870
+ // src/cli/commands/doctor.ts
48517
48871
  function registerDoctorCommand(program2, version2) {
48518
48872
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
48519
- const homeDir2 = os45.homedir();
48873
+ const homeDir2 = os47.homedir();
48520
48874
  let failures = 0;
48521
48875
  function pass(msg) {
48522
48876
  console.log(chalk11.green(" \u2705 ") + msg);
@@ -48562,10 +48916,10 @@ function registerDoctorCommand(program2, version2) {
48562
48916
  );
48563
48917
  }
48564
48918
  section("Configuration");
48565
- const globalConfigPath = path49.join(homeDir2, ".node9", "config.json");
48566
- if (fs51.existsSync(globalConfigPath)) {
48919
+ const globalConfigPath = path50.join(homeDir2, ".node9", "config.json");
48920
+ if (fs52.existsSync(globalConfigPath)) {
48567
48921
  try {
48568
- JSON.parse(fs51.readFileSync(globalConfigPath, "utf-8"));
48922
+ JSON.parse(fs52.readFileSync(globalConfigPath, "utf-8"));
48569
48923
  pass("~/.node9/config.json found and valid");
48570
48924
  } catch {
48571
48925
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -48573,10 +48927,10 @@ function registerDoctorCommand(program2, version2) {
48573
48927
  } else {
48574
48928
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
48575
48929
  }
48576
- const projectConfigPath = path49.join(process.cwd(), "node9.config.json");
48577
- if (fs51.existsSync(projectConfigPath)) {
48930
+ const projectConfigPath = path50.join(process.cwd(), "node9.config.json");
48931
+ if (fs52.existsSync(projectConfigPath)) {
48578
48932
  try {
48579
- JSON.parse(fs51.readFileSync(projectConfigPath, "utf-8"));
48933
+ JSON.parse(fs52.readFileSync(projectConfigPath, "utf-8"));
48580
48934
  pass("node9.config.json found and valid (project)");
48581
48935
  } catch {
48582
48936
  fail(
@@ -48585,8 +48939,8 @@ function registerDoctorCommand(program2, version2) {
48585
48939
  );
48586
48940
  }
48587
48941
  }
48588
- const credsPath = path49.join(homeDir2, ".node9", "credentials.json");
48589
- if (fs51.existsSync(credsPath)) {
48942
+ const credsPath = path50.join(homeDir2, ".node9", "credentials.json");
48943
+ if (fs52.existsSync(credsPath)) {
48590
48944
  pass("Cloud credentials found (~/.node9/credentials.json)");
48591
48945
  } else {
48592
48946
  warn(
@@ -48626,11 +48980,31 @@ function registerDoctorCommand(program2, version2) {
48626
48980
  "Run: node9 daemon --background"
48627
48981
  );
48628
48982
  }
48983
+ const autostart = autostartAdvice({
48984
+ installed: isDaemonServiceInstalled(),
48985
+ enabled: isDaemonServiceEnabled(),
48986
+ cloudEnabled: !!getConfig().settings.approvers?.cloud
48987
+ });
48988
+ if (autostart) warn(autostart.message, autostart.hint);
48989
+ if (fs52.existsSync(path50.join(os47.homedir(), ".node9", "credentials.json")) && getConfig().settings.approvers?.cloud) {
48990
+ section("Policy sync");
48991
+ const health = readSyncHealth();
48992
+ if (isPolicyStale(Date.now(), health)) {
48993
+ const when = health.lastCheckedAt ? `last reached the cloud ${agoLabel(health.lastCheckedAt)}` : "never reached the cloud";
48994
+ const fails = health.consecutiveFailures > 0 ? ` (${health.consecutiveFailures} consecutive failure${health.consecutiveFailures === 1 ? "" : "s"}${health.lastError ? `: ${health.lastError}` : ""})` : "";
48995
+ warn(
48996
+ `Cloud policy is STALE \u2014 ${when}${fails}. The cached policy is still enforced, but changes from the dashboard are not reaching this machine.`,
48997
+ "Run: node9 policy sync (and ensure the daemon autostarts: systemctl --user enable --now node9-daemon)"
48998
+ );
48999
+ } else if (health.lastCheckedAt) {
49000
+ pass(`Cloud policy fresh \u2014 last synced ${agoLabel(health.lastCheckedAt)}`);
49001
+ }
49002
+ }
48629
49003
  section("Cloud audit shipping");
48630
49004
  try {
48631
49005
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
48632
49006
  const cfg = getConfig();
48633
- const creds = fs51.existsSync(path49.join(os45.homedir(), ".node9", "credentials.json"));
49007
+ const creds = fs52.existsSync(path50.join(os47.homedir(), ".node9", "credentials.json"));
48634
49008
  if (!creds) {
48635
49009
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
48636
49010
  } else if (!cfg.settings.approvers.cloud) {
@@ -48680,9 +49054,9 @@ function registerDoctorCommand(program2, version2) {
48680
49054
 
48681
49055
  // src/cli/commands/audit.ts
48682
49056
  import chalk12 from "chalk";
48683
- import fs52 from "fs";
48684
- import path50 from "path";
48685
- import os46 from "os";
49057
+ import fs53 from "fs";
49058
+ import path51 from "path";
49059
+ import os48 from "os";
48686
49060
  function formatRelativeTime(timestamp) {
48687
49061
  const diff = Date.now() - new Date(timestamp).getTime();
48688
49062
  const sec = Math.floor(diff / 1e3);
@@ -48695,14 +49069,14 @@ function formatRelativeTime(timestamp) {
48695
49069
  }
48696
49070
  function registerAuditCommand(program2) {
48697
49071
  program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
48698
- const logPath = path50.join(os46.homedir(), ".node9", "audit.log");
48699
- if (!fs52.existsSync(logPath)) {
49072
+ const logPath = path51.join(os48.homedir(), ".node9", "audit.log");
49073
+ if (!fs53.existsSync(logPath)) {
48700
49074
  console.log(
48701
49075
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
48702
49076
  );
48703
49077
  return;
48704
49078
  }
48705
- const raw = fs52.readFileSync(logPath, "utf-8");
49079
+ const raw = fs53.readFileSync(logPath, "utf-8");
48706
49080
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
48707
49081
  let entries = lines.flatMap((line) => {
48708
49082
  try {
@@ -48761,9 +49135,9 @@ import chalk13 from "chalk";
48761
49135
  init_costSync();
48762
49136
  init_litellm();
48763
49137
  init_cost_codex();
48764
- import fs53 from "fs";
48765
- import os47 from "os";
48766
- import path51 from "path";
49138
+ import fs54 from "fs";
49139
+ import os49 from "os";
49140
+ import path52 from "path";
48767
49141
  var TEST_COMMAND_RE3 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
48768
49142
  function buildTestTimestamps(allEntries) {
48769
49143
  const testTs = /* @__PURE__ */ new Set();
@@ -48843,8 +49217,8 @@ function getDateRange(period, now) {
48843
49217
  }
48844
49218
  }
48845
49219
  function parseAuditLog(logPath) {
48846
- if (!fs53.existsSync(logPath)) return [];
48847
- const raw = fs53.readFileSync(logPath, "utf-8");
49220
+ if (!fs54.existsSync(logPath)) return [];
49221
+ const raw = fs54.readFileSync(logPath, "utf-8");
48848
49222
  return raw.split("\n").flatMap((line) => {
48849
49223
  if (!line.trim()) return [];
48850
49224
  try {
@@ -48891,25 +49265,25 @@ function freezeClaudeCost(acc) {
48891
49265
  };
48892
49266
  }
48893
49267
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
48894
- const projPath = path51.join(projectsDir, proj);
49268
+ const projPath = path52.join(projectsDir, proj);
48895
49269
  let files;
48896
49270
  try {
48897
- const stat = fs53.statSync(projPath);
49271
+ const stat = fs54.statSync(projPath);
48898
49272
  if (!stat.isDirectory()) return;
48899
- files = fs53.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
49273
+ files = fs54.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
48900
49274
  } catch {
48901
49275
  return;
48902
49276
  }
48903
49277
  const startMs = start.getTime();
48904
49278
  for (const file of files) {
48905
- const filePath = path51.join(projPath, file);
49279
+ const filePath = path52.join(projPath, file);
48906
49280
  try {
48907
- if (fs53.statSync(filePath).mtimeMs < startMs) continue;
49281
+ if (fs54.statSync(filePath).mtimeMs < startMs) continue;
48908
49282
  } catch {
48909
49283
  continue;
48910
49284
  }
48911
49285
  try {
48912
- const raw = fs53.readFileSync(filePath, "utf-8");
49286
+ const raw = fs54.readFileSync(filePath, "utf-8");
48913
49287
  for (const line of raw.split("\n")) {
48914
49288
  if (!line.trim()) continue;
48915
49289
  let entry;
@@ -48959,10 +49333,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
48959
49333
  }
48960
49334
  function loadClaudeCost(start, end, projectsDir) {
48961
49335
  const acc = emptyClaudeCostAccumulator();
48962
- if (!fs53.existsSync(projectsDir)) return freezeClaudeCost(acc);
49336
+ if (!fs54.existsSync(projectsDir)) return freezeClaudeCost(acc);
48963
49337
  let dirs;
48964
49338
  try {
48965
- dirs = fs53.readdirSync(projectsDir);
49339
+ dirs = fs54.readdirSync(projectsDir);
48966
49340
  } catch {
48967
49341
  return freezeClaudeCost(acc);
48968
49342
  }
@@ -48974,7 +49348,7 @@ function loadClaudeCost(start, end, projectsDir) {
48974
49348
  function processCodexCostFile(filePath, start, end, acc) {
48975
49349
  let lines;
48976
49350
  try {
48977
- lines = fs53.readFileSync(filePath, "utf-8").split("\n");
49351
+ lines = fs54.readFileSync(filePath, "utf-8").split("\n");
48978
49352
  } catch {
48979
49353
  return;
48980
49354
  }
@@ -49029,31 +49403,31 @@ function processCodexCostFile(filePath, start, end, acc) {
49029
49403
  }
49030
49404
  function listCodexSessionFiles2(sessionsBase) {
49031
49405
  const jsonlFiles = [];
49032
- if (!fs53.existsSync(sessionsBase)) return jsonlFiles;
49406
+ if (!fs54.existsSync(sessionsBase)) return jsonlFiles;
49033
49407
  try {
49034
- for (const year of fs53.readdirSync(sessionsBase)) {
49035
- const yearPath = path51.join(sessionsBase, year);
49408
+ for (const year of fs54.readdirSync(sessionsBase)) {
49409
+ const yearPath = path52.join(sessionsBase, year);
49036
49410
  try {
49037
- if (!fs53.statSync(yearPath).isDirectory()) continue;
49411
+ if (!fs54.statSync(yearPath).isDirectory()) continue;
49038
49412
  } catch {
49039
49413
  continue;
49040
49414
  }
49041
- for (const month of fs53.readdirSync(yearPath)) {
49042
- const monthPath = path51.join(yearPath, month);
49415
+ for (const month of fs54.readdirSync(yearPath)) {
49416
+ const monthPath = path52.join(yearPath, month);
49043
49417
  try {
49044
- if (!fs53.statSync(monthPath).isDirectory()) continue;
49418
+ if (!fs54.statSync(monthPath).isDirectory()) continue;
49045
49419
  } catch {
49046
49420
  continue;
49047
49421
  }
49048
- for (const day of fs53.readdirSync(monthPath)) {
49049
- const dayPath = path51.join(monthPath, day);
49422
+ for (const day of fs54.readdirSync(monthPath)) {
49423
+ const dayPath = path52.join(monthPath, day);
49050
49424
  try {
49051
- if (!fs53.statSync(dayPath).isDirectory()) continue;
49425
+ if (!fs54.statSync(dayPath).isDirectory()) continue;
49052
49426
  } catch {
49053
49427
  continue;
49054
49428
  }
49055
- for (const file of fs53.readdirSync(dayPath)) {
49056
- if (file.endsWith(".jsonl")) jsonlFiles.push(path51.join(dayPath, file));
49429
+ for (const file of fs54.readdirSync(dayPath)) {
49430
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path52.join(dayPath, file));
49057
49431
  }
49058
49432
  }
49059
49433
  }
@@ -49118,13 +49492,13 @@ function freezeGeminiCost(acc) {
49118
49492
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
49119
49493
  const startMs = start.getTime();
49120
49494
  try {
49121
- if (fs53.statSync(filePath).mtimeMs < startMs) return;
49495
+ if (fs54.statSync(filePath).mtimeMs < startMs) return;
49122
49496
  } catch {
49123
49497
  return;
49124
49498
  }
49125
49499
  let raw;
49126
49500
  try {
49127
- raw = fs53.readFileSync(filePath, "utf-8");
49501
+ raw = fs54.readFileSync(filePath, "utf-8");
49128
49502
  } catch {
49129
49503
  return;
49130
49504
  }
@@ -49173,30 +49547,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
49173
49547
  const out = [];
49174
49548
  let dirs;
49175
49549
  try {
49176
- if (!fs53.statSync(geminiTmpDir2).isDirectory()) return out;
49177
- dirs = fs53.readdirSync(geminiTmpDir2);
49550
+ if (!fs54.statSync(geminiTmpDir2).isDirectory()) return out;
49551
+ dirs = fs54.readdirSync(geminiTmpDir2);
49178
49552
  } catch {
49179
49553
  return out;
49180
49554
  }
49181
49555
  for (const proj of dirs) {
49182
- const chatsDir = path51.join(geminiTmpDir2, proj, "chats");
49556
+ const chatsDir = path52.join(geminiTmpDir2, proj, "chats");
49183
49557
  let files;
49184
49558
  try {
49185
- if (!fs53.statSync(chatsDir).isDirectory()) continue;
49186
- files = fs53.readdirSync(chatsDir);
49559
+ if (!fs54.statSync(chatsDir).isDirectory()) continue;
49560
+ files = fs54.readdirSync(chatsDir);
49187
49561
  } catch {
49188
49562
  continue;
49189
49563
  }
49190
49564
  for (const f of files) {
49191
49565
  if (!f.endsWith(".jsonl")) continue;
49192
- out.push({ projectKey: proj, file: path51.join(chatsDir, f) });
49566
+ out.push({ projectKey: proj, file: path52.join(chatsDir, f) });
49193
49567
  }
49194
49568
  }
49195
49569
  return out;
49196
49570
  }
49197
49571
  function loadGeminiCost(start, end, geminiTmpDir2) {
49198
49572
  const acc = emptyGeminiAccumulator();
49199
- if (!fs53.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
49573
+ if (!fs54.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
49200
49574
  for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
49201
49575
  processGeminiCostFile(file, projectKey, start, end, acc);
49202
49576
  }
@@ -49214,11 +49588,11 @@ function dimensionOfBlock(checkedBy, ruleName) {
49214
49588
  }
49215
49589
  function aggregateReportFromAudit(period, opts = {}) {
49216
49590
  const now = opts.now ?? /* @__PURE__ */ new Date();
49217
- const auditLogPath = opts.auditLogPath ?? path51.join(os47.homedir(), ".node9", "audit.log");
49218
- const claudeProjectsDir = opts.claudeProjectsDir ?? path51.join(os47.homedir(), ".claude", "projects");
49219
- const codexSessionsDir2 = opts.codexSessionsDir ?? path51.join(os47.homedir(), ".codex", "sessions");
49220
- const geminiTmpDir2 = opts.geminiTmpDir ?? path51.join(os47.homedir(), ".gemini", "tmp");
49221
- const hasAuditFile = fs53.existsSync(auditLogPath);
49591
+ const auditLogPath = opts.auditLogPath ?? path52.join(os49.homedir(), ".node9", "audit.log");
49592
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path52.join(os49.homedir(), ".claude", "projects");
49593
+ const codexSessionsDir2 = opts.codexSessionsDir ?? path52.join(os49.homedir(), ".codex", "sessions");
49594
+ const geminiTmpDir2 = opts.geminiTmpDir ?? path52.join(os49.homedir(), ".gemini", "tmp");
49595
+ const hasAuditFile = fs54.existsSync(auditLogPath);
49222
49596
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
49223
49597
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
49224
49598
  const { start, end } = getDateRange(period, now);
@@ -50011,10 +50385,12 @@ function registerDaemonCommand(program2) {
50011
50385
  init_core();
50012
50386
  init_daemon();
50013
50387
  init_agent_wiring();
50388
+ init_sync();
50389
+ init_service();
50014
50390
  import chalk15 from "chalk";
50015
- import fs54 from "fs";
50016
- import path52 from "path";
50017
- import os48 from "os";
50391
+ import fs55 from "fs";
50392
+ import path53 from "path";
50393
+ import os50 from "os";
50018
50394
  function printAgentSection(label2, hookPairs, wrapped) {
50019
50395
  console.log(chalk15.bold(` ${label2}`));
50020
50396
  for (const { name, present } of hookPairs) {
@@ -50043,6 +50419,15 @@ function registerStatusCommand(program2) {
50043
50419
  console.log("");
50044
50420
  if (creds && settings.approvers.cloud) {
50045
50421
  console.log(chalk15.green(" \u25CF Agent mode") + chalk15.gray(" \u2014 cloud team policy enforced"));
50422
+ const health = readSyncHealth();
50423
+ if (isPolicyStale(Date.now(), health)) {
50424
+ const when = health.lastCheckedAt ? `last synced ${agoLabel(health.lastCheckedAt)}` : "never synced";
50425
+ const fails = health.consecutiveFailures > 0 ? ` \xB7 ${health.consecutiveFailures} failed attempt${health.consecutiveFailures === 1 ? "" : "s"}${health.lastError ? ` (${health.lastError})` : ""}` : "";
50426
+ console.log(chalk15.yellow(" \u26A0 Policy sync STALE") + chalk15.gray(` \u2014 ${when}${fails}`));
50427
+ console.log(chalk15.gray(" the cached policy is still enforced \u2014 run: node9 doctor"));
50428
+ } else if (health.lastCheckedAt) {
50429
+ console.log(chalk15.gray(` \u21B3 policy synced ${agoLabel(health.lastCheckedAt)}`));
50430
+ }
50046
50431
  } else if (creds && !settings.approvers.cloud) {
50047
50432
  console.log(
50048
50433
  chalk15.blue(" \u25CF Privacy mode \u{1F6E1}\uFE0F") + chalk15.gray(" \u2014 all decisions stay on this machine")
@@ -50060,6 +50445,16 @@ function registerStatusCommand(program2) {
50060
50445
  } else {
50061
50446
  console.log(chalk15.gray(" \u25CB Daemon stopped"));
50062
50447
  }
50448
+ const autostart = autostartAdvice({
50449
+ installed: isDaemonServiceInstalled(),
50450
+ enabled: isDaemonServiceEnabled(),
50451
+ cloudEnabled: !!(creds && settings.approvers.cloud)
50452
+ });
50453
+ if (autostart) {
50454
+ console.log(
50455
+ chalk15.yellow(" \u26A0 daemon autostart not active") + chalk15.gray(" \u2014 won't survive reboot; run: node9 doctor")
50456
+ );
50457
+ }
50063
50458
  if (settings.enableUndo) {
50064
50459
  console.log(
50065
50460
  chalk15.magenta(" \u25CF Undo Engine") + chalk15.gray(` \u2192 Auto-snapshotting Git repos on AI change`)
@@ -50068,20 +50463,20 @@ function registerStatusCommand(program2) {
50068
50463
  console.log("");
50069
50464
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
50070
50465
  console.log(` Mode: ${modeLabel}`);
50071
- const projectConfig = path52.join(process.cwd(), "node9.config.json");
50072
- const globalConfig = path52.join(os48.homedir(), ".node9", "config.json");
50466
+ const projectConfig = path53.join(process.cwd(), "node9.config.json");
50467
+ const globalConfig = path53.join(os50.homedir(), ".node9", "config.json");
50073
50468
  console.log(
50074
- ` Local: ${fs54.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
50469
+ ` Local: ${fs55.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
50075
50470
  );
50076
50471
  console.log(
50077
- ` Global: ${fs54.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
50472
+ ` Global: ${fs55.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
50078
50473
  );
50079
50474
  if (mergedConfig.policy.sandboxPaths.length > 0) {
50080
50475
  console.log(
50081
50476
  ` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
50082
50477
  );
50083
50478
  }
50084
- const wiring = getAgentWiring(os48.homedir()).filter((a) => a.present);
50479
+ const wiring = getAgentWiring(os50.homedir()).filter((a) => a.present);
50085
50480
  if (wiring.length > 0) {
50086
50481
  console.log("");
50087
50482
  console.log(chalk15.bold(" Agent Wiring:"));
@@ -50119,10 +50514,11 @@ init_core();
50119
50514
  init_setup();
50120
50515
  init_shields();
50121
50516
  init_service();
50517
+ init_core();
50122
50518
  import chalk16 from "chalk";
50123
- import fs55 from "fs";
50124
- import path53 from "path";
50125
- import os49 from "os";
50519
+ import fs56 from "fs";
50520
+ import path54 from "path";
50521
+ import os51 from "os";
50126
50522
  import https6 from "https";
50127
50523
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
50128
50524
  function buildTelemetryPayload(agents, firstInstall) {
@@ -50208,16 +50604,16 @@ function registerInitCommand(program2) {
50208
50604
  }
50209
50605
  console.log("");
50210
50606
  }
50211
- const configPath = path53.join(os49.homedir(), ".node9", "config.json");
50212
- const isFirstInstall = !fs55.existsSync(configPath);
50213
- if (fs55.existsSync(configPath) && !options.force) {
50607
+ const configPath = path54.join(os51.homedir(), ".node9", "config.json");
50608
+ const isFirstInstall = !fs56.existsSync(configPath);
50609
+ if (fs56.existsSync(configPath) && !options.force) {
50214
50610
  try {
50215
- const existing = JSON.parse(fs55.readFileSync(configPath, "utf-8"));
50611
+ const existing = JSON.parse(fs56.readFileSync(configPath, "utf-8"));
50216
50612
  const settings = existing.settings ?? {};
50217
50613
  if (settings.mode !== chosenMode) {
50218
50614
  settings.mode = chosenMode;
50219
50615
  existing.settings = settings;
50220
- fs55.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
50616
+ fs56.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
50221
50617
  console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
50222
50618
  } else {
50223
50619
  console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -50230,9 +50626,9 @@ function registerInitCommand(program2) {
50230
50626
  ...DEFAULT_CONFIG,
50231
50627
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
50232
50628
  };
50233
- const dir = path53.dirname(configPath);
50234
- if (!fs55.existsSync(dir)) fs55.mkdirSync(dir, { recursive: true });
50235
- fs55.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
50629
+ const dir = path54.dirname(configPath);
50630
+ if (!fs56.existsSync(dir)) fs56.mkdirSync(dir, { recursive: true });
50631
+ fs56.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
50236
50632
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
50237
50633
  console.log(chalk16.gray(` Mode: ${chosenMode}`));
50238
50634
  }
@@ -50284,8 +50680,13 @@ function registerInitCommand(program2) {
50284
50680
  console.log(chalk16.gray(" You can try again later with: node9 daemon install"));
50285
50681
  }
50286
50682
  }
50683
+ } else if (isDaemonServiceEnabled()) {
50684
+ console.log(chalk16.green(" \u2713 Daemon login service already installed & enabled"));
50287
50685
  } else {
50288
- console.log(chalk16.green(" \u2713 Daemon login service already installed"));
50686
+ const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
50687
+ console.log(
50688
+ healed === "repaired" ? chalk16.green(" \u2713 Re-enabled daemon login service (was installed but disabled)") : chalk16.gray(" \xB7 Daemon login service is disabled (autostart off) \u2014 left as-is")
50689
+ );
50289
50690
  }
50290
50691
  if (!isTestingMode()) {
50291
50692
  process.stdout.write(chalk16.dim(" Starting daemon..."));
@@ -50330,11 +50731,11 @@ init_agent_wiring();
50330
50731
  init_setup();
50331
50732
  init_hook_baseline();
50332
50733
  import chalk17 from "chalk";
50333
- import fs56 from "fs";
50734
+ import fs57 from "fs";
50334
50735
  var hasHookSurface = (a) => a.hooks.length > 0;
50335
50736
  function backupForHeal(file) {
50336
50737
  try {
50337
- if (file && fs56.existsSync(file)) fs56.copyFileSync(file, `${file}.node9-heal-bak`);
50738
+ if (file && fs57.existsSync(file)) fs57.copyFileSync(file, `${file}.node9-heal-bak`);
50338
50739
  } catch {
50339
50740
  }
50340
50741
  }
@@ -50501,7 +50902,7 @@ function registerConnectCommand(program2) {
50501
50902
  }
50502
50903
 
50503
50904
  // src/cli/commands/undo.ts
50504
- import path54 from "path";
50905
+ import path55 from "path";
50505
50906
  import chalk20 from "chalk";
50506
50907
 
50507
50908
  // src/tui/undo-navigator.ts
@@ -50660,7 +51061,7 @@ function findMatchingCwd(startDir, history) {
50660
51061
  let dir = startDir;
50661
51062
  while (true) {
50662
51063
  if (cwds.has(dir)) return dir;
50663
- const parent = path54.dirname(dir);
51064
+ const parent = path55.dirname(dir);
50664
51065
  if (parent === dir) return null;
50665
51066
  dir = parent;
50666
51067
  }
@@ -50828,7 +51229,7 @@ function normalizeClientName(name) {
50828
51229
  const sanitized = sanitize4(name).slice(0, 40);
50829
51230
  return sanitized.length > 0 ? sanitized : void 0;
50830
51231
  }
50831
- function reportPinMismatchToCloud(serverKey, agent) {
51232
+ function reportPinMismatchToCloud(serverKey, serverLabel, agent) {
50832
51233
  try {
50833
51234
  const creds = getCredentials();
50834
51235
  if (!creds) return;
@@ -50837,18 +51238,18 @@ function reportPinMismatchToCloud(serverKey, agent) {
50837
51238
  { serverKey, reason: "tool-pin-mismatch" },
50838
51239
  "mcp-pin-mismatch",
50839
51240
  creds,
50840
- { mcpServer: serverKey, agent },
51241
+ { mcpServer: serverLabel, agent },
50841
51242
  void 0,
50842
51243
  false,
50843
51244
  {
50844
51245
  ruleName: "MCP tool definitions changed (possible rug pull)",
50845
- ruleDescription: `The MCP server "${serverKey}" changed its tool definitions since they were pinned. This can indicate a supply-chain attack (tool poisoning). The session was quarantined. Review with: node9 mcp pin update ${serverKey}`
51246
+ ruleDescription: `The MCP server "${serverLabel}" changed its tool definitions since they were pinned. This can indicate a supply-chain attack (tool poisoning). The session was quarantined. Review with: node9 mcp pin update ${serverKey}`
50846
51247
  }
50847
51248
  );
50848
51249
  } catch {
50849
51250
  }
50850
51251
  }
50851
- function reportInventoryToCloud(serverKey, toolCount, agent) {
51252
+ function reportInventoryToCloud(serverKey, serverLabel, toolCount, agent) {
50852
51253
  try {
50853
51254
  const creds = getCredentials();
50854
51255
  if (!creds) return;
@@ -50857,7 +51258,7 @@ function reportInventoryToCloud(serverKey, toolCount, agent) {
50857
51258
  { serverKey, toolCount },
50858
51259
  "mcp-discovered",
50859
51260
  creds,
50860
- { mcpServer: serverKey, agent },
51261
+ { mcpServer: serverLabel, agent },
50861
51262
  void 0,
50862
51263
  false,
50863
51264
  { mcpToolCount: toolCount }
@@ -50865,7 +51266,7 @@ function reportInventoryToCloud(serverKey, toolCount, agent) {
50865
51266
  } catch {
50866
51267
  }
50867
51268
  }
50868
- function reportLargeResponseToCloud(serverKey, responseBytes, agent) {
51269
+ function reportLargeResponseToCloud(serverKey, serverLabel, responseBytes, agent) {
50869
51270
  try {
50870
51271
  const creds = getCredentials();
50871
51272
  if (!creds) return;
@@ -50874,7 +51275,7 @@ function reportLargeResponseToCloud(serverKey, responseBytes, agent) {
50874
51275
  { serverKey, responseBytes },
50875
51276
  "mcp-large-response",
50876
51277
  creds,
50877
- { mcpServer: serverKey, agent },
51278
+ { mcpServer: serverLabel, agent },
50878
51279
  void 0,
50879
51280
  false,
50880
51281
  { mcpResponseBytes: responseBytes }
@@ -51111,7 +51512,12 @@ async function runMcpGateway(upstreamCommand, configName) {
51111
51512
  const currentHash = hashToolDefinitions(tools);
51112
51513
  const pinStatus = checkPin(serverKey, currentHash, gatewayCwd);
51113
51514
  const token = getInternalToken();
51114
- reportInventoryToCloud(serverKey, tools.length, clientName);
51515
+ reportInventoryToCloud(
51516
+ serverKey,
51517
+ resolveServerLabel("", serverKey, upstreamCommand, configName),
51518
+ tools.length,
51519
+ clientName
51520
+ );
51115
51521
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
51116
51522
  const toolSummary = tools.map((t) => ({ name: t.name, description: t.description }));
51117
51523
  fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/discovered`, {
@@ -51183,7 +51589,11 @@ async function runMcpGateway(upstreamCommand, configName) {
51183
51589
  console.error(chalk21.red(" Session quarantined \u2014 all tool calls blocked."));
51184
51590
  console.error(chalk21.yellow(` Run: node9 mcp pin update ${serverKey}
51185
51591
  `));
51186
- reportPinMismatchToCloud(serverKey, clientName);
51592
+ reportPinMismatchToCloud(
51593
+ serverKey,
51594
+ resolveServerLabel("", serverKey, upstreamCommand, configName),
51595
+ clientName
51596
+ );
51187
51597
  const errorResponse = {
51188
51598
  jsonrpc: "2.0",
51189
51599
  id: parsed.id,
@@ -51229,7 +51639,12 @@ async function runMcpGateway(upstreamCommand, configName) {
51229
51639
  `\u26A1 Node9: Large MCP response from '${toolName}' (${(line.length / 1024).toFixed(0)}KB) \u2014 context window enlarged`
51230
51640
  )
51231
51641
  );
51232
- reportLargeResponseToCloud(serverKey, line.length, clientName);
51642
+ reportLargeResponseToCloud(
51643
+ serverKey,
51644
+ resolveServerLabel("", serverKey, upstreamCommand, configName),
51645
+ line.length,
51646
+ clientName
51647
+ );
51233
51648
  if (isDaemonRunning() && process.env.NODE9_TESTING !== "1") {
51234
51649
  const token = getInternalToken();
51235
51650
  fetch(`http://${DAEMON_HOST}:${DAEMON_PORT}/mcp/large-response`, {
@@ -51280,18 +51695,18 @@ function registerMcpGatewayCommand(program2) {
51280
51695
 
51281
51696
  // src/mcp-server/index.ts
51282
51697
  import readline5 from "readline";
51283
- import fs58 from "fs";
51284
- import os51 from "os";
51285
- import path56 from "path";
51698
+ import fs59 from "fs";
51699
+ import os53 from "os";
51700
+ import path57 from "path";
51286
51701
  import { spawnSync as spawnSync4 } from "child_process";
51287
51702
  init_core();
51288
51703
  init_daemon();
51289
51704
  init_shields();
51290
51705
 
51291
51706
  // src/auth/egress-config.ts
51292
- import fs57 from "fs";
51293
- import os50 from "os";
51294
- import path55 from "path";
51707
+ import fs58 from "fs";
51708
+ import os52 from "os";
51709
+ import path56 from "path";
51295
51710
  var DEFAULT_EGRESS = {
51296
51711
  enabled: false,
51297
51712
  mode: "review",
@@ -51300,12 +51715,12 @@ var DEFAULT_EGRESS = {
51300
51715
  allowPrivate: true
51301
51716
  };
51302
51717
  function egressConfigPath() {
51303
- return path55.join(os50.homedir(), ".node9", "config.json");
51718
+ return path56.join(os52.homedir(), ".node9", "config.json");
51304
51719
  }
51305
51720
  function readEgressRawConfig() {
51306
51721
  let text;
51307
51722
  try {
51308
- text = fs57.readFileSync(egressConfigPath(), "utf8");
51723
+ text = fs58.readFileSync(egressConfigPath(), "utf8");
51309
51724
  } catch (err2) {
51310
51725
  if (err2.code === "ENOENT") return {};
51311
51726
  throw err2;
@@ -51320,8 +51735,8 @@ function readEgressRawConfig() {
51320
51735
  }
51321
51736
  function writeEgressRawConfig(config) {
51322
51737
  const p = egressConfigPath();
51323
- fs57.mkdirSync(path55.dirname(p), { recursive: true });
51324
- fs57.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
51738
+ fs58.mkdirSync(path56.dirname(p), { recursive: true });
51739
+ fs58.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
51325
51740
  }
51326
51741
  function applyEgress(config, change) {
51327
51742
  const policy = config.policy = config.policy ?? {};
@@ -51706,13 +52121,13 @@ function handleStatus() {
51706
52121
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
51707
52122
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
51708
52123
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
51709
- const projectConfig = path56.join(process.cwd(), "node9.config.json");
51710
- const globalConfig = path56.join(os51.homedir(), ".node9", "config.json");
52124
+ const projectConfig = path57.join(process.cwd(), "node9.config.json");
52125
+ const globalConfig = path57.join(os53.homedir(), ".node9", "config.json");
51711
52126
  lines.push(
51712
- `Project config (node9.config.json): ${fs58.existsSync(projectConfig) ? "present" : "not found"}`
52127
+ `Project config (node9.config.json): ${fs59.existsSync(projectConfig) ? "present" : "not found"}`
51713
52128
  );
51714
52129
  lines.push(
51715
- `Global config (~/.node9/config.json): ${fs58.existsSync(globalConfig) ? "present" : "not found"}`
52130
+ `Global config (~/.node9/config.json): ${fs59.existsSync(globalConfig) ? "present" : "not found"}`
51716
52131
  );
51717
52132
  return lines.join("\n");
51718
52133
  }
@@ -51818,21 +52233,21 @@ function handleEgressDeny(args) {
51818
52233
  addEgressHost("deny", host);
51819
52234
  return `Denied egress to ${host} (deny always wins over allow).`;
51820
52235
  }
51821
- var GLOBAL_CONFIG_PATH = path56.join(os51.homedir(), ".node9", "config.json");
52236
+ var GLOBAL_CONFIG_PATH = path57.join(os53.homedir(), ".node9", "config.json");
51822
52237
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
51823
52238
  function readGlobalConfigRaw() {
51824
52239
  try {
51825
- if (fs58.existsSync(GLOBAL_CONFIG_PATH)) {
51826
- return JSON.parse(fs58.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
52240
+ if (fs59.existsSync(GLOBAL_CONFIG_PATH)) {
52241
+ return JSON.parse(fs59.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
51827
52242
  }
51828
52243
  } catch {
51829
52244
  }
51830
52245
  return {};
51831
52246
  }
51832
52247
  function writeGlobalConfigRaw(data) {
51833
- const dir = path56.dirname(GLOBAL_CONFIG_PATH);
51834
- if (!fs58.existsSync(dir)) fs58.mkdirSync(dir, { recursive: true });
51835
- fs58.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
52248
+ const dir = path57.dirname(GLOBAL_CONFIG_PATH);
52249
+ if (!fs59.existsSync(dir)) fs59.mkdirSync(dir, { recursive: true });
52250
+ fs59.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
51836
52251
  }
51837
52252
  function handleApproverList() {
51838
52253
  const config = getConfig();
@@ -51876,9 +52291,9 @@ function handleApproverSet(args) {
51876
52291
  function handleAuditGet(args) {
51877
52292
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
51878
52293
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
51879
- const auditPath = path56.join(os51.homedir(), ".node9", "audit.log");
51880
- if (!fs58.existsSync(auditPath)) return "No audit log found.";
51881
- const rawLines = fs58.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
52294
+ const auditPath = path57.join(os53.homedir(), ".node9", "audit.log");
52295
+ if (!fs59.existsSync(auditPath)) return "No audit log found.";
52296
+ const rawLines = fs59.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
51882
52297
  const parsed = [];
51883
52298
  for (const line of rawLines) {
51884
52299
  try {
@@ -52252,7 +52667,7 @@ function registerTrustCommand(program2) {
52252
52667
  // src/cli/commands/mcp-pin.ts
52253
52668
  init_mcp_pin();
52254
52669
  import chalk24 from "chalk";
52255
- import fs59 from "fs";
52670
+ import fs60 from "fs";
52256
52671
 
52257
52672
  // src/cli/commands/mcp-gateway-cmd.ts
52258
52673
  init_mcp_wrap();
@@ -52459,7 +52874,7 @@ function registerMcpPinCommand(program2) {
52459
52874
  let repoCorrupt = false;
52460
52875
  if (found.source === "repo") {
52461
52876
  try {
52462
- const raw = fs59.readFileSync(found.path, "utf-8");
52877
+ const raw = fs60.readFileSync(found.path, "utf-8");
52463
52878
  const parsed = JSON.parse(raw);
52464
52879
  repoEntries = parsed.servers ?? {};
52465
52880
  } catch {
@@ -52960,8 +53375,8 @@ import chalk30 from "chalk";
52960
53375
 
52961
53376
  // src/ci-check/fetch.ts
52962
53377
  var import_undici = __toESM(require_undici());
52963
- import fs60 from "fs";
52964
- import path57 from "path";
53378
+ import fs61 from "fs";
53379
+ import path58 from "path";
52965
53380
  import { execFileSync as execFileSync2 } from "child_process";
52966
53381
  var cachedGhToken;
52967
53382
  function resolveGitHubToken() {
@@ -53044,7 +53459,7 @@ function parseRepoUrl(input) {
53044
53459
  function isLocalPath(input) {
53045
53460
  if (input.startsWith(".") || input.startsWith("/") || input.startsWith("~")) return true;
53046
53461
  try {
53047
- return fs60.existsSync(input) && fs60.statSync(input).isDirectory();
53462
+ return fs61.existsSync(input) && fs61.statSync(input).isDirectory();
53048
53463
  } catch {
53049
53464
  return false;
53050
53465
  }
@@ -53159,10 +53574,10 @@ function readLocalTree(dir) {
53159
53574
  const files = [];
53160
53575
  const notes = [];
53161
53576
  const add = (rel) => {
53162
- const abs = path57.join(root, rel);
53577
+ const abs = path58.join(root, rel);
53163
53578
  try {
53164
- if (fs60.existsSync(abs) && fs60.statSync(abs).isFile()) {
53165
- files.push({ path: rel, content: fs60.readFileSync(abs, "utf8") });
53579
+ if (fs61.existsSync(abs) && fs61.statSync(abs).isFile()) {
53580
+ files.push({ path: rel, content: fs61.readFileSync(abs, "utf8") });
53166
53581
  }
53167
53582
  } catch {
53168
53583
  }
@@ -53182,7 +53597,7 @@ function readLocalTree(dir) {
53182
53597
  dirsVisited++;
53183
53598
  let entries;
53184
53599
  try {
53185
- entries = fs60.readdirSync(path57.join(root, relDir), { withFileTypes: true });
53600
+ entries = fs61.readdirSync(path58.join(root, relDir), { withFileTypes: true });
53186
53601
  } catch {
53187
53602
  return;
53188
53603
  }
@@ -53203,11 +53618,11 @@ function readLocalTree(dir) {
53203
53618
  `repo is large \u2014 some agent-surface files may be INCOMPLETE (capped at ${MAX_SURFACE_FILES} files / ${MAX_DIRS} dirs).`
53204
53619
  );
53205
53620
  for (const rel of matches) collect(rel);
53206
- const wfDir = path57.join(root, WORKFLOW_DIR);
53621
+ const wfDir = path58.join(root, WORKFLOW_DIR);
53207
53622
  try {
53208
- if (fs60.existsSync(wfDir)) {
53209
- for (const name of fs60.readdirSync(wfDir)) {
53210
- if (/\.ya?ml$/.test(name)) add(path57.join(WORKFLOW_DIR, name));
53623
+ if (fs61.existsSync(wfDir)) {
53624
+ for (const name of fs61.readdirSync(wfDir)) {
53625
+ if (/\.ya?ml$/.test(name)) add(path58.join(WORKFLOW_DIR, name));
53211
53626
  }
53212
53627
  }
53213
53628
  } catch {
@@ -53480,7 +53895,7 @@ function severityFromScore(score) {
53480
53895
  if (score >= 1) return "advisory";
53481
53896
  return null;
53482
53897
  }
53483
- function analyzeWorkflow(path70, content) {
53898
+ function analyzeWorkflow(path71, content) {
53484
53899
  let raw;
53485
53900
  try {
53486
53901
  raw = parseYaml(content) ?? {};
@@ -53601,7 +54016,7 @@ function analyzeWorkflow(path70, content) {
53601
54016
  dimension: "workflows",
53602
54017
  severity,
53603
54018
  title,
53604
- file: path70,
54019
+ file: path71,
53605
54020
  signals,
53606
54021
  mitigations: mitigations.length ? mitigations : void 0,
53607
54022
  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."
@@ -53677,7 +54092,7 @@ function evalAgentJob(job, wf, raw, untrustedTrigger, reusable) {
53677
54092
  if (reusable && !loadedGun && SEVERITY_RANK2[severity] > SEVERITY_RANK2.medium) severity = "medium";
53678
54093
  return { severity, secrets, injectable, canReadEnv };
53679
54094
  }
53680
- function analyzeWorkflowSecrets(path70, content) {
54095
+ function analyzeWorkflowSecrets(path71, content) {
53681
54096
  let raw;
53682
54097
  try {
53683
54098
  raw = parseYaml(content) ?? {};
@@ -53697,7 +54112,7 @@ function analyzeWorkflowSecrets(path70, content) {
53697
54112
  dimension: "data",
53698
54113
  severity: worst.severity,
53699
54114
  title: worst.severity === "advisory" ? "Secrets reachable by the agent \u2014 hardening" : "Exfiltratable secrets reachable by an injectable agent",
53700
- file: path70,
54115
+ file: path71,
53701
54116
  signals: [
53702
54117
  `agent can reach: ${worst.secrets.map((s) => s.name).join(", ")}`,
53703
54118
  worst.injectable ? "the agent is externally triggerable (untrusted trigger, no gate)" : "gated / not externally triggerable \u2014 latent risk only",
@@ -53724,7 +54139,7 @@ function hookCommands(hooks) {
53724
54139
  }
53725
54140
  return out;
53726
54141
  }
53727
- function analyzeAgentConfig(path70, content) {
54142
+ function analyzeAgentConfig(path71, content) {
53728
54143
  let cfg;
53729
54144
  try {
53730
54145
  cfg = JSON.parse(content);
@@ -53743,7 +54158,7 @@ function analyzeAgentConfig(path70, content) {
53743
54158
  dimension: "toolRules",
53744
54159
  severity: high ? "high" : "medium",
53745
54160
  title: high ? "Agent hook runs UNPINNED/remote third-party code on every action" : "Agent hook runs third-party code in the agent hot path",
53746
- file: path70,
54161
+ file: path71,
53747
54162
  signals: [
53748
54163
  `hook command: \`${cmd.slice(0, 120)}\``,
53749
54164
  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"
@@ -53763,7 +54178,7 @@ function analyzeAgentConfig(path70, content) {
53763
54178
  dimension: "toolRules",
53764
54179
  severity: hasBackstop ? "medium" : "high",
53765
54180
  title: hasBackstop ? "Committed agent config pre-authorizes broad tools" : "Committed agent config pre-authorizes broad tools with no deny backstop",
53766
- file: path70,
54181
+ file: path71,
53767
54182
  signals: [
53768
54183
  `broad allow(s): ${broad.slice(0, 5).join(", ")}`,
53769
54184
  hasBackstop ? "a `deny` list backstops the broad allow" : "no `deny` entry covers Bash/Write/Edit \u2014 every contributor is pre-authorized for catastrophic tools"
@@ -53776,16 +54191,16 @@ function analyzeAgentConfig(path70, content) {
53776
54191
 
53777
54192
  // src/ci-check/mcp.ts
53778
54193
  init_dist();
53779
- function analyzeMcp(path70, content) {
54194
+ function analyzeMcp(path71, content) {
53780
54195
  let cfg;
53781
54196
  try {
53782
54197
  cfg = JSON.parse(content);
53783
54198
  } catch {
53784
54199
  return [];
53785
54200
  }
53786
- return analyzeMcpServers(cfg.mcpServers ?? {}, path70);
54201
+ return analyzeMcpServers(cfg.mcpServers ?? {}, path71);
53787
54202
  }
53788
- function analyzeMcpServers(servers, path70) {
54203
+ function analyzeMcpServers(servers, path71) {
53789
54204
  const findings = [];
53790
54205
  for (const [name, srv] of Object.entries(servers ?? {})) {
53791
54206
  if (!srv || srv.disabled) continue;
@@ -53796,7 +54211,7 @@ function analyzeMcpServers(servers, path70) {
53796
54211
  dimension: "mcp",
53797
54212
  severity: "medium",
53798
54213
  title: `MCP server "${name}" runs an unpinned executable`,
53799
- file: path70,
54214
+ file: path71,
53800
54215
  signals: [`\`${argv.slice(0, 120)}\` \u2014 unversioned/@latest npx`],
53801
54216
  fix: "Pin the MCP server package to an exact version so a PR (or a registry compromise) can\u2019t swap the toolchain."
53802
54217
  });
@@ -53810,7 +54225,7 @@ function analyzeMcpServers(servers, path70) {
53810
54225
  dimension: "mcp",
53811
54226
  severity: "high",
53812
54227
  title: `MCP server "${name}" has an inline credential`,
53813
- file: path70,
54228
+ file: path71,
53814
54229
  signals: [
53815
54230
  `env.${k} matches ${hit.patternName} \u2014 agent-reachable secret committed to the repo`
53816
54231
  ],
@@ -53824,7 +54239,7 @@ function analyzeMcpServers(servers, path70) {
53824
54239
 
53825
54240
  // src/ci-check/codex.ts
53826
54241
  import { parse as parseToml5 } from "smol-toml";
53827
- function analyzeCodexConfig(path70, content) {
54242
+ function analyzeCodexConfig(path71, content) {
53828
54243
  let cfg;
53829
54244
  try {
53830
54245
  cfg = parseToml5(content);
@@ -53832,7 +54247,7 @@ function analyzeCodexConfig(path70, content) {
53832
54247
  return [];
53833
54248
  }
53834
54249
  const findings = [];
53835
- findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path70));
54250
+ findings.push(...analyzeMcpServers(cfg.mcp_servers ?? {}, path71));
53836
54251
  const sandbox = typeof cfg.sandbox_mode === "string" ? cfg.sandbox_mode : "";
53837
54252
  const approval = typeof cfg.approval_policy === "string" ? cfg.approval_policy : "";
53838
54253
  const fullAccess = /danger-full-access/i.test(sandbox);
@@ -53847,7 +54262,7 @@ function analyzeCodexConfig(path70, content) {
53847
54262
  dimension: "toolRules",
53848
54263
  severity: fullAccess ? "high" : "medium",
53849
54264
  title: fullAccess ? "Codex config grants a full-access sandbox" : "Codex config never requires approval",
53850
- file: path70,
54265
+ file: path71,
53851
54266
  signals,
53852
54267
  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.'
53853
54268
  });
@@ -53903,10 +54318,10 @@ function decodeSuspiciousBase64(text) {
53903
54318
  }
53904
54319
  return out;
53905
54320
  }
53906
- function mk(severity, title, signals, fix, path70) {
53907
- return { check: "CI-6", dimension: "instructions", severity, title, file: path70, signals, fix };
54321
+ function mk(severity, title, signals, fix, path71) {
54322
+ return { check: "CI-6", dimension: "instructions", severity, title, file: path71, signals, fix };
53908
54323
  }
53909
- function analyzeInstructionFile(path70, content) {
54324
+ function analyzeInstructionFile(path71, content) {
53910
54325
  const findings = [];
53911
54326
  const decoded = decodeSuspiciousBase64(content);
53912
54327
  if (TAG_CHARS.test(content))
@@ -53918,7 +54333,7 @@ function analyzeInstructionFile(path70, content) {
53918
54333
  "contains Unicode tag characters (U+E0000\u2013E007F) \u2014 an invisible instruction-smuggling channel with no legitimate use in text"
53919
54334
  ],
53920
54335
  "Remove the tag characters. Instruction files must be plain, reviewable text.",
53921
- path70
54336
+ path71
53922
54337
  )
53923
54338
  );
53924
54339
  if (BIDI_OVERRIDE.test(content))
@@ -53930,7 +54345,7 @@ function analyzeInstructionFile(path70, content) {
53930
54345
  "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"
53931
54346
  ],
53932
54347
  "Remove the bidi override characters.",
53933
- path70
54348
+ path71
53934
54349
  )
53935
54350
  );
53936
54351
  else if (BIDI_EMBED_ISOLATE.test(content))
@@ -53942,7 +54357,7 @@ function analyzeInstructionFile(path70, content) {
53942
54357
  "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"
53943
54358
  ],
53944
54359
  "Confirm the bidi marks are legitimate RTL formatting; remove otherwise.",
53945
- path70
54360
+ path71
53946
54361
  )
53947
54362
  );
53948
54363
  const zw = suspiciousZeroWidth(content);
@@ -53956,7 +54371,7 @@ function analyzeInstructionFile(path70, content) {
53956
54371
  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)"
53957
54372
  ],
53958
54373
  "Remove the zero-width characters. Instruction files must be plain, reviewable text.",
53959
- path70
54374
+ path71
53960
54375
  )
53961
54376
  );
53962
54377
  }
@@ -53972,7 +54387,7 @@ function analyzeInstructionFile(path70, content) {
53972
54387
  `contains a prompt-override / role-impersonation directive (\`${m[0].slice(0, 60).trim()}\`)${ovEnc ? " \u2014 concealed in a base64 blob" : ""}`
53973
54388
  ],
53974
54389
  "Remove the override text. An instruction file should not tell the agent to ignore its own rules.",
53975
- path70
54390
+ path71
53976
54391
  )
53977
54392
  );
53978
54393
  }
@@ -53984,7 +54399,7 @@ function analyzeInstructionFile(path70, content) {
53984
54399
  "Instruction directs the agent to fetch and run remote code",
53985
54400
  [`\`${fo[0].slice(0, 70).trim()}\` \u2014 fetch-and-obey, outside an install/setup section`],
53986
54401
  "Do not instruct the agent to pipe remote content into a shell; pin and vendor scripts instead.",
53987
- path70
54402
+ path71
53988
54403
  )
53989
54404
  );
53990
54405
  }
@@ -53996,7 +54411,7 @@ function analyzeInstructionFile(path70, content) {
53996
54411
  "Instruction points the agent at credential material",
53997
54412
  [`references \`${sp[0].slice(0, 50).trim()}\` \u2014 directs the agent toward secrets`],
53998
54413
  "Do not reference credential files or paths in agent instructions.",
53999
- path70
54414
+ path71
54000
54415
  )
54001
54416
  );
54002
54417
  }
@@ -54008,7 +54423,7 @@ function analyzeInstructionFile(path70, content) {
54008
54423
  "Instruction directs the agent to send data to an external endpoint",
54009
54424
  [`\`${ex[0].slice(0, 70).trim()}\` \u2014 possible exfiltration directive`],
54010
54425
  "Remove external post/upload directives from agent instructions.",
54011
- path70
54426
+ path71
54012
54427
  )
54013
54428
  );
54014
54429
  }
@@ -54310,17 +54725,17 @@ import chalk32 from "chalk";
54310
54725
  // src/shields/jail.ts
54311
54726
  init_build();
54312
54727
  init_shields();
54313
- import fs61 from "fs";
54314
- import os52 from "os";
54315
- import path58 from "path";
54728
+ import fs62 from "fs";
54729
+ import os54 from "os";
54730
+ import path59 from "path";
54316
54731
  var USER_JAIL_SHIELD = "user-jail";
54317
54732
  function jailStorePath() {
54318
- return path58.join(os52.homedir(), ".node9", "jail-paths.json");
54733
+ return path59.join(os54.homedir(), ".node9", "jail-paths.json");
54319
54734
  }
54320
54735
  function readJailPaths() {
54321
54736
  let text;
54322
54737
  try {
54323
- text = fs61.readFileSync(jailStorePath(), "utf8");
54738
+ text = fs62.readFileSync(jailStorePath(), "utf8");
54324
54739
  } catch (err2) {
54325
54740
  if (err2.code === "ENOENT") return [];
54326
54741
  throw err2;
@@ -54338,8 +54753,8 @@ function readJailPaths() {
54338
54753
  }
54339
54754
  function writeJailPaths(paths) {
54340
54755
  const p = jailStorePath();
54341
- fs61.mkdirSync(path58.dirname(p), { recursive: true });
54342
- fs61.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
54756
+ fs62.mkdirSync(path59.dirname(p), { recursive: true });
54757
+ fs62.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
54343
54758
  }
54344
54759
  function addJailPath(rawPath, verdict) {
54345
54760
  const norm = rawPath.trim();
@@ -54361,14 +54776,14 @@ function removeJailPath(rawPath) {
54361
54776
  return { removed, paths: after };
54362
54777
  }
54363
54778
  function regenerateUserJail(paths) {
54364
- const file = path58.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
54779
+ const file = path59.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
54365
54780
  if (paths.length === 0) {
54366
54781
  const active2 = readActiveShields();
54367
54782
  if (active2.includes(USER_JAIL_SHIELD)) {
54368
54783
  writeActiveShields(active2.filter((s) => s !== USER_JAIL_SHIELD));
54369
54784
  }
54370
54785
  try {
54371
- fs61.rmSync(file, { force: true });
54786
+ fs62.rmSync(file, { force: true });
54372
54787
  } catch {
54373
54788
  }
54374
54789
  return;
@@ -54483,13 +54898,13 @@ function registerJailCommand(program2) {
54483
54898
  // src/cli/commands/sandbox.ts
54484
54899
  init_config();
54485
54900
  import chalk33 from "chalk";
54486
- import fs64 from "fs";
54487
- import path61 from "path";
54901
+ import fs65 from "fs";
54902
+ import path62 from "path";
54488
54903
  import { spawnSync as spawnSync6 } from "child_process";
54489
54904
 
54490
54905
  // src/sandbox/config.ts
54491
- import fs62 from "fs";
54492
- import path59 from "path";
54906
+ import fs63 from "fs";
54907
+ import path60 from "path";
54493
54908
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
54494
54909
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
54495
54910
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -54562,16 +54977,16 @@ function scaffoldSandboxYaml(agent) {
54562
54977
  return header + stringifyYaml(defaultSandboxConfig(agent));
54563
54978
  }
54564
54979
  function sandboxConfigPath(cwd = process.cwd()) {
54565
- return path59.join(cwd, SANDBOX_CONFIG_FILE);
54980
+ return path60.join(cwd, SANDBOX_CONFIG_FILE);
54566
54981
  }
54567
54982
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
54568
54983
  const p = sandboxConfigPath(cwd);
54569
- if (!fs62.existsSync(p)) {
54984
+ if (!fs63.existsSync(p)) {
54570
54985
  throw new Error(`sandbox: ${SANDBOX_CONFIG_FILE} not found \u2014 run \`node9 sandbox new\` first.`);
54571
54986
  }
54572
54987
  let raw;
54573
54988
  try {
54574
- raw = parseYaml2(fs62.readFileSync(p, "utf-8"));
54989
+ raw = parseYaml2(fs63.readFileSync(p, "utf-8"));
54575
54990
  } catch (err2) {
54576
54991
  throw new Error(
54577
54992
  `sandbox: ${SANDBOX_CONFIG_FILE} is not valid YAML \u2014 ${err2.message}`
@@ -54630,13 +55045,13 @@ init_templates();
54630
55045
 
54631
55046
  // src/sandbox/runtime.ts
54632
55047
  init_templates();
54633
- import fs63 from "fs";
54634
- import os53 from "os";
54635
- import path60 from "path";
55048
+ import fs64 from "fs";
55049
+ import os55 from "os";
55050
+ import path61 from "path";
54636
55051
  import crypto9 from "crypto";
54637
55052
  import { spawnSync as spawnSync5 } from "child_process";
54638
55053
  function sandboxDataDir(cwd = process.cwd()) {
54639
- return path60.join(cwd, ".node9", "sandbox", "data");
55054
+ return path61.join(cwd, ".node9", "sandbox", "data");
54640
55055
  }
54641
55056
  function detectEngine(engine) {
54642
55057
  const r = spawnSync5(engine, ["--version"], { encoding: "utf-8" });
@@ -54647,7 +55062,7 @@ function detectEngine(engine) {
54647
55062
  }
54648
55063
  function agentCredentialsMount(agent) {
54649
55064
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
54650
- return { hostPath: path60.join(os53.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
55065
+ return { hostPath: path61.join(os55.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
54651
55066
  }
54652
55067
  function buildRunArgs(opts) {
54653
55068
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -54657,7 +55072,7 @@ function buildRunArgs(opts) {
54657
55072
  args.push("-v", `${allowlistHostPath}:${ALLOWED_DOMAINS_PATH}:ro`);
54658
55073
  if (config.node9.mountAgentCredentials) {
54659
55074
  const creds = agentCredentialsMount(config.agent);
54660
- if (fs63.existsSync(creds.hostPath)) {
55075
+ if (fs64.existsSync(creds.hostPath)) {
54661
55076
  args.push("-v", `${creds.hostPath}:${creds.target}`);
54662
55077
  }
54663
55078
  }
@@ -54675,30 +55090,30 @@ function imageContentHash(dockerfile, entrypoint) {
54675
55090
  return crypto9.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
54676
55091
  }
54677
55092
  function sandboxBuildDir(cwd = process.cwd()) {
54678
- return path60.join(cwd, ".node9", "sandbox", "build");
55093
+ return path61.join(cwd, ".node9", "sandbox", "build");
54679
55094
  }
54680
55095
  function writeBuildContext(cwd, dockerfile, entrypoint) {
54681
55096
  const dir = sandboxBuildDir(cwd);
54682
- fs63.mkdirSync(dir, { recursive: true });
54683
- fs63.writeFileSync(path60.join(dir, "Dockerfile"), dockerfile);
54684
- fs63.writeFileSync(path60.join(dir, "entrypoint.sh"), entrypoint);
55097
+ fs64.mkdirSync(dir, { recursive: true });
55098
+ fs64.writeFileSync(path61.join(dir, "Dockerfile"), dockerfile);
55099
+ fs64.writeFileSync(path61.join(dir, "entrypoint.sh"), entrypoint);
54685
55100
  return dir;
54686
55101
  }
54687
55102
  function writeAllowlist(cwd, hosts) {
54688
- const dir = path60.join(cwd, ".node9", "sandbox");
54689
- fs63.mkdirSync(dir, { recursive: true });
54690
- const p = path60.join(dir, "allowed-domains.txt");
54691
- fs63.writeFileSync(p, hosts.join("\n") + "\n");
55103
+ const dir = path61.join(cwd, ".node9", "sandbox");
55104
+ fs64.mkdirSync(dir, { recursive: true });
55105
+ const p = path61.join(dir, "allowed-domains.txt");
55106
+ fs64.writeFileSync(p, hosts.join("\n") + "\n");
54692
55107
  return p;
54693
55108
  }
54694
55109
  function resolveHomePath(p) {
54695
- return p.startsWith("~") ? path60.join(os53.homedir(), p.slice(1)) : path60.resolve(p);
55110
+ return p.startsWith("~") ? path61.join(os55.homedir(), p.slice(1)) : path61.resolve(p);
54696
55111
  }
54697
55112
 
54698
55113
  // src/cli/commands/sandbox.ts
54699
55114
  function seedDataDirConfig(dataDir, sandbox) {
54700
- fs64.mkdirSync(dataDir, { recursive: true });
54701
- const configPath = path61.join(dataDir, "config.json");
55115
+ fs65.mkdirSync(dataDir, { recursive: true });
55116
+ const configPath = path62.join(dataDir, "config.json");
54702
55117
  const seed = {
54703
55118
  settings: {
54704
55119
  approvers: {
@@ -54709,7 +55124,7 @@ function seedDataDirConfig(dataDir, sandbox) {
54709
55124
  }
54710
55125
  }
54711
55126
  };
54712
- fs64.writeFileSync(configPath, JSON.stringify(seed, null, 2), { mode: 384 });
55127
+ fs65.writeFileSync(configPath, JSON.stringify(seed, null, 2), { mode: 384 });
54713
55128
  }
54714
55129
  function registerSandboxCommand(program2, version2) {
54715
55130
  const node9Version2 = pinnedNode9Version(version2);
@@ -54717,13 +55132,13 @@ function registerSandboxCommand(program2, version2) {
54717
55132
  cmd.command("new").description(`Scaffold ${SANDBOX_CONFIG_FILE} in this project`).option("--agent <agent>", "claude (default) or codex", "claude").action((opts) => {
54718
55133
  const agent = opts.agent === "codex" ? "codex" : "claude";
54719
55134
  const p = sandboxConfigPath();
54720
- if (fs64.existsSync(p)) {
55135
+ if (fs65.existsSync(p)) {
54721
55136
  console.log(
54722
55137
  chalk33.yellow(` ${SANDBOX_CONFIG_FILE} already exists \u2014 leaving it untouched.`)
54723
55138
  );
54724
55139
  return;
54725
55140
  }
54726
- fs64.writeFileSync(p, scaffoldSandboxYaml(agent));
55141
+ fs65.writeFileSync(p, scaffoldSandboxYaml(agent));
54727
55142
  console.log(
54728
55143
  chalk33.green(` \u2713 wrote ${SANDBOX_CONFIG_FILE}`) + chalk33.dim(` (agent: ${agent})`)
54729
55144
  );
@@ -54763,8 +55178,8 @@ function registerSandboxCommand(program2, version2) {
54763
55178
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
54764
55179
  const hash = imageContentHash(dockerfile, entrypoint);
54765
55180
  const image = sandbox.runtime.image;
54766
- const hashFile = path61.join(sandboxBuildDir(cwd), ".image-hash");
54767
- const lastHash = fs64.existsSync(hashFile) ? fs64.readFileSync(hashFile, "utf-8").trim() : "";
55181
+ const hashFile = path62.join(sandboxBuildDir(cwd), ".image-hash");
55182
+ const lastHash = fs65.existsSync(hashFile) ? fs65.readFileSync(hashFile, "utf-8").trim() : "";
54768
55183
  const imageExists = spawnSync6(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
54769
55184
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
54770
55185
  if (needBuild) {
@@ -54776,7 +55191,7 @@ function registerSandboxCommand(program2, version2) {
54776
55191
  console.error(chalk33.red(" build failed."));
54777
55192
  process.exit(b.status ?? 1);
54778
55193
  }
54779
- fs64.writeFileSync(hashFile, hash);
55194
+ fs65.writeFileSync(hashFile, hash);
54780
55195
  }
54781
55196
  const dataDir = sandboxDataDir(cwd);
54782
55197
  seedDataDirConfig(dataDir, sandbox);
@@ -54790,7 +55205,7 @@ function registerSandboxCommand(program2, version2) {
54790
55205
  });
54791
55206
  if (sandbox.node9.mountAgentCredentials) {
54792
55207
  const creds = agentCredentialsMount(sandbox.agent);
54793
- if (fs64.existsSync(creds.hostPath)) {
55208
+ if (fs65.existsSync(creds.hostPath)) {
54794
55209
  console.log(chalk33.dim(` mounting ${creds.hostPath} (agent credentials, rw)`));
54795
55210
  } else {
54796
55211
  console.log(
@@ -54806,20 +55221,20 @@ function registerSandboxCommand(program2, version2) {
54806
55221
  process.exit(r.status ?? 0);
54807
55222
  });
54808
55223
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
54809
- const auditPath = path61.join(sandboxDataDir(), "audit.log");
54810
- if (!fs64.existsSync(auditPath)) {
55224
+ const auditPath = path62.join(sandboxDataDir(), "audit.log");
55225
+ if (!fs65.existsSync(auditPath)) {
54811
55226
  console.log(chalk33.dim(" no sandbox audit yet."));
54812
55227
  return;
54813
55228
  }
54814
55229
  spawnSync6("tail", ["-f", auditPath], { stdio: "inherit" });
54815
55230
  });
54816
55231
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
54817
- const auditPath = path61.join(sandboxDataDir(), "audit.log");
54818
- if (!fs64.existsSync(auditPath)) {
55232
+ const auditPath = path62.join(sandboxDataDir(), "audit.log");
55233
+ if (!fs65.existsSync(auditPath)) {
54819
55234
  console.log(chalk33.dim(" no sandbox audit yet."));
54820
55235
  return;
54821
55236
  }
54822
- process.stdout.write(fs64.readFileSync(auditPath, "utf-8"));
55237
+ process.stdout.write(fs65.readFileSync(auditPath, "utf-8"));
54823
55238
  });
54824
55239
  cmd.command("clean").description("Remove the sandbox image, build context, and data").action(() => {
54825
55240
  const cwd = process.cwd();
@@ -54833,7 +55248,7 @@ function registerSandboxCommand(program2, version2) {
54833
55248
  stdio: "ignore"
54834
55249
  });
54835
55250
  }
54836
- fs64.rmSync(path61.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
55251
+ fs65.rmSync(path62.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
54837
55252
  console.log(chalk33.green(" \u2713 sandbox image + build + data removed."));
54838
55253
  });
54839
55254
  }
@@ -54844,9 +55259,9 @@ init_litellm();
54844
55259
  init_cost_gemini();
54845
55260
  init_cost_codex();
54846
55261
  import chalk34 from "chalk";
54847
- import fs65 from "fs";
54848
- import path62 from "path";
54849
- import os54 from "os";
55262
+ import fs66 from "fs";
55263
+ import path63 from "path";
55264
+ import os56 from "os";
54850
55265
  function modelPrice(model) {
54851
55266
  const t = pricingFor(model);
54852
55267
  if (!t) return null;
@@ -54863,10 +55278,10 @@ function encodeProjectPath(projectPath) {
54863
55278
  }
54864
55279
  function sessionJsonlPath(projectPath, sessionId) {
54865
55280
  const encoded = encodeProjectPath(projectPath);
54866
- return path62.join(os54.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
55281
+ return path63.join(os56.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
54867
55282
  }
54868
55283
  function projectLabel(projectPath) {
54869
- return projectPath.replace(os54.homedir(), "~");
55284
+ return projectPath.replace(os56.homedir(), "~");
54870
55285
  }
54871
55286
  function parseHistoryLines(lines) {
54872
55287
  const entries = [];
@@ -54935,10 +55350,10 @@ function parseSessionLines(lines) {
54935
55350
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
54936
55351
  }
54937
55352
  function loadAuditEntries(auditPath) {
54938
- const aPath = auditPath ?? path62.join(os54.homedir(), ".node9", "audit.log");
55353
+ const aPath = auditPath ?? path63.join(os56.homedir(), ".node9", "audit.log");
54939
55354
  let raw;
54940
55355
  try {
54941
- raw = fs65.readFileSync(aPath, "utf-8");
55356
+ raw = fs66.readFileSync(aPath, "utf-8");
54942
55357
  } catch {
54943
55358
  return [];
54944
55359
  }
@@ -54974,8 +55389,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
54974
55389
  return result;
54975
55390
  }
54976
55391
  function buildGeminiSessions(days, allAuditEntries) {
54977
- const tmpDir = path62.join(os54.homedir(), ".gemini", "tmp");
54978
- if (!fs65.existsSync(tmpDir)) return [];
55392
+ const tmpDir = path63.join(os56.homedir(), ".gemini", "tmp");
55393
+ if (!fs66.existsSync(tmpDir)) return [];
54979
55394
  const cutoff = days !== null ? (() => {
54980
55395
  const d = /* @__PURE__ */ new Date();
54981
55396
  d.setDate(d.getDate() - days);
@@ -54984,35 +55399,35 @@ function buildGeminiSessions(days, allAuditEntries) {
54984
55399
  })() : null;
54985
55400
  let slugDirs;
54986
55401
  try {
54987
- slugDirs = fs65.readdirSync(tmpDir);
55402
+ slugDirs = fs66.readdirSync(tmpDir);
54988
55403
  } catch {
54989
55404
  return [];
54990
55405
  }
54991
55406
  const summaries = [];
54992
55407
  for (const slug2 of slugDirs) {
54993
- const slugPath = path62.join(tmpDir, slug2);
55408
+ const slugPath = path63.join(tmpDir, slug2);
54994
55409
  try {
54995
- if (!fs65.statSync(slugPath).isDirectory()) continue;
55410
+ if (!fs66.statSync(slugPath).isDirectory()) continue;
54996
55411
  } catch {
54997
55412
  continue;
54998
55413
  }
54999
- let projectRoot = path62.join(os54.homedir(), slug2);
55414
+ let projectRoot = path63.join(os56.homedir(), slug2);
55000
55415
  try {
55001
- projectRoot = fs65.readFileSync(path62.join(slugPath, ".project_root"), "utf-8").trim();
55416
+ projectRoot = fs66.readFileSync(path63.join(slugPath, ".project_root"), "utf-8").trim();
55002
55417
  } catch {
55003
55418
  }
55004
- const chatsDir = path62.join(slugPath, "chats");
55005
- if (!fs65.existsSync(chatsDir)) continue;
55419
+ const chatsDir = path63.join(slugPath, "chats");
55420
+ if (!fs66.existsSync(chatsDir)) continue;
55006
55421
  let chatFiles;
55007
55422
  try {
55008
- chatFiles = fs65.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
55423
+ chatFiles = fs66.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
55009
55424
  } catch {
55010
55425
  continue;
55011
55426
  }
55012
55427
  for (const chatFile of chatFiles) {
55013
55428
  let raw;
55014
55429
  try {
55015
- raw = fs65.readFileSync(path62.join(chatsDir, chatFile), "utf-8");
55430
+ raw = fs66.readFileSync(path63.join(chatsDir, chatFile), "utf-8");
55016
55431
  } catch {
55017
55432
  continue;
55018
55433
  }
@@ -55092,8 +55507,8 @@ function buildGeminiSessions(days, allAuditEntries) {
55092
55507
  return summaries;
55093
55508
  }
55094
55509
  function buildCodexSessions(days, allAuditEntries) {
55095
- const sessionsBase = path62.join(os54.homedir(), ".codex", "sessions");
55096
- if (!fs65.existsSync(sessionsBase)) return [];
55510
+ const sessionsBase = path63.join(os56.homedir(), ".codex", "sessions");
55511
+ if (!fs66.existsSync(sessionsBase)) return [];
55097
55512
  const cutoff = days !== null ? (() => {
55098
55513
  const d = /* @__PURE__ */ new Date();
55099
55514
  d.setDate(d.getDate() - days);
@@ -55102,29 +55517,29 @@ function buildCodexSessions(days, allAuditEntries) {
55102
55517
  })() : null;
55103
55518
  const jsonlFiles = [];
55104
55519
  try {
55105
- for (const year of fs65.readdirSync(sessionsBase)) {
55106
- const yearPath = path62.join(sessionsBase, year);
55520
+ for (const year of fs66.readdirSync(sessionsBase)) {
55521
+ const yearPath = path63.join(sessionsBase, year);
55107
55522
  try {
55108
- if (!fs65.statSync(yearPath).isDirectory()) continue;
55523
+ if (!fs66.statSync(yearPath).isDirectory()) continue;
55109
55524
  } catch {
55110
55525
  continue;
55111
55526
  }
55112
- for (const month of fs65.readdirSync(yearPath)) {
55113
- const monthPath = path62.join(yearPath, month);
55527
+ for (const month of fs66.readdirSync(yearPath)) {
55528
+ const monthPath = path63.join(yearPath, month);
55114
55529
  try {
55115
- if (!fs65.statSync(monthPath).isDirectory()) continue;
55530
+ if (!fs66.statSync(monthPath).isDirectory()) continue;
55116
55531
  } catch {
55117
55532
  continue;
55118
55533
  }
55119
- for (const day of fs65.readdirSync(monthPath)) {
55120
- const dayPath = path62.join(monthPath, day);
55534
+ for (const day of fs66.readdirSync(monthPath)) {
55535
+ const dayPath = path63.join(monthPath, day);
55121
55536
  try {
55122
- if (!fs65.statSync(dayPath).isDirectory()) continue;
55537
+ if (!fs66.statSync(dayPath).isDirectory()) continue;
55123
55538
  } catch {
55124
55539
  continue;
55125
55540
  }
55126
- for (const file of fs65.readdirSync(dayPath)) {
55127
- if (file.endsWith(".jsonl")) jsonlFiles.push(path62.join(dayPath, file));
55541
+ for (const file of fs66.readdirSync(dayPath)) {
55542
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path63.join(dayPath, file));
55128
55543
  }
55129
55544
  }
55130
55545
  }
@@ -55136,7 +55551,7 @@ function buildCodexSessions(days, allAuditEntries) {
55136
55551
  for (const filePath of jsonlFiles) {
55137
55552
  let lines;
55138
55553
  try {
55139
- lines = fs65.readFileSync(filePath, "utf-8").split("\n");
55554
+ lines = fs66.readFileSync(filePath, "utf-8").split("\n");
55140
55555
  } catch {
55141
55556
  continue;
55142
55557
  }
@@ -55222,10 +55637,10 @@ function buildCodexSessions(days, allAuditEntries) {
55222
55637
  return summaries;
55223
55638
  }
55224
55639
  function buildSessions(days, historyPath) {
55225
- const hPath = historyPath ?? path62.join(os54.homedir(), ".claude", "history.jsonl");
55640
+ const hPath = historyPath ?? path63.join(os56.homedir(), ".claude", "history.jsonl");
55226
55641
  let historyRaw = "";
55227
55642
  try {
55228
- historyRaw = fs65.readFileSync(hPath, "utf-8");
55643
+ historyRaw = fs66.readFileSync(hPath, "utf-8");
55229
55644
  } catch {
55230
55645
  }
55231
55646
  const cutoff = days !== null ? (() => {
@@ -55249,7 +55664,7 @@ function buildSessions(days, historyPath) {
55249
55664
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
55250
55665
  let sessionLines = [];
55251
55666
  try {
55252
- sessionLines = fs65.readFileSync(jsonlFile, "utf-8").split("\n");
55667
+ sessionLines = fs66.readFileSync(jsonlFile, "utf-8").split("\n");
55253
55668
  } catch {
55254
55669
  }
55255
55670
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -55643,12 +56058,12 @@ function registerSessionTaintCommand(program2) {
55643
56058
 
55644
56059
  // src/cli/commands/skill-pin.ts
55645
56060
  import chalk36 from "chalk";
55646
- import fs66 from "fs";
55647
- import os55 from "os";
55648
- import path63 from "path";
56061
+ import fs67 from "fs";
56062
+ import os57 from "os";
56063
+ import path64 from "path";
55649
56064
  function wipeSkillSessions() {
55650
56065
  try {
55651
- fs66.rmSync(path63.join(os55.homedir(), ".node9", "skill-sessions"), {
56066
+ fs67.rmSync(path64.join(os57.homedir(), ".node9", "skill-sessions"), {
55652
56067
  recursive: true,
55653
56068
  force: true
55654
56069
  });
@@ -55730,15 +56145,15 @@ function registerSkillPinCommand(program2) {
55730
56145
  }
55731
56146
 
55732
56147
  // src/cli/commands/decisions.ts
55733
- import fs67 from "fs";
55734
- import os56 from "os";
55735
- import path64 from "path";
56148
+ import fs68 from "fs";
56149
+ import os58 from "os";
56150
+ import path65 from "path";
55736
56151
  import chalk37 from "chalk";
55737
- var DECISIONS_FILE2 = path64.join(os56.homedir(), ".node9", "decisions.json");
56152
+ var DECISIONS_FILE2 = path65.join(os58.homedir(), ".node9", "decisions.json");
55738
56153
  function readDecisions() {
55739
56154
  try {
55740
- if (!fs67.existsSync(DECISIONS_FILE2)) return {};
55741
- const raw = fs67.readFileSync(DECISIONS_FILE2, "utf-8");
56155
+ if (!fs68.existsSync(DECISIONS_FILE2)) return {};
56156
+ const raw = fs68.readFileSync(DECISIONS_FILE2, "utf-8");
55742
56157
  const parsed = JSON.parse(raw);
55743
56158
  const out = {};
55744
56159
  for (const [k, v] of Object.entries(parsed)) {
@@ -55750,11 +56165,11 @@ function readDecisions() {
55750
56165
  }
55751
56166
  }
55752
56167
  function writeDecisions(d) {
55753
- const dir = path64.dirname(DECISIONS_FILE2);
55754
- if (!fs67.existsSync(dir)) fs67.mkdirSync(dir, { recursive: true });
56168
+ const dir = path65.dirname(DECISIONS_FILE2);
56169
+ if (!fs68.existsSync(dir)) fs68.mkdirSync(dir, { recursive: true });
55755
56170
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
55756
- fs67.writeFileSync(tmp, JSON.stringify(d, null, 2));
55757
- fs67.renameSync(tmp, DECISIONS_FILE2);
56171
+ fs68.writeFileSync(tmp, JSON.stringify(d, null, 2));
56172
+ fs68.renameSync(tmp, DECISIONS_FILE2);
55758
56173
  }
55759
56174
  function registerDecisionsCommand(program2) {
55760
56175
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -55811,18 +56226,18 @@ Persistent decisions (${entries.length})
55811
56226
 
55812
56227
  // src/cli/commands/dlp.ts
55813
56228
  import chalk38 from "chalk";
55814
- import fs68 from "fs";
55815
- import path65 from "path";
55816
- import os57 from "os";
55817
- var AUDIT_LOG = path65.join(os57.homedir(), ".node9", "audit.log");
55818
- var RESOLVED_FILE = path65.join(os57.homedir(), ".node9", "dlp-resolved.json");
56229
+ import fs69 from "fs";
56230
+ import path66 from "path";
56231
+ import os59 from "os";
56232
+ var AUDIT_LOG = path66.join(os59.homedir(), ".node9", "audit.log");
56233
+ var RESOLVED_FILE = path66.join(os59.homedir(), ".node9", "dlp-resolved.json");
55819
56234
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
55820
56235
  function stripAnsi(s) {
55821
56236
  return s.replace(ANSI_RE, "");
55822
56237
  }
55823
56238
  function loadResolved() {
55824
56239
  try {
55825
- const raw = JSON.parse(fs68.readFileSync(RESOLVED_FILE, "utf-8"));
56240
+ const raw = JSON.parse(fs69.readFileSync(RESOLVED_FILE, "utf-8"));
55826
56241
  return new Set(raw);
55827
56242
  } catch {
55828
56243
  return /* @__PURE__ */ new Set();
@@ -55830,13 +56245,13 @@ function loadResolved() {
55830
56245
  }
55831
56246
  function saveResolved(resolved) {
55832
56247
  try {
55833
- fs68.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
56248
+ fs69.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
55834
56249
  } catch {
55835
56250
  }
55836
56251
  }
55837
56252
  function loadDlpFindings() {
55838
- if (!fs68.existsSync(AUDIT_LOG)) return [];
55839
- return fs68.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
56253
+ if (!fs69.existsSync(AUDIT_LOG)) return [];
56254
+ return fs69.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
55840
56255
  if (!line.trim()) return [];
55841
56256
  try {
55842
56257
  const e = JSON.parse(line);
@@ -55935,14 +56350,14 @@ function registerDlpCommand(program2) {
55935
56350
  // src/cli/commands/mask.ts
55936
56351
  init_dlp();
55937
56352
  import chalk39 from "chalk";
55938
- import fs69 from "fs";
55939
- import path66 from "path";
55940
- import os58 from "os";
56353
+ import fs70 from "fs";
56354
+ import path67 from "path";
56355
+ import os60 from "os";
55941
56356
  function findJsonlFiles(dir) {
55942
56357
  const results = [];
55943
- if (!fs69.existsSync(dir)) return results;
55944
- for (const entry of fs69.readdirSync(dir, { withFileTypes: true })) {
55945
- const full = path66.join(dir, entry.name);
56358
+ if (!fs70.existsSync(dir)) return results;
56359
+ for (const entry of fs70.readdirSync(dir, { withFileTypes: true })) {
56360
+ const full = path67.join(dir, entry.name);
55946
56361
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
55947
56362
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
55948
56363
  }
@@ -55985,7 +56400,7 @@ function redactJson(obj) {
55985
56400
  function processFile(filePath, dryRun) {
55986
56401
  let raw;
55987
56402
  try {
55988
- raw = fs69.readFileSync(filePath, "utf-8");
56403
+ raw = fs70.readFileSync(filePath, "utf-8");
55989
56404
  } catch {
55990
56405
  return { redactedLines: 0, patterns: [] };
55991
56406
  }
@@ -56017,14 +56432,14 @@ function processFile(filePath, dryRun) {
56017
56432
  }
56018
56433
  }
56019
56434
  if (!dryRun && redactedLines > 0) {
56020
- fs69.writeFileSync(filePath, newLines.join("\n"), "utf-8");
56435
+ fs70.writeFileSync(filePath, newLines.join("\n"), "utf-8");
56021
56436
  }
56022
56437
  return { redactedLines, patterns };
56023
56438
  }
56024
56439
  function processJsonFile(filePath, dryRun) {
56025
56440
  let raw;
56026
56441
  try {
56027
- raw = fs69.readFileSync(filePath, "utf-8");
56442
+ raw = fs70.readFileSync(filePath, "utf-8");
56028
56443
  } catch {
56029
56444
  return { redactedLines: 0, patterns: [] };
56030
56445
  }
@@ -56037,15 +56452,15 @@ function processJsonFile(filePath, dryRun) {
56037
56452
  const { value, modified, found } = redactJson(parsed);
56038
56453
  if (!modified) return { redactedLines: 0, patterns: [] };
56039
56454
  if (!dryRun) {
56040
- fs69.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
56455
+ fs70.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
56041
56456
  }
56042
56457
  return { redactedLines: 1, patterns: found };
56043
56458
  }
56044
56459
  function findJsonFiles(dir) {
56045
56460
  const results = [];
56046
- if (!fs69.existsSync(dir)) return results;
56047
- for (const entry of fs69.readdirSync(dir, { withFileTypes: true })) {
56048
- const full = path66.join(dir, entry.name);
56461
+ if (!fs70.existsSync(dir)) return results;
56462
+ for (const entry of fs70.readdirSync(dir, { withFileTypes: true })) {
56463
+ const full = path67.join(dir, entry.name);
56049
56464
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
56050
56465
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
56051
56466
  }
@@ -56054,9 +56469,9 @@ function findJsonFiles(dir) {
56054
56469
  function registerMaskCommand(program2) {
56055
56470
  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) => {
56056
56471
  const dryRun = !!options.dryRun;
56057
- const home = os58.homedir();
56058
- const claudeDir = path66.join(home, ".claude", "projects");
56059
- const geminiDir = path66.join(home, ".gemini", "tmp");
56472
+ const home = os60.homedir();
56473
+ const claudeDir = path67.join(home, ".claude", "projects");
56474
+ const geminiDir = path67.join(home, ".gemini", "tmp");
56060
56475
  const allFiles = [
56061
56476
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
56062
56477
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -56064,7 +56479,7 @@ function registerMaskCommand(program2) {
56064
56479
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
56065
56480
  const filtered = cutoff ? allFiles.filter((f) => {
56066
56481
  try {
56067
- return fs69.statSync(f.path).mtime >= cutoff;
56482
+ return fs70.statSync(f.path).mtime >= cutoff;
56068
56483
  } catch {
56069
56484
  return false;
56070
56485
  }
@@ -56120,7 +56535,7 @@ function registerMaskCommand(program2) {
56120
56535
  // src/cli.ts
56121
56536
  init_blast();
56122
56537
  var { version } = JSON.parse(
56123
- fs72.readFileSync(path69.join(__dirname, "../package.json"), "utf-8")
56538
+ fs73.readFileSync(path70.join(__dirname, "../package.json"), "utf-8")
56124
56539
  );
56125
56540
  var program = new Command();
56126
56541
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
@@ -56146,6 +56561,11 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
56146
56561
  } else {
56147
56562
  console.log(chalk41.green(`\u2705 Logged in \u2014 agent mode`));
56148
56563
  console.log(chalk41.gray(` Team policy enforced for all calls via Node9 cloud.`));
56564
+ if (!isTestingMode()) {
56565
+ const healed = ensureAutostartHealthy(!!getConfig().settings.autoStartDaemon);
56566
+ if (healed === "repaired")
56567
+ console.log(chalk41.green(` \u2713 Re-enabled daemon autostart (survives reboot)`));
56568
+ }
56149
56569
  }
56150
56570
  });
56151
56571
  program.command("signup").description("Create your node9 account / open the dashboard in your browser").option("--login", "Open the login page instead of signup").action((options) => {
@@ -56294,15 +56714,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
56294
56714
  } catch {
56295
56715
  }
56296
56716
  if (options.purge) {
56297
- const node9Dir = path69.join(os61.homedir(), ".node9");
56298
- if (fs72.existsSync(node9Dir)) {
56717
+ const node9Dir = path70.join(os63.homedir(), ".node9");
56718
+ if (fs73.existsSync(node9Dir)) {
56299
56719
  const confirmed = await confirm2({
56300
56720
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
56301
56721
  default: false
56302
56722
  });
56303
56723
  if (confirmed) {
56304
- fs72.rmSync(node9Dir, { recursive: true });
56305
- if (fs72.existsSync(node9Dir)) {
56724
+ fs73.rmSync(node9Dir, { recursive: true });
56725
+ if (fs73.existsSync(node9Dir)) {
56306
56726
  console.error(
56307
56727
  chalk41.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
56308
56728
  );
@@ -56427,7 +56847,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
56427
56847
  });
56428
56848
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
56429
56849
  try {
56430
- const dashboardPath = path69.join(__dirname, "dashboard.mjs");
56850
+ const dashboardPath = path70.join(__dirname, "dashboard.mjs");
56431
56851
  const dynamicImport = new Function("id", "return import(id)");
56432
56852
  const mod = await dynamicImport(`file://${dashboardPath}`);
56433
56853
  await mod.startMonitor();
@@ -56465,14 +56885,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
56465
56885
  Run "node9 addto claude" to register it as the statusLine.`
56466
56886
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
56467
56887
  if (subcommand === "debug") {
56468
- const flagFile = path69.join(os61.homedir(), ".node9", "hud-debug");
56888
+ const flagFile = path70.join(os63.homedir(), ".node9", "hud-debug");
56469
56889
  if (state === "on") {
56470
- fs72.mkdirSync(path69.dirname(flagFile), { recursive: true });
56471
- fs72.writeFileSync(flagFile, "");
56890
+ fs73.mkdirSync(path70.dirname(flagFile), { recursive: true });
56891
+ fs73.writeFileSync(flagFile, "");
56472
56892
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
56473
56893
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
56474
56894
  } else if (state === "off") {
56475
- if (fs72.existsSync(flagFile)) fs72.unlinkSync(flagFile);
56895
+ if (fs73.existsSync(flagFile)) fs73.unlinkSync(flagFile);
56476
56896
  console.log("HUD debug logging disabled.");
56477
56897
  } else {
56478
56898
  console.error("Usage: node9 hud debug on|off");
@@ -56595,9 +57015,9 @@ if (process.argv[2] !== "daemon") {
56595
57015
  const isCheckHook = process.argv[2] === "check";
56596
57016
  if (isCheckHook) {
56597
57017
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
56598
- const logPath = path69.join(os61.homedir(), ".node9", "hook-debug.log");
57018
+ const logPath = path70.join(os63.homedir(), ".node9", "hook-debug.log");
56599
57019
  const msg = reason instanceof Error ? reason.message : String(reason);
56600
- fs72.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
57020
+ fs73.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
56601
57021
  `);
56602
57022
  }
56603
57023
  process.exit(0);