@node9/proxy 2.6.2 → 2.7.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.
@@ -228,9 +228,9 @@ function matchesPattern(text, patterns) {
228
228
  const withoutDotSlash = text.replace(/^\.\//, "");
229
229
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
230
230
  }
231
- function getNestedValue(obj, path12) {
231
+ function getNestedValue(obj, path13) {
232
232
  if (!obj || typeof obj !== "object") return null;
233
- const segments = path12.split(".");
233
+ const segments = path13.split(".");
234
234
  for (const seg of segments) {
235
235
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
236
236
  }
@@ -2333,8 +2333,8 @@ function sanitizeConfig(raw) {
2333
2333
  }
2334
2334
  }
2335
2335
  const lines = result.error.issues.map((issue) => {
2336
- const path12 = issue.path.length > 0 ? issue.path.join(".") : "root";
2337
- return ` \u2022 ${path12}: ${issue.message}`;
2336
+ const path13 = issue.path.length > 0 ? issue.path.join(".") : "root";
2337
+ return ` \u2022 ${path13}: ${issue.message}`;
2338
2338
  });
2339
2339
  return {
2340
2340
  sanitized,
@@ -3213,13 +3213,13 @@ function getConfig(cwd) {
3213
3213
  }
3214
3214
  if (Array.isArray(mc.jailPaths)) {
3215
3215
  for (const jp of mc.jailPaths) {
3216
- const path12 = typeof jp?.path === "string" ? jp.path.trim() : "";
3217
- if (!path12) continue;
3216
+ const path13 = typeof jp?.path === "string" ? jp.path.trim() : "";
3217
+ if (!path13) continue;
3218
3218
  const verdict = jp?.verdict === "review" ? "review" : "block";
3219
- for (const r of pathRules(path12, verdict, "org-managed jail")) {
3219
+ for (const r of pathRules(path13, verdict, "org-managed jail")) {
3220
3220
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
3221
3221
  }
3222
- mergedPolicy.managedJailPaths.push({ path: path12, verdict });
3222
+ mergedPolicy.managedJailPaths.push({ path: path13, verdict });
3223
3223
  }
3224
3224
  }
3225
3225
  if (Array.isArray(mc.trustedHosts)) {
@@ -3718,14 +3718,15 @@ import os6 from "os";
3718
3718
  function normalizeModel(raw) {
3719
3719
  return raw.replace(/-\d{8}$/, "").toLowerCase();
3720
3720
  }
3721
- function readCache() {
3721
+ function readCache(opts) {
3722
3722
  try {
3723
3723
  const raw = JSON.parse(fs5.readFileSync(CACHE_FILE(), "utf-8"));
3724
3724
  if (typeof raw.fetchedAt !== "string" || typeof raw.prices !== "object" || raw.prices === null) {
3725
3725
  return null;
3726
3726
  }
3727
3727
  const ageMs = Date.now() - new Date(raw.fetchedAt).getTime();
3728
- if (ageMs < 0 || ageMs > TTL_MS) return null;
3728
+ if (!Number.isFinite(ageMs) || ageMs < 0) return null;
3729
+ if (opts.requireFresh && ageMs > TTL_MS) return null;
3729
3730
  return raw.prices;
3730
3731
  } catch {
3731
3732
  return null;
@@ -3791,7 +3792,7 @@ async function fetchLiteLLMPricing() {
3791
3792
  }
3792
3793
  async function ensurePricingLoaded() {
3793
3794
  if (memCache !== null && Date.now() - memCacheAt < TTL_MS) return;
3794
- const fromDisk = readCache();
3795
+ const fromDisk = readCache({ requireFresh: true });
3795
3796
  if (fromDisk && Object.keys(fromDisk).length > 0) {
3796
3797
  memCache = fromDisk;
3797
3798
  memCacheAt = Date.now();
@@ -3816,7 +3817,7 @@ function pricingFor(model) {
3816
3817
  if (cached !== void 0) return cached;
3817
3818
  if (memCache === null && !diskChecked) {
3818
3819
  diskChecked = true;
3819
- const disk = readCache();
3820
+ const disk = readCache({ requireFresh: false });
3820
3821
  if (disk && Object.keys(disk).length > 0) {
3821
3822
  memCache = disk;
3822
3823
  memCacheAt = Date.now();
@@ -3945,6 +3946,37 @@ var init_cost_copilot = __esm({
3945
3946
  }
3946
3947
  });
3947
3948
 
3949
+ // src/session-files.ts
3950
+ import * as fs6 from "fs";
3951
+ import * as path7 from "path";
3952
+ function listSessionFiles(dir, maxDepth = 6) {
3953
+ const out = [];
3954
+ const walk = (d, rel, depth) => {
3955
+ if (depth > maxDepth) return;
3956
+ let entries;
3957
+ try {
3958
+ entries = fs6.readdirSync(d, { withFileTypes: true });
3959
+ } catch {
3960
+ return;
3961
+ }
3962
+ for (const e of entries) {
3963
+ const childRel = rel ? path7.join(rel, e.name) : e.name;
3964
+ if (e.isDirectory()) walk(path7.join(d, e.name), childRel, depth + 1);
3965
+ else if (e.name.endsWith(".jsonl")) out.push(childRel);
3966
+ }
3967
+ };
3968
+ walk(dir, "", 0);
3969
+ return out;
3970
+ }
3971
+ function sessionIdOf(relPath) {
3972
+ return path7.basename(relPath).replace(/\.jsonl$/, "");
3973
+ }
3974
+ var init_session_files = __esm({
3975
+ "src/session-files.ts"() {
3976
+ "use strict";
3977
+ }
3978
+ });
3979
+
3948
3980
  // src/costSync.ts
3949
3981
  function decodeProjectDirName(dirName) {
3950
3982
  return dirName.replace(/-/g, "/");
@@ -3959,6 +3991,7 @@ var init_costSync = __esm({
3959
3991
  init_cost_codex();
3960
3992
  init_cost_gemini();
3961
3993
  init_cost_copilot();
3994
+ init_session_files();
3962
3995
  SYNC_INTERVAL_MS = 10 * 60 * 1e3;
3963
3996
  }
3964
3997
  });
@@ -4031,9 +4064,9 @@ var init_scan_watermark = __esm({
4031
4064
  });
4032
4065
 
4033
4066
  // src/cli/aggregate/report-audit.ts
4034
- import fs6 from "fs";
4067
+ import fs7 from "fs";
4035
4068
  import os7 from "os";
4036
- import path7 from "path";
4069
+ import path8 from "path";
4037
4070
  function buildTestTimestamps(allEntries) {
4038
4071
  const testTs = /* @__PURE__ */ new Set();
4039
4072
  for (const e of allEntries) {
@@ -4114,8 +4147,8 @@ function getDateRange(period, now) {
4114
4147
  }
4115
4148
  }
4116
4149
  function parseAuditLog(logPath) {
4117
- if (!fs6.existsSync(logPath)) return [];
4118
- const raw = fs6.readFileSync(logPath, "utf-8");
4150
+ if (!fs7.existsSync(logPath)) return [];
4151
+ const raw = fs7.readFileSync(logPath, "utf-8");
4119
4152
  return raw.split("\n").flatMap((line) => {
4120
4153
  if (!line.trim()) return [];
4121
4154
  try {
@@ -4169,25 +4202,25 @@ function freezeClaudeCost(acc) {
4169
4202
  };
4170
4203
  }
4171
4204
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
4172
- const projPath = path7.join(projectsDir, proj);
4205
+ const projPath = path8.join(projectsDir, proj);
4173
4206
  let files;
4174
4207
  try {
4175
- const stat = fs6.statSync(projPath);
4208
+ const stat = fs7.statSync(projPath);
4176
4209
  if (!stat.isDirectory()) return;
4177
- files = fs6.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
4210
+ files = listSessionFiles(projPath);
4178
4211
  } catch {
4179
4212
  return;
4180
4213
  }
4181
4214
  const startMs = start.getTime();
4182
4215
  for (const file of files) {
4183
- const filePath = path7.join(projPath, file);
4216
+ const filePath = path8.join(projPath, file);
4184
4217
  try {
4185
- if (fs6.statSync(filePath).mtimeMs < startMs) continue;
4218
+ if (fs7.statSync(filePath).mtimeMs < startMs) continue;
4186
4219
  } catch {
4187
4220
  continue;
4188
4221
  }
4189
4222
  try {
4190
- const raw = fs6.readFileSync(filePath, "utf-8");
4223
+ const raw = fs7.readFileSync(filePath, "utf-8");
4191
4224
  for (const line of raw.split("\n")) {
4192
4225
  if (!line.trim()) continue;
4193
4226
  let entry;
@@ -4237,10 +4270,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
4237
4270
  }
4238
4271
  function loadClaudeCost(start, end, projectsDir) {
4239
4272
  const acc = emptyClaudeCostAccumulator();
4240
- if (!fs6.existsSync(projectsDir)) return freezeClaudeCost(acc);
4273
+ if (!fs7.existsSync(projectsDir)) return freezeClaudeCost(acc);
4241
4274
  let dirs;
4242
4275
  try {
4243
- dirs = fs6.readdirSync(projectsDir);
4276
+ dirs = fs7.readdirSync(projectsDir);
4244
4277
  } catch {
4245
4278
  return freezeClaudeCost(acc);
4246
4279
  }
@@ -4251,10 +4284,10 @@ function loadClaudeCost(start, end, projectsDir) {
4251
4284
  }
4252
4285
  async function loadClaudeCostAsync(start, end, projectsDir) {
4253
4286
  const acc = emptyClaudeCostAccumulator();
4254
- if (!fs6.existsSync(projectsDir)) return freezeClaudeCost(acc);
4287
+ if (!fs7.existsSync(projectsDir)) return freezeClaudeCost(acc);
4255
4288
  let dirs;
4256
4289
  try {
4257
- dirs = fs6.readdirSync(projectsDir);
4290
+ dirs = fs7.readdirSync(projectsDir);
4258
4291
  } catch {
4259
4292
  return freezeClaudeCost(acc);
4260
4293
  }
@@ -4267,7 +4300,7 @@ async function loadClaudeCostAsync(start, end, projectsDir) {
4267
4300
  function processCodexCostFile(filePath, start, end, acc) {
4268
4301
  let lines;
4269
4302
  try {
4270
- lines = fs6.readFileSync(filePath, "utf-8").split("\n");
4303
+ lines = fs7.readFileSync(filePath, "utf-8").split("\n");
4271
4304
  } catch {
4272
4305
  return;
4273
4306
  }
@@ -4322,31 +4355,31 @@ function processCodexCostFile(filePath, start, end, acc) {
4322
4355
  }
4323
4356
  function listCodexSessionFiles(sessionsBase) {
4324
4357
  const jsonlFiles = [];
4325
- if (!fs6.existsSync(sessionsBase)) return jsonlFiles;
4358
+ if (!fs7.existsSync(sessionsBase)) return jsonlFiles;
4326
4359
  try {
4327
- for (const year of fs6.readdirSync(sessionsBase)) {
4328
- const yearPath = path7.join(sessionsBase, year);
4360
+ for (const year of fs7.readdirSync(sessionsBase)) {
4361
+ const yearPath = path8.join(sessionsBase, year);
4329
4362
  try {
4330
- if (!fs6.statSync(yearPath).isDirectory()) continue;
4363
+ if (!fs7.statSync(yearPath).isDirectory()) continue;
4331
4364
  } catch {
4332
4365
  continue;
4333
4366
  }
4334
- for (const month of fs6.readdirSync(yearPath)) {
4335
- const monthPath = path7.join(yearPath, month);
4367
+ for (const month of fs7.readdirSync(yearPath)) {
4368
+ const monthPath = path8.join(yearPath, month);
4336
4369
  try {
4337
- if (!fs6.statSync(monthPath).isDirectory()) continue;
4370
+ if (!fs7.statSync(monthPath).isDirectory()) continue;
4338
4371
  } catch {
4339
4372
  continue;
4340
4373
  }
4341
- for (const day of fs6.readdirSync(monthPath)) {
4342
- const dayPath = path7.join(monthPath, day);
4374
+ for (const day of fs7.readdirSync(monthPath)) {
4375
+ const dayPath = path8.join(monthPath, day);
4343
4376
  try {
4344
- if (!fs6.statSync(dayPath).isDirectory()) continue;
4377
+ if (!fs7.statSync(dayPath).isDirectory()) continue;
4345
4378
  } catch {
4346
4379
  continue;
4347
4380
  }
4348
- for (const file of fs6.readdirSync(dayPath)) {
4349
- if (file.endsWith(".jsonl")) jsonlFiles.push(path7.join(dayPath, file));
4381
+ for (const file of fs7.readdirSync(dayPath)) {
4382
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path8.join(dayPath, file));
4350
4383
  }
4351
4384
  }
4352
4385
  }
@@ -4427,13 +4460,13 @@ function freezeGeminiCost(acc) {
4427
4460
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
4428
4461
  const startMs = start.getTime();
4429
4462
  try {
4430
- if (fs6.statSync(filePath).mtimeMs < startMs) return;
4463
+ if (fs7.statSync(filePath).mtimeMs < startMs) return;
4431
4464
  } catch {
4432
4465
  return;
4433
4466
  }
4434
4467
  let raw;
4435
4468
  try {
4436
- raw = fs6.readFileSync(filePath, "utf-8");
4469
+ raw = fs7.readFileSync(filePath, "utf-8");
4437
4470
  } catch {
4438
4471
  return;
4439
4472
  }
@@ -4482,30 +4515,30 @@ function listGeminiSessionFiles(geminiTmpDir) {
4482
4515
  const out = [];
4483
4516
  let dirs;
4484
4517
  try {
4485
- if (!fs6.statSync(geminiTmpDir).isDirectory()) return out;
4486
- dirs = fs6.readdirSync(geminiTmpDir);
4518
+ if (!fs7.statSync(geminiTmpDir).isDirectory()) return out;
4519
+ dirs = fs7.readdirSync(geminiTmpDir);
4487
4520
  } catch {
4488
4521
  return out;
4489
4522
  }
4490
4523
  for (const proj of dirs) {
4491
- const chatsDir = path7.join(geminiTmpDir, proj, "chats");
4524
+ const chatsDir = path8.join(geminiTmpDir, proj, "chats");
4492
4525
  let files;
4493
4526
  try {
4494
- if (!fs6.statSync(chatsDir).isDirectory()) continue;
4495
- files = fs6.readdirSync(chatsDir);
4527
+ if (!fs7.statSync(chatsDir).isDirectory()) continue;
4528
+ files = fs7.readdirSync(chatsDir);
4496
4529
  } catch {
4497
4530
  continue;
4498
4531
  }
4499
4532
  for (const f of files) {
4500
4533
  if (!f.endsWith(".jsonl")) continue;
4501
- out.push({ projectKey: proj, file: path7.join(chatsDir, f) });
4534
+ out.push({ projectKey: proj, file: path8.join(chatsDir, f) });
4502
4535
  }
4503
4536
  }
4504
4537
  return out;
4505
4538
  }
4506
4539
  function loadGeminiCost(start, end, geminiTmpDir) {
4507
4540
  const acc = emptyGeminiAccumulator();
4508
- if (!fs6.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
4541
+ if (!fs7.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
4509
4542
  for (const { projectKey, file } of listGeminiSessionFiles(geminiTmpDir)) {
4510
4543
  processGeminiCostFile(file, projectKey, start, end, acc);
4511
4544
  }
@@ -4513,7 +4546,7 @@ function loadGeminiCost(start, end, geminiTmpDir) {
4513
4546
  }
4514
4547
  async function loadGeminiCostAsync(start, end, geminiTmpDir) {
4515
4548
  const acc = emptyGeminiAccumulator();
4516
- if (!fs6.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
4549
+ if (!fs7.existsSync(geminiTmpDir)) return freezeGeminiCost(acc);
4517
4550
  const files = listGeminiSessionFiles(geminiTmpDir);
4518
4551
  const CHUNK_SIZE = 5;
4519
4552
  for (let i = 0; i < files.length; i++) {
@@ -4536,11 +4569,11 @@ function dimensionOfBlock(checkedBy, ruleName) {
4536
4569
  }
4537
4570
  function aggregateReportFromAudit(period, opts = {}) {
4538
4571
  const now = opts.now ?? /* @__PURE__ */ new Date();
4539
- const auditLogPath2 = opts.auditLogPath ?? path7.join(os7.homedir(), ".node9", "audit.log");
4540
- const claudeProjectsDir = opts.claudeProjectsDir ?? path7.join(os7.homedir(), ".claude", "projects");
4541
- const codexSessionsDir = opts.codexSessionsDir ?? path7.join(os7.homedir(), ".codex", "sessions");
4542
- const geminiTmpDir = opts.geminiTmpDir ?? path7.join(os7.homedir(), ".gemini", "tmp");
4543
- const hasAuditFile = fs6.existsSync(auditLogPath2);
4572
+ const auditLogPath2 = opts.auditLogPath ?? path8.join(os7.homedir(), ".node9", "audit.log");
4573
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path8.join(os7.homedir(), ".claude", "projects");
4574
+ const codexSessionsDir = opts.codexSessionsDir ?? path8.join(os7.homedir(), ".codex", "sessions");
4575
+ const geminiTmpDir = opts.geminiTmpDir ?? path8.join(os7.homedir(), ".gemini", "tmp");
4576
+ const hasAuditFile = fs7.existsSync(auditLogPath2);
4544
4577
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath2);
4545
4578
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
4546
4579
  const { start, end } = getDateRange(period, now);
@@ -4770,6 +4803,7 @@ var init_report_audit = __esm({
4770
4803
  init_litellm();
4771
4804
  init_cost_codex();
4772
4805
  init_decision();
4806
+ init_session_files();
4773
4807
  TEST_COMMAND_RE = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
4774
4808
  SUPERSEDE_WINDOW_MS = 6e4;
4775
4809
  GEMINI_FALLBACK_MODELS2 = ["gemini-2.5-flash", "gemini-2.0-flash"];
@@ -4777,18 +4811,18 @@ var init_report_audit = __esm({
4777
4811
  });
4778
4812
 
4779
4813
  // src/utils/provenance.ts
4780
- import path8 from "path";
4814
+ import path9 from "path";
4781
4815
  import os8 from "os";
4782
4816
  var USER_PREFIXES;
4783
4817
  var init_provenance = __esm({
4784
4818
  "src/utils/provenance.ts"() {
4785
4819
  "use strict";
4786
4820
  USER_PREFIXES = [
4787
- path8.join(os8.homedir(), "bin"),
4788
- path8.join(os8.homedir(), ".local", "bin"),
4789
- path8.join(os8.homedir(), ".cargo", "bin"),
4790
- path8.join(os8.homedir(), ".npm-global", "bin"),
4791
- path8.join(os8.homedir(), ".volta", "bin")
4821
+ path9.join(os8.homedir(), "bin"),
4822
+ path9.join(os8.homedir(), ".local", "bin"),
4823
+ path9.join(os8.homedir(), ".cargo", "bin"),
4824
+ path9.join(os8.homedir(), ".npm-global", "bin"),
4825
+ path9.join(os8.homedir(), ".volta", "bin")
4792
4826
  ];
4793
4827
  }
4794
4828
  });
@@ -4832,14 +4866,14 @@ var init_mcp_pin = __esm({
4832
4866
  });
4833
4867
 
4834
4868
  // src/daemon/hook-baseline.ts
4835
- import path9 from "path";
4869
+ import path10 from "path";
4836
4870
  import os9 from "os";
4837
4871
  var BASELINE_FILE, NOTIFIED_FILE;
4838
4872
  var init_hook_baseline = __esm({
4839
4873
  "src/daemon/hook-baseline.ts"() {
4840
4874
  "use strict";
4841
- BASELINE_FILE = path9.join(os9.homedir(), ".node9", "hooks-baseline.json");
4842
- NOTIFIED_FILE = path9.join(os9.homedir(), ".node9", "hook-heal-notified.json");
4875
+ BASELINE_FILE = path10.join(os9.homedir(), ".node9", "hooks-baseline.json");
4876
+ NOTIFIED_FILE = path10.join(os9.homedir(), ".node9", "hook-heal-notified.json");
4843
4877
  }
4844
4878
  });
4845
4879
 
@@ -4898,8 +4932,8 @@ var init_scan_history = __esm({
4898
4932
 
4899
4933
  // src/cli/commands/scan.ts
4900
4934
  import chalk4 from "chalk";
4901
- import fs7 from "fs";
4902
- import path10 from "path";
4935
+ import fs8 from "fs";
4936
+ import path11 from "path";
4903
4937
  import os10 from "os";
4904
4938
  import stringWidth2 from "string-width";
4905
4939
  function claudeModelPrice2(model) {
@@ -5046,10 +5080,11 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
5046
5080
  result.filesScanned++;
5047
5081
  result.sessions++;
5048
5082
  onProgress?.(result.filesScanned);
5049
- const sessionId = file.replace(/\.jsonl$/, "");
5083
+ const sessionId = sessionIdOf(file);
5084
+ const session = { sessionId, costUSD: 0, toolCalls: 0 };
5050
5085
  let raw;
5051
5086
  try {
5052
- raw = fs7.readFileSync(path10.join(projPath, file), "utf-8");
5087
+ raw = fs8.readFileSync(path11.join(projPath, file), "utf-8");
5053
5088
  } catch {
5054
5089
  return;
5055
5090
  }
@@ -5101,7 +5136,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
5101
5136
  if (block.type !== "tool_result") continue;
5102
5137
  const filePath = block.tool_use_id ? toolUseFilePaths.get(block.tool_use_id) : void 0;
5103
5138
  if (filePath) {
5104
- const ext = path10.extname(filePath).toLowerCase();
5139
+ const ext = path11.extname(filePath).toLowerCase();
5105
5140
  if (CODE_EXTENSIONS.has(ext)) continue;
5106
5141
  }
5107
5142
  const resultText = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => c.text ?? "").join("\n") : null;
@@ -5134,7 +5169,9 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
5134
5169
  if (usage && model) {
5135
5170
  const p = claudeModelPrice2(model);
5136
5171
  if (p) {
5137
- result.totalCostUSD += (usage.input_tokens ?? 0) * p.i + (usage.output_tokens ?? 0) * p.o + (usage.cache_creation_input_tokens ?? 0) * p.cw + (usage.cache_read_input_tokens ?? 0) * p.cr;
5172
+ const rowCost = (usage.input_tokens ?? 0) * p.i + (usage.output_tokens ?? 0) * p.o + (usage.cache_creation_input_tokens ?? 0) * p.cw + (usage.cache_read_input_tokens ?? 0) * p.cr;
5173
+ result.totalCostUSD += rowCost;
5174
+ session.costUSD += rowCost;
5138
5175
  }
5139
5176
  }
5140
5177
  const content = entry.message?.content;
@@ -5142,6 +5179,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
5142
5179
  for (const block of content) {
5143
5180
  if (block.type !== "tool_use") continue;
5144
5181
  result.totalToolCalls++;
5182
+ session.toolCalls++;
5145
5183
  const toolName = block.name ?? "";
5146
5184
  const toolNameLower = toolName.toLowerCase();
5147
5185
  const input = block.input ?? {};
@@ -5158,7 +5196,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
5158
5196
  const rawCmd = String(input.command ?? "").trimStart();
5159
5197
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
5160
5198
  const inputFilePath = typeof input.file_path === "string" ? input.file_path : "";
5161
- const inputFileExt = inputFilePath ? path10.extname(inputFilePath).toLowerCase() : "";
5199
+ const inputFileExt = inputFilePath ? path11.extname(inputFilePath).toLowerCase() : "";
5162
5200
  if (CODE_EXTENSIONS.has(inputFileExt)) continue;
5163
5201
  const dlpMatch = scanArgs(input);
5164
5202
  if (dlpMatch) {
@@ -5253,11 +5291,12 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
5253
5291
  if (firstDlpTs !== null && (firstEditTs === null || firstDlpTs < firstEditTs)) {
5254
5292
  result.sessionsWithEarlySecrets++;
5255
5293
  }
5294
+ result.perSession.push(session);
5256
5295
  }
5257
5296
  async function processClaudeProjectAsync(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
5258
- const projPath = path10.join(projectsDir, proj);
5297
+ const projPath = path11.join(projectsDir, proj);
5259
5298
  try {
5260
- if (!fs7.statSync(projPath).isDirectory()) return;
5299
+ if (!fs8.statSync(projPath).isDirectory()) return;
5261
5300
  } catch {
5262
5301
  return;
5263
5302
  }
@@ -5267,7 +5306,7 @@ async function processClaudeProjectAsync(proj, projectsDir, ruleSources, startDa
5267
5306
  );
5268
5307
  let files;
5269
5308
  try {
5270
- files = fs7.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
5309
+ files = listSessionFiles(projPath);
5271
5310
  } catch {
5272
5311
  return;
5273
5312
  }
@@ -5301,16 +5340,17 @@ function emptyClaudeScan() {
5301
5340
  totalCostUSD: 0,
5302
5341
  firstDate: null,
5303
5342
  lastDate: null,
5304
- sessionsWithEarlySecrets: 0
5343
+ sessionsWithEarlySecrets: 0,
5344
+ perSession: []
5305
5345
  };
5306
5346
  }
5307
5347
  async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
5308
- const projectsDir = path10.join(os10.homedir(), ".claude", "projects");
5348
+ const projectsDir = path11.join(os10.homedir(), ".claude", "projects");
5309
5349
  const result = emptyClaudeScan();
5310
- if (!fs7.existsSync(projectsDir)) return result;
5350
+ if (!fs8.existsSync(projectsDir)) return result;
5311
5351
  let projDirs;
5312
5352
  try {
5313
- projDirs = fs7.readdirSync(projectsDir);
5353
+ projDirs = fs8.readdirSync(projectsDir);
5314
5354
  } catch {
5315
5355
  return result;
5316
5356
  }
@@ -5331,7 +5371,7 @@ async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
5331
5371
  return result;
5332
5372
  }
5333
5373
  function scanGeminiHistory(startDate, onProgress, onLine) {
5334
- const tmpDir = path10.join(os10.homedir(), ".gemini", "tmp");
5374
+ const tmpDir = path11.join(os10.homedir(), ".gemini", "tmp");
5335
5375
  const result = {
5336
5376
  filesScanned: 0,
5337
5377
  sessions: 0,
@@ -5343,36 +5383,37 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
5343
5383
  totalCostUSD: 0,
5344
5384
  firstDate: null,
5345
5385
  lastDate: null,
5346
- sessionsWithEarlySecrets: 0
5386
+ sessionsWithEarlySecrets: 0,
5387
+ perSession: []
5347
5388
  };
5348
5389
  const dedup = emptyScanDedup();
5349
- if (!fs7.existsSync(tmpDir)) return result;
5390
+ if (!fs8.existsSync(tmpDir)) return result;
5350
5391
  let slugDirs;
5351
5392
  try {
5352
- slugDirs = fs7.readdirSync(tmpDir);
5393
+ slugDirs = fs8.readdirSync(tmpDir);
5353
5394
  } catch {
5354
5395
  return result;
5355
5396
  }
5356
5397
  const ruleSources = buildRuleSources();
5357
5398
  for (const slug2 of slugDirs) {
5358
- const slugPath = path10.join(tmpDir, slug2);
5399
+ const slugPath = path11.join(tmpDir, slug2);
5359
5400
  try {
5360
- if (!fs7.statSync(slugPath).isDirectory()) continue;
5401
+ if (!fs8.statSync(slugPath).isDirectory()) continue;
5361
5402
  } catch {
5362
5403
  continue;
5363
5404
  }
5364
5405
  let projLabel = stripTerminalEscapes(slug2).slice(0, 40);
5365
5406
  try {
5366
5407
  projLabel = stripTerminalEscapes(
5367
- fs7.readFileSync(path10.join(slugPath, ".project_root"), "utf-8").trim()
5408
+ fs8.readFileSync(path11.join(slugPath, ".project_root"), "utf-8").trim()
5368
5409
  ).replace(os10.homedir(), "~").slice(0, 40);
5369
5410
  } catch {
5370
5411
  }
5371
- const chatsDir = path10.join(slugPath, "chats");
5372
- if (!fs7.existsSync(chatsDir)) continue;
5412
+ const chatsDir = path11.join(slugPath, "chats");
5413
+ if (!fs8.existsSync(chatsDir)) continue;
5373
5414
  let chatFiles;
5374
5415
  try {
5375
- chatFiles = fs7.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
5416
+ chatFiles = fs8.readdirSync(chatsDir).filter((f) => f.endsWith(".json") || f.endsWith(".jsonl")).sort((a, b) => Number(b.endsWith(".jsonl")) - Number(a.endsWith(".jsonl")));
5376
5417
  } catch {
5377
5418
  continue;
5378
5419
  }
@@ -5385,7 +5426,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
5385
5426
  onProgress?.(result.filesScanned);
5386
5427
  let raw;
5387
5428
  try {
5388
- raw = fs7.readFileSync(path10.join(chatsDir, chatFile), "utf-8");
5429
+ raw = fs8.readFileSync(path11.join(chatsDir, chatFile), "utf-8");
5389
5430
  } catch {
5390
5431
  continue;
5391
5432
  }
@@ -5558,7 +5599,7 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
5558
5599
  return result;
5559
5600
  }
5560
5601
  function scanCodexHistory(startDate, onProgress, onLine) {
5561
- const sessionsBase = path10.join(os10.homedir(), ".codex", "sessions");
5602
+ const sessionsBase = path11.join(os10.homedir(), ".codex", "sessions");
5562
5603
  const result = {
5563
5604
  filesScanned: 0,
5564
5605
  sessions: 0,
@@ -5570,35 +5611,36 @@ function scanCodexHistory(startDate, onProgress, onLine) {
5570
5611
  totalCostUSD: 0,
5571
5612
  firstDate: null,
5572
5613
  lastDate: null,
5573
- sessionsWithEarlySecrets: 0
5614
+ sessionsWithEarlySecrets: 0,
5615
+ perSession: []
5574
5616
  };
5575
5617
  const dedup = emptyScanDedup();
5576
- if (!fs7.existsSync(sessionsBase)) return result;
5618
+ if (!fs8.existsSync(sessionsBase)) return result;
5577
5619
  const jsonlFiles = [];
5578
5620
  try {
5579
- for (const year of fs7.readdirSync(sessionsBase)) {
5580
- const yearPath = path10.join(sessionsBase, year);
5621
+ for (const year of fs8.readdirSync(sessionsBase)) {
5622
+ const yearPath = path11.join(sessionsBase, year);
5581
5623
  try {
5582
- if (!fs7.statSync(yearPath).isDirectory()) continue;
5624
+ if (!fs8.statSync(yearPath).isDirectory()) continue;
5583
5625
  } catch {
5584
5626
  continue;
5585
5627
  }
5586
- for (const month of fs7.readdirSync(yearPath)) {
5587
- const monthPath = path10.join(yearPath, month);
5628
+ for (const month of fs8.readdirSync(yearPath)) {
5629
+ const monthPath = path11.join(yearPath, month);
5588
5630
  try {
5589
- if (!fs7.statSync(monthPath).isDirectory()) continue;
5631
+ if (!fs8.statSync(monthPath).isDirectory()) continue;
5590
5632
  } catch {
5591
5633
  continue;
5592
5634
  }
5593
- for (const day of fs7.readdirSync(monthPath)) {
5594
- const dayPath = path10.join(monthPath, day);
5635
+ for (const day of fs8.readdirSync(monthPath)) {
5636
+ const dayPath = path11.join(monthPath, day);
5595
5637
  try {
5596
- if (!fs7.statSync(dayPath).isDirectory()) continue;
5638
+ if (!fs8.statSync(dayPath).isDirectory()) continue;
5597
5639
  } catch {
5598
5640
  continue;
5599
5641
  }
5600
- for (const file of fs7.readdirSync(dayPath)) {
5601
- if (file.endsWith(".jsonl")) jsonlFiles.push(path10.join(dayPath, file));
5642
+ for (const file of fs8.readdirSync(dayPath)) {
5643
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path11.join(dayPath, file));
5602
5644
  }
5603
5645
  }
5604
5646
  }
@@ -5612,7 +5654,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
5612
5654
  onProgress?.(result.filesScanned);
5613
5655
  let lines;
5614
5656
  try {
5615
- lines = fs7.readFileSync(filePath, "utf-8").split("\n");
5657
+ lines = fs8.readFileSync(filePath, "utf-8").split("\n");
5616
5658
  } catch {
5617
5659
  continue;
5618
5660
  }
@@ -5817,6 +5859,7 @@ var init_scan = __esm({
5817
5859
  init_scan_derive();
5818
5860
  init_protection();
5819
5861
  init_scan_json();
5862
+ init_session_files();
5820
5863
  init_scan_history();
5821
5864
  toolInspectionMap = DEFAULT_CONFIG.policy.toolInspection;
5822
5865
  CODE_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -5881,23 +5924,23 @@ var init_scan = __esm({
5881
5924
  });
5882
5925
 
5883
5926
  // src/tui/dashboard/data.ts
5884
- import fs8 from "fs";
5927
+ import fs9 from "fs";
5885
5928
  import os11 from "os";
5886
- import path11 from "path";
5929
+ import path12 from "path";
5887
5930
  import http from "http";
5888
5931
  function auditLogPath() {
5889
- return path11.join(os11.homedir(), ".node9", "audit.log");
5932
+ return path12.join(os11.homedir(), ".node9", "audit.log");
5890
5933
  }
5891
5934
  function readAuditEntriesAsync(chunkSize = 1e3, customPath) {
5892
5935
  return new Promise((resolve) => {
5893
5936
  const p = customPath ?? auditLogPath();
5894
- if (!fs8.existsSync(p)) {
5937
+ if (!fs9.existsSync(p)) {
5895
5938
  resolve([]);
5896
5939
  return;
5897
5940
  }
5898
5941
  let raw;
5899
5942
  try {
5900
- raw = fs8.readFileSync(p, "utf8");
5943
+ raw = fs9.readFileSync(p, "utf8");
5901
5944
  } catch {
5902
5945
  resolve([]);
5903
5946
  return;
@@ -6033,9 +6076,9 @@ function shortenPath(p) {
6033
6076
  return p.startsWith(home) ? p.replace(home, "~") : p;
6034
6077
  }
6035
6078
  async function loadReportAuditAsync(period) {
6036
- const claudeProjectsDir = path11.join(os11.homedir(), ".claude", "projects");
6037
- const codexSessionsDir = path11.join(os11.homedir(), ".codex", "sessions");
6038
- const geminiTmpDir = path11.join(os11.homedir(), ".gemini", "tmp");
6079
+ const claudeProjectsDir = path12.join(os11.homedir(), ".claude", "projects");
6080
+ const codexSessionsDir = path12.join(os11.homedir(), ".codex", "sessions");
6081
+ const geminiTmpDir = path12.join(os11.homedir(), ".gemini", "tmp");
6039
6082
  const { start, end } = getDateRange(period, /* @__PURE__ */ new Date());
6040
6083
  const entries = await readAuditEntriesAsync();
6041
6084
  void ensurePricingLoaded();
@@ -7070,7 +7113,7 @@ function Cost({ audit }) {
7070
7113
  flexGrow: 1,
7071
7114
  flexBasis: 0,
7072
7115
  children: [
7073
- /* @__PURE__ */ jsx3(Text3, { bold: true, children: "COST" }),
7116
+ /* @__PURE__ */ jsx3(Text3, { bold: true, children: "COST \xB7 API value" }),
7074
7117
  audit === null ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "loading\u2026" }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
7075
7118
  /* @__PURE__ */ jsxs3(Box3, { height: 1, children: [
7076
7119
  /* @__PURE__ */ jsx3(Box3, { width: LABEL_W2, children: /* @__PURE__ */ jsx3(Text3, { children: "Total" }) }),
@@ -7116,8 +7159,8 @@ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-run
7116
7159
  function TopToolsProjects({ audit }) {
7117
7160
  const data = audit?.data;
7118
7161
  const tools = data ? [...data.toolMap.entries()].sort(([, a], [, b]) => b.calls - a.calls).slice(0, ROW_LIMIT) : [];
7119
- const projects = data ? [...data.cost.byProject.entries()].map(([path12, r]) => ({
7120
- name: basenameOf(path12),
7162
+ const projects = data ? [...data.cost.byProject.entries()].map(([path13, r]) => ({
7163
+ name: basenameOf(path13),
7121
7164
  cost: r.cost,
7122
7165
  tokens: r.inputTokens + r.outputTokens
7123
7166
  })).sort((a, b) => b.cost - a.cost).slice(0, ROW_LIMIT) : [];
@@ -7554,8 +7597,8 @@ function pickTopLoopFile(loops) {
7554
7597
  map.set(k, (map.get(k) ?? 0) + (l.count ?? 0));
7555
7598
  }
7556
7599
  if (map.size === 0) return void 0;
7557
- const [path12, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
7558
- return { path: path12, count };
7600
+ const [path13, count] = [...map.entries()].sort((a, b) => b[1] - a[1])[0];
7601
+ return { path: path13, count };
7559
7602
  }
7560
7603
  var EMPTY_FILTERED_SCAN;
7561
7604
  var init_derive = __esm({