@node9/proxy 1.45.0 → 1.46.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
@@ -219,8 +219,8 @@ function sanitizeConfig(raw) {
219
219
  }
220
220
  }
221
221
  const lines = result.error.issues.map((issue) => {
222
- const path64 = issue.path.length > 0 ? issue.path.join(".") : "root";
223
- return ` \u2022 ${path64}: ${issue.message}`;
222
+ const path65 = issue.path.length > 0 ? issue.path.join(".") : "root";
223
+ return ` \u2022 ${path65}: ${issue.message}`;
224
224
  });
225
225
  return {
226
226
  sanitized,
@@ -1341,9 +1341,9 @@ function matchesPattern(text, patterns) {
1341
1341
  const withoutDotSlash = text.replace(/^\.\//, "");
1342
1342
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1343
1343
  }
1344
- function getNestedValue(obj, path64) {
1344
+ function getNestedValue(obj, path65) {
1345
1345
  if (!obj || typeof obj !== "object") return null;
1346
- const segments = path64.split(".");
1346
+ const segments = path65.split(".");
1347
1347
  for (const seg of segments) {
1348
1348
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1349
1349
  }
@@ -1396,6 +1396,14 @@ function evaluateSmartConditions(args, rule) {
1396
1396
  });
1397
1397
  return mode === "any" ? results.some((r) => r) : results.every((r) => r);
1398
1398
  }
1399
+ function resolvePinned(matches) {
1400
+ if (matches.length === 0) return void 0;
1401
+ const pinned = matches.filter((r) => r.pinned);
1402
+ if (pinned.length === 0) return matches[0];
1403
+ return pinned.reduce(
1404
+ (best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
1405
+ );
1406
+ }
1399
1407
  function tokenize2(toolName) {
1400
1408
  return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
1401
1409
  }
@@ -1507,9 +1515,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
1507
1515
  }
1508
1516
  }
1509
1517
  if (config.policy.smartRules.length > 0) {
1510
- const matchedRule = config.policy.smartRules.find(
1518
+ const matches = config.policy.smartRules.filter(
1511
1519
  (rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
1512
1520
  );
1521
+ const matchedRule = resolvePinned(matches);
1513
1522
  if (matchedRule) {
1514
1523
  if (matchedRule.verdict === "allow")
1515
1524
  return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
@@ -2248,7 +2257,7 @@ function* stringValues(obj, depth = 0) {
2248
2257
  }
2249
2258
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
2250
2259
  }
2251
- 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, 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;
2260
+ 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;
2252
2261
  var init_dist = __esm({
2253
2262
  "packages/policy-engine/dist/index.mjs"() {
2254
2263
  "use strict";
@@ -3173,6 +3182,11 @@ var init_dist = __esm({
3173
3182
  REGEX_CACHE_MAX = 500;
3174
3183
  regexCache = /* @__PURE__ */ new Map();
3175
3184
  FORBIDDEN_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3185
+ VERDICT_RANK = {
3186
+ allow: 0,
3187
+ review: 1,
3188
+ block: 2
3189
+ };
3176
3190
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
3177
3191
  aws_default = {
3178
3192
  name: "aws",
@@ -3977,6 +3991,7 @@ var init_dist = __esm({
3977
3991
  DEDUPE_PREVIEW_LEN = 120;
3978
3992
  TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
3979
3993
  /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
3994
+ ENGINE_VERSION = "1.4.0";
3980
3995
  }
3981
3996
  });
3982
3997
 
@@ -9700,16 +9715,237 @@ var init_setup = __esm({
9700
9715
  }
9701
9716
  });
9702
9717
 
9703
- // src/pricing/litellm.ts
9718
+ // src/agent-wiring.ts
9704
9719
  import fs14 from "fs";
9705
9720
  import path16 from "path";
9706
9721
  import os13 from "os";
9722
+ import * as yaml2 from "yaml";
9723
+ import { parse as parseToml2 } from "smol-toml";
9724
+ function readJson2(filePath) {
9725
+ if (!fs14.existsSync(filePath)) return null;
9726
+ try {
9727
+ return JSON.parse(fs14.readFileSync(filePath, "utf-8"));
9728
+ } catch {
9729
+ return "invalid";
9730
+ }
9731
+ }
9732
+ function matchersHaveNode9Hook(matchers) {
9733
+ return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
9734
+ }
9735
+ function flatHaveNode9Hook(entries) {
9736
+ return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
9737
+ }
9738
+ function readHookRoot(filePath, format) {
9739
+ if (!fs14.existsSync(filePath)) return "absent";
9740
+ let raw;
9741
+ try {
9742
+ raw = fs14.readFileSync(filePath, "utf-8");
9743
+ } catch {
9744
+ return "absent";
9745
+ }
9746
+ try {
9747
+ const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
9748
+ return parsed?.hooks ?? {};
9749
+ } catch {
9750
+ return "invalid";
9751
+ }
9752
+ }
9753
+ function eventWired(root, ev, format) {
9754
+ const arr = root[ev.key];
9755
+ if (format === "matcher") return matchersHaveNode9Hook(arr);
9756
+ return flatHaveNode9Hook(arr);
9757
+ }
9758
+ function detectMcp(servers) {
9759
+ const entries = Object.entries(servers ?? {});
9760
+ const present = entries.some(([, s]) => s?.command === "node9");
9761
+ const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
9762
+ return { wrapped, present };
9763
+ }
9764
+ function readMcp(filePath, format) {
9765
+ if (!fs14.existsSync(filePath)) return { wrapped: [], present: false };
9766
+ try {
9767
+ if (format === "toml") {
9768
+ const parsed2 = parseToml2(fs14.readFileSync(filePath, "utf-8"));
9769
+ return detectMcp(parsed2?.mcp_servers);
9770
+ }
9771
+ const parsed = readJson2(filePath);
9772
+ if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
9773
+ return detectMcp(parsed.mcpServers);
9774
+ } catch {
9775
+ return { wrapped: [], present: false };
9776
+ }
9777
+ }
9778
+ function getAgentWiring(home = os13.homedir()) {
9779
+ const detected = detectAgents(home);
9780
+ return AGENT_SPECS.map((spec) => {
9781
+ const present = spec.present(home);
9782
+ const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
9783
+ let hooks;
9784
+ let wireState;
9785
+ let hookLabel;
9786
+ let settingsPath;
9787
+ if (spec.shimFile) {
9788
+ const shimWired = exists(spec.shimFile(home));
9789
+ hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
9790
+ wireState = shimWired ? "wired" : present ? "unwired" : "absent";
9791
+ hookLabel = "node9 plugin";
9792
+ settingsPath = spec.shimFile(home);
9793
+ } else {
9794
+ const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
9795
+ const primary = spec.hookEvents[0];
9796
+ const rootPresent = root !== "absent" && root !== "invalid";
9797
+ hooks = spec.hookEvents.map((ev) => ({
9798
+ label: hookLabelOf(ev, pad),
9799
+ wired: rootPresent && eventWired(root, ev, spec.hookFormat)
9800
+ }));
9801
+ if (root === "absent") wireState = "absent";
9802
+ else if (root === "invalid") wireState = "invalid";
9803
+ else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
9804
+ hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
9805
+ settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
9806
+ }
9807
+ const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
9808
+ const anyHookWired = hooks.some((h) => h.wired);
9809
+ return {
9810
+ id: spec.id,
9811
+ label: spec.label,
9812
+ setupCommand: spec.setupCommand,
9813
+ installed: detected[spec.id],
9814
+ present,
9815
+ hooks,
9816
+ wireState,
9817
+ hookLabel,
9818
+ settingsPath,
9819
+ configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
9820
+ mcpServers: mcp ? mcp.wrapped : null,
9821
+ mcpProtected: mcp ? mcp.present : false,
9822
+ isProtected: anyHookWired || (mcp?.present ?? false)
9823
+ };
9824
+ });
9825
+ }
9826
+ var exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
9827
+ var init_agent_wiring = __esm({
9828
+ "src/agent-wiring.ts"() {
9829
+ "use strict";
9830
+ init_setup();
9831
+ exists = (p) => {
9832
+ try {
9833
+ return fs14.existsSync(p);
9834
+ } catch {
9835
+ return false;
9836
+ }
9837
+ };
9838
+ ck = (key) => ({ key, kind: "check" });
9839
+ lg = (key) => ({ key, kind: "log" });
9840
+ DEFAULT_LABEL_PAD = 11;
9841
+ hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
9842
+ AGENT_SPECS = [
9843
+ {
9844
+ id: "claude",
9845
+ label: "Claude Code",
9846
+ setupCommand: "node9 agents add claude",
9847
+ hookFile: (h) => path16.join(h, ".claude", "settings.json"),
9848
+ hookFormat: "matcher",
9849
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
9850
+ mcpFile: (h) => path16.join(h, ".claude.json"),
9851
+ present: (h) => exists(path16.join(h, ".claude", "settings.json")) || exists(path16.join(h, ".claude.json"))
9852
+ },
9853
+ {
9854
+ id: "gemini",
9855
+ label: "Gemini CLI",
9856
+ setupCommand: "node9 agents add gemini",
9857
+ hookFile: (h) => path16.join(h, ".gemini", "settings.json"),
9858
+ hookFormat: "matcher",
9859
+ hookEvents: [ck("BeforeTool"), lg("AfterTool")],
9860
+ mcpFile: (h) => path16.join(h, ".gemini", "settings.json"),
9861
+ present: (h) => exists(path16.join(h, ".gemini", "settings.json"))
9862
+ },
9863
+ {
9864
+ id: "codex",
9865
+ label: "Codex",
9866
+ setupCommand: "node9 agents add codex",
9867
+ hookFile: (h) => path16.join(h, ".codex", "hooks.json"),
9868
+ hookFormat: "matcher",
9869
+ hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
9870
+ mcpFile: (h) => path16.join(h, ".codex", "config.toml"),
9871
+ mcpFormat: "toml",
9872
+ present: (h) => exists(path16.join(h, ".codex"))
9873
+ },
9874
+ {
9875
+ id: "antigravity",
9876
+ label: "Antigravity",
9877
+ setupCommand: "node9 agents add antigravity",
9878
+ hookFile: (h) => path16.join(h, ".gemini", "config", "hooks.json"),
9879
+ hookFormat: "matcher",
9880
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
9881
+ mcpFile: (h) => path16.join(h, ".gemini", "config", "mcp_config.json"),
9882
+ present: (h) => exists(path16.join(h, ".gemini", "config", "hooks.json")) || exists(path16.join(h, ".gemini", "antigravity-cli")) || exists(path16.join(h, ".gemini", "antigravity-ide"))
9883
+ },
9884
+ {
9885
+ id: "copilot",
9886
+ label: "GitHub Copilot",
9887
+ setupCommand: "node9 agents add copilot",
9888
+ hookFile: (h) => path16.join(h, ".copilot", "hooks", "node9.json"),
9889
+ hookFormat: "flat",
9890
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
9891
+ mcpFile: (h) => path16.join(h, ".copilot", "mcp-config.json"),
9892
+ present: (h) => exists(path16.join(h, ".copilot"))
9893
+ },
9894
+ {
9895
+ id: "cursor",
9896
+ label: "Cursor",
9897
+ setupCommand: "node9 agents add cursor",
9898
+ // MCP-only — no hook file (see note above).
9899
+ hookFormat: "flat",
9900
+ hookEvents: [],
9901
+ mcpFile: (h) => path16.join(h, ".cursor", "mcp.json"),
9902
+ present: (h) => exists(path16.join(h, ".cursor", "mcp.json"))
9903
+ },
9904
+ {
9905
+ id: "hermes",
9906
+ label: "Hermes Agent",
9907
+ setupCommand: "node9 agents add hermes",
9908
+ hookFile: (h) => hermesConfigPath(h),
9909
+ hookFormat: "yaml",
9910
+ hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
9911
+ labelPad: 14,
9912
+ // 'post_tool_call' is wider than the default
9913
+ present: (h) => exists(hermesConfigPath(h))
9914
+ },
9915
+ {
9916
+ // Plugin-shim agents — protected by a node9-authored plugin/extension file
9917
+ // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
9918
+ id: "opencode",
9919
+ label: "OpenCode",
9920
+ setupCommand: "node9 agents add opencode",
9921
+ hookFormat: "flat",
9922
+ hookEvents: [],
9923
+ shimFile: (h) => path16.join(h, ".config", "opencode", "plugins", "node9.js"),
9924
+ present: (h) => exists(path16.join(h, ".config", "opencode")) || exists(path16.join(h, ".config", "opencode", "plugins", "node9.js"))
9925
+ },
9926
+ {
9927
+ id: "pi",
9928
+ label: "Pi",
9929
+ setupCommand: "node9 agents add pi",
9930
+ hookFormat: "flat",
9931
+ hookEvents: [],
9932
+ shimFile: (h) => path16.join(h, ".pi", "agent", "extensions", "node9.js"),
9933
+ present: (h) => exists(path16.join(h, ".pi", "agent")) || exists(path16.join(h, ".pi", "agent", "extensions", "node9.js"))
9934
+ }
9935
+ ];
9936
+ }
9937
+ });
9938
+
9939
+ // src/pricing/litellm.ts
9940
+ import fs15 from "fs";
9941
+ import path17 from "path";
9942
+ import os14 from "os";
9707
9943
  function normalizeModel(raw) {
9708
9944
  return raw.replace(/-\d{8}$/, "").toLowerCase();
9709
9945
  }
9710
9946
  function readCache() {
9711
9947
  try {
9712
- const raw = JSON.parse(fs14.readFileSync(CACHE_FILE(), "utf-8"));
9948
+ const raw = JSON.parse(fs15.readFileSync(CACHE_FILE(), "utf-8"));
9713
9949
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
9714
9950
  return null;
9715
9951
  }
@@ -9723,18 +9959,18 @@ function readCache() {
9723
9959
  function writeCache(prices) {
9724
9960
  try {
9725
9961
  const target = CACHE_FILE();
9726
- const dir = path16.dirname(target);
9727
- if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
9962
+ const dir = path17.dirname(target);
9963
+ if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
9728
9964
  const tmp = target + ".tmp";
9729
9965
  const body = {
9730
9966
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
9731
9967
  prices
9732
9968
  };
9733
- fs14.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
9734
- fs14.renameSync(tmp, target);
9969
+ fs15.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
9970
+ fs15.renameSync(tmp, target);
9735
9971
  } catch (err2) {
9736
9972
  try {
9737
- fs14.appendFileSync(
9973
+ fs15.appendFileSync(
9738
9974
  HOOK_DEBUG_LOG,
9739
9975
  `[pricing] cache write failed: ${err2.message}
9740
9976
  `
@@ -9878,7 +10114,7 @@ var init_litellm = __esm({
9878
10114
  "gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
9879
10115
  "gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
9880
10116
  };
9881
- CACHE_FILE = () => path16.join(os13.homedir(), ".node9", "model-pricing.json");
10117
+ CACHE_FILE = () => path17.join(os14.homedir(), ".node9", "model-pricing.json");
9882
10118
  TTL_MS = 24 * 60 * 60 * 1e3;
9883
10119
  memCache = null;
9884
10120
  memCacheAt = 0;
@@ -9888,11 +10124,11 @@ var init_litellm = __esm({
9888
10124
  });
9889
10125
 
9890
10126
  // src/cost-gemini.ts
9891
- import fs15 from "fs";
9892
- import os14 from "os";
9893
- import path17 from "path";
10127
+ import fs16 from "fs";
10128
+ import os15 from "os";
10129
+ import path18 from "path";
9894
10130
  function geminiTmpDir() {
9895
- return path17.join(os14.homedir(), ".gemini", "tmp");
10131
+ return path18.join(os15.homedir(), ".gemini", "tmp");
9896
10132
  }
9897
10133
  function geminiPriceFor(model) {
9898
10134
  let tuple = pricingFor(model);
@@ -9907,14 +10143,14 @@ function geminiPriceFor(model) {
9907
10143
  }
9908
10144
  function safeReaddir(dir) {
9909
10145
  try {
9910
- return fs15.readdirSync(dir);
10146
+ return fs16.readdirSync(dir);
9911
10147
  } catch {
9912
10148
  return [];
9913
10149
  }
9914
10150
  }
9915
10151
  function isDir(p) {
9916
10152
  try {
9917
- return fs15.statSync(p).isDirectory();
10153
+ return fs16.statSync(p).isDirectory();
9918
10154
  } catch {
9919
10155
  return false;
9920
10156
  }
@@ -9922,11 +10158,11 @@ function isDir(p) {
9922
10158
  function listGeminiSessionFiles(base) {
9923
10159
  const out = [];
9924
10160
  for (const project of safeReaddir(base)) {
9925
- const chats = path17.join(base, project, "chats");
10161
+ const chats = path18.join(base, project, "chats");
9926
10162
  if (!isDir(chats)) continue;
9927
10163
  for (const f of safeReaddir(chats)) {
9928
10164
  if (f.startsWith("session-") && f.endsWith(".jsonl")) {
9929
- out.push({ file: path17.join(chats, f), project });
10165
+ out.push({ file: path18.join(chats, f), project });
9930
10166
  }
9931
10167
  }
9932
10168
  }
@@ -9993,7 +10229,7 @@ var init_cost_gemini = __esm({
9993
10229
  id: "gemini",
9994
10230
  available() {
9995
10231
  try {
9996
- return fs15.existsSync(geminiTmpDir());
10232
+ return fs16.existsSync(geminiTmpDir());
9997
10233
  } catch {
9998
10234
  return false;
9999
10235
  }
@@ -10002,13 +10238,13 @@ var init_cost_gemini = __esm({
10002
10238
  const combined = /* @__PURE__ */ new Map();
10003
10239
  for (const { file, project } of listGeminiSessionFiles(geminiTmpDir())) {
10004
10240
  try {
10005
- if (sinceMs !== void 0 && fs15.statSync(file).mtimeMs < sinceMs) continue;
10241
+ if (sinceMs !== void 0 && fs16.statSync(file).mtimeMs < sinceMs) continue;
10006
10242
  } catch {
10007
10243
  continue;
10008
10244
  }
10009
10245
  let content;
10010
10246
  try {
10011
- content = fs15.readFileSync(file, "utf8");
10247
+ content = fs16.readFileSync(file, "utf8");
10012
10248
  } catch {
10013
10249
  continue;
10014
10250
  }
@@ -10033,11 +10269,11 @@ var init_cost_gemini = __esm({
10033
10269
  });
10034
10270
 
10035
10271
  // src/cost-codex.ts
10036
- import fs16 from "fs";
10037
- import os15 from "os";
10038
- import path18 from "path";
10272
+ import fs17 from "fs";
10273
+ import os16 from "os";
10274
+ import path19 from "path";
10039
10275
  function codexSessionsDir() {
10040
- return path18.join(os15.homedir(), ".codex", "sessions");
10276
+ return path19.join(os16.homedir(), ".codex", "sessions");
10041
10277
  }
10042
10278
  function codexPriceFor(model) {
10043
10279
  return pricingFor(model) ?? CODEX_FALLBACK;
@@ -10050,16 +10286,16 @@ function codexSessionCost(model, tokens) {
10050
10286
  function listCodexSessionFiles(base) {
10051
10287
  const out = [];
10052
10288
  for (const y of safeReaddir2(base)) {
10053
- const yp = path18.join(base, y);
10289
+ const yp = path19.join(base, y);
10054
10290
  if (!isDir2(yp)) continue;
10055
10291
  for (const m of safeReaddir2(yp)) {
10056
- const mp = path18.join(yp, m);
10292
+ const mp = path19.join(yp, m);
10057
10293
  if (!isDir2(mp)) continue;
10058
10294
  for (const d of safeReaddir2(mp)) {
10059
- const dp = path18.join(mp, d);
10295
+ const dp = path19.join(mp, d);
10060
10296
  if (!isDir2(dp)) continue;
10061
10297
  for (const f of safeReaddir2(dp)) {
10062
- if (f.endsWith(".jsonl")) out.push(path18.join(dp, f));
10298
+ if (f.endsWith(".jsonl")) out.push(path19.join(dp, f));
10063
10299
  }
10064
10300
  }
10065
10301
  }
@@ -10068,14 +10304,14 @@ function listCodexSessionFiles(base) {
10068
10304
  }
10069
10305
  function safeReaddir2(dir) {
10070
10306
  try {
10071
- return fs16.readdirSync(dir);
10307
+ return fs17.readdirSync(dir);
10072
10308
  } catch {
10073
10309
  return [];
10074
10310
  }
10075
10311
  }
10076
10312
  function isDir2(p) {
10077
10313
  try {
10078
- return fs16.statSync(p).isDirectory();
10314
+ return fs17.statSync(p).isDirectory();
10079
10315
  } catch {
10080
10316
  return false;
10081
10317
  }
@@ -10145,7 +10381,7 @@ var init_cost_codex = __esm({
10145
10381
  id: "codex",
10146
10382
  available() {
10147
10383
  try {
10148
- return fs16.existsSync(codexSessionsDir());
10384
+ return fs17.existsSync(codexSessionsDir());
10149
10385
  } catch {
10150
10386
  return false;
10151
10387
  }
@@ -10155,13 +10391,13 @@ var init_cost_codex = __esm({
10155
10391
  const combined = /* @__PURE__ */ new Map();
10156
10392
  for (const file of listCodexSessionFiles(base)) {
10157
10393
  try {
10158
- if (sinceMs !== void 0 && fs16.statSync(file).mtimeMs < sinceMs) continue;
10394
+ if (sinceMs !== void 0 && fs17.statSync(file).mtimeMs < sinceMs) continue;
10159
10395
  } catch {
10160
10396
  continue;
10161
10397
  }
10162
10398
  let content;
10163
10399
  try {
10164
- content = fs16.readFileSync(file, "utf8");
10400
+ content = fs17.readFileSync(file, "utf8");
10165
10401
  } catch {
10166
10402
  continue;
10167
10403
  }
@@ -10467,85 +10703,85 @@ var init_scan_summary = __esm({
10467
10703
 
10468
10704
  // src/cli/commands/blast.ts
10469
10705
  import chalk2 from "chalk";
10470
- import fs17 from "fs";
10471
- import path19 from "path";
10472
- import os16 from "os";
10706
+ import fs18 from "fs";
10707
+ import path20 from "path";
10708
+ import os17 from "os";
10473
10709
  function buildSensitivePaths(home, cwd) {
10474
10710
  return [
10475
10711
  {
10476
- full: path19.join(home, ".ssh", "id_rsa"),
10712
+ full: path20.join(home, ".ssh", "id_rsa"),
10477
10713
  label: "~/.ssh/id_rsa",
10478
10714
  description: "RSA private key \u2014 grants SSH access to your servers",
10479
10715
  score: 20
10480
10716
  },
10481
10717
  {
10482
- full: path19.join(home, ".ssh", "id_ed25519"),
10718
+ full: path20.join(home, ".ssh", "id_ed25519"),
10483
10719
  label: "~/.ssh/id_ed25519",
10484
10720
  description: "Ed25519 private key \u2014 grants SSH access to your servers",
10485
10721
  score: 20
10486
10722
  },
10487
10723
  {
10488
- full: path19.join(home, ".ssh", "id_ecdsa"),
10724
+ full: path20.join(home, ".ssh", "id_ecdsa"),
10489
10725
  label: "~/.ssh/id_ecdsa",
10490
10726
  description: "ECDSA private key \u2014 grants SSH access to your servers",
10491
10727
  score: 20
10492
10728
  },
10493
10729
  {
10494
- full: path19.join(home, ".aws", "credentials"),
10730
+ full: path20.join(home, ".aws", "credentials"),
10495
10731
  label: "~/.aws/credentials",
10496
10732
  description: "AWS access keys \u2014 full cloud account access",
10497
10733
  score: 20
10498
10734
  },
10499
10735
  {
10500
- full: path19.join(home, ".aws", "config"),
10736
+ full: path20.join(home, ".aws", "config"),
10501
10737
  label: "~/.aws/config",
10502
10738
  description: "AWS configuration \u2014 account and region settings",
10503
10739
  score: 5
10504
10740
  },
10505
10741
  {
10506
- full: path19.join(home, ".config", "gcloud", "credentials.db"),
10742
+ full: path20.join(home, ".config", "gcloud", "credentials.db"),
10507
10743
  label: "~/.config/gcloud/credentials.db",
10508
10744
  description: "Google Cloud credentials",
10509
10745
  score: 15
10510
10746
  },
10511
10747
  {
10512
- full: path19.join(home, ".docker", "config.json"),
10748
+ full: path20.join(home, ".docker", "config.json"),
10513
10749
  label: "~/.docker/config.json",
10514
10750
  description: "Docker registry auth tokens",
10515
10751
  score: 10
10516
10752
  },
10517
10753
  {
10518
- full: path19.join(home, ".netrc"),
10754
+ full: path20.join(home, ".netrc"),
10519
10755
  label: "~/.netrc",
10520
10756
  description: "FTP/HTTP credentials in plain text",
10521
10757
  score: 15
10522
10758
  },
10523
10759
  {
10524
- full: path19.join(home, ".npmrc"),
10760
+ full: path20.join(home, ".npmrc"),
10525
10761
  label: "~/.npmrc",
10526
10762
  description: "npm auth token \u2014 can publish packages as you",
10527
10763
  score: 10
10528
10764
  },
10529
10765
  {
10530
- full: path19.join(home, ".node9", "credentials.json"),
10766
+ full: path20.join(home, ".node9", "credentials.json"),
10531
10767
  label: "~/.node9/credentials.json",
10532
10768
  description: "Node9 cloud API key",
10533
10769
  score: 10
10534
10770
  },
10535
10771
  {
10536
- full: path19.join(cwd, ".env"),
10772
+ full: path20.join(cwd, ".env"),
10537
10773
  label: ".env (current folder)",
10538
10774
  description: "App secrets \u2014 database passwords, API keys",
10539
10775
  score: 20
10540
10776
  },
10541
10777
  {
10542
- full: path19.join(cwd, ".env.local"),
10778
+ full: path20.join(cwd, ".env.local"),
10543
10779
  label: ".env.local (current folder)",
10544
10780
  description: "Local overrides \u2014 often contains real credentials",
10545
10781
  score: 15
10546
10782
  },
10547
10783
  {
10548
- full: path19.join(cwd, ".env.production"),
10784
+ full: path20.join(cwd, ".env.production"),
10549
10785
  label: ".env.production (current folder)",
10550
10786
  description: "Production secrets",
10551
10787
  score: 20
@@ -10554,7 +10790,7 @@ function buildSensitivePaths(home, cwd) {
10554
10790
  }
10555
10791
  function isReadable(filePath) {
10556
10792
  try {
10557
- fs17.accessSync(filePath, fs17.constants.R_OK);
10793
+ fs18.accessSync(filePath, fs18.constants.R_OK);
10558
10794
  return true;
10559
10795
  } catch {
10560
10796
  return false;
@@ -10567,13 +10803,13 @@ function scoreLabel(score) {
10567
10803
  return chalk2.red.bold(`${score}/100 Critical`);
10568
10804
  }
10569
10805
  function runBlast() {
10570
- const home = os16.homedir();
10806
+ const home = os17.homedir();
10571
10807
  const cwd = process.cwd();
10572
10808
  const paths = buildSensitivePaths(home, cwd);
10573
10809
  let scoreDeduction = 0;
10574
10810
  const reachable = [];
10575
10811
  for (const p of paths) {
10576
- if (fs17.existsSync(p.full) && isReadable(p.full)) {
10812
+ if (fs18.existsSync(p.full) && isReadable(p.full)) {
10577
10813
  reachable.push(p);
10578
10814
  scoreDeduction += p.score;
10579
10815
  }
@@ -10591,7 +10827,7 @@ function runBlast() {
10591
10827
  }
10592
10828
  function registerBlastCommand(program2) {
10593
10829
  program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
10594
- const home = os16.homedir();
10830
+ const home = os17.homedir();
10595
10831
  const cwd = process.cwd();
10596
10832
  const { reachable, envFindings, score } = runBlast();
10597
10833
  console.log("");
@@ -10767,17 +11003,17 @@ var init_scan_json = __esm({
10767
11003
  });
10768
11004
 
10769
11005
  // src/cli/render/scan-history.ts
10770
- import fs18 from "fs";
10771
- import path20 from "path";
10772
- import os17 from "os";
11006
+ import fs19 from "fs";
11007
+ import path21 from "path";
11008
+ import os18 from "os";
10773
11009
  function defaultHistoryPath() {
10774
- return path20.join(os17.homedir(), ".node9", "scan-history.json");
11010
+ return path21.join(os18.homedir(), ".node9", "scan-history.json");
10775
11011
  }
10776
11012
  function readPreviousScan(opts = {}) {
10777
11013
  const filePath = opts.path ?? defaultHistoryPath();
10778
11014
  try {
10779
- if (!fs18.existsSync(filePath)) return null;
10780
- const raw = fs18.readFileSync(filePath, "utf8");
11015
+ if (!fs19.existsSync(filePath)) return null;
11016
+ const raw = fs19.readFileSync(filePath, "utf8");
10781
11017
  const parsed = JSON.parse(raw);
10782
11018
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
10783
11019
  const last = parsed[parsed.length - 1];
@@ -10791,11 +11027,11 @@ function appendScanHistory(record, opts = {}) {
10791
11027
  const filePath = opts.path ?? defaultHistoryPath();
10792
11028
  const cap = opts.cap ?? SCAN_HISTORY_CAP;
10793
11029
  try {
10794
- fs18.mkdirSync(path20.dirname(filePath), { recursive: true });
11030
+ fs19.mkdirSync(path21.dirname(filePath), { recursive: true });
10795
11031
  let history = [];
10796
- if (fs18.existsSync(filePath)) {
11032
+ if (fs19.existsSync(filePath)) {
10797
11033
  try {
10798
- const parsed = JSON.parse(fs18.readFileSync(filePath, "utf8"));
11034
+ const parsed = JSON.parse(fs19.readFileSync(filePath, "utf8"));
10799
11035
  if (Array.isArray(parsed)) {
10800
11036
  history = parsed.filter(isValidRecord);
10801
11037
  }
@@ -10806,7 +11042,7 @@ function appendScanHistory(record, opts = {}) {
10806
11042
  if (history.length > cap) {
10807
11043
  history = history.slice(history.length - cap);
10808
11044
  }
10809
- fs18.writeFileSync(filePath, JSON.stringify(history, null, 2));
11045
+ fs19.writeFileSync(filePath, JSON.stringify(history, null, 2));
10810
11046
  } catch (err2) {
10811
11047
  process.stderr.write(
10812
11048
  `[node9] Warning: could not write scan-history.json: ${err2.message}
@@ -10837,15 +11073,15 @@ var init_scan_history = __esm({
10837
11073
  });
10838
11074
 
10839
11075
  // src/cost-copilot.ts
10840
- import fs19 from "fs";
10841
- import os18 from "os";
10842
- import path21 from "path";
11076
+ import fs20 from "fs";
11077
+ import os19 from "os";
11078
+ import path22 from "path";
10843
11079
  function copilotSessionsDir() {
10844
- return path21.join(os18.homedir(), ".copilot", "session-state");
11080
+ return path22.join(os19.homedir(), ".copilot", "session-state");
10845
11081
  }
10846
11082
  function safeReaddir3(dir) {
10847
11083
  try {
10848
- return fs19.readdirSync(dir);
11084
+ return fs20.readdirSync(dir);
10849
11085
  } catch {
10850
11086
  return [];
10851
11087
  }
@@ -10921,7 +11157,7 @@ var init_cost_copilot = __esm({
10921
11157
  id: "copilot",
10922
11158
  available() {
10923
11159
  try {
10924
- return fs19.existsSync(copilotSessionsDir());
11160
+ return fs20.existsSync(copilotSessionsDir());
10925
11161
  } catch {
10926
11162
  return false;
10927
11163
  }
@@ -10930,15 +11166,15 @@ var init_cost_copilot = __esm({
10930
11166
  const base = copilotSessionsDir();
10931
11167
  const combined = /* @__PURE__ */ new Map();
10932
11168
  for (const sid of safeReaddir3(base)) {
10933
- const file = path21.join(base, sid, "events.jsonl");
11169
+ const file = path22.join(base, sid, "events.jsonl");
10934
11170
  try {
10935
- if (sinceMs !== void 0 && fs19.statSync(file).mtimeMs < sinceMs) continue;
11171
+ if (sinceMs !== void 0 && fs20.statSync(file).mtimeMs < sinceMs) continue;
10936
11172
  } catch {
10937
11173
  continue;
10938
11174
  }
10939
11175
  let content;
10940
11176
  try {
10941
- content = fs19.readFileSync(file, "utf8");
11177
+ content = fs20.readFileSync(file, "utf8");
10942
11178
  } catch {
10943
11179
  continue;
10944
11180
  }
@@ -10963,17 +11199,17 @@ var init_cost_copilot = __esm({
10963
11199
  });
10964
11200
 
10965
11201
  // src/costSync.ts
10966
- import fs20 from "fs";
10967
- import path22 from "path";
10968
- import os19 from "os";
11202
+ import fs21 from "fs";
11203
+ import path23 from "path";
11204
+ import os20 from "os";
10969
11205
  function decodeProjectDirName(dirName) {
10970
11206
  return dirName.replace(/-/g, "/");
10971
11207
  }
10972
11208
  function parseJSONLFile(filePath, fallbackWorkingDir) {
10973
- const runId = path22.basename(filePath, ".jsonl");
11209
+ const runId = path23.basename(filePath, ".jsonl");
10974
11210
  let content;
10975
11211
  try {
10976
- content = fs20.readFileSync(filePath, "utf8");
11212
+ content = fs21.readFileSync(filePath, "utf8");
10977
11213
  } catch {
10978
11214
  return /* @__PURE__ */ new Map();
10979
11215
  }
@@ -11073,7 +11309,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11073
11309
  signal: AbortSignal.timeout(15e3)
11074
11310
  });
11075
11311
  if (!res.ok) {
11076
- fs20.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
11312
+ fs21.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
11077
11313
  `);
11078
11314
  } else {
11079
11315
  let stored;
@@ -11083,7 +11319,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11083
11319
  } catch {
11084
11320
  }
11085
11321
  if (typeof stored === "number" && stored < batch.length) {
11086
- fs20.appendFileSync(
11322
+ fs21.appendFileSync(
11087
11323
  HOOK_DEBUG_LOG,
11088
11324
  `[cost-sync] dropped ${batch.length - stored} of ${batch.length} rows
11089
11325
  `
@@ -11091,7 +11327,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11091
11327
  }
11092
11328
  }
11093
11329
  } catch (err2) {
11094
- fs20.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
11330
+ fs21.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
11095
11331
  `);
11096
11332
  }
11097
11333
  }
@@ -11104,10 +11340,10 @@ async function syncCost() {
11104
11340
  if (entries.length === 0) return;
11105
11341
  let username = "unknown";
11106
11342
  try {
11107
- username = os19.userInfo().username;
11343
+ username = os20.userInfo().username;
11108
11344
  } catch {
11109
11345
  }
11110
- const machineId = `${os19.hostname()}:${username}`;
11346
+ const machineId = `${os20.hostname()}:${username}`;
11111
11347
  await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
11112
11348
  }
11113
11349
  function startCostSync() {
@@ -11133,37 +11369,37 @@ var init_costSync = __esm({
11133
11369
  claudeSource = {
11134
11370
  id: "claude",
11135
11371
  available() {
11136
- return fs20.existsSync(path22.join(os19.homedir(), ".claude", "projects"));
11372
+ return fs21.existsSync(path23.join(os20.homedir(), ".claude", "projects"));
11137
11373
  },
11138
11374
  collect(sinceMs) {
11139
- const projectsDir = path22.join(os19.homedir(), ".claude", "projects");
11140
- if (!fs20.existsSync(projectsDir)) return [];
11375
+ const projectsDir = path23.join(os20.homedir(), ".claude", "projects");
11376
+ if (!fs21.existsSync(projectsDir)) return [];
11141
11377
  const combined = /* @__PURE__ */ new Map();
11142
11378
  let dirs;
11143
11379
  try {
11144
- dirs = fs20.readdirSync(projectsDir);
11380
+ dirs = fs21.readdirSync(projectsDir);
11145
11381
  } catch {
11146
11382
  return [];
11147
11383
  }
11148
11384
  for (const dir of dirs) {
11149
- const dirPath = path22.join(projectsDir, dir);
11385
+ const dirPath = path23.join(projectsDir, dir);
11150
11386
  try {
11151
- if (!fs20.statSync(dirPath).isDirectory()) continue;
11387
+ if (!fs21.statSync(dirPath).isDirectory()) continue;
11152
11388
  } catch {
11153
11389
  continue;
11154
11390
  }
11155
11391
  let files;
11156
11392
  try {
11157
- files = fs20.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11393
+ files = fs21.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11158
11394
  } catch {
11159
11395
  continue;
11160
11396
  }
11161
11397
  const fallbackWorkingDir = decodeProjectDirName(dir);
11162
11398
  for (const file of files) {
11163
- const filePath = path22.join(dirPath, file);
11399
+ const filePath = path23.join(dirPath, file);
11164
11400
  if (sinceMs !== void 0) {
11165
11401
  try {
11166
- if (fs20.statSync(filePath).mtimeMs < sinceMs) continue;
11402
+ if (fs21.statSync(filePath).mtimeMs < sinceMs) continue;
11167
11403
  } catch {
11168
11404
  continue;
11169
11405
  }
@@ -11203,9 +11439,9 @@ __export(scan_watermark_exports, {
11203
11439
  tickForensicBroadcast: () => tickForensicBroadcast,
11204
11440
  tickScanWatcher: () => tickScanWatcher
11205
11441
  });
11206
- import fs21 from "fs";
11207
- import os20 from "os";
11208
- import path23 from "path";
11442
+ import fs22 from "fs";
11443
+ import os21 from "os";
11444
+ import path24 from "path";
11209
11445
  import readline from "readline";
11210
11446
  function freshWatermark() {
11211
11447
  return {
@@ -11218,7 +11454,7 @@ function freshWatermark() {
11218
11454
  function loadWatermark() {
11219
11455
  let raw;
11220
11456
  try {
11221
- raw = fs21.readFileSync(WATERMARK_FILE(), "utf-8");
11457
+ raw = fs22.readFileSync(WATERMARK_FILE(), "utf-8");
11222
11458
  } catch {
11223
11459
  return { status: "fresh", wm: freshWatermark() };
11224
11460
  }
@@ -11270,28 +11506,28 @@ function loadWatermark() {
11270
11506
  function saveWatermark(wm) {
11271
11507
  if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
11272
11508
  const target = WATERMARK_FILE();
11273
- const dir = path23.dirname(target);
11274
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
11509
+ const dir = path24.dirname(target);
11510
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
11275
11511
  const tmp = target + ".tmp";
11276
- fs21.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
11277
- fs21.renameSync(tmp, target);
11512
+ fs22.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
11513
+ fs22.renameSync(tmp, target);
11278
11514
  }
11279
11515
  function listJsonlFiles() {
11280
11516
  const root = PROJECTS_DIR();
11281
- if (!fs21.existsSync(root)) return [];
11517
+ if (!fs22.existsSync(root)) return [];
11282
11518
  const out = [];
11283
- for (const entry of fs21.readdirSync(root, { withFileTypes: true })) {
11519
+ for (const entry of fs22.readdirSync(root, { withFileTypes: true })) {
11284
11520
  if (!entry.isDirectory()) continue;
11285
- const projectDir = path23.join(root, entry.name);
11521
+ const projectDir = path24.join(root, entry.name);
11286
11522
  let inner;
11287
11523
  try {
11288
- inner = fs21.readdirSync(projectDir, { withFileTypes: true });
11524
+ inner = fs22.readdirSync(projectDir, { withFileTypes: true });
11289
11525
  } catch {
11290
11526
  continue;
11291
11527
  }
11292
11528
  for (const file of inner) {
11293
11529
  if (file.isFile() && file.name.endsWith(".jsonl")) {
11294
- out.push(path23.join(projectDir, file.name));
11530
+ out.push(path24.join(projectDir, file.name));
11295
11531
  }
11296
11532
  }
11297
11533
  }
@@ -11299,7 +11535,7 @@ function listJsonlFiles() {
11299
11535
  }
11300
11536
  function fileSize(p) {
11301
11537
  try {
11302
- return fs21.statSync(p).size;
11538
+ return fs22.statSync(p).size;
11303
11539
  } catch {
11304
11540
  return 0;
11305
11541
  }
@@ -11307,7 +11543,7 @@ function fileSize(p) {
11307
11543
  async function scanDelta(filePath, fromByte, onLine) {
11308
11544
  const size = fileSize(filePath);
11309
11545
  if (size <= fromByte) return fromByte;
11310
- const stream = fs21.createReadStream(filePath, {
11546
+ const stream = fs22.createReadStream(filePath, {
11311
11547
  start: fromByte,
11312
11548
  end: size - 1,
11313
11549
  highWaterMark: 64 * 1024
@@ -11419,7 +11655,7 @@ async function tickForensicBroadcast(offsets) {
11419
11655
  continue;
11420
11656
  }
11421
11657
  if (size <= offset) continue;
11422
- const sessionId = path23.basename(file, ".jsonl");
11658
+ const sessionId = path24.basename(file, ".jsonl");
11423
11659
  const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
11424
11660
  out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
11425
11661
  });
@@ -11478,7 +11714,7 @@ function emptyTick(uploadAs) {
11478
11714
  function readRawWatermarkPreservingOffsets() {
11479
11715
  let raw;
11480
11716
  try {
11481
- raw = fs21.readFileSync(WATERMARK_FILE(), "utf-8");
11717
+ raw = fs22.readFileSync(WATERMARK_FILE(), "utf-8");
11482
11718
  } catch {
11483
11719
  return null;
11484
11720
  }
@@ -11512,13 +11748,13 @@ async function runActualTick(wm) {
11512
11748
  if (!known) {
11513
11749
  let mtimeMs = 0;
11514
11750
  try {
11515
- mtimeMs = fs21.statSync(filePath).mtime.getTime();
11751
+ mtimeMs = fs22.statSync(filePath).mtime.getTime();
11516
11752
  } catch {
11517
11753
  continue;
11518
11754
  }
11519
11755
  if (mtimeMs >= watermarkCreatedAt) {
11520
11756
  filesNew++;
11521
- const sessionId2 = path23.basename(filePath, ".jsonl");
11757
+ const sessionId2 = path24.basename(filePath, ".jsonl");
11522
11758
  const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
11523
11759
  totalToolCalls++;
11524
11760
  toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
@@ -11536,7 +11772,7 @@ async function runActualTick(wm) {
11536
11772
  filesSkipped++;
11537
11773
  continue;
11538
11774
  }
11539
- const sessionId = path23.basename(filePath, ".jsonl");
11775
+ const sessionId = path24.basename(filePath, ".jsonl");
11540
11776
  const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
11541
11777
  totalToolCalls++;
11542
11778
  toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
@@ -11564,8 +11800,8 @@ var init_scan_watermark = __esm({
11564
11800
  "use strict";
11565
11801
  init_dlp();
11566
11802
  init_dist();
11567
- PROJECTS_DIR = () => path23.join(os20.homedir(), ".claude", "projects");
11568
- WATERMARK_FILE = () => path23.join(os20.homedir(), ".node9", "scan-watermark.json");
11803
+ PROJECTS_DIR = () => path24.join(os21.homedir(), ".claude", "projects");
11804
+ WATERMARK_FILE = () => path24.join(os21.homedir(), ".node9", "scan-watermark.json");
11569
11805
  MAX_LINE_BYTES = 2 * 1024 * 1024;
11570
11806
  WATERMARK_SCHEMA_VERSION = 2;
11571
11807
  LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
@@ -11581,10 +11817,10 @@ __export(scan_upload_history_exports, {
11581
11817
  parseSinceCutoff: () => parseSinceCutoff,
11582
11818
  runUploadHistory: () => runUploadHistory
11583
11819
  });
11584
- import fs22 from "fs";
11820
+ import fs23 from "fs";
11585
11821
  import https from "https";
11586
- import os21 from "os";
11587
- import path24 from "path";
11822
+ import os22 from "os";
11823
+ import path25 from "path";
11588
11824
  import chalk4 from "chalk";
11589
11825
  function emptySignals2() {
11590
11826
  return {
@@ -11619,40 +11855,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
11619
11855
  return now.getTime() - 90 * 864e5;
11620
11856
  }
11621
11857
  function* iterateJsonlFiles(cutoffMs) {
11622
- const projectsDir = path24.join(os21.homedir(), ".claude", "projects");
11858
+ const projectsDir = path25.join(os22.homedir(), ".claude", "projects");
11623
11859
  let dirs;
11624
11860
  try {
11625
- dirs = fs22.readdirSync(projectsDir);
11861
+ dirs = fs23.readdirSync(projectsDir);
11626
11862
  } catch {
11627
11863
  return;
11628
11864
  }
11629
11865
  for (const dir of dirs) {
11630
- const dirPath = path24.join(projectsDir, dir);
11866
+ const dirPath = path25.join(projectsDir, dir);
11631
11867
  let stats;
11632
11868
  try {
11633
- stats = fs22.statSync(dirPath);
11869
+ stats = fs23.statSync(dirPath);
11634
11870
  } catch {
11635
11871
  continue;
11636
11872
  }
11637
11873
  if (!stats.isDirectory()) continue;
11638
11874
  let files;
11639
11875
  try {
11640
- files = fs22.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11876
+ files = fs23.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11641
11877
  } catch {
11642
11878
  continue;
11643
11879
  }
11644
11880
  for (const file of files) {
11645
- const filePath = path24.join(dirPath, file);
11881
+ const filePath = path25.join(dirPath, file);
11646
11882
  let mtime = 0;
11647
11883
  try {
11648
- mtime = fs22.statSync(filePath).mtimeMs;
11884
+ mtime = fs23.statSync(filePath).mtimeMs;
11649
11885
  } catch {
11650
11886
  continue;
11651
11887
  }
11652
11888
  if (mtime < cutoffMs) continue;
11653
11889
  yield {
11654
11890
  filePath,
11655
- sessionId: path24.basename(file, ".jsonl"),
11891
+ sessionId: path25.basename(file, ".jsonl"),
11656
11892
  projectDir: dir
11657
11893
  };
11658
11894
  }
@@ -11720,7 +11956,7 @@ async function runUploadHistory(opts) {
11720
11956
  filesScanned++;
11721
11957
  let content;
11722
11958
  try {
11723
- content = fs22.readFileSync(filePath, "utf8");
11959
+ content = fs23.readFileSync(filePath, "utf8");
11724
11960
  } catch {
11725
11961
  continue;
11726
11962
  }
@@ -11794,10 +12030,10 @@ async function runUploadHistory(opts) {
11794
12030
  const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
11795
12031
  let username = "unknown";
11796
12032
  try {
11797
- username = os21.userInfo().username;
12033
+ username = os22.userInfo().username;
11798
12034
  } catch {
11799
12035
  }
11800
- const machineId = `${os21.hostname()}:${username}`;
12036
+ const machineId = `${os22.hostname()}:${username}`;
11801
12037
  await postJson(costUrl, creds.apiKey, {
11802
12038
  machineId,
11803
12039
  entries: dailyEntries
@@ -11871,9 +12107,9 @@ var init_scan_upload_history = __esm({
11871
12107
 
11872
12108
  // src/cli/commands/scan.ts
11873
12109
  import chalk5 from "chalk";
11874
- import fs23 from "fs";
11875
- import path25 from "path";
11876
- import os22 from "os";
12110
+ import fs24 from "fs";
12111
+ import path26 from "path";
12112
+ import os23 from "os";
11877
12113
  import stringWidth2 from "string-width";
11878
12114
  function claudeModelPrice(model) {
11879
12115
  const t = pricingFor(model);
@@ -12096,14 +12332,14 @@ function buildRuleSources() {
12096
12332
  }
12097
12333
  function countScanFiles() {
12098
12334
  let total = 0;
12099
- const claudeDir = path25.join(os22.homedir(), ".claude", "projects");
12100
- if (fs23.existsSync(claudeDir)) {
12335
+ const claudeDir = path26.join(os23.homedir(), ".claude", "projects");
12336
+ if (fs24.existsSync(claudeDir)) {
12101
12337
  try {
12102
- for (const proj of fs23.readdirSync(claudeDir)) {
12103
- const p = path25.join(claudeDir, proj);
12338
+ for (const proj of fs24.readdirSync(claudeDir)) {
12339
+ const p = path26.join(claudeDir, proj);
12104
12340
  try {
12105
- if (!fs23.statSync(p).isDirectory()) continue;
12106
- total += fs23.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
12341
+ if (!fs24.statSync(p).isDirectory()) continue;
12342
+ total += fs24.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
12107
12343
  } catch {
12108
12344
  continue;
12109
12345
  }
@@ -12111,17 +12347,17 @@ function countScanFiles() {
12111
12347
  } catch {
12112
12348
  }
12113
12349
  }
12114
- const geminiDir = path25.join(os22.homedir(), ".gemini", "tmp");
12115
- if (fs23.existsSync(geminiDir)) {
12350
+ const geminiDir = path26.join(os23.homedir(), ".gemini", "tmp");
12351
+ if (fs24.existsSync(geminiDir)) {
12116
12352
  try {
12117
- for (const slug2 of fs23.readdirSync(geminiDir)) {
12118
- const p = path25.join(geminiDir, slug2);
12353
+ for (const slug2 of fs24.readdirSync(geminiDir)) {
12354
+ const p = path26.join(geminiDir, slug2);
12119
12355
  try {
12120
- if (!fs23.statSync(p).isDirectory()) continue;
12121
- const chatsDir = path25.join(p, "chats");
12122
- if (fs23.existsSync(chatsDir)) {
12356
+ if (!fs24.statSync(p).isDirectory()) continue;
12357
+ const chatsDir = path26.join(p, "chats");
12358
+ if (fs24.existsSync(chatsDir)) {
12123
12359
  try {
12124
- total += fs23.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
12360
+ total += fs24.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
12125
12361
  } catch {
12126
12362
  }
12127
12363
  }
@@ -12133,15 +12369,15 @@ function countScanFiles() {
12133
12369
  }
12134
12370
  }
12135
12371
  for (const surface of ["antigravity-cli", "antigravity-ide"]) {
12136
- const brainDir = path25.join(os22.homedir(), ".gemini", surface, "brain");
12137
- if (!fs23.existsSync(brainDir)) continue;
12372
+ const brainDir = path26.join(os23.homedir(), ".gemini", surface, "brain");
12373
+ if (!fs24.existsSync(brainDir)) continue;
12138
12374
  try {
12139
- for (const conv of fs23.readdirSync(brainDir)) {
12140
- const convPath = path25.join(brainDir, conv);
12375
+ for (const conv of fs24.readdirSync(brainDir)) {
12376
+ const convPath = path26.join(brainDir, conv);
12141
12377
  try {
12142
- if (!fs23.statSync(convPath).isDirectory()) continue;
12143
- const logsDir = path25.join(convPath, ".system_generated", "logs");
12144
- if (fs23.existsSync(path25.join(logsDir, "transcript_full.jsonl")) || fs23.existsSync(path25.join(logsDir, "transcript.jsonl"))) {
12378
+ if (!fs24.statSync(convPath).isDirectory()) continue;
12379
+ const logsDir = path26.join(convPath, ".system_generated", "logs");
12380
+ if (fs24.existsSync(path26.join(logsDir, "transcript_full.jsonl")) || fs24.existsSync(path26.join(logsDir, "transcript.jsonl"))) {
12145
12381
  total += 1;
12146
12382
  }
12147
12383
  } catch {
@@ -12151,31 +12387,31 @@ function countScanFiles() {
12151
12387
  } catch {
12152
12388
  }
12153
12389
  }
12154
- const copilotDir = path25.join(os22.homedir(), ".copilot", "session-state");
12155
- if (fs23.existsSync(copilotDir)) {
12390
+ const copilotDir = path26.join(os23.homedir(), ".copilot", "session-state");
12391
+ if (fs24.existsSync(copilotDir)) {
12156
12392
  try {
12157
- for (const sid of fs23.readdirSync(copilotDir)) {
12158
- if (fs23.existsSync(path25.join(copilotDir, sid, "events.jsonl"))) total += 1;
12393
+ for (const sid of fs24.readdirSync(copilotDir)) {
12394
+ if (fs24.existsSync(path26.join(copilotDir, sid, "events.jsonl"))) total += 1;
12159
12395
  }
12160
12396
  } catch {
12161
12397
  }
12162
12398
  }
12163
- const codexDir = path25.join(os22.homedir(), ".codex", "sessions");
12164
- if (fs23.existsSync(codexDir)) {
12399
+ const codexDir = path26.join(os23.homedir(), ".codex", "sessions");
12400
+ if (fs24.existsSync(codexDir)) {
12165
12401
  try {
12166
- for (const year of fs23.readdirSync(codexDir)) {
12167
- const yp = path25.join(codexDir, year);
12402
+ for (const year of fs24.readdirSync(codexDir)) {
12403
+ const yp = path26.join(codexDir, year);
12168
12404
  try {
12169
- if (!fs23.statSync(yp).isDirectory()) continue;
12170
- for (const month of fs23.readdirSync(yp)) {
12171
- const mp = path25.join(yp, month);
12405
+ if (!fs24.statSync(yp).isDirectory()) continue;
12406
+ for (const month of fs24.readdirSync(yp)) {
12407
+ const mp = path26.join(yp, month);
12172
12408
  try {
12173
- if (!fs23.statSync(mp).isDirectory()) continue;
12174
- for (const day of fs23.readdirSync(mp)) {
12175
- const dp = path25.join(mp, day);
12409
+ if (!fs24.statSync(mp).isDirectory()) continue;
12410
+ for (const day of fs24.readdirSync(mp)) {
12411
+ const dp = path26.join(mp, day);
12176
12412
  try {
12177
- if (!fs23.statSync(dp).isDirectory()) continue;
12178
- total += fs23.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
12413
+ if (!fs24.statSync(dp).isDirectory()) continue;
12414
+ total += fs24.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
12179
12415
  } catch {
12180
12416
  continue;
12181
12417
  }
@@ -12211,7 +12447,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12211
12447
  const sessionId = file.replace(/\.jsonl$/, "");
12212
12448
  let raw;
12213
12449
  try {
12214
- raw = fs23.readFileSync(path25.join(projPath, file), "utf-8");
12450
+ raw = fs24.readFileSync(path26.join(projPath, file), "utf-8");
12215
12451
  } catch {
12216
12452
  return;
12217
12453
  }
@@ -12263,7 +12499,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12263
12499
  if (block.type !== "tool_result") continue;
12264
12500
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
12265
12501
  if (filePath) {
12266
- const ext = path25.extname(filePath).toLowerCase();
12502
+ const ext = path26.extname(filePath).toLowerCase();
12267
12503
  if (CODE_EXTENSIONS.has(ext)) continue;
12268
12504
  }
12269
12505
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -12320,7 +12556,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12320
12556
  const rawCmd = String(input.command ?? "").trimStart();
12321
12557
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
12322
12558
  const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
12323
- const inputFileExt = inputFilePath ? path25.extname(inputFilePath).toLowerCase() : "";
12559
+ const inputFileExt = inputFilePath ? path26.extname(inputFilePath).toLowerCase() : "";
12324
12560
  if (CODE_EXTENSIONS.has(inputFileExt)) continue;
12325
12561
  const dlpMatch = scanArgs(input);
12326
12562
  if (dlpMatch) {
@@ -12417,19 +12653,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12417
12653
  }
12418
12654
  }
12419
12655
  function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
12420
- const projPath = path25.join(projectsDir, proj);
12656
+ const projPath = path26.join(projectsDir, proj);
12421
12657
  try {
12422
- if (!fs23.statSync(projPath).isDirectory()) return;
12658
+ if (!fs24.statSync(projPath).isDirectory()) return;
12423
12659
  } catch {
12424
12660
  return;
12425
12661
  }
12426
- const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os22.homedir(), "~")).slice(
12662
+ const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os23.homedir(), "~")).slice(
12427
12663
  0,
12428
12664
  40
12429
12665
  );
12430
12666
  let files;
12431
12667
  try {
12432
- files = fs23.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12668
+ files = fs24.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12433
12669
  } catch {
12434
12670
  return;
12435
12671
  }
@@ -12463,12 +12699,12 @@ function emptyClaudeScan() {
12463
12699
  };
12464
12700
  }
12465
12701
  function scanClaudeHistory(startDate, onProgress, onLine) {
12466
- const projectsDir = path25.join(os22.homedir(), ".claude", "projects");
12702
+ const projectsDir = path26.join(os23.homedir(), ".claude", "projects");
12467
12703
  const result = emptyClaudeScan();
12468
- if (!fs23.existsSync(projectsDir)) return result;
12704
+ if (!fs24.existsSync(projectsDir)) return result;
12469
12705
  let projDirs;
12470
12706
  try {
12471
- projDirs = fs23.readdirSync(projectsDir);
12707
+ projDirs = fs24.readdirSync(projectsDir);
12472
12708
  } catch {
12473
12709
  return result;
12474
12710
  }
@@ -12489,7 +12725,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
12489
12725
  return result;
12490
12726
  }
12491
12727
  function scanGeminiHistory(startDate, onProgress, onLine) {
12492
- const tmpDir = path25.join(os22.homedir(), ".gemini", "tmp");
12728
+ const tmpDir = path26.join(os23.homedir(), ".gemini", "tmp");
12493
12729
  const result = {
12494
12730
  filesScanned: 0,
12495
12731
  sessions: 0,
@@ -12504,33 +12740,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12504
12740
  sessionsWithEarlySecrets: 0
12505
12741
  };
12506
12742
  const dedup = emptyScanDedup();
12507
- if (!fs23.existsSync(tmpDir)) return result;
12743
+ if (!fs24.existsSync(tmpDir)) return result;
12508
12744
  let slugDirs;
12509
12745
  try {
12510
- slugDirs = fs23.readdirSync(tmpDir);
12746
+ slugDirs = fs24.readdirSync(tmpDir);
12511
12747
  } catch {
12512
12748
  return result;
12513
12749
  }
12514
12750
  const ruleSources = buildRuleSources();
12515
12751
  for (const slug2 of slugDirs) {
12516
- const slugPath = path25.join(tmpDir, slug2);
12752
+ const slugPath = path26.join(tmpDir, slug2);
12517
12753
  try {
12518
- if (!fs23.statSync(slugPath).isDirectory()) continue;
12754
+ if (!fs24.statSync(slugPath).isDirectory()) continue;
12519
12755
  } catch {
12520
12756
  continue;
12521
12757
  }
12522
12758
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
12523
12759
  try {
12524
12760
  projLabel = stripTerminalEscapes(
12525
- fs23.readFileSync(path25.join(slugPath, ".project_root"), "utf-8").trim()
12526
- ).replace(os22.homedir(), "~").slice(0, 40);
12761
+ fs24.readFileSync(path26.join(slugPath, ".project_root"), "utf-8").trim()
12762
+ ).replace(os23.homedir(), "~").slice(0, 40);
12527
12763
  } catch {
12528
12764
  }
12529
- const chatsDir = path25.join(slugPath, "chats");
12530
- if (!fs23.existsSync(chatsDir)) continue;
12765
+ const chatsDir = path26.join(slugPath, "chats");
12766
+ if (!fs24.existsSync(chatsDir)) continue;
12531
12767
  let chatFiles;
12532
12768
  try {
12533
- chatFiles = fs23.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
12769
+ chatFiles = fs24.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
12534
12770
  } catch {
12535
12771
  continue;
12536
12772
  }
@@ -12543,7 +12779,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12543
12779
  onProgress?.(result.filesScanned);
12544
12780
  let raw;
12545
12781
  try {
12546
- raw = fs23.readFileSync(path25.join(chatsDir, chatFile), "utf-8");
12782
+ raw = fs24.readFileSync(path26.join(chatsDir, chatFile), "utf-8");
12547
12783
  } catch {
12548
12784
  continue;
12549
12785
  }
@@ -12716,13 +12952,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12716
12952
  return result;
12717
12953
  }
12718
12954
  function antigravityBrainDirs() {
12719
- return ["antigravity-cli", "antigravity-ide"].map((surface) => path25.join(os22.homedir(), ".gemini", surface, "brain")).filter((p) => fs23.existsSync(p));
12955
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => path26.join(os23.homedir(), ".gemini", surface, "brain")).filter((p) => fs24.existsSync(p));
12720
12956
  }
12721
12957
  function antigravityTranscriptPath(convPath) {
12722
- const logsDir = path25.join(convPath, ".system_generated", "logs");
12958
+ const logsDir = path26.join(convPath, ".system_generated", "logs");
12723
12959
  for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
12724
- const p = path25.join(logsDir, name);
12725
- if (fs23.existsSync(p)) return p;
12960
+ const p = path26.join(logsDir, name);
12961
+ if (fs24.existsSync(p)) return p;
12726
12962
  }
12727
12963
  return null;
12728
12964
  }
@@ -12748,14 +12984,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12748
12984
  for (const brainDir of brainDirs) {
12749
12985
  let convDirs;
12750
12986
  try {
12751
- convDirs = fs23.readdirSync(brainDir);
12987
+ convDirs = fs24.readdirSync(brainDir);
12752
12988
  } catch {
12753
12989
  continue;
12754
12990
  }
12755
12991
  for (const conv of convDirs) {
12756
- const convPath = path25.join(brainDir, conv);
12992
+ const convPath = path26.join(brainDir, conv);
12757
12993
  try {
12758
- if (!fs23.statSync(convPath).isDirectory()) continue;
12994
+ if (!fs24.statSync(convPath).isDirectory()) continue;
12759
12995
  } catch {
12760
12996
  continue;
12761
12997
  }
@@ -12765,7 +13001,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12765
13001
  onProgress?.(result.filesScanned);
12766
13002
  let raw;
12767
13003
  try {
12768
- raw = fs23.readFileSync(transcriptFile, "utf-8");
13004
+ raw = fs24.readFileSync(transcriptFile, "utf-8");
12769
13005
  } catch {
12770
13006
  continue;
12771
13007
  }
@@ -12822,7 +13058,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12822
13058
  result.bashCalls++;
12823
13059
  const cwd = String(input.cwd ?? "");
12824
13060
  if (cwd && projLabel === conv.slice(0, 8)) {
12825
- projLabel = stripTerminalEscapes(cwd).replace(os22.homedir(), "~").slice(0, 40);
13061
+ projLabel = stripTerminalEscapes(cwd).replace(os23.homedir(), "~").slice(0, 40);
12826
13062
  }
12827
13063
  }
12828
13064
  const rawCmd = String(input.command ?? "").trimStart();
@@ -12922,7 +13158,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12922
13158
  return result;
12923
13159
  }
12924
13160
  function scanCopilotHistory(startDate, onProgress, onLine) {
12925
- const sessionDir = path25.join(os22.homedir(), ".copilot", "session-state");
13161
+ const sessionDir = path26.join(os23.homedir(), ".copilot", "session-state");
12926
13162
  const result = {
12927
13163
  filesScanned: 0,
12928
13164
  sessions: 0,
@@ -12938,22 +13174,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
12938
13174
  sessionsWithEarlySecrets: 0
12939
13175
  };
12940
13176
  const dedup = emptyScanDedup();
12941
- if (!fs23.existsSync(sessionDir)) return result;
13177
+ if (!fs24.existsSync(sessionDir)) return result;
12942
13178
  let sessionIds;
12943
13179
  try {
12944
- sessionIds = fs23.readdirSync(sessionDir);
13180
+ sessionIds = fs24.readdirSync(sessionDir);
12945
13181
  } catch {
12946
13182
  return result;
12947
13183
  }
12948
13184
  const ruleSources = buildRuleSources();
12949
13185
  for (const sessionId of sessionIds) {
12950
- const eventsPath = path25.join(sessionDir, sessionId, "events.jsonl");
12951
- if (!fs23.existsSync(eventsPath)) continue;
13186
+ const eventsPath = path26.join(sessionDir, sessionId, "events.jsonl");
13187
+ if (!fs24.existsSync(eventsPath)) continue;
12952
13188
  result.filesScanned++;
12953
13189
  onProgress?.(result.filesScanned);
12954
13190
  let raw;
12955
13191
  try {
12956
- raw = fs23.readFileSync(eventsPath, "utf-8");
13192
+ raw = fs24.readFileSync(eventsPath, "utf-8");
12957
13193
  } catch {
12958
13194
  continue;
12959
13195
  }
@@ -12973,7 +13209,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
12973
13209
  if (ev.type === "session.start") {
12974
13210
  const cwd = ev.data?.context?.cwd;
12975
13211
  if (typeof cwd === "string" && cwd) {
12976
- projLabel = stripTerminalEscapes(cwd).replace(os22.homedir(), "~").slice(0, 40);
13212
+ projLabel = stripTerminalEscapes(cwd).replace(os23.homedir(), "~").slice(0, 40);
12977
13213
  }
12978
13214
  continue;
12979
13215
  }
@@ -13105,7 +13341,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
13105
13341
  return result;
13106
13342
  }
13107
13343
  function scanCodexHistory(startDate, onProgress, onLine) {
13108
- const sessionsBase = path25.join(os22.homedir(), ".codex", "sessions");
13344
+ const sessionsBase = path26.join(os23.homedir(), ".codex", "sessions");
13109
13345
  const result = {
13110
13346
  filesScanned: 0,
13111
13347
  sessions: 0,
@@ -13120,32 +13356,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13120
13356
  sessionsWithEarlySecrets: 0
13121
13357
  };
13122
13358
  const dedup = emptyScanDedup();
13123
- if (!fs23.existsSync(sessionsBase)) return result;
13359
+ if (!fs24.existsSync(sessionsBase)) return result;
13124
13360
  const jsonlFiles = [];
13125
13361
  try {
13126
- for (const year of fs23.readdirSync(sessionsBase)) {
13127
- const yearPath = path25.join(sessionsBase, year);
13362
+ for (const year of fs24.readdirSync(sessionsBase)) {
13363
+ const yearPath = path26.join(sessionsBase, year);
13128
13364
  try {
13129
- if (!fs23.statSync(yearPath).isDirectory()) continue;
13365
+ if (!fs24.statSync(yearPath).isDirectory()) continue;
13130
13366
  } catch {
13131
13367
  continue;
13132
13368
  }
13133
- for (const month of fs23.readdirSync(yearPath)) {
13134
- const monthPath = path25.join(yearPath, month);
13369
+ for (const month of fs24.readdirSync(yearPath)) {
13370
+ const monthPath = path26.join(yearPath, month);
13135
13371
  try {
13136
- if (!fs23.statSync(monthPath).isDirectory()) continue;
13372
+ if (!fs24.statSync(monthPath).isDirectory()) continue;
13137
13373
  } catch {
13138
13374
  continue;
13139
13375
  }
13140
- for (const day of fs23.readdirSync(monthPath)) {
13141
- const dayPath = path25.join(monthPath, day);
13376
+ for (const day of fs24.readdirSync(monthPath)) {
13377
+ const dayPath = path26.join(monthPath, day);
13142
13378
  try {
13143
- if (!fs23.statSync(dayPath).isDirectory()) continue;
13379
+ if (!fs24.statSync(dayPath).isDirectory()) continue;
13144
13380
  } catch {
13145
13381
  continue;
13146
13382
  }
13147
- for (const file of fs23.readdirSync(dayPath)) {
13148
- if (file.endsWith(".jsonl")) jsonlFiles.push(path25.join(dayPath, file));
13383
+ for (const file of fs24.readdirSync(dayPath)) {
13384
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path26.join(dayPath, file));
13149
13385
  }
13150
13386
  }
13151
13387
  }
@@ -13159,7 +13395,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13159
13395
  onProgress?.(result.filesScanned);
13160
13396
  let lines;
13161
13397
  try {
13162
- lines = fs23.readFileSync(filePath, "utf-8").split("\n");
13398
+ lines = fs24.readFileSync(filePath, "utf-8").split("\n");
13163
13399
  } catch {
13164
13400
  continue;
13165
13401
  }
@@ -13186,7 +13422,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13186
13422
  sessionId = String(payload["id"] ?? filePath);
13187
13423
  startTime = String(payload["timestamp"] ?? "");
13188
13424
  const cwd = String(payload["cwd"] ?? "");
13189
- projLabel = stripTerminalEscapes(cwd.replace(os22.homedir(), "~")).slice(0, 40);
13425
+ projLabel = stripTerminalEscapes(cwd.replace(os23.homedir(), "~")).slice(0, 40);
13190
13426
  continue;
13191
13427
  }
13192
13428
  if (entry.type === "turn_context" && typeof payload["model"] === "string") {
@@ -13346,17 +13582,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13346
13582
  return result;
13347
13583
  }
13348
13584
  function scanShellConfig() {
13349
- const home = os22.homedir();
13585
+ const home = os23.homedir();
13350
13586
  const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
13351
- (f) => path25.join(home, f)
13587
+ (f) => path26.join(home, f)
13352
13588
  );
13353
13589
  const findings = [];
13354
13590
  const seen = /* @__PURE__ */ new Set();
13355
13591
  for (const filePath of configFiles) {
13356
- if (!fs23.existsSync(filePath)) continue;
13592
+ if (!fs24.existsSync(filePath)) continue;
13357
13593
  let lines;
13358
13594
  try {
13359
- lines = fs23.readFileSync(filePath, "utf-8").split("\n");
13595
+ lines = fs24.readFileSync(filePath, "utf-8").split("\n");
13360
13596
  } catch {
13361
13597
  continue;
13362
13598
  }
@@ -14162,7 +14398,7 @@ function registerScanCommand(program2) {
14162
14398
  if (!drillDown) {
14163
14399
  const useInk2 = !options.classic;
14164
14400
  if (useInk2) {
14165
- const scanInkPath = path25.join(__dirname, "scan-ink.mjs");
14401
+ const scanInkPath = path26.join(__dirname, "scan-ink.mjs");
14166
14402
  const dynamicImport = new Function("id", "return import(id)");
14167
14403
  const mod = await dynamicImport(`file://${scanInkPath}`);
14168
14404
  const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
@@ -14573,8 +14809,8 @@ var init_suggestion_tracker = __esm({
14573
14809
  });
14574
14810
 
14575
14811
  // src/daemon/taint-store.ts
14576
- import fs24 from "fs";
14577
- import path26 from "path";
14812
+ import fs25 from "fs";
14813
+ import path27 from "path";
14578
14814
  var DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
14579
14815
  var init_taint_store = __esm({
14580
14816
  "src/daemon/taint-store.ts"() {
@@ -14644,9 +14880,9 @@ var init_taint_store = __esm({
14644
14880
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
14645
14881
  _resolve(filePath) {
14646
14882
  try {
14647
- return fs24.realpathSync.native(path26.resolve(filePath));
14883
+ return fs25.realpathSync.native(path27.resolve(filePath));
14648
14884
  } catch {
14649
- return path26.resolve(filePath);
14885
+ return path27.resolve(filePath);
14650
14886
  }
14651
14887
  }
14652
14888
  };
@@ -14811,14 +15047,14 @@ var init_session_history = __esm({
14811
15047
 
14812
15048
  // src/daemon/state.ts
14813
15049
  import net2 from "net";
14814
- import fs25 from "fs";
14815
- import path27 from "path";
14816
- import os23 from "os";
15050
+ import fs26 from "fs";
15051
+ import path28 from "path";
15052
+ import os24 from "os";
14817
15053
  import { randomUUID as randomUUID3 } from "crypto";
14818
15054
  function loadInsightCounts() {
14819
15055
  try {
14820
- if (!fs25.existsSync(INSIGHT_COUNTS_FILE)) return;
14821
- const data = JSON.parse(fs25.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
15056
+ if (!fs26.existsSync(INSIGHT_COUNTS_FILE)) return;
15057
+ const data = JSON.parse(fs26.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
14822
15058
  for (const [tool, count] of Object.entries(data)) {
14823
15059
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
14824
15060
  }
@@ -14857,23 +15093,23 @@ function markRejectionHandlerRegistered() {
14857
15093
  daemonRejectionHandlerRegistered = true;
14858
15094
  }
14859
15095
  function atomicWriteSync2(filePath, data, options) {
14860
- const dir = path27.dirname(filePath);
14861
- if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
15096
+ const dir = path28.dirname(filePath);
15097
+ if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
14862
15098
  const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
14863
15099
  try {
14864
- fs25.writeFileSync(tmpPath, data, options);
15100
+ fs26.writeFileSync(tmpPath, data, options);
14865
15101
  } catch (err2) {
14866
15102
  try {
14867
- fs25.unlinkSync(tmpPath);
15103
+ fs26.unlinkSync(tmpPath);
14868
15104
  } catch {
14869
15105
  }
14870
15106
  throw err2;
14871
15107
  }
14872
15108
  try {
14873
- fs25.renameSync(tmpPath, filePath);
15109
+ fs26.renameSync(tmpPath, filePath);
14874
15110
  } catch (err2) {
14875
15111
  try {
14876
- fs25.unlinkSync(tmpPath);
15112
+ fs26.unlinkSync(tmpPath);
14877
15113
  } catch {
14878
15114
  }
14879
15115
  throw err2;
@@ -14897,16 +15133,16 @@ function appendAuditLog(data) {
14897
15133
  decision: data.decision,
14898
15134
  source: "daemon"
14899
15135
  };
14900
- const dir = path27.dirname(AUDIT_LOG_FILE);
14901
- if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
14902
- fs25.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
15136
+ const dir = path28.dirname(AUDIT_LOG_FILE);
15137
+ if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
15138
+ fs26.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
14903
15139
  } catch {
14904
15140
  }
14905
15141
  }
14906
15142
  function getAuditHistory(limit = 20) {
14907
15143
  try {
14908
- if (!fs25.existsSync(AUDIT_LOG_FILE)) return [];
14909
- const lines = fs25.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
15144
+ if (!fs26.existsSync(AUDIT_LOG_FILE)) return [];
15145
+ const lines = fs26.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
14910
15146
  if (lines.length === 1 && lines[0] === "") return [];
14911
15147
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
14912
15148
  } catch {
@@ -14915,7 +15151,7 @@ function getAuditHistory(limit = 20) {
14915
15151
  }
14916
15152
  function getOrgName() {
14917
15153
  try {
14918
- if (fs25.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
15154
+ if (fs26.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
14919
15155
  } catch {
14920
15156
  }
14921
15157
  return null;
@@ -14923,8 +15159,8 @@ function getOrgName() {
14923
15159
  function writeGlobalSetting(key, value) {
14924
15160
  let config = {};
14925
15161
  try {
14926
- if (fs25.existsSync(GLOBAL_CONFIG_FILE)) {
14927
- config = JSON.parse(fs25.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
15162
+ if (fs26.existsSync(GLOBAL_CONFIG_FILE)) {
15163
+ config = JSON.parse(fs26.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
14928
15164
  }
14929
15165
  } catch {
14930
15166
  }
@@ -14936,8 +15172,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
14936
15172
  try {
14937
15173
  let trust = { entries: [] };
14938
15174
  try {
14939
- if (fs25.existsSync(TRUST_FILE2))
14940
- trust = JSON.parse(fs25.readFileSync(TRUST_FILE2, "utf-8"));
15175
+ if (fs26.existsSync(TRUST_FILE2))
15176
+ trust = JSON.parse(fs26.readFileSync(TRUST_FILE2, "utf-8"));
14941
15177
  } catch {
14942
15178
  }
14943
15179
  trust.entries = trust.entries.filter(
@@ -14954,8 +15190,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
14954
15190
  }
14955
15191
  function readPersistentDecisions() {
14956
15192
  try {
14957
- if (fs25.existsSync(DECISIONS_FILE)) {
14958
- return JSON.parse(fs25.readFileSync(DECISIONS_FILE, "utf-8"));
15193
+ if (fs26.existsSync(DECISIONS_FILE)) {
15194
+ return JSON.parse(fs26.readFileSync(DECISIONS_FILE, "utf-8"));
14959
15195
  }
14960
15196
  } catch {
14961
15197
  }
@@ -14983,7 +15219,7 @@ function estimateToolCost(tool, args) {
14983
15219
  const filePath = a.file_path ?? a.path;
14984
15220
  if (filePath) {
14985
15221
  try {
14986
- const bytes = fs25.statSync(filePath).size;
15222
+ const bytes = fs26.statSync(filePath).size;
14987
15223
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
14988
15224
  } catch {
14989
15225
  }
@@ -15054,7 +15290,7 @@ function abandonPending() {
15054
15290
  });
15055
15291
  if (autoStarted) {
15056
15292
  try {
15057
- fs25.unlinkSync(DAEMON_PID_FILE);
15293
+ fs26.unlinkSync(DAEMON_PID_FILE);
15058
15294
  } catch {
15059
15295
  }
15060
15296
  setTimeout(() => {
@@ -15065,8 +15301,8 @@ function abandonPending() {
15065
15301
  }
15066
15302
  function logActivitySocket(msg) {
15067
15303
  try {
15068
- fs25.appendFileSync(
15069
- path27.join(homeDir, ".node9", "hook-debug.log"),
15304
+ fs26.appendFileSync(
15305
+ path28.join(homeDir, ".node9", "hook-debug.log"),
15070
15306
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
15071
15307
  `
15072
15308
  );
@@ -15088,13 +15324,13 @@ function shouldRebind(now = Date.now()) {
15088
15324
  function startActivitySocket() {
15089
15325
  bindActivitySocket();
15090
15326
  activityHealthInterval = setInterval(() => {
15091
- if (!fs25.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
15327
+ if (!fs26.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
15092
15328
  }, ACTIVITY_HEALTH_PROBE_MS);
15093
15329
  activityHealthInterval.unref();
15094
15330
  process.on("exit", () => {
15095
15331
  if (activityHealthInterval) clearInterval(activityHealthInterval);
15096
15332
  try {
15097
- fs25.unlinkSync(ACTIVITY_SOCKET_PATH2);
15333
+ fs26.unlinkSync(ACTIVITY_SOCKET_PATH2);
15098
15334
  } catch {
15099
15335
  }
15100
15336
  });
@@ -15122,7 +15358,7 @@ function attemptRebind(reason) {
15122
15358
  }
15123
15359
  function bindActivitySocket() {
15124
15360
  try {
15125
- fs25.unlinkSync(ACTIVITY_SOCKET_PATH2);
15361
+ fs26.unlinkSync(ACTIVITY_SOCKET_PATH2);
15126
15362
  } catch {
15127
15363
  }
15128
15364
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -15217,305 +15453,84 @@ function bindActivitySocket() {
15217
15453
  });
15218
15454
  socket.on("error", () => {
15219
15455
  });
15220
- });
15221
- unixServer.on("error", (err2) => {
15222
- logActivitySocket(`server error: ${err2.message}`);
15223
- });
15224
- unixServer.listen(ACTIVITY_SOCKET_PATH2, () => {
15225
- logActivitySocket(`bound to ${ACTIVITY_SOCKET_PATH2}`);
15226
- });
15227
- activitySocketServer = unixServer;
15228
- }
15229
- var homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, sessionTaintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
15230
- var init_state2 = __esm({
15231
- "src/daemon/state.ts"() {
15232
- "use strict";
15233
- init_daemon();
15234
- init_suggestion_tracker();
15235
- init_taint_store();
15236
- init_session_counters();
15237
- init_session_history();
15238
- homeDir = os23.homedir();
15239
- DAEMON_PID_FILE = path27.join(homeDir, ".node9", "daemon.pid");
15240
- DECISIONS_FILE = path27.join(homeDir, ".node9", "decisions.json");
15241
- AUDIT_LOG_FILE = path27.join(homeDir, ".node9", "audit.log");
15242
- TRUST_FILE2 = path27.join(homeDir, ".node9", "trust.json");
15243
- GLOBAL_CONFIG_FILE = path27.join(homeDir, ".node9", "config.json");
15244
- CREDENTIALS_FILE = path27.join(homeDir, ".node9", "credentials.json");
15245
- INSIGHT_COUNTS_FILE = path27.join(homeDir, ".node9", "insight-counts.json");
15246
- pending = /* @__PURE__ */ new Map();
15247
- sseClients = /* @__PURE__ */ new Set();
15248
- suggestionTracker = new SuggestionTracker(3);
15249
- taintStore = new TaintStore();
15250
- sessionTaintStore = new SessionTaintStore();
15251
- insightCounts = /* @__PURE__ */ new Map();
15252
- _abandonTimer = null;
15253
- _hadBrowserClient = false;
15254
- _daemonServer = null;
15255
- daemonRejectionHandlerRegistered = false;
15256
- AUTO_DENY_MS = 12e4;
15257
- TRUST_DURATIONS = {
15258
- "30m": 30 * 6e4,
15259
- "1h": 60 * 6e4,
15260
- "2h": 2 * 60 * 6e4
15261
- };
15262
- autoStarted = process.env.NODE9_AUTO_STARTED === "1";
15263
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path27.join(os23.tmpdir(), "node9-activity.sock");
15264
- ACTIVITY_RING_SIZE = 100;
15265
- activityRing = [];
15266
- LARGE_RESPONSE_RING_SIZE = 20;
15267
- largeResponseRing = [];
15268
- cachedScanResult = null;
15269
- cachedScanTs = 0;
15270
- SCAN_CACHE_TTL_MS = 5 * 60 * 1e3;
15271
- SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
15272
- INPUT_PRICE_PER_1M = 3;
15273
- OUTPUT_PRICE_PER_1M = 15;
15274
- BYTES_PER_TOKEN = 4;
15275
- CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
15276
- "privilege-escalation",
15277
- "destructive-op",
15278
- "eval-of-remote"
15279
- ]);
15280
- WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
15281
- "write",
15282
- "write_file",
15283
- "create_file",
15284
- "edit",
15285
- "multiedit",
15286
- "str_replace_based_edit_tool",
15287
- "replace",
15288
- "notebook_edit",
15289
- "notebookedit"
15290
- ]);
15291
- ACTIVITY_REBIND_MAX_ATTEMPTS = 5;
15292
- ACTIVITY_REBIND_WINDOW_MS = 6e4;
15293
- ACTIVITY_HEALTH_PROBE_MS = 2e3;
15294
- activitySocketServer = null;
15295
- activityHealthInterval = null;
15296
- activityRebindAttempts = [];
15297
- activityCircuitTripped = false;
15298
- }
15299
- });
15300
-
15301
- // src/agent-wiring.ts
15302
- import fs26 from "fs";
15303
- import path28 from "path";
15304
- import os24 from "os";
15305
- import * as yaml2 from "yaml";
15306
- import { parse as parseToml2 } from "smol-toml";
15307
- function readJson2(filePath) {
15308
- if (!fs26.existsSync(filePath)) return null;
15309
- try {
15310
- return JSON.parse(fs26.readFileSync(filePath, "utf-8"));
15311
- } catch {
15312
- return "invalid";
15313
- }
15314
- }
15315
- function matchersHaveNode9Hook(matchers) {
15316
- return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
15317
- }
15318
- function flatHaveNode9Hook(entries) {
15319
- return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
15320
- }
15321
- function readHookRoot(filePath, format) {
15322
- if (!fs26.existsSync(filePath)) return "absent";
15323
- let raw;
15324
- try {
15325
- raw = fs26.readFileSync(filePath, "utf-8");
15326
- } catch {
15327
- return "absent";
15328
- }
15329
- try {
15330
- const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
15331
- return parsed?.hooks ?? {};
15332
- } catch {
15333
- return "invalid";
15334
- }
15335
- }
15336
- function eventWired(root, ev, format) {
15337
- const arr = root[ev.key];
15338
- if (format === "matcher") return matchersHaveNode9Hook(arr);
15339
- return flatHaveNode9Hook(arr);
15340
- }
15341
- function detectMcp(servers) {
15342
- const entries = Object.entries(servers ?? {});
15343
- const present = entries.some(([, s]) => s?.command === "node9");
15344
- const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
15345
- return { wrapped, present };
15346
- }
15347
- function readMcp(filePath, format) {
15348
- if (!fs26.existsSync(filePath)) return { wrapped: [], present: false };
15349
- try {
15350
- if (format === "toml") {
15351
- const parsed2 = parseToml2(fs26.readFileSync(filePath, "utf-8"));
15352
- return detectMcp(parsed2?.mcp_servers);
15353
- }
15354
- const parsed = readJson2(filePath);
15355
- if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
15356
- return detectMcp(parsed.mcpServers);
15357
- } catch {
15358
- return { wrapped: [], present: false };
15359
- }
15360
- }
15361
- function getAgentWiring(home = os24.homedir()) {
15362
- const detected = detectAgents(home);
15363
- return AGENT_SPECS.map((spec) => {
15364
- const present = spec.present(home);
15365
- const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
15366
- let hooks;
15367
- let wireState;
15368
- let hookLabel;
15369
- let settingsPath;
15370
- if (spec.shimFile) {
15371
- const shimWired = exists(spec.shimFile(home));
15372
- hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
15373
- wireState = shimWired ? "wired" : present ? "unwired" : "absent";
15374
- hookLabel = "node9 plugin";
15375
- settingsPath = spec.shimFile(home);
15376
- } else {
15377
- const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
15378
- const primary = spec.hookEvents[0];
15379
- const rootPresent = root !== "absent" && root !== "invalid";
15380
- hooks = spec.hookEvents.map((ev) => ({
15381
- label: hookLabelOf(ev, pad),
15382
- wired: rootPresent && eventWired(root, ev, spec.hookFormat)
15383
- }));
15384
- if (root === "absent") wireState = "absent";
15385
- else if (root === "invalid") wireState = "invalid";
15386
- else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
15387
- hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
15388
- settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
15389
- }
15390
- const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
15391
- const anyHookWired = hooks.some((h) => h.wired);
15392
- return {
15393
- id: spec.id,
15394
- label: spec.label,
15395
- setupCommand: spec.setupCommand,
15396
- installed: detected[spec.id],
15397
- present,
15398
- hooks,
15399
- wireState,
15400
- hookLabel,
15401
- settingsPath,
15402
- configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
15403
- mcpServers: mcp ? mcp.wrapped : null,
15404
- mcpProtected: mcp ? mcp.present : false,
15405
- isProtected: anyHookWired || (mcp?.present ?? false)
15406
- };
15407
- });
15408
- }
15409
- var exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
15410
- var init_agent_wiring = __esm({
15411
- "src/agent-wiring.ts"() {
15412
- "use strict";
15413
- init_setup();
15414
- exists = (p) => {
15415
- try {
15416
- return fs26.existsSync(p);
15417
- } catch {
15418
- return false;
15419
- }
15420
- };
15421
- ck = (key) => ({ key, kind: "check" });
15422
- lg = (key) => ({ key, kind: "log" });
15423
- DEFAULT_LABEL_PAD = 11;
15424
- hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
15425
- AGENT_SPECS = [
15426
- {
15427
- id: "claude",
15428
- label: "Claude Code",
15429
- setupCommand: "node9 agents add claude",
15430
- hookFile: (h) => path28.join(h, ".claude", "settings.json"),
15431
- hookFormat: "matcher",
15432
- hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
15433
- mcpFile: (h) => path28.join(h, ".claude.json"),
15434
- present: (h) => exists(path28.join(h, ".claude", "settings.json")) || exists(path28.join(h, ".claude.json"))
15435
- },
15436
- {
15437
- id: "gemini",
15438
- label: "Gemini CLI",
15439
- setupCommand: "node9 agents add gemini",
15440
- hookFile: (h) => path28.join(h, ".gemini", "settings.json"),
15441
- hookFormat: "matcher",
15442
- hookEvents: [ck("BeforeTool"), lg("AfterTool")],
15443
- mcpFile: (h) => path28.join(h, ".gemini", "settings.json"),
15444
- present: (h) => exists(path28.join(h, ".gemini", "settings.json"))
15445
- },
15446
- {
15447
- id: "codex",
15448
- label: "Codex",
15449
- setupCommand: "node9 agents add codex",
15450
- hookFile: (h) => path28.join(h, ".codex", "hooks.json"),
15451
- hookFormat: "matcher",
15452
- hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
15453
- mcpFile: (h) => path28.join(h, ".codex", "config.toml"),
15454
- mcpFormat: "toml",
15455
- present: (h) => exists(path28.join(h, ".codex"))
15456
- },
15457
- {
15458
- id: "antigravity",
15459
- label: "Antigravity",
15460
- setupCommand: "node9 agents add antigravity",
15461
- hookFile: (h) => path28.join(h, ".gemini", "config", "hooks.json"),
15462
- hookFormat: "matcher",
15463
- hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
15464
- mcpFile: (h) => path28.join(h, ".gemini", "config", "mcp_config.json"),
15465
- present: (h) => exists(path28.join(h, ".gemini", "config", "hooks.json")) || exists(path28.join(h, ".gemini", "antigravity-cli")) || exists(path28.join(h, ".gemini", "antigravity-ide"))
15466
- },
15467
- {
15468
- id: "copilot",
15469
- label: "GitHub Copilot",
15470
- setupCommand: "node9 agents add copilot",
15471
- hookFile: (h) => path28.join(h, ".copilot", "hooks", "node9.json"),
15472
- hookFormat: "flat",
15473
- hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
15474
- mcpFile: (h) => path28.join(h, ".copilot", "mcp-config.json"),
15475
- present: (h) => exists(path28.join(h, ".copilot"))
15476
- },
15477
- {
15478
- id: "cursor",
15479
- label: "Cursor",
15480
- setupCommand: "node9 agents add cursor",
15481
- // MCP-only — no hook file (see note above).
15482
- hookFormat: "flat",
15483
- hookEvents: [],
15484
- mcpFile: (h) => path28.join(h, ".cursor", "mcp.json"),
15485
- present: (h) => exists(path28.join(h, ".cursor", "mcp.json"))
15486
- },
15487
- {
15488
- id: "hermes",
15489
- label: "Hermes Agent",
15490
- setupCommand: "node9 agents add hermes",
15491
- hookFile: (h) => hermesConfigPath(h),
15492
- hookFormat: "yaml",
15493
- hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
15494
- labelPad: 14,
15495
- // 'post_tool_call' is wider than the default
15496
- present: (h) => exists(hermesConfigPath(h))
15497
- },
15498
- {
15499
- // Plugin-shim agents — protected by a node9-authored plugin/extension file
15500
- // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
15501
- id: "opencode",
15502
- label: "OpenCode",
15503
- setupCommand: "node9 agents add opencode",
15504
- hookFormat: "flat",
15505
- hookEvents: [],
15506
- shimFile: (h) => path28.join(h, ".config", "opencode", "plugins", "node9.js"),
15507
- present: (h) => exists(path28.join(h, ".config", "opencode")) || exists(path28.join(h, ".config", "opencode", "plugins", "node9.js"))
15508
- },
15509
- {
15510
- id: "pi",
15511
- label: "Pi",
15512
- setupCommand: "node9 agents add pi",
15513
- hookFormat: "flat",
15514
- hookEvents: [],
15515
- shimFile: (h) => path28.join(h, ".pi", "agent", "extensions", "node9.js"),
15516
- present: (h) => exists(path28.join(h, ".pi", "agent")) || exists(path28.join(h, ".pi", "agent", "extensions", "node9.js"))
15517
- }
15518
- ];
15456
+ });
15457
+ unixServer.on("error", (err2) => {
15458
+ logActivitySocket(`server error: ${err2.message}`);
15459
+ });
15460
+ unixServer.listen(ACTIVITY_SOCKET_PATH2, () => {
15461
+ logActivitySocket(`bound to ${ACTIVITY_SOCKET_PATH2}`);
15462
+ });
15463
+ activitySocketServer = unixServer;
15464
+ }
15465
+ var homeDir, DAEMON_PID_FILE, DECISIONS_FILE, AUDIT_LOG_FILE, TRUST_FILE2, GLOBAL_CONFIG_FILE, CREDENTIALS_FILE, INSIGHT_COUNTS_FILE, pending, sseClients, suggestionTracker, taintStore, sessionTaintStore, insightCounts, _abandonTimer, _hadBrowserClient, _daemonServer, daemonRejectionHandlerRegistered, AUTO_DENY_MS, TRUST_DURATIONS, autoStarted, ACTIVITY_SOCKET_PATH2, ACTIVITY_RING_SIZE, activityRing, LARGE_RESPONSE_RING_SIZE, largeResponseRing, cachedScanResult, cachedScanTs, SCAN_CACHE_TTL_MS, SECRET_KEY_RE, INPUT_PRICE_PER_1M, OUTPUT_PRICE_PER_1M, BYTES_PER_TOKEN, CRITICAL_FORENSIC_CATEGORIES, WRITE_TOOL_NAMES, ACTIVITY_REBIND_MAX_ATTEMPTS, ACTIVITY_REBIND_WINDOW_MS, ACTIVITY_HEALTH_PROBE_MS, activitySocketServer, activityHealthInterval, activityRebindAttempts, activityCircuitTripped;
15466
+ var init_state2 = __esm({
15467
+ "src/daemon/state.ts"() {
15468
+ "use strict";
15469
+ init_daemon();
15470
+ init_suggestion_tracker();
15471
+ init_taint_store();
15472
+ init_session_counters();
15473
+ init_session_history();
15474
+ homeDir = os24.homedir();
15475
+ DAEMON_PID_FILE = path28.join(homeDir, ".node9", "daemon.pid");
15476
+ DECISIONS_FILE = path28.join(homeDir, ".node9", "decisions.json");
15477
+ AUDIT_LOG_FILE = path28.join(homeDir, ".node9", "audit.log");
15478
+ TRUST_FILE2 = path28.join(homeDir, ".node9", "trust.json");
15479
+ GLOBAL_CONFIG_FILE = path28.join(homeDir, ".node9", "config.json");
15480
+ CREDENTIALS_FILE = path28.join(homeDir, ".node9", "credentials.json");
15481
+ INSIGHT_COUNTS_FILE = path28.join(homeDir, ".node9", "insight-counts.json");
15482
+ pending = /* @__PURE__ */ new Map();
15483
+ sseClients = /* @__PURE__ */ new Set();
15484
+ suggestionTracker = new SuggestionTracker(3);
15485
+ taintStore = new TaintStore();
15486
+ sessionTaintStore = new SessionTaintStore();
15487
+ insightCounts = /* @__PURE__ */ new Map();
15488
+ _abandonTimer = null;
15489
+ _hadBrowserClient = false;
15490
+ _daemonServer = null;
15491
+ daemonRejectionHandlerRegistered = false;
15492
+ AUTO_DENY_MS = 12e4;
15493
+ TRUST_DURATIONS = {
15494
+ "30m": 30 * 6e4,
15495
+ "1h": 60 * 6e4,
15496
+ "2h": 2 * 60 * 6e4
15497
+ };
15498
+ autoStarted = process.env.NODE9_AUTO_STARTED === "1";
15499
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path28.join(os24.tmpdir(), "node9-activity.sock");
15500
+ ACTIVITY_RING_SIZE = 100;
15501
+ activityRing = [];
15502
+ LARGE_RESPONSE_RING_SIZE = 20;
15503
+ largeResponseRing = [];
15504
+ cachedScanResult = null;
15505
+ cachedScanTs = 0;
15506
+ SCAN_CACHE_TTL_MS = 5 * 60 * 1e3;
15507
+ SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
15508
+ INPUT_PRICE_PER_1M = 3;
15509
+ OUTPUT_PRICE_PER_1M = 15;
15510
+ BYTES_PER_TOKEN = 4;
15511
+ CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
15512
+ "privilege-escalation",
15513
+ "destructive-op",
15514
+ "eval-of-remote"
15515
+ ]);
15516
+ WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
15517
+ "write",
15518
+ "write_file",
15519
+ "create_file",
15520
+ "edit",
15521
+ "multiedit",
15522
+ "str_replace_based_edit_tool",
15523
+ "replace",
15524
+ "notebook_edit",
15525
+ "notebookedit"
15526
+ ]);
15527
+ ACTIVITY_REBIND_MAX_ATTEMPTS = 5;
15528
+ ACTIVITY_REBIND_WINDOW_MS = 6e4;
15529
+ ACTIVITY_HEALTH_PROBE_MS = 2e3;
15530
+ activitySocketServer = null;
15531
+ activityHealthInterval = null;
15532
+ activityRebindAttempts = [];
15533
+ activityCircuitTripped = false;
15519
15534
  }
15520
15535
  });
15521
15536
 
@@ -16614,9 +16629,95 @@ var init_ship = __esm({
16614
16629
  }
16615
16630
  });
16616
16631
 
16632
+ // src/policy-snapshot/build.ts
16633
+ function buildPolicySnapshot(config, activeShields, overrides) {
16634
+ const p = config.policy;
16635
+ return {
16636
+ mode: config.settings.mode,
16637
+ panicMode: config.settings.panicMode === true,
16638
+ // The proxy expresses shadow/observe as mode === 'observe' (cloud shadowMode
16639
+ // forces it); there's no separate settings flag.
16640
+ shadowMode: config.settings.mode === "observe",
16641
+ activeShields,
16642
+ shieldOverrides: overrides,
16643
+ smartRuleCount: p.smartRules.length,
16644
+ smartRules: p.smartRules.slice(0, MAX_RULES).map((r) => ({
16645
+ name: r.name,
16646
+ tool: r.tool,
16647
+ verdict: r.verdict,
16648
+ reason: r.reason
16649
+ })),
16650
+ egress: {
16651
+ enabled: p.egress.enabled,
16652
+ mode: p.egress.mode,
16653
+ allow: p.egress.allow.slice(0, MAX_EGRESS)
16654
+ },
16655
+ dlpEnabled: p.dlp.enabled,
16656
+ engineVersion: ENGINE_VERSION
16657
+ };
16658
+ }
16659
+ var MAX_RULES, MAX_EGRESS;
16660
+ var init_build = __esm({
16661
+ "src/policy-snapshot/build.ts"() {
16662
+ "use strict";
16663
+ init_dist();
16664
+ MAX_RULES = 500;
16665
+ MAX_EGRESS = 200;
16666
+ }
16667
+ });
16668
+
16669
+ // src/policy-snapshot/ship.ts
16670
+ import http2 from "http";
16671
+ import https3 from "https";
16672
+ import { URL as URL3 } from "url";
16673
+ function policySnapshotUrlFrom(apiUrl) {
16674
+ return apiUrl.endsWith("/policies/sync") ? apiUrl.replace(/\/policies\/sync$/, "/policy/snapshot") : null;
16675
+ }
16676
+ async function shipPolicySnapshot(body, creds) {
16677
+ const url = policySnapshotUrlFrom(creds.apiUrl);
16678
+ if (!url) return false;
16679
+ const payload = JSON.stringify(body);
16680
+ const parsed = new URL3(url);
16681
+ const transport = parsed.protocol === "http:" ? http2 : https3;
16682
+ return new Promise((resolve) => {
16683
+ const req = transport.request(
16684
+ {
16685
+ hostname: parsed.hostname,
16686
+ port: parsed.port ? parseInt(parsed.port, 10) : void 0,
16687
+ path: parsed.pathname + parsed.search,
16688
+ method: "POST",
16689
+ headers: {
16690
+ "Content-Type": "application/json",
16691
+ "Content-Length": Buffer.byteLength(payload),
16692
+ Authorization: `Bearer ${creds.apiKey}`
16693
+ },
16694
+ timeout: 1e4
16695
+ },
16696
+ (res) => {
16697
+ const ok2 = !!res.statusCode && res.statusCode >= 200 && res.statusCode < 300;
16698
+ res.resume();
16699
+ res.on("end", () => resolve(ok2));
16700
+ res.on("error", () => resolve(false));
16701
+ }
16702
+ );
16703
+ req.on("error", () => resolve(false));
16704
+ req.on("timeout", () => {
16705
+ req.destroy();
16706
+ resolve(false);
16707
+ });
16708
+ req.write(payload);
16709
+ req.end();
16710
+ });
16711
+ }
16712
+ var init_ship2 = __esm({
16713
+ "src/policy-snapshot/ship.ts"() {
16714
+ "use strict";
16715
+ }
16716
+ });
16717
+
16617
16718
  // src/daemon/sync.ts
16618
16719
  import fs32 from "fs";
16619
- import https3 from "https";
16720
+ import https4 from "https";
16620
16721
  import os29 from "os";
16621
16722
  import path31 from "path";
16622
16723
  function emptySignals3() {
@@ -16699,7 +16800,7 @@ function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
16699
16800
  };
16700
16801
  if (ifNoneMatch) headers["If-None-Match"] = `"${ifNoneMatch}"`;
16701
16802
  return new Promise((resolve, reject) => {
16702
- const req = https3.request(
16803
+ const req = https4.request(
16703
16804
  {
16704
16805
  hostname: parsed.hostname,
16705
16806
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16779,6 +16880,9 @@ async function syncOnce() {
16779
16880
  if (process.env.NODE9_POSTURE_DISABLE !== "1") {
16780
16881
  void pushPostureSnapshot(creds);
16781
16882
  }
16883
+ if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
16884
+ void pushPolicySnapshot(creds);
16885
+ }
16782
16886
  }
16783
16887
  async function pushBlastSnapshot(creds) {
16784
16888
  try {
@@ -16788,7 +16892,7 @@ async function pushBlastSnapshot(creds) {
16788
16892
  if (!blastUrl) return;
16789
16893
  const parsed = new URL(blastUrl);
16790
16894
  await new Promise((resolve) => {
16791
- const req = https3.request(
16895
+ const req = https4.request(
16792
16896
  {
16793
16897
  hostname: parsed.hostname,
16794
16898
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16825,6 +16929,29 @@ async function pushPostureSnapshot(creds) {
16825
16929
  } catch {
16826
16930
  }
16827
16931
  }
16932
+ async function pushPolicySnapshot(creds) {
16933
+ try {
16934
+ const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
16935
+ await shipPolicySnapshot(body, creds);
16936
+ } catch {
16937
+ }
16938
+ }
16939
+ async function runPolicyPush() {
16940
+ const creds = readCredentials();
16941
+ if (!creds) {
16942
+ return {
16943
+ ok: false,
16944
+ reason: "No API key configured. Add credentials with: node9 login"
16945
+ };
16946
+ }
16947
+ try {
16948
+ const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
16949
+ const sent = await shipPolicySnapshot(body, creds);
16950
+ return sent ? { ok: true } : { ok: false, reason: "Push failed (network or server error)" };
16951
+ } catch (e) {
16952
+ return { ok: false, reason: e instanceof Error ? e.message : String(e) };
16953
+ }
16954
+ }
16828
16955
  async function pushScanSnapshot(creds) {
16829
16956
  try {
16830
16957
  const tick = await tickScanWatcher();
@@ -16842,7 +16969,7 @@ async function pushScanSnapshot(creds) {
16842
16969
  const parsed = new URL(scanUrl);
16843
16970
  let posted = false;
16844
16971
  await new Promise((resolve) => {
16845
- const req = https3.request(
16972
+ const req = https4.request(
16846
16973
  {
16847
16974
  hostname: parsed.hostname,
16848
16975
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16892,6 +17019,9 @@ async function runCloudSync() {
16892
17019
  if (process.env.NODE9_POSTURE_DISABLE !== "1") {
16893
17020
  void pushPostureSnapshot(creds);
16894
17021
  }
17022
+ if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
17023
+ void pushPolicySnapshot(creds);
17024
+ }
16895
17025
  };
16896
17026
  try {
16897
17027
  const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
@@ -16978,6 +17108,9 @@ var init_sync = __esm({
16978
17108
  init_blast();
16979
17109
  init_posture();
16980
17110
  init_ship();
17111
+ init_build();
17112
+ init_ship2();
17113
+ init_shields();
16981
17114
  init_dist();
16982
17115
  init_scan_watermark();
16983
17116
  init_state2();
@@ -17451,7 +17584,7 @@ var init_mcp_tools = __esm({
17451
17584
  });
17452
17585
 
17453
17586
  // src/daemon/server.ts
17454
- import http2 from "http";
17587
+ import http3 from "http";
17455
17588
  import fs36 from "fs";
17456
17589
  import path35 from "path";
17457
17590
  import os33 from "os";
@@ -17486,7 +17619,7 @@ function startDaemon() {
17486
17619
  }
17487
17620
  resetIdleTimer();
17488
17621
  const allowedHosts = /* @__PURE__ */ new Set([`127.0.0.1:${DAEMON_PORT}`, `localhost:${DAEMON_PORT}`]);
17489
- const server = http2.createServer(async (req, res) => {
17622
+ const server = http3.createServer(async (req, res) => {
17490
17623
  const host = req.headers.host ?? "";
17491
17624
  if (!allowedHosts.has(host)) {
17492
17625
  res.writeHead(421, { "Content-Type": "text/plain" });
@@ -18720,11 +18853,11 @@ __export(tail_exports, {
18720
18853
  shortenPathSummary: () => shortenPathSummary,
18721
18854
  startTail: () => startTail
18722
18855
  });
18723
- import http3 from "http";
18856
+ import http4 from "http";
18724
18857
  import chalk35 from "chalk";
18725
18858
  import fs64 from "fs";
18726
- import os54 from "os";
18727
- import path61 from "path";
18859
+ import os55 from "os";
18860
+ import path62 from "path";
18728
18861
  import readline6 from "readline";
18729
18862
  import { spawn as spawn8 } from "child_process";
18730
18863
  function shortenPathSummary(s) {
@@ -18748,18 +18881,18 @@ function getModelContextLimit(model) {
18748
18881
  return 2e5;
18749
18882
  }
18750
18883
  function readSessionUsage() {
18751
- const projectsDir = path61.join(os54.homedir(), ".claude", "projects");
18884
+ const projectsDir = path62.join(os55.homedir(), ".claude", "projects");
18752
18885
  if (!fs64.existsSync(projectsDir)) return null;
18753
18886
  let latestFile = null;
18754
18887
  let latestMtime = 0;
18755
18888
  try {
18756
18889
  for (const dir of fs64.readdirSync(projectsDir)) {
18757
- const dirPath = path61.join(projectsDir, dir);
18890
+ const dirPath = path62.join(projectsDir, dir);
18758
18891
  try {
18759
18892
  if (!fs64.statSync(dirPath).isDirectory()) continue;
18760
18893
  for (const file of fs64.readdirSync(dirPath)) {
18761
18894
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
18762
- const filePath = path61.join(dirPath, file);
18895
+ const filePath = path62.join(dirPath, file);
18763
18896
  try {
18764
18897
  const mtime = fs64.statSync(filePath).mtimeMs;
18765
18898
  if (mtime > latestMtime) {
@@ -18837,7 +18970,7 @@ function formatBase(activity) {
18837
18970
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
18838
18971
  const icon = getIcon(activity.tool);
18839
18972
  const toolName = activity.tool.slice(0, 16).padEnd(16);
18840
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os54.homedir(), "~");
18973
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os55.homedir(), "~");
18841
18974
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
18842
18975
  return `${chalk35.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk35.white.bold(toolName)} ${chalk35.dim(argsPreview)}`;
18843
18976
  }
@@ -18919,7 +19052,7 @@ function postDecisionHttp(id, decision, authToken, port, opts) {
18919
19052
  if (opts?.trustDuration) bodyObj.trustDuration = opts.trustDuration;
18920
19053
  if (opts?.reason) bodyObj.reason = opts.reason;
18921
19054
  const body = JSON.stringify(bodyObj);
18922
- const req = http3.request(
19055
+ const req = http4.request(
18923
19056
  {
18924
19057
  hostname: "127.0.0.1",
18925
19058
  port,
@@ -19034,7 +19167,7 @@ function buildRecoveryCardLines(req) {
19034
19167
  ];
19035
19168
  }
19036
19169
  function readApproversFromDisk() {
19037
- const configPath = path61.join(os54.homedir(), ".node9", "config.json");
19170
+ const configPath = path62.join(os55.homedir(), ".node9", "config.json");
19038
19171
  try {
19039
19172
  const raw = JSON.parse(fs64.readFileSync(configPath, "utf-8"));
19040
19173
  const settings = raw.settings ?? {};
@@ -19052,7 +19185,7 @@ function approverStatusLine() {
19052
19185
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
19053
19186
  }
19054
19187
  function toggleApprover(channel) {
19055
- const configPath = path61.join(os54.homedir(), ".node9", "config.json");
19188
+ const configPath = path62.join(os55.homedir(), ".node9", "config.json");
19056
19189
  try {
19057
19190
  const raw = JSON.parse(fs64.readFileSync(configPath, "utf-8"));
19058
19191
  const settings = raw.settings ?? {};
@@ -19070,7 +19203,7 @@ async function startTail(options = {}) {
19070
19203
  const port = await ensureDaemon();
19071
19204
  if (options.clear) {
19072
19205
  const result = await new Promise((resolve) => {
19073
- const req2 = http3.request(
19206
+ const req2 = http4.request(
19074
19207
  { method: "POST", hostname: "127.0.0.1", port, path: "/events/clear" },
19075
19208
  (res) => {
19076
19209
  const status = res.statusCode ?? 0;
@@ -19233,7 +19366,7 @@ async function startTail(options = {}) {
19233
19366
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
19234
19367
  try {
19235
19368
  fs64.appendFileSync(
19236
- path61.join(os54.homedir(), ".node9", "hook-debug.log"),
19369
+ path62.join(os55.homedir(), ".node9", "hook-debug.log"),
19237
19370
  `[tail] POST /decision failed: ${String(err2)}
19238
19371
  `
19239
19372
  );
@@ -19297,7 +19430,7 @@ async function startTail(options = {}) {
19297
19430
  };
19298
19431
  process.stdin.on("keypress", onKeypress);
19299
19432
  }
19300
- const auditLog = path61.join(os54.homedir(), ".node9", "audit.log");
19433
+ const auditLog = path62.join(os55.homedir(), ".node9", "audit.log");
19301
19434
  try {
19302
19435
  const unackedDlp = fs64.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
19303
19436
  if (unackedDlp > 0) {
@@ -19353,7 +19486,7 @@ async function startTail(options = {}) {
19353
19486
  }, STALL_THRESHOLD_MS / 2);
19354
19487
  stallWatchdog.unref();
19355
19488
  const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
19356
- const req = http3.get(
19489
+ const req = http4.get(
19357
19490
  sseUrl,
19358
19491
  {
19359
19492
  headers: authToken ? { "X-Node9-Internal": authToken } : {}
@@ -19530,7 +19663,7 @@ var init_tail = __esm({
19530
19663
  "use strict";
19531
19664
  init_daemon2();
19532
19665
  init_daemon();
19533
- PID_FILE = path61.join(os54.homedir(), ".node9", "daemon.pid");
19666
+ PID_FILE = path62.join(os55.homedir(), ".node9", "daemon.pid");
19534
19667
  ICONS = {
19535
19668
  bash: "\u{1F4BB}",
19536
19669
  shell: "\u{1F4BB}",
@@ -19579,9 +19712,9 @@ __export(hud_exports, {
19579
19712
  renderEnvironmentLine: () => renderEnvironmentLine
19580
19713
  });
19581
19714
  import fs65 from "fs";
19582
- import path62 from "path";
19583
- import os55 from "os";
19584
- import http4 from "http";
19715
+ import path63 from "path";
19716
+ import os56 from "os";
19717
+ import http5 from "http";
19585
19718
  async function readStdin() {
19586
19719
  const chunks = [];
19587
19720
  for await (const chunk2 of process.stdin) {
@@ -19599,7 +19732,7 @@ function queryDaemon() {
19599
19732
  return new Promise((resolve) => {
19600
19733
  const timeout = setTimeout(() => resolve(null), 50);
19601
19734
  try {
19602
- const req = http4.get(
19735
+ const req = http5.get(
19603
19736
  `http://${DAEMON_HOST}:${DAEMON_PORT}/status`,
19604
19737
  { timeout: 50 },
19605
19738
  (res) => {
@@ -19684,7 +19817,7 @@ function countRulesInDir(rulesDir) {
19684
19817
  try {
19685
19818
  for (const entry of fs65.readdirSync(rulesDir, { withFileTypes: true })) {
19686
19819
  if (entry.isDirectory()) {
19687
- count += countRulesInDir(path62.join(rulesDir, entry.name));
19820
+ count += countRulesInDir(path63.join(rulesDir, entry.name));
19688
19821
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
19689
19822
  count++;
19690
19823
  }
@@ -19695,46 +19828,46 @@ function countRulesInDir(rulesDir) {
19695
19828
  }
19696
19829
  function isSamePath(a, b) {
19697
19830
  try {
19698
- return path62.resolve(a) === path62.resolve(b);
19831
+ return path63.resolve(a) === path63.resolve(b);
19699
19832
  } catch {
19700
19833
  return false;
19701
19834
  }
19702
19835
  }
19703
19836
  function countConfigs(cwd) {
19704
- const homeDir2 = os55.homedir();
19705
- const claudeDir = path62.join(homeDir2, ".claude");
19837
+ const homeDir2 = os56.homedir();
19838
+ const claudeDir = path63.join(homeDir2, ".claude");
19706
19839
  let claudeMdCount = 0;
19707
19840
  let rulesCount = 0;
19708
19841
  let hooksCount = 0;
19709
19842
  const userMcpServers = /* @__PURE__ */ new Set();
19710
19843
  const projectMcpServers = /* @__PURE__ */ new Set();
19711
- if (fs65.existsSync(path62.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
19712
- rulesCount += countRulesInDir(path62.join(claudeDir, "rules"));
19713
- const userSettings = path62.join(claudeDir, "settings.json");
19844
+ if (fs65.existsSync(path63.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
19845
+ rulesCount += countRulesInDir(path63.join(claudeDir, "rules"));
19846
+ const userSettings = path63.join(claudeDir, "settings.json");
19714
19847
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
19715
19848
  hooksCount += countHooksInFile(userSettings);
19716
- const userClaudeJson = path62.join(homeDir2, ".claude.json");
19849
+ const userClaudeJson = path63.join(homeDir2, ".claude.json");
19717
19850
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
19718
19851
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
19719
19852
  userMcpServers.delete(name);
19720
19853
  }
19721
19854
  if (cwd) {
19722
- if (fs65.existsSync(path62.join(cwd, "CLAUDE.md"))) claudeMdCount++;
19723
- if (fs65.existsSync(path62.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
19724
- const projectClaudeDir = path62.join(cwd, ".claude");
19855
+ if (fs65.existsSync(path63.join(cwd, "CLAUDE.md"))) claudeMdCount++;
19856
+ if (fs65.existsSync(path63.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
19857
+ const projectClaudeDir = path63.join(cwd, ".claude");
19725
19858
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
19726
19859
  if (!overlapsUserScope) {
19727
- if (fs65.existsSync(path62.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
19728
- rulesCount += countRulesInDir(path62.join(projectClaudeDir, "rules"));
19729
- const projSettings = path62.join(projectClaudeDir, "settings.json");
19860
+ if (fs65.existsSync(path63.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
19861
+ rulesCount += countRulesInDir(path63.join(projectClaudeDir, "rules"));
19862
+ const projSettings = path63.join(projectClaudeDir, "settings.json");
19730
19863
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
19731
19864
  hooksCount += countHooksInFile(projSettings);
19732
19865
  }
19733
- if (fs65.existsSync(path62.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19734
- const localSettings = path62.join(projectClaudeDir, "settings.local.json");
19866
+ if (fs65.existsSync(path63.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19867
+ const localSettings = path63.join(projectClaudeDir, "settings.local.json");
19735
19868
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
19736
19869
  hooksCount += countHooksInFile(localSettings);
19737
- const mcpJsonServers = getMcpServerNames(path62.join(cwd, ".mcp.json"));
19870
+ const mcpJsonServers = getMcpServerNames(path63.join(cwd, ".mcp.json"));
19738
19871
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
19739
19872
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
19740
19873
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -19767,7 +19900,7 @@ function readActiveShieldsHud() {
19767
19900
  return shieldsCache.value;
19768
19901
  }
19769
19902
  try {
19770
- const shieldsPath = path62.join(os55.homedir(), ".node9", "shields.json");
19903
+ const shieldsPath = path63.join(os56.homedir(), ".node9", "shields.json");
19771
19904
  if (!fs65.existsSync(shieldsPath)) {
19772
19905
  shieldsCache = { value: [], ts: now };
19773
19906
  return [];
@@ -19874,9 +20007,9 @@ function renderContextLine(stdin) {
19874
20007
  async function main() {
19875
20008
  try {
19876
20009
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
19877
- if (fs65.existsSync(path62.join(os55.homedir(), ".node9", "hud-debug"))) {
20010
+ if (fs65.existsSync(path63.join(os56.homedir(), ".node9", "hud-debug"))) {
19878
20011
  try {
19879
- const logPath = path62.join(os55.homedir(), ".node9", "hud-debug.log");
20012
+ const logPath = path63.join(os56.homedir(), ".node9", "hud-debug.log");
19880
20013
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
19881
20014
  let size = 0;
19882
20015
  try {
@@ -19905,8 +20038,8 @@ async function main() {
19905
20038
  try {
19906
20039
  const cwd = stdin.cwd ?? process.cwd();
19907
20040
  for (const configPath of [
19908
- path62.join(cwd, "node9.config.json"),
19909
- path62.join(os55.homedir(), ".node9", "config.json")
20041
+ path63.join(cwd, "node9.config.json"),
20042
+ path63.join(os56.homedir(), ".node9", "config.json")
19910
20043
  ]) {
19911
20044
  if (!fs65.existsSync(configPath)) continue;
19912
20045
  const cfg = JSON.parse(fs65.readFileSync(configPath, "utf-8"));
@@ -19953,12 +20086,45 @@ var init_hud = __esm({
19953
20086
  // src/cli.ts
19954
20087
  init_core();
19955
20088
  init_setup();
19956
- init_daemon2();
19957
20089
  import { Command } from "commander";
20090
+
20091
+ // src/agent-teardowns.ts
20092
+ init_setup();
20093
+ var AGENT_TEARDOWNS = [
20094
+ { id: "claude", label: "Claude", fn: teardownClaude },
20095
+ { id: "gemini", label: "Gemini", fn: teardownGemini },
20096
+ { id: "codex", label: "Codex", fn: teardownCodex },
20097
+ { id: "cursor", label: "Cursor", fn: teardownCursor },
20098
+ { id: "windsurf", label: "Windsurf", fn: teardownWindsurf },
20099
+ { id: "vscode", label: "VSCode", fn: teardownVSCode },
20100
+ { id: "hermes", label: "Hermes", fn: teardownHermes },
20101
+ { id: "antigravity", label: "Antigravity", fn: teardownAntigravity, aliases: ["agy"] },
20102
+ { id: "copilot", label: "Copilot", fn: teardownCopilot },
20103
+ { id: "hud", label: "HUD", fn: teardownHud },
20104
+ {
20105
+ id: "claudedesktop",
20106
+ label: "Claude Desktop",
20107
+ fn: teardownClaudeDesktop,
20108
+ aliases: ["claude-desktop"]
20109
+ },
20110
+ { id: "opencode", label: "OpenCode", fn: teardownOpencode },
20111
+ { id: "pi", label: "Pi", fn: teardownPi }
20112
+ ];
20113
+ function resolveAgentTeardown(target) {
20114
+ const t = target.trim().toLowerCase();
20115
+ return AGENT_TEARDOWNS.find((a) => a.id === t || a.aliases?.includes(t));
20116
+ }
20117
+ function agentTeardownTargets() {
20118
+ return AGENT_TEARDOWNS.flatMap((a) => [a.id, ...a.aliases ?? []]);
20119
+ }
20120
+
20121
+ // src/cli.ts
20122
+ init_agent_wiring();
20123
+ init_daemon2();
19958
20124
  import chalk36 from "chalk";
19959
20125
  import fs66 from "fs";
19960
- import path63 from "path";
19961
- import os56 from "os";
20126
+ import path64 from "path";
20127
+ import os57 from "os";
19962
20128
  import { spawn as spawn9 } from "child_process";
19963
20129
  import { confirm as confirm2 } from "@inquirer/prompts";
19964
20130
 
@@ -21589,6 +21755,8 @@ function registerLogCommand(program2) {
21589
21755
  init_shields();
21590
21756
  import chalk10 from "chalk";
21591
21757
  import fs46 from "fs";
21758
+ import path44 from "path";
21759
+ import os40 from "os";
21592
21760
 
21593
21761
  // src/shields/build.ts
21594
21762
  function escapeRegex(s) {
@@ -21718,10 +21886,10 @@ init_audit();
21718
21886
  init_config();
21719
21887
 
21720
21888
  // src/utils/https-fetch.ts
21721
- import https4 from "https";
21889
+ import https5 from "https";
21722
21890
  function httpsFetch(url) {
21723
21891
  return new Promise((resolve, reject) => {
21724
- https4.get(url, (res) => {
21892
+ https5.get(url, (res) => {
21725
21893
  if (res.statusCode !== 200) {
21726
21894
  reject(new Error(`HTTP ${String(res.statusCode)} for ${url}`));
21727
21895
  res.resume();
@@ -21737,6 +21905,22 @@ function httpsFetch(url) {
21737
21905
 
21738
21906
  // src/cli/commands/shield.ts
21739
21907
  var COMMUNITY_INDEX_URL = "https://raw.githubusercontent.com/node9ai/node9-proxy/main/shields/community/index.json";
21908
+ function readCloudShields() {
21909
+ const out = /* @__PURE__ */ new Set();
21910
+ try {
21911
+ const file = path44.join(os40.homedir(), ".node9", "rules-cache.json");
21912
+ const raw = JSON.parse(fs46.readFileSync(file, "utf-8"));
21913
+ for (const r of raw.rules ?? []) {
21914
+ const rule = r;
21915
+ const fromSource = rule.source?.startsWith("SHIELD:") ? rule.source.slice("SHIELD:".length).toLowerCase() : void 0;
21916
+ const fromDesc = !fromSource && rule.description ? /\b([a-z0-9-]+)\s+shield/i.exec(rule.description)?.[1]?.toLowerCase() : void 0;
21917
+ const name = fromSource ?? fromDesc;
21918
+ if (name) out.add(name);
21919
+ }
21920
+ } catch {
21921
+ }
21922
+ return out;
21923
+ }
21740
21924
  function registerShieldCommand(program2) {
21741
21925
  const shieldCmd = program2.command("shield").description("Manage pre-packaged security shield templates");
21742
21926
  shieldCmd.command("enable <service>").description("Enable a security shield for a specific service").action((service) => {
@@ -21823,10 +22007,17 @@ function registerShieldCommand(program2) {
21823
22007
  return;
21824
22008
  }
21825
22009
  const active = new Set(readActiveShields());
22010
+ const cloud = readCloudShields();
21826
22011
  console.log(chalk10.bold("\n\u{1F6E1}\uFE0F Available Shields\n"));
22012
+ console.log(chalk10.gray(" \u25CF local \xB7 \u2601 cloud\n"));
21827
22013
  for (const shield of listShields()) {
21828
- const status = active.has(shield.name) ? chalk10.green("\u25CF enabled") : chalk10.gray("\u25CB disabled");
21829
- console.log(` ${status} ${chalk10.cyan(shield.name.padEnd(12))} ${shield.description}`);
22014
+ const isLocal = active.has(shield.name);
22015
+ const isCloud = cloud.has(shield.name);
22016
+ const status = isLocal && isCloud ? chalk10.green("\u25CF\u2601 enabled ") : isLocal ? chalk10.green("\u25CF enabled ") : isCloud ? chalk10.cyan("\u2601 cloud ") : chalk10.gray("\u25CB disabled");
22017
+ const via = isCloud && !isLocal ? chalk10.gray(" (via dashboard)") : "";
22018
+ console.log(
22019
+ ` ${status} ${chalk10.cyan(shield.name.padEnd(12))} ${shield.description}${via}`
22020
+ );
21830
22021
  if (shield.aliases.length > 0)
21831
22022
  console.log(chalk10.gray(` aliases: ${shield.aliases.join(", ")}`));
21832
22023
  }
@@ -22159,12 +22350,12 @@ init_config();
22159
22350
  init_agent_wiring();
22160
22351
  import chalk11 from "chalk";
22161
22352
  import fs47 from "fs";
22162
- import path44 from "path";
22163
- import os40 from "os";
22353
+ import path45 from "path";
22354
+ import os41 from "os";
22164
22355
  import { execSync } from "child_process";
22165
22356
  function registerDoctorCommand(program2, version2) {
22166
22357
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
22167
- const homeDir2 = os40.homedir();
22358
+ const homeDir2 = os41.homedir();
22168
22359
  let failures = 0;
22169
22360
  function pass(msg) {
22170
22361
  console.log(chalk11.green(" \u2705 ") + msg);
@@ -22210,7 +22401,7 @@ function registerDoctorCommand(program2, version2) {
22210
22401
  );
22211
22402
  }
22212
22403
  section("Configuration");
22213
- const globalConfigPath = path44.join(homeDir2, ".node9", "config.json");
22404
+ const globalConfigPath = path45.join(homeDir2, ".node9", "config.json");
22214
22405
  if (fs47.existsSync(globalConfigPath)) {
22215
22406
  try {
22216
22407
  JSON.parse(fs47.readFileSync(globalConfigPath, "utf-8"));
@@ -22221,7 +22412,7 @@ function registerDoctorCommand(program2, version2) {
22221
22412
  } else {
22222
22413
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
22223
22414
  }
22224
- const projectConfigPath = path44.join(process.cwd(), "node9.config.json");
22415
+ const projectConfigPath = path45.join(process.cwd(), "node9.config.json");
22225
22416
  if (fs47.existsSync(projectConfigPath)) {
22226
22417
  try {
22227
22418
  JSON.parse(fs47.readFileSync(projectConfigPath, "utf-8"));
@@ -22233,7 +22424,7 @@ function registerDoctorCommand(program2, version2) {
22233
22424
  );
22234
22425
  }
22235
22426
  }
22236
- const credsPath = path44.join(homeDir2, ".node9", "credentials.json");
22427
+ const credsPath = path45.join(homeDir2, ".node9", "credentials.json");
22237
22428
  if (fs47.existsSync(credsPath)) {
22238
22429
  pass("Cloud credentials found (~/.node9/credentials.json)");
22239
22430
  } else {
@@ -22278,7 +22469,7 @@ function registerDoctorCommand(program2, version2) {
22278
22469
  try {
22279
22470
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
22280
22471
  const cfg = getConfig();
22281
- const creds = fs47.existsSync(path44.join(os40.homedir(), ".node9", "credentials.json"));
22472
+ const creds = fs47.existsSync(path45.join(os41.homedir(), ".node9", "credentials.json"));
22282
22473
  if (!creds) {
22283
22474
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
22284
22475
  } else if (!cfg.settings.approvers.cloud) {
@@ -22329,8 +22520,8 @@ function registerDoctorCommand(program2, version2) {
22329
22520
  // src/cli/commands/audit.ts
22330
22521
  import chalk12 from "chalk";
22331
22522
  import fs48 from "fs";
22332
- import path45 from "path";
22333
- import os41 from "os";
22523
+ import path46 from "path";
22524
+ import os42 from "os";
22334
22525
  function formatRelativeTime(timestamp) {
22335
22526
  const diff = Date.now() - new Date(timestamp).getTime();
22336
22527
  const sec = Math.floor(diff / 1e3);
@@ -22343,7 +22534,7 @@ function formatRelativeTime(timestamp) {
22343
22534
  }
22344
22535
  function registerAuditCommand(program2) {
22345
22536
  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) => {
22346
- const logPath = path45.join(os41.homedir(), ".node9", "audit.log");
22537
+ const logPath = path46.join(os42.homedir(), ".node9", "audit.log");
22347
22538
  if (!fs48.existsSync(logPath)) {
22348
22539
  console.log(
22349
22540
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
@@ -22410,8 +22601,8 @@ init_costSync();
22410
22601
  init_litellm();
22411
22602
  init_cost_codex();
22412
22603
  import fs49 from "fs";
22413
- import os42 from "os";
22414
- import path46 from "path";
22604
+ import os43 from "os";
22605
+ import path47 from "path";
22415
22606
  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;
22416
22607
  function buildTestTimestamps(allEntries) {
22417
22608
  const testTs = /* @__PURE__ */ new Set();
@@ -22539,7 +22730,7 @@ function freezeClaudeCost(acc) {
22539
22730
  };
22540
22731
  }
22541
22732
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22542
- const projPath = path46.join(projectsDir, proj);
22733
+ const projPath = path47.join(projectsDir, proj);
22543
22734
  let files;
22544
22735
  try {
22545
22736
  const stat = fs49.statSync(projPath);
@@ -22550,7 +22741,7 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22550
22741
  }
22551
22742
  const startMs = start.getTime();
22552
22743
  for (const file of files) {
22553
- const filePath = path46.join(projPath, file);
22744
+ const filePath = path47.join(projPath, file);
22554
22745
  try {
22555
22746
  if (fs49.statSync(filePath).mtimeMs < startMs) continue;
22556
22747
  } catch {
@@ -22680,28 +22871,28 @@ function listCodexSessionFiles2(sessionsBase) {
22680
22871
  if (!fs49.existsSync(sessionsBase)) return jsonlFiles;
22681
22872
  try {
22682
22873
  for (const year of fs49.readdirSync(sessionsBase)) {
22683
- const yearPath = path46.join(sessionsBase, year);
22874
+ const yearPath = path47.join(sessionsBase, year);
22684
22875
  try {
22685
22876
  if (!fs49.statSync(yearPath).isDirectory()) continue;
22686
22877
  } catch {
22687
22878
  continue;
22688
22879
  }
22689
22880
  for (const month of fs49.readdirSync(yearPath)) {
22690
- const monthPath = path46.join(yearPath, month);
22881
+ const monthPath = path47.join(yearPath, month);
22691
22882
  try {
22692
22883
  if (!fs49.statSync(monthPath).isDirectory()) continue;
22693
22884
  } catch {
22694
22885
  continue;
22695
22886
  }
22696
22887
  for (const day of fs49.readdirSync(monthPath)) {
22697
- const dayPath = path46.join(monthPath, day);
22888
+ const dayPath = path47.join(monthPath, day);
22698
22889
  try {
22699
22890
  if (!fs49.statSync(dayPath).isDirectory()) continue;
22700
22891
  } catch {
22701
22892
  continue;
22702
22893
  }
22703
22894
  for (const file of fs49.readdirSync(dayPath)) {
22704
- if (file.endsWith(".jsonl")) jsonlFiles.push(path46.join(dayPath, file));
22895
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path47.join(dayPath, file));
22705
22896
  }
22706
22897
  }
22707
22898
  }
@@ -22827,7 +23018,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
22827
23018
  return out;
22828
23019
  }
22829
23020
  for (const proj of dirs) {
22830
- const chatsDir = path46.join(geminiTmpDir2, proj, "chats");
23021
+ const chatsDir = path47.join(geminiTmpDir2, proj, "chats");
22831
23022
  let files;
22832
23023
  try {
22833
23024
  if (!fs49.statSync(chatsDir).isDirectory()) continue;
@@ -22837,7 +23028,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
22837
23028
  }
22838
23029
  for (const f of files) {
22839
23030
  if (!f.endsWith(".jsonl")) continue;
22840
- out.push({ projectKey: proj, file: path46.join(chatsDir, f) });
23031
+ out.push({ projectKey: proj, file: path47.join(chatsDir, f) });
22841
23032
  }
22842
23033
  }
22843
23034
  return out;
@@ -22852,10 +23043,10 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
22852
23043
  }
22853
23044
  function aggregateReportFromAudit(period, opts = {}) {
22854
23045
  const now = opts.now ?? /* @__PURE__ */ new Date();
22855
- const auditLogPath = opts.auditLogPath ?? path46.join(os42.homedir(), ".node9", "audit.log");
22856
- const claudeProjectsDir = opts.claudeProjectsDir ?? path46.join(os42.homedir(), ".claude", "projects");
22857
- const codexSessionsDir2 = opts.codexSessionsDir ?? path46.join(os42.homedir(), ".codex", "sessions");
22858
- const geminiTmpDir2 = opts.geminiTmpDir ?? path46.join(os42.homedir(), ".gemini", "tmp");
23046
+ const auditLogPath = opts.auditLogPath ?? path47.join(os43.homedir(), ".node9", "audit.log");
23047
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path47.join(os43.homedir(), ".claude", "projects");
23048
+ const codexSessionsDir2 = opts.codexSessionsDir ?? path47.join(os43.homedir(), ".codex", "sessions");
23049
+ const geminiTmpDir2 = opts.geminiTmpDir ?? path47.join(os43.homedir(), ".gemini", "tmp");
22859
23050
  const hasAuditFile = fs49.existsSync(auditLogPath);
22860
23051
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
22861
23052
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
@@ -23556,8 +23747,8 @@ init_daemon();
23556
23747
  init_agent_wiring();
23557
23748
  import chalk15 from "chalk";
23558
23749
  import fs50 from "fs";
23559
- import path47 from "path";
23560
- import os43 from "os";
23750
+ import path48 from "path";
23751
+ import os44 from "os";
23561
23752
  function printAgentSection(label2, hookPairs, wrapped) {
23562
23753
  console.log(chalk15.bold(` ${label2}`));
23563
23754
  for (const { name, present } of hookPairs) {
@@ -23611,8 +23802,8 @@ function registerStatusCommand(program2) {
23611
23802
  console.log("");
23612
23803
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
23613
23804
  console.log(` Mode: ${modeLabel}`);
23614
- const projectConfig = path47.join(process.cwd(), "node9.config.json");
23615
- const globalConfig = path47.join(os43.homedir(), ".node9", "config.json");
23805
+ const projectConfig = path48.join(process.cwd(), "node9.config.json");
23806
+ const globalConfig = path48.join(os44.homedir(), ".node9", "config.json");
23616
23807
  console.log(
23617
23808
  ` Local: ${fs50.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
23618
23809
  );
@@ -23624,7 +23815,7 @@ function registerStatusCommand(program2) {
23624
23815
  ` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
23625
23816
  );
23626
23817
  }
23627
- const wiring = getAgentWiring(os43.homedir()).filter((a) => a.present);
23818
+ const wiring = getAgentWiring(os44.homedir()).filter((a) => a.present);
23628
23819
  if (wiring.length > 0) {
23629
23820
  console.log("");
23630
23821
  console.log(chalk15.bold(" Agent Wiring:"));
@@ -23664,9 +23855,9 @@ init_shields();
23664
23855
  init_service();
23665
23856
  import chalk16 from "chalk";
23666
23857
  import fs51 from "fs";
23667
- import path48 from "path";
23668
- import os44 from "os";
23669
- import https5 from "https";
23858
+ import path49 from "path";
23859
+ import os45 from "os";
23860
+ import https6 from "https";
23670
23861
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
23671
23862
  function buildTelemetryPayload(agents, firstInstall) {
23672
23863
  return {
@@ -23680,7 +23871,7 @@ function buildTelemetryPayload(agents, firstInstall) {
23680
23871
  function fireTelemetryPing(agents, firstInstall) {
23681
23872
  try {
23682
23873
  const body = JSON.stringify(buildTelemetryPayload(agents, firstInstall));
23683
- const req = https5.request(
23874
+ const req = https6.request(
23684
23875
  {
23685
23876
  hostname: "api.node9.ai",
23686
23877
  path: "/api/v1/telemetry",
@@ -23751,7 +23942,7 @@ function registerInitCommand(program2) {
23751
23942
  }
23752
23943
  console.log("");
23753
23944
  }
23754
- const configPath = path48.join(os44.homedir(), ".node9", "config.json");
23945
+ const configPath = path49.join(os45.homedir(), ".node9", "config.json");
23755
23946
  const isFirstInstall = !fs51.existsSync(configPath);
23756
23947
  if (fs51.existsSync(configPath) && !options.force) {
23757
23948
  try {
@@ -23773,7 +23964,7 @@ function registerInitCommand(program2) {
23773
23964
  ...DEFAULT_CONFIG,
23774
23965
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
23775
23966
  };
23776
- const dir = path48.dirname(configPath);
23967
+ const dir = path49.dirname(configPath);
23777
23968
  if (!fs51.existsSync(dir)) fs51.mkdirSync(dir, { recursive: true });
23778
23969
  fs51.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
23779
23970
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
@@ -23880,7 +24071,7 @@ function registerInitCommand(program2) {
23880
24071
  }
23881
24072
 
23882
24073
  // src/cli/commands/undo.ts
23883
- import path49 from "path";
24074
+ import path50 from "path";
23884
24075
  import chalk18 from "chalk";
23885
24076
 
23886
24077
  // src/tui/undo-navigator.ts
@@ -24039,7 +24230,7 @@ function findMatchingCwd(startDir, history) {
24039
24230
  let dir = startDir;
24040
24231
  while (true) {
24041
24232
  if (cwds.has(dir)) return dir;
24042
- const parent = path49.dirname(dir);
24233
+ const parent = path50.dirname(dir);
24043
24234
  if (parent === dir) return null;
24044
24235
  dir = parent;
24045
24236
  }
@@ -24675,8 +24866,8 @@ function registerMcpGatewayCommand(program2) {
24675
24866
  // src/mcp-server/index.ts
24676
24867
  import readline5 from "readline";
24677
24868
  import fs53 from "fs";
24678
- import os46 from "os";
24679
- import path51 from "path";
24869
+ import os47 from "os";
24870
+ import path52 from "path";
24680
24871
  import { spawnSync as spawnSync4 } from "child_process";
24681
24872
  init_core();
24682
24873
  init_daemon();
@@ -24684,8 +24875,8 @@ init_shields();
24684
24875
 
24685
24876
  // src/auth/egress-config.ts
24686
24877
  import fs52 from "fs";
24687
- import os45 from "os";
24688
- import path50 from "path";
24878
+ import os46 from "os";
24879
+ import path51 from "path";
24689
24880
  var DEFAULT_EGRESS = {
24690
24881
  enabled: false,
24691
24882
  mode: "review",
@@ -24694,7 +24885,7 @@ var DEFAULT_EGRESS = {
24694
24885
  allowPrivate: true
24695
24886
  };
24696
24887
  function egressConfigPath() {
24697
- return path50.join(os45.homedir(), ".node9", "config.json");
24888
+ return path51.join(os46.homedir(), ".node9", "config.json");
24698
24889
  }
24699
24890
  function readEgressRawConfig() {
24700
24891
  let text;
@@ -24714,7 +24905,7 @@ function readEgressRawConfig() {
24714
24905
  }
24715
24906
  function writeEgressRawConfig(config) {
24716
24907
  const p = egressConfigPath();
24717
- fs52.mkdirSync(path50.dirname(p), { recursive: true });
24908
+ fs52.mkdirSync(path51.dirname(p), { recursive: true });
24718
24909
  fs52.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
24719
24910
  }
24720
24911
  function applyEgress(config, change) {
@@ -25100,8 +25291,8 @@ function handleStatus() {
25100
25291
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
25101
25292
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
25102
25293
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
25103
- const projectConfig = path51.join(process.cwd(), "node9.config.json");
25104
- const globalConfig = path51.join(os46.homedir(), ".node9", "config.json");
25294
+ const projectConfig = path52.join(process.cwd(), "node9.config.json");
25295
+ const globalConfig = path52.join(os47.homedir(), ".node9", "config.json");
25105
25296
  lines.push(
25106
25297
  `Project config (node9.config.json): ${fs53.existsSync(projectConfig) ? "present" : "not found"}`
25107
25298
  );
@@ -25212,7 +25403,7 @@ function handleEgressDeny(args) {
25212
25403
  addEgressHost("deny", host);
25213
25404
  return `Denied egress to ${host} (deny always wins over allow).`;
25214
25405
  }
25215
- var GLOBAL_CONFIG_PATH = path51.join(os46.homedir(), ".node9", "config.json");
25406
+ var GLOBAL_CONFIG_PATH = path52.join(os47.homedir(), ".node9", "config.json");
25216
25407
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
25217
25408
  function readGlobalConfigRaw() {
25218
25409
  try {
@@ -25224,7 +25415,7 @@ function readGlobalConfigRaw() {
25224
25415
  return {};
25225
25416
  }
25226
25417
  function writeGlobalConfigRaw(data) {
25227
- const dir = path51.dirname(GLOBAL_CONFIG_PATH);
25418
+ const dir = path52.dirname(GLOBAL_CONFIG_PATH);
25228
25419
  if (!fs53.existsSync(dir)) fs53.mkdirSync(dir, { recursive: true });
25229
25420
  fs53.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
25230
25421
  }
@@ -25270,7 +25461,7 @@ function handleApproverSet(args) {
25270
25461
  function handleAuditGet(args) {
25271
25462
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
25272
25463
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
25273
- const auditPath = path51.join(os46.homedir(), ".node9", "audit.log");
25464
+ const auditPath = path52.join(os47.homedir(), ".node9", "audit.log");
25274
25465
  if (!fs53.existsSync(auditPath)) return "No audit log found.";
25275
25466
  const rawLines = fs53.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
25276
25467
  const parsed = [];
@@ -25778,6 +25969,17 @@ init_sync();
25778
25969
  import chalk22 from "chalk";
25779
25970
  function registerSyncCommand(program2) {
25780
25971
  const policy = program2.command("policy").description("Manage cloud policy rules");
25972
+ policy.command("push").description("Push this machine's effective policy to the node9 dashboard").action(async () => {
25973
+ process.stdout.write(chalk22.cyan("Pushing policy to the dashboard\u2026"));
25974
+ const result = await runPolicyPush();
25975
+ process.stdout.write("\n");
25976
+ if (!result.ok) {
25977
+ console.error(chalk22.red(`\u2717 ${result.reason}`));
25978
+ process.exit(1);
25979
+ }
25980
+ console.log(chalk22.green("\u2713 Policy mirrored to the dashboard"));
25981
+ console.log(chalk22.gray(" See it under Security Policy \u2192 Machines"));
25982
+ });
25781
25983
  policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
25782
25984
  process.stdout.write(chalk22.cyan("Syncing cloud policy rules\u2026"));
25783
25985
  const result = await runCloudSync();
@@ -26226,12 +26428,12 @@ import chalk27 from "chalk";
26226
26428
 
26227
26429
  // src/shields/jail.ts
26228
26430
  import fs55 from "fs";
26229
- import os47 from "os";
26230
- import path52 from "path";
26431
+ import os48 from "os";
26432
+ import path53 from "path";
26231
26433
  init_shields();
26232
26434
  var USER_JAIL_SHIELD = "user-jail";
26233
26435
  function jailStorePath() {
26234
- return path52.join(os47.homedir(), ".node9", "jail-paths.json");
26436
+ return path53.join(os48.homedir(), ".node9", "jail-paths.json");
26235
26437
  }
26236
26438
  function readJailPaths() {
26237
26439
  let text;
@@ -26254,7 +26456,7 @@ function readJailPaths() {
26254
26456
  }
26255
26457
  function writeJailPaths(paths) {
26256
26458
  const p = jailStorePath();
26257
- fs55.mkdirSync(path52.dirname(p), { recursive: true });
26459
+ fs55.mkdirSync(path53.dirname(p), { recursive: true });
26258
26460
  fs55.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
26259
26461
  }
26260
26462
  function addJailPath(rawPath, verdict) {
@@ -26277,7 +26479,7 @@ function removeJailPath(rawPath) {
26277
26479
  return { removed, paths: after };
26278
26480
  }
26279
26481
  function regenerateUserJail(paths) {
26280
- const file = path52.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26482
+ const file = path53.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26281
26483
  if (paths.length === 0) {
26282
26484
  const active2 = readActiveShields();
26283
26485
  if (active2.includes(USER_JAIL_SHIELD)) {
@@ -26400,12 +26602,12 @@ function registerJailCommand(program2) {
26400
26602
  init_config();
26401
26603
  import chalk28 from "chalk";
26402
26604
  import fs58 from "fs";
26403
- import path55 from "path";
26605
+ import path56 from "path";
26404
26606
  import { spawnSync as spawnSync6 } from "child_process";
26405
26607
 
26406
26608
  // src/sandbox/config.ts
26407
26609
  import fs56 from "fs";
26408
- import path53 from "path";
26610
+ import path54 from "path";
26409
26611
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
26410
26612
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
26411
26613
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -26478,7 +26680,7 @@ function scaffoldSandboxYaml(agent) {
26478
26680
  return header + stringifyYaml(defaultSandboxConfig(agent));
26479
26681
  }
26480
26682
  function sandboxConfigPath(cwd = process.cwd()) {
26481
- return path53.join(cwd, SANDBOX_CONFIG_FILE);
26683
+ return path54.join(cwd, SANDBOX_CONFIG_FILE);
26482
26684
  }
26483
26685
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
26484
26686
  const p = sandboxConfigPath(cwd);
@@ -26547,12 +26749,12 @@ init_templates();
26547
26749
  // src/sandbox/runtime.ts
26548
26750
  init_templates();
26549
26751
  import fs57 from "fs";
26550
- import os48 from "os";
26551
- import path54 from "path";
26752
+ import os49 from "os";
26753
+ import path55 from "path";
26552
26754
  import crypto8 from "crypto";
26553
26755
  import { spawnSync as spawnSync5 } from "child_process";
26554
26756
  function sandboxDataDir(cwd = process.cwd()) {
26555
- return path54.join(cwd, ".node9", "sandbox", "data");
26757
+ return path55.join(cwd, ".node9", "sandbox", "data");
26556
26758
  }
26557
26759
  function detectEngine(engine) {
26558
26760
  const r = spawnSync5(engine, ["--version"], { encoding: "utf-8" });
@@ -26563,7 +26765,7 @@ function detectEngine(engine) {
26563
26765
  }
26564
26766
  function agentCredentialsMount(agent) {
26565
26767
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
26566
- return { hostPath: path54.join(os48.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26768
+ return { hostPath: path55.join(os49.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26567
26769
  }
26568
26770
  function buildRunArgs(opts) {
26569
26771
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -26591,30 +26793,30 @@ function imageContentHash(dockerfile, entrypoint) {
26591
26793
  return crypto8.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
26592
26794
  }
26593
26795
  function sandboxBuildDir(cwd = process.cwd()) {
26594
- return path54.join(cwd, ".node9", "sandbox", "build");
26796
+ return path55.join(cwd, ".node9", "sandbox", "build");
26595
26797
  }
26596
26798
  function writeBuildContext(cwd, dockerfile, entrypoint) {
26597
26799
  const dir = sandboxBuildDir(cwd);
26598
26800
  fs57.mkdirSync(dir, { recursive: true });
26599
- fs57.writeFileSync(path54.join(dir, "Dockerfile"), dockerfile);
26600
- fs57.writeFileSync(path54.join(dir, "entrypoint.sh"), entrypoint);
26801
+ fs57.writeFileSync(path55.join(dir, "Dockerfile"), dockerfile);
26802
+ fs57.writeFileSync(path55.join(dir, "entrypoint.sh"), entrypoint);
26601
26803
  return dir;
26602
26804
  }
26603
26805
  function writeAllowlist(cwd, hosts) {
26604
- const dir = path54.join(cwd, ".node9", "sandbox");
26806
+ const dir = path55.join(cwd, ".node9", "sandbox");
26605
26807
  fs57.mkdirSync(dir, { recursive: true });
26606
- const p = path54.join(dir, "allowed-domains.txt");
26808
+ const p = path55.join(dir, "allowed-domains.txt");
26607
26809
  fs57.writeFileSync(p, hosts.join("\n") + "\n");
26608
26810
  return p;
26609
26811
  }
26610
26812
  function resolveHomePath(p) {
26611
- return p.startsWith("~") ? path54.join(os48.homedir(), p.slice(1)) : path54.resolve(p);
26813
+ return p.startsWith("~") ? path55.join(os49.homedir(), p.slice(1)) : path55.resolve(p);
26612
26814
  }
26613
26815
 
26614
26816
  // src/cli/commands/sandbox.ts
26615
26817
  function seedDataDirConfig(dataDir, sandbox) {
26616
26818
  fs58.mkdirSync(dataDir, { recursive: true });
26617
- const configPath = path55.join(dataDir, "config.json");
26819
+ const configPath = path56.join(dataDir, "config.json");
26618
26820
  const seed = {
26619
26821
  settings: {
26620
26822
  approvers: {
@@ -26679,7 +26881,7 @@ function registerSandboxCommand(program2, version2) {
26679
26881
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
26680
26882
  const hash = imageContentHash(dockerfile, entrypoint);
26681
26883
  const image = sandbox.runtime.image;
26682
- const hashFile = path55.join(sandboxBuildDir(cwd), ".image-hash");
26884
+ const hashFile = path56.join(sandboxBuildDir(cwd), ".image-hash");
26683
26885
  const lastHash = fs58.existsSync(hashFile) ? fs58.readFileSync(hashFile, "utf-8").trim() : "";
26684
26886
  const imageExists = spawnSync6(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
26685
26887
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
@@ -26722,7 +26924,7 @@ function registerSandboxCommand(program2, version2) {
26722
26924
  process.exit(r.status ?? 0);
26723
26925
  });
26724
26926
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
26725
- const auditPath = path55.join(sandboxDataDir(), "audit.log");
26927
+ const auditPath = path56.join(sandboxDataDir(), "audit.log");
26726
26928
  if (!fs58.existsSync(auditPath)) {
26727
26929
  console.log(chalk28.dim(" no sandbox audit yet."));
26728
26930
  return;
@@ -26730,7 +26932,7 @@ function registerSandboxCommand(program2, version2) {
26730
26932
  spawnSync6("tail", ["-f", auditPath], { stdio: "inherit" });
26731
26933
  });
26732
26934
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
26733
- const auditPath = path55.join(sandboxDataDir(), "audit.log");
26935
+ const auditPath = path56.join(sandboxDataDir(), "audit.log");
26734
26936
  if (!fs58.existsSync(auditPath)) {
26735
26937
  console.log(chalk28.dim(" no sandbox audit yet."));
26736
26938
  return;
@@ -26749,7 +26951,7 @@ function registerSandboxCommand(program2, version2) {
26749
26951
  stdio: "ignore"
26750
26952
  });
26751
26953
  }
26752
- fs58.rmSync(path55.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26954
+ fs58.rmSync(path56.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26753
26955
  console.log(chalk28.green(" \u2713 sandbox image + build + data removed."));
26754
26956
  });
26755
26957
  }
@@ -26761,8 +26963,8 @@ init_cost_gemini();
26761
26963
  init_cost_codex();
26762
26964
  import chalk29 from "chalk";
26763
26965
  import fs59 from "fs";
26764
- import path56 from "path";
26765
- import os49 from "os";
26966
+ import path57 from "path";
26967
+ import os50 from "os";
26766
26968
  function modelPrice(model) {
26767
26969
  const t = pricingFor(model);
26768
26970
  if (!t) return null;
@@ -26779,10 +26981,10 @@ function encodeProjectPath(projectPath) {
26779
26981
  }
26780
26982
  function sessionJsonlPath(projectPath, sessionId) {
26781
26983
  const encoded = encodeProjectPath(projectPath);
26782
- return path56.join(os49.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
26984
+ return path57.join(os50.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
26783
26985
  }
26784
26986
  function projectLabel(projectPath) {
26785
- return projectPath.replace(os49.homedir(), "~");
26987
+ return projectPath.replace(os50.homedir(), "~");
26786
26988
  }
26787
26989
  function parseHistoryLines(lines) {
26788
26990
  const entries = [];
@@ -26851,7 +27053,7 @@ function parseSessionLines(lines) {
26851
27053
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
26852
27054
  }
26853
27055
  function loadAuditEntries(auditPath) {
26854
- const aPath = auditPath ?? path56.join(os49.homedir(), ".node9", "audit.log");
27056
+ const aPath = auditPath ?? path57.join(os50.homedir(), ".node9", "audit.log");
26855
27057
  let raw;
26856
27058
  try {
26857
27059
  raw = fs59.readFileSync(aPath, "utf-8");
@@ -26890,7 +27092,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
26890
27092
  return result;
26891
27093
  }
26892
27094
  function buildGeminiSessions(days, allAuditEntries) {
26893
- const tmpDir = path56.join(os49.homedir(), ".gemini", "tmp");
27095
+ const tmpDir = path57.join(os50.homedir(), ".gemini", "tmp");
26894
27096
  if (!fs59.existsSync(tmpDir)) return [];
26895
27097
  const cutoff = days !== null ? (() => {
26896
27098
  const d = /* @__PURE__ */ new Date();
@@ -26906,18 +27108,18 @@ function buildGeminiSessions(days, allAuditEntries) {
26906
27108
  }
26907
27109
  const summaries = [];
26908
27110
  for (const slug2 of slugDirs) {
26909
- const slugPath = path56.join(tmpDir, slug2);
27111
+ const slugPath = path57.join(tmpDir, slug2);
26910
27112
  try {
26911
27113
  if (!fs59.statSync(slugPath).isDirectory()) continue;
26912
27114
  } catch {
26913
27115
  continue;
26914
27116
  }
26915
- let projectRoot = path56.join(os49.homedir(), slug2);
27117
+ let projectRoot = path57.join(os50.homedir(), slug2);
26916
27118
  try {
26917
- projectRoot = fs59.readFileSync(path56.join(slugPath, ".project_root"), "utf-8").trim();
27119
+ projectRoot = fs59.readFileSync(path57.join(slugPath, ".project_root"), "utf-8").trim();
26918
27120
  } catch {
26919
27121
  }
26920
- const chatsDir = path56.join(slugPath, "chats");
27122
+ const chatsDir = path57.join(slugPath, "chats");
26921
27123
  if (!fs59.existsSync(chatsDir)) continue;
26922
27124
  let chatFiles;
26923
27125
  try {
@@ -26928,7 +27130,7 @@ function buildGeminiSessions(days, allAuditEntries) {
26928
27130
  for (const chatFile of chatFiles) {
26929
27131
  let raw;
26930
27132
  try {
26931
- raw = fs59.readFileSync(path56.join(chatsDir, chatFile), "utf-8");
27133
+ raw = fs59.readFileSync(path57.join(chatsDir, chatFile), "utf-8");
26932
27134
  } catch {
26933
27135
  continue;
26934
27136
  }
@@ -27008,7 +27210,7 @@ function buildGeminiSessions(days, allAuditEntries) {
27008
27210
  return summaries;
27009
27211
  }
27010
27212
  function buildCodexSessions(days, allAuditEntries) {
27011
- const sessionsBase = path56.join(os49.homedir(), ".codex", "sessions");
27213
+ const sessionsBase = path57.join(os50.homedir(), ".codex", "sessions");
27012
27214
  if (!fs59.existsSync(sessionsBase)) return [];
27013
27215
  const cutoff = days !== null ? (() => {
27014
27216
  const d = /* @__PURE__ */ new Date();
@@ -27019,28 +27221,28 @@ function buildCodexSessions(days, allAuditEntries) {
27019
27221
  const jsonlFiles = [];
27020
27222
  try {
27021
27223
  for (const year of fs59.readdirSync(sessionsBase)) {
27022
- const yearPath = path56.join(sessionsBase, year);
27224
+ const yearPath = path57.join(sessionsBase, year);
27023
27225
  try {
27024
27226
  if (!fs59.statSync(yearPath).isDirectory()) continue;
27025
27227
  } catch {
27026
27228
  continue;
27027
27229
  }
27028
27230
  for (const month of fs59.readdirSync(yearPath)) {
27029
- const monthPath = path56.join(yearPath, month);
27231
+ const monthPath = path57.join(yearPath, month);
27030
27232
  try {
27031
27233
  if (!fs59.statSync(monthPath).isDirectory()) continue;
27032
27234
  } catch {
27033
27235
  continue;
27034
27236
  }
27035
27237
  for (const day of fs59.readdirSync(monthPath)) {
27036
- const dayPath = path56.join(monthPath, day);
27238
+ const dayPath = path57.join(monthPath, day);
27037
27239
  try {
27038
27240
  if (!fs59.statSync(dayPath).isDirectory()) continue;
27039
27241
  } catch {
27040
27242
  continue;
27041
27243
  }
27042
27244
  for (const file of fs59.readdirSync(dayPath)) {
27043
- if (file.endsWith(".jsonl")) jsonlFiles.push(path56.join(dayPath, file));
27245
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path57.join(dayPath, file));
27044
27246
  }
27045
27247
  }
27046
27248
  }
@@ -27138,7 +27340,7 @@ function buildCodexSessions(days, allAuditEntries) {
27138
27340
  return summaries;
27139
27341
  }
27140
27342
  function buildSessions(days, historyPath) {
27141
- const hPath = historyPath ?? path56.join(os49.homedir(), ".claude", "history.jsonl");
27343
+ const hPath = historyPath ?? path57.join(os50.homedir(), ".claude", "history.jsonl");
27142
27344
  let historyRaw = "";
27143
27345
  try {
27144
27346
  historyRaw = fs59.readFileSync(hPath, "utf-8");
@@ -27560,11 +27762,11 @@ function registerSessionTaintCommand(program2) {
27560
27762
  // src/cli/commands/skill-pin.ts
27561
27763
  import chalk31 from "chalk";
27562
27764
  import fs60 from "fs";
27563
- import os50 from "os";
27564
- import path57 from "path";
27765
+ import os51 from "os";
27766
+ import path58 from "path";
27565
27767
  function wipeSkillSessions() {
27566
27768
  try {
27567
- fs60.rmSync(path57.join(os50.homedir(), ".node9", "skill-sessions"), {
27769
+ fs60.rmSync(path58.join(os51.homedir(), ".node9", "skill-sessions"), {
27568
27770
  recursive: true,
27569
27771
  force: true
27570
27772
  });
@@ -27647,10 +27849,10 @@ function registerSkillPinCommand(program2) {
27647
27849
 
27648
27850
  // src/cli/commands/decisions.ts
27649
27851
  import fs61 from "fs";
27650
- import os51 from "os";
27651
- import path58 from "path";
27852
+ import os52 from "os";
27853
+ import path59 from "path";
27652
27854
  import chalk32 from "chalk";
27653
- var DECISIONS_FILE2 = path58.join(os51.homedir(), ".node9", "decisions.json");
27855
+ var DECISIONS_FILE2 = path59.join(os52.homedir(), ".node9", "decisions.json");
27654
27856
  function readDecisions() {
27655
27857
  try {
27656
27858
  if (!fs61.existsSync(DECISIONS_FILE2)) return {};
@@ -27666,7 +27868,7 @@ function readDecisions() {
27666
27868
  }
27667
27869
  }
27668
27870
  function writeDecisions(d) {
27669
- const dir = path58.dirname(DECISIONS_FILE2);
27871
+ const dir = path59.dirname(DECISIONS_FILE2);
27670
27872
  if (!fs61.existsSync(dir)) fs61.mkdirSync(dir, { recursive: true });
27671
27873
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
27672
27874
  fs61.writeFileSync(tmp, JSON.stringify(d, null, 2));
@@ -27728,10 +27930,10 @@ Persistent decisions (${entries.length})
27728
27930
  // src/cli/commands/dlp.ts
27729
27931
  import chalk33 from "chalk";
27730
27932
  import fs62 from "fs";
27731
- import path59 from "path";
27732
- import os52 from "os";
27733
- var AUDIT_LOG = path59.join(os52.homedir(), ".node9", "audit.log");
27734
- var RESOLVED_FILE = path59.join(os52.homedir(), ".node9", "dlp-resolved.json");
27933
+ import path60 from "path";
27934
+ import os53 from "os";
27935
+ var AUDIT_LOG = path60.join(os53.homedir(), ".node9", "audit.log");
27936
+ var RESOLVED_FILE = path60.join(os53.homedir(), ".node9", "dlp-resolved.json");
27735
27937
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
27736
27938
  function stripAnsi(s) {
27737
27939
  return s.replace(ANSI_RE, "");
@@ -27852,13 +28054,13 @@ function registerDlpCommand(program2) {
27852
28054
  init_dlp();
27853
28055
  import chalk34 from "chalk";
27854
28056
  import fs63 from "fs";
27855
- import path60 from "path";
27856
- import os53 from "os";
28057
+ import path61 from "path";
28058
+ import os54 from "os";
27857
28059
  function findJsonlFiles(dir) {
27858
28060
  const results = [];
27859
28061
  if (!fs63.existsSync(dir)) return results;
27860
28062
  for (const entry of fs63.readdirSync(dir, { withFileTypes: true })) {
27861
- const full = path60.join(dir, entry.name);
28063
+ const full = path61.join(dir, entry.name);
27862
28064
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
27863
28065
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
27864
28066
  }
@@ -27961,7 +28163,7 @@ function findJsonFiles(dir) {
27961
28163
  const results = [];
27962
28164
  if (!fs63.existsSync(dir)) return results;
27963
28165
  for (const entry of fs63.readdirSync(dir, { withFileTypes: true })) {
27964
- const full = path60.join(dir, entry.name);
28166
+ const full = path61.join(dir, entry.name);
27965
28167
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
27966
28168
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
27967
28169
  }
@@ -27970,9 +28172,9 @@ function findJsonFiles(dir) {
27970
28172
  function registerMaskCommand(program2) {
27971
28173
  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) => {
27972
28174
  const dryRun = !!options.dryRun;
27973
- const home = os53.homedir();
27974
- const claudeDir = path60.join(home, ".claude", "projects");
27975
- const geminiDir = path60.join(home, ".gemini", "tmp");
28175
+ const home = os54.homedir();
28176
+ const claudeDir = path61.join(home, ".claude", "projects");
28177
+ const geminiDir = path61.join(home, ".gemini", "tmp");
27976
28178
  const allFiles = [
27977
28179
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
27978
28180
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -28036,15 +28238,15 @@ function registerMaskCommand(program2) {
28036
28238
  // src/cli.ts
28037
28239
  init_blast();
28038
28240
  var { version } = JSON.parse(
28039
- fs66.readFileSync(path63.join(__dirname, "../package.json"), "utf-8")
28241
+ fs66.readFileSync(path64.join(__dirname, "../package.json"), "utf-8")
28040
28242
  );
28041
28243
  var program = new Command();
28042
28244
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
28043
28245
  program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
28044
28246
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
28045
- const credPath = path63.join(os56.homedir(), ".node9", "credentials.json");
28046
- if (!fs66.existsSync(path63.dirname(credPath)))
28047
- fs66.mkdirSync(path63.dirname(credPath), { recursive: true });
28247
+ const credPath = path64.join(os57.homedir(), ".node9", "credentials.json");
28248
+ if (!fs66.existsSync(path64.dirname(credPath)))
28249
+ fs66.mkdirSync(path64.dirname(credPath), { recursive: true });
28048
28250
  const profileName = options.profile || "default";
28049
28251
  let existingCreds = {};
28050
28252
  try {
@@ -28064,7 +28266,7 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
28064
28266
  fs66.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
28065
28267
  let effectiveCloud = null;
28066
28268
  if (profileName === "default") {
28067
- const configPath = path63.join(os56.homedir(), ".node9", "config.json");
28269
+ const configPath = path64.join(os57.homedir(), ".node9", "config.json");
28068
28270
  let config = {};
28069
28271
  try {
28070
28272
  if (fs66.existsSync(configPath))
@@ -28083,8 +28285,8 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
28083
28285
  approvers.cloud = false;
28084
28286
  }
28085
28287
  s.approvers = approvers;
28086
- if (!fs66.existsSync(path63.dirname(configPath)))
28087
- fs66.mkdirSync(path63.dirname(configPath), { recursive: true });
28288
+ if (!fs66.existsSync(path64.dirname(configPath)))
28289
+ fs66.mkdirSync(path64.dirname(configPath), { recursive: true });
28088
28290
  fs66.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
28089
28291
  effectiveCloud = approvers.cloud === true;
28090
28292
  }
@@ -28194,37 +28396,21 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
28194
28396
  );
28195
28397
  process.exit(1);
28196
28398
  });
28197
- program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
28198
- "after",
28199
- "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
28200
- ).argument(
28201
- "<target>",
28202
- "The agent to remove from: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
28203
- ).action((target) => {
28204
- let fn;
28205
- if (target === "claude") fn = teardownClaude;
28206
- else if (target === "gemini") fn = teardownGemini;
28207
- else if (target === "antigravity" || target === "agy") fn = teardownAntigravity;
28208
- else if (target === "copilot") fn = teardownCopilot;
28209
- else if (target === "cursor") fn = teardownCursor;
28210
- else if (target === "codex") fn = teardownCodex;
28211
- else if (target === "windsurf") fn = teardownWindsurf;
28212
- else if (target === "vscode") fn = teardownVSCode;
28213
- else if (target === "hermes") fn = teardownHermes;
28214
- else if (target === "hud") fn = teardownHud;
28215
- else {
28399
+ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText("after", `
28400
+ Supported targets: ${agentTeardownTargets().join(" ")}`).argument("<target>", `The agent to remove from: ${agentTeardownTargets().join(" | ")}`).action((target) => {
28401
+ const agent = resolveAgentTeardown(target);
28402
+ if (!agent) {
28216
28403
  console.error(
28217
- chalk36.red(
28218
- `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
28219
- )
28404
+ chalk36.red(`Unknown target: "${target}". Supported: ${agentTeardownTargets().join(", ")}`)
28220
28405
  );
28221
28406
  process.exit(1);
28407
+ return;
28222
28408
  }
28223
28409
  console.log(chalk36.cyan(`
28224
- \u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
28410
+ \u{1F6E1}\uFE0F Node9: removing hooks from ${agent.label}...
28225
28411
  `));
28226
28412
  try {
28227
- fn();
28413
+ agent.fn();
28228
28414
  } catch (err2) {
28229
28415
  console.error(chalk36.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
28230
28416
  process.exit(1);
@@ -28242,15 +28428,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
28242
28428
  }
28243
28429
  console.log(chalk36.bold("\nRemoving hooks..."));
28244
28430
  let teardownFailed = false;
28245
- for (const [label2, fn] of [
28246
- ["Claude", teardownClaude],
28247
- ["Gemini", teardownGemini],
28248
- ["Cursor", teardownCursor],
28249
- ["Codex", teardownCodex],
28250
- ["Windsurf", teardownWindsurf],
28251
- ["VSCode", teardownVSCode],
28252
- ["Hermes", teardownHermes]
28253
- ]) {
28431
+ for (const { label: label2, fn } of AGENT_TEARDOWNS) {
28254
28432
  try {
28255
28433
  fn();
28256
28434
  } catch (err2) {
@@ -28262,8 +28440,21 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
28262
28440
  );
28263
28441
  }
28264
28442
  }
28443
+ try {
28444
+ const residual = getAgentWiring().filter((a) => a.wireState === "wired");
28445
+ if (residual.length === 0) {
28446
+ console.log(chalk36.green(" \u2705 Verified \u2014 no node9 hooks or plugin shims remain"));
28447
+ } else {
28448
+ teardownFailed = true;
28449
+ console.error(chalk36.red(" \u26A0\uFE0F Still wired after teardown:"));
28450
+ for (const a of residual) {
28451
+ console.error(chalk36.red(` \u2022 ${a.label} \u2014 ${a.settingsPath}`));
28452
+ }
28453
+ }
28454
+ } catch {
28455
+ }
28265
28456
  if (options.purge) {
28266
- const node9Dir = path63.join(os56.homedir(), ".node9");
28457
+ const node9Dir = path64.join(os57.homedir(), ".node9");
28267
28458
  if (fs66.existsSync(node9Dir)) {
28268
28459
  const confirmed = await confirm2({
28269
28460
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
@@ -28394,7 +28585,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
28394
28585
  });
28395
28586
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
28396
28587
  try {
28397
- const dashboardPath = path63.join(__dirname, "dashboard.mjs");
28588
+ const dashboardPath = path64.join(__dirname, "dashboard.mjs");
28398
28589
  const dynamicImport = new Function("id", "return import(id)");
28399
28590
  const mod = await dynamicImport(`file://${dashboardPath}`);
28400
28591
  await mod.startMonitor();
@@ -28432,9 +28623,9 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
28432
28623
  Run "node9 addto claude" to register it as the statusLine.`
28433
28624
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
28434
28625
  if (subcommand === "debug") {
28435
- const flagFile = path63.join(os56.homedir(), ".node9", "hud-debug");
28626
+ const flagFile = path64.join(os57.homedir(), ".node9", "hud-debug");
28436
28627
  if (state === "on") {
28437
- fs66.mkdirSync(path63.dirname(flagFile), { recursive: true });
28628
+ fs66.mkdirSync(path64.dirname(flagFile), { recursive: true });
28438
28629
  fs66.writeFileSync(flagFile, "");
28439
28630
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
28440
28631
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
@@ -28561,7 +28752,7 @@ if (process.argv[2] !== "daemon") {
28561
28752
  const isCheckHook = process.argv[2] === "check";
28562
28753
  if (isCheckHook) {
28563
28754
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
28564
- const logPath = path63.join(os56.homedir(), ".node9", "hook-debug.log");
28755
+ const logPath = path64.join(os57.homedir(), ".node9", "hook-debug.log");
28565
28756
  const msg = reason instanceof Error ? reason.message : String(reason);
28566
28757
  fs66.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
28567
28758
  `);