@node9/proxy 1.45.0 → 1.47.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,
@@ -315,6 +315,11 @@ var init_config_schema = __esm({
315
315
  // must run them from the CLI. node9's threat model is the agent itself.
316
316
  mcpAllowWeakening: z.boolean().optional(),
317
317
  cloudSyncIntervalHours: z.number().positive().optional(),
318
+ // Seconds-granular override for the cloud policy sync cadence. Wins over
319
+ // cloudSyncIntervalHours when set. Lets you opt into fast apply (e.g. 20)
320
+ // for an incident; clamped to a 15s floor so it can't hammer the API.
321
+ // Unset → falls back to hours, then the 5h default.
322
+ cloudSyncIntervalSeconds: z.number().positive().optional(),
318
323
  // Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
319
324
  // to true; set false to fall back to local-only auditing.
320
325
  shipper: z.object({
@@ -1341,9 +1346,9 @@ function matchesPattern(text, patterns) {
1341
1346
  const withoutDotSlash = text.replace(/^\.\//, "");
1342
1347
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1343
1348
  }
1344
- function getNestedValue(obj, path64) {
1349
+ function getNestedValue(obj, path65) {
1345
1350
  if (!obj || typeof obj !== "object") return null;
1346
- const segments = path64.split(".");
1351
+ const segments = path65.split(".");
1347
1352
  for (const seg of segments) {
1348
1353
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1349
1354
  }
@@ -1396,6 +1401,14 @@ function evaluateSmartConditions(args, rule) {
1396
1401
  });
1397
1402
  return mode === "any" ? results.some((r) => r) : results.every((r) => r);
1398
1403
  }
1404
+ function resolvePinned(matches) {
1405
+ if (matches.length === 0) return void 0;
1406
+ const pinned = matches.filter((r) => r.pinned);
1407
+ if (pinned.length === 0) return matches[0];
1408
+ return pinned.reduce(
1409
+ (best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
1410
+ );
1411
+ }
1399
1412
  function tokenize2(toolName) {
1400
1413
  return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
1401
1414
  }
@@ -1507,9 +1520,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
1507
1520
  }
1508
1521
  }
1509
1522
  if (config.policy.smartRules.length > 0) {
1510
- const matchedRule = config.policy.smartRules.find(
1523
+ const matches = config.policy.smartRules.filter(
1511
1524
  (rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
1512
1525
  );
1526
+ const matchedRule = resolvePinned(matches);
1513
1527
  if (matchedRule) {
1514
1528
  if (matchedRule.verdict === "allow")
1515
1529
  return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
@@ -2248,7 +2262,7 @@ function* stringValues(obj, depth = 0) {
2248
2262
  }
2249
2263
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
2250
2264
  }
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;
2265
+ 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
2266
  var init_dist = __esm({
2253
2267
  "packages/policy-engine/dist/index.mjs"() {
2254
2268
  "use strict";
@@ -3173,6 +3187,11 @@ var init_dist = __esm({
3173
3187
  REGEX_CACHE_MAX = 500;
3174
3188
  regexCache = /* @__PURE__ */ new Map();
3175
3189
  FORBIDDEN_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3190
+ VERDICT_RANK = {
3191
+ allow: 0,
3192
+ review: 1,
3193
+ block: 2
3194
+ };
3176
3195
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
3177
3196
  aws_default = {
3178
3197
  name: "aws",
@@ -3977,6 +3996,7 @@ var init_dist = __esm({
3977
3996
  DEDUPE_PREVIEW_LEN = 120;
3978
3997
  TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
3979
3998
  /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
3999
+ ENGINE_VERSION = "1.4.0";
3980
4000
  }
3981
4001
  });
3982
4002
 
@@ -9700,16 +9720,237 @@ var init_setup = __esm({
9700
9720
  }
9701
9721
  });
9702
9722
 
9703
- // src/pricing/litellm.ts
9723
+ // src/agent-wiring.ts
9704
9724
  import fs14 from "fs";
9705
9725
  import path16 from "path";
9706
9726
  import os13 from "os";
9727
+ import * as yaml2 from "yaml";
9728
+ import { parse as parseToml2 } from "smol-toml";
9729
+ function readJson2(filePath) {
9730
+ if (!fs14.existsSync(filePath)) return null;
9731
+ try {
9732
+ return JSON.parse(fs14.readFileSync(filePath, "utf-8"));
9733
+ } catch {
9734
+ return "invalid";
9735
+ }
9736
+ }
9737
+ function matchersHaveNode9Hook(matchers) {
9738
+ return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
9739
+ }
9740
+ function flatHaveNode9Hook(entries) {
9741
+ return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
9742
+ }
9743
+ function readHookRoot(filePath, format) {
9744
+ if (!fs14.existsSync(filePath)) return "absent";
9745
+ let raw;
9746
+ try {
9747
+ raw = fs14.readFileSync(filePath, "utf-8");
9748
+ } catch {
9749
+ return "absent";
9750
+ }
9751
+ try {
9752
+ const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
9753
+ return parsed?.hooks ?? {};
9754
+ } catch {
9755
+ return "invalid";
9756
+ }
9757
+ }
9758
+ function eventWired(root, ev, format) {
9759
+ const arr = root[ev.key];
9760
+ if (format === "matcher") return matchersHaveNode9Hook(arr);
9761
+ return flatHaveNode9Hook(arr);
9762
+ }
9763
+ function detectMcp(servers) {
9764
+ const entries = Object.entries(servers ?? {});
9765
+ const present = entries.some(([, s]) => s?.command === "node9");
9766
+ const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
9767
+ return { wrapped, present };
9768
+ }
9769
+ function readMcp(filePath, format) {
9770
+ if (!fs14.existsSync(filePath)) return { wrapped: [], present: false };
9771
+ try {
9772
+ if (format === "toml") {
9773
+ const parsed2 = parseToml2(fs14.readFileSync(filePath, "utf-8"));
9774
+ return detectMcp(parsed2?.mcp_servers);
9775
+ }
9776
+ const parsed = readJson2(filePath);
9777
+ if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
9778
+ return detectMcp(parsed.mcpServers);
9779
+ } catch {
9780
+ return { wrapped: [], present: false };
9781
+ }
9782
+ }
9783
+ function getAgentWiring(home = os13.homedir()) {
9784
+ const detected = detectAgents(home);
9785
+ return AGENT_SPECS.map((spec) => {
9786
+ const present = spec.present(home);
9787
+ const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
9788
+ let hooks;
9789
+ let wireState;
9790
+ let hookLabel;
9791
+ let settingsPath;
9792
+ if (spec.shimFile) {
9793
+ const shimWired = exists(spec.shimFile(home));
9794
+ hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
9795
+ wireState = shimWired ? "wired" : present ? "unwired" : "absent";
9796
+ hookLabel = "node9 plugin";
9797
+ settingsPath = spec.shimFile(home);
9798
+ } else {
9799
+ const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
9800
+ const primary = spec.hookEvents[0];
9801
+ const rootPresent = root !== "absent" && root !== "invalid";
9802
+ hooks = spec.hookEvents.map((ev) => ({
9803
+ label: hookLabelOf(ev, pad),
9804
+ wired: rootPresent && eventWired(root, ev, spec.hookFormat)
9805
+ }));
9806
+ if (root === "absent") wireState = "absent";
9807
+ else if (root === "invalid") wireState = "invalid";
9808
+ else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
9809
+ hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
9810
+ settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
9811
+ }
9812
+ const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
9813
+ const anyHookWired = hooks.some((h) => h.wired);
9814
+ return {
9815
+ id: spec.id,
9816
+ label: spec.label,
9817
+ setupCommand: spec.setupCommand,
9818
+ installed: detected[spec.id],
9819
+ present,
9820
+ hooks,
9821
+ wireState,
9822
+ hookLabel,
9823
+ settingsPath,
9824
+ configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
9825
+ mcpServers: mcp ? mcp.wrapped : null,
9826
+ mcpProtected: mcp ? mcp.present : false,
9827
+ isProtected: anyHookWired || (mcp?.present ?? false)
9828
+ };
9829
+ });
9830
+ }
9831
+ var exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
9832
+ var init_agent_wiring = __esm({
9833
+ "src/agent-wiring.ts"() {
9834
+ "use strict";
9835
+ init_setup();
9836
+ exists = (p) => {
9837
+ try {
9838
+ return fs14.existsSync(p);
9839
+ } catch {
9840
+ return false;
9841
+ }
9842
+ };
9843
+ ck = (key) => ({ key, kind: "check" });
9844
+ lg = (key) => ({ key, kind: "log" });
9845
+ DEFAULT_LABEL_PAD = 11;
9846
+ hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
9847
+ AGENT_SPECS = [
9848
+ {
9849
+ id: "claude",
9850
+ label: "Claude Code",
9851
+ setupCommand: "node9 agents add claude",
9852
+ hookFile: (h) => path16.join(h, ".claude", "settings.json"),
9853
+ hookFormat: "matcher",
9854
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
9855
+ mcpFile: (h) => path16.join(h, ".claude.json"),
9856
+ present: (h) => exists(path16.join(h, ".claude", "settings.json")) || exists(path16.join(h, ".claude.json"))
9857
+ },
9858
+ {
9859
+ id: "gemini",
9860
+ label: "Gemini CLI",
9861
+ setupCommand: "node9 agents add gemini",
9862
+ hookFile: (h) => path16.join(h, ".gemini", "settings.json"),
9863
+ hookFormat: "matcher",
9864
+ hookEvents: [ck("BeforeTool"), lg("AfterTool")],
9865
+ mcpFile: (h) => path16.join(h, ".gemini", "settings.json"),
9866
+ present: (h) => exists(path16.join(h, ".gemini", "settings.json"))
9867
+ },
9868
+ {
9869
+ id: "codex",
9870
+ label: "Codex",
9871
+ setupCommand: "node9 agents add codex",
9872
+ hookFile: (h) => path16.join(h, ".codex", "hooks.json"),
9873
+ hookFormat: "matcher",
9874
+ hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
9875
+ mcpFile: (h) => path16.join(h, ".codex", "config.toml"),
9876
+ mcpFormat: "toml",
9877
+ present: (h) => exists(path16.join(h, ".codex"))
9878
+ },
9879
+ {
9880
+ id: "antigravity",
9881
+ label: "Antigravity",
9882
+ setupCommand: "node9 agents add antigravity",
9883
+ hookFile: (h) => path16.join(h, ".gemini", "config", "hooks.json"),
9884
+ hookFormat: "matcher",
9885
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
9886
+ mcpFile: (h) => path16.join(h, ".gemini", "config", "mcp_config.json"),
9887
+ present: (h) => exists(path16.join(h, ".gemini", "config", "hooks.json")) || exists(path16.join(h, ".gemini", "antigravity-cli")) || exists(path16.join(h, ".gemini", "antigravity-ide"))
9888
+ },
9889
+ {
9890
+ id: "copilot",
9891
+ label: "GitHub Copilot",
9892
+ setupCommand: "node9 agents add copilot",
9893
+ hookFile: (h) => path16.join(h, ".copilot", "hooks", "node9.json"),
9894
+ hookFormat: "flat",
9895
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
9896
+ mcpFile: (h) => path16.join(h, ".copilot", "mcp-config.json"),
9897
+ present: (h) => exists(path16.join(h, ".copilot"))
9898
+ },
9899
+ {
9900
+ id: "cursor",
9901
+ label: "Cursor",
9902
+ setupCommand: "node9 agents add cursor",
9903
+ // MCP-only — no hook file (see note above).
9904
+ hookFormat: "flat",
9905
+ hookEvents: [],
9906
+ mcpFile: (h) => path16.join(h, ".cursor", "mcp.json"),
9907
+ present: (h) => exists(path16.join(h, ".cursor", "mcp.json"))
9908
+ },
9909
+ {
9910
+ id: "hermes",
9911
+ label: "Hermes Agent",
9912
+ setupCommand: "node9 agents add hermes",
9913
+ hookFile: (h) => hermesConfigPath(h),
9914
+ hookFormat: "yaml",
9915
+ hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
9916
+ labelPad: 14,
9917
+ // 'post_tool_call' is wider than the default
9918
+ present: (h) => exists(hermesConfigPath(h))
9919
+ },
9920
+ {
9921
+ // Plugin-shim agents — protected by a node9-authored plugin/extension file
9922
+ // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
9923
+ id: "opencode",
9924
+ label: "OpenCode",
9925
+ setupCommand: "node9 agents add opencode",
9926
+ hookFormat: "flat",
9927
+ hookEvents: [],
9928
+ shimFile: (h) => path16.join(h, ".config", "opencode", "plugins", "node9.js"),
9929
+ present: (h) => exists(path16.join(h, ".config", "opencode")) || exists(path16.join(h, ".config", "opencode", "plugins", "node9.js"))
9930
+ },
9931
+ {
9932
+ id: "pi",
9933
+ label: "Pi",
9934
+ setupCommand: "node9 agents add pi",
9935
+ hookFormat: "flat",
9936
+ hookEvents: [],
9937
+ shimFile: (h) => path16.join(h, ".pi", "agent", "extensions", "node9.js"),
9938
+ present: (h) => exists(path16.join(h, ".pi", "agent")) || exists(path16.join(h, ".pi", "agent", "extensions", "node9.js"))
9939
+ }
9940
+ ];
9941
+ }
9942
+ });
9943
+
9944
+ // src/pricing/litellm.ts
9945
+ import fs15 from "fs";
9946
+ import path17 from "path";
9947
+ import os14 from "os";
9707
9948
  function normalizeModel(raw) {
9708
9949
  return raw.replace(/-\d{8}$/, "").toLowerCase();
9709
9950
  }
9710
9951
  function readCache() {
9711
9952
  try {
9712
- const raw = JSON.parse(fs14.readFileSync(CACHE_FILE(), "utf-8"));
9953
+ const raw = JSON.parse(fs15.readFileSync(CACHE_FILE(), "utf-8"));
9713
9954
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
9714
9955
  return null;
9715
9956
  }
@@ -9723,18 +9964,18 @@ function readCache() {
9723
9964
  function writeCache(prices) {
9724
9965
  try {
9725
9966
  const target = CACHE_FILE();
9726
- const dir = path16.dirname(target);
9727
- if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
9967
+ const dir = path17.dirname(target);
9968
+ if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
9728
9969
  const tmp = target + ".tmp";
9729
9970
  const body = {
9730
9971
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
9731
9972
  prices
9732
9973
  };
9733
- fs14.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
9734
- fs14.renameSync(tmp, target);
9974
+ fs15.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
9975
+ fs15.renameSync(tmp, target);
9735
9976
  } catch (err2) {
9736
9977
  try {
9737
- fs14.appendFileSync(
9978
+ fs15.appendFileSync(
9738
9979
  HOOK_DEBUG_LOG,
9739
9980
  `[pricing] cache write failed: ${err2.message}
9740
9981
  `
@@ -9878,7 +10119,7 @@ var init_litellm = __esm({
9878
10119
  "gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
9879
10120
  "gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
9880
10121
  };
9881
- CACHE_FILE = () => path16.join(os13.homedir(), ".node9", "model-pricing.json");
10122
+ CACHE_FILE = () => path17.join(os14.homedir(), ".node9", "model-pricing.json");
9882
10123
  TTL_MS = 24 * 60 * 60 * 1e3;
9883
10124
  memCache = null;
9884
10125
  memCacheAt = 0;
@@ -9888,11 +10129,11 @@ var init_litellm = __esm({
9888
10129
  });
9889
10130
 
9890
10131
  // src/cost-gemini.ts
9891
- import fs15 from "fs";
9892
- import os14 from "os";
9893
- import path17 from "path";
10132
+ import fs16 from "fs";
10133
+ import os15 from "os";
10134
+ import path18 from "path";
9894
10135
  function geminiTmpDir() {
9895
- return path17.join(os14.homedir(), ".gemini", "tmp");
10136
+ return path18.join(os15.homedir(), ".gemini", "tmp");
9896
10137
  }
9897
10138
  function geminiPriceFor(model) {
9898
10139
  let tuple = pricingFor(model);
@@ -9907,14 +10148,14 @@ function geminiPriceFor(model) {
9907
10148
  }
9908
10149
  function safeReaddir(dir) {
9909
10150
  try {
9910
- return fs15.readdirSync(dir);
10151
+ return fs16.readdirSync(dir);
9911
10152
  } catch {
9912
10153
  return [];
9913
10154
  }
9914
10155
  }
9915
10156
  function isDir(p) {
9916
10157
  try {
9917
- return fs15.statSync(p).isDirectory();
10158
+ return fs16.statSync(p).isDirectory();
9918
10159
  } catch {
9919
10160
  return false;
9920
10161
  }
@@ -9922,11 +10163,11 @@ function isDir(p) {
9922
10163
  function listGeminiSessionFiles(base) {
9923
10164
  const out = [];
9924
10165
  for (const project of safeReaddir(base)) {
9925
- const chats = path17.join(base, project, "chats");
10166
+ const chats = path18.join(base, project, "chats");
9926
10167
  if (!isDir(chats)) continue;
9927
10168
  for (const f of safeReaddir(chats)) {
9928
10169
  if (f.startsWith("session-") && f.endsWith(".jsonl")) {
9929
- out.push({ file: path17.join(chats, f), project });
10170
+ out.push({ file: path18.join(chats, f), project });
9930
10171
  }
9931
10172
  }
9932
10173
  }
@@ -9993,7 +10234,7 @@ var init_cost_gemini = __esm({
9993
10234
  id: "gemini",
9994
10235
  available() {
9995
10236
  try {
9996
- return fs15.existsSync(geminiTmpDir());
10237
+ return fs16.existsSync(geminiTmpDir());
9997
10238
  } catch {
9998
10239
  return false;
9999
10240
  }
@@ -10002,13 +10243,13 @@ var init_cost_gemini = __esm({
10002
10243
  const combined = /* @__PURE__ */ new Map();
10003
10244
  for (const { file, project } of listGeminiSessionFiles(geminiTmpDir())) {
10004
10245
  try {
10005
- if (sinceMs !== void 0 && fs15.statSync(file).mtimeMs < sinceMs) continue;
10246
+ if (sinceMs !== void 0 && fs16.statSync(file).mtimeMs < sinceMs) continue;
10006
10247
  } catch {
10007
10248
  continue;
10008
10249
  }
10009
10250
  let content;
10010
10251
  try {
10011
- content = fs15.readFileSync(file, "utf8");
10252
+ content = fs16.readFileSync(file, "utf8");
10012
10253
  } catch {
10013
10254
  continue;
10014
10255
  }
@@ -10033,11 +10274,11 @@ var init_cost_gemini = __esm({
10033
10274
  });
10034
10275
 
10035
10276
  // src/cost-codex.ts
10036
- import fs16 from "fs";
10037
- import os15 from "os";
10038
- import path18 from "path";
10277
+ import fs17 from "fs";
10278
+ import os16 from "os";
10279
+ import path19 from "path";
10039
10280
  function codexSessionsDir() {
10040
- return path18.join(os15.homedir(), ".codex", "sessions");
10281
+ return path19.join(os16.homedir(), ".codex", "sessions");
10041
10282
  }
10042
10283
  function codexPriceFor(model) {
10043
10284
  return pricingFor(model) ?? CODEX_FALLBACK;
@@ -10050,16 +10291,16 @@ function codexSessionCost(model, tokens) {
10050
10291
  function listCodexSessionFiles(base) {
10051
10292
  const out = [];
10052
10293
  for (const y of safeReaddir2(base)) {
10053
- const yp = path18.join(base, y);
10294
+ const yp = path19.join(base, y);
10054
10295
  if (!isDir2(yp)) continue;
10055
10296
  for (const m of safeReaddir2(yp)) {
10056
- const mp = path18.join(yp, m);
10297
+ const mp = path19.join(yp, m);
10057
10298
  if (!isDir2(mp)) continue;
10058
10299
  for (const d of safeReaddir2(mp)) {
10059
- const dp = path18.join(mp, d);
10300
+ const dp = path19.join(mp, d);
10060
10301
  if (!isDir2(dp)) continue;
10061
10302
  for (const f of safeReaddir2(dp)) {
10062
- if (f.endsWith(".jsonl")) out.push(path18.join(dp, f));
10303
+ if (f.endsWith(".jsonl")) out.push(path19.join(dp, f));
10063
10304
  }
10064
10305
  }
10065
10306
  }
@@ -10068,14 +10309,14 @@ function listCodexSessionFiles(base) {
10068
10309
  }
10069
10310
  function safeReaddir2(dir) {
10070
10311
  try {
10071
- return fs16.readdirSync(dir);
10312
+ return fs17.readdirSync(dir);
10072
10313
  } catch {
10073
10314
  return [];
10074
10315
  }
10075
10316
  }
10076
10317
  function isDir2(p) {
10077
10318
  try {
10078
- return fs16.statSync(p).isDirectory();
10319
+ return fs17.statSync(p).isDirectory();
10079
10320
  } catch {
10080
10321
  return false;
10081
10322
  }
@@ -10145,7 +10386,7 @@ var init_cost_codex = __esm({
10145
10386
  id: "codex",
10146
10387
  available() {
10147
10388
  try {
10148
- return fs16.existsSync(codexSessionsDir());
10389
+ return fs17.existsSync(codexSessionsDir());
10149
10390
  } catch {
10150
10391
  return false;
10151
10392
  }
@@ -10155,13 +10396,13 @@ var init_cost_codex = __esm({
10155
10396
  const combined = /* @__PURE__ */ new Map();
10156
10397
  for (const file of listCodexSessionFiles(base)) {
10157
10398
  try {
10158
- if (sinceMs !== void 0 && fs16.statSync(file).mtimeMs < sinceMs) continue;
10399
+ if (sinceMs !== void 0 && fs17.statSync(file).mtimeMs < sinceMs) continue;
10159
10400
  } catch {
10160
10401
  continue;
10161
10402
  }
10162
10403
  let content;
10163
10404
  try {
10164
- content = fs16.readFileSync(file, "utf8");
10405
+ content = fs17.readFileSync(file, "utf8");
10165
10406
  } catch {
10166
10407
  continue;
10167
10408
  }
@@ -10467,85 +10708,85 @@ var init_scan_summary = __esm({
10467
10708
 
10468
10709
  // src/cli/commands/blast.ts
10469
10710
  import chalk2 from "chalk";
10470
- import fs17 from "fs";
10471
- import path19 from "path";
10472
- import os16 from "os";
10711
+ import fs18 from "fs";
10712
+ import path20 from "path";
10713
+ import os17 from "os";
10473
10714
  function buildSensitivePaths(home, cwd) {
10474
10715
  return [
10475
10716
  {
10476
- full: path19.join(home, ".ssh", "id_rsa"),
10717
+ full: path20.join(home, ".ssh", "id_rsa"),
10477
10718
  label: "~/.ssh/id_rsa",
10478
10719
  description: "RSA private key \u2014 grants SSH access to your servers",
10479
10720
  score: 20
10480
10721
  },
10481
10722
  {
10482
- full: path19.join(home, ".ssh", "id_ed25519"),
10723
+ full: path20.join(home, ".ssh", "id_ed25519"),
10483
10724
  label: "~/.ssh/id_ed25519",
10484
10725
  description: "Ed25519 private key \u2014 grants SSH access to your servers",
10485
10726
  score: 20
10486
10727
  },
10487
10728
  {
10488
- full: path19.join(home, ".ssh", "id_ecdsa"),
10729
+ full: path20.join(home, ".ssh", "id_ecdsa"),
10489
10730
  label: "~/.ssh/id_ecdsa",
10490
10731
  description: "ECDSA private key \u2014 grants SSH access to your servers",
10491
10732
  score: 20
10492
10733
  },
10493
10734
  {
10494
- full: path19.join(home, ".aws", "credentials"),
10735
+ full: path20.join(home, ".aws", "credentials"),
10495
10736
  label: "~/.aws/credentials",
10496
10737
  description: "AWS access keys \u2014 full cloud account access",
10497
10738
  score: 20
10498
10739
  },
10499
10740
  {
10500
- full: path19.join(home, ".aws", "config"),
10741
+ full: path20.join(home, ".aws", "config"),
10501
10742
  label: "~/.aws/config",
10502
10743
  description: "AWS configuration \u2014 account and region settings",
10503
10744
  score: 5
10504
10745
  },
10505
10746
  {
10506
- full: path19.join(home, ".config", "gcloud", "credentials.db"),
10747
+ full: path20.join(home, ".config", "gcloud", "credentials.db"),
10507
10748
  label: "~/.config/gcloud/credentials.db",
10508
10749
  description: "Google Cloud credentials",
10509
10750
  score: 15
10510
10751
  },
10511
10752
  {
10512
- full: path19.join(home, ".docker", "config.json"),
10753
+ full: path20.join(home, ".docker", "config.json"),
10513
10754
  label: "~/.docker/config.json",
10514
10755
  description: "Docker registry auth tokens",
10515
10756
  score: 10
10516
10757
  },
10517
10758
  {
10518
- full: path19.join(home, ".netrc"),
10759
+ full: path20.join(home, ".netrc"),
10519
10760
  label: "~/.netrc",
10520
10761
  description: "FTP/HTTP credentials in plain text",
10521
10762
  score: 15
10522
10763
  },
10523
10764
  {
10524
- full: path19.join(home, ".npmrc"),
10765
+ full: path20.join(home, ".npmrc"),
10525
10766
  label: "~/.npmrc",
10526
10767
  description: "npm auth token \u2014 can publish packages as you",
10527
10768
  score: 10
10528
10769
  },
10529
10770
  {
10530
- full: path19.join(home, ".node9", "credentials.json"),
10771
+ full: path20.join(home, ".node9", "credentials.json"),
10531
10772
  label: "~/.node9/credentials.json",
10532
10773
  description: "Node9 cloud API key",
10533
10774
  score: 10
10534
10775
  },
10535
10776
  {
10536
- full: path19.join(cwd, ".env"),
10777
+ full: path20.join(cwd, ".env"),
10537
10778
  label: ".env (current folder)",
10538
10779
  description: "App secrets \u2014 database passwords, API keys",
10539
10780
  score: 20
10540
10781
  },
10541
10782
  {
10542
- full: path19.join(cwd, ".env.local"),
10783
+ full: path20.join(cwd, ".env.local"),
10543
10784
  label: ".env.local (current folder)",
10544
10785
  description: "Local overrides \u2014 often contains real credentials",
10545
10786
  score: 15
10546
10787
  },
10547
10788
  {
10548
- full: path19.join(cwd, ".env.production"),
10789
+ full: path20.join(cwd, ".env.production"),
10549
10790
  label: ".env.production (current folder)",
10550
10791
  description: "Production secrets",
10551
10792
  score: 20
@@ -10554,7 +10795,7 @@ function buildSensitivePaths(home, cwd) {
10554
10795
  }
10555
10796
  function isReadable(filePath) {
10556
10797
  try {
10557
- fs17.accessSync(filePath, fs17.constants.R_OK);
10798
+ fs18.accessSync(filePath, fs18.constants.R_OK);
10558
10799
  return true;
10559
10800
  } catch {
10560
10801
  return false;
@@ -10567,13 +10808,13 @@ function scoreLabel(score) {
10567
10808
  return chalk2.red.bold(`${score}/100 Critical`);
10568
10809
  }
10569
10810
  function runBlast() {
10570
- const home = os16.homedir();
10811
+ const home = os17.homedir();
10571
10812
  const cwd = process.cwd();
10572
10813
  const paths = buildSensitivePaths(home, cwd);
10573
10814
  let scoreDeduction = 0;
10574
10815
  const reachable = [];
10575
10816
  for (const p of paths) {
10576
- if (fs17.existsSync(p.full) && isReadable(p.full)) {
10817
+ if (fs18.existsSync(p.full) && isReadable(p.full)) {
10577
10818
  reachable.push(p);
10578
10819
  scoreDeduction += p.score;
10579
10820
  }
@@ -10591,7 +10832,7 @@ function runBlast() {
10591
10832
  }
10592
10833
  function registerBlastCommand(program2) {
10593
10834
  program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
10594
- const home = os16.homedir();
10835
+ const home = os17.homedir();
10595
10836
  const cwd = process.cwd();
10596
10837
  const { reachable, envFindings, score } = runBlast();
10597
10838
  console.log("");
@@ -10767,17 +11008,17 @@ var init_scan_json = __esm({
10767
11008
  });
10768
11009
 
10769
11010
  // src/cli/render/scan-history.ts
10770
- import fs18 from "fs";
10771
- import path20 from "path";
10772
- import os17 from "os";
11011
+ import fs19 from "fs";
11012
+ import path21 from "path";
11013
+ import os18 from "os";
10773
11014
  function defaultHistoryPath() {
10774
- return path20.join(os17.homedir(), ".node9", "scan-history.json");
11015
+ return path21.join(os18.homedir(), ".node9", "scan-history.json");
10775
11016
  }
10776
11017
  function readPreviousScan(opts = {}) {
10777
11018
  const filePath = opts.path ?? defaultHistoryPath();
10778
11019
  try {
10779
- if (!fs18.existsSync(filePath)) return null;
10780
- const raw = fs18.readFileSync(filePath, "utf8");
11020
+ if (!fs19.existsSync(filePath)) return null;
11021
+ const raw = fs19.readFileSync(filePath, "utf8");
10781
11022
  const parsed = JSON.parse(raw);
10782
11023
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
10783
11024
  const last = parsed[parsed.length - 1];
@@ -10791,11 +11032,11 @@ function appendScanHistory(record, opts = {}) {
10791
11032
  const filePath = opts.path ?? defaultHistoryPath();
10792
11033
  const cap = opts.cap ?? SCAN_HISTORY_CAP;
10793
11034
  try {
10794
- fs18.mkdirSync(path20.dirname(filePath), { recursive: true });
11035
+ fs19.mkdirSync(path21.dirname(filePath), { recursive: true });
10795
11036
  let history = [];
10796
- if (fs18.existsSync(filePath)) {
11037
+ if (fs19.existsSync(filePath)) {
10797
11038
  try {
10798
- const parsed = JSON.parse(fs18.readFileSync(filePath, "utf8"));
11039
+ const parsed = JSON.parse(fs19.readFileSync(filePath, "utf8"));
10799
11040
  if (Array.isArray(parsed)) {
10800
11041
  history = parsed.filter(isValidRecord);
10801
11042
  }
@@ -10806,7 +11047,7 @@ function appendScanHistory(record, opts = {}) {
10806
11047
  if (history.length > cap) {
10807
11048
  history = history.slice(history.length - cap);
10808
11049
  }
10809
- fs18.writeFileSync(filePath, JSON.stringify(history, null, 2));
11050
+ fs19.writeFileSync(filePath, JSON.stringify(history, null, 2));
10810
11051
  } catch (err2) {
10811
11052
  process.stderr.write(
10812
11053
  `[node9] Warning: could not write scan-history.json: ${err2.message}
@@ -10837,15 +11078,15 @@ var init_scan_history = __esm({
10837
11078
  });
10838
11079
 
10839
11080
  // src/cost-copilot.ts
10840
- import fs19 from "fs";
10841
- import os18 from "os";
10842
- import path21 from "path";
11081
+ import fs20 from "fs";
11082
+ import os19 from "os";
11083
+ import path22 from "path";
10843
11084
  function copilotSessionsDir() {
10844
- return path21.join(os18.homedir(), ".copilot", "session-state");
11085
+ return path22.join(os19.homedir(), ".copilot", "session-state");
10845
11086
  }
10846
11087
  function safeReaddir3(dir) {
10847
11088
  try {
10848
- return fs19.readdirSync(dir);
11089
+ return fs20.readdirSync(dir);
10849
11090
  } catch {
10850
11091
  return [];
10851
11092
  }
@@ -10921,7 +11162,7 @@ var init_cost_copilot = __esm({
10921
11162
  id: "copilot",
10922
11163
  available() {
10923
11164
  try {
10924
- return fs19.existsSync(copilotSessionsDir());
11165
+ return fs20.existsSync(copilotSessionsDir());
10925
11166
  } catch {
10926
11167
  return false;
10927
11168
  }
@@ -10930,15 +11171,15 @@ var init_cost_copilot = __esm({
10930
11171
  const base = copilotSessionsDir();
10931
11172
  const combined = /* @__PURE__ */ new Map();
10932
11173
  for (const sid of safeReaddir3(base)) {
10933
- const file = path21.join(base, sid, "events.jsonl");
11174
+ const file = path22.join(base, sid, "events.jsonl");
10934
11175
  try {
10935
- if (sinceMs !== void 0 && fs19.statSync(file).mtimeMs < sinceMs) continue;
11176
+ if (sinceMs !== void 0 && fs20.statSync(file).mtimeMs < sinceMs) continue;
10936
11177
  } catch {
10937
11178
  continue;
10938
11179
  }
10939
11180
  let content;
10940
11181
  try {
10941
- content = fs19.readFileSync(file, "utf8");
11182
+ content = fs20.readFileSync(file, "utf8");
10942
11183
  } catch {
10943
11184
  continue;
10944
11185
  }
@@ -10963,17 +11204,17 @@ var init_cost_copilot = __esm({
10963
11204
  });
10964
11205
 
10965
11206
  // src/costSync.ts
10966
- import fs20 from "fs";
10967
- import path22 from "path";
10968
- import os19 from "os";
11207
+ import fs21 from "fs";
11208
+ import path23 from "path";
11209
+ import os20 from "os";
10969
11210
  function decodeProjectDirName(dirName) {
10970
11211
  return dirName.replace(/-/g, "/");
10971
11212
  }
10972
11213
  function parseJSONLFile(filePath, fallbackWorkingDir) {
10973
- const runId = path22.basename(filePath, ".jsonl");
11214
+ const runId = path23.basename(filePath, ".jsonl");
10974
11215
  let content;
10975
11216
  try {
10976
- content = fs20.readFileSync(filePath, "utf8");
11217
+ content = fs21.readFileSync(filePath, "utf8");
10977
11218
  } catch {
10978
11219
  return /* @__PURE__ */ new Map();
10979
11220
  }
@@ -11073,7 +11314,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11073
11314
  signal: AbortSignal.timeout(15e3)
11074
11315
  });
11075
11316
  if (!res.ok) {
11076
- fs20.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
11317
+ fs21.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
11077
11318
  `);
11078
11319
  } else {
11079
11320
  let stored;
@@ -11083,7 +11324,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11083
11324
  } catch {
11084
11325
  }
11085
11326
  if (typeof stored === "number" && stored < batch.length) {
11086
- fs20.appendFileSync(
11327
+ fs21.appendFileSync(
11087
11328
  HOOK_DEBUG_LOG,
11088
11329
  `[cost-sync] dropped ${batch.length - stored} of ${batch.length} rows
11089
11330
  `
@@ -11091,7 +11332,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11091
11332
  }
11092
11333
  }
11093
11334
  } catch (err2) {
11094
- fs20.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
11335
+ fs21.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
11095
11336
  `);
11096
11337
  }
11097
11338
  }
@@ -11104,10 +11345,10 @@ async function syncCost() {
11104
11345
  if (entries.length === 0) return;
11105
11346
  let username = "unknown";
11106
11347
  try {
11107
- username = os19.userInfo().username;
11348
+ username = os20.userInfo().username;
11108
11349
  } catch {
11109
11350
  }
11110
- const machineId = `${os19.hostname()}:${username}`;
11351
+ const machineId = `${os20.hostname()}:${username}`;
11111
11352
  await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
11112
11353
  }
11113
11354
  function startCostSync() {
@@ -11133,37 +11374,37 @@ var init_costSync = __esm({
11133
11374
  claudeSource = {
11134
11375
  id: "claude",
11135
11376
  available() {
11136
- return fs20.existsSync(path22.join(os19.homedir(), ".claude", "projects"));
11377
+ return fs21.existsSync(path23.join(os20.homedir(), ".claude", "projects"));
11137
11378
  },
11138
11379
  collect(sinceMs) {
11139
- const projectsDir = path22.join(os19.homedir(), ".claude", "projects");
11140
- if (!fs20.existsSync(projectsDir)) return [];
11380
+ const projectsDir = path23.join(os20.homedir(), ".claude", "projects");
11381
+ if (!fs21.existsSync(projectsDir)) return [];
11141
11382
  const combined = /* @__PURE__ */ new Map();
11142
11383
  let dirs;
11143
11384
  try {
11144
- dirs = fs20.readdirSync(projectsDir);
11385
+ dirs = fs21.readdirSync(projectsDir);
11145
11386
  } catch {
11146
11387
  return [];
11147
11388
  }
11148
11389
  for (const dir of dirs) {
11149
- const dirPath = path22.join(projectsDir, dir);
11390
+ const dirPath = path23.join(projectsDir, dir);
11150
11391
  try {
11151
- if (!fs20.statSync(dirPath).isDirectory()) continue;
11392
+ if (!fs21.statSync(dirPath).isDirectory()) continue;
11152
11393
  } catch {
11153
11394
  continue;
11154
11395
  }
11155
11396
  let files;
11156
11397
  try {
11157
- files = fs20.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11398
+ files = fs21.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11158
11399
  } catch {
11159
11400
  continue;
11160
11401
  }
11161
11402
  const fallbackWorkingDir = decodeProjectDirName(dir);
11162
11403
  for (const file of files) {
11163
- const filePath = path22.join(dirPath, file);
11404
+ const filePath = path23.join(dirPath, file);
11164
11405
  if (sinceMs !== void 0) {
11165
11406
  try {
11166
- if (fs20.statSync(filePath).mtimeMs < sinceMs) continue;
11407
+ if (fs21.statSync(filePath).mtimeMs < sinceMs) continue;
11167
11408
  } catch {
11168
11409
  continue;
11169
11410
  }
@@ -11203,9 +11444,9 @@ __export(scan_watermark_exports, {
11203
11444
  tickForensicBroadcast: () => tickForensicBroadcast,
11204
11445
  tickScanWatcher: () => tickScanWatcher
11205
11446
  });
11206
- import fs21 from "fs";
11207
- import os20 from "os";
11208
- import path23 from "path";
11447
+ import fs22 from "fs";
11448
+ import os21 from "os";
11449
+ import path24 from "path";
11209
11450
  import readline from "readline";
11210
11451
  function freshWatermark() {
11211
11452
  return {
@@ -11218,7 +11459,7 @@ function freshWatermark() {
11218
11459
  function loadWatermark() {
11219
11460
  let raw;
11220
11461
  try {
11221
- raw = fs21.readFileSync(WATERMARK_FILE(), "utf-8");
11462
+ raw = fs22.readFileSync(WATERMARK_FILE(), "utf-8");
11222
11463
  } catch {
11223
11464
  return { status: "fresh", wm: freshWatermark() };
11224
11465
  }
@@ -11270,28 +11511,28 @@ function loadWatermark() {
11270
11511
  function saveWatermark(wm) {
11271
11512
  if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
11272
11513
  const target = WATERMARK_FILE();
11273
- const dir = path23.dirname(target);
11274
- if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
11514
+ const dir = path24.dirname(target);
11515
+ if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
11275
11516
  const tmp = target + ".tmp";
11276
- fs21.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
11277
- fs21.renameSync(tmp, target);
11517
+ fs22.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
11518
+ fs22.renameSync(tmp, target);
11278
11519
  }
11279
11520
  function listJsonlFiles() {
11280
11521
  const root = PROJECTS_DIR();
11281
- if (!fs21.existsSync(root)) return [];
11522
+ if (!fs22.existsSync(root)) return [];
11282
11523
  const out = [];
11283
- for (const entry of fs21.readdirSync(root, { withFileTypes: true })) {
11524
+ for (const entry of fs22.readdirSync(root, { withFileTypes: true })) {
11284
11525
  if (!entry.isDirectory()) continue;
11285
- const projectDir = path23.join(root, entry.name);
11526
+ const projectDir = path24.join(root, entry.name);
11286
11527
  let inner;
11287
11528
  try {
11288
- inner = fs21.readdirSync(projectDir, { withFileTypes: true });
11529
+ inner = fs22.readdirSync(projectDir, { withFileTypes: true });
11289
11530
  } catch {
11290
11531
  continue;
11291
11532
  }
11292
11533
  for (const file of inner) {
11293
11534
  if (file.isFile() && file.name.endsWith(".jsonl")) {
11294
- out.push(path23.join(projectDir, file.name));
11535
+ out.push(path24.join(projectDir, file.name));
11295
11536
  }
11296
11537
  }
11297
11538
  }
@@ -11299,7 +11540,7 @@ function listJsonlFiles() {
11299
11540
  }
11300
11541
  function fileSize(p) {
11301
11542
  try {
11302
- return fs21.statSync(p).size;
11543
+ return fs22.statSync(p).size;
11303
11544
  } catch {
11304
11545
  return 0;
11305
11546
  }
@@ -11307,7 +11548,7 @@ function fileSize(p) {
11307
11548
  async function scanDelta(filePath, fromByte, onLine) {
11308
11549
  const size = fileSize(filePath);
11309
11550
  if (size <= fromByte) return fromByte;
11310
- const stream = fs21.createReadStream(filePath, {
11551
+ const stream = fs22.createReadStream(filePath, {
11311
11552
  start: fromByte,
11312
11553
  end: size - 1,
11313
11554
  highWaterMark: 64 * 1024
@@ -11419,7 +11660,7 @@ async function tickForensicBroadcast(offsets) {
11419
11660
  continue;
11420
11661
  }
11421
11662
  if (size <= offset) continue;
11422
- const sessionId = path23.basename(file, ".jsonl");
11663
+ const sessionId = path24.basename(file, ".jsonl");
11423
11664
  const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
11424
11665
  out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
11425
11666
  });
@@ -11478,7 +11719,7 @@ function emptyTick(uploadAs) {
11478
11719
  function readRawWatermarkPreservingOffsets() {
11479
11720
  let raw;
11480
11721
  try {
11481
- raw = fs21.readFileSync(WATERMARK_FILE(), "utf-8");
11722
+ raw = fs22.readFileSync(WATERMARK_FILE(), "utf-8");
11482
11723
  } catch {
11483
11724
  return null;
11484
11725
  }
@@ -11512,13 +11753,13 @@ async function runActualTick(wm) {
11512
11753
  if (!known) {
11513
11754
  let mtimeMs = 0;
11514
11755
  try {
11515
- mtimeMs = fs21.statSync(filePath).mtime.getTime();
11756
+ mtimeMs = fs22.statSync(filePath).mtime.getTime();
11516
11757
  } catch {
11517
11758
  continue;
11518
11759
  }
11519
11760
  if (mtimeMs >= watermarkCreatedAt) {
11520
11761
  filesNew++;
11521
- const sessionId2 = path23.basename(filePath, ".jsonl");
11762
+ const sessionId2 = path24.basename(filePath, ".jsonl");
11522
11763
  const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
11523
11764
  totalToolCalls++;
11524
11765
  toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
@@ -11536,7 +11777,7 @@ async function runActualTick(wm) {
11536
11777
  filesSkipped++;
11537
11778
  continue;
11538
11779
  }
11539
- const sessionId = path23.basename(filePath, ".jsonl");
11780
+ const sessionId = path24.basename(filePath, ".jsonl");
11540
11781
  const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
11541
11782
  totalToolCalls++;
11542
11783
  toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
@@ -11564,8 +11805,8 @@ var init_scan_watermark = __esm({
11564
11805
  "use strict";
11565
11806
  init_dlp();
11566
11807
  init_dist();
11567
- PROJECTS_DIR = () => path23.join(os20.homedir(), ".claude", "projects");
11568
- WATERMARK_FILE = () => path23.join(os20.homedir(), ".node9", "scan-watermark.json");
11808
+ PROJECTS_DIR = () => path24.join(os21.homedir(), ".claude", "projects");
11809
+ WATERMARK_FILE = () => path24.join(os21.homedir(), ".node9", "scan-watermark.json");
11569
11810
  MAX_LINE_BYTES = 2 * 1024 * 1024;
11570
11811
  WATERMARK_SCHEMA_VERSION = 2;
11571
11812
  LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
@@ -11581,10 +11822,10 @@ __export(scan_upload_history_exports, {
11581
11822
  parseSinceCutoff: () => parseSinceCutoff,
11582
11823
  runUploadHistory: () => runUploadHistory
11583
11824
  });
11584
- import fs22 from "fs";
11825
+ import fs23 from "fs";
11585
11826
  import https from "https";
11586
- import os21 from "os";
11587
- import path24 from "path";
11827
+ import os22 from "os";
11828
+ import path25 from "path";
11588
11829
  import chalk4 from "chalk";
11589
11830
  function emptySignals2() {
11590
11831
  return {
@@ -11619,40 +11860,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
11619
11860
  return now.getTime() - 90 * 864e5;
11620
11861
  }
11621
11862
  function* iterateJsonlFiles(cutoffMs) {
11622
- const projectsDir = path24.join(os21.homedir(), ".claude", "projects");
11863
+ const projectsDir = path25.join(os22.homedir(), ".claude", "projects");
11623
11864
  let dirs;
11624
11865
  try {
11625
- dirs = fs22.readdirSync(projectsDir);
11866
+ dirs = fs23.readdirSync(projectsDir);
11626
11867
  } catch {
11627
11868
  return;
11628
11869
  }
11629
11870
  for (const dir of dirs) {
11630
- const dirPath = path24.join(projectsDir, dir);
11871
+ const dirPath = path25.join(projectsDir, dir);
11631
11872
  let stats;
11632
11873
  try {
11633
- stats = fs22.statSync(dirPath);
11874
+ stats = fs23.statSync(dirPath);
11634
11875
  } catch {
11635
11876
  continue;
11636
11877
  }
11637
11878
  if (!stats.isDirectory()) continue;
11638
11879
  let files;
11639
11880
  try {
11640
- files = fs22.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11881
+ files = fs23.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11641
11882
  } catch {
11642
11883
  continue;
11643
11884
  }
11644
11885
  for (const file of files) {
11645
- const filePath = path24.join(dirPath, file);
11886
+ const filePath = path25.join(dirPath, file);
11646
11887
  let mtime = 0;
11647
11888
  try {
11648
- mtime = fs22.statSync(filePath).mtimeMs;
11889
+ mtime = fs23.statSync(filePath).mtimeMs;
11649
11890
  } catch {
11650
11891
  continue;
11651
11892
  }
11652
11893
  if (mtime < cutoffMs) continue;
11653
11894
  yield {
11654
11895
  filePath,
11655
- sessionId: path24.basename(file, ".jsonl"),
11896
+ sessionId: path25.basename(file, ".jsonl"),
11656
11897
  projectDir: dir
11657
11898
  };
11658
11899
  }
@@ -11720,7 +11961,7 @@ async function runUploadHistory(opts) {
11720
11961
  filesScanned++;
11721
11962
  let content;
11722
11963
  try {
11723
- content = fs22.readFileSync(filePath, "utf8");
11964
+ content = fs23.readFileSync(filePath, "utf8");
11724
11965
  } catch {
11725
11966
  continue;
11726
11967
  }
@@ -11794,10 +12035,10 @@ async function runUploadHistory(opts) {
11794
12035
  const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
11795
12036
  let username = "unknown";
11796
12037
  try {
11797
- username = os21.userInfo().username;
12038
+ username = os22.userInfo().username;
11798
12039
  } catch {
11799
12040
  }
11800
- const machineId = `${os21.hostname()}:${username}`;
12041
+ const machineId = `${os22.hostname()}:${username}`;
11801
12042
  await postJson(costUrl, creds.apiKey, {
11802
12043
  machineId,
11803
12044
  entries: dailyEntries
@@ -11871,9 +12112,9 @@ var init_scan_upload_history = __esm({
11871
12112
 
11872
12113
  // src/cli/commands/scan.ts
11873
12114
  import chalk5 from "chalk";
11874
- import fs23 from "fs";
11875
- import path25 from "path";
11876
- import os22 from "os";
12115
+ import fs24 from "fs";
12116
+ import path26 from "path";
12117
+ import os23 from "os";
11877
12118
  import stringWidth2 from "string-width";
11878
12119
  function claudeModelPrice(model) {
11879
12120
  const t = pricingFor(model);
@@ -12096,14 +12337,14 @@ function buildRuleSources() {
12096
12337
  }
12097
12338
  function countScanFiles() {
12098
12339
  let total = 0;
12099
- const claudeDir = path25.join(os22.homedir(), ".claude", "projects");
12100
- if (fs23.existsSync(claudeDir)) {
12340
+ const claudeDir = path26.join(os23.homedir(), ".claude", "projects");
12341
+ if (fs24.existsSync(claudeDir)) {
12101
12342
  try {
12102
- for (const proj of fs23.readdirSync(claudeDir)) {
12103
- const p = path25.join(claudeDir, proj);
12343
+ for (const proj of fs24.readdirSync(claudeDir)) {
12344
+ const p = path26.join(claudeDir, proj);
12104
12345
  try {
12105
- if (!fs23.statSync(p).isDirectory()) continue;
12106
- total += fs23.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
12346
+ if (!fs24.statSync(p).isDirectory()) continue;
12347
+ total += fs24.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
12107
12348
  } catch {
12108
12349
  continue;
12109
12350
  }
@@ -12111,17 +12352,17 @@ function countScanFiles() {
12111
12352
  } catch {
12112
12353
  }
12113
12354
  }
12114
- const geminiDir = path25.join(os22.homedir(), ".gemini", "tmp");
12115
- if (fs23.existsSync(geminiDir)) {
12355
+ const geminiDir = path26.join(os23.homedir(), ".gemini", "tmp");
12356
+ if (fs24.existsSync(geminiDir)) {
12116
12357
  try {
12117
- for (const slug2 of fs23.readdirSync(geminiDir)) {
12118
- const p = path25.join(geminiDir, slug2);
12358
+ for (const slug2 of fs24.readdirSync(geminiDir)) {
12359
+ const p = path26.join(geminiDir, slug2);
12119
12360
  try {
12120
- if (!fs23.statSync(p).isDirectory()) continue;
12121
- const chatsDir = path25.join(p, "chats");
12122
- if (fs23.existsSync(chatsDir)) {
12361
+ if (!fs24.statSync(p).isDirectory()) continue;
12362
+ const chatsDir = path26.join(p, "chats");
12363
+ if (fs24.existsSync(chatsDir)) {
12123
12364
  try {
12124
- total += fs23.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
12365
+ total += fs24.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
12125
12366
  } catch {
12126
12367
  }
12127
12368
  }
@@ -12133,15 +12374,15 @@ function countScanFiles() {
12133
12374
  }
12134
12375
  }
12135
12376
  for (const surface of ["antigravity-cli", "antigravity-ide"]) {
12136
- const brainDir = path25.join(os22.homedir(), ".gemini", surface, "brain");
12137
- if (!fs23.existsSync(brainDir)) continue;
12377
+ const brainDir = path26.join(os23.homedir(), ".gemini", surface, "brain");
12378
+ if (!fs24.existsSync(brainDir)) continue;
12138
12379
  try {
12139
- for (const conv of fs23.readdirSync(brainDir)) {
12140
- const convPath = path25.join(brainDir, conv);
12380
+ for (const conv of fs24.readdirSync(brainDir)) {
12381
+ const convPath = path26.join(brainDir, conv);
12141
12382
  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"))) {
12383
+ if (!fs24.statSync(convPath).isDirectory()) continue;
12384
+ const logsDir = path26.join(convPath, ".system_generated", "logs");
12385
+ if (fs24.existsSync(path26.join(logsDir, "transcript_full.jsonl")) || fs24.existsSync(path26.join(logsDir, "transcript.jsonl"))) {
12145
12386
  total += 1;
12146
12387
  }
12147
12388
  } catch {
@@ -12151,31 +12392,31 @@ function countScanFiles() {
12151
12392
  } catch {
12152
12393
  }
12153
12394
  }
12154
- const copilotDir = path25.join(os22.homedir(), ".copilot", "session-state");
12155
- if (fs23.existsSync(copilotDir)) {
12395
+ const copilotDir = path26.join(os23.homedir(), ".copilot", "session-state");
12396
+ if (fs24.existsSync(copilotDir)) {
12156
12397
  try {
12157
- for (const sid of fs23.readdirSync(copilotDir)) {
12158
- if (fs23.existsSync(path25.join(copilotDir, sid, "events.jsonl"))) total += 1;
12398
+ for (const sid of fs24.readdirSync(copilotDir)) {
12399
+ if (fs24.existsSync(path26.join(copilotDir, sid, "events.jsonl"))) total += 1;
12159
12400
  }
12160
12401
  } catch {
12161
12402
  }
12162
12403
  }
12163
- const codexDir = path25.join(os22.homedir(), ".codex", "sessions");
12164
- if (fs23.existsSync(codexDir)) {
12404
+ const codexDir = path26.join(os23.homedir(), ".codex", "sessions");
12405
+ if (fs24.existsSync(codexDir)) {
12165
12406
  try {
12166
- for (const year of fs23.readdirSync(codexDir)) {
12167
- const yp = path25.join(codexDir, year);
12407
+ for (const year of fs24.readdirSync(codexDir)) {
12408
+ const yp = path26.join(codexDir, year);
12168
12409
  try {
12169
- if (!fs23.statSync(yp).isDirectory()) continue;
12170
- for (const month of fs23.readdirSync(yp)) {
12171
- const mp = path25.join(yp, month);
12410
+ if (!fs24.statSync(yp).isDirectory()) continue;
12411
+ for (const month of fs24.readdirSync(yp)) {
12412
+ const mp = path26.join(yp, month);
12172
12413
  try {
12173
- if (!fs23.statSync(mp).isDirectory()) continue;
12174
- for (const day of fs23.readdirSync(mp)) {
12175
- const dp = path25.join(mp, day);
12414
+ if (!fs24.statSync(mp).isDirectory()) continue;
12415
+ for (const day of fs24.readdirSync(mp)) {
12416
+ const dp = path26.join(mp, day);
12176
12417
  try {
12177
- if (!fs23.statSync(dp).isDirectory()) continue;
12178
- total += fs23.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
12418
+ if (!fs24.statSync(dp).isDirectory()) continue;
12419
+ total += fs24.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
12179
12420
  } catch {
12180
12421
  continue;
12181
12422
  }
@@ -12211,7 +12452,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12211
12452
  const sessionId = file.replace(/\.jsonl$/, "");
12212
12453
  let raw;
12213
12454
  try {
12214
- raw = fs23.readFileSync(path25.join(projPath, file), "utf-8");
12455
+ raw = fs24.readFileSync(path26.join(projPath, file), "utf-8");
12215
12456
  } catch {
12216
12457
  return;
12217
12458
  }
@@ -12263,7 +12504,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12263
12504
  if (block.type !== "tool_result") continue;
12264
12505
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
12265
12506
  if (filePath) {
12266
- const ext = path25.extname(filePath).toLowerCase();
12507
+ const ext = path26.extname(filePath).toLowerCase();
12267
12508
  if (CODE_EXTENSIONS.has(ext)) continue;
12268
12509
  }
12269
12510
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -12320,7 +12561,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12320
12561
  const rawCmd = String(input.command ?? "").trimStart();
12321
12562
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
12322
12563
  const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
12323
- const inputFileExt = inputFilePath ? path25.extname(inputFilePath).toLowerCase() : "";
12564
+ const inputFileExt = inputFilePath ? path26.extname(inputFilePath).toLowerCase() : "";
12324
12565
  if (CODE_EXTENSIONS.has(inputFileExt)) continue;
12325
12566
  const dlpMatch = scanArgs(input);
12326
12567
  if (dlpMatch) {
@@ -12417,19 +12658,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12417
12658
  }
12418
12659
  }
12419
12660
  function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
12420
- const projPath = path25.join(projectsDir, proj);
12661
+ const projPath = path26.join(projectsDir, proj);
12421
12662
  try {
12422
- if (!fs23.statSync(projPath).isDirectory()) return;
12663
+ if (!fs24.statSync(projPath).isDirectory()) return;
12423
12664
  } catch {
12424
12665
  return;
12425
12666
  }
12426
- const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os22.homedir(), "~")).slice(
12667
+ const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(os23.homedir(), "~")).slice(
12427
12668
  0,
12428
12669
  40
12429
12670
  );
12430
12671
  let files;
12431
12672
  try {
12432
- files = fs23.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12673
+ files = fs24.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12433
12674
  } catch {
12434
12675
  return;
12435
12676
  }
@@ -12463,12 +12704,12 @@ function emptyClaudeScan() {
12463
12704
  };
12464
12705
  }
12465
12706
  function scanClaudeHistory(startDate, onProgress, onLine) {
12466
- const projectsDir = path25.join(os22.homedir(), ".claude", "projects");
12707
+ const projectsDir = path26.join(os23.homedir(), ".claude", "projects");
12467
12708
  const result = emptyClaudeScan();
12468
- if (!fs23.existsSync(projectsDir)) return result;
12709
+ if (!fs24.existsSync(projectsDir)) return result;
12469
12710
  let projDirs;
12470
12711
  try {
12471
- projDirs = fs23.readdirSync(projectsDir);
12712
+ projDirs = fs24.readdirSync(projectsDir);
12472
12713
  } catch {
12473
12714
  return result;
12474
12715
  }
@@ -12489,7 +12730,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
12489
12730
  return result;
12490
12731
  }
12491
12732
  function scanGeminiHistory(startDate, onProgress, onLine) {
12492
- const tmpDir = path25.join(os22.homedir(), ".gemini", "tmp");
12733
+ const tmpDir = path26.join(os23.homedir(), ".gemini", "tmp");
12493
12734
  const result = {
12494
12735
  filesScanned: 0,
12495
12736
  sessions: 0,
@@ -12504,33 +12745,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12504
12745
  sessionsWithEarlySecrets: 0
12505
12746
  };
12506
12747
  const dedup = emptyScanDedup();
12507
- if (!fs23.existsSync(tmpDir)) return result;
12748
+ if (!fs24.existsSync(tmpDir)) return result;
12508
12749
  let slugDirs;
12509
12750
  try {
12510
- slugDirs = fs23.readdirSync(tmpDir);
12751
+ slugDirs = fs24.readdirSync(tmpDir);
12511
12752
  } catch {
12512
12753
  return result;
12513
12754
  }
12514
12755
  const ruleSources = buildRuleSources();
12515
12756
  for (const slug2 of slugDirs) {
12516
- const slugPath = path25.join(tmpDir, slug2);
12757
+ const slugPath = path26.join(tmpDir, slug2);
12517
12758
  try {
12518
- if (!fs23.statSync(slugPath).isDirectory()) continue;
12759
+ if (!fs24.statSync(slugPath).isDirectory()) continue;
12519
12760
  } catch {
12520
12761
  continue;
12521
12762
  }
12522
12763
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
12523
12764
  try {
12524
12765
  projLabel = stripTerminalEscapes(
12525
- fs23.readFileSync(path25.join(slugPath, ".project_root"), "utf-8").trim()
12526
- ).replace(os22.homedir(), "~").slice(0, 40);
12766
+ fs24.readFileSync(path26.join(slugPath, ".project_root"), "utf-8").trim()
12767
+ ).replace(os23.homedir(), "~").slice(0, 40);
12527
12768
  } catch {
12528
12769
  }
12529
- const chatsDir = path25.join(slugPath, "chats");
12530
- if (!fs23.existsSync(chatsDir)) continue;
12770
+ const chatsDir = path26.join(slugPath, "chats");
12771
+ if (!fs24.existsSync(chatsDir)) continue;
12531
12772
  let chatFiles;
12532
12773
  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")));
12774
+ chatFiles = fs24.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
12534
12775
  } catch {
12535
12776
  continue;
12536
12777
  }
@@ -12543,7 +12784,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12543
12784
  onProgress?.(result.filesScanned);
12544
12785
  let raw;
12545
12786
  try {
12546
- raw = fs23.readFileSync(path25.join(chatsDir, chatFile), "utf-8");
12787
+ raw = fs24.readFileSync(path26.join(chatsDir, chatFile), "utf-8");
12547
12788
  } catch {
12548
12789
  continue;
12549
12790
  }
@@ -12716,13 +12957,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12716
12957
  return result;
12717
12958
  }
12718
12959
  function antigravityBrainDirs() {
12719
- return ["antigravity-cli", "antigravity-ide"].map((surface) => path25.join(os22.homedir(), ".gemini", surface, "brain")).filter((p) => fs23.existsSync(p));
12960
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => path26.join(os23.homedir(), ".gemini", surface, "brain")).filter((p) => fs24.existsSync(p));
12720
12961
  }
12721
12962
  function antigravityTranscriptPath(convPath) {
12722
- const logsDir = path25.join(convPath, ".system_generated", "logs");
12963
+ const logsDir = path26.join(convPath, ".system_generated", "logs");
12723
12964
  for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
12724
- const p = path25.join(logsDir, name);
12725
- if (fs23.existsSync(p)) return p;
12965
+ const p = path26.join(logsDir, name);
12966
+ if (fs24.existsSync(p)) return p;
12726
12967
  }
12727
12968
  return null;
12728
12969
  }
@@ -12748,14 +12989,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12748
12989
  for (const brainDir of brainDirs) {
12749
12990
  let convDirs;
12750
12991
  try {
12751
- convDirs = fs23.readdirSync(brainDir);
12992
+ convDirs = fs24.readdirSync(brainDir);
12752
12993
  } catch {
12753
12994
  continue;
12754
12995
  }
12755
12996
  for (const conv of convDirs) {
12756
- const convPath = path25.join(brainDir, conv);
12997
+ const convPath = path26.join(brainDir, conv);
12757
12998
  try {
12758
- if (!fs23.statSync(convPath).isDirectory()) continue;
12999
+ if (!fs24.statSync(convPath).isDirectory()) continue;
12759
13000
  } catch {
12760
13001
  continue;
12761
13002
  }
@@ -12765,7 +13006,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12765
13006
  onProgress?.(result.filesScanned);
12766
13007
  let raw;
12767
13008
  try {
12768
- raw = fs23.readFileSync(transcriptFile, "utf-8");
13009
+ raw = fs24.readFileSync(transcriptFile, "utf-8");
12769
13010
  } catch {
12770
13011
  continue;
12771
13012
  }
@@ -12822,7 +13063,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12822
13063
  result.bashCalls++;
12823
13064
  const cwd = String(input.cwd ?? "");
12824
13065
  if (cwd && projLabel === conv.slice(0, 8)) {
12825
- projLabel = stripTerminalEscapes(cwd).replace(os22.homedir(), "~").slice(0, 40);
13066
+ projLabel = stripTerminalEscapes(cwd).replace(os23.homedir(), "~").slice(0, 40);
12826
13067
  }
12827
13068
  }
12828
13069
  const rawCmd = String(input.command ?? "").trimStart();
@@ -12922,7 +13163,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12922
13163
  return result;
12923
13164
  }
12924
13165
  function scanCopilotHistory(startDate, onProgress, onLine) {
12925
- const sessionDir = path25.join(os22.homedir(), ".copilot", "session-state");
13166
+ const sessionDir = path26.join(os23.homedir(), ".copilot", "session-state");
12926
13167
  const result = {
12927
13168
  filesScanned: 0,
12928
13169
  sessions: 0,
@@ -12938,22 +13179,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
12938
13179
  sessionsWithEarlySecrets: 0
12939
13180
  };
12940
13181
  const dedup = emptyScanDedup();
12941
- if (!fs23.existsSync(sessionDir)) return result;
13182
+ if (!fs24.existsSync(sessionDir)) return result;
12942
13183
  let sessionIds;
12943
13184
  try {
12944
- sessionIds = fs23.readdirSync(sessionDir);
13185
+ sessionIds = fs24.readdirSync(sessionDir);
12945
13186
  } catch {
12946
13187
  return result;
12947
13188
  }
12948
13189
  const ruleSources = buildRuleSources();
12949
13190
  for (const sessionId of sessionIds) {
12950
- const eventsPath = path25.join(sessionDir, sessionId, "events.jsonl");
12951
- if (!fs23.existsSync(eventsPath)) continue;
13191
+ const eventsPath = path26.join(sessionDir, sessionId, "events.jsonl");
13192
+ if (!fs24.existsSync(eventsPath)) continue;
12952
13193
  result.filesScanned++;
12953
13194
  onProgress?.(result.filesScanned);
12954
13195
  let raw;
12955
13196
  try {
12956
- raw = fs23.readFileSync(eventsPath, "utf-8");
13197
+ raw = fs24.readFileSync(eventsPath, "utf-8");
12957
13198
  } catch {
12958
13199
  continue;
12959
13200
  }
@@ -12973,7 +13214,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
12973
13214
  if (ev.type === "session.start") {
12974
13215
  const cwd = ev.data?.context?.cwd;
12975
13216
  if (typeof cwd === "string" && cwd) {
12976
- projLabel = stripTerminalEscapes(cwd).replace(os22.homedir(), "~").slice(0, 40);
13217
+ projLabel = stripTerminalEscapes(cwd).replace(os23.homedir(), "~").slice(0, 40);
12977
13218
  }
12978
13219
  continue;
12979
13220
  }
@@ -13105,7 +13346,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
13105
13346
  return result;
13106
13347
  }
13107
13348
  function scanCodexHistory(startDate, onProgress, onLine) {
13108
- const sessionsBase = path25.join(os22.homedir(), ".codex", "sessions");
13349
+ const sessionsBase = path26.join(os23.homedir(), ".codex", "sessions");
13109
13350
  const result = {
13110
13351
  filesScanned: 0,
13111
13352
  sessions: 0,
@@ -13120,32 +13361,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13120
13361
  sessionsWithEarlySecrets: 0
13121
13362
  };
13122
13363
  const dedup = emptyScanDedup();
13123
- if (!fs23.existsSync(sessionsBase)) return result;
13364
+ if (!fs24.existsSync(sessionsBase)) return result;
13124
13365
  const jsonlFiles = [];
13125
13366
  try {
13126
- for (const year of fs23.readdirSync(sessionsBase)) {
13127
- const yearPath = path25.join(sessionsBase, year);
13367
+ for (const year of fs24.readdirSync(sessionsBase)) {
13368
+ const yearPath = path26.join(sessionsBase, year);
13128
13369
  try {
13129
- if (!fs23.statSync(yearPath).isDirectory()) continue;
13370
+ if (!fs24.statSync(yearPath).isDirectory()) continue;
13130
13371
  } catch {
13131
13372
  continue;
13132
13373
  }
13133
- for (const month of fs23.readdirSync(yearPath)) {
13134
- const monthPath = path25.join(yearPath, month);
13374
+ for (const month of fs24.readdirSync(yearPath)) {
13375
+ const monthPath = path26.join(yearPath, month);
13135
13376
  try {
13136
- if (!fs23.statSync(monthPath).isDirectory()) continue;
13377
+ if (!fs24.statSync(monthPath).isDirectory()) continue;
13137
13378
  } catch {
13138
13379
  continue;
13139
13380
  }
13140
- for (const day of fs23.readdirSync(monthPath)) {
13141
- const dayPath = path25.join(monthPath, day);
13381
+ for (const day of fs24.readdirSync(monthPath)) {
13382
+ const dayPath = path26.join(monthPath, day);
13142
13383
  try {
13143
- if (!fs23.statSync(dayPath).isDirectory()) continue;
13384
+ if (!fs24.statSync(dayPath).isDirectory()) continue;
13144
13385
  } catch {
13145
13386
  continue;
13146
13387
  }
13147
- for (const file of fs23.readdirSync(dayPath)) {
13148
- if (file.endsWith(".jsonl")) jsonlFiles.push(path25.join(dayPath, file));
13388
+ for (const file of fs24.readdirSync(dayPath)) {
13389
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path26.join(dayPath, file));
13149
13390
  }
13150
13391
  }
13151
13392
  }
@@ -13159,7 +13400,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13159
13400
  onProgress?.(result.filesScanned);
13160
13401
  let lines;
13161
13402
  try {
13162
- lines = fs23.readFileSync(filePath, "utf-8").split("\n");
13403
+ lines = fs24.readFileSync(filePath, "utf-8").split("\n");
13163
13404
  } catch {
13164
13405
  continue;
13165
13406
  }
@@ -13186,7 +13427,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13186
13427
  sessionId = String(payload["id"] ?? filePath);
13187
13428
  startTime = String(payload["timestamp"] ?? "");
13188
13429
  const cwd = String(payload["cwd"] ?? "");
13189
- projLabel = stripTerminalEscapes(cwd.replace(os22.homedir(), "~")).slice(0, 40);
13430
+ projLabel = stripTerminalEscapes(cwd.replace(os23.homedir(), "~")).slice(0, 40);
13190
13431
  continue;
13191
13432
  }
13192
13433
  if (entry.type === "turn_context" && typeof payload["model"] === "string") {
@@ -13346,17 +13587,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13346
13587
  return result;
13347
13588
  }
13348
13589
  function scanShellConfig() {
13349
- const home = os22.homedir();
13590
+ const home = os23.homedir();
13350
13591
  const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
13351
- (f) => path25.join(home, f)
13592
+ (f) => path26.join(home, f)
13352
13593
  );
13353
13594
  const findings = [];
13354
13595
  const seen = /* @__PURE__ */ new Set();
13355
13596
  for (const filePath of configFiles) {
13356
- if (!fs23.existsSync(filePath)) continue;
13597
+ if (!fs24.existsSync(filePath)) continue;
13357
13598
  let lines;
13358
13599
  try {
13359
- lines = fs23.readFileSync(filePath, "utf-8").split("\n");
13600
+ lines = fs24.readFileSync(filePath, "utf-8").split("\n");
13360
13601
  } catch {
13361
13602
  continue;
13362
13603
  }
@@ -14162,7 +14403,7 @@ function registerScanCommand(program2) {
14162
14403
  if (!drillDown) {
14163
14404
  const useInk2 = !options.classic;
14164
14405
  if (useInk2) {
14165
- const scanInkPath = path25.join(__dirname, "scan-ink.mjs");
14406
+ const scanInkPath = path26.join(__dirname, "scan-ink.mjs");
14166
14407
  const dynamicImport = new Function("id", "return import(id)");
14167
14408
  const mod = await dynamicImport(`file://${scanInkPath}`);
14168
14409
  const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
@@ -14573,8 +14814,8 @@ var init_suggestion_tracker = __esm({
14573
14814
  });
14574
14815
 
14575
14816
  // src/daemon/taint-store.ts
14576
- import fs24 from "fs";
14577
- import path26 from "path";
14817
+ import fs25 from "fs";
14818
+ import path27 from "path";
14578
14819
  var DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
14579
14820
  var init_taint_store = __esm({
14580
14821
  "src/daemon/taint-store.ts"() {
@@ -14644,9 +14885,9 @@ var init_taint_store = __esm({
14644
14885
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
14645
14886
  _resolve(filePath) {
14646
14887
  try {
14647
- return fs24.realpathSync.native(path26.resolve(filePath));
14888
+ return fs25.realpathSync.native(path27.resolve(filePath));
14648
14889
  } catch {
14649
- return path26.resolve(filePath);
14890
+ return path27.resolve(filePath);
14650
14891
  }
14651
14892
  }
14652
14893
  };
@@ -14811,14 +15052,14 @@ var init_session_history = __esm({
14811
15052
 
14812
15053
  // src/daemon/state.ts
14813
15054
  import net2 from "net";
14814
- import fs25 from "fs";
14815
- import path27 from "path";
14816
- import os23 from "os";
15055
+ import fs26 from "fs";
15056
+ import path28 from "path";
15057
+ import os24 from "os";
14817
15058
  import { randomUUID as randomUUID3 } from "crypto";
14818
15059
  function loadInsightCounts() {
14819
15060
  try {
14820
- if (!fs25.existsSync(INSIGHT_COUNTS_FILE)) return;
14821
- const data = JSON.parse(fs25.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
15061
+ if (!fs26.existsSync(INSIGHT_COUNTS_FILE)) return;
15062
+ const data = JSON.parse(fs26.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
14822
15063
  for (const [tool, count] of Object.entries(data)) {
14823
15064
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
14824
15065
  }
@@ -14857,23 +15098,23 @@ function markRejectionHandlerRegistered() {
14857
15098
  daemonRejectionHandlerRegistered = true;
14858
15099
  }
14859
15100
  function atomicWriteSync2(filePath, data, options) {
14860
- const dir = path27.dirname(filePath);
14861
- if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
15101
+ const dir = path28.dirname(filePath);
15102
+ if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
14862
15103
  const tmpPath = `${filePath}.${randomUUID3()}.tmp`;
14863
15104
  try {
14864
- fs25.writeFileSync(tmpPath, data, options);
15105
+ fs26.writeFileSync(tmpPath, data, options);
14865
15106
  } catch (err2) {
14866
15107
  try {
14867
- fs25.unlinkSync(tmpPath);
15108
+ fs26.unlinkSync(tmpPath);
14868
15109
  } catch {
14869
15110
  }
14870
15111
  throw err2;
14871
15112
  }
14872
15113
  try {
14873
- fs25.renameSync(tmpPath, filePath);
15114
+ fs26.renameSync(tmpPath, filePath);
14874
15115
  } catch (err2) {
14875
15116
  try {
14876
- fs25.unlinkSync(tmpPath);
15117
+ fs26.unlinkSync(tmpPath);
14877
15118
  } catch {
14878
15119
  }
14879
15120
  throw err2;
@@ -14897,16 +15138,16 @@ function appendAuditLog(data) {
14897
15138
  decision: data.decision,
14898
15139
  source: "daemon"
14899
15140
  };
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");
15141
+ const dir = path28.dirname(AUDIT_LOG_FILE);
15142
+ if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
15143
+ fs26.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
14903
15144
  } catch {
14904
15145
  }
14905
15146
  }
14906
15147
  function getAuditHistory(limit = 20) {
14907
15148
  try {
14908
- if (!fs25.existsSync(AUDIT_LOG_FILE)) return [];
14909
- const lines = fs25.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
15149
+ if (!fs26.existsSync(AUDIT_LOG_FILE)) return [];
15150
+ const lines = fs26.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
14910
15151
  if (lines.length === 1 && lines[0] === "") return [];
14911
15152
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
14912
15153
  } catch {
@@ -14915,7 +15156,7 @@ function getAuditHistory(limit = 20) {
14915
15156
  }
14916
15157
  function getOrgName() {
14917
15158
  try {
14918
- if (fs25.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
15159
+ if (fs26.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
14919
15160
  } catch {
14920
15161
  }
14921
15162
  return null;
@@ -14923,8 +15164,8 @@ function getOrgName() {
14923
15164
  function writeGlobalSetting(key, value) {
14924
15165
  let config = {};
14925
15166
  try {
14926
- if (fs25.existsSync(GLOBAL_CONFIG_FILE)) {
14927
- config = JSON.parse(fs25.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
15167
+ if (fs26.existsSync(GLOBAL_CONFIG_FILE)) {
15168
+ config = JSON.parse(fs26.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
14928
15169
  }
14929
15170
  } catch {
14930
15171
  }
@@ -14936,8 +15177,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
14936
15177
  try {
14937
15178
  let trust = { entries: [] };
14938
15179
  try {
14939
- if (fs25.existsSync(TRUST_FILE2))
14940
- trust = JSON.parse(fs25.readFileSync(TRUST_FILE2, "utf-8"));
15180
+ if (fs26.existsSync(TRUST_FILE2))
15181
+ trust = JSON.parse(fs26.readFileSync(TRUST_FILE2, "utf-8"));
14941
15182
  } catch {
14942
15183
  }
14943
15184
  trust.entries = trust.entries.filter(
@@ -14954,8 +15195,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
14954
15195
  }
14955
15196
  function readPersistentDecisions() {
14956
15197
  try {
14957
- if (fs25.existsSync(DECISIONS_FILE)) {
14958
- return JSON.parse(fs25.readFileSync(DECISIONS_FILE, "utf-8"));
15198
+ if (fs26.existsSync(DECISIONS_FILE)) {
15199
+ return JSON.parse(fs26.readFileSync(DECISIONS_FILE, "utf-8"));
14959
15200
  }
14960
15201
  } catch {
14961
15202
  }
@@ -14983,7 +15224,7 @@ function estimateToolCost(tool, args) {
14983
15224
  const filePath = a.file_path ?? a.path;
14984
15225
  if (filePath) {
14985
15226
  try {
14986
- const bytes = fs25.statSync(filePath).size;
15227
+ const bytes = fs26.statSync(filePath).size;
14987
15228
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
14988
15229
  } catch {
14989
15230
  }
@@ -15054,7 +15295,7 @@ function abandonPending() {
15054
15295
  });
15055
15296
  if (autoStarted) {
15056
15297
  try {
15057
- fs25.unlinkSync(DAEMON_PID_FILE);
15298
+ fs26.unlinkSync(DAEMON_PID_FILE);
15058
15299
  } catch {
15059
15300
  }
15060
15301
  setTimeout(() => {
@@ -15065,8 +15306,8 @@ function abandonPending() {
15065
15306
  }
15066
15307
  function logActivitySocket(msg) {
15067
15308
  try {
15068
- fs25.appendFileSync(
15069
- path27.join(homeDir, ".node9", "hook-debug.log"),
15309
+ fs26.appendFileSync(
15310
+ path28.join(homeDir, ".node9", "hook-debug.log"),
15070
15311
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
15071
15312
  `
15072
15313
  );
@@ -15088,13 +15329,13 @@ function shouldRebind(now = Date.now()) {
15088
15329
  function startActivitySocket() {
15089
15330
  bindActivitySocket();
15090
15331
  activityHealthInterval = setInterval(() => {
15091
- if (!fs25.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
15332
+ if (!fs26.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
15092
15333
  }, ACTIVITY_HEALTH_PROBE_MS);
15093
15334
  activityHealthInterval.unref();
15094
15335
  process.on("exit", () => {
15095
15336
  if (activityHealthInterval) clearInterval(activityHealthInterval);
15096
15337
  try {
15097
- fs25.unlinkSync(ACTIVITY_SOCKET_PATH2);
15338
+ fs26.unlinkSync(ACTIVITY_SOCKET_PATH2);
15098
15339
  } catch {
15099
15340
  }
15100
15341
  });
@@ -15122,7 +15363,7 @@ function attemptRebind(reason) {
15122
15363
  }
15123
15364
  function bindActivitySocket() {
15124
15365
  try {
15125
- fs25.unlinkSync(ACTIVITY_SOCKET_PATH2);
15366
+ fs26.unlinkSync(ACTIVITY_SOCKET_PATH2);
15126
15367
  } catch {
15127
15368
  }
15128
15369
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -15218,304 +15459,83 @@ function bindActivitySocket() {
15218
15459
  socket.on("error", () => {
15219
15460
  });
15220
15461
  });
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
- ];
15462
+ unixServer.on("error", (err2) => {
15463
+ logActivitySocket(`server error: ${err2.message}`);
15464
+ });
15465
+ unixServer.listen(ACTIVITY_SOCKET_PATH2, () => {
15466
+ logActivitySocket(`bound to ${ACTIVITY_SOCKET_PATH2}`);
15467
+ });
15468
+ activitySocketServer = unixServer;
15469
+ }
15470
+ 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;
15471
+ var init_state2 = __esm({
15472
+ "src/daemon/state.ts"() {
15473
+ "use strict";
15474
+ init_daemon();
15475
+ init_suggestion_tracker();
15476
+ init_taint_store();
15477
+ init_session_counters();
15478
+ init_session_history();
15479
+ homeDir = os24.homedir();
15480
+ DAEMON_PID_FILE = path28.join(homeDir, ".node9", "daemon.pid");
15481
+ DECISIONS_FILE = path28.join(homeDir, ".node9", "decisions.json");
15482
+ AUDIT_LOG_FILE = path28.join(homeDir, ".node9", "audit.log");
15483
+ TRUST_FILE2 = path28.join(homeDir, ".node9", "trust.json");
15484
+ GLOBAL_CONFIG_FILE = path28.join(homeDir, ".node9", "config.json");
15485
+ CREDENTIALS_FILE = path28.join(homeDir, ".node9", "credentials.json");
15486
+ INSIGHT_COUNTS_FILE = path28.join(homeDir, ".node9", "insight-counts.json");
15487
+ pending = /* @__PURE__ */ new Map();
15488
+ sseClients = /* @__PURE__ */ new Set();
15489
+ suggestionTracker = new SuggestionTracker(3);
15490
+ taintStore = new TaintStore();
15491
+ sessionTaintStore = new SessionTaintStore();
15492
+ insightCounts = /* @__PURE__ */ new Map();
15493
+ _abandonTimer = null;
15494
+ _hadBrowserClient = false;
15495
+ _daemonServer = null;
15496
+ daemonRejectionHandlerRegistered = false;
15497
+ AUTO_DENY_MS = 12e4;
15498
+ TRUST_DURATIONS = {
15499
+ "30m": 30 * 6e4,
15500
+ "1h": 60 * 6e4,
15501
+ "2h": 2 * 60 * 6e4
15502
+ };
15503
+ autoStarted = process.env.NODE9_AUTO_STARTED === "1";
15504
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path28.join(os24.tmpdir(), "node9-activity.sock");
15505
+ ACTIVITY_RING_SIZE = 100;
15506
+ activityRing = [];
15507
+ LARGE_RESPONSE_RING_SIZE = 20;
15508
+ largeResponseRing = [];
15509
+ cachedScanResult = null;
15510
+ cachedScanTs = 0;
15511
+ SCAN_CACHE_TTL_MS = 5 * 60 * 1e3;
15512
+ SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
15513
+ INPUT_PRICE_PER_1M = 3;
15514
+ OUTPUT_PRICE_PER_1M = 15;
15515
+ BYTES_PER_TOKEN = 4;
15516
+ CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
15517
+ "privilege-escalation",
15518
+ "destructive-op",
15519
+ "eval-of-remote"
15520
+ ]);
15521
+ WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
15522
+ "write",
15523
+ "write_file",
15524
+ "create_file",
15525
+ "edit",
15526
+ "multiedit",
15527
+ "str_replace_based_edit_tool",
15528
+ "replace",
15529
+ "notebook_edit",
15530
+ "notebookedit"
15531
+ ]);
15532
+ ACTIVITY_REBIND_MAX_ATTEMPTS = 5;
15533
+ ACTIVITY_REBIND_WINDOW_MS = 6e4;
15534
+ ACTIVITY_HEALTH_PROBE_MS = 2e3;
15535
+ activitySocketServer = null;
15536
+ activityHealthInterval = null;
15537
+ activityRebindAttempts = [];
15538
+ activityCircuitTripped = false;
15519
15539
  }
15520
15540
  });
15521
15541
 
@@ -16614,9 +16634,95 @@ var init_ship = __esm({
16614
16634
  }
16615
16635
  });
16616
16636
 
16637
+ // src/policy-snapshot/build.ts
16638
+ function buildPolicySnapshot(config, activeShields, overrides) {
16639
+ const p = config.policy;
16640
+ return {
16641
+ mode: config.settings.mode,
16642
+ panicMode: config.settings.panicMode === true,
16643
+ // The proxy expresses shadow/observe as mode === 'observe' (cloud shadowMode
16644
+ // forces it); there's no separate settings flag.
16645
+ shadowMode: config.settings.mode === "observe",
16646
+ activeShields,
16647
+ shieldOverrides: overrides,
16648
+ smartRuleCount: p.smartRules.length,
16649
+ smartRules: p.smartRules.slice(0, MAX_RULES).map((r) => ({
16650
+ name: r.name,
16651
+ tool: r.tool,
16652
+ verdict: r.verdict,
16653
+ reason: r.reason
16654
+ })),
16655
+ egress: {
16656
+ enabled: p.egress.enabled,
16657
+ mode: p.egress.mode,
16658
+ allow: p.egress.allow.slice(0, MAX_EGRESS)
16659
+ },
16660
+ dlpEnabled: p.dlp.enabled,
16661
+ engineVersion: ENGINE_VERSION
16662
+ };
16663
+ }
16664
+ var MAX_RULES, MAX_EGRESS;
16665
+ var init_build = __esm({
16666
+ "src/policy-snapshot/build.ts"() {
16667
+ "use strict";
16668
+ init_dist();
16669
+ MAX_RULES = 500;
16670
+ MAX_EGRESS = 200;
16671
+ }
16672
+ });
16673
+
16674
+ // src/policy-snapshot/ship.ts
16675
+ import http2 from "http";
16676
+ import https3 from "https";
16677
+ import { URL as URL3 } from "url";
16678
+ function policySnapshotUrlFrom(apiUrl) {
16679
+ return apiUrl.endsWith("/policies/sync") ? apiUrl.replace(/\/policies\/sync$/, "/policy/snapshot") : null;
16680
+ }
16681
+ async function shipPolicySnapshot(body, creds) {
16682
+ const url = policySnapshotUrlFrom(creds.apiUrl);
16683
+ if (!url) return false;
16684
+ const payload = JSON.stringify(body);
16685
+ const parsed = new URL3(url);
16686
+ const transport = parsed.protocol === "http:" ? http2 : https3;
16687
+ return new Promise((resolve) => {
16688
+ const req = transport.request(
16689
+ {
16690
+ hostname: parsed.hostname,
16691
+ port: parsed.port ? parseInt(parsed.port, 10) : void 0,
16692
+ path: parsed.pathname + parsed.search,
16693
+ method: "POST",
16694
+ headers: {
16695
+ "Content-Type": "application/json",
16696
+ "Content-Length": Buffer.byteLength(payload),
16697
+ Authorization: `Bearer ${creds.apiKey}`
16698
+ },
16699
+ timeout: 1e4
16700
+ },
16701
+ (res) => {
16702
+ const ok2 = !!res.statusCode && res.statusCode >= 200 && res.statusCode < 300;
16703
+ res.resume();
16704
+ res.on("end", () => resolve(ok2));
16705
+ res.on("error", () => resolve(false));
16706
+ }
16707
+ );
16708
+ req.on("error", () => resolve(false));
16709
+ req.on("timeout", () => {
16710
+ req.destroy();
16711
+ resolve(false);
16712
+ });
16713
+ req.write(payload);
16714
+ req.end();
16715
+ });
16716
+ }
16717
+ var init_ship2 = __esm({
16718
+ "src/policy-snapshot/ship.ts"() {
16719
+ "use strict";
16720
+ }
16721
+ });
16722
+
16617
16723
  // src/daemon/sync.ts
16618
16724
  import fs32 from "fs";
16619
- import https3 from "https";
16725
+ import https4 from "https";
16620
16726
  import os29 from "os";
16621
16727
  import path31 from "path";
16622
16728
  function emptySignals3() {
@@ -16650,6 +16756,11 @@ function buildSessionDeltas(findings, toolCallsBySession) {
16650
16756
  signals
16651
16757
  }));
16652
16758
  }
16759
+ function resolveSyncIntervalMs(settings) {
16760
+ const rawSeconds = settings.cloudSyncIntervalSeconds ?? (settings.cloudSyncIntervalHours ?? DEFAULT_INTERVAL_HOURS) * 3600;
16761
+ const clamped = Math.min(Math.max(rawSeconds, MIN_INTERVAL_SECONDS), MAX_INTERVAL_SECONDS);
16762
+ return clamped * 1e3;
16763
+ }
16653
16764
  function readCredentials() {
16654
16765
  if (process.env.NODE9_API_KEY) {
16655
16766
  return {
@@ -16699,7 +16810,7 @@ function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
16699
16810
  };
16700
16811
  if (ifNoneMatch) headers["If-None-Match"] = `"${ifNoneMatch}"`;
16701
16812
  return new Promise((resolve, reject) => {
16702
- const req = https3.request(
16813
+ const req = https4.request(
16703
16814
  {
16704
16815
  hostname: parsed.hostname,
16705
16816
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16779,6 +16890,9 @@ async function syncOnce() {
16779
16890
  if (process.env.NODE9_POSTURE_DISABLE !== "1") {
16780
16891
  void pushPostureSnapshot(creds);
16781
16892
  }
16893
+ if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
16894
+ void pushPolicySnapshot(creds);
16895
+ }
16782
16896
  }
16783
16897
  async function pushBlastSnapshot(creds) {
16784
16898
  try {
@@ -16788,7 +16902,7 @@ async function pushBlastSnapshot(creds) {
16788
16902
  if (!blastUrl) return;
16789
16903
  const parsed = new URL(blastUrl);
16790
16904
  await new Promise((resolve) => {
16791
- const req = https3.request(
16905
+ const req = https4.request(
16792
16906
  {
16793
16907
  hostname: parsed.hostname,
16794
16908
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16825,6 +16939,29 @@ async function pushPostureSnapshot(creds) {
16825
16939
  } catch {
16826
16940
  }
16827
16941
  }
16942
+ async function pushPolicySnapshot(creds) {
16943
+ try {
16944
+ const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
16945
+ await shipPolicySnapshot(body, creds);
16946
+ } catch {
16947
+ }
16948
+ }
16949
+ async function runPolicyPush() {
16950
+ const creds = readCredentials();
16951
+ if (!creds) {
16952
+ return {
16953
+ ok: false,
16954
+ reason: "No API key configured. Add credentials with: node9 login"
16955
+ };
16956
+ }
16957
+ try {
16958
+ const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
16959
+ const sent = await shipPolicySnapshot(body, creds);
16960
+ return sent ? { ok: true } : { ok: false, reason: "Push failed (network or server error)" };
16961
+ } catch (e) {
16962
+ return { ok: false, reason: e instanceof Error ? e.message : String(e) };
16963
+ }
16964
+ }
16828
16965
  async function pushScanSnapshot(creds) {
16829
16966
  try {
16830
16967
  const tick = await tickScanWatcher();
@@ -16842,7 +16979,7 @@ async function pushScanSnapshot(creds) {
16842
16979
  const parsed = new URL(scanUrl);
16843
16980
  let posted = false;
16844
16981
  await new Promise((resolve) => {
16845
- const req = https3.request(
16982
+ const req = https4.request(
16846
16983
  {
16847
16984
  hostname: parsed.hostname,
16848
16985
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16892,6 +17029,9 @@ async function runCloudSync() {
16892
17029
  if (process.env.NODE9_POSTURE_DISABLE !== "1") {
16893
17030
  void pushPostureSnapshot(creds);
16894
17031
  }
17032
+ if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
17033
+ void pushPolicySnapshot(creds);
17034
+ }
16895
17035
  };
16896
17036
  try {
16897
17037
  const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
@@ -16943,9 +17083,7 @@ function getCloudRules() {
16943
17083
  }
16944
17084
  }
16945
17085
  function startCloudSync() {
16946
- const rawHours = getConfig().settings.cloudSyncIntervalHours ?? DEFAULT_INTERVAL_HOURS;
16947
- const intervalHours = Math.max(rawHours, MIN_INTERVAL_HOURS);
16948
- const intervalMs = intervalHours * 60 * 60 * 1e3;
17086
+ const intervalMs = resolveSyncIntervalMs(getConfig().settings);
16949
17087
  const initial = setTimeout(() => void syncOnce(), 3e4);
16950
17088
  initial.unref();
16951
17089
  const recurring = setInterval(() => void syncOnce(), intervalMs);
@@ -16970,7 +17108,7 @@ function startForensicBroadcast() {
16970
17108
  const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
16971
17109
  recurring.unref();
16972
17110
  }
16973
- var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
17111
+ var FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_SECONDS, MAX_INTERVAL_SECONDS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
16974
17112
  var init_sync = __esm({
16975
17113
  "src/daemon/sync.ts"() {
16976
17114
  "use strict";
@@ -16978,6 +17116,9 @@ var init_sync = __esm({
16978
17116
  init_blast();
16979
17117
  init_posture();
16980
17118
  init_ship();
17119
+ init_build();
17120
+ init_ship2();
17121
+ init_shields();
16981
17122
  init_dist();
16982
17123
  init_scan_watermark();
16983
17124
  init_state2();
@@ -16997,7 +17138,8 @@ var init_sync = __esm({
16997
17138
  rulesCacheFile = () => path31.join(os29.homedir(), ".node9", "rules-cache.json");
16998
17139
  DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
16999
17140
  DEFAULT_INTERVAL_HOURS = 5;
17000
- MIN_INTERVAL_HOURS = 1;
17141
+ MIN_INTERVAL_SECONDS = 15;
17142
+ MAX_INTERVAL_SECONDS = 24 * 60 * 60;
17001
17143
  FORENSIC_BROADCAST_INTERVAL_MS = 3e4;
17002
17144
  FORENSIC_INITIAL_DELAY_MS = 5e3;
17003
17145
  forensicBroadcastOffsets = /* @__PURE__ */ new Map();
@@ -17451,7 +17593,7 @@ var init_mcp_tools = __esm({
17451
17593
  });
17452
17594
 
17453
17595
  // src/daemon/server.ts
17454
- import http2 from "http";
17596
+ import http3 from "http";
17455
17597
  import fs36 from "fs";
17456
17598
  import path35 from "path";
17457
17599
  import os33 from "os";
@@ -17486,7 +17628,7 @@ function startDaemon() {
17486
17628
  }
17487
17629
  resetIdleTimer();
17488
17630
  const allowedHosts = /* @__PURE__ */ new Set([`127.0.0.1:${DAEMON_PORT}`, `localhost:${DAEMON_PORT}`]);
17489
- const server = http2.createServer(async (req, res) => {
17631
+ const server = http3.createServer(async (req, res) => {
17490
17632
  const host = req.headers.host ?? "";
17491
17633
  if (!allowedHosts.has(host)) {
17492
17634
  res.writeHead(421, { "Content-Type": "text/plain" });
@@ -18720,11 +18862,11 @@ __export(tail_exports, {
18720
18862
  shortenPathSummary: () => shortenPathSummary,
18721
18863
  startTail: () => startTail
18722
18864
  });
18723
- import http3 from "http";
18865
+ import http4 from "http";
18724
18866
  import chalk35 from "chalk";
18725
18867
  import fs64 from "fs";
18726
- import os54 from "os";
18727
- import path61 from "path";
18868
+ import os55 from "os";
18869
+ import path62 from "path";
18728
18870
  import readline6 from "readline";
18729
18871
  import { spawn as spawn8 } from "child_process";
18730
18872
  function shortenPathSummary(s) {
@@ -18748,18 +18890,18 @@ function getModelContextLimit(model) {
18748
18890
  return 2e5;
18749
18891
  }
18750
18892
  function readSessionUsage() {
18751
- const projectsDir = path61.join(os54.homedir(), ".claude", "projects");
18893
+ const projectsDir = path62.join(os55.homedir(), ".claude", "projects");
18752
18894
  if (!fs64.existsSync(projectsDir)) return null;
18753
18895
  let latestFile = null;
18754
18896
  let latestMtime = 0;
18755
18897
  try {
18756
18898
  for (const dir of fs64.readdirSync(projectsDir)) {
18757
- const dirPath = path61.join(projectsDir, dir);
18899
+ const dirPath = path62.join(projectsDir, dir);
18758
18900
  try {
18759
18901
  if (!fs64.statSync(dirPath).isDirectory()) continue;
18760
18902
  for (const file of fs64.readdirSync(dirPath)) {
18761
18903
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
18762
- const filePath = path61.join(dirPath, file);
18904
+ const filePath = path62.join(dirPath, file);
18763
18905
  try {
18764
18906
  const mtime = fs64.statSync(filePath).mtimeMs;
18765
18907
  if (mtime > latestMtime) {
@@ -18837,7 +18979,7 @@ function formatBase(activity) {
18837
18979
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
18838
18980
  const icon = getIcon(activity.tool);
18839
18981
  const toolName = activity.tool.slice(0, 16).padEnd(16);
18840
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os54.homedir(), "~");
18982
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os55.homedir(), "~");
18841
18983
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
18842
18984
  return `${chalk35.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk35.white.bold(toolName)} ${chalk35.dim(argsPreview)}`;
18843
18985
  }
@@ -18919,7 +19061,7 @@ function postDecisionHttp(id, decision, authToken, port, opts) {
18919
19061
  if (opts?.trustDuration) bodyObj.trustDuration = opts.trustDuration;
18920
19062
  if (opts?.reason) bodyObj.reason = opts.reason;
18921
19063
  const body = JSON.stringify(bodyObj);
18922
- const req = http3.request(
19064
+ const req = http4.request(
18923
19065
  {
18924
19066
  hostname: "127.0.0.1",
18925
19067
  port,
@@ -19034,7 +19176,7 @@ function buildRecoveryCardLines(req) {
19034
19176
  ];
19035
19177
  }
19036
19178
  function readApproversFromDisk() {
19037
- const configPath = path61.join(os54.homedir(), ".node9", "config.json");
19179
+ const configPath = path62.join(os55.homedir(), ".node9", "config.json");
19038
19180
  try {
19039
19181
  const raw = JSON.parse(fs64.readFileSync(configPath, "utf-8"));
19040
19182
  const settings = raw.settings ?? {};
@@ -19052,7 +19194,7 @@ function approverStatusLine() {
19052
19194
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
19053
19195
  }
19054
19196
  function toggleApprover(channel) {
19055
- const configPath = path61.join(os54.homedir(), ".node9", "config.json");
19197
+ const configPath = path62.join(os55.homedir(), ".node9", "config.json");
19056
19198
  try {
19057
19199
  const raw = JSON.parse(fs64.readFileSync(configPath, "utf-8"));
19058
19200
  const settings = raw.settings ?? {};
@@ -19070,7 +19212,7 @@ async function startTail(options = {}) {
19070
19212
  const port = await ensureDaemon();
19071
19213
  if (options.clear) {
19072
19214
  const result = await new Promise((resolve) => {
19073
- const req2 = http3.request(
19215
+ const req2 = http4.request(
19074
19216
  { method: "POST", hostname: "127.0.0.1", port, path: "/events/clear" },
19075
19217
  (res) => {
19076
19218
  const status = res.statusCode ?? 0;
@@ -19233,7 +19375,7 @@ async function startTail(options = {}) {
19233
19375
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
19234
19376
  try {
19235
19377
  fs64.appendFileSync(
19236
- path61.join(os54.homedir(), ".node9", "hook-debug.log"),
19378
+ path62.join(os55.homedir(), ".node9", "hook-debug.log"),
19237
19379
  `[tail] POST /decision failed: ${String(err2)}
19238
19380
  `
19239
19381
  );
@@ -19297,7 +19439,7 @@ async function startTail(options = {}) {
19297
19439
  };
19298
19440
  process.stdin.on("keypress", onKeypress);
19299
19441
  }
19300
- const auditLog = path61.join(os54.homedir(), ".node9", "audit.log");
19442
+ const auditLog = path62.join(os55.homedir(), ".node9", "audit.log");
19301
19443
  try {
19302
19444
  const unackedDlp = fs64.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
19303
19445
  if (unackedDlp > 0) {
@@ -19353,7 +19495,7 @@ async function startTail(options = {}) {
19353
19495
  }, STALL_THRESHOLD_MS / 2);
19354
19496
  stallWatchdog.unref();
19355
19497
  const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
19356
- const req = http3.get(
19498
+ const req = http4.get(
19357
19499
  sseUrl,
19358
19500
  {
19359
19501
  headers: authToken ? { "X-Node9-Internal": authToken } : {}
@@ -19530,7 +19672,7 @@ var init_tail = __esm({
19530
19672
  "use strict";
19531
19673
  init_daemon2();
19532
19674
  init_daemon();
19533
- PID_FILE = path61.join(os54.homedir(), ".node9", "daemon.pid");
19675
+ PID_FILE = path62.join(os55.homedir(), ".node9", "daemon.pid");
19534
19676
  ICONS = {
19535
19677
  bash: "\u{1F4BB}",
19536
19678
  shell: "\u{1F4BB}",
@@ -19579,9 +19721,9 @@ __export(hud_exports, {
19579
19721
  renderEnvironmentLine: () => renderEnvironmentLine
19580
19722
  });
19581
19723
  import fs65 from "fs";
19582
- import path62 from "path";
19583
- import os55 from "os";
19584
- import http4 from "http";
19724
+ import path63 from "path";
19725
+ import os56 from "os";
19726
+ import http5 from "http";
19585
19727
  async function readStdin() {
19586
19728
  const chunks = [];
19587
19729
  for await (const chunk2 of process.stdin) {
@@ -19599,7 +19741,7 @@ function queryDaemon() {
19599
19741
  return new Promise((resolve) => {
19600
19742
  const timeout = setTimeout(() => resolve(null), 50);
19601
19743
  try {
19602
- const req = http4.get(
19744
+ const req = http5.get(
19603
19745
  `http://${DAEMON_HOST}:${DAEMON_PORT}/status`,
19604
19746
  { timeout: 50 },
19605
19747
  (res) => {
@@ -19684,7 +19826,7 @@ function countRulesInDir(rulesDir) {
19684
19826
  try {
19685
19827
  for (const entry of fs65.readdirSync(rulesDir, { withFileTypes: true })) {
19686
19828
  if (entry.isDirectory()) {
19687
- count += countRulesInDir(path62.join(rulesDir, entry.name));
19829
+ count += countRulesInDir(path63.join(rulesDir, entry.name));
19688
19830
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
19689
19831
  count++;
19690
19832
  }
@@ -19695,46 +19837,46 @@ function countRulesInDir(rulesDir) {
19695
19837
  }
19696
19838
  function isSamePath(a, b) {
19697
19839
  try {
19698
- return path62.resolve(a) === path62.resolve(b);
19840
+ return path63.resolve(a) === path63.resolve(b);
19699
19841
  } catch {
19700
19842
  return false;
19701
19843
  }
19702
19844
  }
19703
19845
  function countConfigs(cwd) {
19704
- const homeDir2 = os55.homedir();
19705
- const claudeDir = path62.join(homeDir2, ".claude");
19846
+ const homeDir2 = os56.homedir();
19847
+ const claudeDir = path63.join(homeDir2, ".claude");
19706
19848
  let claudeMdCount = 0;
19707
19849
  let rulesCount = 0;
19708
19850
  let hooksCount = 0;
19709
19851
  const userMcpServers = /* @__PURE__ */ new Set();
19710
19852
  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");
19853
+ if (fs65.existsSync(path63.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
19854
+ rulesCount += countRulesInDir(path63.join(claudeDir, "rules"));
19855
+ const userSettings = path63.join(claudeDir, "settings.json");
19714
19856
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
19715
19857
  hooksCount += countHooksInFile(userSettings);
19716
- const userClaudeJson = path62.join(homeDir2, ".claude.json");
19858
+ const userClaudeJson = path63.join(homeDir2, ".claude.json");
19717
19859
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
19718
19860
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
19719
19861
  userMcpServers.delete(name);
19720
19862
  }
19721
19863
  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");
19864
+ if (fs65.existsSync(path63.join(cwd, "CLAUDE.md"))) claudeMdCount++;
19865
+ if (fs65.existsSync(path63.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
19866
+ const projectClaudeDir = path63.join(cwd, ".claude");
19725
19867
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
19726
19868
  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");
19869
+ if (fs65.existsSync(path63.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
19870
+ rulesCount += countRulesInDir(path63.join(projectClaudeDir, "rules"));
19871
+ const projSettings = path63.join(projectClaudeDir, "settings.json");
19730
19872
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
19731
19873
  hooksCount += countHooksInFile(projSettings);
19732
19874
  }
19733
- if (fs65.existsSync(path62.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19734
- const localSettings = path62.join(projectClaudeDir, "settings.local.json");
19875
+ if (fs65.existsSync(path63.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19876
+ const localSettings = path63.join(projectClaudeDir, "settings.local.json");
19735
19877
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
19736
19878
  hooksCount += countHooksInFile(localSettings);
19737
- const mcpJsonServers = getMcpServerNames(path62.join(cwd, ".mcp.json"));
19879
+ const mcpJsonServers = getMcpServerNames(path63.join(cwd, ".mcp.json"));
19738
19880
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
19739
19881
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
19740
19882
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -19767,7 +19909,7 @@ function readActiveShieldsHud() {
19767
19909
  return shieldsCache.value;
19768
19910
  }
19769
19911
  try {
19770
- const shieldsPath = path62.join(os55.homedir(), ".node9", "shields.json");
19912
+ const shieldsPath = path63.join(os56.homedir(), ".node9", "shields.json");
19771
19913
  if (!fs65.existsSync(shieldsPath)) {
19772
19914
  shieldsCache = { value: [], ts: now };
19773
19915
  return [];
@@ -19874,9 +20016,9 @@ function renderContextLine(stdin) {
19874
20016
  async function main() {
19875
20017
  try {
19876
20018
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
19877
- if (fs65.existsSync(path62.join(os55.homedir(), ".node9", "hud-debug"))) {
20019
+ if (fs65.existsSync(path63.join(os56.homedir(), ".node9", "hud-debug"))) {
19878
20020
  try {
19879
- const logPath = path62.join(os55.homedir(), ".node9", "hud-debug.log");
20021
+ const logPath = path63.join(os56.homedir(), ".node9", "hud-debug.log");
19880
20022
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
19881
20023
  let size = 0;
19882
20024
  try {
@@ -19905,8 +20047,8 @@ async function main() {
19905
20047
  try {
19906
20048
  const cwd = stdin.cwd ?? process.cwd();
19907
20049
  for (const configPath of [
19908
- path62.join(cwd, "node9.config.json"),
19909
- path62.join(os55.homedir(), ".node9", "config.json")
20050
+ path63.join(cwd, "node9.config.json"),
20051
+ path63.join(os56.homedir(), ".node9", "config.json")
19910
20052
  ]) {
19911
20053
  if (!fs65.existsSync(configPath)) continue;
19912
20054
  const cfg = JSON.parse(fs65.readFileSync(configPath, "utf-8"));
@@ -19953,12 +20095,45 @@ var init_hud = __esm({
19953
20095
  // src/cli.ts
19954
20096
  init_core();
19955
20097
  init_setup();
19956
- init_daemon2();
19957
20098
  import { Command } from "commander";
20099
+
20100
+ // src/agent-teardowns.ts
20101
+ init_setup();
20102
+ var AGENT_TEARDOWNS = [
20103
+ { id: "claude", label: "Claude", fn: teardownClaude },
20104
+ { id: "gemini", label: "Gemini", fn: teardownGemini },
20105
+ { id: "codex", label: "Codex", fn: teardownCodex },
20106
+ { id: "cursor", label: "Cursor", fn: teardownCursor },
20107
+ { id: "windsurf", label: "Windsurf", fn: teardownWindsurf },
20108
+ { id: "vscode", label: "VSCode", fn: teardownVSCode },
20109
+ { id: "hermes", label: "Hermes", fn: teardownHermes },
20110
+ { id: "antigravity", label: "Antigravity", fn: teardownAntigravity, aliases: ["agy"] },
20111
+ { id: "copilot", label: "Copilot", fn: teardownCopilot },
20112
+ { id: "hud", label: "HUD", fn: teardownHud },
20113
+ {
20114
+ id: "claudedesktop",
20115
+ label: "Claude Desktop",
20116
+ fn: teardownClaudeDesktop,
20117
+ aliases: ["claude-desktop"]
20118
+ },
20119
+ { id: "opencode", label: "OpenCode", fn: teardownOpencode },
20120
+ { id: "pi", label: "Pi", fn: teardownPi }
20121
+ ];
20122
+ function resolveAgentTeardown(target) {
20123
+ const t = target.trim().toLowerCase();
20124
+ return AGENT_TEARDOWNS.find((a) => a.id === t || a.aliases?.includes(t));
20125
+ }
20126
+ function agentTeardownTargets() {
20127
+ return AGENT_TEARDOWNS.flatMap((a) => [a.id, ...a.aliases ?? []]);
20128
+ }
20129
+
20130
+ // src/cli.ts
20131
+ init_agent_wiring();
20132
+ init_daemon2();
19958
20133
  import chalk36 from "chalk";
19959
20134
  import fs66 from "fs";
19960
- import path63 from "path";
19961
- import os56 from "os";
20135
+ import path64 from "path";
20136
+ import os57 from "os";
19962
20137
  import { spawn as spawn9 } from "child_process";
19963
20138
  import { confirm as confirm2 } from "@inquirer/prompts";
19964
20139
 
@@ -21589,6 +21764,8 @@ function registerLogCommand(program2) {
21589
21764
  init_shields();
21590
21765
  import chalk10 from "chalk";
21591
21766
  import fs46 from "fs";
21767
+ import path44 from "path";
21768
+ import os40 from "os";
21592
21769
 
21593
21770
  // src/shields/build.ts
21594
21771
  function escapeRegex(s) {
@@ -21718,10 +21895,10 @@ init_audit();
21718
21895
  init_config();
21719
21896
 
21720
21897
  // src/utils/https-fetch.ts
21721
- import https4 from "https";
21898
+ import https5 from "https";
21722
21899
  function httpsFetch(url) {
21723
21900
  return new Promise((resolve, reject) => {
21724
- https4.get(url, (res) => {
21901
+ https5.get(url, (res) => {
21725
21902
  if (res.statusCode !== 200) {
21726
21903
  reject(new Error(`HTTP ${String(res.statusCode)} for ${url}`));
21727
21904
  res.resume();
@@ -21737,6 +21914,22 @@ function httpsFetch(url) {
21737
21914
 
21738
21915
  // src/cli/commands/shield.ts
21739
21916
  var COMMUNITY_INDEX_URL = "https://raw.githubusercontent.com/node9ai/node9-proxy/main/shields/community/index.json";
21917
+ function readCloudShields() {
21918
+ const out = /* @__PURE__ */ new Set();
21919
+ try {
21920
+ const file = path44.join(os40.homedir(), ".node9", "rules-cache.json");
21921
+ const raw = JSON.parse(fs46.readFileSync(file, "utf-8"));
21922
+ for (const r of raw.rules ?? []) {
21923
+ const rule = r;
21924
+ const fromSource = rule.source?.startsWith("SHIELD:") ? rule.source.slice("SHIELD:".length).toLowerCase() : void 0;
21925
+ const fromDesc = !fromSource && rule.description ? /\b([a-z0-9-]+)\s+shield/i.exec(rule.description)?.[1]?.toLowerCase() : void 0;
21926
+ const name = fromSource ?? fromDesc;
21927
+ if (name) out.add(name);
21928
+ }
21929
+ } catch {
21930
+ }
21931
+ return out;
21932
+ }
21740
21933
  function registerShieldCommand(program2) {
21741
21934
  const shieldCmd = program2.command("shield").description("Manage pre-packaged security shield templates");
21742
21935
  shieldCmd.command("enable <service>").description("Enable a security shield for a specific service").action((service) => {
@@ -21823,10 +22016,17 @@ function registerShieldCommand(program2) {
21823
22016
  return;
21824
22017
  }
21825
22018
  const active = new Set(readActiveShields());
22019
+ const cloud = readCloudShields();
21826
22020
  console.log(chalk10.bold("\n\u{1F6E1}\uFE0F Available Shields\n"));
22021
+ console.log(chalk10.gray(" \u25CF local \xB7 \u2601 cloud\n"));
21827
22022
  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}`);
22023
+ const isLocal = active.has(shield.name);
22024
+ const isCloud = cloud.has(shield.name);
22025
+ const status = isLocal && isCloud ? chalk10.green("\u25CF\u2601 enabled ") : isLocal ? chalk10.green("\u25CF enabled ") : isCloud ? chalk10.cyan("\u2601 cloud ") : chalk10.gray("\u25CB disabled");
22026
+ const via = isCloud && !isLocal ? chalk10.gray(" (via dashboard)") : "";
22027
+ console.log(
22028
+ ` ${status} ${chalk10.cyan(shield.name.padEnd(12))} ${shield.description}${via}`
22029
+ );
21830
22030
  if (shield.aliases.length > 0)
21831
22031
  console.log(chalk10.gray(` aliases: ${shield.aliases.join(", ")}`));
21832
22032
  }
@@ -22159,12 +22359,12 @@ init_config();
22159
22359
  init_agent_wiring();
22160
22360
  import chalk11 from "chalk";
22161
22361
  import fs47 from "fs";
22162
- import path44 from "path";
22163
- import os40 from "os";
22362
+ import path45 from "path";
22363
+ import os41 from "os";
22164
22364
  import { execSync } from "child_process";
22165
22365
  function registerDoctorCommand(program2, version2) {
22166
22366
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
22167
- const homeDir2 = os40.homedir();
22367
+ const homeDir2 = os41.homedir();
22168
22368
  let failures = 0;
22169
22369
  function pass(msg) {
22170
22370
  console.log(chalk11.green(" \u2705 ") + msg);
@@ -22210,7 +22410,7 @@ function registerDoctorCommand(program2, version2) {
22210
22410
  );
22211
22411
  }
22212
22412
  section("Configuration");
22213
- const globalConfigPath = path44.join(homeDir2, ".node9", "config.json");
22413
+ const globalConfigPath = path45.join(homeDir2, ".node9", "config.json");
22214
22414
  if (fs47.existsSync(globalConfigPath)) {
22215
22415
  try {
22216
22416
  JSON.parse(fs47.readFileSync(globalConfigPath, "utf-8"));
@@ -22221,7 +22421,7 @@ function registerDoctorCommand(program2, version2) {
22221
22421
  } else {
22222
22422
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
22223
22423
  }
22224
- const projectConfigPath = path44.join(process.cwd(), "node9.config.json");
22424
+ const projectConfigPath = path45.join(process.cwd(), "node9.config.json");
22225
22425
  if (fs47.existsSync(projectConfigPath)) {
22226
22426
  try {
22227
22427
  JSON.parse(fs47.readFileSync(projectConfigPath, "utf-8"));
@@ -22233,7 +22433,7 @@ function registerDoctorCommand(program2, version2) {
22233
22433
  );
22234
22434
  }
22235
22435
  }
22236
- const credsPath = path44.join(homeDir2, ".node9", "credentials.json");
22436
+ const credsPath = path45.join(homeDir2, ".node9", "credentials.json");
22237
22437
  if (fs47.existsSync(credsPath)) {
22238
22438
  pass("Cloud credentials found (~/.node9/credentials.json)");
22239
22439
  } else {
@@ -22278,7 +22478,7 @@ function registerDoctorCommand(program2, version2) {
22278
22478
  try {
22279
22479
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
22280
22480
  const cfg = getConfig();
22281
- const creds = fs47.existsSync(path44.join(os40.homedir(), ".node9", "credentials.json"));
22481
+ const creds = fs47.existsSync(path45.join(os41.homedir(), ".node9", "credentials.json"));
22282
22482
  if (!creds) {
22283
22483
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
22284
22484
  } else if (!cfg.settings.approvers.cloud) {
@@ -22329,8 +22529,8 @@ function registerDoctorCommand(program2, version2) {
22329
22529
  // src/cli/commands/audit.ts
22330
22530
  import chalk12 from "chalk";
22331
22531
  import fs48 from "fs";
22332
- import path45 from "path";
22333
- import os41 from "os";
22532
+ import path46 from "path";
22533
+ import os42 from "os";
22334
22534
  function formatRelativeTime(timestamp) {
22335
22535
  const diff = Date.now() - new Date(timestamp).getTime();
22336
22536
  const sec = Math.floor(diff / 1e3);
@@ -22343,7 +22543,7 @@ function formatRelativeTime(timestamp) {
22343
22543
  }
22344
22544
  function registerAuditCommand(program2) {
22345
22545
  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");
22546
+ const logPath = path46.join(os42.homedir(), ".node9", "audit.log");
22347
22547
  if (!fs48.existsSync(logPath)) {
22348
22548
  console.log(
22349
22549
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
@@ -22410,8 +22610,8 @@ init_costSync();
22410
22610
  init_litellm();
22411
22611
  init_cost_codex();
22412
22612
  import fs49 from "fs";
22413
- import os42 from "os";
22414
- import path46 from "path";
22613
+ import os43 from "os";
22614
+ import path47 from "path";
22415
22615
  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
22616
  function buildTestTimestamps(allEntries) {
22417
22617
  const testTs = /* @__PURE__ */ new Set();
@@ -22539,7 +22739,7 @@ function freezeClaudeCost(acc) {
22539
22739
  };
22540
22740
  }
22541
22741
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22542
- const projPath = path46.join(projectsDir, proj);
22742
+ const projPath = path47.join(projectsDir, proj);
22543
22743
  let files;
22544
22744
  try {
22545
22745
  const stat = fs49.statSync(projPath);
@@ -22550,7 +22750,7 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22550
22750
  }
22551
22751
  const startMs = start.getTime();
22552
22752
  for (const file of files) {
22553
- const filePath = path46.join(projPath, file);
22753
+ const filePath = path47.join(projPath, file);
22554
22754
  try {
22555
22755
  if (fs49.statSync(filePath).mtimeMs < startMs) continue;
22556
22756
  } catch {
@@ -22680,28 +22880,28 @@ function listCodexSessionFiles2(sessionsBase) {
22680
22880
  if (!fs49.existsSync(sessionsBase)) return jsonlFiles;
22681
22881
  try {
22682
22882
  for (const year of fs49.readdirSync(sessionsBase)) {
22683
- const yearPath = path46.join(sessionsBase, year);
22883
+ const yearPath = path47.join(sessionsBase, year);
22684
22884
  try {
22685
22885
  if (!fs49.statSync(yearPath).isDirectory()) continue;
22686
22886
  } catch {
22687
22887
  continue;
22688
22888
  }
22689
22889
  for (const month of fs49.readdirSync(yearPath)) {
22690
- const monthPath = path46.join(yearPath, month);
22890
+ const monthPath = path47.join(yearPath, month);
22691
22891
  try {
22692
22892
  if (!fs49.statSync(monthPath).isDirectory()) continue;
22693
22893
  } catch {
22694
22894
  continue;
22695
22895
  }
22696
22896
  for (const day of fs49.readdirSync(monthPath)) {
22697
- const dayPath = path46.join(monthPath, day);
22897
+ const dayPath = path47.join(monthPath, day);
22698
22898
  try {
22699
22899
  if (!fs49.statSync(dayPath).isDirectory()) continue;
22700
22900
  } catch {
22701
22901
  continue;
22702
22902
  }
22703
22903
  for (const file of fs49.readdirSync(dayPath)) {
22704
- if (file.endsWith(".jsonl")) jsonlFiles.push(path46.join(dayPath, file));
22904
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path47.join(dayPath, file));
22705
22905
  }
22706
22906
  }
22707
22907
  }
@@ -22827,7 +23027,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
22827
23027
  return out;
22828
23028
  }
22829
23029
  for (const proj of dirs) {
22830
- const chatsDir = path46.join(geminiTmpDir2, proj, "chats");
23030
+ const chatsDir = path47.join(geminiTmpDir2, proj, "chats");
22831
23031
  let files;
22832
23032
  try {
22833
23033
  if (!fs49.statSync(chatsDir).isDirectory()) continue;
@@ -22837,7 +23037,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
22837
23037
  }
22838
23038
  for (const f of files) {
22839
23039
  if (!f.endsWith(".jsonl")) continue;
22840
- out.push({ projectKey: proj, file: path46.join(chatsDir, f) });
23040
+ out.push({ projectKey: proj, file: path47.join(chatsDir, f) });
22841
23041
  }
22842
23042
  }
22843
23043
  return out;
@@ -22852,10 +23052,10 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
22852
23052
  }
22853
23053
  function aggregateReportFromAudit(period, opts = {}) {
22854
23054
  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");
23055
+ const auditLogPath = opts.auditLogPath ?? path47.join(os43.homedir(), ".node9", "audit.log");
23056
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path47.join(os43.homedir(), ".claude", "projects");
23057
+ const codexSessionsDir2 = opts.codexSessionsDir ?? path47.join(os43.homedir(), ".codex", "sessions");
23058
+ const geminiTmpDir2 = opts.geminiTmpDir ?? path47.join(os43.homedir(), ".gemini", "tmp");
22859
23059
  const hasAuditFile = fs49.existsSync(auditLogPath);
22860
23060
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
22861
23061
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
@@ -23556,8 +23756,8 @@ init_daemon();
23556
23756
  init_agent_wiring();
23557
23757
  import chalk15 from "chalk";
23558
23758
  import fs50 from "fs";
23559
- import path47 from "path";
23560
- import os43 from "os";
23759
+ import path48 from "path";
23760
+ import os44 from "os";
23561
23761
  function printAgentSection(label2, hookPairs, wrapped) {
23562
23762
  console.log(chalk15.bold(` ${label2}`));
23563
23763
  for (const { name, present } of hookPairs) {
@@ -23611,8 +23811,8 @@ function registerStatusCommand(program2) {
23611
23811
  console.log("");
23612
23812
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
23613
23813
  console.log(` Mode: ${modeLabel}`);
23614
- const projectConfig = path47.join(process.cwd(), "node9.config.json");
23615
- const globalConfig = path47.join(os43.homedir(), ".node9", "config.json");
23814
+ const projectConfig = path48.join(process.cwd(), "node9.config.json");
23815
+ const globalConfig = path48.join(os44.homedir(), ".node9", "config.json");
23616
23816
  console.log(
23617
23817
  ` Local: ${fs50.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
23618
23818
  );
@@ -23624,7 +23824,7 @@ function registerStatusCommand(program2) {
23624
23824
  ` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
23625
23825
  );
23626
23826
  }
23627
- const wiring = getAgentWiring(os43.homedir()).filter((a) => a.present);
23827
+ const wiring = getAgentWiring(os44.homedir()).filter((a) => a.present);
23628
23828
  if (wiring.length > 0) {
23629
23829
  console.log("");
23630
23830
  console.log(chalk15.bold(" Agent Wiring:"));
@@ -23664,9 +23864,9 @@ init_shields();
23664
23864
  init_service();
23665
23865
  import chalk16 from "chalk";
23666
23866
  import fs51 from "fs";
23667
- import path48 from "path";
23668
- import os44 from "os";
23669
- import https5 from "https";
23867
+ import path49 from "path";
23868
+ import os45 from "os";
23869
+ import https6 from "https";
23670
23870
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
23671
23871
  function buildTelemetryPayload(agents, firstInstall) {
23672
23872
  return {
@@ -23680,7 +23880,7 @@ function buildTelemetryPayload(agents, firstInstall) {
23680
23880
  function fireTelemetryPing(agents, firstInstall) {
23681
23881
  try {
23682
23882
  const body = JSON.stringify(buildTelemetryPayload(agents, firstInstall));
23683
- const req = https5.request(
23883
+ const req = https6.request(
23684
23884
  {
23685
23885
  hostname: "api.node9.ai",
23686
23886
  path: "/api/v1/telemetry",
@@ -23751,7 +23951,7 @@ function registerInitCommand(program2) {
23751
23951
  }
23752
23952
  console.log("");
23753
23953
  }
23754
- const configPath = path48.join(os44.homedir(), ".node9", "config.json");
23954
+ const configPath = path49.join(os45.homedir(), ".node9", "config.json");
23755
23955
  const isFirstInstall = !fs51.existsSync(configPath);
23756
23956
  if (fs51.existsSync(configPath) && !options.force) {
23757
23957
  try {
@@ -23773,7 +23973,7 @@ function registerInitCommand(program2) {
23773
23973
  ...DEFAULT_CONFIG,
23774
23974
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
23775
23975
  };
23776
- const dir = path48.dirname(configPath);
23976
+ const dir = path49.dirname(configPath);
23777
23977
  if (!fs51.existsSync(dir)) fs51.mkdirSync(dir, { recursive: true });
23778
23978
  fs51.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
23779
23979
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
@@ -23880,7 +24080,7 @@ function registerInitCommand(program2) {
23880
24080
  }
23881
24081
 
23882
24082
  // src/cli/commands/undo.ts
23883
- import path49 from "path";
24083
+ import path50 from "path";
23884
24084
  import chalk18 from "chalk";
23885
24085
 
23886
24086
  // src/tui/undo-navigator.ts
@@ -24039,7 +24239,7 @@ function findMatchingCwd(startDir, history) {
24039
24239
  let dir = startDir;
24040
24240
  while (true) {
24041
24241
  if (cwds.has(dir)) return dir;
24042
- const parent = path49.dirname(dir);
24242
+ const parent = path50.dirname(dir);
24043
24243
  if (parent === dir) return null;
24044
24244
  dir = parent;
24045
24245
  }
@@ -24675,8 +24875,8 @@ function registerMcpGatewayCommand(program2) {
24675
24875
  // src/mcp-server/index.ts
24676
24876
  import readline5 from "readline";
24677
24877
  import fs53 from "fs";
24678
- import os46 from "os";
24679
- import path51 from "path";
24878
+ import os47 from "os";
24879
+ import path52 from "path";
24680
24880
  import { spawnSync as spawnSync4 } from "child_process";
24681
24881
  init_core();
24682
24882
  init_daemon();
@@ -24684,8 +24884,8 @@ init_shields();
24684
24884
 
24685
24885
  // src/auth/egress-config.ts
24686
24886
  import fs52 from "fs";
24687
- import os45 from "os";
24688
- import path50 from "path";
24887
+ import os46 from "os";
24888
+ import path51 from "path";
24689
24889
  var DEFAULT_EGRESS = {
24690
24890
  enabled: false,
24691
24891
  mode: "review",
@@ -24694,7 +24894,7 @@ var DEFAULT_EGRESS = {
24694
24894
  allowPrivate: true
24695
24895
  };
24696
24896
  function egressConfigPath() {
24697
- return path50.join(os45.homedir(), ".node9", "config.json");
24897
+ return path51.join(os46.homedir(), ".node9", "config.json");
24698
24898
  }
24699
24899
  function readEgressRawConfig() {
24700
24900
  let text;
@@ -24714,7 +24914,7 @@ function readEgressRawConfig() {
24714
24914
  }
24715
24915
  function writeEgressRawConfig(config) {
24716
24916
  const p = egressConfigPath();
24717
- fs52.mkdirSync(path50.dirname(p), { recursive: true });
24917
+ fs52.mkdirSync(path51.dirname(p), { recursive: true });
24718
24918
  fs52.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
24719
24919
  }
24720
24920
  function applyEgress(config, change) {
@@ -25100,8 +25300,8 @@ function handleStatus() {
25100
25300
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
25101
25301
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
25102
25302
  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");
25303
+ const projectConfig = path52.join(process.cwd(), "node9.config.json");
25304
+ const globalConfig = path52.join(os47.homedir(), ".node9", "config.json");
25105
25305
  lines.push(
25106
25306
  `Project config (node9.config.json): ${fs53.existsSync(projectConfig) ? "present" : "not found"}`
25107
25307
  );
@@ -25212,7 +25412,7 @@ function handleEgressDeny(args) {
25212
25412
  addEgressHost("deny", host);
25213
25413
  return `Denied egress to ${host} (deny always wins over allow).`;
25214
25414
  }
25215
- var GLOBAL_CONFIG_PATH = path51.join(os46.homedir(), ".node9", "config.json");
25415
+ var GLOBAL_CONFIG_PATH = path52.join(os47.homedir(), ".node9", "config.json");
25216
25416
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
25217
25417
  function readGlobalConfigRaw() {
25218
25418
  try {
@@ -25224,7 +25424,7 @@ function readGlobalConfigRaw() {
25224
25424
  return {};
25225
25425
  }
25226
25426
  function writeGlobalConfigRaw(data) {
25227
- const dir = path51.dirname(GLOBAL_CONFIG_PATH);
25427
+ const dir = path52.dirname(GLOBAL_CONFIG_PATH);
25228
25428
  if (!fs53.existsSync(dir)) fs53.mkdirSync(dir, { recursive: true });
25229
25429
  fs53.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
25230
25430
  }
@@ -25270,7 +25470,7 @@ function handleApproverSet(args) {
25270
25470
  function handleAuditGet(args) {
25271
25471
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
25272
25472
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
25273
- const auditPath = path51.join(os46.homedir(), ".node9", "audit.log");
25473
+ const auditPath = path52.join(os47.homedir(), ".node9", "audit.log");
25274
25474
  if (!fs53.existsSync(auditPath)) return "No audit log found.";
25275
25475
  const rawLines = fs53.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
25276
25476
  const parsed = [];
@@ -25778,6 +25978,17 @@ init_sync();
25778
25978
  import chalk22 from "chalk";
25779
25979
  function registerSyncCommand(program2) {
25780
25980
  const policy = program2.command("policy").description("Manage cloud policy rules");
25981
+ policy.command("push").description("Push this machine's effective policy to the node9 dashboard").action(async () => {
25982
+ process.stdout.write(chalk22.cyan("Pushing policy to the dashboard\u2026"));
25983
+ const result = await runPolicyPush();
25984
+ process.stdout.write("\n");
25985
+ if (!result.ok) {
25986
+ console.error(chalk22.red(`\u2717 ${result.reason}`));
25987
+ process.exit(1);
25988
+ }
25989
+ console.log(chalk22.green("\u2713 Policy mirrored to the dashboard"));
25990
+ console.log(chalk22.gray(" See it under Security Policy \u2192 Machines"));
25991
+ });
25781
25992
  policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
25782
25993
  process.stdout.write(chalk22.cyan("Syncing cloud policy rules\u2026"));
25783
25994
  const result = await runCloudSync();
@@ -26226,12 +26437,12 @@ import chalk27 from "chalk";
26226
26437
 
26227
26438
  // src/shields/jail.ts
26228
26439
  import fs55 from "fs";
26229
- import os47 from "os";
26230
- import path52 from "path";
26440
+ import os48 from "os";
26441
+ import path53 from "path";
26231
26442
  init_shields();
26232
26443
  var USER_JAIL_SHIELD = "user-jail";
26233
26444
  function jailStorePath() {
26234
- return path52.join(os47.homedir(), ".node9", "jail-paths.json");
26445
+ return path53.join(os48.homedir(), ".node9", "jail-paths.json");
26235
26446
  }
26236
26447
  function readJailPaths() {
26237
26448
  let text;
@@ -26254,7 +26465,7 @@ function readJailPaths() {
26254
26465
  }
26255
26466
  function writeJailPaths(paths) {
26256
26467
  const p = jailStorePath();
26257
- fs55.mkdirSync(path52.dirname(p), { recursive: true });
26468
+ fs55.mkdirSync(path53.dirname(p), { recursive: true });
26258
26469
  fs55.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
26259
26470
  }
26260
26471
  function addJailPath(rawPath, verdict) {
@@ -26277,7 +26488,7 @@ function removeJailPath(rawPath) {
26277
26488
  return { removed, paths: after };
26278
26489
  }
26279
26490
  function regenerateUserJail(paths) {
26280
- const file = path52.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26491
+ const file = path53.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26281
26492
  if (paths.length === 0) {
26282
26493
  const active2 = readActiveShields();
26283
26494
  if (active2.includes(USER_JAIL_SHIELD)) {
@@ -26400,12 +26611,12 @@ function registerJailCommand(program2) {
26400
26611
  init_config();
26401
26612
  import chalk28 from "chalk";
26402
26613
  import fs58 from "fs";
26403
- import path55 from "path";
26614
+ import path56 from "path";
26404
26615
  import { spawnSync as spawnSync6 } from "child_process";
26405
26616
 
26406
26617
  // src/sandbox/config.ts
26407
26618
  import fs56 from "fs";
26408
- import path53 from "path";
26619
+ import path54 from "path";
26409
26620
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
26410
26621
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
26411
26622
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -26478,7 +26689,7 @@ function scaffoldSandboxYaml(agent) {
26478
26689
  return header + stringifyYaml(defaultSandboxConfig(agent));
26479
26690
  }
26480
26691
  function sandboxConfigPath(cwd = process.cwd()) {
26481
- return path53.join(cwd, SANDBOX_CONFIG_FILE);
26692
+ return path54.join(cwd, SANDBOX_CONFIG_FILE);
26482
26693
  }
26483
26694
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
26484
26695
  const p = sandboxConfigPath(cwd);
@@ -26547,12 +26758,12 @@ init_templates();
26547
26758
  // src/sandbox/runtime.ts
26548
26759
  init_templates();
26549
26760
  import fs57 from "fs";
26550
- import os48 from "os";
26551
- import path54 from "path";
26761
+ import os49 from "os";
26762
+ import path55 from "path";
26552
26763
  import crypto8 from "crypto";
26553
26764
  import { spawnSync as spawnSync5 } from "child_process";
26554
26765
  function sandboxDataDir(cwd = process.cwd()) {
26555
- return path54.join(cwd, ".node9", "sandbox", "data");
26766
+ return path55.join(cwd, ".node9", "sandbox", "data");
26556
26767
  }
26557
26768
  function detectEngine(engine) {
26558
26769
  const r = spawnSync5(engine, ["--version"], { encoding: "utf-8" });
@@ -26563,7 +26774,7 @@ function detectEngine(engine) {
26563
26774
  }
26564
26775
  function agentCredentialsMount(agent) {
26565
26776
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
26566
- return { hostPath: path54.join(os48.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26777
+ return { hostPath: path55.join(os49.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26567
26778
  }
26568
26779
  function buildRunArgs(opts) {
26569
26780
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -26591,30 +26802,30 @@ function imageContentHash(dockerfile, entrypoint) {
26591
26802
  return crypto8.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
26592
26803
  }
26593
26804
  function sandboxBuildDir(cwd = process.cwd()) {
26594
- return path54.join(cwd, ".node9", "sandbox", "build");
26805
+ return path55.join(cwd, ".node9", "sandbox", "build");
26595
26806
  }
26596
26807
  function writeBuildContext(cwd, dockerfile, entrypoint) {
26597
26808
  const dir = sandboxBuildDir(cwd);
26598
26809
  fs57.mkdirSync(dir, { recursive: true });
26599
- fs57.writeFileSync(path54.join(dir, "Dockerfile"), dockerfile);
26600
- fs57.writeFileSync(path54.join(dir, "entrypoint.sh"), entrypoint);
26810
+ fs57.writeFileSync(path55.join(dir, "Dockerfile"), dockerfile);
26811
+ fs57.writeFileSync(path55.join(dir, "entrypoint.sh"), entrypoint);
26601
26812
  return dir;
26602
26813
  }
26603
26814
  function writeAllowlist(cwd, hosts) {
26604
- const dir = path54.join(cwd, ".node9", "sandbox");
26815
+ const dir = path55.join(cwd, ".node9", "sandbox");
26605
26816
  fs57.mkdirSync(dir, { recursive: true });
26606
- const p = path54.join(dir, "allowed-domains.txt");
26817
+ const p = path55.join(dir, "allowed-domains.txt");
26607
26818
  fs57.writeFileSync(p, hosts.join("\n") + "\n");
26608
26819
  return p;
26609
26820
  }
26610
26821
  function resolveHomePath(p) {
26611
- return p.startsWith("~") ? path54.join(os48.homedir(), p.slice(1)) : path54.resolve(p);
26822
+ return p.startsWith("~") ? path55.join(os49.homedir(), p.slice(1)) : path55.resolve(p);
26612
26823
  }
26613
26824
 
26614
26825
  // src/cli/commands/sandbox.ts
26615
26826
  function seedDataDirConfig(dataDir, sandbox) {
26616
26827
  fs58.mkdirSync(dataDir, { recursive: true });
26617
- const configPath = path55.join(dataDir, "config.json");
26828
+ const configPath = path56.join(dataDir, "config.json");
26618
26829
  const seed = {
26619
26830
  settings: {
26620
26831
  approvers: {
@@ -26679,7 +26890,7 @@ function registerSandboxCommand(program2, version2) {
26679
26890
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
26680
26891
  const hash = imageContentHash(dockerfile, entrypoint);
26681
26892
  const image = sandbox.runtime.image;
26682
- const hashFile = path55.join(sandboxBuildDir(cwd), ".image-hash");
26893
+ const hashFile = path56.join(sandboxBuildDir(cwd), ".image-hash");
26683
26894
  const lastHash = fs58.existsSync(hashFile) ? fs58.readFileSync(hashFile, "utf-8").trim() : "";
26684
26895
  const imageExists = spawnSync6(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
26685
26896
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
@@ -26722,7 +26933,7 @@ function registerSandboxCommand(program2, version2) {
26722
26933
  process.exit(r.status ?? 0);
26723
26934
  });
26724
26935
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
26725
- const auditPath = path55.join(sandboxDataDir(), "audit.log");
26936
+ const auditPath = path56.join(sandboxDataDir(), "audit.log");
26726
26937
  if (!fs58.existsSync(auditPath)) {
26727
26938
  console.log(chalk28.dim(" no sandbox audit yet."));
26728
26939
  return;
@@ -26730,7 +26941,7 @@ function registerSandboxCommand(program2, version2) {
26730
26941
  spawnSync6("tail", ["-f", auditPath], { stdio: "inherit" });
26731
26942
  });
26732
26943
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
26733
- const auditPath = path55.join(sandboxDataDir(), "audit.log");
26944
+ const auditPath = path56.join(sandboxDataDir(), "audit.log");
26734
26945
  if (!fs58.existsSync(auditPath)) {
26735
26946
  console.log(chalk28.dim(" no sandbox audit yet."));
26736
26947
  return;
@@ -26749,7 +26960,7 @@ function registerSandboxCommand(program2, version2) {
26749
26960
  stdio: "ignore"
26750
26961
  });
26751
26962
  }
26752
- fs58.rmSync(path55.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26963
+ fs58.rmSync(path56.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26753
26964
  console.log(chalk28.green(" \u2713 sandbox image + build + data removed."));
26754
26965
  });
26755
26966
  }
@@ -26761,8 +26972,8 @@ init_cost_gemini();
26761
26972
  init_cost_codex();
26762
26973
  import chalk29 from "chalk";
26763
26974
  import fs59 from "fs";
26764
- import path56 from "path";
26765
- import os49 from "os";
26975
+ import path57 from "path";
26976
+ import os50 from "os";
26766
26977
  function modelPrice(model) {
26767
26978
  const t = pricingFor(model);
26768
26979
  if (!t) return null;
@@ -26779,10 +26990,10 @@ function encodeProjectPath(projectPath) {
26779
26990
  }
26780
26991
  function sessionJsonlPath(projectPath, sessionId) {
26781
26992
  const encoded = encodeProjectPath(projectPath);
26782
- return path56.join(os49.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
26993
+ return path57.join(os50.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
26783
26994
  }
26784
26995
  function projectLabel(projectPath) {
26785
- return projectPath.replace(os49.homedir(), "~");
26996
+ return projectPath.replace(os50.homedir(), "~");
26786
26997
  }
26787
26998
  function parseHistoryLines(lines) {
26788
26999
  const entries = [];
@@ -26851,7 +27062,7 @@ function parseSessionLines(lines) {
26851
27062
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
26852
27063
  }
26853
27064
  function loadAuditEntries(auditPath) {
26854
- const aPath = auditPath ?? path56.join(os49.homedir(), ".node9", "audit.log");
27065
+ const aPath = auditPath ?? path57.join(os50.homedir(), ".node9", "audit.log");
26855
27066
  let raw;
26856
27067
  try {
26857
27068
  raw = fs59.readFileSync(aPath, "utf-8");
@@ -26890,7 +27101,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
26890
27101
  return result;
26891
27102
  }
26892
27103
  function buildGeminiSessions(days, allAuditEntries) {
26893
- const tmpDir = path56.join(os49.homedir(), ".gemini", "tmp");
27104
+ const tmpDir = path57.join(os50.homedir(), ".gemini", "tmp");
26894
27105
  if (!fs59.existsSync(tmpDir)) return [];
26895
27106
  const cutoff = days !== null ? (() => {
26896
27107
  const d = /* @__PURE__ */ new Date();
@@ -26906,18 +27117,18 @@ function buildGeminiSessions(days, allAuditEntries) {
26906
27117
  }
26907
27118
  const summaries = [];
26908
27119
  for (const slug2 of slugDirs) {
26909
- const slugPath = path56.join(tmpDir, slug2);
27120
+ const slugPath = path57.join(tmpDir, slug2);
26910
27121
  try {
26911
27122
  if (!fs59.statSync(slugPath).isDirectory()) continue;
26912
27123
  } catch {
26913
27124
  continue;
26914
27125
  }
26915
- let projectRoot = path56.join(os49.homedir(), slug2);
27126
+ let projectRoot = path57.join(os50.homedir(), slug2);
26916
27127
  try {
26917
- projectRoot = fs59.readFileSync(path56.join(slugPath, ".project_root"), "utf-8").trim();
27128
+ projectRoot = fs59.readFileSync(path57.join(slugPath, ".project_root"), "utf-8").trim();
26918
27129
  } catch {
26919
27130
  }
26920
- const chatsDir = path56.join(slugPath, "chats");
27131
+ const chatsDir = path57.join(slugPath, "chats");
26921
27132
  if (!fs59.existsSync(chatsDir)) continue;
26922
27133
  let chatFiles;
26923
27134
  try {
@@ -26928,7 +27139,7 @@ function buildGeminiSessions(days, allAuditEntries) {
26928
27139
  for (const chatFile of chatFiles) {
26929
27140
  let raw;
26930
27141
  try {
26931
- raw = fs59.readFileSync(path56.join(chatsDir, chatFile), "utf-8");
27142
+ raw = fs59.readFileSync(path57.join(chatsDir, chatFile), "utf-8");
26932
27143
  } catch {
26933
27144
  continue;
26934
27145
  }
@@ -27008,7 +27219,7 @@ function buildGeminiSessions(days, allAuditEntries) {
27008
27219
  return summaries;
27009
27220
  }
27010
27221
  function buildCodexSessions(days, allAuditEntries) {
27011
- const sessionsBase = path56.join(os49.homedir(), ".codex", "sessions");
27222
+ const sessionsBase = path57.join(os50.homedir(), ".codex", "sessions");
27012
27223
  if (!fs59.existsSync(sessionsBase)) return [];
27013
27224
  const cutoff = days !== null ? (() => {
27014
27225
  const d = /* @__PURE__ */ new Date();
@@ -27019,28 +27230,28 @@ function buildCodexSessions(days, allAuditEntries) {
27019
27230
  const jsonlFiles = [];
27020
27231
  try {
27021
27232
  for (const year of fs59.readdirSync(sessionsBase)) {
27022
- const yearPath = path56.join(sessionsBase, year);
27233
+ const yearPath = path57.join(sessionsBase, year);
27023
27234
  try {
27024
27235
  if (!fs59.statSync(yearPath).isDirectory()) continue;
27025
27236
  } catch {
27026
27237
  continue;
27027
27238
  }
27028
27239
  for (const month of fs59.readdirSync(yearPath)) {
27029
- const monthPath = path56.join(yearPath, month);
27240
+ const monthPath = path57.join(yearPath, month);
27030
27241
  try {
27031
27242
  if (!fs59.statSync(monthPath).isDirectory()) continue;
27032
27243
  } catch {
27033
27244
  continue;
27034
27245
  }
27035
27246
  for (const day of fs59.readdirSync(monthPath)) {
27036
- const dayPath = path56.join(monthPath, day);
27247
+ const dayPath = path57.join(monthPath, day);
27037
27248
  try {
27038
27249
  if (!fs59.statSync(dayPath).isDirectory()) continue;
27039
27250
  } catch {
27040
27251
  continue;
27041
27252
  }
27042
27253
  for (const file of fs59.readdirSync(dayPath)) {
27043
- if (file.endsWith(".jsonl")) jsonlFiles.push(path56.join(dayPath, file));
27254
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path57.join(dayPath, file));
27044
27255
  }
27045
27256
  }
27046
27257
  }
@@ -27138,7 +27349,7 @@ function buildCodexSessions(days, allAuditEntries) {
27138
27349
  return summaries;
27139
27350
  }
27140
27351
  function buildSessions(days, historyPath) {
27141
- const hPath = historyPath ?? path56.join(os49.homedir(), ".claude", "history.jsonl");
27352
+ const hPath = historyPath ?? path57.join(os50.homedir(), ".claude", "history.jsonl");
27142
27353
  let historyRaw = "";
27143
27354
  try {
27144
27355
  historyRaw = fs59.readFileSync(hPath, "utf-8");
@@ -27560,11 +27771,11 @@ function registerSessionTaintCommand(program2) {
27560
27771
  // src/cli/commands/skill-pin.ts
27561
27772
  import chalk31 from "chalk";
27562
27773
  import fs60 from "fs";
27563
- import os50 from "os";
27564
- import path57 from "path";
27774
+ import os51 from "os";
27775
+ import path58 from "path";
27565
27776
  function wipeSkillSessions() {
27566
27777
  try {
27567
- fs60.rmSync(path57.join(os50.homedir(), ".node9", "skill-sessions"), {
27778
+ fs60.rmSync(path58.join(os51.homedir(), ".node9", "skill-sessions"), {
27568
27779
  recursive: true,
27569
27780
  force: true
27570
27781
  });
@@ -27647,10 +27858,10 @@ function registerSkillPinCommand(program2) {
27647
27858
 
27648
27859
  // src/cli/commands/decisions.ts
27649
27860
  import fs61 from "fs";
27650
- import os51 from "os";
27651
- import path58 from "path";
27861
+ import os52 from "os";
27862
+ import path59 from "path";
27652
27863
  import chalk32 from "chalk";
27653
- var DECISIONS_FILE2 = path58.join(os51.homedir(), ".node9", "decisions.json");
27864
+ var DECISIONS_FILE2 = path59.join(os52.homedir(), ".node9", "decisions.json");
27654
27865
  function readDecisions() {
27655
27866
  try {
27656
27867
  if (!fs61.existsSync(DECISIONS_FILE2)) return {};
@@ -27666,7 +27877,7 @@ function readDecisions() {
27666
27877
  }
27667
27878
  }
27668
27879
  function writeDecisions(d) {
27669
- const dir = path58.dirname(DECISIONS_FILE2);
27880
+ const dir = path59.dirname(DECISIONS_FILE2);
27670
27881
  if (!fs61.existsSync(dir)) fs61.mkdirSync(dir, { recursive: true });
27671
27882
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
27672
27883
  fs61.writeFileSync(tmp, JSON.stringify(d, null, 2));
@@ -27728,10 +27939,10 @@ Persistent decisions (${entries.length})
27728
27939
  // src/cli/commands/dlp.ts
27729
27940
  import chalk33 from "chalk";
27730
27941
  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");
27942
+ import path60 from "path";
27943
+ import os53 from "os";
27944
+ var AUDIT_LOG = path60.join(os53.homedir(), ".node9", "audit.log");
27945
+ var RESOLVED_FILE = path60.join(os53.homedir(), ".node9", "dlp-resolved.json");
27735
27946
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
27736
27947
  function stripAnsi(s) {
27737
27948
  return s.replace(ANSI_RE, "");
@@ -27852,13 +28063,13 @@ function registerDlpCommand(program2) {
27852
28063
  init_dlp();
27853
28064
  import chalk34 from "chalk";
27854
28065
  import fs63 from "fs";
27855
- import path60 from "path";
27856
- import os53 from "os";
28066
+ import path61 from "path";
28067
+ import os54 from "os";
27857
28068
  function findJsonlFiles(dir) {
27858
28069
  const results = [];
27859
28070
  if (!fs63.existsSync(dir)) return results;
27860
28071
  for (const entry of fs63.readdirSync(dir, { withFileTypes: true })) {
27861
- const full = path60.join(dir, entry.name);
28072
+ const full = path61.join(dir, entry.name);
27862
28073
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
27863
28074
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
27864
28075
  }
@@ -27961,7 +28172,7 @@ function findJsonFiles(dir) {
27961
28172
  const results = [];
27962
28173
  if (!fs63.existsSync(dir)) return results;
27963
28174
  for (const entry of fs63.readdirSync(dir, { withFileTypes: true })) {
27964
- const full = path60.join(dir, entry.name);
28175
+ const full = path61.join(dir, entry.name);
27965
28176
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
27966
28177
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
27967
28178
  }
@@ -27970,9 +28181,9 @@ function findJsonFiles(dir) {
27970
28181
  function registerMaskCommand(program2) {
27971
28182
  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
28183
  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");
28184
+ const home = os54.homedir();
28185
+ const claudeDir = path61.join(home, ".claude", "projects");
28186
+ const geminiDir = path61.join(home, ".gemini", "tmp");
27976
28187
  const allFiles = [
27977
28188
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
27978
28189
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -28036,15 +28247,15 @@ function registerMaskCommand(program2) {
28036
28247
  // src/cli.ts
28037
28248
  init_blast();
28038
28249
  var { version } = JSON.parse(
28039
- fs66.readFileSync(path63.join(__dirname, "../package.json"), "utf-8")
28250
+ fs66.readFileSync(path64.join(__dirname, "../package.json"), "utf-8")
28040
28251
  );
28041
28252
  var program = new Command();
28042
28253
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
28043
28254
  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
28255
  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 });
28256
+ const credPath = path64.join(os57.homedir(), ".node9", "credentials.json");
28257
+ if (!fs66.existsSync(path64.dirname(credPath)))
28258
+ fs66.mkdirSync(path64.dirname(credPath), { recursive: true });
28048
28259
  const profileName = options.profile || "default";
28049
28260
  let existingCreds = {};
28050
28261
  try {
@@ -28064,7 +28275,7 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
28064
28275
  fs66.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
28065
28276
  let effectiveCloud = null;
28066
28277
  if (profileName === "default") {
28067
- const configPath = path63.join(os56.homedir(), ".node9", "config.json");
28278
+ const configPath = path64.join(os57.homedir(), ".node9", "config.json");
28068
28279
  let config = {};
28069
28280
  try {
28070
28281
  if (fs66.existsSync(configPath))
@@ -28083,8 +28294,8 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
28083
28294
  approvers.cloud = false;
28084
28295
  }
28085
28296
  s.approvers = approvers;
28086
- if (!fs66.existsSync(path63.dirname(configPath)))
28087
- fs66.mkdirSync(path63.dirname(configPath), { recursive: true });
28297
+ if (!fs66.existsSync(path64.dirname(configPath)))
28298
+ fs66.mkdirSync(path64.dirname(configPath), { recursive: true });
28088
28299
  fs66.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
28089
28300
  effectiveCloud = approvers.cloud === true;
28090
28301
  }
@@ -28194,37 +28405,21 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
28194
28405
  );
28195
28406
  process.exit(1);
28196
28407
  });
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 {
28408
+ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText("after", `
28409
+ Supported targets: ${agentTeardownTargets().join(" ")}`).argument("<target>", `The agent to remove from: ${agentTeardownTargets().join(" | ")}`).action((target) => {
28410
+ const agent = resolveAgentTeardown(target);
28411
+ if (!agent) {
28216
28412
  console.error(
28217
- chalk36.red(
28218
- `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
28219
- )
28413
+ chalk36.red(`Unknown target: "${target}". Supported: ${agentTeardownTargets().join(", ")}`)
28220
28414
  );
28221
28415
  process.exit(1);
28416
+ return;
28222
28417
  }
28223
28418
  console.log(chalk36.cyan(`
28224
- \u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
28419
+ \u{1F6E1}\uFE0F Node9: removing hooks from ${agent.label}...
28225
28420
  `));
28226
28421
  try {
28227
- fn();
28422
+ agent.fn();
28228
28423
  } catch (err2) {
28229
28424
  console.error(chalk36.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
28230
28425
  process.exit(1);
@@ -28242,15 +28437,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
28242
28437
  }
28243
28438
  console.log(chalk36.bold("\nRemoving hooks..."));
28244
28439
  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
- ]) {
28440
+ for (const { label: label2, fn } of AGENT_TEARDOWNS) {
28254
28441
  try {
28255
28442
  fn();
28256
28443
  } catch (err2) {
@@ -28262,8 +28449,21 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
28262
28449
  );
28263
28450
  }
28264
28451
  }
28452
+ try {
28453
+ const residual = getAgentWiring().filter((a) => a.wireState === "wired");
28454
+ if (residual.length === 0) {
28455
+ console.log(chalk36.green(" \u2705 Verified \u2014 no node9 hooks or plugin shims remain"));
28456
+ } else {
28457
+ teardownFailed = true;
28458
+ console.error(chalk36.red(" \u26A0\uFE0F Still wired after teardown:"));
28459
+ for (const a of residual) {
28460
+ console.error(chalk36.red(` \u2022 ${a.label} \u2014 ${a.settingsPath}`));
28461
+ }
28462
+ }
28463
+ } catch {
28464
+ }
28265
28465
  if (options.purge) {
28266
- const node9Dir = path63.join(os56.homedir(), ".node9");
28466
+ const node9Dir = path64.join(os57.homedir(), ".node9");
28267
28467
  if (fs66.existsSync(node9Dir)) {
28268
28468
  const confirmed = await confirm2({
28269
28469
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
@@ -28394,7 +28594,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
28394
28594
  });
28395
28595
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
28396
28596
  try {
28397
- const dashboardPath = path63.join(__dirname, "dashboard.mjs");
28597
+ const dashboardPath = path64.join(__dirname, "dashboard.mjs");
28398
28598
  const dynamicImport = new Function("id", "return import(id)");
28399
28599
  const mod = await dynamicImport(`file://${dashboardPath}`);
28400
28600
  await mod.startMonitor();
@@ -28432,9 +28632,9 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
28432
28632
  Run "node9 addto claude" to register it as the statusLine.`
28433
28633
  ).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
28634
  if (subcommand === "debug") {
28435
- const flagFile = path63.join(os56.homedir(), ".node9", "hud-debug");
28635
+ const flagFile = path64.join(os57.homedir(), ".node9", "hud-debug");
28436
28636
  if (state === "on") {
28437
- fs66.mkdirSync(path63.dirname(flagFile), { recursive: true });
28637
+ fs66.mkdirSync(path64.dirname(flagFile), { recursive: true });
28438
28638
  fs66.writeFileSync(flagFile, "");
28439
28639
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
28440
28640
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
@@ -28561,7 +28761,7 @@ if (process.argv[2] !== "daemon") {
28561
28761
  const isCheckHook = process.argv[2] === "check";
28562
28762
  if (isCheckHook) {
28563
28763
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
28564
- const logPath = path63.join(os56.homedir(), ".node9", "hook-debug.log");
28764
+ const logPath = path64.join(os57.homedir(), ".node9", "hook-debug.log");
28565
28765
  const msg = reason instanceof Error ? reason.message : String(reason);
28566
28766
  fs66.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
28567
28767
  `);