@node9/proxy 2.43.0 → 2.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -881,12 +881,12 @@ function judgeInlineCode(language, code, isProtected) {
881
881
  category
882
882
  };
883
883
  }
884
- const path44 = protectedLiteral(text, isProtected);
885
- if (path44) {
884
+ const path45 = protectedLiteral(text, isProtected);
885
+ if (path45) {
886
886
  return {
887
887
  outcome: "dangerous",
888
888
  what: WHAT["reads-protected"],
889
- evidence: path44.slice(0, 60),
889
+ evidence: path45.slice(0, 60),
890
890
  category: "reads-protected"
891
891
  };
892
892
  }
@@ -3049,9 +3049,9 @@ function ruleServerMatches(rule, serverLabel) {
3049
3049
  if (rule.notServer && label2 && matchesPattern(label2, rule.notServer)) return false;
3050
3050
  return true;
3051
3051
  }
3052
- function getNestedValue(obj, path44) {
3052
+ function getNestedValue(obj, path45) {
3053
3053
  if (!obj || typeof obj !== "object") return null;
3054
- const segments2 = path44.split(".");
3054
+ const segments2 = path45.split(".");
3055
3055
  for (const seg of segments2) {
3056
3056
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
3057
3057
  }
@@ -4031,8 +4031,8 @@ function collectFileAccesses(tool, args, context = {}, inspection = {}) {
4031
4031
  }
4032
4032
  return out;
4033
4033
  }
4034
- function within(path44, root) {
4035
- return path44 === root || path44.startsWith(root === "/" ? "/" : root + "/");
4034
+ function within(path45, root) {
4035
+ return path45 === root || path45.startsWith(root === "/" ? "/" : root + "/");
4036
4036
  }
4037
4037
  function hits(access, paths, directories, context) {
4038
4038
  const home = context.homeDirs?.[0] ?? "~";
@@ -5420,7 +5420,10 @@ function classify(inv) {
5420
5420
  out.push({ ...f, shown: inv.shown, reason: `${f.reason}: ${inv.shown}` });
5421
5421
  };
5422
5422
  if (hooksBypass(inv)) found(HOOKS_BYPASS);
5423
- if (stagesEverything(inv)) found(STAGE_ALL);
5423
+ const staged = stagesEverything(inv);
5424
+ if (staged) {
5425
+ found(staged.ifWorkTreeTop === void 0 ? STAGE_ALL : { ...STAGE_ALL, ...staged });
5426
+ }
5424
5427
  if (inv.sub === null) {
5425
5428
  found(UNREADABLE);
5426
5429
  return out;
@@ -5481,12 +5484,35 @@ function appliedValue(env, name) {
5481
5484
  const word = f?.Stmts?.[0]?.Cmd?.Args?.[1];
5482
5485
  return word ? resolveWordLiteral(word) : null;
5483
5486
  }
5487
+ function lexicalPath(p) {
5488
+ if (/^[\\/~$]/.test(p) || /^[A-Za-z]:/.test(p)) return null;
5489
+ const parts = [];
5490
+ let climbed = false;
5491
+ for (const part of p.split(/[\\/]+/)) {
5492
+ if (part === "" || part === ".") continue;
5493
+ if (part === "..") {
5494
+ if (parts.length === 0) climbed = true;
5495
+ else parts.pop();
5496
+ } else parts.push(part);
5497
+ }
5498
+ return { parts, climbed };
5499
+ }
5500
+ function hooksPathBypasses(value) {
5501
+ if (value === null || value === void 0) return true;
5502
+ const v = value.trim();
5503
+ if (v === "" || v === "true") return true;
5504
+ const lp = lexicalPath(v);
5505
+ if (lp === null || lp.climbed || lp.parts.length === 0) return true;
5506
+ return lp.parts[0] === ".git" && lp.parts[1] !== "hooks";
5507
+ }
5484
5508
  function hooksBypass(inv) {
5485
- if (inv.config.has("core.hookspath")) return true;
5509
+ if (inv.config.has("core.hookspath") && hooksPathBypasses(inv.config.get("core.hookspath")))
5510
+ return true;
5486
5511
  for (const [name, value] of inv.env) {
5487
5512
  if (name === "GIT_CONFIG_PARAMETERS" && value !== null && /core\.hookspath/i.test(value))
5488
5513
  return true;
5489
- if (/^GIT_CONFIG_KEY_\d+$/.test(name) && value !== null && value.toLowerCase() === "core.hookspath")
5514
+ const key = /^GIT_CONFIG_KEY_(\d+)$/.exec(name);
5515
+ if (key && value !== null && value.toLowerCase() === "core.hookspath" && hooksPathBypasses(inv.env.get(`GIT_CONFIG_VALUE_${key[1]}`)))
5490
5516
  return true;
5491
5517
  }
5492
5518
  for (const name of HOOK_KILL_SWITCHES) {
@@ -5528,7 +5554,10 @@ function configWritesHooksPath(args) {
5528
5554
  }
5529
5555
  const key = args[i];
5530
5556
  if (key === null || key === void 0 || key.toLowerCase() !== "core.hookspath") return false;
5531
- return write || i + 1 < args.length;
5557
+ const unset = args.slice(0, i).some((w) => w === "--unset" || w === "--unset-all") || args[0] === "unset";
5558
+ if (unset) return true;
5559
+ if (i + 1 < args.length) return hooksPathBypasses(args[i + 1]);
5560
+ return write;
5532
5561
  }
5533
5562
  function isHookPath(word) {
5534
5563
  return /(^|[\\/])(\.git[\\/]hooks|\.husky)([\\/]|$)/.test(word);
@@ -5563,31 +5592,49 @@ function chmodDisarmsHook(args, shown) {
5563
5592
  function wholeTree(operand) {
5564
5593
  if (/^:\(top\)\.?$/.test(operand) || operand === ":/" || operand === "*") return true;
5565
5594
  if (operand.startsWith(":")) return false;
5595
+ const lp = lexicalPath(operand);
5596
+ return lp !== null && lp.parts.length === 0;
5597
+ }
5598
+ function chdirPrefix(inv) {
5599
+ const dirs = inv.chdir ?? [];
5600
+ if (dirs.length === 0) return null;
5566
5601
  const parts = [];
5567
- for (const part of operand.split("/")) {
5568
- if (!part || part === ".") continue;
5569
- if (part === "..") parts.pop();
5570
- else parts.push(part);
5602
+ for (const d of dirs) {
5603
+ if (d === null || d === "") return null;
5604
+ const lp = lexicalPath(d);
5605
+ if (lp === null || lp.climbed) return null;
5606
+ parts.push(...lp.parts);
5571
5607
  }
5572
- return parts.length === 0 && !operand.startsWith("/");
5608
+ return parts.length === 0 ? null : parts.join("/");
5573
5609
  }
5574
5610
  function stagesEverything(inv) {
5575
5611
  const sub = inv.sub?.toLowerCase();
5612
+ const prefix = chdirPrefix(inv);
5613
+ const judge = (operands2) => {
5614
+ let conditional = false;
5615
+ for (const operand of operands2) {
5616
+ if (!wholeTree(operand)) continue;
5617
+ if (prefix === null || operand.startsWith(":") || operand.startsWith("/")) return {};
5618
+ if (wholeTree(`${prefix}/${operand === "*" ? "." : operand}`)) return {};
5619
+ conditional = true;
5620
+ }
5621
+ return conditional && prefix !== null ? { ifWorkTreeTop: prefix } : null;
5622
+ };
5576
5623
  if (sub === "add") {
5577
5624
  const o = parseOptions(inv.args, "", ADD_LONG_VALUE);
5578
5625
  if (o.flags.has("-n") || o.flags.has("--dry-run") || o.flags.has("-i") || o.flags.has("--interactive") || o.flags.has("-p") || o.flags.has("--patch") || o.flags.has("-e") || o.flags.has("--edit"))
5579
- return false;
5626
+ return null;
5580
5627
  const all = o.flags.has("-A") || o.flags.has("--all") || o.flags.has("--no-ignore-removal") || o.flags.has("-u") || o.flags.has("--update");
5581
- if (all && o.operands.length === 0) return true;
5582
- return o.operands.some(wholeTree);
5628
+ if (all && o.operands.length === 0) return {};
5629
+ return judge(o.operands);
5583
5630
  }
5584
5631
  if (sub === "commit") {
5585
5632
  const o = parseOptions(inv.args, "mFCcut", COMMIT_LONG_VALUE);
5586
- if (o.flags.has("--dry-run")) return false;
5587
- if (o.flags.has("-a") || o.flags.has("--all")) return true;
5588
- return o.operands.some(wholeTree);
5633
+ if (o.flags.has("--dry-run")) return null;
5634
+ if (o.flags.has("-a") || o.flags.has("--all")) return {};
5635
+ return judge(o.operands);
5589
5636
  }
5590
- return false;
5637
+ return null;
5591
5638
  }
5592
5639
  function hookTextRunsNothing(text) {
5593
5640
  return text.split(/\r?\n/).every((line) => INERT_LINE_RE.test(line.trim()));
@@ -7243,9 +7290,9 @@ function bareToolName(toolName) {
7243
7290
  const parts = toolName.split("__");
7244
7291
  return (parts.length >= 3 ? parts.slice(2).join("__") : toolName).toLowerCase();
7245
7292
  }
7246
- function valuesAt(args, path44) {
7293
+ function valuesAt(args, path45) {
7247
7294
  let cursors = [args];
7248
- for (const rawSegment of path44.split(".")) {
7295
+ for (const rawSegment of path45.split(".")) {
7249
7296
  const isArray = rawSegment.endsWith("[]");
7250
7297
  const key = isArray ? rawSegment.slice(0, -2) : rawSegment;
7251
7298
  const next = [];
@@ -7276,8 +7323,8 @@ function extractToolDestinations(toolName, args) {
7276
7323
  try {
7277
7324
  const out = [];
7278
7325
  const paths = DESTINATION_ARGS.get(bareToolName(toolName)) ?? [];
7279
- for (const path44 of paths) {
7280
- for (const value of valuesAt(args, path44)) {
7326
+ for (const path45 of paths) {
7327
+ for (const value of valuesAt(args, path45)) {
7281
7328
  const host2 = hostOf(value);
7282
7329
  if (host2) out.push({ host: host2, binary: toolName, raw: value });
7283
7330
  }
@@ -7303,8 +7350,8 @@ function ssrfDestinationFloor(toolName, args, opts = {}) {
7303
7350
  try {
7304
7351
  const paths = DESTINATION_ARGS.get(bareToolName(toolName));
7305
7352
  if (!paths) return null;
7306
- for (const path44 of paths) {
7307
- for (const value of valuesAt(args, path44)) {
7353
+ for (const path45 of paths) {
7354
+ for (const value of valuesAt(args, path45)) {
7308
7355
  const host2 = hostOf(value);
7309
7356
  if (!host2) continue;
7310
7357
  const m = classifySsrf(host2);
@@ -7313,7 +7360,7 @@ function ssrfDestinationFloor(toolName, args, opts = {}) {
7313
7360
  const decision = ssrfFloorDecision(m, written, opts);
7314
7361
  if (decision === null) continue;
7315
7362
  const reason = decision === "review" ? ssrfDisguisedReason(m, written) : ssrfReason(m, host2);
7316
- return { ...m, argPath: path44, host: host2, reason, decision };
7363
+ return { ...m, argPath: path45, host: host2, reason, decision };
7317
7364
  }
7318
7365
  }
7319
7366
  return null;
@@ -7595,7 +7642,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hostHooks =
7595
7642
  }
7596
7643
  async function evaluatePolicyCore(config, toolName, args, context = {}, hooks = {}) {
7597
7644
  const { agent, cwd, activeEnvironment } = context;
7598
- const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry } = hooks;
7645
+ const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2, isConfiguredRegistry, isGitWorkTreeTop: isGitWorkTreeTop2 } = hooks;
7599
7646
  const settingsView = catalogSettings(config);
7600
7647
  const layers = inspectionLayers(config.policy);
7601
7648
  const cv = (id) => checkValue(settingsView, id);
@@ -7749,6 +7796,8 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
7749
7796
  const action = cv(id);
7750
7797
  const finding = action === "off" ? void 0 : git.findings.find((f) => f.checkId === id);
7751
7798
  if (!finding) continue;
7799
+ if (finding.ifWorkTreeTop !== void 0 && isGitWorkTreeTop2 && cwd && !isGitWorkTreeTop2(finding.ifWorkTreeTop, cwd))
7800
+ continue;
7752
7801
  const v = applyCheckValue(action, {
7753
7802
  decision: "review",
7754
7803
  blockedByLabel: `Node9 (AST): ${finding.ruleName}`,
@@ -16360,6 +16409,7 @@ zoo`.split("\n");
16360
16409
  /** The words after `git`: global options, then the subcommand. */
16361
16410
  visitGit(rest, env, depth, viaXargs, fromAlias, shown) {
16362
16411
  const config = /* @__PURE__ */ new Map();
16412
+ const chdir = [];
16363
16413
  let i = 0;
16364
16414
  while (i < rest.length) {
16365
16415
  const w = rest[i];
@@ -16374,6 +16424,7 @@ zoo`.split("\n");
16374
16424
  i++;
16375
16425
  value = i < rest.length ? rest[i] : null;
16376
16426
  }
16427
+ if (name === "-C") chdir.push(viaXargs || (value ?? "").includes("{}") ? null : value);
16377
16428
  if (name === "-c" && value !== null) {
16378
16429
  const ceq = value.indexOf("=");
16379
16430
  const key = (ceq > 0 ? value.slice(0, ceq) : value).toLowerCase();
@@ -16390,7 +16441,7 @@ zoo`.split("\n");
16390
16441
  }
16391
16442
  const subWord = i < rest.length ? rest[i] : void 0;
16392
16443
  const args = rest.slice(i + 1);
16393
- const inv = { sub: null, args, config, env, shown };
16444
+ const inv = { sub: null, args, config, env, shown, chdir };
16394
16445
  if (subWord === void 0) {
16395
16446
  if (!viaXargs) return;
16396
16447
  this.emit(inv);
@@ -16404,16 +16455,23 @@ zoo`.split("\n");
16404
16455
  const fromConfig = config.get(`alias.${subWord.toLowerCase()}`);
16405
16456
  const alias = fromConfig !== void 0 ? fromConfig : this.aliases.get(subWord);
16406
16457
  if (alias !== void 0 && !fromAlias) {
16407
- this.expandAlias(subWord, alias, args, env, depth, 0, shown);
16458
+ this.expandAlias(subWord, alias, args, env, depth, 0, shown, chdir);
16408
16459
  return;
16409
16460
  }
16410
16461
  inv.sub = subWord;
16411
16462
  this.emit(inv);
16412
16463
  this.recordAliasDefinition(inv, env, depth);
16413
16464
  }
16414
- expandAlias(name, value, args, env, depth, level, shown) {
16465
+ expandAlias(name, value, args, env, depth, level, shown, chdir) {
16415
16466
  if (value === null) {
16416
- this.emit({ sub: null, args, config: /* @__PURE__ */ new Map(), env, shown: `${shown} (alias ${name})` });
16467
+ this.emit({
16468
+ sub: null,
16469
+ args,
16470
+ config: /* @__PURE__ */ new Map(),
16471
+ env,
16472
+ shown: `${shown} (alias ${name})`,
16473
+ chdir
16474
+ });
16417
16475
  return;
16418
16476
  }
16419
16477
  if (value.startsWith("!")) {
@@ -16427,7 +16485,16 @@ zoo`.split("\n");
16427
16485
  const nested = this.aliases.get(sub);
16428
16486
  const expandedShown = `${shown} (alias ${name} = ${value})`;
16429
16487
  if (nested !== void 0 && level < MAX_ALIAS_DEPTH) {
16430
- this.expandAlias(sub, nested, [...words.slice(1), ...args], env, depth, level + 1, shown);
16488
+ this.expandAlias(
16489
+ sub,
16490
+ nested,
16491
+ [...words.slice(1), ...args],
16492
+ env,
16493
+ depth,
16494
+ level + 1,
16495
+ shown,
16496
+ chdir
16497
+ );
16431
16498
  return;
16432
16499
  }
16433
16500
  this.emit({
@@ -16435,7 +16502,8 @@ zoo`.split("\n");
16435
16502
  args: [...words.slice(1), ...args],
16436
16503
  config: /* @__PURE__ */ new Map(),
16437
16504
  env,
16438
- shown: expandedShown
16505
+ shown: expandedShown,
16506
+ chdir
16439
16507
  });
16440
16508
  }
16441
16509
  /**
@@ -16469,7 +16537,7 @@ zoo`.split("\n");
16469
16537
  if (i + 1 >= args.length) return;
16470
16538
  const value = args[i + 1];
16471
16539
  this.aliases.set(m[1], value);
16472
- this.expandAlias(m[1], value, [], env, depth, 0, `${inv.shown}`);
16540
+ this.expandAlias(m[1], value, [], env, depth, 0, `${inv.shown}`, []);
16473
16541
  }
16474
16542
  };
16475
16543
  POSITIONAL2 = "\0positional";
@@ -17971,7 +18039,7 @@ zoo`.split("\n");
17971
18039
  { view: "separators-stripped", decoder: "separators", stripped: true }
17972
18040
  ];
17973
18041
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
17974
- CANONICAL_EXTRACTOR_VERSION = "canonical-v34";
18042
+ CANONICAL_EXTRACTOR_VERSION = "canonical-v35";
17975
18043
  }
17976
18044
  });
17977
18045
 
@@ -19316,8 +19384,8 @@ var init_daemon = __esm({
19316
19384
  import { z } from "zod";
19317
19385
  function formatIssues(issues) {
19318
19386
  const lines = issues.map((issue) => {
19319
- const path44 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
19320
- return ` \u2022 ${path44}: ${issue.message}`;
19387
+ const path45 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
19388
+ return ` \u2022 ${path45}: ${issue.message}`;
19321
19389
  });
19322
19390
  return `Invalid config:
19323
19391
  ${lines.join("\n")}`;
@@ -19330,9 +19398,9 @@ function prunePaths(root, paths) {
19330
19398
  const yi = y[y.length - 1];
19331
19399
  return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
19332
19400
  });
19333
- for (const path44 of ordered) {
19401
+ for (const path45 of ordered) {
19334
19402
  let cur = root;
19335
- for (const key of path44.slice(0, -1)) {
19403
+ for (const key of path45.slice(0, -1)) {
19336
19404
  if (cur === null || typeof cur !== "object") {
19337
19405
  cur = void 0;
19338
19406
  break;
@@ -19340,7 +19408,7 @@ function prunePaths(root, paths) {
19340
19408
  cur = cur[key];
19341
19409
  }
19342
19410
  if (cur === null || typeof cur !== "object") continue;
19343
- const last = path44[path44.length - 1];
19411
+ const last = path45[path45.length - 1];
19344
19412
  if (Array.isArray(cur)) {
19345
19413
  const i = Number(last);
19346
19414
  if (Number.isInteger(i) && i >= 0 && i < cur.length) {
@@ -19376,7 +19444,7 @@ function sanitizeConfig(raw) {
19376
19444
  return issue.keys.map((k) => [...at, k]);
19377
19445
  }
19378
19446
  return [at.slice(0, -level || void 0)];
19379
- }).filter((path44) => path44.length > 0);
19447
+ }).filter((path45) => path45.length > 0);
19380
19448
  if (paths.length === 0 || !prunePaths(working, paths)) break;
19381
19449
  }
19382
19450
  if (ConfigFileSchema.safeParse(working).success) break;
@@ -20700,13 +20768,13 @@ function getConfig(cwd) {
20700
20768
  }
20701
20769
  if (Array.isArray(mc.jailPaths)) {
20702
20770
  for (const jp of mc.jailPaths) {
20703
- const path44 = typeof jp?.path === "string" ? jp.path.trim() : "";
20704
- if (!path44) continue;
20771
+ const path45 = typeof jp?.path === "string" ? jp.path.trim() : "";
20772
+ if (!path45) continue;
20705
20773
  const verdict = jp?.verdict === "review" ? "review" : "block";
20706
- for (const r of pathRules(path44, verdict, "org-managed jail")) {
20774
+ for (const r of pathRules(path45, verdict, "org-managed jail")) {
20707
20775
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
20708
20776
  }
20709
- mergedPolicy.managedJailPaths.push({ path: path44, verdict });
20777
+ mergedPolicy.managedJailPaths.push({ path: path45, verdict });
20710
20778
  }
20711
20779
  }
20712
20780
  if (Array.isArray(mc.trustedHosts)) {
@@ -24920,6 +24988,7 @@ var init_provenance2 = __esm({
24920
24988
 
24921
24989
  // src/policy/index.ts
24922
24990
  import fs30 from "fs";
24991
+ import path32 from "path";
24923
24992
  async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
24924
24993
  const config = opts?.config ?? getConfig(cwd);
24925
24994
  const prepared = opts?.fileAccess ?? prepareFileAccesses(toolName, args, cwd, inspectionLayers(config.policy));
@@ -24945,10 +25014,19 @@ async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
24945
25014
  // reaches a long-lived in-process authorizer (the gateway) within seconds.
24946
25015
  isTrustedHost: config.policy.trustedHostsManaged ? (host2) => matchesTrustedHost(host2, config.policy.trustedHosts) : isTrustedHost,
24947
25016
  readAgentSettings,
24948
- isConfiguredRegistry: (host2, at) => configuredRegistryHosts(at).has(host2)
25017
+ isConfiguredRegistry: (host2, at) => configuredRegistryHosts(at).has(host2),
25018
+ isGitWorkTreeTop
24949
25019
  }
24950
25020
  );
24951
25021
  }
25022
+ function isGitWorkTreeTop(dir2, cwd) {
25023
+ if (!path32.isAbsolute(cwd)) return true;
25024
+ try {
25025
+ return fs30.existsSync(path32.join(path32.resolve(cwd, dir2), ".git"));
25026
+ } catch {
25027
+ return true;
25028
+ }
25029
+ }
24952
25030
  function readAgentSettings(absPath) {
24953
25031
  const r = readCappedText(
24954
25032
  absPath,
@@ -24983,7 +25061,7 @@ var init_policy = __esm({
24983
25061
 
24984
25062
  // src/auth/state.ts
24985
25063
  import fs31 from "fs";
24986
- import path32 from "path";
25064
+ import path33 from "path";
24987
25065
  import os22 from "os";
24988
25066
  function extractCommandPattern(toolName, args) {
24989
25067
  const lower2 = toolName.toLowerCase();
@@ -25061,7 +25139,7 @@ function writeTrustSession(toolName, durationMs, args) {
25061
25139
  }
25062
25140
  function getPersistentDecision(toolName) {
25063
25141
  try {
25064
- const file2 = path32.join(os22.homedir(), ".node9", "decisions.json");
25142
+ const file2 = path33.join(os22.homedir(), ".node9", "decisions.json");
25065
25143
  if (!fs31.existsSync(file2)) return null;
25066
25144
  const decisions = JSON.parse(fs31.readFileSync(file2, "utf-8"));
25067
25145
  const d = decisions[toolName];
@@ -25077,15 +25155,15 @@ var init_state2 = __esm({
25077
25155
  init_policy();
25078
25156
  init_atomic_write();
25079
25157
  init_forecast_context();
25080
- PAUSED_FILE = path32.join(os22.homedir(), ".node9", "PAUSED");
25081
- TRUST_FILE = path32.join(os22.homedir(), ".node9", "trust.json");
25158
+ PAUSED_FILE = path33.join(os22.homedir(), ".node9", "PAUSED");
25159
+ TRUST_FILE = path33.join(os22.homedir(), ".node9", "trust.json");
25082
25160
  }
25083
25161
  });
25084
25162
 
25085
25163
  // src/auth/cloud.ts
25086
25164
  import fs32 from "fs";
25087
25165
  import os23 from "os";
25088
- import path33 from "path";
25166
+ import path34 from "path";
25089
25167
  function maskArgsForCloud(args) {
25090
25168
  if (args === void 0 || args === null) return args;
25091
25169
  try {
@@ -25118,7 +25196,7 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
25118
25196
  let ciContext;
25119
25197
  if (process.env.CI) {
25120
25198
  try {
25121
- const ciContextPath = path33.join(os23.homedir(), ".node9", "ci-context.json");
25199
+ const ciContextPath = path34.join(os23.homedir(), ".node9", "ci-context.json");
25122
25200
  const ci = readCappedText(ciContextPath, 1e4);
25123
25201
  if (!ci) throw new Error("ci-context.json unreadable");
25124
25202
  if (ci.truncated) throw new Error("ci-context.json exceeds 10 KB");
@@ -25265,10 +25343,10 @@ var init_cloud = __esm({
25265
25343
 
25266
25344
  // src/loop-detector.ts
25267
25345
  import fs33 from "fs";
25268
- import path34 from "path";
25346
+ import path35 from "path";
25269
25347
  import os24 from "os";
25270
25348
  function loopStateFile() {
25271
- return path34.join(os24.homedir(), ".node9", "loop-state.json");
25349
+ return path35.join(os24.homedir(), ".node9", "loop-state.json");
25272
25350
  }
25273
25351
  function readState() {
25274
25352
  try {
@@ -25282,7 +25360,7 @@ function readState() {
25282
25360
  }
25283
25361
  }
25284
25362
  function writeState(records) {
25285
- const dir2 = path34.dirname(loopStateFile());
25363
+ const dir2 = path35.dirname(loopStateFile());
25286
25364
  if (!fs33.existsSync(dir2)) fs33.mkdirSync(dir2, { recursive: true });
25287
25365
  const tmpPath = `${loopStateFile()}.${os24.hostname()}.${process.pid}.tmp`;
25288
25366
  fs33.writeFileSync(tmpPath, JSON.stringify(records));
@@ -25445,7 +25523,7 @@ var init_card_result = __esm({
25445
25523
 
25446
25524
  // src/auth/orchestrator.ts
25447
25525
  import os25 from "os";
25448
- import path35 from "path";
25526
+ import path36 from "path";
25449
25527
  function isProtectedPathRule(ruleName) {
25450
25528
  if (!ruleName) return false;
25451
25529
  return /^(?:org:)?block-path-/.test(ruleName) || /^shield:project-jail:block-read-/.test(ruleName);
@@ -25492,9 +25570,9 @@ function isMcpToolName(toolName) {
25492
25570
  return /^mcp__[^_](?:[^_]|_(?!_))*?__/i.test(toolName);
25493
25571
  }
25494
25572
  function absoluteUploadPath(spelled, cwd) {
25495
- const expanded = spelled === "~" || spelled.startsWith("~/") ? path35.join(os25.homedir(), spelled.slice(1)) : spelled;
25496
- if (path35.isAbsolute(expanded)) return expanded;
25497
- return cwd && path35.isAbsolute(cwd) ? path35.resolve(cwd, expanded) : expanded;
25573
+ const expanded = spelled === "~" || spelled.startsWith("~/") ? path36.join(os25.homedir(), spelled.slice(1)) : spelled;
25574
+ if (path36.isAbsolute(expanded)) return expanded;
25575
+ return cwd && path36.isAbsolute(cwd) ? path36.resolve(cwd, expanded) : expanded;
25498
25576
  }
25499
25577
  function extractFilePaths(toolName, args, shellCommand) {
25500
25578
  const paths = [];
@@ -27111,17 +27189,17 @@ var init_forecast = __esm({
27111
27189
 
27112
27190
  // src/version.ts
27113
27191
  import fs34 from "fs";
27114
- import path36 from "path";
27192
+ import path37 from "path";
27115
27193
  function readVersion() {
27116
27194
  let dir2 = __dirname;
27117
27195
  for (let i = 0; i < 5; i++) {
27118
- const p = path36.join(dir2, "package.json");
27196
+ const p = path37.join(dir2, "package.json");
27119
27197
  try {
27120
27198
  const pkg = JSON.parse(fs34.readFileSync(p, "utf-8"));
27121
27199
  if (pkg.name === "@node9/proxy" && typeof pkg.version === "string") return pkg.version;
27122
27200
  } catch {
27123
27201
  }
27124
- const parent = path36.dirname(dir2);
27202
+ const parent = path37.dirname(dir2);
27125
27203
  if (parent === dir2) break;
27126
27204
  dir2 = parent;
27127
27205
  }
@@ -27138,10 +27216,10 @@ var init_version = __esm({
27138
27216
  // src/cli/commands/scan-cache.ts
27139
27217
  import fs35 from "fs";
27140
27218
  import os26 from "os";
27141
- import path37 from "path";
27219
+ import path38 from "path";
27142
27220
  import { createHash as createHash5 } from "crypto";
27143
27221
  function scanCacheRoot() {
27144
- return path37.join(os26.homedir(), ".node9", "scan-cache");
27222
+ return path38.join(os26.homedir(), ".node9", "scan-cache");
27145
27223
  }
27146
27224
  function configHashOf(config) {
27147
27225
  const h = createHash5("sha256");
@@ -27149,7 +27227,7 @@ function configHashOf(config) {
27149
27227
  h.update(JSON.stringify(config.policySource));
27150
27228
  h.update(JSON.stringify(config.policy));
27151
27229
  for (const name of SIDECAR_FILES) {
27152
- const st = fileStamp(path37.join(os26.homedir(), ".node9", name));
27230
+ const st = fileStamp(path38.join(os26.homedir(), ".node9", name));
27153
27231
  h.update(`|${name}:${st ? `${st.size}:${st.mtimeMs}` : "absent"}`);
27154
27232
  }
27155
27233
  return h.digest("hex").slice(0, 16);
@@ -27180,8 +27258,8 @@ var init_scan_cache = __esm({
27180
27258
  "trusted-hosts.json",
27181
27259
  "trusted-recipients.json",
27182
27260
  "rules-cache.json",
27183
- path37.join("osv", "npm", "meta.json"),
27184
- path37.join("osv", "PyPI", "meta.json")
27261
+ path38.join("osv", "npm", "meta.json"),
27262
+ path38.join("osv", "PyPI", "meta.json")
27185
27263
  ];
27186
27264
  ScanCache = class {
27187
27265
  root;
@@ -27210,7 +27288,7 @@ var init_scan_cache = __esm({
27210
27288
  if (!this.enabled) return fresh;
27211
27289
  try {
27212
27290
  const raw = JSON.parse(
27213
- fs35.readFileSync(path37.join(this.root, "index.json"), "utf8")
27291
+ fs35.readFileSync(path38.join(this.root, "index.json"), "utf8")
27214
27292
  );
27215
27293
  if (raw.cacheVersion !== SCAN_CACHE_VERSION || raw.engineVersion !== CANONICAL_EXTRACTOR_VERSION || raw.proxyVersion !== PROXY_VERSION || raw.configHash !== configHash || typeof raw.files !== "object") {
27216
27294
  this.dropAll();
@@ -27247,7 +27325,7 @@ var init_scan_cache = __esm({
27247
27325
  }
27248
27326
  }
27249
27327
  try {
27250
- const stored = JSON.parse(fs35.readFileSync(path37.join(this.root, e.result), "utf8"));
27328
+ const stored = JSON.parse(fs35.readFileSync(path38.join(this.root, e.result), "utf8"));
27251
27329
  if ((stored.judgedFrom ?? null) !== (e.judgedFrom ?? null) || !stored.result) {
27252
27330
  this.misses++;
27253
27331
  return null;
@@ -27262,13 +27340,13 @@ var init_scan_cache = __esm({
27262
27340
  put(filePath, stamp, agent, result, hasher, judgeFrom = null) {
27263
27341
  if (!this.enabled) return;
27264
27342
  const name = createHash5("sha256").update(`${agent}|${filePath}`).digest("hex").slice(0, 40);
27265
- const rel = path37.join("files", name.slice(0, 2), `${name}.json`);
27343
+ const rel = path38.join("files", name.slice(0, 2), `${name}.json`);
27266
27344
  const configHashes = {};
27267
27345
  if (hasher)
27268
27346
  for (const cwd of result.configCwds ?? []) configHashes[cwd] = hasher.configHashFor(cwd);
27269
27347
  try {
27270
- const abs = path37.join(this.root, rel);
27271
- fs35.mkdirSync(path37.dirname(abs), { recursive: true, mode: 448 });
27348
+ const abs = path38.join(this.root, rel);
27349
+ fs35.mkdirSync(path38.dirname(abs), { recursive: true, mode: 448 });
27272
27350
  fs35.writeFileSync(
27273
27351
  abs,
27274
27352
  JSON.stringify({ judgedFrom: judgeFrom ? judgeFrom.toISOString() : null, result }),
@@ -27294,7 +27372,7 @@ var init_scan_cache = __esm({
27294
27372
  for (const [key, e] of Object.entries(this.index.files)) {
27295
27373
  if (fs35.existsSync(key)) continue;
27296
27374
  try {
27297
- fs35.rmSync(path37.join(this.root, e.result), { force: true });
27375
+ fs35.rmSync(path38.join(this.root, e.result), { force: true });
27298
27376
  } catch {
27299
27377
  }
27300
27378
  delete this.index.files[key];
@@ -27316,9 +27394,9 @@ var init_scan_cache = __esm({
27316
27394
  if (!this.dirty) return;
27317
27395
  try {
27318
27396
  fs35.mkdirSync(this.root, { recursive: true, mode: 448 });
27319
- const tmp = path37.join(this.root, `index.${process.pid}.tmp`);
27397
+ const tmp = path38.join(this.root, `index.${process.pid}.tmp`);
27320
27398
  fs35.writeFileSync(tmp, JSON.stringify(this.index), { mode: 384 });
27321
- fs35.renameSync(tmp, path37.join(this.root, "index.json"));
27399
+ fs35.renameSync(tmp, path38.join(this.root, "index.json"));
27322
27400
  this.dirty = false;
27323
27401
  } catch {
27324
27402
  }
@@ -27330,7 +27408,7 @@ var init_scan_cache = __esm({
27330
27408
  // src/cli/commands/scan-judge.ts
27331
27409
  import fs36 from "fs";
27332
27410
  import os27 from "os";
27333
- import path38 from "path";
27411
+ import path39 from "path";
27334
27412
  function findingDedupKey(f) {
27335
27413
  return `${f.source.rule.name ?? "<unnamed>"}|${preview(f.input)}|${f.project}|${f.repo ?? ""}`;
27336
27414
  }
@@ -27429,20 +27507,20 @@ var init_scan_judge = __esm({
27429
27507
  * file, so worktrees count), home-relative; undefined when none. Cached per
27430
27508
  * directory: history repeats the same few cwds thousands of times. */
27431
27509
  repoOf(cwd) {
27432
- if (!cwd || !path38.isAbsolute(cwd)) return void 0;
27510
+ if (!cwd || !path39.isAbsolute(cwd)) return void 0;
27433
27511
  if (this.repos.has(cwd)) return this.repos.get(cwd);
27434
27512
  let dir2 = cwd;
27435
27513
  let found;
27436
27514
  for (; ; ) {
27437
27515
  try {
27438
- if (fs36.existsSync(path38.join(dir2, ".git"))) {
27516
+ if (fs36.existsSync(path39.join(dir2, ".git"))) {
27439
27517
  found = dir2;
27440
27518
  break;
27441
27519
  }
27442
27520
  } catch {
27443
27521
  break;
27444
27522
  }
27445
- const parent = path38.dirname(dir2);
27523
+ const parent = path39.dirname(dir2);
27446
27524
  if (parent === dir2) break;
27447
27525
  dir2 = parent;
27448
27526
  }
@@ -27609,7 +27687,7 @@ var init_scan_summary = __esm({
27609
27687
 
27610
27688
  // src/agent-wiring.ts
27611
27689
  import fs37 from "fs";
27612
- import path39 from "path";
27690
+ import path40 from "path";
27613
27691
  import * as yaml from "yaml";
27614
27692
  import { parse as parseToml } from "smol-toml";
27615
27693
  var exists, ck, lg, AGENT_SPECS;
@@ -27631,55 +27709,55 @@ var init_agent_wiring = __esm({
27631
27709
  id: "claude",
27632
27710
  label: "Claude Code",
27633
27711
  setupCommand: "node9 agents add claude",
27634
- hookFile: (h) => path39.join(h, ".claude", "settings.json"),
27712
+ hookFile: (h) => path40.join(h, ".claude", "settings.json"),
27635
27713
  hookFormat: "matcher",
27636
27714
  // UserPromptSubmit is prompt DLP. setup.ts has written it for Claude since
27637
27715
  // that shipped; the spec must name it too, or status/doctor never show the
27638
27716
  // row and heal (which repairs via setupAgent) has no signal it is missing.
27639
27717
  hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
27640
- mcpFile: (h) => path39.join(h, ".claude.json"),
27641
- present: (h) => exists(path39.join(h, ".claude", "settings.json")) || exists(path39.join(h, ".claude.json"))
27718
+ mcpFile: (h) => path40.join(h, ".claude.json"),
27719
+ present: (h) => exists(path40.join(h, ".claude", "settings.json")) || exists(path40.join(h, ".claude.json"))
27642
27720
  },
27643
27721
  {
27644
27722
  id: "gemini",
27645
27723
  label: "Gemini CLI",
27646
27724
  setupCommand: "node9 agents add gemini",
27647
- hookFile: (h) => path39.join(h, ".gemini", "settings.json"),
27725
+ hookFile: (h) => path40.join(h, ".gemini", "settings.json"),
27648
27726
  hookFormat: "matcher",
27649
27727
  hookEvents: [ck("BeforeTool"), lg("AfterTool")],
27650
- mcpFile: (h) => path39.join(h, ".gemini", "settings.json"),
27651
- present: (h) => exists(path39.join(h, ".gemini", "settings.json"))
27728
+ mcpFile: (h) => path40.join(h, ".gemini", "settings.json"),
27729
+ present: (h) => exists(path40.join(h, ".gemini", "settings.json"))
27652
27730
  },
27653
27731
  {
27654
27732
  id: "codex",
27655
27733
  label: "Codex",
27656
27734
  setupCommand: "node9 agents add codex",
27657
- hookFile: (h) => path39.join(h, ".codex", "hooks.json"),
27735
+ hookFile: (h) => path40.join(h, ".codex", "hooks.json"),
27658
27736
  hookFormat: "matcher",
27659
27737
  hookEvents: [ck("PreToolUse"), ck("UserPromptSubmit")],
27660
- mcpFile: (h) => path39.join(h, ".codex", "config.toml"),
27738
+ mcpFile: (h) => path40.join(h, ".codex", "config.toml"),
27661
27739
  mcpFormat: "toml",
27662
- present: (h) => exists(path39.join(h, ".codex"))
27740
+ present: (h) => exists(path40.join(h, ".codex"))
27663
27741
  },
27664
27742
  {
27665
27743
  id: "antigravity",
27666
27744
  label: "Antigravity",
27667
27745
  setupCommand: "node9 agents add antigravity",
27668
- hookFile: (h) => path39.join(h, ".gemini", "config", "hooks.json"),
27746
+ hookFile: (h) => path40.join(h, ".gemini", "config", "hooks.json"),
27669
27747
  hookFormat: "matcher",
27670
27748
  hookEvents: [ck("PreToolUse"), lg("PostToolUse")],
27671
- mcpFile: (h) => path39.join(h, ".gemini", "config", "mcp_config.json"),
27672
- present: (h) => exists(path39.join(h, ".gemini", "config", "hooks.json")) || exists(path39.join(h, ".gemini", "antigravity-cli")) || exists(path39.join(h, ".gemini", "antigravity-ide"))
27749
+ mcpFile: (h) => path40.join(h, ".gemini", "config", "mcp_config.json"),
27750
+ present: (h) => exists(path40.join(h, ".gemini", "config", "hooks.json")) || exists(path40.join(h, ".gemini", "antigravity-cli")) || exists(path40.join(h, ".gemini", "antigravity-ide"))
27673
27751
  },
27674
27752
  {
27675
27753
  id: "copilot",
27676
27754
  label: "GitHub Copilot",
27677
27755
  setupCommand: "node9 agents add copilot",
27678
- hookFile: (h) => path39.join(h, ".copilot", "hooks", "node9.json"),
27756
+ hookFile: (h) => path40.join(h, ".copilot", "hooks", "node9.json"),
27679
27757
  hookFormat: "flat",
27680
27758
  hookEvents: [ck("PreToolUse"), lg("PostToolUse"), ck("UserPromptSubmit")],
27681
- mcpFile: (h) => path39.join(h, ".copilot", "mcp-config.json"),
27682
- present: (h) => exists(path39.join(h, ".copilot"))
27759
+ mcpFile: (h) => path40.join(h, ".copilot", "mcp-config.json"),
27760
+ present: (h) => exists(path40.join(h, ".copilot"))
27683
27761
  },
27684
27762
  {
27685
27763
  id: "cursor",
@@ -27688,8 +27766,8 @@ var init_agent_wiring = __esm({
27688
27766
  // MCP-only — no hook file (see note above).
27689
27767
  hookFormat: "flat",
27690
27768
  hookEvents: [],
27691
- mcpFile: (h) => path39.join(h, ".cursor", "mcp.json"),
27692
- present: (h) => exists(path39.join(h, ".cursor", "mcp.json"))
27769
+ mcpFile: (h) => path40.join(h, ".cursor", "mcp.json"),
27770
+ present: (h) => exists(path40.join(h, ".cursor", "mcp.json"))
27693
27771
  },
27694
27772
  {
27695
27773
  id: "hermes",
@@ -27710,8 +27788,8 @@ var init_agent_wiring = __esm({
27710
27788
  setupCommand: "node9 agents add opencode",
27711
27789
  hookFormat: "flat",
27712
27790
  hookEvents: [],
27713
- shimFile: (h) => path39.join(opencodeConfigDir(h), "plugins", "node9.js"),
27714
- present: (h) => exists(opencodeConfigDir(h)) || exists(path39.join(opencodeConfigDir(h), "plugins", "node9.js"))
27791
+ shimFile: (h) => path40.join(opencodeConfigDir(h), "plugins", "node9.js"),
27792
+ present: (h) => exists(opencodeConfigDir(h)) || exists(path40.join(opencodeConfigDir(h), "plugins", "node9.js"))
27715
27793
  },
27716
27794
  {
27717
27795
  id: "pi",
@@ -27719,8 +27797,8 @@ var init_agent_wiring = __esm({
27719
27797
  setupCommand: "node9 agents add pi",
27720
27798
  hookFormat: "flat",
27721
27799
  hookEvents: [],
27722
- shimFile: (h) => path39.join(h, ".pi", "agent", "extensions", "node9.js"),
27723
- present: (h) => exists(path39.join(h, ".pi", "agent")) || exists(path39.join(h, ".pi", "agent", "extensions", "node9.js"))
27800
+ shimFile: (h) => path40.join(h, ".pi", "agent", "extensions", "node9.js"),
27801
+ present: (h) => exists(path40.join(h, ".pi", "agent")) || exists(path40.join(h, ".pi", "agent", "extensions", "node9.js"))
27724
27802
  }
27725
27803
  ];
27726
27804
  }
@@ -27771,14 +27849,14 @@ var init_codex_trust = __esm({
27771
27849
  });
27772
27850
 
27773
27851
  // src/daemon/hook-baseline.ts
27774
- import path40 from "path";
27852
+ import path41 from "path";
27775
27853
  import os28 from "os";
27776
27854
  var BASELINE_FILE, NOTIFIED_FILE;
27777
27855
  var init_hook_baseline = __esm({
27778
27856
  "src/daemon/hook-baseline.ts"() {
27779
27857
  "use strict";
27780
- BASELINE_FILE = path40.join(os28.homedir(), ".node9", "hooks-baseline.json");
27781
- NOTIFIED_FILE = path40.join(os28.homedir(), ".node9", "hook-heal-notified.json");
27858
+ BASELINE_FILE = path41.join(os28.homedir(), ".node9", "hooks-baseline.json");
27859
+ NOTIFIED_FILE = path41.join(os28.homedir(), ".node9", "hook-heal-notified.json");
27782
27860
  }
27783
27861
  });
27784
27862
 
@@ -27797,7 +27875,7 @@ var init_setup_pi_shim = __esm({
27797
27875
  });
27798
27876
 
27799
27877
  // src/setup.ts
27800
- import path41 from "path";
27878
+ import path42 from "path";
27801
27879
  import os29 from "os";
27802
27880
  import chalk3 from "chalk";
27803
27881
  import { confirm as rawConfirm } from "@inquirer/prompts";
@@ -27805,16 +27883,16 @@ import { parse as parseToml5, stringify as stringifyToml2 } from "smol-toml";
27805
27883
  import * as yaml2 from "yaml";
27806
27884
  function opencodeConfigDir(home = os29.homedir()) {
27807
27885
  const xdg = process.env.XDG_CONFIG_HOME;
27808
- const base2 = xdg && path41.isAbsolute(xdg) ? xdg : path41.join(home, ".config");
27809
- return path41.join(base2, "opencode");
27886
+ const base2 = xdg && path42.isAbsolute(xdg) ? xdg : path42.join(home, ".config");
27887
+ return path42.join(base2, "opencode");
27810
27888
  }
27811
27889
  function hermesHomeDir(homeDir = os29.homedir()) {
27812
27890
  const env = process.env.HERMES_HOME?.trim();
27813
- if (env && path41.isAbsolute(env)) return env;
27814
- return path41.join(homeDir, ".hermes");
27891
+ if (env && path42.isAbsolute(env)) return env;
27892
+ return path42.join(homeDir, ".hermes");
27815
27893
  }
27816
27894
  function hermesConfigPath(homeDir = os29.homedir()) {
27817
- return path41.join(hermesHomeDir(homeDir), HERMES_CONFIG_FILENAME);
27895
+ return path42.join(hermesHomeDir(homeDir), HERMES_CONFIG_FILENAME);
27818
27896
  }
27819
27897
  var HERMES_CONFIG_FILENAME;
27820
27898
  var init_setup = __esm({
@@ -27860,7 +27938,7 @@ var init_scan_history = __esm({
27860
27938
  // src/cli/commands/scan.ts
27861
27939
  import chalk5 from "chalk";
27862
27940
  import fs38 from "fs";
27863
- import path42 from "path";
27941
+ import path43 from "path";
27864
27942
  import os30 from "os";
27865
27943
  import stringWidth2 from "string-width";
27866
27944
  function dayKey(ts) {
@@ -28074,7 +28152,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
28074
28152
  const session = { sessionId, costUSD: 0, toolCalls: 0 };
28075
28153
  let raw;
28076
28154
  try {
28077
- raw = fs38.readFileSync(path42.join(projPath, file2), "utf-8");
28155
+ raw = fs38.readFileSync(path43.join(projPath, file2), "utf-8");
28078
28156
  } catch {
28079
28157
  return false;
28080
28158
  }
@@ -28143,7 +28221,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
28143
28221
  if (block.type !== "tool_result") continue;
28144
28222
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
28145
28223
  if (filePath) {
28146
- const ext = path42.extname(filePath).toLowerCase();
28224
+ const ext = path43.extname(filePath).toLowerCase();
28147
28225
  if (CODE_EXTENSIONS.has(ext)) continue;
28148
28226
  }
28149
28227
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -28263,7 +28341,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
28263
28341
  return true;
28264
28342
  }
28265
28343
  async function processClaudeProjectAsync(proj, projectsDir, judge, cache7, startDate, result, dedup, canaryVals, onProgress, onLine) {
28266
- const projPath = path42.join(projectsDir, proj);
28344
+ const projPath = path43.join(projectsDir, proj);
28267
28345
  try {
28268
28346
  if (!fs38.statSync(projPath).isDirectory()) return;
28269
28347
  } catch {
@@ -28277,7 +28355,7 @@ async function processClaudeProjectAsync(proj, projectsDir, judge, cache7, start
28277
28355
  return;
28278
28356
  }
28279
28357
  for (const file2 of files) {
28280
- const filePath = path42.join(projPath, file2);
28358
+ const filePath = path43.join(projPath, file2);
28281
28359
  const stamp = fileStamp(filePath);
28282
28360
  if (!fileMayHoldWindow(stamp, startDate)) continue;
28283
28361
  result.filesScanned++;
@@ -28420,7 +28498,7 @@ function mergeFileScan(result, file2, dedup, startDate = null) {
28420
28498
  }
28421
28499
  }
28422
28500
  async function scanClaudeHistoryAsync(startDate, onProgress, onLine, opts = {}) {
28423
- const projectsDir = path42.join(os30.homedir(), ".claude", "projects");
28501
+ const projectsDir = path43.join(os30.homedir(), ".claude", "projects");
28424
28502
  const result = emptyClaudeScan();
28425
28503
  if (!fs38.existsSync(projectsDir)) return result;
28426
28504
  let projDirs;
@@ -28453,7 +28531,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine, opts = {})
28453
28531
  }
28454
28532
  async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
28455
28533
  const canaryVals = safeCanaryScanValues();
28456
- const tmpDir = path42.join(os30.homedir(), ".gemini", "tmp");
28534
+ const tmpDir = path43.join(os30.homedir(), ".gemini", "tmp");
28457
28535
  const result = {
28458
28536
  filesScanned: 0,
28459
28537
  sessions: 0,
@@ -28480,7 +28558,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
28480
28558
  const judge = new HistoryJudge();
28481
28559
  const cache7 = openScanCache(judge, opts);
28482
28560
  for (const slug2 of slugDirs) {
28483
- const slugPath = path42.join(tmpDir, slug2);
28561
+ const slugPath = path43.join(tmpDir, slug2);
28484
28562
  try {
28485
28563
  if (!fs38.statSync(slugPath).isDirectory()) continue;
28486
28564
  } catch {
@@ -28489,11 +28567,11 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
28489
28567
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
28490
28568
  try {
28491
28569
  projLabel = stripTerminalEscapes(
28492
- fs38.readFileSync(path42.join(slugPath, ".project_root"), "utf-8").trim()
28570
+ fs38.readFileSync(path43.join(slugPath, ".project_root"), "utf-8").trim()
28493
28571
  ).replace(os30.homedir(), "~").slice(0, 40);
28494
28572
  } catch {
28495
28573
  }
28496
- const chatsDir = path42.join(slugPath, "chats");
28574
+ const chatsDir = path43.join(slugPath, "chats");
28497
28575
  if (!fs38.existsSync(chatsDir)) continue;
28498
28576
  let chatFiles;
28499
28577
  try {
@@ -28503,7 +28581,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
28503
28581
  }
28504
28582
  for (const chatFile of chatFiles) {
28505
28583
  const sessionId = chatFile.replace(/\.jsonl?$/, "");
28506
- const filePath = path42.join(chatsDir, chatFile);
28584
+ const filePath = path43.join(chatsDir, chatFile);
28507
28585
  const stamp = fileStamp(filePath);
28508
28586
  if (!fileMayHoldWindow(stamp, startDate)) continue;
28509
28587
  result.filesScanned++;
@@ -28517,7 +28595,7 @@ async function scanGeminiHistory(startDate, onProgress, onLine, opts = {}) {
28517
28595
  const fileDedup = emptyScanDedup();
28518
28596
  let raw;
28519
28597
  try {
28520
- raw = fs38.readFileSync(path42.join(chatsDir, chatFile), "utf-8");
28598
+ raw = fs38.readFileSync(path43.join(chatsDir, chatFile), "utf-8");
28521
28599
  } catch {
28522
28600
  continue;
28523
28601
  }
@@ -28712,7 +28790,7 @@ async function scanCodexHistory(startDate, onProgress, onLine, opts = {}) {
28712
28790
  startTime = String(payload["timestamp"] ?? "");
28713
28791
  const cwd = String(payload["cwd"] ?? "");
28714
28792
  projLabel = stripTerminalEscapes(cwd.replace(os30.homedir(), "~")).slice(0, 40);
28715
- if (path42.isAbsolute(cwd)) sessionCwd = cwd;
28793
+ if (path43.isAbsolute(cwd)) sessionCwd = cwd;
28716
28794
  continue;
28717
28795
  }
28718
28796
  if (entry.type === "event_msg" && payload["type"] === "user_message") {
@@ -28912,10 +28990,10 @@ var init_scan = __esm({
28912
28990
  // src/tui/dashboard/data.ts
28913
28991
  import fs39 from "fs";
28914
28992
  import os31 from "os";
28915
- import path43 from "path";
28993
+ import path44 from "path";
28916
28994
  import http2 from "http";
28917
28995
  function auditLogPath() {
28918
- return path43.join(os31.homedir(), ".node9", "audit.log");
28996
+ return path44.join(os31.homedir(), ".node9", "audit.log");
28919
28997
  }
28920
28998
  function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
28921
28999
  return new Promise((resolve) => {
@@ -29062,9 +29140,9 @@ function shortenPath(p) {
29062
29140
  return p.startsWith(home) ? p.replace(home, "~") : p;
29063
29141
  }
29064
29142
  async function loadReportAuditAsync(period) {
29065
- const claudeProjectsDir = path43.join(os31.homedir(), ".claude", "projects");
29143
+ const claudeProjectsDir = path44.join(os31.homedir(), ".claude", "projects");
29066
29144
  const codexSessionsDir2 = codexSessionsDir();
29067
- const geminiTmpDir = path43.join(os31.homedir(), ".gemini", "tmp");
29145
+ const geminiTmpDir = path44.join(os31.homedir(), ".gemini", "tmp");
29068
29146
  const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
29069
29147
  const entries = await readAuditEntriesAsync();
29070
29148
  void ensurePricingLoaded();
@@ -30176,8 +30254,8 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-run
30176
30254
  function TopToolsProjects({ audit }) {
30177
30255
  const data = audit?.data;
30178
30256
  const tools = data ? [...data.toolMap.entries()].sort(([, a], [, b]) => b.calls - a.calls).slice(0, ROW_LIMIT) : [];
30179
- const projects = data ? [...data.cost.byProject.entries()].map(([path44, r]) => ({
30180
- name: basenameOf(path44),
30257
+ const projects = data ? [...data.cost.byProject.entries()].map(([path45, r]) => ({
30258
+ name: basenameOf(path45),
30181
30259
  cost: r.cost,
30182
30260
  tokens: r.inputTokens + r.outputTokens
30183
30261
  })).sort((a, b) => b.cost - a.cost).slice(0, ROW_LIMIT) : [];
@@ -30614,8 +30692,8 @@ function pickTopLoopFile(loops) {
30614
30692
  map.set(k, (map.get(k) ?? 0) + (l.count ?? 0));
30615
30693
  }
30616
30694
  if (map.size === 0) return void 0;
30617
- const [path44, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
30618
- return { path: path44, count };
30695
+ const [path45, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
30696
+ return { path: path45, count };
30619
30697
  }
30620
30698
  var EMPTY_FILTERED_SCAN;
30621
30699
  var init_derive = __esm({