@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.js CHANGED
@@ -240,8 +240,8 @@ function sanitizeConfig(raw) {
240
240
  }
241
241
  }
242
242
  const lines = result.error.issues.map((issue) => {
243
- const path64 = issue.path.length > 0 ? issue.path.join(".") : "root";
244
- return ` \u2022 ${path64}: ${issue.message}`;
243
+ const path65 = issue.path.length > 0 ? issue.path.join(".") : "root";
244
+ return ` \u2022 ${path65}: ${issue.message}`;
245
245
  });
246
246
  return {
247
247
  sanitized,
@@ -337,6 +337,11 @@ var init_config_schema = __esm({
337
337
  // must run them from the CLI. node9's threat model is the agent itself.
338
338
  mcpAllowWeakening: import_zod.z.boolean().optional(),
339
339
  cloudSyncIntervalHours: import_zod.z.number().positive().optional(),
340
+ // Seconds-granular override for the cloud policy sync cadence. Wins over
341
+ // cloudSyncIntervalHours when set. Lets you opt into fast apply (e.g. 20)
342
+ // for an incident; clamped to a 15s floor so it can't hammer the API.
343
+ // Unset → falls back to hours, then the 5h default.
344
+ cloudSyncIntervalSeconds: import_zod.z.number().positive().optional(),
340
345
  // Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
341
346
  // to true; set false to fall back to local-only auditing.
342
347
  shipper: import_zod.z.object({
@@ -1357,9 +1362,9 @@ function matchesPattern(text, patterns) {
1357
1362
  const withoutDotSlash = text.replace(/^\.\//, "");
1358
1363
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1359
1364
  }
1360
- function getNestedValue(obj, path64) {
1365
+ function getNestedValue(obj, path65) {
1361
1366
  if (!obj || typeof obj !== "object") return null;
1362
- const segments = path64.split(".");
1367
+ const segments = path65.split(".");
1363
1368
  for (const seg of segments) {
1364
1369
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1365
1370
  }
@@ -1412,6 +1417,14 @@ function evaluateSmartConditions(args, rule) {
1412
1417
  });
1413
1418
  return mode === "any" ? results.some((r) => r) : results.every((r) => r);
1414
1419
  }
1420
+ function resolvePinned(matches) {
1421
+ if (matches.length === 0) return void 0;
1422
+ const pinned = matches.filter((r) => r.pinned);
1423
+ if (pinned.length === 0) return matches[0];
1424
+ return pinned.reduce(
1425
+ (best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
1426
+ );
1427
+ }
1415
1428
  function tokenize2(toolName) {
1416
1429
  return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
1417
1430
  }
@@ -1523,9 +1536,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
1523
1536
  }
1524
1537
  }
1525
1538
  if (config.policy.smartRules.length > 0) {
1526
- const matchedRule = config.policy.smartRules.find(
1539
+ const matches = config.policy.smartRules.filter(
1527
1540
  (rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
1528
1541
  );
1542
+ const matchedRule = resolvePinned(matches);
1529
1543
  if (matchedRule) {
1530
1544
  if (matchedRule.verdict === "allow")
1531
1545
  return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
@@ -2264,7 +2278,7 @@ function* stringValues(obj, depth = 0) {
2264
2278
  }
2265
2279
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
2266
2280
  }
2267
- var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto3, 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;
2281
+ var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto3, 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;
2268
2282
  var init_dist = __esm({
2269
2283
  "packages/policy-engine/dist/index.mjs"() {
2270
2284
  "use strict";
@@ -3195,6 +3209,11 @@ var init_dist = __esm({
3195
3209
  REGEX_CACHE_MAX = 500;
3196
3210
  regexCache = /* @__PURE__ */ new Map();
3197
3211
  FORBIDDEN_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3212
+ VERDICT_RANK = {
3213
+ allow: 0,
3214
+ review: 1,
3215
+ block: 2
3216
+ };
3198
3217
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
3199
3218
  aws_default = {
3200
3219
  name: "aws",
@@ -3999,6 +4018,7 @@ var init_dist = __esm({
3999
4018
  DEDUPE_PREVIEW_LEN = 120;
4000
4019
  TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
4001
4020
  /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
4021
+ ENGINE_VERSION = "1.4.0";
4002
4022
  }
4003
4023
  });
4004
4024
 
@@ -9725,13 +9745,234 @@ var init_setup = __esm({
9725
9745
  }
9726
9746
  });
9727
9747
 
9748
+ // src/agent-wiring.ts
9749
+ function readJson2(filePath) {
9750
+ if (!import_fs14.default.existsSync(filePath)) return null;
9751
+ try {
9752
+ return JSON.parse(import_fs14.default.readFileSync(filePath, "utf-8"));
9753
+ } catch {
9754
+ return "invalid";
9755
+ }
9756
+ }
9757
+ function matchersHaveNode9Hook(matchers) {
9758
+ return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
9759
+ }
9760
+ function flatHaveNode9Hook(entries) {
9761
+ return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
9762
+ }
9763
+ function readHookRoot(filePath, format) {
9764
+ if (!import_fs14.default.existsSync(filePath)) return "absent";
9765
+ let raw;
9766
+ try {
9767
+ raw = import_fs14.default.readFileSync(filePath, "utf-8");
9768
+ } catch {
9769
+ return "absent";
9770
+ }
9771
+ try {
9772
+ const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
9773
+ return parsed?.hooks ?? {};
9774
+ } catch {
9775
+ return "invalid";
9776
+ }
9777
+ }
9778
+ function eventWired(root, ev, format) {
9779
+ const arr = root[ev.key];
9780
+ if (format === "matcher") return matchersHaveNode9Hook(arr);
9781
+ return flatHaveNode9Hook(arr);
9782
+ }
9783
+ function detectMcp(servers) {
9784
+ const entries = Object.entries(servers ?? {});
9785
+ const present = entries.some(([, s]) => s?.command === "node9");
9786
+ const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
9787
+ return { wrapped, present };
9788
+ }
9789
+ function readMcp(filePath, format) {
9790
+ if (!import_fs14.default.existsSync(filePath)) return { wrapped: [], present: false };
9791
+ try {
9792
+ if (format === "toml") {
9793
+ const parsed2 = (0, import_smol_toml2.parse)(import_fs14.default.readFileSync(filePath, "utf-8"));
9794
+ return detectMcp(parsed2?.mcp_servers);
9795
+ }
9796
+ const parsed = readJson2(filePath);
9797
+ if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
9798
+ return detectMcp(parsed.mcpServers);
9799
+ } catch {
9800
+ return { wrapped: [], present: false };
9801
+ }
9802
+ }
9803
+ function getAgentWiring(home = import_os13.default.homedir()) {
9804
+ const detected = detectAgents(home);
9805
+ return AGENT_SPECS.map((spec) => {
9806
+ const present = spec.present(home);
9807
+ const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
9808
+ let hooks;
9809
+ let wireState;
9810
+ let hookLabel;
9811
+ let settingsPath;
9812
+ if (spec.shimFile) {
9813
+ const shimWired = exists(spec.shimFile(home));
9814
+ hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
9815
+ wireState = shimWired ? "wired" : present ? "unwired" : "absent";
9816
+ hookLabel = "node9 plugin";
9817
+ settingsPath = spec.shimFile(home);
9818
+ } else {
9819
+ const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
9820
+ const primary = spec.hookEvents[0];
9821
+ const rootPresent = root !== "absent" && root !== "invalid";
9822
+ hooks = spec.hookEvents.map((ev) => ({
9823
+ label: hookLabelOf(ev, pad),
9824
+ wired: rootPresent && eventWired(root, ev, spec.hookFormat)
9825
+ }));
9826
+ if (root === "absent") wireState = "absent";
9827
+ else if (root === "invalid") wireState = "invalid";
9828
+ else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
9829
+ hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
9830
+ settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
9831
+ }
9832
+ const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
9833
+ const anyHookWired = hooks.some((h) => h.wired);
9834
+ return {
9835
+ id: spec.id,
9836
+ label: spec.label,
9837
+ setupCommand: spec.setupCommand,
9838
+ installed: detected[spec.id],
9839
+ present,
9840
+ hooks,
9841
+ wireState,
9842
+ hookLabel,
9843
+ settingsPath,
9844
+ configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
9845
+ mcpServers: mcp ? mcp.wrapped : null,
9846
+ mcpProtected: mcp ? mcp.present : false,
9847
+ isProtected: anyHookWired || (mcp?.present ?? false)
9848
+ };
9849
+ });
9850
+ }
9851
+ var import_fs14, import_path16, import_os13, yaml2, import_smol_toml2, exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
9852
+ var init_agent_wiring = __esm({
9853
+ "src/agent-wiring.ts"() {
9854
+ "use strict";
9855
+ import_fs14 = __toESM(require("fs"));
9856
+ import_path16 = __toESM(require("path"));
9857
+ import_os13 = __toESM(require("os"));
9858
+ yaml2 = __toESM(require("yaml"));
9859
+ import_smol_toml2 = require("smol-toml");
9860
+ init_setup();
9861
+ exists = (p) => {
9862
+ try {
9863
+ return import_fs14.default.existsSync(p);
9864
+ } catch {
9865
+ return false;
9866
+ }
9867
+ };
9868
+ ck = (key) => ({ key, kind: "check" });
9869
+ lg = (key) => ({ key, kind: "log" });
9870
+ DEFAULT_LABEL_PAD = 11;
9871
+ hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
9872
+ AGENT_SPECS = [
9873
+ {
9874
+ id: "claude",
9875
+ label: "Claude Code",
9876
+ setupCommand: "node9 agents add claude",
9877
+ hookFile: (h) => import_path16.default.join(h, ".claude", "settings.json"),
9878
+ hookFormat: "matcher",
9879
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
9880
+ mcpFile: (h) => import_path16.default.join(h, ".claude.json"),
9881
+ present: (h) => exists(import_path16.default.join(h, ".claude", "settings.json")) || exists(import_path16.default.join(h, ".claude.json"))
9882
+ },
9883
+ {
9884
+ id: "gemini",
9885
+ label: "Gemini CLI",
9886
+ setupCommand: "node9 agents add gemini",
9887
+ hookFile: (h) => import_path16.default.join(h, ".gemini", "settings.json"),
9888
+ hookFormat: "matcher",
9889
+ hookEvents: [ck("BeforeTool"), lg("AfterTool")],
9890
+ mcpFile: (h) => import_path16.default.join(h, ".gemini", "settings.json"),
9891
+ present: (h) => exists(import_path16.default.join(h, ".gemini", "settings.json"))
9892
+ },
9893
+ {
9894
+ id: "codex",
9895
+ label: "Codex",
9896
+ setupCommand: "node9 agents add codex",
9897
+ hookFile: (h) => import_path16.default.join(h, ".codex", "hooks.json"),
9898
+ hookFormat: "matcher",
9899
+ hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
9900
+ mcpFile: (h) => import_path16.default.join(h, ".codex", "config.toml"),
9901
+ mcpFormat: "toml",
9902
+ present: (h) => exists(import_path16.default.join(h, ".codex"))
9903
+ },
9904
+ {
9905
+ id: "antigravity",
9906
+ label: "Antigravity",
9907
+ setupCommand: "node9 agents add antigravity",
9908
+ hookFile: (h) => import_path16.default.join(h, ".gemini", "config", "hooks.json"),
9909
+ hookFormat: "matcher",
9910
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
9911
+ mcpFile: (h) => import_path16.default.join(h, ".gemini", "config", "mcp_config.json"),
9912
+ present: (h) => exists(import_path16.default.join(h, ".gemini", "config", "hooks.json")) || exists(import_path16.default.join(h, ".gemini", "antigravity-cli")) || exists(import_path16.default.join(h, ".gemini", "antigravity-ide"))
9913
+ },
9914
+ {
9915
+ id: "copilot",
9916
+ label: "GitHub Copilot",
9917
+ setupCommand: "node9 agents add copilot",
9918
+ hookFile: (h) => import_path16.default.join(h, ".copilot", "hooks", "node9.json"),
9919
+ hookFormat: "flat",
9920
+ hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
9921
+ mcpFile: (h) => import_path16.default.join(h, ".copilot", "mcp-config.json"),
9922
+ present: (h) => exists(import_path16.default.join(h, ".copilot"))
9923
+ },
9924
+ {
9925
+ id: "cursor",
9926
+ label: "Cursor",
9927
+ setupCommand: "node9 agents add cursor",
9928
+ // MCP-only — no hook file (see note above).
9929
+ hookFormat: "flat",
9930
+ hookEvents: [],
9931
+ mcpFile: (h) => import_path16.default.join(h, ".cursor", "mcp.json"),
9932
+ present: (h) => exists(import_path16.default.join(h, ".cursor", "mcp.json"))
9933
+ },
9934
+ {
9935
+ id: "hermes",
9936
+ label: "Hermes Agent",
9937
+ setupCommand: "node9 agents add hermes",
9938
+ hookFile: (h) => hermesConfigPath(h),
9939
+ hookFormat: "yaml",
9940
+ hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
9941
+ labelPad: 14,
9942
+ // 'post_tool_call' is wider than the default
9943
+ present: (h) => exists(hermesConfigPath(h))
9944
+ },
9945
+ {
9946
+ // Plugin-shim agents — protected by a node9-authored plugin/extension file
9947
+ // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
9948
+ id: "opencode",
9949
+ label: "OpenCode",
9950
+ setupCommand: "node9 agents add opencode",
9951
+ hookFormat: "flat",
9952
+ hookEvents: [],
9953
+ shimFile: (h) => import_path16.default.join(h, ".config", "opencode", "plugins", "node9.js"),
9954
+ present: (h) => exists(import_path16.default.join(h, ".config", "opencode")) || exists(import_path16.default.join(h, ".config", "opencode", "plugins", "node9.js"))
9955
+ },
9956
+ {
9957
+ id: "pi",
9958
+ label: "Pi",
9959
+ setupCommand: "node9 agents add pi",
9960
+ hookFormat: "flat",
9961
+ hookEvents: [],
9962
+ shimFile: (h) => import_path16.default.join(h, ".pi", "agent", "extensions", "node9.js"),
9963
+ present: (h) => exists(import_path16.default.join(h, ".pi", "agent")) || exists(import_path16.default.join(h, ".pi", "agent", "extensions", "node9.js"))
9964
+ }
9965
+ ];
9966
+ }
9967
+ });
9968
+
9728
9969
  // src/pricing/litellm.ts
9729
9970
  function normalizeModel(raw) {
9730
9971
  return raw.replace(/-\d{8}$/, "").toLowerCase();
9731
9972
  }
9732
9973
  function readCache() {
9733
9974
  try {
9734
- const raw = JSON.parse(import_fs14.default.readFileSync(CACHE_FILE(), "utf-8"));
9975
+ const raw = JSON.parse(import_fs15.default.readFileSync(CACHE_FILE(), "utf-8"));
9735
9976
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
9736
9977
  return null;
9737
9978
  }
@@ -9745,18 +9986,18 @@ function readCache() {
9745
9986
  function writeCache(prices) {
9746
9987
  try {
9747
9988
  const target = CACHE_FILE();
9748
- const dir = import_path16.default.dirname(target);
9749
- if (!import_fs14.default.existsSync(dir)) import_fs14.default.mkdirSync(dir, { recursive: true });
9989
+ const dir = import_path17.default.dirname(target);
9990
+ if (!import_fs15.default.existsSync(dir)) import_fs15.default.mkdirSync(dir, { recursive: true });
9750
9991
  const tmp = target + ".tmp";
9751
9992
  const body = {
9752
9993
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
9753
9994
  prices
9754
9995
  };
9755
- import_fs14.default.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
9756
- import_fs14.default.renameSync(tmp, target);
9996
+ import_fs15.default.writeFileSync(tmp, JSON.stringify(body) + "\n", "utf-8");
9997
+ import_fs15.default.renameSync(tmp, target);
9757
9998
  } catch (err2) {
9758
9999
  try {
9759
- import_fs14.default.appendFileSync(
10000
+ import_fs15.default.appendFileSync(
9760
10001
  HOOK_DEBUG_LOG,
9761
10002
  `[pricing] cache write failed: ${err2.message}
9762
10003
  `
@@ -9857,13 +10098,13 @@ function pricingFor(model) {
9857
10098
  lookupCache.set(norm, resolved);
9858
10099
  return resolved;
9859
10100
  }
9860
- var import_fs14, import_path16, import_os13, LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, diskChecked, lookupCache;
10101
+ var import_fs15, import_path17, import_os14, LITELLM_URL, BUNDLED_PRICING, CACHE_FILE, TTL_MS, memCache, memCacheAt, diskChecked, lookupCache;
9861
10102
  var init_litellm = __esm({
9862
10103
  "src/pricing/litellm.ts"() {
9863
10104
  "use strict";
9864
- import_fs14 = __toESM(require("fs"));
9865
- import_path16 = __toESM(require("path"));
9866
- import_os13 = __toESM(require("os"));
10105
+ import_fs15 = __toESM(require("fs"));
10106
+ import_path17 = __toESM(require("path"));
10107
+ import_os14 = __toESM(require("os"));
9867
10108
  init_audit();
9868
10109
  LITELLM_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
9869
10110
  BUNDLED_PRICING = {
@@ -9903,7 +10144,7 @@ var init_litellm = __esm({
9903
10144
  "gemini-2.0-flash": [75e-9, 3e-7, 0, 0],
9904
10145
  "gemini-1.5-pro": [125e-8, 5e-6, 0, 0]
9905
10146
  };
9906
- CACHE_FILE = () => import_path16.default.join(import_os13.default.homedir(), ".node9", "model-pricing.json");
10147
+ CACHE_FILE = () => import_path17.default.join(import_os14.default.homedir(), ".node9", "model-pricing.json");
9907
10148
  TTL_MS = 24 * 60 * 60 * 1e3;
9908
10149
  memCache = null;
9909
10150
  memCacheAt = 0;
@@ -9914,7 +10155,7 @@ var init_litellm = __esm({
9914
10155
 
9915
10156
  // src/cost-gemini.ts
9916
10157
  function geminiTmpDir() {
9917
- return import_path17.default.join(import_os14.default.homedir(), ".gemini", "tmp");
10158
+ return import_path18.default.join(import_os15.default.homedir(), ".gemini", "tmp");
9918
10159
  }
9919
10160
  function geminiPriceFor(model) {
9920
10161
  let tuple = pricingFor(model);
@@ -9929,14 +10170,14 @@ function geminiPriceFor(model) {
9929
10170
  }
9930
10171
  function safeReaddir(dir) {
9931
10172
  try {
9932
- return import_fs15.default.readdirSync(dir);
10173
+ return import_fs16.default.readdirSync(dir);
9933
10174
  } catch {
9934
10175
  return [];
9935
10176
  }
9936
10177
  }
9937
10178
  function isDir(p) {
9938
10179
  try {
9939
- return import_fs15.default.statSync(p).isDirectory();
10180
+ return import_fs16.default.statSync(p).isDirectory();
9940
10181
  } catch {
9941
10182
  return false;
9942
10183
  }
@@ -9944,11 +10185,11 @@ function isDir(p) {
9944
10185
  function listGeminiSessionFiles(base) {
9945
10186
  const out = [];
9946
10187
  for (const project of safeReaddir(base)) {
9947
- const chats = import_path17.default.join(base, project, "chats");
10188
+ const chats = import_path18.default.join(base, project, "chats");
9948
10189
  if (!isDir(chats)) continue;
9949
10190
  for (const f of safeReaddir(chats)) {
9950
10191
  if (f.startsWith("session-") && f.endsWith(".jsonl")) {
9951
- out.push({ file: import_path17.default.join(chats, f), project });
10192
+ out.push({ file: import_path18.default.join(chats, f), project });
9952
10193
  }
9953
10194
  }
9954
10195
  }
@@ -10005,20 +10246,20 @@ function parseGeminiSession(lines, project) {
10005
10246
  if (runId) for (const e of byKey.values()) e.runId = runId;
10006
10247
  return [...byKey.values()];
10007
10248
  }
10008
- var import_fs15, import_os14, import_path17, GEMINI_FALLBACK_MODELS, geminiSource;
10249
+ var import_fs16, import_os15, import_path18, GEMINI_FALLBACK_MODELS, geminiSource;
10009
10250
  var init_cost_gemini = __esm({
10010
10251
  "src/cost-gemini.ts"() {
10011
10252
  "use strict";
10012
- import_fs15 = __toESM(require("fs"));
10013
- import_os14 = __toESM(require("os"));
10014
- import_path17 = __toESM(require("path"));
10253
+ import_fs16 = __toESM(require("fs"));
10254
+ import_os15 = __toESM(require("os"));
10255
+ import_path18 = __toESM(require("path"));
10015
10256
  init_litellm();
10016
10257
  GEMINI_FALLBACK_MODELS = ["gemini-2.5-flash", "gemini-2.0-flash"];
10017
10258
  geminiSource = {
10018
10259
  id: "gemini",
10019
10260
  available() {
10020
10261
  try {
10021
- return import_fs15.default.existsSync(geminiTmpDir());
10262
+ return import_fs16.default.existsSync(geminiTmpDir());
10022
10263
  } catch {
10023
10264
  return false;
10024
10265
  }
@@ -10027,13 +10268,13 @@ var init_cost_gemini = __esm({
10027
10268
  const combined = /* @__PURE__ */ new Map();
10028
10269
  for (const { file, project } of listGeminiSessionFiles(geminiTmpDir())) {
10029
10270
  try {
10030
- if (sinceMs !== void 0 && import_fs15.default.statSync(file).mtimeMs < sinceMs) continue;
10271
+ if (sinceMs !== void 0 && import_fs16.default.statSync(file).mtimeMs < sinceMs) continue;
10031
10272
  } catch {
10032
10273
  continue;
10033
10274
  }
10034
10275
  let content;
10035
10276
  try {
10036
- content = import_fs15.default.readFileSync(file, "utf8");
10277
+ content = import_fs16.default.readFileSync(file, "utf8");
10037
10278
  } catch {
10038
10279
  continue;
10039
10280
  }
@@ -10059,7 +10300,7 @@ var init_cost_gemini = __esm({
10059
10300
 
10060
10301
  // src/cost-codex.ts
10061
10302
  function codexSessionsDir() {
10062
- return import_path18.default.join(import_os15.default.homedir(), ".codex", "sessions");
10303
+ return import_path19.default.join(import_os16.default.homedir(), ".codex", "sessions");
10063
10304
  }
10064
10305
  function codexPriceFor(model) {
10065
10306
  return pricingFor(model) ?? CODEX_FALLBACK;
@@ -10072,16 +10313,16 @@ function codexSessionCost(model, tokens) {
10072
10313
  function listCodexSessionFiles(base) {
10073
10314
  const out = [];
10074
10315
  for (const y of safeReaddir2(base)) {
10075
- const yp = import_path18.default.join(base, y);
10316
+ const yp = import_path19.default.join(base, y);
10076
10317
  if (!isDir2(yp)) continue;
10077
10318
  for (const m of safeReaddir2(yp)) {
10078
- const mp = import_path18.default.join(yp, m);
10319
+ const mp = import_path19.default.join(yp, m);
10079
10320
  if (!isDir2(mp)) continue;
10080
10321
  for (const d of safeReaddir2(mp)) {
10081
- const dp = import_path18.default.join(mp, d);
10322
+ const dp = import_path19.default.join(mp, d);
10082
10323
  if (!isDir2(dp)) continue;
10083
10324
  for (const f of safeReaddir2(dp)) {
10084
- if (f.endsWith(".jsonl")) out.push(import_path18.default.join(dp, f));
10325
+ if (f.endsWith(".jsonl")) out.push(import_path19.default.join(dp, f));
10085
10326
  }
10086
10327
  }
10087
10328
  }
@@ -10090,14 +10331,14 @@ function listCodexSessionFiles(base) {
10090
10331
  }
10091
10332
  function safeReaddir2(dir) {
10092
10333
  try {
10093
- return import_fs16.default.readdirSync(dir);
10334
+ return import_fs17.default.readdirSync(dir);
10094
10335
  } catch {
10095
10336
  return [];
10096
10337
  }
10097
10338
  }
10098
10339
  function isDir2(p) {
10099
10340
  try {
10100
- return import_fs16.default.statSync(p).isDirectory();
10341
+ return import_fs17.default.statSync(p).isDirectory();
10101
10342
  } catch {
10102
10343
  return false;
10103
10344
  }
@@ -10157,20 +10398,20 @@ function parseCodexSession(lines) {
10157
10398
  cacheWriteTokens: 0
10158
10399
  };
10159
10400
  }
10160
- var import_fs16, import_os15, import_path18, CODEX_FALLBACK, codexSource;
10401
+ var import_fs17, import_os16, import_path19, CODEX_FALLBACK, codexSource;
10161
10402
  var init_cost_codex = __esm({
10162
10403
  "src/cost-codex.ts"() {
10163
10404
  "use strict";
10164
- import_fs16 = __toESM(require("fs"));
10165
- import_os15 = __toESM(require("os"));
10166
- import_path18 = __toESM(require("path"));
10405
+ import_fs17 = __toESM(require("fs"));
10406
+ import_os16 = __toESM(require("os"));
10407
+ import_path19 = __toESM(require("path"));
10167
10408
  init_litellm();
10168
10409
  CODEX_FALLBACK = [5e-6, 15e-6, 0, 25e-7];
10169
10410
  codexSource = {
10170
10411
  id: "codex",
10171
10412
  available() {
10172
10413
  try {
10173
- return import_fs16.default.existsSync(codexSessionsDir());
10414
+ return import_fs17.default.existsSync(codexSessionsDir());
10174
10415
  } catch {
10175
10416
  return false;
10176
10417
  }
@@ -10180,13 +10421,13 @@ var init_cost_codex = __esm({
10180
10421
  const combined = /* @__PURE__ */ new Map();
10181
10422
  for (const file of listCodexSessionFiles(base)) {
10182
10423
  try {
10183
- if (sinceMs !== void 0 && import_fs16.default.statSync(file).mtimeMs < sinceMs) continue;
10424
+ if (sinceMs !== void 0 && import_fs17.default.statSync(file).mtimeMs < sinceMs) continue;
10184
10425
  } catch {
10185
10426
  continue;
10186
10427
  }
10187
10428
  let content;
10188
10429
  try {
10189
- content = import_fs16.default.readFileSync(file, "utf8");
10430
+ content = import_fs17.default.readFileSync(file, "utf8");
10190
10431
  } catch {
10191
10432
  continue;
10192
10433
  }
@@ -10494,79 +10735,79 @@ var init_scan_summary = __esm({
10494
10735
  function buildSensitivePaths(home, cwd) {
10495
10736
  return [
10496
10737
  {
10497
- full: import_path19.default.join(home, ".ssh", "id_rsa"),
10738
+ full: import_path20.default.join(home, ".ssh", "id_rsa"),
10498
10739
  label: "~/.ssh/id_rsa",
10499
10740
  description: "RSA private key \u2014 grants SSH access to your servers",
10500
10741
  score: 20
10501
10742
  },
10502
10743
  {
10503
- full: import_path19.default.join(home, ".ssh", "id_ed25519"),
10744
+ full: import_path20.default.join(home, ".ssh", "id_ed25519"),
10504
10745
  label: "~/.ssh/id_ed25519",
10505
10746
  description: "Ed25519 private key \u2014 grants SSH access to your servers",
10506
10747
  score: 20
10507
10748
  },
10508
10749
  {
10509
- full: import_path19.default.join(home, ".ssh", "id_ecdsa"),
10750
+ full: import_path20.default.join(home, ".ssh", "id_ecdsa"),
10510
10751
  label: "~/.ssh/id_ecdsa",
10511
10752
  description: "ECDSA private key \u2014 grants SSH access to your servers",
10512
10753
  score: 20
10513
10754
  },
10514
10755
  {
10515
- full: import_path19.default.join(home, ".aws", "credentials"),
10756
+ full: import_path20.default.join(home, ".aws", "credentials"),
10516
10757
  label: "~/.aws/credentials",
10517
10758
  description: "AWS access keys \u2014 full cloud account access",
10518
10759
  score: 20
10519
10760
  },
10520
10761
  {
10521
- full: import_path19.default.join(home, ".aws", "config"),
10762
+ full: import_path20.default.join(home, ".aws", "config"),
10522
10763
  label: "~/.aws/config",
10523
10764
  description: "AWS configuration \u2014 account and region settings",
10524
10765
  score: 5
10525
10766
  },
10526
10767
  {
10527
- full: import_path19.default.join(home, ".config", "gcloud", "credentials.db"),
10768
+ full: import_path20.default.join(home, ".config", "gcloud", "credentials.db"),
10528
10769
  label: "~/.config/gcloud/credentials.db",
10529
10770
  description: "Google Cloud credentials",
10530
10771
  score: 15
10531
10772
  },
10532
10773
  {
10533
- full: import_path19.default.join(home, ".docker", "config.json"),
10774
+ full: import_path20.default.join(home, ".docker", "config.json"),
10534
10775
  label: "~/.docker/config.json",
10535
10776
  description: "Docker registry auth tokens",
10536
10777
  score: 10
10537
10778
  },
10538
10779
  {
10539
- full: import_path19.default.join(home, ".netrc"),
10780
+ full: import_path20.default.join(home, ".netrc"),
10540
10781
  label: "~/.netrc",
10541
10782
  description: "FTP/HTTP credentials in plain text",
10542
10783
  score: 15
10543
10784
  },
10544
10785
  {
10545
- full: import_path19.default.join(home, ".npmrc"),
10786
+ full: import_path20.default.join(home, ".npmrc"),
10546
10787
  label: "~/.npmrc",
10547
10788
  description: "npm auth token \u2014 can publish packages as you",
10548
10789
  score: 10
10549
10790
  },
10550
10791
  {
10551
- full: import_path19.default.join(home, ".node9", "credentials.json"),
10792
+ full: import_path20.default.join(home, ".node9", "credentials.json"),
10552
10793
  label: "~/.node9/credentials.json",
10553
10794
  description: "Node9 cloud API key",
10554
10795
  score: 10
10555
10796
  },
10556
10797
  {
10557
- full: import_path19.default.join(cwd, ".env"),
10798
+ full: import_path20.default.join(cwd, ".env"),
10558
10799
  label: ".env (current folder)",
10559
10800
  description: "App secrets \u2014 database passwords, API keys",
10560
10801
  score: 20
10561
10802
  },
10562
10803
  {
10563
- full: import_path19.default.join(cwd, ".env.local"),
10804
+ full: import_path20.default.join(cwd, ".env.local"),
10564
10805
  label: ".env.local (current folder)",
10565
10806
  description: "Local overrides \u2014 often contains real credentials",
10566
10807
  score: 15
10567
10808
  },
10568
10809
  {
10569
- full: import_path19.default.join(cwd, ".env.production"),
10810
+ full: import_path20.default.join(cwd, ".env.production"),
10570
10811
  label: ".env.production (current folder)",
10571
10812
  description: "Production secrets",
10572
10813
  score: 20
@@ -10575,7 +10816,7 @@ function buildSensitivePaths(home, cwd) {
10575
10816
  }
10576
10817
  function isReadable(filePath) {
10577
10818
  try {
10578
- import_fs17.default.accessSync(filePath, import_fs17.default.constants.R_OK);
10819
+ import_fs18.default.accessSync(filePath, import_fs18.default.constants.R_OK);
10579
10820
  return true;
10580
10821
  } catch {
10581
10822
  return false;
@@ -10588,13 +10829,13 @@ function scoreLabel(score) {
10588
10829
  return import_chalk2.default.red.bold(`${score}/100 Critical`);
10589
10830
  }
10590
10831
  function runBlast() {
10591
- const home = import_os16.default.homedir();
10832
+ const home = import_os17.default.homedir();
10592
10833
  const cwd = process.cwd();
10593
10834
  const paths = buildSensitivePaths(home, cwd);
10594
10835
  let scoreDeduction = 0;
10595
10836
  const reachable = [];
10596
10837
  for (const p of paths) {
10597
- if (import_fs17.default.existsSync(p.full) && isReadable(p.full)) {
10838
+ if (import_fs18.default.existsSync(p.full) && isReadable(p.full)) {
10598
10839
  reachable.push(p);
10599
10840
  scoreDeduction += p.score;
10600
10841
  }
@@ -10612,7 +10853,7 @@ function runBlast() {
10612
10853
  }
10613
10854
  function registerBlastCommand(program2) {
10614
10855
  program2.command("blast").description("Map what an AI agent can currently reach on this machine").action(() => {
10615
- const home = import_os16.default.homedir();
10856
+ const home = import_os17.default.homedir();
10616
10857
  const cwd = process.cwd();
10617
10858
  const { reachable, envFindings, score } = runBlast();
10618
10859
  console.log("");
@@ -10659,14 +10900,14 @@ function registerBlastCommand(program2) {
10659
10900
  console.log("");
10660
10901
  });
10661
10902
  }
10662
- var import_chalk2, import_fs17, import_path19, import_os16;
10903
+ var import_chalk2, import_fs18, import_path20, import_os17;
10663
10904
  var init_blast = __esm({
10664
10905
  "src/cli/commands/blast.ts"() {
10665
10906
  "use strict";
10666
10907
  import_chalk2 = __toESM(require("chalk"));
10667
- import_fs17 = __toESM(require("fs"));
10668
- import_path19 = __toESM(require("path"));
10669
- import_os16 = __toESM(require("os"));
10908
+ import_fs18 = __toESM(require("fs"));
10909
+ import_path20 = __toESM(require("path"));
10910
+ import_os17 = __toESM(require("os"));
10670
10911
  init_dlp();
10671
10912
  }
10672
10913
  });
@@ -10794,13 +11035,13 @@ var init_scan_json = __esm({
10794
11035
 
10795
11036
  // src/cli/render/scan-history.ts
10796
11037
  function defaultHistoryPath() {
10797
- return import_path20.default.join(import_os17.default.homedir(), ".node9", "scan-history.json");
11038
+ return import_path21.default.join(import_os18.default.homedir(), ".node9", "scan-history.json");
10798
11039
  }
10799
11040
  function readPreviousScan(opts = {}) {
10800
11041
  const filePath = opts.path ?? defaultHistoryPath();
10801
11042
  try {
10802
- if (!import_fs18.default.existsSync(filePath)) return null;
10803
- const raw = import_fs18.default.readFileSync(filePath, "utf8");
11043
+ if (!import_fs19.default.existsSync(filePath)) return null;
11044
+ const raw = import_fs19.default.readFileSync(filePath, "utf8");
10804
11045
  const parsed = JSON.parse(raw);
10805
11046
  if (!Array.isArray(parsed) || parsed.length === 0) return null;
10806
11047
  const last = parsed[parsed.length - 1];
@@ -10814,11 +11055,11 @@ function appendScanHistory(record, opts = {}) {
10814
11055
  const filePath = opts.path ?? defaultHistoryPath();
10815
11056
  const cap = opts.cap ?? SCAN_HISTORY_CAP;
10816
11057
  try {
10817
- import_fs18.default.mkdirSync(import_path20.default.dirname(filePath), { recursive: true });
11058
+ import_fs19.default.mkdirSync(import_path21.default.dirname(filePath), { recursive: true });
10818
11059
  let history = [];
10819
- if (import_fs18.default.existsSync(filePath)) {
11060
+ if (import_fs19.default.existsSync(filePath)) {
10820
11061
  try {
10821
- const parsed = JSON.parse(import_fs18.default.readFileSync(filePath, "utf8"));
11062
+ const parsed = JSON.parse(import_fs19.default.readFileSync(filePath, "utf8"));
10822
11063
  if (Array.isArray(parsed)) {
10823
11064
  history = parsed.filter(isValidRecord);
10824
11065
  }
@@ -10829,7 +11070,7 @@ function appendScanHistory(record, opts = {}) {
10829
11070
  if (history.length > cap) {
10830
11071
  history = history.slice(history.length - cap);
10831
11072
  }
10832
- import_fs18.default.writeFileSync(filePath, JSON.stringify(history, null, 2));
11073
+ import_fs19.default.writeFileSync(filePath, JSON.stringify(history, null, 2));
10833
11074
  } catch (err2) {
10834
11075
  process.stderr.write(
10835
11076
  `[node9] Warning: could not write scan-history.json: ${err2.message}
@@ -10851,24 +11092,24 @@ function isValidRecord(x) {
10851
11092
  const r = x;
10852
11093
  return typeof r.timestamp === "string" && typeof r.score === "number" && typeof r.blocked === "number" && typeof r.review === "number" && typeof r.leaks === "number" && typeof r.loops === "number" && typeof r.totalCalls === "number";
10853
11094
  }
10854
- var import_fs18, import_path20, import_os17, SCAN_HISTORY_CAP;
11095
+ var import_fs19, import_path21, import_os18, SCAN_HISTORY_CAP;
10855
11096
  var init_scan_history = __esm({
10856
11097
  "src/cli/render/scan-history.ts"() {
10857
11098
  "use strict";
10858
- import_fs18 = __toESM(require("fs"));
10859
- import_path20 = __toESM(require("path"));
10860
- import_os17 = __toESM(require("os"));
11099
+ import_fs19 = __toESM(require("fs"));
11100
+ import_path21 = __toESM(require("path"));
11101
+ import_os18 = __toESM(require("os"));
10861
11102
  SCAN_HISTORY_CAP = 30;
10862
11103
  }
10863
11104
  });
10864
11105
 
10865
11106
  // src/cost-copilot.ts
10866
11107
  function copilotSessionsDir() {
10867
- return import_path21.default.join(import_os18.default.homedir(), ".copilot", "session-state");
11108
+ return import_path22.default.join(import_os19.default.homedir(), ".copilot", "session-state");
10868
11109
  }
10869
11110
  function safeReaddir3(dir) {
10870
11111
  try {
10871
- return import_fs19.default.readdirSync(dir);
11112
+ return import_fs20.default.readdirSync(dir);
10872
11113
  } catch {
10873
11114
  return [];
10874
11115
  }
@@ -10935,19 +11176,19 @@ function parseCopilotSession(lines) {
10935
11176
  }
10936
11177
  return rows;
10937
11178
  }
10938
- var import_fs19, import_os18, import_path21, copilotSource;
11179
+ var import_fs20, import_os19, import_path22, copilotSource;
10939
11180
  var init_cost_copilot = __esm({
10940
11181
  "src/cost-copilot.ts"() {
10941
11182
  "use strict";
10942
- import_fs19 = __toESM(require("fs"));
10943
- import_os18 = __toESM(require("os"));
10944
- import_path21 = __toESM(require("path"));
11183
+ import_fs20 = __toESM(require("fs"));
11184
+ import_os19 = __toESM(require("os"));
11185
+ import_path22 = __toESM(require("path"));
10945
11186
  init_litellm();
10946
11187
  copilotSource = {
10947
11188
  id: "copilot",
10948
11189
  available() {
10949
11190
  try {
10950
- return import_fs19.default.existsSync(copilotSessionsDir());
11191
+ return import_fs20.default.existsSync(copilotSessionsDir());
10951
11192
  } catch {
10952
11193
  return false;
10953
11194
  }
@@ -10956,15 +11197,15 @@ var init_cost_copilot = __esm({
10956
11197
  const base = copilotSessionsDir();
10957
11198
  const combined = /* @__PURE__ */ new Map();
10958
11199
  for (const sid of safeReaddir3(base)) {
10959
- const file = import_path21.default.join(base, sid, "events.jsonl");
11200
+ const file = import_path22.default.join(base, sid, "events.jsonl");
10960
11201
  try {
10961
- if (sinceMs !== void 0 && import_fs19.default.statSync(file).mtimeMs < sinceMs) continue;
11202
+ if (sinceMs !== void 0 && import_fs20.default.statSync(file).mtimeMs < sinceMs) continue;
10962
11203
  } catch {
10963
11204
  continue;
10964
11205
  }
10965
11206
  let content;
10966
11207
  try {
10967
- content = import_fs19.default.readFileSync(file, "utf8");
11208
+ content = import_fs20.default.readFileSync(file, "utf8");
10968
11209
  } catch {
10969
11210
  continue;
10970
11211
  }
@@ -10993,10 +11234,10 @@ function decodeProjectDirName(dirName) {
10993
11234
  return dirName.replace(/-/g, "/");
10994
11235
  }
10995
11236
  function parseJSONLFile(filePath, fallbackWorkingDir) {
10996
- const runId = import_path22.default.basename(filePath, ".jsonl");
11237
+ const runId = import_path23.default.basename(filePath, ".jsonl");
10997
11238
  let content;
10998
11239
  try {
10999
- content = import_fs20.default.readFileSync(filePath, "utf8");
11240
+ content = import_fs21.default.readFileSync(filePath, "utf8");
11000
11241
  } catch {
11001
11242
  return /* @__PURE__ */ new Map();
11002
11243
  }
@@ -11096,7 +11337,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11096
11337
  signal: AbortSignal.timeout(15e3)
11097
11338
  });
11098
11339
  if (!res.ok) {
11099
- import_fs20.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
11340
+ import_fs21.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
11100
11341
  `);
11101
11342
  } else {
11102
11343
  let stored;
@@ -11106,7 +11347,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11106
11347
  } catch {
11107
11348
  }
11108
11349
  if (typeof stored === "number" && stored < batch.length) {
11109
- import_fs20.default.appendFileSync(
11350
+ import_fs21.default.appendFileSync(
11110
11351
  HOOK_DEBUG_LOG,
11111
11352
  `[cost-sync] dropped ${batch.length - stored} of ${batch.length} rows
11112
11353
  `
@@ -11114,7 +11355,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
11114
11355
  }
11115
11356
  }
11116
11357
  } catch (err2) {
11117
- import_fs20.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
11358
+ import_fs21.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
11118
11359
  `);
11119
11360
  }
11120
11361
  }
@@ -11127,10 +11368,10 @@ async function syncCost() {
11127
11368
  if (entries.length === 0) return;
11128
11369
  let username = "unknown";
11129
11370
  try {
11130
- username = import_os19.default.userInfo().username;
11371
+ username = import_os20.default.userInfo().username;
11131
11372
  } catch {
11132
11373
  }
11133
- const machineId = `${import_os19.default.hostname()}:${username}`;
11374
+ const machineId = `${import_os20.default.hostname()}:${username}`;
11134
11375
  await postCostBatches(creds.apiUrl, creds.apiKey, machineId, entries);
11135
11376
  }
11136
11377
  function startCostSync() {
@@ -11142,13 +11383,13 @@ function startCostSync() {
11142
11383
  }, SYNC_INTERVAL_MS);
11143
11384
  timer.unref();
11144
11385
  }
11145
- var import_fs20, import_path22, import_os19, SYNC_INTERVAL_MS, claudeSource, COST_SOURCES, COST_BATCH_SIZE;
11386
+ var import_fs21, import_path23, import_os20, SYNC_INTERVAL_MS, claudeSource, COST_SOURCES, COST_BATCH_SIZE;
11146
11387
  var init_costSync = __esm({
11147
11388
  "src/costSync.ts"() {
11148
11389
  "use strict";
11149
- import_fs20 = __toESM(require("fs"));
11150
- import_path22 = __toESM(require("path"));
11151
- import_os19 = __toESM(require("os"));
11390
+ import_fs21 = __toESM(require("fs"));
11391
+ import_path23 = __toESM(require("path"));
11392
+ import_os20 = __toESM(require("os"));
11152
11393
  init_config();
11153
11394
  init_audit();
11154
11395
  init_litellm();
@@ -11159,37 +11400,37 @@ var init_costSync = __esm({
11159
11400
  claudeSource = {
11160
11401
  id: "claude",
11161
11402
  available() {
11162
- return import_fs20.default.existsSync(import_path22.default.join(import_os19.default.homedir(), ".claude", "projects"));
11403
+ return import_fs21.default.existsSync(import_path23.default.join(import_os20.default.homedir(), ".claude", "projects"));
11163
11404
  },
11164
11405
  collect(sinceMs) {
11165
- const projectsDir = import_path22.default.join(import_os19.default.homedir(), ".claude", "projects");
11166
- if (!import_fs20.default.existsSync(projectsDir)) return [];
11406
+ const projectsDir = import_path23.default.join(import_os20.default.homedir(), ".claude", "projects");
11407
+ if (!import_fs21.default.existsSync(projectsDir)) return [];
11167
11408
  const combined = /* @__PURE__ */ new Map();
11168
11409
  let dirs;
11169
11410
  try {
11170
- dirs = import_fs20.default.readdirSync(projectsDir);
11411
+ dirs = import_fs21.default.readdirSync(projectsDir);
11171
11412
  } catch {
11172
11413
  return [];
11173
11414
  }
11174
11415
  for (const dir of dirs) {
11175
- const dirPath = import_path22.default.join(projectsDir, dir);
11416
+ const dirPath = import_path23.default.join(projectsDir, dir);
11176
11417
  try {
11177
- if (!import_fs20.default.statSync(dirPath).isDirectory()) continue;
11418
+ if (!import_fs21.default.statSync(dirPath).isDirectory()) continue;
11178
11419
  } catch {
11179
11420
  continue;
11180
11421
  }
11181
11422
  let files;
11182
11423
  try {
11183
- files = import_fs20.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11424
+ files = import_fs21.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11184
11425
  } catch {
11185
11426
  continue;
11186
11427
  }
11187
11428
  const fallbackWorkingDir = decodeProjectDirName(dir);
11188
11429
  for (const file of files) {
11189
- const filePath = import_path22.default.join(dirPath, file);
11430
+ const filePath = import_path23.default.join(dirPath, file);
11190
11431
  if (sinceMs !== void 0) {
11191
11432
  try {
11192
- if (import_fs20.default.statSync(filePath).mtimeMs < sinceMs) continue;
11433
+ if (import_fs21.default.statSync(filePath).mtimeMs < sinceMs) continue;
11193
11434
  } catch {
11194
11435
  continue;
11195
11436
  }
@@ -11240,7 +11481,7 @@ function freshWatermark() {
11240
11481
  function loadWatermark() {
11241
11482
  let raw;
11242
11483
  try {
11243
- raw = import_fs21.default.readFileSync(WATERMARK_FILE(), "utf-8");
11484
+ raw = import_fs22.default.readFileSync(WATERMARK_FILE(), "utf-8");
11244
11485
  } catch {
11245
11486
  return { status: "fresh", wm: freshWatermark() };
11246
11487
  }
@@ -11292,28 +11533,28 @@ function loadWatermark() {
11292
11533
  function saveWatermark(wm) {
11293
11534
  if (wm.schemaVersion > WATERMARK_SCHEMA_VERSION) return;
11294
11535
  const target = WATERMARK_FILE();
11295
- const dir = import_path23.default.dirname(target);
11296
- if (!import_fs21.default.existsSync(dir)) import_fs21.default.mkdirSync(dir, { recursive: true });
11536
+ const dir = import_path24.default.dirname(target);
11537
+ if (!import_fs22.default.existsSync(dir)) import_fs22.default.mkdirSync(dir, { recursive: true });
11297
11538
  const tmp = target + ".tmp";
11298
- import_fs21.default.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
11299
- import_fs21.default.renameSync(tmp, target);
11539
+ import_fs22.default.writeFileSync(tmp, JSON.stringify(wm, null, 2) + "\n", "utf-8");
11540
+ import_fs22.default.renameSync(tmp, target);
11300
11541
  }
11301
11542
  function listJsonlFiles() {
11302
11543
  const root = PROJECTS_DIR();
11303
- if (!import_fs21.default.existsSync(root)) return [];
11544
+ if (!import_fs22.default.existsSync(root)) return [];
11304
11545
  const out = [];
11305
- for (const entry of import_fs21.default.readdirSync(root, { withFileTypes: true })) {
11546
+ for (const entry of import_fs22.default.readdirSync(root, { withFileTypes: true })) {
11306
11547
  if (!entry.isDirectory()) continue;
11307
- const projectDir = import_path23.default.join(root, entry.name);
11548
+ const projectDir = import_path24.default.join(root, entry.name);
11308
11549
  let inner;
11309
11550
  try {
11310
- inner = import_fs21.default.readdirSync(projectDir, { withFileTypes: true });
11551
+ inner = import_fs22.default.readdirSync(projectDir, { withFileTypes: true });
11311
11552
  } catch {
11312
11553
  continue;
11313
11554
  }
11314
11555
  for (const file of inner) {
11315
11556
  if (file.isFile() && file.name.endsWith(".jsonl")) {
11316
- out.push(import_path23.default.join(projectDir, file.name));
11557
+ out.push(import_path24.default.join(projectDir, file.name));
11317
11558
  }
11318
11559
  }
11319
11560
  }
@@ -11321,7 +11562,7 @@ function listJsonlFiles() {
11321
11562
  }
11322
11563
  function fileSize(p) {
11323
11564
  try {
11324
- return import_fs21.default.statSync(p).size;
11565
+ return import_fs22.default.statSync(p).size;
11325
11566
  } catch {
11326
11567
  return 0;
11327
11568
  }
@@ -11329,7 +11570,7 @@ function fileSize(p) {
11329
11570
  async function scanDelta(filePath, fromByte, onLine) {
11330
11571
  const size = fileSize(filePath);
11331
11572
  if (size <= fromByte) return fromByte;
11332
- const stream = import_fs21.default.createReadStream(filePath, {
11573
+ const stream = import_fs22.default.createReadStream(filePath, {
11333
11574
  start: fromByte,
11334
11575
  end: size - 1,
11335
11576
  highWaterMark: 64 * 1024
@@ -11441,7 +11682,7 @@ async function tickForensicBroadcast(offsets) {
11441
11682
  continue;
11442
11683
  }
11443
11684
  if (size <= offset) continue;
11444
- const sessionId = import_path23.default.basename(file, ".jsonl");
11685
+ const sessionId = import_path24.default.basename(file, ".jsonl");
11445
11686
  const newOffset = await scanDelta(file, offset, (obj, lineIndex) => {
11446
11687
  out.push(...extractFindingsFromLine(obj, sessionId, lineIndex));
11447
11688
  });
@@ -11500,7 +11741,7 @@ function emptyTick(uploadAs) {
11500
11741
  function readRawWatermarkPreservingOffsets() {
11501
11742
  let raw;
11502
11743
  try {
11503
- raw = import_fs21.default.readFileSync(WATERMARK_FILE(), "utf-8");
11744
+ raw = import_fs22.default.readFileSync(WATERMARK_FILE(), "utf-8");
11504
11745
  } catch {
11505
11746
  return null;
11506
11747
  }
@@ -11534,13 +11775,13 @@ async function runActualTick(wm) {
11534
11775
  if (!known) {
11535
11776
  let mtimeMs = 0;
11536
11777
  try {
11537
- mtimeMs = import_fs21.default.statSync(filePath).mtime.getTime();
11778
+ mtimeMs = import_fs22.default.statSync(filePath).mtime.getTime();
11538
11779
  } catch {
11539
11780
  continue;
11540
11781
  }
11541
11782
  if (mtimeMs >= watermarkCreatedAt) {
11542
11783
  filesNew++;
11543
- const sessionId2 = import_path23.default.basename(filePath, ".jsonl");
11784
+ const sessionId2 = import_path24.default.basename(filePath, ".jsonl");
11544
11785
  const newScannedTo2 = await scanDelta(filePath, 0, (obj, lineIndex) => {
11545
11786
  totalToolCalls++;
11546
11787
  toolCallsBySession[sessionId2] = (toolCallsBySession[sessionId2] ?? 0) + 1;
@@ -11558,7 +11799,7 @@ async function runActualTick(wm) {
11558
11799
  filesSkipped++;
11559
11800
  continue;
11560
11801
  }
11561
- const sessionId = import_path23.default.basename(filePath, ".jsonl");
11802
+ const sessionId = import_path24.default.basename(filePath, ".jsonl");
11562
11803
  const newScannedTo = await scanDelta(filePath, known.scannedTo, (obj, lineIndex) => {
11563
11804
  totalToolCalls++;
11564
11805
  toolCallsBySession[sessionId] = (toolCallsBySession[sessionId] ?? 0) + 1;
@@ -11580,18 +11821,18 @@ async function runActualTick(wm) {
11580
11821
  schemaFuture: false
11581
11822
  };
11582
11823
  }
11583
- var import_fs21, import_os20, import_path23, import_readline, PROJECTS_DIR, WATERMARK_FILE, MAX_LINE_BYTES, WATERMARK_SCHEMA_VERSION, LONG_OUTPUT_THRESHOLD_BYTES2;
11824
+ var import_fs22, import_os21, import_path24, import_readline, PROJECTS_DIR, WATERMARK_FILE, MAX_LINE_BYTES, WATERMARK_SCHEMA_VERSION, LONG_OUTPUT_THRESHOLD_BYTES2;
11584
11825
  var init_scan_watermark = __esm({
11585
11826
  "src/daemon/scan-watermark.ts"() {
11586
11827
  "use strict";
11587
- import_fs21 = __toESM(require("fs"));
11588
- import_os20 = __toESM(require("os"));
11589
- import_path23 = __toESM(require("path"));
11828
+ import_fs22 = __toESM(require("fs"));
11829
+ import_os21 = __toESM(require("os"));
11830
+ import_path24 = __toESM(require("path"));
11590
11831
  import_readline = __toESM(require("readline"));
11591
11832
  init_dlp();
11592
11833
  init_dist();
11593
- PROJECTS_DIR = () => import_path23.default.join(import_os20.default.homedir(), ".claude", "projects");
11594
- WATERMARK_FILE = () => import_path23.default.join(import_os20.default.homedir(), ".node9", "scan-watermark.json");
11834
+ PROJECTS_DIR = () => import_path24.default.join(import_os21.default.homedir(), ".claude", "projects");
11835
+ WATERMARK_FILE = () => import_path24.default.join(import_os21.default.homedir(), ".node9", "scan-watermark.json");
11595
11836
  MAX_LINE_BYTES = 2 * 1024 * 1024;
11596
11837
  WATERMARK_SCHEMA_VERSION = 2;
11597
11838
  LONG_OUTPUT_THRESHOLD_BYTES2 = LONG_OUTPUT_THRESHOLD_BYTES;
@@ -11640,40 +11881,40 @@ function parseSinceCutoff(raw, now = /* @__PURE__ */ new Date()) {
11640
11881
  return now.getTime() - 90 * 864e5;
11641
11882
  }
11642
11883
  function* iterateJsonlFiles(cutoffMs) {
11643
- const projectsDir = import_path24.default.join(import_os21.default.homedir(), ".claude", "projects");
11884
+ const projectsDir = import_path25.default.join(import_os22.default.homedir(), ".claude", "projects");
11644
11885
  let dirs;
11645
11886
  try {
11646
- dirs = import_fs22.default.readdirSync(projectsDir);
11887
+ dirs = import_fs23.default.readdirSync(projectsDir);
11647
11888
  } catch {
11648
11889
  return;
11649
11890
  }
11650
11891
  for (const dir of dirs) {
11651
- const dirPath = import_path24.default.join(projectsDir, dir);
11892
+ const dirPath = import_path25.default.join(projectsDir, dir);
11652
11893
  let stats;
11653
11894
  try {
11654
- stats = import_fs22.default.statSync(dirPath);
11895
+ stats = import_fs23.default.statSync(dirPath);
11655
11896
  } catch {
11656
11897
  continue;
11657
11898
  }
11658
11899
  if (!stats.isDirectory()) continue;
11659
11900
  let files;
11660
11901
  try {
11661
- files = import_fs22.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11902
+ files = import_fs23.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
11662
11903
  } catch {
11663
11904
  continue;
11664
11905
  }
11665
11906
  for (const file of files) {
11666
- const filePath = import_path24.default.join(dirPath, file);
11907
+ const filePath = import_path25.default.join(dirPath, file);
11667
11908
  let mtime = 0;
11668
11909
  try {
11669
- mtime = import_fs22.default.statSync(filePath).mtimeMs;
11910
+ mtime = import_fs23.default.statSync(filePath).mtimeMs;
11670
11911
  } catch {
11671
11912
  continue;
11672
11913
  }
11673
11914
  if (mtime < cutoffMs) continue;
11674
11915
  yield {
11675
11916
  filePath,
11676
- sessionId: import_path24.default.basename(file, ".jsonl"),
11917
+ sessionId: import_path25.default.basename(file, ".jsonl"),
11677
11918
  projectDir: dir
11678
11919
  };
11679
11920
  }
@@ -11741,7 +11982,7 @@ async function runUploadHistory(opts) {
11741
11982
  filesScanned++;
11742
11983
  let content;
11743
11984
  try {
11744
- content = import_fs22.default.readFileSync(filePath, "utf8");
11985
+ content = import_fs23.default.readFileSync(filePath, "utf8");
11745
11986
  } catch {
11746
11987
  continue;
11747
11988
  }
@@ -11815,10 +12056,10 @@ async function runUploadHistory(opts) {
11815
12056
  const costUrl = creds.apiUrl.endsWith("/policies/sync") ? creds.apiUrl.replace(/\/policies\/sync$/, "/cost-sync") : `${creds.apiUrl.replace(/\/$/, "")}/cost-sync`;
11816
12057
  let username = "unknown";
11817
12058
  try {
11818
- username = import_os21.default.userInfo().username;
12059
+ username = import_os22.default.userInfo().username;
11819
12060
  } catch {
11820
12061
  }
11821
- const machineId = `${import_os21.default.hostname()}:${username}`;
12062
+ const machineId = `${import_os22.default.hostname()}:${username}`;
11822
12063
  await postJson(costUrl, creds.apiKey, {
11823
12064
  machineId,
11824
12065
  entries: dailyEntries
@@ -11868,14 +12109,14 @@ async function postJson(url, apiKey, body) {
11868
12109
  req.end();
11869
12110
  });
11870
12111
  }
11871
- var import_fs22, import_https, import_os21, import_path24, import_chalk4, FINDING_TO_SIGNAL2;
12112
+ var import_fs23, import_https, import_os22, import_path25, import_chalk4, FINDING_TO_SIGNAL2;
11872
12113
  var init_scan_upload_history = __esm({
11873
12114
  "src/scan-upload-history.ts"() {
11874
12115
  "use strict";
11875
- import_fs22 = __toESM(require("fs"));
12116
+ import_fs23 = __toESM(require("fs"));
11876
12117
  import_https = __toESM(require("https"));
11877
- import_os21 = __toESM(require("os"));
11878
- import_path24 = __toESM(require("path"));
12118
+ import_os22 = __toESM(require("os"));
12119
+ import_path25 = __toESM(require("path"));
11879
12120
  import_chalk4 = __toESM(require("chalk"));
11880
12121
  init_dist();
11881
12122
  init_config();
@@ -12117,14 +12358,14 @@ function buildRuleSources() {
12117
12358
  }
12118
12359
  function countScanFiles() {
12119
12360
  let total = 0;
12120
- const claudeDir = import_path25.default.join(import_os22.default.homedir(), ".claude", "projects");
12121
- if (import_fs23.default.existsSync(claudeDir)) {
12361
+ const claudeDir = import_path26.default.join(import_os23.default.homedir(), ".claude", "projects");
12362
+ if (import_fs24.default.existsSync(claudeDir)) {
12122
12363
  try {
12123
- for (const proj of import_fs23.default.readdirSync(claudeDir)) {
12124
- const p = import_path25.default.join(claudeDir, proj);
12364
+ for (const proj of import_fs24.default.readdirSync(claudeDir)) {
12365
+ const p = import_path26.default.join(claudeDir, proj);
12125
12366
  try {
12126
- if (!import_fs23.default.statSync(p).isDirectory()) continue;
12127
- total += import_fs23.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
12367
+ if (!import_fs24.default.statSync(p).isDirectory()) continue;
12368
+ total += import_fs24.default.readdirSync(p).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-")).length;
12128
12369
  } catch {
12129
12370
  continue;
12130
12371
  }
@@ -12132,17 +12373,17 @@ function countScanFiles() {
12132
12373
  } catch {
12133
12374
  }
12134
12375
  }
12135
- const geminiDir = import_path25.default.join(import_os22.default.homedir(), ".gemini", "tmp");
12136
- if (import_fs23.default.existsSync(geminiDir)) {
12376
+ const geminiDir = import_path26.default.join(import_os23.default.homedir(), ".gemini", "tmp");
12377
+ if (import_fs24.default.existsSync(geminiDir)) {
12137
12378
  try {
12138
- for (const slug2 of import_fs23.default.readdirSync(geminiDir)) {
12139
- const p = import_path25.default.join(geminiDir, slug2);
12379
+ for (const slug2 of import_fs24.default.readdirSync(geminiDir)) {
12380
+ const p = import_path26.default.join(geminiDir, slug2);
12140
12381
  try {
12141
- if (!import_fs23.default.statSync(p).isDirectory()) continue;
12142
- const chatsDir = import_path25.default.join(p, "chats");
12143
- if (import_fs23.default.existsSync(chatsDir)) {
12382
+ if (!import_fs24.default.statSync(p).isDirectory()) continue;
12383
+ const chatsDir = import_path26.default.join(p, "chats");
12384
+ if (import_fs24.default.existsSync(chatsDir)) {
12144
12385
  try {
12145
- total += import_fs23.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
12386
+ total += import_fs24.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json")).length;
12146
12387
  } catch {
12147
12388
  }
12148
12389
  }
@@ -12154,15 +12395,15 @@ function countScanFiles() {
12154
12395
  }
12155
12396
  }
12156
12397
  for (const surface of ["antigravity-cli", "antigravity-ide"]) {
12157
- const brainDir = import_path25.default.join(import_os22.default.homedir(), ".gemini", surface, "brain");
12158
- if (!import_fs23.default.existsSync(brainDir)) continue;
12398
+ const brainDir = import_path26.default.join(import_os23.default.homedir(), ".gemini", surface, "brain");
12399
+ if (!import_fs24.default.existsSync(brainDir)) continue;
12159
12400
  try {
12160
- for (const conv of import_fs23.default.readdirSync(brainDir)) {
12161
- const convPath = import_path25.default.join(brainDir, conv);
12401
+ for (const conv of import_fs24.default.readdirSync(brainDir)) {
12402
+ const convPath = import_path26.default.join(brainDir, conv);
12162
12403
  try {
12163
- if (!import_fs23.default.statSync(convPath).isDirectory()) continue;
12164
- const logsDir = import_path25.default.join(convPath, ".system_generated", "logs");
12165
- if (import_fs23.default.existsSync(import_path25.default.join(logsDir, "transcript_full.jsonl")) || import_fs23.default.existsSync(import_path25.default.join(logsDir, "transcript.jsonl"))) {
12404
+ if (!import_fs24.default.statSync(convPath).isDirectory()) continue;
12405
+ const logsDir = import_path26.default.join(convPath, ".system_generated", "logs");
12406
+ if (import_fs24.default.existsSync(import_path26.default.join(logsDir, "transcript_full.jsonl")) || import_fs24.default.existsSync(import_path26.default.join(logsDir, "transcript.jsonl"))) {
12166
12407
  total += 1;
12167
12408
  }
12168
12409
  } catch {
@@ -12172,31 +12413,31 @@ function countScanFiles() {
12172
12413
  } catch {
12173
12414
  }
12174
12415
  }
12175
- const copilotDir = import_path25.default.join(import_os22.default.homedir(), ".copilot", "session-state");
12176
- if (import_fs23.default.existsSync(copilotDir)) {
12416
+ const copilotDir = import_path26.default.join(import_os23.default.homedir(), ".copilot", "session-state");
12417
+ if (import_fs24.default.existsSync(copilotDir)) {
12177
12418
  try {
12178
- for (const sid of import_fs23.default.readdirSync(copilotDir)) {
12179
- if (import_fs23.default.existsSync(import_path25.default.join(copilotDir, sid, "events.jsonl"))) total += 1;
12419
+ for (const sid of import_fs24.default.readdirSync(copilotDir)) {
12420
+ if (import_fs24.default.existsSync(import_path26.default.join(copilotDir, sid, "events.jsonl"))) total += 1;
12180
12421
  }
12181
12422
  } catch {
12182
12423
  }
12183
12424
  }
12184
- const codexDir = import_path25.default.join(import_os22.default.homedir(), ".codex", "sessions");
12185
- if (import_fs23.default.existsSync(codexDir)) {
12425
+ const codexDir = import_path26.default.join(import_os23.default.homedir(), ".codex", "sessions");
12426
+ if (import_fs24.default.existsSync(codexDir)) {
12186
12427
  try {
12187
- for (const year of import_fs23.default.readdirSync(codexDir)) {
12188
- const yp = import_path25.default.join(codexDir, year);
12428
+ for (const year of import_fs24.default.readdirSync(codexDir)) {
12429
+ const yp = import_path26.default.join(codexDir, year);
12189
12430
  try {
12190
- if (!import_fs23.default.statSync(yp).isDirectory()) continue;
12191
- for (const month of import_fs23.default.readdirSync(yp)) {
12192
- const mp = import_path25.default.join(yp, month);
12431
+ if (!import_fs24.default.statSync(yp).isDirectory()) continue;
12432
+ for (const month of import_fs24.default.readdirSync(yp)) {
12433
+ const mp = import_path26.default.join(yp, month);
12193
12434
  try {
12194
- if (!import_fs23.default.statSync(mp).isDirectory()) continue;
12195
- for (const day of import_fs23.default.readdirSync(mp)) {
12196
- const dp = import_path25.default.join(mp, day);
12435
+ if (!import_fs24.default.statSync(mp).isDirectory()) continue;
12436
+ for (const day of import_fs24.default.readdirSync(mp)) {
12437
+ const dp = import_path26.default.join(mp, day);
12197
12438
  try {
12198
- if (!import_fs23.default.statSync(dp).isDirectory()) continue;
12199
- total += import_fs23.default.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
12439
+ if (!import_fs24.default.statSync(dp).isDirectory()) continue;
12440
+ total += import_fs24.default.readdirSync(dp).filter((f) => f.endsWith(".jsonl")).length;
12200
12441
  } catch {
12201
12442
  continue;
12202
12443
  }
@@ -12232,7 +12473,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12232
12473
  const sessionId = file.replace(/\.jsonl$/, "");
12233
12474
  let raw;
12234
12475
  try {
12235
- raw = import_fs23.default.readFileSync(import_path25.default.join(projPath, file), "utf-8");
12476
+ raw = import_fs24.default.readFileSync(import_path26.default.join(projPath, file), "utf-8");
12236
12477
  } catch {
12237
12478
  return;
12238
12479
  }
@@ -12284,7 +12525,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12284
12525
  if (block.type !== "tool_result") continue;
12285
12526
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
12286
12527
  if (filePath) {
12287
- const ext = import_path25.default.extname(filePath).toLowerCase();
12528
+ const ext = import_path26.default.extname(filePath).toLowerCase();
12288
12529
  if (CODE_EXTENSIONS.has(ext)) continue;
12289
12530
  }
12290
12531
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -12341,7 +12582,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12341
12582
  const rawCmd = String(input.command ?? "").trimStart();
12342
12583
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
12343
12584
  const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
12344
- const inputFileExt = inputFilePath ? import_path25.default.extname(inputFilePath).toLowerCase() : "";
12585
+ const inputFileExt = inputFilePath ? import_path26.default.extname(inputFilePath).toLowerCase() : "";
12345
12586
  if (CODE_EXTENSIONS.has(inputFileExt)) continue;
12346
12587
  const dlpMatch = scanArgs(input);
12347
12588
  if (dlpMatch) {
@@ -12438,19 +12679,19 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
12438
12679
  }
12439
12680
  }
12440
12681
  function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
12441
- const projPath = import_path25.default.join(projectsDir, proj);
12682
+ const projPath = import_path26.default.join(projectsDir, proj);
12442
12683
  try {
12443
- if (!import_fs23.default.statSync(projPath).isDirectory()) return;
12684
+ if (!import_fs24.default.statSync(projPath).isDirectory()) return;
12444
12685
  } catch {
12445
12686
  return;
12446
12687
  }
12447
- const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(import_os22.default.homedir(), "~")).slice(
12688
+ const projLabel = stripTerminalEscapes(decodeURIComponent(proj).replace(import_os23.default.homedir(), "~")).slice(
12448
12689
  0,
12449
12690
  40
12450
12691
  );
12451
12692
  let files;
12452
12693
  try {
12453
- files = import_fs23.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12694
+ files = import_fs24.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
12454
12695
  } catch {
12455
12696
  return;
12456
12697
  }
@@ -12484,12 +12725,12 @@ function emptyClaudeScan() {
12484
12725
  };
12485
12726
  }
12486
12727
  function scanClaudeHistory(startDate, onProgress, onLine) {
12487
- const projectsDir = import_path25.default.join(import_os22.default.homedir(), ".claude", "projects");
12728
+ const projectsDir = import_path26.default.join(import_os23.default.homedir(), ".claude", "projects");
12488
12729
  const result = emptyClaudeScan();
12489
- if (!import_fs23.default.existsSync(projectsDir)) return result;
12730
+ if (!import_fs24.default.existsSync(projectsDir)) return result;
12490
12731
  let projDirs;
12491
12732
  try {
12492
- projDirs = import_fs23.default.readdirSync(projectsDir);
12733
+ projDirs = import_fs24.default.readdirSync(projectsDir);
12493
12734
  } catch {
12494
12735
  return result;
12495
12736
  }
@@ -12510,7 +12751,7 @@ function scanClaudeHistory(startDate, onProgress, onLine) {
12510
12751
  return result;
12511
12752
  }
12512
12753
  function scanGeminiHistory(startDate, onProgress, onLine) {
12513
- const tmpDir = import_path25.default.join(import_os22.default.homedir(), ".gemini", "tmp");
12754
+ const tmpDir = import_path26.default.join(import_os23.default.homedir(), ".gemini", "tmp");
12514
12755
  const result = {
12515
12756
  filesScanned: 0,
12516
12757
  sessions: 0,
@@ -12525,33 +12766,33 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12525
12766
  sessionsWithEarlySecrets: 0
12526
12767
  };
12527
12768
  const dedup = emptyScanDedup();
12528
- if (!import_fs23.default.existsSync(tmpDir)) return result;
12769
+ if (!import_fs24.default.existsSync(tmpDir)) return result;
12529
12770
  let slugDirs;
12530
12771
  try {
12531
- slugDirs = import_fs23.default.readdirSync(tmpDir);
12772
+ slugDirs = import_fs24.default.readdirSync(tmpDir);
12532
12773
  } catch {
12533
12774
  return result;
12534
12775
  }
12535
12776
  const ruleSources = buildRuleSources();
12536
12777
  for (const slug2 of slugDirs) {
12537
- const slugPath = import_path25.default.join(tmpDir, slug2);
12778
+ const slugPath = import_path26.default.join(tmpDir, slug2);
12538
12779
  try {
12539
- if (!import_fs23.default.statSync(slugPath).isDirectory()) continue;
12780
+ if (!import_fs24.default.statSync(slugPath).isDirectory()) continue;
12540
12781
  } catch {
12541
12782
  continue;
12542
12783
  }
12543
12784
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
12544
12785
  try {
12545
12786
  projLabel = stripTerminalEscapes(
12546
- import_fs23.default.readFileSync(import_path25.default.join(slugPath, ".project_root"), "utf-8").trim()
12547
- ).replace(import_os22.default.homedir(), "~").slice(0, 40);
12787
+ import_fs24.default.readFileSync(import_path26.default.join(slugPath, ".project_root"), "utf-8").trim()
12788
+ ).replace(import_os23.default.homedir(), "~").slice(0, 40);
12548
12789
  } catch {
12549
12790
  }
12550
- const chatsDir = import_path25.default.join(slugPath, "chats");
12551
- if (!import_fs23.default.existsSync(chatsDir)) continue;
12791
+ const chatsDir = import_path26.default.join(slugPath, "chats");
12792
+ if (!import_fs24.default.existsSync(chatsDir)) continue;
12552
12793
  let chatFiles;
12553
12794
  try {
12554
- chatFiles = import_fs23.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
12795
+ chatFiles = import_fs24.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
12555
12796
  } catch {
12556
12797
  continue;
12557
12798
  }
@@ -12564,7 +12805,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12564
12805
  onProgress?.(result.filesScanned);
12565
12806
  let raw;
12566
12807
  try {
12567
- raw = import_fs23.default.readFileSync(import_path25.default.join(chatsDir, chatFile), "utf-8");
12808
+ raw = import_fs24.default.readFileSync(import_path26.default.join(chatsDir, chatFile), "utf-8");
12568
12809
  } catch {
12569
12810
  continue;
12570
12811
  }
@@ -12737,13 +12978,13 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
12737
12978
  return result;
12738
12979
  }
12739
12980
  function antigravityBrainDirs() {
12740
- return ["antigravity-cli", "antigravity-ide"].map((surface) => import_path25.default.join(import_os22.default.homedir(), ".gemini", surface, "brain")).filter((p) => import_fs23.default.existsSync(p));
12981
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => import_path26.default.join(import_os23.default.homedir(), ".gemini", surface, "brain")).filter((p) => import_fs24.default.existsSync(p));
12741
12982
  }
12742
12983
  function antigravityTranscriptPath(convPath) {
12743
- const logsDir = import_path25.default.join(convPath, ".system_generated", "logs");
12984
+ const logsDir = import_path26.default.join(convPath, ".system_generated", "logs");
12744
12985
  for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
12745
- const p = import_path25.default.join(logsDir, name);
12746
- if (import_fs23.default.existsSync(p)) return p;
12986
+ const p = import_path26.default.join(logsDir, name);
12987
+ if (import_fs24.default.existsSync(p)) return p;
12747
12988
  }
12748
12989
  return null;
12749
12990
  }
@@ -12769,14 +13010,14 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12769
13010
  for (const brainDir of brainDirs) {
12770
13011
  let convDirs;
12771
13012
  try {
12772
- convDirs = import_fs23.default.readdirSync(brainDir);
13013
+ convDirs = import_fs24.default.readdirSync(brainDir);
12773
13014
  } catch {
12774
13015
  continue;
12775
13016
  }
12776
13017
  for (const conv of convDirs) {
12777
- const convPath = import_path25.default.join(brainDir, conv);
13018
+ const convPath = import_path26.default.join(brainDir, conv);
12778
13019
  try {
12779
- if (!import_fs23.default.statSync(convPath).isDirectory()) continue;
13020
+ if (!import_fs24.default.statSync(convPath).isDirectory()) continue;
12780
13021
  } catch {
12781
13022
  continue;
12782
13023
  }
@@ -12786,7 +13027,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12786
13027
  onProgress?.(result.filesScanned);
12787
13028
  let raw;
12788
13029
  try {
12789
- raw = import_fs23.default.readFileSync(transcriptFile, "utf-8");
13030
+ raw = import_fs24.default.readFileSync(transcriptFile, "utf-8");
12790
13031
  } catch {
12791
13032
  continue;
12792
13033
  }
@@ -12843,7 +13084,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12843
13084
  result.bashCalls++;
12844
13085
  const cwd = String(input.cwd ?? "");
12845
13086
  if (cwd && projLabel === conv.slice(0, 8)) {
12846
- projLabel = stripTerminalEscapes(cwd).replace(import_os22.default.homedir(), "~").slice(0, 40);
13087
+ projLabel = stripTerminalEscapes(cwd).replace(import_os23.default.homedir(), "~").slice(0, 40);
12847
13088
  }
12848
13089
  }
12849
13090
  const rawCmd = String(input.command ?? "").trimStart();
@@ -12943,7 +13184,7 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
12943
13184
  return result;
12944
13185
  }
12945
13186
  function scanCopilotHistory(startDate, onProgress, onLine) {
12946
- const sessionDir = import_path25.default.join(import_os22.default.homedir(), ".copilot", "session-state");
13187
+ const sessionDir = import_path26.default.join(import_os23.default.homedir(), ".copilot", "session-state");
12947
13188
  const result = {
12948
13189
  filesScanned: 0,
12949
13190
  sessions: 0,
@@ -12959,22 +13200,22 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
12959
13200
  sessionsWithEarlySecrets: 0
12960
13201
  };
12961
13202
  const dedup = emptyScanDedup();
12962
- if (!import_fs23.default.existsSync(sessionDir)) return result;
13203
+ if (!import_fs24.default.existsSync(sessionDir)) return result;
12963
13204
  let sessionIds;
12964
13205
  try {
12965
- sessionIds = import_fs23.default.readdirSync(sessionDir);
13206
+ sessionIds = import_fs24.default.readdirSync(sessionDir);
12966
13207
  } catch {
12967
13208
  return result;
12968
13209
  }
12969
13210
  const ruleSources = buildRuleSources();
12970
13211
  for (const sessionId of sessionIds) {
12971
- const eventsPath = import_path25.default.join(sessionDir, sessionId, "events.jsonl");
12972
- if (!import_fs23.default.existsSync(eventsPath)) continue;
13212
+ const eventsPath = import_path26.default.join(sessionDir, sessionId, "events.jsonl");
13213
+ if (!import_fs24.default.existsSync(eventsPath)) continue;
12973
13214
  result.filesScanned++;
12974
13215
  onProgress?.(result.filesScanned);
12975
13216
  let raw;
12976
13217
  try {
12977
- raw = import_fs23.default.readFileSync(eventsPath, "utf-8");
13218
+ raw = import_fs24.default.readFileSync(eventsPath, "utf-8");
12978
13219
  } catch {
12979
13220
  continue;
12980
13221
  }
@@ -12994,7 +13235,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
12994
13235
  if (ev.type === "session.start") {
12995
13236
  const cwd = ev.data?.context?.cwd;
12996
13237
  if (typeof cwd === "string" && cwd) {
12997
- projLabel = stripTerminalEscapes(cwd).replace(import_os22.default.homedir(), "~").slice(0, 40);
13238
+ projLabel = stripTerminalEscapes(cwd).replace(import_os23.default.homedir(), "~").slice(0, 40);
12998
13239
  }
12999
13240
  continue;
13000
13241
  }
@@ -13126,7 +13367,7 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
13126
13367
  return result;
13127
13368
  }
13128
13369
  function scanCodexHistory(startDate, onProgress, onLine) {
13129
- const sessionsBase = import_path25.default.join(import_os22.default.homedir(), ".codex", "sessions");
13370
+ const sessionsBase = import_path26.default.join(import_os23.default.homedir(), ".codex", "sessions");
13130
13371
  const result = {
13131
13372
  filesScanned: 0,
13132
13373
  sessions: 0,
@@ -13141,32 +13382,32 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13141
13382
  sessionsWithEarlySecrets: 0
13142
13383
  };
13143
13384
  const dedup = emptyScanDedup();
13144
- if (!import_fs23.default.existsSync(sessionsBase)) return result;
13385
+ if (!import_fs24.default.existsSync(sessionsBase)) return result;
13145
13386
  const jsonlFiles = [];
13146
13387
  try {
13147
- for (const year of import_fs23.default.readdirSync(sessionsBase)) {
13148
- const yearPath = import_path25.default.join(sessionsBase, year);
13388
+ for (const year of import_fs24.default.readdirSync(sessionsBase)) {
13389
+ const yearPath = import_path26.default.join(sessionsBase, year);
13149
13390
  try {
13150
- if (!import_fs23.default.statSync(yearPath).isDirectory()) continue;
13391
+ if (!import_fs24.default.statSync(yearPath).isDirectory()) continue;
13151
13392
  } catch {
13152
13393
  continue;
13153
13394
  }
13154
- for (const month of import_fs23.default.readdirSync(yearPath)) {
13155
- const monthPath = import_path25.default.join(yearPath, month);
13395
+ for (const month of import_fs24.default.readdirSync(yearPath)) {
13396
+ const monthPath = import_path26.default.join(yearPath, month);
13156
13397
  try {
13157
- if (!import_fs23.default.statSync(monthPath).isDirectory()) continue;
13398
+ if (!import_fs24.default.statSync(monthPath).isDirectory()) continue;
13158
13399
  } catch {
13159
13400
  continue;
13160
13401
  }
13161
- for (const day of import_fs23.default.readdirSync(monthPath)) {
13162
- const dayPath = import_path25.default.join(monthPath, day);
13402
+ for (const day of import_fs24.default.readdirSync(monthPath)) {
13403
+ const dayPath = import_path26.default.join(monthPath, day);
13163
13404
  try {
13164
- if (!import_fs23.default.statSync(dayPath).isDirectory()) continue;
13405
+ if (!import_fs24.default.statSync(dayPath).isDirectory()) continue;
13165
13406
  } catch {
13166
13407
  continue;
13167
13408
  }
13168
- for (const file of import_fs23.default.readdirSync(dayPath)) {
13169
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path25.default.join(dayPath, file));
13409
+ for (const file of import_fs24.default.readdirSync(dayPath)) {
13410
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path26.default.join(dayPath, file));
13170
13411
  }
13171
13412
  }
13172
13413
  }
@@ -13180,7 +13421,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13180
13421
  onProgress?.(result.filesScanned);
13181
13422
  let lines;
13182
13423
  try {
13183
- lines = import_fs23.default.readFileSync(filePath, "utf-8").split("\n");
13424
+ lines = import_fs24.default.readFileSync(filePath, "utf-8").split("\n");
13184
13425
  } catch {
13185
13426
  continue;
13186
13427
  }
@@ -13207,7 +13448,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13207
13448
  sessionId = String(payload["id"] ?? filePath);
13208
13449
  startTime = String(payload["timestamp"] ?? "");
13209
13450
  const cwd = String(payload["cwd"] ?? "");
13210
- projLabel = stripTerminalEscapes(cwd.replace(import_os22.default.homedir(), "~")).slice(0, 40);
13451
+ projLabel = stripTerminalEscapes(cwd.replace(import_os23.default.homedir(), "~")).slice(0, 40);
13211
13452
  continue;
13212
13453
  }
13213
13454
  if (entry.type === "turn_context" && typeof payload["model"] === "string") {
@@ -13367,17 +13608,17 @@ function scanCodexHistory(startDate, onProgress, onLine) {
13367
13608
  return result;
13368
13609
  }
13369
13610
  function scanShellConfig() {
13370
- const home = import_os22.default.homedir();
13611
+ const home = import_os23.default.homedir();
13371
13612
  const configFiles = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map(
13372
- (f) => import_path25.default.join(home, f)
13613
+ (f) => import_path26.default.join(home, f)
13373
13614
  );
13374
13615
  const findings = [];
13375
13616
  const seen = /* @__PURE__ */ new Set();
13376
13617
  for (const filePath of configFiles) {
13377
- if (!import_fs23.default.existsSync(filePath)) continue;
13618
+ if (!import_fs24.default.existsSync(filePath)) continue;
13378
13619
  let lines;
13379
13620
  try {
13380
- lines = import_fs23.default.readFileSync(filePath, "utf-8").split("\n");
13621
+ lines = import_fs24.default.readFileSync(filePath, "utf-8").split("\n");
13381
13622
  } catch {
13382
13623
  continue;
13383
13624
  }
@@ -14183,7 +14424,7 @@ function registerScanCommand(program2) {
14183
14424
  if (!drillDown) {
14184
14425
  const useInk2 = !options.classic;
14185
14426
  if (useInk2) {
14186
- const scanInkPath = import_path25.default.join(__dirname, "scan-ink.mjs");
14427
+ const scanInkPath = import_path26.default.join(__dirname, "scan-ink.mjs");
14187
14428
  const dynamicImport = new Function("id", "return import(id)");
14188
14429
  const mod = await dynamicImport(`file://${scanInkPath}`);
14189
14430
  const rangeLabel2 = options.all ? "all time" : `last ${options.days ?? 90} days`;
@@ -14415,14 +14656,14 @@ function registerScanCommand(program2) {
14415
14656
  }
14416
14657
  );
14417
14658
  }
14418
- var import_chalk5, import_fs23, import_path25, import_os22, import_string_width2, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, FIXTURE_TOKEN_PATTERNS, TERMINAL_ESCAPE_RE2, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
14659
+ var import_chalk5, import_fs24, import_path26, import_os23, import_string_width2, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, FIXTURE_TOKEN_PATTERNS, TERMINAL_ESCAPE_RE2, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
14419
14660
  var init_scan = __esm({
14420
14661
  "src/cli/commands/scan.ts"() {
14421
14662
  "use strict";
14422
14663
  import_chalk5 = __toESM(require("chalk"));
14423
- import_fs23 = __toESM(require("fs"));
14424
- import_path25 = __toESM(require("path"));
14425
- import_os22 = __toESM(require("os"));
14664
+ import_fs24 = __toESM(require("fs"));
14665
+ import_path26 = __toESM(require("path"));
14666
+ import_os23 = __toESM(require("os"));
14426
14667
  init_shields();
14427
14668
  init_config();
14428
14669
  init_policy();
@@ -14599,12 +14840,12 @@ var init_suggestion_tracker = __esm({
14599
14840
  });
14600
14841
 
14601
14842
  // src/daemon/taint-store.ts
14602
- var import_fs24, import_path26, DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
14843
+ var import_fs25, import_path27, DEFAULT_TTL_MS, TaintStore, SESSION_TAINT_TTL_MS, SessionTaintStore;
14603
14844
  var init_taint_store = __esm({
14604
14845
  "src/daemon/taint-store.ts"() {
14605
14846
  "use strict";
14606
- import_fs24 = __toESM(require("fs"));
14607
- import_path26 = __toESM(require("path"));
14847
+ import_fs25 = __toESM(require("fs"));
14848
+ import_path27 = __toESM(require("path"));
14608
14849
  DEFAULT_TTL_MS = 60 * 60 * 1e3;
14609
14850
  TaintStore = class {
14610
14851
  records = /* @__PURE__ */ new Map();
@@ -14670,9 +14911,9 @@ var init_taint_store = __esm({
14670
14911
  /** Resolve to absolute path, falling back to path.resolve if file doesn't exist yet. */
14671
14912
  _resolve(filePath) {
14672
14913
  try {
14673
- return import_fs24.default.realpathSync.native(import_path26.default.resolve(filePath));
14914
+ return import_fs25.default.realpathSync.native(import_path27.default.resolve(filePath));
14674
14915
  } catch {
14675
- return import_path26.default.resolve(filePath);
14916
+ return import_path27.default.resolve(filePath);
14676
14917
  }
14677
14918
  }
14678
14919
  };
@@ -14838,8 +15079,8 @@ var init_session_history = __esm({
14838
15079
  // src/daemon/state.ts
14839
15080
  function loadInsightCounts() {
14840
15081
  try {
14841
- if (!import_fs25.default.existsSync(INSIGHT_COUNTS_FILE)) return;
14842
- const data = JSON.parse(import_fs25.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
15082
+ if (!import_fs26.default.existsSync(INSIGHT_COUNTS_FILE)) return;
15083
+ const data = JSON.parse(import_fs26.default.readFileSync(INSIGHT_COUNTS_FILE, "utf-8"));
14843
15084
  for (const [tool, count] of Object.entries(data)) {
14844
15085
  if (typeof count === "number" && count > 0) insightCounts.set(tool, count);
14845
15086
  }
@@ -14878,23 +15119,23 @@ function markRejectionHandlerRegistered() {
14878
15119
  daemonRejectionHandlerRegistered = true;
14879
15120
  }
14880
15121
  function atomicWriteSync2(filePath, data, options) {
14881
- const dir = import_path27.default.dirname(filePath);
14882
- if (!import_fs25.default.existsSync(dir)) import_fs25.default.mkdirSync(dir, { recursive: true });
15122
+ const dir = import_path28.default.dirname(filePath);
15123
+ if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
14883
15124
  const tmpPath = `${filePath}.${(0, import_crypto8.randomUUID)()}.tmp`;
14884
15125
  try {
14885
- import_fs25.default.writeFileSync(tmpPath, data, options);
15126
+ import_fs26.default.writeFileSync(tmpPath, data, options);
14886
15127
  } catch (err2) {
14887
15128
  try {
14888
- import_fs25.default.unlinkSync(tmpPath);
15129
+ import_fs26.default.unlinkSync(tmpPath);
14889
15130
  } catch {
14890
15131
  }
14891
15132
  throw err2;
14892
15133
  }
14893
15134
  try {
14894
- import_fs25.default.renameSync(tmpPath, filePath);
15135
+ import_fs26.default.renameSync(tmpPath, filePath);
14895
15136
  } catch (err2) {
14896
15137
  try {
14897
- import_fs25.default.unlinkSync(tmpPath);
15138
+ import_fs26.default.unlinkSync(tmpPath);
14898
15139
  } catch {
14899
15140
  }
14900
15141
  throw err2;
@@ -14918,16 +15159,16 @@ function appendAuditLog(data) {
14918
15159
  decision: data.decision,
14919
15160
  source: "daemon"
14920
15161
  };
14921
- const dir = import_path27.default.dirname(AUDIT_LOG_FILE);
14922
- if (!import_fs25.default.existsSync(dir)) import_fs25.default.mkdirSync(dir, { recursive: true });
14923
- import_fs25.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
15162
+ const dir = import_path28.default.dirname(AUDIT_LOG_FILE);
15163
+ if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
15164
+ import_fs26.default.appendFileSync(AUDIT_LOG_FILE, JSON.stringify(entry) + "\n");
14924
15165
  } catch {
14925
15166
  }
14926
15167
  }
14927
15168
  function getAuditHistory(limit = 20) {
14928
15169
  try {
14929
- if (!import_fs25.default.existsSync(AUDIT_LOG_FILE)) return [];
14930
- const lines = import_fs25.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
15170
+ if (!import_fs26.default.existsSync(AUDIT_LOG_FILE)) return [];
15171
+ const lines = import_fs26.default.readFileSync(AUDIT_LOG_FILE, "utf-8").trim().split("\n");
14931
15172
  if (lines.length === 1 && lines[0] === "") return [];
14932
15173
  return lines.slice(-limit).map((l) => JSON.parse(l)).reverse();
14933
15174
  } catch {
@@ -14936,7 +15177,7 @@ function getAuditHistory(limit = 20) {
14936
15177
  }
14937
15178
  function getOrgName() {
14938
15179
  try {
14939
- if (import_fs25.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
15180
+ if (import_fs26.default.existsSync(CREDENTIALS_FILE)) return "Node9 Cloud";
14940
15181
  } catch {
14941
15182
  }
14942
15183
  return null;
@@ -14944,8 +15185,8 @@ function getOrgName() {
14944
15185
  function writeGlobalSetting(key, value) {
14945
15186
  let config = {};
14946
15187
  try {
14947
- if (import_fs25.default.existsSync(GLOBAL_CONFIG_FILE)) {
14948
- config = JSON.parse(import_fs25.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
15188
+ if (import_fs26.default.existsSync(GLOBAL_CONFIG_FILE)) {
15189
+ config = JSON.parse(import_fs26.default.readFileSync(GLOBAL_CONFIG_FILE, "utf-8"));
14949
15190
  }
14950
15191
  } catch {
14951
15192
  }
@@ -14957,8 +15198,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
14957
15198
  try {
14958
15199
  let trust = { entries: [] };
14959
15200
  try {
14960
- if (import_fs25.default.existsSync(TRUST_FILE2))
14961
- trust = JSON.parse(import_fs25.default.readFileSync(TRUST_FILE2, "utf-8"));
15201
+ if (import_fs26.default.existsSync(TRUST_FILE2))
15202
+ trust = JSON.parse(import_fs26.default.readFileSync(TRUST_FILE2, "utf-8"));
14962
15203
  } catch {
14963
15204
  }
14964
15205
  trust.entries = trust.entries.filter(
@@ -14975,8 +15216,8 @@ function writeTrustEntry(toolName, durationMs, commandPattern) {
14975
15216
  }
14976
15217
  function readPersistentDecisions() {
14977
15218
  try {
14978
- if (import_fs25.default.existsSync(DECISIONS_FILE)) {
14979
- return JSON.parse(import_fs25.default.readFileSync(DECISIONS_FILE, "utf-8"));
15219
+ if (import_fs26.default.existsSync(DECISIONS_FILE)) {
15220
+ return JSON.parse(import_fs26.default.readFileSync(DECISIONS_FILE, "utf-8"));
14980
15221
  }
14981
15222
  } catch {
14982
15223
  }
@@ -15004,7 +15245,7 @@ function estimateToolCost(tool, args) {
15004
15245
  const filePath = a.file_path ?? a.path;
15005
15246
  if (filePath) {
15006
15247
  try {
15007
- const bytes = import_fs25.default.statSync(filePath).size;
15248
+ const bytes = import_fs26.default.statSync(filePath).size;
15008
15249
  return bytes / BYTES_PER_TOKEN / 1e6 * INPUT_PRICE_PER_1M;
15009
15250
  } catch {
15010
15251
  }
@@ -15075,7 +15316,7 @@ function abandonPending() {
15075
15316
  });
15076
15317
  if (autoStarted) {
15077
15318
  try {
15078
- import_fs25.default.unlinkSync(DAEMON_PID_FILE);
15319
+ import_fs26.default.unlinkSync(DAEMON_PID_FILE);
15079
15320
  } catch {
15080
15321
  }
15081
15322
  setTimeout(() => {
@@ -15086,8 +15327,8 @@ function abandonPending() {
15086
15327
  }
15087
15328
  function logActivitySocket(msg) {
15088
15329
  try {
15089
- import_fs25.default.appendFileSync(
15090
- import_path27.default.join(homeDir, ".node9", "hook-debug.log"),
15330
+ import_fs26.default.appendFileSync(
15331
+ import_path28.default.join(homeDir, ".node9", "hook-debug.log"),
15091
15332
  `[${(/* @__PURE__ */ new Date()).toISOString()}] [activity-socket] ${msg}
15092
15333
  `
15093
15334
  );
@@ -15109,13 +15350,13 @@ function shouldRebind(now = Date.now()) {
15109
15350
  function startActivitySocket() {
15110
15351
  bindActivitySocket();
15111
15352
  activityHealthInterval = setInterval(() => {
15112
- if (!import_fs25.default.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
15353
+ if (!import_fs26.default.existsSync(ACTIVITY_SOCKET_PATH2)) attemptRebind("health-probe");
15113
15354
  }, ACTIVITY_HEALTH_PROBE_MS);
15114
15355
  activityHealthInterval.unref();
15115
15356
  process.on("exit", () => {
15116
15357
  if (activityHealthInterval) clearInterval(activityHealthInterval);
15117
15358
  try {
15118
- import_fs25.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
15359
+ import_fs26.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
15119
15360
  } catch {
15120
15361
  }
15121
15362
  });
@@ -15143,7 +15384,7 @@ function attemptRebind(reason) {
15143
15384
  }
15144
15385
  function bindActivitySocket() {
15145
15386
  try {
15146
- import_fs25.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
15387
+ import_fs26.default.unlinkSync(ACTIVITY_SOCKET_PATH2);
15147
15388
  } catch {
15148
15389
  }
15149
15390
  const ACTIVITY_MAX_BYTES = 1024 * 1024;
@@ -15231,317 +15472,96 @@ function bindActivitySocket() {
15231
15472
  agent: data.agent,
15232
15473
  mcpServer: data.mcpServer,
15233
15474
  sessionId: data.sessionId
15234
- });
15235
- }
15236
- } catch {
15237
- }
15238
- });
15239
- socket.on("error", () => {
15240
- });
15241
- });
15242
- unixServer.on("error", (err2) => {
15243
- logActivitySocket(`server error: ${err2.message}`);
15244
- });
15245
- unixServer.listen(ACTIVITY_SOCKET_PATH2, () => {
15246
- logActivitySocket(`bound to ${ACTIVITY_SOCKET_PATH2}`);
15247
- });
15248
- activitySocketServer = unixServer;
15249
- }
15250
- var import_net2, import_fs25, import_path27, import_os23, import_crypto8, 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;
15251
- var init_state2 = __esm({
15252
- "src/daemon/state.ts"() {
15253
- "use strict";
15254
- import_net2 = __toESM(require("net"));
15255
- import_fs25 = __toESM(require("fs"));
15256
- import_path27 = __toESM(require("path"));
15257
- import_os23 = __toESM(require("os"));
15258
- import_crypto8 = require("crypto");
15259
- init_daemon();
15260
- init_suggestion_tracker();
15261
- init_taint_store();
15262
- init_session_counters();
15263
- init_session_history();
15264
- homeDir = import_os23.default.homedir();
15265
- DAEMON_PID_FILE = import_path27.default.join(homeDir, ".node9", "daemon.pid");
15266
- DECISIONS_FILE = import_path27.default.join(homeDir, ".node9", "decisions.json");
15267
- AUDIT_LOG_FILE = import_path27.default.join(homeDir, ".node9", "audit.log");
15268
- TRUST_FILE2 = import_path27.default.join(homeDir, ".node9", "trust.json");
15269
- GLOBAL_CONFIG_FILE = import_path27.default.join(homeDir, ".node9", "config.json");
15270
- CREDENTIALS_FILE = import_path27.default.join(homeDir, ".node9", "credentials.json");
15271
- INSIGHT_COUNTS_FILE = import_path27.default.join(homeDir, ".node9", "insight-counts.json");
15272
- pending = /* @__PURE__ */ new Map();
15273
- sseClients = /* @__PURE__ */ new Set();
15274
- suggestionTracker = new SuggestionTracker(3);
15275
- taintStore = new TaintStore();
15276
- sessionTaintStore = new SessionTaintStore();
15277
- insightCounts = /* @__PURE__ */ new Map();
15278
- _abandonTimer = null;
15279
- _hadBrowserClient = false;
15280
- _daemonServer = null;
15281
- daemonRejectionHandlerRegistered = false;
15282
- AUTO_DENY_MS = 12e4;
15283
- TRUST_DURATIONS = {
15284
- "30m": 30 * 6e4,
15285
- "1h": 60 * 6e4,
15286
- "2h": 2 * 60 * 6e4
15287
- };
15288
- autoStarted = process.env.NODE9_AUTO_STARTED === "1";
15289
- ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path27.default.join(import_os23.default.tmpdir(), "node9-activity.sock");
15290
- ACTIVITY_RING_SIZE = 100;
15291
- activityRing = [];
15292
- LARGE_RESPONSE_RING_SIZE = 20;
15293
- largeResponseRing = [];
15294
- cachedScanResult = null;
15295
- cachedScanTs = 0;
15296
- SCAN_CACHE_TTL_MS = 5 * 60 * 1e3;
15297
- SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
15298
- INPUT_PRICE_PER_1M = 3;
15299
- OUTPUT_PRICE_PER_1M = 15;
15300
- BYTES_PER_TOKEN = 4;
15301
- CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
15302
- "privilege-escalation",
15303
- "destructive-op",
15304
- "eval-of-remote"
15305
- ]);
15306
- WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
15307
- "write",
15308
- "write_file",
15309
- "create_file",
15310
- "edit",
15311
- "multiedit",
15312
- "str_replace_based_edit_tool",
15313
- "replace",
15314
- "notebook_edit",
15315
- "notebookedit"
15316
- ]);
15317
- ACTIVITY_REBIND_MAX_ATTEMPTS = 5;
15318
- ACTIVITY_REBIND_WINDOW_MS = 6e4;
15319
- ACTIVITY_HEALTH_PROBE_MS = 2e3;
15320
- activitySocketServer = null;
15321
- activityHealthInterval = null;
15322
- activityRebindAttempts = [];
15323
- activityCircuitTripped = false;
15324
- }
15325
- });
15326
-
15327
- // src/agent-wiring.ts
15328
- function readJson2(filePath) {
15329
- if (!import_fs26.default.existsSync(filePath)) return null;
15330
- try {
15331
- return JSON.parse(import_fs26.default.readFileSync(filePath, "utf-8"));
15332
- } catch {
15333
- return "invalid";
15334
- }
15335
- }
15336
- function matchersHaveNode9Hook(matchers) {
15337
- return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
15338
- }
15339
- function flatHaveNode9Hook(entries) {
15340
- return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
15341
- }
15342
- function readHookRoot(filePath, format) {
15343
- if (!import_fs26.default.existsSync(filePath)) return "absent";
15344
- let raw;
15345
- try {
15346
- raw = import_fs26.default.readFileSync(filePath, "utf-8");
15347
- } catch {
15348
- return "absent";
15349
- }
15350
- try {
15351
- const parsed = format === "yaml" ? yaml2.parse(raw) : JSON.parse(raw);
15352
- return parsed?.hooks ?? {};
15353
- } catch {
15354
- return "invalid";
15355
- }
15356
- }
15357
- function eventWired(root, ev, format) {
15358
- const arr = root[ev.key];
15359
- if (format === "matcher") return matchersHaveNode9Hook(arr);
15360
- return flatHaveNode9Hook(arr);
15361
- }
15362
- function detectMcp(servers) {
15363
- const entries = Object.entries(servers ?? {});
15364
- const present = entries.some(([, s]) => s?.command === "node9");
15365
- const wrapped = entries.filter(([, s]) => s?.command === "node9" && Array.isArray(s.args) && s.args.length > 0).map(([name, s]) => `${name} \u2192 ${s.args.join(" ")}`);
15366
- return { wrapped, present };
15367
- }
15368
- function readMcp(filePath, format) {
15369
- if (!import_fs26.default.existsSync(filePath)) return { wrapped: [], present: false };
15370
- try {
15371
- if (format === "toml") {
15372
- const parsed2 = (0, import_smol_toml2.parse)(import_fs26.default.readFileSync(filePath, "utf-8"));
15373
- return detectMcp(parsed2?.mcp_servers);
15374
- }
15375
- const parsed = readJson2(filePath);
15376
- if (parsed === null || parsed === "invalid") return { wrapped: [], present: false };
15377
- return detectMcp(parsed.mcpServers);
15378
- } catch {
15379
- return { wrapped: [], present: false };
15380
- }
15381
- }
15382
- function getAgentWiring(home = import_os24.default.homedir()) {
15383
- const detected = detectAgents(home);
15384
- return AGENT_SPECS.map((spec) => {
15385
- const present = spec.present(home);
15386
- const pad = spec.labelPad ?? DEFAULT_LABEL_PAD;
15387
- let hooks;
15388
- let wireState;
15389
- let hookLabel;
15390
- let settingsPath;
15391
- if (spec.shimFile) {
15392
- const shimWired = exists(spec.shimFile(home));
15393
- hooks = [{ label: "node9 plugin (node9 check)", wired: shimWired }];
15394
- wireState = shimWired ? "wired" : present ? "unwired" : "absent";
15395
- hookLabel = "node9 plugin";
15396
- settingsPath = spec.shimFile(home);
15397
- } else {
15398
- const root = spec.hookFile ? readHookRoot(spec.hookFile(home), spec.hookFormat) : "absent";
15399
- const primary = spec.hookEvents[0];
15400
- const rootPresent = root !== "absent" && root !== "invalid";
15401
- hooks = spec.hookEvents.map((ev) => ({
15402
- label: hookLabelOf(ev, pad),
15403
- wired: rootPresent && eventWired(root, ev, spec.hookFormat)
15404
- }));
15405
- if (root === "absent") wireState = "absent";
15406
- else if (root === "invalid") wireState = "invalid";
15407
- else wireState = primary && eventWired(root, primary, spec.hookFormat) ? "wired" : "unwired";
15408
- hookLabel = primary ? `${primary.key} hook` : "MCP proxy";
15409
- settingsPath = spec.hookFile ? spec.hookFile(home) : spec.mcpFile ? spec.mcpFile(home) : "";
15410
- }
15411
- const mcp = spec.mcpFile ? readMcp(spec.mcpFile(home), spec.mcpFormat ?? "json") : null;
15412
- const anyHookWired = hooks.some((h) => h.wired);
15413
- return {
15414
- id: spec.id,
15415
- label: spec.label,
15416
- setupCommand: spec.setupCommand,
15417
- installed: detected[spec.id],
15418
- present,
15419
- hooks,
15420
- wireState,
15421
- hookLabel,
15422
- settingsPath,
15423
- configFormat: spec.hookFormat === "yaml" ? "YAML" : "JSON",
15424
- mcpServers: mcp ? mcp.wrapped : null,
15425
- mcpProtected: mcp ? mcp.present : false,
15426
- isProtected: anyHookWired || (mcp?.present ?? false)
15427
- };
15428
- });
15429
- }
15430
- var import_fs26, import_path28, import_os24, yaml2, import_smol_toml2, exists, ck, lg, DEFAULT_LABEL_PAD, hookLabelOf, AGENT_SPECS;
15431
- var init_agent_wiring = __esm({
15432
- "src/agent-wiring.ts"() {
15433
- "use strict";
15434
- import_fs26 = __toESM(require("fs"));
15435
- import_path28 = __toESM(require("path"));
15436
- import_os24 = __toESM(require("os"));
15437
- yaml2 = __toESM(require("yaml"));
15438
- import_smol_toml2 = require("smol-toml");
15439
- init_setup();
15440
- exists = (p) => {
15441
- try {
15442
- return import_fs26.default.existsSync(p);
15443
- } catch {
15444
- return false;
15445
- }
15446
- };
15447
- ck = (key) => ({ key, kind: "check" });
15448
- lg = (key) => ({ key, kind: "log" });
15449
- DEFAULT_LABEL_PAD = 11;
15450
- hookLabelOf = (ev, pad) => `${ev.key.padEnd(pad)} (node9 ${ev.kind})`;
15451
- AGENT_SPECS = [
15452
- {
15453
- id: "claude",
15454
- label: "Claude Code",
15455
- setupCommand: "node9 agents add claude",
15456
- hookFile: (h) => import_path28.default.join(h, ".claude", "settings.json"),
15457
- hookFormat: "matcher",
15458
- hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
15459
- mcpFile: (h) => import_path28.default.join(h, ".claude.json"),
15460
- present: (h) => exists(import_path28.default.join(h, ".claude", "settings.json")) || exists(import_path28.default.join(h, ".claude.json"))
15461
- },
15462
- {
15463
- id: "gemini",
15464
- label: "Gemini CLI",
15465
- setupCommand: "node9 agents add gemini",
15466
- hookFile: (h) => import_path28.default.join(h, ".gemini", "settings.json"),
15467
- hookFormat: "matcher",
15468
- hookEvents: [ck("BeforeTool"), lg("AfterTool")],
15469
- mcpFile: (h) => import_path28.default.join(h, ".gemini", "settings.json"),
15470
- present: (h) => exists(import_path28.default.join(h, ".gemini", "settings.json"))
15471
- },
15472
- {
15473
- id: "codex",
15474
- label: "Codex",
15475
- setupCommand: "node9 agents add codex",
15476
- hookFile: (h) => import_path28.default.join(h, ".codex", "hooks.json"),
15477
- hookFormat: "matcher",
15478
- hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
15479
- mcpFile: (h) => import_path28.default.join(h, ".codex", "config.toml"),
15480
- mcpFormat: "toml",
15481
- present: (h) => exists(import_path28.default.join(h, ".codex"))
15482
- },
15483
- {
15484
- id: "antigravity",
15485
- label: "Antigravity",
15486
- setupCommand: "node9 agents add antigravity",
15487
- hookFile: (h) => import_path28.default.join(h, ".gemini", "config", "hooks.json"),
15488
- hookFormat: "matcher",
15489
- hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
15490
- mcpFile: (h) => import_path28.default.join(h, ".gemini", "config", "mcp_config.json"),
15491
- present: (h) => exists(import_path28.default.join(h, ".gemini", "config", "hooks.json")) || exists(import_path28.default.join(h, ".gemini", "antigravity-cli")) || exists(import_path28.default.join(h, ".gemini", "antigravity-ide"))
15492
- },
15493
- {
15494
- id: "copilot",
15495
- label: "GitHub Copilot",
15496
- setupCommand: "node9 agents add copilot",
15497
- hookFile: (h) => import_path28.default.join(h, ".copilot", "hooks", "node9.json"),
15498
- hookFormat: "flat",
15499
- hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
15500
- mcpFile: (h) => import_path28.default.join(h, ".copilot", "mcp-config.json"),
15501
- present: (h) => exists(import_path28.default.join(h, ".copilot"))
15502
- },
15503
- {
15504
- id: "cursor",
15505
- label: "Cursor",
15506
- setupCommand: "node9 agents add cursor",
15507
- // MCP-only — no hook file (see note above).
15508
- hookFormat: "flat",
15509
- hookEvents: [],
15510
- mcpFile: (h) => import_path28.default.join(h, ".cursor", "mcp.json"),
15511
- present: (h) => exists(import_path28.default.join(h, ".cursor", "mcp.json"))
15512
- },
15513
- {
15514
- id: "hermes",
15515
- label: "Hermes Agent",
15516
- setupCommand: "node9 agents add hermes",
15517
- hookFile: (h) => hermesConfigPath(h),
15518
- hookFormat: "yaml",
15519
- hookEvents: [ck("pre_tool_call"), lg("post_tool_call")],
15520
- labelPad: 14,
15521
- // 'post_tool_call' is wider than the default
15522
- present: (h) => exists(hermesConfigPath(h))
15523
- },
15524
- {
15525
- // Plugin-shim agents — protected by a node9-authored plugin/extension file
15526
- // (no hooks, no MCP). hookFormat is unused for these (shimFile drives it).
15527
- id: "opencode",
15528
- label: "OpenCode",
15529
- setupCommand: "node9 agents add opencode",
15530
- hookFormat: "flat",
15531
- hookEvents: [],
15532
- shimFile: (h) => import_path28.default.join(h, ".config", "opencode", "plugins", "node9.js"),
15533
- present: (h) => exists(import_path28.default.join(h, ".config", "opencode")) || exists(import_path28.default.join(h, ".config", "opencode", "plugins", "node9.js"))
15534
- },
15535
- {
15536
- id: "pi",
15537
- label: "Pi",
15538
- setupCommand: "node9 agents add pi",
15539
- hookFormat: "flat",
15540
- hookEvents: [],
15541
- shimFile: (h) => import_path28.default.join(h, ".pi", "agent", "extensions", "node9.js"),
15542
- present: (h) => exists(import_path28.default.join(h, ".pi", "agent")) || exists(import_path28.default.join(h, ".pi", "agent", "extensions", "node9.js"))
15475
+ });
15476
+ }
15477
+ } catch {
15543
15478
  }
15544
- ];
15479
+ });
15480
+ socket.on("error", () => {
15481
+ });
15482
+ });
15483
+ unixServer.on("error", (err2) => {
15484
+ logActivitySocket(`server error: ${err2.message}`);
15485
+ });
15486
+ unixServer.listen(ACTIVITY_SOCKET_PATH2, () => {
15487
+ logActivitySocket(`bound to ${ACTIVITY_SOCKET_PATH2}`);
15488
+ });
15489
+ activitySocketServer = unixServer;
15490
+ }
15491
+ var import_net2, import_fs26, import_path28, import_os24, import_crypto8, 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;
15492
+ var init_state2 = __esm({
15493
+ "src/daemon/state.ts"() {
15494
+ "use strict";
15495
+ import_net2 = __toESM(require("net"));
15496
+ import_fs26 = __toESM(require("fs"));
15497
+ import_path28 = __toESM(require("path"));
15498
+ import_os24 = __toESM(require("os"));
15499
+ import_crypto8 = require("crypto");
15500
+ init_daemon();
15501
+ init_suggestion_tracker();
15502
+ init_taint_store();
15503
+ init_session_counters();
15504
+ init_session_history();
15505
+ homeDir = import_os24.default.homedir();
15506
+ DAEMON_PID_FILE = import_path28.default.join(homeDir, ".node9", "daemon.pid");
15507
+ DECISIONS_FILE = import_path28.default.join(homeDir, ".node9", "decisions.json");
15508
+ AUDIT_LOG_FILE = import_path28.default.join(homeDir, ".node9", "audit.log");
15509
+ TRUST_FILE2 = import_path28.default.join(homeDir, ".node9", "trust.json");
15510
+ GLOBAL_CONFIG_FILE = import_path28.default.join(homeDir, ".node9", "config.json");
15511
+ CREDENTIALS_FILE = import_path28.default.join(homeDir, ".node9", "credentials.json");
15512
+ INSIGHT_COUNTS_FILE = import_path28.default.join(homeDir, ".node9", "insight-counts.json");
15513
+ pending = /* @__PURE__ */ new Map();
15514
+ sseClients = /* @__PURE__ */ new Set();
15515
+ suggestionTracker = new SuggestionTracker(3);
15516
+ taintStore = new TaintStore();
15517
+ sessionTaintStore = new SessionTaintStore();
15518
+ insightCounts = /* @__PURE__ */ new Map();
15519
+ _abandonTimer = null;
15520
+ _hadBrowserClient = false;
15521
+ _daemonServer = null;
15522
+ daemonRejectionHandlerRegistered = false;
15523
+ AUTO_DENY_MS = 12e4;
15524
+ TRUST_DURATIONS = {
15525
+ "30m": 30 * 6e4,
15526
+ "1h": 60 * 6e4,
15527
+ "2h": 2 * 60 * 6e4
15528
+ };
15529
+ autoStarted = process.env.NODE9_AUTO_STARTED === "1";
15530
+ ACTIVITY_SOCKET_PATH2 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path28.default.join(import_os24.default.tmpdir(), "node9-activity.sock");
15531
+ ACTIVITY_RING_SIZE = 100;
15532
+ activityRing = [];
15533
+ LARGE_RESPONSE_RING_SIZE = 20;
15534
+ largeResponseRing = [];
15535
+ cachedScanResult = null;
15536
+ cachedScanTs = 0;
15537
+ SCAN_CACHE_TTL_MS = 5 * 60 * 1e3;
15538
+ SECRET_KEY_RE = /password|secret|token|key|apikey|credential|auth/i;
15539
+ INPUT_PRICE_PER_1M = 3;
15540
+ OUTPUT_PRICE_PER_1M = 15;
15541
+ BYTES_PER_TOKEN = 4;
15542
+ CRITICAL_FORENSIC_CATEGORIES = /* @__PURE__ */ new Set([
15543
+ "privilege-escalation",
15544
+ "destructive-op",
15545
+ "eval-of-remote"
15546
+ ]);
15547
+ WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
15548
+ "write",
15549
+ "write_file",
15550
+ "create_file",
15551
+ "edit",
15552
+ "multiedit",
15553
+ "str_replace_based_edit_tool",
15554
+ "replace",
15555
+ "notebook_edit",
15556
+ "notebookedit"
15557
+ ]);
15558
+ ACTIVITY_REBIND_MAX_ATTEMPTS = 5;
15559
+ ACTIVITY_REBIND_WINDOW_MS = 6e4;
15560
+ ACTIVITY_HEALTH_PROBE_MS = 2e3;
15561
+ activitySocketServer = null;
15562
+ activityHealthInterval = null;
15563
+ activityRebindAttempts = [];
15564
+ activityCircuitTripped = false;
15545
15565
  }
15546
15566
  });
15547
15567
 
@@ -16643,6 +16663,93 @@ var init_ship = __esm({
16643
16663
  }
16644
16664
  });
16645
16665
 
16666
+ // src/policy-snapshot/build.ts
16667
+ function buildPolicySnapshot(config, activeShields, overrides) {
16668
+ const p = config.policy;
16669
+ return {
16670
+ mode: config.settings.mode,
16671
+ panicMode: config.settings.panicMode === true,
16672
+ // The proxy expresses shadow/observe as mode === 'observe' (cloud shadowMode
16673
+ // forces it); there's no separate settings flag.
16674
+ shadowMode: config.settings.mode === "observe",
16675
+ activeShields,
16676
+ shieldOverrides: overrides,
16677
+ smartRuleCount: p.smartRules.length,
16678
+ smartRules: p.smartRules.slice(0, MAX_RULES).map((r) => ({
16679
+ name: r.name,
16680
+ tool: r.tool,
16681
+ verdict: r.verdict,
16682
+ reason: r.reason
16683
+ })),
16684
+ egress: {
16685
+ enabled: p.egress.enabled,
16686
+ mode: p.egress.mode,
16687
+ allow: p.egress.allow.slice(0, MAX_EGRESS)
16688
+ },
16689
+ dlpEnabled: p.dlp.enabled,
16690
+ engineVersion: ENGINE_VERSION
16691
+ };
16692
+ }
16693
+ var MAX_RULES, MAX_EGRESS;
16694
+ var init_build = __esm({
16695
+ "src/policy-snapshot/build.ts"() {
16696
+ "use strict";
16697
+ init_dist();
16698
+ MAX_RULES = 500;
16699
+ MAX_EGRESS = 200;
16700
+ }
16701
+ });
16702
+
16703
+ // src/policy-snapshot/ship.ts
16704
+ function policySnapshotUrlFrom(apiUrl) {
16705
+ return apiUrl.endsWith("/policies/sync") ? apiUrl.replace(/\/policies\/sync$/, "/policy/snapshot") : null;
16706
+ }
16707
+ async function shipPolicySnapshot(body, creds) {
16708
+ const url = policySnapshotUrlFrom(creds.apiUrl);
16709
+ if (!url) return false;
16710
+ const payload = JSON.stringify(body);
16711
+ const parsed = new import_url2.URL(url);
16712
+ const transport = parsed.protocol === "http:" ? import_http2.default : import_https3.default;
16713
+ return new Promise((resolve) => {
16714
+ const req = transport.request(
16715
+ {
16716
+ hostname: parsed.hostname,
16717
+ port: parsed.port ? parseInt(parsed.port, 10) : void 0,
16718
+ path: parsed.pathname + parsed.search,
16719
+ method: "POST",
16720
+ headers: {
16721
+ "Content-Type": "application/json",
16722
+ "Content-Length": Buffer.byteLength(payload),
16723
+ Authorization: `Bearer ${creds.apiKey}`
16724
+ },
16725
+ timeout: 1e4
16726
+ },
16727
+ (res) => {
16728
+ const ok2 = !!res.statusCode && res.statusCode >= 200 && res.statusCode < 300;
16729
+ res.resume();
16730
+ res.on("end", () => resolve(ok2));
16731
+ res.on("error", () => resolve(false));
16732
+ }
16733
+ );
16734
+ req.on("error", () => resolve(false));
16735
+ req.on("timeout", () => {
16736
+ req.destroy();
16737
+ resolve(false);
16738
+ });
16739
+ req.write(payload);
16740
+ req.end();
16741
+ });
16742
+ }
16743
+ var import_http2, import_https3, import_url2;
16744
+ var init_ship2 = __esm({
16745
+ "src/policy-snapshot/ship.ts"() {
16746
+ "use strict";
16747
+ import_http2 = __toESM(require("http"));
16748
+ import_https3 = __toESM(require("https"));
16749
+ import_url2 = require("url");
16750
+ }
16751
+ });
16752
+
16646
16753
  // src/daemon/sync.ts
16647
16754
  function emptySignals3() {
16648
16755
  return {
@@ -16675,6 +16782,11 @@ function buildSessionDeltas(findings, toolCallsBySession) {
16675
16782
  signals
16676
16783
  }));
16677
16784
  }
16785
+ function resolveSyncIntervalMs(settings) {
16786
+ const rawSeconds = settings.cloudSyncIntervalSeconds ?? (settings.cloudSyncIntervalHours ?? DEFAULT_INTERVAL_HOURS) * 3600;
16787
+ const clamped = Math.min(Math.max(rawSeconds, MIN_INTERVAL_SECONDS), MAX_INTERVAL_SECONDS);
16788
+ return clamped * 1e3;
16789
+ }
16678
16790
  function readCredentials() {
16679
16791
  if (process.env.NODE9_API_KEY) {
16680
16792
  return {
@@ -16724,7 +16836,7 @@ function fetchCloudPolicy(apiKey, apiUrl, ifNoneMatch) {
16724
16836
  };
16725
16837
  if (ifNoneMatch) headers["If-None-Match"] = `"${ifNoneMatch}"`;
16726
16838
  return new Promise((resolve, reject) => {
16727
- const req = import_https3.default.request(
16839
+ const req = import_https4.default.request(
16728
16840
  {
16729
16841
  hostname: parsed.hostname,
16730
16842
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16804,6 +16916,9 @@ async function syncOnce() {
16804
16916
  if (process.env.NODE9_POSTURE_DISABLE !== "1") {
16805
16917
  void pushPostureSnapshot(creds);
16806
16918
  }
16919
+ if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
16920
+ void pushPolicySnapshot(creds);
16921
+ }
16807
16922
  }
16808
16923
  async function pushBlastSnapshot(creds) {
16809
16924
  try {
@@ -16813,7 +16928,7 @@ async function pushBlastSnapshot(creds) {
16813
16928
  if (!blastUrl) return;
16814
16929
  const parsed = new URL(blastUrl);
16815
16930
  await new Promise((resolve) => {
16816
- const req = import_https3.default.request(
16931
+ const req = import_https4.default.request(
16817
16932
  {
16818
16933
  hostname: parsed.hostname,
16819
16934
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16850,6 +16965,29 @@ async function pushPostureSnapshot(creds) {
16850
16965
  } catch {
16851
16966
  }
16852
16967
  }
16968
+ async function pushPolicySnapshot(creds) {
16969
+ try {
16970
+ const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
16971
+ await shipPolicySnapshot(body, creds);
16972
+ } catch {
16973
+ }
16974
+ }
16975
+ async function runPolicyPush() {
16976
+ const creds = readCredentials();
16977
+ if (!creds) {
16978
+ return {
16979
+ ok: false,
16980
+ reason: "No API key configured. Add credentials with: node9 login"
16981
+ };
16982
+ }
16983
+ try {
16984
+ const body = buildPolicySnapshot(getConfig(), readActiveShields(), readShieldOverrides());
16985
+ const sent = await shipPolicySnapshot(body, creds);
16986
+ return sent ? { ok: true } : { ok: false, reason: "Push failed (network or server error)" };
16987
+ } catch (e) {
16988
+ return { ok: false, reason: e instanceof Error ? e.message : String(e) };
16989
+ }
16990
+ }
16853
16991
  async function pushScanSnapshot(creds) {
16854
16992
  try {
16855
16993
  const tick = await tickScanWatcher();
@@ -16867,7 +17005,7 @@ async function pushScanSnapshot(creds) {
16867
17005
  const parsed = new URL(scanUrl);
16868
17006
  let posted = false;
16869
17007
  await new Promise((resolve) => {
16870
- const req = import_https3.default.request(
17008
+ const req = import_https4.default.request(
16871
17009
  {
16872
17010
  hostname: parsed.hostname,
16873
17011
  port: parsed.port ? parseInt(parsed.port, 10) : void 0,
@@ -16917,6 +17055,9 @@ async function runCloudSync() {
16917
17055
  if (process.env.NODE9_POSTURE_DISABLE !== "1") {
16918
17056
  void pushPostureSnapshot(creds);
16919
17057
  }
17058
+ if (process.env.NODE9_POLICY_MIRROR_DISABLE !== "1") {
17059
+ void pushPolicySnapshot(creds);
17060
+ }
16920
17061
  };
16921
17062
  try {
16922
17063
  const result = await fetchCloudPolicy(creds.apiKey, creds.apiUrl, readCachedEtag());
@@ -16968,9 +17109,7 @@ function getCloudRules() {
16968
17109
  }
16969
17110
  }
16970
17111
  function startCloudSync() {
16971
- const rawHours = getConfig().settings.cloudSyncIntervalHours ?? DEFAULT_INTERVAL_HOURS;
16972
- const intervalHours = Math.max(rawHours, MIN_INTERVAL_HOURS);
16973
- const intervalMs = intervalHours * 60 * 60 * 1e3;
17112
+ const intervalMs = resolveSyncIntervalMs(getConfig().settings);
16974
17113
  const initial = setTimeout(() => void syncOnce(), 3e4);
16975
17114
  initial.unref();
16976
17115
  const recurring = setInterval(() => void syncOnce(), intervalMs);
@@ -16995,18 +17134,21 @@ function startForensicBroadcast() {
16995
17134
  const recurring = setInterval(() => void tick(), FORENSIC_BROADCAST_INTERVAL_MS);
16996
17135
  recurring.unref();
16997
17136
  }
16998
- var import_fs32, import_https3, import_os29, import_path31, FINDING_TO_SIGNAL3, rulesCacheFile, DEFAULT_API_URL, DEFAULT_INTERVAL_HOURS, MIN_INTERVAL_HOURS, FORENSIC_BROADCAST_INTERVAL_MS, FORENSIC_INITIAL_DELAY_MS, forensicBroadcastOffsets;
17137
+ var import_fs32, import_https4, import_os29, import_path31, 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;
16999
17138
  var init_sync = __esm({
17000
17139
  "src/daemon/sync.ts"() {
17001
17140
  "use strict";
17002
17141
  import_fs32 = __toESM(require("fs"));
17003
- import_https3 = __toESM(require("https"));
17142
+ import_https4 = __toESM(require("https"));
17004
17143
  import_os29 = __toESM(require("os"));
17005
17144
  import_path31 = __toESM(require("path"));
17006
17145
  init_config();
17007
17146
  init_blast();
17008
17147
  init_posture();
17009
17148
  init_ship();
17149
+ init_build();
17150
+ init_ship2();
17151
+ init_shields();
17010
17152
  init_dist();
17011
17153
  init_scan_watermark();
17012
17154
  init_state2();
@@ -17026,7 +17168,8 @@ var init_sync = __esm({
17026
17168
  rulesCacheFile = () => import_path31.default.join(import_os29.default.homedir(), ".node9", "rules-cache.json");
17027
17169
  DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept/policies/sync";
17028
17170
  DEFAULT_INTERVAL_HOURS = 5;
17029
- MIN_INTERVAL_HOURS = 1;
17171
+ MIN_INTERVAL_SECONDS = 15;
17172
+ MAX_INTERVAL_SECONDS = 24 * 60 * 60;
17030
17173
  FORENSIC_BROADCAST_INTERVAL_MS = 3e4;
17031
17174
  FORENSIC_INITIAL_DELAY_MS = 5e3;
17032
17175
  forensicBroadcastOffsets = /* @__PURE__ */ new Map();
@@ -17509,7 +17652,7 @@ function startDaemon() {
17509
17652
  }
17510
17653
  resetIdleTimer();
17511
17654
  const allowedHosts = /* @__PURE__ */ new Set([`127.0.0.1:${DAEMON_PORT}`, `localhost:${DAEMON_PORT}`]);
17512
- const server = import_http2.default.createServer(async (req, res) => {
17655
+ const server = import_http3.default.createServer(async (req, res) => {
17513
17656
  const host = req.headers.host ?? "";
17514
17657
  if (!allowedHosts.has(host)) {
17515
17658
  res.writeHead(421, { "Content-Type": "text/plain" });
@@ -18411,11 +18554,11 @@ data: ${JSON.stringify(item.data)}
18411
18554
  }
18412
18555
  startActivitySocket();
18413
18556
  }
18414
- var import_http2, import_fs36, import_path35, import_os33, import_crypto10, import_child_process2, import_chalk6;
18557
+ var import_http3, import_fs36, import_path35, import_os33, import_crypto10, import_child_process2, import_chalk6;
18415
18558
  var init_server = __esm({
18416
18559
  "src/daemon/server.ts"() {
18417
18560
  "use strict";
18418
- import_http2 = __toESM(require("http"));
18561
+ import_http3 = __toESM(require("http"));
18419
18562
  import_fs36 = __toESM(require("fs"));
18420
18563
  import_path35 = __toESM(require("path"));
18421
18564
  import_os33 = __toESM(require("os"));
@@ -18772,18 +18915,18 @@ function getModelContextLimit(model) {
18772
18915
  return 2e5;
18773
18916
  }
18774
18917
  function readSessionUsage() {
18775
- const projectsDir = import_path61.default.join(import_os54.default.homedir(), ".claude", "projects");
18918
+ const projectsDir = import_path62.default.join(import_os55.default.homedir(), ".claude", "projects");
18776
18919
  if (!import_fs64.default.existsSync(projectsDir)) return null;
18777
18920
  let latestFile = null;
18778
18921
  let latestMtime = 0;
18779
18922
  try {
18780
18923
  for (const dir of import_fs64.default.readdirSync(projectsDir)) {
18781
- const dirPath = import_path61.default.join(projectsDir, dir);
18924
+ const dirPath = import_path62.default.join(projectsDir, dir);
18782
18925
  try {
18783
18926
  if (!import_fs64.default.statSync(dirPath).isDirectory()) continue;
18784
18927
  for (const file of import_fs64.default.readdirSync(dirPath)) {
18785
18928
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
18786
- const filePath = import_path61.default.join(dirPath, file);
18929
+ const filePath = import_path62.default.join(dirPath, file);
18787
18930
  try {
18788
18931
  const mtime = import_fs64.default.statSync(filePath).mtimeMs;
18789
18932
  if (mtime > latestMtime) {
@@ -18861,7 +19004,7 @@ function formatBase(activity) {
18861
19004
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
18862
19005
  const icon = getIcon(activity.tool);
18863
19006
  const toolName = activity.tool.slice(0, 16).padEnd(16);
18864
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os54.default.homedir(), "~");
19007
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os55.default.homedir(), "~");
18865
19008
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
18866
19009
  return `${import_chalk35.default.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${import_chalk35.default.white.bold(toolName)} ${import_chalk35.default.dim(argsPreview)}`;
18867
19010
  }
@@ -18943,7 +19086,7 @@ function postDecisionHttp(id, decision, authToken, port, opts) {
18943
19086
  if (opts?.trustDuration) bodyObj.trustDuration = opts.trustDuration;
18944
19087
  if (opts?.reason) bodyObj.reason = opts.reason;
18945
19088
  const body = JSON.stringify(bodyObj);
18946
- const req = import_http3.default.request(
19089
+ const req = import_http4.default.request(
18947
19090
  {
18948
19091
  hostname: "127.0.0.1",
18949
19092
  port,
@@ -19058,7 +19201,7 @@ function buildRecoveryCardLines(req) {
19058
19201
  ];
19059
19202
  }
19060
19203
  function readApproversFromDisk() {
19061
- const configPath = import_path61.default.join(import_os54.default.homedir(), ".node9", "config.json");
19204
+ const configPath = import_path62.default.join(import_os55.default.homedir(), ".node9", "config.json");
19062
19205
  try {
19063
19206
  const raw = JSON.parse(import_fs64.default.readFileSync(configPath, "utf-8"));
19064
19207
  const settings = raw.settings ?? {};
@@ -19076,7 +19219,7 @@ function approverStatusLine() {
19076
19219
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
19077
19220
  }
19078
19221
  function toggleApprover(channel) {
19079
- const configPath = import_path61.default.join(import_os54.default.homedir(), ".node9", "config.json");
19222
+ const configPath = import_path62.default.join(import_os55.default.homedir(), ".node9", "config.json");
19080
19223
  try {
19081
19224
  const raw = JSON.parse(import_fs64.default.readFileSync(configPath, "utf-8"));
19082
19225
  const settings = raw.settings ?? {};
@@ -19094,7 +19237,7 @@ async function startTail(options = {}) {
19094
19237
  const port = await ensureDaemon();
19095
19238
  if (options.clear) {
19096
19239
  const result = await new Promise((resolve) => {
19097
- const req2 = import_http3.default.request(
19240
+ const req2 = import_http4.default.request(
19098
19241
  { method: "POST", hostname: "127.0.0.1", port, path: "/events/clear" },
19099
19242
  (res) => {
19100
19243
  const status = res.statusCode ?? 0;
@@ -19257,7 +19400,7 @@ async function startTail(options = {}) {
19257
19400
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
19258
19401
  try {
19259
19402
  import_fs64.default.appendFileSync(
19260
- import_path61.default.join(import_os54.default.homedir(), ".node9", "hook-debug.log"),
19403
+ import_path62.default.join(import_os55.default.homedir(), ".node9", "hook-debug.log"),
19261
19404
  `[tail] POST /decision failed: ${String(err2)}
19262
19405
  `
19263
19406
  );
@@ -19321,7 +19464,7 @@ async function startTail(options = {}) {
19321
19464
  };
19322
19465
  process.stdin.on("keypress", onKeypress);
19323
19466
  }
19324
- const auditLog = import_path61.default.join(import_os54.default.homedir(), ".node9", "audit.log");
19467
+ const auditLog = import_path62.default.join(import_os55.default.homedir(), ".node9", "audit.log");
19325
19468
  try {
19326
19469
  const unackedDlp = import_fs64.default.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
19327
19470
  if (unackedDlp > 0) {
@@ -19377,7 +19520,7 @@ async function startTail(options = {}) {
19377
19520
  }, STALL_THRESHOLD_MS / 2);
19378
19521
  stallWatchdog.unref();
19379
19522
  const sseUrl = `http://127.0.0.1:${port}/events?capabilities=input`;
19380
- const req = import_http3.default.get(
19523
+ const req = import_http4.default.get(
19381
19524
  sseUrl,
19382
19525
  {
19383
19526
  headers: authToken ? { "X-Node9-Internal": authToken } : {}
@@ -19548,20 +19691,20 @@ async function startTail(options = {}) {
19548
19691
  process.exit(1);
19549
19692
  });
19550
19693
  }
19551
- var import_http3, import_chalk35, import_fs64, import_os54, import_path61, import_readline6, import_child_process14, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
19694
+ var import_http4, import_chalk35, import_fs64, import_os55, import_path62, import_readline6, import_child_process14, PID_FILE, ICONS, MODEL_CONTEXT_LIMITS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
19552
19695
  var init_tail = __esm({
19553
19696
  "src/tui/tail.ts"() {
19554
19697
  "use strict";
19555
- import_http3 = __toESM(require("http"));
19698
+ import_http4 = __toESM(require("http"));
19556
19699
  import_chalk35 = __toESM(require("chalk"));
19557
19700
  import_fs64 = __toESM(require("fs"));
19558
- import_os54 = __toESM(require("os"));
19559
- import_path61 = __toESM(require("path"));
19701
+ import_os55 = __toESM(require("os"));
19702
+ import_path62 = __toESM(require("path"));
19560
19703
  import_readline6 = __toESM(require("readline"));
19561
19704
  import_child_process14 = require("child_process");
19562
19705
  init_daemon2();
19563
19706
  init_daemon();
19564
- PID_FILE = import_path61.default.join(import_os54.default.homedir(), ".node9", "daemon.pid");
19707
+ PID_FILE = import_path62.default.join(import_os55.default.homedir(), ".node9", "daemon.pid");
19565
19708
  ICONS = {
19566
19709
  bash: "\u{1F4BB}",
19567
19710
  shell: "\u{1F4BB}",
@@ -19626,7 +19769,7 @@ function queryDaemon() {
19626
19769
  return new Promise((resolve) => {
19627
19770
  const timeout = setTimeout(() => resolve(null), 50);
19628
19771
  try {
19629
- const req = import_http4.default.get(
19772
+ const req = import_http5.default.get(
19630
19773
  `http://${DAEMON_HOST}:${DAEMON_PORT}/status`,
19631
19774
  { timeout: 50 },
19632
19775
  (res) => {
@@ -19711,7 +19854,7 @@ function countRulesInDir(rulesDir) {
19711
19854
  try {
19712
19855
  for (const entry of import_fs65.default.readdirSync(rulesDir, { withFileTypes: true })) {
19713
19856
  if (entry.isDirectory()) {
19714
- count += countRulesInDir(import_path62.default.join(rulesDir, entry.name));
19857
+ count += countRulesInDir(import_path63.default.join(rulesDir, entry.name));
19715
19858
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
19716
19859
  count++;
19717
19860
  }
@@ -19722,46 +19865,46 @@ function countRulesInDir(rulesDir) {
19722
19865
  }
19723
19866
  function isSamePath(a, b) {
19724
19867
  try {
19725
- return import_path62.default.resolve(a) === import_path62.default.resolve(b);
19868
+ return import_path63.default.resolve(a) === import_path63.default.resolve(b);
19726
19869
  } catch {
19727
19870
  return false;
19728
19871
  }
19729
19872
  }
19730
19873
  function countConfigs(cwd) {
19731
- const homeDir2 = import_os55.default.homedir();
19732
- const claudeDir = import_path62.default.join(homeDir2, ".claude");
19874
+ const homeDir2 = import_os56.default.homedir();
19875
+ const claudeDir = import_path63.default.join(homeDir2, ".claude");
19733
19876
  let claudeMdCount = 0;
19734
19877
  let rulesCount = 0;
19735
19878
  let hooksCount = 0;
19736
19879
  const userMcpServers = /* @__PURE__ */ new Set();
19737
19880
  const projectMcpServers = /* @__PURE__ */ new Set();
19738
- if (import_fs65.default.existsSync(import_path62.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
19739
- rulesCount += countRulesInDir(import_path62.default.join(claudeDir, "rules"));
19740
- const userSettings = import_path62.default.join(claudeDir, "settings.json");
19881
+ if (import_fs65.default.existsSync(import_path63.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
19882
+ rulesCount += countRulesInDir(import_path63.default.join(claudeDir, "rules"));
19883
+ const userSettings = import_path63.default.join(claudeDir, "settings.json");
19741
19884
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
19742
19885
  hooksCount += countHooksInFile(userSettings);
19743
- const userClaudeJson = import_path62.default.join(homeDir2, ".claude.json");
19886
+ const userClaudeJson = import_path63.default.join(homeDir2, ".claude.json");
19744
19887
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
19745
19888
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
19746
19889
  userMcpServers.delete(name);
19747
19890
  }
19748
19891
  if (cwd) {
19749
- if (import_fs65.default.existsSync(import_path62.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
19750
- if (import_fs65.default.existsSync(import_path62.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
19751
- const projectClaudeDir = import_path62.default.join(cwd, ".claude");
19892
+ if (import_fs65.default.existsSync(import_path63.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
19893
+ if (import_fs65.default.existsSync(import_path63.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
19894
+ const projectClaudeDir = import_path63.default.join(cwd, ".claude");
19752
19895
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
19753
19896
  if (!overlapsUserScope) {
19754
- if (import_fs65.default.existsSync(import_path62.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
19755
- rulesCount += countRulesInDir(import_path62.default.join(projectClaudeDir, "rules"));
19756
- const projSettings = import_path62.default.join(projectClaudeDir, "settings.json");
19897
+ if (import_fs65.default.existsSync(import_path63.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
19898
+ rulesCount += countRulesInDir(import_path63.default.join(projectClaudeDir, "rules"));
19899
+ const projSettings = import_path63.default.join(projectClaudeDir, "settings.json");
19757
19900
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
19758
19901
  hooksCount += countHooksInFile(projSettings);
19759
19902
  }
19760
- if (import_fs65.default.existsSync(import_path62.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19761
- const localSettings = import_path62.default.join(projectClaudeDir, "settings.local.json");
19903
+ if (import_fs65.default.existsSync(import_path63.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
19904
+ const localSettings = import_path63.default.join(projectClaudeDir, "settings.local.json");
19762
19905
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
19763
19906
  hooksCount += countHooksInFile(localSettings);
19764
- const mcpJsonServers = getMcpServerNames(import_path62.default.join(cwd, ".mcp.json"));
19907
+ const mcpJsonServers = getMcpServerNames(import_path63.default.join(cwd, ".mcp.json"));
19765
19908
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
19766
19909
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
19767
19910
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -19794,7 +19937,7 @@ function readActiveShieldsHud() {
19794
19937
  return shieldsCache.value;
19795
19938
  }
19796
19939
  try {
19797
- const shieldsPath = import_path62.default.join(import_os55.default.homedir(), ".node9", "shields.json");
19940
+ const shieldsPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "shields.json");
19798
19941
  if (!import_fs65.default.existsSync(shieldsPath)) {
19799
19942
  shieldsCache = { value: [], ts: now };
19800
19943
  return [];
@@ -19901,9 +20044,9 @@ function renderContextLine(stdin) {
19901
20044
  async function main() {
19902
20045
  try {
19903
20046
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
19904
- if (import_fs65.default.existsSync(import_path62.default.join(import_os55.default.homedir(), ".node9", "hud-debug"))) {
20047
+ if (import_fs65.default.existsSync(import_path63.default.join(import_os56.default.homedir(), ".node9", "hud-debug"))) {
19905
20048
  try {
19906
- const logPath = import_path62.default.join(import_os55.default.homedir(), ".node9", "hud-debug.log");
20049
+ const logPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "hud-debug.log");
19907
20050
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
19908
20051
  let size = 0;
19909
20052
  try {
@@ -19932,8 +20075,8 @@ async function main() {
19932
20075
  try {
19933
20076
  const cwd = stdin.cwd ?? process.cwd();
19934
20077
  for (const configPath of [
19935
- import_path62.default.join(cwd, "node9.config.json"),
19936
- import_path62.default.join(import_os55.default.homedir(), ".node9", "config.json")
20078
+ import_path63.default.join(cwd, "node9.config.json"),
20079
+ import_path63.default.join(import_os56.default.homedir(), ".node9", "config.json")
19937
20080
  ]) {
19938
20081
  if (!import_fs65.default.existsSync(configPath)) continue;
19939
20082
  const cfg = JSON.parse(import_fs65.default.readFileSync(configPath, "utf-8"));
@@ -19954,14 +20097,14 @@ async function main() {
19954
20097
  renderOffline();
19955
20098
  }
19956
20099
  }
19957
- var import_fs65, import_path62, import_os55, import_http4, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
20100
+ var import_fs65, import_path63, import_os56, import_http5, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
19958
20101
  var init_hud = __esm({
19959
20102
  "src/cli/hud.ts"() {
19960
20103
  "use strict";
19961
20104
  import_fs65 = __toESM(require("fs"));
19962
- import_path62 = __toESM(require("path"));
19963
- import_os55 = __toESM(require("os"));
19964
- import_http4 = __toESM(require("http"));
20105
+ import_path63 = __toESM(require("path"));
20106
+ import_os56 = __toESM(require("os"));
20107
+ import_http5 = __toESM(require("http"));
19965
20108
  init_daemon();
19966
20109
  RESET3 = "\x1B[0m";
19967
20110
  BOLD3 = "\x1B[1m";
@@ -19985,11 +20128,44 @@ var init_hud = __esm({
19985
20128
  var import_commander = require("commander");
19986
20129
  init_core();
19987
20130
  init_setup();
20131
+
20132
+ // src/agent-teardowns.ts
20133
+ init_setup();
20134
+ var AGENT_TEARDOWNS = [
20135
+ { id: "claude", label: "Claude", fn: teardownClaude },
20136
+ { id: "gemini", label: "Gemini", fn: teardownGemini },
20137
+ { id: "codex", label: "Codex", fn: teardownCodex },
20138
+ { id: "cursor", label: "Cursor", fn: teardownCursor },
20139
+ { id: "windsurf", label: "Windsurf", fn: teardownWindsurf },
20140
+ { id: "vscode", label: "VSCode", fn: teardownVSCode },
20141
+ { id: "hermes", label: "Hermes", fn: teardownHermes },
20142
+ { id: "antigravity", label: "Antigravity", fn: teardownAntigravity, aliases: ["agy"] },
20143
+ { id: "copilot", label: "Copilot", fn: teardownCopilot },
20144
+ { id: "hud", label: "HUD", fn: teardownHud },
20145
+ {
20146
+ id: "claudedesktop",
20147
+ label: "Claude Desktop",
20148
+ fn: teardownClaudeDesktop,
20149
+ aliases: ["claude-desktop"]
20150
+ },
20151
+ { id: "opencode", label: "OpenCode", fn: teardownOpencode },
20152
+ { id: "pi", label: "Pi", fn: teardownPi }
20153
+ ];
20154
+ function resolveAgentTeardown(target) {
20155
+ const t = target.trim().toLowerCase();
20156
+ return AGENT_TEARDOWNS.find((a) => a.id === t || a.aliases?.includes(t));
20157
+ }
20158
+ function agentTeardownTargets() {
20159
+ return AGENT_TEARDOWNS.flatMap((a) => [a.id, ...a.aliases ?? []]);
20160
+ }
20161
+
20162
+ // src/cli.ts
20163
+ init_agent_wiring();
19988
20164
  init_daemon2();
19989
20165
  var import_chalk36 = __toESM(require("chalk"));
19990
20166
  var import_fs66 = __toESM(require("fs"));
19991
- var import_path63 = __toESM(require("path"));
19992
- var import_os56 = __toESM(require("os"));
20167
+ var import_path64 = __toESM(require("path"));
20168
+ var import_os57 = __toESM(require("os"));
19993
20169
  var import_child_process15 = require("child_process");
19994
20170
  var import_prompts2 = require("@inquirer/prompts");
19995
20171
 
@@ -21619,6 +21795,8 @@ function registerLogCommand(program2) {
21619
21795
  // src/cli/commands/shield.ts
21620
21796
  var import_chalk10 = __toESM(require("chalk"));
21621
21797
  var import_fs46 = __toESM(require("fs"));
21798
+ var import_path44 = __toESM(require("path"));
21799
+ var import_os40 = __toESM(require("os"));
21622
21800
  init_shields();
21623
21801
 
21624
21802
  // src/shields/build.ts
@@ -21749,10 +21927,10 @@ init_audit();
21749
21927
  init_config();
21750
21928
 
21751
21929
  // src/utils/https-fetch.ts
21752
- var import_https4 = __toESM(require("https"));
21930
+ var import_https5 = __toESM(require("https"));
21753
21931
  function httpsFetch(url) {
21754
21932
  return new Promise((resolve, reject) => {
21755
- import_https4.default.get(url, (res) => {
21933
+ import_https5.default.get(url, (res) => {
21756
21934
  if (res.statusCode !== 200) {
21757
21935
  reject(new Error(`HTTP ${String(res.statusCode)} for ${url}`));
21758
21936
  res.resume();
@@ -21768,6 +21946,22 @@ function httpsFetch(url) {
21768
21946
 
21769
21947
  // src/cli/commands/shield.ts
21770
21948
  var COMMUNITY_INDEX_URL = "https://raw.githubusercontent.com/node9ai/node9-proxy/main/shields/community/index.json";
21949
+ function readCloudShields() {
21950
+ const out = /* @__PURE__ */ new Set();
21951
+ try {
21952
+ const file = import_path44.default.join(import_os40.default.homedir(), ".node9", "rules-cache.json");
21953
+ const raw = JSON.parse(import_fs46.default.readFileSync(file, "utf-8"));
21954
+ for (const r of raw.rules ?? []) {
21955
+ const rule = r;
21956
+ const fromSource = rule.source?.startsWith("SHIELD:") ? rule.source.slice("SHIELD:".length).toLowerCase() : void 0;
21957
+ const fromDesc = !fromSource && rule.description ? /\b([a-z0-9-]+)\s+shield/i.exec(rule.description)?.[1]?.toLowerCase() : void 0;
21958
+ const name = fromSource ?? fromDesc;
21959
+ if (name) out.add(name);
21960
+ }
21961
+ } catch {
21962
+ }
21963
+ return out;
21964
+ }
21771
21965
  function registerShieldCommand(program2) {
21772
21966
  const shieldCmd = program2.command("shield").description("Manage pre-packaged security shield templates");
21773
21967
  shieldCmd.command("enable <service>").description("Enable a security shield for a specific service").action((service) => {
@@ -21854,10 +22048,17 @@ function registerShieldCommand(program2) {
21854
22048
  return;
21855
22049
  }
21856
22050
  const active = new Set(readActiveShields());
22051
+ const cloud = readCloudShields();
21857
22052
  console.log(import_chalk10.default.bold("\n\u{1F6E1}\uFE0F Available Shields\n"));
22053
+ console.log(import_chalk10.default.gray(" \u25CF local \xB7 \u2601 cloud\n"));
21858
22054
  for (const shield of listShields()) {
21859
- const status = active.has(shield.name) ? import_chalk10.default.green("\u25CF enabled") : import_chalk10.default.gray("\u25CB disabled");
21860
- console.log(` ${status} ${import_chalk10.default.cyan(shield.name.padEnd(12))} ${shield.description}`);
22055
+ const isLocal = active.has(shield.name);
22056
+ const isCloud = cloud.has(shield.name);
22057
+ const status = isLocal && isCloud ? import_chalk10.default.green("\u25CF\u2601 enabled ") : isLocal ? import_chalk10.default.green("\u25CF enabled ") : isCloud ? import_chalk10.default.cyan("\u2601 cloud ") : import_chalk10.default.gray("\u25CB disabled");
22058
+ const via = isCloud && !isLocal ? import_chalk10.default.gray(" (via dashboard)") : "";
22059
+ console.log(
22060
+ ` ${status} ${import_chalk10.default.cyan(shield.name.padEnd(12))} ${shield.description}${via}`
22061
+ );
21861
22062
  if (shield.aliases.length > 0)
21862
22063
  console.log(import_chalk10.default.gray(` aliases: ${shield.aliases.join(", ")}`));
21863
22064
  }
@@ -22187,15 +22388,15 @@ function registerConfigShowCommand(program2) {
22187
22388
  // src/cli/commands/doctor.ts
22188
22389
  var import_chalk11 = __toESM(require("chalk"));
22189
22390
  var import_fs47 = __toESM(require("fs"));
22190
- var import_path44 = __toESM(require("path"));
22191
- var import_os40 = __toESM(require("os"));
22391
+ var import_path45 = __toESM(require("path"));
22392
+ var import_os41 = __toESM(require("os"));
22192
22393
  var import_child_process8 = require("child_process");
22193
22394
  init_daemon();
22194
22395
  init_config();
22195
22396
  init_agent_wiring();
22196
22397
  function registerDoctorCommand(program2, version2) {
22197
22398
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
22198
- const homeDir2 = import_os40.default.homedir();
22399
+ const homeDir2 = import_os41.default.homedir();
22199
22400
  let failures = 0;
22200
22401
  function pass(msg) {
22201
22402
  console.log(import_chalk11.default.green(" \u2705 ") + msg);
@@ -22241,7 +22442,7 @@ function registerDoctorCommand(program2, version2) {
22241
22442
  );
22242
22443
  }
22243
22444
  section("Configuration");
22244
- const globalConfigPath = import_path44.default.join(homeDir2, ".node9", "config.json");
22445
+ const globalConfigPath = import_path45.default.join(homeDir2, ".node9", "config.json");
22245
22446
  if (import_fs47.default.existsSync(globalConfigPath)) {
22246
22447
  try {
22247
22448
  JSON.parse(import_fs47.default.readFileSync(globalConfigPath, "utf-8"));
@@ -22252,7 +22453,7 @@ function registerDoctorCommand(program2, version2) {
22252
22453
  } else {
22253
22454
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
22254
22455
  }
22255
- const projectConfigPath = import_path44.default.join(process.cwd(), "node9.config.json");
22456
+ const projectConfigPath = import_path45.default.join(process.cwd(), "node9.config.json");
22256
22457
  if (import_fs47.default.existsSync(projectConfigPath)) {
22257
22458
  try {
22258
22459
  JSON.parse(import_fs47.default.readFileSync(projectConfigPath, "utf-8"));
@@ -22264,7 +22465,7 @@ function registerDoctorCommand(program2, version2) {
22264
22465
  );
22265
22466
  }
22266
22467
  }
22267
- const credsPath = import_path44.default.join(homeDir2, ".node9", "credentials.json");
22468
+ const credsPath = import_path45.default.join(homeDir2, ".node9", "credentials.json");
22268
22469
  if (import_fs47.default.existsSync(credsPath)) {
22269
22470
  pass("Cloud credentials found (~/.node9/credentials.json)");
22270
22471
  } else {
@@ -22309,7 +22510,7 @@ function registerDoctorCommand(program2, version2) {
22309
22510
  try {
22310
22511
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
22311
22512
  const cfg = getConfig();
22312
- const creds = import_fs47.default.existsSync(import_path44.default.join(import_os40.default.homedir(), ".node9", "credentials.json"));
22513
+ const creds = import_fs47.default.existsSync(import_path45.default.join(import_os41.default.homedir(), ".node9", "credentials.json"));
22313
22514
  if (!creds) {
22314
22515
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
22315
22516
  } else if (!cfg.settings.approvers.cloud) {
@@ -22360,8 +22561,8 @@ function registerDoctorCommand(program2, version2) {
22360
22561
  // src/cli/commands/audit.ts
22361
22562
  var import_chalk12 = __toESM(require("chalk"));
22362
22563
  var import_fs48 = __toESM(require("fs"));
22363
- var import_path45 = __toESM(require("path"));
22364
- var import_os41 = __toESM(require("os"));
22564
+ var import_path46 = __toESM(require("path"));
22565
+ var import_os42 = __toESM(require("os"));
22365
22566
  function formatRelativeTime(timestamp) {
22366
22567
  const diff = Date.now() - new Date(timestamp).getTime();
22367
22568
  const sec = Math.floor(diff / 1e3);
@@ -22374,7 +22575,7 @@ function formatRelativeTime(timestamp) {
22374
22575
  }
22375
22576
  function registerAuditCommand(program2) {
22376
22577
  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) => {
22377
- const logPath = import_path45.default.join(import_os41.default.homedir(), ".node9", "audit.log");
22578
+ const logPath = import_path46.default.join(import_os42.default.homedir(), ".node9", "audit.log");
22378
22579
  if (!import_fs48.default.existsSync(logPath)) {
22379
22580
  console.log(
22380
22581
  import_chalk12.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
@@ -22438,8 +22639,8 @@ var import_chalk13 = __toESM(require("chalk"));
22438
22639
 
22439
22640
  // src/cli/aggregate/report-audit.ts
22440
22641
  var import_fs49 = __toESM(require("fs"));
22441
- var import_os42 = __toESM(require("os"));
22442
- var import_path46 = __toESM(require("path"));
22642
+ var import_os43 = __toESM(require("os"));
22643
+ var import_path47 = __toESM(require("path"));
22443
22644
  init_costSync();
22444
22645
  init_litellm();
22445
22646
  init_cost_codex();
@@ -22570,7 +22771,7 @@ function freezeClaudeCost(acc) {
22570
22771
  };
22571
22772
  }
22572
22773
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22573
- const projPath = import_path46.default.join(projectsDir, proj);
22774
+ const projPath = import_path47.default.join(projectsDir, proj);
22574
22775
  let files;
22575
22776
  try {
22576
22777
  const stat = import_fs49.default.statSync(projPath);
@@ -22581,7 +22782,7 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
22581
22782
  }
22582
22783
  const startMs = start.getTime();
22583
22784
  for (const file of files) {
22584
- const filePath = import_path46.default.join(projPath, file);
22785
+ const filePath = import_path47.default.join(projPath, file);
22585
22786
  try {
22586
22787
  if (import_fs49.default.statSync(filePath).mtimeMs < startMs) continue;
22587
22788
  } catch {
@@ -22711,28 +22912,28 @@ function listCodexSessionFiles2(sessionsBase) {
22711
22912
  if (!import_fs49.default.existsSync(sessionsBase)) return jsonlFiles;
22712
22913
  try {
22713
22914
  for (const year of import_fs49.default.readdirSync(sessionsBase)) {
22714
- const yearPath = import_path46.default.join(sessionsBase, year);
22915
+ const yearPath = import_path47.default.join(sessionsBase, year);
22715
22916
  try {
22716
22917
  if (!import_fs49.default.statSync(yearPath).isDirectory()) continue;
22717
22918
  } catch {
22718
22919
  continue;
22719
22920
  }
22720
22921
  for (const month of import_fs49.default.readdirSync(yearPath)) {
22721
- const monthPath = import_path46.default.join(yearPath, month);
22922
+ const monthPath = import_path47.default.join(yearPath, month);
22722
22923
  try {
22723
22924
  if (!import_fs49.default.statSync(monthPath).isDirectory()) continue;
22724
22925
  } catch {
22725
22926
  continue;
22726
22927
  }
22727
22928
  for (const day of import_fs49.default.readdirSync(monthPath)) {
22728
- const dayPath = import_path46.default.join(monthPath, day);
22929
+ const dayPath = import_path47.default.join(monthPath, day);
22729
22930
  try {
22730
22931
  if (!import_fs49.default.statSync(dayPath).isDirectory()) continue;
22731
22932
  } catch {
22732
22933
  continue;
22733
22934
  }
22734
22935
  for (const file of import_fs49.default.readdirSync(dayPath)) {
22735
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path46.default.join(dayPath, file));
22936
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path47.default.join(dayPath, file));
22736
22937
  }
22737
22938
  }
22738
22939
  }
@@ -22858,7 +23059,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
22858
23059
  return out;
22859
23060
  }
22860
23061
  for (const proj of dirs) {
22861
- const chatsDir = import_path46.default.join(geminiTmpDir2, proj, "chats");
23062
+ const chatsDir = import_path47.default.join(geminiTmpDir2, proj, "chats");
22862
23063
  let files;
22863
23064
  try {
22864
23065
  if (!import_fs49.default.statSync(chatsDir).isDirectory()) continue;
@@ -22868,7 +23069,7 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
22868
23069
  }
22869
23070
  for (const f of files) {
22870
23071
  if (!f.endsWith(".jsonl")) continue;
22871
- out.push({ projectKey: proj, file: import_path46.default.join(chatsDir, f) });
23072
+ out.push({ projectKey: proj, file: import_path47.default.join(chatsDir, f) });
22872
23073
  }
22873
23074
  }
22874
23075
  return out;
@@ -22883,10 +23084,10 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
22883
23084
  }
22884
23085
  function aggregateReportFromAudit(period, opts = {}) {
22885
23086
  const now = opts.now ?? /* @__PURE__ */ new Date();
22886
- const auditLogPath = opts.auditLogPath ?? import_path46.default.join(import_os42.default.homedir(), ".node9", "audit.log");
22887
- const claudeProjectsDir = opts.claudeProjectsDir ?? import_path46.default.join(import_os42.default.homedir(), ".claude", "projects");
22888
- const codexSessionsDir2 = opts.codexSessionsDir ?? import_path46.default.join(import_os42.default.homedir(), ".codex", "sessions");
22889
- const geminiTmpDir2 = opts.geminiTmpDir ?? import_path46.default.join(import_os42.default.homedir(), ".gemini", "tmp");
23087
+ const auditLogPath = opts.auditLogPath ?? import_path47.default.join(import_os43.default.homedir(), ".node9", "audit.log");
23088
+ const claudeProjectsDir = opts.claudeProjectsDir ?? import_path47.default.join(import_os43.default.homedir(), ".claude", "projects");
23089
+ const codexSessionsDir2 = opts.codexSessionsDir ?? import_path47.default.join(import_os43.default.homedir(), ".codex", "sessions");
23090
+ const geminiTmpDir2 = opts.geminiTmpDir ?? import_path47.default.join(import_os43.default.homedir(), ".gemini", "tmp");
22890
23091
  const hasAuditFile = import_fs49.default.existsSync(auditLogPath);
22891
23092
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
22892
23093
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
@@ -23584,8 +23785,8 @@ function registerDaemonCommand(program2) {
23584
23785
  // src/cli/commands/status.ts
23585
23786
  var import_chalk15 = __toESM(require("chalk"));
23586
23787
  var import_fs50 = __toESM(require("fs"));
23587
- var import_path47 = __toESM(require("path"));
23588
- var import_os43 = __toESM(require("os"));
23788
+ var import_path48 = __toESM(require("path"));
23789
+ var import_os44 = __toESM(require("os"));
23589
23790
  init_core();
23590
23791
  init_daemon();
23591
23792
  init_agent_wiring();
@@ -23642,8 +23843,8 @@ function registerStatusCommand(program2) {
23642
23843
  console.log("");
23643
23844
  const modeLabel = settings.mode === "audit" ? import_chalk15.default.blue("audit") : settings.mode === "strict" ? import_chalk15.default.red("strict") : import_chalk15.default.white("standard");
23644
23845
  console.log(` Mode: ${modeLabel}`);
23645
- const projectConfig = import_path47.default.join(process.cwd(), "node9.config.json");
23646
- const globalConfig = import_path47.default.join(import_os43.default.homedir(), ".node9", "config.json");
23846
+ const projectConfig = import_path48.default.join(process.cwd(), "node9.config.json");
23847
+ const globalConfig = import_path48.default.join(import_os44.default.homedir(), ".node9", "config.json");
23647
23848
  console.log(
23648
23849
  ` Local: ${import_fs50.default.existsSync(projectConfig) ? import_chalk15.default.green("Active (node9.config.json)") : import_chalk15.default.gray("Not present")}`
23649
23850
  );
@@ -23655,7 +23856,7 @@ function registerStatusCommand(program2) {
23655
23856
  ` Sandbox: ${import_chalk15.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
23656
23857
  );
23657
23858
  }
23658
- const wiring = getAgentWiring(import_os43.default.homedir()).filter((a) => a.present);
23859
+ const wiring = getAgentWiring(import_os44.default.homedir()).filter((a) => a.present);
23659
23860
  if (wiring.length > 0) {
23660
23861
  console.log("");
23661
23862
  console.log(import_chalk15.default.bold(" Agent Wiring:"));
@@ -23691,9 +23892,9 @@ function registerStatusCommand(program2) {
23691
23892
  // src/cli/commands/init.ts
23692
23893
  var import_chalk16 = __toESM(require("chalk"));
23693
23894
  var import_fs51 = __toESM(require("fs"));
23694
- var import_path48 = __toESM(require("path"));
23695
- var import_os44 = __toESM(require("os"));
23696
- var import_https5 = __toESM(require("https"));
23895
+ var import_path49 = __toESM(require("path"));
23896
+ var import_os45 = __toESM(require("os"));
23897
+ var import_https6 = __toESM(require("https"));
23697
23898
  init_core();
23698
23899
  init_setup();
23699
23900
  init_shields();
@@ -23711,7 +23912,7 @@ function buildTelemetryPayload(agents, firstInstall) {
23711
23912
  function fireTelemetryPing(agents, firstInstall) {
23712
23913
  try {
23713
23914
  const body = JSON.stringify(buildTelemetryPayload(agents, firstInstall));
23714
- const req = import_https5.default.request(
23915
+ const req = import_https6.default.request(
23715
23916
  {
23716
23917
  hostname: "api.node9.ai",
23717
23918
  path: "/api/v1/telemetry",
@@ -23782,7 +23983,7 @@ function registerInitCommand(program2) {
23782
23983
  }
23783
23984
  console.log("");
23784
23985
  }
23785
- const configPath = import_path48.default.join(import_os44.default.homedir(), ".node9", "config.json");
23986
+ const configPath = import_path49.default.join(import_os45.default.homedir(), ".node9", "config.json");
23786
23987
  const isFirstInstall = !import_fs51.default.existsSync(configPath);
23787
23988
  if (import_fs51.default.existsSync(configPath) && !options.force) {
23788
23989
  try {
@@ -23804,7 +24005,7 @@ function registerInitCommand(program2) {
23804
24005
  ...DEFAULT_CONFIG,
23805
24006
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
23806
24007
  };
23807
- const dir = import_path48.default.dirname(configPath);
24008
+ const dir = import_path49.default.dirname(configPath);
23808
24009
  if (!import_fs51.default.existsSync(dir)) import_fs51.default.mkdirSync(dir, { recursive: true });
23809
24010
  import_fs51.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
23810
24011
  console.log(import_chalk16.default.green(`\u2705 Config created: ${configPath}`));
@@ -23911,7 +24112,7 @@ function registerInitCommand(program2) {
23911
24112
  }
23912
24113
 
23913
24114
  // src/cli/commands/undo.ts
23914
- var import_path49 = __toESM(require("path"));
24115
+ var import_path50 = __toESM(require("path"));
23915
24116
  var import_chalk18 = __toESM(require("chalk"));
23916
24117
 
23917
24118
  // src/tui/undo-navigator.ts
@@ -24070,7 +24271,7 @@ function findMatchingCwd(startDir, history) {
24070
24271
  let dir = startDir;
24071
24272
  while (true) {
24072
24273
  if (cwds.has(dir)) return dir;
24073
- const parent = import_path49.default.dirname(dir);
24274
+ const parent = import_path50.default.dirname(dir);
24074
24275
  if (parent === dir) return null;
24075
24276
  dir = parent;
24076
24277
  }
@@ -24706,8 +24907,8 @@ function registerMcpGatewayCommand(program2) {
24706
24907
  // src/mcp-server/index.ts
24707
24908
  var import_readline5 = __toESM(require("readline"));
24708
24909
  var import_fs53 = __toESM(require("fs"));
24709
- var import_os46 = __toESM(require("os"));
24710
- var import_path51 = __toESM(require("path"));
24910
+ var import_os47 = __toESM(require("os"));
24911
+ var import_path52 = __toESM(require("path"));
24711
24912
  var import_child_process11 = require("child_process");
24712
24913
  init_core();
24713
24914
  init_daemon();
@@ -24715,8 +24916,8 @@ init_shields();
24715
24916
 
24716
24917
  // src/auth/egress-config.ts
24717
24918
  var import_fs52 = __toESM(require("fs"));
24718
- var import_os45 = __toESM(require("os"));
24719
- var import_path50 = __toESM(require("path"));
24919
+ var import_os46 = __toESM(require("os"));
24920
+ var import_path51 = __toESM(require("path"));
24720
24921
  var DEFAULT_EGRESS = {
24721
24922
  enabled: false,
24722
24923
  mode: "review",
@@ -24725,7 +24926,7 @@ var DEFAULT_EGRESS = {
24725
24926
  allowPrivate: true
24726
24927
  };
24727
24928
  function egressConfigPath() {
24728
- return import_path50.default.join(import_os45.default.homedir(), ".node9", "config.json");
24929
+ return import_path51.default.join(import_os46.default.homedir(), ".node9", "config.json");
24729
24930
  }
24730
24931
  function readEgressRawConfig() {
24731
24932
  let text;
@@ -24745,7 +24946,7 @@ function readEgressRawConfig() {
24745
24946
  }
24746
24947
  function writeEgressRawConfig(config) {
24747
24948
  const p = egressConfigPath();
24748
- import_fs52.default.mkdirSync(import_path50.default.dirname(p), { recursive: true });
24949
+ import_fs52.default.mkdirSync(import_path51.default.dirname(p), { recursive: true });
24749
24950
  import_fs52.default.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", { mode: 384 });
24750
24951
  }
24751
24952
  function applyEgress(config, change) {
@@ -25131,8 +25332,8 @@ function handleStatus() {
25131
25332
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
25132
25333
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
25133
25334
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
25134
- const projectConfig = import_path51.default.join(process.cwd(), "node9.config.json");
25135
- const globalConfig = import_path51.default.join(import_os46.default.homedir(), ".node9", "config.json");
25335
+ const projectConfig = import_path52.default.join(process.cwd(), "node9.config.json");
25336
+ const globalConfig = import_path52.default.join(import_os47.default.homedir(), ".node9", "config.json");
25136
25337
  lines.push(
25137
25338
  `Project config (node9.config.json): ${import_fs53.default.existsSync(projectConfig) ? "present" : "not found"}`
25138
25339
  );
@@ -25243,7 +25444,7 @@ function handleEgressDeny(args) {
25243
25444
  addEgressHost("deny", host);
25244
25445
  return `Denied egress to ${host} (deny always wins over allow).`;
25245
25446
  }
25246
- var GLOBAL_CONFIG_PATH = import_path51.default.join(import_os46.default.homedir(), ".node9", "config.json");
25447
+ var GLOBAL_CONFIG_PATH = import_path52.default.join(import_os47.default.homedir(), ".node9", "config.json");
25247
25448
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
25248
25449
  function readGlobalConfigRaw() {
25249
25450
  try {
@@ -25255,7 +25456,7 @@ function readGlobalConfigRaw() {
25255
25456
  return {};
25256
25457
  }
25257
25458
  function writeGlobalConfigRaw(data) {
25258
- const dir = import_path51.default.dirname(GLOBAL_CONFIG_PATH);
25459
+ const dir = import_path52.default.dirname(GLOBAL_CONFIG_PATH);
25259
25460
  if (!import_fs53.default.existsSync(dir)) import_fs53.default.mkdirSync(dir, { recursive: true });
25260
25461
  import_fs53.default.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
25261
25462
  }
@@ -25301,7 +25502,7 @@ function handleApproverSet(args) {
25301
25502
  function handleAuditGet(args) {
25302
25503
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
25303
25504
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
25304
- const auditPath = import_path51.default.join(import_os46.default.homedir(), ".node9", "audit.log");
25505
+ const auditPath = import_path52.default.join(import_os47.default.homedir(), ".node9", "audit.log");
25305
25506
  if (!import_fs53.default.existsSync(auditPath)) return "No audit log found.";
25306
25507
  const rawLines = import_fs53.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
25307
25508
  const parsed = [];
@@ -25809,6 +26010,17 @@ var import_chalk22 = __toESM(require("chalk"));
25809
26010
  init_sync();
25810
26011
  function registerSyncCommand(program2) {
25811
26012
  const policy = program2.command("policy").description("Manage cloud policy rules");
26013
+ policy.command("push").description("Push this machine's effective policy to the node9 dashboard").action(async () => {
26014
+ process.stdout.write(import_chalk22.default.cyan("Pushing policy to the dashboard\u2026"));
26015
+ const result = await runPolicyPush();
26016
+ process.stdout.write("\n");
26017
+ if (!result.ok) {
26018
+ console.error(import_chalk22.default.red(`\u2717 ${result.reason}`));
26019
+ process.exit(1);
26020
+ }
26021
+ console.log(import_chalk22.default.green("\u2713 Policy mirrored to the dashboard"));
26022
+ console.log(import_chalk22.default.gray(" See it under Security Policy \u2192 Machines"));
26023
+ });
25812
26024
  policy.command("sync").description("Sync cloud policy rules to local cache (~/.node9/rules-cache.json)").action(async () => {
25813
26025
  process.stdout.write(import_chalk22.default.cyan("Syncing cloud policy rules\u2026"));
25814
26026
  const result = await runCloudSync();
@@ -26257,12 +26469,12 @@ var import_chalk27 = __toESM(require("chalk"));
26257
26469
 
26258
26470
  // src/shields/jail.ts
26259
26471
  var import_fs55 = __toESM(require("fs"));
26260
- var import_os47 = __toESM(require("os"));
26261
- var import_path52 = __toESM(require("path"));
26472
+ var import_os48 = __toESM(require("os"));
26473
+ var import_path53 = __toESM(require("path"));
26262
26474
  init_shields();
26263
26475
  var USER_JAIL_SHIELD = "user-jail";
26264
26476
  function jailStorePath() {
26265
- return import_path52.default.join(import_os47.default.homedir(), ".node9", "jail-paths.json");
26477
+ return import_path53.default.join(import_os48.default.homedir(), ".node9", "jail-paths.json");
26266
26478
  }
26267
26479
  function readJailPaths() {
26268
26480
  let text;
@@ -26285,7 +26497,7 @@ function readJailPaths() {
26285
26497
  }
26286
26498
  function writeJailPaths(paths) {
26287
26499
  const p = jailStorePath();
26288
- import_fs55.default.mkdirSync(import_path52.default.dirname(p), { recursive: true });
26500
+ import_fs55.default.mkdirSync(import_path53.default.dirname(p), { recursive: true });
26289
26501
  import_fs55.default.writeFileSync(p, JSON.stringify({ paths }, null, 2) + "\n", { mode: 384 });
26290
26502
  }
26291
26503
  function addJailPath(rawPath, verdict) {
@@ -26308,7 +26520,7 @@ function removeJailPath(rawPath) {
26308
26520
  return { removed, paths: after };
26309
26521
  }
26310
26522
  function regenerateUserJail(paths) {
26311
- const file = import_path52.default.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26523
+ const file = import_path53.default.join(USER_SHIELDS_DIR_PATH, `${USER_JAIL_SHIELD}.json`);
26312
26524
  if (paths.length === 0) {
26313
26525
  const active2 = readActiveShields();
26314
26526
  if (active2.includes(USER_JAIL_SHIELD)) {
@@ -26430,13 +26642,13 @@ function registerJailCommand(program2) {
26430
26642
  // src/cli/commands/sandbox.ts
26431
26643
  var import_chalk28 = __toESM(require("chalk"));
26432
26644
  var import_fs58 = __toESM(require("fs"));
26433
- var import_path55 = __toESM(require("path"));
26645
+ var import_path56 = __toESM(require("path"));
26434
26646
  var import_child_process13 = require("child_process");
26435
26647
  init_config();
26436
26648
 
26437
26649
  // src/sandbox/config.ts
26438
26650
  var import_fs56 = __toESM(require("fs"));
26439
- var import_path53 = __toESM(require("path"));
26651
+ var import_path54 = __toESM(require("path"));
26440
26652
  var import_yaml = require("yaml");
26441
26653
  var SANDBOX_CONFIG_FILE = "node9.sandbox.yaml";
26442
26654
  var FORBIDDEN_ENV = /* @__PURE__ */ new Set(["NODE9_API_KEY", "NODE9_API_URL"]);
@@ -26509,7 +26721,7 @@ function scaffoldSandboxYaml(agent) {
26509
26721
  return header + (0, import_yaml.stringify)(defaultSandboxConfig(agent));
26510
26722
  }
26511
26723
  function sandboxConfigPath(cwd = process.cwd()) {
26512
- return import_path53.default.join(cwd, SANDBOX_CONFIG_FILE);
26724
+ return import_path54.default.join(cwd, SANDBOX_CONFIG_FILE);
26513
26725
  }
26514
26726
  function loadSandboxConfig(cwd = process.cwd(), fallbackAgent = "claude") {
26515
26727
  const p = sandboxConfigPath(cwd);
@@ -26577,13 +26789,13 @@ init_templates();
26577
26789
 
26578
26790
  // src/sandbox/runtime.ts
26579
26791
  var import_fs57 = __toESM(require("fs"));
26580
- var import_os48 = __toESM(require("os"));
26581
- var import_path54 = __toESM(require("path"));
26792
+ var import_os49 = __toESM(require("os"));
26793
+ var import_path55 = __toESM(require("path"));
26582
26794
  var import_crypto13 = __toESM(require("crypto"));
26583
26795
  var import_child_process12 = require("child_process");
26584
26796
  init_templates();
26585
26797
  function sandboxDataDir(cwd = process.cwd()) {
26586
- return import_path54.default.join(cwd, ".node9", "sandbox", "data");
26798
+ return import_path55.default.join(cwd, ".node9", "sandbox", "data");
26587
26799
  }
26588
26800
  function detectEngine(engine) {
26589
26801
  const r = (0, import_child_process12.spawnSync)(engine, ["--version"], { encoding: "utf-8" });
@@ -26594,7 +26806,7 @@ function detectEngine(engine) {
26594
26806
  }
26595
26807
  function agentCredentialsMount(agent) {
26596
26808
  const rel = agent === "codex" ? ".codex/auth.json" : ".claude/.credentials.json";
26597
- return { hostPath: import_path54.default.join(import_os48.default.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26809
+ return { hostPath: import_path55.default.join(import_os49.default.homedir(), rel), target: `/home/${RUN_AS_USER}/${rel}` };
26598
26810
  }
26599
26811
  function buildRunArgs(opts) {
26600
26812
  const { config, workspaceHostPath, dataHostPath, allowlistHostPath, agentArgs } = opts;
@@ -26622,30 +26834,30 @@ function imageContentHash(dockerfile, entrypoint) {
26622
26834
  return import_crypto13.default.createHash("sha256").update(dockerfile).update("\0").update(entrypoint).digest("hex").slice(0, 16);
26623
26835
  }
26624
26836
  function sandboxBuildDir(cwd = process.cwd()) {
26625
- return import_path54.default.join(cwd, ".node9", "sandbox", "build");
26837
+ return import_path55.default.join(cwd, ".node9", "sandbox", "build");
26626
26838
  }
26627
26839
  function writeBuildContext(cwd, dockerfile, entrypoint) {
26628
26840
  const dir = sandboxBuildDir(cwd);
26629
26841
  import_fs57.default.mkdirSync(dir, { recursive: true });
26630
- import_fs57.default.writeFileSync(import_path54.default.join(dir, "Dockerfile"), dockerfile);
26631
- import_fs57.default.writeFileSync(import_path54.default.join(dir, "entrypoint.sh"), entrypoint);
26842
+ import_fs57.default.writeFileSync(import_path55.default.join(dir, "Dockerfile"), dockerfile);
26843
+ import_fs57.default.writeFileSync(import_path55.default.join(dir, "entrypoint.sh"), entrypoint);
26632
26844
  return dir;
26633
26845
  }
26634
26846
  function writeAllowlist(cwd, hosts) {
26635
- const dir = import_path54.default.join(cwd, ".node9", "sandbox");
26847
+ const dir = import_path55.default.join(cwd, ".node9", "sandbox");
26636
26848
  import_fs57.default.mkdirSync(dir, { recursive: true });
26637
- const p = import_path54.default.join(dir, "allowed-domains.txt");
26849
+ const p = import_path55.default.join(dir, "allowed-domains.txt");
26638
26850
  import_fs57.default.writeFileSync(p, hosts.join("\n") + "\n");
26639
26851
  return p;
26640
26852
  }
26641
26853
  function resolveHomePath(p) {
26642
- return p.startsWith("~") ? import_path54.default.join(import_os48.default.homedir(), p.slice(1)) : import_path54.default.resolve(p);
26854
+ return p.startsWith("~") ? import_path55.default.join(import_os49.default.homedir(), p.slice(1)) : import_path55.default.resolve(p);
26643
26855
  }
26644
26856
 
26645
26857
  // src/cli/commands/sandbox.ts
26646
26858
  function seedDataDirConfig(dataDir, sandbox) {
26647
26859
  import_fs58.default.mkdirSync(dataDir, { recursive: true });
26648
- const configPath = import_path55.default.join(dataDir, "config.json");
26860
+ const configPath = import_path56.default.join(dataDir, "config.json");
26649
26861
  const seed = {
26650
26862
  settings: {
26651
26863
  approvers: {
@@ -26710,7 +26922,7 @@ function registerSandboxCommand(program2, version2) {
26710
26922
  const buildDir = writeBuildContext(cwd, dockerfile, entrypoint);
26711
26923
  const hash = imageContentHash(dockerfile, entrypoint);
26712
26924
  const image = sandbox.runtime.image;
26713
- const hashFile = import_path55.default.join(sandboxBuildDir(cwd), ".image-hash");
26925
+ const hashFile = import_path56.default.join(sandboxBuildDir(cwd), ".image-hash");
26714
26926
  const lastHash = import_fs58.default.existsSync(hashFile) ? import_fs58.default.readFileSync(hashFile, "utf-8").trim() : "";
26715
26927
  const imageExists = (0, import_child_process13.spawnSync)(sandbox.runtime.engine, ["image", "inspect", image], { stdio: "ignore" }).status === 0;
26716
26928
  const needBuild = sandbox.runtime.rebuild === "always" || !imageExists || sandbox.runtime.rebuild !== "never" && lastHash !== hash;
@@ -26753,7 +26965,7 @@ function registerSandboxCommand(program2, version2) {
26753
26965
  process.exit(r.status ?? 0);
26754
26966
  });
26755
26967
  cmd.command("tail").description("Stream the sandbox's audit log (host-side)").action(() => {
26756
- const auditPath = import_path55.default.join(sandboxDataDir(), "audit.log");
26968
+ const auditPath = import_path56.default.join(sandboxDataDir(), "audit.log");
26757
26969
  if (!import_fs58.default.existsSync(auditPath)) {
26758
26970
  console.log(import_chalk28.default.dim(" no sandbox audit yet."));
26759
26971
  return;
@@ -26761,7 +26973,7 @@ function registerSandboxCommand(program2, version2) {
26761
26973
  (0, import_child_process13.spawnSync)("tail", ["-f", auditPath], { stdio: "inherit" });
26762
26974
  });
26763
26975
  cmd.command("logs").description("Dump the sandbox's audit log").action(() => {
26764
- const auditPath = import_path55.default.join(sandboxDataDir(), "audit.log");
26976
+ const auditPath = import_path56.default.join(sandboxDataDir(), "audit.log");
26765
26977
  if (!import_fs58.default.existsSync(auditPath)) {
26766
26978
  console.log(import_chalk28.default.dim(" no sandbox audit yet."));
26767
26979
  return;
@@ -26780,7 +26992,7 @@ function registerSandboxCommand(program2, version2) {
26780
26992
  stdio: "ignore"
26781
26993
  });
26782
26994
  }
26783
- import_fs58.default.rmSync(import_path55.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26995
+ import_fs58.default.rmSync(import_path56.default.join(cwd, ".node9", "sandbox"), { recursive: true, force: true });
26784
26996
  console.log(import_chalk28.default.green(" \u2713 sandbox image + build + data removed."));
26785
26997
  });
26786
26998
  }
@@ -26788,8 +27000,8 @@ function registerSandboxCommand(program2, version2) {
26788
27000
  // src/cli/commands/sessions.ts
26789
27001
  var import_chalk29 = __toESM(require("chalk"));
26790
27002
  var import_fs59 = __toESM(require("fs"));
26791
- var import_path56 = __toESM(require("path"));
26792
- var import_os49 = __toESM(require("os"));
27003
+ var import_path57 = __toESM(require("path"));
27004
+ var import_os50 = __toESM(require("os"));
26793
27005
  init_scan_summary();
26794
27006
  init_litellm();
26795
27007
  init_cost_gemini();
@@ -26810,10 +27022,10 @@ function encodeProjectPath(projectPath) {
26810
27022
  }
26811
27023
  function sessionJsonlPath(projectPath, sessionId) {
26812
27024
  const encoded = encodeProjectPath(projectPath);
26813
- return import_path56.default.join(import_os49.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
27025
+ return import_path57.default.join(import_os50.default.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
26814
27026
  }
26815
27027
  function projectLabel(projectPath) {
26816
- return projectPath.replace(import_os49.default.homedir(), "~");
27028
+ return projectPath.replace(import_os50.default.homedir(), "~");
26817
27029
  }
26818
27030
  function parseHistoryLines(lines) {
26819
27031
  const entries = [];
@@ -26882,7 +27094,7 @@ function parseSessionLines(lines) {
26882
27094
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
26883
27095
  }
26884
27096
  function loadAuditEntries(auditPath) {
26885
- const aPath = auditPath ?? import_path56.default.join(import_os49.default.homedir(), ".node9", "audit.log");
27097
+ const aPath = auditPath ?? import_path57.default.join(import_os50.default.homedir(), ".node9", "audit.log");
26886
27098
  let raw;
26887
27099
  try {
26888
27100
  raw = import_fs59.default.readFileSync(aPath, "utf-8");
@@ -26921,7 +27133,7 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
26921
27133
  return result;
26922
27134
  }
26923
27135
  function buildGeminiSessions(days, allAuditEntries) {
26924
- const tmpDir = import_path56.default.join(import_os49.default.homedir(), ".gemini", "tmp");
27136
+ const tmpDir = import_path57.default.join(import_os50.default.homedir(), ".gemini", "tmp");
26925
27137
  if (!import_fs59.default.existsSync(tmpDir)) return [];
26926
27138
  const cutoff = days !== null ? (() => {
26927
27139
  const d = /* @__PURE__ */ new Date();
@@ -26937,18 +27149,18 @@ function buildGeminiSessions(days, allAuditEntries) {
26937
27149
  }
26938
27150
  const summaries = [];
26939
27151
  for (const slug2 of slugDirs) {
26940
- const slugPath = import_path56.default.join(tmpDir, slug2);
27152
+ const slugPath = import_path57.default.join(tmpDir, slug2);
26941
27153
  try {
26942
27154
  if (!import_fs59.default.statSync(slugPath).isDirectory()) continue;
26943
27155
  } catch {
26944
27156
  continue;
26945
27157
  }
26946
- let projectRoot = import_path56.default.join(import_os49.default.homedir(), slug2);
27158
+ let projectRoot = import_path57.default.join(import_os50.default.homedir(), slug2);
26947
27159
  try {
26948
- projectRoot = import_fs59.default.readFileSync(import_path56.default.join(slugPath, ".project_root"), "utf-8").trim();
27160
+ projectRoot = import_fs59.default.readFileSync(import_path57.default.join(slugPath, ".project_root"), "utf-8").trim();
26949
27161
  } catch {
26950
27162
  }
26951
- const chatsDir = import_path56.default.join(slugPath, "chats");
27163
+ const chatsDir = import_path57.default.join(slugPath, "chats");
26952
27164
  if (!import_fs59.default.existsSync(chatsDir)) continue;
26953
27165
  let chatFiles;
26954
27166
  try {
@@ -26959,7 +27171,7 @@ function buildGeminiSessions(days, allAuditEntries) {
26959
27171
  for (const chatFile of chatFiles) {
26960
27172
  let raw;
26961
27173
  try {
26962
- raw = import_fs59.default.readFileSync(import_path56.default.join(chatsDir, chatFile), "utf-8");
27174
+ raw = import_fs59.default.readFileSync(import_path57.default.join(chatsDir, chatFile), "utf-8");
26963
27175
  } catch {
26964
27176
  continue;
26965
27177
  }
@@ -27039,7 +27251,7 @@ function buildGeminiSessions(days, allAuditEntries) {
27039
27251
  return summaries;
27040
27252
  }
27041
27253
  function buildCodexSessions(days, allAuditEntries) {
27042
- const sessionsBase = import_path56.default.join(import_os49.default.homedir(), ".codex", "sessions");
27254
+ const sessionsBase = import_path57.default.join(import_os50.default.homedir(), ".codex", "sessions");
27043
27255
  if (!import_fs59.default.existsSync(sessionsBase)) return [];
27044
27256
  const cutoff = days !== null ? (() => {
27045
27257
  const d = /* @__PURE__ */ new Date();
@@ -27050,28 +27262,28 @@ function buildCodexSessions(days, allAuditEntries) {
27050
27262
  const jsonlFiles = [];
27051
27263
  try {
27052
27264
  for (const year of import_fs59.default.readdirSync(sessionsBase)) {
27053
- const yearPath = import_path56.default.join(sessionsBase, year);
27265
+ const yearPath = import_path57.default.join(sessionsBase, year);
27054
27266
  try {
27055
27267
  if (!import_fs59.default.statSync(yearPath).isDirectory()) continue;
27056
27268
  } catch {
27057
27269
  continue;
27058
27270
  }
27059
27271
  for (const month of import_fs59.default.readdirSync(yearPath)) {
27060
- const monthPath = import_path56.default.join(yearPath, month);
27272
+ const monthPath = import_path57.default.join(yearPath, month);
27061
27273
  try {
27062
27274
  if (!import_fs59.default.statSync(monthPath).isDirectory()) continue;
27063
27275
  } catch {
27064
27276
  continue;
27065
27277
  }
27066
27278
  for (const day of import_fs59.default.readdirSync(monthPath)) {
27067
- const dayPath = import_path56.default.join(monthPath, day);
27279
+ const dayPath = import_path57.default.join(monthPath, day);
27068
27280
  try {
27069
27281
  if (!import_fs59.default.statSync(dayPath).isDirectory()) continue;
27070
27282
  } catch {
27071
27283
  continue;
27072
27284
  }
27073
27285
  for (const file of import_fs59.default.readdirSync(dayPath)) {
27074
- if (file.endsWith(".jsonl")) jsonlFiles.push(import_path56.default.join(dayPath, file));
27286
+ if (file.endsWith(".jsonl")) jsonlFiles.push(import_path57.default.join(dayPath, file));
27075
27287
  }
27076
27288
  }
27077
27289
  }
@@ -27169,7 +27381,7 @@ function buildCodexSessions(days, allAuditEntries) {
27169
27381
  return summaries;
27170
27382
  }
27171
27383
  function buildSessions(days, historyPath) {
27172
- const hPath = historyPath ?? import_path56.default.join(import_os49.default.homedir(), ".claude", "history.jsonl");
27384
+ const hPath = historyPath ?? import_path57.default.join(import_os50.default.homedir(), ".claude", "history.jsonl");
27173
27385
  let historyRaw = "";
27174
27386
  try {
27175
27387
  historyRaw = import_fs59.default.readFileSync(hPath, "utf-8");
@@ -27591,11 +27803,11 @@ function registerSessionTaintCommand(program2) {
27591
27803
  // src/cli/commands/skill-pin.ts
27592
27804
  var import_chalk31 = __toESM(require("chalk"));
27593
27805
  var import_fs60 = __toESM(require("fs"));
27594
- var import_os50 = __toESM(require("os"));
27595
- var import_path57 = __toESM(require("path"));
27806
+ var import_os51 = __toESM(require("os"));
27807
+ var import_path58 = __toESM(require("path"));
27596
27808
  function wipeSkillSessions() {
27597
27809
  try {
27598
- import_fs60.default.rmSync(import_path57.default.join(import_os50.default.homedir(), ".node9", "skill-sessions"), {
27810
+ import_fs60.default.rmSync(import_path58.default.join(import_os51.default.homedir(), ".node9", "skill-sessions"), {
27599
27811
  recursive: true,
27600
27812
  force: true
27601
27813
  });
@@ -27678,10 +27890,10 @@ function registerSkillPinCommand(program2) {
27678
27890
 
27679
27891
  // src/cli/commands/decisions.ts
27680
27892
  var import_fs61 = __toESM(require("fs"));
27681
- var import_os51 = __toESM(require("os"));
27682
- var import_path58 = __toESM(require("path"));
27893
+ var import_os52 = __toESM(require("os"));
27894
+ var import_path59 = __toESM(require("path"));
27683
27895
  var import_chalk32 = __toESM(require("chalk"));
27684
- var DECISIONS_FILE2 = import_path58.default.join(import_os51.default.homedir(), ".node9", "decisions.json");
27896
+ var DECISIONS_FILE2 = import_path59.default.join(import_os52.default.homedir(), ".node9", "decisions.json");
27685
27897
  function readDecisions() {
27686
27898
  try {
27687
27899
  if (!import_fs61.default.existsSync(DECISIONS_FILE2)) return {};
@@ -27697,7 +27909,7 @@ function readDecisions() {
27697
27909
  }
27698
27910
  }
27699
27911
  function writeDecisions(d) {
27700
- const dir = import_path58.default.dirname(DECISIONS_FILE2);
27912
+ const dir = import_path59.default.dirname(DECISIONS_FILE2);
27701
27913
  if (!import_fs61.default.existsSync(dir)) import_fs61.default.mkdirSync(dir, { recursive: true });
27702
27914
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
27703
27915
  import_fs61.default.writeFileSync(tmp, JSON.stringify(d, null, 2));
@@ -27759,10 +27971,10 @@ Persistent decisions (${entries.length})
27759
27971
  // src/cli/commands/dlp.ts
27760
27972
  var import_chalk33 = __toESM(require("chalk"));
27761
27973
  var import_fs62 = __toESM(require("fs"));
27762
- var import_path59 = __toESM(require("path"));
27763
- var import_os52 = __toESM(require("os"));
27764
- var AUDIT_LOG = import_path59.default.join(import_os52.default.homedir(), ".node9", "audit.log");
27765
- var RESOLVED_FILE = import_path59.default.join(import_os52.default.homedir(), ".node9", "dlp-resolved.json");
27974
+ var import_path60 = __toESM(require("path"));
27975
+ var import_os53 = __toESM(require("os"));
27976
+ var AUDIT_LOG = import_path60.default.join(import_os53.default.homedir(), ".node9", "audit.log");
27977
+ var RESOLVED_FILE = import_path60.default.join(import_os53.default.homedir(), ".node9", "dlp-resolved.json");
27766
27978
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
27767
27979
  function stripAnsi(s) {
27768
27980
  return s.replace(ANSI_RE, "");
@@ -27882,14 +28094,14 @@ function registerDlpCommand(program2) {
27882
28094
  // src/cli/commands/mask.ts
27883
28095
  var import_chalk34 = __toESM(require("chalk"));
27884
28096
  var import_fs63 = __toESM(require("fs"));
27885
- var import_path60 = __toESM(require("path"));
27886
- var import_os53 = __toESM(require("os"));
28097
+ var import_path61 = __toESM(require("path"));
28098
+ var import_os54 = __toESM(require("os"));
27887
28099
  init_dlp();
27888
28100
  function findJsonlFiles(dir) {
27889
28101
  const results = [];
27890
28102
  if (!import_fs63.default.existsSync(dir)) return results;
27891
28103
  for (const entry of import_fs63.default.readdirSync(dir, { withFileTypes: true })) {
27892
- const full = import_path60.default.join(dir, entry.name);
28104
+ const full = import_path61.default.join(dir, entry.name);
27893
28105
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
27894
28106
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
27895
28107
  }
@@ -27992,7 +28204,7 @@ function findJsonFiles(dir) {
27992
28204
  const results = [];
27993
28205
  if (!import_fs63.default.existsSync(dir)) return results;
27994
28206
  for (const entry of import_fs63.default.readdirSync(dir, { withFileTypes: true })) {
27995
- const full = import_path60.default.join(dir, entry.name);
28207
+ const full = import_path61.default.join(dir, entry.name);
27996
28208
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
27997
28209
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
27998
28210
  }
@@ -28001,9 +28213,9 @@ function findJsonFiles(dir) {
28001
28213
  function registerMaskCommand(program2) {
28002
28214
  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) => {
28003
28215
  const dryRun = !!options.dryRun;
28004
- const home = import_os53.default.homedir();
28005
- const claudeDir = import_path60.default.join(home, ".claude", "projects");
28006
- const geminiDir = import_path60.default.join(home, ".gemini", "tmp");
28216
+ const home = import_os54.default.homedir();
28217
+ const claudeDir = import_path61.default.join(home, ".claude", "projects");
28218
+ const geminiDir = import_path61.default.join(home, ".gemini", "tmp");
28007
28219
  const allFiles = [
28008
28220
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
28009
28221
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -28067,15 +28279,15 @@ function registerMaskCommand(program2) {
28067
28279
  // src/cli.ts
28068
28280
  init_blast();
28069
28281
  var { version } = JSON.parse(
28070
- import_fs66.default.readFileSync(import_path63.default.join(__dirname, "../package.json"), "utf-8")
28282
+ import_fs66.default.readFileSync(import_path64.default.join(__dirname, "../package.json"), "utf-8")
28071
28283
  );
28072
28284
  var program = new import_commander.Command();
28073
28285
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
28074
28286
  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) => {
28075
28287
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
28076
- const credPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "credentials.json");
28077
- if (!import_fs66.default.existsSync(import_path63.default.dirname(credPath)))
28078
- import_fs66.default.mkdirSync(import_path63.default.dirname(credPath), { recursive: true });
28288
+ const credPath = import_path64.default.join(import_os57.default.homedir(), ".node9", "credentials.json");
28289
+ if (!import_fs66.default.existsSync(import_path64.default.dirname(credPath)))
28290
+ import_fs66.default.mkdirSync(import_path64.default.dirname(credPath), { recursive: true });
28079
28291
  const profileName = options.profile || "default";
28080
28292
  let existingCreds = {};
28081
28293
  try {
@@ -28095,7 +28307,7 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
28095
28307
  import_fs66.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
28096
28308
  let effectiveCloud = null;
28097
28309
  if (profileName === "default") {
28098
- const configPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "config.json");
28310
+ const configPath = import_path64.default.join(import_os57.default.homedir(), ".node9", "config.json");
28099
28311
  let config = {};
28100
28312
  try {
28101
28313
  if (import_fs66.default.existsSync(configPath))
@@ -28114,8 +28326,8 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
28114
28326
  approvers.cloud = false;
28115
28327
  }
28116
28328
  s.approvers = approvers;
28117
- if (!import_fs66.default.existsSync(import_path63.default.dirname(configPath)))
28118
- import_fs66.default.mkdirSync(import_path63.default.dirname(configPath), { recursive: true });
28329
+ if (!import_fs66.default.existsSync(import_path64.default.dirname(configPath)))
28330
+ import_fs66.default.mkdirSync(import_path64.default.dirname(configPath), { recursive: true });
28119
28331
  import_fs66.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
28120
28332
  effectiveCloud = approvers.cloud === true;
28121
28333
  }
@@ -28225,37 +28437,21 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
28225
28437
  );
28226
28438
  process.exit(1);
28227
28439
  });
28228
- program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
28229
- "after",
28230
- "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
28231
- ).argument(
28232
- "<target>",
28233
- "The agent to remove from: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
28234
- ).action((target) => {
28235
- let fn;
28236
- if (target === "claude") fn = teardownClaude;
28237
- else if (target === "gemini") fn = teardownGemini;
28238
- else if (target === "antigravity" || target === "agy") fn = teardownAntigravity;
28239
- else if (target === "copilot") fn = teardownCopilot;
28240
- else if (target === "cursor") fn = teardownCursor;
28241
- else if (target === "codex") fn = teardownCodex;
28242
- else if (target === "windsurf") fn = teardownWindsurf;
28243
- else if (target === "vscode") fn = teardownVSCode;
28244
- else if (target === "hermes") fn = teardownHermes;
28245
- else if (target === "hud") fn = teardownHud;
28246
- else {
28440
+ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText("after", `
28441
+ Supported targets: ${agentTeardownTargets().join(" ")}`).argument("<target>", `The agent to remove from: ${agentTeardownTargets().join(" | ")}`).action((target) => {
28442
+ const agent = resolveAgentTeardown(target);
28443
+ if (!agent) {
28247
28444
  console.error(
28248
- import_chalk36.default.red(
28249
- `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
28250
- )
28445
+ import_chalk36.default.red(`Unknown target: "${target}". Supported: ${agentTeardownTargets().join(", ")}`)
28251
28446
  );
28252
28447
  process.exit(1);
28448
+ return;
28253
28449
  }
28254
28450
  console.log(import_chalk36.default.cyan(`
28255
- \u{1F6E1}\uFE0F Node9: removing hooks from ${target}...
28451
+ \u{1F6E1}\uFE0F Node9: removing hooks from ${agent.label}...
28256
28452
  `));
28257
28453
  try {
28258
- fn();
28454
+ agent.fn();
28259
28455
  } catch (err2) {
28260
28456
  console.error(import_chalk36.default.red(` \u26A0\uFE0F Failed: ${err2 instanceof Error ? err2.message : String(err2)}`));
28261
28457
  process.exit(1);
@@ -28273,15 +28469,7 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
28273
28469
  }
28274
28470
  console.log(import_chalk36.default.bold("\nRemoving hooks..."));
28275
28471
  let teardownFailed = false;
28276
- for (const [label2, fn] of [
28277
- ["Claude", teardownClaude],
28278
- ["Gemini", teardownGemini],
28279
- ["Cursor", teardownCursor],
28280
- ["Codex", teardownCodex],
28281
- ["Windsurf", teardownWindsurf],
28282
- ["VSCode", teardownVSCode],
28283
- ["Hermes", teardownHermes]
28284
- ]) {
28472
+ for (const { label: label2, fn } of AGENT_TEARDOWNS) {
28285
28473
  try {
28286
28474
  fn();
28287
28475
  } catch (err2) {
@@ -28293,8 +28481,21 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
28293
28481
  );
28294
28482
  }
28295
28483
  }
28484
+ try {
28485
+ const residual = getAgentWiring().filter((a) => a.wireState === "wired");
28486
+ if (residual.length === 0) {
28487
+ console.log(import_chalk36.default.green(" \u2705 Verified \u2014 no node9 hooks or plugin shims remain"));
28488
+ } else {
28489
+ teardownFailed = true;
28490
+ console.error(import_chalk36.default.red(" \u26A0\uFE0F Still wired after teardown:"));
28491
+ for (const a of residual) {
28492
+ console.error(import_chalk36.default.red(` \u2022 ${a.label} \u2014 ${a.settingsPath}`));
28493
+ }
28494
+ }
28495
+ } catch {
28496
+ }
28296
28497
  if (options.purge) {
28297
- const node9Dir = import_path63.default.join(import_os56.default.homedir(), ".node9");
28498
+ const node9Dir = import_path64.default.join(import_os57.default.homedir(), ".node9");
28298
28499
  if (import_fs66.default.existsSync(node9Dir)) {
28299
28500
  const confirmed = await (0, import_prompts2.confirm)({
28300
28501
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
@@ -28425,7 +28626,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
28425
28626
  });
28426
28627
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
28427
28628
  try {
28428
- const dashboardPath = import_path63.default.join(__dirname, "dashboard.mjs");
28629
+ const dashboardPath = import_path64.default.join(__dirname, "dashboard.mjs");
28429
28630
  const dynamicImport = new Function("id", "return import(id)");
28430
28631
  const mod = await dynamicImport(`file://${dashboardPath}`);
28431
28632
  await mod.startMonitor();
@@ -28463,9 +28664,9 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
28463
28664
  Run "node9 addto claude" to register it as the statusLine.`
28464
28665
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
28465
28666
  if (subcommand === "debug") {
28466
- const flagFile = import_path63.default.join(import_os56.default.homedir(), ".node9", "hud-debug");
28667
+ const flagFile = import_path64.default.join(import_os57.default.homedir(), ".node9", "hud-debug");
28467
28668
  if (state === "on") {
28468
- import_fs66.default.mkdirSync(import_path63.default.dirname(flagFile), { recursive: true });
28669
+ import_fs66.default.mkdirSync(import_path64.default.dirname(flagFile), { recursive: true });
28469
28670
  import_fs66.default.writeFileSync(flagFile, "");
28470
28671
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
28471
28672
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
@@ -28592,7 +28793,7 @@ if (process.argv[2] !== "daemon") {
28592
28793
  const isCheckHook = process.argv[2] === "check";
28593
28794
  if (isCheckHook) {
28594
28795
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
28595
- const logPath = import_path63.default.join(import_os56.default.homedir(), ".node9", "hook-debug.log");
28796
+ const logPath = import_path64.default.join(import_os57.default.homedir(), ".node9", "hook-debug.log");
28596
28797
  const msg = reason instanceof Error ? reason.message : String(reason);
28597
28798
  import_fs66.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
28598
28799
  `);